locomotivecms_mounter_pull_19 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +7 -0
  6. data/Gemfile.lock +131 -0
  7. data/MIT-LICENSE +20 -0
  8. data/NOTES +4 -0
  9. data/README.md +26 -0
  10. data/Rakefile +37 -0
  11. data/TODO +82 -0
  12. data/init.rb +2 -0
  13. data/lib/locomotive/mounter.rb +106 -0
  14. data/lib/locomotive/mounter/config.rb +21 -0
  15. data/lib/locomotive/mounter/engine_api.rb +205 -0
  16. data/lib/locomotive/mounter/exceptions.rb +44 -0
  17. data/lib/locomotive/mounter/extensions/compass.rb +38 -0
  18. data/lib/locomotive/mounter/extensions/httmultiparty.rb +22 -0
  19. data/lib/locomotive/mounter/extensions/sprockets.rb +46 -0
  20. data/lib/locomotive/mounter/extensions/tilt/css.rb +31 -0
  21. data/lib/locomotive/mounter/fields.rb +254 -0
  22. data/lib/locomotive/mounter/models/base.rb +42 -0
  23. data/lib/locomotive/mounter/models/content_asset.rb +84 -0
  24. data/lib/locomotive/mounter/models/content_entry.rb +372 -0
  25. data/lib/locomotive/mounter/models/content_field.rb +190 -0
  26. data/lib/locomotive/mounter/models/content_select_option.rb +29 -0
  27. data/lib/locomotive/mounter/models/content_type.rb +274 -0
  28. data/lib/locomotive/mounter/models/editable_element.rb +27 -0
  29. data/lib/locomotive/mounter/models/page.rb +442 -0
  30. data/lib/locomotive/mounter/models/site.rb +28 -0
  31. data/lib/locomotive/mounter/models/snippet.rb +55 -0
  32. data/lib/locomotive/mounter/models/theme_asset.rb +148 -0
  33. data/lib/locomotive/mounter/models/translation.rb +28 -0
  34. data/lib/locomotive/mounter/mounting_point.rb +65 -0
  35. data/lib/locomotive/mounter/reader/api.rb +64 -0
  36. data/lib/locomotive/mounter/reader/api/base.rb +67 -0
  37. data/lib/locomotive/mounter/reader/api/content_assets_reader.rb +39 -0
  38. data/lib/locomotive/mounter/reader/api/content_entries_reader.rb +142 -0
  39. data/lib/locomotive/mounter/reader/api/content_types_reader.rb +76 -0
  40. data/lib/locomotive/mounter/reader/api/pages_reader.rb +192 -0
  41. data/lib/locomotive/mounter/reader/api/site_reader.rb +42 -0
  42. data/lib/locomotive/mounter/reader/api/snippets_reader.rb +61 -0
  43. data/lib/locomotive/mounter/reader/api/theme_assets_reader.rb +42 -0
  44. data/lib/locomotive/mounter/reader/api/translations_reader.rb +30 -0
  45. data/lib/locomotive/mounter/reader/file_system.rb +43 -0
  46. data/lib/locomotive/mounter/reader/file_system/base.rb +65 -0
  47. data/lib/locomotive/mounter/reader/file_system/content_assets_reader.rb +90 -0
  48. data/lib/locomotive/mounter/reader/file_system/content_entries_reader.rb +97 -0
  49. data/lib/locomotive/mounter/reader/file_system/content_types_reader.rb +88 -0
  50. data/lib/locomotive/mounter/reader/file_system/pages_reader.rb +211 -0
  51. data/lib/locomotive/mounter/reader/file_system/site_reader.rb +27 -0
  52. data/lib/locomotive/mounter/reader/file_system/snippets_reader.rb +115 -0
  53. data/lib/locomotive/mounter/reader/file_system/theme_assets_reader.rb +83 -0
  54. data/lib/locomotive/mounter/reader/file_system/translations_reader.rb +36 -0
  55. data/lib/locomotive/mounter/reader/runner.rb +89 -0
  56. data/lib/locomotive/mounter/utils/hash.rb +31 -0
  57. data/lib/locomotive/mounter/utils/output.rb +124 -0
  58. data/lib/locomotive/mounter/utils/string.rb +40 -0
  59. data/lib/locomotive/mounter/utils/yaml.rb +125 -0
  60. data/lib/locomotive/mounter/utils/yaml_front_matters_template.rb +45 -0
  61. data/lib/locomotive/mounter/version.rb +8 -0
  62. data/lib/locomotive/mounter/writer/api.rb +74 -0
  63. data/lib/locomotive/mounter/writer/api/base.rb +172 -0
  64. data/lib/locomotive/mounter/writer/api/content_assets_writer.rb +74 -0
  65. data/lib/locomotive/mounter/writer/api/content_entries_writer.rb +227 -0
  66. data/lib/locomotive/mounter/writer/api/content_types_writer.rb +151 -0
  67. data/lib/locomotive/mounter/writer/api/pages_writer.rb +250 -0
  68. data/lib/locomotive/mounter/writer/api/site_writer.rb +125 -0
  69. data/lib/locomotive/mounter/writer/api/snippets_writer.rb +111 -0
  70. data/lib/locomotive/mounter/writer/api/theme_assets_writer.rb +201 -0
  71. data/lib/locomotive/mounter/writer/api/translations_writer.rb +85 -0
  72. data/lib/locomotive/mounter/writer/file_system.rb +44 -0
  73. data/lib/locomotive/mounter/writer/file_system/base.rb +70 -0
  74. data/lib/locomotive/mounter/writer/file_system/content_assets_writer.rb +38 -0
  75. data/lib/locomotive/mounter/writer/file_system/content_entries_writer.rb +33 -0
  76. data/lib/locomotive/mounter/writer/file_system/content_types_writer.rb +32 -0
  77. data/lib/locomotive/mounter/writer/file_system/pages_writer.rb +93 -0
  78. data/lib/locomotive/mounter/writer/file_system/site_writer.rb +30 -0
  79. data/lib/locomotive/mounter/writer/file_system/snippets_writer.rb +59 -0
  80. data/lib/locomotive/mounter/writer/file_system/theme_assets_writer.rb +72 -0
  81. data/lib/locomotive/mounter/writer/file_system/translations_writer.rb +29 -0
  82. data/lib/locomotive/mounter/writer/runner.rb +73 -0
  83. data/locomotivecms_mounter.gemspec +64 -0
  84. metadata +539 -0
@@ -0,0 +1,27 @@
1
+ module Locomotive
2
+ module Mounter
3
+ module Models
4
+
5
+ class EditableElement < Base
6
+
7
+ ## fields ##
8
+ field :content, localized: true
9
+
10
+ ## other accessors
11
+ attr_accessor :block, :slug
12
+
13
+ ## methods ##
14
+
15
+ def to_params
16
+ { block: self.block, slug: self.slug, content: self.content }
17
+ end
18
+
19
+ def to_yaml
20
+ { "#{block}/#{slug}" => content }
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,442 @@
1
+ # encoding: UTF-8
2
+ module Locomotive
3
+ module Mounter
4
+ module Models
5
+
6
+ class Page < Base
7
+
8
+ ## fields ##
9
+ field :parent, association: true
10
+ field :title, localized: true
11
+ field :slug, localized: true
12
+ field :fullpath, localized: true
13
+ field :is_layout, default: false
14
+ field :allow_layout, default: false
15
+ field :redirect_url, localized: true
16
+ field :redirect_type, default: 301
17
+ field :template, localized: true
18
+ field :handle
19
+ field :listed, default: false
20
+ field :searchable
21
+ field :templatized, default: false
22
+ field :content_type
23
+ field :published, default: true
24
+ field :cache_strategy
25
+ field :response_type
26
+ field :position
27
+
28
+ field :seo_title, localized: true
29
+ field :meta_keywords, localized: true
30
+ field :meta_description, localized: true
31
+
32
+ field :editable_elements, type: :array, class_name: 'Locomotive::Mounter::Models::EditableElement'
33
+
34
+ ## other accessors ##
35
+ attr_accessor :content_type_id, :content_entry, :parent_id, :children, :templatized_from_parent
36
+
37
+ ## path to the file of the template (if mounted from a FS) ##
38
+ attr_accessor :filepath
39
+
40
+ ## aliases ##
41
+ alias :listed? :listed
42
+ alias :published? :published
43
+ alias :templatized? :templatized
44
+ alias :searchable? :searchable
45
+ alias :is_layout? :is_layout
46
+
47
+ ## methods ##
48
+
49
+ # Tell if the page is either the index page.
50
+ #
51
+ # @return [ Boolean ] True if index page.
52
+ #
53
+ def index?
54
+ self.depth == 0 && 'index' == self.slug
55
+ end
56
+
57
+ # Tell if the page is either the index or the 404 page.
58
+ #
59
+ # @return [ Boolean ] True if index or 404 page.
60
+ #
61
+ def index_or_404?
62
+ self.depth == 0 && %w(index 404).include?(self.slug)
63
+ end
64
+
65
+ # Return the fullpath dasherized and with the "*" character
66
+ # for the slug of templatized page.
67
+ #
68
+ # @param [ Boolean ] wildcard If true, replace the slug of a templatized page by the "*" character (default: true)
69
+ #
70
+ # @return [ String ] The safe full path or nil if the page is not translated in the current locale
71
+ #
72
+ def safe_fullpath(wildcard = true)
73
+ if self.index_or_404?
74
+ self.slug
75
+ else
76
+ base = self.parent.safe_fullpath(wildcard)
77
+ _slug = if self.templatized? && !self.templatized_from_parent
78
+ wildcard ? '*' : self.slug
79
+ elsif !self.translated_in?(Locomotive::Mounter.locale)
80
+ self.slug_translations[self.mounting_point.default_locale]
81
+ else
82
+ self.slug
83
+ end
84
+ (base == 'index' ? _slug : File.join(base, _slug)).dasherize
85
+ end
86
+ end
87
+
88
+ # Return the fullpath in the current locale. If it does not exist,
89
+ # return the one of the main locale.
90
+ #
91
+ # @return [ String ] A non-blank fullpath
92
+ #
93
+ def fullpath_or_default
94
+ self.fullpath || self.fullpath_in_default_locale
95
+ end
96
+
97
+ # Return the fullpath in the default locale no matter the current locale is.
98
+ #
99
+ # @return [ String ] The fullpath
100
+ #
101
+ def fullpath_in_default_locale
102
+ self.fullpath_translations[self.mounting_point.default_locale]
103
+ end
104
+
105
+ # Get the id of the parent page.
106
+ #
107
+ # @return [ String ] The _id attribute of the parent page
108
+ #
109
+ def parent_id
110
+ @parent_id || self.parent.try(:_id)
111
+ end
112
+
113
+ # Force the translations of a page
114
+ #
115
+ # @param [ Array ] locales List of locales (Symbol or String)
116
+ #
117
+ def translated_in=(locales)
118
+ self._locales = locales.map(&:to_sym)
119
+ end
120
+
121
+ # Set the content type, attribute required for templatized page.
122
+ # @deprecated. Use content_type= instead.
123
+ #
124
+ # @param [ Object ] content_type The content type
125
+ #
126
+ def model=(content_type)
127
+ Locomotive::Mounter.logger.warn 'The model attribute is deprecated. Use content_type instead.'
128
+ self.content_type = content_type
129
+ end
130
+
131
+ # Modified setter in order to set correctly the slug
132
+ #
133
+ # @param [ String ] fullpath The fullpath
134
+ #
135
+ def fullpath_with_setting_slug=(fullpath)
136
+ if fullpath && self.slug.nil?
137
+ self.slug = File.basename(fullpath)
138
+ end
139
+
140
+ self.fullpath_without_setting_slug = fullpath
141
+ end
142
+
143
+ alias_method_chain :fullpath=, :setting_slug
144
+
145
+ # Depth of the page in the site tree.
146
+ # Both the index and 404 pages are 0-depth.
147
+ #
148
+ # @return [ Integer ] The depth
149
+ #
150
+ def depth
151
+ return 0 if %w(index 404).include?(self.fullpath)
152
+ self.fullpath_or_default.split('/').size
153
+ end
154
+
155
+ # Depth and position in the site tree
156
+ #
157
+ # @return [ Integer ] An unique id corresponding to the depth and position
158
+ #
159
+ def depth_and_position
160
+ self.depth * 1000 + (self.position || 199)
161
+ end
162
+
163
+ # Tell if the page extends the template of another page.
164
+ # Basically, we check if the template of the page includes
165
+ # the "extends" liquid tag.
166
+ #
167
+ # @return [ Boolean ] True if the template can be a layout.
168
+ #
169
+ def extends_template?
170
+ !self.template_fullpath.nil?
171
+ end
172
+
173
+ # Return the fullpath of the page whose template is extended
174
+ # in the current template.
175
+ #
176
+ # @return [ String ] The fullpath of the "extended" page or nil if no extends tag
177
+ #
178
+ def template_fullpath
179
+ return nil if self.source.nil? || self.source.strip.blank?
180
+
181
+ self.source =~ /\{%\s*extends\s+\'?([[\w|\-|\_]|\/]+)\'?\s*%\}/
182
+ $1
183
+ end
184
+
185
+ # Is it a redirect page ?
186
+ #
187
+ # @return [ Boolean ] True if the redirect_url property is set
188
+ #
189
+ def redirect?
190
+ !self.redirect_url.blank?
191
+ end
192
+
193
+ # Add a child to the page. It also sets the parent of the child.
194
+ # If the parent page is a templatized one, give the same properties to the child.
195
+ #
196
+ # @param [ Object ] page The child page
197
+ #
198
+ # @return [ Object ] The child page
199
+ #
200
+ def add_child(page)
201
+ page.parent = self
202
+
203
+ if self.templatized?
204
+ page.templatized_from_parent = true
205
+
206
+ # copy properties from the parent
207
+ %w(templatized content_type content_type_id).each do |name|
208
+ page.send(:"#{name}=", self.send(name.to_sym))
209
+ end
210
+ end
211
+
212
+ (self.children ||= []) << page
213
+
214
+ self.children.sort! { |a, b| (a.position || 999) <=> (b.position || 999) }
215
+
216
+ page
217
+ end
218
+
219
+ # Build or update the list of editable elements from a hash whose
220
+ # keys are the couple "[block]/[slug]" and the values the content
221
+ # of the editable elements OR an array of attributes
222
+ #
223
+ # @param [ Hash / Array ] attributes The attributes of the editable elements
224
+ #
225
+ def set_editable_elements(attributes)
226
+ return if attributes.blank?
227
+
228
+ self.editable_elements ||= []
229
+
230
+ attributes.to_a.each do |_attributes|
231
+ if _attributes.is_a?(Array) # attributes is maybe a Hash
232
+ _splashed = _attributes.first.split('/')
233
+
234
+ block, slug = _splashed[0..-2].join('/'), _splashed.last
235
+ block = nil if block.blank?
236
+
237
+ _attributes = { 'block' => block, 'slug' => slug, 'content' => _attributes.last }
238
+ end
239
+
240
+ # does an editable element exist with the same couple block/slug ?
241
+ if editable_element = self.find_editable_element(_attributes['block'], _attributes['slug'])
242
+ editable_element.content = _attributes['content']
243
+ else
244
+ self.editable_elements << Locomotive::Mounter::Models::EditableElement.new(_attributes)
245
+ end
246
+ end
247
+ end
248
+
249
+ # Find an editable element from its block and slug (the couple is unique)
250
+ #
251
+ # @param [ String ] block The name of the block
252
+ # @param [ String ] slug The slug of the element
253
+ #
254
+ # @return [ Object ] The editable element or nil if not found
255
+ #
256
+ def find_editable_element(block, slug)
257
+ (self.editable_elements || []).detect do |el|
258
+ el.block.to_s == block.to_s && el.slug.to_s == slug.to_s
259
+ end
260
+ end
261
+
262
+ # Localize the fullpath based on the parent fullpath in the locales
263
+ # passed in parameter.
264
+ #
265
+ # @param [ Array ] locales The list of locales the fullpath will be translated to. Can be nil (will use the locales returned by translated_in)
266
+ #
267
+ def localize_fullpath(locales = nil)
268
+ locales ||= self.translated_in
269
+ _parent_fullpath = self.parent.try(:fullpath)
270
+ _fullpath, _slug = self.fullpath.try(:clone), self.slug.to_s.clone
271
+
272
+ locales.each do |locale|
273
+ Locomotive::Mounter.with_locale(locale) do
274
+ if %w(index 404).include?(_slug) && (_fullpath.nil? || _fullpath == _slug)
275
+ self.fullpath = _slug
276
+ self.slug = _slug
277
+ elsif _parent_fullpath == 'index'
278
+ self.fullpath = self.slug || _slug
279
+ else
280
+ self.fullpath = File.join(parent.fullpath || _parent_fullpath, self.slug || _slug)
281
+ end
282
+ end
283
+ end
284
+ end
285
+
286
+ # Assign a default template for each locale which
287
+ # has an empty template. This default template
288
+ # is the one defined in the default locale.
289
+ #
290
+ # @param [ Symbol / String ] default_locale The default locale
291
+ #
292
+ def set_default_template_for_each_locale(default_locale)
293
+ default_template = self.template_translations[default_locale.to_sym]
294
+
295
+ return if self.template_blank?(default_template)
296
+
297
+ self.translated_in.each do |locale|
298
+ next if locale.to_s == default_locale.to_s
299
+
300
+ # current template
301
+ _template = self.template_translations[locale]
302
+
303
+ # is it blank ?
304
+ if self.template_blank?(_template)
305
+ self.template_translations[locale] = default_template
306
+ end
307
+ end
308
+ end
309
+
310
+ # Set the source of the page without any pre-rendering. Used by the API reader.
311
+ #
312
+ # @param [ String ] content The HTML raw template
313
+ #
314
+ def raw_template=(content)
315
+ @source ||= {}
316
+ @source[Locomotive::Mounter.locale] = content
317
+ end
318
+
319
+ # Return the Liquid template based on the raw_template property
320
+ # of the page. If the template is HAML or SLIM, then a pre-rendering to Liquid is done.
321
+ #
322
+ # @return [ String ] The liquid template or nil if not template has been provided
323
+ #
324
+ def source
325
+ @source ||= {}
326
+
327
+ if @source[Locomotive::Mounter.locale]
328
+ @source[Locomotive::Mounter.locale] # memoization
329
+ elsif self.template
330
+ @source[Locomotive::Mounter.locale] = self.template.source
331
+ else
332
+ nil
333
+ end
334
+ end
335
+
336
+ # Return the YAML front matters of the page
337
+ #
338
+ # @return [ String ] The YAML version of the page
339
+ #
340
+ def to_yaml
341
+ fields = %w(title slug redirect_url redirect_type handle published listed allow_layout is_layout searchable cache_strategy response_type position seo_title meta_description meta_keywords)
342
+
343
+ _attributes = self.attributes.delete_if do |k, v|
344
+ !fields.include?(k.to_s) || (!v.is_a?(FalseClass) && v.blank?)
345
+ end.deep_stringify_keys
346
+
347
+ # useless attributes
348
+ _attributes.delete('redirect_type') if self.redirect_url.blank?
349
+
350
+ # templatized page
351
+ _attributes['content_type'] = self.content_type.slug if self.templatized? && !self.templatized_from_parent
352
+
353
+ # editable elements
354
+ _attributes['editable_elements'] = {}
355
+ (self.editable_elements || []).each do |editable_element|
356
+ _attributes['editable_elements'].merge!(editable_element.to_yaml)
357
+ end
358
+
359
+ _attributes.delete('editable_elements') if _attributes['editable_elements'].empty?
360
+
361
+ _attributes.delete('slug') if self.depth == 0
362
+
363
+ "#{_attributes.to_yaml}---\n#{self.source}"
364
+ end
365
+
366
+ # Return the params used for the API
367
+ #
368
+ # @return [ Hash ] The params
369
+ #
370
+ def to_params
371
+ params = self.filter_attributes %w(title parent_id slug redirect_url redirect_type handle listed is_layout
372
+ allow_layout published searchable cache_strategy
373
+ response_type position templatized seo_title meta_description meta_keywords)
374
+
375
+ # slug
376
+ params.delete(:slug) if self.depth == 0
377
+
378
+ # redirect_url
379
+ params[:redirect] = true unless self.redirect_url.blank?
380
+
381
+ # parent_id
382
+ params[:parent_id] = self.parent_id unless self.parent_id.blank?
383
+
384
+ # content_type
385
+ params[:target_klass_slug] = self.content_type.slug if self.templatized && self.content_type
386
+
387
+ # editable_elements
388
+ params[:editable_elements] = (self.editable_elements || []).map(&:to_params)
389
+
390
+ # raw_template
391
+ params[:raw_template] = self.source rescue nil
392
+
393
+ params
394
+ end
395
+
396
+ # Return the params used for the API but without all the params.
397
+ # This can be explained by the fact that for instance the update should preserve
398
+ # the content.
399
+ #
400
+ # @return [ Hash ] The safe params
401
+ #
402
+ def to_safe_params
403
+ fields = %w(title slug listed is_layout allow_layout published searchable handle cache_strategy
404
+ redirect_url response_type templatized content_type_id position
405
+ seo_title meta_description meta_keywords)
406
+
407
+ params = self.attributes.delete_if do |k, v|
408
+ !fields.include?(k.to_s) || (!v.is_a?(FalseClass) && v.blank?)
409
+ end.deep_symbolize_keys
410
+
411
+ # redirect_url
412
+ params[:redirect] = true unless self.redirect_url.blank?
413
+
414
+ # raw_template
415
+ params[:raw_template] = self.source rescue nil
416
+
417
+ params
418
+ end
419
+
420
+ def to_s
421
+ self.fullpath_or_default
422
+ end
423
+
424
+ protected
425
+
426
+ # Tell if a template is strictly blank (nil or empty).
427
+ # If a template is invalid, it is not considered as a
428
+ # blank one.
429
+ #
430
+ # @param [ PageTemplate ] template The template to test
431
+ #
432
+ # @return [ Boolean ] True if the template is strictly blank
433
+ #
434
+ def template_blank?(template)
435
+ template.nil? || template.raw_source.strip.blank?
436
+ end
437
+
438
+ end
439
+
440
+ end
441
+ end
442
+ end