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.
@@ -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.
@@ -0,0 +1,52 @@
1
+ [![Gem Version](https://img.shields.io/gem/v/bunto-admin.svg)](https://rubygems.org/gems/bunto-admin)
2
+ [![Build Status](https://travis-ci.org/bunto/bunto-admin.svg?branch=master)](https://travis-ci.org/bunto/bunto-admin)
3
+ [![Build status](https://ci.appveyor.com/api/projects/status/ciih4n6v0yfa5pvm/branch/master?svg=true)](https://ci.appveyor.com/project/SuriyaaKudoIsc/bunto-admin/branch/master)
4
+ [![Coverage Status](https://coveralls.io/repos/github/bunto/bunto-admin/badge.svg?branch=master)](https://coveralls.io/github/bunto/bunto-admin?branch=master)
5
+ [![NPM Dependencies](https://david-dm.org/bunto/bunto-admin.svg)](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).
@@ -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