src/pyams_content/zmi/tinymce.py
changeset 1155 f944a4efb65b
parent 1070 ea0c7ac589c4
equal deleted inserted replaced
1154:8e16cc0efae5 1155:f944a4efb65b
    10 # FOR A PARTICULAR PURPOSE.
    10 # FOR A PARTICULAR PURPOSE.
    11 #
    11 #
    12 
    12 
    13 __docformat__ = 'restructuredtext'
    13 __docformat__ = 'restructuredtext'
    14 
    14 
       
    15 import json
    15 
    16 
    16 # import standard library
    17 import tinycss2
       
    18 from tinycss2.ast import Comment, IdentToken, QualifiedRule, WhitespaceToken
       
    19 from zope.dublincore.interfaces import IZopeDublinCore
    17 
    20 
    18 # import interfaces
       
    19 from pyams_content.zmi import pyams_content
    21 from pyams_content.zmi import pyams_content
    20 from pyams_form.interfaces.form import IForm
    22 from pyams_form.interfaces.form import IForm
       
    23 from pyams_skin.interfaces import ISkinnable
    21 from pyams_skin.interfaces.tinymce import ITinyMCEConfiguration
    24 from pyams_skin.interfaces.tinymce import ITinyMCEConfiguration
    22 from pyams_skin.layer import IPyAMSLayer
    25 from pyams_skin.layer import IPyAMSLayer
       
    26 from pyams_utils.adapter import ContextRequestAdapter, adapter_config
       
    27 from pyams_utils.fanstatic import get_resource_path
       
    28 from pyams_utils.traversing import get_parent
       
    29 from pyams_utils.url import absolute_url
    23 
    30 
    24 # import packages
    31 
    25 from pyams_utils.adapter import adapter_config, ContextRequestAdapter
    32 def parse_css(data):
    26 from pyams_utils.fanstatic import get_resource_path
    33     """Parse given CSS data to extract required styles"""
       
    34     result = []
       
    35     label = None
       
    36     rules, encoding = tinycss2.parse_stylesheet_bytes(data, skip_whitespace=True)
       
    37     for rule in rules:
       
    38         if isinstance(rule, Comment):
       
    39             label = rule.value.strip()
       
    40             if ':' in label:
       
    41                 label, selector = label.split(':', 1)
       
    42         elif isinstance(rule, QualifiedRule):
       
    43             ident_token = class_token = None
       
    44             tokens = iter(rule.prelude)
       
    45             for token in tokens:
       
    46                 if isinstance(token, IdentToken):
       
    47                     ident_token = token
       
    48                     break
       
    49             for token in tokens:
       
    50                 if isinstance(token, IdentToken):
       
    51                     class_token = token
       
    52                     break
       
    53             token_id = ''.join((token.value for token in rule.prelude
       
    54                                 if not isinstance(token, WhitespaceToken)))
       
    55             result.append({
       
    56                 'title': label or token_id,
       
    57                 'classes': class_token.value,
       
    58                 'block': ident_token.lower_value if ident_token and
       
    59                     (ident_token.lower_value in ('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'section', 'p', 'div',
       
    60                                                  'blockquote', 'pre')) else None,
       
    61                 'inline': ident_token.lower_value if ident_token and
       
    62                     (ident_token.lower_value in ('span',)) else None
       
    63             })
       
    64             label = None
       
    65     return result
    27 
    66 
    28 
    67 
    29 @adapter_config(context=(IForm, IPyAMSLayer), provides=ITinyMCEConfiguration)
    68 @adapter_config(context=(IForm, IPyAMSLayer), provides=ITinyMCEConfiguration)
    30 class TinyMCEEditorConfiguration(ContextRequestAdapter):
    69 class TinyMCEEditorConfiguration(ContextRequestAdapter):
    31     """TinyMCE editor configuration"""
    70     """TinyMCE editor configuration"""
    32 
    71 
    33     @property
    72     @property
    34     def configuration(self):
    73     def configuration(self):
    35         return {
    74         result = {
    36             'ams-plugins': 'pyams_content',
    75             'ams-plugins': 'pyams_content',
    37             'ams-plugin-pyams_content-src': get_resource_path(pyams_content),
    76             'ams-plugin-pyams_content-src': get_resource_path(pyams_content),
    38             'ams-plugin-pyams_content-async': 'false',
    77             'ams-plugin-pyams_content-async': 'false',
    39             'ams-tinymce-init-callback': 'PyAMS_content.TinyMCE.initEditor'
    78             'ams-tinymce-init-callback': 'PyAMS_content.TinyMCE.initEditor'
    40         }
    79         }
       
    80         skinnable = get_parent(self.context.context, ISkinnable)
       
    81         if skinnable is not None:
       
    82             editor_stylesheet = skinnable.editor_stylesheet
       
    83             if editor_stylesheet:
       
    84                 modified = IZopeDublinCore(editor_stylesheet).modified
       
    85                 result['ams-tinymce-content-css'] = absolute_url(editor_stylesheet, self.request,
       
    86                                                                  query={'_': modified.timestamp()})
       
    87                 result['ams-tinymce-editor-styles'] = json.dumps(parse_css(editor_stylesheet.data))
       
    88         return result