alchemy_cms 7.0.16 → 7.1.0.pre.b1

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 (359) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/backport.yml +36 -0
  3. data/.github/workflows/brakeman-analysis.yml +5 -13
  4. data/.github/workflows/lint.yml +2 -9
  5. data/.github/workflows/stale.yml +2 -5
  6. data/.github/workflows/test.yml +7 -15
  7. data/.gitignore +0 -1
  8. data/.standard.yml +1 -1
  9. data/CHANGELOG.md +144 -51
  10. data/Gemfile +7 -18
  11. data/README.md +10 -8
  12. data/alchemy_cms.gemspec +4 -3
  13. data/app/assets/config/alchemy_manifest.js +0 -1
  14. data/app/assets/javascripts/alchemy/admin.js +1 -19
  15. data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +2 -3
  16. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +19 -34
  17. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +38 -13
  18. data/app/assets/javascripts/alchemy/alchemy.file_progress.js.coffee +1 -1
  19. data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +32 -25
  20. data/app/assets/javascripts/alchemy/alchemy.growler.js.coffee +1 -1
  21. data/app/assets/javascripts/alchemy/alchemy.image_overlay.coffee +3 -5
  22. data/app/assets/javascripts/alchemy/alchemy.initializer.js.coffee +0 -57
  23. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +22 -63
  24. data/app/assets/javascripts/alchemy/alchemy.list_filter.js.coffee +2 -2
  25. data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +5 -4
  26. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +5 -5
  27. data/app/assets/javascripts/alchemy/templates/index.js +0 -2
  28. data/app/assets/javascripts/alchemy/templates/node_folder.hbs +1 -1
  29. data/app/assets/javascripts/alchemy/templates/page.hbs +1 -1
  30. data/app/assets/javascripts/alchemy/templates/page_folder.hbs +2 -2
  31. data/app/assets/stylesheets/alchemy/_custom-properties.scss +82 -0
  32. data/app/assets/stylesheets/alchemy/_mixins.scss +38 -30
  33. data/app/assets/stylesheets/alchemy/_variables.scss +12 -5
  34. data/app/assets/stylesheets/alchemy/admin.scss +3 -4
  35. data/app/assets/stylesheets/alchemy/archive.scss +107 -50
  36. data/app/assets/stylesheets/alchemy/attachments.scss +5 -4
  37. data/app/assets/stylesheets/alchemy/buttons.scss +38 -164
  38. data/app/assets/stylesheets/alchemy/dashboard.scss +31 -6
  39. data/app/assets/stylesheets/alchemy/dialogs.scss +12 -28
  40. data/app/assets/stylesheets/alchemy/elements.scss +273 -282
  41. data/app/assets/stylesheets/alchemy/flash.scss +20 -12
  42. data/app/assets/stylesheets/alchemy/forms.scss +21 -34
  43. data/app/assets/stylesheets/alchemy/frame.scss +11 -32
  44. data/app/assets/stylesheets/alchemy/hints.scss +4 -62
  45. data/app/assets/stylesheets/alchemy/image_library.scss +36 -33
  46. data/app/assets/stylesheets/alchemy/labels.scss +4 -1
  47. data/app/assets/stylesheets/alchemy/menubar.scss +7 -6
  48. data/app/assets/stylesheets/alchemy/navigation.scss +27 -15
  49. data/app/assets/stylesheets/alchemy/nodes.scss +11 -7
  50. data/app/assets/stylesheets/alchemy/notices.scss +16 -4
  51. data/app/assets/stylesheets/alchemy/page-select.scss +10 -2
  52. data/app/assets/stylesheets/alchemy/pagination.scss +22 -13
  53. data/app/assets/stylesheets/alchemy/preview_window.scss +4 -8
  54. data/app/assets/stylesheets/alchemy/resource_info.scss +7 -5
  55. data/app/assets/stylesheets/alchemy/selects.scss +49 -42
  56. data/app/assets/stylesheets/alchemy/shoelace.scss +345 -0
  57. data/app/assets/stylesheets/alchemy/sitemap.scss +24 -14
  58. data/app/assets/stylesheets/alchemy/spinner.scss +9 -19
  59. data/app/assets/stylesheets/alchemy/tables.scss +16 -24
  60. data/app/assets/stylesheets/alchemy/tags.scss +4 -0
  61. data/app/assets/stylesheets/alchemy/toolbar.scss +29 -25
  62. data/app/assets/stylesheets/alchemy/upload.scss +140 -89
  63. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +80 -108
  64. data/app/components/alchemy/admin/node_select.rb +39 -0
  65. data/app/components/alchemy/admin/page_select.rb +42 -0
  66. data/app/components/alchemy/ingredients/audio_view.rb +1 -1
  67. data/app/components/alchemy/ingredients/base_view.rb +1 -1
  68. data/app/components/alchemy/ingredients/boolean_view.rb +1 -1
  69. data/app/components/alchemy/ingredients/datetime_view.rb +3 -4
  70. data/app/components/alchemy/ingredients/file_view.rb +1 -1
  71. data/app/components/alchemy/ingredients/headline_view.rb +7 -16
  72. data/app/components/alchemy/ingredients/link_view.rb +1 -1
  73. data/app/components/alchemy/ingredients/page_view.rb +1 -1
  74. data/app/components/alchemy/ingredients/picture_view.rb +1 -1
  75. data/app/components/alchemy/ingredients/richtext_view.rb +1 -1
  76. data/app/components/alchemy/ingredients/text_view.rb +1 -1
  77. data/app/components/alchemy/ingredients/video_view.rb +1 -1
  78. data/app/controllers/alchemy/admin/base_controller.rb +7 -32
  79. data/app/controllers/alchemy/admin/elements_controller.rb +63 -35
  80. data/app/controllers/alchemy/admin/languages_controller.rb +2 -3
  81. data/app/controllers/alchemy/admin/layoutpages_controller.rb +0 -19
  82. data/app/controllers/alchemy/admin/pages_controller.rb +4 -5
  83. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  84. data/app/controllers/alchemy/base_controller.rb +4 -2
  85. data/app/controllers/alchemy/messages_controller.rb +1 -1
  86. data/app/controllers/concerns/alchemy/admin/current_language.rb +1 -5
  87. data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +1 -1
  88. data/app/decorators/alchemy/element_editor.rb +0 -2
  89. data/app/helpers/alchemy/admin/attachments_helper.rb +6 -5
  90. data/app/helpers/alchemy/admin/base_helper.rb +17 -12
  91. data/app/helpers/alchemy/admin/ingredients_helper.rb +4 -1
  92. data/app/helpers/alchemy/admin/pages_helper.rb +5 -11
  93. data/app/helpers/alchemy/base_helper.rb +47 -13
  94. data/app/javascript/alchemy_admin/components/alchemy_html_element.js +129 -0
  95. data/app/javascript/alchemy_admin/components/button.js +59 -0
  96. data/app/javascript/alchemy_admin/components/char_counter.js +40 -0
  97. data/app/javascript/alchemy_admin/components/datepicker.js +39 -0
  98. data/app/javascript/alchemy_admin/components/dialog_link.js +45 -0
  99. data/app/javascript/alchemy_admin/components/element_editor/publish_element_button.js +36 -0
  100. data/app/javascript/alchemy_admin/components/element_editor.js +553 -0
  101. data/app/javascript/alchemy_admin/components/ingredient_group.js +54 -0
  102. data/app/javascript/alchemy_admin/components/link_buttons/link_button.js +48 -0
  103. data/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js +38 -0
  104. data/app/javascript/alchemy_admin/components/link_buttons.js +79 -0
  105. data/app/javascript/alchemy_admin/components/node_select.js +45 -0
  106. data/app/javascript/alchemy_admin/components/overlay.js +18 -0
  107. data/app/javascript/alchemy_admin/components/page_select.js +63 -0
  108. data/app/javascript/alchemy_admin/components/remote_select.js +134 -0
  109. data/app/javascript/alchemy_admin/components/select.js +12 -0
  110. data/app/javascript/alchemy_admin/components/spinner.js +31 -0
  111. data/app/javascript/alchemy_admin/components/tinymce.js +146 -0
  112. data/app/javascript/alchemy_admin/components/uploader/file_upload.js +266 -0
  113. data/app/javascript/alchemy_admin/components/uploader/progress.js +258 -0
  114. data/app/javascript/alchemy_admin/components/uploader.js +132 -0
  115. data/app/javascript/alchemy_admin/dirty.js +49 -0
  116. data/app/javascript/alchemy_admin/file_editors.js +1 -1
  117. data/app/javascript/alchemy_admin/gui.js +14 -0
  118. data/app/javascript/alchemy_admin/i18n.js +12 -8
  119. data/app/javascript/alchemy_admin/image_cropper.js +6 -3
  120. data/app/javascript/alchemy_admin/image_loader.js +7 -15
  121. data/app/javascript/alchemy_admin/ingredient_anchor_link.js +2 -5
  122. data/app/javascript/alchemy_admin/initializer.js +65 -0
  123. data/app/javascript/alchemy_admin/locales/en.js +31 -0
  124. data/app/javascript/alchemy_admin/picture_editors.js +2 -2
  125. data/app/javascript/alchemy_admin/picture_selector.js +38 -0
  126. data/app/javascript/alchemy_admin/please_wait_overlay.js +8 -0
  127. data/app/javascript/alchemy_admin/sortable_elements.js +78 -0
  128. data/app/javascript/alchemy_admin/spinner.js +36 -0
  129. data/app/javascript/alchemy_admin/tags_autocomplete.js +46 -0
  130. data/app/javascript/alchemy_admin/utils/ajax.js +6 -5
  131. data/app/javascript/alchemy_admin/utils/dom_helpers.js +20 -0
  132. data/app/javascript/alchemy_admin/utils/format.js +11 -0
  133. data/app/javascript/alchemy_admin/utils/string_conversions.js +10 -0
  134. data/app/javascript/alchemy_admin.js +70 -13
  135. data/app/javascript/menubar.js +10 -0
  136. data/app/models/alchemy/attachment.rb +9 -11
  137. data/app/models/alchemy/element.rb +11 -0
  138. data/app/models/alchemy/ingredients/audio.rb +0 -11
  139. data/app/models/alchemy/ingredients/datetime.rb +1 -1
  140. data/app/models/alchemy/ingredients/richtext.rb +1 -10
  141. data/app/models/alchemy/ingredients/video.rb +0 -12
  142. data/app/models/alchemy/node.rb +4 -0
  143. data/app/models/alchemy/page/page_elements.rb +2 -11
  144. data/app/models/alchemy/page/page_natures.rb +10 -2
  145. data/app/models/alchemy/page.rb +12 -54
  146. data/app/models/alchemy/picture/url.rb +1 -9
  147. data/app/models/concerns/alchemy/picture_thumbnails.rb +5 -4
  148. data/app/serializers/alchemy/page_tree_serializer.rb +2 -1
  149. data/app/services/alchemy/copy_page.rb +98 -0
  150. data/app/views/alchemy/_menubar.html.erb +17 -13
  151. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +14 -10
  152. data/app/views/alchemy/admin/attachments/_attachment.html.erb +44 -36
  153. data/app/views/alchemy/admin/attachments/_replace_button.html.erb +15 -21
  154. data/app/views/alchemy/admin/attachments/archive_overlay.js.erb +0 -1
  155. data/app/views/alchemy/admin/attachments/assign.js.erb +1 -1
  156. data/app/views/alchemy/admin/attachments/index.html.erb +6 -4
  157. data/app/views/alchemy/admin/attachments/show.html.erb +8 -8
  158. data/app/views/alchemy/admin/clipboard/clear.js.erb +1 -1
  159. data/app/views/alchemy/admin/clipboard/index.html.erb +3 -7
  160. data/app/views/alchemy/admin/clipboard/insert.js.erb +1 -1
  161. data/app/views/alchemy/admin/crop.html.erb +1 -1
  162. data/app/views/alchemy/admin/dashboard/_locked_pages.html.erb +1 -1
  163. data/app/views/alchemy/admin/dashboard/index.html.erb +13 -11
  164. data/app/views/alchemy/admin/dashboard/info.html.erb +7 -7
  165. data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +21 -23
  166. data/app/views/alchemy/admin/elements/_element.html.erb +52 -44
  167. data/app/views/alchemy/admin/elements/_footer.html.erb +1 -1
  168. data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
  169. data/app/views/alchemy/admin/elements/_header.html.erb +11 -12
  170. data/app/views/alchemy/admin/elements/_toolbar.html.erb +33 -45
  171. data/app/views/alchemy/admin/elements/create.js.erb +7 -15
  172. data/app/views/alchemy/admin/elements/destroy.js.erb +0 -2
  173. data/app/views/alchemy/admin/elements/index.html.erb +27 -24
  174. data/app/views/alchemy/admin/elements/new.html.erb +9 -11
  175. data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +2 -2
  176. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +3 -3
  177. data/app/views/alchemy/admin/ingredients/_video_fields.html.erb +1 -2
  178. data/app/views/alchemy/admin/languages/_form.html.erb +2 -3
  179. data/app/views/alchemy/admin/languages/_language.html.erb +15 -8
  180. data/app/views/alchemy/admin/languages/_table.html.erb +1 -0
  181. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +28 -16
  182. data/app/views/alchemy/admin/layoutpages/edit.html.erb +1 -1
  183. data/app/views/alchemy/admin/layoutpages/index.html.erb +2 -2
  184. data/app/views/alchemy/admin/legacy_page_urls/_legacy_page_url.html.erb +12 -8
  185. data/app/views/alchemy/admin/legacy_page_urls/_new.html.erb +1 -1
  186. data/app/views/alchemy/admin/nodes/_form.html.erb +20 -21
  187. data/app/views/alchemy/admin/nodes/_node.html.erb +39 -34
  188. data/app/views/alchemy/admin/nodes/index.html.erb +1 -1
  189. data/app/views/alchemy/admin/pages/_anchor_link.html.erb +4 -4
  190. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +2 -2
  191. data/app/views/alchemy/admin/pages/_current_page.html.erb +1 -1
  192. data/app/views/alchemy/admin/pages/_external_link.html.erb +4 -4
  193. data/app/views/alchemy/admin/pages/_file_link.html.erb +5 -5
  194. data/app/views/alchemy/admin/pages/_form.html.erb +10 -21
  195. data/app/views/alchemy/admin/pages/_internal_link.html.erb +4 -4
  196. data/app/views/alchemy/admin/pages/_locked_page.html.erb +2 -2
  197. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +4 -17
  198. data/app/views/alchemy/admin/pages/_page.html.erb +76 -72
  199. data/app/views/alchemy/admin/pages/_page_infos.html.erb +23 -7
  200. data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +2 -1
  201. data/app/views/alchemy/admin/pages/_page_status.html.erb +11 -21
  202. data/app/views/alchemy/admin/pages/_publication_fields.html.erb +2 -5
  203. data/app/views/alchemy/admin/pages/_table.html.erb +1 -1
  204. data/app/views/alchemy/admin/pages/_table_row.html.erb +43 -39
  205. data/app/views/alchemy/admin/pages/_toolbar.html.erb +43 -38
  206. data/app/views/alchemy/admin/pages/configure.html.erb +12 -14
  207. data/app/views/alchemy/admin/pages/edit.html.erb +80 -103
  208. data/app/views/alchemy/admin/pages/info.html.erb +20 -11
  209. data/app/views/alchemy/admin/pages/link.html.erb +22 -16
  210. data/app/views/alchemy/admin/pages/new.html.erb +9 -11
  211. data/app/views/alchemy/admin/pages/unlock.js.erb +10 -3
  212. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +15 -13
  213. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +3 -5
  214. data/app/views/alchemy/admin/partials/_routes.html.erb +10 -2
  215. data/app/views/alchemy/admin/partials/_site_select.html.erb +6 -5
  216. data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +28 -23
  217. data/app/views/alchemy/admin/pictures/_archive.html.erb +5 -5
  218. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +1 -1
  219. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +21 -23
  220. data/app/views/alchemy/admin/pictures/_infos.html.erb +2 -6
  221. data/app/views/alchemy/admin/pictures/_picture.html.erb +15 -17
  222. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +17 -16
  223. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -1
  224. data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +1 -1
  225. data/app/views/alchemy/admin/pictures/assign.js.erb +1 -1
  226. data/app/views/alchemy/admin/pictures/index.html.erb +34 -30
  227. data/app/views/alchemy/admin/pictures/show.html.erb +3 -3
  228. data/app/views/alchemy/admin/resources/_filter.html.erb +2 -2
  229. data/app/views/alchemy/admin/resources/_form.html.erb +2 -2
  230. data/app/views/alchemy/admin/resources/_per_page_select.html.erb +1 -1
  231. data/app/views/alchemy/admin/resources/_resource.html.erb +16 -9
  232. data/app/views/alchemy/admin/resources/_table.html.erb +4 -1
  233. data/app/views/alchemy/admin/resources/index.html.erb +22 -19
  234. data/app/views/alchemy/admin/sites/index.html.erb +2 -1
  235. data/app/views/alchemy/admin/styleguide/index.html.erb +54 -28
  236. data/app/views/alchemy/admin/tags/_tag.html.erb +16 -18
  237. data/app/views/alchemy/admin/tags/index.html.erb +15 -12
  238. data/app/views/alchemy/admin/tinymce/_setup.html.erb +29 -0
  239. data/app/views/alchemy/admin/uploader/_button.html.erb +23 -29
  240. data/app/views/alchemy/admin/uploader/_setup.html.erb +3 -8
  241. data/app/views/alchemy/base/500.html.erb +1 -1
  242. data/app/views/alchemy/base/error_notice.js.erb +0 -1
  243. data/app/views/alchemy/ingredients/_boolean_editor.html.erb +1 -1
  244. data/app/views/alchemy/ingredients/_datetime_editor.html.erb +2 -3
  245. data/app/views/alchemy/ingredients/_file_editor.html.erb +5 -5
  246. data/app/views/alchemy/ingredients/_link_editor.html.erb +1 -1
  247. data/app/views/alchemy/ingredients/_node_editor.html.erb +6 -19
  248. data/app/views/alchemy/ingredients/_page_editor.html.erb +7 -19
  249. data/app/views/alchemy/ingredients/_picture_editor.html.erb +2 -2
  250. data/app/views/alchemy/ingredients/_richtext_editor.html.erb +6 -15
  251. data/app/views/alchemy/ingredients/_select_editor.html.erb +2 -1
  252. data/app/views/alchemy/ingredients/_text_editor.html.erb +1 -1
  253. data/app/views/alchemy/ingredients/shared/_anchor.html.erb +1 -1
  254. data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +10 -20
  255. data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +42 -49
  256. data/app/views/kaminari/alchemy/_first_page.html.erb +4 -2
  257. data/app/views/kaminari/alchemy/_gap.html.erb +1 -1
  258. data/app/views/kaminari/alchemy/_last_page.html.erb +4 -2
  259. data/app/views/kaminari/alchemy/_next_page.html.erb +4 -2
  260. data/app/views/kaminari/alchemy/_prev_page.html.erb +4 -2
  261. data/app/views/layouts/alchemy/admin.html.erb +10 -29
  262. data/config/alchemy/modules.yml +30 -30
  263. data/config/importmap.rb +10 -1
  264. data/config/initializers/rails_live_reload.rb +13 -0
  265. data/config/locales/alchemy.en.yml +23 -9
  266. data/config/routes.rb +3 -2
  267. data/lib/alchemy/auth_accessors.rb +6 -1
  268. data/lib/alchemy/controller_actions.rb +17 -4
  269. data/lib/alchemy/dev_support/live_reload_watcher.rb +5 -0
  270. data/lib/alchemy/engine.rb +8 -2
  271. data/lib/alchemy/forms/builder.rb +18 -12
  272. data/lib/alchemy/modules.rb +2 -2
  273. data/lib/alchemy/permissions.rb +1 -1
  274. data/lib/alchemy/resources_helper.rb +3 -3
  275. data/lib/alchemy/routing_constraints.rb +1 -1
  276. data/lib/alchemy/test_support/capybara_helpers.rb +8 -5
  277. data/lib/alchemy/test_support/rspec_matchers.rb +14 -0
  278. data/lib/alchemy/test_support/shared_uploader_examples.rb +1 -1
  279. data/lib/alchemy/tinymce.rb +8 -3
  280. data/lib/alchemy/version.rb +1 -1
  281. data/lib/tasks/alchemy/tidy.rake +1 -0
  282. data/package.json +14 -5
  283. data/vendor/assets/fonts/remixicon.eot +0 -0
  284. data/vendor/assets/fonts/remixicon.svg +7816 -0
  285. data/vendor/assets/fonts/remixicon.ttf +0 -0
  286. data/vendor/assets/fonts/remixicon.woff +0 -0
  287. data/vendor/assets/fonts/remixicon.woff2 +0 -0
  288. data/vendor/assets/stylesheets/remixicon.scss +10480 -0
  289. metadata +87 -97
  290. data/.gem_release.yml +0 -8
  291. data/app/assets/javascripts/alchemy/alchemy.autocomplete.js.coffee +0 -30
  292. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +0 -53
  293. data/app/assets/javascripts/alchemy/alchemy.buttons.js.coffee +0 -45
  294. data/app/assets/javascripts/alchemy/alchemy.char_counter.js.coffee +0 -19
  295. data/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +0 -59
  296. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +0 -79
  297. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +0 -267
  298. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +0 -27
  299. data/app/assets/javascripts/alchemy/alchemy.spinner.js +0 -32
  300. data/app/assets/javascripts/alchemy/alchemy.tooltips.coffee +0 -10
  301. data/app/assets/javascripts/alchemy/alchemy.uploader.js.coffee +0 -131
  302. data/app/assets/javascripts/alchemy/menubar.js.coffee +0 -8
  303. data/app/assets/javascripts/alchemy/node_select.js +0 -39
  304. data/app/assets/javascripts/alchemy/page_select.js +0 -46
  305. data/app/assets/javascripts/alchemy/templates/node.hbs +0 -16
  306. data/app/assets/javascripts/alchemy/templates/spinner.hbs +0 -7
  307. data/app/assets/stylesheets/alchemy/jquery-ui.scss +0 -435
  308. data/app/javascript/alchemy_admin/datepicker.js +0 -40
  309. data/app/javascript/alchemy_admin/tinymce.js +0 -146
  310. data/app/javascript/alchemy_admin/translations.js +0 -32
  311. data/app/views/alchemy/admin/elements/fold.js.erb +0 -33
  312. data/app/views/alchemy/admin/elements/order.js.erb +0 -11
  313. data/app/views/alchemy/admin/elements/publish.js.erb +0 -21
  314. data/app/views/alchemy/admin/elements/update.js.erb +0 -27
  315. data/vendor/assets/fonts/fa-regular-400.eot +0 -0
  316. data/vendor/assets/fonts/fa-regular-400.svg +0 -803
  317. data/vendor/assets/fonts/fa-regular-400.ttf +0 -0
  318. data/vendor/assets/fonts/fa-regular-400.woff +0 -0
  319. data/vendor/assets/fonts/fa-regular-400.woff2 +0 -0
  320. data/vendor/assets/fonts/fa-solid-900.eot +0 -0
  321. data/vendor/assets/fonts/fa-solid-900.svg +0 -4938
  322. data/vendor/assets/fonts/fa-solid-900.ttf +0 -0
  323. data/vendor/assets/fonts/fa-solid-900.woff +0 -0
  324. data/vendor/assets/fonts/fa-solid-900.woff2 +0 -0
  325. data/vendor/assets/javascripts/fileupload/jquery.fileupload-process.js +0 -178
  326. data/vendor/assets/javascripts/fileupload/jquery.fileupload-validate.js +0 -125
  327. data/vendor/assets/javascripts/fileupload/jquery.fileupload.js +0 -1502
  328. data/vendor/assets/javascripts/fileupload/jquery.iframe-transport.js +0 -224
  329. data/vendor/assets/javascripts/jquery-ui/data.js +0 -45
  330. data/vendor/assets/javascripts/jquery-ui/ie.js +0 -20
  331. data/vendor/assets/javascripts/jquery-ui/keycode.js +0 -51
  332. data/vendor/assets/javascripts/jquery-ui/plugin.js +0 -49
  333. data/vendor/assets/javascripts/jquery-ui/safe-active-element.js +0 -46
  334. data/vendor/assets/javascripts/jquery-ui/safe-blur.js +0 -27
  335. data/vendor/assets/javascripts/jquery-ui/scroll-parent.js +0 -50
  336. data/vendor/assets/javascripts/jquery-ui/unique-id.js +0 -54
  337. data/vendor/assets/javascripts/jquery-ui/version.js +0 -20
  338. data/vendor/assets/javascripts/jquery-ui/widget.js +0 -754
  339. data/vendor/assets/javascripts/jquery-ui/widgets/draggable.js +0 -1268
  340. data/vendor/assets/javascripts/jquery-ui/widgets/mouse.js +0 -241
  341. data/vendor/assets/javascripts/jquery-ui/widgets/sortable.js +0 -1623
  342. data/vendor/assets/javascripts/jquery-ui/widgets/tabs.js +0 -931
  343. data/vendor/assets/javascripts/jquery_plugins/jquery.scrollTo.min.js +0 -7
  344. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js +0 -296
  345. data/vendor/assets/stylesheets/fontawesome/_animated.scss +0 -20
  346. data/vendor/assets/stylesheets/fontawesome/_bordered-pulled.scss +0 -20
  347. data/vendor/assets/stylesheets/fontawesome/_core.scss +0 -21
  348. data/vendor/assets/stylesheets/fontawesome/_fixed-width.scss +0 -6
  349. data/vendor/assets/stylesheets/fontawesome/_icons.scss +0 -1441
  350. data/vendor/assets/stylesheets/fontawesome/_larger.scss +0 -23
  351. data/vendor/assets/stylesheets/fontawesome/_list.scss +0 -18
  352. data/vendor/assets/stylesheets/fontawesome/_mixins.scss +0 -56
  353. data/vendor/assets/stylesheets/fontawesome/_rotated-flipped.scss +0 -24
  354. data/vendor/assets/stylesheets/fontawesome/_screen-reader.scss +0 -5
  355. data/vendor/assets/stylesheets/fontawesome/_stacked.scss +0 -31
  356. data/vendor/assets/stylesheets/fontawesome/_variables.scss +0 -1458
  357. data/vendor/assets/stylesheets/fontawesome/fontawesome.scss +0 -16
  358. data/vendor/assets/stylesheets/fontawesome/regular.scss +0 -23
  359. data/vendor/assets/stylesheets/fontawesome/solid.scss +0 -24
@@ -0,0 +1,79 @@
1
+ import "alchemy_admin/components/link_buttons/link_button"
2
+ import "alchemy_admin/components/link_buttons/unlink_button"
3
+
4
+ class LinkButtons extends HTMLElement {
5
+ constructor() {
6
+ super()
7
+ this.addEventListener("alchemy:link", this)
8
+ this.addEventListener("alchemy:unlink", this)
9
+ }
10
+
11
+ handleEvent(event) {
12
+ switch (event.type) {
13
+ case "alchemy:link":
14
+ this.setLink(event.detail)
15
+ break
16
+ case "alchemy:unlink":
17
+ this.removeLink()
18
+ }
19
+ event.stopPropagation()
20
+ }
21
+
22
+ setLink(data) {
23
+ this.linkUrlField.value = data.url
24
+ this.linkUrlField.dispatchEvent(new Event("change"))
25
+ this.linkTitleField.value = data.title
26
+ this.linkClassField.value = data.type
27
+ this.linkTargetField.value = data.target
28
+
29
+ this.unlinkButton.linked = true
30
+ this.elementEditor.setDirty()
31
+ }
32
+
33
+ removeLink() {
34
+ this.linkUrlField.value = ""
35
+ this.linkUrlField.dispatchEvent(new Event("change"))
36
+ this.linkTitleField.value = ""
37
+ this.linkClassField.value = ""
38
+ this.linkTargetField.value = ""
39
+
40
+ this.linkButton.classList.remove("linked")
41
+
42
+ this.elementEditor.setDirty()
43
+ }
44
+
45
+ get linkButton() {
46
+ return this.querySelector('[is="alchemy-link-button"]')
47
+ }
48
+
49
+ get unlinkButton() {
50
+ return this.querySelector('[is="alchemy-unlink-button"]')
51
+ }
52
+
53
+ get ingredientEditor() {
54
+ const ingredientId = this.dataset.ingredientId
55
+ return this.parentElement.closest(`[data-ingredient-id='${ingredientId}']`)
56
+ }
57
+
58
+ get elementEditor() {
59
+ return this.closest("alchemy-element-editor")
60
+ }
61
+
62
+ get linkUrlField() {
63
+ return this.ingredientEditor.querySelector("[data-link-value]")
64
+ }
65
+
66
+ get linkTitleField() {
67
+ return this.ingredientEditor.querySelector("[data-link-title]")
68
+ }
69
+
70
+ get linkTargetField() {
71
+ return this.ingredientEditor.querySelector("[data-link-target]")
72
+ }
73
+
74
+ get linkClassField() {
75
+ return this.ingredientEditor.querySelector("[data-link-class]")
76
+ }
77
+ }
78
+
79
+ customElements.define("alchemy-link-buttons", LinkButtons)
@@ -0,0 +1,45 @@
1
+ import { RemoteSelect } from "alchemy_admin/components/remote_select"
2
+
3
+ class NodeSelect extends RemoteSelect {
4
+ _searchQuery(term, page) {
5
+ return {
6
+ filter: {
7
+ name_or_page_name_cont: term,
8
+ ...JSON.parse(this.queryParams)
9
+ },
10
+ page: page
11
+ }
12
+ }
13
+
14
+ _renderResult(item) {
15
+ return this._renderListEntry(item)
16
+ }
17
+
18
+ /**
19
+ * html template for each list entry
20
+ * @param {object} node
21
+ * @returns {string}
22
+ * @private
23
+ */
24
+ _renderListEntry(node) {
25
+ const ancestors = node.ancestors.map((a) => a.name)
26
+ return `
27
+ <div class="node-select--node">
28
+ <i class="icon ri-menu-2-line"></i>
29
+ <div class="node-select--node-display_name">
30
+ <span class="node-select--node-ancestors">
31
+ ${ancestors.join(" /&nbsp;")}
32
+ </span>
33
+ <span class="node-select--node-name">
34
+ ${node.name}
35
+ </span>
36
+ </div>
37
+ <div class="node-select--node-url">
38
+ ${node.url || ""}
39
+ </div>
40
+ </div>
41
+ `
42
+ }
43
+ }
44
+
45
+ customElements.define("alchemy-node-select", NodeSelect)
@@ -0,0 +1,18 @@
1
+ import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
2
+
3
+ class Overlay extends AlchemyHTMLElement {
4
+ render() {
5
+ return `
6
+ <alchemy-spinner></alchemy-spinner>
7
+ <div id="overlay_text_box">
8
+ <span id="overlay_text">${this.getAttribute("text")}</span>
9
+ </div>
10
+ `
11
+ }
12
+
13
+ set show(value) {
14
+ this.style.setProperty("display", value ? "block" : "none")
15
+ }
16
+ }
17
+
18
+ customElements.define("alchemy-overlay", Overlay)
@@ -0,0 +1,63 @@
1
+ import { RemoteSelect } from "alchemy_admin/components/remote_select"
2
+
3
+ class PageSelect extends RemoteSelect {
4
+ onChange(event) {
5
+ if (event.added) {
6
+ this.dispatchCustomEvent("PageSelect.ItemAdded", event.added)
7
+ } else {
8
+ this.dispatchCustomEvent("PageSelect.ItemRemoved")
9
+ }
10
+ }
11
+
12
+ _searchQuery(term, page) {
13
+ return {
14
+ q: {
15
+ name_cont: term,
16
+ ...JSON.parse(this.queryParams)
17
+ },
18
+ page: page
19
+ }
20
+ }
21
+
22
+ _parseResponse(response) {
23
+ const meta = response.meta
24
+ return {
25
+ results: response.pages,
26
+ more: meta.page * meta.per_page < meta.total_count
27
+ }
28
+ }
29
+
30
+ /**
31
+ * result which is visible if a page was selected
32
+ * @param {object} page
33
+ * @returns {string}
34
+ * @private
35
+ */
36
+ _renderResult(page) {
37
+ return page.text || page.name
38
+ }
39
+
40
+ /**
41
+ * html template for each list entry
42
+ * @param {object} page
43
+ * @returns {string}
44
+ * @private
45
+ */
46
+ _renderListEntry(page) {
47
+ return `
48
+ <div class="page-select--page">
49
+ <div class="page-select--top">
50
+ <i class="icon ri-file-3-line"></i>
51
+ <span class="page-select--page-name">${page.name}</span>
52
+ <span class="page-select--page-urlname">${page.url_path}</span>
53
+ </div>
54
+ <div class="page-select--bottom">
55
+ <span class="page-select--site-name">${page.site.name}</span>
56
+ <span class="page-select--language-code">${page.language.name}</span>
57
+ </div>
58
+ </div>
59
+ `
60
+ }
61
+ }
62
+
63
+ customElements.define("alchemy-page-select", PageSelect)
@@ -0,0 +1,134 @@
1
+ import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
2
+
3
+ export class RemoteSelect extends AlchemyHTMLElement {
4
+ static properties = {
5
+ allowClear: { default: false },
6
+ selection: { default: undefined },
7
+ placeholder: { default: "" },
8
+ queryParams: { default: "{}" },
9
+ url: { default: "" }
10
+ }
11
+
12
+ connected() {
13
+ this.input.classList.add("alchemy_selectbox")
14
+
15
+ $(this.input)
16
+ .select2(this.select2Config)
17
+ .on("select2-open", (evt) => {
18
+ this.onOpen(evt)
19
+ })
20
+ .on("change", (evt) => {
21
+ this.onChange(evt)
22
+ })
23
+ }
24
+
25
+ /**
26
+ * Optional on change handler called by Select2.
27
+ * @param {Event} event
28
+ */
29
+ onChange(event) {
30
+ // Left empty for sub classes to define this as needed.
31
+ }
32
+
33
+ /**
34
+ * Optional on open handler called by Select2.
35
+ * @param {Event} event
36
+ */
37
+ onOpen(event) {
38
+ // add focus to the search input. Select2 is handling the focus on the first opening,
39
+ // but it does not work the second time. One process in select2 is "stealing" the focus
40
+ // if the command is not delayed. It is an intermediate solution until we are going to
41
+ // move away from Select2
42
+ setTimeout(() => {
43
+ document.querySelector("#select2-drop .select2-input").focus()
44
+ }, 100)
45
+ }
46
+
47
+ get input() {
48
+ return this.getElementsByTagName("input")[0]
49
+ }
50
+
51
+ get select2Config() {
52
+ return {
53
+ placeholder: this.placeholder,
54
+ allowClear: this.allowClear,
55
+ initSelection: (_$el, callback) => {
56
+ if (this.selection) {
57
+ callback(JSON.parse(this.selection))
58
+ }
59
+ },
60
+ ajax: this.ajaxConfig,
61
+ formatSelection: (item) => this._renderResult(item),
62
+ formatResult: (item) => this._renderListEntry(item)
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Ajax configuration for Select2
68
+ * @returns {object}
69
+ */
70
+ get ajaxConfig() {
71
+ return {
72
+ url: this.url,
73
+ datatype: "json",
74
+ quietMillis: 300,
75
+ data: (term, page) => this._searchQuery(term, page),
76
+ results: (response) => this._parseResponse(response)
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Search query send to server from select2
82
+ * @param {string} term
83
+ * @param {number} page
84
+ * @returns {object}
85
+ * @private
86
+ */
87
+ _searchQuery(term, page) {
88
+ return {
89
+ q: {
90
+ name_cont: term,
91
+ ...JSON.parse(this.queryParams)
92
+ },
93
+ page: page
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Parses server response into select2 results object
99
+ * @param {object} response
100
+ * @returns {object}
101
+ * @private
102
+ */
103
+ _parseResponse(response) {
104
+ const meta = response.meta
105
+ return {
106
+ results: response.data,
107
+ more: meta.page * meta.per_page < meta.total_count
108
+ }
109
+ }
110
+
111
+ /**
112
+ * result which is visible if a page was selected
113
+ * @param {object} item
114
+ * @returns {string}
115
+ * @private
116
+ */
117
+ _renderResult() {
118
+ throw new Error(
119
+ "You need to define a _renderResult function on your sub class!"
120
+ )
121
+ }
122
+
123
+ /**
124
+ * html template for each list entry
125
+ * @param {object} item
126
+ * @returns {string}
127
+ * @private
128
+ */
129
+ _renderListEntry() {
130
+ throw new Error(
131
+ "You need to define a _renderListEntry function on your sub class!"
132
+ )
133
+ }
134
+ }
@@ -0,0 +1,12 @@
1
+ class Select extends HTMLSelectElement {
2
+ connectedCallback() {
3
+ this.classList.add("alchemy_selectbox")
4
+
5
+ $(this).select2({
6
+ minimumResultsForSearch: 5,
7
+ dropdownAutoWidth: true
8
+ })
9
+ }
10
+ }
11
+
12
+ customElements.define("alchemy-select", Select, { extends: "select" })
@@ -0,0 +1,31 @@
1
+ import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
2
+
3
+ class Spinner extends AlchemyHTMLElement {
4
+ static properties = {
5
+ size: { default: "medium" },
6
+ color: { default: "currentColor" }
7
+ }
8
+
9
+ render() {
10
+ this.className = `spinner spinner--${this.size}`
11
+
12
+ return `
13
+ <svg width="100%" viewBox="0 0 28 28" style="--spinner-color: ${this.color}">
14
+ <path
15
+ class="hex1"
16
+ d="M5.938,18.07l-5.878-5.9l2.154-8.058l8.024-2.161l5.87,5.9l-2.144,8.058L5.938,18.07z"
17
+ />
18
+ <path
19
+ class="hex2"
20
+ d="M19.686,20.785l-4.731-4.754l1.725-6.487l6.468-1.742l4.733,4.754l-1.734,6.487L19.686,20.785z"
21
+ />
22
+ <path
23
+ class="hex3"
24
+ d="M11.708,26.294l-3.47-3.485l1.276-4.758l4.74-1.276l3.468,3.485l-1.265,4.758L11.708,26.294z"
25
+ />
26
+ </svg>
27
+ `
28
+ }
29
+ }
30
+
31
+ customElements.define("alchemy-spinner", Spinner)
@@ -0,0 +1,146 @@
1
+ import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
2
+ import { currentLocale } from "alchemy_admin/i18n"
3
+
4
+ const TOOLBAR_ROW_HEIGHT = 30
5
+ const TOOLBAR_BORDER_WIDTH = 1
6
+ const STATUSBAR_HEIGHT = 29.5
7
+ const EDITOR_BORDER_WIDTH = 2
8
+
9
+ class Tinymce extends AlchemyHTMLElement {
10
+ /**
11
+ * the observer will initialize Tinymce if the textarea becomes visible
12
+ */
13
+ connected() {
14
+ this.className = "tinymce_container"
15
+
16
+ const observerCallback = (entries, observer) => {
17
+ entries.forEach((entry) => {
18
+ if (entry.intersectionRatio > 0) {
19
+ this._initTinymceEditor()
20
+ // disable observer after the Tinymce was initialized
21
+ observer.unobserve(entry.target)
22
+ }
23
+ })
24
+ }
25
+
26
+ const options = {
27
+ root: document.getElementById("element_area"),
28
+ rootMargin: "0px",
29
+ threshold: [0.05]
30
+ }
31
+
32
+ this.tinymceIntersectionObserver = new IntersectionObserver(
33
+ observerCallback,
34
+ options
35
+ )
36
+ this.tinymceIntersectionObserver.observe(this)
37
+ }
38
+
39
+ /**
40
+ * disconnect intersection observer and remove Tinymce editor if the web components get destroyed
41
+ */
42
+ disconnected() {
43
+ if (this.tinymceIntersectionObserver !== null) {
44
+ this.tinymceIntersectionObserver.disconnect()
45
+ }
46
+
47
+ tinymce.get(this.editorId)?.remove(this.editorId)
48
+ }
49
+
50
+ render() {
51
+ return `
52
+ ${this.initialContent}
53
+ <alchemy-spinner size="small"></alchemy-spinner>
54
+ `
55
+ }
56
+
57
+ /**
58
+ * hide the textarea until TinyMCE is ready to show the editor
59
+ */
60
+ afterRender() {
61
+ this.style.minHeight = `${this.minHeight}px`
62
+ this.editor.style.display = "none"
63
+ }
64
+
65
+ /**
66
+ * initialize Richtext area after the Intersection observer triggered
67
+ * @private
68
+ */
69
+ _initTinymceEditor() {
70
+ tinymce.init(this.configuration).then((editors) => {
71
+ editors.forEach((editor) => {
72
+ // mark the editor container as visible
73
+ // without these correction the editor remains hidden
74
+ // after a drag and drop action
75
+ editor.show()
76
+
77
+ // remove the spinner after the Tinymce initialized
78
+ this.getElementsByTagName("alchemy-spinner")[0].remove()
79
+
80
+ // event listener to mark the editor as dirty
81
+ editor.on("dirty", () => this.elementEditor.setDirty())
82
+ editor.on("click", () => this.elementEditor.onClickElement(false))
83
+ })
84
+ })
85
+ }
86
+
87
+ get configuration() {
88
+ const customConfig = {}
89
+
90
+ // read the attributes on the component and add them as custom configuration
91
+ this.getAttributeNames().forEach((attributeName) => {
92
+ if (!["class", "id", "is", "name"].includes(attributeName)) {
93
+ const config = this.getAttribute(attributeName)
94
+ const key = attributeName.replaceAll("-", "_")
95
+
96
+ try {
97
+ customConfig[key] = JSON.parse(config)
98
+ } catch (e) {
99
+ // also string values as parameter
100
+ customConfig[key] = config
101
+ }
102
+ }
103
+ })
104
+
105
+ return {
106
+ ...Alchemy.TinymceDefaults,
107
+ ...customConfig,
108
+ locale: currentLocale(),
109
+ selector: `#${this.editorId}`
110
+ }
111
+ }
112
+
113
+ get editorId() {
114
+ return this.editor.id
115
+ }
116
+
117
+ get editor() {
118
+ return this.getElementsByTagName("textarea")[0]
119
+ }
120
+
121
+ get elementEditor() {
122
+ return document
123
+ .getElementById(this.editorId)
124
+ .closest("alchemy-element-editor")
125
+ }
126
+
127
+ get minHeight() {
128
+ let minHeight = this.configuration.min_height || 0
129
+
130
+ if (Array.isArray(this.configuration.toolbar)) {
131
+ minHeight += this.configuration.toolbar.length * TOOLBAR_ROW_HEIGHT
132
+ minHeight += TOOLBAR_BORDER_WIDTH
133
+ } else if (this.configuration.toolbar) {
134
+ minHeight += TOOLBAR_ROW_HEIGHT
135
+ minHeight += TOOLBAR_BORDER_WIDTH
136
+ }
137
+ if (this.configuration.statusbar) {
138
+ minHeight += STATUSBAR_HEIGHT
139
+ }
140
+ minHeight += EDITOR_BORDER_WIDTH
141
+
142
+ return minHeight
143
+ }
144
+ }
145
+
146
+ customElements.define("alchemy-tinymce", Tinymce)