scribo 1.0.38

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +93 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/config/scribo_manifest.js +2 -0
  6. data/app/assets/javascripts/scribo/scribo.js +1 -0
  7. data/app/assets/stylesheets/scribo/scribo.css +1 -0
  8. data/app/controllers/concerns/maintenance_standards.rb +24 -0
  9. data/app/controllers/scribo/admin/sites/contents_controller.rb +86 -0
  10. data/app/controllers/scribo/admin/sites_controller.rb +61 -0
  11. data/app/controllers/scribo/api/sites_controller.rb +23 -0
  12. data/app/controllers/scribo/application_admin_controller.rb +11 -0
  13. data/app/controllers/scribo/application_controller.rb +6 -0
  14. data/app/controllers/scribo/contents_controller.rb +20 -0
  15. data/app/drops/scribo/action_dispatch/request_drop.rb +15 -0
  16. data/app/drops/scribo/active_model/errors_drop.rb +16 -0
  17. data/app/drops/scribo/application_drop.rb +9 -0
  18. data/app/drops/scribo/array_drop.rb +13 -0
  19. data/app/drops/scribo/content_drop.rb +73 -0
  20. data/app/drops/scribo/data_drop.rb +35 -0
  21. data/app/drops/scribo/form_drop.rb +26 -0
  22. data/app/drops/scribo/include_drop.rb +13 -0
  23. data/app/drops/scribo/paginator_drop.rb +73 -0
  24. data/app/drops/scribo/site_drop.rb +83 -0
  25. data/app/helpers/scribo/application_helper.rb +13 -0
  26. data/app/jobs/scribo/application_job.rb +6 -0
  27. data/app/models/scribo/application_record.rb +11 -0
  28. data/app/models/scribo/content.rb +378 -0
  29. data/app/models/scribo/site.rb +161 -0
  30. data/app/services/scribo/application_service.rb +9 -0
  31. data/app/services/scribo/content_find_service.rb +69 -0
  32. data/app/services/scribo/content_render_service.rb +127 -0
  33. data/app/services/scribo/site_export_service.rb +41 -0
  34. data/app/services/scribo/site_find_service.rb +26 -0
  35. data/app/services/scribo/site_import_service.rb +96 -0
  36. data/app/views/layouts/scribo.html.slim +1 -0
  37. data/app/views/scribo/admin/sites/_create_sites.html.slim +22 -0
  38. data/app/views/scribo/admin/sites/_sites.html.slim +18 -0
  39. data/app/views/scribo/admin/sites/contents/_form.html.slim +17 -0
  40. data/app/views/scribo/admin/sites/contents/create.json.jbuilder +11 -0
  41. data/app/views/scribo/admin/sites/contents/edit.html.slim +2 -0
  42. data/app/views/scribo/admin/sites/contents/edit.json.jbuilder +9 -0
  43. data/app/views/scribo/admin/sites/contents/index.html.slim +1 -0
  44. data/app/views/scribo/admin/sites/contents/move.json.jbuilder +9 -0
  45. data/app/views/scribo/admin/sites/contents/rename.json.jbuilder +9 -0
  46. data/app/views/scribo/admin/sites/contents/upload.json.jbuilder +9 -0
  47. data/app/views/scribo/admin/sites/import.json.jbuilder +4 -0
  48. data/app/views/scribo/admin/sites/index.html.slim +7 -0
  49. data/app/views/scribo/shared/_entry.html.slim +17 -0
  50. data/app/views/scribo/shared/_ide.html.slim +14 -0
  51. data/app/views/scribo/shared/_tree-view.html.slim +32 -0
  52. data/config/locales/en.yml +39 -0
  53. data/config/routes.rb +35 -0
  54. data/db/migrate/20200123095630_initial_scribo.rb +34 -0
  55. data/db/migrate/20220919124119_add_ancestry_to_scribo_contents.rb +13 -0
  56. data/db/migrate/20220919124749_remove_parent_id_from_scribo_contents.rb +8 -0
  57. data/lib/scribo/action_controller_helpers.rb +51 -0
  58. data/lib/scribo/action_controller_renderers.rb +71 -0
  59. data/lib/scribo/action_view_helpers.rb +17 -0
  60. data/lib/scribo/active_record_helpers.rb +11 -0
  61. data/lib/scribo/configuration.rb +97 -0
  62. data/lib/scribo/engine.rb +24 -0
  63. data/lib/scribo/i18n_store.rb +100 -0
  64. data/lib/scribo/liquid/filters/jekyll_filters.rb +94 -0
  65. data/lib/scribo/liquid/filters/markdownify.rb +11 -0
  66. data/lib/scribo/liquid/filters/url_filters.rb +76 -0
  67. data/lib/scribo/liquid/parser.rb +32 -0
  68. data/lib/scribo/liquid/tags/application_assets_tag.rb +14 -0
  69. data/lib/scribo/liquid/tags/application_js_tag.rb +16 -0
  70. data/lib/scribo/liquid/tags/asset_tag.rb +56 -0
  71. data/lib/scribo/liquid/tags/button_tag.rb +24 -0
  72. data/lib/scribo/liquid/tags/check_box_tag.rb +31 -0
  73. data/lib/scribo/liquid/tags/csrf_meta_tags_tag.rb +16 -0
  74. data/lib/scribo/liquid/tags/date_field_tag.rb +23 -0
  75. data/lib/scribo/liquid/tags/editable_url_tag.rb +19 -0
  76. data/lib/scribo/liquid/tags/email_field_tag.rb +23 -0
  77. data/lib/scribo/liquid/tags/feed_meta_tag.rb +22 -0
  78. data/lib/scribo/liquid/tags/field_error_tag.rb +26 -0
  79. data/lib/scribo/liquid/tags/fields_for_tag.rb +82 -0
  80. data/lib/scribo/liquid/tags/form_tag.rb +57 -0
  81. data/lib/scribo/liquid/tags/google_analytics_javascript_tag.rb +30 -0
  82. data/lib/scribo/liquid/tags/google_tag_manager_javascript_tag.rb +28 -0
  83. data/lib/scribo/liquid/tags/hidden_field_tag.rb +23 -0
  84. data/lib/scribo/liquid/tags/highlight_tag.rb +106 -0
  85. data/lib/scribo/liquid/tags/include_tag.rb +28 -0
  86. data/lib/scribo/liquid/tags/label_tag.rb +24 -0
  87. data/lib/scribo/liquid/tags/number_field_tag.rb +23 -0
  88. data/lib/scribo/liquid/tags/options_for_select.rb +36 -0
  89. data/lib/scribo/liquid/tags/password_field_tag.rb +23 -0
  90. data/lib/scribo/liquid/tags/search_field_tag.rb +23 -0
  91. data/lib/scribo/liquid/tags/search_tag.rb +29 -0
  92. data/lib/scribo/liquid/tags/select_tag.rb +22 -0
  93. data/lib/scribo/liquid/tags/seo_tag.rb +35 -0
  94. data/lib/scribo/liquid/tags/set_session.rb +19 -0
  95. data/lib/scribo/liquid/tags/telephone_field_tag.rb +23 -0
  96. data/lib/scribo/liquid/tags/text_field_tag.rb +35 -0
  97. data/lib/scribo/liquid/tags/textarea_tag.rb +28 -0
  98. data/lib/scribo/liquid/tags/time_field_tag.rb +23 -0
  99. data/lib/scribo/liquid/tags/url_field_tag.rb +23 -0
  100. data/lib/scribo/preamble.rb +89 -0
  101. data/lib/scribo/sassc/importer.rb +72 -0
  102. data/lib/scribo/utility.rb +94 -0
  103. data/lib/scribo/version.rb +5 -0
  104. data/lib/scribo.rb +59 -0
  105. data/lib/tasks/scribo_tasks.rake +28 -0
  106. metadata +498 -0
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_drop'
4
+
5
+ module Scribo
6
+ # https://jekyllrb.com/docs/pagination/#liquid-attributes-available
7
+ class PaginatorDrop < ApplicationDrop
8
+ def initialize(site, content)
9
+ @site = site
10
+ @object = content
11
+ @per_page = site&.properties&.[]('paginate') ? site.properties['paginate'].to_i : 5
12
+ end
13
+
14
+ def posts
15
+ @site.contents.posts.order(created_at: :desc).page(page).per(@per_page).to_a
16
+ end
17
+
18
+ def page
19
+ page = 1
20
+ current_path = @context.registers['controller'].request.original_fullpath
21
+ page = current_path.match(%r[/(\d+)/$])[1].to_i if Scribo::Content.paginated?(current_path)
22
+ page
23
+ end
24
+
25
+ attr_reader :per_page
26
+
27
+ def total_posts
28
+ @site.contents.posts.page(page).per(@per_page).total_count
29
+ end
30
+
31
+ def total_pages
32
+ @site.contents.posts.page(page).per(@per_page).total_pages
33
+ end
34
+
35
+ def previous_page
36
+ page > 1 ? page - 1 : nil
37
+ end
38
+
39
+ def previous_page_path
40
+ current_path = @context.registers['controller'].request.original_fullpath
41
+ previous_path = nil
42
+ if previous_page
43
+ if Scribo::Content.paginated?(current_path)
44
+ previous_path = current_path.gsub(%r[/(\d+)/$], "/#{previous_page}/")
45
+ else
46
+ previous_path = current_path
47
+ previous_path += '/' unless previous_path.ends_with?('/')
48
+ previous_path += "#{next_page}/"
49
+ end
50
+ end
51
+ previous_path
52
+ end
53
+
54
+ def next_page
55
+ page < total_pages ? page + 1 : nil
56
+ end
57
+
58
+ def next_page_path
59
+ current_path = @context.registers['controller'].request.original_fullpath
60
+ next_path = nil
61
+ if next_page
62
+ if Scribo::Content.paginated?(current_path)
63
+ next_path = current_path.gsub(%r[/(\d+)/$], "/#{next_page}/")
64
+ else
65
+ next_path = current_path
66
+ next_path += '/' unless next_path.ends_with?('/')
67
+ next_path += "#{next_page}/"
68
+ end
69
+ end
70
+ next_path
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_drop'
4
+ require 'pry'
5
+ module Scribo
6
+ # See https://jekyllrb.com/docs/variables/#site-variables
7
+ class SiteDrop < ApplicationDrop
8
+ delegate :collections, to: :@object
9
+
10
+ def initialize(object)
11
+ @object = object
12
+ @properties = object.properties
13
+ end
14
+
15
+ def time
16
+ Time.current
17
+ end
18
+
19
+ def pages
20
+ @object.contents.pages.to_a
21
+ end
22
+
23
+ def posts
24
+ @object.contents.posts.sort_by { |p| - p.date.to_i }.to_a
25
+ end
26
+
27
+ # TODO
28
+ def related_posts
29
+ []
30
+ end
31
+
32
+ def static_files
33
+ @object.contents.assets.to_a
34
+ end
35
+
36
+ def html_pages
37
+ @object.contents.html_pages.to_a
38
+ end
39
+
40
+ def html_files
41
+ @object.contents.html_files.to_a
42
+ end
43
+
44
+ def data
45
+ Scribo::DataDrop.new(@object)
46
+ end
47
+
48
+ # TODO
49
+ def documents
50
+ []
51
+ end
52
+
53
+ # TODO
54
+ def categories
55
+ []
56
+ end
57
+
58
+ # TODO
59
+ def tags
60
+ []
61
+ end
62
+
63
+ def url
64
+ @properties['url']
65
+ end
66
+
67
+ def current_locale
68
+ I18n.locale.to_s
69
+ end
70
+
71
+ def locale
72
+ @properties['locale']
73
+ end
74
+
75
+ def liquid_method_missing(method)
76
+ if collections.include?(method)
77
+ @object.contents.in_folder("_#{method}").order(:created_at).to_a
78
+ else
79
+ @properties[method.to_s]
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scribo
4
+ module ApplicationHelper
5
+ def method_missing(method, *args, &block)
6
+ if main_app.respond_to?(method)
7
+ main_app.send(method, *args)
8
+ else
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scribo
4
+ class ApplicationJob < ActiveJob::Base
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scribo
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+
7
+ def to_liquid
8
+ Kernel.const_get("#{self.class.name}Drop").new(self)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,378 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_record'
4
+
5
+ module Scribo
6
+ # Represents any content in the system
7
+ class Content < ApplicationRecord
8
+ has_ancestry(primary_key_format: /\A[\w\-]+(\/[\w\-]+)*\z/, counter_cache: true, cache_depth: true)
9
+ belongs_to :site, class_name: 'Site', foreign_key: 'scribo_site_id'
10
+ has_one_attached :asset
11
+
12
+ validate :post_path
13
+ validate :layout_cant_be_current_content
14
+
15
+ before_save :store_properties, if: :config?
16
+ before_save :upload_asset
17
+
18
+ after_save -> { store_full_path(true) }
19
+ after_update -> {store_full_path(true)}
20
+
21
+ scope :layouts, -> { in_folder('_layouts') }
22
+ scope :posts, -> { in_folder('_posts') }
23
+ scope :pages, -> { not_in_folder('_posts').restricted.where(kind: 'text') }
24
+ scope :assets, -> { where(kind: 'asset') }
25
+ scope :html_pages, -> { where("full_path LIKE '%.html' OR full_path LIKE '%.md' OR full_path LIKE '%.markdown'") }
26
+ # html files should be non-filtered html files
27
+ scope :html_files, -> { where("full_path LIKE '%.html'") }
28
+ scope :include, ->(name) { published.where(full_path: ["/_includes/#{name}"]) }
29
+ scope :layout, lambda { |name|
30
+ published.where(full_path: %W[/_layouts/#{name}.html /_layouts/#{name}.md /_layouts/#{name}.xml /_layouts/#{name}.css])
31
+ }
32
+ scope :data, lambda { |name|
33
+ published.where(full_path: %W[/_data/#{name}.yml /_data/#{name}.yaml /_data/#{name}.json /_data/#{name}.csv /_data/#{name}])
34
+ }
35
+
36
+ scope :locale, ->(name) { published.where(full_path: "/_locales/#{name}.yml") }
37
+ scope :locales, -> { published.in_folder('_locales') }
38
+
39
+ scope :published, lambda {
40
+ where("properties->>'published' = 'true' OR properties->>'published' IS NULL").where("properties->>'published_at' IS NULL OR properties->>'published_at' <= :now", now: Time.current.utc)
41
+ }
42
+ scope :restricted, -> { where("full_path NOT LIKE '/\\_%'") }
43
+
44
+ scope :not_in_folder, lambda { |folder_name|
45
+ where.not(id: in_folder(folder_name).pluck(:id))
46
+ }
47
+ scope :in_folder, lambda { |folder_name|
48
+ where(kind: 'folder').find_by(path: folder_name)&.descendants || none
49
+ }
50
+
51
+ scope :permalinked, ->(paths) { where("properties->>'permalink' IN (?)", paths) }
52
+
53
+ def self.located(path, restricted: true)
54
+ restricted = true if restricted.nil? # If blank it's still restricted
55
+
56
+ result = published.where(full_path: search_paths_for(path))
57
+ result = result.restricted if restricted
58
+ result.or(published.permalinked(search_paths_for(path)))
59
+ end
60
+
61
+ def path
62
+ self[:path]
63
+ end
64
+
65
+ # Uses https://www.postgresql.org/docs/current/textsearch-controls.html
66
+ def self.search(search_string)
67
+ where(
68
+ "to_tsvector(scribo_contents.data || ' ' || COALESCE(scribo_contents.properties::text, '')) @@ to_tsquery(?)", search_string
69
+ )
70
+ end
71
+
72
+ # Name of the currently in use layout
73
+ def layout_name
74
+ properties&.key?('layout') ? properties&.[]('layout') : ''
75
+ end
76
+
77
+ # Layout as content
78
+ def layout
79
+ return nil unless layout_name.present?
80
+
81
+ site.contents.layout(layout_name).first
82
+ end
83
+
84
+ def identifier
85
+ File.basename(path, File.extname(path))
86
+ end
87
+
88
+ # Data with frontmatter, used for maintenance and import/export
89
+ def data_with_frontmatter
90
+ return asset.attachment&.download || data if kind != 'text'
91
+
92
+ result = ''
93
+ # Use attributes['properties'] here, to always use content-local properties
94
+ result += (YAML.dump(attributes['properties']) + "---\n") if attributes['properties'].present?
95
+
96
+ result + data.to_s
97
+ end
98
+
99
+ # Data with frontmatter setter
100
+ def data_with_frontmatter=(text)
101
+ if kind == 'text'
102
+ data_with_metadata = Scribo::Preamble.parse(text)
103
+ self.properties = data_with_metadata.metadata
104
+ self.data = data_with_metadata.content
105
+ else
106
+ self.data = text
107
+ end
108
+ end
109
+
110
+ # Used for merging with defaults
111
+ def properties
112
+ attributes['properties']
113
+ # defaults.merge(attributes['properties'] || {})
114
+ end
115
+
116
+ def properties=(text)
117
+ props = text.is_a?(String) ? Scribo::Utility.yaml_safe_parse(text.gsub("\t", ' ')) : text
118
+ write_attribute :properties, props
119
+ end
120
+
121
+ def type
122
+ collection_name
123
+ end
124
+
125
+ def permalink
126
+ properties&.[]('permalink')
127
+ end
128
+
129
+ def url
130
+ result = permalink || Scribo::Utility.switch_extension(full_path)
131
+ result += '/' unless result.end_with?('/')
132
+ result
133
+ end
134
+
135
+ def date
136
+ # return nil unless post?
137
+
138
+ prop_date = begin
139
+ Time.zone.parse(properties['date'])
140
+ rescue StandardError
141
+ nil
142
+ end
143
+
144
+ prop_date || post_date
145
+ end
146
+
147
+ def post_date
148
+ Time.zone.strptime(path[0, 10], '%Y-%m-%d')
149
+ rescue StandardError
150
+ nil
151
+ end
152
+
153
+ def data
154
+ return attributes['data'] if kind == 'asset'
155
+
156
+ attributes['data']&.force_encoding('utf-8')
157
+ end
158
+
159
+ def excerpt
160
+ # FIXME: This is a terrible implementation
161
+ excerpt_part = data.gsub("\r\n", "\n\n").split("\n\n").reject(&:empty?).reject { |p| p.start_with?('#') }.first
162
+ Scribo::ContentRenderService.new(self, {}, data: excerpt_part, layout: false).call
163
+ end
164
+
165
+ def render(context = {}, options = {})
166
+ Scribo::ContentRenderService.new(self, context, options).call
167
+ end
168
+
169
+ def categories
170
+ if properties&.[]('categories').is_a? Array
171
+ properties&.[]('categories')
172
+ else
173
+ (properties&.[]('categories') || '').split
174
+ end
175
+ end
176
+
177
+ def tags
178
+ if properties&.[]('tags').is_a? Array
179
+ properties&.[]('tags')
180
+ else
181
+ (properties&.[]('tags') || '').split
182
+ end
183
+ end
184
+
185
+ def content_type
186
+ properties&.[]('content_type') || mime_type&.content_type || 'application/octet-stream'
187
+ end
188
+
189
+ def media_type
190
+ mime_type&.media_type
191
+ end
192
+
193
+ def extension
194
+ mime_type&.extensions&.first
195
+ end
196
+
197
+ def mime_type
198
+ MIME::Types.type_for(path).first
199
+ end
200
+
201
+ def dir
202
+ File.dirname(full_path)
203
+ end
204
+
205
+ def extname
206
+ File.extname(full_path).tr('.', '')
207
+ end
208
+
209
+ def translation_scope
210
+ scope = File.dirname(full_path).split('/')
211
+ scope << File.basename(full_path, File.extname(full_path))
212
+ scope.join('.')
213
+ end
214
+
215
+ def cache_key
216
+ "#{super}-#{updated_at}-#{I18n.locale}"
217
+ end
218
+
219
+ def collection_name
220
+ return nil unless part_of_collection?
221
+
222
+ ancestors.first.path[1..-1]
223
+ end
224
+
225
+ def part_of_collection?
226
+ return false unless ancestors.first&.path&.start_with?('_')
227
+
228
+ site.collections.include?(ancestors.first.path[1..-1])
229
+ end
230
+
231
+ def redirect?
232
+ extname == 'link'
233
+ end
234
+
235
+ def layout?
236
+ full_path.start_with?('/_layouts/')
237
+ end
238
+
239
+ def post?
240
+ ancestors.map(&:path).join('/').start_with?('_posts')
241
+ end
242
+
243
+ def page?
244
+ Scribo::Utility.output_content_type(self) == 'text/html'
245
+ end
246
+
247
+ def config?
248
+ path == '_config.yml' && parent.nil?
249
+ end
250
+
251
+ def asset?
252
+ kind == 'asset'
253
+ end
254
+
255
+ def folder?
256
+ kind == 'folder'
257
+ end
258
+
259
+ def self.paginated?(path)
260
+ path.match(%r[/(\d+)/$])
261
+ end
262
+
263
+ def self.search_paths_for(path)
264
+ search_paths = []
265
+
266
+ search_path = path
267
+ search_path = "/#{search_path}" unless search_path.start_with?('/')
268
+ search_path.gsub!(%r[/\d+/$], '/') if paginated?(search_path)
269
+ search_path = "#{search_path}index.html" if search_path.ends_with?('/')
270
+
271
+ search_paths.concat(alternative_paths_for(search_path))
272
+
273
+ secondary_search_path = path.sub(%r[/$], '')
274
+ secondary_search_path = "/#{secondary_search_path}" unless secondary_search_path.start_with?('/')
275
+ search_paths.concat(alternative_paths_for(secondary_search_path)) if secondary_search_path != '' && secondary_search_path != search_path
276
+
277
+ permalink_paths = [path]
278
+
279
+ normalized_path = path
280
+ normalized_path = "/#{normalized_path}" unless normalized_path.start_with?('/')
281
+ normalized_path = "#{normalized_path}/" unless normalized_path.ends_with?('/')
282
+ permalink_paths << normalized_path
283
+ search_paths.concat(permalink_paths) # deal with permalinks
284
+
285
+ search_paths.uniq
286
+ end
287
+
288
+ def self.alternative_paths_for(search_path)
289
+ search_paths = []
290
+ search_path = Scribo::Utility.switch_extension(search_path, 'html') unless File.extname(search_path).present?
291
+ search_paths.concat(Scribo::Utility.variations_for_path(search_path))
292
+ search_paths << Scribo::Utility.switch_extension(search_path, 'link')
293
+ end
294
+
295
+ def store_full_path(force = false)
296
+ if force || saved_changes.include?(:path) || saved_changes.include?(:ancestry)
297
+ if post?
298
+ result = categories.join('/') + '/'
299
+ result += date.strftime('%Y/%m/%d/') if date
300
+ result += path[11..-1]
301
+ elsif part_of_collection? && site.output_collection?(collection_name)
302
+ result = "#{collection_name}/#{path}"
303
+ else
304
+ result = (ancestors.map(&:path) << path).join('/')
305
+ end
306
+ result = '/' + result unless result.start_with?('/')
307
+
308
+ update_column(:full_path, result)
309
+
310
+ children.reload.each do |child|
311
+ child.store_full_path(true)
312
+ end
313
+
314
+ end
315
+ end
316
+
317
+ def tree_path
318
+ result = (ancestors.map(&:path) << path).join('/')
319
+ result = '/' + result unless result.start_with?('/')
320
+ result
321
+ end
322
+
323
+ private
324
+
325
+ def upload_asset
326
+ return unless asset?
327
+ # return unless data.present? -> Breaks with utf8?
328
+ return unless data
329
+ return unless data.size.positive?
330
+
331
+ si = StringIO.new
332
+ si.write(data)
333
+ si.rewind
334
+
335
+ asset.attach(io: si, filename: path, content_type: content_type)
336
+
337
+ self.data = nil
338
+ end
339
+
340
+ def store_properties
341
+ return unless attributes['data'].present?
342
+
343
+ new_properties = Scribo::Utility.yaml_safe_parse(attributes['data'])
344
+ old_properties = site.properties
345
+ site.update(properties: new_properties)
346
+
347
+ # Only reshuffle if they need to
348
+ if old_properties['collections'] != new_properties['collections'] ||
349
+ old_properties['permalink'] != new_properties['permalink']
350
+ site.reshuffle!
351
+ end
352
+ end
353
+
354
+ def post_path
355
+ return unless post?
356
+
357
+ errors.add(:path, 'path must be of format YYYY-MM-DD-title') unless path.match(/[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}-.*/)
358
+ end
359
+
360
+ def layout_cant_be_current_content
361
+ return unless layout
362
+
363
+ errors.add(:base, "layout can't be layout of itself") if layout.full_path == full_path
364
+ end
365
+
366
+ class << self
367
+ def redirect_options(redirect_data)
368
+ options = redirect_data.split
369
+ if options.length == 2
370
+ options[0] = options[0].to_i
371
+ else
372
+ options.unshift 302
373
+ end
374
+ options
375
+ end
376
+ end
377
+ end
378
+ end