scribo 1.0.38
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +93 -0
- data/Rakefile +40 -0
- data/app/assets/config/scribo_manifest.js +2 -0
- data/app/assets/javascripts/scribo/scribo.js +1 -0
- data/app/assets/stylesheets/scribo/scribo.css +1 -0
- data/app/controllers/concerns/maintenance_standards.rb +24 -0
- data/app/controllers/scribo/admin/sites/contents_controller.rb +86 -0
- data/app/controllers/scribo/admin/sites_controller.rb +61 -0
- data/app/controllers/scribo/api/sites_controller.rb +23 -0
- data/app/controllers/scribo/application_admin_controller.rb +11 -0
- data/app/controllers/scribo/application_controller.rb +6 -0
- data/app/controllers/scribo/contents_controller.rb +20 -0
- data/app/drops/scribo/action_dispatch/request_drop.rb +15 -0
- data/app/drops/scribo/active_model/errors_drop.rb +16 -0
- data/app/drops/scribo/application_drop.rb +9 -0
- data/app/drops/scribo/array_drop.rb +13 -0
- data/app/drops/scribo/content_drop.rb +73 -0
- data/app/drops/scribo/data_drop.rb +35 -0
- data/app/drops/scribo/form_drop.rb +26 -0
- data/app/drops/scribo/include_drop.rb +13 -0
- data/app/drops/scribo/paginator_drop.rb +73 -0
- data/app/drops/scribo/site_drop.rb +83 -0
- data/app/helpers/scribo/application_helper.rb +13 -0
- data/app/jobs/scribo/application_job.rb +6 -0
- data/app/models/scribo/application_record.rb +11 -0
- data/app/models/scribo/content.rb +378 -0
- data/app/models/scribo/site.rb +161 -0
- data/app/services/scribo/application_service.rb +9 -0
- data/app/services/scribo/content_find_service.rb +69 -0
- data/app/services/scribo/content_render_service.rb +127 -0
- data/app/services/scribo/site_export_service.rb +41 -0
- data/app/services/scribo/site_find_service.rb +26 -0
- data/app/services/scribo/site_import_service.rb +96 -0
- data/app/views/layouts/scribo.html.slim +1 -0
- data/app/views/scribo/admin/sites/_create_sites.html.slim +22 -0
- data/app/views/scribo/admin/sites/_sites.html.slim +18 -0
- data/app/views/scribo/admin/sites/contents/_form.html.slim +17 -0
- data/app/views/scribo/admin/sites/contents/create.json.jbuilder +11 -0
- data/app/views/scribo/admin/sites/contents/edit.html.slim +2 -0
- data/app/views/scribo/admin/sites/contents/edit.json.jbuilder +9 -0
- data/app/views/scribo/admin/sites/contents/index.html.slim +1 -0
- data/app/views/scribo/admin/sites/contents/move.json.jbuilder +9 -0
- data/app/views/scribo/admin/sites/contents/rename.json.jbuilder +9 -0
- data/app/views/scribo/admin/sites/contents/upload.json.jbuilder +9 -0
- data/app/views/scribo/admin/sites/import.json.jbuilder +4 -0
- data/app/views/scribo/admin/sites/index.html.slim +7 -0
- data/app/views/scribo/shared/_entry.html.slim +17 -0
- data/app/views/scribo/shared/_ide.html.slim +14 -0
- data/app/views/scribo/shared/_tree-view.html.slim +32 -0
- data/config/locales/en.yml +39 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20200123095630_initial_scribo.rb +34 -0
- data/db/migrate/20220919124119_add_ancestry_to_scribo_contents.rb +13 -0
- data/db/migrate/20220919124749_remove_parent_id_from_scribo_contents.rb +8 -0
- data/lib/scribo/action_controller_helpers.rb +51 -0
- data/lib/scribo/action_controller_renderers.rb +71 -0
- data/lib/scribo/action_view_helpers.rb +17 -0
- data/lib/scribo/active_record_helpers.rb +11 -0
- data/lib/scribo/configuration.rb +97 -0
- data/lib/scribo/engine.rb +24 -0
- data/lib/scribo/i18n_store.rb +100 -0
- data/lib/scribo/liquid/filters/jekyll_filters.rb +94 -0
- data/lib/scribo/liquid/filters/markdownify.rb +11 -0
- data/lib/scribo/liquid/filters/url_filters.rb +76 -0
- data/lib/scribo/liquid/parser.rb +32 -0
- data/lib/scribo/liquid/tags/application_assets_tag.rb +14 -0
- data/lib/scribo/liquid/tags/application_js_tag.rb +16 -0
- data/lib/scribo/liquid/tags/asset_tag.rb +56 -0
- data/lib/scribo/liquid/tags/button_tag.rb +24 -0
- data/lib/scribo/liquid/tags/check_box_tag.rb +31 -0
- data/lib/scribo/liquid/tags/csrf_meta_tags_tag.rb +16 -0
- data/lib/scribo/liquid/tags/date_field_tag.rb +23 -0
- data/lib/scribo/liquid/tags/editable_url_tag.rb +19 -0
- data/lib/scribo/liquid/tags/email_field_tag.rb +23 -0
- data/lib/scribo/liquid/tags/feed_meta_tag.rb +22 -0
- data/lib/scribo/liquid/tags/field_error_tag.rb +26 -0
- data/lib/scribo/liquid/tags/fields_for_tag.rb +82 -0
- data/lib/scribo/liquid/tags/form_tag.rb +57 -0
- data/lib/scribo/liquid/tags/google_analytics_javascript_tag.rb +30 -0
- data/lib/scribo/liquid/tags/google_tag_manager_javascript_tag.rb +28 -0
- data/lib/scribo/liquid/tags/hidden_field_tag.rb +23 -0
- data/lib/scribo/liquid/tags/highlight_tag.rb +106 -0
- data/lib/scribo/liquid/tags/include_tag.rb +28 -0
- data/lib/scribo/liquid/tags/label_tag.rb +24 -0
- data/lib/scribo/liquid/tags/number_field_tag.rb +23 -0
- data/lib/scribo/liquid/tags/options_for_select.rb +36 -0
- data/lib/scribo/liquid/tags/password_field_tag.rb +23 -0
- data/lib/scribo/liquid/tags/search_field_tag.rb +23 -0
- data/lib/scribo/liquid/tags/search_tag.rb +29 -0
- data/lib/scribo/liquid/tags/select_tag.rb +22 -0
- data/lib/scribo/liquid/tags/seo_tag.rb +35 -0
- data/lib/scribo/liquid/tags/set_session.rb +19 -0
- data/lib/scribo/liquid/tags/telephone_field_tag.rb +23 -0
- data/lib/scribo/liquid/tags/text_field_tag.rb +35 -0
- data/lib/scribo/liquid/tags/textarea_tag.rb +28 -0
- data/lib/scribo/liquid/tags/time_field_tag.rb +23 -0
- data/lib/scribo/liquid/tags/url_field_tag.rb +23 -0
- data/lib/scribo/preamble.rb +89 -0
- data/lib/scribo/sassc/importer.rb +72 -0
- data/lib/scribo/utility.rb +94 -0
- data/lib/scribo/version.rb +5 -0
- data/lib/scribo.rb +59 -0
- data/lib/tasks/scribo_tasks.rake +28 -0
- 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,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
|