rethoth 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +26 -0
- data/bin/thoth +233 -0
- data/lib/proto/config.ru +45 -0
- data/lib/proto/thoth.conf.sample +206 -0
- data/lib/thoth/cache.rb +53 -0
- data/lib/thoth/config.rb +158 -0
- data/lib/thoth/controller/admin.rb +75 -0
- data/lib/thoth/controller/api/comment.rb +73 -0
- data/lib/thoth/controller/api/page.rb +134 -0
- data/lib/thoth/controller/api/post.rb +122 -0
- data/lib/thoth/controller/api/tag.rb +59 -0
- data/lib/thoth/controller/archive.rb +50 -0
- data/lib/thoth/controller/comment.rb +173 -0
- data/lib/thoth/controller/main.rb +193 -0
- data/lib/thoth/controller/media.rb +172 -0
- data/lib/thoth/controller/page.rb +167 -0
- data/lib/thoth/controller/post.rb +310 -0
- data/lib/thoth/controller/search.rb +86 -0
- data/lib/thoth/controller/tag.rb +107 -0
- data/lib/thoth/controller.rb +48 -0
- data/lib/thoth/errors.rb +35 -0
- data/lib/thoth/helper/admin.rb +86 -0
- data/lib/thoth/helper/cookie.rb +45 -0
- data/lib/thoth/helper/error.rb +122 -0
- data/lib/thoth/helper/pagination.rb +131 -0
- data/lib/thoth/helper/wiki.rb +77 -0
- data/lib/thoth/helper/ysearch.rb +89 -0
- data/lib/thoth/importer/pants.rb +81 -0
- data/lib/thoth/importer/poseidon.rb +54 -0
- data/lib/thoth/importer/thoth.rb +103 -0
- data/lib/thoth/importer.rb +131 -0
- data/lib/thoth/layout/default.rhtml +47 -0
- data/lib/thoth/middleware/minify.rb +82 -0
- data/lib/thoth/migrate/001_create_schema.rb +108 -0
- data/lib/thoth/migrate/002_add_media_size.rb +37 -0
- data/lib/thoth/migrate/003_add_post_draft.rb +38 -0
- data/lib/thoth/migrate/004_add_comment_email.rb +37 -0
- data/lib/thoth/migrate/005_add_page_position.rb +37 -0
- data/lib/thoth/migrate/006_add_comment_close_delete.rb +43 -0
- data/lib/thoth/migrate/007_add_comment_summary.rb +37 -0
- data/lib/thoth/model/comment.rb +216 -0
- data/lib/thoth/model/media.rb +87 -0
- data/lib/thoth/model/page.rb +204 -0
- data/lib/thoth/model/post.rb +262 -0
- data/lib/thoth/model/tag.rb +80 -0
- data/lib/thoth/model/tags_posts_map.rb +34 -0
- data/lib/thoth/monkeypatch/sequel/model/errors.rb +37 -0
- data/lib/thoth/plugin/thoth_delicious.rb +105 -0
- data/lib/thoth/plugin/thoth_flickr.rb +86 -0
- data/lib/thoth/plugin/thoth_pinboard.rb +98 -0
- data/lib/thoth/plugin/thoth_tags.rb +68 -0
- data/lib/thoth/plugin/thoth_twitter.rb +175 -0
- data/lib/thoth/plugin.rb +59 -0
- data/lib/thoth/public/css/admin.css +223 -0
- data/lib/thoth/public/css/thoth.css +592 -0
- data/lib/thoth/public/images/admin-sprite.png +0 -0
- data/lib/thoth/public/images/thoth-sprite.png +0 -0
- data/lib/thoth/public/js/admin/comments.js +116 -0
- data/lib/thoth/public/js/admin/name.js +244 -0
- data/lib/thoth/public/js/admin/tagcomplete.js +332 -0
- data/lib/thoth/public/js/lazyload-min.js +4 -0
- data/lib/thoth/public/js/thoth.js +203 -0
- data/lib/thoth/public/robots.txt +5 -0
- data/lib/thoth/version.rb +37 -0
- data/lib/thoth/view/admin/index.rhtml +1 -0
- data/lib/thoth/view/admin/login.rhtml +23 -0
- data/lib/thoth/view/admin/toolbar.rhtml +117 -0
- data/lib/thoth/view/admin/welcome.rhtml +58 -0
- data/lib/thoth/view/archive/index.rhtml +24 -0
- data/lib/thoth/view/comment/comment.rhtml +47 -0
- data/lib/thoth/view/comment/delete.rhtml +15 -0
- data/lib/thoth/view/comment/form.rhtml +81 -0
- data/lib/thoth/view/comment/index.rhtml +68 -0
- data/lib/thoth/view/comment/list.rhtml +48 -0
- data/lib/thoth/view/media/delete.rhtml +15 -0
- data/lib/thoth/view/media/edit.rhtml +12 -0
- data/lib/thoth/view/media/form.rhtml +7 -0
- data/lib/thoth/view/media/list.rhtml +35 -0
- data/lib/thoth/view/media/media.rhtml +44 -0
- data/lib/thoth/view/media/new.rhtml +7 -0
- data/lib/thoth/view/page/delete.rhtml +15 -0
- data/lib/thoth/view/page/edit.rhtml +15 -0
- data/lib/thoth/view/page/form.rhtml +57 -0
- data/lib/thoth/view/page/index.rhtml +9 -0
- data/lib/thoth/view/page/list.rhtml +49 -0
- data/lib/thoth/view/page/new.rhtml +15 -0
- data/lib/thoth/view/post/comments.rhtml +12 -0
- data/lib/thoth/view/post/compact.rhtml +48 -0
- data/lib/thoth/view/post/delete.rhtml +15 -0
- data/lib/thoth/view/post/edit.rhtml +15 -0
- data/lib/thoth/view/post/form.rhtml +83 -0
- data/lib/thoth/view/post/index.rhtml +48 -0
- data/lib/thoth/view/post/list.rhtml +61 -0
- data/lib/thoth/view/post/new.rhtml +15 -0
- data/lib/thoth/view/post/tiny.rhtml +4 -0
- data/lib/thoth/view/search/index.rhtml +45 -0
- data/lib/thoth/view/tag/index.rhtml +34 -0
- data/lib/thoth/view/thoth/css.rhtml +9 -0
- data/lib/thoth/view/thoth/footer.rhtml +15 -0
- data/lib/thoth/view/thoth/header.rhtml +23 -0
- data/lib/thoth/view/thoth/index.rhtml +11 -0
- data/lib/thoth/view/thoth/js.rhtml +6 -0
- data/lib/thoth/view/thoth/sidebar.rhtml +38 -0
- data/lib/thoth/view/thoth/util/pager.rhtml +23 -0
- data/lib/thoth/view/thoth/util/simple_pager.rhtml +15 -0
- data/lib/thoth/view/thoth/util/table_sortheader.rhtml +20 -0
- data/lib/thoth.rb +394 -0
- metadata +409 -0
data/lib/thoth/config.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of this project nor the names of its contributors may be
|
14
|
+
# used to endorse or promote products derived from this software without
|
15
|
+
# specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
module Thoth
|
30
|
+
|
31
|
+
module Config; class << self
|
32
|
+
|
33
|
+
# Adds the specified config Hash to Thoth's config lookup chain. Any
|
34
|
+
# configuration values in _config_ will be used as defaults unless they're
|
35
|
+
# specified earlier in the lookup chain (i.e. in Thoth's config file).
|
36
|
+
def <<(config)
|
37
|
+
raise ArgumentError, "config must be a Hash" unless config.is_a?(Hash)
|
38
|
+
|
39
|
+
(@lookup ||= []) << config
|
40
|
+
cache_config
|
41
|
+
|
42
|
+
@lookup
|
43
|
+
end
|
44
|
+
|
45
|
+
# Loads the specified configuration file.
|
46
|
+
def load(file)
|
47
|
+
raise Thoth::Error, "Config file not found: #{file}" unless File.file?(file)
|
48
|
+
|
49
|
+
@live = {
|
50
|
+
'db' => "sqlite:///#{HOME_DIR}/db/live.db",
|
51
|
+
|
52
|
+
'site' => {
|
53
|
+
'name' => "New Thoth Blog",
|
54
|
+
'desc' => "Thoth is awesome.",
|
55
|
+
'url' => "http://localhost:7000/",
|
56
|
+
|
57
|
+
'core_js' => [
|
58
|
+
'http://yui.yahooapis.com/2.8.0/build/yahoo-dom-event/yahoo-dom-event.js',
|
59
|
+
'/js/thoth.js'
|
60
|
+
],
|
61
|
+
|
62
|
+
'css' => [],
|
63
|
+
'js' => [],
|
64
|
+
|
65
|
+
'enable_comments' => true,
|
66
|
+
'enable_sitemap' => true,
|
67
|
+
|
68
|
+
'gravatar' => {
|
69
|
+
'enabled' => true,
|
70
|
+
'default' => "identicon",
|
71
|
+
'rating' => "g",
|
72
|
+
'size' => 32
|
73
|
+
}
|
74
|
+
},
|
75
|
+
|
76
|
+
'admin' => {
|
77
|
+
'name' => "John Doe",
|
78
|
+
'email' => "",
|
79
|
+
'user' => "thoth",
|
80
|
+
'pass' => "thoth",
|
81
|
+
'seed' => "6d552ac197a862b82b85868d6c245feb"
|
82
|
+
},
|
83
|
+
|
84
|
+
'plugins' => [],
|
85
|
+
|
86
|
+
'media' => File.join(HOME_DIR, 'media'),
|
87
|
+
|
88
|
+
'server' => {
|
89
|
+
'adapter' => 'webrick',
|
90
|
+
'address' => '0.0.0.0',
|
91
|
+
'port' => 7000,
|
92
|
+
'enable_cache' => true,
|
93
|
+
'enable_minify' => true,
|
94
|
+
'error_log' => File.join(HOME_DIR, 'log', 'error.log'),
|
95
|
+
|
96
|
+
'memcache' => {
|
97
|
+
'enabled' => false,
|
98
|
+
'servers' => ['localhost:11211:1']
|
99
|
+
}
|
100
|
+
},
|
101
|
+
|
102
|
+
'timestamp' => {
|
103
|
+
'long' => "%A %B %d, %Y @ %I:%M %p (%Z)",
|
104
|
+
'short' => "%Y-%m-%d %I:%M"
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
@dev = {
|
109
|
+
'db' => "sqlite:///#{HOME_DIR}/db/dev.db",
|
110
|
+
|
111
|
+
'server' => {
|
112
|
+
'enable_cache' => false,
|
113
|
+
'enable_minify' => false
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
begin
|
118
|
+
config = YAML.load(Erubis::Eruby.new(File.read(file)).result(binding)) || {}
|
119
|
+
rescue => e
|
120
|
+
raise Thoth::ConfigError, "Config error in #{file}: #{e}"
|
121
|
+
end
|
122
|
+
|
123
|
+
@lookup ||= if Thoth.trait[:mode] == :production
|
124
|
+
[config['live'] || {}, @live]
|
125
|
+
else
|
126
|
+
[config['dev'] || {}, config['live'] || {}, @dev, @live]
|
127
|
+
end
|
128
|
+
|
129
|
+
cache_config
|
130
|
+
end
|
131
|
+
|
132
|
+
def method_missing(name)
|
133
|
+
(@cached || {})[name.to_s] || {}
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
# Merges configs such that those earlier in the lookup chain override those
|
139
|
+
# later in the chain.
|
140
|
+
def cache_config
|
141
|
+
@cached = {}
|
142
|
+
|
143
|
+
@lookup.reverse.each do |c|
|
144
|
+
c.each {|k, v| @cached[k] = config_merge(@cached[k] || {}, v) }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def config_merge(master, value)
|
149
|
+
if value.is_a?(Hash)
|
150
|
+
value.each {|k, v| master[k] = config_merge(master[k] || {}, v) }
|
151
|
+
return master
|
152
|
+
end
|
153
|
+
|
154
|
+
value
|
155
|
+
end
|
156
|
+
|
157
|
+
end; end
|
158
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2017 John Pagonis <john@pagonis.org>
|
3
|
+
# Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer.
|
11
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
# this list of conditions and the following disclaimer in the documentation
|
13
|
+
# and/or other materials provided with the distribution.
|
14
|
+
# * Neither the name of this project nor the names of its contributors may be
|
15
|
+
# used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
22
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
#++
|
29
|
+
|
30
|
+
module Thoth
|
31
|
+
class AdminController < Controller
|
32
|
+
map '/admin'
|
33
|
+
|
34
|
+
def index
|
35
|
+
if auth_key_valid?
|
36
|
+
@title = 'Welcome to Thoth'
|
37
|
+
@public_root = PUBLIC_DIR
|
38
|
+
@view_root = VIEW_DIR
|
39
|
+
else
|
40
|
+
@title = 'Login'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Authenticates an admin login by checking the _username_ and _password_
|
45
|
+
# request parameters against the +ADMIN_USER+ and +ADMIN_PASS+ values in the
|
46
|
+
# Thoth config file.
|
47
|
+
#
|
48
|
+
# On a successful login, an auth cookie named <em>thoth_auth</em> will be
|
49
|
+
# set and the user will be redirected to the referring URL. On an
|
50
|
+
# unsuccessful login attempt, a flash message named <em>login_error</em>
|
51
|
+
# will be set and the user will be redirected to the referring URL without
|
52
|
+
# an auth cookie.
|
53
|
+
def login
|
54
|
+
username, password = request[:username, :password]
|
55
|
+
|
56
|
+
if username == Config.admin['user'] && password == Config.admin['pass']
|
57
|
+
# Set an auth cookie that expires in two weeks.
|
58
|
+
response.set_cookie('thoth_auth', :expires => Time.now + 1209600,
|
59
|
+
:path => '/', :value => auth_key)
|
60
|
+
|
61
|
+
redirect_referrer
|
62
|
+
end
|
63
|
+
|
64
|
+
flash[:error] = 'Invalid username or password.'
|
65
|
+
redirect_referrer
|
66
|
+
end
|
67
|
+
|
68
|
+
# Deletes the <em>thoth_auth</em> cookie and redirects to the home page.
|
69
|
+
def logout
|
70
|
+
response.delete_cookie('thoth_auth', :path => '/')
|
71
|
+
redirect(MainController.r())
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of this project nor the names of its contributors may be
|
14
|
+
# used to endorse or promote products derived from this software without
|
15
|
+
# specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
module Thoth
|
30
|
+
class CommentApiController < Controller
|
31
|
+
map '/api/comment'
|
32
|
+
layout nil
|
33
|
+
|
34
|
+
# Deletes the specified comment. Returns an HTTP 200 response on success, an
|
35
|
+
# HTTP 500 response on failure, or an HTTP 404 response if the specified
|
36
|
+
# comment does not exist.
|
37
|
+
#
|
38
|
+
# ==== Query Parameters (POST only)
|
39
|
+
#
|
40
|
+
# id:: comment id
|
41
|
+
# token:: form token
|
42
|
+
#
|
43
|
+
# ==== Sample Response
|
44
|
+
#
|
45
|
+
# ===== Success
|
46
|
+
#
|
47
|
+
# {"success":true}
|
48
|
+
#
|
49
|
+
# ===== Failure
|
50
|
+
#
|
51
|
+
# {"error":"The comment could not be deleted due to a database error."}
|
52
|
+
#
|
53
|
+
def delete
|
54
|
+
error_403 unless auth_key_valid? && form_token_valid?
|
55
|
+
error_405 unless request.post?
|
56
|
+
error_404 unless request[:id] && @comment = Comment[request[:id]]
|
57
|
+
|
58
|
+
response['Content-Type'] = 'application/json'
|
59
|
+
|
60
|
+
@comment.deleted = true
|
61
|
+
|
62
|
+
if @comment.save(:changed => true, :validate => false)
|
63
|
+
Ramaze::Cache.action.clear
|
64
|
+
Ramaze::Cache.cache_helper_value.clear
|
65
|
+
JSON.generate({:success => true})
|
66
|
+
else
|
67
|
+
respond(JSON.generate({
|
68
|
+
:error => 'The comment could not be deleted due to a database error.'
|
69
|
+
}, 500))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of this project nor the names of its contributors may be
|
14
|
+
# used to endorse or promote products derived from this software without
|
15
|
+
# specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
module Thoth
|
30
|
+
class PageApiController < Controller
|
31
|
+
map '/api/page'
|
32
|
+
layout nil
|
33
|
+
|
34
|
+
# Returns a response indicating whether the specified page name is valid and
|
35
|
+
# not already taken. Returns an HTTP 200 response on success.
|
36
|
+
#
|
37
|
+
# ==== Query Parameters
|
38
|
+
#
|
39
|
+
# name:: page name to check
|
40
|
+
#
|
41
|
+
# ==== Sample Response
|
42
|
+
#
|
43
|
+
# {"valid":true,"unique":true}
|
44
|
+
#
|
45
|
+
def check_name
|
46
|
+
error_403 unless auth_key_valid?
|
47
|
+
|
48
|
+
unless request[:name] && request[:name].length > 0
|
49
|
+
error_400('Missing required parameter: name')
|
50
|
+
end
|
51
|
+
|
52
|
+
response['Content-Type'] = 'application/json'
|
53
|
+
|
54
|
+
name = request[:name].to_s
|
55
|
+
|
56
|
+
JSON.generate({
|
57
|
+
:valid => Page.name_valid?(name),
|
58
|
+
:unique => Page.name_unique?(name)
|
59
|
+
})
|
60
|
+
end
|
61
|
+
|
62
|
+
# Suggests a valid and unique name for the specified page title. Returns an
|
63
|
+
# HTTP 200 response on success.
|
64
|
+
#
|
65
|
+
# ==== Query Parameters
|
66
|
+
#
|
67
|
+
# title:: page title
|
68
|
+
#
|
69
|
+
# ==== Sample Response
|
70
|
+
#
|
71
|
+
# {"name":"ninjas-are-awesome"}
|
72
|
+
#
|
73
|
+
def suggest_name
|
74
|
+
error_403 unless auth_key_valid?
|
75
|
+
|
76
|
+
unless request[:title] && request[:title].length > 0
|
77
|
+
error_400('Missing required parameter: title')
|
78
|
+
end
|
79
|
+
|
80
|
+
response['Content-Type'] = 'application/json'
|
81
|
+
|
82
|
+
JSON.generate({"name" => Page.suggest_name(request[:title])})
|
83
|
+
end
|
84
|
+
|
85
|
+
# Sets the display position of the specified page. If the new position is
|
86
|
+
# already in use by another page, that page's position (and any others) will
|
87
|
+
# be adjusted as necessary. Returns an HTTP 200 response on success. This
|
88
|
+
# action only accepts POST requests.
|
89
|
+
#
|
90
|
+
# ==== POST Parameters
|
91
|
+
#
|
92
|
+
# id:: page id
|
93
|
+
# position:: new display position
|
94
|
+
#
|
95
|
+
# ==== Sample Response
|
96
|
+
#
|
97
|
+
# Indicates that the display position for page id 42 was successfully set to
|
98
|
+
# 3.
|
99
|
+
#
|
100
|
+
# {"id":42,"position":3}
|
101
|
+
#
|
102
|
+
def set_position
|
103
|
+
error_403 unless auth_key_valid?
|
104
|
+
error_405 unless request.post?
|
105
|
+
|
106
|
+
[:id, :position].each do |param|
|
107
|
+
unless request[param] && request[param].length > 0
|
108
|
+
error_400("Missing required parameter: #{param}")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
id = request[:id].to_i
|
113
|
+
position = request[:position].to_i
|
114
|
+
|
115
|
+
unless page = Page[id]
|
116
|
+
error_400("Invalid page id: #{id}")
|
117
|
+
end
|
118
|
+
|
119
|
+
begin
|
120
|
+
Page.normalize_positions
|
121
|
+
Page.set_position(page, position)
|
122
|
+
|
123
|
+
rescue => e
|
124
|
+
error_400("Error setting page position: #{e}")
|
125
|
+
end
|
126
|
+
|
127
|
+
JSON.generate({
|
128
|
+
:id => id,
|
129
|
+
:position => position
|
130
|
+
})
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of this project nor the names of its contributors may be
|
14
|
+
# used to endorse or promote products derived from this software without
|
15
|
+
# specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
module Thoth
|
30
|
+
class PostApiController < Controller
|
31
|
+
map '/api/post'
|
32
|
+
layout nil
|
33
|
+
|
34
|
+
# Returns a response indicating whether the specified post name is valid and
|
35
|
+
# not already taken. Returns an HTTP 200 response on success or an HTTP 500
|
36
|
+
# response on error.
|
37
|
+
#
|
38
|
+
# ==== Query Parameters
|
39
|
+
#
|
40
|
+
# name:: post name to check
|
41
|
+
#
|
42
|
+
# ==== Sample Response
|
43
|
+
#
|
44
|
+
# {"valid":true,"unique":true}
|
45
|
+
#
|
46
|
+
def check_name
|
47
|
+
error_403 unless auth_key_valid?
|
48
|
+
|
49
|
+
unless request[:name] && request[:name].length > 0
|
50
|
+
error_400('Missing required parameter: name')
|
51
|
+
end
|
52
|
+
|
53
|
+
response['Content-Type'] = 'application/json'
|
54
|
+
|
55
|
+
name = request[:name].to_s
|
56
|
+
|
57
|
+
JSON.generate({
|
58
|
+
:valid => Post.name_valid?(name),
|
59
|
+
:unique => Post.name_unique?(name)
|
60
|
+
})
|
61
|
+
end
|
62
|
+
|
63
|
+
# Deletes the specified comment. Returns an HTTP 200 response on success, an
|
64
|
+
# HTTP 500 response on error, or an HTTP 404 response if the specified
|
65
|
+
# comment does not exist.
|
66
|
+
#
|
67
|
+
# ==== Query Parameters (POST only)
|
68
|
+
#
|
69
|
+
# id:: comment id
|
70
|
+
# token:: form token
|
71
|
+
#
|
72
|
+
# ==== Sample Response
|
73
|
+
#
|
74
|
+
# ===== Success
|
75
|
+
#
|
76
|
+
# {"success":true}
|
77
|
+
#
|
78
|
+
# ===== Failure
|
79
|
+
#
|
80
|
+
# {"error":"The comment could not be deleted due to a database error."}
|
81
|
+
#
|
82
|
+
def delete
|
83
|
+
error_403 unless auth_key_valid? && form_token_valid?
|
84
|
+
error_405 unless request.post?
|
85
|
+
error_404 unless request[:id] && @comment = Comment[request[:id]]
|
86
|
+
|
87
|
+
response['Content-Type'] = 'application/json'
|
88
|
+
|
89
|
+
if @comment.destroy
|
90
|
+
Ramaze::Cache.action.clear
|
91
|
+
JSON.generate({:success => true})
|
92
|
+
else
|
93
|
+
respond(JSON.generate({
|
94
|
+
:error => 'The comment could not be deleted due to a database error.'
|
95
|
+
}, 500))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Suggests a valid and unique name for the specified post title. Returns an
|
100
|
+
# HTTP 200 response on success or an HTTP 500 response on error.
|
101
|
+
#
|
102
|
+
# ==== Query Parameters
|
103
|
+
#
|
104
|
+
# title:: post title
|
105
|
+
#
|
106
|
+
# ==== Sample Response
|
107
|
+
#
|
108
|
+
# {"name":"ninjas-are-awesome"}
|
109
|
+
#
|
110
|
+
def suggest_name
|
111
|
+
error_403 unless auth_key_valid?
|
112
|
+
|
113
|
+
unless request[:title] && request[:title].length > 0
|
114
|
+
error_400('Missing required parameter: title')
|
115
|
+
end
|
116
|
+
|
117
|
+
response['Content-Type'] = 'application/json'
|
118
|
+
|
119
|
+
JSON.generate({"name" => Post.suggest_name(request[:title])})
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of this project nor the names of its contributors may be
|
14
|
+
# used to endorse or promote products derived from this software without
|
15
|
+
# specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
module Thoth
|
30
|
+
class TagApiController < Controller
|
31
|
+
map '/api/tag'
|
32
|
+
layout nil
|
33
|
+
|
34
|
+
# Returns a JSON array of existing tag names and usage counts for tags that
|
35
|
+
# begin with the specified query string.
|
36
|
+
#
|
37
|
+
# ==== Query Parameters
|
38
|
+
#
|
39
|
+
# q:: query string
|
40
|
+
# limit:: (optional) maximum number of tags to return
|
41
|
+
#
|
42
|
+
# ==== Sample Response
|
43
|
+
#
|
44
|
+
# [["foo",15],["foobar",11],["foosball",2]]
|
45
|
+
def suggest
|
46
|
+
error_403 unless auth_key_valid?
|
47
|
+
|
48
|
+
unless request[:q]
|
49
|
+
error_400('Missing required parameter: q')
|
50
|
+
end
|
51
|
+
|
52
|
+
query = request[:q].lstrip
|
53
|
+
limit = request[:limit] ? request[:limit].to_i : 1000
|
54
|
+
|
55
|
+
response['Content-Type'] = 'application/json'
|
56
|
+
JSON.generate(Tag.suggest(query, limit))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Ryan Grove <ryan@wonko.com>
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of this project nor the names of its contributors may be
|
14
|
+
# used to endorse or promote products derived from this software without
|
15
|
+
# specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
module Thoth
|
30
|
+
class ArchiveController < Controller
|
31
|
+
map '/archive'
|
32
|
+
helper :cache, :pagination
|
33
|
+
|
34
|
+
cache_action(:method => :index, :ttl => 120) { auth_key_valid? }
|
35
|
+
|
36
|
+
def index(page = 1)
|
37
|
+
page = page.to_i
|
38
|
+
error_404 unless page >= 1
|
39
|
+
|
40
|
+
@posts = Post.recent(page, 10)
|
41
|
+
|
42
|
+
error_404 if page > @posts.page_count && @posts.page_count > 0
|
43
|
+
|
44
|
+
@title = "#{Config.site['name']} Archives (page #{page} of " <<
|
45
|
+
"#{@posts.page_count > 0 ? @posts.page_count : 1})"
|
46
|
+
|
47
|
+
@pager = pager(@posts)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|