locomotive_cms 2.0.1 → 2.0.2
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.
- data/app/controllers/locomotive/api/base_controller.rb +11 -0
- data/lib/locomotive/custom_fields.rb +1 -1
- data/lib/locomotive/middlewares/seo_trailing_slash.rb +26 -5
- data/lib/locomotive/version.rb +1 -1
- data/mongodb/migrate/20130204072721_make_editable_elements_consistent.rb +107 -0
- data/mongodb/migrate/20130326201349_rename_entry_to_content_entry.rb +107 -0
- data/spec/models/locomotive/content_entry_spec.rb +46 -33
- data/spec/requests/seo_trailing_slash_spec.rb +6 -0
- metadata +6 -4
@@ -15,6 +15,8 @@ module Locomotive
|
|
15
15
|
|
16
16
|
before_filter :set_current_thread_variables
|
17
17
|
|
18
|
+
rescue_from Exception, with: :render_access_denied
|
19
|
+
|
18
20
|
self.responder = Locomotive::ActionController::Responder # custom responder
|
19
21
|
|
20
22
|
respond_to :json, :xml
|
@@ -41,6 +43,15 @@ module Locomotive
|
|
41
43
|
self.setup_i18n_fallbacks
|
42
44
|
end
|
43
45
|
|
46
|
+
def render_access_denied(exception)
|
47
|
+
status = (case exception
|
48
|
+
when ::CanCan::AccessDenied then 401
|
49
|
+
when ::Mongoid::Errors::DocumentNotFound then 404
|
50
|
+
else 500
|
51
|
+
end)
|
52
|
+
render json: { error: exception.message }, status: status, layout: false
|
53
|
+
end
|
54
|
+
|
44
55
|
def self.cancan_resource_class
|
45
56
|
Locomotive::Api::CanCan::ControllerResource
|
46
57
|
end
|
@@ -38,7 +38,7 @@ module CustomFields
|
|
38
38
|
|
39
39
|
# Set correct paths
|
40
40
|
def store_dir
|
41
|
-
"sites/#{model.site_id}
|
41
|
+
"sites/#{model.site_id}/#{model.class.model_name.demodulize.underscore}/#{model.id}/files"
|
42
42
|
end
|
43
43
|
|
44
44
|
# In some situations, for instance, for the notification email when a content entry is created,
|
@@ -7,18 +7,39 @@ module Locomotive
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
-
path = env['PATH_INFO']
|
10
|
+
path, query = env['PATH_INFO'], env['QUERY_STRING']
|
11
11
|
|
12
12
|
if !path.starts_with?("#{Locomotive.mounted_on}/") && (match = path.match(%r{(.+)/$}))
|
13
|
-
|
14
|
-
|
15
|
-
response.finish
|
16
|
-
response.to_a
|
13
|
+
url = self.redirect_url(match[1], query)
|
14
|
+
self.redirect_to(url)
|
17
15
|
else
|
18
16
|
@app.call(env)
|
19
17
|
end
|
20
18
|
end
|
21
19
|
|
20
|
+
protected
|
21
|
+
|
22
|
+
# Create a 301 response and set it up accordingly.
|
23
|
+
#
|
24
|
+
# @params [ String ] url The url for the redirection
|
25
|
+
#
|
26
|
+
# @return [ Array ] It has the 3 parameters (status, header, body)
|
27
|
+
#
|
28
|
+
def redirect_to(url)
|
29
|
+
response = Rack::Response.new
|
30
|
+
response.redirect(url, 301) # moved permanently
|
31
|
+
response.finish
|
32
|
+
response.to_a
|
33
|
+
end
|
34
|
+
|
35
|
+
def redirect_url(base, query)
|
36
|
+
if query.blank?
|
37
|
+
base
|
38
|
+
else
|
39
|
+
"#{base}?#{query}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
22
43
|
end
|
23
44
|
end
|
24
45
|
end
|
data/lib/locomotive/version.rb
CHANGED
@@ -0,0 +1,107 @@
|
|
1
|
+
class MakeEditableElementsConsistent < MongoidMigration::Migration
|
2
|
+
def self.up
|
3
|
+
Locomotive::Site.all.each do |site|
|
4
|
+
site.locales.each do |locale|
|
5
|
+
::Mongoid::Fields::I18n.locale = locale
|
6
|
+
site.pages.each do |page|
|
7
|
+
puts "[#{site.name}] #{page.fullpath} (#{locale})"
|
8
|
+
|
9
|
+
found_elements = []
|
10
|
+
|
11
|
+
page.template.walk do |node, memo|
|
12
|
+
case node
|
13
|
+
when Locomotive::Liquid::Tags::InheritedBlock
|
14
|
+
puts "found block ! #{node.name} --- #{memo[:parent_block_name]}"
|
15
|
+
|
16
|
+
# set the new name based on a potential parent block
|
17
|
+
name = node.name.gsub(/[\"\']/o, '')
|
18
|
+
|
19
|
+
if memo[:parent_block_name] && !name.starts_with?(memo[:parent_block_name])
|
20
|
+
name = "#{memo[:parent_block_name]}/#{name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
puts "new_name = #{name}"
|
24
|
+
|
25
|
+
# retrieve all the editable elements of this block and set them the new name
|
26
|
+
page.find_editable_elements(node.name).each do |el|
|
27
|
+
# puts "**> hurray found the element #{el.block} _ #{el.slug}"
|
28
|
+
el.block = name
|
29
|
+
puts "**> hurray found the element #{el.block} _ #{el.slug} | #{page.find_editable_element(name, el.slug).present?.inspect}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# assign the new name to the block
|
33
|
+
node.instance_variable_set :@name, name
|
34
|
+
|
35
|
+
# record the new parent block name for children
|
36
|
+
memo[:parent_block_name] = name
|
37
|
+
|
38
|
+
when Locomotive::Liquid::Tags::Editable::ShortText,
|
39
|
+
Locomotive::Liquid::Tags::Editable::LongText,
|
40
|
+
Locomotive::Liquid::Tags::Editable::Control,
|
41
|
+
Locomotive::Liquid::Tags::Editable::File
|
42
|
+
|
43
|
+
puts "\tfound editable_element ! #{node.slug} --- #{memo[:parent_block_name]}"
|
44
|
+
|
45
|
+
slug = node.slug.gsub(/[\"\']/o, '')
|
46
|
+
|
47
|
+
# assign the new slug to the editable element
|
48
|
+
puts "\t\t...looking for #{node.slug} inside #{memo[:parent_block_name]}"
|
49
|
+
|
50
|
+
options = node.instance_variable_get :@options
|
51
|
+
block = options[:block].blank? ? memo[:parent_block_name] : options[:block]
|
52
|
+
|
53
|
+
if el = page.find_editable_element(block, node.slug)
|
54
|
+
puts "\t\t--> yep found the element"
|
55
|
+
|
56
|
+
el.slug = slug
|
57
|
+
el.block = memo[:parent_block_name] # just to make sure
|
58
|
+
|
59
|
+
node.instance_variable_set :@slug, slug
|
60
|
+
|
61
|
+
options.delete(:block)
|
62
|
+
node.instance_variable_set :@block, nil # just to make sure
|
63
|
+
|
64
|
+
found_elements << el._id
|
65
|
+
else
|
66
|
+
puts "\t\t[WARNING] el not found (#{block} - #{slug})"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
memo
|
72
|
+
end # page walk
|
73
|
+
|
74
|
+
puts "found elements = #{found_elements.join(', ')} / #{page.editable_elements.count}"
|
75
|
+
|
76
|
+
# "hide" useless editable elements
|
77
|
+
page.editable_elements.each do |el|
|
78
|
+
next if found_elements.include?(el._id)
|
79
|
+
el.disabled = true
|
80
|
+
end
|
81
|
+
|
82
|
+
# serialize
|
83
|
+
page.send(:_serialize_template)
|
84
|
+
|
85
|
+
# puts page.template.inspect
|
86
|
+
|
87
|
+
# save ?
|
88
|
+
page.instance_variable_set :@template_changed, false
|
89
|
+
page.save
|
90
|
+
|
91
|
+
# TODO:
|
92
|
+
# x ", block: 'Asset #1'"" ???? les re-assigner a "main" d'une facon ou d'une autre
|
93
|
+
# => en fait, ce sont des editable elements qui n'ont pas vrais blocks
|
94
|
+
# x hide useless editable elements
|
95
|
+
# x re-serializer le template
|
96
|
+
# ? skipper la methode parse (quoique pas besoin car template non modifie)
|
97
|
+
# x snippets
|
98
|
+
# x sauvegarder (sans callbacks ??)
|
99
|
+
end # loop: pages
|
100
|
+
end # loop: locales
|
101
|
+
end # loop: sites
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.down
|
105
|
+
raise MongoidMigration::IrreversibleMigration
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
class RenameEntryToContentEntry < MongoidMigration::Migration
|
2
|
+
|
3
|
+
def self.up
|
4
|
+
regexp = /(^|Locomotive::)(Content)*Entry([0-9a-fA-F]{24})$/
|
5
|
+
replacement = "\\1ContentEntry\\3"
|
6
|
+
|
7
|
+
# content entries
|
8
|
+
self.update_content_entries(regexp, replacement)
|
9
|
+
|
10
|
+
# content types
|
11
|
+
self.update_content_types(regexp, replacement)
|
12
|
+
|
13
|
+
# templatized pages
|
14
|
+
self.update_templatized_pages(regexp, replacement)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.down
|
18
|
+
regexp = /(^|Locomotive::)(Content)+Entry([0-9a-fA-F]{24})$/
|
19
|
+
replacement = "\\1Entry\\3"
|
20
|
+
|
21
|
+
# content entries
|
22
|
+
self.update_content_entries(regexp, replacement)
|
23
|
+
|
24
|
+
# content types
|
25
|
+
self.update_content_types(regexp, replacement)
|
26
|
+
|
27
|
+
# templatized pages
|
28
|
+
self.update_templatized_pages(regexp, replacement)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def self.update_content_entries(regexp, replacement)
|
34
|
+
self.fetch_rows(Locomotive::ContentEntry) do |collection, attributes|
|
35
|
+
recipe = attributes['custom_fields_recipe']
|
36
|
+
type = attributes['_type']
|
37
|
+
new_recipe = replace_value_by(recipe, regexp, replacement)
|
38
|
+
selector = { '_id' => attributes['_id'] }
|
39
|
+
operations = { '$set' => {
|
40
|
+
'_type' => replace_value_by(type, regexp, replacement),
|
41
|
+
'custom_fields_recipe' => new_recipe
|
42
|
+
} }
|
43
|
+
collection.update selector, operations
|
44
|
+
end
|
45
|
+
puts "content entries UPDATED"
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.update_content_types(regexp, replacement)
|
49
|
+
self.fetch_rows(Locomotive::ContentType) do |collection, attributes|
|
50
|
+
updates = {}
|
51
|
+
attributes['entries_custom_fields'].each_with_index do |custom_field, index|
|
52
|
+
new_custom_field = replace_value_by(custom_field, regexp, replacement)
|
53
|
+
updates["entries_custom_fields.#{index}"] = new_custom_field
|
54
|
+
end
|
55
|
+
|
56
|
+
selector = { '_id' => attributes['_id'] }
|
57
|
+
operations = { '$set' => updates }
|
58
|
+
collection.update selector, operations
|
59
|
+
end
|
60
|
+
puts "content types UPDATED"
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.update_templatized_pages(regexp, replacement)
|
64
|
+
self.fetch_rows(Locomotive::Page) do |collection, attributes|
|
65
|
+
if klass_name = attributes['target_klass_name']
|
66
|
+
new_klass_name = replace_value_by(klass_name, regexp, replacement)
|
67
|
+
selector = { '_id' => attributes['_id'] }
|
68
|
+
operations = { '$set' => { 'target_klass_name' => new_klass_name } }
|
69
|
+
collection.update selector, operations
|
70
|
+
end
|
71
|
+
end
|
72
|
+
puts "templatized pages UPDATED"
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.fetch_rows(klass, &block)
|
76
|
+
per_page = 100
|
77
|
+
collection = klass.collection.driver
|
78
|
+
count = collection.count
|
79
|
+
num_pages = (count.to_f / per_page).floor
|
80
|
+
|
81
|
+
# paginate the whole collection to avoid mongodb cursor error
|
82
|
+
(0..num_pages).each do |page|
|
83
|
+
offset = per_page * page.to_i
|
84
|
+
collection.find.sort("_id").limit(per_page).skip(offset).each do |attributes|
|
85
|
+
block.call(collection, attributes)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.replace_value_by(object, regexp, replacement)
|
91
|
+
return object.gsub(regexp, replacement) if object.is_a?(String)
|
92
|
+
|
93
|
+
list = nil
|
94
|
+
list = object if object.is_a?(Array)
|
95
|
+
list = object.values if object.is_a?(Hash)
|
96
|
+
|
97
|
+
list.each do |value|
|
98
|
+
case value
|
99
|
+
when String then value.gsub!(regexp, replacement)
|
100
|
+
when Array, Hash then self.replace_value_by(value, regexp, replacement)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
object
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -7,10 +7,10 @@ describe Locomotive::ContentEntry do
|
|
7
7
|
before(:each) do
|
8
8
|
Locomotive::Site.any_instance.stubs(:create_default_pages!).returns(true)
|
9
9
|
@content_type = FactoryGirl.build(:content_type)
|
10
|
-
@content_type.entries_custom_fields.build :
|
11
|
-
@content_type.entries_custom_fields.build :
|
12
|
-
@content_type.entries_custom_fields.build :
|
13
|
-
@content_type.entries_custom_fields.build :
|
10
|
+
@content_type.entries_custom_fields.build label: 'Title', type: 'string'
|
11
|
+
@content_type.entries_custom_fields.build label: 'Description', type: 'text'
|
12
|
+
@content_type.entries_custom_fields.build label: 'Visible ?', type: 'boolean', name: 'visible'
|
13
|
+
@content_type.entries_custom_fields.build label: 'File', type: 'file'
|
14
14
|
@content_type.valid?
|
15
15
|
@content_type.send(:set_label_field)
|
16
16
|
end
|
@@ -24,56 +24,57 @@ describe Locomotive::ContentEntry do
|
|
24
24
|
## Validations ##
|
25
25
|
|
26
26
|
it 'requires the presence of title' do
|
27
|
-
content_entry = build_content_entry :
|
27
|
+
content_entry = build_content_entry title: nil
|
28
28
|
content_entry.should_not be_valid
|
29
29
|
content_entry.errors[:title].should == ["can't be blank"]
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'requires the presence of the permalink (_slug)' do
|
33
|
-
content_entry = build_content_entry :
|
33
|
+
content_entry = build_content_entry title: nil
|
34
34
|
content_entry.should_not be_valid
|
35
35
|
content_entry.errors[:_slug].should == ["can't be blank"]
|
36
36
|
end
|
37
37
|
|
38
38
|
end
|
39
39
|
|
40
|
-
|
40
|
+
describe '#slug' do
|
41
|
+
|
41
42
|
before :each do
|
42
|
-
build_content_entry(:
|
43
|
+
build_content_entry(_slug: 'dogs').tap(&:save!)._slug.should == 'dogs'
|
43
44
|
end
|
44
45
|
|
45
46
|
it 'uses the given slug if it is unique' do
|
46
|
-
build_content_entry(:
|
47
|
-
build_content_entry(:
|
47
|
+
build_content_entry(_slug: 'monkeys').tap(&:save!)._slug.should == 'monkeys'
|
48
|
+
build_content_entry(_slug: 'cats-2').tap(&:save!)._slug.should == 'cats-2'
|
48
49
|
end
|
49
50
|
|
50
51
|
it 'appends a number to the end of the slug if it is not unique' do
|
51
|
-
build_content_entry(:
|
52
|
-
build_content_entry(:
|
53
|
-
build_content_entry(:
|
54
|
-
build_content_entry(:
|
52
|
+
build_content_entry(_slug: 'dogs').tap(&:save!)._slug.should == 'dogs-1'
|
53
|
+
build_content_entry(_slug: 'dogs').tap(&:save!)._slug.should == 'dogs-2'
|
54
|
+
build_content_entry(_slug: 'dogs-2').tap(&:save!)._slug.should == 'dogs-3'
|
55
|
+
build_content_entry(_slug: 'dogs-2').tap(&:save!)._slug.should == 'dogs-4'
|
55
56
|
end
|
56
57
|
|
57
58
|
it 'ignores the case of a slug' do
|
58
|
-
build_content_entry(:
|
59
|
-
build_content_entry(:
|
59
|
+
build_content_entry(_slug: 'dogs').tap(&:save!)._slug.should == 'dogs-1'
|
60
|
+
build_content_entry(_slug: 'DOGS').tap(&:save!)._slug.should == 'dogs-2'
|
60
61
|
end
|
61
62
|
|
62
63
|
it 'correctly handles slugs with multiple numbers' do
|
63
|
-
build_content_entry(:
|
64
|
-
build_content_entry(:
|
64
|
+
build_content_entry(_slug: 'fish-1-2').tap(&:save!)._slug.should == 'fish-1-2'
|
65
|
+
build_content_entry(_slug: 'fish-1-2').tap(&:save!)._slug.should == 'fish-1-3'
|
65
66
|
|
66
|
-
build_content_entry(:
|
67
|
-
build_content_entry(:
|
67
|
+
build_content_entry(_slug: 'fish-1-hi').tap(&:save!)._slug.should == 'fish-1-hi'
|
68
|
+
build_content_entry(_slug: 'fish-1-hi').tap(&:save!)._slug.should == 'fish-1-hi-1'
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
71
|
-
|
72
|
+
describe '#i18n' do
|
72
73
|
|
73
74
|
before(:each) do
|
74
75
|
localize_content_type @content_type
|
75
76
|
::Mongoid::Fields::I18n.locale = 'en'
|
76
|
-
@content_entry = build_content_entry(:
|
77
|
+
@content_entry = build_content_entry(title: 'Hello world')
|
77
78
|
::Mongoid::Fields::I18n.locale = 'fr'
|
78
79
|
end
|
79
80
|
|
@@ -90,12 +91,13 @@ describe Locomotive::ContentEntry do
|
|
90
91
|
end
|
91
92
|
|
92
93
|
describe "#navigation" do
|
94
|
+
|
93
95
|
before(:each) do
|
94
96
|
@content_type.order_by = '_position'
|
95
97
|
@content_type.save
|
96
98
|
|
97
99
|
%w(first second third).each_with_index do |item, index|
|
98
|
-
content = build_content_entry(:
|
100
|
+
content = build_content_entry(title: item.to_s, _position: index)
|
99
101
|
content.save
|
100
102
|
instance_variable_set "@#{item}", content
|
101
103
|
end
|
@@ -196,24 +198,35 @@ describe Locomotive::ContentEntry do
|
|
196
198
|
end
|
197
199
|
|
198
200
|
it 'uses the to_label method if the value of the label field defined it' do
|
199
|
-
entry = build_content_entry(:
|
200
|
-
entry.stubs(:with_to_label).returns(mock('with_to_label', :
|
201
|
+
entry = build_content_entry(_label_field_name: 'with_to_label')
|
202
|
+
entry.stubs(:with_to_label).returns(mock('with_to_label', to_label: 'acme'))
|
201
203
|
entry._label.should == 'acme'
|
202
204
|
end
|
203
205
|
|
204
206
|
it 'uses the to_s method at last if the label field did not define the to_label method' do
|
205
|
-
entry = build_content_entry(:
|
206
|
-
entry.stubs(:not_a_string).returns(mock('not_a_string', :
|
207
|
+
entry = build_content_entry(_label_field_name: 'not_a_string')
|
208
|
+
entry.stubs(:not_a_string).returns(mock('not_a_string', to_s: 'not_a_string'))
|
207
209
|
entry._label.should == 'not_a_string'
|
208
210
|
end
|
209
211
|
|
210
212
|
end
|
211
213
|
|
214
|
+
describe '#file' do
|
215
|
+
|
216
|
+
let(:entry) { build_content_entry(title: 'Hello world', file: FixturedAsset.open('5k.png')) }
|
217
|
+
|
218
|
+
it 'writes the file to the filesystem' do
|
219
|
+
entry.save
|
220
|
+
entry.file.url.should_not =~ /content_content_entry/
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
212
225
|
describe '#public_submission' do
|
213
226
|
|
214
227
|
before(:each) do
|
215
|
-
@account_1 = FactoryGirl.build('admin user', :
|
216
|
-
@account_2 = FactoryGirl.build('frenchy user', :
|
228
|
+
@account_1 = FactoryGirl.build('admin user', id: fake_bson_id('1'))
|
229
|
+
@account_2 = FactoryGirl.build('frenchy user', id: fake_bson_id('2'))
|
217
230
|
|
218
231
|
@content_type.public_submission_enabled = true
|
219
232
|
@content_type.public_submission_accounts = ['', @account_1._id, @account_2._id.to_s]
|
@@ -221,7 +234,7 @@ describe Locomotive::ContentEntry do
|
|
221
234
|
site = FactoryGirl.build(:site)
|
222
235
|
site.stubs(:accounts).returns([@account_1, @account_2])
|
223
236
|
|
224
|
-
@content_entry = build_content_entry(:
|
237
|
+
@content_entry = build_content_entry(site: site)
|
225
238
|
end
|
226
239
|
|
227
240
|
it 'does not send email notifications if the api is disabled' do
|
@@ -237,8 +250,8 @@ describe Locomotive::ContentEntry do
|
|
237
250
|
end
|
238
251
|
|
239
252
|
it 'sends email notifications when a new instance is created' do
|
240
|
-
Locomotive::Notifications.expects(:new_content_entry).with(@account_1, @content_entry).returns(mock('mailer', :
|
241
|
-
Locomotive::Notifications.expects(:new_content_entry).with(@account_2, @content_entry).returns(mock('mailer', :
|
253
|
+
Locomotive::Notifications.expects(:new_content_entry).with(@account_1, @content_entry).returns(mock('mailer', deliver: true))
|
254
|
+
Locomotive::Notifications.expects(:new_content_entry).with(@account_2, @content_entry).returns(mock('mailer', deliver: true))
|
242
255
|
@content_entry.save
|
243
256
|
end
|
244
257
|
|
@@ -260,7 +273,7 @@ describe Locomotive::ContentEntry do
|
|
260
273
|
end
|
261
274
|
|
262
275
|
def build_content_entry(options = {})
|
263
|
-
@content_type.entries.build({ :
|
276
|
+
@content_type.entries.build({ title: 'Locomotive', description: 'Lorem ipsum....', _label_field_name: 'title' }.merge(options))
|
264
277
|
end
|
265
278
|
|
266
279
|
def fake_bson_id(id)
|
@@ -22,4 +22,10 @@ describe 'Locomotive::Middlewares::SeoTrailingSlash' do
|
|
22
22
|
response.status.should be(301)
|
23
23
|
end
|
24
24
|
|
25
|
+
it 'removes the trailing slash but preserves the query' do
|
26
|
+
get '/hello_world/?test=name'
|
27
|
+
response.status.should be(301)
|
28
|
+
response.location.should == '/hello_world?test=name'
|
29
|
+
end
|
30
|
+
|
25
31
|
end
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: locomotive_cms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 2.0.
|
5
|
+
version: 2.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Didier Lafforgue
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
type: :runtime
|
@@ -1104,6 +1104,8 @@ files:
|
|
1104
1104
|
- lib/locomotive_cms.rb
|
1105
1105
|
- lib/tasks/development.rake
|
1106
1106
|
- lib/tasks/locomotive.rake
|
1107
|
+
- mongodb/migrate/20130204072721_make_editable_elements_consistent.rb
|
1108
|
+
- mongodb/migrate/20130326201349_rename_entry_to_content_entry.rb
|
1107
1109
|
- public/favicon.ico
|
1108
1110
|
- vendor/assets/fonts/locomotive/fontawesome-webfont.eot
|
1109
1111
|
- vendor/assets/fonts/locomotive/fontawesome-webfont.svg
|
@@ -1337,7 +1339,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
1337
1339
|
- !ruby/object:Gem::Version
|
1338
1340
|
segments:
|
1339
1341
|
- 0
|
1340
|
-
hash: -
|
1342
|
+
hash: -4389909398335603459
|
1341
1343
|
version: '0'
|
1342
1344
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
1343
1345
|
none: false
|
@@ -1346,7 +1348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1346
1348
|
- !ruby/object:Gem::Version
|
1347
1349
|
segments:
|
1348
1350
|
- 0
|
1349
|
-
hash: -
|
1351
|
+
hash: -4389909398335603459
|
1350
1352
|
version: '0'
|
1351
1353
|
requirements: []
|
1352
1354
|
rubyforge_project:
|