locomotive_cms 2.3.1 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/locomotive/models/content_entry.js.coffee +4 -0
  3. data/app/assets/javascripts/locomotive/views/content_assets/picker_view.js.coffee +0 -2
  4. data/app/assets/javascripts/locomotive/views/content_entries/_form_view.js.coffee +1 -1
  5. data/app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee +7 -0
  6. data/app/assets/javascripts/locomotive/views/inline_editor/application_view.js.coffee +1 -1
  7. data/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee +2 -1
  8. data/app/assets/javascripts/locomotive/views/shared/fields/_relationship_view.js.coffee +45 -0
  9. data/app/assets/javascripts/locomotive/views/shared/fields/belongs_to_view.js.coffee +4 -20
  10. data/app/assets/javascripts/locomotive/views/shared/fields/many_to_many_view.js.coffee +53 -42
  11. data/app/assets/javascripts/locomotive/views/snippets/_form_view.js.coffee +0 -1
  12. data/app/assets/stylesheets/locomotive/backoffice/application.css.scss +31 -0
  13. data/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.scss +19 -0
  14. data/app/controllers/locomotive/api/accounts_controller.rb +6 -0
  15. data/app/controllers/locomotive/content_entries_controller.rb +1 -1
  16. data/app/helpers/locomotive/content_entries_helper.rb +22 -0
  17. data/app/models/locomotive/content_entry.rb +16 -47
  18. data/app/models/locomotive/content_type.rb +19 -3
  19. data/app/models/locomotive/editable_text.rb +7 -1
  20. data/app/models/locomotive/extensions/content_entry/localized.rb +62 -0
  21. data/app/models/locomotive/extensions/page/tree.rb +5 -0
  22. data/app/models/locomotive/extensions/shared/slug.rb +33 -0
  23. data/app/models/locomotive/page.rb +4 -11
  24. data/app/models/locomotive/snippet.rb +6 -7
  25. data/app/models/locomotive/translation.rb +2 -2
  26. data/app/presenters/locomotive/account_presenter.rb +1 -1
  27. data/app/views/locomotive/content_entries/_list.html.haml +3 -1
  28. data/app/views/locomotive/custom_fields/types/_boolean.html.haml +1 -1
  29. data/app/views/locomotive/custom_fields/types/_date.html.haml +1 -1
  30. data/app/views/locomotive/custom_fields/types/_date_time.html.haml +1 -1
  31. data/app/views/locomotive/custom_fields/types/_email.html.haml +1 -1
  32. data/app/views/locomotive/custom_fields/types/_file.html.haml +1 -1
  33. data/app/views/locomotive/custom_fields/types/_float.html.haml +1 -1
  34. data/app/views/locomotive/custom_fields/types/_integer.html.haml +1 -1
  35. data/app/views/locomotive/custom_fields/types/_many_to_many.html.haml +1 -5
  36. data/app/views/locomotive/custom_fields/types/_select.html.haml +1 -1
  37. data/app/views/locomotive/custom_fields/types/_string.html.haml +1 -1
  38. data/app/views/locomotive/custom_fields/types/_tags.html.haml +1 -1
  39. data/app/views/locomotive/custom_fields/types/_text.html.haml +1 -1
  40. data/config/locales/admin_ui.de.yml +4 -0
  41. data/config/locales/default.zh-CN.yml +162 -52
  42. data/config/locales/devise.bg.yml +1 -1
  43. data/config/locales/devise.cs.yml +1 -1
  44. data/config/locales/devise.de.yml +1 -1
  45. data/config/locales/devise.en.yml +1 -1
  46. data/config/locales/devise.es.yml +2 -2
  47. data/config/locales/devise.et.yml +1 -1
  48. data/config/locales/devise.fr.yml +1 -1
  49. data/config/locales/devise.it.yml +1 -1
  50. data/config/locales/devise.ja.yml +1 -1
  51. data/config/locales/devise.nb.yml +1 -1
  52. data/config/locales/devise.nl.yml +2 -2
  53. data/config/locales/devise.pl.yml +1 -1
  54. data/config/locales/devise.pt-BR.yml +2 -2
  55. data/config/locales/devise.ru.yml +1 -1
  56. data/config/locales/devise.zh-CN.yml +1 -1
  57. data/config/routes.rb +2 -2
  58. data/features/api/accounts.feature +19 -1
  59. data/features/api/authorization/accounts.feature +52 -12
  60. data/features/backoffice/content_types/has_many.feature +3 -3
  61. data/features/backoffice/content_types/integer.feature +4 -4
  62. data/features/backoffice/content_types/many_to_many.feature +1 -1
  63. data/features/backoffice/my_account.feature +1 -0
  64. data/features/step_definitions/web_steps.rb +7 -0
  65. data/lib/locomotive/action_controller/public_responder.rb +24 -1
  66. data/lib/locomotive/core_ext.rb +8 -2
  67. data/lib/locomotive/devise.rb +21 -0
  68. data/lib/locomotive/httparty/webservice.rb +17 -9
  69. data/lib/locomotive/liquid.rb +1 -0
  70. data/lib/locomotive/liquid/tags/consume.rb +12 -11
  71. data/lib/locomotive/liquid/tags/link_to.rb +6 -60
  72. data/lib/locomotive/liquid/tags/path_helper.rb +82 -0
  73. data/lib/locomotive/liquid/tags/path_to.rb +21 -0
  74. data/lib/locomotive/liquid/tags/snippet.rb +1 -1
  75. data/lib/locomotive/render.rb +2 -1
  76. data/lib/locomotive/version.rb +1 -1
  77. data/spec/lib/locomotive/liquid/tags/consume_spec.rb +12 -0
  78. data/spec/lib/locomotive/liquid/tags/path_to_spec.rb +111 -0
  79. data/spec/models/locomotive/content_entry_spec.rb +2 -2
  80. data/spec/models/locomotive/snippet_spec.rb +21 -0
  81. metadata +13 -6
@@ -45,7 +45,7 @@ pl:
45
45
  unlock_instructions: 'Instrukcje odblokowywania'
46
46
 
47
47
 
48
- locomotive_account:
48
+ locomotive:
49
49
  devise_mailer:
50
50
  common:
51
51
  hello: Witaj
@@ -44,7 +44,7 @@ pt-BR:
44
44
  unlock_instructions: 'Instruções de desbloqueio'
45
45
 
46
46
 
47
- locomotive_account:
47
+ locomotive:
48
48
  devise_mailer:
49
49
  common:
50
50
  hello: Olá
@@ -59,4 +59,4 @@ pt-BR:
59
59
  unlock_instructions:
60
60
  locked_account_message: "Sua conta foi bloqueada devido a excessiva tentativa de logins sem sucesso."
61
61
  unlock_account_instruction: "Clique no link abaixo para desbloquear sua conta:"
62
- unlock_my_account: "Desbloquear minha conta"
62
+ unlock_my_account: "Desbloquear minha conta"
@@ -45,7 +45,7 @@ ru:
45
45
  unlock_instructions: 'Инструкции по разблокированию'
46
46
 
47
47
 
48
- locomotive_account:
48
+ locomotive:
49
49
  devise_mailer:
50
50
  common:
51
51
  hello: Здравствуйте
@@ -45,7 +45,7 @@ zh-CN:
45
45
  unlock_instructions: '解锁说明'
46
46
 
47
47
 
48
- locomotive_account:
48
+ locomotive:
49
49
  devise_mailer:
50
50
  common:
51
51
  hello: 你好
@@ -73,10 +73,10 @@ Rails.application.routes.draw do
73
73
 
74
74
  resource :my_account, controller: 'my_account', only: :show
75
75
 
76
- resources :accounts, only: [:index, :show, :create, :destroy]
77
-
78
76
  with_options only: [:index, :show, :create, :update, :destroy] do |api|
79
77
 
78
+ api.resources :accounts
79
+
80
80
  api.resources :sites
81
81
 
82
82
  api.resources :pages
@@ -22,4 +22,22 @@ Feature: Accounts
22
22
  When I do an API GET request to accounts.json
23
23
  Then the JSON response should be an array
24
24
  And the JSON response should have 2 entries
25
- And the JSON response at "1/name" should be "New User"
25
+ And the JSON response at "1/name" should be "New User"
26
+
27
+ Scenario: Updating a account
28
+ Given I have an "admin" API token
29
+ And I have accounts:
30
+ | name | email | id |
31
+ | User | new-user1@a.com | 4f832c2cb0d86d3f42fffffc |
32
+
33
+ When I do an API PUT to accounts/4f832c2cb0d86d3f42fffffc.json with:
34
+ """
35
+ {
36
+ "account": {
37
+ "name": "Modified User"
38
+ }
39
+ }
40
+ """
41
+ When I do an API GET request to accounts/4f832c2cb0d86d3f42fffffc.json
42
+ Then the JSON response should be an hash
43
+ And the JSON response at "name" should be "Modified User"
@@ -34,9 +34,9 @@ Feature: Accounts
34
34
  Given I have an "author" API token
35
35
  When I do an API GET request to accounts.json
36
36
  Then an access denied error should occur
37
-
37
+
38
38
  # showing account
39
-
39
+
40
40
  Scenario: Accessing account as an Admin
41
41
  Given I have an "admin" API token
42
42
  When I do an API GET request to accounts/4f832c2cb0d86d3f42fffffc.json
@@ -45,19 +45,19 @@ Feature: Accounts
45
45
  Then the JSON response at "email" should be "new-user2@a.com"
46
46
  When I do an API GET request to accounts/4f832c2cb0d86d3f42fffffe.json
47
47
  Then the JSON response at "email" should be "new-user3@a.com"
48
-
48
+
49
49
  Scenario: Accessing account as a Designer
50
50
  Given I have a "designer" API token
51
51
  When I do an API GET request to accounts/4f832c2cb0d86d3f42fffffc.json
52
52
  Then an access denied error should occur
53
-
53
+
54
54
  Scenario: Accessing account as an Author
55
55
  Given I have an "author" API token
56
56
  When I do an API GET request to accounts/4f832c2cb0d86d3f42fffffc.json
57
57
  Then an access denied error should occur
58
-
58
+
59
59
  # create account
60
-
60
+
61
61
  Scenario: Creating new account as an Admin
62
62
  Given I have an "admin" API token
63
63
  When I do an API POST to accounts.json with:
@@ -73,7 +73,7 @@ Feature: Accounts
73
73
  When I do an API GET request to accounts.json
74
74
  Then the JSON response should be an array
75
75
  And the JSON response should have 7 entries
76
-
76
+
77
77
  Scenario: Creating new account as a Designer
78
78
  Given I have a "designer" API token
79
79
  When I do an API POST to accounts.json with:
@@ -87,7 +87,7 @@ Feature: Accounts
87
87
  }
88
88
  """
89
89
  Then an access denied error should occur
90
-
90
+
91
91
  Scenario: Creating new account as an Author
92
92
  Given I have an "author" API token
93
93
  When I do an API POST to accounts.json with:
@@ -101,9 +101,49 @@ Feature: Accounts
101
101
  }
102
102
  """
103
103
  Then an access denied error should occur
104
-
104
+
105
+ # update account
106
+
107
+ Scenario: Creating new account as an Admin
108
+ Given I have an "admin" API token
109
+ When I do an API PUT to accounts/4f832c2cb0d86d3f42fffffc.json with:
110
+ """
111
+ {
112
+ "account": {
113
+ "name": "Modified User"
114
+ }
115
+ }
116
+ """
117
+ When I do an API GET request to accounts/4f832c2cb0d86d3f42fffffc.json
118
+ Then the JSON response should be an hash
119
+ And the JSON response at "name" should be "Modified User"
120
+
121
+ Scenario: Creating new account as a Designer
122
+ Given I have a "designer" API token
123
+ When I do an API PUT to accounts/4f832c2cb0d86d3f42fffffc.json with:
124
+ """
125
+ {
126
+ "account": {
127
+ "name": "Modified User"
128
+ }
129
+ }
130
+ """
131
+ Then an access denied error should occur
132
+
133
+ Scenario: Creating new account as an Author
134
+ Given I have an "author" API token
135
+ When I do an API PUT to accounts/4f832c2cb0d86d3f42fffffc.json with:
136
+ """
137
+ {
138
+ "account": {
139
+ "name": "Modified User"
140
+ }
141
+ }
142
+ """
143
+ Then an access denied error should occur
144
+
105
145
  # destroy account
106
-
146
+
107
147
  Scenario: Destroying account as an Admin
108
148
  Given I have an "admin" API token
109
149
  When I do an API GET request to accounts.json
@@ -113,12 +153,12 @@ Feature: Accounts
113
153
  When I do an API GET request to accounts.json
114
154
  Then the JSON response should be an array
115
155
  And the JSON response should have 5 entries
116
-
156
+
117
157
  Scenario: Destroying account as a Designer
118
158
  Given I have a "designer" API token
119
159
  When I do an API DELETE to accounts/4f832c2cb0d86d3f42fffffe.json
120
160
  Then an access denied error should occur
121
-
161
+
122
162
  Scenario: Deleting account as an Author
123
163
  Given I have a "author" API token
124
164
  When I do an API DELETE to accounts/4f832c2cb0d86d3f42fffffe.json
@@ -37,7 +37,7 @@ Scenario: I view a client without any projects
37
37
  Scenario: I add a project to a client
38
38
  When I go to the list of "Projects"
39
39
  And I choose "Fun project" in the list
40
- And I select "Alpha, Inc" from "Client"
40
+ And I select2 "Alpha, Inc" from "content_entry_client_id"
41
41
  And I press "Save"
42
42
  Then I should see "Entry was successfully updated."
43
43
  When I go to the list of "Clients"
@@ -73,8 +73,8 @@ Scenario: with_scope with label value
73
73
  Then the rendered output should look like:
74
74
  """
75
75
  <hr>
76
-
76
+
77
77
  - Fun project<br>
78
-
78
+
79
79
  <hr>
80
80
  """
@@ -8,8 +8,8 @@ Background:
8
8
  And I have a custom model named "ToDos" with
9
9
  | label | type | required |
10
10
  | Task | string | true |
11
- | priority | integer | true |
12
-
11
+ | Priority | integer | true |
12
+
13
13
  And I am an authenticated user
14
14
 
15
15
  @javascript
@@ -17,10 +17,10 @@ Scenario:
17
17
  And I go to the list of "ToDos"
18
18
  And I follow "new entry" within the main content
19
19
  And I fill in "Task" with "Buy milk"
20
- And I fill in "priority" with "one"
20
+ And I fill in "Priority" with "one"
21
21
  And I press "Create"
22
22
  Then I should see "Entry was not created."
23
23
 
24
- When I fill in "priority" with "1"
24
+ When I fill in "Priority" with "1"
25
25
  And I press "Create"
26
26
  Then I should see "Entry was successfully created."
@@ -31,7 +31,7 @@ Scenario: I attach projects to an article
31
31
  When I go to the list of "Articles"
32
32
  And I choose "Hello world" in the list
33
33
  Then I should see "The list is empty. Add an entry from the select box below."
34
- When I select "My sexy project" from "entry"
34
+ When I select2 "My sexy project" from "entry"
35
35
  And I follow "+ add"
36
36
  Then I should see "My sexy project" within the list of entries
37
37
  And "p.empty" should not be visible within the list of entries
@@ -17,5 +17,6 @@ Scenario: Viewing my API key
17
17
  Scenario: Changing my API key
18
18
  When I go to the account settings
19
19
  And I click on the "Credentials" folder
20
+ And I should see "d49cd50f6f0d2b163f48fc73cb249f0244c37074"
20
21
  And I press "Regenerate"
21
22
  Then I should not see "d49cd50f6f0d2b163f48fc73cb249f0244c37074"
@@ -215,4 +215,11 @@ end
215
215
 
216
216
  Then(/^I click on "(.*?)"$/) do |selector|
217
217
  find(selector).click
218
+ end
219
+
220
+ When(/^I select2 "(.*?)" from "(.*?)"$/) do |text, from|
221
+ page.find("#s2id_#{from} a").click
222
+ find(:xpath, "//body").find("input.select2-input").set(text)
223
+ page.execute_script(%|$("input.select2-input:visible").keyup();|)
224
+ find(:xpath, '//body').find('ul.select2-results li', text: text).click
218
225
  end
@@ -14,7 +14,7 @@ module Locomotive
14
14
  redirect_to navigation_location
15
15
  else
16
16
  # render the locomotive page
17
- self.controller.send :render_locomotive_page, navigation_location, {
17
+ self.controller.send :render_locomotive_page, navigation_location_for_locomotive, {
18
18
  entry.content_type.slug.singularize => entry.to_presenter(include_errors: true).as_json
19
19
  }
20
20
  end
@@ -28,6 +28,29 @@ module Locomotive
28
28
  end
29
29
  end
30
30
 
31
+ def navigation_location_for_locomotive
32
+ locale, location = self.extract_locale_and_location
33
+
34
+ if locale
35
+ ::I18n.locale = ::Mongoid::Fields::I18n.locale = locale
36
+ location
37
+ else
38
+ navigation_location
39
+ end
40
+ end
41
+
42
+ protected
43
+
44
+ def extract_locale_and_location
45
+ locales = self.controller.send(:current_site).locales.join('|')
46
+
47
+ if navigation_location =~ /\/(#{locales})+\/(.+)/
48
+ [$1, $2]
49
+ else
50
+ nil
51
+ end
52
+ end
53
+
31
54
  end
32
55
  end
33
56
  end
@@ -5,8 +5,14 @@
5
5
  class String #:nodoc
6
6
 
7
7
  def permalink(underscore = false)
8
- permalink = self.to_url
9
- underscore ? permalink.underscore : permalink
8
+ # if the slug includes one "_" at least, we consider that the "_" is used instead of "-".
9
+ _permalink = if !self.index('_').nil?
10
+ self.to_url(replace_whitespace_with: '_')
11
+ else
12
+ self.to_url
13
+ end
14
+
15
+ underscore ? _permalink.underscore : _permalink
10
16
  end
11
17
 
12
18
  def permalink!(underscore = false)
@@ -1 +1,22 @@
1
1
  # patches for devise here
2
+
3
+ # http://blog.plataformatec.com.br/2013/11/e-mail-enumeration-in-devise-in-paranoid-mode/
4
+ # Put this inside config/initializers/devise_paranoid_fix.rb
5
+ require 'devise/strategies/database_authenticatable'
6
+
7
+ Devise::Strategies::DatabaseAuthenticatable.class_eval do
8
+ def authenticate!
9
+ resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
10
+ encrypted = false
11
+
12
+ return fail(:invalid) unless resource
13
+
14
+ if validate(resource){ encrypted = true; resource.valid_password?(password) }
15
+ resource.after_database_authentication
16
+ success!(resource)
17
+ end
18
+
19
+ mapping.to.new.password = password if !encrypted && Devise.paranoid
20
+ fail(:not_found_in_database) unless resource
21
+ end
22
+ end
@@ -7,21 +7,30 @@ module Locomotive
7
7
  include ::HTTParty
8
8
 
9
9
  def self.consume(url, options = {})
10
- url = HTTParty.normalize_base_uri(url)
11
-
12
- uri = URI.parse(url)
13
- options[:base_uri] = "#{uri.scheme}://#{uri.host}"
14
- options[:base_uri] += ":#{uri.port}" if uri.port != 80
15
- path = uri.request_uri
10
+ options[:base_uri], path = self.extract_base_uri_and_path(url)
16
11
 
17
12
  options.delete(:format) if options[:format] == 'default'
18
13
 
14
+ # auth ?
19
15
  username, password = options.delete(:username), options.delete(:password)
20
16
  options[:basic_auth] = { username: username, password: password } if username
21
17
 
22
- path ||= '/'
18
+ self.perform_request(path, options)
19
+ end
20
+
21
+ def self.extract_base_uri_and_path(url)
22
+ url = HTTParty.normalize_base_uri(url)
23
23
 
24
- # puts "[WebService] consuming #{path}, #{options.inspect}"
24
+ uri = URI.parse(url)
25
+ path = uri.request_uri || '/'
26
+ base_uri = "#{uri.scheme}://#{uri.host}"
27
+ base_uri += ":#{uri.port}" if uri.port != 80
28
+
29
+ [base_uri, path]
30
+ end
31
+
32
+ def self.perform_request(path, options)
33
+ # [DEBUG] puts "[WebService] consuming #{path}, #{options.inspect}"
25
34
 
26
35
  response = self.get(path, options)
27
36
  parsed_response = response.parsed_response
@@ -35,7 +44,6 @@ module Locomotive
35
44
  else
36
45
  nil
37
46
  end
38
-
39
47
  end
40
48
 
41
49
  end
@@ -2,6 +2,7 @@ require 'locomotive/liquid/drops/base'
2
2
  require 'locomotive/liquid/drops/proxy_collection'
3
3
  require 'locomotive/liquid/filters/base'
4
4
  require 'locomotive/liquid/tags/hybrid'
5
+ require 'locomotive/liquid/tags/path_helper'
5
6
 
6
7
  %w{. tags drops filters}.each do |dir|
7
8
  Dir[File.join(File.dirname(__FILE__), 'liquid', dir, '*.rb')].each { |lib| require lib }
@@ -25,28 +25,25 @@ module Locomotive
25
25
  raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]")
26
26
  end
27
27
 
28
- @cache_key = Digest::SHA1.hexdigest(@target)
29
28
  @local_cache_key = self.hash
30
29
 
31
30
  super
32
31
  end
33
32
 
34
33
  def render(context)
35
- if @is_var
36
- @url = context[@url]
34
+ if instance_variable_defined? :@variable_name
35
+ @url = context[@variable_name]
37
36
  end
38
37
  render_all_and_cache_it(context)
39
38
  end
40
39
 
41
40
  protected
42
41
 
43
- def prepare_url(url)
44
- @url = url
45
- @is_var = false
46
- if @url.match(::Liquid::QuotedString)
47
- @url.gsub!(/['"]/, '')
48
- elsif @url.match(::Liquid::VariableSignature)
49
- @is_var = true
42
+ def prepare_url(token)
43
+ if token.match(::Liquid::QuotedString)
44
+ @url = token.gsub(/['"]/, '')
45
+ elsif token.match(::Liquid::VariableSignature)
46
+ @variable_name = token
50
47
  else
51
48
  raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]")
52
49
  end
@@ -61,6 +58,10 @@ module Locomotive
61
58
  @expires_in = (@options.delete('expires_in') || 0).to_i
62
59
  end
63
60
 
61
+ def page_fragment_cache_key(url)
62
+ Digest::SHA1.hexdigest(@target.to_s + url)
63
+ end
64
+
64
65
  def cached_response
65
66
  @@local_cache ||= {}
66
67
  @@local_cache[@local_cache_key]
@@ -72,7 +73,7 @@ module Locomotive
72
73
  end
73
74
 
74
75
  def render_all_and_cache_it(context)
75
- Rails.cache.fetch(@cache_key, expires_in: @expires_in, force: @expires_in == 0) do
76
+ Rails.cache.fetch(page_fragment_cache_key(@url), expires_in: @expires_in, force: @expires_in == 0) do
76
77
  self.render_all_without_cache(context)
77
78
  end
78
79
  end