jekyll-manager 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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