jekyll-admin 0.9.0 → 0.10.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -1
  3. data/lib/jekyll-admin.rb +14 -13
  4. data/lib/jekyll-admin/apiable.rb +71 -10
  5. data/lib/jekyll-admin/data_file.rb +19 -16
  6. data/lib/jekyll-admin/directory.rb +24 -26
  7. data/lib/jekyll-admin/file_helper.rb +14 -0
  8. data/lib/jekyll-admin/path_helper.rb +8 -1
  9. data/lib/jekyll-admin/public/12f0820c451bdc75f4d1ef97732bf6e8.woff +0 -0
  10. data/lib/jekyll-admin/public/{03945ac4fc7fdefc44bc110bf1ba2393.svg → 792dcd18baf5f544aabcad1883d673c2.svg} +14 -8
  11. data/lib/jekyll-admin/public/{bfc14ac982326f7d0b1340e20d3e0c37.ttf → bc7c4a59f924cf037aad6e1f9edba366.eot} +0 -0
  12. data/lib/jekyll-admin/public/bundle.js +27 -17
  13. data/lib/jekyll-admin/public/bundle.js.map +1 -1
  14. data/lib/jekyll-admin/public/{e44520ab9079ea7633bfa874bed5d21d.eot → eceddf474df95d8d4a7e316668c3be85.ttf} +0 -0
  15. data/lib/jekyll-admin/public/styles.css +103 -2
  16. data/lib/jekyll-admin/public/styles.css.map +1 -1
  17. data/lib/jekyll-admin/server.rb +21 -12
  18. data/lib/jekyll-admin/server/{collection.rb → collections.rb} +11 -4
  19. data/lib/jekyll-admin/server/configuration.rb +5 -2
  20. data/lib/jekyll-admin/server/data.rb +3 -1
  21. data/lib/jekyll-admin/server/{draft.rb → drafts.rb} +8 -3
  22. data/lib/jekyll-admin/server/{page.rb → pages.rb} +22 -4
  23. data/lib/jekyll-admin/server/site_meta.rb +25 -0
  24. data/lib/jekyll-admin/server/static_files.rb +83 -0
  25. data/lib/jekyll-admin/static_server.rb +3 -1
  26. data/lib/jekyll-admin/urlable.rb +4 -0
  27. data/lib/jekyll-admin/version.rb +3 -1
  28. data/lib/jekyll/commands/serve.rb +2 -0
  29. metadata +14 -14
  30. data/lib/jekyll-admin/page_without_a_file.rb +0 -7
  31. data/lib/jekyll-admin/public/99adb54b0f30c0758bb4cb9ed5b80aa8.woff +0 -0
  32. data/lib/jekyll-admin/server/static_file.rb +0 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23d2ff89a0eaf62c63c8b52e919ae8a347ec623c4f0db5a2810ca387d5aa92f0
4
- data.tar.gz: 5ac8a35dfa123e685c61a75488e19c0106dd55b3984e60b6fd8ed083d87b67a7
3
+ metadata.gz: 812331dbfeef3f0d734b9ed66c1d9a1acdc3dc10c86cbeaac9ec86be92fe2bc9
4
+ data.tar.gz: 8b072ab67bfccb13f8a33f56504da72296337a03ccb633a742e2fac0cd00e247
5
5
  SHA512:
6
- metadata.gz: 4527e5b2b8c03417a198784c2949f47f596e3d004982a6a603a0f007f16094559eab7815b2b916e3e93c4f62dd6289401a33eea218c60e3f6a303ee257a96ff1
7
- data.tar.gz: d6770f35c386dfd341731e6ea27ba403d53c6b8d1afd9acbe8b2ce97917bb178598e32f21e8232ef566e3337126e23534ce755ebf8f78cfc2f532322e22ef2a3
6
+ metadata.gz: 70be96805de811a7b00bd279318e5285bd3f22ae5efc2a9849a8eba4e82c2a725319c31219ba347cd2582aad148b428586a0932ac8140e81cf260ae036553ea8
7
+ data.tar.gz: 3b17ef26207b523088fc917efd755d7844a9d8238e5ac24245c07d590d9d22a5b95382c775ecf035c8cc2aa2587a989f55d34899f17eeba9b48ff45f9a875dc6
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  [![Gem Version](https://img.shields.io/gem/v/jekyll-admin.svg)](https://rubygems.org/gems/jekyll-admin)
2
2
  [![Build Status](https://travis-ci.org/jekyll/jekyll-admin.svg?branch=master)](https://travis-ci.org/jekyll/jekyll-admin)
3
- [![Build status](https://ci.appveyor.com/api/projects/status/biop1r6ae524xlm2/branch/master?svg=true)](https://ci.appveyor.com/project/benbalter/jekyll-admin/branch/master)
3
+ [![Build status](https://ci.appveyor.com/api/projects/status/u6u9tn7rk5tln33s/branch/master?svg=true)](https://ci.appveyor.com/project/jekyll/jekyll-admin)
4
4
  [![Coverage Status](https://coveralls.io/repos/github/jekyll/jekyll-admin/badge.svg?branch=master)](https://coveralls.io/github/jekyll/jekyll-admin?branch=master)
5
5
  [![NPM Dependencies](https://david-dm.org/jekyll/jekyll-admin.svg)](https://david-dm.org/jekyll/jekyll-admin)
6
+ [![Financial Contributors on Open Collective](https://opencollective.com/jekyll-admin/all/badge.svg?label=financial+contributors)](https://opencollective.com/jekyll-admin)
6
7
 
7
8
  A Jekyll plugin that provides users with a traditional CMS-style graphical interface to author content and administer Jekyll sites. 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.
8
9
 
@@ -41,10 +42,53 @@ jekyll_admin:
41
42
  - configuration
42
43
  ```
43
44
 
45
+ ### Customizing collection label in Sidebar
46
+
47
+ The plugin allows you to customize the name of a collection that is displayed in the sidebar by defining it in the collection's
48
+ metadata in the config file. For example, if your source's *posts* are actually *news-items* on the deployed site, then it can
49
+ be distracting to see the label `Posts` in the admin's sidebar. This situation can be resolved with the following configuration:
50
+
51
+ ```yaml
52
+ collections:
53
+ posts:
54
+ output: true
55
+ sidebar_label: News
56
+ ```
57
+
44
58
  ## Contributing
45
59
 
46
60
  Interested in contributing to Jekyll Admin? We’d love your help. Jekyll 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://jekyll.github.io/jekyll-admin/development/) for more information.
47
61
 
62
+ ## Contributors
63
+
64
+ ### Code Contributors
65
+
66
+ This project exists thanks to all the people who contribute. [[Contribute](.github/CONTRIBUTING.md)].
67
+ <a href="https://github.com/jekyll/jekyll-admin/graphs/contributors"><img src="https://opencollective.com/jekyll-admin/contributors.svg?width=890&button=false" /></a>
68
+
69
+ ### Financial Contributors
70
+
71
+ Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/jekyll-admin/contribute)]
72
+
73
+ #### Individuals
74
+
75
+ <a href="https://opencollective.com/jekyll-admin"><img src="https://opencollective.com/jekyll-admin/individuals.svg?width=890"></a>
76
+
77
+ #### Organizations
78
+
79
+ Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/jekyll-admin/contribute)]
80
+
81
+ <a href="https://opencollective.com/jekyll-admin/organization/0/website"><img src="https://opencollective.com/jekyll-admin/organization/0/avatar.svg"></a>
82
+ <a href="https://opencollective.com/jekyll-admin/organization/1/website"><img src="https://opencollective.com/jekyll-admin/organization/1/avatar.svg"></a>
83
+ <a href="https://opencollective.com/jekyll-admin/organization/2/website"><img src="https://opencollective.com/jekyll-admin/organization/2/avatar.svg"></a>
84
+ <a href="https://opencollective.com/jekyll-admin/organization/3/website"><img src="https://opencollective.com/jekyll-admin/organization/3/avatar.svg"></a>
85
+ <a href="https://opencollective.com/jekyll-admin/organization/4/website"><img src="https://opencollective.com/jekyll-admin/organization/4/avatar.svg"></a>
86
+ <a href="https://opencollective.com/jekyll-admin/organization/5/website"><img src="https://opencollective.com/jekyll-admin/organization/5/avatar.svg"></a>
87
+ <a href="https://opencollective.com/jekyll-admin/organization/6/website"><img src="https://opencollective.com/jekyll-admin/organization/6/avatar.svg"></a>
88
+ <a href="https://opencollective.com/jekyll-admin/organization/7/website"><img src="https://opencollective.com/jekyll-admin/organization/7/avatar.svg"></a>
89
+ <a href="https://opencollective.com/jekyll-admin/organization/8/website"><img src="https://opencollective.com/jekyll-admin/organization/8/avatar.svg"></a>
90
+ <a href="https://opencollective.com/jekyll-admin/organization/9/website"><img src="https://opencollective.com/jekyll-admin/organization/9/avatar.svg"></a>
91
+
48
92
  ## License
49
93
 
50
94
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Default Sinatra to "production" mode (surpress errors) unless
2
4
  # otherwise specified by the `RACK_ENV` environmental variable.
3
5
  # Must be done prior to requiring Sinatra, or we'll get a LoadError
@@ -17,16 +19,15 @@ require "sinatra/reloader"
17
19
  require "sinatra/namespace"
18
20
 
19
21
  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"
22
+ autoload :APIable, "jekyll-admin/apiable"
23
+ autoload :DataFile, "jekyll-admin/data_file"
24
+ autoload :Directory, "jekyll-admin/directory"
25
+ autoload :FileHelper, "jekyll-admin/file_helper"
26
+ autoload :PathHelper, "jekyll-admin/path_helper"
27
+ autoload :Server, "jekyll-admin/server"
28
+ autoload :StaticServer, "jekyll-admin/static_server"
29
+ autoload :URLable, "jekyll-admin/urlable"
30
+ autoload :Version, "jekyll-admin/version"
30
31
 
31
32
  def self.site
32
33
  @site ||= begin
@@ -37,10 +38,10 @@ module JekyllAdmin
37
38
  end
38
39
  end
39
40
 
40
- # Monkey Patches
41
- require_relative "./jekyll/commands/serve"
42
-
43
41
  [Jekyll::Page, Jekyll::Document, Jekyll::StaticFile, Jekyll::Collection].each do |klass|
44
42
  klass.include JekyllAdmin::APIable
45
43
  klass.include JekyllAdmin::URLable
46
44
  end
45
+
46
+ # Monkey Patches
47
+ require_relative "jekyll/commands/serve"
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JekyllAdmin
2
4
  # Abstract module to be included in Convertible and Document to provide
3
5
  # additional, API-specific functionality without duplicating logic
4
6
  module APIable
5
7
  CONTENT_FIELDS = %w(next previous content excerpt).freeze
8
+ API_SCAFFOLD = %w(name path relative_path).map { |i| [i, nil] }.to_h.freeze
6
9
 
7
10
  # Returns a hash suitable for use as an API response.
8
11
  #
@@ -18,15 +21,13 @@ module JekyllAdmin
18
21
  # include_content - if true, includes the content in the respond, false by default
19
22
  # to support mapping on indexes where we only want metadata
20
23
  #
21
- #
22
24
  # Returns a hash (which can then be to_json'd)
23
25
  def to_api(include_content: false)
24
- output = hash_for_api
25
- output = output.merge(url_fields)
26
+ output = API_SCAFFOLD.merge hash_for_api
26
27
 
27
28
  # Include content, if requested, otherwise remove it
28
29
  if include_content
29
- output = output.merge(content_fields)
30
+ output.merge!(content_fields)
30
31
  else
31
32
  CONTENT_FIELDS.each { |field| output.delete(field) }
32
33
  end
@@ -35,24 +36,73 @@ module JekyllAdmin
35
36
  # Since it's an API, use `content` in both for consistency
36
37
  output.delete("output")
37
38
 
39
+ output["name"] = basename if is_a?(Jekyll::Document)
40
+ output["from_theme"] = from_theme_gem? if is_a?(Jekyll::StaticFile)
41
+ output["relative_path"] = relative_path_for_api
42
+
38
43
  # By default, calling to_liquid on a collection will return a docs
39
44
  # array with each rendered document, which we don't want.
40
45
  if is_a?(Jekyll::Collection)
41
46
  output.delete("docs")
47
+ output["name"] = label
48
+ output["path"] = relative_directory
42
49
  output["entries_url"] = entries_url
43
50
  end
44
51
 
45
- if is_a?(Jekyll::Document)
46
- output["relative_path"] = relative_path.sub("_drafts/", "") if draft?
47
- output["name"] = basename
52
+ output.merge!(url_fields)
53
+ output
54
+ end
55
+
56
+ private
57
+
58
+ # Relative path of files and directories with their *special directories*
59
+ # and any leading slashes stripped away.
60
+ #
61
+ # Examples:
62
+ # `_drafts/foo/draft-post.md` => `foo/draft-post.md`
63
+ # `_posts/foo/2019-10-18-hello.md` => `foo/2019-10-18-hello.md`
64
+ # `/assets/img/logo.png` => `assets/img/logo.png`
65
+ def relative_path_for_api
66
+ return unless respond_to?(:relative_path) && relative_path
67
+
68
+ @relative_path_for_api ||= begin
69
+ rel_path = relative_path.dup
70
+ strip_leading_slash!(rel_path)
71
+ strip_leading_special_directory!(rel_path)
72
+ strip_leading_slash!(rel_path)
73
+
74
+ rel_path
48
75
  end
76
+ end
49
77
 
50
- output["from_theme"] = from_theme_gem? if is_a?(Jekyll::StaticFile)
78
+ # Prefer substituting substrings instead of using a regex in order to avoid multiple
79
+ # regex allocations. String literals are frozen and reused.
51
80
 
52
- output
81
+ def strip_leading_slash!(path)
82
+ return unless path.start_with?("/")
83
+
84
+ path.sub!("/", "")
53
85
  end
54
86
 
55
- private
87
+ def strip_leading_special_directory!(path)
88
+ return unless special_directory && path.start_with?(special_directory)
89
+
90
+ path.sub!(special_directory, "")
91
+ end
92
+
93
+ def special_directory
94
+ return @special_directory if defined?(@special_directory)
95
+
96
+ @special_directory = begin
97
+ if is_a?(Jekyll::Document) && draft?
98
+ "_drafts"
99
+ elsif is_a?(Jekyll::Document)
100
+ collection.relative_directory
101
+ elsif is_a?(Jekyll::Collection)
102
+ relative_directory
103
+ end
104
+ end
105
+ end
56
106
 
57
107
  # Pages don't have a hash method, but Documents do
58
108
  def file_path
@@ -84,6 +134,16 @@ module JekyllAdmin
84
134
  Jekyll::Utils.merged_file_read_opts(site, {})
85
135
  end
86
136
 
137
+ def front_matter_defaults
138
+ return unless file_exists?
139
+
140
+ @front_matter_defaults ||= begin
141
+ return {} unless respond_to?(:relative_path) && respond_to?(:type)
142
+
143
+ site.frontmatter_defaults.all(relative_path, type)
144
+ end
145
+ end
146
+
87
147
  def front_matter
88
148
  return unless file_exists?
89
149
 
@@ -137,6 +197,7 @@ module JekyllAdmin
137
197
  else
138
198
  output["raw_content"] = raw_content
139
199
  output["front_matter"] = front_matter
200
+ output["front_matter_defaults"] = front_matter_defaults
140
201
  end
141
202
 
142
203
  # Include next and previous documents non-recursively
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JekyllAdmin
2
4
  class DataFile
3
- METHODS_FOR_LIQUID = %w(path relative_path slug ext title).freeze
5
+ METHODS_FOR_LIQUID = %w(name path relative_path slug ext title).freeze
4
6
  EXTENSIONS = %w(yaml yml json csv).freeze
5
7
 
6
8
  include APIable
@@ -8,16 +10,26 @@ module JekyllAdmin
8
10
  include PathHelper
9
11
  extend PathHelper
10
12
 
11
- attr_reader :id
13
+ attr_reader :id, :ext
12
14
 
13
15
  # Initialize a new DataFile object
14
16
  #
15
17
  # id - the file ID as passed from the API. This may or may not have an extension
16
18
  def initialize(id)
17
- @id = File.extname(id).empty? ? "#{id}.yml" : id
19
+ extname = File.extname(id)
20
+ if extname.empty?
21
+ @id = "#{id}.yml"
22
+ @ext = ".yml"
23
+ else
24
+ @id = id
25
+ @ext = extname
26
+ end
18
27
  end
19
28
  alias_method :relative_path, :id
20
29
 
30
+ # Returns the file's extension with preceeding `.`
31
+ alias_method :extension, :ext
32
+
21
33
  def exists?
22
34
  @exists ||= File.exist?(absolute_path)
23
35
  end
@@ -32,16 +44,6 @@ module JekyllAdmin
32
44
  @content ||= data_reader.read_data_file(absolute_path)
33
45
  end
34
46
 
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
47
  # Returns the file's sanitized slug (as used in `site.data[slug]`)
46
48
  def slug
47
49
  @slug ||= data_reader.sanitize_filename(basename)
@@ -54,7 +56,7 @@ module JekyllAdmin
54
56
 
55
57
  # Returns path relative to site source
56
58
  def path
57
- ensure_leading_slash(File.join(DataFile.data_dir, relative_path))
59
+ @path ||= File.join(DataFile.data_dir, relative_path)
58
60
  end
59
61
 
60
62
  def absolute_path
@@ -92,9 +94,10 @@ module JekyllAdmin
92
94
  end
93
95
 
94
96
  def basename_with_extension
95
- [basename, extension].join
97
+ "#{basename}#{extension}"
96
98
  end
97
- alias_method :filename, :basename_with_extension
99
+ alias_method :name, :basename_with_extension
100
+ public :name
98
101
 
99
102
  def namespace
100
103
  "data"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JekyllAdmin
2
4
  class Directory
3
5
  extend Forwardable
@@ -9,45 +11,41 @@ module JekyllAdmin
9
11
  include JekyllAdmin::URLable
10
12
  include JekyllAdmin::APIable
11
13
 
12
- TYPE = :directory
14
+ RESOURCE_TYPES = %w(pages data drafts static_files).freeze
15
+ DOT_DIRECTORIES = [".", ".."].freeze
13
16
 
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`
17
+ private_constant :RESOURCE_TYPES, :DOT_DIRECTORIES
18
+
19
+ # Parameters:
20
+ # path - The full path of the directory at which its entries will be listed.
22
21
  #
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
22
+ # Named parameters:
23
+ # base: - The full path to the directory from source.
24
+ # splat: - The requested directory path relative to content namespace.
25
+ # content_type: - The type of the requested directory entries. Corresponds to
26
+ # the resources' API namespace.
27
+ def initialize(path, base:, splat:, content_type:)
28
+ @path = Pathname.new path
29
+ @base = Pathname.new base
27
30
  @splat = Pathname.new splat
28
- @path = Pathname.new path
31
+ @content_type = content_type
29
32
  end
30
33
 
31
34
  def to_liquid
32
35
  {
33
- :name => name,
34
- :modified_time => modified_time,
35
- :path => relative_path,
36
- :type => TYPE,
36
+ "name" => name,
37
+ "modified_time" => modified_time,
38
+ "path" => relative_path,
39
+ "type" => "directory",
37
40
  }
38
41
  end
39
42
 
40
43
  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
44
+ @relative_path ||= path.relative_path_from(base).to_s
46
45
  end
47
46
 
48
47
  def resource_path
49
- types = %w(pages data drafts)
50
- if types.include?(content_type)
48
+ if RESOURCE_TYPES.include?(content_type)
51
49
  "/#{content_type}/#{splat}/#{name}"
52
50
  else
53
51
  "/collections/#{content_type}/entries/#{splat}/#{name}"
@@ -61,7 +59,7 @@ module JekyllAdmin
61
59
 
62
60
  def directories
63
61
  path.entries.map do |entry|
64
- next if [".", ".."].include? entry.to_s
62
+ next if DOT_DIRECTORIES.include? entry.to_s
65
63
  next unless path.join(entry).directory?
66
64
 
67
65
  self.class.new(
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JekyllAdmin
2
4
  module FileHelper
3
5
  # The file the user requested in the URL
@@ -50,6 +52,10 @@ module JekyllAdmin
50
52
  ensure_file(written_file)
51
53
  end
52
54
 
55
+ def ensure_not_overwriting_existing_file
56
+ ensure_not_file(written_file)
57
+ end
58
+
53
59
  def find_by_path(path)
54
60
  files = case namespace
55
61
  when "collections"
@@ -70,6 +76,14 @@ module JekyllAdmin
70
76
  render_404 if file.nil?
71
77
  end
72
78
 
79
+ def ensure_not_file(file)
80
+ return if file.nil?
81
+
82
+ Jekyll.logger.warn "Jekyll Admin:", "Could not create file."
83
+ Jekyll.logger.warn "", "Path #{file.relative_path.inspect} already exists!"
84
+ render_404
85
+ end
86
+
73
87
  def ensure_directory
74
88
  render_404 unless Dir.exist?(directory_path)
75
89
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JekyllAdmin
2
4
  module PathHelper
3
5
  def sanitized_path(path)
@@ -49,6 +51,11 @@ module JekyllAdmin
49
51
  ensure_leading_slash(request_payload["path"]) != relative_path
50
52
  end
51
53
 
54
+ # Is this request creating a new file?
55
+ def new?
56
+ !request_payload["path"]
57
+ end
58
+
52
59
  private
53
60
 
54
61
  # Returns the path to the requested file's containing directory
@@ -56,7 +63,7 @@ module JekyllAdmin
56
63
  sanitized_path(
57
64
  case namespace
58
65
  when "collections"
59
- File.join(collection.relative_directory, params["splat"].first)
66
+ File.join(collection.directory, params["splat"].first)
60
67
  when "data"
61
68
  File.join(DataFile.data_dir, params["splat"].first)
62
69
  when "drafts"