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.
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