servicetrees 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. servicetrees/__init__.py +3 -0
  2. servicetrees/ckeditor/README.md +46 -0
  3. servicetrees/ckeditor/SETTINGS.md +10 -0
  4. servicetrees/ckeditor/__init__.py +0 -0
  5. servicetrees/ckeditor/admin.py +1 -0
  6. servicetrees/ckeditor/apps.py +7 -0
  7. servicetrees/ckeditor/conf.py +14 -0
  8. servicetrees/ckeditor/sanitize.py +87 -0
  9. servicetrees/ckeditor/static/ckeditor/css/ckeditor.css +497 -0
  10. servicetrees/ckeditor/static/ckeditor/js/ckeditor-custom.js +1332 -0
  11. servicetrees/ckeditor/tests.py +36 -0
  12. servicetrees/ckeditor/urls.py +6 -0
  13. servicetrees/ckeditor/utils.py +42 -0
  14. servicetrees/ckeditor/widgets.py +72 -0
  15. servicetrees/clientip.py +32 -0
  16. servicetrees/conf.py +32 -0
  17. servicetrees/contentref.py +35 -0
  18. servicetrees/cookie_consent/README.md +65 -0
  19. servicetrees/cookie_consent/SETTINGS.md +15 -0
  20. servicetrees/cookie_consent/__init__.py +0 -0
  21. servicetrees/cookie_consent/admin.py +136 -0
  22. servicetrees/cookie_consent/apps.py +7 -0
  23. servicetrees/cookie_consent/conf.py +53 -0
  24. servicetrees/cookie_consent/context_processors.py +58 -0
  25. servicetrees/cookie_consent/migrations/0001_initial.py +66 -0
  26. servicetrees/cookie_consent/migrations/__init__.py +0 -0
  27. servicetrees/cookie_consent/models.py +196 -0
  28. servicetrees/cookie_consent/static/cookie_consent/cookie_consent.js +972 -0
  29. servicetrees/cookie_consent/static/cookie_consent/klaro_init.js +1024 -0
  30. servicetrees/cookie_consent/static/cookie_consent/vendor/klaro/LICENSE +31 -0
  31. servicetrees/cookie_consent/static/cookie_consent/vendor/klaro/VERSION +1 -0
  32. servicetrees/cookie_consent/static/cookie_consent/vendor/klaro/klaro-no-css.js +2 -0
  33. servicetrees/cookie_consent/static/cookie_consent/vendor/klaro/klaro.js +2 -0
  34. servicetrees/cookie_consent/static/cookie_consent/vendor/klaro/klaro.min.css +1 -0
  35. servicetrees/cookie_consent/templatetags/__init__.py +0 -0
  36. servicetrees/cookie_consent/templatetags/cookie_consent_tags.py +26 -0
  37. servicetrees/cookie_consent/urls.py +11 -0
  38. servicetrees/cookie_consent/utils.py +476 -0
  39. servicetrees/cookie_consent/views.py +133 -0
  40. servicetrees/displaypage/README.md +150 -0
  41. servicetrees/displaypage/SETTINGS.md +16 -0
  42. servicetrees/displaypage/__init__.py +1 -0
  43. servicetrees/displaypage/admin.py +45 -0
  44. servicetrees/displaypage/apps.py +13 -0
  45. servicetrees/displaypage/conf.py +22 -0
  46. servicetrees/displaypage/embeds.py +114 -0
  47. servicetrees/displaypage/forms.py +12 -0
  48. servicetrees/displaypage/management/__init__.py +0 -0
  49. servicetrees/displaypage/management/commands/__init__.py +0 -0
  50. servicetrees/displaypage/management/commands/refresh_video_playlists.py +16 -0
  51. servicetrees/displaypage/migrations/0001_initial.py +287 -0
  52. servicetrees/displaypage/migrations/0002_initial.py +149 -0
  53. servicetrees/displaypage/migrations/__init__.py +0 -0
  54. servicetrees/displaypage/models.py +167 -0
  55. servicetrees/displaypage/registry.py +90 -0
  56. servicetrees/displaypage/safe_links.py +31 -0
  57. servicetrees/displaypage/section_forms.py +334 -0
  58. servicetrees/displaypage/section_models.py +419 -0
  59. servicetrees/displaypage/section_views.py +56 -0
  60. servicetrees/displaypage/sections.py +121 -0
  61. servicetrees/displaypage/static/displaypage/css/displaypage.css +1008 -0
  62. servicetrees/displaypage/static/displaypage/css/section-editor.css +348 -0
  63. servicetrees/displaypage/static/displaypage/css/site.css +231 -0
  64. servicetrees/displaypage/static/displaypage/js/section-editor.js +630 -0
  65. servicetrees/displaypage/static/displaypage/js/sections.js +541 -0
  66. servicetrees/displaypage/templates/displaypage/base.html +103 -0
  67. servicetrees/displaypage/templates/displaypage/includes/carousel_controls.html +7 -0
  68. servicetrees/displaypage/templates/displaypage/includes/gallery_item.html +21 -0
  69. servicetrees/displaypage/templates/displaypage/includes/quote.html +16 -0
  70. servicetrees/displaypage/templates/displaypage/includes/section.html +12 -0
  71. servicetrees/displaypage/templates/displaypage/includes/section_header.html +8 -0
  72. servicetrees/displaypage/templates/displaypage/includes/text_media_media.html +57 -0
  73. servicetrees/displaypage/templates/displaypage/includes/text_media_text.html +5 -0
  74. servicetrees/displaypage/templates/displaypage/page.html +28 -0
  75. servicetrees/displaypage/templates/displaypage/partials/audio_section_edit_modal.html +17 -0
  76. servicetrees/displaypage/templates/displaypage/partials/columns_section_edit_modal.html +10 -0
  77. servicetrees/displaypage/templates/displaypage/partials/gallery_section_edit_modal.html +20 -0
  78. servicetrees/displaypage/templates/displaypage/partials/hero_slideshow_section_edit_modal.html +12 -0
  79. servicetrees/displaypage/templates/displaypage/partials/quotes_section_edit_modal.html +17 -0
  80. servicetrees/displaypage/templates/displaypage/partials/section_add_modal.html +14 -0
  81. servicetrees/displaypage/templates/displaypage/partials/section_edit/actions.html +7 -0
  82. servicetrees/displaypage/templates/displaypage/partials/section_edit/audio_track_row.html +15 -0
  83. servicetrees/displaypage/templates/displaypage/partials/section_edit/column_row.html +14 -0
  84. servicetrees/displaypage/templates/displaypage/partials/section_edit/field.html +10 -0
  85. servicetrees/displaypage/templates/displaypage/partials/section_edit/form_start.html +4 -0
  86. servicetrees/displaypage/templates/displaypage/partials/section_edit/formset.html +10 -0
  87. servicetrees/displaypage/templates/displaypage/partials/section_edit/gallery_item_row.html +13 -0
  88. servicetrees/displaypage/templates/displaypage/partials/section_edit/gallery_picker.html +26 -0
  89. servicetrees/displaypage/templates/displaypage/partials/section_edit/media_picker.html +22 -0
  90. servicetrees/displaypage/templates/displaypage/partials/section_edit/quote_row.html +16 -0
  91. servicetrees/displaypage/templates/displaypage/partials/section_edit/row_tools.html +9 -0
  92. servicetrees/displaypage/templates/displaypage/partials/section_edit/slide_row.html +21 -0
  93. servicetrees/displaypage/templates/displaypage/partials/section_edit/style_fields.html +9 -0
  94. servicetrees/displaypage/templates/displaypage/partials/section_edit/video_row.html +12 -0
  95. servicetrees/displaypage/templates/displaypage/partials/section_edit_controls.html +37 -0
  96. servicetrees/displaypage/templates/displaypage/partials/section_reorder_modal.html +80 -0
  97. servicetrees/displaypage/templates/displaypage/partials/text_media_section_edit_modal.html +42 -0
  98. servicetrees/displaypage/templates/displaypage/partials/text_section_edit_modal.html +13 -0
  99. servicetrees/displaypage/templates/displaypage/partials/video_gallery_section_edit_modal.html +17 -0
  100. servicetrees/displaypage/templates/displaypage/partials/visibility_picker.html +48 -0
  101. servicetrees/displaypage/templates/displaypage/sections/audio.html +43 -0
  102. servicetrees/displaypage/templates/displaypage/sections/columns.html +12 -0
  103. servicetrees/displaypage/templates/displaypage/sections/gallery.html +22 -0
  104. servicetrees/displaypage/templates/displaypage/sections/hero_slideshow.html +45 -0
  105. servicetrees/displaypage/templates/displaypage/sections/quotes.html +27 -0
  106. servicetrees/displaypage/templates/displaypage/sections/text.html +7 -0
  107. servicetrees/displaypage/templates/displaypage/sections/text_media.html +35 -0
  108. servicetrees/displaypage/templates/displaypage/sections/video_gallery.html +30 -0
  109. servicetrees/displaypage/templatetags/__init__.py +0 -0
  110. servicetrees/displaypage/templatetags/displaypage_tags.py +314 -0
  111. servicetrees/displaypage/tests.py +84 -0
  112. servicetrees/displaypage/urls.py +9 -0
  113. servicetrees/displaypage/urls_public.py +23 -0
  114. servicetrees/displaypage/video_playlists.py +391 -0
  115. servicetrees/displaypage/views.py +234 -0
  116. servicetrees/displaypage/views_public.py +160 -0
  117. servicetrees/domains/SETTINGS.md +15 -0
  118. servicetrees/domains/__init__.py +0 -0
  119. servicetrees/domains/admin.py +132 -0
  120. servicetrees/domains/apps.py +14 -0
  121. servicetrees/domains/backends.py +22 -0
  122. servicetrees/domains/conf.py +27 -0
  123. servicetrees/domains/content_registry.py +89 -0
  124. servicetrees/domains/context_processors.py +118 -0
  125. servicetrees/domains/cookie_domain_utils.py +56 -0
  126. servicetrees/domains/custom_settings.py +107 -0
  127. servicetrees/domains/date_formats.py +131 -0
  128. servicetrees/domains/management/__init__.py +1 -0
  129. servicetrees/domains/management/commands/__init__.py +1 -0
  130. servicetrees/domains/management/commands/align_domains_to_base.py +108 -0
  131. servicetrees/domains/middleware.py +239 -0
  132. servicetrees/domains/migrations/0001_initial.py +53 -0
  133. servicetrees/domains/migrations/__init__.py +0 -0
  134. servicetrees/domains/models.py +90 -0
  135. servicetrees/domains/roles.py +105 -0
  136. servicetrees/domains/static/domains/css/domain-editor.css +1371 -0
  137. servicetrees/domains/static/domains/js/common.js +82 -0
  138. servicetrees/domains/static/domains/js/domain_user_inline.js +99 -0
  139. servicetrees/domains/static/domains/js/domains.js +56 -0
  140. servicetrees/domains/static/domains/vendor/fontawesome/LICENSE.txt +165 -0
  141. servicetrees/domains/static/domains/vendor/fontawesome/VERSION +1 -0
  142. servicetrees/domains/static/domains/vendor/fontawesome/css/all.min.css +9 -0
  143. servicetrees/domains/static/domains/vendor/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
  144. servicetrees/domains/static/domains/vendor/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
  145. servicetrees/domains/static/domains/vendor/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
  146. servicetrees/domains/static/domains/vendor/fontawesome/webfonts/fa-v4compatibility.woff2 +0 -0
  147. servicetrees/domains/templates/domains/dashboard.html +400 -0
  148. servicetrees/domains/templates/domains/includes/custom_settings_fields.html +56 -0
  149. servicetrees/domains/templates/domains/includes/domain_manager_panel.html +33 -0
  150. servicetrees/domains/templates/domains/includes/settings_extra_panels.html +1 -0
  151. servicetrees/domains/templates/domains/landing.html +75 -0
  152. servicetrees/domains/templates/domains/settings_edit.html +739 -0
  153. servicetrees/domains/templatetags/__init__.py +1 -0
  154. servicetrees/domains/templatetags/domain_date_tags.py +236 -0
  155. servicetrees/domains/templatetags/domain_tags.py +13 -0
  156. servicetrees/domains/tests/__init__.py +0 -0
  157. servicetrees/domains/tests/test_date_tags.py +44 -0
  158. servicetrees/domains/tests/test_domain_users.py +100 -0
  159. servicetrees/domains/tests/test_internal_host.py +55 -0
  160. servicetrees/domains/tests/test_middleware.py +21 -0
  161. servicetrees/domains/urls.py +13 -0
  162. servicetrees/domains/utils.py +149 -0
  163. servicetrees/domains/views.py +613 -0
  164. servicetrees/error_handling/__init__.py +0 -0
  165. servicetrees/error_handling/admin.py +21 -0
  166. servicetrees/error_handling/apps.py +7 -0
  167. servicetrees/error_handling/migrations/0001_initial.py +32 -0
  168. servicetrees/error_handling/migrations/__init__.py +1 -0
  169. servicetrees/error_handling/models.py +62 -0
  170. servicetrees/error_handling/templates/error_handling/error.html +99 -0
  171. servicetrees/error_handling/utils.py +136 -0
  172. servicetrees/links/README.md +41 -0
  173. servicetrees/links/SETTINGS.md +14 -0
  174. servicetrees/links/__init__.py +0 -0
  175. servicetrees/links/admin.py +32 -0
  176. servicetrees/links/apps.py +7 -0
  177. servicetrees/links/conf.py +17 -0
  178. servicetrees/links/management/__init__.py +0 -0
  179. servicetrees/links/management/commands/__init__.py +0 -0
  180. servicetrees/links/management/commands/delete_unused_redirects.py +18 -0
  181. servicetrees/links/management/commands/scan_broken_links.py +149 -0
  182. servicetrees/links/middleware.py +159 -0
  183. servicetrees/links/migrations/0001_initial.py +126 -0
  184. servicetrees/links/migrations/__init__.py +0 -0
  185. servicetrees/links/models.py +125 -0
  186. servicetrees/links/static/links/css/links.css +301 -0
  187. servicetrees/links/static/links/js/links.js +49 -0
  188. servicetrees/links/templates/links/create_redirect.html +70 -0
  189. servicetrees/links/templates/links/link_check.html +454 -0
  190. servicetrees/links/tests.py +113 -0
  191. servicetrees/links/urls.py +11 -0
  192. servicetrees/links/utils.py +346 -0
  193. servicetrees/links/views.py +140 -0
  194. servicetrees/mediaapp/README.md +42 -0
  195. servicetrees/mediaapp/SETTINGS.md +24 -0
  196. servicetrees/mediaapp/__init__.py +0 -0
  197. servicetrees/mediaapp/admin.py +92 -0
  198. servicetrees/mediaapp/apps.py +11 -0
  199. servicetrees/mediaapp/conf.py +37 -0
  200. servicetrees/mediaapp/management/__init__.py +0 -0
  201. servicetrees/mediaapp/management/commands/__init__.py +0 -0
  202. servicetrees/mediaapp/management/commands/cleanup_temp_media.py +56 -0
  203. servicetrees/mediaapp/migrations/0001_initial.py +85 -0
  204. servicetrees/mediaapp/migrations/0002_media_site.py +20 -0
  205. servicetrees/mediaapp/migrations/__init__.py +0 -0
  206. servicetrees/mediaapp/models.py +99 -0
  207. servicetrees/mediaapp/permissions.py +31 -0
  208. servicetrees/mediaapp/scopes.py +31 -0
  209. servicetrees/mediaapp/signals.py +1 -0
  210. servicetrees/mediaapp/static/mediaapp/css/browser.css +1140 -0
  211. servicetrees/mediaapp/static/mediaapp/js/media-browse.js +1180 -0
  212. servicetrees/mediaapp/storage.py +60 -0
  213. servicetrees/mediaapp/templates/admin/st_mediaapp/change_list.html +195 -0
  214. servicetrees/mediaapp/templates/mediaapp/includes/media_browser_modals.html +88 -0
  215. servicetrees/mediaapp/tests.py +94 -0
  216. servicetrees/mediaapp/upload_pipeline.py +143 -0
  217. servicetrees/mediaapp/urls.py +15 -0
  218. servicetrees/mediaapp/utils.py +612 -0
  219. servicetrees/mediaapp/views.py +390 -0
  220. servicetrees/openid_connect/README.md +49 -0
  221. servicetrees/openid_connect/SETTINGS.md +21 -0
  222. servicetrees/openid_connect/__init__.py +0 -0
  223. servicetrees/openid_connect/admin.py +145 -0
  224. servicetrees/openid_connect/apps.py +8 -0
  225. servicetrees/openid_connect/backend.py +78 -0
  226. servicetrees/openid_connect/conf.py +47 -0
  227. servicetrees/openid_connect/middleware.py +51 -0
  228. servicetrees/openid_connect/migrations/0001_initial.py +33 -0
  229. servicetrees/openid_connect/migrations/__init__.py +0 -0
  230. servicetrees/openid_connect/models.py +26 -0
  231. servicetrees/openid_connect/tests.py +72 -0
  232. servicetrees/openid_connect/urls.py +14 -0
  233. servicetrees/openid_connect/utils.py +189 -0
  234. servicetrees/openid_connect/views.py +241 -0
  235. servicetrees/search/README.md +44 -0
  236. servicetrees/search/SETTINGS.md +17 -0
  237. servicetrees/search/__init__.py +0 -0
  238. servicetrees/search/admin.py +3 -0
  239. servicetrees/search/apps.py +7 -0
  240. servicetrees/search/conf.py +20 -0
  241. servicetrees/search/migrations/__init__.py +0 -0
  242. servicetrees/search/templates/search/search-results.html +131 -0
  243. servicetrees/search/templatetags/__init__.py +1 -0
  244. servicetrees/search/templatetags/search_tags.py +116 -0
  245. servicetrees/search/urls.py +8 -0
  246. servicetrees/search/utils.py +383 -0
  247. servicetrees/search/views.py +110 -0
  248. servicetrees/tests_clientip.py +26 -0
  249. servicetrees/translations/SETTINGS.md +13 -0
  250. servicetrees/translations/TRANSLATION_MODEL_ANALYSIS.md +85 -0
  251. servicetrees/translations/__init__.py +0 -0
  252. servicetrees/translations/admin.py +35 -0
  253. servicetrees/translations/apps.py +7 -0
  254. servicetrees/translations/babel_locales.py +63 -0
  255. servicetrees/translations/captions.py +38 -0
  256. servicetrees/translations/conf.py +18 -0
  257. servicetrees/translations/context_processors.py +45 -0
  258. servicetrees/translations/management/__init__.py +0 -0
  259. servicetrees/translations/management/commands/__init__.py +0 -0
  260. servicetrees/translations/management/commands/import_pot.py +195 -0
  261. servicetrees/translations/management/commands/scan_translations.py +283 -0
  262. servicetrees/translations/migrations/0001_initial.py +57 -0
  263. servicetrees/translations/migrations/__init__.py +0 -0
  264. servicetrees/translations/models.py +60 -0
  265. servicetrees/translations/static/translations/css/translations.css +127 -0
  266. servicetrees/translations/static/translations/js/translations.js +27 -0
  267. servicetrees/translations/templates/translations/translations-list.html +273 -0
  268. servicetrees/translations/templatetags/__init__.py +1 -0
  269. servicetrees/translations/templatetags/translation_tags.py +237 -0
  270. servicetrees/translations/tests.py +30 -0
  271. servicetrees/translations/urls.py +10 -0
  272. servicetrees/translations/utils.py +148 -0
  273. servicetrees/translations/views.py +194 -0
  274. servicetrees/userprofiles/__init__.py +1 -0
  275. servicetrees/userprofiles/admin.py +14 -0
  276. servicetrees/userprofiles/apps.py +8 -0
  277. servicetrees/userprofiles/migrations/0001_initial.py +32 -0
  278. servicetrees/userprofiles/migrations/__init__.py +1 -0
  279. servicetrees/userprofiles/models.py +28 -0
  280. servicetrees/userprofiles/templates/userprofiles/partials/profile_card.html +20 -0
  281. servicetrees/userprofiles/templates/userprofiles/partials/profile_edit_form.html +32 -0
  282. servicetrees/userprofiles/urls.py +10 -0
  283. servicetrees/userprofiles/views.py +42 -0
  284. servicetrees/webform/SETTINGS.md +12 -0
  285. servicetrees/webform/__init__.py +1 -0
  286. servicetrees/webform/apps.py +8 -0
  287. servicetrees/webform/conf.py +14 -0
  288. servicetrees/webform/editor.py +124 -0
  289. servicetrees/webform/forms.py +104 -0
  290. servicetrees/webform/management/__init__.py +1 -0
  291. servicetrees/webform/management/commands/__init__.py +1 -0
  292. servicetrees/webform/management/commands/clear_expired_webform_submissions.py +11 -0
  293. servicetrees/webform/migrations/0001_initial.py +72 -0
  294. servicetrees/webform/migrations/__init__.py +0 -0
  295. servicetrees/webform/models.py +147 -0
  296. servicetrees/webform/services.py +102 -0
  297. servicetrees/webform/static/webform/webform-builder.js +97 -0
  298. servicetrees/webform/static/webform/webform.css +249 -0
  299. servicetrees/webform/templates/webform/detail.html +22 -0
  300. servicetrees/webform/templates/webform/edit.html +71 -0
  301. servicetrees/webform/templates/webform/includes/builder_field.html +45 -0
  302. servicetrees/webform/templates/webform/includes/public_form.html +27 -0
  303. servicetrees/webform/templates/webform/submissions.html +38 -0
  304. servicetrees/webform/templatetags/__init__.py +1 -0
  305. servicetrees/webform/templatetags/webform_tags.py +26 -0
  306. servicetrees/webform/tests.py +20 -0
  307. servicetrees/webform/urls.py +10 -0
  308. servicetrees/webform/utils.py +62 -0
  309. servicetrees/webform/views.py +16 -0
  310. servicetrees-0.1.0.dist-info/METADATA +253 -0
  311. servicetrees-0.1.0.dist-info/RECORD +314 -0
  312. servicetrees-0.1.0.dist-info/WHEEL +5 -0
  313. servicetrees-0.1.0.dist-info/licenses/LICENSE +21 -0
  314. servicetrees-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3 @@
1
+ """Reusable Django apps for content-driven multi-site projects."""
2
+
3
+ __version__ = '0.1.0'
@@ -0,0 +1,46 @@
1
+ # CKEditor App Configuration
2
+
3
+ This app provides CKEditor 5 integration for rich text editing.
4
+
5
+ ## Available Constants
6
+
7
+ All constants are defined in `config/app_configs/ckeditor_config.py`:
8
+
9
+ ### `CKEDITOR_COLOR_PALETTE`
10
+ List of color options available in the CKEditor color picker. Each item contains:
11
+ - `color`: HSL color value
12
+ - `label`: Display name for the color
13
+
14
+ ### `ST_CKEDITOR_CONFIGS`
15
+ Dictionary of CKEditor configurations. The default configuration includes:
16
+ - Toolbar items (headings, formatting, lists, links, etc.)
17
+ - Heading options (paragraph, h1, h2, h3)
18
+ - Link decorators (open in new tab)
19
+ - Image upload configuration
20
+ - Image styles and resize options
21
+ - Media embed support
22
+ - HTML support settings
23
+
24
+ ## Utility Functions
25
+
26
+ Utility functions are available in `ckeditor/utils.py`:
27
+
28
+ - `get_ckeditor_config(config_name='default')`: Retrieve a CKEditor configuration by name
29
+ - `register_ckeditor_config(config_name, config_dict)`: Register a new CKEditor configuration
30
+
31
+ ## Usage
32
+
33
+ ```python
34
+ from config.app_configs.ckeditor_config import ST_CKEDITOR_CONFIGS, CKEDITOR_COLOR_PALETTE
35
+ from ckeditor.utils import get_ckeditor_config, register_ckeditor_config
36
+
37
+ # Get default configuration
38
+ config = get_ckeditor_config()
39
+
40
+ # Register a custom configuration
41
+ register_ckeditor_config('simple', {
42
+ 'toolbar': {
43
+ 'items': ['bold', 'italic', 'link']
44
+ }
45
+ })
46
+ ```
@@ -0,0 +1,10 @@
1
+ # ckeditor settings
2
+
3
+ Generated from `servicetrees/ckeditor/conf.py` by `scripts/settings_docs.py`; edit the comments there.
4
+ Every setting is optional: set it in the project settings only to change the default.
5
+
6
+ | Setting | Default | |
7
+ |---|---|---|
8
+ | `ST_CKEDITOR_CONFIGS` | `{'default': {}}` | Named CKEditor 5 configs ({'default': {...}, ...}); usually loaded from JSON by the project. |
9
+ | `ST_CKEDITOR_SANITIZER` | `None` | Function (or dotted path) that cleans editor HTML on save; None uses servicetrees.ckeditor.sanitize.default_clean_html. A function returning its input turns cleaning off. |
10
+ | `ST_CKEDITOR_IFRAME_HOSTS` | see `conf.py` | Hosts (and their subdomains) whose iframes survive cleaning. |
File without changes
@@ -0,0 +1 @@
1
+ from django.contrib import admin
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+ class CKEditorConfig(AppConfig):
4
+ default_auto_field = 'django.db.models.BigAutoField'
5
+ name = 'servicetrees.ckeditor'
6
+ label = 'st_ckeditor'
7
+ verbose_name = 'CKEditor'
@@ -0,0 +1,14 @@
1
+ """Settings for ckeditor with their defaults; override any of them in the project settings."""
2
+ from servicetrees.conf import app_settings
3
+
4
+ __getattr__ = app_settings({
5
+ # Named CKEditor 5 configs ({'default': {...}, ...}); usually loaded from JSON by the project.
6
+ 'ST_CKEDITOR_CONFIGS': {'default': {}},
7
+ # Function (or dotted path) that cleans editor HTML on save; None uses
8
+ # servicetrees.ckeditor.sanitize.default_clean_html. A function returning its input turns cleaning off.
9
+ 'ST_CKEDITOR_SANITIZER': None,
10
+ # Hosts (and their subdomains) whose iframes survive cleaning.
11
+ 'ST_CKEDITOR_IFRAME_HOSTS': [
12
+ 'youtube.com', 'youtube-nocookie.com', 'vimeo.com', 'google.com', 'spotify.com', 'soundcloud.com',
13
+ ],
14
+ })
@@ -0,0 +1,87 @@
1
+ """
2
+ Server-side cleaning of editor HTML.
3
+
4
+ CKEditor's own filtering runs in the browser, so anything posted directly skips
5
+ it. clean_html() keeps what the editor configs produce (text formatting, links,
6
+ images, figures, tables, media embeds) and drops scripts, event handlers,
7
+ javascript: URLs and iframes from hosts not in ST_CKEDITOR_IFRAME_HOSTS.
8
+ """
9
+ from urllib.parse import urlsplit
10
+
11
+ import nh3
12
+ from django.utils.module_loading import import_string
13
+
14
+ from servicetrees.ckeditor import conf as ckeditor_conf
15
+
16
+ TAGS = nh3.ALLOWED_TAGS | {
17
+ 'audio', 'figcaption', 'figure', 'iframe', 'oembed', 'picture', 'source', 'video',
18
+ }
19
+
20
+ ATTRIBUTES = {
21
+ '*': {'class', 'style', 'title', 'id', 'lang', 'dir'},
22
+ 'a': {'href', 'target', 'rel', 'name'},
23
+ 'img': {'src', 'alt', 'width', 'height', 'srcset', 'sizes', 'loading'},
24
+ 'iframe': {'src', 'width', 'height', 'allow', 'allowfullscreen', 'frameborder', 'loading', 'referrerpolicy'},
25
+ 'video': {'src', 'poster', 'controls', 'preload', 'width', 'height', 'loop', 'muted', 'playsinline'},
26
+ 'audio': {'src', 'controls', 'preload', 'loop'},
27
+ 'source': {'src', 'srcset', 'type', 'media', 'sizes'},
28
+ 'oembed': {'url'},
29
+ 'div': {'data-oembed-url'},
30
+ 'ol': {'start', 'type', 'reversed'},
31
+ 'li': {'value'},
32
+ 'td': {'colspan', 'rowspan'},
33
+ 'th': {'colspan', 'rowspan', 'scope'},
34
+ 'col': {'span'},
35
+ 'colgroup': {'span'},
36
+ }
37
+
38
+ STYLE_PROPERTIES = {
39
+ 'aspect-ratio', 'background-color', 'border', 'border-collapse', 'clear', 'color', 'display', 'float',
40
+ 'font-style', 'font-weight', 'height', 'left', 'margin', 'margin-bottom', 'margin-left',
41
+ 'margin-right', 'margin-top', 'max-width', 'min-width', 'padding', 'padding-bottom', 'padding-left',
42
+ 'padding-right', 'padding-top', 'position', 'text-align', 'text-decoration', 'top', 'vertical-align',
43
+ 'width',
44
+ }
45
+
46
+ URL_SCHEMES = {'http', 'https', 'mailto', 'tel'}
47
+
48
+
49
+ def _iframe_host_allowed(url):
50
+ try:
51
+ host = (urlsplit(url).hostname or '').lower()
52
+ except ValueError:
53
+ return False
54
+ if not url.lower().startswith(('https://', 'http://', '//')):
55
+ return False
56
+ return any(host == allowed or host.endswith('.' + allowed) for allowed in ckeditor_conf.ST_CKEDITOR_IFRAME_HOSTS)
57
+
58
+
59
+ def _attribute_filter(tag, attribute, value):
60
+ if tag == 'iframe' and attribute == 'src':
61
+ return value if _iframe_host_allowed(value) else None
62
+ return value
63
+
64
+
65
+ def default_clean_html(html):
66
+ return nh3.clean(
67
+ html,
68
+ tags=TAGS,
69
+ clean_content_tags={'script', 'style'},
70
+ attributes=ATTRIBUTES,
71
+ attribute_filter=_attribute_filter,
72
+ link_rel=None,
73
+ url_schemes=URL_SCHEMES,
74
+ filter_style_properties=STYLE_PROPERTIES,
75
+ )
76
+
77
+
78
+ def clean_html(html):
79
+ """Editor HTML made safe to render with |safe (ST_CKEDITOR_SANITIZER replaces the cleaner)."""
80
+ if not html:
81
+ return html
82
+ sanitizer = ckeditor_conf.ST_CKEDITOR_SANITIZER
83
+ if sanitizer is None:
84
+ return default_clean_html(html)
85
+ if isinstance(sanitizer, str):
86
+ sanitizer = import_string(sanitizer)
87
+ return sanitizer(html)
@@ -0,0 +1,497 @@
1
+ /* CKEditor admin editor overrides — loaded after site style.css */
2
+ img.cke_drupal_break { height: 5px !important; }
3
+ img.cke_anchor { min-width: auto !important; }
4
+ .media-dropdown-option { padding: 5px 10px !important }
5
+ .media-dropdown-panel { position: absolute !important; top: 40px !important; background: #fff !important; }
6
+ .ck-content figcaption { min-height: 20px !important; background: transparent !important; }
7
+ .ck-widget.html-object-embed:before { content: none !important; }
8
+ .ck-editor figure p { margin: 0 !important; padding: 0 !important; }
9
+
10
+ .ck-toolbar .media-dropdown-toolbar { display: flex; align-items: center; margin-right: 2px; }
11
+ .ck-toolbar .media-dropdown-toolbar.ck-disabled { cursor: not-allowed; }
12
+ .media-dropdown { position: relative; }
13
+ .media-dropdown-btn {
14
+ display: inline-flex;
15
+ align-items: center;
16
+ gap: 6px;
17
+ padding: 4px 10px;
18
+ height: 28px;
19
+ min-width: 28px;
20
+ border: 1px solid #ccc;
21
+ border-radius: 6px;
22
+ background: transparent;
23
+ color: #2d2d2d;
24
+ font-size: 13px;
25
+ cursor: pointer;
26
+ font-family: inherit;
27
+ line-height: 1.2;
28
+ }
29
+ .media-dropdown-btn:hover { background: #f5f5f5; border-color: #e0e0e0; }
30
+ .media-dropdown-btn:focus { outline: none; border-color: #007bff; }
31
+ .media-dropdown-icon { display: inline-flex; align-items: center; justify-content: center; }
32
+ .media-dropdown-icon svg { flex-shrink: 0; }
33
+ .media-dropdown-label { white-space: nowrap; }
34
+ .media-dropdown-panel {
35
+ display: none;
36
+ position: absolute;
37
+ top: 100%;
38
+ left: 0;
39
+ margin-top: 4px;
40
+ min-width: 200px;
41
+ border: 1px solid #e0e0e0;
42
+ border-radius: 6px;
43
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
44
+ z-index: 1000;
45
+ }
46
+ .media-dropdown:focus-within .media-dropdown-panel { display: block; }
47
+ .media-dropdown-option {
48
+ display: flex;
49
+ align-items: center;
50
+ gap: 10px;
51
+ width: 100%;
52
+ padding: 22px 20px;
53
+ border: none;
54
+ background: none;
55
+ text-align: left;
56
+ font-size: 14px;
57
+ cursor: pointer;
58
+ color: #2d2d2d;
59
+ font-family: inherit;
60
+ }
61
+ .media-dropdown-option-icon { display: inline-flex; flex-shrink: 0; color: #666; }
62
+ .media-dropdown-option:hover { background: #f5f5f5; }
63
+ .media-dropdown-option:hover .media-dropdown-option-icon { color: #2d2d2d; }
64
+ .media-dropdown-option:first-child { border-radius: 6px 6px 0 0; }
65
+ .media-dropdown-option:last-child { border-radius: 0 0 6px 6px; }
66
+
67
+ .ck-editor-extra-bar {
68
+ position: absolute !important;
69
+ top: 4px;
70
+ right: 6px;
71
+ z-index: 10;
72
+ display: inline-flex;
73
+ align-items: center;
74
+ gap: 6px;
75
+ }
76
+ .ck-editor-extra-bar .media-dropdown-btn, .ck-editor-extra-bar .footnote-extra-btn {
77
+ display: inline-flex;
78
+ align-items: center;
79
+ gap: 6px;
80
+ padding: 4px 10px !important;
81
+ border-radius: 4px !important;
82
+ border: 1px solid #ccc !important;
83
+ height: 28px;
84
+ background: transparent;
85
+ color: #2d2d2d;
86
+ cursor: pointer;
87
+ font-size: 13px;
88
+ font-family: inherit;
89
+ line-height: 1.2;
90
+ white-space: nowrap;
91
+ }
92
+ .ck-editor-extra-bar .ck-editor-extra-btn:hover,
93
+ .ck-editor-extra-bar .media-dropdown-btn:hover,
94
+ .ck-editor-extra-bar .footnote-extra-btn:hover {
95
+ background: #f5f5f5;
96
+ border-color: #e0e0e0 !important;
97
+ }
98
+ .ck-editor-extra-bar .ck-editor-extra-btn:focus,
99
+ .ck-editor-extra-bar .media-dropdown-btn:focus,
100
+ .ck-editor-extra-bar .footnote-extra-btn:focus {
101
+ outline: none;
102
+ border-color: #007bff !important;
103
+ box-shadow: 0 0 0 1px rgba(0, 123, 255, 0.2);
104
+ }
105
+ .ck-editor-extra-bar .extra-bar-media-slot { display: inline-flex; align-items: center; }
106
+ .ck-editor-extra-bar .media-dropdown-toolbar { margin: 0; }
107
+ .ck.ck-editor .ck.ck-editor__top { position: sticky; top: 0; z-index: 100; background: #fff; }
108
+ .ck.ck-sticky-panel .ck-sticky-panel__content_sticky { position: relative !important; top: auto !important; width: auto !important; box-shadow: none !important; }
109
+
110
+ .ck-editor .ck-content sup.footnote-ref,
111
+ .ck-editor .ck-content a.footnote-ref,
112
+ .ck-editor .ck-editor__editable sup.footnote-ref,
113
+ .ck-editor .ck-editor__editable a.footnote-ref { opacity: 1 !important; }
114
+
115
+ .ck-editor .ck-editor__editable,
116
+ .ck-editor .ck-content.page-content.ckeditor-content,
117
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content {
118
+ max-width: 650px;
119
+ min-height: 420px;
120
+ color: #606572;
121
+ font-size: 1.1em;
122
+ line-height: 150%;
123
+ text-align: left;
124
+ font-family: "Roboto Slab", sans-serif;
125
+ }
126
+
127
+ .ck-editor .ck-editor__editable img,
128
+ .ck-editor .ck-editor__editable video,
129
+ .ck-editor .ck-content.page-content.ckeditor-content img,
130
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content img,
131
+ .ck-editor .ck-content.page-content.ckeditor-content video,
132
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content video {
133
+ width: 100% !important;
134
+ max-width: 100%;
135
+ height: auto !important;
136
+ }
137
+
138
+ .ck-editor .ck-editor__editable img.left,
139
+ .ck-editor .ck-editor__editable figure.left,
140
+ .ck-editor .ck-editor__editable .left,
141
+ .ck-editor .ck-content.page-content.ckeditor-content img.left,
142
+ .ck-editor .ck-content.page-content.ckeditor-content figure.left,
143
+ .ck-editor .ck-content.page-content.ckeditor-content .left,
144
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content img.left,
145
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content figure.left,
146
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .left {
147
+ float: left !important;
148
+ width: auto !important;
149
+ max-width: 40%;
150
+ margin: 0 15px 1em 0;
151
+ }
152
+
153
+ .ck-editor .ck-editor__editable img.right,
154
+ .ck-editor .ck-editor__editable figure.right,
155
+ .ck-editor .ck-editor__editable .right,
156
+ .ck-editor .ck-content.page-content.ckeditor-content img.right,
157
+ .ck-editor .ck-content.page-content.ckeditor-content figure.right,
158
+ .ck-editor .ck-content.page-content.ckeditor-content .right,
159
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content img.right,
160
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content figure.right,
161
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .right {
162
+ float: right !important;
163
+ width: auto !important;
164
+ max-width: 40%;
165
+ margin: 0 0 0.5em 15px;
166
+ }
167
+
168
+ /* desktop-only: in editor always show as float (editor width never triggers mobile breakpoint) */
169
+
170
+ .ck-editor .ck-content.page-content.ckeditor-content p,
171
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content p {
172
+ margin-top: 0;
173
+ margin-bottom: 15px;
174
+ padding-top: 0;
175
+ padding-bottom: 13px;
176
+ color: #606572;
177
+ font-size: 1em;
178
+ line-height: 150%;
179
+ text-align: left;
180
+ font-family: "Roboto Slab", serif;
181
+ }
182
+
183
+ .ck-editor .ck-content.page-content.ckeditor-content h1,
184
+ .ck-editor .ck-content.page-content.ckeditor-content h2,
185
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content h1,
186
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content h2 {
187
+ margin-top: 0;
188
+ color: #80abe0;
189
+ font-family: Lato, sans-serif;
190
+ font-weight: 300;
191
+ font-style: italic;
192
+ line-height: 1.1;
193
+ clear: left;
194
+ }
195
+
196
+ .ck-editor .ck-content.page-content.ckeditor-content h1,
197
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content h1 { font-size: 3em; }
198
+ .ck-editor .ck-content.page-content.ckeditor-content h2,
199
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content h2 { font-size: 1.8em; }
200
+
201
+ .ck-editor .ck-content.page-content.ckeditor-content h3,
202
+ .ck-editor .ck-content.page-content.ckeditor-content h4,
203
+ .ck-editor .ck-content.page-content.ckeditor-content h5,
204
+ .ck-editor .ck-content.page-content.ckeditor-content h6,
205
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content h3,
206
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content h4,
207
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content h5,
208
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content h6 {
209
+ color: #80abe0;
210
+ line-height: 1.1;
211
+ clear: left;
212
+ }
213
+
214
+ .ck-editor .ck-content.page-content.ckeditor-content ul,
215
+ .ck-editor .ck-content.page-content.ckeditor-content ol,
216
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content ul,
217
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content ol {
218
+ margin: 1em 0;
219
+ padding-left: 1.5em;
220
+ }
221
+
222
+ .ck-editor .ck-content.page-content.ckeditor-content li,
223
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content li {
224
+ margin-bottom: 7px;
225
+ }
226
+
227
+ .ck-editor .ck-content.page-content.ckeditor-content li p,
228
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content li p {
229
+ margin-bottom: 0;
230
+ padding-bottom: 0;
231
+ }
232
+
233
+ .ck-editor .ck-content.page-content.ckeditor-content blockquote,
234
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content blockquote {
235
+ margin: 1.5em 0;
236
+ padding: 30px 7%;
237
+ clear: both;
238
+ background: aliceblue;
239
+ color: #606572;
240
+ text-align: left;
241
+ border-left: none;
242
+ }
243
+
244
+ .ck-editor .ck-content.page-content.ckeditor-content blockquote::after,
245
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content blockquote::after {
246
+ content: "";
247
+ display: table;
248
+ clear: both;
249
+ }
250
+
251
+ .ck-editor .ck-content.page-content.ckeditor-content blockquote p,
252
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content blockquote p {
253
+ font-size: 1em;
254
+ padding-bottom: 0;
255
+ }
256
+
257
+ .ck-editor .ck-content.page-content.ckeditor-content blockquote p:last-child,
258
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content blockquote p:last-child {
259
+ margin-bottom: 0;
260
+ }
261
+
262
+ .ck-editor .ck-content.page-content.ckeditor-content blockquote p:last-child > strong,
263
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content blockquote p:last-child > strong,
264
+ .ck-editor .ck-content.page-content.ckeditor-content blockquote p:last-child > em,
265
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content blockquote p:last-child > em {
266
+ font-size: 0.85em;
267
+ line-height: 1.3;
268
+ }
269
+
270
+ .ck-editor .ck-content.page-content.ckeditor-content blockquote p:last-child > strong,
271
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content blockquote p:last-child > strong {
272
+ text-transform: uppercase;
273
+ font-style: normal;
274
+ }
275
+
276
+ .ck-editor .ck-content.page-content.ckeditor-content a,
277
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content a {
278
+ padding-bottom: 3px;
279
+ color: #80abe0;
280
+ text-decoration-color: #dedede;
281
+ text-underline-offset: 3px;
282
+ }
283
+
284
+ .ck-editor .ck-content.page-content.ckeditor-content hr,
285
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content hr {
286
+ height: 1px;
287
+ margin: 2.5em 0;
288
+ padding: 0;
289
+ clear: both;
290
+ border: none;
291
+ border-top: 1px #d8e5fb solid;
292
+ }
293
+
294
+ .ck-editor .ck-content.page-content.ckeditor-content figure,
295
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content figure {
296
+ margin: 1em 0;
297
+ }
298
+
299
+ .ck-editor .ck-content.page-content.ckeditor-content p.caption,
300
+ .ck-editor .ck-content.page-content.ckeditor-content figcaption,
301
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content p.caption,
302
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content figcaption {
303
+ margin-top: 0;
304
+ margin-bottom: 27px;
305
+ padding: 7px 7% 5px 20px;
306
+ border-left: 1px dotted #606572;
307
+ color: #606572;
308
+ font-size: 0.9em;
309
+ line-height: 150%;
310
+ font-style: italic;
311
+ text-align: left;
312
+ font-family: Lato, sans-serif !important;
313
+ }
314
+
315
+ .ck-editor .ck-content.page-content.ckeditor-content .videocaption,
316
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .videocaption,
317
+ .ck-editor .ck-editor__editable .videocaption {
318
+ margin: 0.25em 0 1.5em 0;
319
+ border-left: 3px #555 solid;
320
+ font-style: italic;
321
+ }
322
+
323
+ .ck-editor .ck-content.page-content.ckeditor-content .videocaption p,
324
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .videocaption p,
325
+ .ck-editor .ck-editor__editable .videocaption p {
326
+ padding: 7px 7% 3px 20px;
327
+ font-family: Lato, sans-serif;
328
+ font-size: 0.9em;
329
+ }
330
+
331
+ .ck-editor .ck-editor__editable .videocaption p {
332
+ margin-bottom: 0;
333
+ }
334
+
335
+ .ck-editor .ck-content.page-content.ckeditor-content .audiocaption,
336
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .audiocaption,
337
+ .ck-editor .ck-editor__editable figure.audio,
338
+ .ck-editor .ck-editor__editable .audiocaption {
339
+ display: flex;
340
+ align-items: center;
341
+ margin: 1.5em 0;
342
+ padding: 1em;
343
+ border-top: 1px #dbdbdb solid;
344
+ border-bottom: 1px #dbdbdb solid;
345
+ }
346
+
347
+ .ck-editor .ck-editor__editable figure.audio,
348
+ .ck-editor .ck-editor__editable .audiocaption {
349
+ background: transparent;
350
+ border-radius: 4px;
351
+ min-height: 56px;
352
+ }
353
+
354
+ .ck-editor .ck-content.page-content.ckeditor-content .audiocaption p,
355
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .audiocaption p,
356
+ .ck-editor .ck-editor__editable figure.audio figcaption,
357
+ .ck-editor .ck-editor__editable .audiocaption p {
358
+ margin-bottom: 0;
359
+ padding-bottom: 0;
360
+ font-family: Lato, sans-serif;
361
+ font-size: 0.9em;
362
+ font-style: italic;
363
+ }
364
+
365
+ .ck-editor .ck-editor__editable figure.audio figcaption,
366
+ .ck-editor .ck-editor__editable .audiocaption p {
367
+ flex-grow: 1;
368
+ color: #555;
369
+ }
370
+
371
+ .ck-editor .ck-editor__editable figure.video,
372
+ .ck-editor .ck-editor__editable figure.ckeditor-video-wrapper {
373
+ display: block;
374
+ position: relative;
375
+ background: transparent;
376
+ border-radius: 5px;
377
+ overflow: hidden;
378
+ margin: 1em 0 0;
379
+ min-height: 200px;
380
+ }
381
+
382
+ .ck-editor .ck-editor__editable figure.video video,
383
+ .ck-editor .ck-editor__editable figure.ckeditor-video-wrapper video {
384
+ display: block;
385
+ width: 100%;
386
+ max-height: 340px;
387
+ object-fit: contain;
388
+ }
389
+
390
+ .ck-editor .ck-editor__editable figure.video::before,
391
+ .ck-editor .ck-editor__editable figure.ckeditor-video-wrapper::before {
392
+ content: "\25B6";
393
+ position: absolute;
394
+ top: 10px;
395
+ left: 12px;
396
+ background: rgba(0, 0, 0, 0.55);
397
+ color: #fff;
398
+ width: 36px;
399
+ height: 36px;
400
+ border-radius: 50%;
401
+ display: flex;
402
+ align-items: center;
403
+ justify-content: center;
404
+ font-size: 14px;
405
+ pointer-events: none;
406
+ z-index: 2;
407
+ }
408
+
409
+ .ck-editor .ck-editor__editable figure.video figcaption,
410
+ .ck-editor .ck-editor__editable figure.ckeditor-video-wrapper figcaption,
411
+ .ck-editor .ck-editor__editable figure.ckeditor-video-wrapper .videocaption {
412
+ background: transparent;
413
+ margin: 0;
414
+ padding: 6px 10px;
415
+ font-family: Lato, sans-serif;
416
+ font-size: 0.9em;
417
+ font-style: italic;
418
+ color: #555;
419
+ }
420
+
421
+ .ck-editor .ck-editor__editable figure.audio::before,
422
+ .ck-editor .ck-editor__editable .audiocaption::before {
423
+ content: "\25B6";
424
+ display: flex;
425
+ align-items: center;
426
+ justify-content: center;
427
+ flex-shrink: 0;
428
+ width: 38px;
429
+ height: 38px;
430
+ border-radius: 50%;
431
+ background: #417690;
432
+ color: #fff;
433
+ font-size: 13px;
434
+ margin-right: 14px;
435
+ }
436
+
437
+ .ck-editor .ck-editor__editable .ck-widget__resizer { display: none !important; }
438
+
439
+ .ck-editor .ck-editor__editable figure.audio.ck-widget_selected,
440
+ .ck-editor .ck-editor__editable figure.audio.audio-figure--ck-selected,
441
+ .ck-editor .ck-editor__editable .audiocaption.ck-widget_selected,
442
+ .ck-editor .ck-editor__editable .ck-widget_selected .audiocaption,
443
+ .ck-editor .ck-editor__editable .audiocaption--ck-selected,
444
+ .ck-editor .ck-editor__editable .audio-figure--ck-selected,
445
+ .ck-editor .ck-editor__editable figure.video.ck-widget_selected,
446
+ .ck-editor .ck-editor__editable figure.ckeditor-video-wrapper.ck-widget_selected {
447
+ outline: 3px solid #3399ff;
448
+ }
449
+
450
+ .ck-editor .ck-editor__editable figure.audio.ck-widget_selected,
451
+ .ck-editor .ck-editor__editable figure.audio.audio-figure--ck-selected,
452
+ .ck-editor .ck-editor__editable .audiocaption.ck-widget_selected,
453
+ .ck-editor .ck-editor__editable .ck-widget_selected .audiocaption,
454
+ .ck-editor .ck-editor__editable .audiocaption--ck-selected,
455
+ .ck-editor .ck-editor__editable .audio-figure--ck-selected {
456
+ outline-offset: 2px;
457
+ }
458
+
459
+ .ck-editor .ck-editor__editable audio {
460
+ display: none !important;
461
+ }
462
+
463
+ .ck-editor .ck-editor__editable figure.audio .ck-widget,
464
+ .ck-editor .ck-editor__editable figure.audio [class*="html-object"],
465
+ .ck-editor .ck-editor__editable figure.audio [class*="html-embed"],
466
+ .ck-editor .ck-editor__editable .audiocaption .ck-widget,
467
+ .ck-editor .ck-editor__editable .audiocaption [class*="html-object"],
468
+ .ck-editor .ck-editor__editable .audiocaption [class*="html-embed"],
469
+ .ck-editor .ck-editor__editable .audiocaption p:empty {
470
+ display: none !important;
471
+ }
472
+
473
+ .ck-editor .ck-content.page-content.ckeditor-content .furtherreading,
474
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .furtherreading {
475
+ display: inline-block;
476
+ margin-bottom: 2em;
477
+ padding: 5px 15px;
478
+ border: 1px #ddd solid;
479
+ border-radius: 20px;
480
+ text-align: right;
481
+ }
482
+
483
+ .ck-editor .ck-content.page-content.ckeditor-content .furtherreading p,
484
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .furtherreading p {
485
+ margin-bottom: 0;
486
+ padding-bottom: 0;
487
+ color: #999;
488
+ text-align: right;
489
+ font-family: Lato, sans-serif;
490
+ font-weight: 700;
491
+ }
492
+
493
+ .ck-editor .ck-content.page-content.ckeditor-content .furtherreading p a,
494
+ .ck-editor .ck-editor__editable.page-content.ckeditor-content .furtherreading p a {
495
+ font-weight: 400;
496
+ text-decoration: none;
497
+ }