2017-07-07 09:13:53 -05:00
|
|
|
# -*- coding: utf-8; -*-
|
2012-04-10 12:39:30 -05:00
|
|
|
################################################################################
|
|
|
|
#
|
|
|
|
# Rattail -- Retail Software Framework
|
2024-04-14 20:56:11 -05:00
|
|
|
# Copyright © 2010-2024 Lance Edgar
|
2012-04-10 12:39:30 -05:00
|
|
|
#
|
|
|
|
# This file is part of Rattail.
|
|
|
|
#
|
|
|
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
2017-07-06 23:47:56 -05:00
|
|
|
# terms of the GNU General Public License as published by the Free Software
|
|
|
|
# Foundation, either version 3 of the License, or (at your option) any later
|
|
|
|
# version.
|
2012-04-10 12:39:30 -05:00
|
|
|
#
|
|
|
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
2017-07-06 23:47:56 -05:00
|
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
# details.
|
2012-04-10 12:39:30 -05:00
|
|
|
#
|
2017-07-06 23:47:56 -05:00
|
|
|
# You should have received a copy of the GNU General Public License along with
|
|
|
|
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
2012-04-10 12:39:30 -05:00
|
|
|
#
|
|
|
|
################################################################################
|
|
|
|
"""
|
2013-09-01 09:27:47 -05:00
|
|
|
Event Subscribers
|
2012-04-10 12:39:30 -05:00
|
|
|
"""
|
|
|
|
|
2018-01-25 17:02:53 -06:00
|
|
|
import datetime
|
2024-04-24 16:13:14 -05:00
|
|
|
import logging
|
2024-04-19 21:18:57 -05:00
|
|
|
import warnings
|
2024-04-24 16:13:14 -05:00
|
|
|
from collections import OrderedDict
|
2017-07-07 09:13:53 -05:00
|
|
|
|
2012-04-10 12:39:30 -05:00
|
|
|
import rattail
|
|
|
|
|
2019-05-23 16:29:29 -05:00
|
|
|
import colander
|
|
|
|
import deform
|
2016-02-11 18:47:17 -06:00
|
|
|
from pyramid import threadlocal
|
2018-11-27 17:52:02 -06:00
|
|
|
from webhelpers2.html import tags
|
2016-02-11 18:47:17 -06:00
|
|
|
|
2024-07-12 09:35:34 -05:00
|
|
|
from wuttaweb import subscribers as base
|
|
|
|
|
2016-10-07 19:33:41 -05:00
|
|
|
import tailbone
|
2016-02-11 18:47:17 -06:00
|
|
|
from tailbone import helpers
|
|
|
|
from tailbone.db import Session
|
2023-02-01 18:44:55 -06:00
|
|
|
from tailbone.config import csrf_header_name, should_expose_websockets
|
2024-07-12 09:35:34 -05:00
|
|
|
from tailbone.util import get_available_themes, get_global_search_options
|
2024-04-24 16:13:14 -05:00
|
|
|
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
2013-09-01 17:31:50 -05:00
|
|
|
|
2012-04-10 12:39:30 -05:00
|
|
|
|
2024-08-15 14:34:20 -05:00
|
|
|
def new_request(event, session=None):
|
2014-02-21 12:10:10 -06:00
|
|
|
"""
|
2024-07-12 09:35:34 -05:00
|
|
|
Event hook called when processing a new request.
|
|
|
|
|
2024-08-05 15:00:11 -05:00
|
|
|
This first invokes the upstream hooks:
|
|
|
|
|
|
|
|
* :func:`wuttaweb:wuttaweb.subscribers.new_request()`
|
|
|
|
* :func:`wuttaweb:wuttaweb.subscribers.new_request_set_user()`
|
2024-07-12 09:35:34 -05:00
|
|
|
|
|
|
|
It then adds more things to the request object; among them:
|
|
|
|
|
|
|
|
.. attribute:: request.rattail_config
|
|
|
|
|
|
|
|
Reference to the app :term:`config object`. Note that this
|
2024-08-05 15:00:11 -05:00
|
|
|
will be the same as :attr:`wuttaweb:request.wutta_config`.
|
2023-03-25 01:01:52 -05:00
|
|
|
|
2024-07-12 09:35:34 -05:00
|
|
|
.. method:: request.register_component(tagname, classname)
|
|
|
|
|
|
|
|
Function to register a Vue component for use with the app.
|
|
|
|
|
|
|
|
This can be called from wherever a component is defined, and
|
|
|
|
then in the base template all registered components will be
|
|
|
|
properly loaded.
|
2014-02-21 12:10:10 -06:00
|
|
|
"""
|
|
|
|
request = event.request
|
2024-07-12 09:35:34 -05:00
|
|
|
|
2024-08-05 15:00:11 -05:00
|
|
|
# invoke main upstream logic
|
2024-07-14 23:29:17 -05:00
|
|
|
# nb. this sets request.wutta_config
|
2024-07-12 09:35:34 -05:00
|
|
|
base.new_request(event)
|
|
|
|
|
2024-07-14 23:29:17 -05:00
|
|
|
config = request.wutta_config
|
|
|
|
app = config.get_app()
|
|
|
|
auth = app.get_auth_handler()
|
2024-08-15 14:34:20 -05:00
|
|
|
session = session or Session()
|
2024-07-14 23:29:17 -05:00
|
|
|
|
2024-07-12 09:35:34 -05:00
|
|
|
# compatibility
|
2024-07-14 23:29:17 -05:00
|
|
|
rattail_config = config
|
2024-07-12 09:35:34 -05:00
|
|
|
request.rattail_config = rattail_config
|
2014-02-21 12:10:10 -06:00
|
|
|
|
2024-08-05 15:00:11 -05:00
|
|
|
def user_getter(request, db_session=None):
|
|
|
|
user = base.default_user_getter(request, db_session=db_session)
|
|
|
|
if user:
|
|
|
|
# nb. we also assign continuum user to session
|
|
|
|
session = db_session or Session()
|
|
|
|
session.set_continuum_user(user)
|
|
|
|
return user
|
2022-08-17 18:19:37 -05:00
|
|
|
|
2024-08-05 15:00:11 -05:00
|
|
|
# invoke upstream hook to set user
|
2024-08-15 14:34:20 -05:00
|
|
|
base.new_request_set_user(event, user_getter=user_getter, db_session=session)
|
2018-10-17 12:50:02 -05:00
|
|
|
|
2019-06-15 19:02:51 -05:00
|
|
|
# assign client IP address to the session, for sake of versioning
|
2024-08-15 14:34:20 -05:00
|
|
|
if hasattr(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()
|
|
|
|
|
|
|
|
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
|
2024-04-24 16:13:14 -05:00
|
|
|
|
2014-02-21 12:10:10 -06:00
|
|
|
|
2012-04-10 12:39:30 -05:00
|
|
|
def before_render(event):
|
|
|
|
"""
|
2013-09-01 17:31:50 -05:00
|
|
|
Adds goodies to the global template renderer context.
|
2012-04-10 12:39:30 -05:00
|
|
|
"""
|
2024-07-12 09:35:34 -05:00
|
|
|
# log.debug("before_render: %s", event)
|
|
|
|
|
|
|
|
# invoke upstream logic
|
|
|
|
base.before_render(event)
|
2012-04-10 12:39:30 -05:00
|
|
|
|
|
|
|
request = event.get('request') or threadlocal.get_current_request()
|
2024-07-12 09:35:34 -05:00
|
|
|
config = request.wutta_config
|
|
|
|
app = config.get_app()
|
2012-04-10 12:39:30 -05:00
|
|
|
|
|
|
|
renderer_globals = event
|
2024-07-12 09:35:34 -05:00
|
|
|
|
2024-07-14 10:52:32 -05:00
|
|
|
# overrides
|
2024-07-12 09:35:34 -05:00
|
|
|
renderer_globals['h'] = helpers
|
|
|
|
|
|
|
|
# misc.
|
|
|
|
renderer_globals['datetime'] = datetime
|
|
|
|
renderer_globals['colander'] = colander
|
|
|
|
renderer_globals['deform'] = deform
|
|
|
|
renderer_globals['csrf_header_name'] = csrf_header_name(config)
|
|
|
|
|
|
|
|
# TODO: deprecate / remove these
|
2024-06-09 23:07:52 -05:00
|
|
|
renderer_globals['rattail_app'] = app
|
|
|
|
renderer_globals['app_title'] = app.get_title()
|
2024-06-10 09:07:10 -05:00
|
|
|
renderer_globals['app_version'] = app.get_version()
|
2012-04-10 12:39:30 -05:00
|
|
|
renderer_globals['rattail'] = rattail
|
2016-10-07 19:33:41 -05:00
|
|
|
renderer_globals['tailbone'] = tailbone
|
2024-07-11 13:16:02 -05:00
|
|
|
renderer_globals['model'] = app.model
|
2024-07-12 09:35:34 -05:00
|
|
|
renderer_globals['enum'] = app.enum
|
2012-04-10 12:39:30 -05:00
|
|
|
|
2018-11-27 17:52:02 -06:00
|
|
|
# theme - we only want do this for classic web app, *not* API
|
|
|
|
# TODO: so, clearly we need a better way to distinguish the two
|
|
|
|
if 'tailbone.theme' in request.registry.settings:
|
|
|
|
renderer_globals['theme'] = request.registry.settings['tailbone.theme']
|
|
|
|
# note, this is just a global flag; user still needs permission to see picker
|
2024-07-12 09:35:34 -05:00
|
|
|
expose_picker = config.get_bool('tailbone.themes.expose_picker',
|
|
|
|
default=False)
|
2018-11-27 17:52:02 -06:00
|
|
|
renderer_globals['expose_theme_picker'] = expose_picker
|
|
|
|
if expose_picker:
|
2023-11-01 08:13:36 -05:00
|
|
|
|
|
|
|
# TODO: should remove 'falafel' option altogether
|
2024-07-12 09:35:34 -05:00
|
|
|
available = get_available_themes(config)
|
2023-11-01 08:13:36 -05:00
|
|
|
|
2023-10-25 10:45:33 -05:00
|
|
|
options = [tags.Option(theme, value=theme) for theme in available]
|
2018-11-27 17:52:02 -06:00
|
|
|
renderer_globals['theme_picker_options'] = options
|
|
|
|
|
2018-11-29 14:51:57 -06:00
|
|
|
# TODO: ugh, same deal here
|
2024-07-12 09:35:34 -05:00
|
|
|
renderer_globals['messaging_enabled'] = config.get_bool('tailbone.messaging.enabled',
|
|
|
|
default=False)
|
2018-11-29 14:51:57 -06:00
|
|
|
|
2019-03-10 16:36:16 -05:00
|
|
|
# background color may be set per-request, by some apps
|
|
|
|
if hasattr(request, 'background_color') and request.background_color:
|
|
|
|
renderer_globals['background_color'] = request.background_color
|
|
|
|
else: # otherwise we use the one from config
|
2024-07-12 09:35:34 -05:00
|
|
|
renderer_globals['background_color'] = config.get('tailbone.background_color')
|
2019-02-03 14:40:21 -06:00
|
|
|
|
2023-02-03 12:05:17 -06:00
|
|
|
# maybe set custom stylesheet
|
|
|
|
css = None
|
|
|
|
if request.user:
|
2024-07-12 09:35:34 -05:00
|
|
|
css = config.get(f'tailbone.{request.user.uuid}', 'user_css')
|
2024-04-19 21:18:57 -05:00
|
|
|
if not css:
|
2024-07-12 09:35:34 -05:00
|
|
|
css = config.get(f'tailbone.{request.user.uuid}', 'buefy_css')
|
2024-04-19 21:18:57 -05:00
|
|
|
if css:
|
|
|
|
warnings.warn(f"setting 'tailbone.{request.user.uuid}.buefy_css' should be"
|
|
|
|
f"changed to 'tailbone.{request.user.uuid}.user_css'",
|
|
|
|
DeprecationWarning)
|
2024-04-15 13:02:13 -05:00
|
|
|
renderer_globals['user_css'] = css
|
2021-03-05 12:02:32 -06:00
|
|
|
|
2023-02-03 12:05:17 -06:00
|
|
|
# add global search data for quick access
|
|
|
|
renderer_globals['global_search_data'] = get_global_search_options(request)
|
2022-12-26 17:31:37 -06:00
|
|
|
|
2019-05-08 17:13:01 -05:00
|
|
|
# here we globally declare widths for grid filter pseudo-columns
|
2024-07-12 09:35:34 -05:00
|
|
|
widths = config.get('tailbone.grids.filters.column_widths')
|
2019-05-08 17:13:01 -05:00
|
|
|
if widths:
|
|
|
|
widths = widths.split(';')
|
|
|
|
if len(widths) < 2:
|
|
|
|
widths = None
|
|
|
|
if not widths:
|
|
|
|
widths = ['15em', '15em']
|
|
|
|
renderer_globals['filter_fieldname_width'] = widths[0]
|
|
|
|
renderer_globals['filter_verb_width'] = widths[1]
|
|
|
|
|
2022-08-17 18:19:37 -05:00
|
|
|
# declare global support for websockets, or lack thereof
|
2024-07-12 09:35:34 -05:00
|
|
|
renderer_globals['expose_websockets'] = should_expose_websockets(config)
|
2022-08-17 18:19:37 -05:00
|
|
|
|
2012-04-10 12:39:30 -05:00
|
|
|
|
2016-02-11 18:47:17 -06:00
|
|
|
def add_inbox_count(event):
|
|
|
|
"""
|
|
|
|
Adds the current user's inbox message count to the global renderer context.
|
|
|
|
|
|
|
|
Note that this is not enabled by default; to turn it on you must do this:
|
|
|
|
|
|
|
|
config.add_subscriber('tailbone.subscribers.add_inbox_count', 'pyramid.events.BeforeRender')
|
|
|
|
"""
|
|
|
|
request = event.get('request') or threadlocal.get_current_request()
|
|
|
|
if request.user:
|
|
|
|
renderer_globals = event
|
2024-07-11 13:16:02 -05:00
|
|
|
app = request.rattail_config.get_app()
|
|
|
|
model = app.model
|
2016-11-21 01:07:35 -06:00
|
|
|
enum = request.rattail_config.get_enum()
|
2016-02-11 18:47:17 -06:00
|
|
|
renderer_globals['inbox_count'] = Session.query(model.Message)\
|
|
|
|
.outerjoin(model.MessageRecipient)\
|
|
|
|
.filter(model.MessageRecipient.recipient == Session.merge(request.user))\
|
|
|
|
.filter(model.MessageRecipient.status == enum.MESSAGE_STATUS_INBOX)\
|
|
|
|
.count()
|
|
|
|
|
|
|
|
|
2013-09-01 17:31:50 -05:00
|
|
|
def context_found(event):
|
|
|
|
"""
|
2023-03-25 01:01:52 -05:00
|
|
|
Attach some more goodies to the request object:
|
2013-09-01 17:31:50 -05:00
|
|
|
|
|
|
|
The following is attached to the request:
|
|
|
|
|
2023-03-25 01:01:52 -05:00
|
|
|
* ``get_session_timeout()`` function
|
2013-09-01 17:31:50 -05:00
|
|
|
"""
|
|
|
|
request = event.request
|
|
|
|
|
2017-02-16 13:32:20 -06:00
|
|
|
def get_session_timeout():
|
|
|
|
"""
|
|
|
|
Returns the timeout in effect for the current session
|
|
|
|
"""
|
|
|
|
return request.session.get('_timeout')
|
|
|
|
request.get_session_timeout = get_session_timeout
|
|
|
|
|
2013-09-01 17:31:50 -05:00
|
|
|
|
2012-04-10 12:39:30 -05:00
|
|
|
def includeme(config):
|
2018-10-17 12:50:02 -05:00
|
|
|
config.add_subscriber(new_request, 'pyramid.events.NewRequest')
|
2013-09-01 17:31:50 -05:00
|
|
|
config.add_subscriber(before_render, 'pyramid.events.BeforeRender')
|
|
|
|
config.add_subscriber(context_found, 'pyramid.events.ContextFound')
|