parent
783d4dc8ef
commit
16ed125113
|
@ -29,6 +29,7 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|||
intersphinx_mapping = {
|
||||
'colander': ('https://docs.pylonsproject.org/projects/colander/en/latest/', None),
|
||||
'deform': ('https://docs.pylonsproject.org/projects/deform/en/latest/', None),
|
||||
'fanstatic': ('https://www.fanstatic.org/en/latest/', None),
|
||||
'pyramid': ('https://docs.pylonsproject.org/projects/pyramid/en/latest/', None),
|
||||
'python': ('https://docs.python.org/3/', None),
|
||||
'rattail-manual': ('https://rattailproject.org/docs/rattail-manual/', None),
|
||||
|
|
|
@ -13,3 +13,18 @@ Glossary
|
|||
tools.
|
||||
|
||||
See also the :class:`~wuttaweb.grids.base.Grid` base class.
|
||||
|
||||
menu handler
|
||||
This is the :term:`handler` responsible for constructing the main
|
||||
app menu at top of page.
|
||||
|
||||
The menu handler is accessed by way of the :term:`web handler`.
|
||||
|
||||
See also the :class:`~wuttaweb.menus.MenuHandler` base class.
|
||||
|
||||
web handler
|
||||
This is the :term:`handler` responsible for overall web layer
|
||||
customizations, e.g. logo image and menu overrides. Although
|
||||
the latter it delegates to the :term:`menu handler`.
|
||||
|
||||
See also the :class:`~wuttaweb.handler.WebHandler` base class.
|
||||
|
|
|
@ -53,6 +53,10 @@ docs = ["Sphinx", "furo"]
|
|||
tests = ["pytest-cov", "tox"]
|
||||
|
||||
|
||||
[project.entry-points."fanstatic.libraries"]
|
||||
wuttaweb_img = "wuttaweb.static:img"
|
||||
|
||||
|
||||
[project.entry-points."paste.app_factory"]
|
||||
main = "wuttaweb.app:main"
|
||||
|
||||
|
|
|
@ -22,21 +22,88 @@
|
|||
################################################################################
|
||||
"""
|
||||
Web Handler
|
||||
|
||||
This defines the :term:`handler` for the web layer.
|
||||
"""
|
||||
|
||||
from wuttjamaican.app import GenericHandler
|
||||
|
||||
from wuttaweb import static
|
||||
|
||||
|
||||
class WebHandler(GenericHandler):
|
||||
"""
|
||||
Base class and default implementation for the "web" :term:`handler`.
|
||||
Base class and default implementation for the :term:`web handler`.
|
||||
|
||||
This is responsible for determining the "menu handler" and
|
||||
(eventually) possibly other things.
|
||||
This is responsible for determining the :term:`menu handler` and
|
||||
various other customizations.
|
||||
"""
|
||||
|
||||
def get_fanstatic_url(self, request, resource):
|
||||
"""
|
||||
Returns the full URL to the given Fanstatic resource.
|
||||
|
||||
:param request: Current :term:`request` object.
|
||||
|
||||
:param resource: :class:`fanstatic:fanstatic.Resource`
|
||||
instance representing an image file or other resource.
|
||||
"""
|
||||
needed = request.environ['fanstatic.needed']
|
||||
url = needed.library_url(resource.library) + '/'
|
||||
if request.script_name:
|
||||
url = request.script_name + url
|
||||
return url + resource.relpath
|
||||
|
||||
def get_favicon_url(self, request):
|
||||
"""
|
||||
Returns the canonical app favicon image URL.
|
||||
|
||||
This will return the fallback favicon from WuttaWeb unless
|
||||
config specifies an override:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[wuttaweb]
|
||||
favicon_url = http://example.com/favicon.ico
|
||||
"""
|
||||
url = self.config.get('wuttaweb.favicon_url')
|
||||
if url:
|
||||
return url
|
||||
return self.get_fanstatic_url(request, static.favicon)
|
||||
|
||||
def get_header_logo_url(self, request):
|
||||
"""
|
||||
Returns the canonical app header image URL.
|
||||
|
||||
This will return the value from config if specified (as shown
|
||||
below); otherwise it will just call :meth:`get_favicon_url()`
|
||||
and return that.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[wuttaweb]
|
||||
header_logo_url = http://example.com/logo.png
|
||||
"""
|
||||
url = self.config.get('wuttaweb.header_logo_url')
|
||||
if url:
|
||||
return url
|
||||
return self.get_favicon_url(request)
|
||||
|
||||
def get_main_logo_url(self, request):
|
||||
"""
|
||||
Returns the canonical app logo image URL.
|
||||
|
||||
This will return the fallback logo from WuttaWeb unless config
|
||||
specifies an override:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[wuttaweb]
|
||||
logo_url = http://example.com/logo.png
|
||||
"""
|
||||
url = self.config.get('wuttaweb.logo_url')
|
||||
if url:
|
||||
return url
|
||||
return self.get_fanstatic_url(request, static.logo)
|
||||
|
||||
def get_menu_handler(self, **kwargs):
|
||||
"""
|
||||
Get the configured "menu" handler for the web app.
|
||||
|
|
|
@ -35,7 +35,7 @@ log = logging.getLogger(__name__)
|
|||
|
||||
class MenuHandler(GenericHandler):
|
||||
"""
|
||||
Base class and default implementation for menu handler.
|
||||
Base class and default implementation for :term:`menu handler`.
|
||||
|
||||
It is assumed that most apps will override the menu handler with
|
||||
their own subclass. In particular the subclass will override
|
||||
|
|
|
@ -23,15 +23,52 @@
|
|||
"""
|
||||
Static Assets
|
||||
|
||||
It is assumed that all (i.e. even custom) apps will include this
|
||||
module somewhere during startup. For instance this happens within
|
||||
:func:`~wuttaweb.app.main()`::
|
||||
Note that (for now?) It is assumed that *all* (i.e. even custom) apps
|
||||
will include this module somewhere during startup. For instance this
|
||||
happens within :func:`wuttaweb.app.main()`::
|
||||
|
||||
pyramid_config.include('wuttaweb.static')
|
||||
|
||||
This allows for certain common assets to be available for all apps.
|
||||
|
||||
However, an attempt is being made to incorporate Fanstatic for use
|
||||
with the built-in static assets. It is possible the above mechanism
|
||||
could be abandoned in the future.
|
||||
|
||||
So on the Fanstatic front, we currently have defined:
|
||||
|
||||
.. data:: img
|
||||
|
||||
A :class:`fanstatic:fanstatic.Library` representing the ``img``
|
||||
static folder.
|
||||
|
||||
.. data:: favicon
|
||||
|
||||
A :class:`fanstatic:fanstatic.Resource` representing the
|
||||
``img/favicon.ico`` image file.
|
||||
|
||||
.. data:: logo
|
||||
|
||||
A :class:`fanstatic:fanstatic.Resource` representing the
|
||||
``img/logo.png`` image file.
|
||||
|
||||
.. data:: testing
|
||||
|
||||
A :class:`fanstatic:fanstatic.Resource` representing the
|
||||
``img/testing.png`` image file.
|
||||
"""
|
||||
|
||||
from fanstatic import Library, Resource
|
||||
|
||||
|
||||
# fanstatic img library
|
||||
img = Library('wuttaweb_img', 'img')
|
||||
favicon = Resource(img, 'favicon.ico')
|
||||
# nb. mock out the renderers here, to appease fanstatic
|
||||
logo = Resource(img, 'logo.png', renderer=True)
|
||||
testing = Resource(img, 'testing.png', renderer=True)
|
||||
|
||||
|
||||
# TODO: should consider deprecating this?
|
||||
def includeme(config):
|
||||
config.add_static_view('wuttaweb', 'wuttaweb:static')
|
||||
|
|
|
@ -260,13 +260,17 @@ def before_render(event):
|
|||
|
||||
Here are the keys added to context dict by this hook:
|
||||
|
||||
.. data:: 'config'
|
||||
|
||||
Reference to the app :term:`config object`.
|
||||
|
||||
.. data:: 'app'
|
||||
|
||||
Reference to the :term:`app handler`.
|
||||
|
||||
.. data:: 'config'
|
||||
.. data:: 'web'
|
||||
|
||||
Reference to the app :term:`config object`.
|
||||
Reference to the :term:`web handler`.
|
||||
|
||||
.. data:: 'h'
|
||||
|
||||
|
@ -293,8 +297,9 @@ def before_render(event):
|
|||
web = app.get_web_handler()
|
||||
|
||||
context = event
|
||||
context['app'] = app
|
||||
context['config'] = config
|
||||
context['app'] = app
|
||||
context['web'] = web
|
||||
context['h'] = helpers
|
||||
context['url'] = request.route_url
|
||||
context['json'] = json
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
<%def name="extra_styles()"></%def>
|
||||
|
||||
<%def name="favicon()">
|
||||
<link rel="icon" type="image/x-icon" href="${config.get('wuttaweb.favicon_url', default=request.static_url('wuttaweb:static/img/favicon.ico'))}" />
|
||||
<link rel="icon" type="image/x-icon" href="${web.get_favicon_url(request)}" />
|
||||
</%def>
|
||||
|
||||
<%def name="header_logo()">
|
||||
${h.image(config.get('wuttaweb.header_logo_url', default=request.static_url('wuttaweb:static/img/favicon.ico')), "Header Logo", style="height: 49px;")}
|
||||
${h.image(web.get_header_logo_url(request), "Header Logo", style="height: 49px;")}
|
||||
</%def>
|
||||
|
||||
<%def name="full_logo(image_url=None)">
|
||||
${h.image(image_url or config.get('wuttaweb.logo_url', default=request.static_url('wuttaweb:static/img/logo.png')), f"{app.get_title()} logo")}
|
||||
${h.image(image_url or web.get_main_logo_url(request), f"App Logo for {app.get_title()}")}
|
||||
</%def>
|
||||
|
||||
<%def name="footer()">
|
||||
|
|
|
@ -1,20 +1,64 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from wuttjamaican.conf import WuttaConfig
|
||||
|
||||
from wuttaweb import handler as mod
|
||||
from wuttaweb import handler as mod, static
|
||||
from wuttaweb.menus import MenuHandler
|
||||
from tests.util import WebTestCase
|
||||
|
||||
|
||||
class TestWebHandler(TestCase):
|
||||
class TestWebHandler(WebTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.config = WuttaConfig()
|
||||
self.app = self.config.get_app()
|
||||
self.handler = mod.WebHandler(self.config)
|
||||
def make_handler(self):
|
||||
return mod.WebHandler(self.config)
|
||||
|
||||
def test_get_fanstatic_url(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# default with / root path
|
||||
url = handler.get_fanstatic_url(self.request, static.logo)
|
||||
self.assertEqual(url, '/fanstatic/wuttaweb_img/logo.png')
|
||||
|
||||
# what about a subpath
|
||||
self.request.script_name = '/testing'
|
||||
url = handler.get_fanstatic_url(self.request, static.logo)
|
||||
self.assertEqual(url, '/testing/fanstatic/wuttaweb_img/logo.png')
|
||||
|
||||
def test_get_favicon_url(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# default
|
||||
url = handler.get_favicon_url(self.request)
|
||||
self.assertEqual(url, '/fanstatic/wuttaweb_img/favicon.ico')
|
||||
|
||||
# config override
|
||||
self.config.setdefault('wuttaweb.favicon_url', '/testing/other.ico')
|
||||
url = handler.get_favicon_url(self.request)
|
||||
self.assertEqual(url, '/testing/other.ico')
|
||||
|
||||
def test_get_header_logo_url(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# default
|
||||
url = handler.get_header_logo_url(self.request)
|
||||
self.assertEqual(url, '/fanstatic/wuttaweb_img/favicon.ico')
|
||||
|
||||
# config override
|
||||
self.config.setdefault('wuttaweb.header_logo_url', '/testing/header.png')
|
||||
url = handler.get_header_logo_url(self.request)
|
||||
self.assertEqual(url, '/testing/header.png')
|
||||
|
||||
def test_get_main_logo_url(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# default
|
||||
url = handler.get_main_logo_url(self.request)
|
||||
self.assertEqual(url, '/fanstatic/wuttaweb_img/logo.png')
|
||||
|
||||
# config override
|
||||
self.config.setdefault('wuttaweb.logo_url', '/testing/other.png')
|
||||
url = handler.get_main_logo_url(self.request)
|
||||
self.assertEqual(url, '/testing/other.png')
|
||||
|
||||
def test_menu_handler_default(self):
|
||||
menus = self.handler.get_menu_handler()
|
||||
handler = self.make_handler()
|
||||
menus = handler.get_menu_handler()
|
||||
self.assertIsInstance(menus, MenuHandler)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import fanstatic
|
||||
from pyramid import testing
|
||||
|
||||
from wuttjamaican.conf import WuttaConfig
|
||||
|
@ -66,6 +67,10 @@ class WebTestCase(DataTestCase):
|
|||
'pyramid.events.BeforeRender')
|
||||
self.pyramid_config.include('wuttaweb.static')
|
||||
|
||||
# nb. mock out fanstatic env..good enough for now to avoid errors..
|
||||
needed = fanstatic.init_needed()
|
||||
self.request.environ[fanstatic.NEEDED] = needed
|
||||
|
||||
# setup new request w/ anonymous user
|
||||
event = MagicMock(request=self.request)
|
||||
subscribers.new_request(event)
|
||||
|
|
|
@ -1028,7 +1028,7 @@ class TestMasterView(WebTestCase):
|
|||
self.session.commit()
|
||||
|
||||
def get_instance():
|
||||
setting = self.session.query(model.Setting).get('foo.bar')
|
||||
setting = self.session.get(model.Setting, 'foo.bar')
|
||||
return {
|
||||
'name': setting.name,
|
||||
'value': setting.value,
|
||||
|
@ -1092,7 +1092,7 @@ class TestMasterView(WebTestCase):
|
|||
self.assertEqual(self.session.query(model.Setting).count(), 1)
|
||||
|
||||
def get_instance():
|
||||
setting = self.session.query(model.Setting).get('foo.bar')
|
||||
setting = self.session.get(model.Setting, 'foo.bar')
|
||||
return {
|
||||
'name': setting.name,
|
||||
'value': setting.value,
|
||||
|
|
Loading…
Reference in a new issue