src/pyams_content/component/paragraph/html.py
changeset 1398 fc32ec8a8f53
parent 1341 09247c61f985
equal deleted inserted replaced
1397:d05fc6aa4217 1398:fc32ec8a8f53
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    10 # FOR A PARTICULAR PURPOSE.
    10 # FOR A PARTICULAR PURPOSE.
    11 #
    11 #
    12 
    12 
    13 import re
       
    14 
       
    15 from pyquery import PyQuery
       
    16 from pyramid.events import subscriber
    13 from pyramid.events import subscriber
    17 from pyramid.threadlocal import get_current_registry
       
    18 from zope.interface import implementer
    14 from zope.interface import implementer
    19 from zope.lifecycleevent import ObjectCreatedEvent
       
    20 from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent
    15 from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent
    21 from zope.schema.fieldproperty import FieldProperty
    16 from zope.schema.fieldproperty import FieldProperty
    22 
    17 
    23 from pyams_content.component.association.interfaces import IAssociationContainer
    18 from pyams_content.component.extfile.interfaces import IExtFileContainerTarget
    24 from pyams_content.component.extfile.interfaces import IBaseExtFile, IExtFileContainerTarget
       
    25 from pyams_content.component.illustration.interfaces import IIllustrationTarget
    19 from pyams_content.component.illustration.interfaces import IIllustrationTarget
    26 from pyams_content.component.links import ExternalLink, InternalLink, MailtoLink
    20 from pyams_content.component.links.interfaces import ILinkContainerTarget
    27 from pyams_content.component.links.interfaces import IExternalLink, IInternalLink, \
       
    28     ILinkContainerTarget, IMailtoLink
       
    29 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, \
    21 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, \
    30     BaseParagraphFactory
    22     BaseParagraphFactory
       
    23 from pyams_content.component.paragraph.association import check_associations
    31 from pyams_content.component.paragraph.interfaces import IParagraphFactory
    24 from pyams_content.component.paragraph.interfaces import IParagraphFactory
    32 from pyams_content.component.paragraph.interfaces.html import HTML_PARAGRAPH_NAME, \
    25 from pyams_content.component.paragraph.interfaces.html import HTML_PARAGRAPH_NAME, \
    33     HTML_PARAGRAPH_RENDERERS, HTML_PARAGRAPH_TYPE, IHTMLParagraph, IRawParagraph, \
    26     HTML_PARAGRAPH_RENDERERS, HTML_PARAGRAPH_TYPE, IHTMLParagraph, IRawParagraph, \
    34     RAW_PARAGRAPH_NAME, RAW_PARAGRAPH_RENDERERS, RAW_PARAGRAPH_TYPE
    27     RAW_PARAGRAPH_NAME, RAW_PARAGRAPH_RENDERERS, RAW_PARAGRAPH_TYPE
    35 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_LANG_VALUE, \
    28 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_LANG_VALUE, \
    36     MISSING_VALUE
    29     MISSING_VALUE
    37 from pyams_content.features.renderer import RenderersVocabulary
    30 from pyams_content.features.renderer import RenderersVocabulary
    38 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
    31 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
    39 from pyams_sequence.interfaces import ISequentialIntIds
       
    40 from pyams_utils.adapter import adapter_config
    32 from pyams_utils.adapter import adapter_config
    41 from pyams_utils.factory import factory_config
    33 from pyams_utils.factory import factory_config
    42 from pyams_utils.registry import get_utility, utility_config
    34 from pyams_utils.registry import get_utility, utility_config
    43 from pyams_utils.request import check_request
       
    44 from pyams_utils.traversing import get_parent
    35 from pyams_utils.traversing import get_parent
    45 from pyams_utils.url import absolute_url
       
    46 from pyams_utils.vocabulary import vocabulary_config
    36 from pyams_utils.vocabulary import vocabulary_config
    47 
    37 
    48 
    38 
    49 __docformat__ = 'restructuredtext'
    39 __docformat__ = 'restructuredtext'
    50 
    40 
   127 
   117 
   128     name = HTML_PARAGRAPH_NAME
   118     name = HTML_PARAGRAPH_NAME
   129     content_type = HTMLParagraph
   119     content_type = HTMLParagraph
   130 
   120 
   131 
   121 
   132 FULL_EMAIL = re.compile('(.*) \<(.*)\>')
       
   133 
       
   134 
       
   135 def check_associations(context, body, lang, notify=True):
       
   136     """Check for link associations from HTML content"""
       
   137     registry = get_current_registry()
       
   138     associations = IAssociationContainer(context)
       
   139     html = PyQuery('<html>{0}</html>'.format(body))
       
   140     for link in html('a[href]'):
       
   141         link_info = None
       
   142         has_link = False
       
   143         href = link.attrib['href']
       
   144         if href.startswith('oid://'):
       
   145             sequence = get_utility(ISequentialIntIds)
       
   146             oid = sequence.get_full_oid(href.split('//', 1)[1])
       
   147             for association in associations.values():
       
   148                 internal_info = IInternalLink(association, None)
       
   149                 if (internal_info is not None) and (internal_info.reference == oid):
       
   150                     has_link = True
       
   151                     break
       
   152             if not has_link:
       
   153                 link_info = InternalLink()
       
   154                 link_info.visible = False
       
   155                 link_info.reference = oid
       
   156                 link_info.title = {lang: link.attrib.get('title') or link.text}
       
   157         elif href.startswith('mailto:'):
       
   158             name = None
       
   159             email = href[7:]
       
   160             if '<' in email:
       
   161                 groups = FULL_EMAIL.findall(email)
       
   162                 if groups:
       
   163                     name, email = groups[0]
       
   164             for association in associations.values():
       
   165                 mailto_info = IMailtoLink(association, None)
       
   166                 if (mailto_info is not None) and (mailto_info.address == email):
       
   167                     has_link = True
       
   168                     break
       
   169             if not has_link:
       
   170                 link_info = MailtoLink()
       
   171                 link_info.visible = False
       
   172                 link_info.address = email
       
   173                 link_info.address_name = name or email
       
   174                 link_info.title = {lang: link.attrib.get('title') or link.text}
       
   175         elif href.startswith('http://') or href.startswith('https://'):
       
   176             for association in associations.values():
       
   177                 external_info = IExternalLink(association, None)
       
   178                 if (external_info is not None) and (external_info.url == href):
       
   179                     has_link = True
       
   180                     break
       
   181                 else:
       
   182                     extfile_info = IBaseExtFile(association, None)
       
   183                     if extfile_info is not None:
       
   184                         request = check_request()
       
   185                         extfile_url = absolute_url(
       
   186                             II18n(extfile_info).query_attribute('data', request=request),
       
   187                             request=request)
       
   188                         if extfile_url.endswith(href):
       
   189                             has_link = True
       
   190                             break
       
   191             if not has_link:
       
   192                 link_info = ExternalLink()
       
   193                 link_info.visible = False
       
   194                 link_info.url = href
       
   195                 link_info.title = {lang: link.attrib.get('title') or link.text}
       
   196         if link_info is not None:
       
   197             registry.notify(ObjectCreatedEvent(link_info))
       
   198             associations.append(link_info, notify=notify)
       
   199 
       
   200 
       
   201 @subscriber(IObjectAddedEvent, context_selector=IHTMLParagraph)
   122 @subscriber(IObjectAddedEvent, context_selector=IHTMLParagraph)
   202 def handle_added_html_paragraph(event):
   123 def handle_added_html_paragraph(event):
   203     """Check for new associations from added paragraph"""
   124     """Check for new associations from added paragraph"""
   204     paragraph = event.object
   125     paragraph = event.object
   205     for lang, body in (paragraph.body or {}).items():
   126     for lang, body in (paragraph.body or {}).items():