locomotivecms_steam 1.5.3 → 1.6.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -3
  4. data/Gemfile +2 -3
  5. data/Gemfile.lock +57 -54
  6. data/README.md +17 -8
  7. data/bin/steam.rb +3 -13
  8. data/lib/locomotive/steam/adapters/filesystem/sanitizers/content_entry.rb +1 -1
  9. data/lib/locomotive/steam/adapters/mongodb.rb +3 -2
  10. data/lib/locomotive/steam/entities/theme_asset.rb +0 -1
  11. data/lib/locomotive/steam/liquid/drops/section.rb +31 -2
  12. data/lib/locomotive/steam/liquid/drops/section_block.rb +13 -0
  13. data/lib/locomotive/steam/liquid/drops/section_content_proxy.rb +44 -5
  14. data/lib/locomotive/steam/liquid/drops/section_editor_setting_data.rb +1 -1
  15. data/lib/locomotive/steam/liquid/tags/concerns/path.rb +1 -1
  16. data/lib/locomotive/steam/liquid/tags/concerns/section.rb +1 -1
  17. data/lib/locomotive/steam/liquid/tags/section.rb +6 -1
  18. data/lib/locomotive/steam/middlewares/logging.rb +4 -1
  19. data/lib/locomotive/steam/middlewares/site.rb +1 -1
  20. data/lib/locomotive/steam/repositories/content_entry_repository.rb +3 -3
  21. data/lib/locomotive/steam/repositories/content_type_field_select_option_repository.rb +8 -1
  22. data/lib/locomotive/steam/services/asset_host_service.rb +1 -1
  23. data/lib/locomotive/steam/services/url_finder_service.rb +2 -0
  24. data/lib/locomotive/steam/version.rb +1 -1
  25. data/lib/locomotive/steam.rb +2 -2
  26. data/locomotivecms_steam.gemspec +8 -8
  27. data/script/ci/before_build.sh +1 -0
  28. data/spec/fixtures/default/public/fonts/chunkfive-webfont.eot +0 -0
  29. data/spec/fixtures/default/public/fonts/chunkfive-webfont.svg +0 -0
  30. data/spec/fixtures/default/public/fonts/chunkfive-webfont.ttf +0 -0
  31. data/spec/fixtures/default/public/fonts/chunkfive-webfont.woff +0 -0
  32. data/spec/fixtures/default/public/fonts/chunkfive.css +0 -0
  33. data/spec/fixtures/default/public/fonts/chunkfive.otf +0 -0
  34. data/spec/integration/adapters/mongodb_spec.rb +5 -5
  35. data/spec/integration/liquid/drops/content_types_spec.rb +62 -0
  36. data/spec/integration/repositories/content_entry_repository_spec.rb +5 -0
  37. data/spec/unit/liquid/drops/section_block_spec.rb +26 -0
  38. data/spec/unit/liquid/drops/section_content_proxy_spec.rb +61 -2
  39. data/spec/unit/liquid/drops/section_spec.rb +31 -4
  40. data/spec/unit/liquid/tags/section_spec.rb +8 -9
  41. data/spec/unit/middlewares/cache_spec.rb +4 -4
  42. data/spec/unit/middlewares/section_spec.rb +3 -3
  43. data/spec/unit/middlewares/site_spec.rb +11 -1
  44. data/spec/unit/services/url_finder_service_spec.rb +25 -11
  45. metadata +35 -22
@@ -10,6 +10,7 @@ module Locomotive
10
10
  @section = section
11
11
  @block = block || { 'settings' => {} }
12
12
  @index = index
13
+ @leaves = []
13
14
  @definition = section.definition['blocks'].find do |block|
14
15
  block['type'] == type
15
16
  end || {}
@@ -23,6 +24,18 @@ module Locomotive
23
24
  @block['type']
24
25
  end
25
26
 
27
+ def depth
28
+ @block['depth'].presence || 0
29
+ end
30
+
31
+ def leaves
32
+ @leaves
33
+ end
34
+
35
+ def has_leaves?
36
+ leaves.size > 0
37
+ end
38
+
26
39
  def settings
27
40
  @content_proxy ||= SectionContentProxy.new(
28
41
  @block['settings'] || {},
@@ -18,10 +18,12 @@ module Locomotive
18
18
  return nil if value.blank?
19
19
 
20
20
  case type_of(name)
21
- when 'url' then SectionUrlField.new(*url_finder.url_for(value))
22
- when 'image_picker' then SectionImagePickerField.new(value)
23
- when 'integer' then value.to_i
24
- when 'text' then url_finder.decode_urls_for(value)
21
+ when 'url' then SectionUrlField.new(*url_finder.url_for(value))
22
+ when 'image_picker' then SectionImagePickerField.new(value)
23
+ when 'asset_picker' then SectionAssetPickerField.new(value)
24
+ when 'integer' then value.to_i
25
+ when 'text' then url_finder.decode_urls_for(value)
26
+ when 'content_entry' then find_content_entry(name, value)
25
27
  else value
26
28
  end
27
29
  end
@@ -40,9 +42,46 @@ module Locomotive
40
42
  @context.registers[:services].url_finder
41
43
  end
42
44
 
45
+ def find_content_entry(name, value)
46
+ type_slug = setting_of(name).try(:[], 'content_type')
47
+ service = @context.registers[:services].content_entry
48
+
49
+ return nil if type_slug.nil? || value.blank?
50
+
51
+ service.find(type_slug, value['id'])
52
+ end
53
+
54
+ end
55
+
56
+
57
+ class SectionAssetPickerField < ::Liquid::Drop
58
+
59
+ def initialize(url_or_attributes)
60
+ if url_or_attributes.is_a?(String) || url_or_attributes.blank?
61
+ @attributes = { url: url_or_attributes }
62
+ else
63
+ @attributes = url_or_attributes.symbolize_keys || {}
64
+ end
65
+ end
66
+
67
+ def url
68
+ @attributes[:url]
69
+ end
70
+
71
+ def size
72
+ @attributes[:size]
73
+ end
74
+
75
+ def name
76
+ self.url.present? ? File.basename(self.url) : nil
77
+ end
78
+
79
+ def to_s
80
+ self.url || ''
81
+ end
43
82
  end
44
83
 
45
- # Drop representing the valud of an image picker.
84
+ # Drop representing the value of an image picker.
46
85
  # It holds extra attributes like:
47
86
  # the width, height, format and cropped of the image
48
87
  class SectionImagePickerField < ::Liquid::Drop
@@ -40,7 +40,7 @@ module Locomotive
40
40
  return false if settings.blank?
41
41
 
42
42
  text_inputs(settings).include?(id)
43
- end
43
+ end
44
44
 
45
45
  def text_inputs(settings)
46
46
  settings.map do |input|
@@ -29,7 +29,7 @@ module Locomotive
29
29
  handle = @context[@handle] || @handle
30
30
 
31
31
  # is external url?
32
- if handle =~ Locomotive::Steam::IsHTTP
32
+ if handle.to_s =~ Locomotive::Steam::IsHTTP
33
33
  handle
34
34
  elsif page = self.retrieve_page_drop_from_handle(handle) # return a drop or model?
35
35
  # make sure we've got the page/content entry (if templatized)
@@ -13,7 +13,7 @@ module Locomotive::Steam::Liquid::Tags::Concerns
13
13
  )
14
14
 
15
15
  # assign an id if specified in the context
16
- context['section'].id ||= context['section_id'] if context['section_id'].present?
16
+ context['section'].id = context['section_id'] if context['section_id'].present?
17
17
 
18
18
  begin
19
19
  _render(context, template)
@@ -59,7 +59,12 @@ module Locomotive
59
59
  private
60
60
 
61
61
  def set_section_dom_id(context)
62
- context['section_id'] = "page-#{attributes[:id] || section_type}"
62
+ # is the section being rendered from the section middleware?
63
+ if content = context.registers[:_section_content]
64
+ context['section_id'] = content['id'] || section_type
65
+ else
66
+ context['section_id'] = "page-#{attributes[:id] || section_type}"
67
+ end
63
68
  end
64
69
 
65
70
  def find_section(context)
@@ -13,7 +13,10 @@ module Locomotive::Steam
13
13
  now = Time.now
14
14
 
15
15
  log "Started #{env['REQUEST_METHOD'].upcase} \"#{env['PATH_INFO']}\" at #{now}".light_white, 0
16
- log "Params: #{env.fetch('steam.request').params.inspect}"
16
+
17
+ if Locomotive::Steam.configuration.mode == :test
18
+ log "Params: #{env.fetch('steam.request').params.inspect}"
19
+ end
17
20
 
18
21
  app.call(env).tap do |response|
19
22
  done_in_ms = ((Time.now - now) * 10000).truncate / 10.0
@@ -42,7 +42,7 @@ module Locomotive::Steam
42
42
  end
43
43
 
44
44
  def redirect_if_required(site)
45
- return if env['steam.is_default_host']
45
+ return if env['steam.is_default_host'] || env['steam.live_editing']
46
46
 
47
47
  if redirect_to_first_domain?(site) || redirect_to_https?(site)
48
48
  klass = request.scheme == 'https' || redirect_to_https?(site) ? URI::HTTPS : URI::HTTP
@@ -241,7 +241,7 @@ module Locomotive
241
241
 
242
242
  # belongs_to
243
243
  _prepare(@fields.belongs_to) { |field, value| value_to_id(value, field.target_id) }
244
-
244
+
245
245
  # many_to_many
246
246
  _prepare(@fields.many_to_many) { |field, value| values_to_ids(value, field.target_id) }
247
247
 
@@ -276,10 +276,10 @@ module Locomotive
276
276
  end
277
277
 
278
278
  def value_to_id(value, target_id)
279
+ return values_to_ids(value, target_id) if value.kind_of?(Array)
280
+
279
281
  _value = if value.is_a?(Hash)
280
282
  value['_id'] || value[:_id]
281
- elsif value.respond_to?(:each) # array
282
- values_to_ids(value, target_id)
283
283
  else
284
284
  value.respond_to?(:_id) ? value._id : value
285
285
  end
@@ -19,13 +19,20 @@ module Locomotive
19
19
  end
20
20
 
21
21
  def by_name(name)
22
- query { where(name: name) }.first
22
+ scope.with_locale(query_locale) do
23
+ query { where(name: name) }.first
24
+ end
23
25
  end
24
26
 
25
27
  def by_id_or_name(id_or_name)
26
28
  find(id_or_name) || by_name(id_or_name)
27
29
  end
28
30
 
31
+ def query_locale
32
+ # if the select field is not localized, query in the default locale of the site
33
+ content_type_field.localized? ? locale : scope.default_locale
34
+ end
35
+
29
36
  end
30
37
  end
31
38
  end
@@ -14,7 +14,7 @@ module Locomotive
14
14
 
15
15
  timestamp ||= (site.try(:template_version) || site.try(:updated_at)).to_i
16
16
 
17
- return add_timestamp_suffix(source, timestamp) if source =~ Steam::IsHTTP
17
+ return add_timestamp_suffix(source, timestamp) if source.to_s =~ Steam::IsHTTP
18
18
 
19
19
  url = self.host ? build_url(host, source) : source
20
20
 
@@ -74,6 +74,8 @@ module Locomotive
74
74
  when 'page'
75
75
  page_finder.find_by_id(value)
76
76
  when 'content_entry'
77
+ return nil if value.blank?
78
+
77
79
  # find the page template
78
80
  page_finder.find_by_id(value['page_id']).tap do |_page|
79
81
  entry = content_entry_finder.find(value['content_type_slug'], value['id'])
@@ -3,6 +3,6 @@
3
3
  # 1.0.0.alpha < 1.0.0.alpha1 < 1.0.0.beta < 1.0.0.beta2 < 1.0.0.beta11 < 1.0.0.rc1 < 1.0.0
4
4
  module Locomotive
5
5
  module Steam
6
- VERSION = '1.5.3'
6
+ VERSION = '1.6.0.beta1'
7
7
  end
8
8
  end
@@ -22,8 +22,8 @@ module Locomotive
22
22
 
23
23
  CONTENT_ENTRY_ENGINE_CLASS_NAME = /^Locomotive::ContentEntry(.*)$/o.freeze
24
24
 
25
- SECTIONS_SETTINGS_VARIABLE_REGEXP = /^\s*([a-z]+\.)?settings\.(?<id>.*)\s*$/o.freeze
26
- SECTIONS_BLOCK_FORLOOP_REGEXP = /(?<name>.+)-section\.blocks$/o.freeze
25
+ SECTIONS_SETTINGS_VARIABLE_REGEXP = /^\s*([a-z\_]+\.)?settings\.(?<id>.*)\s*$/o.freeze
26
+ SECTIONS_BLOCK_FORLOOP_REGEXP = /(?<name>.+)-(section|block)\.(blocks|blocks_as_tree|leaves)$/o.freeze
27
27
  SECTIONS_LINK_TARGET_REGEXP = /[^\"]+\/_locomotive-link\/(?<link>[^\"]+)/mo.freeze
28
28
 
29
29
  ASSET_URL_REGEXP = /("|')(https:\/\/[^\/]+)?\/(sites|steam)\/(\S+)("|')/.freeze
@@ -17,22 +17,22 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.add_development_dependency 'rake', '~> 13.0.1'
19
19
 
20
- spec.add_development_dependency 'mongo', '~> 2.8.0'
20
+ spec.add_development_dependency 'mongo', '~> 2.13.1'
21
21
  spec.add_development_dependency 'origin', '~> 2.3.1'
22
22
 
23
- spec.add_dependency 'nokogiri', '~> 1.10.8'
24
- spec.add_dependency 'sanitize', '~> 4.6.4'
23
+ spec.add_dependency 'nokogiri', '~> 1.11.0'
24
+ spec.add_dependency 'sanitize', '~> 5.2.1'
25
25
  spec.add_dependency 'morphine', '~> 0.1.1'
26
26
  spec.add_dependency 'httparty', '~> 0.16.0'
27
27
  spec.add_dependency 'chronic', '~> 0.10.2'
28
28
  spec.add_dependency 'bcrypt', '~> 3.1.11'
29
- spec.add_dependency 'multi_json', '~> 1.13.1'
29
+ spec.add_dependency 'multi_json', '~> 1.15.0'
30
30
  spec.add_dependency 'liquid', '~> 4.0.3'
31
31
 
32
32
  spec.add_dependency 'rack-rewrite', '~> 1.5.1'
33
33
  spec.add_dependency 'rack-cache', '~> 1.7.0'
34
34
  spec.add_dependency 'rack_csrf', '~> 2.6.0'
35
- spec.add_dependency 'dragonfly', '~> 1.2.0'
35
+ spec.add_dependency 'dragonfly', '>= 1.2', '< 1.5'
36
36
  spec.add_dependency 'moneta', '~> 1.0.0'
37
37
 
38
38
  spec.add_dependency 'sprockets', '~> 3.7.1'
@@ -42,14 +42,14 @@ Gem::Specification.new do |spec|
42
42
  spec.add_dependency 'compass', '~> 1.0.3'
43
43
  spec.add_dependency 'autoprefixer-rails', '~> 8.0.0'
44
44
 
45
- spec.add_dependency 'kramdown', '~> 1.16.2'
45
+ spec.add_dependency 'kramdown', '~> 2.3.0'
46
46
  spec.add_dependency 'RedCloth', '~> 4.3.2'
47
47
  spec.add_dependency 'mimetype-fu', '~> 0.1.2'
48
- spec.add_dependency 'mime-types', '~> 3.1.0'
48
+ spec.add_dependency 'mime-types', '~> 3.3.0'
49
49
  spec.add_dependency 'duktape', '~> 2.0.1.1'
50
50
  spec.add_dependency 'pony', '~> 1.12'
51
51
 
52
52
  spec.add_dependency 'locomotivecms_common', '~> 0.4.0'
53
53
 
54
- spec.required_ruby_version = '>= 2.0'
54
+ spec.required_ruby_version = ['>= 2.5', '< 3']
55
55
  end
@@ -2,3 +2,4 @@
2
2
  echo 'America/Chicago' | sudo tee /etc/timezone
3
3
  sudo dpkg-reconfigure --frontend noninteractive tzdata
4
4
  date
5
+ mongo --version
File without changes
File without changes
@@ -32,12 +32,12 @@ describe Locomotive::Steam::MongoDBAdapter do
32
32
 
33
33
  subject { described_class.disconnect_session }
34
34
 
35
- it { is_expected.to eq nil }
36
-
37
35
  it 'closes clients' do
38
- 10.times { connection['locomotive_sites'].find.count }
39
- subject
40
- expect(current_connections).to eq @before_connections
36
+ 10.times { connection['locomotive_sites'].find.count }
37
+ @before_connections = current_connections
38
+ is_expected.to eq true
39
+ sleep(2) # NOTE: wait for the connections to be completely closed
40
+ expect(current_connections).to be < @before_connections
41
41
  end
42
42
 
43
43
  end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ require_relative '../../../../lib/locomotive/steam/adapters/filesystem.rb'
4
+ require_relative '../../../../lib/locomotive/steam/adapters/mongodb.rb'
5
+
6
+ describe Locomotive::Steam::Liquid::Drops::ContentTypes do
7
+
8
+ describe 'access a content type through a variable' do
9
+
10
+ let(:source) { <<-EOF
11
+ {% assign myContentType = 'songs' %}
12
+ {% for song in contents[myContentType] %}
13
+ <span>{{ song.title }}</span>
14
+ {% endfor %}
15
+ EOF
16
+ }
17
+
18
+ let(:page) { nil }
19
+ let(:site) { Locomotive::Steam::Site.new(_id: site_id, locales: %w(en fr nb)) }
20
+ let(:services) { Locomotive::Steam::Services.build_instance }
21
+ let(:assigns) { { 'contents' => Locomotive::Steam::Liquid::Drops::ContentTypes.new, 'fullpath' => '/', 'current_page' => page } }
22
+ let(:context) { ::Liquid::Context.new(assigns, {}, { services: services }) }
23
+
24
+ before {
25
+ services.locale = :en
26
+ services.repositories.adapter = adapter
27
+ services.repositories.current_site = site
28
+ }
29
+
30
+ subject { render_template(source, context) }
31
+
32
+ shared_examples_for 'listing content entries of a dynamic content type' do
33
+
34
+ it { is_expected.to match /<span>Song #1<\/span>/ }
35
+
36
+ end
37
+
38
+ context 'Filesystem' do
39
+
40
+ it_should_behave_like 'listing content entries of a dynamic content type' do
41
+
42
+ let(:site_id) { 1 }
43
+ let(:adapter) { Locomotive::Steam::FilesystemAdapter.new(default_fixture_site_path) }
44
+
45
+ end
46
+
47
+ end
48
+
49
+ context 'MongoDB' do
50
+
51
+ it_should_behave_like 'listing content entries of a dynamic content type' do
52
+
53
+ let(:site_id) { mongodb_site_id }
54
+ let(:adapter) { Locomotive::Steam::MongoDBAdapter.new(database: mongodb_database, hosts: ['127.0.0.1:27017']) }
55
+
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -70,6 +70,11 @@ describe Locomotive::Steam::ContentEntryRepository do
70
70
  describe 'filter by a select field' do
71
71
  subject { repository.all(kind: 'grunge') }
72
72
  it { expect(subject.map { |entry| entry[:name] }).to eq(['Alice in Chains', 'Pearl Jam']) }
73
+
74
+ context 'switching to a different locale' do
75
+ let(:locale) { 'fr' }
76
+ it { expect(subject.map { |entry| entry[:name] }).to eq(['Alice in Chains', 'Pearl Jam']) }
77
+ end
73
78
  end
74
79
 
75
80
  describe 'filter by a belongs_to field' do
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Locomotive::Steam::Liquid::Drops::SectionBlock do
4
+
5
+ let(:section) { instance_double('Section', definition: { 'blocks' => [] }) }
6
+ let(:block) { instance_double('block') }
7
+ let(:index) { 0 }
8
+ let(:drop) { described_class.new(section, block, index) }
9
+
10
+ describe '#has_leaves?' do
11
+
12
+ subject { drop.has_leaves? }
13
+
14
+ it { is_expected.to eq false }
15
+
16
+ context 'the block has leaves' do
17
+
18
+ before { drop.leaves << instance_double('SectionBlock') }
19
+
20
+ it { is_expected.to eq true }
21
+
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -2,8 +2,9 @@ require 'spec_helper'
2
2
 
3
3
  describe Locomotive::Steam::Liquid::Drops::SectionContentProxy do
4
4
 
5
- let(:url_finder_service) { instance_double('UrlFinderService') }
6
- let(:services) { instance_double('Services', url_finder: url_finder_service) }
5
+ let(:url_finder_service) { instance_double('UrlFinderService') }
6
+ let(:content_entry_service) { instance_double('ContentEntryService') }
7
+ let(:services) { instance_double('Services', url_finder: url_finder_service, content_entry: content_entry_service) }
7
8
  let(:site) { instance_double('Site', default_locale: 'en') }
8
9
  let(:context) { ::Liquid::Context.new({}, {}, { locale: 'en', services: services, site: site }) }
9
10
  let(:drop) { described_class.new(content, settings).tap { |d| d.context = context } }
@@ -97,6 +98,30 @@ describe Locomotive::Steam::Liquid::Drops::SectionContentProxy do
97
98
 
98
99
  end
99
100
 
101
+ describe 'content entry picker' do
102
+
103
+ let(:settings) { [{ 'id' => 'article', 'type' => 'content_entry', 'content_type' => 'articles' }] }
104
+ let(:value) { nil }
105
+ let(:content) { { 'article' => value } }
106
+
107
+ subject { drop.liquid_method_missing(:article) }
108
+
109
+ it { is_expected.to eq nil }
110
+
111
+ context 'an id to a content entry has been passed' do
112
+
113
+ let(:article) { instance_double('Article', title: 'Hello world!') }
114
+ let(:value) { { 'id' => '42' } }
115
+
116
+ it 'calls the content_entry service to fetch the article' do
117
+ expect(content_entry_service).to receive(:find).with('articles', '42').and_return(article)
118
+ expect(subject.title).to eq('Hello world!')
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
100
125
  describe 'image picker type setting' do
101
126
 
102
127
  let(:settings) { [{ 'id' => 'image', 'type' => 'image_picker' }] }
@@ -144,4 +169,38 @@ describe Locomotive::Steam::Liquid::Drops::SectionContentProxy do
144
169
 
145
170
  end
146
171
 
172
+ describe 'asset picker type setting' do
173
+
174
+ let(:settings) { [{ 'id' => 'file', 'type' => 'asset_picker' }] }
175
+ let(:value) { nil }
176
+ let(:content) { { 'file' => value } }
177
+ let(:page) { instance_double('Page') }
178
+ let(:asset_drop) { drop.liquid_method_missing(:file) }
179
+
180
+ subject { asset_drop.to_s }
181
+ it { is_expected.to eq '' }
182
+
183
+ context 'the asset is a string' do
184
+ let(:value) { '/foo/bar/specs.pdf' }
185
+ it { is_expected.to eq('/foo/bar/specs.pdf') }
186
+ end
187
+
188
+ context 'the asset is a hash' do
189
+ let(:value) { { url: '/foo/specs.pdf', size: 30 } }
190
+ it { is_expected.to eq('/foo/specs.pdf') }
191
+ it 'has access to size and name of the asset' do
192
+ expect(asset_drop.url).to eq('/foo/specs.pdf')
193
+ expect(asset_drop.name).to eq('specs.pdf')
194
+ expect(asset_drop.size).to eq(30)
195
+ end
196
+ end
197
+
198
+ context 'the asset is nil' do
199
+ let(:value) { nil }
200
+ subject { asset_drop }
201
+ it { is_expected.to eq nil }
202
+ end
203
+
204
+ end
205
+
147
206
  end