serif 0.0.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/Gemfile.lock +48 -0
- data/README.md +243 -0
- data/bin/serif +81 -0
- data/lib/serif/admin_server.rb +186 -0
- data/lib/serif/config.rb +25 -0
- data/lib/serif/content_file.rb +154 -0
- data/lib/serif/draft.rb +58 -0
- data/lib/serif/markup_renderer.rb +24 -0
- data/lib/serif/post.rb +65 -0
- data/lib/serif/server.rb +32 -0
- data/lib/serif/site.rb +148 -0
- data/lib/serif.rb +19 -0
- data/serif.gemspec +28 -0
- data/statics/assets/js/jquery.autosize.js +175 -0
- data/statics/assets/js/mousetrap.min.js +28 -0
- data/statics/skeleton/_config.yml +14 -0
- data/statics/skeleton/_layouts/default.html +6 -0
- data/statics/skeleton/_templates/post.html +5 -0
- data/statics/skeleton/index.html +9 -0
- data/statics/templates/admin/edit_draft.liquid +54 -0
- data/statics/templates/admin/edit_post.liquid +36 -0
- data/statics/templates/admin/index.liquid +15 -0
- data/statics/templates/admin/layout.liquid +539 -0
- data/statics/templates/admin/new_draft.liquid +35 -0
- metadata +175 -6
- data/serif-stub.gemspec +0 -10
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
serif (0.1.1)
|
5
|
+
liquid (~> 2.4)
|
6
|
+
pygments.rb (~> 0.3)
|
7
|
+
rack (~> 1.0)
|
8
|
+
rake (~> 0.9)
|
9
|
+
redcarpet (~> 2.2)
|
10
|
+
redhead (~> 0.0.6)
|
11
|
+
sinatra (~> 1.3)
|
12
|
+
slop (~> 3.3)
|
13
|
+
yui-compressor
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: http://rubygems.org/
|
17
|
+
specs:
|
18
|
+
POpen4 (0.1.4)
|
19
|
+
Platform (>= 0.4.0)
|
20
|
+
open4
|
21
|
+
Platform (0.4.0)
|
22
|
+
liquid (2.4.1)
|
23
|
+
open4 (1.3.0)
|
24
|
+
posix-spawn (0.3.6)
|
25
|
+
pygments.rb (0.3.2)
|
26
|
+
posix-spawn (~> 0.3.6)
|
27
|
+
yajl-ruby (~> 1.1.0)
|
28
|
+
rack (1.4.1)
|
29
|
+
rack-protection (1.2.0)
|
30
|
+
rack
|
31
|
+
rake (0.9.2.2)
|
32
|
+
redcarpet (2.2.2)
|
33
|
+
redhead (0.0.6)
|
34
|
+
sinatra (1.3.3)
|
35
|
+
rack (~> 1.3, >= 1.3.6)
|
36
|
+
rack-protection (~> 1.2)
|
37
|
+
tilt (~> 1.3, >= 1.3.3)
|
38
|
+
slop (3.3.3)
|
39
|
+
tilt (1.3.3)
|
40
|
+
yajl-ruby (1.1.0)
|
41
|
+
yui-compressor (0.9.6)
|
42
|
+
POpen4 (>= 0.1.4)
|
43
|
+
|
44
|
+
PLATFORMS
|
45
|
+
ruby
|
46
|
+
|
47
|
+
DEPENDENCIES
|
48
|
+
serif!
|
data/README.md
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
# Serif
|
2
|
+
|
3
|
+
Serif is a file-based blogging engine intended for simple sites. It compiles Markdown content to static files, and there is a web interface for editing and publishing ([simple video demo](https://docs.google.com/open?id=0BxPQpxGSOOyKS1J4MmlnM3JIaXM)), because managing everything with `ssh` and `git` can be a pain, compared to having a more universally accessible editing interface.
|
4
|
+
|
5
|
+
_It should be considered alpha._ I make no promises it won't all change underneath you. **It is all subject to change, and is in a rough state.**
|
6
|
+
|
7
|
+
# Intro
|
8
|
+
|
9
|
+
Serif is a lot like Jekyll with a few extra moving parts, although it didn't start that way. It went through two reworkings before being converted into something based on generated Markdown files. The aim for Serif is to provide two things:
|
10
|
+
|
11
|
+
1. Simplicity: the source and generated content are just files that can be served by any web server.
|
12
|
+
2. Ease of publishing, wherever you are: having everything based on files that you edit in a text editor is a nice idea, but what if you're on a machine that doesn't give you ssh access to your server? What if you need to edit creation timestamps? What about editing drafts without having to make commits and push to git repos?
|
13
|
+
|
14
|
+
With this in mind, you might think of Serif's aim as to merge Jekyll, [Second Crack](https://github.com/marcoarment/secondcrack) and ideas from [Svbtle](http://dcurt.is/codename-svbtle). There should be many ways of editing and publishing, such as using the web interface, `rsync`ing from a remote machine, or editing a draft file on the remote server and having everything happen for you. (This last feature doesn't quite exist as planned yet.)
|
15
|
+
|
16
|
+
## Planned features
|
17
|
+
|
18
|
+
Some things I'm hoping to implement one day:
|
19
|
+
|
20
|
+
1. Custom hooks to fire after particular events, such as minifying CSS after publish, or committing changes and pushing to a git repository.
|
21
|
+
2. Simple Markdown pages instead of plain HTML.
|
22
|
+
3. Automatically detecting file changes and regenerating the site.
|
23
|
+
4. Adding custom Liquid filters and tags.
|
24
|
+
|
25
|
+
# License and contributing
|
26
|
+
|
27
|
+
Serif is released under the MIT license. See LICENSE for details.
|
28
|
+
|
29
|
+
Any contributions will be assumed by default to be under the same terms.
|
30
|
+
|
31
|
+
# Basics
|
32
|
+
|
33
|
+
## Installing
|
34
|
+
|
35
|
+
Installation is via [RubyGems](https://rubygems.org/). If you don't have Ruby installed, I recommend using [RVM](https://rvm.io/).
|
36
|
+
|
37
|
+
```bash
|
38
|
+
$ gem install serif
|
39
|
+
```
|
40
|
+
|
41
|
+
## Generating the site
|
42
|
+
|
43
|
+
```bash
|
44
|
+
$ cd path/to/site/directory
|
45
|
+
$ serif generate
|
46
|
+
```
|
47
|
+
|
48
|
+
(You may get warnings about "undefined method `gsub' for nil:NilClass" after a warning about trying to get headers out of a file. You should be able to ignore those.)
|
49
|
+
|
50
|
+
## Starting the admin server
|
51
|
+
|
52
|
+
```bash
|
53
|
+
$ cd path/to/site/directory
|
54
|
+
$ ENV=production serif admin
|
55
|
+
```
|
56
|
+
|
57
|
+
Once this is run, visit <http://localhost:4567/admin> and log in with whatever is in `_config.yml` as auth credentials.
|
58
|
+
|
59
|
+
Drop the `ENV=production` part if you're running it locally.
|
60
|
+
|
61
|
+
## Serving up the site for development
|
62
|
+
|
63
|
+
This runs a very simple web server that is mainly designed to test what the site will look like and let you make changes to stuff like CSS files without having to regenerate everything. Changes to post content will not be detected (yet).
|
64
|
+
|
65
|
+
```bash
|
66
|
+
$ cd path/to/site/directory
|
67
|
+
$ serif dev
|
68
|
+
```
|
69
|
+
|
70
|
+
Once this is run, visit <http://localhost:8000>.
|
71
|
+
|
72
|
+
## Generate a skeleton application
|
73
|
+
|
74
|
+
You can generate a skeletal directory to get you going, using the `new` command.
|
75
|
+
|
76
|
+
```bash
|
77
|
+
$ cd path/to/site/directory
|
78
|
+
$ serif new
|
79
|
+
```
|
80
|
+
|
81
|
+
# Content and site structure
|
82
|
+
|
83
|
+
The structure of a Serif site is something like this:
|
84
|
+
|
85
|
+
```
|
86
|
+
.
|
87
|
+
├── _site
|
88
|
+
├── _layouts
|
89
|
+
│ └── default.html
|
90
|
+
├── _drafts
|
91
|
+
│ ├── some-draft
|
92
|
+
│ └── another-unfinished-post
|
93
|
+
├── _posts
|
94
|
+
│ ├── 2012-01-01-a-post-you-have-written
|
95
|
+
│ ├── 2012-02-28-another-post
|
96
|
+
│ └── 2012-03-30-and-a-third
|
97
|
+
├── _templates
|
98
|
+
│ └─── post.html
|
99
|
+
├── _trash
|
100
|
+
├── _config.yml
|
101
|
+
├── css
|
102
|
+
│ └── ...
|
103
|
+
├── js
|
104
|
+
│ └── ...
|
105
|
+
├── images
|
106
|
+
│ └── ...
|
107
|
+
├── 404.html
|
108
|
+
├── favicon.ico
|
109
|
+
├── feed.xml
|
110
|
+
└── index.html
|
111
|
+
```
|
112
|
+
|
113
|
+
## `_site`
|
114
|
+
|
115
|
+
This is where generated content gets saved. You should serve files out of here, be it with Nginx or Apache. You should assume everything in this directory will get erased at some point in future. Don't keep anything in it!
|
116
|
+
|
117
|
+
## `_layouts`
|
118
|
+
|
119
|
+
This is where layouts for the site go. At the moment, there is only one supported: `default.html`.
|
120
|
+
|
121
|
+
## `_drafts` and `_posts`
|
122
|
+
|
123
|
+
Drafts go in `_drafts`, posts go in `_posts`. Simple enough.
|
124
|
+
|
125
|
+
Posts must have filenames in the format of `YYYY-MM-DD-your-post`. Drafts do not have a date part, since they're drafts and not published.
|
126
|
+
|
127
|
+
All files in these directories are assumed to be written in Markdown, with simple HTTP-style headers. The Markdown renderer is [Redcarpet](https://github.com/vmg/redcarpet) (with fenced code blocks enabled), with Smarty for punctuation tweaks, and Pygments to allow syntax highlighting (although you'll need your own CSS).
|
128
|
+
|
129
|
+
Here's an example post:
|
130
|
+
|
131
|
+
```
|
132
|
+
Title: A title of a post
|
133
|
+
Created: 2012-01-01T14:30:00+00:00
|
134
|
+
|
135
|
+
Something something.
|
136
|
+
|
137
|
+
1. A list
|
138
|
+
2. Of some stuff
|
139
|
+
3. Goes here
|
140
|
+
|
141
|
+
End of the post
|
142
|
+
```
|
143
|
+
|
144
|
+
The headers are similar to Jekyll's YAML front matter, but here there are no formatting requirements beyond `Key: value` pairs. Header names are case-insensitive (so `title` is the same as `Title`), but values are not.
|
145
|
+
|
146
|
+
(The headers `created` and `updated` must be a string that Ruby's standard Time library can parse, but this will mostly be handled for you.)
|
147
|
+
|
148
|
+
## `_templates`
|
149
|
+
|
150
|
+
This directory currently only has a single file in it, `post.html`, which is used to produce HTML content from Markdown files in `_posts`.
|
151
|
+
|
152
|
+
## `_trash`
|
153
|
+
|
154
|
+
Deleted drafts go in here just in case you want them back.
|
155
|
+
|
156
|
+
## `_config.yml`
|
157
|
+
|
158
|
+
Used for configuration settings.
|
159
|
+
|
160
|
+
Here's a sample configuration:
|
161
|
+
|
162
|
+
```yaml
|
163
|
+
admin:
|
164
|
+
username: username
|
165
|
+
password: password
|
166
|
+
permalink: /blog/:year/:month/:title
|
167
|
+
```
|
168
|
+
|
169
|
+
If a permalink setting is not given in the configuration, the default is `/:title`. There are the following options available for permalinks:
|
170
|
+
|
171
|
+
Placeholder | Value
|
172
|
+
----------- |:-----
|
173
|
+
`:title` | URL "slug", e.g., "your-post-title"
|
174
|
+
`:year` | Year as given in the filename, e.g., "2012"
|
175
|
+
`:month` | Month as given in the filename, e.g., "01"
|
176
|
+
`:day` | Day as given in the filename, e.g., "28"
|
177
|
+
|
178
|
+
## Other files
|
179
|
+
|
180
|
+
Any other file in the directory's root will be copied over exactly as-is, with two caveats for any file ending in `.html` or `.xml`:
|
181
|
+
|
182
|
+
1. These files are assumed to contain [Liquid markup](http://liquidmarkup.org/) and will be processed as such.
|
183
|
+
2. Any header data will not be included in the processed output.
|
184
|
+
|
185
|
+
For example, this would work as an `about.html`:
|
186
|
+
|
187
|
+
```html
|
188
|
+
<h1>All about me</h1>
|
189
|
+
<p>Where do I begin? {{ 'Well...' }}</p>
|
190
|
+
```
|
191
|
+
|
192
|
+
And so would this:
|
193
|
+
|
194
|
+
```html
|
195
|
+
title: My about page
|
196
|
+
|
197
|
+
<h1>All about me</h1>
|
198
|
+
<p>Where do I begin? Well...</p>
|
199
|
+
```
|
200
|
+
|
201
|
+
In both cases, the output is, of course:
|
202
|
+
|
203
|
+
```html
|
204
|
+
<h1>All about me</h1>
|
205
|
+
<p>Where do I begin? Well...</p>
|
206
|
+
```
|
207
|
+
|
208
|
+
If you have a file like `feed.xml` that you wish to _not_ be contained within a layout, specify `layout: none` in the header for the file.
|
209
|
+
|
210
|
+
# Deploying
|
211
|
+
|
212
|
+
To serve the site, set any web server to use `/path/to/site/directory/_site` as its root. *NOTE:* URLs generated in the site do not contain `.html` "extensions" by default, so you will need a rewrite rule. Here's an example rewrite for nginx:
|
213
|
+
|
214
|
+
```
|
215
|
+
error_page 404 @not_found_page;
|
216
|
+
|
217
|
+
location / {
|
218
|
+
index index.html index.htm;
|
219
|
+
|
220
|
+
try_files $uri.html $uri $uri/ @not_found_page;
|
221
|
+
}
|
222
|
+
|
223
|
+
location @not_found_page {
|
224
|
+
rewrite .* /404.html last;
|
225
|
+
}
|
226
|
+
```
|
227
|
+
|
228
|
+
## Admin interface
|
229
|
+
|
230
|
+
The admin server can be started on the live server the same way it's started locally (with `ENV=production`). To access it from anywhere on the web, you will need to proxy/forward `/admin` HTTP requests to port 4567 to let the admin web server handle it. As an alternative, you could forward a local port with SSH --- you might use this if you didn't want to rely on just HTTP basic auth, which isn't very secure over non-HTTPS connections.
|
231
|
+
|
232
|
+
# Customising the admin interface
|
233
|
+
|
234
|
+
The admin interface is intended to be a minimal place to focus on writing content. You are free to customise the admin interface by creating a stylesheet at `$your_site_directory/css/admin/admin.css`. As an example, if your main site's stylesheet is `/css/style.css`, you can use an `@import` rule to inherit the look-and-feel of your main site editing content and looking at rendered previews.
|
235
|
+
|
236
|
+
|
237
|
+
```css
|
238
|
+
/* Import the main site's CSS to provide a similar look-and-feel for the admin interface */
|
239
|
+
|
240
|
+
@import url("/css/style.css");
|
241
|
+
|
242
|
+
/* more customisation below */
|
243
|
+
```
|
data/bin/serif
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
4
|
+
|
5
|
+
require "fileutils"
|
6
|
+
require "slop"
|
7
|
+
|
8
|
+
def initialize_admin_server(source_dir)
|
9
|
+
# need to cd to the directory before requiring the admin
|
10
|
+
# server, because otherwise Dir.pwd won't be right when
|
11
|
+
# the admin server class is defined at require time.
|
12
|
+
FileUtils.cd(source_dir)
|
13
|
+
require "serif"
|
14
|
+
require "serif/admin_server"
|
15
|
+
|
16
|
+
server = Serif::AdminServer.new(source_dir)
|
17
|
+
server.start
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize_dev_server(source_dir)
|
21
|
+
FileUtils.cd(source_dir)
|
22
|
+
require "serif"
|
23
|
+
require "serif/server"
|
24
|
+
|
25
|
+
server = Serif::DevelopmentServer.new(source_dir)
|
26
|
+
server.start
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_site(source_dir)
|
30
|
+
require "serif"
|
31
|
+
|
32
|
+
site = Serif::Site.new(source_dir)
|
33
|
+
site.generate
|
34
|
+
end
|
35
|
+
|
36
|
+
def verify_directory(dir)
|
37
|
+
unless Dir.exist?(dir)
|
38
|
+
puts "No such directory: #{dir}'"
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def produce_skeleton(dir)
|
44
|
+
if !Dir[File.join(dir, "*")].empty?
|
45
|
+
abort "Directory is not empty."
|
46
|
+
end
|
47
|
+
|
48
|
+
FileUtils.cd(File.join(File.dirname(__FILE__), "..", "statics", "skeleton"))
|
49
|
+
files = Dir["*"]
|
50
|
+
files.each do |f|
|
51
|
+
FileUtils.cp_r(f, dir, verbose: true)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
commands = Slop::Commands.new do
|
56
|
+
on :admin do
|
57
|
+
add_callback :empty do
|
58
|
+
initialize_admin_server(Dir.pwd)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
on :generate do
|
63
|
+
add_callback :empty do
|
64
|
+
generate_site(Dir.pwd)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
on :dev do
|
69
|
+
add_callback :empty do
|
70
|
+
initialize_dev_server(Dir.pwd)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
on :new do
|
75
|
+
add_callback :empty do
|
76
|
+
produce_skeleton(Dir.pwd)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
commands.parse
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require "sinatra/base"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module Serif
|
5
|
+
class AdminServer
|
6
|
+
class AdminApp < Sinatra::Base
|
7
|
+
Tilt.register :html, Tilt[:liquid]
|
8
|
+
|
9
|
+
|
10
|
+
set :root, Dir.pwd
|
11
|
+
set :public_folder, settings.root + (ENV["ENV"] == "production" ? "/_site" : "")
|
12
|
+
set :views, File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "statics", "templates", "admin"))
|
13
|
+
|
14
|
+
site = Serif::Site.new(settings.root)
|
15
|
+
|
16
|
+
use(Rack::Auth::Basic, "Login credentials") do |username, password|
|
17
|
+
[username, password] == [site.config.admin_username, site.config.admin_password]
|
18
|
+
end
|
19
|
+
|
20
|
+
# multiple public folders??
|
21
|
+
get "/admin/js/:file" do |file|
|
22
|
+
assets_dir = File.join(File.dirname(__FILE__), "..", "..", "statics", "assets", "js")
|
23
|
+
|
24
|
+
[200, { "Content-Type" => "text/javascript" }, File.read(File.join(assets_dir, file))]
|
25
|
+
end
|
26
|
+
|
27
|
+
get "/" do
|
28
|
+
redirect to("/admin")
|
29
|
+
end
|
30
|
+
|
31
|
+
get "/admin/?" do
|
32
|
+
posts = site.posts.sort_by { |p| p.created }.reverse
|
33
|
+
drafts = site.drafts.sort_by { |p| p.slug }.reverse
|
34
|
+
|
35
|
+
liquid :index, locals: { posts: posts, drafts: drafts }
|
36
|
+
end
|
37
|
+
|
38
|
+
get "/admin/edit/?" do
|
39
|
+
redirect to("/admin"), 301
|
40
|
+
end
|
41
|
+
|
42
|
+
get "/admin/new/draft" do
|
43
|
+
content = Draft.new(site)
|
44
|
+
autofocus = "slug"
|
45
|
+
liquid :new_draft, locals: { post: content, autofocus: autofocus }
|
46
|
+
end
|
47
|
+
|
48
|
+
post "/admin/new/draft" do
|
49
|
+
content = Draft.new(site)
|
50
|
+
content.slug = params[:slug].strip
|
51
|
+
content.title = params[:title].strip
|
52
|
+
|
53
|
+
if params[:markdown].strip.empty? || params[:title].empty? || params[:slug].empty?
|
54
|
+
[:title, :slug, :markdown].each do |p|
|
55
|
+
params[p] = nil if params[p] && params[p].empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
error_message = "There must be a URL, a title, and content to save."
|
59
|
+
|
60
|
+
autofocus = "markdown" unless params[:markdown]
|
61
|
+
autofocus = "title" unless params[:title]
|
62
|
+
autofocus = "slug" unless params[:slug]
|
63
|
+
|
64
|
+
liquid :new_draft, locals: { error_message: error_message, post: content, autofocus: autofocus }
|
65
|
+
else
|
66
|
+
if Draft.exist?(site, params[:slug])
|
67
|
+
liquid :new_draft, locals: { error_message: error_message, post: content, autofocus: autofocus }
|
68
|
+
else
|
69
|
+
content.save(params[:markdown])
|
70
|
+
redirect to("/admin")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
post "/admin/edit/drafts" do
|
76
|
+
content = Draft.from_slug(site, params[:original_slug])
|
77
|
+
|
78
|
+
params[:markdown] = params[:markdown].strip
|
79
|
+
|
80
|
+
# check if the slug has been edited, i.e., if we're renaming.
|
81
|
+
if !params[:slug].empty? && params[:original_slug] && params[:original_slug] != params[:slug]
|
82
|
+
if Draft.exist?(site, params[:slug])
|
83
|
+
conflicting_name = true
|
84
|
+
|
85
|
+
# we need to re-edit, so reload but use the original slug name
|
86
|
+
# not the new one that was attempted to be saved.
|
87
|
+
content = Draft.from_slug(site, params[:original_slug])
|
88
|
+
else
|
89
|
+
Draft.rename(params[:original_slug], params[:slug])
|
90
|
+
|
91
|
+
# re-load after the rename
|
92
|
+
content = Draft.from_slug(site, params[:slug])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# make sure the title is whatever was just submitted
|
97
|
+
content.title = params[:title]
|
98
|
+
|
99
|
+
# any errors
|
100
|
+
if conflicting_name || params[:markdown].empty? || params[:slug].empty?
|
101
|
+
if conflicting_name
|
102
|
+
error_message = "This name is already being used for a draft."
|
103
|
+
elsif params[:markdown].empty?
|
104
|
+
error_message = "Content must not be blank."
|
105
|
+
elsif params[:slug].empty?
|
106
|
+
error_message = "You must pick a URL to use"
|
107
|
+
end
|
108
|
+
|
109
|
+
liquid :edit_draft, locals: { error_message: error_message, post: content }
|
110
|
+
else
|
111
|
+
content.save(params[:markdown])
|
112
|
+
|
113
|
+
# TODO: move the entire notion of generating a site out into
|
114
|
+
# a directory-change-level event.
|
115
|
+
if params[:publish] == "yes"
|
116
|
+
content.publish!
|
117
|
+
site.generate
|
118
|
+
end
|
119
|
+
|
120
|
+
redirect to("/admin")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
post "/admin/edit/posts" do
|
125
|
+
content = Post.from_slug(site, params[:original_slug])
|
126
|
+
|
127
|
+
params[:markdown] = params[:markdown].strip
|
128
|
+
params[:title] = params[:title].strip
|
129
|
+
|
130
|
+
content.title = params[:title]
|
131
|
+
|
132
|
+
if params[:markdown].empty? || params[:title].empty?
|
133
|
+
error_message = "Content must not be blank." if params[:markdown].empty?
|
134
|
+
error_message = "Title must not be blank." if params[:title].empty?
|
135
|
+
|
136
|
+
liquid :edit_post, locals: { error_message: error_message, post: content }
|
137
|
+
else
|
138
|
+
content.save(params[:markdown])
|
139
|
+
site.generate
|
140
|
+
|
141
|
+
redirect to("/admin")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
get "/admin/edit/:type/:slug" do
|
146
|
+
redirect to("/admin") unless params[:slug]
|
147
|
+
|
148
|
+
if params[:type] == "posts"
|
149
|
+
content = site.posts.find { |p| p.slug == params[:slug] }
|
150
|
+
liquid :edit_post, locals: { post: content, autofocus: "markdown" }
|
151
|
+
elsif params[:type] == "drafts"
|
152
|
+
content = Draft.from_slug(site, params[:slug])
|
153
|
+
liquid :edit_draft, locals: { post: content, autofocus: "markdown" }
|
154
|
+
else
|
155
|
+
response.status = 404
|
156
|
+
return "Nope"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
post "/admin/delete/?" do
|
161
|
+
content = Draft.from_slug(site, params[:original_slug])
|
162
|
+
content.delete!
|
163
|
+
|
164
|
+
redirect to("/admin")
|
165
|
+
end
|
166
|
+
|
167
|
+
post "/admin/convert-markdown/?" do
|
168
|
+
content = params["content"]
|
169
|
+
|
170
|
+
if request.xhr?
|
171
|
+
Redcarpet::Markdown.new(Serif::MarkupRenderer, fenced_code_blocks: true).render(content).strip
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def initialize(source_directory)
|
177
|
+
@source_directory = File.expand_path(source_directory)
|
178
|
+
end
|
179
|
+
|
180
|
+
def start
|
181
|
+
FileUtils.cd @source_directory
|
182
|
+
app = Sinatra.new(AdminApp)
|
183
|
+
app.run!
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
data/lib/serif/config.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Serif
|
4
|
+
class Config
|
5
|
+
def initialize(config_file)
|
6
|
+
@config_file = config_file
|
7
|
+
end
|
8
|
+
|
9
|
+
def yaml
|
10
|
+
YAML.load_file(@config_file)
|
11
|
+
end
|
12
|
+
|
13
|
+
def admin_username
|
14
|
+
yaml["admin"]["username"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def admin_password
|
18
|
+
yaml["admin"]["password"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def permalink
|
22
|
+
yaml["permalink"] || "/:title"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|