alchemy_cms 3.0.0 → 3.0.1

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/README.md +3 -6
  4. data/alchemy_cms.gemspec +1 -1
  5. data/app/assets/stylesheets/alchemy/buttons.scss +4 -4
  6. data/app/assets/stylesheets/alchemy/sitemap.scss +1 -1
  7. data/app/controllers/alchemy/pages_controller.rb +5 -5
  8. data/app/helpers/alchemy/admin/elements_helper.rb +3 -3
  9. data/app/helpers/alchemy/admin/navigation_helper.rb +2 -1
  10. data/app/models/alchemy/cell.rb +1 -1
  11. data/app/models/alchemy/element.rb +10 -0
  12. data/app/models/alchemy/page/page_naming.rb +10 -3
  13. data/app/models/alchemy/picture.rb +6 -2
  14. data/app/views/alchemy/admin/pages/_page.html.erb +1 -1
  15. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  16. data/app/views/alchemy/admin/pages/info.html.erb +10 -1
  17. data/app/views/alchemy/base/leave.html.erb +1 -1
  18. data/config/locales/alchemy.de.yml +1 -1
  19. data/config/locales/alchemy.en.yml +3 -3
  20. data/config/locales/alchemy.fr.yml +1 -1
  21. data/config/locales/alchemy.nl.yml +1 -1
  22. data/lib/alchemy/test_support/factories.rb +4 -4
  23. data/lib/alchemy/version.rb +1 -1
  24. data/lib/rails/generators/alchemy/essence/essence_generator.rb +1 -1
  25. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +1 -1
  26. data/spec/features/page_feature_spec.rb +20 -0
  27. data/spec/helpers/admin/elements_helper_spec.rb +11 -2
  28. data/spec/helpers/admin/navigation_helper_spec.rb +30 -0
  29. data/spec/models/element_spec.rb +27 -4
  30. data/spec/models/page_spec.rb +10 -7
  31. data/spec/models/picture_spec.rb +19 -0
  32. data/vendor/assets/javascripts/tinymce/langs/fr.js +184 -0
  33. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 611a1e37fe56a17099be81544930085f61508212
4
- data.tar.gz: cd2ec040d0c1099bd916761ff305f976b20c7908
3
+ metadata.gz: 22899ddac5bccee25b1e3438bcec28374381edc6
4
+ data.tar.gz: 92792959062f3873ff293a491a7c981e83ed2797
5
5
  SHA512:
6
- metadata.gz: 7345ebd721ad3d3823c25d0ea98f768684fd8187df860bba180967060a272d7f03a08b0c7d0b68bf610d38da1a268909857f080b0bb9196be2322fd61d615f58
7
- data.tar.gz: c957bfb8d7512694346a4213c91c7e0714365ad1593dd34ecf9252937b2767f258792f45f6b543862a143a0fa2f4d41e353bf596f2e59c7fd6dc26ec746cbb1d
6
+ metadata.gz: ec3084ada69925e12a6458ed3768d4f63e23c3b20def9ebe63543946b05ad3d4288fd040ef20c88fa2857a0c123daa8462ef2f87d2facaa8b78ff7dd4926eea7
7
+ data.tar.gz: 8f0f74f54d0b903c5d02c445107ea8358927c5faa2ff4ab30ac37f0b1612f92ef63ed00d9b4bdbe6cb856200d43664e82754e4d197200a044fe84e797b8dc2c3
@@ -5,7 +5,7 @@ rvm:
5
5
  - 2.1
6
6
  branches:
7
7
  only:
8
- - master
8
+ - 3.0-stable
9
9
  script: rake
10
10
  env:
11
11
  - DB=mysql
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ![Alchemy CMS](http://alchemy-cms.com/assets/alchemy_logo.svg)
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/alchemy_cms.png)](http://badge.fury.io/rb/alchemy_cms)
4
- [![Build Status](https://travis-ci.org/magiclabs/alchemy_cms.svg?branch=master)](https://travis-ci.org/magiclabs/alchemy_cms) [![Code Climate](https://codeclimate.com/github/magiclabs/alchemy_cms.png)](https://codeclimate.com/github/magiclabs/alchemy_cms) [![Coverage Status](https://coveralls.io/repos/magiclabs/alchemy_cms/badge.png?branch=master)](https://coveralls.io/r/magiclabs/alchemy_cms?branch=master)
4
+ [![Build Status](https://travis-ci.org/magiclabs/alchemy_cms.svg?branch=3.0-stable)](https://travis-ci.org/magiclabs/alchemy_cms) [![Code Climate](https://codeclimate.com/github/magiclabs/alchemy_cms.png)](https://codeclimate.com/github/magiclabs/alchemy_cms) [![Coverage Status](https://coveralls.io/repos/magiclabs/alchemy_cms/badge.png?branch=3.0-stable)](https://coveralls.io/r/magiclabs/alchemy_cms?branch=3.0-stable)
5
5
 
6
6
  About
7
7
  -----
@@ -10,9 +10,6 @@ Alchemy is the most powerful, userfriendly and flexible Rails CMS.
10
10
 
11
11
  Read more on the [website](http://alchemy-cms.com) and in the [guidelines](http://guides.alchemy-cms.com).
12
12
 
13
- **This master branch is a development branch that can contain bugs. For productive environments you should use the [current Ruby gem version](http://rubygems.org/gems/alchemy_cms/versions/3.0.0),
14
- or the [latest stable branch (3.0-stable)](https://github.com/magiclabs/alchemy_cms/tree/3.0-stable).**
15
-
16
13
  Features
17
14
  --------
18
15
 
@@ -60,7 +57,7 @@ Installation
60
57
 
61
58
  #### 1. Use the installer:
62
59
 
63
- gem install alchemy_cms --pre
60
+ gem install alchemy_cms
64
61
  alchemy new my_magicpage
65
62
  cd my_magicpage
66
63
 
@@ -84,7 +81,7 @@ Open `http://localhost:3000` and follow the on screen instructions.
84
81
 
85
82
  In your App's Gemfile:
86
83
 
87
- gem 'alchemy_cms', github: 'magiclabs/alchemy_cms', branch: 'master'
84
+ gem 'alchemy_cms', github: 'magiclabs/alchemy_cms', branch: '3.0-stable'
88
85
 
89
86
  #### 2. Install Alchemy into your app:
90
87
 
@@ -25,7 +25,7 @@ Gem::Specification.new do |gem|
25
25
  gem.add_runtime_dependency 'acts-as-taggable-on', '~> 3.1'
26
26
  gem.add_runtime_dependency 'cancan', '~> 1.6.10'
27
27
  gem.add_runtime_dependency 'dragonfly', '~> 1.0.1'
28
- gem.add_runtime_dependency 'kaminari', '~> 0.15.0'
28
+ gem.add_runtime_dependency 'kaminari', '~> 0.15'
29
29
  gem.add_runtime_dependency 'acts_as_list', '~> 0.3.0'
30
30
  gem.add_runtime_dependency 'magiclabs-userstamp', '~> 2.1.0'
31
31
  gem.add_runtime_dependency 'simple_form', '~> 3.0.1'
@@ -117,12 +117,12 @@ button, input[type="submit"], a.button, input.button {
117
117
  border: $default-border-width $default-border-style $button-hover-border-color;
118
118
  @include border-radius($default-border-radius);
119
119
  background: $button-hover-bg-color;
120
- top: 0px;
121
- right: 0px;
120
+ top: -1px;
121
+ right: -1px;
122
122
 
123
123
  .icon {
124
- left: 0px;
125
- top: 2px;
124
+ left: 1px;
125
+ top: 3px;
126
126
  }
127
127
  }
128
128
  }
@@ -262,7 +262,7 @@ div.page_infos {
262
262
  }
263
263
 
264
264
  .alchemy-dialog .page_status {
265
- margin: 4px 4px 0 0;
265
+ margin: 2px 4px 0 0;
266
266
  }
267
267
 
268
268
  #sitemap {
@@ -8,20 +8,20 @@ module Alchemy
8
8
 
9
9
  rescue_from ActionController::RoutingError, :with => :render_404
10
10
 
11
- before_filter :enforce_primary_host_for_site
12
- before_filter :render_page_or_redirect, :only => [:show]
13
- before_filter :load_page
14
- authorize_resource only: 'show'
11
+ before_action :enforce_primary_host_for_site
12
+ before_action :render_page_or_redirect, only: [:show]
15
13
 
16
14
  # Showing page from params[:urlname]
17
15
  #
18
16
  def show
17
+ authorize! :show, @page
18
+
19
19
  if render_fresh_page?
20
20
  respond_to do |format|
21
21
  format.html { render layout: !request.xhr? }
22
22
  format.rss do
23
23
  if @page.contains_feed?
24
- render action: 'show', layout: false, handlers: [:builder]
24
+ render layout: false, handlers: [:builder]
25
25
  else
26
26
  render xml: {error: 'Not found'}, status: 404
27
27
  end
@@ -72,12 +72,12 @@ module Alchemy
72
72
  end
73
73
  other_elements = elements - celled_elements
74
74
  unless other_elements.blank?
75
- optgroup_label = _t(:main_content)
76
- options[optgroup_label] = other_elements.map do |e|
75
+ options[_t(:main_content)] = other_elements.map do |e|
77
76
  element_array_for_options(e, object_method)
78
77
  end
79
78
  end
80
- options
79
+ # We don't want to show empty cells
80
+ options.delete_if { |cell, elements| elements.blank? }
81
81
  end
82
82
 
83
83
  def element_array_for_options(e, object_method, cell = nil)
@@ -163,7 +163,8 @@ module Alchemy
163
163
  {
164
164
  controller: entry['controller'],
165
165
  action: entry['action'],
166
- only_path: true
166
+ only_path: true,
167
+ params: entry['params']
167
168
  }
168
169
  end
169
170
 
@@ -19,7 +19,7 @@
19
19
  #
20
20
  # Render cells with the +render_cell+ helper
21
21
  #
22
- # Views for cells are inside the +app/views/cells+ folder in you project.
22
+ # Views for cells are inside the +app/views/cells+ folder in your project.
23
23
  #
24
24
  module Alchemy
25
25
  class Cell < ActiveRecord::Base
@@ -47,6 +47,7 @@ module Alchemy
47
47
 
48
48
  after_create :create_contents, :unless => proc { |e| e.create_contents_after_create == false }
49
49
  after_update :touch_pages
50
+ after_update :touch_cell, unless: -> { self.cell.nil? }
50
51
 
51
52
  scope :trashed, -> { where(position: nil).order('updated_at DESC') }
52
53
  scope :not_trashed, -> { where(Element.arel_table[:position].not_eq(nil)) }
@@ -461,5 +462,14 @@ module Alchemy
461
462
  available_page_cells(page).collect(&:name).uniq
462
463
  end
463
464
 
465
+ # If element has a +cell+ associated,
466
+ # it updates it's timestamp.
467
+ #
468
+ # Called after_update
469
+ #
470
+ def touch_cell
471
+ self.cell.touch
472
+ end
473
+
464
474
  end
465
475
  end
@@ -72,11 +72,11 @@ module Alchemy
72
72
  def set_urlname
73
73
  if Config.get(:url_nesting)
74
74
  url_name = [
75
- parent.nil? || parent.language_root? ? nil : parent.urlname,
76
- convert_url_name((urlname.blank? ? name : slug))
75
+ parent_urlname,
76
+ convert_url_name(urlname.blank? ? name : slug)
77
77
  ].compact.join('/')
78
78
  else
79
- url_name = convert_url_name((urlname.blank? ? name : urlname))
79
+ url_name = convert_url_name(urlname.blank? ? name : urlname)
80
80
  end
81
81
  write_attribute :urlname, url_name
82
82
  end
@@ -98,5 +98,12 @@ module Alchemy
98
98
  url_name
99
99
  end
100
100
  end
101
+
102
+ # Urlname of parent page.
103
+ # Returns nil, if the parent is either a language root page or the root page itself
104
+ def parent_urlname
105
+ return if parent.nil? || parent.language_root? || parent.root?
106
+ parent.urlname
107
+ end
101
108
  end
102
109
  end
@@ -39,9 +39,13 @@ module Alchemy
39
39
  raise PictureInUseError, I18n.t(:cannot_delete_picture_notice) % { name: name }
40
40
  end
41
41
 
42
+ # Enables Dragonfly image processing
42
43
  dragonfly_accessor :image_file, app: :alchemy_pictures do
43
- if Config.get(:preprocess_image_resize).present?
44
- after_assign { |a| a.process!(:resize, "#{Config.get(:preprocess_image_resize)}>") }
44
+ # Preprocess after uploading the picture
45
+ after_assign do |p|
46
+ if Config.get(:preprocess_image_resize).present?
47
+ p.thumb!("#{Config.get(:preprocess_image_resize)}>")
48
+ end
45
49
  end
46
50
  end
47
51
 
@@ -12,7 +12,7 @@
12
12
  alchemy.info_admin_page_path(page),
13
13
  {
14
14
  title: _t(:page_infos),
15
- size: '480x290'
15
+ size: '520x290'
16
16
  },
17
17
  {
18
18
  title: _t(:page_infos),
@@ -32,7 +32,7 @@
32
32
  alchemy.info_admin_page_path(@page),
33
33
  {
34
34
  title: _t(:page_infos),
35
- size: '480x320'
35
+ size: '520x320'
36
36
  },
37
37
  {
38
38
  class: 'icon_button',
@@ -5,9 +5,18 @@
5
5
  <% end %>
6
6
  <% end %>
7
7
  <div class="value">
8
- <label><%= _t(:page_type) %></label>
8
+ <label><%= Alchemy::Page.human_attribute_name(:page_layout) %></label>
9
9
  <p><%= @page.layout_display_name %></p>
10
10
  </div>
11
+ <div class="value">
12
+ <% if @page.redirects_to_external? %>
13
+ <label><%= Alchemy::Page.human_attribute_name(:urlname) %></label>
14
+ <p><%= @page.urlname %></p>
15
+ <% else %>
16
+ <label><%= Alchemy::LegacyPageUrl.human_attribute_name(:urlname) %></label>
17
+ <p><%= "/#{@page.urlname}" %></p>
18
+ <% end %>
19
+ </div>
11
20
  <div class="value">
12
21
  <label><%= _t(:page_status) %></label>
13
22
  <p><%= combined_page_status(@page) %></p>
@@ -6,6 +6,6 @@
6
6
  <%= link_to _t('stay logged in'), alchemy.root_path, class: 'button' %>
7
7
  </p>
8
8
  <%= form_tag Alchemy.logout_path, method: 'delete', class: 'buttons' do %>
9
- <label><%= _t('or to completly') %></label>
9
+ <label><%= _t('or to completely') %></label>
10
10
  <%= button _t(:logout), autofocus: true %>
11
11
  <% end %>
@@ -579,7 +579,7 @@ de:
579
579
  "You are about to leave Alchemy": "Sie sind dabei Alchemy zu verlassen"
580
580
  "Do you want to": "Wollen Sie dabei"
581
581
  "stay logged in": "angemeldet bleiben"
582
- "or to completly": "oder Sich komplett"
582
+ "or to completely": "oder Sich komplett"
583
583
  "Are you sure?": "Sind Sie sicher?"
584
584
  "Create": "Hinzufügen"
585
585
  "Edit": "Bearbeiten"
@@ -201,7 +201,7 @@ en:
201
201
  "Hide Elements": "Hide Elements"
202
202
  "If you have any problems using the Flash uploader you can switch to": "If you have any problems using the Flash uploader you can switch to %{link}"
203
203
  "Image missing": "Image missing"
204
- "Image size": "Imagesize"
204
+ "Image size": "Image size"
205
205
  "Language successfully created": "Language successfully created."
206
206
  "Language successfully removed": "Language successfully removed."
207
207
  "Language successfully updated": "Language successfully updated."
@@ -244,7 +244,7 @@ en:
244
244
  "Please enter a new password": "Please enter a new password."
245
245
  "Please enter your email address": "Please enter your email address."
246
246
  "Please log in": "Please log in."
247
- "Please seperate the tags with commata": "* Please seperate the tags with commata."
247
+ "Please seperate the tags with commata": "* Please seperate the tags with commas."
248
248
  "Properties": "Properties"
249
249
  "Publish page": "Publish page"
250
250
  "Read the License": "Read the License"
@@ -579,7 +579,7 @@ en:
579
579
  "You are about to leave Alchemy": "You are about to leave Alchemy"
580
580
  "Do you want to": "Do you want to"
581
581
  "stay logged in": "stay logged in"
582
- "or to completly": "or to completly"
582
+ "or to completely": "or to completely"
583
583
  "Are you sure?": "Are you sure?"
584
584
  "Create": "Create"
585
585
  "Edit": "Edit"
@@ -605,7 +605,7 @@ fr:
605
605
  "You are about to leave Alchemy": "Vous êtes sur le point de quitter l'Alchimie"
606
606
  "Do you want to": "Voulez-vous "
607
607
  "stay logged in": "rester connecté"
608
- "or to completly": "ou vous-même complètement"
608
+ "or to completely": "ou vous-même complètement"
609
609
  "Please wait. Flash® is loading...": "S'il vous plaît attendre. Uploader Flash ® est en cours de chargement..."
610
610
  "Are you sure?": "Etes-vous sûr?"
611
611
  "Create": "ajouter"
@@ -579,7 +579,7 @@ nl:
579
579
  "You are about to leave Alchemy": "U staat op het punt de NVM Regiosite te verlaten"
580
580
  "Do you want to": "Wilt u"
581
581
  "stay logged in": "ingelogd blijven"
582
- "or to completly": "of compleet"
582
+ "or to completely": "of compleet"
583
583
  "Are you sure?": "Weet u het zeker?"
584
584
  "Create": "Aanmaken"
585
585
  "Edit": "Aanpassen"
@@ -5,19 +5,19 @@ FactoryGirl.define do
5
5
  password 's3cr3t'
6
6
 
7
7
  factory :admin_user do
8
- alchemy_roles 'admin'
8
+ alchemy_roles ['admin']
9
9
  end
10
10
 
11
11
  factory :member_user do
12
- alchemy_roles 'member'
12
+ alchemy_roles ['member']
13
13
  end
14
14
 
15
15
  factory :author_user do
16
- alchemy_roles 'author'
16
+ alchemy_roles ['author']
17
17
  end
18
18
 
19
19
  factory :editor_user do
20
- alchemy_roles 'editor'
20
+ alchemy_roles ['editor']
21
21
  end
22
22
  end
23
23
 
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
- VERSION = "3.0.0"
2
+ VERSION = "3.0.1"
3
3
 
4
4
  def self.version
5
5
  VERSION
@@ -36,7 +36,7 @@ CLASSMETHOD
36
36
  end
37
37
 
38
38
  def copy_templates
39
- essence_name = @essence_name.classify.demodulize
39
+ essence_name = @essence_name.classify.demodulize.underscore
40
40
  template "view.html.erb", "#{@essence_view_path}/_#{essence_name}_view.html.erb"
41
41
  template "editor.html.erb", "#{@essence_view_path}/_#{essence_name}_editor.html.erb"
42
42
  end
@@ -6,7 +6,7 @@
6
6
  Please consult Alchemy::Content.rb docs for further methods on the content object
7
7
  %>
8
8
  <%% cache(content) do %>
9
- <div class="content_editor <%= @essence_name.classify.demodulize %>" id="<%%= content.dom_id %>">
9
+ <div class="content_editor <%= @essence_name.classify.demodulize.underscore %>" id="<%%= content.dom_id %>" data-content-id="<%%= content.id %>">
10
10
  <%%= label_and_remove_link(content) %>
11
11
  <%%= text_field_tag(
12
12
  content.form_field_name,
@@ -234,5 +234,25 @@ module Alchemy
234
234
  end
235
235
  end
236
236
 
237
+ describe 'accessing restricted pages' do
238
+ let!(:restricted_page) { create(:restricted_page, public: true) }
239
+
240
+ context 'as a guest user' do
241
+ it "I am not able to visit the page" do
242
+ visit restricted_page.urlname
243
+ current_path.should == Alchemy.login_path
244
+ end
245
+ end
246
+
247
+ context 'as a member user' do
248
+ before { authorize_as_admin(create(:member_user)) }
249
+
250
+ it "I am able to visit the page" do
251
+ visit restricted_page.urlname
252
+ current_path.should == "/#{restricted_page.urlname}"
253
+ end
254
+ end
255
+ end
256
+
237
257
  end
238
258
  end
@@ -26,8 +26,11 @@ module Alchemy
26
26
  }
27
27
 
28
28
  before do
29
- page.stub(layout_description: {'name' => "foo", 'cells' => ["foo_cell"]})
30
- cell_descriptions = [{'name' => "foo_cell", 'elements' => ["1", "2"]}]
29
+ page.stub(layout_description: {'name' => "foo", 'cells' => ["foo_cell", "empty_cell"]})
30
+ cell_descriptions = [
31
+ {'name' => "foo_cell", 'elements' => ["1", "2"]},
32
+ {'name' => 'empty_cell', 'elements' => []}
33
+ ]
31
34
  Cell.stub(:definitions).and_return(cell_descriptions)
32
35
  helper.instance_variable_set('@page', page)
33
36
  end
@@ -48,6 +51,12 @@ module Alchemy
48
51
  helper.grouped_elements_for_select(elements).should == ""
49
52
  end
50
53
  end
54
+
55
+ context "with cell having no elements" do
56
+ it "should remove that cell from hash" do
57
+ expect(helper.grouped_elements_for_select(elements)['Empty cell']).to be_nil
58
+ end
59
+ end
51
60
  end
52
61
 
53
62
  describe "#elements_for_select" do
@@ -27,6 +27,24 @@ describe Alchemy::Admin::NavigationHelper do
27
27
  }
28
28
  } }
29
29
 
30
+ let(:event_module_with_params) { {
31
+ 'navigation' => {
32
+ 'controller' => '/admin/events',
33
+ 'action' => 'index',
34
+ 'params' => {
35
+ 'key' => 'value'
36
+ },
37
+ 'sub_navigation' => [{
38
+ 'controller' => '/admin/events',
39
+ 'action' => 'index',
40
+ 'params' => {
41
+ 'key' => 'value',
42
+ 'key2' => 'value2'
43
+ }
44
+ }]
45
+ }
46
+ } }
47
+
30
48
  let(:navigation) { alchemy_module['navigation'] }
31
49
 
32
50
  describe '#alchemy_main_navigation_entry' do
@@ -180,6 +198,10 @@ describe Alchemy::Admin::NavigationHelper do
180
198
  it "returns correct url string" do
181
199
  helper.url_for_module(event_module).should == '/admin/events'
182
200
  end
201
+
202
+ it "returns correct url string with params" do
203
+ helper.url_for_module(event_module_with_params).should == '/admin/events?key=value'
204
+ end
183
205
  end
184
206
  end
185
207
 
@@ -209,6 +231,14 @@ describe Alchemy::Admin::NavigationHelper do
209
231
  end
210
232
  end
211
233
 
234
+ context "with module within host app with params" do
235
+ let(:current_module) { event_module_with_params }
236
+
237
+ it "returns correct url string with params" do
238
+ should == '/admin/events?key2=value2&key=value'
239
+ end
240
+ end
241
+
212
242
  context 'without module found' do
213
243
  before do
214
244
  helper.stub(module_definition_for: nil)
@@ -422,16 +422,20 @@ module Alchemy
422
422
  end
423
423
 
424
424
  describe '.after_update' do
425
+ let(:page) { create(:page) }
426
+ let(:element) { create(:element, page: page) }
427
+ let(:now) { Time.now }
428
+
429
+ before do
430
+ Time.stub(now: now)
431
+ end
432
+
425
433
  context 'with touchable pages' do
426
434
  let(:locker) { mock_model('DummyUser') }
427
- let(:page) { create(:page) }
428
- let(:element) { create(:element, page: page) }
429
- let(:now) { Time.now }
430
435
  let(:pages) { [page] }
431
436
 
432
437
  before do
433
438
  Alchemy.user_class.stub(:stamper).and_return(locker.id)
434
- Time.stub(now: now)
435
439
  end
436
440
 
437
441
  it "updates page timestamps" do
@@ -446,6 +450,25 @@ module Alchemy
446
450
  page.updater_id.should eq(locker.id)
447
451
  end
448
452
  end
453
+
454
+ context 'with cell associated' do
455
+ let(:cell) { mock_model('Cell') }
456
+
457
+ before do
458
+ element.stub(cell: cell)
459
+ end
460
+
461
+ it "updates timestamp of cell" do
462
+ element.cell.should_receive(:touch)
463
+ element.save
464
+ end
465
+ end
466
+
467
+ context 'without cell associated' do
468
+ it "does not update timestamp of cell" do
469
+ expect { element.save }.to_not raise_error
470
+ end
471
+ end
449
472
  end
450
473
 
451
474
  describe '#taggable?' do
@@ -1203,12 +1203,13 @@ module Alchemy
1203
1203
  end
1204
1204
 
1205
1205
  context 'urlname updating' do
1206
- let(:parentparent) { FactoryGirl.create(:page, name: 'parentparent', visible: true) }
1207
- let(:parent) { FactoryGirl.create(:page, parent_id: parentparent.id, name: 'parent', visible: true) }
1208
- let(:page) { FactoryGirl.create(:page, parent_id: parent.id, name: 'page', visible: true) }
1209
- let(:invisible) { FactoryGirl.create(:page, parent_id: page.id, name: 'invisible', visible: false) }
1210
- let(:contact) { FactoryGirl.create(:page, parent_id: invisible.id, name: 'contact', visible: true) }
1211
- let(:external) { FactoryGirl.create(:page, parent_id: parent.id, name: 'external', page_layout: 'external', urlname: 'http://google.com') }
1206
+ let(:parentparent) { FactoryGirl.create(:page, name: 'parentparent', visible: true) }
1207
+ let(:parent) { FactoryGirl.create(:page, parent_id: parentparent.id, name: 'parent', visible: true) }
1208
+ let(:page) { FactoryGirl.create(:page, parent_id: parent.id, name: 'page', visible: true) }
1209
+ let(:invisible) { FactoryGirl.create(:page, parent_id: page.id, name: 'invisible', visible: false) }
1210
+ let(:contact) { FactoryGirl.create(:page, parent_id: invisible.id, name: 'contact', visible: true) }
1211
+ let(:external) { FactoryGirl.create(:page, parent_id: parent.id, name: 'external', page_layout: 'external', urlname: 'http://google.com') }
1212
+ let(:language_root) { parentparent.parent }
1212
1213
 
1213
1214
  context "with activated url_nesting" do
1214
1215
  before { Config.stub(:get).and_return(true) }
@@ -1218,7 +1219,9 @@ module Alchemy
1218
1219
  end
1219
1220
 
1220
1221
  it "should not include the root page" do
1221
- page.urlname.should_not =~ /root/
1222
+ Page.root.update_column(:urlname, 'root')
1223
+ language_root.update(urlname: 'new-urlname')
1224
+ language_root.urlname.should_not =~ /root/
1222
1225
  end
1223
1226
 
1224
1227
  it "should not include the language root page" do
@@ -32,6 +32,25 @@ module Alchemy
32
32
  picture.should be_valid
33
33
  end
34
34
 
35
+ context 'with enabled preprocess_image_resize config option' do
36
+ let(:image_file) do
37
+ File.new(File.expand_path('../../fixtures/80x60.png', __FILE__))
38
+ end
39
+
40
+ before do
41
+ Config.stub(:get) do |arg|
42
+ if arg == :preprocess_image_resize
43
+ '10x10'
44
+ end
45
+ end
46
+ end
47
+
48
+ it "it resizes the image after upload" do
49
+ picture = Picture.new(image_file: image_file)
50
+ expect(picture.image_file.data[0x10..0x18].unpack('NN')).to eq([10, 8])
51
+ end
52
+ end
53
+
35
54
  describe '#suffix' do
36
55
  it "should return the suffix of original filename" do
37
56
  pic = stub_model(Picture, image_file_name: 'kitten.JPG')
@@ -0,0 +1,184 @@
1
+ tinymce.addI18n('fr',{
2
+ "Cut": "Couper",
3
+ "Header 2": "Titre 2",
4
+ "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Votre navigateur ne supporte pas la copie directe. Merci d'utiliser les touches Ctrl+X\/C\/V.",
5
+ "Div": "Div",
6
+ "Paste": "Coller",
7
+ "Close": "Fermer",
8
+ "Font Family": "Polices de caract\u00e8res",
9
+ "Pre": "Pre",
10
+ "Align right": "Aligner \u00e0 droite",
11
+ "New document": "Nouveau document",
12
+ "Blockquote": "Citation",
13
+ "Numbered list": "Num\u00e9rotation",
14
+ "Increase indent": "Augmenter le retrait",
15
+ "Formats": "Formats",
16
+ "Headers": "Titres",
17
+ "Select all": "Tout s\u00e9lectionner",
18
+ "Header 3": "Titre 3",
19
+ "Blocks": "Blocs",
20
+ "Undo": "Annuler",
21
+ "Strikethrough": "Barr\u00e9",
22
+ "Bullet list": "Puces",
23
+ "Header 1": "Titre 1",
24
+ "Superscript": "Exposant",
25
+ "Clear formatting": "Effacer la mise en forme",
26
+ "Font Sizes": "Tailles de la police",
27
+ "Subscript": "Indice",
28
+ "Header 6": "Titre 6",
29
+ "Redo": "R\u00e9tablir",
30
+ "Paragraph": "Paragraphe",
31
+ "Ok": "Ok",
32
+ "Bold": "Gras",
33
+ "Code": "Code",
34
+ "Italic": "Italique",
35
+ "Align center": "Aligner au centre",
36
+ "Header 5": "Titre 5",
37
+ "Decrease indent": "Diminuer le retrait",
38
+ "Header 4": "Titre 4",
39
+ "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Le presse-papiers est maintenant en mode \"texte plein\". Les contenus seront coll\u00e9s sans retenir les formatages jusqu'\u00e0 ce que vous d\u00e9sactiviez cette option.",
40
+ "Underline": "Soulign\u00e9",
41
+ "Cancel": "Annuler",
42
+ "Justify": "Justifi\u00e9",
43
+ "Inline": "En ligne",
44
+ "Copy": "Copier",
45
+ "Align left": "Aligner \u00e0 gauche",
46
+ "Visual aids": "Aides visuelle",
47
+ "Lower Greek": "Grec minuscule",
48
+ "Square": "Carr\u00e9",
49
+ "Default": "Par d\u00e9faut",
50
+ "Lower Alpha": "Alpha minuscule",
51
+ "Circle": "Cercle",
52
+ "Disc": "Disque",
53
+ "Upper Alpha": "Alpha majuscule",
54
+ "Upper Roman": "Romain majuscule",
55
+ "Lower Roman": "Romain minuscule",
56
+ "Name": "Nom",
57
+ "Anchor": "Ancre",
58
+ "You have unsaved changes are you sure you want to navigate away?": "Vous avez des modifications non enregistr\u00e9es, \u00eates-vous s\u00fbr de quitter la page?",
59
+ "Restore last draft": "Restaurer le dernier brouillon",
60
+ "Special character": "Caract\u00e8res sp\u00e9ciaux",
61
+ "Source code": "Code source",
62
+ "Right to left": "Droite \u00e0 gauche",
63
+ "Left to right": "Gauche \u00e0 droite",
64
+ "Emoticons": "Emotic\u00f4nes",
65
+ "Robots": "Robots",
66
+ "Document properties": "Propri\u00e9t\u00e9 du document",
67
+ "Title": "Titre",
68
+ "Keywords": "Mots-cl\u00e9s",
69
+ "Encoding": "Encodage",
70
+ "Description": "Description",
71
+ "Author": "Auteur",
72
+ "Fullscreen": "Plein \u00e9cran",
73
+ "Horizontal line": "Ligne horizontale",
74
+ "Horizontal space": "Espacement horizontal",
75
+ "Insert\/edit image": "Ins\u00e9rer\/\u00e9diter une image",
76
+ "General": "G\u00e9n\u00e9ral",
77
+ "Advanced": "Avanc\u00e9",
78
+ "Source": "Source",
79
+ "Border": "Bordure",
80
+ "Constrain proportions": "Contraindre les proportions",
81
+ "Vertical space": "Espacement vertical",
82
+ "Image description": "Description de l'image",
83
+ "Style": "Style",
84
+ "Dimensions": "Dimensions",
85
+ "Insert image": "Ins\u00e9rer une image",
86
+ "Insert date\/time": "Ins\u00e9rer date\/heure",
87
+ "Remove link": "Enlever le lien",
88
+ "Url": "Url",
89
+ "Text to display": "Texte \u00e0 afficher",
90
+ "Anchors": "Ancres",
91
+ "Insert link": "Ins\u00e9rer un lien",
92
+ "New window": "Nouvelle fen\u00eatre",
93
+ "None": "n\/a",
94
+ "The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "L'URL que vous avez entr\u00e9e semble \u00eatre un lien externe. Voulez-vous ajouter le pr\u00e9fixe http:\/\/ n\u00e9cessaire?",
95
+ "Target": "Cible",
96
+ "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "L'URL que vous avez entr\u00e9e semble \u00eatre une adresse e-mail. Voulez-vous ajouter le pr\u00e9fixe mailto: n\u00e9cessaire?",
97
+ "Insert\/edit link": "Ins\u00e9rer\/\u00e9diter un lien",
98
+ "Insert\/edit video": "Ins\u00e9rer\/\u00e9diter une vid\u00e9o",
99
+ "Poster": "Publier",
100
+ "Alternative source": "Source alternative",
101
+ "Paste your embed code below:": "Collez votre code d'int\u00e9gration ci-dessous :",
102
+ "Insert video": "Ins\u00e9rer une vid\u00e9o",
103
+ "Embed": "Ins\u00e9rer",
104
+ "Nonbreaking space": "Espace ins\u00e9cable",
105
+ "Page break": "Saut de page",
106
+ "Paste as text": "Coller comme texte",
107
+ "Preview": "Pr\u00e9visualiser",
108
+ "Print": "Imprimer",
109
+ "Save": "Enregistrer",
110
+ "Could not find the specified string.": "Impossible de trouver la cha\u00eene sp\u00e9cifi\u00e9e.",
111
+ "Replace": "Remplacer",
112
+ "Next": "Suiv",
113
+ "Whole words": "Mots entiers",
114
+ "Find and replace": "Trouver et remplacer",
115
+ "Replace with": "Remplacer par",
116
+ "Find": "Chercher",
117
+ "Replace all": "Tout remplacer",
118
+ "Match case": "Respecter la casse",
119
+ "Prev": "Pr\u00e9c ",
120
+ "Spellcheck": "V\u00e9rification orthographique",
121
+ "Finish": "Finie",
122
+ "Ignore all": "Tout ignorer",
123
+ "Ignore": "Ignorer",
124
+ "Insert row before": "Ins\u00e9rer une ligne avant",
125
+ "Rows": "Lignes",
126
+ "Height": "Hauteur",
127
+ "Paste row after": "Coller la ligne apr\u00e8s",
128
+ "Alignment": "Alignement",
129
+ "Column group": "Groupe de colonnes",
130
+ "Row": "Ligne",
131
+ "Insert column before": "Ins\u00e9rer une colonne avant",
132
+ "Split cell": "Diviser la cellule",
133
+ "Cell padding": "Espacement interne cellule",
134
+ "Cell spacing": "Espacement inter-cellulles",
135
+ "Row type": "Type de ligne",
136
+ "Insert table": "Ins\u00e9rer un tableau",
137
+ "Body": "Corps",
138
+ "Caption": "Titre",
139
+ "Footer": "Pied",
140
+ "Delete row": "Effacer la ligne",
141
+ "Paste row before": "Coller la ligne avant",
142
+ "Scope": "Etendue",
143
+ "Delete table": "Supprimer le tableau",
144
+ "H Align": "Alignement H",
145
+ "Top": "Haut",
146
+ "Header cell": "Cellule d'en-t\u00eate",
147
+ "Column": "Colonne",
148
+ "Row group": "Groupe de lignes",
149
+ "Cell": "Cellule",
150
+ "Middle": "Milieu",
151
+ "Cell type": "Type de cellule",
152
+ "Copy row": "Copier la ligne",
153
+ "Row properties": "Propri\u00e9t\u00e9s de la ligne",
154
+ "Table properties": "Propri\u00e9t\u00e9s du tableau",
155
+ "Bottom": "Bas",
156
+ "V Align": "Alignement V",
157
+ "Header": "En-t\u00eate",
158
+ "Right": "Droite",
159
+ "Insert column after": "Ins\u00e9rer une colonne apr\u00e8s",
160
+ "Cols": "Colonnes",
161
+ "Insert row after": "Ins\u00e9rer une ligne apr\u00e8s",
162
+ "Width": "Largeur",
163
+ "Cell properties": "Propri\u00e9t\u00e9s de la cellule",
164
+ "Left": "Gauche",
165
+ "Cut row": "Couper la ligne",
166
+ "Delete column": "Effacer la colonne",
167
+ "Center": "Centr\u00e9",
168
+ "Merge cells": "Fusionner les cellules",
169
+ "Insert template": "Ajouter un th\u00e8me",
170
+ "Templates": "Th\u00e8mes",
171
+ "Background color": "Couleur d'arri\u00e8re-plan",
172
+ "Text color": "Couleur du texte",
173
+ "Show blocks": "Afficher les blocs",
174
+ "Show invisible characters": "Afficher les caract\u00e8res invisibles",
175
+ "Words: {0}": "Mots : {0}",
176
+ "Insert": "Ins\u00e9rer",
177
+ "File": "Fichier",
178
+ "Edit": "Editer",
179
+ "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Zone Texte Riche. Appuyer sur ALT-F9 pour le menu. Appuyer sur ALT-F10 pour la barre d'outils. Appuyer sur ALT-0 pour de l'aide.",
180
+ "Tools": "Outils",
181
+ "View": "Voir",
182
+ "Table": "Tableau",
183
+ "Format": "Format"
184
+ });
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2014-07-03 00:00:00.000000000 Z
15
+ date: 2014-09-11 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: rails
@@ -110,14 +110,14 @@ dependencies:
110
110
  requirements:
111
111
  - - "~>"
112
112
  - !ruby/object:Gem::Version
113
- version: 0.15.0
113
+ version: '0.15'
114
114
  type: :runtime
115
115
  prerelease: false
116
116
  version_requirements: !ruby/object:Gem::Requirement
117
117
  requirements:
118
118
  - - "~>"
119
119
  - !ruby/object:Gem::Version
120
- version: 0.15.0
120
+ version: '0.15'
121
121
  - !ruby/object:Gem::Dependency
122
122
  name: acts_as_list
123
123
  requirement: !ruby/object:Gem::Requirement
@@ -1075,6 +1075,7 @@ files:
1075
1075
  - vendor/assets/javascripts/requestAnimationFrame.js
1076
1076
  - vendor/assets/javascripts/spin.min.js
1077
1077
  - vendor/assets/javascripts/tinymce/langs/de.js
1078
+ - vendor/assets/javascripts/tinymce/langs/fr.js
1078
1079
  - vendor/assets/javascripts/tinymce/langs/nl.js
1079
1080
  - vendor/assets/javascripts/tinymce/license.txt
1080
1081
  - vendor/assets/javascripts/tinymce/plugins/anchor/plugin.min.js