rethoth 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +26 -0
  3. data/bin/thoth +233 -0
  4. data/lib/proto/config.ru +45 -0
  5. data/lib/proto/thoth.conf.sample +206 -0
  6. data/lib/thoth/cache.rb +53 -0
  7. data/lib/thoth/config.rb +158 -0
  8. data/lib/thoth/controller/admin.rb +75 -0
  9. data/lib/thoth/controller/api/comment.rb +73 -0
  10. data/lib/thoth/controller/api/page.rb +134 -0
  11. data/lib/thoth/controller/api/post.rb +122 -0
  12. data/lib/thoth/controller/api/tag.rb +59 -0
  13. data/lib/thoth/controller/archive.rb +50 -0
  14. data/lib/thoth/controller/comment.rb +173 -0
  15. data/lib/thoth/controller/main.rb +193 -0
  16. data/lib/thoth/controller/media.rb +172 -0
  17. data/lib/thoth/controller/page.rb +167 -0
  18. data/lib/thoth/controller/post.rb +310 -0
  19. data/lib/thoth/controller/search.rb +86 -0
  20. data/lib/thoth/controller/tag.rb +107 -0
  21. data/lib/thoth/controller.rb +48 -0
  22. data/lib/thoth/errors.rb +35 -0
  23. data/lib/thoth/helper/admin.rb +86 -0
  24. data/lib/thoth/helper/cookie.rb +45 -0
  25. data/lib/thoth/helper/error.rb +122 -0
  26. data/lib/thoth/helper/pagination.rb +131 -0
  27. data/lib/thoth/helper/wiki.rb +77 -0
  28. data/lib/thoth/helper/ysearch.rb +89 -0
  29. data/lib/thoth/importer/pants.rb +81 -0
  30. data/lib/thoth/importer/poseidon.rb +54 -0
  31. data/lib/thoth/importer/thoth.rb +103 -0
  32. data/lib/thoth/importer.rb +131 -0
  33. data/lib/thoth/layout/default.rhtml +47 -0
  34. data/lib/thoth/middleware/minify.rb +82 -0
  35. data/lib/thoth/migrate/001_create_schema.rb +108 -0
  36. data/lib/thoth/migrate/002_add_media_size.rb +37 -0
  37. data/lib/thoth/migrate/003_add_post_draft.rb +38 -0
  38. data/lib/thoth/migrate/004_add_comment_email.rb +37 -0
  39. data/lib/thoth/migrate/005_add_page_position.rb +37 -0
  40. data/lib/thoth/migrate/006_add_comment_close_delete.rb +43 -0
  41. data/lib/thoth/migrate/007_add_comment_summary.rb +37 -0
  42. data/lib/thoth/model/comment.rb +216 -0
  43. data/lib/thoth/model/media.rb +87 -0
  44. data/lib/thoth/model/page.rb +204 -0
  45. data/lib/thoth/model/post.rb +262 -0
  46. data/lib/thoth/model/tag.rb +80 -0
  47. data/lib/thoth/model/tags_posts_map.rb +34 -0
  48. data/lib/thoth/monkeypatch/sequel/model/errors.rb +37 -0
  49. data/lib/thoth/plugin/thoth_delicious.rb +105 -0
  50. data/lib/thoth/plugin/thoth_flickr.rb +86 -0
  51. data/lib/thoth/plugin/thoth_pinboard.rb +98 -0
  52. data/lib/thoth/plugin/thoth_tags.rb +68 -0
  53. data/lib/thoth/plugin/thoth_twitter.rb +175 -0
  54. data/lib/thoth/plugin.rb +59 -0
  55. data/lib/thoth/public/css/admin.css +223 -0
  56. data/lib/thoth/public/css/thoth.css +592 -0
  57. data/lib/thoth/public/images/admin-sprite.png +0 -0
  58. data/lib/thoth/public/images/thoth-sprite.png +0 -0
  59. data/lib/thoth/public/js/admin/comments.js +116 -0
  60. data/lib/thoth/public/js/admin/name.js +244 -0
  61. data/lib/thoth/public/js/admin/tagcomplete.js +332 -0
  62. data/lib/thoth/public/js/lazyload-min.js +4 -0
  63. data/lib/thoth/public/js/thoth.js +203 -0
  64. data/lib/thoth/public/robots.txt +5 -0
  65. data/lib/thoth/version.rb +37 -0
  66. data/lib/thoth/view/admin/index.rhtml +1 -0
  67. data/lib/thoth/view/admin/login.rhtml +23 -0
  68. data/lib/thoth/view/admin/toolbar.rhtml +117 -0
  69. data/lib/thoth/view/admin/welcome.rhtml +58 -0
  70. data/lib/thoth/view/archive/index.rhtml +24 -0
  71. data/lib/thoth/view/comment/comment.rhtml +47 -0
  72. data/lib/thoth/view/comment/delete.rhtml +15 -0
  73. data/lib/thoth/view/comment/form.rhtml +81 -0
  74. data/lib/thoth/view/comment/index.rhtml +68 -0
  75. data/lib/thoth/view/comment/list.rhtml +48 -0
  76. data/lib/thoth/view/media/delete.rhtml +15 -0
  77. data/lib/thoth/view/media/edit.rhtml +12 -0
  78. data/lib/thoth/view/media/form.rhtml +7 -0
  79. data/lib/thoth/view/media/list.rhtml +35 -0
  80. data/lib/thoth/view/media/media.rhtml +44 -0
  81. data/lib/thoth/view/media/new.rhtml +7 -0
  82. data/lib/thoth/view/page/delete.rhtml +15 -0
  83. data/lib/thoth/view/page/edit.rhtml +15 -0
  84. data/lib/thoth/view/page/form.rhtml +57 -0
  85. data/lib/thoth/view/page/index.rhtml +9 -0
  86. data/lib/thoth/view/page/list.rhtml +49 -0
  87. data/lib/thoth/view/page/new.rhtml +15 -0
  88. data/lib/thoth/view/post/comments.rhtml +12 -0
  89. data/lib/thoth/view/post/compact.rhtml +48 -0
  90. data/lib/thoth/view/post/delete.rhtml +15 -0
  91. data/lib/thoth/view/post/edit.rhtml +15 -0
  92. data/lib/thoth/view/post/form.rhtml +83 -0
  93. data/lib/thoth/view/post/index.rhtml +48 -0
  94. data/lib/thoth/view/post/list.rhtml +61 -0
  95. data/lib/thoth/view/post/new.rhtml +15 -0
  96. data/lib/thoth/view/post/tiny.rhtml +4 -0
  97. data/lib/thoth/view/search/index.rhtml +45 -0
  98. data/lib/thoth/view/tag/index.rhtml +34 -0
  99. data/lib/thoth/view/thoth/css.rhtml +9 -0
  100. data/lib/thoth/view/thoth/footer.rhtml +15 -0
  101. data/lib/thoth/view/thoth/header.rhtml +23 -0
  102. data/lib/thoth/view/thoth/index.rhtml +11 -0
  103. data/lib/thoth/view/thoth/js.rhtml +6 -0
  104. data/lib/thoth/view/thoth/sidebar.rhtml +38 -0
  105. data/lib/thoth/view/thoth/util/pager.rhtml +23 -0
  106. data/lib/thoth/view/thoth/util/simple_pager.rhtml +15 -0
  107. data/lib/thoth/view/thoth/util/table_sortheader.rhtml +20 -0
  108. data/lib/thoth.rb +394 -0
  109. 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
@@ -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