hallo.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import json
  2. from collections import OrderedDict
  3. from django.forms import Media, widgets
  4. from wagtail.admin.edit_handlers import RichTextFieldPanel
  5. from wagtail.admin.rich_text.converters.editor_html import EditorHTMLConverter
  6. from wagtail.core.rich_text import features
  7. from wagtail.utils.widgets import WidgetWithScript
  8. class HalloPlugin:
  9. def __init__(self, **kwargs):
  10. self.name = kwargs.get('name', None)
  11. self.options = kwargs.get('options', {})
  12. self.js = kwargs.get('js', None)
  13. self.css = kwargs.get('css', None)
  14. self.order = kwargs.get('order', 100)
  15. def construct_plugins_list(self, plugins):
  16. if self.name is not None:
  17. plugins[self.name] = self.options
  18. @property
  19. def media(self):
  20. return Media(js=self.js, css=self.css)
  21. class HalloFormatPlugin(HalloPlugin):
  22. def __init__(self, **kwargs):
  23. kwargs.setdefault('name', 'halloformat')
  24. kwargs.setdefault('order', 10)
  25. self.format_name = kwargs['format_name']
  26. super().__init__(**kwargs)
  27. def construct_plugins_list(self, plugins):
  28. plugins.setdefault(self.name, {'formattings': {
  29. 'bold': False, 'italic': False, 'strikeThrough': False, 'underline': False
  30. }})
  31. plugins[self.name]['formattings'][self.format_name] = True
  32. class HalloHeadingPlugin(HalloPlugin):
  33. def __init__(self, **kwargs):
  34. kwargs.setdefault('name', 'halloheadings')
  35. kwargs.setdefault('order', 20)
  36. self.element = kwargs.pop('element')
  37. super().__init__(**kwargs)
  38. def construct_plugins_list(self, plugins):
  39. plugins.setdefault(self.name, {'formatBlocks': []})
  40. plugins[self.name]['formatBlocks'].append(self.element)
  41. class HalloListPlugin(HalloPlugin):
  42. def __init__(self, **kwargs):
  43. kwargs.setdefault('name', 'hallolists')
  44. kwargs.setdefault('order', 40)
  45. self.list_type = kwargs['list_type']
  46. super().__init__(**kwargs)
  47. def construct_plugins_list(self, plugins):
  48. plugins.setdefault(self.name, {'lists': {
  49. 'ordered': False, 'unordered': False
  50. }})
  51. plugins[self.name]['lists'][self.list_type] = True
  52. # Plugins which are always imported, and cannot be enabled/disabled via 'features'
  53. CORE_HALLO_PLUGINS = [
  54. HalloPlugin(name='halloreundo', order=50),
  55. HalloPlugin(name='hallorequireparagraphs', js=[
  56. 'wagtailadmin/js/hallo-plugins/hallo-requireparagraphs.js',
  57. ]),
  58. HalloHeadingPlugin(element='p')
  59. ]
  60. class HalloRichTextArea(WidgetWithScript, widgets.Textarea):
  61. # this class's constructor accepts a 'features' kwarg
  62. accepts_features = True
  63. def get_panel(self):
  64. return RichTextFieldPanel
  65. def __init__(self, *args, **kwargs):
  66. self.options = kwargs.pop('options', None)
  67. self.features = kwargs.pop('features', None)
  68. if self.features is None:
  69. self.features = features.get_default_features()
  70. self.converter = EditorHTMLConverter(self.features)
  71. # construct a list of plugin objects, by querying the feature registry
  72. # and keeping the non-null responses from get_editor_plugin
  73. self.plugins = CORE_HALLO_PLUGINS + list(filter(None, [
  74. features.get_editor_plugin('hallo', feature_name)
  75. for feature_name in self.features
  76. ]))
  77. self.plugins.sort(key=lambda plugin: plugin.order)
  78. super().__init__(*args, **kwargs)
  79. def render(self, name, value, attrs=None):
  80. if value is None:
  81. translated_value = None
  82. else:
  83. translated_value = self.converter.from_database_format(value)
  84. return super().render(name, translated_value, attrs)
  85. def render_js_init(self, id_, name, value):
  86. if self.options is not None and 'plugins' in self.options:
  87. # explicit 'plugins' config passed in options, so use that
  88. plugin_data = self.options['plugins']
  89. else:
  90. plugin_data = OrderedDict()
  91. for plugin in self.plugins:
  92. plugin.construct_plugins_list(plugin_data)
  93. return "makeHalloRichTextEditable({0}, {1});".format(
  94. json.dumps(id_), json.dumps(plugin_data)
  95. )
  96. def value_from_datadict(self, data, files, name):
  97. original_value = super().value_from_datadict(data, files, name)
  98. if original_value is None:
  99. return None
  100. return self.converter.to_database_format(original_value)
  101. @property
  102. def media(self):
  103. media = Media(js=[
  104. 'wagtailadmin/js/vendor/hallo.js',
  105. 'wagtailadmin/js/hallo-bootstrap.js',
  106. ], css={
  107. 'all': ['wagtailadmin/css/panels/hallo.css']
  108. })
  109. for plugin in self.plugins:
  110. media += plugin.media
  111. return media