alchemy_cms 2.2.rc11 → 2.2.rc13

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 (46) hide show
  1. data/.travis.yml +4 -1
  2. data/Gemfile +10 -9
  3. data/alchemy_cms.gemspec +8 -4
  4. data/app/assets/javascripts/alchemy/alchemy.base.js +0 -13
  5. data/app/assets/javascripts/alchemy/alchemy.dirty.js +6 -4
  6. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +2 -2
  7. data/app/assets/stylesheets/alchemy/elements.css.scss +4 -7
  8. data/app/controllers/alchemy/admin/base_controller.rb +2 -3
  9. data/app/controllers/alchemy/admin/clipboard_controller.rb +10 -10
  10. data/app/controllers/alchemy/admin/elements_controller.rb +4 -4
  11. data/app/controllers/alchemy/admin/pages_controller.rb +1 -2
  12. data/app/controllers/alchemy/base_controller.rb +1 -15
  13. data/app/helpers/alchemy/admin/base_helper.rb +15 -21
  14. data/app/helpers/alchemy/admin/contents_helper.rb +0 -6
  15. data/app/helpers/alchemy/admin/pages_helper.rb +62 -27
  16. data/app/helpers/alchemy/base_helper.rb +0 -9
  17. data/app/models/alchemy/clipboard.rb +74 -0
  18. data/app/models/alchemy/clipboard_spec.rb +0 -0
  19. data/app/models/alchemy/content.rb +19 -0
  20. data/app/views/alchemy/admin/contents/create.js.erb +1 -1
  21. data/app/views/alchemy/admin/elements/_element.html.erb +0 -1
  22. data/app/views/alchemy/admin/elements/_element_head.html.erb +4 -5
  23. data/app/views/alchemy/admin/essence_pictures/assign.js.erb +3 -2
  24. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +3 -4
  25. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +1 -1
  26. data/app/views/alchemy/admin/essence_pictures/update.js.erb +1 -10
  27. data/app/views/alchemy/admin/pages/_contactform_links.html.erb +3 -9
  28. data/app/views/alchemy/admin/pages/_external_link.html.erb +3 -12
  29. data/app/views/alchemy/admin/pages/_file_link.html.erb +3 -12
  30. data/app/views/alchemy/admin/pages/_internal_link.html.erb +5 -14
  31. data/app/views/alchemy/admin/pages/_page_for_links.html.erb +3 -9
  32. data/app/views/alchemy/admin/pages/edit.html.erb +16 -17
  33. data/app/views/alchemy/admin/pictures/_picture.html.erb +3 -2
  34. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +1 -0
  35. data/app/views/alchemy/essences/_essence_picture_tools.html.erb +6 -8
  36. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +9 -3
  37. data/lib/alchemy/authentication_helpers.rb +26 -0
  38. data/lib/alchemy/engine.rb +4 -0
  39. data/lib/alchemy/tinymce.rb +4 -0
  40. data/lib/alchemy/version.rb +1 -1
  41. data/spec/controllers/admin/clipboard_controller_spec.rb +35 -35
  42. data/spec/controllers/admin/elements_controller_spec.rb +71 -14
  43. data/spec/controllers/admin/pages_controller_spec.rb +79 -33
  44. data/spec/models/clipboard_spec.rb +100 -0
  45. data/spec/models/element_spec.rb +17 -7
  46. metadata +77 -8
data/.travis.yml CHANGED
@@ -1,10 +1,10 @@
1
1
  language: ruby
2
- bundler_args: --without development
3
2
  rvm:
4
3
  - 1.8.7
5
4
  - 1.9.2
6
5
  - 1.9.3
7
6
  - ree
7
+ - rbx-19mode
8
8
  branches:
9
9
  only:
10
10
  - master
@@ -15,3 +15,6 @@ env:
15
15
  - DB=mysql
16
16
  - DB=postgresql
17
17
  - DB=sqlite
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: rbx-19mode
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
@@ -10,12 +10,10 @@ group :test do
10
10
  gem 'sqlite3' if ENV['DB'].nil? || ENV['DB'] == 'sqlite'
11
11
  gem 'mysql2' if ENV['DB'] == 'mysql'
12
12
  gem 'pg' if ENV['DB'] == 'postgresql'
13
- gem 'rspec-rails'
14
- gem 'factory_girl_rails', '~> 1.7.0'
15
- gem "capybara"
16
- gem 'capybara-webkit' unless ENV['CI']
17
- gem "launchy" unless ENV['CI']
18
- gem "database_cleaner"
13
+ unless ENV['CI']
14
+ gem 'capybara-webkit'
15
+ gem 'launchy'
16
+ end
19
17
  end
20
18
 
21
19
  group :assets do
@@ -25,6 +23,9 @@ group :assets do
25
23
  end
26
24
 
27
25
  group :development do
28
- gem 'guard-spork'
29
- gem 'yard'
26
+ unless ENV['CI']
27
+ gem 'guard-spork'
28
+ gem 'ruby-debug19', :require => 'ruby-debug', :platform => :ruby_19
29
+ gem 'ruby-debug', :platform => :ruby_18
30
+ end
30
31
  end
data/alchemy_cms.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
21
  s.require_paths = ["lib"]
22
22
 
23
- s.add_runtime_dependency(%q<rails>, ["~> 3.2.1"])
23
+ s.add_runtime_dependency(%q<rails>, ["~> 3.2.5"])
24
24
  s.add_runtime_dependency(%q<authlogic>)
25
25
  s.add_runtime_dependency(%q<awesome_nested_set>, ["~> 2.0"])
26
26
  s.add_runtime_dependency(%q<declarative_authorization>, ["~> 0.5.4"])
@@ -32,10 +32,14 @@ Gem::Specification.new do |s|
32
32
  s.add_runtime_dependency(%q<dynamic_form>, ["~> 1.1"])
33
33
  s.add_runtime_dependency(%q<jquery-rails>, ["~> 2.0.0"])
34
34
  s.add_runtime_dependency(%q<attachment_magic>, ["~> 0.2.1"])
35
- s.add_runtime_dependency('sass-rails', ['~> 3.2.3'])
35
+ s.add_runtime_dependency(%q<sass-rails>, ['~> 3.2.3'])
36
36
 
37
- s.add_development_dependency(%q<rspec-rails>, ["~> 2.8"])
37
+ s.add_development_dependency(%q<bumpy>)
38
+ s.add_development_dependency(%q<capybara>)
39
+ s.add_development_dependency(%q<database_cleaner>)
40
+ s.add_development_dependency(%q<factory_girl_rails>, ['~> 1.7.0'])
41
+ s.add_development_dependency(%q<rspec-rails>)
38
42
  s.add_development_dependency(%q<sqlite3>)
39
- s.add_development_dependency(%q<jasminerice>)
43
+ s.add_development_dependency(%q<yard>)
40
44
 
41
45
  end
@@ -118,19 +118,6 @@ if (typeof(Alchemy) === 'undefined') {
118
118
  }
119
119
  },
120
120
 
121
- saveElement:function (form) {
122
- // disabled for now. I think we don't need this.
123
- return true;
124
- // remove this comment if you have problems with saving tinymce content
125
- // var $rtf_contents = $(form).find('div.content_rtf_editor');
126
- // if ($rtf_contents.size() > 0) {
127
- // $rtf_contents.each(function() {
128
- // var id = $(this).children('textarea.tinymce').attr('id');
129
- // tinymce.get(id).save();
130
- // });
131
- // }
132
- },
133
-
134
121
  setElementSaved:function (selector) {
135
122
  var $element = $(selector);
136
123
  Alchemy.setElementClean(selector);
@@ -8,12 +8,14 @@ if (typeof(Alchemy) === 'undefined') {
8
8
 
9
9
  ElementDirtyObserver:function (selector) {
10
10
  var $elements = $(selector);
11
- $elements.find('textarea.tinymce').map(function () {
11
+ $elements.find('textarea.default_tinymce').map(function () {
12
12
  var $this = $(this);
13
13
  var ed = tinymce.get(this.id);
14
- ed.onChange.add(function (ed, l) {
15
- Alchemy.setElementDirty($this.parents('.element_editor'));
16
- });
14
+ if (ed) {
15
+ ed.onChange.add(function (ed, l) {
16
+ Alchemy.setElementDirty($this.parents('.element_editor'));
17
+ });
18
+ }
17
19
  });
18
20
  $elements.find('input[type="text"]').bind('change', function () {
19
21
  $(this).addClass('dirty');
@@ -47,13 +47,13 @@ if (typeof(Alchemy) === 'undefined') {
47
47
  });
48
48
  },
49
49
  start:function (event, ui) {
50
- var $textareas = ui.item.find('textarea.tinymce');
50
+ var $textareas = ui.item.find('textarea.default_tinymce, textarea.custom_tinymce');
51
51
  $textareas.each(function () {
52
52
  tinymce.get(this.id).remove();
53
53
  });
54
54
  },
55
55
  stop:function (event, ui) {
56
- var $textareas = ui.item.find('textarea.tinymce');
56
+ var $textareas = ui.item.find('textarea.default_tinymce, textarea.custom_tinymce');
57
57
  $textareas.each(function () {
58
58
  Alchemy.Tinymce.addEditor(this.id);
59
59
  });
@@ -634,8 +634,8 @@ div.essence_richtext_loader {
634
634
  z-index: 1;
635
635
  width: 100%;
636
636
  height: 100%;
637
- background-color: #ededed;
638
- @include opacity(75);
637
+ background-color: #F0E8D7;
638
+ @include opacity(90);
639
639
  }
640
640
 
641
641
  div.essence_richtext_loader img {
@@ -828,11 +828,8 @@ textarea {
828
828
  height: 140px;
829
829
  }
830
830
 
831
- textarea.tinymce {
832
- padding: 0;
833
- margin: 0;
834
- width: 100%;
835
- border: 0 none;
831
+ textarea.default_tinymce, textarea.custom_tinymce {
832
+ visibility: hidden;
836
833
  }
837
834
 
838
835
  .text_short_float_left {
@@ -59,9 +59,8 @@ module Alchemy
59
59
  end
60
60
  end
61
61
 
62
- def get_clipboard(category = nil)
63
- clipboard = (session[:clipboard] ||= {})
64
- clipboard[category.to_s.pluralize] ||= [] if category
62
+ def get_clipboard
63
+ session[:clipboard] ||= Clipboard.new
65
64
  end
66
65
 
67
66
  def clipboard_empty?(category = nil)
@@ -4,18 +4,18 @@ module Alchemy
4
4
  class ClipboardController < Alchemy::Admin::BaseController
5
5
 
6
6
  def index
7
- clipboard = get_clipboard(params[:remarkable_type])
8
- @clipboard_items = model_class.all_from_clipboard(clipboard)
7
+ @clipboard = get_clipboard
8
+ @clipboard_items = model_class.all_from_clipboard(@clipboard.all(params[:remarkable_type]))
9
9
  respond_to do |format|
10
10
  format.html { render :layout => false }
11
11
  end
12
12
  end
13
13
 
14
14
  def insert
15
- @clipboard = get_clipboard(params[:remarkable_type])
15
+ @clipboard = get_clipboard
16
16
  @item = model_class.find(params[:remarkable_id])
17
- unless @clipboard.collect { |i| i[:id].to_s }.include?(params[:remarkable_id])
18
- @clipboard.push({:id => params[:remarkable_id], :action => params[:remove] ? 'cut' : 'copy'})
17
+ unless @clipboard.contains? params[:remarkable_type], params[:remarkable_id]
18
+ @clipboard.push params[:remarkable_type], {:id => params[:remarkable_id], :action => params[:remove] ? 'cut' : 'copy'}
19
19
  end
20
20
  respond_to do |format|
21
21
  format.js
@@ -23,19 +23,19 @@ module Alchemy
23
23
  end
24
24
 
25
25
  def remove
26
- @clipboard = get_clipboard(params[:remarkable_type])
26
+ @clipboard = get_clipboard
27
27
  @item = model_class.find(params[:remarkable_id])
28
- @clipboard.delete_if { |i| i[:id].to_s == params[:remarkable_id] }
28
+ @clipboard.remove params[:remarkable_type], params[:remarkable_id]
29
29
  respond_to do |format|
30
30
  format.js
31
31
  end
32
32
  end
33
33
 
34
34
  def clear
35
- session[:clipboard] = {}
35
+ session[:clipboard].clear(params[:remarkable_type])
36
36
  end
37
37
 
38
- private
38
+ private
39
39
 
40
40
  def model_class
41
41
  "alchemy/#{params[:remarkable_type]}".classify.constantize
@@ -43,4 +43,4 @@ module Alchemy
43
43
 
44
44
  end
45
45
  end
46
- end
46
+ end
@@ -27,7 +27,7 @@ module Alchemy
27
27
  @page = Page.find_by_id(params[:page_id])
28
28
  @element = @page.elements.build
29
29
  @elements = Element.all_for_page(@page)
30
- clipboard_elements = get_clipboard('elements')
30
+ clipboard_elements = get_clipboard[:elements]
31
31
  unless clipboard_elements.blank?
32
32
  @clipboard_items = Element.all_from_clipboard_for_page(clipboard_elements, @page)
33
33
  end
@@ -43,7 +43,7 @@ module Alchemy
43
43
  @element = Element.copy(source_element, {:page_id => @page.id})
44
44
  if element_from_clipboard[:action] == 'cut'
45
45
  @cutted_element_id = source_element.id
46
- @clipboard.delete_if { |i| i[:id].to_i == source_element.id }
46
+ @clipboard.remove :elements, source_element.id
47
47
  source_element.destroy
48
48
  end
49
49
  else
@@ -115,8 +115,8 @@ module Alchemy
115
115
  end
116
116
 
117
117
  def element_from_clipboard
118
- @clipboard = get_clipboard(:elements)
119
- @clipboard.detect { |i| i[:id].to_i == params[:paste_from_clipboard].to_i }
118
+ @clipboard = get_clipboard
119
+ @clipboard.get(:elements, params[:paste_from_clipboard])
120
120
  end
121
121
 
122
122
  end
@@ -39,7 +39,7 @@ module Alchemy
39
39
  def new
40
40
  @page = Page.new(:layoutpage => params[:layoutpage] == 'true', :parent_id => params[:parent_id])
41
41
  @page_layouts = PageLayout.get_layouts_for_select(session[:language_id], @page.layoutpage?)
42
- @clipboard_items = Page.all_from_clipboard_for_select(get_clipboard('pages'), session[:language_id], @page.layoutpage?)
42
+ @clipboard_items = Page.all_from_clipboard_for_select(get_clipboard[:pages], session[:language_id], @page.layoutpage?)
43
43
  render :layout => false
44
44
  end
45
45
 
@@ -104,7 +104,6 @@ module Alchemy
104
104
  session[:language_id] = @page.language_id
105
105
  if @page.destroy
106
106
  @page_root = Page.language_root_for(session[:language_id])
107
- get_clipboard('pages').delete(@page.id)
108
107
  @message = t("Page deleted", :name => name)
109
108
  flash[:notice] = @message
110
109
  respond_to do |format|
@@ -9,7 +9,7 @@ module Alchemy
9
9
  before_filter :set_language
10
10
  before_filter :mailer_set_url_options
11
11
 
12
- helper_method :current_server, :current_user, :t
12
+ helper_method :current_server, :t
13
13
 
14
14
  # Returns a host string with the domain the app is running on.
15
15
  def current_server
@@ -30,20 +30,6 @@ module Alchemy
30
30
  Language.published.count > 1
31
31
  end
32
32
 
33
- def current_user
34
- return @current_user if defined?(@current_user)
35
- @current_user = current_user_session && current_user_session.record
36
- end
37
-
38
- def current_user_session
39
- return @current_user_session if defined?(@current_user_session)
40
- @current_user_session = UserSession.find
41
- end
42
-
43
- def logged_in?
44
- !current_user.blank?
45
- end
46
-
47
33
  def raise_not_found_error
48
34
  raise ActionController::RoutingError.new('Not Found')
49
35
  end
@@ -31,18 +31,13 @@ module Alchemy
31
31
  :resizable => false
32
32
  }
33
33
  options = default_options.merge(options)
34
- link_to_function(
34
+ size = options[:size].to_s.split('x')
35
+ size_x = options[:size] ? size[0] : 'auto'
36
+ size_y = options[:size] ? size[1] : 'auto'
37
+ link_to(
35
38
  content,
36
- "Alchemy.openWindow(
37
- \'#{url}\',
38
- \'#{options[:title]}\',
39
- \'#{options[:size] ? options[:size].split('x')[0].to_s : 'auto'}\',
40
- \'#{options[:size] ? options[:size].split('x')[1].to_s : 'auto'}\',
41
- #{options[:resizable]},
42
- #{options[:modal]},
43
- #{options[:overflow]}
44
- )",
45
- html_options
39
+ '#',
40
+ html_options.merge(:onclick => "Alchemy.openWindow('#{url}', '#{options[:title]}', '#{size}', '#{size_y}', #{options[:resizable]}, #{options[:modal]}, #{options[:overflow]})")
46
41
  )
47
42
  end
48
43
 
@@ -90,18 +85,17 @@ module Alchemy
90
85
  }
91
86
  options = default_options.merge(options)
92
87
  options[:onkeyup] << ";jQuery('#search_field').val().length >= 1 ? jQuery('.js_filter_field_clear').show() : jQuery('.js_filter_field_clear').hide();"
93
- filter_field = "<div class=\"js_filter_field_box\">"
88
+ filter_field = '<div class="js_filter_field_box">'
94
89
  filter_field << text_field_tag("filter", '', options)
95
90
  filter_field << content_tag('span', '', :class => 'icon search')
96
- filter_field << link_to_function(
97
- "",
98
- "jQuery('##{options[:id]}').val('');#{options[:onkeyup]}",
91
+ filter_field << link_to('', '#', {
92
+ :onclick => "jQuery('##{options[:id]}').val('');#{options[:onkeyup]}",
99
93
  :class => "js_filter_field_clear",
100
94
  :style => "display:none",
101
95
  :title => t("click_to_show_all")
102
- )
103
- filter_field << "<label for=\"search_field\">" + t("search") + "</label>"
104
- filter_field << "</div>"
96
+ })
97
+ filter_field << %(<label for="search_field">#{t(:search)}</label>)
98
+ filter_field << '</div>'
105
99
  filter_field.html_safe
106
100
  end
107
101
 
@@ -122,10 +116,10 @@ module Alchemy
122
116
  title = t("please_confirm")
123
117
  ok_lable = t("Yes")
124
118
  cancel_lable = t("No")
125
- link_to_function(
119
+ link_to(
126
120
  link_string,
127
- "Alchemy.confirmToDeleteWindow('#{url}', '#{title}', '#{message}', '#{ok_lable}', '#{cancel_lable}');",
128
- html_options
121
+ '#',
122
+ html_options.merge(:onclick => "Alchemy.confirmToDeleteWindow('#{url}', '#{title}', '#{message}', '#{ok_lable}', '#{cancel_lable}');")
129
123
  )
130
124
  end
131
125
 
@@ -70,12 +70,6 @@ module Alchemy
70
70
  )
71
71
  end
72
72
 
73
- # Returns a textarea ready to use with tinymce
74
- def tinymce_tag(name, content = '', options = {})
75
- append_class_name(options, 'tinymce')
76
- text_area_tag(name, content, options)
77
- end
78
-
79
73
  end
80
74
  end
81
75
  end
@@ -11,34 +11,69 @@ module Alchemy
11
11
  end
12
12
  end
13
13
  init = init.collect { |key, value| "#{key} : #{value.to_json}" }.join(', ')
14
-
15
14
  setup = "init.setup = #{Alchemy::Tinymce.setup};" if Alchemy::Tinymce.setup
16
- return "
17
- <script type='text/javascript'>
18
- jQuery(function(){
19
- if (typeof(Alchemy) !== 'object') { Alchemy = {}; };
20
- Alchemy.Tinymce = {
21
- init : function(callback) {
22
- var init = { #{init} };
23
- init.mode = 'specific_textareas';
24
- init.editor_selector = 'tinymce';
25
- init.plugins = '#{Alchemy::Tinymce.plugins.join(',')}';
26
- init.language = '#{::I18n.locale.to_s.split('-')[0].downcase }';
27
- init.init_instance_callback = function(inst) {
28
- jQuery('#' + inst.editorId).prev('.essence_richtext_loader').hide();
29
- }
30
- if (callback)
31
- init.oninit = callback;
32
- #{setup}
33
- tinymce.init(init);
34
- },
35
- addEditor : function(dom_id) {
36
- tinymce.execCommand('mceAddControl', true, dom_id);
37
- }
38
- };
39
- Alchemy.Tinymce.init();
40
- });
41
- </script>".html_safe
15
+ tinymce_javascript_string = "
16
+ <script type='text/javascript'>
17
+ jQuery(function($) {
18
+ if (typeof(Alchemy) !== 'object') { Alchemy = {}; };
19
+ Alchemy.Tinymce = {
20
+ init : function(callback) {
21
+ var init = { #{init} };
22
+ init.mode = 'specific_textareas';
23
+ init.editor_selector = 'default_tinymce';
24
+ init.plugins = '#{Alchemy::Tinymce.plugins.join(',')}';
25
+ init.language = '#{::I18n.locale.to_s.split('-')[0].downcase }';
26
+ init.init_instance_callback = function(inst) {
27
+ $('#' + inst.editorId).prev('.essence_richtext_loader').hide();
28
+ }
29
+ if (callback)
30
+ init.oninit = callback;
31
+ #{setup}
32
+ tinymce.init(init);
33
+ },
34
+ addEditor : function(dom_id) {
35
+ tinymce.execCommand('mceAddControl', true, dom_id);
36
+ }
37
+ };
38
+ });
39
+ </script>"
40
+ if Alchemy::Tinymce.custom_config_contents.any?
41
+ (tinymce_javascript_string + custom_tinymce_javascript_tags).html_safe
42
+ else
43
+ tinymce_javascript_string.html_safe
44
+ end
45
+ end
46
+
47
+ def custom_tinymce_javascript_tags
48
+ custom_config_string = "
49
+ <script type='text/javascript'>
50
+ jQuery(function($) {
51
+ Alchemy.Tinymce.customInits = [];"
52
+ Alchemy::Tinymce.custom_config_contents.each do |content|
53
+ next unless content['settings']['tinymce']
54
+ config = Alchemy::Tinymce.init.merge(content['settings']['tinymce'].symbolize_keys)
55
+ config = config.collect { |key, value| "#{key} : #{value.to_json}" }.join(', ')
56
+ custom_config_string += "
57
+ Alchemy.Tinymce.customInits.push(function(callback) {
58
+ var init = { #{config} };
59
+ init.mode = 'specific_textareas';
60
+ init.editor_selector = /custom_tinymce #{Regexp.escape(content['name'])}/;
61
+ init.plugins = '#{Alchemy::Tinymce.plugins.join(',')}';
62
+ init.language = '#{::I18n.locale.to_s.split('-')[0].downcase }';
63
+ init.init_instance_callback = function(inst) {
64
+ var $this = $('#' + inst.editorId);
65
+ $this.prev('.essence_richtext_loader').hide();
66
+ inst.onChange.add(function (ed, l) {
67
+ Alchemy.setElementDirty($this.parents('.element_editor'));
68
+ });
69
+ }
70
+ tinymce.init(init);
71
+ });"
72
+ end
73
+ custom_config_string += "
74
+ });
75
+ </script>"
76
+ custom_config_string.html_safe
42
77
  end
43
78
 
44
79
  end