alchemy_cms 3.1.0.rc1 → 3.1.0.rc2

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -10
  3. data/.teatro.yml +7 -0
  4. data/Gemfile +13 -13
  5. data/README.md +20 -3
  6. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +3 -3
  7. data/app/assets/javascripts/alchemy/alchemy.uploader.js.coffee +5 -0
  8. data/app/controllers/alchemy/admin/essence_files_controller.rb +23 -3
  9. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +7 -2
  10. data/app/controllers/alchemy/admin/pictures_controller.rb +1 -4
  11. data/app/controllers/alchemy/base_controller.rb +3 -1
  12. data/app/controllers/alchemy/pages_controller.rb +3 -1
  13. data/app/models/alchemy/content.rb +0 -3
  14. data/app/models/alchemy/element.rb +1 -2
  15. data/app/models/alchemy/page/page_scopes.rb +3 -1
  16. data/app/models/alchemy/page/page_users.rb +10 -3
  17. data/app/models/alchemy/picture.rb +29 -6
  18. data/app/models/alchemy/picture/transformations.rb +14 -7
  19. data/app/views/alchemy/_menubar.html.erb +1 -1
  20. data/app/views/alchemy/admin/leave.html.erb +1 -1
  21. data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +2 -1
  22. data/config/initializers/dragonfly.rb +0 -2
  23. data/lib/alchemy/engine.rb +0 -1
  24. data/lib/alchemy/locale.rb +15 -13
  25. data/lib/alchemy/upgrader.rb +1 -0
  26. data/lib/alchemy/upgrader/three_point_zero.rb +27 -9
  27. data/lib/alchemy/version.rb +1 -1
  28. data/lib/tasks/alchemy/tidy.rake +34 -16
  29. data/spec/controllers/admin/essence_files_controller_spec.rb +13 -2
  30. data/spec/controllers/admin/essence_pictures_controller_spec.rb +8 -0
  31. data/spec/controllers/pages_controller_spec.rb +42 -0
  32. data/spec/dummy/app/controllers/application_controller.rb +5 -1
  33. data/spec/dummy/app/models/dummy_user.rb +4 -0
  34. data/spec/dummy/bin/bundle +0 -0
  35. data/spec/dummy/bin/rails +0 -0
  36. data/spec/dummy/bin/rake +0 -0
  37. data/spec/dummy/config/environments/production.rb +1 -1
  38. data/spec/dummy/db/seeds.rb +1 -0
  39. data/spec/features/translation_integration_spec.rb +14 -11
  40. data/spec/models/page_spec.rb +143 -52
  41. data/spec/spec_helper.rb +0 -3
  42. data/spec/support/transformation_examples.rb +7 -0
  43. data/spec/views/essences/essence_boolean_editor_spec.rb +1 -0
  44. data/vendor/assets/javascripts/fileupload/jquery.fileupload-process.js +6 -3
  45. data/vendor/assets/javascripts/fileupload/jquery.fileupload-validate.js +5 -3
  46. data/vendor/assets/javascripts/fileupload/jquery.fileupload.js +97 -46
  47. data/vendor/assets/javascripts/fileupload/jquery.iframe-transport.js +11 -4
  48. metadata +6 -4
  49. data/lib/extensions/action_view.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90a343fa98a444500a06cfbaa172e6bb8c6b4856
4
- data.tar.gz: fc1c96763f1a10a0938f54057b5bdc04f81dc002
3
+ metadata.gz: 64b4c80e471321ff237ae2fa6232841e2769f1fc
4
+ data.tar.gz: c66e6fbc68e16aa584ac6801d32821a3e04267f1
5
5
  SHA512:
6
- metadata.gz: 910b2641a2a94dfd31289b4f3d41f13e5b02f2ae1e543a1f4dc30627c9451500c9e34510058be064fa53de2c576b9bd770ed07f479bc69c0323b479e97c5fa92
7
- data.tar.gz: f05aac4f401eb397193dbb5a07a22e279fdb2cae66165b8ca7033e0f2415074f42d0d91e12588c2e44d8f7b2108fa25e1f3fbc62d7e7ce696cff23097778688c
6
+ metadata.gz: 3e43c4db264c179ebc193d8ace01227b9e94570b82c8c6f4c572e4cb1e5cfcd66995fa99a7326d57ac65f144da95f47c8aa28b2c2358dcf79afc4ea844672464
7
+ data.tar.gz: 326af8d144fdab241db4c6e41c8f981364b4ab5552c86132ba05f61aafe25ac06f635742c9f1f610e0c2f0d2f6ff026b833e69aaf32c1268b0f2a2860cf7f9ae
data/.gitignore CHANGED
@@ -6,22 +6,16 @@ doc/
6
6
  rdoc/
7
7
  Gemfile.lock
8
8
  pkg
9
- /spec/dummy/log/development.log
10
- /spec/dummy/log/test.log
11
- /spec/dummy/db/test.sqlite3
12
- /spec/dummy/db/development.sqlite3
13
9
  tmp
14
- spec/dummy/index/
15
10
  log
16
- spec/dummy/uploads/
17
11
  .sass-cache
18
- spec/dummy/db/test.sqlite3-journal
19
- spec/dummy/db/seeds.rb
20
- .rvmrc
12
+ spec/dummy/uploads/
13
+ spec/dummy/db/*.sqlite3*
14
+ spec/dummy/public/assets
21
15
  spec/dummy/config/locales/**/*
16
+ .rvmrc
22
17
  /coverage/
23
18
  *.gem
24
- /.ruby-version
25
19
  /index/
26
20
  /.bundle/
27
21
  .rbenv-version
@@ -0,0 +1,7 @@
1
+ project:
2
+ after:
3
+ - apt-get -y install imagemagick
4
+ stage:
5
+ before: cd spec/dummy
6
+ database: bin/rake db:setup RAILS_ENV=production
7
+ run: bin/rails server -e production
data/Gemfile CHANGED
@@ -6,20 +6,16 @@ if ENV['RAILS_VERSION']
6
6
  gem 'rails', "~> #{ENV['RAILS_VERSION']}"
7
7
  end
8
8
 
9
- # Code coverage plattform
10
- gem 'coveralls', require: false
11
-
12
- group :test do
13
- gem 'sqlite3' if ENV['DB'].nil? || ENV['DB'] == 'sqlite'
14
- gem 'mysql2' if ENV['DB'] == 'mysql'
15
- gem 'pg' if ENV['DB'] == 'postgresql'
16
- unless ENV['CI']
17
- gem 'launchy'
18
- end
19
- end
9
+ gem 'sqlite3' if ENV['DB'].nil? || ENV['DB'] == 'sqlite'
10
+ gem 'mysql2' if ENV['DB'] == 'mysql'
11
+ gem 'pg' if ENV['DB'] == 'postgresql'
20
12
 
21
13
  group :development, :test do
14
+ gem 'jasmine-rails', github: 'searls/jasmine-rails'
15
+ gem 'jasmine-jquery-rails', github: 'travisjeffery/jasmine-jquery-rails'
16
+ gem 'coveralls', require: false
22
17
  unless ENV['CI']
18
+ gem 'launchy'
23
19
  gem 'annotate'
24
20
  gem 'bumpy'
25
21
  gem 'yard'
@@ -28,6 +24,10 @@ group :development, :test do
28
24
  gem 'spring'
29
25
  gem 'spring-commands-rspec'
30
26
  end
31
- gem 'jasmine-rails', github: 'searls/jasmine-rails'
32
- gem 'jasmine-jquery-rails', github: 'travisjeffery/jasmine-jquery-rails'
27
+ end
28
+
29
+ # We need this if we want to start the dummy app in production, ie on Teatro.io
30
+ group :production do
31
+ gem 'uglifier', '>= 1.0.3'
32
+ gem 'therubyracer'
33
33
  end
data/README.md CHANGED
@@ -261,11 +261,18 @@ Please take a look into Alchemys [Capistrano receipt](https://github.com/Alchemy
261
261
 
262
262
  If you want to contribute to Alchemy ([and we encourage you to do so](https://github.com/AlchemyCMS/alchemy_cms/blob/master/CONTRIBUTING.md)) we have a strong test suite that helps you to not break anything.
263
263
 
264
+ ### Preparation
265
+
266
+ First of all you need to clone your fork to your local development machine. Then you need to install the dependencies with bundler.
267
+
268
+ ```shell
269
+ $ bundle install
270
+ ```
271
+
264
272
  To prepare the tests of your Alchemy fork please make sure to run the preparation task:
265
273
 
266
274
  ```shell
267
- # ~/your/local/alchemy_cms
268
- $ bin/rake alchemy:spec:prepare
275
+ $ bundle exec rake alchemy:spec:prepare
269
276
  ```
270
277
 
271
278
  to set up the database for testing.
@@ -276,7 +283,7 @@ to set up the database for testing.
276
283
  $ bundle exec rspec
277
284
  ```
278
285
 
279
- **Alternatively** you can just run:
286
+ **Alternatively** you can just run*:
280
287
 
281
288
  ```shell
282
289
  $ bundle exec rake
@@ -284,6 +291,16 @@ $ bundle exec rake
284
291
 
285
292
  *) This default task executes the database preparations and runs all defined test cases.
286
293
 
294
+ ### Start the dummy app
295
+
296
+ You can even start the dummy app and use it to manually test your changes with:
297
+
298
+ ```shell
299
+ $ cd spec/dummy
300
+ $ bin/rake db:setup
301
+ $ bin/rails s
302
+ ```
303
+
287
304
  **A note about RSpec version:**
288
305
 
289
306
  Alchemy specs are written **in RSpec 3**. Please **do not use deprecated RSpec 2.x syntax**. Thanks
@@ -22,8 +22,8 @@ Alchemy.translations =
22
22
  'File is too large': 'Datei ist zu groß.'
23
23
  'File is too small': 'Datei ist zu klein.'
24
24
  'File type not allowed': 'Dateityp nicht erlaubt.'
25
- 'Maximum number of files exceeded': 'Maximale Anzahl an Dateien erreicht.'
26
- 'Uploaded bytes exceed file size': 'Maximal erlaubte Dateigröße erreicht.'
25
+ 'Maximum number of files exceeded': 'Maximale Anzahl gleichzeitig erlaubter Datei-Uploads erreicht.'
26
+ 'Uploaded bytes exceed file size': 'Maximale Größe der erlaubten Dateigröße erreicht.'
27
27
 
28
28
  # English
29
29
  en:
@@ -109,7 +109,7 @@ Alchemy.translations =
109
109
  'File type not allowed': 'Этот тип файла не разрешен'
110
110
  'Maximum number of files exceeded': 'Исчерпано максимальное количество файлов.'
111
111
  'Uploaded bytes exceed file size': 'Превышен максимальный размер файла'
112
-
112
+
113
113
  # Spanish
114
114
  es:
115
115
  allowed_chars: 'de %{count} caracteres'
@@ -25,6 +25,7 @@ Alchemy.Uploader = (settings) ->
25
25
  $("#fileupload").fileupload
26
26
  dropZone: '#dropbox'
27
27
  dataType: 'json'
28
+ filesContainer: $('#uploadProgressContainer')
28
29
  acceptFileTypes: new RegExp("(.|/)(#{file_types})", "i")
29
30
  maxNumberOfFiles: settings.file_upload_limit
30
31
  maxFileSize: settings.file_size_limit * 1000000
@@ -32,6 +33,10 @@ Alchemy.Uploader = (settings) ->
32
33
  form_data = form.serializeArray()
33
34
  $.merge(form_data, settings.post_params)
34
35
  form_data
36
+ getNumberOfFiles: ->
37
+ @filesContainer
38
+ .children()
39
+ .not('.progressBarInProgress').length - 1
35
40
  add: (e, data) ->
36
41
  $this = $(this)
37
42
  data.context = new Alchemy.FileProgress(data.files[0])
@@ -3,24 +3,44 @@ module Alchemy
3
3
  class EssenceFilesController < Alchemy::Admin::BaseController
4
4
  authorize_resource class: Alchemy::EssenceFile
5
5
 
6
+ before_filter :load_essence_file, only: [:edit, :update]
7
+
6
8
  helper "Alchemy::Admin::Contents"
7
9
 
8
10
  def edit
9
- @essence_file = EssenceFile.find(params[:id])
10
11
  @content = @essence_file.content
11
12
  @options = options_from_params
12
13
  end
13
14
 
14
15
  def update
15
- @essence_file = EssenceFile.find(params[:id])
16
- @essence_file.update(params[:essence_file])
16
+ @essence_file.update(essence_file_params)
17
17
  end
18
18
 
19
+ # Assigns file, but does not saves it.
20
+ #
21
+ # When the user saves the element the content gets updated as well.
22
+ #
19
23
  def assign
20
24
  @content = Content.find_by(id: params[:content_id])
21
25
  @attachment = Attachment.find_by(id: params[:attachment_id])
22
26
  @content.essence.attachment = @attachment
23
27
  @options = options_from_params
28
+
29
+ # We need to update timestamp here because we don't save yet,
30
+ # but the cache needs to be get invalid.
31
+ # And we don't user @content.touch here, because that updates
32
+ # also the element and page timestamps what we don't want yet.
33
+ @content.update_column(:updated_at, Time.now)
34
+ end
35
+
36
+ private
37
+
38
+ def essence_file_params
39
+ params.require(:essence_file).permit(:title, :css_class)
40
+ end
41
+
42
+ def load_essence_file
43
+ @essence_file = EssenceFile.find(params[:id])
24
44
  end
25
45
  end
26
46
  end
@@ -36,15 +36,20 @@ module Alchemy
36
36
 
37
37
  # Assigns picture, but does not saves it.
38
38
  #
39
- # When the user press save on the element, it gets saved.
39
+ # When the user saves the element the content gets updated as well.
40
40
  #
41
41
  def assign
42
42
  @picture = Picture.find_by(id: params[:picture_id])
43
43
  @content.essence.picture = @picture
44
- @content.touch # We need to touch manually because its not saved yet.
45
44
  @element = @content.element
46
45
  @dragable = @options[:grouped]
47
46
  @options = @options.merge(dragable: @dragable)
47
+
48
+ # We need to update timestamp here because we don't save yet,
49
+ # but the cache needs to be get invalid.
50
+ # And we don't user @content.touch here, because that updates
51
+ # also the element and page timestamps what we don't want yet.
52
+ @content.update_column(:updated_at, Time.now)
48
53
  end
49
54
 
50
55
  def destroy
@@ -12,10 +12,7 @@ module Alchemy
12
12
 
13
13
  def index
14
14
  @size = params[:size].present? ? params[:size] : 'medium'
15
- @pictures = Picture.all
16
- @pictures = @pictures.tagged_with(params[:tagged_with]) if params[:tagged_with].present?
17
- @pictures = @pictures.filtered_by(params[:filter]) if params[:filter]
18
- @pictures = @pictures.find_paginated(params, pictures_per_page_for_size(@size))
15
+ @pictures = Picture.find_paginated(params, pictures_per_page_for_size(@size))
19
16
  if in_overlay?
20
17
  archive_overlay
21
18
  end
@@ -96,11 +96,13 @@ module Alchemy
96
96
  protected
97
97
 
98
98
  def permission_denied(exception = nil)
99
- Rails.logger.debug <<-WARN
99
+ if exception
100
+ Rails.logger.debug <<-WARN
100
101
 
101
102
  /!\\ Failed to permit #{exception.action} on #{exception.subject.inspect} for:
102
103
  #{current_alchemy_user.inspect}
103
104
  WARN
105
+ end
104
106
  if current_alchemy_user
105
107
  handle_redirect_for_user
106
108
  else
@@ -184,7 +184,9 @@ module Alchemy
184
184
  # @returns Boolean
185
185
  #
186
186
  def cache_page?
187
- return false unless @page && Alchemy::Config.get(:cache_pages)
187
+ return false if @page.nil? ||
188
+ !Rails.application.config.action_controller.perform_caching ||
189
+ !Alchemy::Config.get(:cache_pages)
188
190
  page_layout = PageLayout.get(@page.page_layout)
189
191
  page_layout['cache'] != false && page_layout['searchresults'] != true
190
192
  end
@@ -37,9 +37,6 @@ module Alchemy
37
37
  "element_id = #{element_id || 'null'} AND essence_type = '#{essence_type}'"
38
38
  end
39
39
 
40
- # Validations
41
- validates :position, uniqueness: {scope: [:element_id, :essence_type]}
42
-
43
40
  # Essence scopes
44
41
  scope :essence_booleans, -> { where(essence_type: "Alchemy::EssenceBoolean") }
45
42
  scope :essence_dates, -> { where(essence_type: "Alchemy::EssenceDate") }
@@ -29,7 +29,7 @@ module Alchemy
29
29
  acts_as_taggable
30
30
 
31
31
  # All Elements inside a cell are a list. All Elements not in cell are in the cell_id.nil list.
32
- acts_as_list :scope => [:page_id, :cell_id]
32
+ acts_as_list scope: [:page_id, :cell_id]
33
33
  stampable stamper_class_name: Alchemy.user_class_name
34
34
 
35
35
  has_many :contents, -> { order(:position) }, dependent: :destroy
@@ -39,7 +39,6 @@ module Alchemy
39
39
  class_name: 'Alchemy::Page',
40
40
  join_table: 'alchemy_elements_alchemy_pages'
41
41
 
42
- validates_uniqueness_of :position, :scope => [:page_id, :cell_id], :if => lambda { |e| e.position != nil }
43
42
  validates_presence_of :name, :on => :create
44
43
  validates_format_of :name, :on => :create, :with => /\A[a-z0-9_-]+\z/
45
44
 
@@ -21,7 +21,9 @@ module Alchemy
21
21
  # All pages locked by given user
22
22
  #
23
23
  scope :all_locked_by, ->(user) {
24
- all_locked.where(locked_by: user.id)
24
+ if user.class.respond_to? :primary_key
25
+ all_locked.where(locked_by: user.send(user.class.primary_key))
26
+ end
25
27
  }
26
28
 
27
29
  # All not locked pages
@@ -5,19 +5,19 @@ module Alchemy
5
5
  # Returns the creator of this page.
6
6
  #
7
7
  def creator
8
- Alchemy.user_class.try(:find_by, {id: creator_id})
8
+ get_page_user(creator_id)
9
9
  end
10
10
 
11
11
  # Returns the last updater of this page.
12
12
  #
13
13
  def updater
14
- Alchemy.user_class.try(:find_by, {id: updater_id})
14
+ get_page_user(updater_id)
15
15
  end
16
16
 
17
17
  # Returns the user currently editing this page.
18
18
  #
19
19
  def locker
20
- Alchemy.user_class.try(:find_by, {id: locked_by})
20
+ get_page_user(locked_by)
21
21
  end
22
22
 
23
23
  # Returns the name of the creator of this page.
@@ -47,5 +47,12 @@ module Alchemy
47
47
  (locker && locker.try(:name)) || I18n.t('unknown')
48
48
  end
49
49
 
50
+ private
51
+
52
+ def get_page_user(id)
53
+ if Alchemy.user_class.respond_to? :primary_key
54
+ Alchemy.user_class.try(:find_by, {Alchemy.user_class.primary_key => id})
55
+ end
56
+ end
50
57
  end
51
58
  end
@@ -62,10 +62,21 @@ module Alchemy
62
62
 
63
63
  stampable stamper_class_name: Alchemy.user_class_name
64
64
 
65
- scope :named, ->(name) { where("name LIKE ?", "%#{name}%") }
66
- scope :recent, -> { where("#{self.table_name}.created_at > ?", Time.now - 24.hours).order(:created_at) }
67
- scope :deletable, -> { where('alchemy_pictures.id NOT IN (SELECT picture_id FROM alchemy_essence_pictures)') }
68
- scope :without_tag, -> { where("cached_tag_list IS NULL OR cached_tag_list = ''") }
65
+ scope :named, ->(name) {
66
+ where("#{self.table_name}.name LIKE ?", "%#{name}%")
67
+ }
68
+
69
+ scope :recent, -> {
70
+ where("#{self.table_name}.created_at > ?", Time.now - 24.hours).order(:created_at)
71
+ }
72
+
73
+ scope :deletable, -> {
74
+ where("#{self.table_name}.id NOT IN (SELECT picture_id FROM alchemy_essence_pictures)")
75
+ }
76
+
77
+ scope :without_tag, -> {
78
+ where("#{self.table_name}.cached_tag_list IS NULL OR #{self.table_name}.cached_tag_list = ''")
79
+ }
69
80
 
70
81
  after_update :touch_contents
71
82
 
@@ -73,8 +84,21 @@ module Alchemy
73
84
 
74
85
  class << self
75
86
 
87
+ # Returns filtered, paginated and ordered picture collection.
76
88
  def find_paginated(params, per_page)
77
- Picture.named(params[:query]).page(params[:page] || 1).per(per_page).order(:name)
89
+ @pictures = Picture.all
90
+
91
+ if params[:tagged_with].present?
92
+ @pictures = @pictures.tagged_with(params[:tagged_with])
93
+ end
94
+ if params[:filter].present?
95
+ @pictures = @pictures.filtered_by(params[:filter])
96
+ end
97
+ if params[:query].present?
98
+ @pictures = @pictures.named(params[:query])
99
+ end
100
+
101
+ @pictures.page(params[:page] || 1).per(per_page).order(:name)
78
102
  end
79
103
 
80
104
  def last_upload
@@ -92,7 +116,6 @@ module Alchemy
92
116
  all
93
117
  end
94
118
  end
95
-
96
119
  end
97
120
 
98
121
  # Instance methods
@@ -173,17 +173,24 @@ module Alchemy
173
173
  # This function takes a target and a base dimensions hash and returns
174
174
  # the dimensions of the image when the base dimensions hash fills
175
175
  # the target.
176
+ #
176
177
  # Aspect ratio will be preserved.
177
178
  #
178
179
  def size_when_fitting(target, dimensions = get_base_dimensions)
179
- zoom_x = dimensions[:width].to_f / target[:width]
180
- zoom_y = dimensions[:height].to_f / target[:height]
180
+ zoom = [
181
+ dimensions[:width].to_f / target[:width],
182
+ dimensions[:height].to_f / target[:height]
183
+ ].max
184
+
185
+ if zoom == 0.0
186
+ width = target[:width]
187
+ height = target[:height]
188
+ else
189
+ width = (dimensions[:width] / zoom).round
190
+ height = (dimensions[:height] / zoom).round
191
+ end
181
192
 
182
- zoom = [zoom_x, zoom_y].max
183
- {
184
- width: (dimensions[:width] / zoom).round.to_i,
185
- height: (dimensions[:height] / zoom).round.to_i
186
- }
193
+ {width: width.to_i, height: height.to_i}
187
194
  end
188
195
 
189
196
  # Given a point as a Hash with :x and :y, and a mask with