bunto-admin 0.6.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +52 -0
- data/lib/bunto-admin.rb +47 -0
- data/lib/bunto-admin/apiable.rb +161 -0
- data/lib/bunto-admin/data_file.rb +106 -0
- data/lib/bunto-admin/directory.rb +70 -0
- data/lib/bunto-admin/file_helper.rb +66 -0
- data/lib/bunto-admin/page_without_a_file.rb +7 -0
- data/lib/bunto-admin/path_helper.rb +74 -0
- data/lib/bunto-admin/server.rb +100 -0
- data/lib/bunto-admin/server/collection.rb +82 -0
- data/lib/bunto-admin/server/configuration.rb +57 -0
- data/lib/bunto-admin/server/data.rb +82 -0
- data/lib/bunto-admin/server/page.rb +90 -0
- data/lib/bunto-admin/server/static_file.rb +61 -0
- data/lib/bunto-admin/static_server.rb +24 -0
- data/lib/bunto-admin/urlable.rb +65 -0
- data/lib/bunto-admin/version.rb +3 -0
- data/lib/bunto/commands/build.rb +14 -0
- data/lib/bunto/commands/serve.rb +26 -0
- metadata +209 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6a94550c2d097b53acd31b350a63b6c4e69e6170
|
4
|
+
data.tar.gz: 96293e84a9555a6b0a923f057b2f834c39b3db86
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 53241eb2316aa7151ddb272954e95cbb8a4a98547241b306aff1bebca1f905a593a61370fd894496ce472508903af5e204e63abbf57b70ac351d9a720d748d16
|
7
|
+
data.tar.gz: 43e085fd123baa45bad5e0ce5dc8e155d372501972dd87dc1e676dd6ccf70bcec13e7e1698db48edc507d32c811d03af3c08437939bed2c1b1f90a959ab72832
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright 2017-present Mert Kahyaoğlu and the Bunto/Jekyll Admin contributors
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
[](https://rubygems.org/gems/bunto-admin)
|
2
|
+
[](https://travis-ci.org/bunto/bunto-admin)
|
3
|
+
[](https://ci.appveyor.com/project/SuriyaaKudoIsc/bunto-admin/branch/master)
|
4
|
+
[](https://coveralls.io/github/bunto/bunto-admin?branch=master)
|
5
|
+
[](https://david-dm.org/bunto/bunto-admin)
|
6
|
+
|
7
|
+
A Bunto plugin that provides users with a traditional CMS-style graphical interface to author content and administer Bunto sites. The project is divided into two parts. A Ruby-based HTTP API that handles Bunto and filesystem operations, and a Javascript-based front end, built on that API.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Refer to the [installing plugins](https://buntowaf.tk/docs/plugins/#installing-a-plugin) section of Bunto's documentation and install the `bunto-admin` plugin as you would any other plugin. Here's the short version:
|
12
|
+
|
13
|
+
1. Add the following to your site's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'bunto-admin', group: :bunto_plugins
|
17
|
+
```
|
18
|
+
|
19
|
+
2. Run `bundle install`
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
1. Start Bunto as you would normally (`bundle exec bunto serve`)
|
24
|
+
2. Navigate to `http://localhost:4000/admin` to access the administrative interface
|
25
|
+
|
26
|
+
## Options
|
27
|
+
|
28
|
+
Bunto Admin related options can be specified in `_config.yml`
|
29
|
+
under a key called `bunto_admin`. Currently it has only one option `hidden_links`
|
30
|
+
which is for hiding unwanted links on the sidebar. The following keys under `hidden_links` can be used in order to hide default links;
|
31
|
+
|
32
|
+
```yaml
|
33
|
+
bunto_admin:
|
34
|
+
hidden_links:
|
35
|
+
- posts
|
36
|
+
- pages
|
37
|
+
- staticfiles
|
38
|
+
- datafiles
|
39
|
+
- configuration
|
40
|
+
```
|
41
|
+
|
42
|
+
## Contributing
|
43
|
+
|
44
|
+
Interested in contributing to Bunto Admin? We’d love your help. Bunto Admin is an open source project, built one contribution at a time by users like you. See [the contributing instructions](.github/CONTRIBUTING.md), and [the development docs](https://bunto.github.io/bunto-admin/development/) for more information.
|
45
|
+
|
46
|
+
## Looking for a hosted version?
|
47
|
+
|
48
|
+
Bunto Admin is intended to be run on your computer alongside your local Bunto installation. If you're looking for a hosted version, we'd recommend checking out [Siteleaf](https://www.siteleaf.com/) a hosted Bunto editor with deep GitHub integration (whom we'd also like to thank for inspiring parts of Bunto Admin itself!).
|
49
|
+
|
50
|
+
## License
|
51
|
+
|
52
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/bunto-admin.rb
ADDED
@@ -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 "bunto"
|
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 BuntoAdmin
|
20
|
+
autoload :APIable, "bunto-admin/apiable"
|
21
|
+
autoload :DataFile, "bunto-admin/data_file"
|
22
|
+
autoload :Directory, "bunto-admin/directory"
|
23
|
+
autoload :FileHelper, "bunto-admin/file_helper"
|
24
|
+
autoload :PageWithoutAFile, "bunto-admin/page_without_a_file"
|
25
|
+
autoload :PathHelper, "bunto-admin/path_helper"
|
26
|
+
autoload :Server, "bunto-admin/server"
|
27
|
+
autoload :StaticServer, "bunto-admin/static_server"
|
28
|
+
autoload :URLable, "bunto-admin/urlable"
|
29
|
+
autoload :Version, "bunto-admin/version"
|
30
|
+
|
31
|
+
def self.site
|
32
|
+
@site ||= begin
|
33
|
+
site = Bunto.sites.first
|
34
|
+
site.future = true
|
35
|
+
site
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Monkey Patches
|
41
|
+
require_relative "./bunto/commands/serve"
|
42
|
+
require_relative "./bunto/commands/build"
|
43
|
+
|
44
|
+
[Bunto::Page, Bunto::Document, Bunto::StaticFile, Bunto::Collection].each do |klass|
|
45
|
+
klass.include BuntoAdmin::APIable
|
46
|
+
klass.include BuntoAdmin::URLable
|
47
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module BuntoAdmin
|
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?(Bunto::Collection)
|
42
|
+
output.delete("docs")
|
43
|
+
output["entries_url"] = entries_url
|
44
|
+
end
|
45
|
+
|
46
|
+
if is_a?(Bunto::Document)
|
47
|
+
output["name"] = basename
|
48
|
+
end
|
49
|
+
|
50
|
+
if is_a?(Bunto::StaticFile)
|
51
|
+
output["from_theme"] = from_theme_gem?
|
52
|
+
end
|
53
|
+
|
54
|
+
output
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Pages don't have a hash method, but Documents do
|
60
|
+
def file_path
|
61
|
+
if is_a?(Bunto::Document)
|
62
|
+
path
|
63
|
+
else
|
64
|
+
File.join(@base, @dir, name)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def from_theme_gem?
|
69
|
+
@base == site.in_theme_dir
|
70
|
+
end
|
71
|
+
|
72
|
+
# StaticFiles don't have `attr_accesor` set for @site or @name
|
73
|
+
def site
|
74
|
+
@site
|
75
|
+
end
|
76
|
+
|
77
|
+
def name
|
78
|
+
@name
|
79
|
+
end
|
80
|
+
|
81
|
+
def file_contents
|
82
|
+
@file_contents ||= File.read(file_path, file_read_options) if file_exists?
|
83
|
+
end
|
84
|
+
|
85
|
+
def file_read_options
|
86
|
+
Bunto::Utils.merged_file_read_opts(site, {})
|
87
|
+
end
|
88
|
+
|
89
|
+
def front_matter
|
90
|
+
return unless file_exists?
|
91
|
+
@front_matter ||= if file_contents =~ Bunto::Document::YAML_FRONT_MATTER_REGEXP
|
92
|
+
SafeYAML.load(Regexp.last_match(1))
|
93
|
+
else
|
94
|
+
{}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def raw_content
|
99
|
+
return unless file_exists?
|
100
|
+
@raw_content ||= if file_contents =~ Bunto::Document::YAML_FRONT_MATTER_REGEXP
|
101
|
+
$POSTMATCH
|
102
|
+
else
|
103
|
+
file_contents
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def encoded_content
|
108
|
+
@encoded_content ||= Base64.encode64(file_contents) if file_exists?
|
109
|
+
end
|
110
|
+
|
111
|
+
def file_exists?
|
112
|
+
return @file_exists if defined? @file_exists
|
113
|
+
@file_exists = File.exist?(file_path)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Base hash from which to generate the API output
|
117
|
+
def hash_for_api
|
118
|
+
output = to_liquid
|
119
|
+
if output.respond_to?(:hash_for_json)
|
120
|
+
output.hash_for_json
|
121
|
+
else
|
122
|
+
output.to_h
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns a hash of content fields for inclusion in the API output
|
127
|
+
def content_fields
|
128
|
+
output = {}
|
129
|
+
|
130
|
+
# Include file content-related fields
|
131
|
+
if is_a?(Bunto::StaticFile)
|
132
|
+
output["encoded_content"] = encoded_content
|
133
|
+
elsif is_a?(BuntoAdmin::DataFile)
|
134
|
+
output["content"] = content
|
135
|
+
output["raw_content"] = raw_content
|
136
|
+
else
|
137
|
+
output["raw_content"] = raw_content
|
138
|
+
output["front_matter"] = front_matter
|
139
|
+
end
|
140
|
+
|
141
|
+
# Include next and previous documents non-recursively
|
142
|
+
if is_a?(Bunto::Document)
|
143
|
+
%w(next previous).each do |direction|
|
144
|
+
method = "#{direction}_doc".to_sym
|
145
|
+
doc = public_send(method)
|
146
|
+
output[direction] = doc.to_api if doc
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
output
|
151
|
+
end
|
152
|
+
|
153
|
+
def url_fields
|
154
|
+
return {} unless respond_to?(:http_url) && respond_to?(:api_url)
|
155
|
+
{
|
156
|
+
"http_url" => http_url,
|
157
|
+
"api_url" => api_url,
|
158
|
+
}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module BuntoAdmin
|
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 :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 Bunto'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 ||= Bunto::Utils.titleize_slug(slug.tr("_", "-"))
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return path relative to configured `data_dir`
|
56
|
+
def relative_path
|
57
|
+
id.sub("/#{DataFile.data_dir}/", "")
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return the absolute path to given data file
|
61
|
+
def absolute_path
|
62
|
+
sanitized_path id
|
63
|
+
end
|
64
|
+
|
65
|
+
# Mimics Bunto'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 = sanitized_path DataFile.data_dir
|
73
|
+
files = Dir["#{data_dir}/**/*.{#{EXTENSIONS.join(",")}}"].reject do |d|
|
74
|
+
File.directory?(d)
|
75
|
+
end
|
76
|
+
|
77
|
+
files.map do |path|
|
78
|
+
new path_without_site_source(path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Relative path to data directory within site source
|
83
|
+
def self.data_dir
|
84
|
+
BuntoAdmin.site.config["data_dir"]
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def data_reader
|
90
|
+
@data_reader = Bunto::DataReader.new(BuntoAdmin.site)
|
91
|
+
end
|
92
|
+
|
93
|
+
def basename
|
94
|
+
@basename ||= File.basename(id, ".*")
|
95
|
+
end
|
96
|
+
|
97
|
+
def basename_with_extension
|
98
|
+
[basename, extension].join
|
99
|
+
end
|
100
|
+
alias_method :filename, :basename_with_extension
|
101
|
+
|
102
|
+
def namespace
|
103
|
+
"data"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module BuntoAdmin
|
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 BuntoAdmin::URLable
|
10
|
+
include BuntoAdmin::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
|
+
path.relative_path_from(base).to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def resource_path
|
45
|
+
if content_type == "pages"
|
46
|
+
"/pages/#{splat}/#{name}"
|
47
|
+
elsif content_type == "data"
|
48
|
+
"/data/#{splat}/#{name}/"
|
49
|
+
else
|
50
|
+
"/collections/#{content_type}/entries/#{splat}/#{name}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alias_method :url, :resource_path
|
54
|
+
|
55
|
+
def http_url
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def directories
|
60
|
+
path.entries.map do |entry|
|
61
|
+
next if [".", ".."].include? entry.to_s
|
62
|
+
next unless path.join(entry).directory?
|
63
|
+
self.class.new(
|
64
|
+
path.join(entry),
|
65
|
+
:base => base, :content_type => content_type, :splat => splat
|
66
|
+
)
|
67
|
+
end.compact!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|