locomotive_cms 2.5.4 → 2.5.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/images/locomotive/icons/flags/bf.png +0 -0
- data/app/assets/images/locomotive/icons/flags/mg.png +0 -0
- data/app/assets/javascripts/locomotive/views/sites/memberships_view.js.coffee +4 -0
- data/app/controllers/locomotive/api/content_entries_controller.rb +1 -1
- data/app/controllers/locomotive/content_entries_controller.rb +1 -1
- data/app/controllers/locomotive/public/sitemaps_controller.rb +9 -0
- data/app/helpers/locomotive/base_helper.rb +13 -0
- data/app/helpers/locomotive/content_types_helper.rb +2 -1
- data/app/models/locomotive/content_entry.rb +46 -10
- data/app/models/locomotive/content_type.rb +3 -0
- data/app/models/locomotive/extensions/content_entry/csv.rb +2 -2
- data/app/models/locomotive/theme_asset.rb +2 -1
- data/app/views/locomotive/content_entries/_list.html.haml +1 -1
- data/app/views/locomotive/public/sitemaps/show.xml.builder +6 -4
- data/config/locales/admin_ui.es.yml +7 -0
- data/features/public/content_entries.feature +1 -1
- data/lib/generators/locomotive/install/templates/locomotive.rb +4 -0
- data/lib/locomotive/configuration.rb +2 -1
- data/lib/locomotive/liquid/asset_host.rb +51 -0
- data/lib/locomotive/liquid/drops/uploader.rb +7 -1
- data/lib/locomotive/liquid/filters/base.rb +8 -4
- data/lib/locomotive/liquid/tags/editable/file.rb +9 -5
- data/lib/locomotive/mongoid/patches.rb +44 -0
- data/lib/locomotive/render.rb +2 -1
- data/lib/locomotive/version.rb +1 -1
- data/spec/lib/locomotive/liquid/asset_host_spec.rb +79 -0
- data/spec/lib/locomotive/liquid/drops/content_entry_spec.rb +55 -19
- data/spec/lib/locomotive/liquid/filters/html_spec.rb +3 -1
- data/spec/lib/locomotive/liquid/tags/editable/file_spec.rb +72 -0
- data/spec/models/locomotive/content_type_spec.rb +31 -0
- data/spec/models/locomotive/theme_asset_spec.rb +4 -4
- data/spec/support/asset_host_stubs.rb +17 -0
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 934e34a25d00b8c0c55c81d20e17346b7a75f105
|
4
|
+
data.tar.gz: dd086d5f1ef9307fd1b6f4deb59c0581840b45c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3dd291b6d7e877f9187ec006a9d66300b176d7cecba047cf94a842f9d29f72d614f13e67a41006f0a1ac97537eac170134f8d31f468b99a1c2a3ca00b47c1c7a
|
7
|
+
data.tar.gz: 249b2e6f9b65fc55e13ff4aa31e40853d7f163a8c9e846fedeba3619ca456dd81e6610f5858b719fd8f8966f0e558e924772ab985bb5e840056120fe006d7df7
|
Binary file
|
Binary file
|
@@ -4,6 +4,8 @@ class Locomotive.Views.Sites.MembershipsView extends Backbone.View
|
|
4
4
|
|
5
5
|
tagName: 'div'
|
6
6
|
|
7
|
+
id: 'site_memberships'
|
8
|
+
|
7
9
|
className: 'list'
|
8
10
|
|
9
11
|
_entry_views = []
|
@@ -13,6 +15,8 @@ class Locomotive.Views.Sites.MembershipsView extends Backbone.View
|
|
13
15
|
|
14
16
|
@enable_ui_effects()
|
15
17
|
|
18
|
+
$(@el).append('<span class="error-anchor"></span>')
|
19
|
+
|
16
20
|
return @
|
17
21
|
|
18
22
|
change_entry: (membership, value) ->
|
@@ -2,6 +2,8 @@ module Locomotive
|
|
2
2
|
module Public
|
3
3
|
class SitemapsController < Public::BaseController
|
4
4
|
|
5
|
+
before_filter :set_locale
|
6
|
+
|
5
7
|
respond_to :xml
|
6
8
|
|
7
9
|
def show
|
@@ -9,6 +11,13 @@ module Locomotive
|
|
9
11
|
respond_with @pages
|
10
12
|
end
|
11
13
|
|
14
|
+
protected
|
15
|
+
|
16
|
+
def set_locale
|
17
|
+
::Mongoid::Fields::I18n.locale = params[:locale] || current_site.default_locale
|
18
|
+
::I18n.locale = ::Mongoid::Fields::I18n.locale
|
19
|
+
end
|
20
|
+
|
12
21
|
end
|
13
22
|
end
|
14
23
|
end
|
@@ -200,5 +200,18 @@ module Locomotive
|
|
200
200
|
end
|
201
201
|
alias :l :localize
|
202
202
|
|
203
|
+
# other helpers
|
204
|
+
|
205
|
+
# MongoDB crashes when performing a query on a big collection
|
206
|
+
# where there is a sort without an index on the fields to sort.
|
207
|
+
def empty_collection?(collection)
|
208
|
+
# criteria ?
|
209
|
+
if collection.respond_to?(:without_sorting)
|
210
|
+
collection.without_sorting.empty?
|
211
|
+
else
|
212
|
+
collection.empty?
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
203
216
|
end
|
204
217
|
end
|
@@ -61,7 +61,8 @@ module Locomotive
|
|
61
61
|
registers = {
|
62
62
|
controller: self,
|
63
63
|
site: current_site,
|
64
|
-
current_locomotive_account: current_locomotive_account
|
64
|
+
current_locomotive_account: current_locomotive_account,
|
65
|
+
asset_host: Locomotive::Liquid::AssetHost.new(request, current_site, Locomotive.config.asset_host)
|
65
66
|
}
|
66
67
|
|
67
68
|
preserve(content_type.item_template.render(::Liquid::Context.new({}, assigns, registers)))
|
@@ -36,6 +36,19 @@ module Locomotive
|
|
36
36
|
scope :latest_updated, order_by(updated_at: :desc).limit(Locomotive.config.ui[:latest_entries_nb])
|
37
37
|
scope :next_or_previous, ->(condition, order_by) { where({ _visible: true }.merge(condition)).limit(1).order_by(order_by) }
|
38
38
|
|
39
|
+
## indexes ##
|
40
|
+
index site_id: 1
|
41
|
+
index _type: 1
|
42
|
+
index content_type_id: 1
|
43
|
+
Locomotive.config.site_locales.each do |locale|
|
44
|
+
index _type: 1, "_slug.#{locale}" => 1
|
45
|
+
index content_type_id: 1, "_slug.#{locale}" => 1
|
46
|
+
end
|
47
|
+
index content_type_id: 1, created_at: 1
|
48
|
+
index content_type_id: 1, _type: 1, created_at: 1
|
49
|
+
index _type: 1, _position: 1
|
50
|
+
index content_type_id: 1, _position: 1
|
51
|
+
|
39
52
|
## methods ##
|
40
53
|
|
41
54
|
alias :visible? :_visible?
|
@@ -153,7 +166,7 @@ module Locomotive
|
|
153
166
|
if self._slug.present?
|
154
167
|
self._slug.permalink!
|
155
168
|
|
156
|
-
self.
|
169
|
+
self.find_next_unique_slug if self.slug_already_taken?
|
157
170
|
end
|
158
171
|
|
159
172
|
# all the site locales share the same slug ONLY IF the entry is not localized.
|
@@ -174,12 +187,36 @@ module Locomotive
|
|
174
187
|
end
|
175
188
|
end
|
176
189
|
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
190
|
+
# Find the next available unique slug as a string
|
191
|
+
# and replace the current _slug.
|
192
|
+
def find_next_unique_slug
|
193
|
+
_index = 0
|
194
|
+
_base = self._slug.gsub(/-\d+$/, '')
|
195
|
+
|
196
|
+
if _similar = similar_slug(_base)
|
197
|
+
_index = _similar.scan(/-(\d+)$/).flatten.last.to_i
|
198
|
+
end
|
199
|
+
|
200
|
+
loop do
|
201
|
+
_index += 1
|
202
|
+
self._slug = [_base, _index].join('-')
|
203
|
+
break unless self.slug_already_taken?
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def similar_slug(slug)
|
208
|
+
_last = self.class.where(:_id.ne => self._id, _slug: /^#{slug}-?\d*$/i)
|
209
|
+
.only(:_slug)
|
210
|
+
.order_by(:_id.desc)
|
211
|
+
.context
|
212
|
+
.query
|
213
|
+
.first
|
214
|
+
|
215
|
+
if _last
|
216
|
+
_last['_slug'][::Mongoid::Fields::I18n.locale.to_s]
|
217
|
+
else
|
218
|
+
nil
|
219
|
+
end
|
183
220
|
end
|
184
221
|
|
185
222
|
def slug_already_taken?
|
@@ -203,9 +240,8 @@ module Locomotive
|
|
203
240
|
end
|
204
241
|
|
205
242
|
def add_to_list_bottom
|
206
|
-
|
207
|
-
|
208
|
-
end
|
243
|
+
max = self.class.indexed_max(:_position)
|
244
|
+
self._position = max + 1 if max
|
209
245
|
end
|
210
246
|
|
211
247
|
def send_notifications
|
@@ -37,7 +37,7 @@ module Locomotive
|
|
37
37
|
when :file
|
38
38
|
value.blank? ? '' : value.guess_url(options[:host])
|
39
39
|
when :belongs_to
|
40
|
-
value.try(:_label)
|
40
|
+
value.try(:_label) || ''
|
41
41
|
when :has_many, :many_to_many
|
42
42
|
value.map(&:_label).join(', ')
|
43
43
|
when :tags
|
@@ -68,7 +68,7 @@ module Locomotive
|
|
68
68
|
# header
|
69
69
|
csv << labels
|
70
70
|
# body
|
71
|
-
all.
|
71
|
+
all.each_by(100) do |entry|
|
72
72
|
csv << entry.to_values(options)
|
73
73
|
end
|
74
74
|
end
|
@@ -155,7 +155,8 @@ module Locomotive
|
|
155
155
|
sanitized_path = path.gsub(/[("')]/, '').gsub(/^\//, '').gsub(/\?[0-9]+$/, '')
|
156
156
|
|
157
157
|
if asset = self.site.theme_assets.where(local_path: sanitized_path).first
|
158
|
-
|
158
|
+
timestamp = self.updated_at.to_i
|
159
|
+
"#{path.first}#{asset.source.url}?#{timestamp}#{path.last}"
|
159
160
|
else
|
160
161
|
path
|
161
162
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
- if entries
|
1
|
+
- if empty_collection?(entries)
|
2
2
|
%p.no-items!= t('.no_items', url: new_content_entry_path(content_type.slug))
|
3
3
|
- else
|
4
4
|
%ul{ id: 'entries-list', class: "#{content_type.groupable? ? 'grouped' : 'list'} #{'sortable' if content_type.order_manually?}", :'data-url' => sort_content_entries_path(content_type.slug, :json) }
|
@@ -10,10 +10,12 @@ xml.urlset "xmlns" => "http://www.sitemaps.org/schemas/sitemap/0.9" do
|
|
10
10
|
if not page.index_or_not_found?
|
11
11
|
if page.templatized?
|
12
12
|
page.fetch_target_entries(_visible: true).each do |c|
|
13
|
-
|
14
|
-
xml.
|
15
|
-
|
16
|
-
|
13
|
+
if c._slug.present?
|
14
|
+
xml.url do
|
15
|
+
xml.loc public_page_url(page, { content: c })
|
16
|
+
xml.lastmod c.updated_at.to_date.to_s('%Y-%m-%d')
|
17
|
+
xml.priority 0.9
|
18
|
+
end
|
17
19
|
end
|
18
20
|
end
|
19
21
|
else
|
@@ -60,11 +60,16 @@ es:
|
|
60
60
|
disable_with: "locomotive.disable_with.form_actions"
|
61
61
|
footer:
|
62
62
|
who_is_behind: "Servicio desarrollado por %{development} y diseñado por <a href=\"http://www.sachagreif.com\">Sacha Greif</a>"
|
63
|
+
form:
|
64
|
+
change_file: cambiar
|
65
|
+
delete_file: eliminar
|
66
|
+
cancel: cancelar
|
63
67
|
form_actions:
|
64
68
|
back: Volver sin guardar
|
65
69
|
create: Crear
|
66
70
|
update: Actualizar
|
67
71
|
send: Enviar
|
72
|
+
disable_with: Pendiente...
|
68
73
|
|
69
74
|
notifications:
|
70
75
|
new_content_entry:
|
@@ -86,6 +91,8 @@ es:
|
|
86
91
|
index:
|
87
92
|
is_required: requerido
|
88
93
|
default_label: Nombre del campo
|
94
|
+
form:
|
95
|
+
default_label: Nombre del campo
|
89
96
|
|
90
97
|
sessions:
|
91
98
|
new:
|
@@ -65,6 +65,10 @@ Locomotive.configure do |config|
|
|
65
65
|
#
|
66
66
|
# config.theme_assets_checksum = true
|
67
67
|
|
68
|
+
# Enable serving of images, stylesheets, and JavaScripts from an asset server
|
69
|
+
# config.asset_host = 'http://assets.example.com'
|
70
|
+
# config.asset_host = -> (request, site) { ... }
|
71
|
+
|
68
72
|
# Rack-cache settings, mainly used for the inline resizing image module. Default options:
|
69
73
|
# config.rack_cache = {
|
70
74
|
# verbose: true,
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Locomotive
|
2
|
+
module Liquid
|
3
|
+
|
4
|
+
class AssetHost
|
5
|
+
|
6
|
+
IsHTTP = /^https?\/\//o
|
7
|
+
|
8
|
+
attr_reader :request, :site, :host
|
9
|
+
|
10
|
+
def initialize(request, site, host)
|
11
|
+
@request, @site = request, site
|
12
|
+
|
13
|
+
@host = build_host(host, request, site)
|
14
|
+
end
|
15
|
+
|
16
|
+
def compute(source, timestamp = nil)
|
17
|
+
return source if source.nil?
|
18
|
+
|
19
|
+
return add_timestamp_suffix(source, timestamp) if source =~ IsHTTP
|
20
|
+
|
21
|
+
url = self.host ? URI.join(host, source).to_s : source
|
22
|
+
|
23
|
+
add_timestamp_suffix(url, timestamp)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def build_host(host, request, site)
|
29
|
+
if host
|
30
|
+
if host.respond_to?(:call)
|
31
|
+
host.call(request, site)
|
32
|
+
else
|
33
|
+
host
|
34
|
+
end
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_timestamp_suffix(source, timestamp)
|
41
|
+
if timestamp.nil? || timestamp == 0 || source.include?('?')
|
42
|
+
source
|
43
|
+
else
|
44
|
+
"#{source}?#{timestamp}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -3,7 +3,13 @@ module Locomotive
|
|
3
3
|
module Drops
|
4
4
|
class Uploader < Base
|
5
5
|
|
6
|
-
delegate :
|
6
|
+
delegate :size, to: :@_source
|
7
|
+
|
8
|
+
def url
|
9
|
+
url, timestamp = @_source.url, @_source.model.updated_at.to_i
|
10
|
+
|
11
|
+
@context.registers[:asset_host].compute(url, timestamp)
|
12
|
+
end
|
7
13
|
|
8
14
|
def filename
|
9
15
|
File.basename(@_source.url)
|
@@ -35,11 +35,15 @@ module Locomotive
|
|
35
35
|
path.gsub!(/(\?+.+)$/, '')
|
36
36
|
query_string = $1
|
37
37
|
|
38
|
-
url
|
38
|
+
# build the url of the theme asset based on the site and without loading
|
39
|
+
# the whole theme asset from database
|
40
|
+
_url = ThemeAssetUploader.url_for(@context.registers[:site], path)
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
42
|
+
# get a timestamp only the source url does not include a query string
|
43
|
+
timestamp = query_string.blank? ? @context.registers[:theme_assets_checksum][path] : nil
|
44
|
+
|
45
|
+
# prefix by a asset host if given
|
46
|
+
url = @context.registers[:asset_host].compute(_url, timestamp)
|
43
47
|
|
44
48
|
query_string ? "#{url}#{query_string}" : url
|
45
49
|
end
|
@@ -15,15 +15,19 @@ module Locomotive
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def render_element(context, element)
|
18
|
-
|
19
|
-
|
18
|
+
default_timestamp = context.registers[:page].updated_at.to_i
|
19
|
+
|
20
|
+
url, timestamp = (if element.source?
|
21
|
+
[element.source.url, default_timestamp]
|
20
22
|
else
|
21
23
|
if element.default_source_url.present?
|
22
|
-
element.default_source_url
|
24
|
+
[element.default_source_url, default_timestamp]
|
23
25
|
else
|
24
|
-
render_default_content(context)
|
26
|
+
[render_default_content(context), nil]
|
25
27
|
end
|
26
|
-
end
|
28
|
+
end)
|
29
|
+
|
30
|
+
context.registers[:asset_host].compute(url, timestamp)
|
27
31
|
end
|
28
32
|
|
29
33
|
def document_type
|
@@ -22,9 +22,53 @@ module Mongoid#:nodoc:
|
|
22
22
|
end
|
23
23
|
|
24
24
|
class Criteria
|
25
|
+
def without_sorting
|
26
|
+
clone.tap { |crit| crit.options.delete(:sort) }
|
27
|
+
end
|
28
|
+
|
29
|
+
# http://code.dblock.org/paging-and-iterating-over-large-mongo-collections
|
30
|
+
def each_by(by, &block)
|
31
|
+
idx = total = 0
|
32
|
+
set_limit = options[:limit]
|
33
|
+
while ((results = ordered_clone.limit(by).skip(idx)) && results.any?)
|
34
|
+
results.each do |result|
|
35
|
+
return self if set_limit and set_limit >= total
|
36
|
+
total += 1
|
37
|
+
yield result
|
38
|
+
end
|
39
|
+
idx += by
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Optimized version of the max aggregate method.
|
45
|
+
# It works efficiently only if the field is part of a MongoDB index.
|
46
|
+
# more here: http://stackoverflow.com/questions/4762980/getting-the-highest-value-of-a-column-in-mongodb
|
47
|
+
def indexed_max(field)
|
48
|
+
_criteria = criteria.order_by(field.to_sym.desc).only(field.to_sym)
|
49
|
+
selector = _criteria.send(:selector_with_type_selection)
|
50
|
+
fields = _criteria.options[:fields]
|
51
|
+
sort = _criteria.options[:sort]
|
52
|
+
|
53
|
+
document = collection.find(selector).select(fields).sort(sort).limit(1).first
|
54
|
+
document ? document[field.to_s].to_i : nil
|
55
|
+
end
|
56
|
+
|
25
57
|
def to_liquid
|
26
58
|
Locomotive::Liquid::Drops::ProxyCollection.new(self)
|
27
59
|
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def ordered_clone
|
64
|
+
options[:sort] ? clone : clone.asc(:_id)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module Finders
|
69
|
+
def indexed_max(field)
|
70
|
+
with_default_scope.indexed_max(field)
|
71
|
+
end
|
28
72
|
end
|
29
73
|
|
30
74
|
module Criterion
|
data/lib/locomotive/render.rb
CHANGED
@@ -222,7 +222,8 @@ module Locomotive
|
|
222
222
|
inline_editor: self.editing_page?,
|
223
223
|
logger: Rails.logger,
|
224
224
|
current_locomotive_account: current_locomotive_account,
|
225
|
-
theme_assets_checksum: Locomotive.config.theme_assets_checksum ? current_site.theme_assets.checksums : {}
|
225
|
+
theme_assets_checksum: Locomotive.config.theme_assets_checksum ? current_site.theme_assets.checksums : {},
|
226
|
+
asset_host: Locomotive::Liquid::AssetHost.new(request, current_site, Locomotive.config.asset_host)
|
226
227
|
}
|
227
228
|
end
|
228
229
|
|
data/lib/locomotive/version.rb
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Locomotive::Liquid::AssetHost do
|
4
|
+
|
5
|
+
let(:request) { nil }
|
6
|
+
let(:site) { nil }
|
7
|
+
let(:host) { nil }
|
8
|
+
let(:timestamp) { nil }
|
9
|
+
let(:asset_host) { Locomotive::Liquid::AssetHost.new(request, site, host) }
|
10
|
+
let(:source) { '/sites/42/assets/1/banner.png' }
|
11
|
+
|
12
|
+
subject { asset_host.compute(source, timestamp) }
|
13
|
+
|
14
|
+
describe 'no host provided' do
|
15
|
+
|
16
|
+
it { should eq '/sites/42/assets/1/banner.png' }
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'with a timestamp' do
|
21
|
+
|
22
|
+
let(:timestamp) { '42' }
|
23
|
+
it { should eq '/sites/42/assets/1/banner.png?42' }
|
24
|
+
|
25
|
+
context 'the source already includes a query string' do
|
26
|
+
|
27
|
+
let(:source) { '/sites/42/assets/1/banner.png?foo' }
|
28
|
+
it { should eq '/sites/42/assets/1/banner.png?foo' }
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'the source is already a full url' do
|
35
|
+
|
36
|
+
let(:source) { 'http://somewhere.net/sites/42/assets/1/banner.png' }
|
37
|
+
it { should eq 'http://somewhere.net/sites/42/assets/1/banner.png' }
|
38
|
+
|
39
|
+
describe 'also with https' do
|
40
|
+
|
41
|
+
let(:source) { 'https://somewhere.net/sites/42/assets/1/banner.png' }
|
42
|
+
it { should eq 'https://somewhere.net/sites/42/assets/1/banner.png' }
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'the host is a string' do
|
49
|
+
|
50
|
+
let(:host) { 'http://assets.locomotivecms.com' }
|
51
|
+
it { should eq 'http://assets.locomotivecms.com/sites/42/assets/1/banner.png' }
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'the host is a block' do
|
56
|
+
|
57
|
+
let(:request) { stub(ssl: true) }
|
58
|
+
let(:site) { stub(cdn: true) }
|
59
|
+
let(:host) { ->(request, site) { site.cdn ? "http#{request.ssl ? 's' : ''}://assets.locomotivecms.com" : nil } }
|
60
|
+
|
61
|
+
it { should eq 'https://assets.locomotivecms.com/sites/42/assets/1/banner.png' }
|
62
|
+
|
63
|
+
context 'with a different request var' do
|
64
|
+
|
65
|
+
let(:request) { stub(ssl: false) }
|
66
|
+
it { should eq 'http://assets.locomotivecms.com/sites/42/assets/1/banner.png' }
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'with a different site var' do
|
71
|
+
|
72
|
+
let(:site) { stub(cdn: false) }
|
73
|
+
it { should eq '/sites/42/assets/1/banner.png' }
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -2,45 +2,81 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Locomotive::Liquid::Drops::ContentEntry do
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@category = Locomotive::Liquid::Drops::ContentEntry.new(mock('category', projects: @list))
|
9
|
-
end
|
5
|
+
describe 'with a file' do
|
6
|
+
|
7
|
+
before { Locomotive::Site.any_instance.stubs(:create_default_pages!).returns(true) }
|
10
8
|
|
11
|
-
|
9
|
+
let(:content_type) { build_content_type }
|
10
|
+
let(:content_entry) { content_type.entries.build(title: 'Locomotive', description: 'Lorem ipsum....', _label_field_name: 'title', created_at: Time.zone.parse('2013-07-05 00:00:00'), file: FixturedAsset.open('5k.png'), updated_at: DateTime.parse('2007-06-29 21:00:00')) }
|
11
|
+
let(:content_entry_drop) { Locomotive::Liquid::Drops::ContentEntry.new(content_entry) }
|
12
12
|
|
13
|
-
|
14
|
-
template = %({% for project in category.projects %}{{ project }},{% endfor %})
|
13
|
+
describe 'displaying the timestamp' do
|
15
14
|
|
16
|
-
|
15
|
+
subject { render('{{ article.file.url }}', { 'article' => content_entry_drop }) }
|
16
|
+
|
17
|
+
it { should include '5k.png?1183150800' }
|
17
18
|
|
18
|
-
render(template, { 'category' => @category }).should == 'a,b,'
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
template = %({% with_scope order_by: 'name ASC', active: true %}{% for project in category.projects %}{{ project }},{% endfor %}{% endwith_scope %})
|
21
|
+
end
|
23
22
|
|
24
|
-
|
23
|
+
describe 'a list of entries' do
|
25
24
|
|
26
|
-
|
25
|
+
before(:each) do
|
26
|
+
@list = mock('list')
|
27
|
+
@list.stubs(:all).returns(true)
|
28
|
+
@category = Locomotive::Liquid::Drops::ContentEntry.new(mock('category', projects: @list))
|
27
29
|
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
+
context '#accessing a has_many relationship' do
|
32
|
+
|
33
|
+
it 'loops through the list' do
|
34
|
+
template = %({% for project in category.projects %}{{ project }},{% endfor %})
|
35
|
+
|
36
|
+
@list.expects(:ordered).returns(%w(a b))
|
37
|
+
|
38
|
+
render(template, { 'category' => @category }).should == 'a,b,'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'filters the list' do
|
42
|
+
template = %({% with_scope order_by: 'name ASC', active: true %}{% for project in category.projects %}{{ project }},{% endfor %}{% endwith_scope %})
|
31
43
|
|
32
|
-
|
44
|
+
@list.expects(:filtered).with({ 'active' => true }, ['name', 'ASC']).returns(%w(a b))
|
45
|
+
|
46
|
+
render(template, { 'category' => @category }).should == 'a,b,'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'filters the list and uses the default order' do
|
50
|
+
template = %({% with_scope active: true %}{% for project in category.projects %}{{ project }},{% endfor %}{% endwith_scope %})
|
51
|
+
|
52
|
+
@list.expects(:filtered).with({ 'active' => true }, nil).returns(%w(a b))
|
53
|
+
|
54
|
+
render(template, { 'category' => @category }).should == 'a,b,'
|
55
|
+
end
|
33
56
|
|
34
|
-
render(template, { 'category' => @category }).should == 'a,b,'
|
35
57
|
end
|
36
58
|
|
37
59
|
end
|
38
60
|
|
39
61
|
def render(template, assigns = {})
|
40
|
-
liquid_context = ::Liquid::Context.new(assigns, {}, {
|
62
|
+
liquid_context = ::Liquid::Context.new(assigns, {}, {
|
63
|
+
asset_host: TimestampAssetHost.new
|
64
|
+
})
|
41
65
|
|
42
66
|
output = ::Liquid::Template.parse(template).render(liquid_context)
|
43
67
|
output.gsub(/\n\s{0,}/, '')
|
44
68
|
end
|
45
69
|
|
70
|
+
def build_content_type
|
71
|
+
FactoryGirl.build(:content_type).tap do |content_type|
|
72
|
+
content_type.entries_custom_fields.build label: 'Title', type: 'string'
|
73
|
+
content_type.entries_custom_fields.build label: 'Description', type: 'text'
|
74
|
+
content_type.entries_custom_fields.build label: 'Visible ?', type: 'boolean', name: 'visible'
|
75
|
+
content_type.entries_custom_fields.build label: 'File', type: 'file'
|
76
|
+
content_type.entries_custom_fields.build label: 'Published at', type: 'date'
|
77
|
+
content_type.valid?
|
78
|
+
content_type.send(:set_label_field)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
46
82
|
end
|
@@ -236,12 +236,14 @@ describe Locomotive::Liquid::Filters::Html do
|
|
236
236
|
end
|
237
237
|
|
238
238
|
def build_context
|
239
|
+
|
239
240
|
klass = Class.new
|
240
241
|
klass.class_eval do
|
241
242
|
def registers
|
242
243
|
@registers ||= {
|
243
244
|
site: FactoryGirl.build(:site, id: fake_bson_id(42)),
|
244
|
-
theme_assets_checksum: {}
|
245
|
+
theme_assets_checksum: {},
|
246
|
+
asset_host: TimestampAssetHost.new
|
245
247
|
}
|
246
248
|
end
|
247
249
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Locomotive::Liquid::Tags::Editable::File do
|
4
|
+
|
5
|
+
before { Locomotive::Site.any_instance.stubs(:create_default_pages!).returns(true) }
|
6
|
+
|
7
|
+
let(:asset_host) { CdnAssetHost.new }
|
8
|
+
let(:page) { Locomotive::Page.new(updated_at: DateTime.parse('2007-06-29 21:00:00')) }
|
9
|
+
|
10
|
+
subject { render("{% editable_file banner %}http://www.placehold.it/500x500{% endeditable_file %}") }
|
11
|
+
|
12
|
+
describe 'no uploaded file' do
|
13
|
+
|
14
|
+
let(:asset_host) { IsoAssetHost.new }
|
15
|
+
before { add_editable_file({}) }
|
16
|
+
it { should eq 'http://www.placehold.it/500x500' }
|
17
|
+
|
18
|
+
context 'a timestamp is not applicable' do
|
19
|
+
|
20
|
+
let(:asset_host) { TimestampAssetHost.new }
|
21
|
+
it { should eq 'http://www.placehold.it/500x500' }
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'with a default source url' do
|
28
|
+
|
29
|
+
before { add_editable_file(default_source_url: '/assets/42/assets/1/foo.png') }
|
30
|
+
it { should eq 'http://cdn.locomotivecms.com/assets/42/assets/1/foo.png' }
|
31
|
+
|
32
|
+
context 'has a timestamp' do
|
33
|
+
|
34
|
+
let(:asset_host) { TimestampAssetHost.new }
|
35
|
+
it { should eq '/assets/42/assets/1/foo.png?1183150800' }
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'with an uploaded file' do
|
42
|
+
|
43
|
+
before { add_editable_file }
|
44
|
+
it { should match /^http:\/\/cdn\.locomotivecms\.com\/spec\/(.*)\/5k.png$/ }
|
45
|
+
|
46
|
+
context 'has a timestamp' do
|
47
|
+
|
48
|
+
let(:asset_host) { TimestampAssetHost.new }
|
49
|
+
it { should match /^\/spec\/(.*)\/5k.png\?1183150800$/ }
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def render(template, assigns = {})
|
56
|
+
liquid_context = ::Liquid::Context.new(assigns, {}, {
|
57
|
+
asset_host: asset_host,
|
58
|
+
page: page
|
59
|
+
}, true)
|
60
|
+
|
61
|
+
output = ::Liquid::Template.parse(template).render(liquid_context)
|
62
|
+
output.gsub(/\n\s{0,}/, '')
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_editable_file(attributes = nil)
|
66
|
+
attributes ||= { source: FixturedAsset.open('5k.png') }
|
67
|
+
|
68
|
+
editable_file = Locomotive::EditableFile.new({ slug: 'banner' }.merge(attributes))
|
69
|
+
page.editable_elements << editable_file
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -348,6 +348,37 @@ describe Locomotive::ContentType do
|
|
348
348
|
|
349
349
|
end
|
350
350
|
|
351
|
+
describe 'finding by id or slug' do
|
352
|
+
|
353
|
+
let(:content_type) { build_content_type }
|
354
|
+
let(:id_or_slug) { 'unknown' }
|
355
|
+
|
356
|
+
subject { Locomotive::ContentType.by_id_or_slug(id_or_slug).first }
|
357
|
+
|
358
|
+
before { content_type.save }
|
359
|
+
|
360
|
+
describe 'unknown id' do
|
361
|
+
|
362
|
+
it { should eq nil }
|
363
|
+
|
364
|
+
end
|
365
|
+
|
366
|
+
describe 'existing id' do
|
367
|
+
|
368
|
+
let(:id_or_slug) { content_type._id.to_s }
|
369
|
+
its(:name) { should eq 'My project' }
|
370
|
+
|
371
|
+
end
|
372
|
+
|
373
|
+
describe 'existing slug' do
|
374
|
+
|
375
|
+
let(:id_or_slug) { 'my_project' }
|
376
|
+
its(:name) { should eq 'My project' }
|
377
|
+
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
381
|
+
|
351
382
|
def build_content_type(options = {}, &block)
|
352
383
|
FactoryGirl.build(:content_type, options).tap do |content_type|
|
353
384
|
content_type.entries_custom_fields.build label: 'Name', type: 'string'
|
@@ -6,7 +6,7 @@ describe Locomotive::ThemeAsset do
|
|
6
6
|
|
7
7
|
let(:site) { FactoryGirl.build(:site, domains: %w{www.acme.com}) }
|
8
8
|
|
9
|
-
let(:asset) { FactoryGirl.build(:theme_asset, site: site) }
|
9
|
+
let(:asset) { FactoryGirl.build(:theme_asset, site: site, updated_at: DateTime.parse('2007/06/29 21:10:00')) }
|
10
10
|
|
11
11
|
describe 'attaching a file' do
|
12
12
|
|
@@ -145,21 +145,21 @@ describe Locomotive::ThemeAsset do
|
|
145
145
|
context 'simple url' do
|
146
146
|
|
147
147
|
let(:text) { "background: url(/images/banner.png) no-repeat 0 0" }
|
148
|
-
it { should == "background: url(http://engine.dev/images/banner.png) no-repeat 0 0" }
|
148
|
+
it { should == "background: url(http://engine.dev/images/banner.png?1183151400) no-repeat 0 0" }
|
149
149
|
|
150
150
|
end
|
151
151
|
|
152
152
|
context 'url with quotes' do
|
153
153
|
|
154
154
|
let(:text) { "background: url(\"/images/banner.png\") no-repeat 0 0" }
|
155
|
-
it { should == "background: url(\"http://engine.dev/images/banner.png\") no-repeat 0 0" }
|
155
|
+
it { should == "background: url(\"http://engine.dev/images/banner.png?1183151400\") no-repeat 0 0" }
|
156
156
|
|
157
157
|
end
|
158
158
|
|
159
159
|
context 'url with quotes and timestamps' do
|
160
160
|
|
161
161
|
let(:text) { "background: url(\"/images/banner.png?123456\") no-repeat 0 0" }
|
162
|
-
it { should == "background: url(\"http://engine.dev/images/banner.png\") no-repeat 0 0" }
|
162
|
+
it { should == "background: url(\"http://engine.dev/images/banner.png?1183151400\") no-repeat 0 0" }
|
163
163
|
|
164
164
|
end
|
165
165
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class IsoAssetHost
|
2
|
+
def compute(source, timestamp = nil)
|
3
|
+
source
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class CdnAssetHost
|
8
|
+
def compute(source, timestamp = nil)
|
9
|
+
"http://cdn.locomotivecms.com#{source}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class TimestampAssetHost
|
14
|
+
def compute(source, timestamp = nil)
|
15
|
+
timestamp ? "#{source}?#{timestamp}" : source
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: locomotive_cms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.5.
|
4
|
+
version: 2.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Didier Lafforgue
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -545,6 +545,7 @@ files:
|
|
545
545
|
- app/assets/images/locomotive/datepicker/ui-widget-content-top.png
|
546
546
|
- app/assets/images/locomotive/form/error-arrow.png
|
547
547
|
- app/assets/images/locomotive/form/input-sep.png
|
548
|
+
- app/assets/images/locomotive/icons/flags/bf.png
|
548
549
|
- app/assets/images/locomotive/icons/flags/bg.png
|
549
550
|
- app/assets/images/locomotive/icons/flags/cs.png
|
550
551
|
- app/assets/images/locomotive/icons/flags/de.png
|
@@ -554,6 +555,7 @@ files:
|
|
554
555
|
- app/assets/images/locomotive/icons/flags/fr.png
|
555
556
|
- app/assets/images/locomotive/icons/flags/it.png
|
556
557
|
- app/assets/images/locomotive/icons/flags/ja.png
|
558
|
+
- app/assets/images/locomotive/icons/flags/mg.png
|
557
559
|
- app/assets/images/locomotive/icons/flags/nb.png
|
558
560
|
- app/assets/images/locomotive/icons/flags/nl.png
|
559
561
|
- app/assets/images/locomotive/icons/flags/pl.png
|
@@ -1172,6 +1174,7 @@ files:
|
|
1172
1174
|
- lib/locomotive/httparty/webservice.rb
|
1173
1175
|
- lib/locomotive/kaminari.rb
|
1174
1176
|
- lib/locomotive/liquid.rb
|
1177
|
+
- lib/locomotive/liquid/asset_host.rb
|
1175
1178
|
- lib/locomotive/liquid/drops/base.rb
|
1176
1179
|
- lib/locomotive/liquid/drops/content_entry.rb
|
1177
1180
|
- lib/locomotive/liquid/drops/content_types.rb
|
@@ -1299,6 +1302,7 @@ files:
|
|
1299
1302
|
- spec/lib/core_ext_spec.rb
|
1300
1303
|
- spec/lib/locomotive/configuration_spec.rb
|
1301
1304
|
- spec/lib/locomotive/httparty/webservice_spec.rb
|
1305
|
+
- spec/lib/locomotive/liquid/asset_host_spec.rb
|
1302
1306
|
- spec/lib/locomotive/liquid/drops/content_entry_spec.rb
|
1303
1307
|
- spec/lib/locomotive/liquid/drops/current_user.rb
|
1304
1308
|
- spec/lib/locomotive/liquid/drops/page_spec.rb
|
@@ -1311,6 +1315,7 @@ files:
|
|
1311
1315
|
- spec/lib/locomotive/liquid/filters/translate_spec.rb
|
1312
1316
|
- spec/lib/locomotive/liquid/tags/consume_spec.rb
|
1313
1317
|
- spec/lib/locomotive/liquid/tags/csrf_spec.rb
|
1318
|
+
- spec/lib/locomotive/liquid/tags/editable/file_spec.rb
|
1314
1319
|
- spec/lib/locomotive/liquid/tags/editable/text_spec.rb
|
1315
1320
|
- spec/lib/locomotive/liquid/tags/extends_spec.rb
|
1316
1321
|
- spec/lib/locomotive/liquid/tags/javascript_spec.rb
|
@@ -1346,6 +1351,7 @@ files:
|
|
1346
1351
|
- spec/models/locomotive/theme_asset_spec.rb
|
1347
1352
|
- spec/requests/admin_ssl_spec.rb
|
1348
1353
|
- spec/requests/seo_trailing_slash_spec.rb
|
1354
|
+
- spec/support/asset_host_stubs.rb
|
1349
1355
|
- spec/support/carrierwave.rb
|
1350
1356
|
- spec/support/cells.rb
|
1351
1357
|
- spec/support/controller.rb
|
@@ -1532,6 +1538,7 @@ test_files:
|
|
1532
1538
|
- spec/lib/core_ext_spec.rb
|
1533
1539
|
- spec/lib/locomotive/configuration_spec.rb
|
1534
1540
|
- spec/lib/locomotive/httparty/webservice_spec.rb
|
1541
|
+
- spec/lib/locomotive/liquid/asset_host_spec.rb
|
1535
1542
|
- spec/lib/locomotive/liquid/drops/content_entry_spec.rb
|
1536
1543
|
- spec/lib/locomotive/liquid/drops/current_user.rb
|
1537
1544
|
- spec/lib/locomotive/liquid/drops/page_spec.rb
|
@@ -1544,6 +1551,7 @@ test_files:
|
|
1544
1551
|
- spec/lib/locomotive/liquid/filters/translate_spec.rb
|
1545
1552
|
- spec/lib/locomotive/liquid/tags/consume_spec.rb
|
1546
1553
|
- spec/lib/locomotive/liquid/tags/csrf_spec.rb
|
1554
|
+
- spec/lib/locomotive/liquid/tags/editable/file_spec.rb
|
1547
1555
|
- spec/lib/locomotive/liquid/tags/editable/text_spec.rb
|
1548
1556
|
- spec/lib/locomotive/liquid/tags/extends_spec.rb
|
1549
1557
|
- spec/lib/locomotive/liquid/tags/javascript_spec.rb
|
@@ -1579,6 +1587,7 @@ test_files:
|
|
1579
1587
|
- spec/models/locomotive/theme_asset_spec.rb
|
1580
1588
|
- spec/requests/admin_ssl_spec.rb
|
1581
1589
|
- spec/requests/seo_trailing_slash_spec.rb
|
1590
|
+
- spec/support/asset_host_stubs.rb
|
1582
1591
|
- spec/support/carrierwave.rb
|
1583
1592
|
- spec/support/cells.rb
|
1584
1593
|
- spec/support/controller.rb
|