Compare commits
No commits in common. "master" and "v0.15.4" have entirely different histories.
181 changed files with 3874 additions and 6403 deletions
CHANGELOG.mdREADME.rst
docs
pyproject.tomltailbone
api
app.pyauth.pyconfig.pydiffs.pyforms
grids
helpers.pymenus.pystatic
subscribers.pytemplates
appinfo
appsettings.makobase.makobase_meta.makobatch
configure-menus.makoconfigure.makocustomers
custorders
datasync
deform
departments
form.makoformposter.makoforms
generate_feature.makogrids
home.makoimporting
login.makoluigi
master
members
messages
ordering
page.makopeople
poser
principal
products
purchases/credits
receiving
reports
roles
settings/email
tables
254
CHANGELOG.md
254
CHANGELOG.md
|
@ -5,260 +5,6 @@ All notable changes to Tailbone will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v0.22.7 (2025-02-19)
|
||||
|
||||
### Fix
|
||||
|
||||
- stop using old config for logo image url on login page
|
||||
- fix warning msg for deprecated Grid param
|
||||
|
||||
## v0.22.6 (2025-02-01)
|
||||
|
||||
### Fix
|
||||
|
||||
- register vue3 form component for products -> make batch
|
||||
|
||||
## v0.22.5 (2024-12-16)
|
||||
|
||||
### Fix
|
||||
|
||||
- whoops this is latest rattail
|
||||
- require newer rattail lib
|
||||
- require newer wuttaweb
|
||||
- let caller request safe HTML literal for rendered grid table
|
||||
|
||||
## v0.22.4 (2024-11-22)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid error in product search for duplicated key
|
||||
- use vmodel for confirm password widget input
|
||||
|
||||
## v0.22.3 (2024-11-19)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid error for trainwreck query when not a customer
|
||||
|
||||
## v0.22.2 (2024-11-18)
|
||||
|
||||
### Fix
|
||||
|
||||
- use local/custom enum for continuum operations
|
||||
- add basic master view for Product Costs
|
||||
- show continuum operation type when viewing version history
|
||||
- always define `app` attr for ViewSupplement
|
||||
- avoid deprecated import
|
||||
|
||||
## v0.22.1 (2024-11-02)
|
||||
|
||||
### Fix
|
||||
|
||||
- fix submit button for running problem report
|
||||
- avoid deprecated grid method
|
||||
|
||||
## v0.22.0 (2024-10-22)
|
||||
|
||||
### Feat
|
||||
|
||||
- add support for new ordering batch from parsed file
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid deprecated method to suggest username
|
||||
|
||||
## v0.21.11 (2024-10-03)
|
||||
|
||||
### Fix
|
||||
|
||||
- custom method for adding grid action
|
||||
- become/stop root should redirect to previous url
|
||||
|
||||
## v0.21.10 (2024-09-15)
|
||||
|
||||
### Fix
|
||||
|
||||
- update project repo links, kallithea -> forgejo
|
||||
- use better icon for submit button on login page
|
||||
- wrap notes text for batch view
|
||||
- expose datasync consumer batch size via configure page
|
||||
|
||||
## v0.21.9 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- render custom attrs in form component tag
|
||||
|
||||
## v0.21.8 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- ignore session kwarg for `MasterView.make_row_grid()`
|
||||
|
||||
## v0.21.7 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid error when form value cannot be obtained
|
||||
|
||||
## v0.21.6 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid error when grid value cannot be obtained
|
||||
|
||||
## v0.21.5 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- set empty string for "-new-" file configure option
|
||||
|
||||
## v0.21.4 (2024-08-26)
|
||||
|
||||
### Fix
|
||||
|
||||
- handle differing email profile keys for appinfo/configure
|
||||
|
||||
## v0.21.3 (2024-08-26)
|
||||
|
||||
### Fix
|
||||
|
||||
- show non-standard config values for app info configure email
|
||||
|
||||
## v0.21.2 (2024-08-26)
|
||||
|
||||
### Fix
|
||||
|
||||
- refactor waterpark base template to use wutta feedback component
|
||||
- fix input/output file upload feature for configure pages, per oruga
|
||||
- tweak how grid data translates to Vue template context
|
||||
- merge filters into main grid template
|
||||
- add basic wutta view for users
|
||||
- some fixes for wutta people view
|
||||
- various fixes for waterpark theme
|
||||
- avoid deprecated `component` form kwarg
|
||||
|
||||
## v0.21.1 (2024-08-22)
|
||||
|
||||
### Fix
|
||||
|
||||
- misc. bugfixes per recent changes
|
||||
|
||||
## v0.21.0 (2024-08-22)
|
||||
|
||||
### Feat
|
||||
|
||||
- move "most" filtering logic for grid class to wuttaweb
|
||||
- inherit from wuttaweb templates for home, login pages
|
||||
- inherit from wuttaweb for AppInfoView, appinfo/configure template
|
||||
- add "has output file templates" config option for master view
|
||||
|
||||
### Fix
|
||||
|
||||
- change grid reset-view param name to match wuttaweb
|
||||
- move "searchable columns" grid feature to wuttaweb
|
||||
- use wuttaweb to get/render csrf token
|
||||
- inherit from wuttaweb for appinfo/index template
|
||||
- prefer wuttaweb config for "home redirect to login" feature
|
||||
- fix master/index template rendering for waterpark theme
|
||||
- fix spacing for navbar logo/title in waterpark theme
|
||||
|
||||
## v0.20.1 (2024-08-20)
|
||||
|
||||
### Fix
|
||||
|
||||
- fix default filter verbs logic for workorder status
|
||||
|
||||
## v0.20.0 (2024-08-20)
|
||||
|
||||
### Feat
|
||||
|
||||
- add new 'waterpark' theme, based on wuttaweb w/ vue2 + buefy
|
||||
- refactor templates to simplify base/page/form structure
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid deprecated reference to app db engine
|
||||
|
||||
## v0.19.3 (2024-08-19)
|
||||
|
||||
### Fix
|
||||
|
||||
- add pager stats to all grid vue data (fixes view history)
|
||||
|
||||
## v0.19.2 (2024-08-19)
|
||||
|
||||
### Fix
|
||||
|
||||
- sort on frontend for appinfo package listing grid
|
||||
- prefer attr over key lookup when getting model values
|
||||
- replace all occurrences of `component_studly` => `vue_component`
|
||||
|
||||
## v0.19.1 (2024-08-19)
|
||||
|
||||
### Fix
|
||||
|
||||
- fix broken user auth for web API app
|
||||
|
||||
## v0.19.0 (2024-08-18)
|
||||
|
||||
### Feat
|
||||
|
||||
- move multi-column grid sorting logic to wuttaweb
|
||||
- move single-column grid sorting logic to wuttaweb
|
||||
|
||||
### Fix
|
||||
|
||||
- fix misc. errors in grid template per wuttaweb
|
||||
- fix broken permission directives in web api startup
|
||||
|
||||
## v0.18.0 (2024-08-16)
|
||||
|
||||
### Feat
|
||||
|
||||
- move "basic" grid pagination logic to wuttaweb
|
||||
- inherit from wutta base class for Grid
|
||||
- inherit most logic from wuttaweb, for GridAction
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid route error in user view, when using wutta people view
|
||||
- fix some more wutta compat for base template
|
||||
|
||||
## v0.17.0 (2024-08-15)
|
||||
|
||||
### Feat
|
||||
|
||||
- use wuttaweb for `get_liburl()` logic
|
||||
|
||||
## v0.16.1 (2024-08-15)
|
||||
|
||||
### Fix
|
||||
|
||||
- improve wutta People view a bit
|
||||
- update references to `get_class_hierarchy()`
|
||||
- tweak template for `people/view_profile` per wutta compat
|
||||
|
||||
## v0.16.0 (2024-08-15)
|
||||
|
||||
### Feat
|
||||
|
||||
- add first wutta-based master, for PersonView
|
||||
- refactor forms/grids/views/templates per wuttaweb compat
|
||||
|
||||
## v0.15.6 (2024-08-13)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid `before_render` subscriber hook for web API
|
||||
- simplify verbiage for batch execution panel
|
||||
|
||||
## v0.15.5 (2024-08-09)
|
||||
|
||||
### Fix
|
||||
|
||||
- assign convenience attrs for all views (config, app, enum, model)
|
||||
|
||||
## v0.15.4 (2024-08-09)
|
||||
|
||||
### Fix
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
|
||||
# Tailbone
|
||||
Tailbone
|
||||
========
|
||||
|
||||
Tailbone is an extensible web application based on Rattail. It provides a
|
||||
"back-office network environment" (BONE) for use in managing retail data.
|
||||
|
||||
Please see Rattail's [home page](http://rattailproject.org/) for more
|
||||
information.
|
||||
Please see Rattail's `home page`_ for more information.
|
||||
|
||||
.. _home page: http://rattailproject.org/
|
|
@ -27,10 +27,10 @@ templates_path = ['_templates']
|
|||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
intersphinx_mapping = {
|
||||
'rattail': ('https://docs.wuttaproject.org/rattail/', None),
|
||||
'rattail': ('https://rattailproject.org/docs/rattail/', None),
|
||||
'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None),
|
||||
'wuttaweb': ('https://docs.wuttaproject.org/wuttaweb/', None),
|
||||
'wuttjamaican': ('https://docs.wuttaproject.org/wuttjamaican/', None),
|
||||
'wuttaweb': ('https://rattailproject.org/docs/wuttaweb/', None),
|
||||
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
|
||||
}
|
||||
|
||||
# allow todo entries to show up
|
||||
|
|
|
@ -6,9 +6,9 @@ build-backend = "hatchling.build"
|
|||
|
||||
[project]
|
||||
name = "Tailbone"
|
||||
version = "0.22.7"
|
||||
version = "0.15.4"
|
||||
description = "Backoffice Web Application for Rattail"
|
||||
readme = "README.md"
|
||||
readme = "README.rst"
|
||||
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
||||
license = {text = "GNU GPL v3+"}
|
||||
classifiers = [
|
||||
|
@ -53,13 +53,13 @@ dependencies = [
|
|||
"pyramid_mako",
|
||||
"pyramid_retry",
|
||||
"pyramid_tm",
|
||||
"rattail[db,bouncer]>=0.20.1",
|
||||
"rattail[db,bouncer]>=0.17.0",
|
||||
"sa-filters",
|
||||
"simplejson",
|
||||
"transaction",
|
||||
"waitress",
|
||||
"WebHelpers2",
|
||||
"WuttaWeb>=0.21.0",
|
||||
"WuttaWeb>=0.2.0",
|
||||
"zope.sqlalchemy>=1.5",
|
||||
]
|
||||
|
||||
|
@ -84,9 +84,9 @@ tailbone = "tailbone.config:ConfigExtension"
|
|||
|
||||
[project.urls]
|
||||
Homepage = "https://rattailproject.org"
|
||||
Repository = "https://forgejo.wuttaproject.org/rattail/tailbone"
|
||||
Issues = "https://forgejo.wuttaproject.org/rattail/tailbone/issues"
|
||||
Changelog = "https://forgejo.wuttaproject.org/rattail/tailbone/src/branch/master/CHANGELOG.md"
|
||||
Repository = "https://kallithea.rattailproject.org/rattail-project/tailbone"
|
||||
Issues = "https://redmine.rattailproject.org/projects/tailbone/issues"
|
||||
Changelog = "https://kallithea.rattailproject.org/rattail-project/tailbone/files/master/CHANGELOG.md"
|
||||
|
||||
|
||||
[tool.commitizen]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,6 +24,8 @@
|
|||
Tailbone Web API - Auth Views
|
||||
"""
|
||||
|
||||
from rattail.db.auth import set_user_password
|
||||
|
||||
from cornice import Service
|
||||
|
||||
from tailbone.api import APIView, api
|
||||
|
@ -40,10 +42,11 @@ class AuthenticationView(APIView):
|
|||
This will establish a server-side web session for the user if none
|
||||
exists. Note that this also resets the user's session timer.
|
||||
"""
|
||||
data = {'ok': True, 'permissions': []}
|
||||
data = {'ok': True}
|
||||
if self.request.user:
|
||||
data['user'] = self.get_user_info(self.request.user)
|
||||
data['permissions'] = list(self.request.user_permissions)
|
||||
|
||||
data['permissions'] = list(self.request.tailbone_cached_permissions)
|
||||
|
||||
# background color may be set per-request, by some apps
|
||||
if hasattr(self.request, 'background_color') and self.request.background_color:
|
||||
|
@ -173,8 +176,7 @@ class AuthenticationView(APIView):
|
|||
return {'error': "The current/old password you provided is incorrect"}
|
||||
|
||||
# okay then, set new password
|
||||
auth = self.app.get_auth_handler()
|
||||
auth.set_user_password(self.request.user, data['new_password'])
|
||||
set_user_password(self.request.user, data['new_password'])
|
||||
return {
|
||||
'ok': True,
|
||||
'user': self.get_user_info(self.request.user),
|
||||
|
|
|
@ -29,7 +29,8 @@ import logging
|
|||
import humanize
|
||||
import sqlalchemy as sa
|
||||
|
||||
from rattail.db.model import PurchaseBatch, PurchaseBatchRow
|
||||
from rattail.db import model
|
||||
from rattail.util import pretty_quantity
|
||||
|
||||
from cornice import Service
|
||||
from deform import widget as dfwidget
|
||||
|
@ -44,7 +45,7 @@ log = logging.getLogger(__name__)
|
|||
|
||||
class ReceivingBatchViews(APIBatchView):
|
||||
|
||||
model_class = PurchaseBatch
|
||||
model_class = model.PurchaseBatch
|
||||
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
|
||||
route_prefix = 'receivingbatchviews'
|
||||
permission_prefix = 'receiving'
|
||||
|
@ -54,8 +55,7 @@ class ReceivingBatchViews(APIBatchView):
|
|||
supports_execute = True
|
||||
|
||||
def base_query(self):
|
||||
model = self.app.model
|
||||
query = super().base_query()
|
||||
query = super(ReceivingBatchViews, self).base_query()
|
||||
query = query.filter(model.PurchaseBatch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING)
|
||||
return query
|
||||
|
||||
|
@ -85,7 +85,7 @@ class ReceivingBatchViews(APIBatchView):
|
|||
|
||||
# assume "receive from PO" if given a PO key
|
||||
if data.get('purchase_key'):
|
||||
data['workflow'] = 'from_po'
|
||||
data['receiving_workflow'] = 'from_po'
|
||||
|
||||
return super().create_object(data)
|
||||
|
||||
|
@ -120,7 +120,6 @@ class ReceivingBatchViews(APIBatchView):
|
|||
return self._get(obj=batch)
|
||||
|
||||
def eligible_purchases(self):
|
||||
model = self.app.model
|
||||
uuid = self.request.params.get('vendor_uuid')
|
||||
vendor = self.Session.get(model.Vendor, uuid) if uuid else None
|
||||
if not vendor:
|
||||
|
@ -177,7 +176,7 @@ class ReceivingBatchViews(APIBatchView):
|
|||
|
||||
class ReceivingBatchRowViews(APIBatchRowView):
|
||||
|
||||
model_class = PurchaseBatchRow
|
||||
model_class = model.PurchaseBatchRow
|
||||
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
|
||||
route_prefix = 'receiving.rows'
|
||||
permission_prefix = 'receiving'
|
||||
|
@ -186,8 +185,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
supports_quick_entry = True
|
||||
|
||||
def make_filter_spec(self):
|
||||
model = self.app.model
|
||||
filters = super().make_filter_spec()
|
||||
filters = super(ReceivingBatchRowViews, self).make_filter_spec()
|
||||
if filters:
|
||||
|
||||
# must translate certain convenience filters
|
||||
|
@ -298,11 +296,11 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
return filters
|
||||
|
||||
def normalize(self, row):
|
||||
data = super().normalize(row)
|
||||
model = self.app.model
|
||||
data = super(ReceivingBatchRowViews, self).normalize(row)
|
||||
|
||||
batch = row.batch
|
||||
prodder = self.app.get_products_handler()
|
||||
app = self.get_rattail_app()
|
||||
prodder = app.get_products_handler()
|
||||
|
||||
data['product_uuid'] = row.product_uuid
|
||||
data['item_id'] = row.item_id
|
||||
|
@ -377,7 +375,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
if accounted_for:
|
||||
# some product accounted for; button should receive "remainder" only
|
||||
if remainder:
|
||||
remainder = self.app.render_quantity(remainder)
|
||||
remainder = pretty_quantity(remainder)
|
||||
data['quick_receive_quantity'] = remainder
|
||||
data['quick_receive_text'] = "Receive Remainder ({} {})".format(
|
||||
remainder, data['unit_uom'])
|
||||
|
@ -388,7 +386,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
else: # nothing yet accounted for, button should receive "all"
|
||||
if not remainder:
|
||||
log.warning("quick receive remainder is empty for row %s", row.uuid)
|
||||
remainder = self.app.render_quantity(remainder)
|
||||
remainder = pretty_quantity(remainder)
|
||||
data['quick_receive_quantity'] = remainder
|
||||
data['quick_receive_text'] = "Receive ALL ({} {})".format(
|
||||
remainder, data['unit_uom'])
|
||||
|
@ -416,7 +414,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
data['received_alert'] = None
|
||||
if self.batch_handler.get_units_confirmed(row):
|
||||
msg = "You have already received some of this product; last update was {}.".format(
|
||||
humanize.naturaltime(self.app.make_utc() - row.modified))
|
||||
humanize.naturaltime(app.make_utc() - row.modified))
|
||||
data['received_alert'] = msg
|
||||
|
||||
return data
|
||||
|
@ -425,8 +423,6 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
"""
|
||||
View which handles "receiving" against a particular batch row.
|
||||
"""
|
||||
model = self.app.model
|
||||
|
||||
# first do basic input validation
|
||||
schema = ReceiveRow().bind(session=self.Session())
|
||||
form = forms.Form(schema=schema, request=self.request)
|
||||
|
|
|
@ -26,6 +26,7 @@ Tailbone Web API - Master View
|
|||
|
||||
import json
|
||||
|
||||
from rattail.config import parse_bool
|
||||
from rattail.db.util import get_fieldnames
|
||||
|
||||
from cornice import resource, Service
|
||||
|
@ -184,7 +185,7 @@ class APIMasterView(APIView):
|
|||
if sortcol:
|
||||
spec = {
|
||||
'field': sortcol.field_name,
|
||||
'direction': 'asc' if self.config.parse_bool(self.request.params['ascending']) else 'desc',
|
||||
'direction': 'asc' if parse_bool(self.request.params['ascending']) else 'desc',
|
||||
}
|
||||
if sortcol.model_name:
|
||||
spec['model'] = sortcol.model_name
|
||||
|
|
|
@ -25,15 +25,19 @@ Application Entry Point
|
|||
"""
|
||||
|
||||
import os
|
||||
import warnings
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||
|
||||
from wuttjamaican.util import parse_list
|
||||
|
||||
from rattail.config import make_config
|
||||
from rattail.exceptions import ConfigurationError
|
||||
from rattail.db.types import GPCType
|
||||
|
||||
from pyramid.config import Configurator
|
||||
from pyramid.authentication import SessionAuthenticationPolicy
|
||||
from zope.sqlalchemy import register
|
||||
|
||||
import tailbone.db
|
||||
|
@ -62,20 +66,9 @@ def make_rattail_config(settings):
|
|||
# nb. this is for compaibility with wuttaweb
|
||||
settings['wutta_config'] = rattail_config
|
||||
|
||||
# must import all sqlalchemy models before things get rolling,
|
||||
# otherwise can have errors about continuum TransactionMeta class
|
||||
# not yet mapped, when relevant pages are first requested...
|
||||
# cf. https://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/database/sqlalchemy.html#importing-all-sqlalchemy-models
|
||||
# hat tip to https://stackoverflow.com/a/59241485
|
||||
if getattr(rattail_config, 'tempmon_engine', None):
|
||||
from rattail_tempmon.db import model as tempmon_model, Session as TempmonSession
|
||||
tempmon_session = TempmonSession()
|
||||
tempmon_session.query(tempmon_model.Appliance).first()
|
||||
tempmon_session.close()
|
||||
|
||||
# configure database sessions
|
||||
if hasattr(rattail_config, 'appdb_engine'):
|
||||
tailbone.db.Session.configure(bind=rattail_config.appdb_engine)
|
||||
if hasattr(rattail_config, 'rattail_engine'):
|
||||
tailbone.db.Session.configure(bind=rattail_config.rattail_engine)
|
||||
if hasattr(rattail_config, 'trainwreck_engine'):
|
||||
tailbone.db.TrainwreckSession.configure(bind=rattail_config.trainwreck_engine)
|
||||
if hasattr(rattail_config, 'tempmon_engine'):
|
||||
|
@ -196,16 +189,9 @@ def make_pyramid_config(settings, configure_csrf=True):
|
|||
for spec in includes:
|
||||
config.include(spec)
|
||||
|
||||
# add some permissions magic
|
||||
config.add_directive('add_wutta_permission_group',
|
||||
'wuttaweb.auth.add_permission_group')
|
||||
config.add_directive('add_wutta_permission',
|
||||
'wuttaweb.auth.add_permission')
|
||||
# TODO: deprecate / remove these
|
||||
config.add_directive('add_tailbone_permission_group',
|
||||
'wuttaweb.auth.add_permission_group')
|
||||
config.add_directive('add_tailbone_permission',
|
||||
'wuttaweb.auth.add_permission')
|
||||
# Add some permissions magic.
|
||||
config.add_directive('add_tailbone_permission_group', 'tailbone.auth.add_permission_group')
|
||||
config.add_directive('add_tailbone_permission', 'tailbone.auth.add_permission')
|
||||
|
||||
# and some similar magic for certain master views
|
||||
config.add_directive('add_tailbone_index_page', 'tailbone.app.add_index_page')
|
||||
|
@ -332,8 +318,7 @@ def main(global_config, **settings):
|
|||
"""
|
||||
This function returns a Pyramid WSGI application.
|
||||
"""
|
||||
settings.setdefault('mako.directories', ['tailbone:templates',
|
||||
'wuttaweb:templates'])
|
||||
settings.setdefault('mako.directories', ['tailbone:templates'])
|
||||
rattail_config = make_rattail_config(settings)
|
||||
pyramid_config = make_pyramid_config(settings)
|
||||
pyramid_config.include('tailbone')
|
||||
|
|
|
@ -27,18 +27,20 @@ Authentication & Authorization
|
|||
import logging
|
||||
import re
|
||||
|
||||
from wuttjamaican.util import UNSPECIFIED
|
||||
from rattail.util import prettify, NOTSET
|
||||
|
||||
from zope.interface import implementer
|
||||
from pyramid.authentication import SessionAuthenticationHelper
|
||||
from pyramid.request import RequestLocalCache
|
||||
from pyramid.security import remember, forget
|
||||
|
||||
from wuttaweb.auth import WuttaSecurityPolicy
|
||||
from tailbone.db import Session
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def login_user(request, user, timeout=UNSPECIFIED):
|
||||
def login_user(request, user, timeout=NOTSET):
|
||||
"""
|
||||
Perform the steps necessary to login the given user. Note that this
|
||||
returns a ``headers`` dict which you should pass to the redirect.
|
||||
|
@ -47,7 +49,7 @@ def login_user(request, user, timeout=UNSPECIFIED):
|
|||
app = config.get_app()
|
||||
user.record_event(app.enum.USER_EVENT_LOGIN)
|
||||
headers = remember(request, user.uuid)
|
||||
if timeout is UNSPECIFIED:
|
||||
if timeout is NOTSET:
|
||||
timeout = session_timeout_for_user(config, user)
|
||||
log.debug("setting session timeout for '{}' to {}".format(user.username, timeout))
|
||||
set_session_timeout(request, timeout)
|
||||
|
@ -92,12 +94,12 @@ def set_session_timeout(request, timeout):
|
|||
request.session['_timeout'] = timeout or None
|
||||
|
||||
|
||||
class TailboneSecurityPolicy(WuttaSecurityPolicy):
|
||||
class TailboneSecurityPolicy:
|
||||
|
||||
def __init__(self, db_session=None, api_mode=False, **kwargs):
|
||||
kwargs['db_session'] = db_session or Session()
|
||||
super().__init__(**kwargs)
|
||||
def __init__(self, api_mode=False):
|
||||
self.api_mode = api_mode
|
||||
self.session_helper = SessionAuthenticationHelper()
|
||||
self.identity_cache = RequestLocalCache(self.load_identity)
|
||||
|
||||
def load_identity(self, request):
|
||||
config = request.registry.settings.get('rattail_config')
|
||||
|
@ -113,7 +115,7 @@ class TailboneSecurityPolicy(WuttaSecurityPolicy):
|
|||
if match:
|
||||
token = match.group(1)
|
||||
auth = app.get_auth_handler()
|
||||
user = auth.authenticate_user_token(self.db_session, token)
|
||||
user = auth.authenticate_user_token(Session(), token)
|
||||
|
||||
if not user:
|
||||
|
||||
|
@ -124,10 +126,63 @@ class TailboneSecurityPolicy(WuttaSecurityPolicy):
|
|||
|
||||
# fetch user object from db
|
||||
model = app.model
|
||||
user = self.db_session.get(model.User, uuid)
|
||||
user = Session.get(model.User, uuid)
|
||||
if not user:
|
||||
return
|
||||
|
||||
# this user is responsible for data changes in current request
|
||||
self.db_session.set_continuum_user(user)
|
||||
Session().set_continuum_user(user)
|
||||
return user
|
||||
|
||||
def identity(self, request):
|
||||
return self.identity_cache.get_or_create(request)
|
||||
|
||||
def authenticated_userid(self, request):
|
||||
user = self.identity(request)
|
||||
if user is not None:
|
||||
return user.uuid
|
||||
|
||||
def remember(self, request, userid, **kw):
|
||||
return self.session_helper.remember(request, userid, **kw)
|
||||
|
||||
def forget(self, request, **kw):
|
||||
return self.session_helper.forget(request, **kw)
|
||||
|
||||
def permits(self, request, context, permission):
|
||||
# nb. root user can do anything
|
||||
if request.is_root:
|
||||
return True
|
||||
|
||||
config = request.registry.settings.get('rattail_config')
|
||||
app = config.get_app()
|
||||
auth = app.get_auth_handler()
|
||||
|
||||
user = self.identity(request)
|
||||
return auth.has_permission(Session(), user, permission)
|
||||
|
||||
|
||||
def add_permission_group(config, key, label=None, overwrite=True):
|
||||
"""
|
||||
Add a permission group to the app configuration.
|
||||
"""
|
||||
def action():
|
||||
perms = config.get_settings().get('tailbone_permissions', {})
|
||||
if key not in perms or overwrite:
|
||||
group = perms.setdefault(key, {'key': key})
|
||||
group['label'] = label or prettify(key)
|
||||
config.add_settings({'tailbone_permissions': perms})
|
||||
config.action(None, action)
|
||||
|
||||
|
||||
def add_permission(config, groupkey, key, label=None):
|
||||
"""
|
||||
Add a permission to the app configuration.
|
||||
"""
|
||||
def action():
|
||||
perms = config.get_settings().get('tailbone_permissions', {})
|
||||
group = perms.setdefault(groupkey, {'key': groupkey})
|
||||
group.setdefault('label', prettify(groupkey))
|
||||
perm = group.setdefault('perms', {}).setdefault(key, {'key': key})
|
||||
perm['label'] = label or prettify(key)
|
||||
config.add_settings({'tailbone_permissions': perms})
|
||||
config.action(None, action)
|
||||
|
|
|
@ -26,14 +26,13 @@ Rattail config extension for Tailbone
|
|||
|
||||
import warnings
|
||||
|
||||
from wuttjamaican.conf import WuttaConfigExtension
|
||||
|
||||
from rattail.config import ConfigExtension as BaseExtension
|
||||
from rattail.db.config import configure_session
|
||||
|
||||
from tailbone.db import Session
|
||||
|
||||
|
||||
class ConfigExtension(WuttaConfigExtension):
|
||||
class ConfigExtension(BaseExtension):
|
||||
"""
|
||||
Rattail config extension for Tailbone. Does the following:
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -270,21 +270,9 @@ class VersionDiff(Diff):
|
|||
for field in self.fields:
|
||||
values[field] = {'before': self.render_old_value(field),
|
||||
'after': self.render_new_value(field)}
|
||||
|
||||
operation = None
|
||||
if self.version.operation_type == continuum.Operation.INSERT:
|
||||
operation = 'INSERT'
|
||||
elif self.version.operation_type == continuum.Operation.UPDATE:
|
||||
operation = 'UPDATE'
|
||||
elif self.version.operation_type == continuum.Operation.DELETE:
|
||||
operation = 'DELETE'
|
||||
else:
|
||||
operation = self.version.operation_type
|
||||
|
||||
return {
|
||||
'key': id(self.version),
|
||||
'model_title': self.title,
|
||||
'operation': operation,
|
||||
'diff_class': self.nature,
|
||||
'fields': self.fields,
|
||||
'values': values,
|
||||
|
|
|
@ -47,7 +47,7 @@ from pyramid_deform import SessionFileUploadTempStore
|
|||
from pyramid.renderers import render
|
||||
from webhelpers2.html import tags, HTML
|
||||
|
||||
from wuttaweb.util import FieldList, get_form_data, make_json_safe
|
||||
from wuttaweb.util import get_form_data
|
||||
|
||||
from tailbone.db import Session
|
||||
from tailbone.util import raw_datetime, render_markdown
|
||||
|
@ -328,7 +328,7 @@ class Form(object):
|
|||
"""
|
||||
Base class for all forms.
|
||||
"""
|
||||
save_label = "Submit"
|
||||
save_label = "Save"
|
||||
update_label = "Save"
|
||||
show_cancel = True
|
||||
auto_disable = True
|
||||
|
@ -339,12 +339,10 @@ class Form(object):
|
|||
model_instance=None, model_class=None, appstruct=UNSPECIFIED, nodes={}, enums={}, labels={},
|
||||
assume_local_times=False, renderers=None, renderer_kwargs={},
|
||||
hidden={}, widgets={}, defaults={}, validators={}, required={}, helptext={}, focus_spec=None,
|
||||
action_url=None, cancel_url=None,
|
||||
vue_tagname=None,
|
||||
action_url=None, cancel_url=None, component='tailbone-form',
|
||||
vuejs_component_kwargs=None, vuejs_field_converters={}, json_data={}, included_templates={},
|
||||
# TODO: ugh this is getting out hand!
|
||||
can_edit_help=False, edit_help_url=None, route_prefix=None,
|
||||
**kwargs
|
||||
):
|
||||
self.fields = None
|
||||
if fields is not None:
|
||||
|
@ -382,17 +380,7 @@ class Form(object):
|
|||
self.focus_spec = focus_spec
|
||||
self.action_url = action_url
|
||||
self.cancel_url = cancel_url
|
||||
|
||||
# vue_tagname
|
||||
self.vue_tagname = vue_tagname
|
||||
if not self.vue_tagname and kwargs.get('component'):
|
||||
warnings.warn("component kwarg is deprecated for Form(); "
|
||||
"please use vue_tagname param instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
self.vue_tagname = kwargs['component']
|
||||
if not self.vue_tagname:
|
||||
self.vue_tagname = 'tailbone-form'
|
||||
|
||||
self.component = component
|
||||
self.vuejs_component_kwargs = vuejs_component_kwargs or {}
|
||||
self.vuejs_field_converters = vuejs_field_converters or {}
|
||||
self.json_data = json_data or {}
|
||||
|
@ -401,59 +389,13 @@ class Form(object):
|
|||
self.edit_help_url = edit_help_url
|
||||
self.route_prefix = route_prefix
|
||||
|
||||
self.button_icon_submit = kwargs.get('button_icon_submit', 'save')
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.fields)
|
||||
|
||||
@property
|
||||
def vue_component(self):
|
||||
"""
|
||||
String name for the Vue component, e.g. ``'TailboneGrid'``.
|
||||
|
||||
This is a generated value based on :attr:`vue_tagname`.
|
||||
"""
|
||||
words = self.vue_tagname.split('-')
|
||||
return ''.join([word.capitalize() for word in words])
|
||||
|
||||
@property
|
||||
def component(self):
|
||||
"""
|
||||
DEPRECATED - use :attr:`vue_tagname` instead.
|
||||
"""
|
||||
warnings.warn("Form.component is deprecated; "
|
||||
"please use vue_tagname instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return self.vue_tagname
|
||||
|
||||
@property
|
||||
def component_studly(self):
|
||||
"""
|
||||
DEPRECATED - use :attr:`vue_component` instead.
|
||||
"""
|
||||
warnings.warn("Form.component_studly is deprecated; "
|
||||
"please use vue_component instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return self.vue_component
|
||||
|
||||
def get_button_label_submit(self):
|
||||
""" """
|
||||
if hasattr(self, '_button_label_submit'):
|
||||
return self._button_label_submit
|
||||
|
||||
label = getattr(self, 'submit_label', None)
|
||||
if label:
|
||||
return label
|
||||
|
||||
return self.save_label
|
||||
|
||||
def set_button_label_submit(self, value):
|
||||
""" """
|
||||
self._button_label_submit = value
|
||||
|
||||
# wutta compat
|
||||
button_label_submit = property(get_button_label_submit,
|
||||
set_button_label_submit)
|
||||
words = self.component.split('-')
|
||||
return ''.join([word.capitalize() for word in words])
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.fields
|
||||
|
@ -863,10 +805,6 @@ class Form(object):
|
|||
DeprecationWarning, stacklevel=2)
|
||||
return self.render_deform(**kwargs)
|
||||
|
||||
def get_deform(self):
|
||||
""" """
|
||||
return self.make_deform_form()
|
||||
|
||||
def make_deform_form(self):
|
||||
if not hasattr(self, 'deform_form'):
|
||||
|
||||
|
@ -905,11 +843,6 @@ class Form(object):
|
|||
|
||||
return self.deform_form
|
||||
|
||||
def render_vue_template(self, template='/forms/deform.mako', **context):
|
||||
""" """
|
||||
output = self.render_deform(template=template, **context)
|
||||
return HTML.literal(output)
|
||||
|
||||
def render_deform(self, dform=None, template=None, **kwargs):
|
||||
if not template:
|
||||
template = '/forms/deform.mako'
|
||||
|
@ -932,8 +865,8 @@ class Form(object):
|
|||
context.setdefault('form_kwargs', {})
|
||||
# TODO: deprecate / remove the latter option here
|
||||
if self.auto_disable_save or self.auto_disable:
|
||||
context['form_kwargs'].setdefault('ref', self.vue_component)
|
||||
context['form_kwargs']['@submit'] = 'submit{}'.format(self.vue_component)
|
||||
context['form_kwargs'].setdefault('ref', self.component_studly)
|
||||
context['form_kwargs']['@submit'] = 'submit{}'.format(self.component_studly)
|
||||
if self.focus_spec:
|
||||
context['form_kwargs']['data-focus'] = self.focus_spec
|
||||
context['request'] = self.request
|
||||
|
@ -945,13 +878,12 @@ class Form(object):
|
|||
return dict([(field, self.get_label(field))
|
||||
for field in self])
|
||||
|
||||
def get_field_markdowns(self, session=None):
|
||||
def get_field_markdowns(self):
|
||||
app = self.request.rattail_config.get_app()
|
||||
model = app.model
|
||||
session = session or Session()
|
||||
|
||||
if not hasattr(self, 'field_markdowns'):
|
||||
infos = session.query(model.TailboneFieldInfo)\
|
||||
infos = Session.query(model.TailboneFieldInfo)\
|
||||
.filter(model.TailboneFieldInfo.route_prefix == self.route_prefix)\
|
||||
.all()
|
||||
self.field_markdowns = dict([(info.field_name, info.markdown_text)
|
||||
|
@ -959,18 +891,6 @@ class Form(object):
|
|||
|
||||
return self.field_markdowns
|
||||
|
||||
def get_vue_field_value(self, key):
|
||||
""" """
|
||||
if key not in self.fields:
|
||||
return
|
||||
|
||||
dform = self.get_deform()
|
||||
if key not in dform:
|
||||
return
|
||||
|
||||
field = dform[key]
|
||||
return make_json_safe(field.cstruct)
|
||||
|
||||
def get_vuejs_model_value(self, field):
|
||||
"""
|
||||
This method must return "raw" JS which will be assigned as the initial
|
||||
|
@ -1037,11 +957,7 @@ class Form(object):
|
|||
def set_vuejs_component_kwargs(self, **kwargs):
|
||||
self.vuejs_component_kwargs.update(kwargs)
|
||||
|
||||
def render_vue_tag(self, **kwargs):
|
||||
""" """
|
||||
return self.render_vuejs_component(**kwargs)
|
||||
|
||||
def render_vuejs_component(self, **kwargs):
|
||||
def render_vuejs_component(self):
|
||||
"""
|
||||
Render the Vue.js component HTML for the form.
|
||||
|
||||
|
@ -1052,11 +968,10 @@ class Form(object):
|
|||
<tailbone-form :configure-fields-help="configureFieldsHelp">
|
||||
</tailbone-form>
|
||||
"""
|
||||
kw = dict(self.vuejs_component_kwargs)
|
||||
kw.update(kwargs)
|
||||
kwargs = dict(self.vuejs_component_kwargs)
|
||||
if self.can_edit_help:
|
||||
kw.setdefault(':configure-fields-help', 'configureFieldsHelp')
|
||||
return HTML.tag(self.vue_tagname, **kw)
|
||||
kwargs.setdefault(':configure-fields-help', 'configureFieldsHelp')
|
||||
return HTML.tag(self.component, **kwargs)
|
||||
|
||||
def set_json_data(self, key, value):
|
||||
"""
|
||||
|
@ -1082,12 +997,7 @@ class Form(object):
|
|||
templates.append(HTML.literal(render(template, context)))
|
||||
return HTML.literal('\n').join(templates)
|
||||
|
||||
def render_vue_field(self, fieldname, **kwargs):
|
||||
""" """
|
||||
return self.render_field_complete(fieldname, **kwargs)
|
||||
|
||||
def render_field_complete(self, fieldname, bfield_attrs={},
|
||||
session=None):
|
||||
def render_field_complete(self, fieldname, bfield_attrs={}):
|
||||
"""
|
||||
Render the given field completely, i.e. with ``<b-field>``
|
||||
wrapper. Note that this is meant to render *editable* fields,
|
||||
|
@ -1105,7 +1015,7 @@ class Form(object):
|
|||
|
||||
if self.field_visible(fieldname):
|
||||
label = self.get_label(fieldname)
|
||||
markdowns = self.get_field_markdowns(session=session)
|
||||
markdowns = self.get_field_markdowns()
|
||||
|
||||
# these attrs will be for the <b-field> (*not* the widget)
|
||||
attrs = {
|
||||
|
@ -1224,18 +1134,6 @@ class Form(object):
|
|||
# TODO: again, why does serialize() not return literal?
|
||||
return HTML.literal(field.serialize())
|
||||
|
||||
# TODO: this was copied from wuttaweb; can remove when we align
|
||||
# Form class structure
|
||||
def render_vue_finalize(self):
|
||||
""" """
|
||||
set_data = f"{self.vue_component}.data = function() {{ return {self.vue_component}Data }}"
|
||||
make_component = f"Vue.component('{self.vue_tagname}', {self.vue_component})"
|
||||
return HTML.tag('script', c=['\n',
|
||||
HTML.literal(set_data),
|
||||
'\n',
|
||||
HTML.literal(make_component),
|
||||
'\n'])
|
||||
|
||||
def render_field_readonly(self, field_name, **kwargs):
|
||||
"""
|
||||
Render the given field completely, but in read-only fashion.
|
||||
|
@ -1375,19 +1273,12 @@ class Form(object):
|
|||
|
||||
def obtain_value(self, record, field_name):
|
||||
if record:
|
||||
|
||||
if isinstance(record, dict):
|
||||
return record[field_name]
|
||||
|
||||
try:
|
||||
return getattr(record, field_name)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
return record[field_name]
|
||||
except KeyError:
|
||||
return None
|
||||
except TypeError:
|
||||
pass
|
||||
return getattr(record, field_name, None)
|
||||
|
||||
# TODO: is this always safe to do?
|
||||
elif self.defaults and field_name in self.defaults:
|
||||
|
@ -1441,6 +1332,30 @@ class Form(object):
|
|||
return False
|
||||
|
||||
|
||||
class FieldList(list):
|
||||
"""
|
||||
Convenience wrapper for a form's field list.
|
||||
"""
|
||||
|
||||
def insert_before(self, field, newfield):
|
||||
if field in self:
|
||||
i = self.index(field)
|
||||
self.insert(i, newfield)
|
||||
else:
|
||||
log.warning("field '%s' not found, will append new field: %s",
|
||||
field, newfield)
|
||||
self.append(newfield)
|
||||
|
||||
def insert_after(self, field, newfield):
|
||||
if field in self:
|
||||
i = self.index(field)
|
||||
self.insert(i + 1, newfield)
|
||||
else:
|
||||
log.warning("field '%s' not found, will append new field: %s",
|
||||
field, newfield)
|
||||
self.append(newfield)
|
||||
|
||||
|
||||
@colander.deferred
|
||||
def upload_widget(node, kw):
|
||||
request = kw['request']
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,9 +24,6 @@
|
|||
Template Context Helpers
|
||||
"""
|
||||
|
||||
# start off with all from wuttaweb
|
||||
from wuttaweb.helpers import *
|
||||
|
||||
import os
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
|
@ -36,9 +33,14 @@ from rattail.time import localtime, make_utc
|
|||
from rattail.util import pretty_quantity, pretty_hours, hours_as_decimal
|
||||
from rattail.db.util import maxlen
|
||||
|
||||
from tailbone.util import (pretty_datetime, raw_datetime,
|
||||
from webhelpers2.html import *
|
||||
from webhelpers2.html.tags import *
|
||||
|
||||
from tailbone.util import (csrf_token, get_csrf_token,
|
||||
pretty_datetime, raw_datetime,
|
||||
render_markdown,
|
||||
route_exists)
|
||||
route_exists,
|
||||
get_liburl)
|
||||
|
||||
|
||||
def pretty_date(date):
|
||||
|
|
|
@ -394,11 +394,6 @@ class TailboneMenuHandler(WuttaMenuHandler):
|
|||
'route': 'products',
|
||||
'perm': 'products.list',
|
||||
},
|
||||
{
|
||||
'title': "Product Costs",
|
||||
'route': 'product_costs',
|
||||
'perm': 'product_costs.list',
|
||||
},
|
||||
{
|
||||
'title': "Departments",
|
||||
'route': 'departments',
|
||||
|
@ -456,11 +451,6 @@ class TailboneMenuHandler(WuttaMenuHandler):
|
|||
'route': 'vendors',
|
||||
'perm': 'vendors.list',
|
||||
},
|
||||
{
|
||||
'title': "Product Costs",
|
||||
'route': 'product_costs',
|
||||
'perm': 'product_costs.list',
|
||||
},
|
||||
{'type': 'sep'},
|
||||
{
|
||||
'title': "Ordering",
|
||||
|
@ -713,7 +703,7 @@ class TailboneMenuHandler(WuttaMenuHandler):
|
|||
},
|
||||
{'type': 'sep'},
|
||||
{
|
||||
'title': "App Info",
|
||||
'title': "App Details",
|
||||
'route': 'appinfo',
|
||||
'perm': 'appinfo.list',
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
# Copyright © 2010-2017 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,8 +24,9 @@
|
|||
Static Assets
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
|
||||
def includeme(config):
|
||||
config.include('wuttaweb.static')
|
||||
config.add_static_view('tailbone', 'tailbone:static')
|
||||
config.add_static_view('deform', 'deform:static')
|
||||
|
|
|
@ -48,7 +48,7 @@ from tailbone.util import get_available_themes, get_global_search_options
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def new_request(event, session=None):
|
||||
def new_request(event):
|
||||
"""
|
||||
Event hook called when processing a new request.
|
||||
|
||||
|
@ -64,6 +64,15 @@ def new_request(event, session=None):
|
|||
Reference to the app :term:`config object`. Note that this
|
||||
will be the same as :attr:`wuttaweb:request.wutta_config`.
|
||||
|
||||
.. method:: request.has_perm(name)
|
||||
|
||||
Function to check if current user has the given permission.
|
||||
|
||||
.. method:: request.has_any_perm(*names)
|
||||
|
||||
Function to check if current user has any of the given
|
||||
permissions.
|
||||
|
||||
.. method:: request.register_component(tagname, classname)
|
||||
|
||||
Function to register a Vue component for use with the app.
|
||||
|
@ -81,7 +90,6 @@ def new_request(event, session=None):
|
|||
config = request.wutta_config
|
||||
app = config.get_app()
|
||||
auth = app.get_auth_handler()
|
||||
session = session or Session()
|
||||
|
||||
# compatibility
|
||||
rattail_config = config
|
||||
|
@ -96,31 +104,50 @@ def new_request(event, session=None):
|
|||
return user
|
||||
|
||||
# invoke upstream hook to set user
|
||||
base.new_request_set_user(event, user_getter=user_getter, db_session=session)
|
||||
base.new_request_set_user(event, user_getter=user_getter, db_session=Session())
|
||||
|
||||
# assign client IP address to the session, for sake of versioning
|
||||
if hasattr(request, 'client_addr'):
|
||||
session.continuum_remote_addr = request.client_addr
|
||||
Session().continuum_remote_addr = request.client_addr
|
||||
|
||||
# request.register_component()
|
||||
def register_component(tagname, classname):
|
||||
"""
|
||||
Register a Vue 3 component, so the base template knows to
|
||||
declare it for use within the app (page).
|
||||
"""
|
||||
if not hasattr(request, '_tailbone_registered_components'):
|
||||
request._tailbone_registered_components = OrderedDict()
|
||||
# TODO: why would this ever be null?
|
||||
if rattail_config:
|
||||
|
||||
if tagname in request._tailbone_registered_components:
|
||||
log.warning("component with tagname '%s' already registered "
|
||||
"with class '%s' but we are replacing that with "
|
||||
"class '%s'",
|
||||
tagname,
|
||||
request._tailbone_registered_components[tagname],
|
||||
classname)
|
||||
app = rattail_config.get_app()
|
||||
auth = app.get_auth_handler()
|
||||
request.tailbone_cached_permissions = auth.get_permissions(
|
||||
Session(), request.user)
|
||||
|
||||
request._tailbone_registered_components[tagname] = classname
|
||||
request.register_component = register_component
|
||||
def has_perm(name):
|
||||
if name in request.tailbone_cached_permissions:
|
||||
return True
|
||||
return request.is_root
|
||||
request.has_perm = has_perm
|
||||
|
||||
def has_any_perm(*names):
|
||||
for name in names:
|
||||
if has_perm(name):
|
||||
return True
|
||||
return False
|
||||
request.has_any_perm = has_any_perm
|
||||
|
||||
def register_component(tagname, classname):
|
||||
"""
|
||||
Register a Vue 3 component, so the base template knows to
|
||||
declare it for use within the app (page).
|
||||
"""
|
||||
if not hasattr(request, '_tailbone_registered_components'):
|
||||
request._tailbone_registered_components = OrderedDict()
|
||||
|
||||
if tagname in request._tailbone_registered_components:
|
||||
log.warning("component with tagname '%s' already registered "
|
||||
"with class '%s' but we are replacing that with "
|
||||
"class '%s'",
|
||||
tagname,
|
||||
request._tailbone_registered_components[tagname],
|
||||
classname)
|
||||
|
||||
request._tailbone_registered_components[tagname] = classname
|
||||
request.register_component = register_component
|
||||
|
||||
|
||||
def before_render(event):
|
||||
|
|
|
@ -1,2 +1,250 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="wuttaweb:templates/appinfo/configure.mako" />
|
||||
<%inherit file="/configure.mako" />
|
||||
|
||||
<%def name="form_content()">
|
||||
|
||||
<h3 class="block is-size-3">Basics</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="App Title">
|
||||
<b-input name="rattail.app_title"
|
||||
v-model="simpleSettings['rattail.app_title']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Node Type">
|
||||
## TODO: should be a dropdown, app handler defines choices
|
||||
<b-input name="rattail.node_type"
|
||||
v-model="simpleSettings['rattail.node_type']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Node Title">
|
||||
<b-input name="rattail.node_title"
|
||||
v-model="simpleSettings['rattail.node_title']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.production"
|
||||
v-model="simpleSettings['rattail.production']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Production Mode
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.running_from_source"
|
||||
v-model="simpleSettings['rattail.running_from_source']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Running from Source
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<b-field label="Top-Level Package" horizontal
|
||||
v-if="simpleSettings['rattail.running_from_source']">
|
||||
<b-input name="rattail.running_from_source.rootpkg"
|
||||
v-model="simpleSettings['rattail.running_from_source.rootpkg']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Display</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="Background Color">
|
||||
<b-input name="tailbone.background_color"
|
||||
v-model="simpleSettings['tailbone.background_color']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Grids</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="Default Page Size">
|
||||
<b-input name="tailbone.grid.default_pagesize"
|
||||
v-model="simpleSettings['tailbone.grid.default_pagesize']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Web Libraries</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<${b}-table :data="weblibs">
|
||||
|
||||
<${b}-table-column field="title"
|
||||
label="Name"
|
||||
v-slot="props">
|
||||
{{ props.row.title }}
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="configured_version"
|
||||
label="Version"
|
||||
v-slot="props">
|
||||
{{ props.row.configured_version || props.row.default_version }}
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="configured_url"
|
||||
label="URL Override"
|
||||
v-slot="props">
|
||||
{{ props.row.configured_url }}
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="live_url"
|
||||
label="Effective (Live) URL"
|
||||
v-slot="props">
|
||||
<span v-if="props.row.modified"
|
||||
class="has-text-warning">
|
||||
save settings and refresh page to see new URL
|
||||
</span>
|
||||
<span v-if="!props.row.modified">
|
||||
{{ props.row.live_url }}
|
||||
</span>
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="actions"
|
||||
label="Actions"
|
||||
v-slot="props">
|
||||
<a href="#"
|
||||
@click.prevent="editWebLibraryInit(props.row)">
|
||||
% if request.use_oruga:
|
||||
<o-icon icon="edit" />
|
||||
% else:
|
||||
<i class="fas fa-edit"></i>
|
||||
% endif
|
||||
Edit
|
||||
</a>
|
||||
</${b}-table-column>
|
||||
|
||||
</${b}-table>
|
||||
|
||||
% for weblib in weblibs:
|
||||
${h.hidden('tailbone.libver.{}'.format(weblib['key']), **{':value': "simpleSettings['tailbone.libver.{}']".format(weblib['key'])})}
|
||||
${h.hidden('tailbone.liburl.{}'.format(weblib['key']), **{':value': "simpleSettings['tailbone.liburl.{}']".format(weblib['key'])})}
|
||||
% endfor
|
||||
|
||||
<${b}-modal has-modal-card
|
||||
% if request.use_oruga:
|
||||
v-model:active="editWebLibraryShowDialog"
|
||||
% else:
|
||||
:active.sync="editWebLibraryShowDialog"
|
||||
% endif
|
||||
>
|
||||
<div class="modal-card">
|
||||
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Web Library: {{ editWebLibraryRecord.title }}</p>
|
||||
</header>
|
||||
|
||||
<section class="modal-card-body">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="Default Version">
|
||||
<b-input v-model="editWebLibraryRecord.default_version"
|
||||
disabled>
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Override Version">
|
||||
<b-input v-model="editWebLibraryVersion">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
<b-field label="Override URL">
|
||||
<b-input v-model="editWebLibraryURL"
|
||||
expanded />
|
||||
</b-field>
|
||||
|
||||
<b-field label="Effective URL (as of last page load)">
|
||||
<b-input v-model="editWebLibraryRecord.live_url"
|
||||
disabled
|
||||
expanded />
|
||||
</b-field>
|
||||
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
<b-button type="is-primary"
|
||||
@click="editWebLibrarySave()"
|
||||
icon-pack="fas"
|
||||
icon-left="save">
|
||||
Save
|
||||
</b-button>
|
||||
<b-button @click="editWebLibraryShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
</footer>
|
||||
</div>
|
||||
</${b}-modal>
|
||||
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.weblibs = ${json.dumps(weblibs)|n}
|
||||
|
||||
ThisPageData.editWebLibraryShowDialog = false
|
||||
ThisPageData.editWebLibraryRecord = {}
|
||||
ThisPageData.editWebLibraryVersion = null
|
||||
ThisPageData.editWebLibraryURL = null
|
||||
|
||||
ThisPage.methods.editWebLibraryInit = function(row) {
|
||||
this.editWebLibraryRecord = row
|
||||
this.editWebLibraryVersion = row.configured_version
|
||||
this.editWebLibraryURL = row.configured_url
|
||||
this.editWebLibraryShowDialog = true
|
||||
}
|
||||
|
||||
ThisPage.methods.editWebLibrarySave = function() {
|
||||
this.editWebLibraryRecord.configured_version = this.editWebLibraryVersion
|
||||
this.editWebLibraryRecord.configured_url = this.editWebLibraryURL
|
||||
this.editWebLibraryRecord.modified = true
|
||||
|
||||
this.simpleSettings[`tailbone.libver.${'$'}{this.editWebLibraryRecord.key}`] = this.editWebLibraryVersion
|
||||
this.simpleSettings[`tailbone.liburl.${'$'}{this.editWebLibraryRecord.key}`] = this.editWebLibraryURL
|
||||
|
||||
this.settingsNeedSaved = true
|
||||
this.editWebLibraryShowDialog = false
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="wuttaweb:templates/appinfo/index.mako" />
|
||||
<%inherit file="/master/index.mako" />
|
||||
|
||||
<%def name="render_grid_component()">
|
||||
|
||||
<%def name="page_content()">
|
||||
<div class="buttons">
|
||||
|
||||
<once-button type="is-primary"
|
||||
|
@ -27,5 +28,100 @@
|
|||
|
||||
</div>
|
||||
|
||||
${parent.page_content()}
|
||||
<${b}-collapse class="panel" open>
|
||||
|
||||
<template #trigger="props">
|
||||
<div class="panel-heading"
|
||||
style="cursor: pointer;"
|
||||
role="button">
|
||||
|
||||
## TODO: for some reason buefy will "reuse" the icon
|
||||
## element in such a way that its display does not
|
||||
## refresh. so to work around that, we use different
|
||||
## structure for the two icons, so buefy is forced to
|
||||
## re-draw
|
||||
|
||||
<b-icon v-if="props.open"
|
||||
pack="fas"
|
||||
icon="angle-down">
|
||||
</b-icon>
|
||||
|
||||
<span v-if="!props.open">
|
||||
<b-icon pack="fas"
|
||||
icon="angle-right">
|
||||
</b-icon>
|
||||
</span>
|
||||
|
||||
<span>Configuration Files</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="panel-block">
|
||||
<div style="width: 100%;">
|
||||
<${b}-table :data="configFiles">
|
||||
|
||||
<${b}-table-column field="priority"
|
||||
label="Priority"
|
||||
v-slot="props">
|
||||
{{ props.row.priority }}
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="path"
|
||||
label="File Path"
|
||||
v-slot="props">
|
||||
{{ props.row.path }}
|
||||
</${b}-table-column>
|
||||
|
||||
</${b}-table>
|
||||
</div>
|
||||
</div>
|
||||
</${b}-collapse>
|
||||
|
||||
<${b}-collapse class="panel"
|
||||
:open="false">
|
||||
|
||||
<template #trigger="props">
|
||||
<div class="panel-heading"
|
||||
style="cursor: pointer;"
|
||||
role="button">
|
||||
|
||||
## TODO: for some reason buefy will "reuse" the icon
|
||||
## element in such a way that its display does not
|
||||
## refresh. so to work around that, we use different
|
||||
## structure for the two icons, so buefy is forced to
|
||||
## re-draw
|
||||
|
||||
<b-icon v-if="props.open"
|
||||
pack="fas"
|
||||
icon="angle-down">
|
||||
</b-icon>
|
||||
|
||||
<span v-if="!props.open">
|
||||
<b-icon pack="fas"
|
||||
icon="angle-right">
|
||||
</b-icon>
|
||||
</span>
|
||||
|
||||
<strong>Installed Packages</strong>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="panel-block">
|
||||
<div style="width: 100%;">
|
||||
${parent.render_grid_component()}
|
||||
</div>
|
||||
</div>
|
||||
</${b}-collapse>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.configFiles = ${json.dumps([dict(path=p, priority=i) for i, p in enumerate(request.rattail_config.prioritized_files, 1)])|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<app-settings :groups="groups" :showing-group="showingGroup"></app-settings>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<script type="text/x-template" id="app-settings-template">
|
||||
|
||||
<div class="form">
|
||||
|
@ -150,18 +150,19 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.groups = ${json.dumps(settings_data)|n}
|
||||
ThisPageData.showingGroup = ${json.dumps(current_group or '')|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
Vue.component('app-settings', {
|
||||
template: '#app-settings-template',
|
||||
|
@ -192,3 +193,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%namespace file="/wutta-components.mako" import="make_wutta_components" />
|
||||
<%namespace file="/grids/nav.mako" import="grid_index_nav" />
|
||||
<%namespace file="/autocomplete.mako" import="tailbone_autocomplete_template" />
|
||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||
|
@ -35,21 +34,17 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app" style="height: 100%;">
|
||||
${declare_formposter_mixin()}
|
||||
|
||||
${self.body()}
|
||||
|
||||
<div id="whole-page-app">
|
||||
<whole-page></whole-page>
|
||||
</div>
|
||||
|
||||
## TODO: this must come before the self.body() call..but why?
|
||||
${declare_formposter_mixin()}
|
||||
|
||||
## content body from derived/child template
|
||||
${self.body()}
|
||||
|
||||
## Vue app
|
||||
${self.render_vue_templates()}
|
||||
${self.modify_vue_vars()}
|
||||
${self.make_vue_components()}
|
||||
${self.make_vue_app()}
|
||||
${self.render_whole_page_template()}
|
||||
${self.make_whole_page_component()}
|
||||
${self.make_whole_page_app()}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -127,16 +122,16 @@
|
|||
</%def>
|
||||
|
||||
<%def name="vuejs()">
|
||||
${h.javascript_link(h.get_liburl(request, 'vue', prefix='tailbone'))}
|
||||
${h.javascript_link(h.get_liburl(request, 'vue_resource', prefix='tailbone'))}
|
||||
${h.javascript_link(h.get_liburl(request, 'vue'))}
|
||||
${h.javascript_link(h.get_liburl(request, 'vue_resource'))}
|
||||
</%def>
|
||||
|
||||
<%def name="buefy()">
|
||||
${h.javascript_link(h.get_liburl(request, 'buefy', prefix='tailbone'))}
|
||||
${h.javascript_link(h.get_liburl(request, 'buefy'))}
|
||||
</%def>
|
||||
|
||||
<%def name="fontawesome()">
|
||||
<script defer src="${h.get_liburl(request, 'fontawesome', prefix='tailbone')}"></script>
|
||||
<script defer src="${h.get_liburl(request, 'fontawesome')}"></script>
|
||||
</%def>
|
||||
|
||||
<%def name="extra_javascript()"></%def>
|
||||
|
@ -158,16 +153,12 @@
|
|||
<style type="text/css">
|
||||
.filters .filter-fieldname,
|
||||
.filters .filter-fieldname .button {
|
||||
% if filter_fieldname_width is not Undefined:
|
||||
min-width: ${filter_fieldname_width};
|
||||
% endif
|
||||
justify-content: left;
|
||||
}
|
||||
% if filter_fieldname_width is not Undefined:
|
||||
.filters .filter-verb {
|
||||
min-width: ${filter_verb_width};
|
||||
}
|
||||
% endif
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
|
@ -176,7 +167,7 @@
|
|||
${h.stylesheet_link(user_css)}
|
||||
% else:
|
||||
## upstream Buefy CSS
|
||||
${h.stylesheet_link(h.get_liburl(request, 'buefy.css', prefix='tailbone'))}
|
||||
${h.stylesheet_link(h.get_liburl(request, 'buefy.css'))}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
@ -186,7 +177,7 @@
|
|||
|
||||
<%def name="head_tags()"></%def>
|
||||
|
||||
<%def name="render_vue_template_whole_page()">
|
||||
<%def name="render_whole_page_template()">
|
||||
<script type="text/x-template" id="whole-page-template">
|
||||
<div>
|
||||
<header>
|
||||
|
@ -285,7 +276,7 @@
|
|||
<span class="header-text">
|
||||
${index_title}
|
||||
</span>
|
||||
% if master.creatable and getattr(master, 'show_create_link', True) and master.has_perm('create'):
|
||||
% if master.creatable and master.show_create_link and master.has_perm('create'):
|
||||
<once-button type="is-primary"
|
||||
tag="a" href="${url('{}.create'.format(route_prefix))}"
|
||||
icon-left="plus"
|
||||
|
@ -311,7 +302,7 @@
|
|||
<span class="header-text">
|
||||
${h.link_to(instance_title, instance_url)}
|
||||
</span>
|
||||
% elif master.creatable and getattr(master, 'show_create_link', True) and master.has_perm('create'):
|
||||
% elif master.creatable and master.show_create_link and master.has_perm('create'):
|
||||
% if not request.matched_route.name.endswith('.create'):
|
||||
<once-button type="is-primary"
|
||||
tag="a" href="${url('{}.create'.format(route_prefix))}"
|
||||
|
@ -632,23 +623,9 @@
|
|||
% endif
|
||||
<div class="navbar-dropdown">
|
||||
% if request.is_root:
|
||||
${h.form(url('stop_root'), ref='stopBeingRootForm')}
|
||||
${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" value="${request.current_route_url()}" />
|
||||
<a @click="$refs.stopBeingRootForm.submit()"
|
||||
class="navbar-item root-user">
|
||||
Stop being root
|
||||
</a>
|
||||
${h.end_form()}
|
||||
${h.link_to("Stop being root", url('stop_root'), class_='navbar-item root-user')}
|
||||
% elif request.is_admin:
|
||||
${h.form(url('become_root'), ref='startBeingRootForm')}
|
||||
${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" value="${request.current_route_url()}" />
|
||||
<a @click="$refs.startBeingRootForm.submit()"
|
||||
class="navbar-item root-user">
|
||||
Become root
|
||||
</a>
|
||||
${h.end_form()}
|
||||
${h.link_to("Become root", url('become_root'), class_='navbar-item root-user')}
|
||||
% endif
|
||||
% if messaging_enabled:
|
||||
${h.link_to("Messages{}".format(" ({})".format(inbox_count) if inbox_count else ''), url('messages.inbox'), class_='navbar-item')}
|
||||
|
@ -656,11 +633,7 @@
|
|||
% if request.is_root or not request.user.prevent_password_change:
|
||||
${h.link_to("Change Password", url('change_password'), class_='navbar-item')}
|
||||
% endif
|
||||
% try:
|
||||
## nb. does not exist yet for wuttaweb
|
||||
${h.link_to("Edit Preferences", url('my.preferences'), class_='navbar-item')}
|
||||
% except:
|
||||
% endtry
|
||||
${h.link_to("Edit Preferences", url('my.preferences'), class_='navbar-item')}
|
||||
${h.link_to("Logout", url('logout'), class_='navbar-item')}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -681,19 +654,19 @@
|
|||
## TODO: is there a better way to check if viewing parent?
|
||||
% if parent_instance is Undefined:
|
||||
% if master.editable and instance_editable and master.has_perm('edit'):
|
||||
<once-button tag="a" href="${master.get_action_url('edit', instance)}"
|
||||
<once-button tag="a" href="${action_url('edit', instance)}"
|
||||
icon-left="edit"
|
||||
text="Edit This">
|
||||
</once-button>
|
||||
% endif
|
||||
% if getattr(master, 'cloneable', False) and not master.cloning and master.has_perm('clone'):
|
||||
<once-button tag="a" href="${master.get_action_url('clone', instance)}"
|
||||
% if master.cloneable and master.has_perm('clone'):
|
||||
<once-button tag="a" href="${action_url('clone', instance)}"
|
||||
icon-left="object-ungroup"
|
||||
text="Clone This">
|
||||
</once-button>
|
||||
% endif
|
||||
% if master.deletable and instance_deletable and master.has_perm('delete'):
|
||||
<once-button tag="a" href="${master.get_action_url('delete', instance)}"
|
||||
<once-button tag="a" href="${action_url('delete', instance)}"
|
||||
type="is-danger"
|
||||
icon-left="trash"
|
||||
text="Delete This">
|
||||
|
@ -702,7 +675,7 @@
|
|||
% else:
|
||||
## viewing row
|
||||
% if instance_deletable and master.has_perm('delete_row'):
|
||||
<once-button tag="a" href="${master.get_action_url('delete', instance)}"
|
||||
<once-button tag="a" href="${action_url('delete', instance)}"
|
||||
type="is-danger"
|
||||
icon-left="trash"
|
||||
text="Delete This">
|
||||
|
@ -711,13 +684,13 @@
|
|||
% endif
|
||||
% elif master and master.editing:
|
||||
% if master.viewable and master.has_perm('view'):
|
||||
<once-button tag="a" href="${master.get_action_url('view', instance)}"
|
||||
<once-button tag="a" href="${action_url('view', instance)}"
|
||||
icon-left="eye"
|
||||
text="View This">
|
||||
</once-button>
|
||||
% endif
|
||||
% if master.deletable and instance_deletable and master.has_perm('delete'):
|
||||
<once-button tag="a" href="${master.get_action_url('delete', instance)}"
|
||||
<once-button tag="a" href="${action_url('delete', instance)}"
|
||||
type="is-danger"
|
||||
icon-left="trash"
|
||||
text="Delete This">
|
||||
|
@ -725,13 +698,13 @@
|
|||
% endif
|
||||
% elif master and master.deleting:
|
||||
% if master.viewable and master.has_perm('view'):
|
||||
<once-button tag="a" href="${master.get_action_url('view', instance)}"
|
||||
<once-button tag="a" href="${action_url('view', instance)}"
|
||||
icon-left="eye"
|
||||
text="View This">
|
||||
</once-button>
|
||||
% endif
|
||||
% if master.editable and instance_editable and master.has_perm('edit'):
|
||||
<once-button tag="a" href="${master.get_action_url('edit', instance)}"
|
||||
<once-button tag="a" href="${action_url('edit', instance)}"
|
||||
icon-left="edit"
|
||||
text="Edit This">
|
||||
</once-button>
|
||||
|
@ -772,8 +745,11 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_script_whole_page()">
|
||||
<script>
|
||||
<%def name="declare_whole_page_vars()">
|
||||
${page_help.declare_vars()}
|
||||
${multi_file_upload.declare_vars()}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
<script type="text/javascript">
|
||||
|
||||
let WholePage = {
|
||||
template: '#whole-page-template',
|
||||
|
@ -880,7 +856,7 @@
|
|||
feedbackMessage: "",
|
||||
|
||||
% if expose_theme_picker and request.has_perm('common.change_app_theme'):
|
||||
globalTheme: ${json.dumps(theme or None)|n},
|
||||
globalTheme: ${json.dumps(theme)|n},
|
||||
referrer: location.href,
|
||||
% endif
|
||||
|
||||
|
@ -890,7 +866,7 @@
|
|||
|
||||
globalSearchActive: false,
|
||||
globalSearchTerm: '',
|
||||
globalSearchData: ${json.dumps(global_search_data or [])|n},
|
||||
globalSearchData: ${json.dumps(global_search_data)|n},
|
||||
|
||||
mountedHooks: [],
|
||||
}
|
||||
|
@ -909,6 +885,57 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_whole_page_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
% if request.user:
|
||||
FeedbackFormData.userUUID = ${json.dumps(request.user.uuid)|n}
|
||||
FeedbackFormData.userName = ${json.dumps(str(request.user))|n}
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="finalize_whole_page_vars()">
|
||||
## NOTE: if you override this, must use <script> tags
|
||||
</%def>
|
||||
|
||||
<%def name="make_whole_page_component()">
|
||||
|
||||
${make_grid_filter_components()}
|
||||
|
||||
${self.declare_whole_page_vars()}
|
||||
${self.modify_whole_page_vars()}
|
||||
${self.finalize_whole_page_vars()}
|
||||
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.autocomplete.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
|
||||
${page_help.make_component()}
|
||||
${multi_file_upload.make_component()}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
FeedbackForm.data = function() { return FeedbackFormData }
|
||||
|
||||
Vue.component('feedback-form', FeedbackForm)
|
||||
|
||||
WholePage.data = function() { return WholePageData }
|
||||
|
||||
Vue.component('whole-page', WholePage)
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_whole_page_app()">
|
||||
<script type="text/javascript">
|
||||
|
||||
new Vue({
|
||||
el: '#whole-page-app'
|
||||
})
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="wtfield(form, name, **kwargs)">
|
||||
<div class="field-wrapper${' error' if form[name].errors else ''}">
|
||||
<label for="${name}">${form[name].label}</label>
|
||||
|
@ -930,88 +957,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## vue components + app
|
||||
##############################
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${page_help.declare_vars()}
|
||||
${multi_file_upload.declare_vars()}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.autocomplete.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.render_whole_page_template()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_whole_page_template()">
|
||||
${self.render_vue_template_whole_page()}
|
||||
${self.declare_whole_page_vars()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="declare_whole_page_vars()">
|
||||
${self.render_vue_script_whole_page()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.modify_whole_page_vars()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="modify_whole_page_vars()">
|
||||
<script>
|
||||
|
||||
% if request.user:
|
||||
FeedbackFormData.userUUID = ${json.dumps(request.user.uuid)|n}
|
||||
FeedbackFormData.userName = ${json.dumps(str(request.user))|n}
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${make_wutta_components()}
|
||||
${make_grid_filter_components()}
|
||||
${page_help.make_component()}
|
||||
${multi_file_upload.make_component()}
|
||||
<script>
|
||||
FeedbackForm.data = function() { return FeedbackFormData }
|
||||
Vue.component('feedback-form', FeedbackForm)
|
||||
</script>
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.finalize_whole_page_vars()}
|
||||
${self.make_whole_page_component()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="make_whole_page_component()">
|
||||
<script>
|
||||
WholePage.data = function() { return WholePageData }
|
||||
Vue.component('whole-page', WholePage)
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_app()">
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.make_whole_page_app()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="make_whole_page_app()">
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app'
|
||||
})
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## DEPRECATED
|
||||
##############################
|
||||
|
||||
<%def name="finalize_whole_page_vars()"></%def>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="wuttaweb:templates/base_meta.mako" />
|
||||
|
||||
<%def name="app_title()">${app.get_node_title()}</%def>
|
||||
<%def name="app_title()">${rattail_app.get_node_title()}</%def>
|
||||
|
||||
<%def name="global_title()">${"[STAGE] " if not request.rattail_config.production() else ''}${self.app_title()}</%def>
|
||||
|
||||
<%def name="extra_styles()"></%def>
|
||||
|
||||
<%def name="favicon()">
|
||||
<link rel="icon" type="image/x-icon" href="${request.rattail_config.get('tailbone', 'favicon_url', default=request.static_url('tailbone:static/img/rattail.ico'))}" />
|
||||
|
@ -10,3 +13,9 @@
|
|||
<%def name="header_logo()">
|
||||
${h.image(request.rattail_config.get('tailbone', 'header_image_url', default=request.static_url('tailbone:static/img/rattail.ico')), "Header Logo", style="height: 49px;")}
|
||||
</%def>
|
||||
|
||||
<%def name="footer()">
|
||||
<p class="has-text-centered">
|
||||
powered by ${h.link_to("Rattail", url('about'))}
|
||||
</p>
|
||||
</%def>
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<br />
|
||||
<div class="form-wrapper">
|
||||
<div class="form">
|
||||
${execute_form.render_vue_tag(ref='executeResultsForm')}
|
||||
<${execute_form.component} ref="executeResultsForm"></${execute_form.component}>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -64,17 +64,10 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if master.results_executable and master.has_perm('execute_multiple'):
|
||||
${execute_form.render_vue_template(form_kwargs={'ref': 'actualExecuteForm'}, buttons=False)}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if master.results_refreshable and master.has_perm('refresh'):
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
|
||||
TailboneGridData.refreshResultsButtonText = "Refresh Results"
|
||||
TailboneGridData.refreshResultsButtonDisabled = false
|
||||
|
@ -88,9 +81,9 @@
|
|||
</script>
|
||||
% endif
|
||||
% if master.results_executable and master.has_perm('execute_multiple'):
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
|
||||
${execute_form.vue_component}.methods.submit = function() {
|
||||
${execute_form.component_studly}.methods.submit = function() {
|
||||
this.$refs.actualExecuteForm.submit()
|
||||
}
|
||||
|
||||
|
@ -125,9 +118,25 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
% if master.results_executable and master.has_perm('execute_multiple'):
|
||||
${execute_form.render_vue_finalize()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${execute_form.component_studly}.data = function() { return ${execute_form.component_studly}Data }
|
||||
|
||||
Vue.component('${execute_form.component}', ${execute_form.component_studly})
|
||||
|
||||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
% if master.results_executable and master.has_perm('execute_multiple'):
|
||||
${execute_form.render_deform(form_kwargs={'ref': 'actualExecuteForm'}, buttons=False)|n}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
|
||||
let ${form.vue_component} = {
|
||||
let ${form.component_studly} = {
|
||||
template: '#${form.component}-template',
|
||||
mixins: [SimpleRequestMixin],
|
||||
|
||||
|
@ -278,7 +278,7 @@
|
|||
},
|
||||
}
|
||||
|
||||
let ${form.vue_component}Data = {
|
||||
let ${form.component_studly}Data = {
|
||||
submitting: false,
|
||||
|
||||
productUPC: null,
|
||||
|
@ -297,9 +297,14 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.toggleCompleteSubmitting = false
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/batch/view.mako" />
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
${form.vue_component}Data.taxesData = ${json.dumps(taxes_data)|n}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.component_studly}Data.taxesData = ${json.dumps(taxes_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -39,9 +39,14 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.catalogParsers = ${json.dumps(catalog_parsers_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/batch/create.mako" />
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}Data.parsers = ${json.dumps(parsers_data)|n}
|
||||
${form.component_studly}Data.parsers = ${json.dumps(parsers_data)|n}
|
||||
|
||||
${form.vue_component}Data.vendorName = null
|
||||
${form.vue_component}Data.vendorNameReplacement = null
|
||||
${form.component_studly}Data.vendorName = null
|
||||
${form.component_studly}Data.vendorNameReplacement = null
|
||||
|
||||
${form.vue_component}.watch.field_model_parser_key = function(val) {
|
||||
${form.component_studly}.watch.field_model_parser_key = function(val) {
|
||||
let parser = this.parsers[val]
|
||||
if (parser.vendor_uuid) {
|
||||
if (this.field_model_vendor_uuid != parser.vendor_uuid) {
|
||||
|
@ -24,11 +24,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
${form.vue_component}.methods.vendorLabelChanging = function(label) {
|
||||
${form.component_studly}.methods.vendorLabelChanging = function(label) {
|
||||
this.vendorNameReplacement = label
|
||||
}
|
||||
|
||||
${form.vue_component}.methods.vendorChanged = function(uuid) {
|
||||
${form.component_studly}.methods.vendorChanged = function(uuid) {
|
||||
if (uuid) {
|
||||
this.vendorName = this.vendorNameReplacement
|
||||
this.vendorNameReplacement = null
|
||||
|
@ -37,3 +37,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -85,11 +85,13 @@
|
|||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
% if batch.executed:
|
||||
<p>
|
||||
Batch was executed
|
||||
${h.pretty_datetime(request.rattail_config, batch.executed)}
|
||||
by ${batch.executed_by}
|
||||
</p>
|
||||
% elif master.handler.executable(batch):
|
||||
% if master.has_perm('execute'):
|
||||
<p>Batch has not yet been executed.</p>
|
||||
<b-button type="is-primary"
|
||||
% if not execute_enabled:
|
||||
disabled
|
||||
|
@ -119,7 +121,8 @@
|
|||
<div class="markdown">
|
||||
${execution_described|n}
|
||||
</div>
|
||||
${execute_form.render_vue_tag(ref='executeBatchForm')}
|
||||
<${execute_form.component} ref="executeBatchForm">
|
||||
</${execute_form.component}>
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
|
@ -148,6 +151,12 @@
|
|||
</nav>
|
||||
</%def>
|
||||
|
||||
<%def name="render_form_template()">
|
||||
## TODO: should use self.render_form_buttons()
|
||||
## ${form.render_deform(form_id='batch-form', buttons=capture(self.render_form_buttons))|n}
|
||||
${form.render_deform(form_id='batch-form', buttons=capture(buttons))|n}
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page()">
|
||||
${parent.render_this_page()}
|
||||
|
||||
|
@ -167,7 +176,8 @@
|
|||
Please be certain to use the right one!
|
||||
</p>
|
||||
<br />
|
||||
${upload_worksheet_form.render_vue_tag(ref='uploadForm')}
|
||||
<${upload_worksheet_form.component} ref="uploadForm">
|
||||
</${upload_worksheet_form.component}>
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
|
@ -189,6 +199,16 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
% if master.has_worksheet_file and master.allow_worksheet(batch) and master.has_perm('worksheet'):
|
||||
${upload_worksheet_form.render_deform(buttons=False, form_kwargs={'ref': 'actualUploadForm'})|n}
|
||||
% endif
|
||||
% if master.handler.executable(batch) and master.has_perm('execute'):
|
||||
${execute_form.render_deform(form_kwargs={'ref': 'actualExecuteForm'}, buttons=False)|n}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_form()">
|
||||
<div class="form">
|
||||
<${form.component} @show-upload="showUploadDialog = true">
|
||||
|
@ -249,27 +269,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if master.has_worksheet_file and master.allow_worksheet(batch) and master.has_perm('worksheet'):
|
||||
${upload_worksheet_form.render_vue_template(buttons=False, form_kwargs={'ref': 'actualUploadForm'})}
|
||||
% endif
|
||||
% if master.handler.executable(batch) and master.has_perm('execute'):
|
||||
${execute_form.render_vue_template(form_kwargs={'ref': 'actualExecuteForm'}, buttons=False)}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
## nb. this is called by parent template, /form.mako
|
||||
<%def name="render_form_template()">
|
||||
## TODO: should use self.render_form_buttons()
|
||||
## ${form.render_deform(form_id='batch-form', buttons=capture(self.render_form_buttons))|n}
|
||||
${form.render_deform(form_id='batch-form', buttons=capture(buttons))|n}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.statusBreakdownData = ${json.dumps(status_breakdown_data)|n}
|
||||
|
||||
|
@ -285,7 +287,7 @@
|
|||
}
|
||||
|
||||
% if not batch.executed and master.has_perm('edit'):
|
||||
${form.vue_component}Data.togglingBatchComplete = false
|
||||
${form.component_studly}Data.togglingBatchComplete = false
|
||||
% endif
|
||||
|
||||
% if master.has_worksheet_file and master.allow_worksheet(batch) and master.has_perm('worksheet'):
|
||||
|
@ -306,7 +308,7 @@
|
|||
form.submit()
|
||||
}
|
||||
|
||||
${upload_worksheet_form.vue_component}.methods.submit = function() {
|
||||
${upload_worksheet_form.component_studly}.methods.submit = function() {
|
||||
this.$refs.actualUploadForm.submit()
|
||||
}
|
||||
|
||||
|
@ -321,7 +323,7 @@
|
|||
this.$refs.executeBatchForm.submit()
|
||||
}
|
||||
|
||||
${execute_form.vue_component}.methods.submit = function() {
|
||||
${execute_form.component_studly}.methods.submit = function() {
|
||||
this.$refs.actualExecuteForm.submit()
|
||||
}
|
||||
|
||||
|
@ -329,9 +331,9 @@
|
|||
|
||||
% if master.rows_bulk_deletable and not batch.executed and master.has_perm('delete_rows'):
|
||||
|
||||
${rows_grid.vue_component}Data.deleteResultsShowDialog = false
|
||||
${rows_grid.component_studly}Data.deleteResultsShowDialog = false
|
||||
|
||||
${rows_grid.vue_component}.methods.deleteResultsInit = function() {
|
||||
${rows_grid.component_studly}.methods.deleteResultsInit = function() {
|
||||
this.deleteResultsShowDialog = true
|
||||
}
|
||||
|
||||
|
@ -340,12 +342,28 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
% if master.has_worksheet_file and master.allow_worksheet(batch) and master.has_perm('worksheet'):
|
||||
${upload_worksheet_form.render_vue_finalize()}
|
||||
<script type="text/javascript">
|
||||
|
||||
## UploadForm
|
||||
${upload_worksheet_form.component_studly}.data = function() { return ${upload_worksheet_form.component_studly}Data }
|
||||
Vue.component('${upload_worksheet_form.component}', ${upload_worksheet_form.component_studly})
|
||||
|
||||
</script>
|
||||
% endif
|
||||
|
||||
% if execute_enabled and master.has_perm('execute'):
|
||||
${execute_form.render_vue_finalize()}
|
||||
<script type="text/javascript">
|
||||
|
||||
## ExecuteForm
|
||||
${execute_form.component_studly}.data = function() { return ${execute_form.component_studly}Data }
|
||||
Vue.component('${execute_form.component}', ${execute_form.component_studly})
|
||||
|
||||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -208,9 +208,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.menuSequence = ${json.dumps([m['key'] for m in menus])|n}
|
||||
|
||||
|
@ -443,3 +443,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
<b-select name="${tmpl['setting_file']}"
|
||||
v-model="inputFileTemplateSettings['${tmpl['setting_file']}']"
|
||||
@input="settingsNeedSaved = true">
|
||||
<option value="">-new-</option>
|
||||
<option :value="null">-new-</option>
|
||||
<option v-for="option in inputFileTemplateFileOptions['${tmpl['key']}']"
|
||||
:key="option"
|
||||
:value="option">
|
||||
|
@ -104,40 +104,22 @@
|
|||
<b-field label="Upload"
|
||||
v-show="inputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted' && !inputFileTemplateSettings['${tmpl['setting_file']}']">
|
||||
|
||||
% if request.use_oruga:
|
||||
<o-field class="file">
|
||||
<o-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
v-slot="{ onclick }"
|
||||
@input="settingsNeedSaved = true">
|
||||
<o-button variant="primary"
|
||||
@click="onclick">
|
||||
<o-icon icon="upload" />
|
||||
<span>Click to upload</span>
|
||||
</o-button>
|
||||
<span class="file-name" v-if="inputFileTemplateUploads['${tmpl['key']}']">
|
||||
{{ inputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</o-upload>
|
||||
</o-field>
|
||||
% else:
|
||||
<b-field class="file is-primary"
|
||||
:class="{'has-name': !!inputFileTemplateSettings['${tmpl['setting_file']}']}">
|
||||
<b-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-label"
|
||||
@input="settingsNeedSaved = true">
|
||||
<span class="file-cta">
|
||||
<b-icon class="file-icon" pack="fas" icon="upload"></b-icon>
|
||||
<span class="file-label">Click to upload</span>
|
||||
</span>
|
||||
</b-upload>
|
||||
<span v-if="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-name">
|
||||
{{ inputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</b-field>
|
||||
% endif
|
||||
<b-field class="file is-primary"
|
||||
:class="{'has-name': !!inputFileTemplateSettings['${tmpl['setting_file']}']}">
|
||||
<b-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-label"
|
||||
@input="settingsNeedSaved = true">
|
||||
<span class="file-cta">
|
||||
<b-icon class="file-icon" pack="fas" icon="upload"></b-icon>
|
||||
<span class="file-label">Click to upload</span>
|
||||
</span>
|
||||
</b-upload>
|
||||
<span v-if="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-name">
|
||||
{{ inputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
|
@ -161,85 +143,6 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="output_file_template_field(key)">
|
||||
<% tmpl = output_file_templates[key] %>
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="${tmpl['label']}">
|
||||
<b-select name="${tmpl['setting_mode']}"
|
||||
v-model="outputFileTemplateSettings['${tmpl['setting_mode']}']"
|
||||
@input="settingsNeedSaved = true">
|
||||
<option value="default">use default</option>
|
||||
<option value="hosted">use uploaded file</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
|
||||
<b-field label="File"
|
||||
v-show="outputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted'"
|
||||
:message="outputFileTemplateSettings['${tmpl['setting_file']}'] ? 'This file lives on disk at: ${output_file_option_dirs[tmpl['key']]}' : null">
|
||||
<b-select name="${tmpl['setting_file']}"
|
||||
v-model="outputFileTemplateSettings['${tmpl['setting_file']}']"
|
||||
@input="settingsNeedSaved = true">
|
||||
<option value="">-new-</option>
|
||||
<option v-for="option in outputFileTemplateFileOptions['${tmpl['key']}']"
|
||||
:key="option"
|
||||
:value="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Upload"
|
||||
v-show="outputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted' && !outputFileTemplateSettings['${tmpl['setting_file']}']">
|
||||
|
||||
% if request.use_oruga:
|
||||
<o-field class="file">
|
||||
<o-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="outputFileTemplateUploads['${tmpl['key']}']"
|
||||
v-slot="{ onclick }"
|
||||
@input="settingsNeedSaved = true">
|
||||
<o-button variant="primary"
|
||||
@click="onclick">
|
||||
<o-icon icon="upload" />
|
||||
<span>Click to upload</span>
|
||||
</o-button>
|
||||
<span class="file-name" v-if="outputFileTemplateUploads['${tmpl['key']}']">
|
||||
{{ outputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</o-upload>
|
||||
</o-field>
|
||||
% else:
|
||||
<b-field class="file is-primary"
|
||||
:class="{'has-name': !!outputFileTemplateSettings['${tmpl['setting_file']}']}">
|
||||
<b-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="outputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-label"
|
||||
@input="settingsNeedSaved = true">
|
||||
<span class="file-cta">
|
||||
<b-icon class="file-icon" pack="fas" icon="upload"></b-icon>
|
||||
<span class="file-label">Click to upload</span>
|
||||
</span>
|
||||
</b-upload>
|
||||
<span v-if="outputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-name">
|
||||
{{ outputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</b-field>
|
||||
% endif
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
</%def>
|
||||
|
||||
<%def name="output_file_templates_section()">
|
||||
<h3 class="block is-size-3">Output File Templates</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
% for key in output_file_templates:
|
||||
${self.output_file_template_field(key)}
|
||||
% endfor
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="form_content()"></%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
|
@ -280,14 +183,15 @@
|
|||
<b-button @click="purgeSettingsShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
${h.form(request.current_route_url(), **{'@submit': 'purgingSettings = true'})}
|
||||
${h.form(request.current_route_url())}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('remove_settings', 'true')}
|
||||
<b-button type="is-danger"
|
||||
native-type="submit"
|
||||
:disabled="purgingSettings"
|
||||
icon-pack="fas"
|
||||
icon-left="trash">
|
||||
icon-left="trash"
|
||||
@click="purgingSettings = true">
|
||||
{{ purgingSettings ? "Working, please wait..." : "Remove All Settings" }}
|
||||
</b-button>
|
||||
${h.end_form()}
|
||||
|
@ -301,42 +205,62 @@
|
|||
${h.end_form()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if simple_settings is not Undefined:
|
||||
ThisPageData.simpleSettings = ${json.dumps(simple_settings)|n}
|
||||
% endif
|
||||
|
||||
% if input_file_template_settings is not Undefined:
|
||||
ThisPageData.inputFileTemplateSettings = ${json.dumps(input_file_template_settings)|n}
|
||||
ThisPageData.inputFileTemplateFileOptions = ${json.dumps(input_file_options)|n}
|
||||
ThisPageData.inputFileTemplateUploads = {
|
||||
% for key in input_file_templates:
|
||||
'${key}': null,
|
||||
% endfor
|
||||
}
|
||||
% endif
|
||||
|
||||
ThisPageData.purgeSettingsShowDialog = false
|
||||
ThisPageData.purgingSettings = false
|
||||
|
||||
ThisPageData.settingsNeedSaved = false
|
||||
ThisPageData.undoChanges = false
|
||||
ThisPageData.savingSettings = false
|
||||
ThisPageData.validators = []
|
||||
|
||||
ThisPage.methods.purgeSettingsInit = function() {
|
||||
this.purgeSettingsShowDialog = true
|
||||
}
|
||||
|
||||
ThisPage.methods.validateSettings = function() {}
|
||||
% if input_file_template_settings is not Undefined:
|
||||
ThisPage.methods.validateInputFileTemplateSettings = function() {
|
||||
% for tmpl in input_file_templates.values():
|
||||
if (this.inputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted') {
|
||||
if (!this.inputFileTemplateSettings['${tmpl['setting_file']}']) {
|
||||
if (!this.inputFileTemplateUploads['${tmpl['key']}']) {
|
||||
return "You must provide a file to upload for the ${tmpl['label']} template."
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
% endif
|
||||
|
||||
ThisPage.methods.saveSettings = function() {
|
||||
ThisPage.methods.validateSettings = function() {
|
||||
let msg
|
||||
|
||||
// nb. this is the future
|
||||
for (let validator of this.validators) {
|
||||
msg = validator.call(this)
|
||||
% if input_file_template_settings is not Undefined:
|
||||
msg = this.validateInputFileTemplateSettings()
|
||||
if (msg) {
|
||||
alert(msg)
|
||||
return
|
||||
return msg
|
||||
}
|
||||
}
|
||||
% endif
|
||||
}
|
||||
|
||||
// nb. legacy method
|
||||
msg = this.validateSettings()
|
||||
ThisPage.methods.saveSettings = function() {
|
||||
let msg = this.validateSettings()
|
||||
if (msg) {
|
||||
alert(msg)
|
||||
return
|
||||
|
@ -367,65 +291,8 @@
|
|||
window.addEventListener('beforeunload', this.beforeWindowUnload)
|
||||
}
|
||||
|
||||
##############################
|
||||
## input file templates
|
||||
##############################
|
||||
|
||||
% if input_file_template_settings is not Undefined:
|
||||
|
||||
ThisPageData.inputFileTemplateSettings = ${json.dumps(input_file_template_settings)|n}
|
||||
ThisPageData.inputFileTemplateFileOptions = ${json.dumps(input_file_options)|n}
|
||||
ThisPageData.inputFileTemplateUploads = {
|
||||
% for key in input_file_templates:
|
||||
'${key}': null,
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPage.methods.validateInputFileTemplateSettings = function() {
|
||||
% for tmpl in input_file_templates.values():
|
||||
if (this.inputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted') {
|
||||
if (!this.inputFileTemplateSettings['${tmpl['setting_file']}']) {
|
||||
if (!this.inputFileTemplateUploads['${tmpl['key']}']) {
|
||||
return "You must provide a file to upload for the ${tmpl['label']} template."
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPageData.validators.push(ThisPage.methods.validateInputFileTemplateSettings)
|
||||
|
||||
% endif
|
||||
|
||||
##############################
|
||||
## output file templates
|
||||
##############################
|
||||
|
||||
% if output_file_template_settings is not Undefined:
|
||||
|
||||
ThisPageData.outputFileTemplateSettings = ${json.dumps(output_file_template_settings)|n}
|
||||
ThisPageData.outputFileTemplateFileOptions = ${json.dumps(output_file_options)|n}
|
||||
ThisPageData.outputFileTemplateUploads = {
|
||||
% for key in output_file_templates:
|
||||
'${key}': null,
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPage.methods.validateOutputFileTemplateSettings = function() {
|
||||
% for tmpl in output_file_templates.values():
|
||||
if (this.outputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted') {
|
||||
if (!this.outputFileTemplateSettings['${tmpl['setting_file']}']) {
|
||||
if (!this.outputFileTemplateUploads['${tmpl['key']}']) {
|
||||
return "You must provide a file to upload for the ${tmpl['label']} template."
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPageData.validators.push(ThisPage.methods.validateOutputFileTemplateSettings)
|
||||
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -88,9 +88,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPage.methods.getLabelForKey = function(key) {
|
||||
switch (key) {
|
||||
|
@ -111,3 +111,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -106,9 +106,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.resolvePersonShowDialog = false
|
||||
ThisPageData.resolvePersonUUID = null
|
||||
|
@ -139,3 +139,5 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if expose_shoppers:
|
||||
${form.vue_component}Data.shoppers = ${json.dumps(shoppers_data)|n}
|
||||
${form.component_studly}Data.shoppers = ${json.dumps(shoppers_data)|n}
|
||||
% endif
|
||||
% if expose_people:
|
||||
${form.vue_component}Data.peopleData = ${json.dumps(people_data)|n}
|
||||
${form.component_studly}Data.peopleData = ${json.dumps(people_data)|n}
|
||||
% endif
|
||||
|
||||
ThisPage.methods.detachPerson = function(url) {
|
||||
|
@ -36,3 +36,5 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -47,9 +47,10 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
${product_lookup.tailbone_product_lookup_template()}
|
||||
|
||||
<script type="text/x-template" id="customer-order-creator-template">
|
||||
<div>
|
||||
|
||||
|
@ -1264,7 +1265,12 @@
|
|||
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
${product_lookup.tailbone_product_lookup_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
const CustomerOrderCreator = {
|
||||
template: '#customer-order-creator-template',
|
||||
|
@ -2400,7 +2406,5 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
${product_lookup.tailbone_product_lookup_component()}
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -291,11 +291,11 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}Data.eventsData = ${json.dumps(events_data)|n}
|
||||
${form.component_studly}Data.eventsData = ${json.dumps(events_data)|n}
|
||||
|
||||
% if master.has_perm('confirm_price'):
|
||||
|
||||
|
@ -392,9 +392,9 @@
|
|||
this.$refs.changeStatusForm.submit()
|
||||
}
|
||||
|
||||
${form.vue_component}Data.changeFlaggedSubmitting = false
|
||||
${form.component_studly}Data.changeFlaggedSubmitting = false
|
||||
|
||||
${form.vue_component}.methods.changeFlaggedSubmit = function() {
|
||||
${form.component_studly}.methods.changeFlaggedSubmit = function() {
|
||||
this.changeFlaggedSubmitting = true
|
||||
}
|
||||
|
||||
|
@ -448,3 +448,5 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if request.has_perm('datasync.restart'):
|
||||
TailboneGridData.restartDatasyncFormSubmitting = false
|
||||
|
@ -50,3 +50,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -83,8 +83,8 @@
|
|||
</b-notification>
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.datasync.use_profile_settings"
|
||||
v-model="simpleSettings['rattail.datasync.use_profile_settings']"
|
||||
<b-checkbox name="use_profile_settings"
|
||||
v-model="useProfileSettings"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Use these Settings to configure watchers and consumers
|
||||
|
@ -99,7 +99,7 @@
|
|||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item"
|
||||
v-show="simpleSettings['rattail.datasync.use_profile_settings']">
|
||||
v-show="useProfileSettings">
|
||||
<b-button type="is-primary"
|
||||
@click="newProfile()"
|
||||
icon-pack="fas"
|
||||
|
@ -162,7 +162,7 @@
|
|||
</${b}-table-column>
|
||||
<${b}-table-column label="Actions"
|
||||
v-slot="props"
|
||||
v-if="simpleSettings['rattail.datasync.use_profile_settings']">
|
||||
v-if="useProfileSettings">
|
||||
<a href="#"
|
||||
class="grid-action"
|
||||
@click.prevent="editProfile(props.row)">
|
||||
|
@ -580,27 +580,18 @@
|
|||
<b-field label="Supervisor Process Name"
|
||||
message="This should be the complete name, including group - e.g. poser:poser_datasync"
|
||||
expanded>
|
||||
<b-input name="rattail.datasync.supervisor_process_name"
|
||||
v-model="simpleSettings['rattail.datasync.supervisor_process_name']"
|
||||
<b-input name="supervisor_process_name"
|
||||
v-model="supervisorProcessName"
|
||||
@input="settingsNeedSaved = true"
|
||||
expanded>
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Consumer Batch Size"
|
||||
message="Max number of changes to be consumed at once."
|
||||
expanded>
|
||||
<numeric-input name="rattail.datasync.batch_size_limit"
|
||||
v-model="simpleSettings['rattail.datasync.batch_size_limit']"
|
||||
@input="settingsNeedSaved = true" />
|
||||
</b-field>
|
||||
|
||||
<h3 class="is-size-3">Legacy</h3>
|
||||
<b-field label="Restart Command"
|
||||
message="This will run as '${system_user}' system user - please configure sudoers as needed. Typical command is like: sudo supervisorctl restart poser:poser_datasync"
|
||||
expanded>
|
||||
<b-input name="tailbone.datasync.restart"
|
||||
v-model="simpleSettings['tailbone.datasync.restart']"
|
||||
<b-input name="restart_command"
|
||||
v-model="restartCommand"
|
||||
@input="settingsNeedSaved = true"
|
||||
expanded>
|
||||
</b-input>
|
||||
|
@ -608,13 +599,14 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.showConfigFilesNote = false
|
||||
ThisPageData.profilesData = ${json.dumps(profiles_data)|n}
|
||||
ThisPageData.showDisabledProfiles = false
|
||||
ThisPageData.useProfileSettings = ${json.dumps(use_profile_settings)|n}
|
||||
|
||||
ThisPageData.editProfileShowDialog = false
|
||||
ThisPageData.editingProfile = null
|
||||
|
@ -639,6 +631,9 @@
|
|||
ThisPageData.editingConsumerRunas = null
|
||||
ThisPageData.editingConsumerEnabled = true
|
||||
|
||||
ThisPageData.supervisorProcessName = ${json.dumps(supervisor_process_name)|n}
|
||||
ThisPageData.restartCommand = ${json.dumps(restart_command)|n}
|
||||
|
||||
ThisPage.computed.updateConsumerDisabled = function() {
|
||||
if (!this.editingConsumerKey) {
|
||||
return true
|
||||
|
@ -987,3 +982,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -115,9 +115,8 @@
|
|||
</${b}-table>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.processInfo = ${json.dumps(process_info)|n}
|
||||
|
||||
|
@ -172,3 +171,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<div i18n:domain="deform" tal:omit-tag=""
|
||||
tal:define="oid oid|field.oid;
|
||||
name name|field.name;
|
||||
vmodel vmodel|'field_model_' + name;
|
||||
css_class css_class|field.widget.css_class;
|
||||
style style|field.widget.style;">
|
||||
|
||||
|
@ -9,7 +8,7 @@
|
|||
${field.start_mapping()}
|
||||
<b-input type="password"
|
||||
name="${name}"
|
||||
v-model="${vmodel}"
|
||||
value="${field.widget.redisplay and cstruct or ''}"
|
||||
tal:attributes="class string: form-control ${css_class or ''};
|
||||
style style;
|
||||
attributes|field.widget.attributes|{};"
|
||||
|
@ -19,6 +18,7 @@
|
|||
</b-input>
|
||||
<b-input type="password"
|
||||
name="${name}-confirm"
|
||||
value="${field.widget.redisplay and confirm or ''}"
|
||||
tal:attributes="class string: form-control ${css_class or ''};
|
||||
style style;
|
||||
confirm_attributes|field.widget.confirm_attributes|{};"
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/master/view.mako" />
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
${form.vue_component}Data.employeesData = ${json.dumps(employees_data)|n}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.component_studly}Data.employeesData = ${json.dumps(employees_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
<%def name="render_form_buttons()"></%def>
|
||||
|
||||
<%def name="render_form_template()">
|
||||
${form.render_vue_template(buttons=capture(self.render_form_buttons))|n}
|
||||
${form.render_deform(buttons=capture(self.render_form_buttons))|n}
|
||||
</%def>
|
||||
|
||||
<%def name="render_form()">
|
||||
<div class="form">
|
||||
${form.render_vue_tag()}
|
||||
${form.render_vuejs_component()}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
|
@ -90,15 +90,15 @@
|
|||
|
||||
<%def name="before_object_helpers()"></%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
% if form is not Undefined:
|
||||
${self.render_form_template()}
|
||||
% endif
|
||||
${parent.render_this_page_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if main_form_collapsible:
|
||||
<script>
|
||||
ThisPageData.mainFormPanelOpen = ${'false' if main_form_autocollapse else 'true'}
|
||||
|
@ -106,9 +106,18 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<%def name="finalize_this_page_vars()">
|
||||
${parent.finalize_this_page_vars()}
|
||||
% if form is not Undefined:
|
||||
${form.render_vue_finalize()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.component_studly}.data = function() { return ${form.component_studly}Data }
|
||||
|
||||
Vue.component('${form.component}', ${form.component_studly})
|
||||
|
||||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
simplePOST(action, params, success, failure) {
|
||||
|
||||
let csrftoken = ${json.dumps(h.get_csrf_token(request))|n}
|
||||
let csrftoken = ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n}
|
||||
|
||||
let headers = {
|
||||
'${csrf_header_name}': csrftoken,
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
|
||||
<% request.register_component(form.vue_tagname, form.vue_component) %>
|
||||
<% request.register_component(form.component, form.component_studly) %>
|
||||
|
||||
<script type="text/x-template" id="${form.vue_tagname}-template">
|
||||
<script type="text/x-template" id="${form.component}-template">
|
||||
|
||||
<div>
|
||||
% if not form.readonly:
|
||||
${h.form(form.action_url, id=dform.formid, method='post', enctype='multipart/form-data', **(form_kwargs or {}))}
|
||||
${h.form(form.action_url, id=dform.formid, method='post', enctype='multipart/form-data', **form_kwargs)}
|
||||
${h.csrf_token(request)}
|
||||
% endif
|
||||
|
||||
<section>
|
||||
% if form_body is not Undefined and form_body:
|
||||
${form_body|n}
|
||||
% elif getattr(form, 'grouping', None):
|
||||
% elif form.grouping:
|
||||
% for group in form.grouping:
|
||||
<nav class="panel">
|
||||
<p class="panel-heading">${group}</p>
|
||||
|
@ -27,8 +27,8 @@
|
|||
</nav>
|
||||
% endfor
|
||||
% else:
|
||||
% for fieldname in form.fields:
|
||||
${form.render_vue_field(fieldname, session=session)}
|
||||
% for field in form.fields:
|
||||
${form.render_field_complete(field)}
|
||||
% endfor
|
||||
% endif
|
||||
</section>
|
||||
|
@ -54,20 +54,20 @@
|
|||
<input type="reset" value="Reset" class="button" />
|
||||
% endif
|
||||
## TODO: deprecate / remove the latter option here
|
||||
% if getattr(form, 'auto_disable_submit', False) or form.auto_disable_save or form.auto_disable:
|
||||
% if form.auto_disable_save or form.auto_disable:
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
:disabled="${form.vue_component}Submitting"
|
||||
:disabled="${form.component_studly}Submitting"
|
||||
icon-pack="fas"
|
||||
icon-left="${form.button_icon_submit}">
|
||||
{{ ${form.vue_component}Submitting ? "Working, please wait..." : "${form.button_label_submit}" }}
|
||||
icon-left="save">
|
||||
{{ ${form.component_studly}ButtonText }}
|
||||
</b-button>
|
||||
% else:
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
icon-pack="fas"
|
||||
icon-left="save">
|
||||
${form.button_label_submit}
|
||||
${getattr(form, 'submit_label', getattr(form, 'save_label', "Submit"))}
|
||||
</b-button>
|
||||
% endif
|
||||
</div>
|
||||
|
@ -122,8 +122,8 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
|
||||
let ${form.vue_component} = {
|
||||
template: '#${form.vue_tagname}-template',
|
||||
let ${form.component_studly} = {
|
||||
template: '#${form.component}-template',
|
||||
mixins: [FormPosterMixin],
|
||||
components: {},
|
||||
props: {
|
||||
|
@ -136,9 +136,10 @@
|
|||
methods: {
|
||||
|
||||
## TODO: deprecate / remove the latter option here
|
||||
% if getattr(form, 'auto_disable_submit', False) or form.auto_disable_save or form.auto_disable:
|
||||
submit${form.vue_component}() {
|
||||
this.${form.vue_component}Submitting = true
|
||||
% if form.auto_disable_save or form.auto_disable:
|
||||
submit${form.component_studly}() {
|
||||
this.${form.component_studly}Submitting = true
|
||||
this.${form.component_studly}ButtonText = "Working, please wait..."
|
||||
},
|
||||
% endif
|
||||
|
||||
|
@ -177,10 +178,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
let ${form.vue_component}Data = {
|
||||
let ${form.component_studly}Data = {
|
||||
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(h.get_csrf_token(request))|n},
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
|
||||
% if can_edit_help:
|
||||
fieldLabels: ${json.dumps(field_labels)|n},
|
||||
|
@ -197,14 +198,16 @@
|
|||
% if not form.readonly:
|
||||
% for field in form.fields:
|
||||
% if field in dform:
|
||||
field_model_${field}: ${json.dumps(form.get_vue_field_value(field))|n},
|
||||
<% field = dform[field] %>
|
||||
field_model_${field.name}: ${form.get_vuejs_model_value(field)|n},
|
||||
% endif
|
||||
% endfor
|
||||
% endif
|
||||
|
||||
## TODO: deprecate / remove the latter option here
|
||||
% if getattr(form, 'auto_disable_submit', False) or form.auto_disable_save or form.auto_disable:
|
||||
${form.vue_component}Submitting: false,
|
||||
% if form.auto_disable_save or form.auto_disable:
|
||||
${form.component_studly}Submitting: false,
|
||||
${form.component_studly}ButtonText: ${json.dumps(getattr(form, 'submit_label', getattr(form, 'save_label', "Submit")))|n},
|
||||
% endif
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/forms/deform.mako" />
|
||||
${parent.body()}
|
|
@ -276,9 +276,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.featureType = ${json.dumps(feature_type)|n}
|
||||
ThisPageData.resultGenerated = ${json.dumps(bool(result))|n}
|
||||
|
@ -385,3 +385,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -53,11 +53,11 @@
|
|||
</${b}-table-column>
|
||||
% endfor
|
||||
|
||||
% if grid.actions:
|
||||
% if grid.main_actions or grid.more_actions:
|
||||
<${b}-table-column field="actions"
|
||||
label="Actions"
|
||||
v-slot="props">
|
||||
% for action in grid.actions:
|
||||
% for action in grid.main_actions:
|
||||
<a :href="props.row._action_url_${action.key}"
|
||||
% if action.link_class:
|
||||
class="${action.link_class}"
|
||||
|
@ -68,7 +68,12 @@
|
|||
@click.prevent="${action.click_handler}"
|
||||
% endif
|
||||
>
|
||||
${action.render_icon_and_label()}
|
||||
% if request.use_oruga:
|
||||
<o-icon icon="${action.icon}" />
|
||||
% else:
|
||||
<i class="fas fa-${action.icon}"></i>
|
||||
% endif
|
||||
${action.label}
|
||||
</a>
|
||||
|
||||
% endfor
|
||||
|
|
|
@ -1,79 +1,17 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
|
||||
<% request.register_component(grid.vue_tagname, grid.vue_component) %>
|
||||
<% request.register_component(grid.component, grid.component_studly) %>
|
||||
|
||||
<script type="text/x-template" id="${grid.vue_tagname}-template">
|
||||
<script type="text/x-template" id="${grid.component}-template">
|
||||
<div>
|
||||
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 0.5em;">
|
||||
|
||||
<div style="display: flex; flex-direction: column; justify-content: end;">
|
||||
<div class="filters">
|
||||
% if getattr(grid, 'filterable', False):
|
||||
<form method="GET" @submit.prevent="applyFilters()">
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<grid-filter v-for="key in filtersSequence"
|
||||
:key="key"
|
||||
:filter="filters[key]"
|
||||
ref="gridFilters">
|
||||
</grid-filter>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
|
||||
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
icon-pack="fas"
|
||||
icon-left="check">
|
||||
Apply Filters
|
||||
</b-button>
|
||||
|
||||
<b-button v-if="!addFilterShow"
|
||||
icon-pack="fas"
|
||||
icon-left="plus"
|
||||
@click="addFilterInit()">
|
||||
Add Filter
|
||||
</b-button>
|
||||
|
||||
<b-autocomplete v-if="addFilterShow"
|
||||
ref="addFilterAutocomplete"
|
||||
:data="addFilterChoices"
|
||||
v-model="addFilterTerm"
|
||||
placeholder="Add Filter"
|
||||
field="key"
|
||||
:custom-formatter="formatAddFilterItem"
|
||||
open-on-focus
|
||||
keep-first
|
||||
icon-pack="fas"
|
||||
clearable
|
||||
clear-on-select
|
||||
@select="addFilterSelect">
|
||||
</b-autocomplete>
|
||||
|
||||
<b-button @click="resetView()"
|
||||
icon-pack="fas"
|
||||
icon-left="home">
|
||||
Default View
|
||||
</b-button>
|
||||
|
||||
<b-button @click="clearFilters()"
|
||||
icon-pack="fas"
|
||||
icon-left="trash">
|
||||
No Filters
|
||||
</b-button>
|
||||
|
||||
% if allow_save_defaults and request.user:
|
||||
<b-button @click="saveDefaults()"
|
||||
icon-pack="fas"
|
||||
icon-left="save"
|
||||
:disabled="savingDefaults">
|
||||
{{ savingDefaults ? "Working, please wait..." : "Save Defaults" }}
|
||||
</b-button>
|
||||
% endif
|
||||
|
||||
</div>
|
||||
</form>
|
||||
% if grid.filterable:
|
||||
## TODO: stop using |n filter
|
||||
${grid.render_filters(allow_save_defaults=allow_save_defaults)|n}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -117,7 +55,7 @@
|
|||
|
||||
:checkable="checkable"
|
||||
|
||||
% if getattr(grid, 'checkboxes', False):
|
||||
% if grid.checkboxes:
|
||||
% if request.use_oruga:
|
||||
v-model:checked-rows="checkedRows"
|
||||
% else:
|
||||
|
@ -128,64 +66,51 @@
|
|||
% endif
|
||||
% endif
|
||||
|
||||
% if getattr(grid, 'check_handler', None):
|
||||
% if grid.check_handler:
|
||||
@check="${grid.check_handler}"
|
||||
% endif
|
||||
% if getattr(grid, 'check_all_handler', None):
|
||||
% if grid.check_all_handler:
|
||||
@check-all="${grid.check_all_handler}"
|
||||
% endif
|
||||
|
||||
% if hasattr(grid, 'checkable'):
|
||||
% if isinstance(grid.checkable, str):
|
||||
:is-row-checkable="${grid.row_checkable}"
|
||||
% elif grid.checkable:
|
||||
:is-row-checkable="row => row._checkable"
|
||||
% endif
|
||||
% endif
|
||||
|
||||
## sorting
|
||||
% if grid.sortable:
|
||||
## nb. buefy/oruga only support *one* default sorter
|
||||
:default-sort="sorters.length ? [sorters[0].field, sorters[0].order] : null"
|
||||
% if grid.sort_on_backend:
|
||||
backend-sorting
|
||||
@sort="onSort"
|
||||
% endif
|
||||
% if grid.sort_multiple:
|
||||
% if grid.sort_on_backend:
|
||||
## TODO: there is a bug (?) which prevents the arrow
|
||||
## from displaying for simple default single-column sort,
|
||||
## when multi-column sort is allowed for the table. for
|
||||
## now we work around that by waiting until mount to
|
||||
## enable the multi-column support. see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
:sort-multiple="allowMultiSort"
|
||||
:sort-multiple-data="sortingPriority"
|
||||
@sorting-priority-removed="sortingPriorityRemoved"
|
||||
% else:
|
||||
sort-multiple
|
||||
% endif
|
||||
## nb. user must ctrl-click column header for multi-sort
|
||||
sort-multiple-key="ctrlKey"
|
||||
% endif
|
||||
backend-sorting
|
||||
@sort="onSort"
|
||||
@sorting-priority-removed="sortingPriorityRemoved"
|
||||
|
||||
## TODO: there is a bug (?) which prevents the arrow from
|
||||
## displaying for simple default single-column sort. so to
|
||||
## work around that, we *disable* multi-sort until the
|
||||
## component is mounted. seems to work for now..see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
:sort-multiple="allowMultiSort"
|
||||
|
||||
## nb. specify default sort only if single-column
|
||||
:default-sort="backendSorters.length == 1 ? [backendSorters[0].field, backendSorters[0].order] : null"
|
||||
|
||||
## nb. otherwise there may be default multi-column sort
|
||||
:sort-multiple-data="sortingPriority"
|
||||
|
||||
## user must ctrl-click column header to do multi-sort
|
||||
sort-multiple-key="ctrlKey"
|
||||
% endif
|
||||
|
||||
% if getattr(grid, 'click_handlers', None):
|
||||
% if grid.click_handlers:
|
||||
@cellclick="cellClick"
|
||||
% endif
|
||||
|
||||
## paging
|
||||
% if grid.paginated:
|
||||
paginated
|
||||
pagination-size="${'small' if request.use_oruga else 'is-small'}"
|
||||
:per-page="perPage"
|
||||
:current-page="currentPage"
|
||||
@page-change="onPageChange"
|
||||
% if grid.paginate_on_backend:
|
||||
backend-pagination
|
||||
:total="pagerStats.item_count"
|
||||
% endif
|
||||
% endif
|
||||
:paginated="paginated"
|
||||
:per-page="perPage"
|
||||
:current-page="currentPage"
|
||||
backend-pagination
|
||||
:total="total"
|
||||
@page-change="onPageChange"
|
||||
|
||||
## TODO: should let grid (or master view) decide how to set these?
|
||||
icon-pack="fas"
|
||||
|
@ -194,15 +119,17 @@
|
|||
:hoverable="true"
|
||||
:narrowed="true">
|
||||
|
||||
% for column in grid.get_vue_columns():
|
||||
% for column in grid_columns:
|
||||
<${b}-table-column field="${column['field']}"
|
||||
label="${column['label']}"
|
||||
v-slot="props"
|
||||
:sortable="${json.dumps(column.get('sortable', False))|n}"
|
||||
:searchable="${json.dumps(column.get('searchable', False))|n}"
|
||||
:sortable="${json.dumps(column['sortable'])}"
|
||||
% if grid.is_searchable(column['field']):
|
||||
searchable
|
||||
% endif
|
||||
cell-class="c_${column['field']}"
|
||||
:visible="${json.dumps(column.get('visible', True))}">
|
||||
% if hasattr(grid, 'raw_renderers') and column['field'] in grid.raw_renderers:
|
||||
:visible="${json.dumps(column['visible'])}">
|
||||
% if column['field'] in grid.raw_renderers:
|
||||
${grid.raw_renderers[column['field']]()}
|
||||
% elif grid.is_linked(column['field']):
|
||||
<a :href="props.row._action_url_view"
|
||||
|
@ -217,24 +144,30 @@
|
|||
</${b}-table-column>
|
||||
% endfor
|
||||
|
||||
% if grid.actions:
|
||||
% if grid.main_actions or grid.more_actions:
|
||||
<${b}-table-column field="actions"
|
||||
label="Actions"
|
||||
v-slot="props">
|
||||
## TODO: we do not currently differentiate for "main vs. more"
|
||||
## here, but ideally we would tuck "more" away in a drawer etc.
|
||||
% for action in grid.actions:
|
||||
% for action in grid.main_actions + grid.more_actions:
|
||||
<a v-if="props.row._action_url_${action.key}"
|
||||
:href="props.row._action_url_${action.key}"
|
||||
class="grid-action${' has-text-danger' if action.key == 'delete' else ''} ${action.link_class or ''}"
|
||||
% if getattr(action, 'click_handler', None):
|
||||
% if action.click_handler:
|
||||
@click.prevent="${action.click_handler}"
|
||||
% endif
|
||||
% if getattr(action, 'target', None):
|
||||
% if action.target:
|
||||
target="${action.target}"
|
||||
% endif
|
||||
>
|
||||
${action.render_icon_and_label()}
|
||||
% if request.use_oruga:
|
||||
<o-icon icon="${action.icon}" />
|
||||
<span>${action.render_label()|n}</span>
|
||||
% else:
|
||||
${action.render_icon()|n}
|
||||
${action.render_label()|n}
|
||||
% endif
|
||||
</a>
|
||||
|
||||
% endfor
|
||||
|
@ -259,7 +192,7 @@
|
|||
<template #footer>
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
|
||||
% if getattr(grid, 'expose_direct_link', False):
|
||||
% if grid.expose_direct_link:
|
||||
<b-button type="is-primary"
|
||||
size="is-small"
|
||||
@click="copyDirectLink()"
|
||||
|
@ -274,14 +207,13 @@
|
|||
<div></div>
|
||||
% endif
|
||||
|
||||
% if grid.paginated:
|
||||
<div v-if="pagerStats.first_item"
|
||||
% if grid.pageable:
|
||||
<div v-if="firstItem"
|
||||
style="display: flex; gap: 0.5rem; align-items: center;">
|
||||
<span>
|
||||
showing
|
||||
{{ renderNumber(pagerStats.first_item) }}
|
||||
- {{ renderNumber(pagerStats.last_item) }}
|
||||
of {{ renderNumber(pagerStats.item_count) }} results;
|
||||
{{ firstItem.toLocaleString('en') }} - {{ lastItem.toLocaleString('en') }}
|
||||
of {{ total.toLocaleString('en') }} results;
|
||||
</span>
|
||||
<b-select v-model="perPage"
|
||||
size="is-small"
|
||||
|
@ -302,7 +234,7 @@
|
|||
</${b}-table>
|
||||
|
||||
## dummy input field needed for sharing links on *insecure* sites
|
||||
% if getattr(request, 'scheme', None) == 'http':
|
||||
% if request.scheme == 'http':
|
||||
<b-input v-model="shareLink" ref="shareLink" v-show="shareLink"></b-input>
|
||||
% endif
|
||||
|
||||
|
@ -311,72 +243,65 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
|
||||
const ${grid.vue_component}Context = ${json.dumps(grid.get_vue_context())|n}
|
||||
let ${grid.vue_component}CurrentData = ${grid.vue_component}Context.data
|
||||
let ${grid.component_studly}CurrentData = ${json.dumps(grid_data['data'])|n}
|
||||
|
||||
let ${grid.vue_component}Data = {
|
||||
let ${grid.component_studly}Data = {
|
||||
loading: false,
|
||||
ajaxDataUrl: ${json.dumps(getattr(grid, 'ajax_data_url', request.path_url))|n},
|
||||
|
||||
## nb. this tracks whether grid.fetchFirstData() happened
|
||||
fetchedFirstData: false,
|
||||
ajaxDataUrl: ${json.dumps(grid.ajax_data_url)|n},
|
||||
|
||||
savingDefaults: false,
|
||||
|
||||
data: ${grid.vue_component}CurrentData,
|
||||
rowStatusMap: ${json.dumps(grid_data['row_status_map'] if grid_data is not Undefined else {})|n},
|
||||
data: ${grid.component_studly}CurrentData,
|
||||
rowStatusMap: ${json.dumps(grid_data['row_status_map'])|n},
|
||||
|
||||
checkable: ${json.dumps(getattr(grid, 'checkboxes', False))|n},
|
||||
% if getattr(grid, 'checkboxes', False):
|
||||
checkable: ${json.dumps(grid.checkboxes)|n},
|
||||
% if grid.checkboxes:
|
||||
checkedRows: ${grid_data['checked_rows_code']|n},
|
||||
% endif
|
||||
|
||||
## paging
|
||||
% if grid.paginated:
|
||||
pageSizeOptions: ${json.dumps(grid.pagesize_options)|n},
|
||||
perPage: ${json.dumps(grid.pagesize)|n},
|
||||
currentPage: ${json.dumps(grid.page)|n},
|
||||
% if grid.paginate_on_backend:
|
||||
pagerStats: ${json.dumps(grid.get_vue_pager_stats())|n},
|
||||
% endif
|
||||
% endif
|
||||
paginated: ${json.dumps(grid.pageable)|n},
|
||||
total: ${len(grid_data['data']) if static_data else grid_data['total_items']},
|
||||
perPage: ${json.dumps(grid.pagesize if grid.pageable else None)|n},
|
||||
currentPage: ${json.dumps(grid.page if grid.pageable else None)|n},
|
||||
firstItem: ${json.dumps(grid_data['first_item'] if grid.pageable else None)|n},
|
||||
lastItem: ${json.dumps(grid_data['last_item'] if grid.pageable else None)|n},
|
||||
|
||||
## sorting
|
||||
% if grid.sortable:
|
||||
sorters: ${json.dumps(grid.get_vue_active_sorters())|n},
|
||||
% if grid.sort_multiple:
|
||||
% if grid.sort_on_backend:
|
||||
## TODO: there is a bug (?) which prevents the arrow
|
||||
## from displaying for simple default single-column sort,
|
||||
## when multi-column sort is allowed for the table. for
|
||||
## now we work around that by waiting until mount to
|
||||
## enable the multi-column support. see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
allowMultiSort: false,
|
||||
## nb. this should be empty when current sort is single-column
|
||||
% if len(grid.active_sorters) > 1:
|
||||
sortingPriority: ${json.dumps(grid.get_vue_active_sorters())|n},
|
||||
% else:
|
||||
sortingPriority: [],
|
||||
% endif
|
||||
% endif
|
||||
|
||||
## TODO: there is a bug (?) which prevents the arrow from
|
||||
## displaying for simple default single-column sort. so to
|
||||
## work around that, we *disable* multi-sort until the
|
||||
## component is mounted. seems to work for now..see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
allowMultiSort: false,
|
||||
|
||||
## nb. this contains all truly active sorters
|
||||
backendSorters: ${json.dumps(grid.active_sorters)|n},
|
||||
|
||||
## nb. whereas this will only contain multi-column sorters,
|
||||
## but will be *empty* for single-column sorting
|
||||
% if len(grid.active_sorters) > 1:
|
||||
sortingPriority: ${json.dumps(grid.active_sorters)|n},
|
||||
% else:
|
||||
sortingPriority: [],
|
||||
% endif
|
||||
|
||||
% endif
|
||||
|
||||
## filterable: ${json.dumps(grid.filterable)|n},
|
||||
filters: ${json.dumps(filters_data if getattr(grid, 'filterable', False) else None)|n},
|
||||
filtersSequence: ${json.dumps(filters_sequence if getattr(grid, 'filterable', False) else None)|n},
|
||||
filters: ${json.dumps(filters_data if grid.filterable else None)|n},
|
||||
filtersSequence: ${json.dumps(filters_sequence if grid.filterable else None)|n},
|
||||
addFilterTerm: '',
|
||||
addFilterShow: false,
|
||||
|
||||
## dummy input value needed for sharing links on *insecure* sites
|
||||
% if getattr(request, 'scheme', None) == 'http':
|
||||
% if request.scheme == 'http':
|
||||
shareLink: null,
|
||||
% endif
|
||||
}
|
||||
|
||||
let ${grid.vue_component} = {
|
||||
template: '#${grid.vue_tagname}-template',
|
||||
let ${grid.component_studly} = {
|
||||
template: '#${grid.component}-template',
|
||||
|
||||
mixins: [FormPosterMixin],
|
||||
|
||||
|
@ -386,32 +311,6 @@
|
|||
|
||||
computed: {
|
||||
|
||||
## TODO: this should be temporary? but anyway 'total' is
|
||||
## still referenced in other places, e.g. "delete results"
|
||||
% if grid.paginated:
|
||||
total() { return this.pagerStats.item_count },
|
||||
% endif
|
||||
|
||||
% if not grid.paginate_on_backend:
|
||||
|
||||
pagerStats() {
|
||||
const data = this.visibleData
|
||||
let last = this.currentPage * this.perPage
|
||||
let first = last - this.perPage + 1
|
||||
if (last > data.length) {
|
||||
last = data.length
|
||||
}
|
||||
return {
|
||||
'item_count': data.length,
|
||||
'items_per_page': this.perPage,
|
||||
'page': this.currentPage,
|
||||
'first_item': first,
|
||||
'last_item': last,
|
||||
}
|
||||
},
|
||||
|
||||
% endif
|
||||
|
||||
addFilterChoices() {
|
||||
// nb. this returns all choices available for "Add Filter" operation
|
||||
|
||||
|
@ -459,32 +358,21 @@
|
|||
|
||||
directLink() {
|
||||
let params = new URLSearchParams(this.getAllParams())
|
||||
return `${request.path_url}?${'$'}{params}`
|
||||
return `${request.current_route_url(_query=None)}?${'$'}{params}`
|
||||
},
|
||||
},
|
||||
|
||||
% if grid.sortable and grid.sort_multiple and grid.sort_on_backend:
|
||||
|
||||
## TODO: there is a bug (?) which prevents the arrow
|
||||
## from displaying for simple default single-column sort,
|
||||
## when multi-column sort is allowed for the table. for
|
||||
## now we work around that by waiting until mount to
|
||||
## enable the multi-column support. see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
mounted() {
|
||||
this.allowMultiSort = true
|
||||
},
|
||||
|
||||
% endif
|
||||
mounted() {
|
||||
## TODO: there is a bug (?) which prevents the arrow from
|
||||
## displaying for simple default single-column sort. so to
|
||||
## work around that, we *disable* multi-sort until the
|
||||
## component is mounted. seems to work for now..see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
this.allowMultiSort = true
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
renderNumber(value) {
|
||||
if (value != undefined) {
|
||||
return value.toLocaleString('en')
|
||||
}
|
||||
},
|
||||
|
||||
formatAddFilterItem(filtr) {
|
||||
if (!filtr.key) {
|
||||
filtr = this.filters[filtr]
|
||||
|
@ -492,7 +380,7 @@
|
|||
return filtr.label || filtr.key
|
||||
},
|
||||
|
||||
% if getattr(grid, 'click_handlers', None):
|
||||
% if grid.click_handlers:
|
||||
cellClick(row, column, rowIndex, columnIndex) {
|
||||
% for key in grid.click_handlers:
|
||||
if (column._props.field == '${key}') {
|
||||
|
@ -548,18 +436,17 @@
|
|||
},
|
||||
|
||||
getBasicParams() {
|
||||
const params = {
|
||||
% if grid.paginated and grid.paginate_on_backend:
|
||||
pagesize: this.perPage,
|
||||
page: this.currentPage,
|
||||
% endif
|
||||
}
|
||||
% if grid.sortable and grid.sort_on_backend:
|
||||
for (let i = 1; i <= this.sorters.length; i++) {
|
||||
params['sort'+i+'key'] = this.sorters[i-1].field
|
||||
params['sort'+i+'dir'] = this.sorters[i-1].order
|
||||
let params = {}
|
||||
% if grid.sortable:
|
||||
for (let i = 1; i <= this.backendSorters.length; i++) {
|
||||
params['sort'+i+'key'] = this.backendSorters[i-1].field
|
||||
params['sort'+i+'dir'] = this.backendSorters[i-1].order
|
||||
}
|
||||
% endif
|
||||
% if grid.pageable:
|
||||
params.pagesize = this.perPage
|
||||
params.page = this.currentPage
|
||||
% endif
|
||||
return params
|
||||
},
|
||||
|
||||
|
@ -583,17 +470,6 @@
|
|||
...this.getFilterParams()}
|
||||
},
|
||||
|
||||
## nb. this is meant to call for a grid which is hidden at
|
||||
## first, when it is first being shown to the user. and if
|
||||
## it was initialized with empty data set.
|
||||
async fetchFirstData() {
|
||||
if (this.fetchedFirstData) {
|
||||
return
|
||||
}
|
||||
await this.loadAsyncData()
|
||||
this.fetchedFirstData = true
|
||||
},
|
||||
|
||||
## TODO: i noticed buefy docs show using `async` keyword here,
|
||||
## so now i am too. knowing nothing at all of if/how this is
|
||||
## supposed to improve anything. we shall see i guess
|
||||
|
@ -610,23 +486,23 @@
|
|||
params = params.toString()
|
||||
|
||||
this.loading = true
|
||||
this.$http.get(`${'$'}{this.ajaxDataUrl}?${'$'}{params}`).then(response => {
|
||||
if (!response.data.error) {
|
||||
${grid.vue_component}CurrentData = response.data.data
|
||||
this.data = ${grid.vue_component}CurrentData
|
||||
% if grid.paginated and grid.paginate_on_backend:
|
||||
this.pagerStats = response.data.pager_stats
|
||||
% endif
|
||||
this.rowStatusMap = response.data.row_status_map || {}
|
||||
this.$http.get(`${'$'}{this.ajaxDataUrl}?${'$'}{params}`).then(({ data }) => {
|
||||
if (!data.error) {
|
||||
${grid.component_studly}CurrentData = data.data
|
||||
this.data = ${grid.component_studly}CurrentData
|
||||
this.rowStatusMap = data.row_status_map
|
||||
this.total = data.total_items
|
||||
this.firstItem = data.first_item
|
||||
this.lastItem = data.last_item
|
||||
this.loading = false
|
||||
this.savingDefaults = false
|
||||
this.checkedRows = this.locateCheckedRows(response.data.checked_rows || [])
|
||||
this.checkedRows = this.locateCheckedRows(data.checked_rows)
|
||||
if (success) {
|
||||
success()
|
||||
}
|
||||
} else {
|
||||
this.$buefy.toast.open({
|
||||
message: response.data.error,
|
||||
message: data.error,
|
||||
type: 'is-danger',
|
||||
duration: 2000, // 4 seconds
|
||||
})
|
||||
|
@ -638,11 +514,8 @@
|
|||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
${grid.vue_component}CurrentData = []
|
||||
this.data = []
|
||||
% if grid.paginated and grid.paginate_on_backend:
|
||||
this.pagerStats = {}
|
||||
% endif
|
||||
this.total = 0
|
||||
this.loading = false
|
||||
this.savingDefaults = false
|
||||
if (failure) {
|
||||
|
@ -681,72 +554,55 @@
|
|||
})
|
||||
},
|
||||
|
||||
% if grid.sortable and grid.sort_on_backend:
|
||||
onSort(field, order, event) {
|
||||
|
||||
onSort(field, order, event) {
|
||||
// nb. buefy passes field name, oruga passes object
|
||||
if (field.field) {
|
||||
field = field.field
|
||||
}
|
||||
|
||||
## nb. buefy passes field name; oruga passes field object
|
||||
% if request.use_oruga:
|
||||
field = field.field
|
||||
% endif
|
||||
if (event.ctrlKey) {
|
||||
|
||||
% if grid.sort_multiple:
|
||||
// engage or enhance multi-column sorting
|
||||
let sorter = this.backendSorters.filter(i => i.field === field)[0]
|
||||
if (sorter) {
|
||||
sorter.order = sorter.order === 'desc' ? 'asc' : 'desc'
|
||||
} else {
|
||||
this.backendSorters.push({field, order})
|
||||
}
|
||||
this.sortingPriority = this.backendSorters
|
||||
|
||||
// did user ctrl-click the column header?
|
||||
if (event.ctrlKey) {
|
||||
|
||||
// toggle direction for existing, or add new sorter
|
||||
const sorter = this.sorters.filter(s => s.field === field)[0]
|
||||
if (sorter) {
|
||||
sorter.order = sorter.order === 'desc' ? 'asc' : 'desc'
|
||||
} else {
|
||||
this.sorters.push({field, order})
|
||||
}
|
||||
|
||||
// apply multi-column sorting
|
||||
this.sortingPriority = this.sorters
|
||||
|
||||
} else {
|
||||
|
||||
% endif
|
||||
} else {
|
||||
|
||||
// sort by single column only
|
||||
this.sorters = [{field, order}]
|
||||
this.backendSorters = [{field, order}]
|
||||
this.sortingPriority = []
|
||||
}
|
||||
|
||||
% if grid.sort_multiple:
|
||||
// multi-column sort not engaged
|
||||
this.sortingPriority = []
|
||||
}
|
||||
% endif
|
||||
// always reset to first page when changing sort options
|
||||
// TODO: i mean..right? would we ever not want that?
|
||||
this.currentPage = 1
|
||||
this.loadAsyncData()
|
||||
},
|
||||
|
||||
// nb. always reset to first page when sorting changes
|
||||
this.currentPage = 1
|
||||
this.loadAsyncData()
|
||||
},
|
||||
sortingPriorityRemoved(field) {
|
||||
|
||||
% if grid.sort_multiple:
|
||||
// prune field from active sorters
|
||||
this.backendSorters = this.backendSorters.filter(
|
||||
(sorter) => sorter.field !== field)
|
||||
|
||||
sortingPriorityRemoved(field) {
|
||||
// nb. must keep active sorter list "as-is" even if
|
||||
// there is only one sorter; buefy seems to expect it
|
||||
this.sortingPriority = this.backendSorters
|
||||
|
||||
// prune from active sorters
|
||||
this.sorters = this.sorters.filter(s => s.field !== field)
|
||||
|
||||
// nb. even though we might have just one sorter
|
||||
// now, we are still technically in multi-sort mode
|
||||
this.sortingPriority = this.sorters
|
||||
|
||||
this.loadAsyncData()
|
||||
},
|
||||
|
||||
% endif
|
||||
|
||||
% endif
|
||||
this.loadAsyncData()
|
||||
},
|
||||
|
||||
resetView() {
|
||||
this.loading = true
|
||||
|
||||
// use current url proper, plus reset param
|
||||
let url = '?reset-view=true'
|
||||
let url = '?reset-to-default-filters=true'
|
||||
|
||||
// add current hash, to preserve that in redirect
|
||||
if (location.hash) {
|
||||
|
@ -920,7 +776,7 @@
|
|||
} else {
|
||||
this.checkedRows.push(row)
|
||||
}
|
||||
% if getattr(grid, 'check_handler', None):
|
||||
% if grid.check_handler:
|
||||
this.${grid.check_handler}(this.checkedRows, row)
|
||||
% endif
|
||||
},
|
||||
|
|
67
tailbone/templates/grids/filters.mako
Normal file
67
tailbone/templates/grids/filters.mako
Normal file
|
@ -0,0 +1,67 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
|
||||
<form action="${form.action_url}" method="GET" @submit.prevent="applyFilters()">
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<grid-filter v-for="key in filtersSequence"
|
||||
:key="key"
|
||||
:filter="filters[key]"
|
||||
ref="gridFilters">
|
||||
</grid-filter>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
|
||||
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
icon-pack="fas"
|
||||
icon-left="check">
|
||||
Apply Filters
|
||||
</b-button>
|
||||
|
||||
<b-button v-if="!addFilterShow"
|
||||
icon-pack="fas"
|
||||
icon-left="plus"
|
||||
@click="addFilterInit()">
|
||||
Add Filter
|
||||
</b-button>
|
||||
|
||||
<b-autocomplete v-if="addFilterShow"
|
||||
ref="addFilterAutocomplete"
|
||||
:data="addFilterChoices"
|
||||
v-model="addFilterTerm"
|
||||
placeholder="Add Filter"
|
||||
field="key"
|
||||
:custom-formatter="formatAddFilterItem"
|
||||
open-on-focus
|
||||
keep-first
|
||||
icon-pack="fas"
|
||||
clearable
|
||||
clear-on-select
|
||||
@select="addFilterSelect">
|
||||
</b-autocomplete>
|
||||
|
||||
<b-button @click="resetView()"
|
||||
icon-pack="fas"
|
||||
icon-left="home">
|
||||
Default View
|
||||
</b-button>
|
||||
|
||||
<b-button @click="clearFilters()"
|
||||
icon-pack="fas"
|
||||
icon-left="trash">
|
||||
No Filters
|
||||
</b-button>
|
||||
|
||||
% if allow_save_defaults and request.user:
|
||||
<b-button @click="saveDefaults()"
|
||||
icon-pack="fas"
|
||||
icon-left="save"
|
||||
:disabled="savingDefaults">
|
||||
{{ savingDefaults ? "Working, please wait..." : "Save Defaults" }}
|
||||
</b-button>
|
||||
% endif
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
|
@ -1,3 +0,0 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/grids/complete.mako" />
|
||||
${parent.body()}
|
|
@ -1,7 +1,33 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="wuttaweb:templates/home.mako" />
|
||||
<%inherit file="/page.mako" />
|
||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||
|
||||
<%def name="title()">Home</%def>
|
||||
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
<style type="text/css">
|
||||
.logo {
|
||||
text-align: center;
|
||||
}
|
||||
.logo img {
|
||||
margin: 3em auto;
|
||||
max-height: 350px;
|
||||
max-width: 800px;
|
||||
}
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_this_page()">
|
||||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
<div class="logo">
|
||||
${h.image(image_url, "{} logo".format(capture(base_meta.app_title)))}
|
||||
<h1>Welcome to ${base_meta.app_title()}</h1>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -144,9 +144,9 @@
|
|||
</b-modal>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.handlersData = ${json.dumps(handlers_data)|n}
|
||||
|
||||
|
@ -203,3 +203,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -63,26 +63,28 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}Data.submittingRun = false
|
||||
${form.vue_component}Data.submittingExplain = false
|
||||
${form.vue_component}Data.runJob = false
|
||||
${form.component_studly}Data.submittingRun = false
|
||||
${form.component_studly}Data.submittingExplain = false
|
||||
${form.component_studly}Data.runJob = false
|
||||
|
||||
${form.vue_component}.methods.submitRun = function() {
|
||||
${form.component_studly}.methods.submitRun = function() {
|
||||
this.submittingRun = true
|
||||
this.runJob = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.${form.vue_component}.submit()
|
||||
this.$refs.${form.component_studly}.submit()
|
||||
})
|
||||
}
|
||||
|
||||
${form.vue_component}.methods.submitExplain = function() {
|
||||
${form.component_studly}.methods.submitExplain = function() {
|
||||
this.submittingExplain = true
|
||||
this.$refs.${form.vue_component}.submit()
|
||||
this.$refs.${form.component_studly}.submit()
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,17 +1,86 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="wuttaweb:templates/auth/login.mako" />
|
||||
<%inherit file="/form.mako" />
|
||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||
|
||||
<%def name="title()">Login</%def>
|
||||
|
||||
## TODO: this will not be needed with wuttaform
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
<style>
|
||||
.card-content .buttons {
|
||||
<style type="text/css">
|
||||
.logo img {
|
||||
display: block;
|
||||
margin: 3rem auto;
|
||||
max-height: 350px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
/* must force a particular label with, in order to make sure */
|
||||
/* the username and password inputs are the same size */
|
||||
.field.is-horizontal .field-label .label {
|
||||
text-align: left;
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
justify-content: right;
|
||||
}
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="logo()">
|
||||
${h.image(image_url, "{} logo".format(capture(base_meta.app_title)))}
|
||||
</%def>
|
||||
|
||||
<%def name="login_form()">
|
||||
<div class="form">
|
||||
${form.render_deform(form_kwargs={'data-ajax': 'false'})|n}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page()">
|
||||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
<div class="logo">
|
||||
${self.logo()}
|
||||
</div>
|
||||
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-narrow">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<tailbone-form></tailbone-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.component_studly}Data.usernameInput = null
|
||||
|
||||
${form.component_studly}.mounted = function() {
|
||||
this.$refs.username.focus()
|
||||
this.usernameInput = this.$refs.username.$el.querySelector('input')
|
||||
this.usernameInput.addEventListener('keydown', this.usernameKeydown)
|
||||
}
|
||||
|
||||
${form.component_studly}.beforeDestroy = function() {
|
||||
this.usernameInput.removeEventListener('keydown', this.usernameKeydown)
|
||||
}
|
||||
|
||||
${form.component_studly}.methods.usernameKeydown = function(event) {
|
||||
if (event.which == 13) {
|
||||
event.preventDefault()
|
||||
this.$refs.password.focus()
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -297,9 +297,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.overnightTasks = ${json.dumps(overnight_tasks)|n}
|
||||
ThisPageData.overnightTaskShowDialog = false
|
||||
|
@ -425,3 +425,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -255,9 +255,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if master.has_perm('restart_scheduler'):
|
||||
|
||||
|
@ -374,3 +374,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -34,9 +34,9 @@
|
|||
${h.end_form()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
TailboneFormData.formSubmitting = false
|
||||
TailboneFormData.submitButtonText = "Yes, please clone away"
|
||||
|
@ -48,3 +48,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/form.mako" />
|
||||
|
||||
<%def name="title()">New ${model_title_plural if getattr(master, 'creates_multiple', False) else model_title}</%def>
|
||||
<%def name="title()">New ${model_title_plural if master.creates_multiple else model_title}</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -27,21 +27,26 @@
|
|||
<b-button type="is-primary is-danger"
|
||||
native-type="submit"
|
||||
:disabled="formSubmitting">
|
||||
{{ formSubmitting ? "Working, please wait..." : "${form.button_label_submit}" }}
|
||||
{{ formButtonText }}
|
||||
</b-button>
|
||||
</div>
|
||||
${h.end_form()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}Data.formSubmitting = false
|
||||
TailboneFormData.formSubmitting = false
|
||||
TailboneFormData.formButtonText = "Yes, please DELETE this data forever!"
|
||||
|
||||
${form.vue_component}.methods.submitForm = function() {
|
||||
TailboneForm.methods.submitForm = function() {
|
||||
this.formSubmitting = true
|
||||
this.formButtonText = "Working, please wait..."
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/form.mako" />
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
## declare extra data needed by form
|
||||
% if form is not Undefined and getattr(form, 'json_data', None):
|
||||
% if form is not Undefined:
|
||||
% for key, value in form.json_data.items():
|
||||
${form.vue_component}Data.${key} = ${json.dumps(value)|n}
|
||||
${form.component_studly}Data.${key} = ${json.dumps(value)|n}
|
||||
% endfor
|
||||
% endif
|
||||
|
||||
% if master.deletable and instance_deletable and master.has_perm('delete') and getattr(master, 'delete_confirm', 'full') == 'simple':
|
||||
% if master.deletable and instance_deletable and master.has_perm('delete') and master.delete_confirm == 'simple':
|
||||
|
||||
ThisPage.methods.deleteObject = function() {
|
||||
if (confirm("Are you sure you wish to delete this ${model_title}?")) {
|
||||
|
@ -23,8 +23,11 @@
|
|||
% endif
|
||||
</script>
|
||||
|
||||
% if form is not Undefined and hasattr(form, 'render_included_templates'):
|
||||
% if form is not Undefined:
|
||||
${form.render_included_templates()}
|
||||
% endif
|
||||
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<%def name="grid_tools()">
|
||||
|
||||
## grid totals
|
||||
% if getattr(master, 'supports_grid_totals', False):
|
||||
% if master.supports_grid_totals:
|
||||
<div style="display: flex; align-items: center;">
|
||||
<b-button v-if="gridTotalsDisplay == null"
|
||||
:disabled="gridTotalsFetching"
|
||||
|
@ -30,7 +30,7 @@
|
|||
% endif
|
||||
|
||||
## download search results
|
||||
% if getattr(master, 'results_downloadable', False) and master.has_perm('download_results'):
|
||||
% if master.results_downloadable and master.has_perm('download_results'):
|
||||
<div>
|
||||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
|
@ -180,7 +180,7 @@
|
|||
% endif
|
||||
|
||||
## download rows for search results
|
||||
% if getattr(master, 'has_rows', False) and master.results_rows_downloadable and master.has_perm('download_results_rows'):
|
||||
% if master.has_rows and master.results_rows_downloadable and master.has_perm('download_results_rows'):
|
||||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
icon-left="download"
|
||||
|
@ -194,7 +194,7 @@
|
|||
% endif
|
||||
|
||||
## merge 2 objects
|
||||
% if getattr(master, 'mergeable', False) and request.has_perm('{}.merge'.format(permission_prefix)):
|
||||
% if master.mergeable and request.has_perm('{}.merge'.format(permission_prefix)):
|
||||
|
||||
${h.form(url('{}.merge'.format(route_prefix)), class_='control', **{'@submit': 'submitMergeForm'})}
|
||||
${h.csrf_token(request)}
|
||||
|
@ -212,7 +212,7 @@
|
|||
% endif
|
||||
|
||||
## enable / disable selected objects
|
||||
% if getattr(master, 'supports_set_enabled_toggle', False) and master.has_perm('enable_disable_set'):
|
||||
% if master.supports_set_enabled_toggle and master.has_perm('enable_disable_set'):
|
||||
|
||||
${h.form(url('{}.enable_set'.format(route_prefix)), class_='control', ref='enable_selected_form')}
|
||||
${h.csrf_token(request)}
|
||||
|
@ -234,7 +234,7 @@
|
|||
% endif
|
||||
|
||||
## delete selected objects
|
||||
% if getattr(master, 'set_deletable', False) and master.has_perm('delete_set'):
|
||||
% if master.set_deletable and master.has_perm('delete_set'):
|
||||
${h.form(url('{}.delete_set'.format(route_prefix)), ref='delete_selected_form', class_='control')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('uuids', v_model='selected_uuids')}
|
||||
|
@ -249,7 +249,7 @@
|
|||
% endif
|
||||
|
||||
## delete search results
|
||||
% if getattr(master, 'bulk_deletable', False) and request.has_perm('{}.bulk_delete'.format(permission_prefix)):
|
||||
% if master.bulk_deletable and request.has_perm('{}.bulk_delete'.format(permission_prefix)):
|
||||
${h.form(url('{}.bulk_delete'.format(route_prefix)), ref='delete_results_form', class_='control')}
|
||||
${h.csrf_token(request)}
|
||||
<b-button type="is-danger"
|
||||
|
@ -265,11 +265,6 @@
|
|||
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_this_page()">
|
||||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
|
||||
% if download_results_path:
|
||||
|
@ -288,42 +283,56 @@
|
|||
|
||||
${self.render_grid_component()}
|
||||
|
||||
% if master.deletable and master.has_perm('delete') and getattr(master, 'delete_confirm', 'full') == 'simple':
|
||||
% if master.deletable and request.has_perm('{}.delete'.format(permission_prefix)) and master.delete_confirm == 'simple':
|
||||
${h.form('#', ref='deleteObjectForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.end_form()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_grid_component()">
|
||||
${grid.render_vue_tag()}
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## vue components
|
||||
##############################
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.make_grid_component()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="make_grid_component()">
|
||||
${grid.render_vue_template(tools=capture(self.grid_tools).strip(), context_menu=capture(self.context_menu_items).strip())}
|
||||
## TODO: stop using |n filter?
|
||||
${grid.render_complete(tools=capture(self.grid_tools).strip(), context_menu=capture(self.context_menu_items).strip())|n}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="render_grid_component()">
|
||||
<${grid.component} ref="grid" :csrftoken="csrftoken"
|
||||
% if master.deletable and request.has_perm('{}.delete'.format(permission_prefix)) and master.delete_confirm == 'simple':
|
||||
@deleteActionClicked="deleteObject"
|
||||
% endif
|
||||
>
|
||||
</${grid.component}>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
|
||||
## define grid
|
||||
${self.make_grid_component()}
|
||||
|
||||
${parent.make_this_page_component()}
|
||||
|
||||
## finalize grid
|
||||
<script>
|
||||
|
||||
${grid.component_studly}.data = () => { return ${grid.component_studly}Data }
|
||||
Vue.component('${grid.component}', ${grid.component_studly})
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page()">
|
||||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if getattr(master, 'supports_grid_totals', False):
|
||||
${grid.vue_component}Data.gridTotalsDisplay = null
|
||||
${grid.vue_component}Data.gridTotalsFetching = false
|
||||
% if master.supports_grid_totals:
|
||||
${grid.component_studly}Data.gridTotalsDisplay = null
|
||||
${grid.component_studly}Data.gridTotalsFetching = false
|
||||
|
||||
${grid.vue_component}.methods.gridTotalsFetch = function() {
|
||||
${grid.component_studly}.methods.gridTotalsFetch = function() {
|
||||
this.gridTotalsFetching = true
|
||||
|
||||
let url = '${url(f'{route_prefix}.fetch_grid_totals')}'
|
||||
|
@ -335,7 +344,7 @@
|
|||
})
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.appliedFiltersHook = function() {
|
||||
${grid.component_studly}.methods.appliedFiltersHook = function() {
|
||||
this.gridTotalsDisplay = null
|
||||
this.gridTotalsFetching = false
|
||||
}
|
||||
|
@ -379,7 +388,7 @@
|
|||
% endif
|
||||
|
||||
## delete single object
|
||||
% if master.deletable and master.has_perm('delete') and getattr(master, 'delete_confirm', 'full') == 'simple':
|
||||
% if master.deletable and master.has_perm('delete') and master.delete_confirm == 'simple':
|
||||
ThisPage.methods.deleteObject = function(url) {
|
||||
if (confirm("Are you sure you wish to delete this ${model_title}?")) {
|
||||
let form = this.$refs.deleteObjectForm
|
||||
|
@ -390,19 +399,19 @@
|
|||
% endif
|
||||
|
||||
## download results
|
||||
% if getattr(master, 'results_downloadable', False) and master.has_perm('download_results'):
|
||||
% if master.results_downloadable and master.has_perm('download_results'):
|
||||
|
||||
${grid.vue_component}Data.downloadResultsFormat = '${master.download_results_default_format()}'
|
||||
${grid.vue_component}Data.showDownloadResultsDialog = false
|
||||
${grid.vue_component}Data.downloadResultsFieldsMode = 'default'
|
||||
${grid.vue_component}Data.downloadResultsFieldsAvailable = ${json.dumps(download_results_fields_available)|n}
|
||||
${grid.vue_component}Data.downloadResultsFieldsDefault = ${json.dumps(download_results_fields_default)|n}
|
||||
${grid.vue_component}Data.downloadResultsFieldsIncluded = ${json.dumps(download_results_fields_default)|n}
|
||||
${grid.component_studly}Data.downloadResultsFormat = '${master.download_results_default_format()}'
|
||||
${grid.component_studly}Data.showDownloadResultsDialog = false
|
||||
${grid.component_studly}Data.downloadResultsFieldsMode = 'default'
|
||||
${grid.component_studly}Data.downloadResultsFieldsAvailable = ${json.dumps(download_results_fields_available)|n}
|
||||
${grid.component_studly}Data.downloadResultsFieldsDefault = ${json.dumps(download_results_fields_default)|n}
|
||||
${grid.component_studly}Data.downloadResultsFieldsIncluded = ${json.dumps(download_results_fields_default)|n}
|
||||
|
||||
${grid.vue_component}Data.downloadResultsExcludedFieldsSelected = []
|
||||
${grid.vue_component}Data.downloadResultsIncludedFieldsSelected = []
|
||||
${grid.component_studly}Data.downloadResultsExcludedFieldsSelected = []
|
||||
${grid.component_studly}Data.downloadResultsIncludedFieldsSelected = []
|
||||
|
||||
${grid.vue_component}.computed.downloadResultsFieldsExcluded = function() {
|
||||
${grid.component_studly}.computed.downloadResultsFieldsExcluded = function() {
|
||||
let excluded = []
|
||||
this.downloadResultsFieldsAvailable.forEach(field => {
|
||||
if (!this.downloadResultsFieldsIncluded.includes(field)) {
|
||||
|
@ -412,7 +421,7 @@
|
|||
return excluded
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.downloadResultsExcludeFields = function() {
|
||||
${grid.component_studly}.methods.downloadResultsExcludeFields = function() {
|
||||
const selected = Array.from(this.downloadResultsIncludedFieldsSelected)
|
||||
if (!selected) {
|
||||
return
|
||||
|
@ -436,7 +445,7 @@
|
|||
})
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.downloadResultsIncludeFields = function() {
|
||||
${grid.component_studly}.methods.downloadResultsIncludeFields = function() {
|
||||
const selected = Array.from(this.downloadResultsExcludedFieldsSelected)
|
||||
if (!selected) {
|
||||
return
|
||||
|
@ -457,28 +466,28 @@
|
|||
})
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.downloadResultsUseDefaultFields = function() {
|
||||
${grid.component_studly}.methods.downloadResultsUseDefaultFields = function() {
|
||||
this.downloadResultsFieldsIncluded = Array.from(this.downloadResultsFieldsDefault)
|
||||
this.downloadResultsFieldsMode = 'default'
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.downloadResultsUseAllFields = function() {
|
||||
${grid.component_studly}.methods.downloadResultsUseAllFields = function() {
|
||||
this.downloadResultsFieldsIncluded = Array.from(this.downloadResultsFieldsAvailable)
|
||||
this.downloadResultsFieldsMode = 'all'
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.downloadResultsSubmit = function() {
|
||||
${grid.component_studly}.methods.downloadResultsSubmit = function() {
|
||||
this.$refs.download_results_form.submit()
|
||||
}
|
||||
% endif
|
||||
|
||||
## download rows for results
|
||||
% if getattr(master, 'has_rows', False) and master.results_rows_downloadable and master.has_perm('download_results_rows'):
|
||||
% if master.has_rows and master.results_rows_downloadable and master.has_perm('download_results_rows'):
|
||||
|
||||
${grid.vue_component}Data.downloadResultsRowsButtonDisabled = false
|
||||
${grid.vue_component}Data.downloadResultsRowsButtonText = "Download Rows for Results"
|
||||
${grid.component_studly}Data.downloadResultsRowsButtonDisabled = false
|
||||
${grid.component_studly}Data.downloadResultsRowsButtonText = "Download Rows for Results"
|
||||
|
||||
${grid.vue_component}.methods.downloadResultsRows = function() {
|
||||
${grid.component_studly}.methods.downloadResultsRows = function() {
|
||||
if (confirm("This will generate an Excel file which contains "
|
||||
+ "not the results themselves, but the *rows* for "
|
||||
+ "each.\n\nAre you sure you want this?")) {
|
||||
|
@ -490,12 +499,12 @@
|
|||
% endif
|
||||
|
||||
## enable / disable selected objects
|
||||
% if getattr(master, 'supports_set_enabled_toggle', False) and master.has_perm('enable_disable_set'):
|
||||
% if master.supports_set_enabled_toggle and master.has_perm('enable_disable_set'):
|
||||
|
||||
${grid.vue_component}Data.enableSelectedSubmitting = false
|
||||
${grid.vue_component}Data.enableSelectedText = "Enable Selected"
|
||||
${grid.component_studly}Data.enableSelectedSubmitting = false
|
||||
${grid.component_studly}Data.enableSelectedText = "Enable Selected"
|
||||
|
||||
${grid.vue_component}.computed.enableSelectedDisabled = function() {
|
||||
${grid.component_studly}.computed.enableSelectedDisabled = function() {
|
||||
if (this.enableSelectedSubmitting) {
|
||||
return true
|
||||
}
|
||||
|
@ -505,7 +514,7 @@
|
|||
return false
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.enableSelectedSubmit = function() {
|
||||
${grid.component_studly}.methods.enableSelectedSubmit = function() {
|
||||
let uuids = this.checkedRowUUIDs()
|
||||
if (!uuids.length) {
|
||||
alert("You must first select one or more objects to disable.")
|
||||
|
@ -520,10 +529,10 @@
|
|||
this.$refs.enable_selected_form.submit()
|
||||
}
|
||||
|
||||
${grid.vue_component}Data.disableSelectedSubmitting = false
|
||||
${grid.vue_component}Data.disableSelectedText = "Disable Selected"
|
||||
${grid.component_studly}Data.disableSelectedSubmitting = false
|
||||
${grid.component_studly}Data.disableSelectedText = "Disable Selected"
|
||||
|
||||
${grid.vue_component}.computed.disableSelectedDisabled = function() {
|
||||
${grid.component_studly}.computed.disableSelectedDisabled = function() {
|
||||
if (this.disableSelectedSubmitting) {
|
||||
return true
|
||||
}
|
||||
|
@ -533,7 +542,7 @@
|
|||
return false
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.disableSelectedSubmit = function() {
|
||||
${grid.component_studly}.methods.disableSelectedSubmit = function() {
|
||||
let uuids = this.checkedRowUUIDs()
|
||||
if (!uuids.length) {
|
||||
alert("You must first select one or more objects to disable.")
|
||||
|
@ -551,12 +560,12 @@
|
|||
% endif
|
||||
|
||||
## delete selected objects
|
||||
% if getattr(master, 'set_deletable', False) and master.has_perm('delete_set'):
|
||||
% if master.set_deletable and master.has_perm('delete_set'):
|
||||
|
||||
${grid.vue_component}Data.deleteSelectedSubmitting = false
|
||||
${grid.vue_component}Data.deleteSelectedText = "Delete Selected"
|
||||
${grid.component_studly}Data.deleteSelectedSubmitting = false
|
||||
${grid.component_studly}Data.deleteSelectedText = "Delete Selected"
|
||||
|
||||
${grid.vue_component}.computed.deleteSelectedDisabled = function() {
|
||||
${grid.component_studly}.computed.deleteSelectedDisabled = function() {
|
||||
if (this.deleteSelectedSubmitting) {
|
||||
return true
|
||||
}
|
||||
|
@ -566,7 +575,7 @@
|
|||
return false
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.deleteSelectedSubmit = function() {
|
||||
${grid.component_studly}.methods.deleteSelectedSubmit = function() {
|
||||
let uuids = this.checkedRowUUIDs()
|
||||
if (!uuids.length) {
|
||||
alert("You must first select one or more objects to disable.")
|
||||
|
@ -582,12 +591,12 @@
|
|||
}
|
||||
% endif
|
||||
|
||||
% if getattr(master, 'bulk_deletable', False) and master.has_perm('bulk_delete'):
|
||||
% if master.bulk_deletable and master.has_perm('bulk_delete'):
|
||||
|
||||
${grid.vue_component}Data.deleteResultsSubmitting = false
|
||||
${grid.vue_component}Data.deleteResultsText = "Delete Results"
|
||||
${grid.component_studly}Data.deleteResultsSubmitting = false
|
||||
${grid.component_studly}Data.deleteResultsText = "Delete Results"
|
||||
|
||||
${grid.vue_component}.computed.deleteResultsDisabled = function() {
|
||||
${grid.component_studly}.computed.deleteResultsDisabled = function() {
|
||||
if (this.deleteResultsSubmitting) {
|
||||
return true
|
||||
}
|
||||
|
@ -597,7 +606,7 @@
|
|||
return false
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.deleteResultsSubmit = function() {
|
||||
${grid.component_studly}.methods.deleteResultsSubmit = function() {
|
||||
// TODO: show "plural model title" here?
|
||||
if (!confirm("You are about to delete " + this.total.toLocaleString('en') + " objects.\n\nAre you sure?")) {
|
||||
return
|
||||
|
@ -610,12 +619,12 @@
|
|||
|
||||
% endif
|
||||
|
||||
% if getattr(master, 'mergeable', False) and master.has_perm('merge'):
|
||||
% if master.mergeable and master.has_perm('merge'):
|
||||
|
||||
${grid.vue_component}Data.mergeFormButtonText = "Merge 2 ${model_title_plural}"
|
||||
${grid.vue_component}Data.mergeFormSubmitting = false
|
||||
${grid.component_studly}Data.mergeFormButtonText = "Merge 2 ${model_title_plural}"
|
||||
${grid.component_studly}Data.mergeFormSubmitting = false
|
||||
|
||||
${grid.vue_component}.methods.submitMergeForm = function() {
|
||||
${grid.component_studly}.methods.submitMergeForm = function() {
|
||||
this.mergeFormSubmitting = true
|
||||
this.mergeFormButtonText = "Working, please wait..."
|
||||
}
|
||||
|
@ -623,10 +632,5 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
${grid.vue_component}.data = function() { return ${grid.vue_component}Data }
|
||||
Vue.component('${grid.vue_tagname}', ${grid.vue_component})
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -109,8 +109,8 @@
|
|||
<merge-buttons></merge-buttons>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
|
||||
<script type="text/x-template" id="merge-buttons-template">
|
||||
<div class="level" style="margin-top: 2em;">
|
||||
|
@ -147,7 +147,11 @@
|
|||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
const MergeButtons = {
|
||||
template: '#merge-buttons-template',
|
||||
|
@ -171,13 +175,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
Vue.component('merge-buttons', MergeButtons)
|
||||
|
||||
<% request.register_component('merge-buttons', 'MergeButtons') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
Vue.component('merge-buttons', MergeButtons)
|
||||
<% request.register_component('merge-buttons', 'MergeButtons') %>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -16,16 +16,27 @@
|
|||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
TailboneGrid.data = function() { return TailboneGridData }
|
||||
|
||||
Vue.component('tailbone-grid', TailboneGrid)
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
|
||||
## TODO: stop using |n filter
|
||||
${grid.render_complete()|n}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
${grid.render_vue_tag(**{':csrftoken': 'csrftoken'})}
|
||||
<tailbone-grid :csrftoken="csrftoken">
|
||||
</tailbone-grid>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${grid.render_vue_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
${grid.render_vue_finalize()}
|
||||
</%def>
|
||||
${parent.body()}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</%def>
|
||||
|
||||
<%def name="render_instance_header_title_extras()">
|
||||
% if getattr(master, 'touchable', False) and master.has_perm('touch'):
|
||||
% if master.touchable and master.has_perm('touch'):
|
||||
<b-button title=""Touch" this record to trigger sync"
|
||||
@click="touchRecord()"
|
||||
:disabled="touchSubmitting">
|
||||
|
@ -93,7 +93,7 @@
|
|||
${parent.render_this_page()}
|
||||
|
||||
## render row grid
|
||||
% if getattr(master, 'has_rows', False):
|
||||
% if master.has_rows:
|
||||
<br />
|
||||
% if rows_title:
|
||||
<h4 class="block is-size-4">${rows_title}</h4>
|
||||
|
@ -120,7 +120,9 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
${versions_grid.render_vue_tag(ref='versionsGrid', **{'@view-revision': 'viewRevision'})}
|
||||
<versions-grid ref="versionsGrid"
|
||||
@view-revision="viewRevision">
|
||||
</versions-grid>
|
||||
|
||||
<${b}-modal :width="1200"
|
||||
% if request.use_oruga:
|
||||
|
@ -196,7 +198,6 @@
|
|||
|
||||
<p class="block has-text-weight-bold">
|
||||
{{ version.model_title }}
|
||||
({{ version.operation }})
|
||||
</p>
|
||||
|
||||
<table class="diff monospace is-size-7"
|
||||
|
@ -236,37 +237,25 @@
|
|||
</%def>
|
||||
|
||||
<%def name="render_row_grid_component()">
|
||||
${rows_grid.render_vue_tag(id='rowGrid', ref='rowGrid')}
|
||||
<tailbone-grid ref="rowGrid" id="rowGrid"></tailbone-grid>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if getattr(master, 'has_rows', False):
|
||||
${rows_grid.render_vue_template(allow_save_defaults=False, tools=capture(self.render_row_grid_tools))}
|
||||
<%def name="render_this_page_template()">
|
||||
% if master.has_rows:
|
||||
## TODO: stop using |n filter
|
||||
${rows_grid.render_complete(allow_save_defaults=False, tools=capture(self.render_row_grid_tools))|n}
|
||||
% endif
|
||||
${parent.render_this_page_template()}
|
||||
% if expose_versions:
|
||||
${versions_grid.render_vue_template()}
|
||||
${versions_grid.render_complete()|n}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if expose_versions:
|
||||
<script type="text/javascript">
|
||||
|
||||
% if getattr(master, 'touchable', False) and master.has_perm('touch'):
|
||||
|
||||
WholePageData.touchSubmitting = false
|
||||
|
||||
WholePage.methods.touchRecord = function() {
|
||||
this.touchSubmitting = true
|
||||
location.href = '${master.get_action_url('touch', instance)}'
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
% if expose_versions:
|
||||
|
||||
WholePageData.viewingHistory = false
|
||||
ThisPage.props.viewingHistory = Boolean
|
||||
|
||||
ThisPageData.gettingRevisions = false
|
||||
|
@ -321,16 +310,48 @@
|
|||
this.viewVersionShowAllFields = !this.viewVersionShowAllFields
|
||||
}
|
||||
|
||||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_whole_page_vars()">
|
||||
${parent.modify_whole_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if master.touchable and master.has_perm('touch'):
|
||||
|
||||
WholePageData.touchSubmitting = false
|
||||
|
||||
WholePage.methods.touchRecord = function() {
|
||||
this.touchSubmitting = true
|
||||
location.href = '${master.get_action_url('touch', instance)}'
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
% if expose_versions:
|
||||
WholePageData.viewingHistory = false
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
% if getattr(master, 'has_rows', False):
|
||||
${rows_grid.render_vue_finalize()}
|
||||
% endif
|
||||
% if expose_versions:
|
||||
${versions_grid.render_vue_finalize()}
|
||||
% endif
|
||||
<%def name="finalize_this_page_vars()">
|
||||
${parent.finalize_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if master.has_rows:
|
||||
TailboneGrid.data = function() { return TailboneGridData }
|
||||
Vue.component('tailbone-grid', TailboneGrid)
|
||||
% endif
|
||||
|
||||
% if expose_versions:
|
||||
VersionsGrid.data = function() { return VersionsGridData }
|
||||
Vue.component('versions-grid', VersionsGrid)
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -52,9 +52,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPage.methods.getLabelForKey = function(key) {
|
||||
switch (key) {
|
||||
|
@ -75,3 +75,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -32,14 +32,14 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
${message_recipients_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
TailboneFormData.possibleRecipients = new Map(${json.dumps(available_recipients)|n})
|
||||
TailboneFormData.recipientDisplayMap = ${json.dumps(recipient_display_map)|n}
|
||||
|
@ -59,3 +59,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -22,15 +22,15 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if request.matched_route.name in ('messages.inbox', 'messages.archive'):
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
|
||||
${grid.vue_component}Data.moveMessagesSubmitting = false
|
||||
${grid.vue_component}Data.moveMessagesText = null
|
||||
TailboneGridData.moveMessagesSubmitting = false
|
||||
TailboneGridData.moveMessagesText = null
|
||||
|
||||
${grid.vue_component}.computed.moveMessagesTextCurrent = function() {
|
||||
TailboneGrid.computed.moveMessagesTextCurrent = function() {
|
||||
if (this.moveMessagesText) {
|
||||
return this.moveMessagesText
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
|||
return "Move " + count.toString() + " selected to ${'Archive' if request.matched_route.name == 'messages.inbox' else 'Inbox'}"
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.moveMessagesSubmit = function() {
|
||||
TailboneGrid.methods.moveMessagesSubmit = function() {
|
||||
this.moveMessagesSubmitting = true
|
||||
this.moveMessagesText = "Working, please wait..."
|
||||
}
|
||||
|
@ -46,3 +46,6 @@
|
|||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -82,19 +82,22 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}Data.showingAllRecipients = false
|
||||
TailboneFormData.showingAllRecipients = false
|
||||
|
||||
${form.vue_component}.methods.showMoreRecipients = function() {
|
||||
TailboneForm.methods.showMoreRecipients = function() {
|
||||
this.showingAllRecipients = true
|
||||
}
|
||||
|
||||
${form.vue_component}.methods.hideMoreRecipients = function() {
|
||||
TailboneForm.methods.hideMoreRecipients = function() {
|
||||
this.showingAllRecipients = false
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/configure.mako" />
|
||||
|
||||
<%def name="form_content()">
|
||||
|
||||
<h3 class="block is-size-3">Workflows</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<p class="block">
|
||||
Users can only choose from the workflows enabled below.
|
||||
</p>
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.batch.purchase.allow_ordering_from_scratch"
|
||||
v-model="simpleSettings['rattail.batch.purchase.allow_ordering_from_scratch']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
From Scratch
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.batch.purchase.allow_ordering_from_file"
|
||||
v-model="simpleSettings['rattail.batch.purchase.allow_ordering_from_file']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
From Order File
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Vendors</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field message="If not set, user must choose a "supported" vendor.">
|
||||
<b-checkbox name="rattail.batch.purchase.allow_ordering_any_vendor"
|
||||
v-model="simpleSettings['rattail.batch.purchase.allow_ordering_any_vendor']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Allow ordering for <span class="has-text-weight-bold">any</span> vendor
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Order Parsers</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<p class="block">
|
||||
Only the selected file parsers will be exposed to users.
|
||||
</p>
|
||||
|
||||
% for Parser in order_parsers:
|
||||
<b-field message="${Parser.key}">
|
||||
<b-checkbox name="order_parser_${Parser.key}"
|
||||
v-model="orderParsers['${Parser.key}']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
${Parser.title}
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
% endfor
|
||||
|
||||
</div>
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.orderParsers = ${json.dumps(order_parsers_data)|n}
|
||||
</script>
|
||||
</%def>
|
|
@ -21,8 +21,8 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
% if not batch.executed and not batch.complete and master.has_perm('edit_row'):
|
||||
<script type="text/x-template" id="ordering-scanner-template">
|
||||
<div>
|
||||
|
@ -185,10 +185,10 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if not batch.executed and not batch.complete and master.has_perm('edit_row'):
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
|
||||
let OrderingScanner = {
|
||||
template: '#ordering-scanner-template',
|
||||
|
@ -204,7 +204,7 @@
|
|||
saving: false,
|
||||
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(h.get_csrf_token(request))|n},
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -408,11 +408,16 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
% if not batch.executed and not batch.complete and master.has_perm('edit_row'):
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
|
||||
Vue.component('ordering-scanner', OrderingScanner)
|
||||
|
||||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -199,8 +199,9 @@
|
|||
<ordering-worksheet></ordering-worksheet>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
|
||||
<script type="text/x-template" id="ordering-worksheet-template">
|
||||
<div>
|
||||
<div class="form-wrapper">
|
||||
|
@ -238,7 +239,11 @@
|
|||
${self.order_form_grid()}
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
const OrderingWorksheet = {
|
||||
template: '#ordering-worksheet-template',
|
||||
|
@ -250,7 +255,7 @@
|
|||
submitting: false,
|
||||
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(h.get_csrf_token(request))|n},
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -293,12 +298,14 @@
|
|||
},
|
||||
}
|
||||
|
||||
Vue.component('ordering-worksheet', OrderingWorksheet)
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
Vue.component('ordering-worksheet', OrderingWorksheet)
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## page body
|
||||
##############################
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,26 +1,42 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/base.mako" />
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${self.render_vue_template_this_page()}
|
||||
<%def name="context_menu_items()">
|
||||
% if context_menu_list_items is not Undefined:
|
||||
% for item in context_menu_list_items:
|
||||
<li>${item}</li>
|
||||
% endfor
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_template_this_page()">
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.render_this_page_template()}
|
||||
<%def name="page_content()"></%def>
|
||||
|
||||
<%def name="render_this_page()">
|
||||
<div style="display: flex;">
|
||||
|
||||
<div class="this-page-content" style="flex-grow: 1;">
|
||||
${self.page_content()}
|
||||
</div>
|
||||
|
||||
<ul id="context-menu">
|
||||
${self.context_menu_items()}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
<script type="text/x-template" id="this-page-template">
|
||||
<div>
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.render_this_page()}
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
</%def>
|
||||
|
||||
const ThisPage = {
|
||||
<%def name="declare_this_page_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
let ThisPage = {
|
||||
template: '#this-page-template',
|
||||
mixins: [SimpleRequestMixin],
|
||||
props: {
|
||||
|
@ -36,71 +52,37 @@
|
|||
},
|
||||
}
|
||||
|
||||
const ThisPageData = {
|
||||
let ThisPageData = {
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(h.get_csrf_token(request))|n},
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_this_page()">
|
||||
<div style="display: flex;">
|
||||
|
||||
<div class="this-page-content" style="flex-grow: 1;">
|
||||
${self.page_content()}
|
||||
</div>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<ul id="context-menu">
|
||||
${self.context_menu_items()}
|
||||
</ul>
|
||||
</div>
|
||||
<%def name="modify_this_page_vars()">
|
||||
## NOTE: if you override this, must use <script> tags
|
||||
</%def>
|
||||
|
||||
## nb. this is the canonical block for page content!
|
||||
<%def name="page_content()"></%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="context_menu_items()">
|
||||
% if context_menu_list_items is not Undefined:
|
||||
% for item in context_menu_list_items:
|
||||
<li>${item}</li>
|
||||
% endfor
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.declare_this_page_vars()}
|
||||
${self.modify_this_page_vars()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.make_this_page_component()}
|
||||
<%def name="finalize_this_page_vars()">
|
||||
## NOTE: if you override this, must use <script> tags
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${self.declare_this_page_vars()}
|
||||
${self.modify_this_page_vars()}
|
||||
${self.finalize_this_page_vars()}
|
||||
<script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPage.data = function() { return ThisPageData }
|
||||
|
||||
Vue.component('this-page', ThisPage)
|
||||
<% request.register_component('this-page', 'ThisPage') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## DEPRECATED
|
||||
##############################
|
||||
|
||||
<%def name="declare_this_page_vars()"></%def>
|
||||
|
||||
<%def name="modify_this_page_vars()"></%def>
|
||||
|
||||
<%def name="finalize_this_page_vars()"></%def>
|
||||
${self.render_this_page_template()}
|
||||
${self.make_this_page_component()}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<%def name="grid_tools()">
|
||||
|
||||
% if getattr(master, 'mergeable', False) and master.has_perm('request_merge'):
|
||||
% if master.mergeable and master.has_perm('request_merge'):
|
||||
<b-button @click="showMergeRequest()"
|
||||
icon-pack="fas"
|
||||
icon-left="object-ungroup"
|
||||
|
@ -61,37 +61,37 @@
|
|||
${parent.grid_tools()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if getattr(master, 'mergeable', False) and master.has_perm('request_merge'):
|
||||
% if master.mergeable and master.has_perm('request_merge'):
|
||||
|
||||
${grid.vue_component}Data.mergeRequestShowDialog = false
|
||||
${grid.vue_component}Data.mergeRequestRows = []
|
||||
${grid.vue_component}Data.mergeRequestSubmitText = "Submit Merge Request"
|
||||
${grid.vue_component}Data.mergeRequestSubmitting = false
|
||||
${grid.component_studly}Data.mergeRequestShowDialog = false
|
||||
${grid.component_studly}Data.mergeRequestRows = []
|
||||
${grid.component_studly}Data.mergeRequestSubmitText = "Submit Merge Request"
|
||||
${grid.component_studly}Data.mergeRequestSubmitting = false
|
||||
|
||||
${grid.vue_component}.computed.mergeRequestRemovingUUID = function() {
|
||||
${grid.component_studly}.computed.mergeRequestRemovingUUID = function() {
|
||||
if (this.mergeRequestRows.length) {
|
||||
return this.mergeRequestRows[0].uuid
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
${grid.vue_component}.computed.mergeRequestKeepingUUID = function() {
|
||||
${grid.component_studly}.computed.mergeRequestKeepingUUID = function() {
|
||||
if (this.mergeRequestRows.length) {
|
||||
return this.mergeRequestRows[1].uuid
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.showMergeRequest = function() {
|
||||
${grid.component_studly}.methods.showMergeRequest = function() {
|
||||
this.mergeRequestRows = this.checkedRows
|
||||
this.mergeRequestShowDialog = true
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.submitMergeRequest = function() {
|
||||
${grid.component_studly}.methods.submitMergeRequest = function() {
|
||||
this.mergeRequestSubmitting = true
|
||||
this.mergeRequestSubmitText = "Working, please wait..."
|
||||
}
|
||||
|
@ -100,3 +100,5 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if not instance.merged and request.has_perm('people.merge'):
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.mergeFormButtonText = "Perform Merge"
|
||||
ThisPageData.mergeFormSubmitting = false
|
||||
|
@ -34,3 +34,5 @@
|
|||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -2,16 +2,6 @@
|
|||
<%inherit file="/master/view.mako" />
|
||||
<%namespace file="/util.mako" import="view_profiles_helper" />
|
||||
|
||||
<%def name="page_content()">
|
||||
${parent.page_content()}
|
||||
% if not instance.users and request.has_perm('users.create'):
|
||||
${h.form(url('people.make_user'), ref='makeUserForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('person_uuid', value=instance.uuid)}
|
||||
${h.end_form()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="object_helpers()">
|
||||
${parent.object_helpers()}
|
||||
${view_profiles_helper([instance])}
|
||||
|
@ -19,15 +9,15 @@
|
|||
|
||||
<%def name="render_form()">
|
||||
<div class="form">
|
||||
<${form.vue_tagname} v-on:make-user="makeUser"></${form.vue_tagname}>
|
||||
<tailbone-form v-on:make-user="makeUser"></tailbone-form>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}.methods.clickMakeUser = function(event) {
|
||||
TailboneForm.methods.clickMakeUser = function(event) {
|
||||
this.$emit('make-user')
|
||||
}
|
||||
|
||||
|
@ -39,3 +29,17 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
${parent.page_content()}
|
||||
% if not instance.users and request.has_perm('users.create'):
|
||||
${h.form(url('people.make_user'), ref='makeUserForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('person_uuid', value=instance.uuid)}
|
||||
${h.end_form()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</%def>
|
||||
|
||||
<%def name="content_title()">
|
||||
${dynamic_content_title or str(instance)}
|
||||
${dynamic_content_title}
|
||||
</%def>
|
||||
|
||||
<%def name="render_instance_header_title_extras()">
|
||||
|
@ -1008,7 +1008,7 @@
|
|||
<div style="display: flex; justify-content: space-between; width: 100%;">
|
||||
<div style="flex-grow: 1;">
|
||||
|
||||
<b-field horizontal label="${customer_key_label or 'TODO: Customer Key'}">
|
||||
<b-field horizontal label="${customer_key_label}">
|
||||
{{ customer._key }}
|
||||
</b-field>
|
||||
|
||||
|
@ -1966,106 +1966,37 @@
|
|||
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
</%def>
|
||||
|
||||
let ProfileInfoData = {
|
||||
activeTab: location.hash ? location.hash.substring(1) : 'personal',
|
||||
tabchecks: ${json.dumps(tabchecks or {})|n},
|
||||
today: '${rattail_app.today()}',
|
||||
profileLastChanged: Date.now(),
|
||||
person: ${json.dumps(person_data or {})|n},
|
||||
phoneTypeOptions: ${json.dumps(phone_type_options or [])|n},
|
||||
emailTypeOptions: ${json.dumps(email_type_options or [])|n},
|
||||
maxLengths: ${json.dumps(max_lengths or {})|n},
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
${self.render_personal_tab_template()}
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
loadingRevisions: false,
|
||||
showingRevisionDialog: false,
|
||||
revision: {},
|
||||
revisionShowAllFields: false,
|
||||
% endif
|
||||
}
|
||||
% if expose_members:
|
||||
${self.render_member_tab_template()}
|
||||
% endif
|
||||
|
||||
let ProfileInfo = {
|
||||
template: '#profile-info-template',
|
||||
props: {
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
viewingHistory: Boolean,
|
||||
gettingRevisions: Boolean,
|
||||
revisions: Array,
|
||||
revisionVersionMap: null,
|
||||
% endif
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
${self.render_customer_tab_template()}
|
||||
% if expose_customer_shoppers:
|
||||
${self.render_shopper_tab_template()}
|
||||
% endif
|
||||
${self.render_employee_tab_template()}
|
||||
${self.render_notes_tab_template()}
|
||||
|
||||
// auto-refresh whichever tab is shown first
|
||||
## TODO: how to not assume 'personal' is the default tab?
|
||||
let tab = this.$refs['tab_' + (this.activeTab || 'personal')]
|
||||
if (tab && tab.refreshTab) {
|
||||
tab.refreshTab()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
% if expose_transactions:
|
||||
${transactions_grid.render_complete(allow_save_defaults=False)|n}
|
||||
${self.render_transactions_tab_template()}
|
||||
% endif
|
||||
|
||||
profileChanged(data) {
|
||||
this.$emit('change-content-title', data.person.dynamic_content_title)
|
||||
this.person = data.person
|
||||
this.tabchecks = data.tabchecks
|
||||
this.profileLastChanged = Date.now()
|
||||
},
|
||||
|
||||
activeTabChanged(value) {
|
||||
location.hash = value
|
||||
this.refreshTabIfNeeded(value)
|
||||
this.activeTabChangedExtra(value)
|
||||
},
|
||||
|
||||
refreshTabIfNeeded(key) {
|
||||
// TODO: this is *always* refreshing, should be more selective (?)
|
||||
let tab = this.$refs['tab_' + key]
|
||||
if (tab && tab.refreshIfNeeded) {
|
||||
tab.refreshIfNeeded(this.profileLastChanged)
|
||||
}
|
||||
},
|
||||
|
||||
activeTabChangedExtra(value) {},
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
|
||||
viewRevision(row) {
|
||||
this.revision = this.revisionVersionMap[row.txnid]
|
||||
this.showingRevisionDialog = true
|
||||
},
|
||||
|
||||
viewPrevRevision() {
|
||||
let txnid = this.revision.prev_txnid
|
||||
this.revision = this.revisionVersionMap[txnid]
|
||||
},
|
||||
|
||||
viewNextRevision() {
|
||||
let txnid = this.revision.next_txnid
|
||||
this.revision = this.revisionVersionMap[txnid]
|
||||
},
|
||||
|
||||
toggleVersionFields() {
|
||||
this.revisionShowAllFields = !this.revisionShowAllFields
|
||||
},
|
||||
|
||||
% endif
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
${self.render_user_tab_template()}
|
||||
${self.render_profile_info_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="declare_personal_tab_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
let PersonalTabData = {
|
||||
% if hasattr(master, 'profile_tab_personal'):
|
||||
refreshTabURL: '${url('people.profile_tab_personal', uuid=person.uuid)}',
|
||||
% endif
|
||||
|
||||
// nb. hack to force refresh for vue3
|
||||
refreshPersonalCard: 1,
|
||||
|
@ -2516,9 +2447,7 @@
|
|||
<script type="text/javascript">
|
||||
|
||||
let CustomerTabData = {
|
||||
% if hasattr(master, 'profile_tab_customer'):
|
||||
refreshTabURL: '${url('people.profile_tab_customer', uuid=person.uuid)}',
|
||||
% endif
|
||||
customers: [],
|
||||
}
|
||||
|
||||
|
@ -2592,9 +2521,7 @@
|
|||
<script type="text/javascript">
|
||||
|
||||
let EmployeeTabData = {
|
||||
% if hasattr(master, 'profile_tab_employee'):
|
||||
refreshTabURL: '${url('people.profile_tab_employee', uuid=person.uuid)}',
|
||||
% endif
|
||||
employee: {},
|
||||
employeeHistory: [],
|
||||
|
||||
|
@ -2829,9 +2756,7 @@
|
|||
<script type="text/javascript">
|
||||
|
||||
let NotesTabData = {
|
||||
% if hasattr(master, 'profile_tab_notes'):
|
||||
refreshTabURL: '${url('people.profile_tab_notes', uuid=person.uuid)}',
|
||||
% endif
|
||||
notes: [],
|
||||
noteTypeOptions: [],
|
||||
|
||||
|
@ -2995,9 +2920,7 @@
|
|||
<script type="text/javascript">
|
||||
|
||||
let UserTabData = {
|
||||
% if hasattr(master, 'profile_tab_user'):
|
||||
refreshTabURL: '${url('people.profile_tab_user', uuid=person.uuid)}',
|
||||
% endif
|
||||
users: [],
|
||||
|
||||
% if request.has_perm('users.create'):
|
||||
|
@ -3053,9 +2976,7 @@
|
|||
createUserSave() {
|
||||
this.createUserSaving = true
|
||||
|
||||
% if hasattr(master, 'profile_make_user'):
|
||||
let url = '${master.get_action_url('profile_make_user', instance)}'
|
||||
% endif
|
||||
let params = {
|
||||
username: this.createUserUsername,
|
||||
active: this.createUserActive,
|
||||
|
@ -3089,46 +3010,114 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_profile_info_component()">
|
||||
<%def name="declare_profile_info_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.declare_profile_info_vars()}
|
||||
let ProfileInfoData = {
|
||||
activeTab: location.hash ? location.hash.substring(1) : 'personal',
|
||||
tabchecks: ${json.dumps(tabchecks)|n},
|
||||
today: '${rattail_app.today()}',
|
||||
profileLastChanged: Date.now(),
|
||||
person: ${json.dumps(person_data)|n},
|
||||
phoneTypeOptions: ${json.dumps(phone_type_options)|n},
|
||||
emailTypeOptions: ${json.dumps(email_type_options)|n},
|
||||
maxLengths: ${json.dumps(max_lengths)|n},
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
loadingRevisions: false,
|
||||
showingRevisionDialog: false,
|
||||
revision: {},
|
||||
revisionShowAllFields: false,
|
||||
% endif
|
||||
}
|
||||
|
||||
let ProfileInfo = {
|
||||
template: '#profile-info-template',
|
||||
props: {
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
viewingHistory: Boolean,
|
||||
gettingRevisions: Boolean,
|
||||
revisions: Array,
|
||||
revisionVersionMap: null,
|
||||
% endif
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
|
||||
// auto-refresh whichever tab is shown first
|
||||
## TODO: how to not assume 'personal' is the default tab?
|
||||
let tab = this.$refs['tab_' + (this.activeTab || 'personal')]
|
||||
if (tab && tab.refreshTab) {
|
||||
tab.refreshTab()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
profileChanged(data) {
|
||||
this.$emit('change-content-title', data.person.dynamic_content_title)
|
||||
this.person = data.person
|
||||
this.tabchecks = data.tabchecks
|
||||
this.profileLastChanged = Date.now()
|
||||
},
|
||||
|
||||
activeTabChanged(value) {
|
||||
location.hash = value
|
||||
this.refreshTabIfNeeded(value)
|
||||
this.activeTabChangedExtra(value)
|
||||
},
|
||||
|
||||
refreshTabIfNeeded(key) {
|
||||
// TODO: this is *always* refreshing, should be more selective (?)
|
||||
let tab = this.$refs['tab_' + key]
|
||||
if (tab && tab.refreshIfNeeded) {
|
||||
tab.refreshIfNeeded(this.profileLastChanged)
|
||||
}
|
||||
},
|
||||
|
||||
activeTabChangedExtra(value) {},
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
|
||||
viewRevision(row) {
|
||||
this.revision = this.revisionVersionMap[row.txnid]
|
||||
this.showingRevisionDialog = true
|
||||
},
|
||||
|
||||
viewPrevRevision() {
|
||||
let txnid = this.revision.prev_txnid
|
||||
this.revision = this.revisionVersionMap[txnid]
|
||||
},
|
||||
|
||||
viewNextRevision() {
|
||||
let txnid = this.revision.next_txnid
|
||||
this.revision = this.revisionVersionMap[txnid]
|
||||
},
|
||||
|
||||
toggleVersionFields() {
|
||||
this.revisionShowAllFields = !this.revisionShowAllFields
|
||||
},
|
||||
|
||||
% endif
|
||||
},
|
||||
}
|
||||
|
||||
<script>
|
||||
ProfileInfo.data = function() { return ProfileInfoData }
|
||||
Vue.component('profile-info', ProfileInfo)
|
||||
<% request.register_component('profile-info', 'ProfileInfo') %>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="make_profile_info_component()">
|
||||
${self.declare_profile_info_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${self.render_personal_tab_template()}
|
||||
ProfileInfo.data = function() { return ProfileInfoData }
|
||||
Vue.component('profile-info', ProfileInfo)
|
||||
<% request.register_component('profile-info', 'ProfileInfo') %>
|
||||
|
||||
% if expose_members:
|
||||
${self.render_member_tab_template()}
|
||||
% endif
|
||||
|
||||
${self.render_customer_tab_template()}
|
||||
% if expose_customer_shoppers:
|
||||
${self.render_shopper_tab_template()}
|
||||
% endif
|
||||
${self.render_employee_tab_template()}
|
||||
${self.render_notes_tab_template()}
|
||||
|
||||
% if expose_transactions:
|
||||
${transactions_grid.render_complete(allow_save_defaults=False)|n}
|
||||
${self.render_transactions_tab_template()}
|
||||
% endif
|
||||
|
||||
${self.render_user_tab_template()}
|
||||
${self.render_profile_info_template()}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
ThisPage.props.viewingHistory = Boolean
|
||||
|
@ -3176,8 +3165,45 @@
|
|||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
${self.make_personal_tab_component()}
|
||||
|
||||
% if expose_members:
|
||||
${self.make_member_tab_component()}
|
||||
% endif
|
||||
|
||||
${self.make_customer_tab_component()}
|
||||
% if expose_customer_shoppers:
|
||||
${self.make_shopper_tab_component()}
|
||||
% endif
|
||||
${self.make_employee_tab_component()}
|
||||
${self.make_notes_tab_component()}
|
||||
|
||||
% if expose_transactions:
|
||||
<script type="text/javascript">
|
||||
|
||||
TransactionsGrid.data = function() { return TransactionsGridData }
|
||||
Vue.component('transactions-grid', TransactionsGrid)
|
||||
## TODO: why is this line not needed?
|
||||
## <% request.register_component('transactions-grid', 'TransactionsGrid') %>
|
||||
|
||||
</script>
|
||||
${self.make_transactions_tab_component()}
|
||||
% endif
|
||||
|
||||
${self.make_user_tab_component()}
|
||||
${self.make_profile_info_component()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_whole_page_vars()">
|
||||
${parent.modify_whole_page_vars()}
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
<script type="text/javascript">
|
||||
|
||||
WholePageData.viewingHistory = false
|
||||
WholePageData.gettingRevisions = false
|
||||
|
@ -3213,44 +3239,9 @@
|
|||
})
|
||||
}
|
||||
|
||||
% endif
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
|
||||
${self.make_personal_tab_component()}
|
||||
|
||||
% if expose_members:
|
||||
${self.make_member_tab_component()}
|
||||
% endif
|
||||
|
||||
${self.make_customer_tab_component()}
|
||||
% if expose_customer_shoppers:
|
||||
${self.make_shopper_tab_component()}
|
||||
% endif
|
||||
${self.make_employee_tab_component()}
|
||||
${self.make_notes_tab_component()}
|
||||
|
||||
% if expose_transactions:
|
||||
<script type="text/javascript">
|
||||
|
||||
TransactionsGrid.data = function() { return TransactionsGridData }
|
||||
Vue.component('transactions-grid', TransactionsGrid)
|
||||
## TODO: why is this line not needed?
|
||||
## <% request.register_component('transactions-grid', 'TransactionsGrid') %>
|
||||
|
||||
</script>
|
||||
${self.make_transactions_tab_component()}
|
||||
% endif
|
||||
|
||||
${self.make_user_tab_component()}
|
||||
${self.make_profile_info_component()}
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## DEPRECATED
|
||||
##############################
|
||||
|
||||
<%def name="declare_profile_info_vars()"></%def>
|
||||
${parent.body()}
|
||||
|
|
|
@ -62,13 +62,19 @@
|
|||
<br />
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if master.has_perm('replace'):
|
||||
<script>
|
||||
${form.vue_component}Data.showUploadForm = false
|
||||
${form.vue_component}Data.uploadFile = null
|
||||
${form.vue_component}Data.uploadSubmitting = false
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.component_studly}Data.showUploadForm = false
|
||||
|
||||
${form.component_studly}Data.uploadFile = null
|
||||
|
||||
${form.component_studly}Data.uploadSubmitting = false
|
||||
|
||||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -118,9 +118,14 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.setupSubmitting = false
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -10,20 +10,12 @@
|
|||
</find-principals>
|
||||
</%def>
|
||||
|
||||
<%def name="principal_table()">
|
||||
<div
|
||||
style="width: 50%;"
|
||||
>
|
||||
${grid.render_table_element(data_prop='principalsData')|n}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<script type="text/x-template" id="find-principals-template">
|
||||
<div>
|
||||
|
||||
${h.form(request.url, method='GET', **{'@submit': 'formSubmitting = true'})}
|
||||
${h.form(request.current_route_url(), method='GET', **{'@submit': 'formSubmitting = true'})}
|
||||
<div style="margin-left: 10rem; max-width: 50%;">
|
||||
|
||||
${h.hidden('permission_group', **{':value': 'selectedGroup'})}
|
||||
|
@ -71,7 +63,7 @@
|
|||
<b-field horizontal>
|
||||
<div class="buttons" style="margin-top: 1rem;">
|
||||
<once-button tag="a"
|
||||
href="${request.path_url}"
|
||||
href="${request.current_route_url(_query=None)}"
|
||||
text="Reset Form">
|
||||
</once-button>
|
||||
<b-button type="is-primary"
|
||||
|
@ -98,6 +90,28 @@
|
|||
|
||||
</div>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="principal_table()">
|
||||
<div
|
||||
style="width: 50%;"
|
||||
>
|
||||
${grid.render_table_element(data_prop='principalsData')|n}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.permissionGroups = ${json.dumps(perms_data)|n}
|
||||
ThisPageData.sortedGroups = ${json.dumps(sorted_groups_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
const FindPrincipals = {
|
||||
|
@ -226,21 +240,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.permissionGroups = ${json.dumps(perms_data)|n}
|
||||
ThisPageData.sortedGroups = ${json.dumps(sorted_groups_data)|n}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
Vue.component('find-principals', FindPrincipals)
|
||||
|
||||
<% request.register_component('find-principals', 'FindPrincipals') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
</%def>
|
||||
|
||||
<%def name="render_form_innards()">
|
||||
${h.form(request.current_route_url(), **{'@submit': 'submit{}'.format(form.vue_component)})}
|
||||
${h.form(request.current_route_url(), **{'@submit': 'submit{}'.format(form.component_studly)})}
|
||||
${h.csrf_token(request)}
|
||||
|
||||
<section>
|
||||
|
@ -43,8 +43,8 @@
|
|||
<div class="buttons">
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
:disabled="${form.vue_component}Submitting">
|
||||
{{ ${form.vue_component}ButtonText }}
|
||||
:disabled="${form.component_studly}Submitting">
|
||||
{{ ${form.component_studly}ButtonText }}
|
||||
</b-button>
|
||||
<b-button tag="a" href="${url('products')}">
|
||||
Cancel
|
||||
|
@ -55,33 +55,32 @@
|
|||
</%def>
|
||||
|
||||
<%def name="render_form_template()">
|
||||
<script type="text/x-template" id="${form.vue_tagname}-template">
|
||||
<script type="text/x-template" id="${form.component}-template">
|
||||
${self.render_form_innards()}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<% request.register_component(form.vue_tagname, form.vue_component) %>
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
## TODO: ugh, an awful lot of duplicated code here (from /forms/deform.mako)
|
||||
|
||||
let ${form.vue_component} = {
|
||||
template: '#${form.vue_tagname}-template',
|
||||
let ${form.component_studly} = {
|
||||
template: '#${form.component}-template',
|
||||
methods: {
|
||||
|
||||
## TODO: deprecate / remove the latter option here
|
||||
% if form.auto_disable_save or form.auto_disable:
|
||||
submit${form.vue_component}() {
|
||||
this.${form.vue_component}Submitting = true
|
||||
this.${form.vue_component}ButtonText = "Working, please wait..."
|
||||
submit${form.component_studly}() {
|
||||
this.${form.component_studly}Submitting = true
|
||||
this.${form.component_studly}ButtonText = "Working, please wait..."
|
||||
}
|
||||
% endif
|
||||
}
|
||||
}
|
||||
|
||||
let ${form.vue_component}Data = {
|
||||
let ${form.component_studly}Data = {
|
||||
|
||||
## TODO: ugh, this seems pretty hacky. need to declare some data models
|
||||
## for various field components to bind to...
|
||||
|
@ -96,8 +95,8 @@
|
|||
|
||||
## TODO: deprecate / remove the latter option here
|
||||
% if form.auto_disable_save or form.auto_disable:
|
||||
${form.vue_component}Submitting: false,
|
||||
${form.vue_component}ButtonText: ${json.dumps(getattr(form, 'submit_label', getattr(form, 'save_label', "Submit")))|n},
|
||||
${form.component_studly}Submitting: false,
|
||||
${form.component_studly}ButtonText: ${json.dumps(getattr(form, 'submit_label', getattr(form, 'save_label', "Submit")))|n},
|
||||
% endif
|
||||
|
||||
## TODO: more hackiness, this is for the sake of batch params
|
||||
|
@ -115,3 +114,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -95,9 +95,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPage.methods.getTitleForKey = function(key) {
|
||||
switch (key) {
|
||||
|
@ -118,3 +118,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -36,16 +36,16 @@
|
|||
</${grid.component}>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if label_profiles and master.has_perm('print_labels'):
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
|
||||
${grid.vue_component}Data.quickLabelProfile = ${json.dumps(label_profiles[0].uuid)|n}
|
||||
${grid.vue_component}Data.quickLabelQuantity = 1
|
||||
${grid.vue_component}Data.quickLabelSpeedbumpThreshold = ${json.dumps(quick_label_speedbump_threshold)|n}
|
||||
${grid.component_studly}Data.quickLabelProfile = ${json.dumps(label_profiles[0].uuid)|n}
|
||||
${grid.component_studly}Data.quickLabelQuantity = 1
|
||||
${grid.component_studly}Data.quickLabelSpeedbumpThreshold = ${json.dumps(quick_label_speedbump_threshold)|n}
|
||||
|
||||
${grid.vue_component}.methods.quickLabelPrint = function(row) {
|
||||
${grid.component_studly}.methods.quickLabelPrint = function(row) {
|
||||
|
||||
let quantity = parseInt(this.quickLabelQuantity)
|
||||
if (isNaN(quantity)) {
|
||||
|
@ -83,3 +83,6 @@
|
|||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<%inherit file="/master/view.mako" />
|
||||
<%namespace name="product_lookup" file="/products/lookup.mako" />
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
${product_lookup.tailbone_product_lookup_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
${parent.page_content()}
|
||||
|
||||
|
@ -62,14 +67,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${product_lookup.tailbone_product_lookup_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if master.has_perm('ignore_product') and instance.status_code in (enum.PENDING_PRODUCT_STATUS_PENDING, enum.PENDING_PRODUCT_STATUS_READY):
|
||||
|
||||
|
@ -124,7 +124,10 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
${product_lookup.tailbone_product_lookup_component()}
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -282,9 +282,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.vendorSourcesData = ${json.dumps(vendor_sources['data'])|n}
|
||||
ThisPageData.lookupCodesData = ${json.dumps(lookup_codes['data'])|n}
|
||||
|
@ -411,3 +411,6 @@
|
|||
% endif
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -59,24 +59,27 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${grid.vue_component}Data.changeStatusShowDialog = false
|
||||
${grid.vue_component}Data.changeStatusOptions = ${json.dumps(status_options)|n}
|
||||
${grid.vue_component}Data.changeStatusValue = null
|
||||
${grid.vue_component}Data.changeStatusSubmitting = false
|
||||
${grid.component_studly}Data.changeStatusShowDialog = false
|
||||
${grid.component_studly}Data.changeStatusOptions = ${json.dumps(status_options)|n}
|
||||
${grid.component_studly}Data.changeStatusValue = null
|
||||
${grid.component_studly}Data.changeStatusSubmitting = false
|
||||
|
||||
${grid.vue_component}.methods.changeStatusInit = function() {
|
||||
${grid.component_studly}.methods.changeStatusInit = function() {
|
||||
this.changeStatusValue = null
|
||||
this.changeStatusShowDialog = true
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.changeStatusSubmit = function() {
|
||||
${grid.component_studly}.methods.changeStatusSubmit = function() {
|
||||
this.changeStatusSubmitting = true
|
||||
this.$refs.changeStatusForm.submit()
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -69,12 +69,12 @@
|
|||
<h3 class="block is-size-3">Vendors</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field message="If not set, user must choose a "supported" vendor.">
|
||||
<b-checkbox name="rattail.batch.purchase.allow_receiving_any_vendor"
|
||||
v-model="simpleSettings['rattail.batch.purchase.allow_receiving_any_vendor']"
|
||||
<b-field message="If set, user must choose a "supported" vendor; otherwise they may choose "any" vendor.">
|
||||
<b-checkbox name="rattail.batch.purchase.supported_vendors_only"
|
||||
v-model="simpleSettings['rattail.batch.purchase.supported_vendors_only']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Allow receiving for <span class="has-text-weight-bold">any</span> vendor
|
||||
Only allow batch for "supported" vendors
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
|
|
|
@ -139,15 +139,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="object_helpers()">
|
||||
${self.render_status_breakdown()}
|
||||
${self.render_po_vs_invoice_helper()}
|
||||
${self.render_execute_helper()}
|
||||
${self.render_tools_helper()}
|
||||
</%def>
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if allow_edit_catalog_unit_cost or allow_edit_invoice_unit_cost:
|
||||
<script type="text/x-template" id="receiving-cost-editor-template">
|
||||
<div>
|
||||
|
@ -168,9 +162,16 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="object_helpers()">
|
||||
${self.render_status_breakdown()}
|
||||
${self.render_po_vs_invoice_helper()}
|
||||
${self.render_execute_helper()}
|
||||
${self.render_tools_helper()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if allow_confirm_all_costs:
|
||||
|
||||
|
@ -317,13 +318,13 @@
|
|||
|
||||
% if allow_edit_catalog_unit_cost:
|
||||
|
||||
${rows_grid.vue_component}.methods.catalogUnitCostClicked = function(row) {
|
||||
${rows_grid.component_studly}.methods.catalogUnitCostClicked = function(row) {
|
||||
|
||||
// start edit for clicked cell
|
||||
this.$refs['catalogUnitCost_' + row.uuid].startEdit()
|
||||
}
|
||||
|
||||
${rows_grid.vue_component}.methods.catalogCostConfirmed = function(amount, index) {
|
||||
${rows_grid.component_studly}.methods.catalogCostConfirmed = function(amount, index) {
|
||||
|
||||
// update display to indicate cost was confirmed
|
||||
this.addRowClass(index, 'catalog_cost_confirmed')
|
||||
|
@ -352,13 +353,13 @@
|
|||
|
||||
% if allow_edit_invoice_unit_cost:
|
||||
|
||||
${rows_grid.vue_component}.methods.invoiceUnitCostClicked = function(row) {
|
||||
${rows_grid.component_studly}.methods.invoiceUnitCostClicked = function(row) {
|
||||
|
||||
// start edit for clicked cell
|
||||
this.$refs['invoiceUnitCost_' + row.uuid].startEdit()
|
||||
}
|
||||
|
||||
${rows_grid.vue_component}.methods.invoiceCostConfirmed = function(amount, index) {
|
||||
${rows_grid.component_studly}.methods.invoiceCostConfirmed = function(amount, index) {
|
||||
|
||||
// update display to indicate cost was confirmed
|
||||
this.addRowClass(index, 'invoice_cost_confirmed')
|
||||
|
@ -388,3 +389,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -484,9 +484,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
## ThisPage.methods.editUnitCost = function() {
|
||||
## alert("TODO: not yet implemented")
|
||||
|
@ -720,3 +720,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -53,13 +53,13 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}Data.reportDescriptions = ${json.dumps(report_descriptions)|n}
|
||||
TailboneFormData.reportDescriptions = ${json.dumps(report_descriptions)|n}
|
||||
|
||||
${form.vue_component}.methods.reportTypeChanged = function(reportType) {
|
||||
TailboneForm.methods.reportTypeChanged = function(reportType) {
|
||||
this.$emit('report-change', this.reportDescriptions[reportType])
|
||||
}
|
||||
|
||||
|
@ -71,3 +71,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/master/delete.mako" />
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if params_data is not Undefined:
|
||||
${form.vue_component}Data.paramsData = ${json.dumps(params_data)|n}
|
||||
${form.component_studly}Data.paramsData = ${json.dumps(params_data)|n}
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -23,11 +23,16 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if params_data is not Undefined:
|
||||
${form.vue_component}Data.paramsData = ${json.dumps(params_data)|n}
|
||||
${form.component_studly}Data.paramsData = ${json.dumps(params_data)|n}
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -48,10 +48,15 @@
|
|||
${h.end_form()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.departments = ${json.dumps([{'uuid': d.uuid, 'name': d.name} for d in departments])|n}
|
||||
ThisPageData.excludeNotForSale = true
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -81,9 +81,9 @@
|
|||
|
||||
<%def name="extra_fields()"></%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.vendorUUID = null
|
||||
ThisPageData.departments = []
|
||||
|
@ -127,3 +127,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -45,10 +45,11 @@
|
|||
<b-button @click="runReportShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
${h.form(master.get_action_url('execute', instance), **{'@submit': 'runReportSubmitting = true'})}
|
||||
${h.form(master.get_action_url('execute', instance))}
|
||||
${h.csrf_token(request)}
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
@click="runReportSubmitting = true"
|
||||
:disabled="runReportSubmitting"
|
||||
icon-pack="fas"
|
||||
icon-left="arrow-circle-right">
|
||||
|
@ -61,12 +62,12 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if weekdays_data is not Undefined:
|
||||
${form.vue_component}Data.weekdaysData = ${json.dumps(weekdays_data)|n}
|
||||
${form.component_studly}Data.weekdaysData = ${json.dumps(weekdays_data)|n}
|
||||
% endif
|
||||
|
||||
ThisPageData.runReportShowDialog = false
|
||||
|
@ -74,3 +75,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,11 +6,15 @@
|
|||
${h.stylesheet_link(request.static_url('tailbone:static/css/perms.css'))}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
// TODO: this variable name should be more dynamic (?) since this is
|
||||
// connected to (and only here b/c of) the permissions field
|
||||
${form.vue_component}Data.showingPermissionGroup = ''
|
||||
TailboneFormData.showingPermissionGroup = ''
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,11 +6,15 @@
|
|||
${h.stylesheet_link(request.static_url('tailbone:static/css/perms.css'))}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
// TODO: this variable name should be more dynamic (?) since this is
|
||||
// connected to (and only here b/c of) the permissions field
|
||||
${form.vue_component}Data.showingPermissionGroup = ''
|
||||
TailboneFormData.showingPermissionGroup = ''
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
${h.stylesheet_link(request.static_url('tailbone:static/css/perms.css'))}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if users_data is not Undefined:
|
||||
${form.vue_component}Data.usersData = ${json.dumps(users_data)|n}
|
||||
${form.component_studly}Data.usersData = ${json.dumps(users_data)|n}
|
||||
% endif
|
||||
|
||||
ThisPage.methods.detachPerson = function(url) {
|
||||
|
@ -23,3 +23,5 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -86,9 +86,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.testRecipient = ${json.dumps(user_email_address)|n}
|
||||
ThisPageData.sendingTest = false
|
||||
|
@ -137,3 +137,6 @@
|
|||
% endif
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
${parent.render_grid_component()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if master.has_perm('configure'):
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.showEmails = 'available'
|
||||
|
||||
|
@ -26,9 +26,9 @@
|
|||
this.$refs.grid.showEmails = this.showEmails
|
||||
}
|
||||
|
||||
${grid.vue_component}Data.showEmails = 'available'
|
||||
${grid.component_studly}Data.showEmails = 'available'
|
||||
|
||||
${grid.vue_component}.computed.visibleData = function() {
|
||||
${grid.component_studly}.computed.visibleData = function() {
|
||||
|
||||
if (this.showEmails == 'available') {
|
||||
return this.data.filter(email => email.hidden == 'No')
|
||||
|
@ -41,11 +41,11 @@
|
|||
return this.data
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.renderLabelToggleHidden = function(row) {
|
||||
${grid.component_studly}.methods.renderLabelToggleHidden = function(row) {
|
||||
return row.hidden == 'Yes' ? "Un-hide" : "Hide"
|
||||
}
|
||||
|
||||
${grid.vue_component}.methods.toggleHidden = function(row) {
|
||||
${grid.component_studly}.methods.toggleHidden = function(row) {
|
||||
let url = '${url('{}.toggle_hidden'.format(route_prefix))}'
|
||||
let params = {
|
||||
key: row.key,
|
||||
|
@ -65,3 +65,5 @@
|
|||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<email-preview-tools></email-preview-tools>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<script type="text/x-template" id="email-preview-tools-template">
|
||||
|
||||
${h.form(url('email.preview'), **{'@submit': 'submitPreviewForm'})}
|
||||
|
@ -72,6 +72,10 @@
|
|||
|
||||
${h.end_form()}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
const EmailPreviewTools = {
|
||||
|
@ -96,13 +100,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
Vue.component('email-preview-tools', EmailPreviewTools)
|
||||
|
||||
<% request.register_component('email-preview-tools', 'EmailPreviewTools') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
Vue.component('email-preview-tools', EmailPreviewTools)
|
||||
<% request.register_component('email-preview-tools', 'EmailPreviewTools') %>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -695,9 +695,9 @@
|
|||
</b-steps>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
// nb. for warning user they may lose changes if leaving page
|
||||
ThisPageData.dirty = false
|
||||
|
@ -983,3 +983,6 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue