--- /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 <tflorac AT ulthar.net>
+# 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 = '<span class="thesaurus-term">{}</span>'
+ 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
--- /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 <tflorac AT ulthar.net>
+# 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 {}
--- /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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
+ tal:define="url tales:absolute_url(request.root)">
+ <tal:loop repeat="term context.terms.values()">
+ <url tal:condition="term.status == 'published'">
+ <loc>${url}/get-glossary-term-page.html?term=${term.label}</loc>
+ <lastmod>${tales:timestamp(term, 'iso')}</lastmod>
+ </url>
+ </tal:loop>
+</urlset>
--- /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 @@
+<div class="modal-content" i18n:domain="pyams_default_theme"
+ tal:define="term view.term">
+ <div class="modal-header">
+ <button type="button" class="close"
+ data-dismiss="modal" aria-hidden="true">
+ <i class="fa fa-fw fa-times-circle"></i>
+ </button>
+ <h3 class="modal-title">${view.title}</h3>
+ </div>
+ <div class="modal-body no-padding">
+ <div class="ams-form">
+ <div class="modal-viewport">
+ <fieldset>
+ <legend>
+ <i></i>
+ ${term.alt or term.label}
+ </legend>
+ <div class="clearfix"></div>
+ <tal:loop repeat="renderer view.renderers">
+ ${structure:renderer.render()}
+ </tal:loop>
+ </fieldset>
+ </div>
+ <footer tal:condition="view.is_dialog">
+ <button type="button" data-dismiss="modal"
+ class="btn close-widget closebutton-field"
+ value="Cancel" i18n:translate="">Close</button>
+ </footer>
+ </div>
+ </div>
+</div>
--- /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 @@
+<div class="modal-content" i18n:domain="pyams_default_theme">
+ <div class="modal-header">
+ <h3 class="modal-title">${view.title}</h3>
+ </div>
+ <div class="modal-body no-padding">
+ <div class="ams-form">
+ <div class="modal-viewport">
+ <div class="clearfix spacer"></div>
+ <div class="col-md-12"
+ tal:define="description view.thesaurus.description"
+ tal:condition="description">
+ ${structure:tales:html(description, 'markdown')}
+ </div>
+ <div class="clearfix spacer"></div>
+ <tal:loop repeat="term view.thesaurus.terms.values()">
+ <div class="col-md-3 col-sm-4 col-xs-6" tal:condition="term.status == 'published'">
+ <span class="thesaurus-term">${term.alt or term.label}</span>
+ </div>
+ </tal:loop>
+ <div class="clearfix spacer"></div>
+ </div>
+ </div>
+ </div>
+</div>
--- 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 @@
-<div class="modal-content" i18n:domain="pyams_default_theme"
- tal:define="term view.term">
- <div class="modal-header">
- <button type="button" class="close"
- data-dismiss="modal" aria-hidden="true">
- <i class="fa fa-fw fa-times-circle"></i>
- </button>
- <h3 class="modal-title"></h3>
- </div>
- <div class="modal-body no-padding">
- <div class="modal-viewport">
- <fieldset>
- <legend>
- <i></i>
- ${term.alt or term.label}
- </legend>
- <div class="clearfix"></div>
- <tal:loop repeat="renderer view.renderers">
- ${structure:renderer.render()}
- </tal:loop>
- </fieldset>
- </div>
- <footer>
- <button type="button" data-dismiss="modal"
- class="btn close-widget closebutton-field"
- value="Cancel" i18n:translate="">Close</button>
- </footer>
- </div>
-</div>