src/pyams_content/component/paragraph/html.py
changeset 140 67bad9f880ee
parent 0 7c0001cacf8e
child 192 8a16d2f507d7
--- a/src/pyams_content/component/paragraph/html.py	Mon Sep 11 14:53:15 2017 +0200
+++ b/src/pyams_content/component/paragraph/html.py	Mon Sep 11 14:54:30 2017 +0200
@@ -14,25 +14,134 @@
 
 
 # import standard library
+import re
 
 # import interfaces
-from pyams_content.component.extfile.interfaces import IExtFileLinksContainerTarget
-from pyams_content.component.gallery.interfaces import IGalleryLinksContainerTarget
-from pyams_content.component.links.interfaces import ILinkLinksContainerTarget
-from pyams_content.component.paragraph.interfaces import IHTMLParagraph
+from pyams_content.component.association.interfaces import IAssociationContainer
+from pyams_content.component.extfile.interfaces import IExtFileContainerTarget, IBaseExtFile
+from pyams_content.component.illustration.interfaces import IIllustrationTarget
+from pyams_content.component.links.interfaces import ILinkContainerTarget, IInternalLink, IExternalLink, IMailtoLink
+from pyams_content.component.paragraph.interfaces import IParagraphFactory
+from pyams_content.component.paragraph.interfaces.html import IHTMLParagraph
+from pyams_i18n.interfaces import II18n
+from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent
 
 # import packages
+from pyams_content.component.links import InternalLink, ExternalLink, MailtoLink
 from pyams_content.component.paragraph import BaseParagraph
+from pyams_utils.registry import utility_config
+from pyams_utils.request import check_request
+from pyams_utils.url import absolute_url
+from pyquery import PyQuery
+from pyramid.events import subscriber
+from pyramid.threadlocal import get_current_registry
 from zope.interface import implementer
+from zope.lifecycleevent import ObjectCreatedEvent
 from zope.schema.fieldproperty import FieldProperty
 
+from pyams_content import _
+
 
 #
 # HTML paragraph
 #
 
-@implementer(IHTMLParagraph, IExtFileLinksContainerTarget, ILinkLinksContainerTarget, IGalleryLinksContainerTarget)
+@implementer(IHTMLParagraph, IIllustrationTarget, IExtFileContainerTarget, ILinkContainerTarget)
 class HTMLParagraph(BaseParagraph):
     """HTML paragraph"""
 
+    icon_class = 'fa-html5'
+    icon_hint = _("HTML paragraph")
+
     body = FieldProperty(IHTMLParagraph['body'])
+
+
+@utility_config(name='HTML paragraph', provides=IParagraphFactory)
+class HTMLParagraphFactory(object):
+    """HTML paragraph factory"""
+
+    name = _("HTML paragraph")
+    content_type = HTMLParagraph
+
+
+FULL_EMAIL = re.compile('(.*) \<(.*)\>')
+
+
+def check_associations(context, body, lang, notify=True):
+    """Check for link associations from HTML content"""
+    registry = get_current_registry()
+    associations = IAssociationContainer(context)
+    html = PyQuery('<html>{0}</html>'.format(body))
+    for link in html('a[href]'):
+        link_info = None
+        has_link = False
+        href = link.attrib['href']
+        if href.startswith('oid://'):
+            oid = href.split('//', 1)[1]
+            for association in associations.values():
+                internal_info = IInternalLink(association, None)
+                if (internal_info is not None) and (internal_info.reference == oid):
+                    has_link = True
+                    break
+            if not has_link:
+                link_info = InternalLink()
+                link_info.visible = False
+                link_info.reference = oid
+                link_info.title = {lang: link.attrib.get('title') or link.text}
+        elif href.startswith('mailto:'):
+            name = None
+            email = href[7:]
+            if '<' in email:
+                groups = FULL_EMAIL.findall(email)
+                if groups:
+                    name, email = groups[0]
+            for association in associations.values():
+                mailto_info = IMailtoLink(association, None)
+                if (mailto_info is not None) and (mailto_info.address == email):
+                    has_link = True
+                    break
+            if not has_link:
+                link_info = MailtoLink()
+                link_info.visible = False
+                link_info.address = email
+                link_info.address_name = name or email
+                link_info.title = {lang: link.attrib.get('title') or link.text}
+        else:
+            for association in associations.values():
+                external_info = IExternalLink(association, None)
+                if (external_info is not None) and (external_info.url == href):
+                    has_link = True
+                    break
+                else:
+                    extfile_info = IBaseExtFile(association, None)
+                    if extfile_info is not None:
+                        request = check_request()
+                        extfile_url = absolute_url(II18n(extfile_info).query_attribute('data', request=request),
+                                                   request=request)
+                        if extfile_url.endswith(href):
+                            has_link = True
+                            break
+            if not has_link:
+                link_info = ExternalLink()
+                link_info.visible = False
+                link_info.url = href
+                link_info.title = {lang: link.attrib.get('title') or link.text}
+        if link_info is not None:
+            registry.notify(ObjectCreatedEvent(link_info))
+            associations.append(link_info, notify=notify)
+
+
+@subscriber(IObjectAddedEvent, context_selector=IHTMLParagraph)
+def handle_added_html_paragraph(event):
+    """Check for new associations from added paragraph"""
+    paragraph = event.object
+    for lang, body in (paragraph.body or {}).items():
+        check_associations(paragraph, body, lang, notify=False)
+
+
+@subscriber(IObjectModifiedEvent, context_selector=IHTMLParagraph)
+def handle_modified_html_paragraph(event):
+    """Check for new associations from modified paragraph"""
+    paragraph = event.object
+    for lang, body in (paragraph.body or {}).items():
+        check_associations(paragraph, body, lang, notify=False)