-
Notifications
You must be signed in to change notification settings - Fork 14
/
docpad.coffee
313 lines (289 loc) · 16.8 KB
/
docpad.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
module.exports =
# The data generated by minicms is just like any other content
# So we can create custom collections or simply add template helpers
templateData:
getIndex: ->
@getCollection('html').findOne(url: '/')?.toJSON()
getArticles: ->
@getCollection('html').findAllLive(type: 'article').sortArray(date:-1)
getLinks: ->
@getCollection('html').findAllLive(type: 'link').sortArray(name:1)
plugins:
# This contains all the configuration of minicms, used for the admin panel of our blog
# It allows you to define lists, forms and how to generate the final content
minicms:
#prefix:
# url: 'cms' # Access the admin panel through '/cms' by default
# meta: 'cms' # Store form data of each content into a 'cms' field by default (inside metadata)
# Secret, required by signed cookie session
secret: 'keyboard cat blog'
# Implement the logic you want for authentication
auth: (login, password, callback) ->
if login is 'admin' and password is 'password'
callback null, true
else
callback "Invalid login or password.", false
# List all the content that is managed by the plugin
models: [
# Example of a content that is unique and only has 1 entry
name: ['Configuration', 'Configuration'] # First is singular name, second is plural. Here we use the same because we never have multiple "Configurations"
unique: true # Make this model "unique", that means there can only be 1 entry, not more, not less
form:
url: "/index" # Save configuration meta in index file
ext: 'html.md' # With a ".html.md" extension
# Set the metadata template of the file
# Each function that we put in the metadata will be called and the result will be used
# The context (this, @) contains the @docpad instance, a @slugify function and all the properties of the current model
meta:
title: -> @title
layout: 'home'
about: -> @about
# The content follows the metadata. For this file, it is in markdown (because we are making a html.md file)
content: -> @title
# For each model, we can create a form with several components that will allow the user to input data
components: [
# A 'title' field with a text input component (notice that we use this title field in the metadata template, above)
field: 'title'
label: 'Website Title' # The label is what will appear as field name in the form for the admin panel
type: 'text'
,
# Another text input component to type the Author of the website (this one is still stored in the cms: key of the meta, but we don't really use it for now. Maybe later?)
field: 'author'
label: 'Website Author'
type: 'text'
,
# An 'about' field using a plain textarea component
field: 'about'
label: 'About me'
type: 'textarea'
height: 100
,
# A color picker example
field: 'myColor'
label: 'My Favourite Color'
type: 'color'
,
# We can choose a 'category' with a choice component.
field: 'category'
label: 'Category'
type: 'choice'
expanded: true # The component will be "expanded" using html radio inputs
# The data: option returns an array of available categories.
# For this example, it is hardcoded but you can make your own logic using @docpad collections
data: -> ['Sport', 'News']
,
# We add a 'sub-category' field that depends on the 'category' field
field: 'subCategory'
deps: ['category'] # This field will update when the category field changes value
label: 'Sub-category'
type: 'choice'
# Here, we use the @category value to decide which sub-categories are available
# It will be called each time the field reloads
data: ->
if @category is 'Sport'
return ['Footbal', 'Tennis', 'Handball', 'Swimming']
else if @category is 'News'
return ['Technology', 'Finance', 'Gossip']
else # No category selected, then no sub-category available
return []
,
# Allow the user to change the blog logo by uploading an image
field: 'logo'
label: 'Website Logo'
type: 'file'
use: 'standard' # There can be multiple image profiles, here we define the one that will be displayed inside the form
optional: true # We can make a field optional
images: # Image profiles
# For this field, we only make 1 profile called 'standard' that will resize the image to fit in a 220x220 rectangle.
standard:
# You can hardcode an extension (png, jpg, gif), or use the @ext value to dynamically generate the right type.
# Also works with animated gifs (but a bit experimental)
url: -> "/logo.#{@ext}" # The url to create for the image
width: 220 # The maximum width of the image
height: 220 # The maximum height of the image
,
# An example of rich text field 'wysiwyg' component
# The resulting value will be valid html
field: 'wysiwygExample'
label: 'Wysiwyg example'
type: 'wysiwyg'
height: 450 # Optional, set the height of the field inside the form
,
# An example of 'markdown' component, so you can choose your philosophy: wysiwyg or markdown
field: 'markdownExample'
label: 'Markdown example'
type: 'markdown'
height: 450 # Optional, set the height of the field inside the form
]
,
# Example of a model that can have several entries.
# We are making a blog, so we need articles!
name: ['Article', 'Articles'] # First is singular form, second is plural form. Note that urls inside admin panel will be generated by slugifying those names.
list:
# Because this model can have several entries, we need a list page.
# Here is the configuration of it
# A list is showing several 'fields' of each entries inside a table layout
fields: [
name: 'Title' # Name of the 'field' in the table
value: -> @title # The function will be called and the value will be used for display. Inside the function, you have access to all the entry's meta
,
name: 'Image'
# If you want to display html that won't be escaped, use 'html' instead of 'value'
html: ->
if @image?
return '<div style="height:32px"><img src="'[email protected]+'" style="width:32px;height:32px" alt="image" /></div>'
else
return '<div style="height:32px"> - </div>'
,
name: 'Tags'
html: ->
if @tags instanceof Array
return @tags.join(', ')
else
return ''
]
# You can add filters to you list to make browsing easier
filters: [
name: 'Tag' # Filter by tag
# The data function returns all the available values to use on the filter
# Here, we are walking through the articles to find all tags
data: ->
tags = []
filter = type: 'article'
for item in @docpad.getCollection('html').findAll(filter).models
itemTags = item.get('tags')
if itemTags instanceof Array
for tag in itemTags
if not (tag in tags)
tags.push tag
return tags
,
# A custom filter to choose articles with image or articles without image only
name: 'Kind'
data: -> ['With Image', 'Textual']
]
# The list's data function is returning all the entries of the list.
# It is in charge to take in account the filters values
# When a filter changes, this function is called again to update the list
# The result of this function can be a Docpad Collection or a JSON-like array
data: ->
filter = type: 'article'
# Filter by kind (with image or not)
if @kind is 'with-image'
filter.image = $ne: null
else if @kind is 'textual'
filter.image = null
collection = @docpad.getCollection('html').findAll(filter)
if @tag?
# Filter by tags
finalModels = []
if collection.models instanceof Array
for model in collection.models
tags = model.get('tags')
for tag in tags
if @slugify(tag) is @tag
finalModels.push model.toJSON()
break
return finalModels
else
return collection
form:
# As with the configuration model, we need a form to add/edit articles
url: -> "/blog/#{@slugify @title}" # Each article's url. We slugify the title to generate the url
ext: 'html.md'
meta:
title: -> @title
type: 'article'
layout: 'article'
image: -> @image
tags: -> if @tags instanceof Array then @tags else []
date: -> new Date(@date)
content: -> @content
components: [
field: 'title'
type: 'text'
,
# A 'date' field with a datetime picker
field: 'date'
type: 'date'
# You can remove the hours by adding time: false
#time: false
,
# Choose the tags of your article
field: 'tags'
type: 'tags'
data: ->
# The data is used for autocompletion
tags = []
for item in @docpad.getCollection('html').findAll().models
itemTags = item.get('tags')
if itemTags instanceof Array
for tag in itemTags
if not (tag in tags)
tags.push tag
return tags
,
field: 'content'
type: 'markdown'
# You can add your custom validator on any field
# Well, this is actually useless here because the default validator is doing the same check,
# but feel free to check more things for your own needs.
validate: (val) -> typeof(val) is 'string' and val.length > 0
# You can also add your custom sanitizer that will be called before saving the content
sanitize: (val) -> return val?.trim()
,
field: 'image'
type: 'file'
use: 'thumbnail'
optional: true
images:
# This time we have 3 image profiles
# Each of them will be generated from the original picture
# Notice they all have a different url
standard:
url: -> "/blog/#{@slugify @title}.#{@ext}"
width: 498
height: 9999999
thumbnail:
url: -> "/blog/#{@slugify @title}.tn.#{@ext}"
width: 9999999
height: 128
square:
url: -> "/blog/#{@slugify @title}.sq.#{@ext}"
width: 32
height: 32
crop: true # With this option, the image will be cropped in order to have the exact 32x32 size
]
,
# Another model to add links on the sidebar of the blog
# Nothing more than the articles model
name: ['Link', 'Links']
list:
fields: [
name: 'Name'
value: -> @title
,
name: 'URL'
html: -> @href
]
data: ->
filter = type: 'link'
return @docpad.getCollection('html').findAll(filter)
form:
url: -> "/link/#{@slugify @name}"
ext: 'html.md'
meta:
title: -> @name
type: 'link'
layout: 'link'
href: -> @url
content: -> @url
components: [
field: 'name'
type: 'text'
,
field: 'url'
label: 'URL'
type: 'text'
]
]