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,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_record'
4
+ require_dependency 'scribo/liquid/parser'
5
+
6
+ module Scribo
7
+ class Site < ApplicationRecord
8
+ NEW_SITE_NAME = 'Untitled site'
9
+
10
+ belongs_to :scribable, polymorphic: true, optional: true
11
+
12
+ has_many :contents, class_name: 'Content', foreign_key: 'scribo_site_id', dependent: :destroy
13
+
14
+ attr_accessor :zip_file
15
+
16
+ scope :adminable, -> { where(scribable: Scribo.config.scribable_objects) }
17
+ scope :owned_by, ->(owner) { where(scribable: owner) }
18
+ scope :titled, ->(title) { where("properties->>'title' = ?", title).first }
19
+
20
+ def filter_cache
21
+ @filter_cache ||= {}
22
+ end
23
+
24
+ def reshuffle!
25
+ contents.roots.each(&:store_full_path)
26
+ end
27
+
28
+ # See https://jekyllrb.com/docs/permalinks/
29
+ def perma_link
30
+ properties['permalink'] || '/:year/:month/:day/:title:output_ext'
31
+ end
32
+
33
+ def collections
34
+ result = begin
35
+ properties['collections'].to_h.keys.map(&:to_s)
36
+ rescue StandardError
37
+ []
38
+ end
39
+ result += %w[posts]
40
+ result
41
+ end
42
+
43
+ def output_collection?(collection)
44
+ return true if collection.to_s == 'posts'
45
+
46
+ col = properties['collections'].to_h[collection.to_s]
47
+ col&.[]('output') == true
48
+ end
49
+
50
+ def title
51
+ properties['title'] || NEW_SITE_NAME
52
+ end
53
+
54
+ def baseurl
55
+ properties['baseurl'] || '/'
56
+ end
57
+
58
+ # This returns the full thumbnail URL
59
+ def thumbnail
60
+ return unless properties['thumbnail']
61
+
62
+ url + baseurl + properties['thumbnail'].to_s
63
+ end
64
+
65
+ def url
66
+ properties['url'].to_s
67
+ end
68
+
69
+ def properties
70
+ attributes['properties'].present? ? attributes['properties'] : { 'title' => NEW_SITE_NAME, 'baseurl' => '/' }
71
+ end
72
+
73
+ def sass_dir
74
+ result = properties.value_at_keypath('sass.sass_dir') || '/_sass/'
75
+ result = "/#{result}" unless result.start_with?('/')
76
+ result = "#{result}/" unless result.ends_with?('/')
77
+
78
+ result
79
+ end
80
+
81
+ def defaults_for(content)
82
+ site_defaults = properties&.[]('defaults')
83
+ return {} unless site_defaults
84
+ return {} unless content.full_path
85
+ return {} if !content.page? && !content.collection_name # scoping only possible for pages & collections
86
+
87
+ props = site_defaults.select do |d|
88
+ s = d['scope']
89
+ next unless s
90
+
91
+ result = false
92
+
93
+ if s['path']
94
+ result = true
95
+ p = s['path']
96
+ unless p.include?('*')
97
+ p = "/#{p}" unless p.start_with?('/')
98
+ p = "#{p}/" unless p.ends_with?('/')
99
+ p += '*'
100
+ end
101
+
102
+ result &= File.fnmatch?(p, content.full_path.to_s, File::FNM_EXTGLOB)
103
+ end
104
+
105
+ result &= s['type'] == content.type if s['type']
106
+
107
+ result
108
+ end
109
+ # Sort by longest scope array (ie most specific-ish) and return the first
110
+ props = props.min_by { |p| -p['scope'].to_a.size }
111
+
112
+ (props || {}).fetch('values', {})
113
+ end
114
+
115
+ #
116
+ # Calculates the total size of the site in bytes, including assets
117
+ #
118
+ # @return [Integer] size in bytes
119
+ #
120
+ def total_size
121
+ contents.map { |c| c.data ? c.data.size : c.asset.attachment&.download&.size || 0 }.sum
122
+ end
123
+
124
+ class << self
125
+ def for_path(path)
126
+ return none if path.blank?
127
+
128
+ # Remove any segment which does not end in /
129
+ search_path = File.dirname(path)
130
+
131
+ paths = []
132
+ paths.concat(search_path.split('/').map.with_index { |_, i| search_path.split('/')[0..i].join('/') }.reject(&:empty?))
133
+ paths <<= '/'
134
+ paths <<= path.gsub(%r[/$], '')
135
+
136
+ where("properties->>'baseurl' IN (?) OR properties->>'baseurl' = '' OR properties->>'baseurl' IS NULL",
137
+ paths.uniq).order(Arel.sql("COALESCE(LENGTH(scribo_sites.properties->>'baseurl'), 0) DESC"))
138
+ end
139
+
140
+ def for_host(host)
141
+ where("properties->>'host' = ? OR properties->>'host' = '' OR properties->>'host' IS NULL", host).order(Arel.sql("COALESCE(LENGTH(scribo_sites.properties->>'host'), 0) DESC"))
142
+ end
143
+
144
+ def all_translation_keys
145
+ parser = Scribo::LiquidParser.new
146
+ result = {}
147
+ Scribo::Content.includes(:site).where(content_type: Scribo.config.supported_mime_types[:text]).each do |content|
148
+ parts = parser.parse(content.data)
149
+ [*parts].select { |part| part.is_a?(Hash) && part[:filter] == 't' }.each do |part|
150
+ result[content.translation_scope + part[:value].to_s] = part[:value].to_s[1..-1].humanize
151
+ end
152
+ end
153
+ result
154
+ end
155
+
156
+ def default(request: nil)
157
+ Scribo.config.default_site(request) || new
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scribo
4
+ class ApplicationService
5
+ def call
6
+ perform
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_service'
4
+
5
+ module Scribo
6
+ class ContentFindService < ApplicationService
7
+ attr_reader :options, :site
8
+
9
+ def initialize(site, options = {})
10
+ super()
11
+ @site = site
12
+ @options = options
13
+ end
14
+
15
+ def perform
16
+ return options[:content] if options[:content].is_a?(Scribo::Content)
17
+
18
+ scope = site.contents
19
+
20
+ if options[:path]
21
+ path = CGI.unescape(options[:path])
22
+ path = path[site.baseurl.length..-1] if path.start_with?(site.baseurl)
23
+
24
+ # Deal with collections
25
+ path_parts = path.split('/')
26
+ first_path_part = path_parts[0]
27
+
28
+ if site.collections.include?(first_path_part)
29
+ path_parts[0] = '_' + path_parts[0]
30
+ options[:restricted] = false
31
+ path = path_parts.join('/')
32
+ end
33
+ # End - Deal with collections
34
+
35
+ scope = scope.located(path, restricted: options[:restricted])
36
+ end
37
+
38
+ # FIXME: Not to pretty
39
+ content = if options[:root]
40
+ scope.roots.first
41
+ else
42
+ result = scope.where.not(kind: 'folder').first
43
+
44
+ result = scope.where(kind: 'folder').first unless scope.present?
45
+
46
+ result = Scribo::Content.new(kind: 'text', path: '/directory.link', full_path: '/directory.link', data: "#{options[:path]}/") if result&.folder?
47
+
48
+ result
49
+ end
50
+
51
+ # Find by content id
52
+ content ||= Scribo::Content&.published&.find_by_id(options[:path][1..-1]) if options[:path] && options[:path][1..-1].length == 36
53
+
54
+ if options[:path] == '/humans.txt'
55
+ content ||= Scribo::Content.new(kind: 'text', path: '/humans.txt', full_path: '/humans.txt', data: Scribo.config.default_humans_txt)
56
+ elsif options[:path] == '/robots.txt'
57
+ content ||= Scribo::Content.new(kind: 'text', path: '/robots.txt', full_path: '/robots.txt', data: Scribo.config.default_robots_txt)
58
+ elsif options[:path] == '/favicon.ico'
59
+ content ||= Scribo::Content.new(kind: 'asset', path: '/favicon.ico', full_path: '/favicon.ico', data: Base64.decode64(Scribo.config.default_favicon_ico))
60
+ end
61
+
62
+ # FIXME: Find a better way for this
63
+ content ||= site&.contents&.located('/404')&.first
64
+ content ||= site&.contents&.located('/404.html')&.first
65
+ content ||= site&.contents&.located('/404.md')&.first
66
+ content
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_service'
4
+
5
+ module Scribo
6
+ class ContentRenderService < ApplicationService
7
+ attr_reader :content, :context, :options
8
+
9
+ def initialize(content, context, options = {})
10
+ super()
11
+ @content = content
12
+ @context = context
13
+ @options = options
14
+ @assigns = nil
15
+ @registers = nil
16
+ end
17
+
18
+ def perform
19
+ if content.kind == 'asset'
20
+ render_asset
21
+ else
22
+ layout = options[:layout] == false ? nil : content.layout
23
+
24
+ # FIXME: Though this works for layout, we need to be able to merge all properties with defaults
25
+ # Luckily this is mostly used for layouts
26
+ if options[:site]
27
+ layout_name = options[:site].defaults_for(content)['layout']
28
+ layout ||= options[:site].contents.layout(layout_name).first if layout_name
29
+ end
30
+
31
+ render_liquidum(options[:data] || content.data, layout)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def render_liquidum(data, layout)
38
+ result = Liquidum.render(data, assigns: assigns.merge!('content' => data), registers: registers, filter: filter,
39
+ filter_options: filter_options, layout: layout&.data)
40
+
41
+ while layout&.layout
42
+ next unless layout&.layout
43
+
44
+ layout = layout.layout
45
+ result = Liquidum.render(layout.data, assigns: assigns.merge('content' => result), registers: registers)
46
+ end
47
+
48
+ result
49
+ end
50
+
51
+ def render_asset
52
+ return unless content.kind == 'asset'
53
+ return content.data if content.data.present?
54
+ return unless content.asset.attached?
55
+
56
+ content.asset.download
57
+ end
58
+
59
+ def filter
60
+ return options[:filter] if options.key?(:filter)
61
+
62
+ if content.properties&.key?('filter')
63
+ content.properties['filter']
64
+ elsif content.path
65
+ Scribo::Utility.filter_for_path(content.path)
66
+ end
67
+ end
68
+
69
+ def filter_options
70
+ @filter_options = { full_path: content.full_path, content: content }
71
+ @filter_options[:importer] = Scribo::SassC::Importer if %w[sass scss].include?(filter)
72
+ @filter_options.merge!(markdown_filter_options) if %w[markdown].include?(filter)
73
+ @filter_options
74
+ end
75
+
76
+ def assigns
77
+ return @assigns if @assigns
78
+
79
+ @assigns = {}
80
+ @assigns.merge!(options[:assigns]) if options[:assigns]
81
+
82
+ context.instance_variables.reject { |i| i.to_s.starts_with?('@_') }.each do |i|
83
+ @assigns[i.to_s[1..-1]] = context.instance_variable_get(i)
84
+ end
85
+
86
+ @assigns['request'] = Scribo::ActionDispatch::RequestDrop.new(context.request) if context.respond_to?(:request)
87
+ @assigns['site'] = options[:site] || content.site
88
+ @assigns['page'] = content
89
+ @assigns['paginator'] = Scribo::PaginatorDrop.new(@assigns['site'], content)
90
+
91
+ @assigns = @assigns.stringify_keys
92
+ @assigns
93
+ end
94
+
95
+ def registers
96
+ return @registers if @registers
97
+
98
+ @registers = { 'controller' => context, 'content' => content, 'site' => content.site }
99
+ @registers.merge!(options[:registers]) if options[:registers]
100
+ @registers = @registers.stringify_keys
101
+ @registers
102
+ end
103
+
104
+ def markdown_filter_options
105
+ {
106
+ auto_ids: true,
107
+ toc_levels: '1..6',
108
+ entity_output: 'as_char',
109
+ smart_quotes: 'lsquo,rsquo,ldquo,rdquo',
110
+ input: 'GFM',
111
+ hard_wrap: false,
112
+ guess_lang: true,
113
+ footnote_nr: 1,
114
+ show_warnings: false,
115
+ syntax_highlighter: 'rouge'
116
+
117
+ # syntax_highlighter_opts: {
118
+ # bold_every: 8,
119
+ # css: :class,
120
+ # css_class: 'highlight',
121
+ # formatter: ::Rouge::Formatters::HTMLLegacy,
122
+ # foobar: 'lipsum'
123
+ # }
124
+ }
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_service'
4
+
5
+ module Scribo
6
+ class SiteExportService < ApplicationService
7
+ attr_reader :site
8
+
9
+ def initialize(site)
10
+ super()
11
+ @site = site
12
+ end
13
+
14
+ def perform
15
+ return unless site.contents.count.positive?
16
+
17
+ zip_name = (site.properties['title'] || 'untitled').to_s
18
+ base_path = "#{zip_name}/"
19
+
20
+ stringio = Zip::OutputStream.write_buffer do |zio|
21
+ site.contents.each do |content|
22
+ content_path = content_path_for_zip(content)
23
+ next unless content_path
24
+
25
+ next if content.kind == 'folder'
26
+
27
+ zio.put_next_entry(base_path + content_path)
28
+ zio.write content.data_with_frontmatter
29
+ end
30
+ end
31
+
32
+ ["#{zip_name}.zip", stringio.string]
33
+ end
34
+
35
+ private
36
+
37
+ def content_path_for_zip(content)
38
+ content.tree_path[0] == '/' ? content.tree_path[1..-1] : content.tree_path
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_service'
4
+
5
+ module Scribo
6
+ class SiteFindService < ApplicationService
7
+ attr_reader :options
8
+
9
+ def initialize(options = {})
10
+ super()
11
+ @options = options
12
+ end
13
+
14
+ def perform
15
+ return options[:site] if options[:site].is_a?(Scribo::Site)
16
+
17
+ scribable = Scribo.config.scribable_for_request(options[:request])
18
+ return nil unless scribable
19
+
20
+ scope = scribable.sites
21
+ scope = scope.for_host(options[:host])
22
+ scope = scope.for_path(options[:path]) if options[:path]
23
+ scope.first
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'scribo/application_service'
4
+ require 'mime/types'
5
+ require 'yaml'
6
+
7
+ module Scribo
8
+ class SiteImportService < ApplicationService
9
+ attr_reader :path, :scribable, :properties_override
10
+
11
+ IGNORED_FILES = [%r[^__MACOSX/], %r[/\.DS_Store], %r[^/_site], /^node_modules/].freeze
12
+
13
+ def initialize(path, scribable: nil, properties: {})
14
+ super()
15
+ @path = path
16
+ @scribable = scribable
17
+ @properties_override = properties
18
+ end
19
+
20
+ def perform
21
+ Dir.mktmpdir do |dir|
22
+ Dir.chdir(dir) do
23
+ unzip(dir)
24
+
25
+ all_files = Dir.glob('**/*')
26
+ all_files = all_files.reject { |p| p == '_config.yml' } if properties_override.present?
27
+ all_files.each do |name|
28
+ parent = if File.dirname(name) == name
29
+ nil
30
+ else
31
+ site.contents.located(File.dirname(name), restricted: false).first
32
+ end
33
+ content = parent.children.find_by(path: File.basename(name)) if parent
34
+ content = site.contents.find_by(path: File.basename(name), ancestry: nil) if parent.nil? && content.nil?
35
+ content = site.contents.create!(path: File.basename(name), parent: parent) if content.nil?
36
+ if File.directory?(name)
37
+ content.kind = 'folder'
38
+ else
39
+ File.open(name) do |f|
40
+ content.kind = Scribo::Utility.kind_for_path(name)
41
+ content.data_with_frontmatter = f.read
42
+ end
43
+ end
44
+ content.save!
45
+ end
46
+ end
47
+ end
48
+ site.contents.create(path: '_config.yml', data: YAML.dump(site.properties)) if properties_override.present?
49
+ site
50
+ end
51
+
52
+ private
53
+
54
+ def unzip(dir)
55
+ Zip::File.open(path) do |zipfile|
56
+ zipfile.reject { |e| IGNORED_FILES.any? { |r| r.match(e.name) } }.each do |f|
57
+ file_path = File.join(dir, f.name[base_path(zipfile).length..-1])
58
+ FileUtils.mkdir_p(File.dirname(file_path)) unless File.exist?(File.dirname(file_path))
59
+
60
+ f.extract(file_path) unless File.exist?(file_path)
61
+ end
62
+ end
63
+ end
64
+
65
+ def base_path(zipfile)
66
+ return @base_path if @base_path
67
+
68
+ base_paths = zipfile.reject { |e| IGNORED_FILES.any? { |r| r.match(e.name) } }
69
+ .map { |f| f.name.split('/').first }.uniq
70
+ @base_path = base_paths.size == 1 ? base_paths.first : ''
71
+ end
72
+
73
+ def site
74
+ return @site if @site
75
+
76
+ @site = Site.where(scribable: scribable)
77
+
78
+ %w[host title baseurl].each do |property|
79
+ if properties[property].nil?
80
+ @site = @site.where("properties->>'#{property}' IS NULL")
81
+ else
82
+ @site = @site.where("properties->>'#{property}' = ?", properties[property])
83
+ end
84
+ end
85
+
86
+ @site = @site.first
87
+
88
+ @site ||= Site.create!(scribable: scribable, properties: properties.merge(properties_override))
89
+ end
90
+
91
+ def properties
92
+ @properties = Scribo::Utility.yaml_safe_parse(File.read('_config.yml')) if File.exist?('_config.yml')
93
+ @properties ||= {}
94
+ end
95
+ end
96
+ end
@@ -0,0 +1 @@
1
+ = layout_with_scribo(scribo_layout_identifier, yield)
@@ -0,0 +1,22 @@
1
+ - if Scribo.config.scribable_for_request(request)
2
+ = link_to(scribo.new_admin_site_path, { 'data-turbolinks' => false }) do
3
+ .site
4
+ .icon
5
+ i.fal.fa-plus.fa-7x
6
+ .title = t('scribo.sites.create')
7
+
8
+ - if Scribo.config.templates
9
+ - for template in Scribo.config.templates
10
+ = link_to(scribo.new_admin_site_path(template_id: template[:id]), { 'data-turbolinks' => false }) do
11
+ .site
12
+ .icon
13
+ img src=template[:thumbnail]
14
+ .title = t('scribo.sites.create_from_template')
15
+
16
+ .site.site--upload data-controller="upload" data-upload-url="#{scribo.import_admin_sites_path}" data-upload-param-name="files[]" data-upload-extra-data='{}'
17
+ .icon.upload
18
+ i.fal.fa-upload.fa-7x
19
+ .icon.uploading
20
+ i.fal.fa-circle-notch.fa-7x.fa-spin
21
+ .title
22
+ = t('scribo.sites.upload')
@@ -0,0 +1,18 @@
1
+ - for site in @sites
2
+ = link_to scribo.admin_site_contents_path(site), title: t('scribo.sites.ide') do
3
+ div.site
4
+ .icon
5
+ - if site.thumbnail
6
+ img src=site.thumbnail
7
+ - else
8
+ i.fal.fa-globe.fa-7x
9
+ .title
10
+ = site.title
11
+ - if site.url
12
+ =< link_to site.url, title: t('scribo.sites.preview') do
13
+ i.fal.fa-eye
14
+ - if site.contents.count > 0
15
+ =< link_to(scribo.export_admin_site_path(site), 'data-turbolinks' => 'false', title: t('scribo.sites.export')) do
16
+ i.fal.fa-download
17
+ =< link_to scribo.admin_site_path(site), title: t('scribo.sites.delete'), data: { turbo_method: :delete, turbo_confirm: t('scribo.sites.delete_confirm') } do
18
+ i.fal.fa-trash
@@ -0,0 +1,17 @@
1
+ div style="position: absolute; width: 100%; height: 100%;"
2
+ - if @content.kind == 'asset'
3
+ - if @content.media_type == 'image'
4
+ div id="content-editor-#{@content.id}" data-controller="image-editor" data-image-editor-content-id=@content.id data-image-editor-url=content_url(@content) data-image-editor-mime-type=@content.mime_type data-image-editor-save-url=admin_site_content_path(@site, @content) style="max-width: 100%;"
5
+ - if @content.media_type == 'font' || @content.mime_type == "application/vnd.ms-fontobject"
6
+ css:
7
+ @font-face {
8
+ font-family: 'MyWebFont';
9
+ src: url(#{"#{content_path(@content)}"}); /* IE9 Compat Modes */
10
+ }
11
+ - for i in [10, 12, 14, 16, 18, 20, 22, 24]
12
+ div style="font-family: MyWebFont; font-size: #{i}px;"
13
+ | #{i} - THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG. the quick brown fox jumped over the lazy dog.
14
+
15
+ - else
16
+ textarea id="content-editor-#{@content.id}" name='content[data_with_frontmatter]' data-controller="text-editor" data-text-editor-content-id=@content.id data-text-editor-target="textarea" data-text-editor-mode="#{@content.content_type}" data-text-editor-save-url=admin_site_content_path(@site, @content) style="display: block; height: 100%;"
17
+ = @content.data_with_frontmatter || 'Start typing here'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.content do |content|
4
+ content.id @content.id
5
+ content.kind @content.kind
6
+ content.path @content.path
7
+ content.full_path @content.full_path
8
+ content.url admin_site_content_path(@site, @content)
9
+ end
10
+ json.html render partial: 'scribo/admin/sites/contents/form', layout: false, formats: [:html]
11
+ json.itemHtml render partial: 'scribo/shared/entry', layout: false, formats: [:html], locals: { content: @content, state: 'open' }
@@ -0,0 +1,2 @@
1
+ = render 'scribo/shared/ide' do
2
+ = render partial: 'form'
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.content do |content|
4
+ content.id @content.id
5
+ content.path @content.path
6
+ content.full_path @content.full_path
7
+ content.url admin_site_content_path(@site, @content)
8
+ end
9
+ json.html render partial: 'scribo/admin/sites/contents/form', layout: false, formats: [:html]
@@ -0,0 +1 @@
1
+ = render 'scribo/shared/ide'
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.content do |content|
4
+ content.id @content.id
5
+ content.kind @content.kind
6
+ content.path @content.path
7
+ content.full_path @content.full_path
8
+ content.url admin_site_content_path(@site, @content)
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.content do |content|
4
+ content.id @content.id
5
+ content.kind @content.kind
6
+ content.path @content.path
7
+ content.full_path @content.full_path
8
+ content.url admin_site_content_path(@site, @content)
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ if @parent
4
+ json.selector "li.entry.directory[data-content=\"#{@parent.id}\"]"
5
+ json.html render partial: 'scribo/shared/entry', layout: false, formats: [:html], locals: { site: @site, content: @parent, state: 'open' }
6
+ else
7
+ json.selector '.tree-view'
8
+ json.html render partial: 'scribo/shared/tree-view', layout: false, locals: { site: @site }
9
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.selector '#sites'
4
+ json.html render partial: 'sites', layout: false, locals: { site: @site }
@@ -0,0 +1,7 @@
1
+ h4= t('.existing_sites')
2
+ .scribo-sites.scribo-sites--existing
3
+ = render partial: 'sites'
4
+
5
+ h4= t('.create_site')
6
+ .scribo-sites.scribo-sites--create
7
+ = render partial: 'create_sites'