robin_cms 0.1.2 → 0.1.3
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 +4 -4
- data/README.md +4 -0
- data/lib/robin_cms/cms.rb +32 -30
- data/lib/robin_cms/collection_library.rb +70 -0
- data/lib/robin_cms/configuration-schema.json +2 -0
- data/lib/robin_cms/configuration.rb +2 -16
- data/lib/robin_cms/data_library.rb +58 -0
- data/lib/robin_cms/editable.rb +34 -0
- data/lib/robin_cms/item.rb +89 -19
- data/lib/robin_cms/library.rb +83 -0
- data/lib/robin_cms/queryable.rb +9 -7
- data/lib/robin_cms/version.rb +1 -1
- data/lib/robin_cms/views/filter_form.erb +1 -1
- data/lib/robin_cms/views/hidden_field.erb +1 -1
- data/lib/robin_cms/views/input_field.erb +1 -1
- data/lib/robin_cms/views/layout.erb +6 -3
- data/lib/robin_cms/views/library.erb +2 -2
- data/lib/robin_cms/views/library_actions.erb +3 -0
- data/lib/robin_cms/views/library_item.erb +9 -14
- data/lib/robin_cms/views/richtext_field.erb +1 -1
- data/lib/robin_cms/views/select_field.erb +2 -2
- data/lib/robin_cms/views/stylesheet.css.erb +443 -0
- data/lib/robin_cms.rb +6 -5
- metadata +9 -9
- data/lib/robin_cms/collection_item.rb +0 -82
- data/lib/robin_cms/data_item.rb +0 -87
- data/lib/robin_cms/itemable.rb +0 -124
- data/lib/robin_cms/static_item.rb +0 -92
- data/lib/robin_cms/views/style.erb +0 -441
- /data/lib/robin_cms/views/{logo.erb → logo.svg.erb} +0 -0
- /data/lib/robin_cms/views/{new_tab.erb → new_tab.svg.erb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7496d517d8e733bc0e0876e806347a0c3670efc489d974b16718e3b015d0a8e1
|
4
|
+
data.tar.gz: cc6df56fde6950e75fddf3fef947e5363cdc67f506bd07c16f5bbe0f1940d629
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f4c01cfe9c26b0c7e864af98d405dfe55fbe986f78a3ee9295cb20e9c6bd808bbd9473685ddd17c158574540a63535c16a783a650a937f3e6419f215a81c51f
|
7
|
+
data.tar.gz: 36e240374f3bcf0c564d15347b4847a6b781d7b6192b3b3214fbf35f297748415d1729773039dca5f61b00ce088d681d7b3b02ac340fc3aa1f981c2fa4dd63dc
|
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Robin CMS
|
2
2
|
|
3
|
+
> [!IMPORTANT]
|
4
|
+
> This software is currently in beta. There may be bugs and breaking changes.
|
5
|
+
> If you find a bug, I'd love to hear about it.
|
6
|
+
|
3
7
|

|
4
8
|
|
5
9
|
Robin CMS is a minimalist flat-file CMS built with Ruby and Sinatra. It is
|
data/lib/robin_cms/cms.rb
CHANGED
@@ -2,9 +2,8 @@
|
|
2
2
|
|
3
3
|
module RobinCMS
|
4
4
|
class CMS < Sinatra::Base
|
5
|
-
|
6
|
-
|
7
|
-
set :session_secret, ENV.fetch("SESSION_SECRET", SecureRandom.hex(64))
|
5
|
+
include Helpers
|
6
|
+
include Auth
|
8
7
|
|
9
8
|
attr_accessor :config
|
10
9
|
|
@@ -14,10 +13,9 @@ module RobinCMS
|
|
14
13
|
@config = Configuration.parse(**opts)
|
15
14
|
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
16
|
+
set :logging, true
|
17
|
+
set :sessions, true
|
18
|
+
set :session_secret, ENV.fetch("SESSION_SECRET", SecureRandom.hex(64))
|
21
19
|
|
22
20
|
before do
|
23
21
|
if %w[/login /logout].include?(request.path_info)
|
@@ -27,13 +25,18 @@ module RobinCMS
|
|
27
25
|
redirect to("/login") unless session[:auth_user]
|
28
26
|
end
|
29
27
|
|
30
|
-
before
|
28
|
+
before /\/libraries\/(.*).*/ do
|
31
29
|
kind = params[:captures].first.split("/").first
|
30
|
+
library_schema = @config.find_library(kind)
|
32
31
|
|
33
|
-
|
34
|
-
@sorted_fields = @config.sorted_fields(kind)
|
32
|
+
halt 404 unless library_schema
|
35
33
|
|
36
|
-
|
34
|
+
@library = case library_schema[:type]
|
35
|
+
when "collection"
|
36
|
+
CollectionLibrary.new(library_schema)
|
37
|
+
when "data"
|
38
|
+
DataLibrary.new(library_schema)
|
39
|
+
end
|
37
40
|
end
|
38
41
|
|
39
42
|
after do
|
@@ -92,20 +95,21 @@ module RobinCMS
|
|
92
95
|
end
|
93
96
|
|
94
97
|
get "/libraries/:kind" do
|
95
|
-
@items =
|
98
|
+
@items = @library.query(
|
96
99
|
sort: params[:sort],
|
97
100
|
published: params[:published],
|
98
|
-
q: params[:q]
|
101
|
+
q: params[:q]
|
102
|
+
)
|
99
103
|
|
100
104
|
erb :library
|
101
105
|
end
|
102
106
|
|
103
107
|
get "/libraries/:kind/item" do
|
104
108
|
if params[:id]
|
105
|
-
@item =
|
109
|
+
@item = @library.find_one(params[:id])
|
106
110
|
halt 404 unless @item
|
107
111
|
else
|
108
|
-
@item =
|
112
|
+
@item = @library.blank
|
109
113
|
end
|
110
114
|
|
111
115
|
erb :library_item
|
@@ -113,32 +117,30 @@ module RobinCMS
|
|
113
117
|
|
114
118
|
post "/libraries/:kind/item" do
|
115
119
|
if params[:id]
|
116
|
-
@item =
|
120
|
+
@item = @library.find_one(params[:id])
|
117
121
|
halt 404 unless @item
|
118
122
|
|
119
123
|
if params[:image]
|
120
124
|
filename = params[:image][:filename]
|
121
|
-
tempfile = params[:image][:tempfile]
|
125
|
+
tempfile = params[:image][:tempfile].to_path
|
122
126
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
127
|
+
@library.delete_static(@item.attributes[:image_src])
|
128
|
+
static_path = @library.create_static(filename, tempfile)
|
129
|
+
params[:image_src] = "/" + static_path
|
127
130
|
end
|
128
131
|
|
129
132
|
@item.attributes = params
|
130
|
-
@item
|
133
|
+
@library.write(@item)
|
131
134
|
else
|
132
135
|
if params[:image]
|
133
136
|
filename = params[:image][:filename]
|
134
|
-
tempfile = params[:image][:tempfile]
|
137
|
+
tempfile = params[:image][:tempfile].to_path
|
135
138
|
|
136
|
-
|
137
|
-
|
138
|
-
end
|
139
|
+
static_path = @library.create_static(filename, tempfile)
|
140
|
+
params[:image_src] = "/" + static_path
|
139
141
|
end
|
140
142
|
|
141
|
-
|
143
|
+
@library.create(params)
|
142
144
|
end
|
143
145
|
|
144
146
|
redirect to("/libraries/#{params[:kind]}")
|
@@ -149,14 +151,14 @@ module RobinCMS
|
|
149
151
|
|
150
152
|
post "/libraries/:kind/item/delete" do
|
151
153
|
if params[:id]
|
152
|
-
@item =
|
154
|
+
@item = @library.find_one(params[:id])
|
153
155
|
halt 404 unless @item
|
154
156
|
|
155
157
|
if @item.attributes[:image_src]
|
156
|
-
|
158
|
+
@library.delete_static(@item.attributes[:image_src])
|
157
159
|
end
|
158
160
|
|
159
|
-
@item.
|
161
|
+
@library.delete(@item.id)
|
160
162
|
else
|
161
163
|
halt 404
|
162
164
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RobinCMS
|
4
|
+
class CollectionLibrary < Library
|
5
|
+
def create(attributes)
|
6
|
+
id = make_slug(attributes[:title], @schema[:pattern])
|
7
|
+
item = Item.new(id, attributes, **@schema)
|
8
|
+
|
9
|
+
if File.exist?(filepath(id))
|
10
|
+
raise ItemExistsError, "An item with the same name already exists"
|
11
|
+
end
|
12
|
+
|
13
|
+
write(item)
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_one(id)
|
17
|
+
path = filepath(id)
|
18
|
+
|
19
|
+
return unless File.exist?(path)
|
20
|
+
|
21
|
+
deserialize(path)
|
22
|
+
end
|
23
|
+
|
24
|
+
def all
|
25
|
+
Dir.glob(filepath("*"))
|
26
|
+
.filter { |f| File.file?(f) }
|
27
|
+
.map { |path| deserialize(path) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def write(item)
|
31
|
+
path = filepath(item.id)
|
32
|
+
|
33
|
+
FileUtils.mkdir_p(File.dirname(path))
|
34
|
+
FileUtils.touch(path)
|
35
|
+
|
36
|
+
item.update_timestamps!
|
37
|
+
|
38
|
+
serialized = YAML.dump(item.frontmatter, stringify_names: true)
|
39
|
+
if item.attributes[:content]
|
40
|
+
serialized << "---\n"
|
41
|
+
serialized << item.attributes[:content]
|
42
|
+
end
|
43
|
+
File.write(path, serialized)
|
44
|
+
|
45
|
+
path
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete(id)
|
49
|
+
File.delete(filepath(id))
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def filepath(id)
|
55
|
+
File.join(@schema[:location], "#{id}.#{@schema[:filetype]}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def deserialize(path)
|
59
|
+
raw = File.read(path)
|
60
|
+
id = File.basename(path, File.extname(path))
|
61
|
+
|
62
|
+
_, frontmatter, content = raw.split("---")
|
63
|
+
|
64
|
+
attributes = YAML.load(frontmatter, symbolize_names: true)
|
65
|
+
attributes[:content] = content.strip
|
66
|
+
|
67
|
+
Item.new(id, attributes, **@schema)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -50,6 +50,7 @@
|
|
50
50
|
"can_create": { "type": "boolean" },
|
51
51
|
"can_delete": { "type": "boolean" },
|
52
52
|
"pattern": { "type": "string" },
|
53
|
+
"display_name_pattern": { "type": "string" },
|
53
54
|
"fields": {
|
54
55
|
"type": "array",
|
55
56
|
"items": {
|
@@ -89,6 +90,7 @@
|
|
89
90
|
"can_create": { "type": "boolean" },
|
90
91
|
"can_delete": { "type": "boolean" },
|
91
92
|
"pattern": { "type": "string" },
|
93
|
+
"display_name_pattern": { "type": "string" },
|
92
94
|
"fields": {
|
93
95
|
"type": "array",
|
94
96
|
"items": {
|
@@ -102,27 +102,13 @@ module RobinCMS
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def find_library(kind)
|
105
|
-
self[:libraries].find { |
|
106
|
-
end
|
107
|
-
|
108
|
-
def sorted_fields(kind)
|
109
|
-
find_library(kind)
|
110
|
-
&.dig(:fields)
|
111
|
-
&.sort_by { |field| field[:order] || Float::INFINITY }
|
112
|
-
end
|
113
|
-
|
114
|
-
def drafts_enabled?(library)
|
115
|
-
collection?(library)
|
105
|
+
self[:libraries].find { |library| library[:id] == kind }
|
116
106
|
end
|
117
107
|
|
118
108
|
def collection?(library)
|
119
109
|
library[:type].nil? || library[:type] == "collection"
|
120
110
|
end
|
121
111
|
|
122
|
-
def data?(library)
|
123
|
-
!collection?(library)
|
124
|
-
end
|
125
|
-
|
126
112
|
def has_image?(library)
|
127
113
|
library[:fields].find { |f| f[:type] == "image" }
|
128
114
|
end
|
@@ -148,7 +134,7 @@ module RobinCMS
|
|
148
134
|
config[:libraries].each do |library|
|
149
135
|
library[:fields].append(*AUTOMATIC_FIELDS)
|
150
136
|
|
151
|
-
if
|
137
|
+
if collection?(library)
|
152
138
|
library[:fields].append(*AUTOMATIC_DRAFT_FIELDS)
|
153
139
|
end
|
154
140
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RobinCMS
|
4
|
+
class DataLibrary < Library
|
5
|
+
def create(attributes)
|
6
|
+
item = Item.new(nil, attributes, **@schema)
|
7
|
+
write(item)
|
8
|
+
end
|
9
|
+
|
10
|
+
def find_one(id)
|
11
|
+
all[id.to_i]
|
12
|
+
end
|
13
|
+
|
14
|
+
def all
|
15
|
+
return [] unless File.exist?(filepath)
|
16
|
+
|
17
|
+
items = YAML.load_file(filepath, symbolize_names: true)
|
18
|
+
|
19
|
+
return [] unless items
|
20
|
+
|
21
|
+
items.each_with_index.map do |attrs, i|
|
22
|
+
Item.new(i, attrs, **@schema)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def write(item)
|
27
|
+
FileUtils.mkdir_p(File.dirname(filepath))
|
28
|
+
FileUtils.touch(filepath)
|
29
|
+
|
30
|
+
item.update_timestamps!
|
31
|
+
|
32
|
+
all_items = YAML.load_file(filepath) || []
|
33
|
+
if item.id.nil?
|
34
|
+
all_items.append(item.frontmatter)
|
35
|
+
else
|
36
|
+
all_items[item.id.to_i] = item.frontmatter
|
37
|
+
end
|
38
|
+
|
39
|
+
serialized = YAML.dump(all_items, stringify_names: true)
|
40
|
+
File.write(filepath, serialized)
|
41
|
+
|
42
|
+
filepath
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete(id)
|
46
|
+
all_items = YAML.load_file(filepath)
|
47
|
+
all_items[id.to_i] = nil
|
48
|
+
serialized = YAML.dump(all_items.compact, stringify_names: true)
|
49
|
+
File.write(filepath, serialized)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def filepath
|
55
|
+
File.join(@schema[:location], "#{@schema[:id]}.#{@schema[:filetype]}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RobinCMS
|
4
|
+
# This module provides methods for editing image files.
|
5
|
+
# No methods are required in the base class in order to use this mixin.
|
6
|
+
module Editable
|
7
|
+
def resize_image(filepath, dimensions)
|
8
|
+
# The mogrify command edits images in place. For more info, see
|
9
|
+
# mogrify(1).
|
10
|
+
|
11
|
+
system("mogrify -resize #{dimensions} #{filepath}")
|
12
|
+
if $?.exitstatus != 0
|
13
|
+
raise ConversionError, "Could not resize image #{filepath}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def format_image(filepath, filetype)
|
18
|
+
system("mogrify -format #{filetype} #{filepath}")
|
19
|
+
if $?.exitstatus != 0
|
20
|
+
raise ConversionError, "Could not format image #{filepath}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# The name of the converted file will be the same as the original but
|
24
|
+
# with a new file extension.
|
25
|
+
converted = filepath.sub(/#{File.extname(filepath)}$/, ".#{filetype}")
|
26
|
+
|
27
|
+
# Mogrify's format command creates a new file with the new extension.
|
28
|
+
# Copy the contents of this file to the original, then delete the newly
|
29
|
+
# created file.
|
30
|
+
IO.copy_stream(converted, filepath)
|
31
|
+
File.delete(converted)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/robin_cms/item.rb
CHANGED
@@ -1,34 +1,104 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RobinCMS
|
4
|
-
# This is a wrapper class for providing a uniform interface to both the
|
5
|
-
# DataItem class and the CollectionItem class. It's sole job is to dispatch
|
6
|
-
# methods to the appropriate class based on the settings of the library.
|
7
4
|
class Item
|
8
|
-
|
5
|
+
attr_reader :id, :attributes
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
DATETIME_FORMAT = "%Y-%m-%d"
|
8
|
+
|
9
|
+
# The keys which we don't want to serialize.
|
10
|
+
FRONTMATTER_IGNORE_KEYS = [:id, :content, :image, :captures].freeze
|
11
|
+
|
12
|
+
def initialize(id, attrs = {}, **opts)
|
13
|
+
if !attrs.empty? && !attrs.has_key?(:title)
|
14
|
+
raise TypeError, "Missing required field `title'"
|
15
|
+
end
|
16
|
+
|
17
|
+
@id = id
|
18
|
+
@opts = opts
|
19
|
+
|
20
|
+
# Be sure to use the setter here so the keys get converted to symbols.
|
21
|
+
self.attributes = attrs
|
22
|
+
end
|
23
|
+
|
24
|
+
def attributes=(attributes)
|
25
|
+
@attributes = attributes.to_h.transform_keys(&:to_sym)
|
26
|
+
|
27
|
+
if attributes.has_key?(:published)
|
28
|
+
@attributes[:published] = attributes[:published].to_s == "true"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"<#{self.class} id=\"#{id}\">"
|
34
|
+
end
|
35
|
+
|
36
|
+
def published?
|
37
|
+
if @attributes.has_key?(:published)
|
38
|
+
@attributes[:published]
|
39
|
+
else
|
40
|
+
true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def published_label
|
45
|
+
if published?
|
46
|
+
"Published"
|
47
|
+
else
|
48
|
+
"Draft"
|
14
49
|
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def created_at
|
53
|
+
return unless @attributes[:created_at]
|
54
|
+
|
55
|
+
Time.parse(@attributes[:created_at])
|
56
|
+
end
|
15
57
|
|
16
|
-
|
17
|
-
|
18
|
-
library = args.first
|
58
|
+
def updated_at
|
59
|
+
return unless @attributes[:updated_at]
|
19
60
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
61
|
+
Time.parse(@attributes[:updated_at])
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_timestamps!
|
65
|
+
timestamp = Time.now.strftime(DATETIME_FORMAT)
|
66
|
+
|
67
|
+
if !@attributes.has_key?(:created_at) || @attributes[:created_at].empty?
|
68
|
+
@attributes[:created_at] = timestamp
|
69
|
+
end
|
70
|
+
@attributes[:updated_at] = timestamp
|
71
|
+
end
|
72
|
+
|
73
|
+
def display_name
|
74
|
+
return @attributes[:title] unless @opts[:display_name_pattern]
|
75
|
+
|
76
|
+
@opts[:display_name_pattern].clone.tap do |name|
|
77
|
+
@attributes.each { |key, value| name.gsub!(":#{key}", value) }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Get the frontmatter as a hash with stringified keys.
|
82
|
+
def frontmatter
|
83
|
+
frontmatter = @attributes.clone
|
84
|
+
FRONTMATTER_IGNORE_KEYS.each { |key| frontmatter.delete(key) }
|
85
|
+
|
86
|
+
if published?
|
87
|
+
frontmatter.delete(:published)
|
25
88
|
else
|
26
|
-
|
89
|
+
frontmatter[:published] = false
|
27
90
|
end
|
91
|
+
|
92
|
+
# The Psych module (for which YAML is an alias) has a
|
93
|
+
# stringify_names option which does exactly this. However it was
|
94
|
+
# only introduced in Ruby 3.4. Using transform_keys is a workaround
|
95
|
+
# to support earlier versions of Ruby.
|
96
|
+
frontmatter.to_h.transform_keys(&:to_s)
|
28
97
|
end
|
29
98
|
|
30
|
-
def
|
31
|
-
|
99
|
+
def field_value_or_default(field)
|
100
|
+
value = @attributes[field[:id].to_sym] || field[:default]
|
101
|
+
value.to_s
|
32
102
|
end
|
33
103
|
end
|
34
104
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RobinCMS
|
4
|
+
class Library
|
5
|
+
include Editable
|
6
|
+
include Sluggable
|
7
|
+
include Queryable
|
8
|
+
|
9
|
+
attr_reader :schema
|
10
|
+
|
11
|
+
def initialize(schema)
|
12
|
+
@schema = schema.freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
def blank
|
16
|
+
Item.new(nil, **@schema)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(attributes)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_one(id)
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def all
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
# Writes the specified item to disk.
|
32
|
+
#
|
33
|
+
# Returns the path where the item was written.
|
34
|
+
def write(item)
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
# Delete the item specified by +id+.
|
39
|
+
def delete(id)
|
40
|
+
raise NotImplementedError
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_static(filename, tempfile)
|
44
|
+
path = File.join(@schema[:static_location], File.basename(make_slug(filename)))
|
45
|
+
|
46
|
+
if File.exist?(path)
|
47
|
+
raise ItemExistsError, "An item with the same name already exists"
|
48
|
+
end
|
49
|
+
|
50
|
+
image_field = @schema[:fields].find { |f| f[:type] == "image" }
|
51
|
+
dimensions = image_field&.dig(:dimensions)
|
52
|
+
filetype = image_field&.dig(:filetype)
|
53
|
+
|
54
|
+
resize_image(tempfile, dimensions) if dimensions
|
55
|
+
format_image(tempfile, filetype) if filetype
|
56
|
+
|
57
|
+
FileUtils.mkdir_p(File.dirname(path))
|
58
|
+
FileUtils.cp(tempfile, path)
|
59
|
+
|
60
|
+
path
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete_static(filename)
|
64
|
+
path = File.join(@schema[:static_location], File.basename(filename))
|
65
|
+
|
66
|
+
return unless File.exist?(path)
|
67
|
+
|
68
|
+
File.delete(path)
|
69
|
+
end
|
70
|
+
|
71
|
+
def drafts_enabled?
|
72
|
+
@schema[:type].nil? || @schema[:type] == "collection"
|
73
|
+
end
|
74
|
+
|
75
|
+
def sorted_fields
|
76
|
+
@schema[:fields].sort_by { |field| field[:order] || Float::INFINITY }
|
77
|
+
end
|
78
|
+
|
79
|
+
def [](key)
|
80
|
+
@schema[key]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/robin_cms/queryable.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RobinCMS
|
4
|
+
# This module provides methods for querying items.
|
5
|
+
#
|
6
|
+
# In order to use this as a mixin, you only need to implement an +all+
|
7
|
+
# method in the base class. This method should take no parameters, and
|
8
|
+
# return all items.
|
4
9
|
module Queryable
|
5
|
-
def
|
10
|
+
def query(**kwargs)
|
6
11
|
by_published = lambda do |i|
|
7
12
|
case kwargs[:published]
|
8
13
|
when nil, ""
|
@@ -34,14 +39,11 @@ module RobinCMS
|
|
34
39
|
end * sort_direction
|
35
40
|
end
|
36
41
|
|
37
|
-
all(
|
38
|
-
.filter(&by_search)
|
39
|
-
.filter(&by_published)
|
40
|
-
.sort(&by_field)
|
42
|
+
all.filter(&by_search).filter(&by_published).sort(&by_field)
|
41
43
|
end
|
42
44
|
|
43
|
-
def count
|
44
|
-
all
|
45
|
+
def count
|
46
|
+
all.size
|
45
47
|
end
|
46
48
|
end
|
47
49
|
end
|
data/lib/robin_cms/version.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
id="<%= safe_id(field[:id], 'field') %>"
|
4
4
|
type="<%= field[:type] %>"
|
5
5
|
name="<%= field[:id] %>"
|
6
|
-
value="<%= @item.
|
6
|
+
value="<%= @item.field_value_or_default(field) %>"
|
7
7
|
<% if field[:required] %>required<% end %>
|
8
8
|
<% if field[:readonly] %>readonly<% end %>
|
9
9
|
/>
|
@@ -3,8 +3,11 @@
|
|
3
3
|
<head>
|
4
4
|
<meta charset="utf-8" />
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
6
|
+
<meta name="robots" content="noindex,nofollow" />
|
6
7
|
<title><%= @config[:title] %> | admin</title>
|
7
|
-
|
8
|
+
<style>
|
9
|
+
<%= erb :"stylesheet.css" %>
|
10
|
+
</style>
|
8
11
|
</head>
|
9
12
|
<body <% if flash[:success_dialog] %>onload="successdialog.showModal()"<% end %>>
|
10
13
|
<% if session[:auth_user] %>
|
@@ -14,7 +17,7 @@
|
|
14
17
|
</span>
|
15
18
|
<span class="controls --gap-md">
|
16
19
|
<a href="<%= @config[:url] %>" target="_blank">
|
17
|
-
<%= @config[:url] %><%= erb :new_tab %>
|
20
|
+
<%= @config[:url] %><%= erb :"new_tab.svg" %>
|
18
21
|
</a>
|
19
22
|
<% if @config[:build_command] %>
|
20
23
|
<form id="publish-form" action="/admin/publish" method="post">
|
@@ -29,7 +32,7 @@
|
|
29
32
|
<main id="site-content">
|
30
33
|
<%= yield %>
|
31
34
|
<footer id="site-footer" class="card --clear">
|
32
|
-
<%= erb :logo %>
|
35
|
+
<%= erb :"logo.svg" %>
|
33
36
|
<p>
|
34
37
|
Made with 🧡
|
35
38
|
<br />
|