rethoth 0.4.1
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 +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
@@ -0,0 +1,310 @@
|
|
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 PostController < Controller
|
32
|
+
map '/post'
|
33
|
+
helper :cache, :pagination, :wiki
|
34
|
+
|
35
|
+
cache_action(:method => :atom, :ttl => 120)
|
36
|
+
|
37
|
+
def index(name = nil)
|
38
|
+
error_404 unless name && @post = Post.get(name)
|
39
|
+
|
40
|
+
# Permanently redirect id-based URLs to name-based URLs to reduce search
|
41
|
+
# result dupes and improve pagerank.
|
42
|
+
raw_redirect(@post.url, :status => 301) if name =~ /^\d+$/
|
43
|
+
|
44
|
+
cache_key = "comments_#{@post.id}_#{auth_key_valid?.to_s}"
|
45
|
+
|
46
|
+
if request.post? && @post.allow_comments && Config.site['enable_comments']
|
47
|
+
# Dump the request if the robot traps were triggered.
|
48
|
+
error_404 unless request['captcha'].empty? && request['comment'].empty?
|
49
|
+
|
50
|
+
# Create a new comment.
|
51
|
+
comment = Comment.new do |c|
|
52
|
+
c.post_id = @post.id
|
53
|
+
c.author = request[:author]
|
54
|
+
c.author_email = request[:author_email]
|
55
|
+
c.author_url = request[:author_url]
|
56
|
+
c.title = ''
|
57
|
+
c.body = request[:body]
|
58
|
+
c.ip = request.ip
|
59
|
+
end
|
60
|
+
|
61
|
+
# Set cookies.
|
62
|
+
expire = Time.now + 5184000 # two months from now
|
63
|
+
|
64
|
+
response.set_cookie(:thoth_author, :expires => expire, :path => '/',
|
65
|
+
:value => comment.author)
|
66
|
+
response.set_cookie(:thoth_author_email, :expires => expire,
|
67
|
+
:path => '/', :value => comment.author_email)
|
68
|
+
response.set_cookie(:thoth_author_url, :expires => expire, :path => '/',
|
69
|
+
:value => comment.author_url)
|
70
|
+
|
71
|
+
if comment.valid? && request[:action] == 'Post Comment'
|
72
|
+
begin
|
73
|
+
raise unless comment.save
|
74
|
+
rescue => e
|
75
|
+
@comment_error = 'There was an error posting your comment. ' <<
|
76
|
+
'Please try again later.'
|
77
|
+
else
|
78
|
+
flash[:success] = 'Comment posted.'
|
79
|
+
cache_value.delete(cache_key)
|
80
|
+
redirect(rs(@post.name).to_s + "#comment-#{comment.id}")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
@author = comment.author
|
85
|
+
@author_email = comment.author_email
|
86
|
+
@author_url = comment.author_url
|
87
|
+
@preview = comment
|
88
|
+
elsif @post.allow_comments && Config.site['enable_comments']
|
89
|
+
@author = cookie(:thoth_author, '')
|
90
|
+
@author_email = cookie(:thoth_author_email, '')
|
91
|
+
@author_url = cookie(:thoth_author_url, '')
|
92
|
+
end
|
93
|
+
|
94
|
+
@title = @post.title
|
95
|
+
|
96
|
+
if Config.site['enable_comments']
|
97
|
+
@comment_action = r(:/, @post.name).to_s + '#post-comment'
|
98
|
+
|
99
|
+
# Since repeated calls to render_view are very expensive, we pre-render
|
100
|
+
# the comments here and cache them to speed up future pageviews.
|
101
|
+
unless @comments_rendered = cache_value[cache_key]
|
102
|
+
@comments_rendered = ''
|
103
|
+
|
104
|
+
@post.comments.all.each do |comment|
|
105
|
+
@comments_rendered << CommentController.render_view(:comment,
|
106
|
+
:comment => comment)
|
107
|
+
end
|
108
|
+
|
109
|
+
cache_value.store(cache_key, @comments_rendered, :ttl => 300)
|
110
|
+
end
|
111
|
+
|
112
|
+
@feeds = [{
|
113
|
+
:href => @post.atom_url,
|
114
|
+
:title => 'Comments on this post',
|
115
|
+
:type => 'application/atom+xml'
|
116
|
+
}]
|
117
|
+
end
|
118
|
+
|
119
|
+
@show_post_edit = true
|
120
|
+
end
|
121
|
+
|
122
|
+
def atom(name = nil)
|
123
|
+
error_404 unless name && post = Post.get(name)
|
124
|
+
|
125
|
+
# Permanently redirect id-based URLs to name-based URLs to reduce search
|
126
|
+
# result dupes and improve pagerank.
|
127
|
+
raw_redirect(post.atom_url, :status => 301) if name =~ /^\d+$/
|
128
|
+
|
129
|
+
response['Content-Type'] = 'application/atom+xml'
|
130
|
+
|
131
|
+
comments = post.comments.reverse_order.limit(20)
|
132
|
+
updated = comments.count > 0 ? comments.first.created_at.xmlschema :
|
133
|
+
post.created_at.xmlschema
|
134
|
+
|
135
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
136
|
+
x.instruct!
|
137
|
+
|
138
|
+
x.feed(:xmlns => 'http://www.w3.org/2005/Atom') {
|
139
|
+
x.id post.url
|
140
|
+
x.title "Comments on \"#{post.title}\" - #{Config.site['name']}"
|
141
|
+
x.updated updated
|
142
|
+
x.link :href => post.url
|
143
|
+
x.link :href => post.atom_url, :rel => 'self'
|
144
|
+
|
145
|
+
comments.all do |comment|
|
146
|
+
x.entry {
|
147
|
+
x.id comment.url
|
148
|
+
x.title comment.title
|
149
|
+
x.published comment.created_at.xmlschema
|
150
|
+
x.updated comment.updated_at.xmlschema
|
151
|
+
x.link :href => comment.url, :rel => 'alternate'
|
152
|
+
x.content comment.body_rendered, :type => 'html'
|
153
|
+
|
154
|
+
x.author {
|
155
|
+
x.name comment.author
|
156
|
+
|
157
|
+
if comment.author_url && !comment.author_url.empty?
|
158
|
+
x.uri comment.author_url
|
159
|
+
end
|
160
|
+
}
|
161
|
+
}
|
162
|
+
end
|
163
|
+
}
|
164
|
+
|
165
|
+
throw(:respond, x.target!)
|
166
|
+
end
|
167
|
+
|
168
|
+
def delete(id = nil)
|
169
|
+
require_auth
|
170
|
+
|
171
|
+
error_404 unless id && @post = Post[id]
|
172
|
+
|
173
|
+
if request.post?
|
174
|
+
error_403 unless form_token_valid?
|
175
|
+
|
176
|
+
if request[:confirm] == 'yes'
|
177
|
+
@post.destroy
|
178
|
+
Ramaze::Cache.action.clear
|
179
|
+
flash[:success] = 'Blog post deleted.'
|
180
|
+
redirect(MainController.r())
|
181
|
+
else
|
182
|
+
redirect(@post.url)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
@title = "Delete Post: #{@post.title}"
|
187
|
+
@show_post_edit = true
|
188
|
+
end
|
189
|
+
|
190
|
+
def edit(id = nil)
|
191
|
+
require_auth
|
192
|
+
|
193
|
+
unless @post = Post[id]
|
194
|
+
flash[:error] = 'Invalid post id.'
|
195
|
+
redirect(rs(:new))
|
196
|
+
end
|
197
|
+
|
198
|
+
if request.post?
|
199
|
+
error_403 unless form_token_valid?
|
200
|
+
|
201
|
+
if request[:name] && !request[:name].empty?
|
202
|
+
@post.name = request[:name]
|
203
|
+
end
|
204
|
+
|
205
|
+
@post.title = request[:title]
|
206
|
+
@post.body = request[:body]
|
207
|
+
@post.tags = request[:tags]
|
208
|
+
@post.allow_comments = !!request[:allow_comments]
|
209
|
+
|
210
|
+
@post.is_draft = @post.is_draft ? request[:action] != 'Publish' :
|
211
|
+
request[:action] == 'Unpublish & Save as Draft'
|
212
|
+
|
213
|
+
@post.created_at = Time.now if @post.is_draft
|
214
|
+
|
215
|
+
if @post.valid? && (@post.is_draft || request[:action] == 'Publish')
|
216
|
+
begin
|
217
|
+
Thoth.db.transaction do
|
218
|
+
raise unless @post.save && @post.tags = request[:tags]
|
219
|
+
end
|
220
|
+
rescue => e
|
221
|
+
@post_error = "There was an error saving your post: #{e}"
|
222
|
+
else
|
223
|
+
if @post.is_draft
|
224
|
+
flash[:success] = 'Draft saved.'
|
225
|
+
redirect(rs(:edit, @post.id))
|
226
|
+
else
|
227
|
+
Ramaze::Cache.action.clear
|
228
|
+
flash[:success] = 'Blog post published.'
|
229
|
+
redirect(rs(@post.name))
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
@title = "Edit blog post - #{@post.title}"
|
236
|
+
@form_action = rs(:edit, id)
|
237
|
+
@show_post_edit = true
|
238
|
+
end
|
239
|
+
|
240
|
+
def list(page = 1)
|
241
|
+
require_auth
|
242
|
+
|
243
|
+
page = page.to_i
|
244
|
+
|
245
|
+
@columns = [:id, :title, :created_at, :updated_at]
|
246
|
+
@order = (request[:order] || :desc).to_sym
|
247
|
+
@sort = (request[:sort] || :created_at).to_sym
|
248
|
+
@sort = :created_at unless @columns.include?(@sort)
|
249
|
+
@sort_url = rs(:list, page)
|
250
|
+
|
251
|
+
@posts = Post.filter(:is_draft => false).paginate(page, 20).order(
|
252
|
+
@order == :desc ? Sequel.desc(@sort) : @sort)
|
253
|
+
|
254
|
+
if page == 1
|
255
|
+
@drafts = Post.filter(:is_draft => true).order(
|
256
|
+
@order == :desc ? Sequel.desc(@sort) : @sort)
|
257
|
+
end
|
258
|
+
|
259
|
+
@title = "Blog Posts (page #{page} of #{[@posts.page_count, 1].max})"
|
260
|
+
@pager = pager(@posts, rs(:list, '__page__', :sort => @sort, :order => @order))
|
261
|
+
end
|
262
|
+
|
263
|
+
def new
|
264
|
+
require_auth
|
265
|
+
|
266
|
+
@title = "New blog post - Untitled"
|
267
|
+
@form_action = rs(:new)
|
268
|
+
|
269
|
+
if request.post?
|
270
|
+
error_403 unless form_token_valid?
|
271
|
+
|
272
|
+
@post = Post.new do |p|
|
273
|
+
if request[:name] && !request[:name].empty?
|
274
|
+
p.name = request[:name]
|
275
|
+
end
|
276
|
+
|
277
|
+
p.title = request[:title]
|
278
|
+
p.body = request[:body]
|
279
|
+
p.tags = request[:tags]
|
280
|
+
p.allow_comments = !!request[:allow_comments]
|
281
|
+
p.is_draft = request[:action] == 'Save & Preview'
|
282
|
+
end
|
283
|
+
|
284
|
+
if @post.valid?
|
285
|
+
begin
|
286
|
+
Thoth.db.transaction do
|
287
|
+
raise unless @post.save && @post.tags = request[:tags]
|
288
|
+
end
|
289
|
+
rescue => e
|
290
|
+
@post.is_draft = true
|
291
|
+
@post_error = "There was an error saving your post: #{e}"
|
292
|
+
else
|
293
|
+
if @post.is_draft
|
294
|
+
flash[:success] = 'Draft saved.'
|
295
|
+
redirect(rs(:edit, @post.id))
|
296
|
+
else
|
297
|
+
Ramaze::Cache.action.clear
|
298
|
+
flash[:success] = 'Blog post published.'
|
299
|
+
redirect(rs(@post.name))
|
300
|
+
end
|
301
|
+
end
|
302
|
+
else
|
303
|
+
@post.is_draft = true
|
304
|
+
end
|
305
|
+
|
306
|
+
@title = "New blog post - #{@post.title}"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
@@ -0,0 +1,86 @@
|
|
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 SearchController < Controller
|
31
|
+
map '/search'
|
32
|
+
helper :cache, :ysearch
|
33
|
+
|
34
|
+
cache_action(:method => :index, :ttl => 300) do
|
35
|
+
auth_key_valid?.to_s + request[:q] + (request[:start] || '') +
|
36
|
+
(request[:count] || '')
|
37
|
+
end
|
38
|
+
|
39
|
+
def index
|
40
|
+
redirect_referrer if request[:q].nil? || request[:q].empty?
|
41
|
+
@query = request[:q].strip
|
42
|
+
redirect_referrer if @query.empty?
|
43
|
+
|
44
|
+
count = request[:count] ? request[:count].strip.to_i : 10
|
45
|
+
start = request[:start] ? request[:start].strip.to_i : 1
|
46
|
+
|
47
|
+
count = 5 if count < 5
|
48
|
+
count = 100 if count > 100
|
49
|
+
start = 1 if start < 1
|
50
|
+
start = 990 if start > 990
|
51
|
+
|
52
|
+
@title = "Search results for #{@query}"
|
53
|
+
|
54
|
+
@data = yahoo_search(
|
55
|
+
"#{@query} -inurl:/tag -inurl:/archive -inurl:/search",
|
56
|
+
:adult_ok => 1,
|
57
|
+
:results => count,
|
58
|
+
:site => Config.site['url'].gsub(/^https?:\/\/([^\/]+)\/?$/i){$1},
|
59
|
+
:start => start
|
60
|
+
)
|
61
|
+
|
62
|
+
# Set up pagination links.
|
63
|
+
if @data[:available] > @data[:returned]
|
64
|
+
if @data[:start] > 1
|
65
|
+
prev_start = start - count
|
66
|
+
prev_start = 1 if prev_start < 1
|
67
|
+
|
68
|
+
@prev_url = "#{rs()}?q=#{u(@query)}&count=#{count}&start=" <<
|
69
|
+
prev_start.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
if @data[:available] > (@data[:start] + @data[:returned])
|
73
|
+
next_start = start + @data[:returned]
|
74
|
+
next_start = 1001 - count if next_start > (1001 - count)
|
75
|
+
|
76
|
+
@next_url = "#{rs()}?q=#{u(@query)}&count=#{count}&start=" <<
|
77
|
+
next_start.to_s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
rescue SearchError => e
|
82
|
+
@error = e.message
|
83
|
+
@data = {:results => []}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,107 @@
|
|
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 TagController < Controller
|
31
|
+
map '/tag'
|
32
|
+
helper :cache, :pagination
|
33
|
+
|
34
|
+
cache_action(:method => :index, :ttl => 120) { auth_key_valid? }
|
35
|
+
cache_action(:method => :atom, :ttl => 120)
|
36
|
+
|
37
|
+
def index(name = nil, page = 1)
|
38
|
+
error_404 unless name && @tag = Tag[:name => name.strip.downcase]
|
39
|
+
|
40
|
+
page = page.to_i
|
41
|
+
page = 1 unless page >= 1
|
42
|
+
|
43
|
+
@posts = @tag.posts.paginate(page, 10)
|
44
|
+
|
45
|
+
if page > @posts.page_count && @posts.page_count > 0
|
46
|
+
page = @posts.page_count
|
47
|
+
@posts = @tag.posts.paginate(page, 10)
|
48
|
+
end
|
49
|
+
|
50
|
+
@title = "Posts tagged with \"#{@tag.name}\" (page #{page} of " <<
|
51
|
+
"#{@posts.page_count > 0 ? @posts.page_count : 1})"
|
52
|
+
|
53
|
+
@pager = pager(@posts, rs(:/, name, '__page__'))
|
54
|
+
|
55
|
+
@feeds = [{
|
56
|
+
:href => @tag.atom_url,
|
57
|
+
:title => 'Posts with this tag',
|
58
|
+
:type => 'application/atom+xml'
|
59
|
+
}]
|
60
|
+
end
|
61
|
+
|
62
|
+
def atom(name = nil)
|
63
|
+
error_404 unless name && tag = Tag[:name => name.strip.downcase]
|
64
|
+
|
65
|
+
response['Content-Type'] = 'application/atom+xml'
|
66
|
+
|
67
|
+
posts = tag.posts.limit(10)
|
68
|
+
updated = posts.count > 0 ? posts.first.created_at.xmlschema :
|
69
|
+
Time.at(0).xmlschema
|
70
|
+
|
71
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
72
|
+
x.instruct!
|
73
|
+
|
74
|
+
x.feed(:xmlns => 'http://www.w3.org/2005/Atom') {
|
75
|
+
x.id tag.url
|
76
|
+
x.title "Posts tagged with \"#{tag.name}\" - #{Config.site['name']}"
|
77
|
+
x.updated updated
|
78
|
+
x.link :href => tag.url
|
79
|
+
x.link :href => tag.atom_url, :rel => 'self'
|
80
|
+
|
81
|
+
x.author {
|
82
|
+
x.name Config.admin['name']
|
83
|
+
x.email Config.admin['email']
|
84
|
+
x.uri Config.site['url']
|
85
|
+
}
|
86
|
+
|
87
|
+
posts.all do |post|
|
88
|
+
x.entry {
|
89
|
+
x.id post.url
|
90
|
+
x.title post.title
|
91
|
+
x.published post.created_at.xmlschema
|
92
|
+
x.updated post.updated_at.xmlschema
|
93
|
+
x.link :href => post.url, :rel => 'alternate'
|
94
|
+
x.content post.body_rendered, :type => 'html'
|
95
|
+
|
96
|
+
post.tags.each do |tag|
|
97
|
+
x.category :term => tag.name, :label => tag.name,
|
98
|
+
:scheme => tag.url
|
99
|
+
end
|
100
|
+
}
|
101
|
+
end
|
102
|
+
}
|
103
|
+
|
104
|
+
throw(:respond, x.target!)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,48 @@
|
|
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 Controller < Ramaze::Controller
|
32
|
+
helper :admin, :cookie, :error
|
33
|
+
|
34
|
+
engine :Erubis
|
35
|
+
layout :default
|
36
|
+
map_layouts '/'
|
37
|
+
|
38
|
+
# Displays a custom 404 error when a nonexistent action is requested.
|
39
|
+
def self.action_missing(path)
|
40
|
+
return if path == '/error_404'
|
41
|
+
try_resolve('/error_404')
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
Thoth.acquire(File.join(LIB_DIR, 'controller', '*'))
|
47
|
+
Thoth.acquire(File.join(LIB_DIR, 'controller', 'api', '*'))
|
48
|
+
end
|
data/lib/thoth/errors.rb
ADDED
@@ -0,0 +1,35 @@
|
|
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
|
+
class Error < StandardError; end
|
32
|
+
class ConfigError < Error; end
|
33
|
+
class SchemaError < Error; end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,86 @@
|
|
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; module Helper
|
31
|
+
|
32
|
+
# The Admin helper provides methods for checking for or requiring
|
33
|
+
# authorization from within other actions and views.
|
34
|
+
module Admin
|
35
|
+
# Generates and returns an auth key suitable for storage in a client-side
|
36
|
+
# auth cookie. The key is an SHA256 hash of the following elements:
|
37
|
+
#
|
38
|
+
# - Thoth HOME_DIR path
|
39
|
+
# - user's IP address
|
40
|
+
# - AUTH_SEED from Thoth config
|
41
|
+
# - ADMIN_USER from Thoth config
|
42
|
+
# - ADMIN_PASS from Thoth config
|
43
|
+
def auth_key
|
44
|
+
Digest::SHA256.hexdigest(HOME_DIR + request.ip + Config.admin['seed'] +
|
45
|
+
Config.admin['user'] + Config.admin['pass'])
|
46
|
+
end
|
47
|
+
|
48
|
+
# Validates the auth cookie and returns +true+ if the user is authenticated,
|
49
|
+
# +false+ otherwise.
|
50
|
+
def auth_key_valid?
|
51
|
+
return false unless thoth_auth = cookie(:thoth_auth)
|
52
|
+
thoth_auth == auth_key
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns a String that can be included in a hidden form field and used on
|
56
|
+
# submission to verify that the form was not submitted by an unauthorized
|
57
|
+
# third party.
|
58
|
+
def form_token
|
59
|
+
cookie_token = cookie(:thoth_token)
|
60
|
+
return cookie_token if cookie_token
|
61
|
+
|
62
|
+
chaos = [srand, rand, Time.now.to_f, HOME_DIR].join
|
63
|
+
cookie_token = Digest::SHA256.hexdigest(chaos)
|
64
|
+
|
65
|
+
response.set_cookie(:thoth_token,
|
66
|
+
:path => '/',
|
67
|
+
:value => cookie_token
|
68
|
+
)
|
69
|
+
|
70
|
+
cookie_token
|
71
|
+
end
|
72
|
+
|
73
|
+
# Checks the form token specified by _name_ and returns +true+ if it's
|
74
|
+
# valid, +false+ otherwise.
|
75
|
+
def form_token_valid?(name = 'token')
|
76
|
+
request[name] == form_token
|
77
|
+
end
|
78
|
+
|
79
|
+
# Checks the auth cookie and redirects to the login page if the user is not
|
80
|
+
# authenticated.
|
81
|
+
def require_auth
|
82
|
+
redirect(AdminController.r()) unless auth_key_valid?
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end; end
|