alchemy_cms 3.0.0.rc5 → 3.0.0.rc6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -5
  3. data/README.md +32 -5
  4. data/alchemy_cms.gemspec +1 -1
  5. data/app/assets/javascripts/alchemy/alchemy.buttons.js.coffee +3 -3
  6. data/app/assets/javascripts/alchemy/alchemy.char_counter.js.coffee +19 -0
  7. data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +5 -0
  8. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +3 -2
  9. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +2 -0
  10. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +1 -1
  11. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +2 -26
  12. data/app/assets/javascripts/alchemy/alchemy.file_progress.js.coffee +1 -1
  13. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +2 -0
  14. data/app/assets/javascripts/alchemy/alchemy.i18n.js.coffee +12 -7
  15. data/app/assets/javascripts/alchemy/alchemy.js +1 -1
  16. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +33 -4
  17. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +1 -1
  18. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +2 -13
  19. data/app/assets/javascripts/alchemy/{alchemy.tinymce.js.coffee.erb → alchemy.tinymce.js.coffee} +1 -25
  20. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +63 -105
  21. data/app/assets/javascripts/alchemy/alchemy.uploader.js.coffee +3 -3
  22. data/app/assets/stylesheets/alchemy/_extends.scss +2 -8
  23. data/app/assets/stylesheets/alchemy/_mixins.scss +4 -9
  24. data/app/assets/stylesheets/alchemy/base.scss +6 -6
  25. data/app/assets/stylesheets/alchemy/buttons.scss +56 -29
  26. data/app/assets/stylesheets/alchemy/elements.scss +66 -14
  27. data/app/assets/stylesheets/alchemy/form_fields.scss +39 -6
  28. data/app/assets/stylesheets/alchemy/forms.scss +32 -0
  29. data/app/assets/stylesheets/alchemy/frame.scss +44 -44
  30. data/app/assets/stylesheets/alchemy/icons.scss +2 -2
  31. data/app/assets/stylesheets/alchemy/jquery-ui.scss +2 -0
  32. data/app/assets/stylesheets/alchemy/notices.scss +6 -0
  33. data/app/assets/stylesheets/alchemy/selects.scss +10 -0
  34. data/app/assets/stylesheets/alchemy/sitemap.scss +8 -10
  35. data/app/assets/stylesheets/alchemy/toolbar.scss +40 -31
  36. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +11 -22
  37. data/app/controllers/alchemy/admin/base_controller.rb +14 -1
  38. data/app/controllers/alchemy/admin/elements_controller.rb +4 -2
  39. data/app/controllers/alchemy/admin/layoutpages_controller.rb +5 -0
  40. data/app/controllers/alchemy/admin/legacy_page_urls_controller.rb +39 -0
  41. data/app/controllers/alchemy/admin/pages_controller.rb +0 -3
  42. data/app/controllers/alchemy/base_controller.rb +7 -4
  43. data/app/helpers/alchemy/admin/base_helper.rb +33 -3
  44. data/app/helpers/alchemy/pages_helper.rb +1 -1
  45. data/app/models/alchemy/element.rb +6 -4
  46. data/app/models/alchemy/legacy_page_url.rb +5 -1
  47. data/app/models/alchemy/message.rb +2 -2
  48. data/app/models/alchemy/page.rb +8 -9
  49. data/app/models/alchemy/page/{cells.rb → page_cells.rb} +1 -1
  50. data/app/models/alchemy/page/{elements.rb → page_elements.rb} +1 -1
  51. data/app/models/alchemy/page/{naming.rb → page_naming.rb} +33 -18
  52. data/app/models/alchemy/page/{natures.rb → page_natures.rb} +1 -1
  53. data/app/models/alchemy/page/{scopes.rb → page_scopes.rb} +1 -1
  54. data/app/models/alchemy/page/{users.rb → page_users.rb} +13 -1
  55. data/app/views/alchemy/admin/elements/_add_element_button.html.erb +18 -0
  56. data/app/views/alchemy/admin/elements/_add_picture.html.erb +1 -1
  57. data/app/views/alchemy/admin/elements/_element.html.erb +12 -11
  58. data/app/views/alchemy/admin/elements/_new_element_form.html.erb +3 -0
  59. data/app/views/alchemy/admin/elements/create.js.erb +3 -3
  60. data/app/views/alchemy/admin/elements/index.html.erb +19 -4
  61. data/app/views/alchemy/admin/elements/new.html.erb +3 -0
  62. data/app/views/alchemy/admin/elements/trash.js.erb +16 -12
  63. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +1 -1
  64. data/app/views/alchemy/admin/layoutpages/edit.html.erb +11 -0
  65. data/app/views/alchemy/admin/layoutpages/index.html.erb +4 -16
  66. data/app/views/alchemy/admin/legacy_page_urls/_form.html.erb +5 -0
  67. data/app/views/alchemy/admin/legacy_page_urls/_label.html.erb +1 -0
  68. data/app/views/alchemy/admin/legacy_page_urls/_legacy_page_url.html.erb +13 -0
  69. data/app/views/alchemy/admin/legacy_page_urls/_new.html.erb +20 -0
  70. data/app/views/alchemy/admin/legacy_page_urls/create.js.erb +10 -0
  71. data/app/views/alchemy/admin/legacy_page_urls/destroy.js.erb +6 -0
  72. data/app/views/alchemy/admin/legacy_page_urls/update.js.erb +2 -0
  73. data/app/views/alchemy/admin/pages/_form.html.erb +58 -0
  74. data/app/views/alchemy/admin/pages/_internal_link.html.erb +9 -4
  75. data/app/views/alchemy/admin/pages/_legacy_urls.html.erb +23 -0
  76. data/app/views/alchemy/admin/pages/_locked_page.html.erb +21 -0
  77. data/app/views/alchemy/admin/pages/_page.html.erb +2 -2
  78. data/app/views/alchemy/admin/pages/_page_status.html.erb +11 -9
  79. data/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb +13 -0
  80. data/app/views/alchemy/admin/pages/configure.html.erb +16 -57
  81. data/app/views/alchemy/admin/pages/edit.html.erb +64 -66
  82. data/app/views/alchemy/admin/pages/index.html.erb +9 -19
  83. data/app/views/alchemy/admin/pages/update.js.erb +1 -1
  84. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +7 -12
  85. data/app/views/alchemy/admin/partials/_routes.html.erb +25 -0
  86. data/app/views/alchemy/admin/partials/_search_form.html.erb +10 -12
  87. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +53 -47
  88. data/app/views/alchemy/admin/pictures/index.html.erb +34 -29
  89. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +1 -1
  90. data/app/views/alchemy/navigation/_link.html.erb +9 -9
  91. data/app/views/alchemy/pages/show.rss.builder +2 -2
  92. data/app/views/layouts/alchemy/admin.html.erb +20 -9
  93. data/bin/alchemy +1 -1
  94. data/config/alchemy/config.yml +1 -0
  95. data/config/locales/alchemy.de.yml +15 -1
  96. data/config/locales/alchemy.en.yml +29 -19
  97. data/config/locales/alchemy.nl.yml +11 -1
  98. data/config/routes.rb +2 -1
  99. data/lib/alchemy/errors.rb +2 -2
  100. data/lib/alchemy/permissions.rb +2 -0
  101. data/lib/alchemy/resource.rb +22 -9
  102. data/lib/alchemy/tinymce.rb +13 -7
  103. data/lib/alchemy/version.rb +1 -1
  104. data/spec/controllers/admin/base_controller_spec.rb +39 -0
  105. data/spec/controllers/admin/elements_controller_spec.rb +17 -14
  106. data/spec/controllers/admin/pages_controller_spec.rb +1 -2
  107. data/spec/controllers/pages_controller_spec.rb +7 -3
  108. data/spec/dummy/app/models/dummy_user.rb +12 -2
  109. data/spec/features/admin/dashboard_spec.rb +45 -0
  110. data/spec/features/admin/legacy_page_url_management_spec.rb +62 -0
  111. data/spec/features/admin/page_editing_feature_spec.rb +66 -6
  112. data/spec/features/page_feature_spec.rb +13 -0
  113. data/spec/helpers/admin/base_helper_spec.rb +36 -0
  114. data/spec/libraries/resource_spec.rb +168 -84
  115. data/spec/libraries/tinymce_spec.rb +10 -0
  116. data/spec/models/element_spec.rb +16 -0
  117. data/spec/models/legacy_page_url_spec.rb +21 -0
  118. data/spec/models/message_spec.rb +23 -7
  119. data/spec/models/page_spec.rb +89 -12
  120. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.min.js +1 -0
  121. data/vendor/assets/javascripts/tinymce/plugins/hr/plugin.min.js +1 -0
  122. metadata +96 -75
  123. data/app/assets/javascripts/alchemy/alchemy.routes.js.erb +0 -38
  124. data/spec/models/resource_spec.rb +0 -159
@@ -131,6 +131,8 @@
131
131
 
132
132
  .ui-widget-content a {
133
133
  color: $text-color;
134
+
135
+ &:hover { color: #000 }
134
136
  }
135
137
 
136
138
  .ui-widget-header {
@@ -27,6 +27,12 @@ div.message, p.message {
27
27
  margin: 16px;
28
28
  }
29
29
 
30
+ a {
31
+ text-decoration: underline;
32
+
33
+ &:hover { color: #000 }
34
+ }
35
+
30
36
  ol {
31
37
  margin: 0 0 8px;
32
38
  padding-left: 20px;
@@ -197,3 +197,13 @@ select {
197
197
  width: 100%;
198
198
  }
199
199
  }
200
+
201
+ .select_with_label {
202
+ margin: 0 3*$default-margin;
203
+ @include inline-block(top);
204
+
205
+ label {
206
+ @include inline-block;
207
+ margin-right: 2*$default-margin;
208
+ }
209
+ }
@@ -1,6 +1,6 @@
1
- div#sort_panel {
1
+ #sort_panel {
2
2
  background: $medium-gray;
3
- padding: 60px 0 8px 0;
3
+ padding: 45px 0 8px 0;
4
4
  position: fixed;
5
5
  top: 27px;
6
6
  left: 0;
@@ -9,12 +9,12 @@ div#sort_panel {
9
9
  border-bottom: $default-border;
10
10
 
11
11
  div.info {
12
- margin: 8px 8px 8px 104px;
12
+ margin: 8px 8px 8px 78px;
13
13
  width: 720px;
14
14
  }
15
15
 
16
- div.buttons {
17
- margin-left: 104px;
16
+ .buttons {
17
+ margin-left: 78px;
18
18
  }
19
19
  }
20
20
 
@@ -216,15 +216,13 @@ div.page_infos {
216
216
  width: 68px;
217
217
  }
218
218
 
219
- #sub_navigation span.page_status_and_name span.page_status {
219
+ #sub_navigation .page_status_and_name .page_status {
220
220
  float: none;
221
- @include inline-block;
222
- position: relative;
223
- top: -4px;
221
+ @include inline-block(-1px);
224
222
  margin-left: 2px;
225
223
  }
226
224
 
227
- span.page_status {
225
+ .page_status {
228
226
  width: 16px;
229
227
  height: 16px;
230
228
  background-image: image-url('alchemy/icons.png');
@@ -1,7 +1,5 @@
1
1
  #toolbar {
2
2
  z-index: 10;
3
- padding: 4px;
4
- height: 44px;
5
3
  @extend %gradiated-toolbar;
6
4
  margin-right: 0px;
7
5
  border: $default-border;
@@ -9,7 +7,7 @@
9
7
  border-right-style: none;
10
8
  position: relative;
11
9
 
12
- div.button_with_label {
10
+ .button_with_label {
13
11
 
14
12
  form {
15
13
  margin: 0;
@@ -17,10 +15,6 @@
17
15
  line-height: 5px;
18
16
 
19
17
  select { margin: 0 }
20
-
21
- label {
22
- margin-top: $default-margin;
23
- }
24
18
  }
25
19
  }
26
20
 
@@ -30,7 +24,7 @@
30
24
  padding: 0;
31
25
  }
32
26
 
33
- a.button {
27
+ .button {
34
28
  width: 25px;
35
29
  height: 21px;
36
30
  text-decoration: none;
@@ -62,7 +56,7 @@
62
56
 
63
57
  .search_form {
64
58
  position: absolute;
65
- top: 4px;
59
+ top: 8px;
66
60
  right: 8px;
67
61
  text-align: center;
68
62
  }
@@ -74,30 +68,38 @@
74
68
 
75
69
  &.right {
76
70
  position: absolute;
77
- right: 8px;
78
- top: 4px;
71
+ right: 0;
72
+ top: 8px;
79
73
  margin-left: 8px;
74
+
75
+ label {
76
+ left: auto;
77
+ right: 50%;
78
+ margin-left: 0;
79
+ margin-right: -12px;
80
+
81
+ &:before {
82
+ left: auto;
83
+ right: 8px;
84
+ }
85
+ }
80
86
  }
81
87
  }
82
88
 
83
89
  .toolbar_spacer {
84
90
  @include inline-block;
85
- width: 1px;
86
- height: 37px;
87
- border-right-style: dotted;
91
+ width: 0px;
92
+ height: 29px;
93
+ border-right-style: solid;
88
94
  border-right-width: 1px;
89
- margin-right: 2*$default-margin;
95
+ border-right-color: $default-border-color;
96
+ margin-right: $default-margin;
90
97
  margin-left: $default-margin;
91
98
  }
92
99
 
93
100
  #overlay_toolbar {
94
101
  @extend %gradiated-toolbar;
95
- border: $default-border;
96
- height: 44px;
97
- border-top-style: none;
98
- border-left-style: none;
99
- border-right-style: none;
100
- padding: $default-padding;
102
+ border-bottom: $default-border;
101
103
 
102
104
  a.button {
103
105
  width: 25px;
@@ -117,21 +119,28 @@
117
119
  }
118
120
  }
119
121
 
120
- .search_field {
121
- width: 225px;
122
- }
123
-
124
122
  form {
125
123
  float: right;
126
- width: 310px;
127
- margin-top: 2px;
128
- padding: 0 !important;
129
- margin-right: 0;
124
+ margin-right: 8px;
130
125
 
131
126
  .button {
132
127
  position: absolute;
133
- top: 2px;
134
- right: 4px;
128
+ top: 4px;
129
+ right: 8px;
135
130
  }
136
131
  }
137
132
  }
133
+
134
+ .button_group {
135
+ position: relative;
136
+ @include inline-block(top);
137
+ margin: 0 2*$default-margin;
138
+
139
+ &:hover label {
140
+ display: block;
141
+ }
142
+
143
+ .button_with_label {
144
+ margin: 0;
145
+ }
146
+ }
@@ -21,7 +21,7 @@
21
21
  line-height: normal;
22
22
  font-weight: normal;
23
23
  text-align: left;
24
- @include box-sizing(content-box);
24
+ @include box-sizing(border-box);
25
25
  direction: ltr;
26
26
  }
27
27
 
@@ -483,29 +483,18 @@ body .mce-abs-layout-item,.mce-abs-end {
483
483
  .mce-tooltip {
484
484
  position: absolute;
485
485
  padding: 5px;
486
- opacity: .8;
487
- filter: alpha(opacity=80);
488
- zoom: 1;
489
486
  }
490
487
 
491
488
  .mce-tooltip-inner {
492
489
  font-size: 11px;
493
- background-color: #000;
490
+ background-color: $dark-gray;
494
491
  color: #fff;
495
492
  max-width: 200px;
496
- padding: 5px 8px 4px 8px;
493
+ padding: $default-padding 2*$default-padding;
497
494
  text-align: center;
498
495
  white-space: normal;
499
- }
500
-
501
- .mce-tooltip-inner {
502
- @include border-radius($default-border-radius);
503
- }
504
-
505
- .mce-tooltip-inner {
506
- -webkit-box-shadow: 0 0 5px #000;
507
- -moz-box-shadow: 0 0 5px #000;
508
- box-shadow: 0 0 5px #000;
496
+ @include box-shadow(0 0 4px $default-border-color);
497
+ @extend %rounded-border;
509
498
  }
510
499
 
511
500
  .mce-tooltip-arrow {
@@ -513,23 +502,23 @@ body .mce-abs-layout-item,.mce-abs-end {
513
502
  width: 0;
514
503
  height: 0;
515
504
  line-height: 0;
516
- border: 5px dashed #000;
505
+ border: 5px solid $dark-gray;
517
506
  }
518
507
 
519
508
  .mce-tooltip-arrow-n {
520
- border-bottom-color: #000;
509
+ border-bottom-color: $dark-gray;
521
510
  }
522
511
 
523
512
  .mce-tooltip-arrow-s {
524
- border-top-color: #000;
513
+ border-top-color: $dark-gray;
525
514
  }
526
515
 
527
516
  .mce-tooltip-arrow-e {
528
- border-left-color: #000;
517
+ border-left-color: $dark-gray;
529
518
  }
530
519
 
531
520
  .mce-tooltip-arrow-w {
532
- border-right-color: #000;
521
+ border-right-color: $dark-gray;
533
522
  }
534
523
 
535
524
  .mce-tooltip-nw,.mce-tooltip-sw {
@@ -990,7 +979,7 @@ i.mce-i-checkbox {
990
979
  display: inline-block;
991
980
  *display: inline;
992
981
  *zoom: 1;
993
- padding: 8px;
982
+ padding: 4px 6px;
994
983
  white-space: normal;
995
984
  }
996
985
 
@@ -13,7 +13,7 @@ module Alchemy
13
13
  rescue_from Exception do |exception|
14
14
  if exception.is_a? CanCan::AccessDenied
15
15
  permission_denied(exception)
16
- elsif Rails.env.test?
16
+ elsif raise_exception?
17
17
  raise
18
18
  else
19
19
  exception_handler(exception)
@@ -151,6 +151,19 @@ module Alchemy
151
151
  end.symbolize_keys
152
152
  end
153
153
 
154
+ # This method decides if we want to raise an exception or not.
155
+ #
156
+ # I.e. in test environment.
157
+ #
158
+ def raise_exception?
159
+ Rails.env.test? || is_page_preview?
160
+ end
161
+
162
+ # Are we currently in the page edit mode page preview.
163
+ def is_page_preview?
164
+ controller_path == 'alchemy/admin/pages' && action_name == 'show'
165
+ end
166
+
154
167
  end
155
168
  end
156
169
  end
@@ -44,8 +44,10 @@ module Alchemy
44
44
  end
45
45
  @element.save
46
46
  end
47
- if @page.definition['insert_elements_at'] == 'top'
48
- @insert_at_top = true
47
+ if params[:insert_after].present?
48
+ @element_before = Element.find(params[:insert_after])
49
+ @element.insert_at(@element_before.position + 1)
50
+ else
49
51
  @element.move_to_top
50
52
  end
51
53
  end
@@ -9,6 +9,11 @@ module Alchemy
9
9
  @languages = Language.all
10
10
  end
11
11
 
12
+ def edit
13
+ @page = Page.find(params[:id])
14
+ @page_layouts = PageLayout.layouts_with_own_for_select(@page.page_layout, Language.current.id, true)
15
+ end
16
+
12
17
  end
13
18
  end
14
19
  end
@@ -0,0 +1,39 @@
1
+ module Alchemy
2
+ class Admin::LegacyPageUrlsController < Alchemy::Admin::ResourcesController
3
+ before_action :load_page
4
+
5
+ def new
6
+ @legacy_page_url = @page.legacy_urls.build
7
+ end
8
+
9
+ def create
10
+ @legacy_page_url = @page.legacy_urls.build(legacy_page_url_params)
11
+ @legacy_page_url.save
12
+ end
13
+
14
+ def update
15
+ @legacy_page_url = LegacyPageUrl.find(params[:id])
16
+ if @legacy_page_url.update(legacy_page_url_params)
17
+ render :update
18
+ else
19
+ render :edit
20
+ end
21
+ end
22
+
23
+ def destroy
24
+ @legacy_page_url = LegacyPageUrl.find(params[:id])
25
+ @legacy_page_url.destroy
26
+ end
27
+
28
+ private
29
+
30
+ def load_page
31
+ @page = Page.find(params[:page_id])
32
+ end
33
+
34
+ def legacy_page_url_params
35
+ params.require(:legacy_page_url).permit(:urlname)
36
+ end
37
+
38
+ end
39
+ end
@@ -31,9 +31,6 @@ module Alchemy
31
31
  # Setting the locale to pages language, so the page content has it's correct translations.
32
32
  ::I18n.locale = @page.language_code
33
33
  render layout: 'application'
34
- rescue Exception => e
35
- exception_logger(e)
36
- render file: Rails.root.join('public', '500.html'), status: 500, layout: false
37
34
  end
38
35
 
39
36
  def info
@@ -120,10 +120,13 @@ WARN
120
120
  def redirect_or_render_notice
121
121
  if request.xhr?
122
122
  respond_to do |format|
123
- format.js { render status: 403 }
124
- format.html {
125
- render(partial: 'alchemy/admin/partials/flash', locals: {message: _t('You are not authorized'), flash_type: 'warning'})
126
- }
123
+ format.js do
124
+ render text: flash.discard(:warning), status: 403
125
+ end
126
+ format.html do
127
+ render partial: 'alchemy/admin/partials/flash',
128
+ locals: {message: flash[:warning], flash_type: 'warning'}
129
+ end
127
130
  end
128
131
  else
129
132
  redirect_to(alchemy.admin_dashboard_path)
@@ -14,6 +14,18 @@ module Alchemy
14
14
  include Alchemy::BaseHelper
15
15
  include Alchemy::Admin::NavigationHelper
16
16
 
17
+ # Returns a string showing the name of the currently logged in user.
18
+ #
19
+ # In order to represent your own +User+'s class instance,
20
+ # you should add a +alchemy_display_name+ method to your +User+ class
21
+ #
22
+ def current_alchemy_user_name
23
+ name = current_alchemy_user.try(:alchemy_display_name)
24
+ if name.present?
25
+ content_tag :span, "#{_t('Logged in as')} #{name}", class: 'current-user-name'
26
+ end
27
+ end
28
+
17
29
  # This helper renders the link to an dialog.
18
30
  #
19
31
  # We use this for our fancy modal dialogs in the Alchemy cockpit.
@@ -22,6 +34,12 @@ module Alchemy
22
34
  #
23
35
  # <%= link_to_dialog('Edit', edit_product_path, {size: '200x300'}, {class: 'icon_button'}) %>
24
36
  #
37
+ # Or you call it with a block
38
+ #
39
+ # <%= link_to_dialog edit_product_path, {size: 200x300} do %>
40
+ # <%= render_icon(:edit) %>
41
+ # <% end %>
42
+ #
25
43
  # @param [String] content
26
44
  # The string inside the link tag
27
45
  # @param [String or Hash] url
@@ -38,11 +56,18 @@ module Alchemy
38
56
  # @option options [Boolean] :modal (true)
39
57
  # Show as modal window.
40
58
  #
41
- def link_to_dialog(content, url, options={}, html_options={})
59
+ def link_to_dialog(content = nil, url = nil, options = {}, html_options = {}, &block)
60
+ html_options, options, url, content = options, url, content, block if block_given?
42
61
  default_options = {modal: true}
43
62
  options = default_options.merge(options)
44
- link_to content, url,
45
- html_options.merge('data-alchemy-dialog' => options.to_json)
63
+ if block_given?
64
+ link_to url,
65
+ html_options.merge('data-alchemy-dialog' => options.to_json),
66
+ &block
67
+ else
68
+ link_to content, url,
69
+ html_options.merge('data-alchemy-dialog' => options.to_json)
70
+ end
46
71
  end
47
72
 
48
73
  # Used for translations selector in Alchemy cockpit user settings.
@@ -424,6 +449,11 @@ module Alchemy
424
449
  end
425
450
  end
426
451
 
452
+ # Returns the regular expression used for external url validation in link dialog.
453
+ def link_url_regexp
454
+ Alchemy::Config.get(:format_matchers)['link_url'] || /^(mailto:|\/|[a-z]+:\/\/)/
455
+ end
456
+
427
457
  private
428
458
 
429
459
  def permission_from_options(options)