bunto-admin 0.6.0

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