jekyll-admin-josercc 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +54 -0
  4. data/lib/jekyll/commands/build.rb +14 -0
  5. data/lib/jekyll/commands/serve.rb +26 -0
  6. data/lib/jekyll-admin/apiable.rb +153 -0
  7. data/lib/jekyll-admin/data_file.rb +106 -0
  8. data/lib/jekyll-admin/directory.rb +70 -0
  9. data/lib/jekyll-admin/file_helper.rb +64 -0
  10. data/lib/jekyll-admin/page_without_a_file.rb +7 -0
  11. data/lib/jekyll-admin/path_helper.rb +74 -0
  12. data/lib/jekyll-admin/public/03945ac4fc7fdefc44bc110bf1ba2393.svg +18 -0
  13. data/lib/jekyll-admin/public/059514c92565e4045da1a69525dd9ec0.ttf +0 -0
  14. data/lib/jekyll-admin/public/1dc35d25e61d819a9c357074014867ab.ttf +0 -0
  15. data/lib/jekyll-admin/public/25a32416abee198dd821b0b17a198a8f.eot +0 -0
  16. data/lib/jekyll-admin/public/33a752211d05af6684e26ec63c2ed965.gif +0 -0
  17. data/lib/jekyll-admin/public/55131026930a0cd4539d1e2fdb92722d.ttf +0 -0
  18. data/lib/jekyll-admin/public/847c038a8202754b465604459e16715d.png +0 -0
  19. data/lib/jekyll-admin/public/99adb54b0f30c0758bb4cb9ed5b80aa8.woff +0 -0
  20. data/lib/jekyll-admin/public/a770b6797b68e3f8920e473eb824bac0.gif +0 -0
  21. data/lib/jekyll-admin/public/bfc14ac982326f7d0b1340e20d3e0c37.ttf +0 -0
  22. data/lib/jekyll-admin/public/bundle.js +53 -0
  23. data/lib/jekyll-admin/public/bundle.js.map +1 -0
  24. data/lib/jekyll-admin/public/c4c172d28f17a61e4476715c58640841.png +0 -0
  25. data/lib/jekyll-admin/public/c8ddf1e5e5bf3682bc7bebf30f394148.woff +0 -0
  26. data/lib/jekyll-admin/public/d7c639084f684d66a1bc66855d193ed8.svg +685 -0
  27. data/lib/jekyll-admin/public/e44520ab9079ea7633bfa874bed5d21d.eot +0 -0
  28. data/lib/jekyll-admin/public/e6cf7c6ec7c2d6f670ae9d762604cb0b.woff2 +0 -0
  29. data/lib/jekyll-admin/public/favicon.ico +0 -0
  30. data/lib/jekyll-admin/public/index.html +12 -0
  31. data/lib/jekyll-admin/public/styles.css +5 -0
  32. data/lib/jekyll-admin/public/styles.css.map +1 -0
  33. data/lib/jekyll-admin/server/collection.rb +82 -0
  34. data/lib/jekyll-admin/server/configuration.rb +57 -0
  35. data/lib/jekyll-admin/server/data.rb +82 -0
  36. data/lib/jekyll-admin/server/page.rb +90 -0
  37. data/lib/jekyll-admin/server/static_file.rb +61 -0
  38. data/lib/jekyll-admin/server.rb +100 -0
  39. data/lib/jekyll-admin/static_server.rb +24 -0
  40. data/lib/jekyll-admin/urlable.rb +65 -0
  41. data/lib/jekyll-admin/version.rb +3 -0
  42. data/lib/jekyll-admin.rb +47 -0
  43. metadata +228 -0
@@ -0,0 +1,82 @@
1
+ module JekyllAdmin
2
+ class Server < Sinatra::Base
3
+ namespace "/collections" do
4
+ get do
5
+ json(site.collections.map { |c| c[1].to_api })
6
+ end
7
+
8
+ get "/:collection_id" do
9
+ ensure_collection
10
+ json collection.to_api
11
+ end
12
+
13
+ get "/:collection_id/*?/?:path.:ext" do
14
+ ensure_requested_file
15
+ json requested_file.to_api(:include_content => true)
16
+ end
17
+
18
+ get "/:collection_id/entries/?*" do
19
+ ensure_directory
20
+ json entries.map(&:to_api)
21
+ end
22
+
23
+ put "/:collection_id/*?/?:path.:ext" do
24
+ ensure_collection
25
+
26
+ if renamed?
27
+ ensure_requested_file
28
+ delete_file path
29
+ end
30
+
31
+ write_file write_path, document_body
32
+ json written_file.to_api(:include_content => true)
33
+ end
34
+
35
+ delete "/:collection_id/*?/?:path.:ext" do
36
+ ensure_requested_file
37
+ delete_file path
38
+ content_type :json
39
+ status 200
40
+ halt
41
+ end
42
+
43
+ private
44
+
45
+ def collection
46
+ collection = site.collections.find { |l, _c| l == params["collection_id"] }
47
+ collection[1] if collection
48
+ end
49
+
50
+ def document_id
51
+ path = "#{params["splat"].first}/#{filename}"
52
+ path.gsub(%r!(\d{4})/(\d{2})/(\d{2})/(.*)!, '\1-\2-\3-\4')
53
+ end
54
+
55
+ def directory_docs
56
+ collection.docs.find_all { |d| File.dirname(d.path) == directory_path }
57
+ end
58
+
59
+ def ensure_collection
60
+ render_404 if collection.nil?
61
+ end
62
+
63
+ def ensure_directory
64
+ ensure_collection
65
+ render_404 unless Dir.exist?(directory_path)
66
+ end
67
+
68
+ def entries
69
+ args = {
70
+ :base => site.source,
71
+ :content_type => params["collection_id"],
72
+ :splat => params["splat"].first,
73
+ }
74
+ # get the directories inside the requested directory
75
+ directory = JekyllAdmin::Directory.new(directory_path, args)
76
+ directories = directory.directories
77
+ # merge directories with the documents at the same level
78
+ directories.concat(directory_docs.sort_by(&:date).reverse)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,57 @@
1
+ module JekyllAdmin
2
+ class Server < Sinatra::Base
3
+ namespace "/configuration" do
4
+ get do
5
+ json({
6
+ :content => parsed_configuration,
7
+ :raw_content => raw_configuration,
8
+ })
9
+ end
10
+
11
+ put do
12
+ write_file(configuration_path, configuration_body)
13
+ json request_payload
14
+ end
15
+
16
+ private
17
+
18
+ def overrides
19
+ {
20
+ "source" => sanitized_path("/"),
21
+ }
22
+ end
23
+
24
+ # Computed configuration, with updates and defaults
25
+ def configuration
26
+ @configuration ||= Jekyll.configuration(overrides)
27
+ end
28
+
29
+ # Configuration data, as read by Jekyll
30
+ def parsed_configuration
31
+ configuration.read_config_file(configuration_path)
32
+ end
33
+
34
+ # Raw configuration content, as it sits on disk
35
+ def raw_configuration
36
+ File.read(
37
+ configuration_path,
38
+ Jekyll::Utils.merged_file_read_opts(site, {})
39
+ )
40
+ end
41
+
42
+ # Returns the path to the *first* config file discovered
43
+ def configuration_path
44
+ sanitized_path configuration.config_files(overrides).first
45
+ end
46
+
47
+ # The user's uploaded configuration for updates
48
+ # Instead of extracting `raw_content` directly from the `request_payload`,
49
+ # assign the data to a new variable and then extract the `raw_content`
50
+ # from it to circumvent CORS violation in `development` mode.
51
+ def configuration_body
52
+ payload = request_payload
53
+ payload["raw_content"]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,82 @@
1
+ module JekyllAdmin
2
+ class Server < Sinatra::Base
3
+ # supported extensions, in order of preference, for now, no .csv
4
+ EXTENSIONS = %w(yml json).freeze
5
+
6
+ namespace "/data" do
7
+ get "/*?/?:path.:ext" do
8
+ ensure_requested_file
9
+ json requested_file.to_api(:include_content => true)
10
+ end
11
+
12
+ get "/?*" do
13
+ ensure_directory
14
+ json entries.map(&:to_api)
15
+ end
16
+
17
+ put "/*?/?:path.:ext" do
18
+ if renamed?
19
+ ensure_requested_file
20
+ delete_file path
21
+ end
22
+
23
+ write_file write_path, data_file_body
24
+ json written_file.to_api(:include_content => true)
25
+ end
26
+
27
+ delete "/*?/?:path.:ext" do
28
+ ensure_requested_file
29
+ delete_file path
30
+ content_type :json
31
+ status 200
32
+ halt
33
+ end
34
+
35
+ private
36
+
37
+ # returns relative path of root level directories that contain data files
38
+ def directory_paths
39
+ DataFile.all.map { |p| File.dirname(p.relative_path).split("/")[0] }.uniq
40
+ end
41
+
42
+ def directory_pages
43
+ DataFile.all.find_all do |p|
44
+ sanitized_path(File.dirname(p.path)) == directory_path
45
+ end
46
+ end
47
+
48
+ def entries
49
+ args = {
50
+ :base => sanitized_path(DataFile.data_dir),
51
+ :content_type => "data",
52
+ :splat => splats.first,
53
+ }
54
+ # get all directories inside the requested directory
55
+ directory = JekyllAdmin::Directory.new(directory_path, args)
56
+ directories = directory.directories
57
+
58
+ # exclude root level directories which do not have data files
59
+ if splats.first.empty?
60
+ directories = directories.select do |d|
61
+ directory_paths.include? d.name.to_s
62
+ end
63
+ end
64
+
65
+ # merge directories with the pages at the same level
66
+ directories.concat(directory_pages)
67
+ end
68
+
69
+ def data_file_body
70
+ if !request_payload["raw_content"].to_s.empty?
71
+ request_payload["raw_content"]
72
+ elsif !request_payload["content"].to_s.empty?
73
+ YAML.dump(request_payload["content"]).sub(%r!\A---\n!, "")
74
+ end
75
+ end
76
+
77
+ def splats
78
+ params["splat"] || ["/"]
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,90 @@
1
+ module JekyllAdmin
2
+ class Server < Sinatra::Base
3
+ namespace "/pages" do
4
+ get "/*?/?:path.:ext" do
5
+ ensure_requested_file
6
+ json requested_file.to_api(:include_content => true)
7
+ end
8
+
9
+ get "/?*" do
10
+ ensure_directory
11
+ json entries.map(&:to_api)
12
+ end
13
+
14
+ put "/*?/?:path.:ext" do
15
+ ensure_html_content
16
+
17
+ if renamed?
18
+ ensure_requested_file
19
+ delete_file path
20
+ end
21
+
22
+ write_file write_path, page_body
23
+
24
+ json written_file.to_api(:include_content => true)
25
+ end
26
+
27
+ delete "/*?/?:path.:ext" do
28
+ ensure_requested_file
29
+ delete_file path
30
+ content_type :json
31
+ status 200
32
+ halt
33
+ end
34
+
35
+ private
36
+
37
+ def ensure_html_content
38
+ return if html_content?
39
+ content_type :json
40
+ halt 422, json("error_message" => "Invalid file extension for pages")
41
+ end
42
+
43
+ def html_content?
44
+ page = JekyllAdmin::PageWithoutAFile.new(
45
+ site,
46
+ site.source,
47
+ "",
48
+ request_payload["path"] || filename
49
+ )
50
+ page.data = request_payload["front_matter"]
51
+ page.html?
52
+ end
53
+
54
+ def pages
55
+ site.pages.select(&:html?)
56
+ end
57
+
58
+ def directory_pages
59
+ pages.find_all do |p|
60
+ sanitized_path(File.dirname(p.path)) == directory_path
61
+ end
62
+ end
63
+
64
+ # returns relative path of root level directories that contain pages
65
+ def directory_paths
66
+ pages.map { |p| File.dirname(p.path).split("/")[0] }.uniq
67
+ end
68
+
69
+ def entries
70
+ args = {
71
+ :base => site.source,
72
+ :content_type => "pages",
73
+ :splat => params["splat"].first,
74
+ }
75
+ # get all directories inside the requested directory
76
+ directory = JekyllAdmin::Directory.new(directory_path, args)
77
+ directories = directory.directories
78
+
79
+ # exclude root level directories which do not have pages
80
+ if params["splat"].first.empty?
81
+ directories = directories.select do |d|
82
+ directory_paths.include? d.name.to_s
83
+ end
84
+ end
85
+ # merge directories with the pages at the same level
86
+ directories.concat(directory_pages)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,61 @@
1
+ module JekyllAdmin
2
+ class Server < Sinatra::Base
3
+ namespace "/static_files" do
4
+ get do
5
+ json static_files.map(&:to_api)
6
+ end
7
+
8
+ get "/*" do
9
+ if requested_file
10
+ json requested_file.to_api(:include_content => true)
11
+ elsif !static_files_for_path.empty?
12
+ json static_files_for_path.map(&:to_api)
13
+ else
14
+ render_404
15
+ end
16
+ end
17
+
18
+ put "/*" do
19
+ if renamed?
20
+ ensure_requested_file
21
+ delete_file path
22
+ end
23
+
24
+ write_file(write_path, static_file_body)
25
+ json written_file.to_api(:include_content => true)
26
+ end
27
+
28
+ delete "/*" do
29
+ ensure_requested_file
30
+ delete_file path
31
+ content_type :json
32
+ status 200
33
+ halt
34
+ end
35
+
36
+ private
37
+
38
+ def static_file_body
39
+ if !request_payload["raw_content"].to_s.empty?
40
+ request_payload["raw_content"].to_s
41
+ else
42
+ Base64.decode64 request_payload["encoded_content"].to_s
43
+ end
44
+ end
45
+
46
+ def static_files
47
+ site.static_files
48
+ end
49
+
50
+ def file_list_dir(path) end
51
+
52
+ def static_files_for_path
53
+ # Joined with / to ensure user can't do partial paths
54
+ base_path = File.join(path, "/")
55
+ static_files.select do |f|
56
+ f.path.start_with? base_path
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,100 @@
1
+ module JekyllAdmin
2
+ class Server < Sinatra::Base
3
+ ROUTES = %w(collections configuration data pages static_files).freeze
4
+ include JekyllAdmin::PathHelper
5
+ include JekyllAdmin::FileHelper
6
+
7
+ register Sinatra::Namespace
8
+
9
+ configure :development do
10
+ register Sinatra::Reloader
11
+ enable :logging
12
+ end
13
+
14
+ configure :development, :test do
15
+ require "sinatra/cross_origin"
16
+ register Sinatra::CrossOrigin
17
+ enable :cross_origin
18
+ disable :allow_credentials
19
+ set :allow_methods, %i[delete get options post put]
20
+ end
21
+
22
+ get "/" do
23
+ json ROUTES.map { |r| ["#{r}_api", URI.join(base_url, "/_api/", r)] }.to_h
24
+ end
25
+
26
+ # CORS preflight
27
+ options "*" do
28
+ render_404 unless settings.development? || settings.test?
29
+ status 204
30
+ end
31
+
32
+ private
33
+
34
+ def site
35
+ JekyllAdmin.site
36
+ end
37
+
38
+ def render_404
39
+ status 404
40
+ content_type :json
41
+ halt
42
+ end
43
+
44
+ def request_payload
45
+ @request_payload ||= if request_body.to_s.empty?
46
+ {}
47
+ else
48
+ JSON.parse(request_body)
49
+ end
50
+ end
51
+
52
+ def base_url
53
+ "#{request.scheme}://#{request.host_with_port}"
54
+ end
55
+
56
+ def front_matter
57
+ request_payload["front_matter"]
58
+ end
59
+
60
+ def document_body
61
+ body = if front_matter && !front_matter.empty?
62
+ YAML.dump(restored_front_matter).strip
63
+ .gsub(": 'null'", ": null") # restore null values
64
+ else
65
+ "---"
66
+ end
67
+ body << "\n---\n\n"
68
+ body << request_payload["raw_content"].to_s
69
+ end
70
+ alias page_body document_body
71
+
72
+ private
73
+
74
+ def request_body
75
+ @request_body ||= begin
76
+ request.body.rewind
77
+ request.body.read
78
+ end
79
+ end
80
+
81
+ def namespace
82
+ namespace = request.path_info.split("/")[1].to_s.downcase
83
+ namespace if ROUTES.include?(namespace)
84
+ end
85
+
86
+ # verbose 'null' values in front matter
87
+ def restored_front_matter
88
+ front_matter.map do |key, value|
89
+ value = "null" if value.nil?
90
+ [key, value]
91
+ end.to_h
92
+ end
93
+ end
94
+ end
95
+
96
+ require "jekyll-admin/server/collection"
97
+ require "jekyll-admin/server/configuration"
98
+ require "jekyll-admin/server/data"
99
+ require "jekyll-admin/server/page"
100
+ require "jekyll-admin/server/static_file"
@@ -0,0 +1,24 @@
1
+ module JekyllAdmin
2
+ class StaticServer < Sinatra::Base
3
+ set :public_dir, File.expand_path("./public", File.dirname(__FILE__))
4
+
5
+ MUST_BUILD_MESSAGE = "Front end not yet built. Run `script/build` to build.".freeze
6
+
7
+ # Allow `/admin` and `/admin/`, and `/admin/*` to serve `/public/dist/index.html`
8
+ get "/*" do
9
+ send_file index_path
10
+ end
11
+
12
+ # Provide a descriptive error message in dev. if frontend is not build
13
+ not_found do
14
+ status 404
15
+ MUST_BUILD_MESSAGE if settings.development? || settings.test?
16
+ end
17
+
18
+ private
19
+
20
+ def index_path
21
+ @index_path ||= File.join(settings.public_folder, "index.html")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,65 @@
1
+ module JekyllAdmin
2
+ # Abstract module to be included in Convertible and Document to provide
3
+ # additional, URL-specific functionality without duplicating logic
4
+ module URLable
5
+
6
+ # Absolute URL to the HTTP(S) rendered/served representation of this resource
7
+ def http_url
8
+ return if is_a?(Jekyll::Collection) || is_a?(JekyllAdmin::DataFile)
9
+ return if is_a?(Jekyll::Document) && !collection.write?
10
+ @http_url ||= Addressable::URI.new(
11
+ :scheme => scheme, :host => host, :port => port,
12
+ :path => path_with_base(JekyllAdmin.site.config["baseurl"], url)
13
+ ).normalize.to_s
14
+ end
15
+
16
+ # Absolute URL to the API representation of this resource
17
+ def api_url
18
+ @api_url ||= Addressable::URI.new(
19
+ :scheme => scheme, :host => host, :port => port,
20
+ :path => path_with_base("/_api", resource_path)
21
+ ).normalize.to_s
22
+ end
23
+
24
+ def entries_url
25
+ return unless is_a?(Jekyll::Collection)
26
+ "#{api_url}/entries"
27
+ end
28
+
29
+ private
30
+
31
+ # URL path relative to `_api/` to retreive the given resource via the API
32
+ # Note: we can't use a case statement here, because === doesn't like includes
33
+ def resource_path
34
+ if is_a?(Jekyll::Document)
35
+ "/collections/#{relative_path.sub(%r!\A_!, "")}"
36
+ elsif is_a?(Jekyll::Collection)
37
+ "/collections/#{label}"
38
+ elsif is_a?(JekyllAdmin::DataFile)
39
+ "/data/#{relative_path}"
40
+ elsif is_a?(Jekyll::StaticFile)
41
+ "/static_files/#{relative_path}"
42
+ elsif is_a?(Jekyll::Page)
43
+ "/pages/#{relative_path}"
44
+ end
45
+ end
46
+
47
+ # URI.join doesn't like joining two relative paths, and File.join may join
48
+ # with `\` rather than with `/` on windows
49
+ def path_with_base(base, path)
50
+ [base, path].join("/").squeeze("/")
51
+ end
52
+
53
+ def scheme
54
+ JekyllAdmin.site.config["scheme"] || "http"
55
+ end
56
+
57
+ def host
58
+ JekyllAdmin.site.config["host"].sub("127.0.0.1", "localhost")
59
+ end
60
+
61
+ def port
62
+ JekyllAdmin.site.config["port"]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,3 @@
1
+ module JekyllAdmin
2
+ VERSION = "0.5.2".freeze
3
+ end
@@ -0,0 +1,47 @@
1
+ # Default Sinatra to "production" mode (surpress errors) unless
2
+ # otherwise specified by the `RACK_ENV` environmental variable.
3
+ # Must be done prior to requiring Sinatra, or we'll get a LoadError
4
+ # as it looks for sinatra/cross-origin, which is development only
5
+ ENV["RACK_ENV"] = "production" if ENV["RACK_ENV"].to_s.empty?
6
+
7
+ require "json"
8
+ require "jekyll"
9
+ require "base64"
10
+ require "webrick"
11
+ require "sinatra"
12
+ require "fileutils"
13
+ require "sinatra/base"
14
+ require "sinatra/json"
15
+ require "addressable/uri"
16
+ require "sinatra/reloader"
17
+ require "sinatra/namespace"
18
+
19
+ module JekyllAdmin
20
+ autoload :APIable, "jekyll-admin/apiable"
21
+ autoload :DataFile, "jekyll-admin/data_file"
22
+ autoload :Directory, "jekyll-admin/directory"
23
+ autoload :FileHelper, "jekyll-admin/file_helper"
24
+ autoload :PageWithoutAFile, "jekyll-admin/page_without_a_file"
25
+ autoload :PathHelper, "jekyll-admin/path_helper"
26
+ autoload :Server, "jekyll-admin/server"
27
+ autoload :StaticServer, "jekyll-admin/static_server"
28
+ autoload :URLable, "jekyll-admin/urlable"
29
+ autoload :Version, "jekyll-admin/version"
30
+
31
+ def self.site
32
+ @site ||= begin
33
+ site = Jekyll.sites.first
34
+ site.future = true
35
+ site
36
+ end
37
+ end
38
+ end
39
+
40
+ # Monkey Patches
41
+ require_relative "./jekyll/commands/serve"
42
+ require_relative "./jekyll/commands/build"
43
+
44
+ [Jekyll::Page, Jekyll::Document, Jekyll::StaticFile, Jekyll::Collection].each do |klass|
45
+ klass.include JekyllAdmin::APIable
46
+ klass.include JekyllAdmin::URLable
47
+ end