alchemy_cms 2.1.5 → 2.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.travis.yml +1 -0
  2. data/app/assets/javascripts/alchemy/alchemy.base.js +1 -1
  3. data/app/assets/javascripts/alchemy/alchemy.buttons.js +5 -5
  4. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +1 -1
  5. data/app/assets/javascripts/alchemy/alchemy.element_editor_selector.js +2 -2
  6. data/app/assets/javascripts/alchemy/alchemy.windows.js +1 -1
  7. data/app/assets/stylesheets/alchemy/base.css.scss +22 -13
  8. data/app/assets/stylesheets/alchemy/buttons.css.scss +14 -14
  9. data/app/assets/stylesheets/alchemy/elements.css.scss +79 -46
  10. data/app/controllers/alchemy/admin/elements_controller.rb +1 -1
  11. data/app/helpers/alchemy/admin/base_helper.rb +4 -3
  12. data/app/helpers/alchemy/admin/elements_helper.rb +2 -2
  13. data/app/helpers/alchemy/admin/essences_helper.rb +7 -14
  14. data/app/models/alchemy/element.rb +13 -10
  15. data/app/models/alchemy/message.rb +2 -2
  16. data/app/models/alchemy/page.rb +1 -1
  17. data/app/views/alchemy/admin/clipboard/index.html.erb +4 -3
  18. data/app/views/alchemy/admin/clipboard/insert.js.erb +2 -2
  19. data/app/views/alchemy/admin/contents/create.js.erb +48 -13
  20. data/app/views/alchemy/admin/contents/new.html.erb +1 -1
  21. data/app/views/alchemy/admin/elements/_element.html.erb +1 -1
  22. data/app/views/alchemy/admin/elements/_elements_select.html.erb +1 -1
  23. data/app/views/alchemy/admin/elements/create.js.erb +9 -5
  24. data/app/views/alchemy/admin/elements/fold.js.erb +9 -7
  25. data/app/views/alchemy/admin/elements/trash.js.erb +13 -11
  26. data/app/views/alchemy/admin/elements/update.js.erb +10 -7
  27. data/app/views/alchemy/admin/essence_files/edit.html.erb +1 -1
  28. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +3 -3
  29. data/app/views/alchemy/admin/pages/_contactform_links.html.erb +1 -1
  30. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +1 -1
  31. data/app/views/alchemy/admin/pages/_external_link.html.erb +1 -1
  32. data/app/views/alchemy/admin/pages/_file_link.html.erb +2 -2
  33. data/app/views/alchemy/admin/pages/_internal_link.html.erb +1 -1
  34. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +1 -1
  35. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +1 -1
  36. data/app/views/alchemy/admin/partials/_upload_form.html.erb +2 -2
  37. data/app/views/alchemy/admin/resources/destroy.js.erb +1 -1
  38. data/app/views/alchemy/admin/users/_table.html.erb +3 -3
  39. data/app/views/alchemy/elements/_contactform_editor.html.erb +1 -4
  40. data/app/views/alchemy/essences/_essence_date_editor.html.erb +2 -3
  41. data/app/views/alchemy/essences/_essence_date_view.html.erb +4 -6
  42. data/app/views/alchemy/essences/_essence_text_editor.html.erb +2 -2
  43. data/app/views/layouts/alchemy/admin.html.erb +1 -1
  44. data/config/alchemy/elements.yml +2 -0
  45. data/config/locales/alchemy.de.yml +8 -3
  46. data/config/locales/alchemy.en.yml +4 -3
  47. data/lib/alchemy/capistrano.rb +6 -6
  48. data/lib/alchemy/essence.rb +9 -4
  49. data/lib/alchemy/version.rb +1 -1
  50. data/spec/helpers/admin/elements_helper_spec.rb +1 -1
  51. data/spec/integration/security_spec.rb +2 -2
  52. data/spec/models/page_spec.rb +28 -1
  53. data/spec/support/alchemy/specs_helpers.rb +1 -1
  54. metadata +33 -34
  55. data/app/views/alchemy/admin/elements/destroy.js.erb +0 -6
data/.travis.yml CHANGED
@@ -1,3 +1,4 @@
1
+ language: ruby
1
2
  bundler_args: --without development
2
3
  rvm:
3
4
  - 1.8.7
@@ -133,7 +133,7 @@ if (typeof(Alchemy) === 'undefined') {
133
133
  setElementSaved : function(selector) {
134
134
  var $element = $(selector);
135
135
  Alchemy.setElementClean(selector);
136
- Alchemy.enableButton(selector + ' button.button');
136
+ Alchemy.enableButton('button.button', $element);
137
137
  },
138
138
 
139
139
  resizeFrame : function() {
@@ -7,17 +7,17 @@ if (typeof(Alchemy) === 'undefined') {
7
7
  $.extend(Alchemy, {
8
8
 
9
9
  ButtonObserver: function (selector) {
10
- $(selector).click(function(event) {
10
+ $(selector).not('.no-spinner').click(function(event) {
11
11
  Alchemy.disableButton(this);
12
12
  });
13
13
  },
14
14
 
15
15
  disableButton: function (button) {
16
16
  var $button = $(button), $clone = $button.clone(), width = $button.outerWidth(), text = $button.text();
17
- $button.hide();
17
+ $button.hide().addClass('disabled');
18
18
  $button.parent().append($clone);
19
19
  $clone.attr({disabled: true, href: 'javascript:void(0)'})
20
- .addClass('disabled cloned-button')
20
+ .addClass('cloned-button')
21
21
  .css({width: width})
22
22
  .html('<img src="/assets/alchemy/ajax_loader.gif" style="width: 16px; height: 16px">')
23
23
  .show();
@@ -25,8 +25,8 @@ if (typeof(Alchemy) === 'undefined') {
25
25
  },
26
26
 
27
27
  enableButton: function (button) {
28
- var $button = $(button);
29
- $button.show();
28
+ var $button = $(button).not('.no-spinner');
29
+ $button.show().removeClass('disabled');
30
30
  $button.parent().find('.cloned-button').remove();
31
31
  return true;
32
32
  }
@@ -22,7 +22,7 @@ if (typeof(Alchemy) === 'undefined') {
22
22
  return $(child).attr('data-element-id');
23
23
  });
24
24
  var params_string = '';
25
- var cell_id = $(event.target).attr('data-cell-id');
25
+ var cell_id = $(this).attr('data-cell-id');
26
26
  // Is the trash window open?
27
27
  if ($('#alchemyTrashWindow').length > 0) {
28
28
  // updating the trash icon
@@ -18,8 +18,8 @@ if (typeof(Alchemy) === 'undefined') {
18
18
  reinit : function(elements) {
19
19
  var self = Alchemy.ElementEditorSelector;
20
20
  var $elements = $(elements);
21
- $elements.each(function () {
22
- self.bindEvent(this, $elements);
21
+ $elements.each(function() {
22
+ self.bindEvent(this);
23
23
  });
24
24
  $elements.find('.element_head').click(self.onClickElement);
25
25
  $elements.find('.element_head').dblclick(function() {
@@ -170,7 +170,7 @@ if (typeof(Alchemy) === 'undefined') {
170
170
  Alchemy.AjaxErrorHandler($dialog, XMLHttpRequest.status, textStatus, errorThrown);
171
171
  },
172
172
  complete: function(jqXHR, textStatus) {
173
- Alchemy.enableButton('.button');
173
+ Alchemy.enableButton('.disabled.button');
174
174
  }
175
175
  });
176
176
  },
@@ -1469,19 +1469,28 @@ div#overlay_toolbar a.button:active {
1469
1469
  margin: 1px 0 0 -12px;
1470
1470
  }
1471
1471
 
1472
- #clipboard_items ul {
1473
- list-style-type: none;
1474
- margin: 0 0 16px;
1475
- padding: 0;
1476
- height: 180px;
1477
- overflow-y: auto;
1478
- overflow-x: hidden;
1479
- }
1480
-
1481
- #clipboard_items ul li {
1482
- padding: $default-padding;
1483
- background: white;
1484
- }
1472
+ #clipboard_items {
1473
+
1474
+ ul {
1475
+ list-style-type: none;
1476
+ margin: 0 0 8px;
1477
+ padding: 0;
1478
+ height: 220px;
1479
+ overflow-y: auto;
1480
+ overflow-x: hidden;
1481
+
1482
+ li {
1483
+ padding: 2*$default-padding;
1484
+ border: $default-border;
1485
+ background-color: white;
1486
+ @include rounded-corner;
1487
+ &.element {
1488
+ background-color: #E5DCCA;
1489
+ border: 1px solid #BBA589;
1490
+ }
1491
+ }
1492
+ }
1493
+ }
1485
1494
 
1486
1495
  .mceEditor table {
1487
1496
  border-spacing: 0 !important;
@@ -1,6 +1,6 @@
1
1
  @import "alchemy/defaults";
2
2
 
3
- a.button.small.disabled img {
3
+ a.button.small.cloned-button img {
4
4
  display: inline-block;
5
5
  float: none;
6
6
  position: relative;
@@ -82,15 +82,15 @@ a.button:active {
82
82
  background-color: #e5e5e5;
83
83
  }
84
84
 
85
- a.button.disabled,
86
- a.button.disabled:hover,
87
- a.button.disabled:active,
88
- input.button.disabled,
89
- input.button.disabled:hover,
90
- input.button.disabled:active,
91
- button.button.disabled,
92
- button.button.disabled:hover,
93
- button.button.disabled:active {
85
+ a.button.cloned-button,
86
+ a.button.cloned-button:hover,
87
+ a.button.cloned-button:active,
88
+ input.button.cloned-button,
89
+ input.button.cloned-button:hover,
90
+ input.button.cloned-button:active,
91
+ button.button.cloned-button,
92
+ button.button.cloned-button:hover,
93
+ button.button.cloned-button:active {
94
94
  color: $text-color;
95
95
  text-shadow: none;
96
96
  border-color: #ccc;
@@ -101,15 +101,15 @@ button.button.disabled:active {
101
101
  line-height: 15px;
102
102
  }
103
103
 
104
- a.button.disabled,
105
- a.button.disabled:hover,
106
- a.button.disabled:active {
104
+ a.button.cloned-button,
105
+ a.button.cloned-button:hover,
106
+ a.button.cloned-button:active {
107
107
  padding: 2px 0 !important;
108
108
  line-height: 13px !important;
109
109
  text-align: center;
110
110
  }
111
111
 
112
- a.button.disabled img {
112
+ a.button.cloned-button img {
113
113
  display: inline-block;
114
114
  float: none;
115
115
  }
@@ -437,6 +437,17 @@ a.icon_button.linked {
437
437
  cursor: default;
438
438
  }
439
439
 
440
+ div.content_editor.essence_date {
441
+ float: none;
442
+ display: inline;
443
+ display: inline-block;
444
+ vertical-align: top;
445
+
446
+ input.date {
447
+ width: 154px;
448
+ }
449
+ }
450
+
440
451
  div.essence_picture_editor {
441
452
  float: left;
442
453
  height: 126px;
@@ -613,12 +624,6 @@ table.content_editor_table {
613
624
  width: 210px;
614
625
  }
615
626
 
616
- div.content_editor label.inline {
617
- display: inline-block;
618
- min-width: 90px;
619
- margin-right: 4px;
620
- }
621
-
622
627
  a.new_content_link {
623
628
  float: none;
624
629
  display: inline-block;
@@ -649,10 +654,6 @@ div.content_text_editor.text_short {
649
654
  display: inline;
650
655
  }
651
656
 
652
- div.content_editor input.thin_border {
653
- width: 97%;
654
- }
655
-
656
657
  input.long,
657
658
  input.text_long {
658
659
  width: 363px;
@@ -671,53 +672,85 @@ div.content_text_editor input.text_short {
671
672
  padding: 2px;
672
673
  }
673
674
 
675
+ #alchemy .ui-dialog-content div.content_editor input.auto_resize {
676
+ width: 100%;
677
+ }
678
+
679
+ // div.element_content {
680
+ // white-space: nowrap;
681
+
682
+ // p, div {
683
+ // white-space: normal;
684
+ // }
685
+ // }
686
+
674
687
  div.content_editor {
675
688
  margin-bottom: 8px;
676
689
  margin-top: 8px;
677
690
  position: relative;
678
- }
679
691
 
680
- div.content_editor.missing p {
681
- line-height: 25px;
682
- padding: 2*$default-padding;
683
- border: 1px solid #f5b04e;
684
- background-color: #f5dea9;
685
- @include rounded-corner;
686
- font-size: 11px;
687
- }
692
+ input.thin_border {
693
+ width: 97%;
694
+ }
688
695
 
689
- div.content_editor.missing p span.icon.warning {
690
- position: relative;
691
- top: 2px;
692
- left: 2px;
693
- margin-right: 8px;
694
- }
696
+ &.missing p {
697
+ line-height: 25px;
698
+ padding: 2*$default-padding;
699
+ border: 1px solid #f5b04e;
700
+ background-color: #f5dea9;
701
+ @include rounded-corner;
702
+ font-size: 11px;
703
+
704
+ span.icon.warning {
705
+ position: relative;
706
+ top: 2px;
707
+ left: 2px;
708
+ margin-right: 8px;
709
+ }
710
+ }
695
711
 
696
- #alchemy .ui-dialog-content div.content_editor input.auto_resize {
697
- width: 100%;
698
- }
712
+ &.display_inline {
713
+ display: inline;
714
+ display: inline-block;
715
+ margin-right: 4px;
716
+ vertical-align: top;
717
+
718
+ input.thin_border {
719
+ width: 170px;
720
+ }
721
+ }
699
722
 
700
- div.content_editor.validation_failed label {
701
- color: #931f23;
702
- }
723
+ &.validation_failed {
703
724
 
704
- div.content_editor.validation_failed input {
705
- border: 1px solid #931f23;
706
- background-color: #f9e8e9;
707
- }
725
+ label {
726
+ color: #931f23;
727
+ }
708
728
 
709
- div.content_editor label {
710
- display: block;
711
- margin-bottom: 0.5em;
712
- font-size: 10px;
713
- text-shadow: #fff5e1 1px 1px 0;
714
- line-height: 15px;
715
- text-indent: 1px;
716
- }
729
+ input {
730
+ border: 1px solid #931f23;
731
+ background-color: #f9e8e9;
732
+ }
733
+ }
717
734
 
718
- div.content_editor label span.warning.icon {
719
- position: relative;
720
- top: 2px;
735
+ label {
736
+ display: block;
737
+ margin-bottom: 0.5em;
738
+ font-size: 10px;
739
+ text-shadow: #fff5e1 1px 1px 0;
740
+ line-height: 15px;
741
+ text-indent: 1px;
742
+
743
+ span.warning.icon {
744
+ position: relative;
745
+ top: 2px;
746
+ }
747
+
748
+ &.inline {
749
+ display: inline-block;
750
+ min-width: 90px;
751
+ margin-right: 4px;
752
+ }
753
+ }
721
754
  }
722
755
 
723
756
  .element_editor div.error {
@@ -70,7 +70,7 @@ module Alchemy
70
70
  else
71
71
  @element_validated = false
72
72
  @notice = t('Validation failed')
73
- @error_message = "<h2>#{@notice}</h2><p>#{t('Please check contents below.')}</p>".html_safe
73
+ @error_message = "<h2>#{@notice}</h2><p>#{t(:content_validations_headline)}</p>".html_safe
74
74
  end
75
75
  end
76
76
 
@@ -189,7 +189,8 @@ module Alchemy
189
189
  select_options = options_for_select(select_options, content.essence.content)
190
190
  select_tag(
191
191
  "contents[content_#{content.id}]",
192
- select_options
192
+ select_options,
193
+ :class => 'alchemy_selectbox'
193
194
  )
194
195
  end
195
196
 
@@ -301,13 +302,13 @@ module Alchemy
301
302
  def clipboard_select_tag(items, html_options = {})
302
303
  options = [[t('Please choose'), ""]]
303
304
  items.each do |item|
304
- options << [item.class.to_s == 'Element' ? item.display_name_with_preview_text : item.name, item.id]
305
+ options << [item.class.to_s == 'Alchemy::Element' ? item.display_name_with_preview_text : item.name, item.id]
305
306
  end
306
307
  select_tag(
307
308
  'paste_from_clipboard',
308
309
  !@page.new_record? && @page.can_have_cells? ? grouped_elements_for_select(items, :id) : options_for_select(options),
309
310
  {
310
- :class => html_options[:class],
311
+ :class => [html_options[:class], 'alchemy_selectbox'].join(' '),
311
312
  :style => html_options[:style]
312
313
  }
313
314
  )
@@ -99,14 +99,14 @@ module Alchemy
99
99
  end
100
100
 
101
101
  def element_array_for_options(e, object_method, cell = nil)
102
- if e.class.name == 'Element'
102
+ if e.class.name == 'Alchemy::Element'
103
103
  [
104
104
  e.display_name_with_preview_text,
105
105
  e.send(object_method).to_s + (cell ? "##{cell['name']}" : "")
106
106
  ]
107
107
  else
108
108
  [
109
- t(e['name'], :scope => :element_names),
109
+ Alchemy::I18n.t(e['name'], :scope => :element_names),
110
110
  e[object_method] + (cell ? "##{cell['name']}" : "")
111
111
  ]
112
112
  end
@@ -78,28 +78,21 @@ module Alchemy
78
78
  end
79
79
 
80
80
  # Renders the EssenceText editor partial with a form select for storing page urlnames
81
- # Options:
82
- # * element - element the Content find via content_name to store the pages urlname in.
83
- # * content_name - the name of the content from element to store the pages urlname in.
84
- # * options (Hash)
85
- # ** :only (Hash) - pass page_layout names to :page_layout => [""] so only pages with this page_layout will be displayed inside the select.
86
- # ** :except (Hash) - pass page_layout names to :page_layout => [""] so all pages except these with this page_layout will be displayed inside the select.
87
- # ** :page_attribute (Symbol) - The Page attribute which will be stored.
81
+ #
82
+ # === Options:
83
+ #
84
+ # :only [Hash] # Pagelayout names. Only pages with this page_layout will be displayed inside the select.
85
+ # :page_attribute [Symbol] # The Page attribute which will be stored.
86
+ #
88
87
  def page_selector(element, content_name, options = {}, select_options = {})
89
88
  default_options = {
90
- :except => {
91
- :page_layout => [""]
92
- },
93
- :only => {
94
- :page_layout => [""]
95
- },
96
89
  :page_attribute => :id,
97
90
  :prompt => t('Choose page')
98
91
  }
99
92
  options = default_options.merge(options)
100
93
  pages = Page.where({
101
94
  :language_id => session[:language_id],
102
- :page_layout => options[:only][:page_layout],
95
+ :page_layout => options[:only],
103
96
  :public => true
104
97
  })
105
98
  content = element.content_by_name(content_name)
@@ -372,7 +372,7 @@ module Alchemy
372
372
  end
373
373
 
374
374
  def has_ingredient?(name)
375
- !self.ingredient(name).blank?
375
+ self.ingredient(name).present?
376
376
  end
377
377
 
378
378
  def save_contents(params)
@@ -400,14 +400,17 @@ module Alchemy
400
400
  def essence_errors
401
401
  essence_errors = {}
402
402
  essences.each do |essence|
403
- unless essence.essence_errors.blank?
404
- essence_errors[essence.content.name] = essence.essence_errors
403
+ unless essence.errors.blank?
404
+ essence_errors[essence.content.name] = essence.validation_errors
405
405
  end
406
406
  end
407
407
  essence_errors
408
408
  end
409
409
 
410
- # Essence validation errors messages are translated via ::I18n.
410
+ # Essence validation errors
411
+ #
412
+ # Messages are translated via I18n.
413
+ #
411
414
  # Inside your translation file add translations like:
412
415
  #
413
416
  # alchemy:
@@ -422,12 +425,12 @@ module Alchemy
422
425
  # * taken
423
426
  # * wrong_format
424
427
  #
425
- # Example:
428
+ # === Example:
426
429
  #
427
430
  # de:
428
431
  # alchemy:
429
432
  # content_validations:
430
- # contact:
433
+ # contactform:
431
434
  # email:
432
435
  # wrong_format: 'Die Email hat nicht das richtige Format'
433
436
  #
@@ -435,11 +438,11 @@ module Alchemy
435
438
  messages = []
436
439
  essence_errors.each do |content_name, errors|
437
440
  errors.each do |error|
438
- messages << I18n.t(
439
- "content_validations.#{self.name}.#{content_name}.#{error}",
441
+ messages << I18n.t(error,
442
+ :scope => [:content_validations, self.name, content_name],
440
443
  :default => [
441
- "content_validations.fields.#{content_name}.#{error}".to_sym,
442
- "content_validations.errors.#{error}".to_sym
444
+ "alchemy.content_validations.fields.#{content_name}.#{error}".to_sym,
445
+ "alchemy.content_validations.errors.#{error}".to_sym
443
446
  ],
444
447
  :field => Content.translated_label_for(content_name)
445
448
  )