jekyll-manager 0.1.0

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 (49) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +63 -0
  4. data/lib/jekyll-admin.rb +47 -0
  5. data/lib/jekyll-admin/apiable.rb +167 -0
  6. data/lib/jekyll-admin/data_file.rb +103 -0
  7. data/lib/jekyll-admin/directory.rb +73 -0
  8. data/lib/jekyll-admin/file_helper.rb +68 -0
  9. data/lib/jekyll-admin/page_without_a_file.rb +7 -0
  10. data/lib/jekyll-admin/path_helper.rb +78 -0
  11. data/lib/jekyll-admin/public/bundle.js +58 -0
  12. data/lib/jekyll-admin/public/bundle.js.map +1 -0
  13. data/lib/jekyll-admin/public/favicon.ico +0 -0
  14. data/lib/jekyll-admin/public/fonts/fontawesome-webfont.eot +0 -0
  15. data/lib/jekyll-admin/public/fonts/fontawesome-webfont.ttf +0 -0
  16. data/lib/jekyll-admin/public/fonts/fontawesome-webfont.woff +0 -0
  17. data/lib/jekyll-admin/public/fonts/fontawesome-webfont.woff2 +0 -0
  18. data/lib/jekyll-admin/public/fonts/lato-bold.ttf +0 -0
  19. data/lib/jekyll-admin/public/fonts/lato-regular.ttf +0 -0
  20. data/lib/jekyll-admin/public/fonts/rw-widgets.eot +0 -0
  21. data/lib/jekyll-admin/public/fonts/rw-widgets.ttf +0 -0
  22. data/lib/jekyll-admin/public/fonts/rw-widgets.woff +0 -0
  23. data/lib/jekyll-admin/public/images/fontawesome-webfont.svg +685 -0
  24. data/lib/jekyll-admin/public/images/loader-big.gif +0 -0
  25. data/lib/jekyll-admin/public/images/loading.gif +0 -0
  26. data/lib/jekyll-admin/public/images/logo-black-red.png +0 -0
  27. data/lib/jekyll-admin/public/images/logo.png +0 -0
  28. data/lib/jekyll-admin/public/images/no-image.svg +1 -0
  29. data/lib/jekyll-admin/public/images/rw-widgets.svg +18 -0
  30. data/lib/jekyll-admin/public/index.html +13 -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.rb +119 -0
  34. data/lib/jekyll-admin/server/collection.rb +88 -0
  35. data/lib/jekyll-admin/server/configuration.rb +57 -0
  36. data/lib/jekyll-admin/server/dashboard.rb +113 -0
  37. data/lib/jekyll-admin/server/data.rb +82 -0
  38. data/lib/jekyll-admin/server/draft.rb +110 -0
  39. data/lib/jekyll-admin/server/page.rb +90 -0
  40. data/lib/jekyll-admin/server/static_file.rb +82 -0
  41. data/lib/jekyll-admin/server/template.rb +196 -0
  42. data/lib/jekyll-admin/server/theme.rb +135 -0
  43. data/lib/jekyll-admin/static_server.rb +24 -0
  44. data/lib/jekyll-admin/urlable.rb +67 -0
  45. data/lib/jekyll-admin/version.rb +3 -0
  46. data/lib/jekyll-manager.rb +5 -0
  47. data/lib/jekyll/commands/build.rb +14 -0
  48. data/lib/jekyll/commands/serve.rb +26 -0
  49. metadata +253 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d0ed1d6abe3190db2e01c9b5f6f1e161c94ac67
4
+ data.tar.gz: 95725959f6f2d1f3bc41e8d31c4f4dc7044ea198
5
+ SHA512:
6
+ metadata.gz: 8adc158be57c256dfd4182efd28f351324beec4c56ff1e37aa974a842d944143dc8b7009be239856e07f616490677ae95c3ebca5787e65ab8bfb52d305b9dcc4
7
+ data.tar.gz: 208927717049efaa4e0a435078dcf8036c33a42eefbc07c206eadf457d1fce3a1bd044e0ffaa2948ceb7c54a345fe23c5028c5c2cecc614a12feb009fcbea0d4
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright 2016-2017 Mert Kahyaoğlu and the Jekyll Admin contributors
4
+ Copyright 2017 Ashwin Maroli
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1,63 @@
1
+ [![Gem Version](https://img.shields.io/gem/v/jekyll-manager.svg)](https://rubygems.org/gems/jekyll-manager)
2
+ [![Build Status](https://travis-ci.org/ashmaroli/jekyll-manager.svg?branch=master)](https://travis-ci.org/ashmaroli/jekyll-manager)
3
+ [![Build status](https://ci.appveyor.com/api/projects/status/biop1r6ae524xlm2/branch/master?svg=true)](https://ci.appveyor.com/project/ashmaroli/jekyll-manager/branch/master)
4
+ [![Coverage Status](https://coveralls.io/repos/github/ashmaroli/jekyll-manager/badge.svg?branch=master)](https://coveralls.io/github/ashmaroli/jekyll-manager?branch=master)
5
+ [![NPM Dependencies](https://david-dm.org/ashmaroli/jekyll-manager.svg)](https://david-dm.org/ashmaroli/jekyll-manager)
6
+
7
+ Forked from the official Jekyll plugin [Jekyll Admin](https://github.com/jekyll/jekyll-admin), Jekyll Manager provides users with a traditional CMS-style graphical interface to author content and administer Jekyll sites.<br/>
8
+ The project is divided into two parts. A Ruby-based HTTP API that handles Jekyll and filesystem operations, and a Javascript-based front end, built on that API.
9
+
10
+ ![screenshot of Jekyll Manager](/screenshot.png)
11
+
12
+ ## Installation
13
+
14
+ Refer to the [installing plugins](https://jekyllrb.com/docs/plugins/#installing-a-plugin) section of Jekyll's documentation and install the `jekyll-manager` plugin as you would any other plugin. Here's the short version:
15
+
16
+ 1. Add the following to your site's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'jekyll-manager', group: :jekyll_plugins
20
+ ```
21
+
22
+ 2. Run `bundle install`
23
+
24
+ ## Usage
25
+
26
+ 1. Start Jekyll as you would normally (`bundle exec jekyll serve`)
27
+ 2. Navigate to `http://localhost:4000/admin` to access the administrative interface
28
+
29
+
30
+ ## Divergence
31
+
32
+ Jekyll Manager is an open source project, forked from the official Jekyll plugin [Jekyll Admin](https://github.com/jekyll/jekyll-admin), and repackaged with some alterations and additions, a few of which, may eventually be included in the official version.
33
+
34
+ ### Notable alterations:
35
+
36
+ * Sidebar routes cannot be manually hidden. They're rendered based on whether Jekyll has read-in at least one file of the concerned type.
37
+ * Routes to Collections other than Posts are hidden within a collapsed list-item by default.
38
+ * Metadata fields for front matter are hidden with a collapsed section by default.
39
+ * Input path fields show / require the full `relative_path` of the requested file.
40
+ * Minor style changes.
41
+ * Other miscellaneous changes.
42
+
43
+ ### Additional Features:
44
+
45
+ * A basic dashboard that provides insight on the current site and a means to add files to cetain empty content types (*Pages, Posts, Data Files, Static Files*).
46
+ * Draft posts can be created and edited via the admin interface provided your config file has `show_drafts: true`
47
+ * Template files (files within `_layouts`, `_includes`, `_sass` and `assets` at the root of your site) can be edited via the interface.
48
+ * Template files (and files within any directory) within a theme-gem can be *viewed* and copied over to the source directory for editing.
49
+ * Ability to select layouts for a document based on available layouts in the Site.
50
+ * Special metadata field for tags.
51
+
52
+
53
+ ## Contributing
54
+
55
+ Unless your contribution improves the changes outlined above or updates this repo's documentation, we'd appreciate it if you propose those changes at the upstream repo. Upstream changes *may* eventually find their way here after being altered as
56
+ required.
57
+
58
+ Interested in contributing to Jekyll Manager anyways?. See [the contributing instructions](.github/CONTRIBUTING.md), and [the development docs](http://ashmaroli.github.io/jekyll-manager/development/) for more information.
59
+
60
+
61
+ ## License
62
+
63
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -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 "oj" # Optimized JSON. https://github.com/ohler55/oj
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
@@ -0,0 +1,167 @@
1
+ module JekyllAdmin
2
+ # Abstract module to be included in Convertible and Document to provide
3
+ # additional, API-specific functionality without duplicating logic
4
+ module APIable
5
+
6
+ CONTENT_FIELDS = %w(next previous content excerpt).freeze
7
+
8
+ # Returns a hash suitable for use as an API response.
9
+ #
10
+ # For Documents and Pages:
11
+ #
12
+ # 1. Adds the file's raw content to the `raw_content` field
13
+ # 2. Adds the file's raw YAML front matter to the `front_matter` field
14
+ #
15
+ # For Static Files it addes the Base64 `encoded_content` field
16
+ #
17
+ # Options:
18
+ #
19
+ # include_content - if true, includes the content in the respond, false by default
20
+ # to support mapping on indexes where we only want metadata
21
+ #
22
+ #
23
+ # Returns a hash (which can then be to_json'd)
24
+ def to_api(include_content: false)
25
+ output = hash_for_api
26
+ output = output.merge(url_fields)
27
+
28
+ # Include content, if requested, otherwise remove it
29
+ if include_content
30
+ output = output.merge(content_fields)
31
+ else
32
+ CONTENT_FIELDS.each { |field| output.delete(field) }
33
+ end
34
+
35
+ # Documents have duplicate output and content fields, Pages do not
36
+ # Since it's an API, use `content` in both for consistency
37
+ output.delete("output")
38
+
39
+ # By default, calling to_liquid on a collection will return a docs
40
+ # array with each rendered document, which we don't want.
41
+ if is_a?(Jekyll::Collection)
42
+ output.delete("docs")
43
+ output["entries_url"] = entries_url
44
+ end
45
+
46
+ if is_a?(Jekyll::Document)
47
+ output["name"] = basename
48
+ output["modified_time"] = mtime
49
+ output["relative_path"] = relative_path.sub("_drafts/", "") if draft?
50
+ end
51
+
52
+ if is_a?(Jekyll::StaticFile)
53
+ output["from_theme"] = from_theme_gem?
54
+ end
55
+
56
+ output
57
+ end
58
+
59
+ private
60
+
61
+ # Pages don't have a hash method, but Documents do
62
+ def file_path
63
+ if is_a?(Jekyll::Document)
64
+ path
65
+ else
66
+ File.join(@base, @dir, name)
67
+ end
68
+ end
69
+
70
+ def from_theme_gem?
71
+ @base == site.in_theme_dir
72
+ end
73
+
74
+ # StaticFiles don't have `attr_accesor` set for @site or @name
75
+ def site
76
+ @site
77
+ end
78
+
79
+ def name
80
+ @name
81
+ end
82
+
83
+ def mtime
84
+ @mtime ||= File.stat(file_path).mtime
85
+ end
86
+
87
+ def file_contents
88
+ @file_contents ||= File.read(file_path, file_read_options) if file_exists?
89
+ end
90
+
91
+ def file_read_options
92
+ Jekyll::Utils.merged_file_read_opts(site, {})
93
+ end
94
+
95
+ def front_matter
96
+ return unless file_exists?
97
+ @front_matter ||= if file_contents =~ Jekyll::Document::YAML_FRONT_MATTER_REGEXP
98
+ SafeYAML.load(Regexp.last_match(1))
99
+ else
100
+ {}
101
+ end
102
+ end
103
+
104
+ def raw_content
105
+ return unless file_exists?
106
+ @raw_content ||= if file_contents =~ Jekyll::Document::YAML_FRONT_MATTER_REGEXP
107
+ $POSTMATCH
108
+ else
109
+ file_contents
110
+ end
111
+ end
112
+
113
+ def encoded_content
114
+ @encoded_content ||= Base64.encode64(file_contents) if file_exists?
115
+ end
116
+
117
+ def file_exists?
118
+ return @file_exists if defined? @file_exists
119
+ @file_exists = File.exist?(file_path)
120
+ end
121
+
122
+ # Base hash from which to generate the API output
123
+ def hash_for_api
124
+ output = to_liquid
125
+ if output.respond_to?(:hash_for_json)
126
+ output.hash_for_json
127
+ else
128
+ output.to_h
129
+ end
130
+ end
131
+
132
+ # Returns a hash of content fields for inclusion in the API output
133
+ def content_fields
134
+ output = {}
135
+
136
+ # Include file content-related fields
137
+ if is_a?(Jekyll::StaticFile)
138
+ output["encoded_content"] = encoded_content
139
+ elsif is_a?(JekyllAdmin::DataFile)
140
+ output["content"] = content
141
+ output["raw_content"] = raw_content
142
+ else
143
+ output["raw_content"] = raw_content
144
+ output["front_matter"] = front_matter
145
+ end
146
+
147
+ # Include next and previous documents non-recursively
148
+ if is_a?(Jekyll::Document)
149
+ %w(next previous).each do |direction|
150
+ method = "#{direction}_doc".to_sym
151
+ doc = public_send(method)
152
+ output[direction] = doc.to_api if doc
153
+ end
154
+ end
155
+
156
+ output
157
+ end
158
+
159
+ def url_fields
160
+ return {} unless respond_to?(:http_url) && respond_to?(:api_url)
161
+ {
162
+ "http_url" => http_url,
163
+ "api_url" => api_url,
164
+ }
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,103 @@
1
+ module JekyllAdmin
2
+ class DataFile
3
+ METHODS_FOR_LIQUID = %w(path relative_path slug ext title).freeze
4
+ EXTENSIONS = %w(yaml yml json csv).freeze
5
+
6
+ include APIable
7
+ include URLable
8
+ include PathHelper
9
+ extend PathHelper
10
+
11
+ attr_reader :id
12
+
13
+ # Initialize a new DataFile object
14
+ #
15
+ # id - the file ID as passed from the API. This may or may not have an extension
16
+ def initialize(id)
17
+ @id ||= File.extname(id).empty? ? "#{id}.yml" : id
18
+ end
19
+ alias_method :relative_path, :id
20
+
21
+ def exists?
22
+ @exists ||= File.exist?(absolute_path)
23
+ end
24
+
25
+ # Returns unparsed content as it exists on disk
26
+ def raw_content
27
+ @raw_content ||= File.open(absolute_path, "r:UTF-8", &:read)
28
+ end
29
+
30
+ # Returnes (re)parsed content using Jekyll's native parsing mechanism
31
+ def content
32
+ @content ||= data_reader.read_data_file(absolute_path)
33
+ end
34
+
35
+ # Returns the file's extension with preceeding `.`
36
+ def ext
37
+ @ext ||= if File.extname(id).to_s.empty?
38
+ ".yml"
39
+ else
40
+ File.extname(id)
41
+ end
42
+ end
43
+ alias_method :extension, :ext
44
+
45
+ # Returns the file's sanitized slug (as used in `site.data[slug]`)
46
+ def slug
47
+ @slug ||= data_reader.sanitize_filename(basename)
48
+ end
49
+
50
+ # Returns the human-readable title of the data file
51
+ def title
52
+ @title ||= Jekyll::Utils.titleize_slug(slug.tr("_", "-"))
53
+ end
54
+
55
+ # Returns path relative to site source
56
+ def path
57
+ ensure_leading_slash(File.join(DataFile.data_dir, relative_path))
58
+ end
59
+
60
+ def absolute_path
61
+ sanitized_path(path)
62
+ end
63
+ alias_method :file_path, :absolute_path
64
+
65
+ # Mimics Jekyll's native to_liquid functionality by returning a hash
66
+ # of data file metadata
67
+ def to_liquid
68
+ @to_liquid ||= METHODS_FOR_LIQUID.map { |key| [key, public_send(key)] }.to_h
69
+ end
70
+
71
+ def self.all
72
+ data_dir = Jekyll.sanitized_path(JekyllAdmin.site.source, DataFile.data_dir)
73
+ data_dir = Pathname.new(data_dir)
74
+ Dir["#{data_dir}/**/*.{#{EXTENSIONS.join(",")}}"].map do |path|
75
+ new(Pathname.new(path).relative_path_from(data_dir).to_s)
76
+ end
77
+ end
78
+
79
+ # Relative path to data directory within site source
80
+ def self.data_dir
81
+ JekyllAdmin.site.config["data_dir"]
82
+ end
83
+
84
+ private
85
+
86
+ def data_reader
87
+ @data_reader = Jekyll::DataReader.new(JekyllAdmin.site)
88
+ end
89
+
90
+ def basename
91
+ @basename ||= File.basename(id, ".*")
92
+ end
93
+
94
+ def basename_with_extension
95
+ [basename, extension].join
96
+ end
97
+ alias_method :filename, :basename_with_extension
98
+
99
+ def namespace
100
+ "data"
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,73 @@
1
+ module JekyllAdmin
2
+ class Directory
3
+ extend Forwardable
4
+ def_delegator :@path, :basename, :name
5
+ def_delegator :@path, :mtime, :modified_time
6
+ attr_reader :path, :splat, :content_type, :base
7
+
8
+ include Enumerable
9
+ include JekyllAdmin::URLable
10
+ include JekyllAdmin::APIable
11
+
12
+ TYPE = :directory
13
+
14
+ # Arguments:
15
+ #
16
+ # path - full path of the directory which its entries will be listed
17
+ #
18
+ # base - passes site.source to generate `relative_path` needed for `to_api`
19
+ #
20
+ # content_type - type of the requested directory entries, this is used to generate
21
+ # API endpoint of the directory along with `splat`
22
+ #
23
+ # splat - the requested directory path relative to content namespace
24
+ def initialize(path, base: nil, content_type: nil, splat: nil)
25
+ @base = Pathname.new base
26
+ @content_type = content_type
27
+ @splat = Pathname.new splat
28
+ @path = Pathname.new path
29
+ end
30
+
31
+ def to_liquid
32
+ {
33
+ :name => name,
34
+ :modified_time => modified_time,
35
+ :path => relative_path,
36
+ :type => TYPE,
37
+ }
38
+ end
39
+
40
+ def relative_path
41
+ if content_type == "drafts"
42
+ path.relative_path_from(base).to_s.sub("_drafts/", "")
43
+ else
44
+ path.relative_path_from(base).to_s
45
+ end
46
+ end
47
+
48
+ def resource_path
49
+ types = %w(pages data drafts static_files templates theme)
50
+ if types.include?(content_type)
51
+ "/#{content_type}/#{splat}/#{name}"
52
+ else
53
+ "/collections/#{content_type}/entries/#{splat}/#{name}"
54
+ end
55
+ end
56
+ alias_method :url, :resource_path
57
+
58
+ def http_url
59
+ nil
60
+ end
61
+
62
+ def directories
63
+ path.entries.map do |entry|
64
+ next if [".", ".."].include? entry.to_s
65
+ next unless path.join(entry).directory?
66
+ self.class.new(
67
+ path.join(entry),
68
+ :base => base, :content_type => content_type, :splat => splat
69
+ )
70
+ end.compact!
71
+ end
72
+ end
73
+ end