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,173 @@
|
|
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 CommentController < Controller
|
32
|
+
map '/comment'
|
33
|
+
helper :aspect, :cache, :pagination
|
34
|
+
|
35
|
+
cache_action(:method => :index, :ttl => 60) { auth_key_valid? }
|
36
|
+
cache_action(:method => :atom, :ttl => 120)
|
37
|
+
cache_action(:method => :rss, :ttl => 120)
|
38
|
+
|
39
|
+
before_all { error_404 unless Thoth::Config.site['enable_comments'] }
|
40
|
+
|
41
|
+
def index
|
42
|
+
now = Time.now.strftime('%Y%j')
|
43
|
+
|
44
|
+
comments = Comment.recent.partition do |comment|
|
45
|
+
comment.created_at('%Y%j') == now
|
46
|
+
end
|
47
|
+
|
48
|
+
@title = 'Recent Comments'
|
49
|
+
@today, @ancient = comments
|
50
|
+
end
|
51
|
+
|
52
|
+
def atom
|
53
|
+
response['Content-Type'] = 'application/atom+xml'
|
54
|
+
|
55
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
56
|
+
x.instruct!
|
57
|
+
|
58
|
+
x.feed(:xmlns => 'http://www.w3.org/2005/Atom') {
|
59
|
+
comments_url = Config.site['url'].chomp('/') + rs().to_s
|
60
|
+
|
61
|
+
x.id comments_url
|
62
|
+
x.title "#{Config.site['name']} Recent Comments"
|
63
|
+
x.subtitle Config.site['desc']
|
64
|
+
x.updated Time.now.xmlschema # TODO: use modification time of the last post
|
65
|
+
x.link :href => comments_url
|
66
|
+
x.link :href => Config.site['url'].chomp('/') + rs(:atom).to_s,
|
67
|
+
:rel => 'self'
|
68
|
+
|
69
|
+
Comment.recent.all.each do |comment|
|
70
|
+
x.entry {
|
71
|
+
x.id comment.url
|
72
|
+
x.title comment.title.empty? ? comment.summary : comment.title
|
73
|
+
x.published comment.created_at.xmlschema
|
74
|
+
x.updated comment.updated_at.xmlschema
|
75
|
+
x.link :href => comment.url, :rel => 'alternate'
|
76
|
+
x.content comment.body_rendered, :type => 'html'
|
77
|
+
|
78
|
+
x.author {
|
79
|
+
x.name comment.author
|
80
|
+
|
81
|
+
if comment.author_url && !comment.author_url.empty?
|
82
|
+
x.uri comment.author_url
|
83
|
+
end
|
84
|
+
}
|
85
|
+
}
|
86
|
+
end
|
87
|
+
}
|
88
|
+
|
89
|
+
throw(:respond, x.target!)
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete(id = nil)
|
93
|
+
require_auth
|
94
|
+
|
95
|
+
error_404 unless id && @comment = Comment[id]
|
96
|
+
|
97
|
+
if request.post?
|
98
|
+
error_403 unless form_token_valid?
|
99
|
+
|
100
|
+
comment_url = @comment.url
|
101
|
+
|
102
|
+
if request[:confirm] == 'yes'
|
103
|
+
@comment.deleted = true
|
104
|
+
|
105
|
+
if @comment.save(:changed => true, :validate => false)
|
106
|
+
Ramaze::Cache.action.clear
|
107
|
+
Ramaze::Cache.cache_helper_value.clear
|
108
|
+
flash[:success] = 'Comment deleted.'
|
109
|
+
else
|
110
|
+
flash[:error] = 'Error deleting comment.'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
redirect(comment_url)
|
115
|
+
end
|
116
|
+
|
117
|
+
@title = "Delete Comment: #{@comment.title}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def list(page = 1)
|
121
|
+
require_auth
|
122
|
+
|
123
|
+
page = page.to_i
|
124
|
+
|
125
|
+
@columns = [:id, :title, :author, :created_at, :deleted]
|
126
|
+
@order = (request[:order] || :desc).to_sym
|
127
|
+
@sort = (request[:sort] || :created_at).to_sym
|
128
|
+
@sort = :created_at unless @columns.include?(@sort)
|
129
|
+
@sort_url = rs(:list, page)
|
130
|
+
|
131
|
+
@comments = Comment.order(@order == :desc ? Sequel.desc(@sort) : @sort).paginate(page, 20)
|
132
|
+
@title = "Comments (page #{page} of #{[@comments.page_count, 1].max})"
|
133
|
+
@pager = pager(@comments, rs(:list, '__page__', :sort => @sort, :order => @order))
|
134
|
+
end
|
135
|
+
|
136
|
+
def rss
|
137
|
+
response['Content-Type'] = 'application/rss+xml'
|
138
|
+
|
139
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
140
|
+
x.instruct!
|
141
|
+
|
142
|
+
x.rss(:version => '2.0',
|
143
|
+
'xmlns:atom' => 'http://www.w3.org/2005/Atom',
|
144
|
+
'xmlns:dc' => 'http://purl.org/dc/elements/1.1/') {
|
145
|
+
x.channel {
|
146
|
+
x.title "#{Config.site['name']} Recent Comments"
|
147
|
+
x.link Config.site['url']
|
148
|
+
x.description Config.site['desc']
|
149
|
+
x.managingEditor "#{Config.admin['email']} (#{Config.admin['name']})"
|
150
|
+
x.webMaster "#{Config.admin['email']} (#{Config.admin['name']})"
|
151
|
+
x.docs 'http://backend.userland.com/rss/'
|
152
|
+
x.ttl 30
|
153
|
+
x.atom :link, :rel => 'self',
|
154
|
+
:type => 'application/rss+xml',
|
155
|
+
:href => Config.site['url'].chomp('/') + rs(:rss).to_s
|
156
|
+
|
157
|
+
Comment.recent.all.each do |comment|
|
158
|
+
x.item {
|
159
|
+
x.title comment.title.empty? ? comment.summary : comment.title
|
160
|
+
x.link comment.url
|
161
|
+
x.dc :creator, comment.author
|
162
|
+
x.guid comment.url, :isPermaLink => 'true'
|
163
|
+
x.pubDate comment.created_at.rfc2822
|
164
|
+
x.description comment.body_rendered
|
165
|
+
}
|
166
|
+
end
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
throw(:respond, x.target!)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,193 @@
|
|
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 MainController < Controller
|
32
|
+
map '/'
|
33
|
+
map_views '/thoth'
|
34
|
+
helper :cache, :pagination
|
35
|
+
|
36
|
+
cache_action(:method => :index, :ttl => 60) do
|
37
|
+
auth_key_valid?.to_s + (request[:type] || '') + flash.inspect
|
38
|
+
end
|
39
|
+
|
40
|
+
cache_action(:method => :atom, :ttl => 120)
|
41
|
+
cache_action(:method => :rss, :ttl => 120)
|
42
|
+
cache_action(:method => :sitemap, :ttl => 3600)
|
43
|
+
|
44
|
+
def index
|
45
|
+
# Check for legacy feed requests and redirect if necessary.
|
46
|
+
if type = request[:type]
|
47
|
+
redirect rs(type), :status => 301
|
48
|
+
end
|
49
|
+
|
50
|
+
@title = Config.site['name']
|
51
|
+
@posts = Post.recent
|
52
|
+
@pager = pager(@posts, rs(:archive, '__page__'))
|
53
|
+
end
|
54
|
+
|
55
|
+
def atom
|
56
|
+
response['Content-Type'] = 'application/atom+xml'
|
57
|
+
|
58
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
59
|
+
x.instruct!
|
60
|
+
|
61
|
+
x.feed(:xmlns => 'http://www.w3.org/2005/Atom') {
|
62
|
+
x.id Config.site['url']
|
63
|
+
x.title Config.site['name']
|
64
|
+
x.subtitle Config.site['desc']
|
65
|
+
x.updated Time.now.xmlschema # TODO: use modification time of the last post
|
66
|
+
x.link :href => Config.site['url']
|
67
|
+
x.link :href => Config.site['url'].chomp('/') + rs(:atom).to_s,
|
68
|
+
:rel => 'self'
|
69
|
+
|
70
|
+
x.author {
|
71
|
+
x.name Config.admin['name']
|
72
|
+
x.email Config.admin['email']
|
73
|
+
x.uri Config.site['url']
|
74
|
+
}
|
75
|
+
|
76
|
+
Post.recent.all.each do |post|
|
77
|
+
x.entry {
|
78
|
+
x.id post.url
|
79
|
+
x.title post.title
|
80
|
+
x.published post.created_at.xmlschema
|
81
|
+
x.updated post.updated_at.xmlschema
|
82
|
+
x.link :href => post.url, :rel => 'alternate'
|
83
|
+
x.content post.body_rendered, :type => 'html'
|
84
|
+
|
85
|
+
post.tags.each do |tag|
|
86
|
+
x.category :term => tag.name, :label => tag.name,
|
87
|
+
:scheme => tag.url
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
91
|
+
}
|
92
|
+
|
93
|
+
throw(:respond, x.target!)
|
94
|
+
end
|
95
|
+
|
96
|
+
def rss
|
97
|
+
response['Content-Type'] = 'application/rss+xml'
|
98
|
+
|
99
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
100
|
+
x.instruct!
|
101
|
+
|
102
|
+
x.rss(:version => '2.0',
|
103
|
+
'xmlns:atom' => 'http://www.w3.org/2005/Atom') {
|
104
|
+
x.channel {
|
105
|
+
x.title Config.site['name']
|
106
|
+
x.link Config.site['url']
|
107
|
+
x.description Config.site['desc']
|
108
|
+
x.managingEditor "#{Config.admin['email']} (#{Config.admin['name']})"
|
109
|
+
x.webMaster "#{Config.admin['email']} (#{Config.admin['name']})"
|
110
|
+
x.docs 'http://backend.userland.com/rss/'
|
111
|
+
x.ttl 60
|
112
|
+
x.atom :link, :rel => 'self',
|
113
|
+
:type => 'application/rss+xml',
|
114
|
+
:href => Config.site['url'].chomp('/') + rs(:rss).to_s
|
115
|
+
|
116
|
+
Post.recent.all.each do |post|
|
117
|
+
x.item {
|
118
|
+
x.title post.title
|
119
|
+
x.link post.url
|
120
|
+
x.guid post.url, :isPermaLink => 'true'
|
121
|
+
x.pubDate post.created_at.rfc2822
|
122
|
+
x.description post.body_rendered
|
123
|
+
|
124
|
+
post.tags.each do |tag|
|
125
|
+
x.category tag.name, :domain => tag.url
|
126
|
+
end
|
127
|
+
}
|
128
|
+
end
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
throw(:respond, x.target!)
|
133
|
+
end
|
134
|
+
|
135
|
+
def sitemap
|
136
|
+
error_404 unless Config.site['enable_sitemap']
|
137
|
+
|
138
|
+
response['Content-Type'] = 'text/xml'
|
139
|
+
|
140
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
141
|
+
x.instruct!
|
142
|
+
|
143
|
+
x.urlset(:xmlns => 'http://www.sitemaps.org/schemas/sitemap/0.9') {
|
144
|
+
x.url {
|
145
|
+
x.loc Config.site['url']
|
146
|
+
x.changefreq 'hourly'
|
147
|
+
x.priority '1.0'
|
148
|
+
}
|
149
|
+
|
150
|
+
Page.reverse_order(:updated_at).all do |page|
|
151
|
+
x.url {
|
152
|
+
x.loc page.url
|
153
|
+
x.lastmod page.updated_at.xmlschema
|
154
|
+
x.changefreq 'weekly'
|
155
|
+
x.priority '0.7'
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
Post.filter(:is_draft => false).reverse_order(:updated_at).all do |post|
|
160
|
+
x.url {
|
161
|
+
x.loc post.url
|
162
|
+
x.lastmod post.updated_at.xmlschema
|
163
|
+
x.changefreq 'weekly'
|
164
|
+
x.priority '0.6'
|
165
|
+
}
|
166
|
+
end
|
167
|
+
}
|
168
|
+
|
169
|
+
throw(:respond, x.target!)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Legacy redirect to /archive/+page+.
|
173
|
+
def archives(page = 1)
|
174
|
+
redirect ArchiveController.r(:/, page), :status => 301
|
175
|
+
end
|
176
|
+
|
177
|
+
# Legacy redirect to /post/+name+.
|
178
|
+
def article(name)
|
179
|
+
redirect PostController.r(:/, name), :status => 301
|
180
|
+
end
|
181
|
+
|
182
|
+
# Legacy redirect to /comment.
|
183
|
+
def comments
|
184
|
+
if type = request[:type]
|
185
|
+
redirect CommentController.r(:/, type), :status => 301
|
186
|
+
else
|
187
|
+
redirect CommentController.r(), :status => 301
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
alias_method 'recent-comments', :comments
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,172 @@
|
|
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
|
+
require 'rack/utils'
|
31
|
+
|
32
|
+
module Thoth
|
33
|
+
class MediaController < Controller
|
34
|
+
map '/media'
|
35
|
+
helper :pagination
|
36
|
+
|
37
|
+
def index(filename = nil)
|
38
|
+
error_404 unless filename && file = Media[:filename => filename.strip]
|
39
|
+
|
40
|
+
send_media(file.path)
|
41
|
+
|
42
|
+
rescue Errno::ENOENT => e
|
43
|
+
error_404
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(id = nil)
|
47
|
+
require_auth
|
48
|
+
|
49
|
+
error_404 unless id && @file = Media[id]
|
50
|
+
|
51
|
+
if request.post?
|
52
|
+
error_403 unless form_token_valid?
|
53
|
+
|
54
|
+
if request[:confirm] == 'yes'
|
55
|
+
@file.destroy
|
56
|
+
flash[:success] = 'File deleted.'
|
57
|
+
redirect(rs(:list))
|
58
|
+
else
|
59
|
+
redirect(rs(:edit, id))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@title = "Delete File: #{@file.filename}"
|
64
|
+
@delete = true
|
65
|
+
@show_file_edit = true
|
66
|
+
end
|
67
|
+
|
68
|
+
def edit(id = nil)
|
69
|
+
require_auth
|
70
|
+
|
71
|
+
redirect(rs(:new)) unless id && @file = Media[id]
|
72
|
+
|
73
|
+
@title = "Edit Media - #{@file.filename}"
|
74
|
+
@form_action = rs(:edit, id).to_s
|
75
|
+
@show_file_edit = true
|
76
|
+
|
77
|
+
if request.post?
|
78
|
+
error_403 unless form_token_valid?
|
79
|
+
|
80
|
+
tempfile, filename, type = request[:file].values_at(
|
81
|
+
:tempfile, :filename, :type)
|
82
|
+
|
83
|
+
@file.mimetype = type || 'application/octet-stream'
|
84
|
+
|
85
|
+
begin
|
86
|
+
unless File.directory?(File.dirname(@file.path))
|
87
|
+
FileUtils.mkdir_p(File.dirname(@file.path))
|
88
|
+
end
|
89
|
+
|
90
|
+
FileUtils.mv(tempfile.path, @file.path)
|
91
|
+
@file.save
|
92
|
+
|
93
|
+
flash[:success] = 'File saved.'
|
94
|
+
redirect(rs(:edit, id))
|
95
|
+
rescue => e
|
96
|
+
@media_error = "Error: #{e}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def list(page = 1)
|
102
|
+
require_auth
|
103
|
+
|
104
|
+
page = page.to_i
|
105
|
+
|
106
|
+
@columns = [:filename, :size, :created_at, :updated_at]
|
107
|
+
@order = (request[:order] || :asc).to_sym
|
108
|
+
@sort = (request[:sort] || :filename).to_sym
|
109
|
+
@sort = :created_at unless @columns.include?(@sort)
|
110
|
+
@sort_url = rs(:list, page)
|
111
|
+
|
112
|
+
@files = Media.order(@order == :desc ? Sequel.desc(@sort) : @sort).paginate(page, 20)
|
113
|
+
|
114
|
+
@title = "Media (page #{page} of #{[@files.page_count, 1].max})"
|
115
|
+
@pager = pager(@files, rs(:list, '__page__', :sort => @sort, :order => @order))
|
116
|
+
end
|
117
|
+
|
118
|
+
def new
|
119
|
+
require_auth
|
120
|
+
|
121
|
+
@title = "Upload Media"
|
122
|
+
@form_action = rs(:new).to_s
|
123
|
+
|
124
|
+
if request.post?
|
125
|
+
error_403 unless form_token_valid?
|
126
|
+
|
127
|
+
tempfile, filename, type = request[:file].values_at(
|
128
|
+
:tempfile, :filename, :type)
|
129
|
+
|
130
|
+
# Ensure that the filename is a name only and not a full path, since
|
131
|
+
# certain browsers are stupid (I'm looking at you, IE).
|
132
|
+
filename = filename[/([^\/\\]+)$/].strip
|
133
|
+
|
134
|
+
if filename.empty?
|
135
|
+
return @media_error = 'Error: Invalid filename.'
|
136
|
+
end
|
137
|
+
|
138
|
+
file = Media.new do |f|
|
139
|
+
f.filename = filename
|
140
|
+
f.mimetype = type || 'application/octet-stream'
|
141
|
+
end
|
142
|
+
|
143
|
+
begin
|
144
|
+
unless File.directory?(File.dirname(file.path))
|
145
|
+
FileUtils.mkdir_p(File.dirname(file.path))
|
146
|
+
end
|
147
|
+
|
148
|
+
FileUtils.mv(tempfile.path, file.path)
|
149
|
+
file.save
|
150
|
+
|
151
|
+
flash[:success] = 'File uploaded.'
|
152
|
+
redirect(rs(:edit, file.id))
|
153
|
+
rescue => e
|
154
|
+
@media_error = "Error: #{e}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def send_media(filename, content_type = nil)
|
162
|
+
# This should eventually be eliminated in favor of using the frontend
|
163
|
+
# server to send files directly without passing through Thoth/Ramaze.
|
164
|
+
|
165
|
+
respond!(::File.open(filename, 'rb'), 200,
|
166
|
+
'Content-Length' => ::File.size(filename).to_s,
|
167
|
+
'Content-Type' => content_type || Rack::Mime.mime_type(::File.extname(filename))
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,167 @@
|
|
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 PageController < Controller
|
32
|
+
map '/page'
|
33
|
+
helper :cache, :pagination, :wiki
|
34
|
+
|
35
|
+
cache_action(:method => :index, :ttl => 120) { auth_key_valid? }
|
36
|
+
|
37
|
+
def index(name = nil)
|
38
|
+
error_404 unless name && @page = Page[:name => name.strip.downcase]
|
39
|
+
|
40
|
+
@title = @page.title
|
41
|
+
@show_page_edit = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete(id = nil)
|
45
|
+
require_auth
|
46
|
+
|
47
|
+
error_404 unless id && @page = Page[id]
|
48
|
+
|
49
|
+
if request.post?
|
50
|
+
error_403 unless form_token_valid?
|
51
|
+
|
52
|
+
if request[:confirm] == 'yes'
|
53
|
+
@page.destroy
|
54
|
+
Ramaze::Cache.action.clear
|
55
|
+
flash[:success] = 'Page deleted.'
|
56
|
+
redirect(MainController.r())
|
57
|
+
else
|
58
|
+
redirect(@page.url)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
@title = "Delete Page: #{@page.title}"
|
63
|
+
@show_page_edit = true
|
64
|
+
end
|
65
|
+
|
66
|
+
def edit(id = nil)
|
67
|
+
require_auth
|
68
|
+
|
69
|
+
unless @page = Page[id]
|
70
|
+
flash[:error] = 'Invalid page id.'
|
71
|
+
redirect(rs(:new))
|
72
|
+
end
|
73
|
+
|
74
|
+
if request.post?
|
75
|
+
error_403 unless form_token_valid?
|
76
|
+
|
77
|
+
@page.name = request[:name]
|
78
|
+
@page.title = request[:title]
|
79
|
+
@page.body = request[:body]
|
80
|
+
|
81
|
+
if @page.valid? && request[:action] == 'Post'
|
82
|
+
begin
|
83
|
+
raise unless @page.save
|
84
|
+
rescue => e
|
85
|
+
@page_error = "There was an error saving your page: #{e}"
|
86
|
+
else
|
87
|
+
Ramaze::Cache.action.clear
|
88
|
+
flash[:success] = 'Page saved.'
|
89
|
+
redirect(rs(@page.name))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
@title = "Edit page - #{@page.title}"
|
95
|
+
@form_action = rs(:edit, id)
|
96
|
+
@show_page_edit = true
|
97
|
+
end
|
98
|
+
|
99
|
+
def list(page = 1)
|
100
|
+
require_auth
|
101
|
+
|
102
|
+
# If this is a POST request, set page display positions.
|
103
|
+
if request.post? && !request[:position].nil? &&
|
104
|
+
request[:position].is_a?(Hash)
|
105
|
+
|
106
|
+
error_403 unless form_token_valid?
|
107
|
+
|
108
|
+
Page.normalize_positions
|
109
|
+
|
110
|
+
Page.order(:position).all do |p|
|
111
|
+
unless request[:position][p.id.to_s].nil? ||
|
112
|
+
request[:position][p.id.to_s].to_i == p.position
|
113
|
+
Page.set_position(p, request[:position][p.id.to_s].to_i)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
Page.normalize_positions
|
118
|
+
end
|
119
|
+
|
120
|
+
page = page.to_i
|
121
|
+
|
122
|
+
@columns = [:name, :title, :created_at, :updated_at, :position]
|
123
|
+
@order = (request[:order] || :asc).to_sym
|
124
|
+
@sort = (request[:sort] || :display_order).to_sym
|
125
|
+
@sort = :position unless @columns.include?(@sort)
|
126
|
+
@sort_url = rs(:list, page)
|
127
|
+
|
128
|
+
@pages = Page.order(@order == :desc ? Sequel.desc(@sort) : @sort).paginate(page, 20)
|
129
|
+
|
130
|
+
@title = "Pages (page #{page} of #{[@pages.page_count, 1].max})"
|
131
|
+
@pager = pager(@pages, rs(:list, '__page__', :sort => @sort, :order => @order))
|
132
|
+
@form_action = rs(:list)
|
133
|
+
end
|
134
|
+
|
135
|
+
def new
|
136
|
+
require_auth
|
137
|
+
|
138
|
+
@title = "New page - Untitled"
|
139
|
+
@form_action = rs(:new)
|
140
|
+
|
141
|
+
if request.post?
|
142
|
+
error_403 unless form_token_valid?
|
143
|
+
|
144
|
+
@page = Page.new do |p|
|
145
|
+
p.name = request[:name]
|
146
|
+
p.title = request[:title]
|
147
|
+
p.body = request[:body]
|
148
|
+
p.position = Page.dataset.max(:position).to_i + 1
|
149
|
+
end
|
150
|
+
|
151
|
+
if @page.valid? && request[:action] == 'Post'
|
152
|
+
begin
|
153
|
+
raise unless @page.save
|
154
|
+
rescue => e
|
155
|
+
@page_error = "There was an error saving your page: #{e}"
|
156
|
+
else
|
157
|
+
Ramaze::Cache.action.clear
|
158
|
+
flash[:success] = 'Page created.'
|
159
|
+
redirect(rs(@page.name))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
@title = "New page - #{@page.title}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|