# HG changeset patch # User Thierry Florac # Date 1545990311 -3600 # Node ID 9e2e4c39f732e826e35c1306c79683189c926a4f # Parent cf5d41f5e2104336659da28cfdada7d126920e9f Added glossary support features diff -r cf5d41f5e210 -r 9e2e4c39f732 src/pyams_default_theme/features/glossary/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_default_theme/features/glossary/__init__.py Fri Dec 28 10:45:11 2018 +0100 @@ -0,0 +1,168 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + +import logging +import pickle + +from pyramid.decorator import reify +from pyramid.exceptions import NotFound +from pyramid.interfaces import IRequest +from pyramid.view import view_config +from zope.interface import implementer + +from pyams_cache.beaker import get_cache +from pyams_content.component.theme import ITagsManager +from pyams_content.features.glossary import get_glossary_automaton, GLOSSARY_CACHE_REGION, GLOSSARY_CACHE_NAME, \ + GLOSSARY_CACHE_KEY +from pyams_default_theme.features.thesaurus.interfaces import IThesaurusTermRenderer +from pyams_default_theme.page import BaseIndexPage +from pyams_pagelet.pagelet import pagelet_config +from pyams_skin.interfaces import IDialog +from pyams_skin.layer import IPyAMSUserLayer +from pyams_template.template import template_config +from pyams_thesaurus.interfaces.thesaurus import IThesaurus +from pyams_utils.adapter import ContextRequestAdapter, adapter_config +from pyams_utils.interfaces import VIEW_PERMISSION +from pyams_utils.interfaces.text import IHTMLRenderer +from pyams_utils.registry import query_utility + +logger = logging.getLogger("PyAMS (default theme)") + + +# +# Full glossary terms page +# + +@pagelet_config(name='get-glossary.html', layer=IPyAMSUserLayer, permission=VIEW_PERMISSION) +@template_config(template='templates/glossary.pt', layer=IPyAMSUserLayer) +class GlossaryView(BaseIndexPage): + """Glossary view""" + + @reify + def thesaurus(self): + tags_manager = ITagsManager(self.request.root) + if not tags_manager.enable_glossary: + raise NotFound("Glossary access is not defined.") + thesaurus = query_utility(IThesaurus, name=tags_manager.glossary_thesaurus_name) + if thesaurus is None: + raise NotFound("Glossary thesaurus can't be found.") + return thesaurus + + @property + def title(self): + return self.thesaurus.title + + def update(self): + super(GlossaryView, self).update() + self.request.context = self.thesaurus + + +# +# Single glossary term views +# + +@template_config(template='templates/glossary-term.pt', layer=IPyAMSUserLayer) +class BaseGlossaryTermView(object): + """Base glossary term view""" + + @reify + def thesaurus(self): + tags_manager = ITagsManager(self.request.root) + if not tags_manager.enable_glossary: + raise NotFound("Glossary access is not defined.") + thesaurus = query_utility(IThesaurus, name=tags_manager.glossary_thesaurus_name) + if thesaurus is None: + raise NotFound("Glossary thesaurus can't be found.") + return thesaurus + + @property + def title(self): + return self.thesaurus.title + + @reify + def term(self): + term = self.request.params.get('term') + if not term: + raise NotFound("No glossary term defined.") + term = self.thesaurus.terms.get(term) + if term is None: + raise NotFound("Can't find specified term.") + return term + + @property + def renderers(self): + registry = self.request.registry + for name, renderer in sorted(registry.getAdapters((self.term, self.request, self), IThesaurusTermRenderer), + key=lambda x: x[1].weight): + renderer.update() + yield renderer + + @property + def is_dialog(self): + return IDialog.providedBy(self) + + +@pagelet_config(name='get-glossary-term-page.html', layer=IPyAMSUserLayer, permission=VIEW_PERMISSION) +class GlossaryTermPage(BaseGlossaryTermView, BaseIndexPage): + """Full-page glossary term view""" + + def update(self): + super(GlossaryTermPage, self).update() + self.request.context = self.term + + +@pagelet_config(name='get-glossary-term.html', layer=IPyAMSUserLayer, permission=VIEW_PERMISSION) +@implementer(IDialog) +class GlossaryTermView(BaseGlossaryTermView): + """Dialog glossary term view""" + + dialog_class = 'modal-large' + + +@adapter_config(name='glossary', context=(str, IRequest), provides=IHTMLRenderer) +class GlossaryHTMLRenderer(ContextRequestAdapter): + """Glossary HTML renderer""" + + def render(self): + source = self.context + glossary_cache = get_cache(GLOSSARY_CACHE_REGION, GLOSSARY_CACHE_NAME) + try: + logger.debug("Loading glossary automaton...") + automaton = glossary_cache.get_value(GLOSSARY_CACHE_KEY) + except KeyError: + logger.debug("Automaton not found, loading new one...") + automaton = get_glossary_automaton(self.request.root) + else: + automaton = pickle.loads(automaton) + if automaton is None: + logger.debug("Missing automaton, skipping HTML conversion") + return source + logger.debug("Automaton loaded with {} terms!".format(len(automaton))) + found, last_found_index = set(), 0 + marker = '{}' + marker_length = len(marker) - 2 + for position, text in automaton.iter(self.context): + if (text in found) or (position <= last_found_index): + continue + logger.debug("Found term '{}' at position {}".format(text, position)) + count = len(found) + offset = marker_length * count + start_offset = position + offset - len(text) + if source[start_offset] in '<>': + logger.debug("Already tagged term, skipping...") + continue + source = source[0:start_offset + 1] + marker.format(text) + source[position + offset + 1:] + found.add(text) + last_found_index = position + return source diff -r cf5d41f5e210 -r 9e2e4c39f732 src/pyams_default_theme/features/glossary/sitemap.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_default_theme/features/glossary/sitemap.py Fri Dec 28 10:45:11 2018 +0100 @@ -0,0 +1,49 @@ +# +# Copyright (c) 2008-2018 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + +from pyramid.view import view_config + +from pyams_content.component.theme import ITagsManager +from pyams_content.root import ISiteRoot +from pyams_default_theme.features.sitemap import ISitemapExtension +from pyams_skin.layer import IPyAMSUserLayer +from pyams_thesaurus.interfaces.thesaurus import IThesaurus +from pyams_utils.adapter import ContextRequestAdapter, adapter_config +from pyams_utils.registry import query_utility + + +@adapter_config(name='glossary', context=(ISiteRoot, IPyAMSUserLayer), provides=ISitemapExtension) +class SiteRootGlossarySitemap(ContextRequestAdapter): + """Site root glossary sitemap extension""" + + @property + def source(self): + tags_manager = ITagsManager(self.context) + if tags_manager.enable_glossary: + thesaurus = query_utility(IThesaurus, name=tags_manager.glossary_thesaurus_name) + if thesaurus is not None: + return thesaurus + + +@view_config(name='sitemap.xml', context=IThesaurus, request_type=IPyAMSUserLayer, + renderer='templates/glossary-sitemap.pt') +class GlossarySitemapView(object): + """Glossary sitemap view""" + + def __init__(self, request): + self.request = request + + def __call__(self): + self.request.response.content_type = 'text/xml' + return {} diff -r cf5d41f5e210 -r 9e2e4c39f732 src/pyams_default_theme/features/glossary/templates/glossary-sitemap.pt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_default_theme/features/glossary/templates/glossary-sitemap.pt Fri Dec 28 10:45:11 2018 +0100 @@ -0,0 +1,10 @@ + + + + + ${url}/get-glossary-term-page.html?term=${term.label} + ${tales:timestamp(term, 'iso')} + + + diff -r cf5d41f5e210 -r 9e2e4c39f732 src/pyams_default_theme/features/glossary/templates/glossary-term.pt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_default_theme/features/glossary/templates/glossary-term.pt Fri Dec 28 10:45:11 2018 +0100 @@ -0,0 +1,31 @@ + diff -r cf5d41f5e210 -r 9e2e4c39f732 src/pyams_default_theme/features/glossary/templates/glossary.pt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_default_theme/features/glossary/templates/glossary.pt Fri Dec 28 10:45:11 2018 +0100 @@ -0,0 +1,24 @@ + diff -r cf5d41f5e210 -r 9e2e4c39f732 src/pyams_default_theme/features/thesaurus/templates/glossary.pt --- a/src/pyams_default_theme/features/thesaurus/templates/glossary.pt Fri Dec 28 10:44:50 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -