alchemy_cms 7.1.13 → 7.2.0.b

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -39
  3. data/Gemfile +2 -11
  4. data/LICENSE +1 -1
  5. data/README.md +5 -6
  6. data/SECURITY.md +1 -1
  7. data/alchemy_cms.gemspec +3 -4
  8. data/app/assets/javascripts/alchemy/admin.js +0 -9
  9. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +5 -15
  10. data/app/assets/javascripts/alchemy/alchemy.image_overlay.coffee +5 -4
  11. data/app/assets/javascripts/alchemy/templates/index.js +0 -1
  12. data/app/assets/javascripts/alchemy/templates/node_folder.hbs +1 -1
  13. data/app/assets/javascripts/alchemy/templates/page_folder.hbs +1 -1
  14. data/app/assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js +20 -7
  15. data/app/assets/stylesheets/alchemy/_custom-properties.scss +12 -0
  16. data/app/assets/stylesheets/alchemy/_mixins.scss +10 -6
  17. data/app/assets/stylesheets/alchemy/_variables.scss +3 -0
  18. data/app/assets/stylesheets/alchemy/admin.scss +2 -2
  19. data/app/assets/stylesheets/alchemy/archive.scss +4 -3
  20. data/app/assets/stylesheets/alchemy/attachment-select.scss +19 -0
  21. data/app/assets/stylesheets/alchemy/base.scss +31 -18
  22. data/app/assets/stylesheets/alchemy/buttons.scss +3 -4
  23. data/app/assets/stylesheets/alchemy/dashboard.scss +1 -1
  24. data/app/assets/stylesheets/alchemy/dialogs.scss +2 -5
  25. data/app/assets/stylesheets/alchemy/elements.scss +74 -42
  26. data/app/assets/stylesheets/alchemy/flash.scss +20 -70
  27. data/app/assets/stylesheets/alchemy/forms.scss +41 -36
  28. data/app/assets/stylesheets/alchemy/frame.scss +12 -3
  29. data/app/assets/stylesheets/alchemy/icons.scss +34 -2
  30. data/app/assets/stylesheets/alchemy/image_library.scss +18 -9
  31. data/app/assets/stylesheets/alchemy/{filter_field.scss → list_filter.scss} +8 -7
  32. data/app/assets/stylesheets/alchemy/lists.scss +1 -1
  33. data/app/assets/stylesheets/alchemy/navigation.scss +9 -12
  34. data/app/assets/stylesheets/alchemy/node-select.scss +1 -1
  35. data/app/assets/stylesheets/alchemy/nodes.scss +15 -13
  36. data/app/assets/stylesheets/alchemy/notices.scss +56 -39
  37. data/app/assets/stylesheets/alchemy/page-select.scss +1 -4
  38. data/app/assets/stylesheets/alchemy/pagination.scss +11 -1
  39. data/app/assets/stylesheets/alchemy/preview_window.scss +3 -3
  40. data/app/assets/stylesheets/alchemy/search.scss +4 -4
  41. data/app/assets/stylesheets/alchemy/selects.scss +13 -7
  42. data/app/assets/stylesheets/alchemy/shoelace.scss +33 -2
  43. data/app/assets/stylesheets/alchemy/sitemap.scss +155 -159
  44. data/app/assets/stylesheets/alchemy/tables.scss +49 -12
  45. data/app/assets/stylesheets/alchemy/tags.scss +17 -11
  46. data/app/assets/stylesheets/alchemy/toolbar.scss +2 -2
  47. data/app/assets/stylesheets/alchemy/typography.scss +41 -22
  48. data/app/assets/stylesheets/alchemy/upload.scss +5 -4
  49. data/app/components/alchemy/admin/attachment_select.rb +39 -0
  50. data/app/components/alchemy/admin/icon.rb +72 -0
  51. data/app/components/alchemy/admin/link_dialog/anchor_tab.rb +41 -0
  52. data/app/components/alchemy/admin/link_dialog/base_tab.rb +75 -0
  53. data/app/components/alchemy/admin/link_dialog/external_tab.rb +42 -0
  54. data/app/components/alchemy/admin/link_dialog/file_tab.rb +45 -0
  55. data/app/components/alchemy/admin/link_dialog/internal_tab.rb +66 -0
  56. data/app/components/alchemy/admin/link_dialog/tabs.rb +33 -0
  57. data/app/components/alchemy/admin/list_filter.rb +42 -0
  58. data/app/components/alchemy/admin/message.rb +19 -0
  59. data/app/components/alchemy/admin/tags_autocomplete.rb +25 -0
  60. data/app/components/alchemy/admin/toolbar_button.rb +111 -0
  61. data/app/components/alchemy/ingredients/datetime_view.rb +2 -3
  62. data/app/components/alchemy/ingredients/link_view.rb +1 -7
  63. data/app/components/alchemy/ingredients/picture_view.rb +2 -2
  64. data/app/components/alchemy/ingredients/text_view.rb +1 -2
  65. data/app/controllers/alchemy/admin/base_controller.rb +3 -27
  66. data/app/controllers/alchemy/admin/elements_controller.rb +4 -2
  67. data/app/controllers/alchemy/admin/ingredients_controller.rb +2 -0
  68. data/app/controllers/alchemy/admin/languages_controller.rb +2 -2
  69. data/app/controllers/alchemy/admin/layoutpages_controller.rb +0 -19
  70. data/app/controllers/alchemy/admin/legacy_page_urls_controller.rb +12 -4
  71. data/app/controllers/alchemy/admin/nodes_controller.rb +26 -0
  72. data/app/controllers/alchemy/admin/pages_controller.rb +16 -79
  73. data/app/controllers/alchemy/admin/picture_descriptions_controller.rb +15 -0
  74. data/app/controllers/alchemy/admin/pictures_controller.rb +18 -1
  75. data/app/controllers/alchemy/admin/resources_controller.rb +16 -11
  76. data/app/controllers/alchemy/api/attachments_controller.rb +44 -0
  77. data/app/controllers/alchemy/api/pages_controller.rb +10 -6
  78. data/app/controllers/alchemy/base_controller.rb +2 -2
  79. data/app/controllers/alchemy/messages_controller.rb +3 -3
  80. data/app/controllers/alchemy/pages_controller.rb +8 -6
  81. data/app/controllers/concerns/alchemy/admin/current_language.rb +1 -11
  82. data/app/controllers/concerns/alchemy/legacy_page_redirects.rb +1 -1
  83. data/app/decorators/alchemy/element_editor.rb +2 -2
  84. data/app/helpers/alchemy/admin/base_helper.rb +8 -60
  85. data/app/helpers/alchemy/admin/elements_helper.rb +1 -1
  86. data/app/helpers/alchemy/admin/ingredients_helper.rb +1 -1
  87. data/app/helpers/alchemy/base_helper.rb +9 -91
  88. data/app/helpers/alchemy/elements_helper.rb +3 -3
  89. data/app/helpers/alchemy/pages_helper.rb +16 -9
  90. data/app/javascript/alchemy_admin/components/attachment_select.js +24 -0
  91. data/app/javascript/alchemy_admin/components/button.js +3 -0
  92. data/app/javascript/alchemy_admin/components/clipboard_button.js +3 -2
  93. data/app/javascript/alchemy_admin/components/dialog_link.js +10 -7
  94. data/app/javascript/alchemy_admin/components/dom_id_select.js +69 -0
  95. data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +42 -0
  96. data/app/javascript/alchemy_admin/components/element_editor/publish_element_button.js +4 -2
  97. data/app/javascript/alchemy_admin/components/element_editor.js +21 -13
  98. data/app/javascript/alchemy_admin/components/elements_window.js +87 -0
  99. data/app/javascript/alchemy_admin/components/growl.js +13 -0
  100. data/app/javascript/alchemy_admin/components/icon.js +51 -0
  101. data/app/javascript/alchemy_admin/components/index.js +24 -0
  102. data/app/javascript/alchemy_admin/components/ingredient_group.js +6 -0
  103. data/app/javascript/alchemy_admin/components/link_buttons/link_button.js +21 -11
  104. data/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js +2 -1
  105. data/app/javascript/alchemy_admin/components/link_buttons.js +1 -0
  106. data/app/javascript/alchemy_admin/components/list_filter.js +68 -0
  107. data/app/javascript/alchemy_admin/components/message.js +69 -0
  108. data/app/javascript/alchemy_admin/components/node_select.js +1 -1
  109. data/app/javascript/alchemy_admin/components/overlay.js +6 -6
  110. data/app/javascript/alchemy_admin/components/page_select.js +3 -7
  111. data/app/javascript/alchemy_admin/components/preview_window.js +121 -0
  112. data/app/javascript/alchemy_admin/components/remote_select.js +4 -1
  113. data/app/javascript/alchemy_admin/components/select.js +37 -1
  114. data/app/javascript/alchemy_admin/components/tags_autocomplete.js +57 -0
  115. data/app/javascript/alchemy_admin/components/uploader/file_upload.js +4 -3
  116. data/app/javascript/alchemy_admin/components/uploader/progress.js +1 -1
  117. data/app/javascript/alchemy_admin/confirm_dialog.js +133 -0
  118. data/app/javascript/alchemy_admin/dirty.js +19 -14
  119. data/app/javascript/alchemy_admin/fixed_elements.js +24 -0
  120. data/app/javascript/alchemy_admin/growler.js +15 -0
  121. data/app/javascript/alchemy_admin/gui.js +2 -4
  122. data/app/javascript/alchemy_admin/hotkeys.js +60 -0
  123. data/app/javascript/alchemy_admin/image_loader.js +2 -2
  124. data/app/javascript/alchemy_admin/ingredient_anchor_link.js +2 -3
  125. data/app/javascript/alchemy_admin/initializer.js +1 -8
  126. data/app/javascript/alchemy_admin/link_dialog.js +131 -0
  127. data/app/javascript/alchemy_admin/locales/en.js +3 -0
  128. data/app/javascript/alchemy_admin/node_tree.js +4 -3
  129. data/app/javascript/alchemy_admin/page_sorter.js +23 -14
  130. data/app/javascript/alchemy_admin/picture_editors.js +6 -5
  131. data/app/javascript/alchemy_admin/shoelace_theme.js +60 -0
  132. data/app/javascript/alchemy_admin/sitemap.js +9 -3
  133. data/app/javascript/alchemy_admin/sortable_elements.js +4 -6
  134. data/app/javascript/alchemy_admin.js +18 -42
  135. data/app/models/alchemy/current.rb +26 -0
  136. data/app/models/alchemy/element.rb +1 -1
  137. data/app/models/alchemy/ingredients/audio.rb +0 -11
  138. data/app/models/alchemy/ingredients/datetime.rb +1 -1
  139. data/app/models/alchemy/ingredients/headline.rb +8 -1
  140. data/app/models/alchemy/ingredients/picture.rb +6 -0
  141. data/app/models/alchemy/ingredients/video.rb +0 -12
  142. data/app/models/alchemy/language.rb +8 -6
  143. data/app/models/alchemy/node.rb +2 -2
  144. data/app/models/alchemy/page/page_elements.rb +8 -8
  145. data/app/models/alchemy/page/page_layouts.rb +3 -3
  146. data/app/models/alchemy/page/page_natures.rb +13 -9
  147. data/app/models/alchemy/page/page_scopes.rb +2 -2
  148. data/app/models/alchemy/page/publisher.rb +1 -0
  149. data/app/models/alchemy/page.rb +16 -31
  150. data/app/models/alchemy/picture.rb +8 -0
  151. data/app/models/alchemy/picture_description.rb +8 -0
  152. data/app/models/alchemy/picture_variant.rb +1 -1
  153. data/app/models/alchemy/site.rb +10 -7
  154. data/app/models/concerns/alchemy/picture_thumbnails.rb +5 -4
  155. data/app/serializers/alchemy/attachment_serializer.rb +8 -0
  156. data/app/serializers/alchemy/page_node_serializer.rb +9 -0
  157. data/app/views/alchemy/_menubar.html.erb +1 -1
  158. data/app/views/alchemy/_preview_mode_code.html.erb +1 -1
  159. data/app/views/alchemy/admin/attachments/_tag_list.html.erb +2 -2
  160. data/app/views/alchemy/admin/attachments/archive_overlay.js.erb +0 -1
  161. data/app/views/alchemy/admin/attachments/edit.html.erb +3 -4
  162. data/app/views/alchemy/admin/clipboard/clear.js.erb +1 -1
  163. data/app/views/alchemy/admin/clipboard/index.html.erb +1 -1
  164. data/app/views/alchemy/admin/clipboard/insert.js.erb +1 -1
  165. data/app/views/alchemy/admin/clipboard/remove.js.erb +1 -1
  166. data/app/views/alchemy/admin/dashboard/_locked_pages.html.erb +1 -1
  167. data/app/views/alchemy/admin/dashboard/_sites.html.erb +1 -1
  168. data/app/views/alchemy/admin/dashboard/help.html.erb +48 -12
  169. data/app/views/alchemy/admin/dashboard/index.html.erb +1 -1
  170. data/app/views/alchemy/admin/dashboard/info.html.erb +5 -8
  171. data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +1 -1
  172. data/app/views/alchemy/admin/elements/_element.html.erb +5 -5
  173. data/app/views/alchemy/admin/elements/_footer.html.erb +1 -1
  174. data/app/views/alchemy/admin/elements/_header.html.erb +6 -2
  175. data/app/views/alchemy/admin/elements/_toolbar.html.erb +8 -6
  176. data/app/views/alchemy/admin/elements/create.js.erb +0 -5
  177. data/app/views/alchemy/admin/elements/index.html.erb +70 -34
  178. data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +1 -2
  179. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +3 -5
  180. data/app/views/alchemy/admin/languages/_language.html.erb +1 -1
  181. data/app/views/alchemy/admin/languages/index.html.erb +2 -2
  182. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +18 -18
  183. data/app/views/alchemy/admin/layoutpages/edit.html.erb +4 -5
  184. data/app/views/alchemy/admin/layoutpages/index.html.erb +2 -2
  185. data/app/views/alchemy/admin/legacy_page_urls/_legacy_page_url.html.erb +10 -11
  186. data/app/views/alchemy/admin/legacy_page_urls/_new.html.erb +15 -17
  187. data/app/views/alchemy/admin/legacy_page_urls/_table.html.erb +16 -0
  188. data/app/views/alchemy/admin/legacy_page_urls/_update.turbo_stream.erb +12 -0
  189. data/app/views/alchemy/admin/legacy_page_urls/create.turbo_stream.erb +8 -0
  190. data/app/views/alchemy/admin/legacy_page_urls/destroy.turbo_stream.erb +1 -0
  191. data/app/views/alchemy/admin/legacy_page_urls/edit.html.erb +27 -0
  192. data/app/views/alchemy/admin/legacy_page_urls/show.html.erb +1 -0
  193. data/app/views/alchemy/admin/legacy_page_urls/update.turbo_stream.erb +1 -0
  194. data/app/views/alchemy/admin/nodes/_form.html.erb +12 -11
  195. data/app/views/alchemy/admin/nodes/_label.html.erb +1 -0
  196. data/app/views/alchemy/admin/nodes/_node.html.erb +19 -19
  197. data/app/views/alchemy/admin/nodes/_page_nodes.html.erb +48 -0
  198. data/app/views/alchemy/admin/nodes/_update.turbo_stream.erb +9 -0
  199. data/app/views/alchemy/admin/nodes/create.turbo_stream.erb +1 -0
  200. data/app/views/alchemy/admin/nodes/destroy.turbo_stream.erb +1 -0
  201. data/app/views/alchemy/admin/nodes/index.html.erb +3 -3
  202. data/app/views/alchemy/admin/pages/_form.html.erb +3 -4
  203. data/app/views/alchemy/admin/pages/_legacy_urls.html.erb +4 -15
  204. data/app/views/alchemy/admin/pages/_page.html.erb +39 -39
  205. data/app/views/alchemy/admin/pages/_table_row.html.erb +3 -3
  206. data/app/views/alchemy/admin/pages/_toolbar.html.erb +2 -2
  207. data/app/views/alchemy/admin/pages/configure.html.erb +6 -0
  208. data/app/views/alchemy/admin/pages/edit.html.erb +15 -62
  209. data/app/views/alchemy/admin/pages/unlock.js.erb +3 -3
  210. data/app/views/alchemy/admin/partials/_autocomplete_tag_list.html.erb +3 -1
  211. data/app/views/alchemy/admin/partials/_flash_notices.html.erb +4 -2
  212. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +1 -1
  213. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +5 -2
  214. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +2 -2
  215. data/app/views/alchemy/admin/partials/_search_form.html.erb +2 -2
  216. data/app/views/alchemy/admin/partials/_site_select.html.erb +1 -1
  217. data/app/views/alchemy/admin/picture_descriptions/_form.html.erb +11 -0
  218. data/app/views/alchemy/admin/picture_descriptions/edit.html.erb +6 -0
  219. data/app/views/alchemy/admin/pictures/_form.html.erb +4 -3
  220. data/app/views/alchemy/admin/pictures/_infos.html.erb +1 -1
  221. data/app/views/alchemy/admin/pictures/_picture_description_field.html.erb +29 -0
  222. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +2 -2
  223. data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +0 -2
  224. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +3 -3
  225. data/app/views/alchemy/admin/pictures/show.html.erb +3 -3
  226. data/app/views/alchemy/admin/resources/_form.html.erb +3 -4
  227. data/app/views/alchemy/admin/resources/_per_page_select.html.erb +1 -1
  228. data/app/views/alchemy/admin/resources/_tag_list.html.erb +2 -2
  229. data/app/views/alchemy/admin/resources/index.html.erb +2 -2
  230. data/app/views/alchemy/admin/sites/index.html.erb +1 -1
  231. data/app/views/alchemy/admin/styleguide/index.html.erb +29 -24
  232. data/app/views/alchemy/admin/tags/_tag.html.erb +1 -1
  233. data/app/views/alchemy/admin/tags/edit.html.erb +1 -1
  234. data/app/views/alchemy/admin/tags/index.html.erb +1 -1
  235. data/app/views/alchemy/base/500.html.erb +7 -18
  236. data/app/views/alchemy/base/error_notice.html.erb +3 -1
  237. data/app/views/alchemy/ingredients/_boolean_editor.html.erb +1 -1
  238. data/app/views/alchemy/ingredients/_datetime_editor.html.erb +2 -3
  239. data/app/views/alchemy/ingredients/_headline_editor.html.erb +13 -8
  240. data/app/views/alchemy/ingredients/_picture_editor.html.erb +1 -1
  241. data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +1 -1
  242. data/app/views/alchemy/language_links/_language.html.erb +1 -1
  243. data/app/views/kaminari/alchemy/_first_page.html.erb +2 -2
  244. data/app/views/kaminari/alchemy/_gap.html.erb +1 -1
  245. data/app/views/kaminari/alchemy/_last_page.html.erb +2 -2
  246. data/app/views/kaminari/alchemy/_next_page.html.erb +2 -2
  247. data/app/views/kaminari/alchemy/_prev_page.html.erb +2 -2
  248. data/app/views/layouts/alchemy/admin.html.erb +2 -1
  249. data/bundles/shoelace.js +3 -1
  250. data/config/locales/alchemy.en.yml +16 -3
  251. data/config/routes.rb +4 -2
  252. data/db/migrate/20240314105244_create_alchemy_picture_descriptions.rb +11 -0
  253. data/lib/alchemy/configuration_methods.rb +1 -1
  254. data/lib/alchemy/controller_actions.rb +3 -3
  255. data/lib/alchemy/element_definition.rb +10 -6
  256. data/lib/alchemy/engine.rb +19 -2
  257. data/lib/alchemy/page_layout.rb +10 -6
  258. data/lib/alchemy/permissions.rb +4 -3
  259. data/lib/alchemy/resource.rb +4 -14
  260. data/lib/alchemy/routing_constraints.rb +1 -1
  261. data/lib/alchemy/seeder.rb +2 -2
  262. data/lib/alchemy/test_support/capybara_helpers.rb +4 -0
  263. data/lib/alchemy/test_support/factories/language_factory.rb +1 -1
  264. data/lib/alchemy/test_support/shared_contexts.rb +8 -0
  265. data/lib/alchemy/tinymce.rb +2 -1
  266. data/lib/alchemy/version.rb +1 -1
  267. data/lib/alchemy.rb +36 -0
  268. data/lib/alchemy_cms.rb +0 -1
  269. data/lib/generators/alchemy/menus/templates/node.html.erb +2 -2
  270. data/lib/generators/alchemy/menus/templates/node.html.haml +2 -2
  271. data/lib/generators/alchemy/menus/templates/node.html.slim +2 -2
  272. data/lib/generators/alchemy/menus/templates/wrapper.html.erb +1 -1
  273. data/lib/generators/alchemy/menus/templates/wrapper.html.haml +1 -1
  274. data/lib/generators/alchemy/menus/templates/wrapper.html.slim +1 -1
  275. data/lib/tasks/alchemy/sitemap.rake +97 -0
  276. data/lib/tasks/alchemy/tidy.rake +1 -0
  277. data/package.json +8 -8
  278. data/vendor/assets/fonts/remixicon.symbol.svg +11 -0
  279. data/vendor/javascript/shoelace.min.js +333 -118
  280. data/vendor/javascript/sortable.min.js +1 -1
  281. data/vendor/javascript/tinymce.min.js +1 -1
  282. data/vendor/javascript/ungap-custom-elements.min.js +1 -1
  283. metadata +63 -55
  284. data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +0 -85
  285. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +0 -107
  286. data/app/assets/javascripts/alchemy/alchemy.file_progress.js.coffee +0 -66
  287. data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +0 -45
  288. data/app/assets/javascripts/alchemy/alchemy.growler.js.coffee +0 -24
  289. data/app/assets/javascripts/alchemy/alchemy.hotkeys.js.coffee +0 -49
  290. data/app/assets/javascripts/alchemy/alchemy.initializer.js.coffee +0 -0
  291. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +0 -230
  292. data/app/assets/javascripts/alchemy/alchemy.list_filter.js.coffee +0 -49
  293. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +0 -82
  294. data/app/assets/javascripts/alchemy/alchemy.string_extension.js.coffee +0 -11
  295. data/app/assets/javascripts/alchemy/templates/page.hbs +0 -19
  296. data/app/javascript/alchemy_admin/tags_autocomplete.js +0 -46
  297. data/app/models/alchemy/tree_node.rb +0 -7
  298. data/app/views/alchemy/admin/elements/destroy.js.erb +0 -8
  299. data/app/views/alchemy/admin/legacy_page_urls/_form.html.erb +0 -5
  300. data/app/views/alchemy/admin/legacy_page_urls/create.js.erb +0 -9
  301. data/app/views/alchemy/admin/legacy_page_urls/destroy.js.erb +0 -6
  302. data/app/views/alchemy/admin/legacy_page_urls/update.js.erb +0 -2
  303. data/app/views/alchemy/admin/pages/_anchor_link.html.erb +0 -22
  304. data/app/views/alchemy/admin/pages/_external_link.html.erb +0 -31
  305. data/app/views/alchemy/admin/pages/_file_link.html.erb +0 -31
  306. data/app/views/alchemy/admin/pages/_internal_link.html.erb +0 -35
  307. data/app/views/alchemy/admin/pages/link.html.erb +0 -26
  308. data/app/views/alchemy/admin/partials/_flash.html.erb +0 -4
  309. data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +0 -29
  310. data/lib/alchemy/test_support/current_language_shared_examples.rb +0 -33
  311. data/vendor/assets/fonts/remixicon.eot +0 -0
  312. data/vendor/assets/fonts/remixicon.svg +0 -7816
  313. data/vendor/assets/fonts/remixicon.ttf +0 -0
  314. data/vendor/assets/fonts/remixicon.woff +0 -0
  315. data/vendor/assets/fonts/remixicon.woff2 +0 -0
  316. data/vendor/assets/stylesheets/remixicon.scss +0 -10480
@@ -1,45 +0,0 @@
1
- window.Alchemy = Alchemy || {}
2
-
3
- Alchemy.FixedElements = {
4
- WRAPPER: '<sl-tab-group id="fixed-elements" />',
5
- TABS: '<sl-tab slot="nav" panel="main-content-elements">{{label}}</sl-tab>',
6
-
7
- // Builds fixed elements tabs
8
- buildTabs: function (label) {
9
- var $wrapper = $(this.WRAPPER),
10
- $tabs = $(this.TABS.replace(/{{label}}/, label))
11
-
12
- $("#main-content-elements").wrap($wrapper)
13
- $("#fixed-elements").prepend($tabs)
14
- },
15
-
16
- // Creates a fixed element tab.
17
- createTab: function (element_id, label) {
18
- var $fixed_elements = $("#fixed-elements")
19
- var panel_name = "fixed-element-" + element_id
20
-
21
- var $tab =
22
- '<sl-tab slot="nav" panel="' + panel_name + '">' + label + "</sl-tab>"
23
- $fixed_elements.append($tab)
24
-
25
- var $panel = $(
26
- '<sl-tab-panel name="' + panel_name + '" style="--padding: 0" />'
27
- )
28
- $fixed_elements.append($panel)
29
- window.requestAnimationFrame(function () {
30
- $fixed_elements.get(0).show(panel_name)
31
- })
32
- },
33
-
34
- removeTab: function (element_id) {
35
- var $fixed_elements = $("#fixed-elements")
36
-
37
- $fixed_elements
38
- .find('sl-tab[panel="fixed-element-' + element_id + '"]')
39
- .remove()
40
- $fixed_elements
41
- .find('sl-tab-panel[name="fixed-element-' + element_id + '"]')
42
- .remove()
43
- $fixed_elements.get(0).show("main-content-elements")
44
- }
45
- }
@@ -1,24 +0,0 @@
1
- window.Alchemy = {} if typeof (window.Alchemy) is "undefined"
2
-
3
- Alchemy.Growler =
4
-
5
- build: (message, flash_type) ->
6
- $flash_container = $("<div class=\"flash #{flash_type}\" />")
7
- $flash_container.append Alchemy.messageIcon(flash_type)
8
- $flash_container.append message
9
- $("#flash_notices").append $flash_container
10
- $("#flash_notices").show()
11
- Alchemy.Growler.fade()
12
-
13
- fade: ->
14
- $(".flash:not(.error)", "#flash_notices").delay(5000).queue(-> Alchemy.Growler.dismiss(this))
15
- $(".flash", "#flash_notices").on("click", (e) => @dismiss(e.currentTarget))
16
- return
17
-
18
- dismiss: (element) ->
19
- $(element).on 'transitionend', => $(element).remove()
20
- $(element).addClass('dismissed')
21
- return
22
-
23
- Alchemy.growl = (message, style = "notice") ->
24
- Alchemy.Growler.build message, style
@@ -1,49 +0,0 @@
1
- window.Alchemy = {} if typeof(window.Alchemy) is 'undefined'
2
-
3
- # Handles Alchemy hotkeys
4
- #
5
- Alchemy.bindedHotkeys = []
6
-
7
- Alchemy.Hotkeys = (scope) ->
8
-
9
- # Unbind all previously registered hotkeys.
10
- unless scope
11
- $(document).off('keypress')
12
- for hotkey in Alchemy.bindedHotkeys
13
- key.unbind(hotkey)
14
-
15
- # Binds keyboard shortcuts to search fields.
16
- $search_fields = $('.search_input_field', scope)
17
- $search_fields_clear = $('.search_field_clear, .js_filter_field_clear', scope)
18
-
19
- key 'alt+f', ->
20
- key.setScope('search')
21
- $search_fields.focus()
22
- false
23
- Alchemy.bindedHotkeys.push('alt+f')
24
-
25
- key 'esc', 'search', ->
26
- $search_fields_clear.click()
27
- $search_fields.blur()
28
- Alchemy.bindedHotkeys.push('esc')
29
-
30
- unless scope
31
- $(document).on 'keypress', (e) ->
32
- if !$(e.target).is('input, textarea') && String.fromCharCode(e.which) == '?'
33
- Alchemy.openDialog '/admin/help',
34
- title: Alchemy.t('help')
35
- size: '400x492'
36
- false
37
- else
38
- true
39
-
40
- # Binds click events to hotkeys.
41
- #
42
- # Simply add a data-alchemy-hotkey attribute to your link.
43
- # If a hotkey is triggered by user, the click event of the element gets triggerd.
44
- #
45
- $('[data-alchemy-hotkey]', scope).each ->
46
- $this = $(this)
47
- hotkey = $this.data('alchemy-hotkey')
48
- key hotkey, -> $this.click()
49
- Alchemy.bindedHotkeys.push(hotkey)
@@ -1,230 +0,0 @@
1
- # Represents the link Dialog that appears, if a user clicks the link buttons
2
- # in TinyMCE or on an Ingredient that has links enabled (e.g. Picture)
3
- #
4
- class window.Alchemy.LinkDialog extends Alchemy.Dialog
5
-
6
- constructor: (@link_object) ->
7
- @url = Alchemy.routes.link_admin_pages_path
8
- @$link_object = $(@link_object)
9
- @options =
10
- size: '600x320'
11
- title: 'Link'
12
- super(@url, @options)
13
-
14
- # Called from Dialog class after the url was loaded
15
- replace: (data) ->
16
- # let Dialog class handle the content replacement
17
- super(data)
18
- # attach events we handle
19
- @attachEvents()
20
- # Store some jQuery objects for further reference
21
- @$internal_link = $('#internal_link', @dialog_body)
22
- @$element_anchor = $('#element_anchor', @dialog_body)
23
- @$anchor_link = $('#anchor_link', @dialog_body)
24
- @$external_link = $('#external_link', @dialog_body)
25
- @$file_link = $('#file_link', @dialog_body)
26
- @$overlay_tabs = $('#overlay_tabs', @dialog_body)
27
- @$page_container = $('#page_selector_container')
28
- @initAnchorLinks()
29
- # if we edit an existing link
30
- if @link_object
31
- # we select the correct tab
32
- @selectTab()
33
- @initPageSelect()
34
- return
35
-
36
- # Attaches click events to forms in the link dialog.
37
- attachEvents: ->
38
- $('[data-link-form-type]', @dialog_body).on "submit", (e) =>
39
- e.preventDefault()
40
- @link_type = e.target.dataset.linkFormType
41
- url = $("##{@link_type}_link").val()
42
- if @link_type == 'internal' && @$element_anchor.val() != ''
43
- url += "##{@$element_anchor.val()}"
44
- # Create the link
45
- @createLink
46
- url: url
47
- title: $("##{@link_type}_link_title").val()
48
- target: $("##{@link_type}_link_target").val()
49
- false
50
-
51
- # Initializes the select2 based Page select
52
- initPageSelect: ->
53
- pageTemplate = HandlebarsTemplates.page
54
- element_anchor_placeholder = @$element_anchor.attr('placeholder')
55
- @$internal_link.select2
56
- placeholder: Alchemy.t('Search page')
57
- allowClear: true
58
- minimumInputLength: 3
59
- ajax:
60
- url: Alchemy.routes.api_pages_path
61
- datatype: 'json'
62
- quietMillis: 300
63
- data: (term, page) ->
64
- q:
65
- name_cont: term
66
- page: page
67
- results: (data) ->
68
- meta = data.meta
69
- results:
70
- data.pages.map (page) ->
71
- id: page.url_path
72
- name: page.name
73
- url_path: page.url_path
74
- page_id: page.id
75
- language: page.language
76
- site: page.site
77
- more: meta.page * meta.per_page < meta.total_count
78
- initSelection: ($element, callback) =>
79
- urlname = $element.val()
80
- $.get Alchemy.routes.api_pages_path,
81
- q:
82
- urlname_eq: urlname.replace(/^\/([a-z]{2}(-[A-Z]{2})?\/)?(.+?)\/?$/, '$3')
83
- page: 1
84
- per_page: 1,
85
- (data) =>
86
- page = data.pages[0]
87
- if page
88
- @initDomIdSelect(page.id)
89
- callback
90
- id: page.url_path
91
- name: page.name
92
- url_path: page.url_path
93
- page_id: page.id
94
- formatSelection: (page) ->
95
- page.name
96
- formatResult: (page) ->
97
- pageTemplate(page: page)
98
- .on 'change', (event) =>
99
- if event.val == ''
100
- @$element_anchor.val(element_anchor_placeholder)
101
- @$element_anchor.select2('destroy').prop('disabled', true)
102
- else
103
- @$element_anchor.val('')
104
- @initDomIdSelect(event.added.page_id)
105
-
106
- # Initializes the select2 based dom id select
107
- # reveals after a page has been selected
108
- initDomIdSelect: (page_id) ->
109
- $.get Alchemy.routes.api_ingredients_path, page_id: page_id, (data) =>
110
- dom_ids = data.ingredients.filter (ingredient) ->
111
- ingredient.data?.dom_id
112
- .map (ingredient) ->
113
- id: ingredient.data.dom_id
114
- text: "##{ingredient.data.dom_id}"
115
- @$element_anchor.prop('disabled', false).removeAttr('placeholder').select2
116
- data: [ id: '', text: Alchemy.t('None') ].concat(dom_ids)
117
-
118
- # Creates a link if no validation errors are present.
119
- # Otherwise shows an error notice.
120
- createLink: (options) ->
121
- if @link_type == 'external'
122
- if @validateURLFormat(options.url)
123
- @setLink(options.url, options.title, options.target)
124
- else
125
- return @showValidationError()
126
- else
127
- @setLink(options.url, options.title, options.target)
128
- @close()
129
-
130
- # Sets the link either in TinyMCE or on an Ingredient.
131
- setLink: (url, title, target) ->
132
- trimmedUrl = url.trim()
133
- if @link_object.editor
134
- @setTinyMCELink(trimmedUrl, title, target)
135
- else
136
- @link_object.setLink(trimmedUrl, title, target, @link_type)
137
- return
138
-
139
- # Sets a link in TinyMCE editor.
140
- setTinyMCELink: (url, title, target) ->
141
- editor = @link_object.editor
142
- editor.execCommand 'mceInsertLink', false,
143
- 'href': url
144
- 'class': @link_type
145
- 'title': title
146
- 'data-link-target': target
147
- 'target': if target == 'blank' then '_blank' else null
148
- editor.selection.collapse()
149
- true
150
-
151
- # Selects the correct tab for link type and fills all fields.
152
- selectTab: ->
153
- # Restoring the bookmarked selection inside the TinyMCE of an Richtext.
154
- if @link_object.node?.nodeName == 'A'
155
- @$link = $(@link_object.node)
156
- @link_object.selection.moveToBookmark(@link_object.bookmark)
157
- # Creating an temporary anchor node if we are linking an Picture Ingredient.
158
- else if @link_object.getAttribute && @link_object.getAttribute("is") == "alchemy-link-button"
159
- @$link = $(@createTempLink())
160
- else
161
- return false
162
- # Populate title and target fields.
163
- $('.link_title', @dialog_body).val @$link.attr('title')
164
- $('.link_target', @dialog_body).select2('val', @$link.attr('data-link-target'))
165
- # Checking of what kind the link is (internal, external or file).
166
- if @$link.hasClass('external')
167
- # Handles an external link.
168
- tab = 'overlay_tab_external_link'
169
- @$external_link.val(@$link.attr('href'))
170
- else if @$link.hasClass('file')
171
- # Handles a file link.
172
- tab = 'overlay_tab_file_link'
173
- @$file_link.select2('val', @$link[0].pathname + @$link[0].search)
174
- else if @$link.attr('href').match(/^#/)
175
- # Handles an anchor link.
176
- tab = 'overlay_tab_anchor_link'
177
- @$anchor_link.select2('val', @$link.attr('href'))
178
- else if @$link.hasClass('internal')
179
- # Handles an internal link.
180
- tab = 'overlay_tab_internal_link'
181
- @initInternalLinkTab()
182
- else
183
- # Emit an event to allow extensions hook into the link overlay.
184
- @$overlay_tabs.trigger 'SelectLinkTab.Alchemy',
185
- link: @$link
186
- if tab
187
- window.requestAnimationFrame =>
188
- @$overlay_tabs.get(0).show(tab)
189
- return
190
-
191
- # Handles actions for internal link tab.
192
- initInternalLinkTab: ->
193
- url = @$link.attr('href').split('#')
194
- # update the url field
195
- @$internal_link.val(url[0])
196
- # store the anchor
197
- @$element_anchor.val(url[1])
198
-
199
- # Creates a temporay 'a' element that holds all values on it.
200
- createTempLink: ->
201
- tmp_link = document.createElement("a")
202
- tmp_link.setAttribute('href', @link_object.linkUrl)
203
- tmp_link.setAttribute('title', @link_object.linkTitle)
204
- tmp_link.setAttribute('data-link-target', @link_object.linkTarget)
205
- tmp_link.setAttribute('target', if @link_object.target == 'blank' then '_blank' else "")
206
- tmp_link.classList.add(@link_object.linkClass) if @link_object.linkClass != ''
207
- tmp_link
208
-
209
- # Validates url for beginning with an protocol.
210
- validateURLFormat: (url) ->
211
- if url.match(Alchemy.link_url_regexp)
212
- true
213
- else
214
- false
215
-
216
- # Shows validation errors
217
- showValidationError: ->
218
- $('#errors ul', @dialog_body).html("<li>#{Alchemy.t('url_validation_failed')}</li>")
219
- $('#errors', @dialog_body).show()
220
-
221
- # Populates the internal anchors select
222
- initAnchorLinks: ->
223
- frame = document.getElementById('alchemy_preview_window')
224
- elements = frame.contentDocument?.querySelectorAll('[id]') || []
225
- if elements.length > 0
226
- for element in elements
227
- @$anchor_link.append("<option value='##{element.id}'>##{element.id}</option>")
228
- else
229
- @$anchor_link.html("<option>#{Alchemy.t('No anchors found')}</option>")
230
- return
@@ -1,49 +0,0 @@
1
- window.Alchemy = {} if typeof(window.Alchemy) is 'undefined'
2
-
3
- # The Alchemy list filter
4
- #
5
- # The list items must have a name attribute.
6
- #
7
- # It hides all list items that don't match the term from filter input field.
8
- #
9
- class Alchemy.ListFilterHandler
10
-
11
- # Pass a input field with a data-alchemy-list-filter attribute to this constructor
12
- constructor: (filter) ->
13
- @filter_field = $(filter)
14
- @items = $(@filter_field.data('alchemy-list-filter'))
15
- @clear = @filter_field.siblings('.js_filter_field_clear')
16
- @_observe()
17
-
18
- _observe: ->
19
- @filter_field.on 'keyup', (e) =>
20
- @clear.show()
21
- @_filter @filter_field.val()
22
- @clear.on "click", (e) =>
23
- e.preventDefault()
24
- @_clear()
25
- @filter_field.on "focus", ->
26
- key.setScope('list_filter')
27
- key 'esc', 'list_filter', =>
28
- @_clear()
29
- @filter_field.blur()
30
-
31
- _filter: (term) ->
32
- @clear.hide() if term == ''
33
- @items.map ->
34
- item = $(this)
35
- # indexOf is much faster then match()
36
- if item.attr('name').toLowerCase().indexOf(term.toLowerCase()) != -1
37
- item.show()
38
- else
39
- item.hide()
40
-
41
- _clear: ->
42
- @filter_field.val ''
43
- @_filter ''
44
-
45
- # Initializes an Alchemy.ListFilterHandler on all input fields with a data-alchemy-list-filter attribute.
46
- #
47
- Alchemy.ListFilter = (scope) ->
48
- $('[data-alchemy-list-filter]', scope).map ->
49
- new Alchemy.ListFilterHandler(this)
@@ -1,82 +0,0 @@
1
- window.Alchemy = {} if typeof(window.Alchemy) is 'undefined'
2
-
3
- Alchemy.PreviewWindow =
4
- MIN_WIDTH: 240
5
- HEIGHT: 75 # Top menu height
6
-
7
- init: (previewUrl) ->
8
- @select = document.querySelector('#preview_url')
9
- @previewUrl = @_getCurrentPreviewUrl() || previewUrl[1]
10
- $iframe = $("<iframe name=\"alchemy_preview_window\" src=\"#{@previewUrl}\" id=\"alchemy_preview_window\" frameborder=\"0\"/>")
11
- $reload = $('#reload_preview_button')
12
- @_showSpinner()
13
- $iframe.on "load", =>
14
- @_hideSpinner()
15
- $('body').append($iframe)
16
- @currentWindow = $iframe
17
- @_bindReloadButton()
18
- if @select
19
- @select.value = @previewUrl
20
- @_bindSelect()
21
- return
22
-
23
- resize: (width) ->
24
- width = @MIN_WIDTH if width < @MIN_WIDTH
25
- @currentWidth = width
26
- @currentWindow.css
27
- width: width
28
-
29
- refresh: (callback) ->
30
- $iframe = $('#alchemy_preview_window')
31
- @_showSpinner()
32
- # We need to be sure that no load event is binded on the preview frame.
33
- $iframe.off('load')
34
- $iframe.on "load", (e) =>
35
- @_hideSpinner()
36
- if callback
37
- callback.call(e, $iframe)
38
- $iframe.attr('src', @previewUrl)
39
- true
40
-
41
- postMessage: (data) ->
42
- frameWindow = @currentWindow[0].contentWindow
43
- frameWindow.postMessage(data, "*")
44
-
45
- _showSpinner: ->
46
- @reload = $('#reload_preview_button')
47
- @spinner = new Alchemy.Spinner('small')
48
- @reload.html @spinner.spin().el
49
-
50
- _hideSpinner: ->
51
- @spinner.stop()
52
- @reload.html('<i class="icon ri-refresh-line ri-fw"></i>')
53
-
54
- _bindReloadButton: ->
55
- $reload = $('#reload_preview_button')
56
- key 'alt+r', =>
57
- @refresh()
58
- $reload.on "click", (e) =>
59
- e.preventDefault()
60
- @refresh()
61
-
62
- _getCurrentPreviewUrl: ->
63
- if @select
64
- option = Array.from(@select.options).find (o) =>
65
- o.text == window.localStorage.getItem("alchemyPreview")
66
- if option
67
- option.value
68
- else
69
- null
70
- else
71
- null
72
-
73
- _bindSelect: ->
74
- $(@select).on "change", (e) =>
75
- @previewUrl = e.target.value
76
- option = e.target.querySelector("option[value='#{@previewUrl}']")
77
- window.localStorage.setItem("alchemyPreview", option.text)
78
- @refresh()
79
- return
80
-
81
- Alchemy.reloadPreview = ->
82
- Alchemy.PreviewWindow.refresh()
@@ -1,11 +0,0 @@
1
- String::beginsWith = (t, i) ->
2
- if i is false
3
- t is @substring(0, t.length)
4
- else
5
- t.toLowerCase() is @substring(0, t.length).toLowerCase()
6
-
7
- String::endsWith = (t, i) ->
8
- if i is false
9
- t is @substring(@length - t.length)
10
- else
11
- t.toLowerCase() is @substring(@length - t.length).toLowerCase()
@@ -1,19 +0,0 @@
1
- <div class="page-select--page">
2
- <div class="page-select--top">
3
- <i class="icon ri-file-3-line"></i>
4
- <span class="page-select--page-name">
5
- {{ page.name }}
6
- </span>
7
- <span class="page-select--page-urlname">
8
- {{ page.url_path }}
9
- </span>
10
- </div>
11
- <div class="page-select--bottom">
12
- <span class="page-select--site-name">
13
- {{ page.site.name }}
14
- </span>
15
- <span class="page-select--language-code">
16
- {{ page.language.name }}
17
- </span>
18
- </div>
19
- </div>
@@ -1,46 +0,0 @@
1
- function createSearchChoice(term, data) {
2
- if (
3
- $(data).filter(function () {
4
- return this.text.localeCompare(term) === 0
5
- }).length === 0
6
- ) {
7
- return {
8
- id: term,
9
- text: term
10
- }
11
- }
12
- }
13
-
14
- function initSelection(element, callback) {
15
- const data = []
16
- $(element.val().split(",")).each(function () {
17
- data.push({
18
- id: this.trim(),
19
- text: this
20
- })
21
- })
22
- callback(data)
23
- }
24
-
25
- export default function TagsAutocomplete(scope) {
26
- const field = $("[data-autocomplete]", scope)
27
- const url = field.data("autocomplete")
28
- field.select2({
29
- tags: true,
30
- tokenSeparators: [","],
31
- minimumInputLength: 1,
32
- openOnEnter: false,
33
- createSearchChoice,
34
- ajax: {
35
- url,
36
- dataType: "json",
37
- data: (term) => {
38
- return { term }
39
- },
40
- results: (data) => {
41
- return { results: data }
42
- }
43
- },
44
- initSelection
45
- })
46
- }
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Represents a node in the admin page tree
4
- #
5
- # Used by page reorder
6
- #
7
- Alchemy::TreeNode = Struct.new(:left, :right, :parent, :depth, :url, :restricted)
@@ -1,8 +0,0 @@
1
- $('#element_<%= @element.id %>').hide(200, function() {
2
- $(this).remove();
3
- Alchemy.growl('<%= j @notice %>');
4
- Alchemy.PreviewWindow.refresh();
5
- <% if @element.fixed? %>
6
- Alchemy.FixedElements.removeTab(<%= @element.id %>);
7
- <% end %>
8
- });
@@ -1,5 +0,0 @@
1
- <%= alchemy_form_for [:admin, @legacy_page_url ||= Alchemy::LegacyPageUrl.new] do |f| %>
2
- <%= hidden_field_tag :page_id, @page.id %>
3
- <%= f.input :urlname %>
4
- <%= f.submit Alchemy.t(:save) %>
5
- <% end %>
@@ -1,9 +0,0 @@
1
- <% if @legacy_page_url.valid? %>
2
- $('.error.message', '#new_legacy_page_url').remove();
3
- $('#legacy_urls_label').text('<%=j render("label", count: @page.legacy_urls.size) %>');
4
- $('#legacy_page_url_urlname').val('');
5
- $('#no_page_links_notice').hide();
6
- $('#legacy_page_urls').append('<%=j render "legacy_page_url", legacy_page_url: @legacy_page_url %>');
7
- <% else %>
8
- $('#new_legacy_page_url').replaceWith('<%=j render("new") %>');
9
- <% end %>
@@ -1,6 +0,0 @@
1
- $('#legacy_urls_label').text('<%=j render("label", count: @page.legacy_urls.size) %>');
2
- $('#<%= dom_id(@legacy_page_url) %>').remove();
3
- Alchemy.pleaseWaitOverlay(false);
4
- <% if @page.legacy_urls.size == 0 %>
5
- $('#no_page_links_notice').show();
6
- <% end %>
@@ -1,2 +0,0 @@
1
- Alchemy.closeCurrentDialog();
2
- $('td.name', '#<%= dom_id(@legacy_page_url) %>').text('<%= @legacy_page_url.urlname %>');
@@ -1,22 +0,0 @@
1
- <form data-link-form-type="anchor">
2
- <%= render_message do %>
3
- <p><%= Alchemy.t(:anchor_link_headline) %></p>
4
- <% end %>
5
- <div class="input select">
6
- <label for="anchor_link" class="control-label">
7
- <%= Alchemy.t(:anchor) %>
8
- </label>
9
- <%= select_tag(:anchor_link,
10
- options_for_select([[Alchemy.t('Please choose'), '']]),
11
- is: 'alchemy-select') %>
12
- </div>
13
- <div class="input text">
14
- <label for="anchor_link_title" class="control-label">
15
- <%= Alchemy.t(:link_title) %>
16
- </label>
17
- <%= text_field_tag "anchor_link_title", '', class: 'link_title' %>
18
- </div>
19
- <div class="submit">
20
- <%= button_tag Alchemy.t(:apply) %>
21
- </div>
22
- </form>
@@ -1,31 +0,0 @@
1
- <form data-link-form-type="external">
2
- <%= render_message do %>
3
- <h3><%= Alchemy.t(:enter_external_link) %></h3>
4
- <p><%= Alchemy.t(:external_link_notice_1) %></p>
5
- <p><%= Alchemy.t(:external_link_notice_2) %></p>
6
- <% end %>
7
- <div id="errors" class="errors">
8
- <ul></ul>
9
- </div>
10
- <div class="input text">
11
- <label for="external_link" class="control-label">URL</label>
12
- <%= text_field_tag "external_link" %>
13
- </div>
14
- <div class="input text">
15
- <label for="external_link_title" class="control-label">
16
- <%= Alchemy.t(:link_title) %>
17
- </label>
18
- <%= text_field_tag "external_link_title", '', class: 'link_title' %>
19
- </div>
20
- <div class="input select">
21
- <label for="external_link_target" class="control-label">
22
- <%= Alchemy.t("Open Link in") %>
23
- </label>
24
- <%= select_tag 'external_link_target',
25
- options_for_select(Alchemy::Page.link_target_options),
26
- class: 'link_target' %>
27
- </div>
28
- <div class="submit">
29
- <%= button_tag Alchemy.t(:apply) %>
30
- </div>
31
- </form>