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,98 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010 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
|
+
require 'cgi'
|
30
|
+
require 'json'
|
31
|
+
require 'open-uri'
|
32
|
+
require 'timeout'
|
33
|
+
|
34
|
+
module Thoth; module Plugin
|
35
|
+
|
36
|
+
# Pinboard plugin for Thoth.
|
37
|
+
module Pinboard
|
38
|
+
FEED_URL = 'http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20rss%20where%20url%3D%22http%3A%2F%2Ffeeds.pinboard.in%2Frss%2Fu%3A{username}%22%20limit%20{limit}&format=json'
|
39
|
+
|
40
|
+
Config << {
|
41
|
+
'pinboard' => {
|
42
|
+
|
43
|
+
# Time in seconds to cache results.
|
44
|
+
'cache_ttl' => 900,
|
45
|
+
|
46
|
+
# Request timeout in seconds.
|
47
|
+
'request_timeout' => 5
|
48
|
+
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
class << self
|
53
|
+
|
54
|
+
# Gets recent Pinboard bookmarks for the specified _username_. The
|
55
|
+
# return value of this method is cached to improve performance.
|
56
|
+
#
|
57
|
+
# Available options:
|
58
|
+
# [<tt>:count</tt>] Number of bookmarks to return (default is 5)
|
59
|
+
#
|
60
|
+
def recent_bookmarks(username, options = {})
|
61
|
+
cache = Ramaze::Cache.plugin
|
62
|
+
options = {:count => 5}.merge(options)
|
63
|
+
request = FEED_URL.gsub('{username}', ::CGI.escape(username)).
|
64
|
+
gsub('{limit}', options[:count].to_s)
|
65
|
+
|
66
|
+
if value = cache[request]
|
67
|
+
return value
|
68
|
+
end
|
69
|
+
|
70
|
+
response = []
|
71
|
+
|
72
|
+
Timeout.timeout(Config.pinboard['request_timeout'], StandardError) do
|
73
|
+
response = JSON.parse(open(request).read)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Parse the response into a more friendly format.
|
77
|
+
data = []
|
78
|
+
|
79
|
+
response['query']['results']['item'].each do |item|
|
80
|
+
data << {
|
81
|
+
:url => item['link'],
|
82
|
+
:title => item['title'].strip,
|
83
|
+
:note => (item['description'] || '').strip,
|
84
|
+
:tags => (item['subject'] || '').strip.split(' ')
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
return cache.store(request, data, :ttl => Config.pinboard['cache_ttl'])
|
89
|
+
|
90
|
+
rescue => e
|
91
|
+
Ramaze::Log.error "Thoth::Plugin::Pinboard: #{e.message}"
|
92
|
+
return []
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end; end
|
@@ -0,0 +1,68 @@
|
|
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; module Plugin
|
30
|
+
|
31
|
+
# Tags plugin for Thoth.
|
32
|
+
module Tags
|
33
|
+
|
34
|
+
Config << {
|
35
|
+
'tags' => {
|
36
|
+
|
37
|
+
# Time in seconds to cache tag data. It's a good idea to keep this nice
|
38
|
+
# and high to improve the performance of your site. Default is 1800
|
39
|
+
# seconds (30 minutes).
|
40
|
+
'cache_ttl' => 1800
|
41
|
+
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
class << self
|
46
|
+
|
47
|
+
# Gets an Array of the most heavily-used tags. The first element of the
|
48
|
+
# array is a Tag object, the second element is the number of times it's
|
49
|
+
# used.
|
50
|
+
def top_tags(limit = 10)
|
51
|
+
cache = Ramaze::Cache.plugin
|
52
|
+
|
53
|
+
if tags = cache["top_tags_#{limit}"]
|
54
|
+
return tags
|
55
|
+
end
|
56
|
+
|
57
|
+
tags = []
|
58
|
+
tag_ids = TagsPostsMap.group_and_count(:tag_id).reverse_order(:count).
|
59
|
+
limit(limit)
|
60
|
+
|
61
|
+
tag_ids.all {|row| tags << [Tag[row[:tag_id]], row[:count]] }
|
62
|
+
cache.store("top_tags_#{limit}", tags, :ttl => Config.tags['cache_ttl'])
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end; end
|
@@ -0,0 +1,175 @@
|
|
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
|
+
require 'cgi'
|
30
|
+
require 'json'
|
31
|
+
require 'open-uri'
|
32
|
+
require 'timeout'
|
33
|
+
require 'uri'
|
34
|
+
|
35
|
+
module Thoth; module Plugin
|
36
|
+
|
37
|
+
# Twitter plugin for Thoth.
|
38
|
+
module Twitter
|
39
|
+
|
40
|
+
Config << {
|
41
|
+
'twitter' => {
|
42
|
+
|
43
|
+
# Whether or not to include replies. If this is false, the most recent
|
44
|
+
# non-reply tweets will be displayed.
|
45
|
+
'include_replies' => false,
|
46
|
+
|
47
|
+
# Time in seconds to cache results. It's a good idea to keep this nice
|
48
|
+
# and high both to improve the performance of your site and to avoid
|
49
|
+
# pounding on Twitter's servers. Default is 600 seconds (10 minutes).
|
50
|
+
'cache_ttl' => 600,
|
51
|
+
|
52
|
+
# Request timeout in seconds.
|
53
|
+
'request_timeout' => 3,
|
54
|
+
|
55
|
+
# If Twitter fails to respond at least this many times in a row, no new
|
56
|
+
# requests will be sent until the failure_timeout expires in order to
|
57
|
+
# avoid hindering your blog's performance.
|
58
|
+
'failure_threshold' => 3,
|
59
|
+
|
60
|
+
# After the failure_threshold is reached, the plugin will wait this many
|
61
|
+
# seconds before trying again. Default is 600 seconds (10 minutes).
|
62
|
+
'failure_timeout' => 600
|
63
|
+
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
class << self
|
68
|
+
|
69
|
+
# Parses tweet text and converts it into HTML. Explicit URLs and @username
|
70
|
+
# or #hashtag references will be turned into links.
|
71
|
+
def parse_tweet(tweet)
|
72
|
+
index = 0
|
73
|
+
html = tweet.dup
|
74
|
+
protocols = ['ftp', 'ftps', 'git', 'http', 'https', 'mailto', 'scp',
|
75
|
+
'sftp', 'ssh', 'telnet']
|
76
|
+
urls = []
|
77
|
+
|
78
|
+
# Extract URLs and replace them with placeholders for later.
|
79
|
+
URI.extract(html.dup, protocols) do |url|
|
80
|
+
html.sub!(url, "__URL#{index}__")
|
81
|
+
urls << url
|
82
|
+
index += 1
|
83
|
+
end
|
84
|
+
|
85
|
+
# Replace URL placeholders with links.
|
86
|
+
urls.each_with_index do |url, index|
|
87
|
+
html.sub!("__URL#{index}__", "<a href=\"#{url}\">" <<
|
88
|
+
"#{url.length > 26 ? url[0..26] + '...' : url}</a>")
|
89
|
+
end
|
90
|
+
|
91
|
+
# Turn @username into a link to the specified user's Twitter profile.
|
92
|
+
html.gsub!(/@([a-zA-Z0-9_]{1,16})([^a-zA-Z0-9_])?/,
|
93
|
+
'@<a href="http://twitter.com/\1">\1</a>\2')
|
94
|
+
|
95
|
+
# Turn #hashtags into links.
|
96
|
+
html.gsub!(/#([a-zA-Z0-9_]{1,32})([^a-zA-Z0-9_])?/,
|
97
|
+
'<a href="http://search.twitter.com/search?q=%23\1">#\1</a>\2')
|
98
|
+
|
99
|
+
return html
|
100
|
+
end
|
101
|
+
|
102
|
+
# Gets a Hash containing recent tweets for the specified _user_. The only
|
103
|
+
# valid option currently is <code>:count</code>, which specifies the
|
104
|
+
# maximum number of tweets that should be returned.
|
105
|
+
def recent_tweets(user, options = {})
|
106
|
+
if @skip_until
|
107
|
+
return [] if @skip_until > Time.now
|
108
|
+
@skip_until = nil
|
109
|
+
end
|
110
|
+
|
111
|
+
cache = Ramaze::Cache.plugin
|
112
|
+
options = {:count => 5}.merge(options)
|
113
|
+
count = options[:count].to_i
|
114
|
+
|
115
|
+
count += 10 unless Config.twitter['include_replies']
|
116
|
+
count = 200 if count > 200
|
117
|
+
|
118
|
+
url = "http://twitter.com/statuses/user_timeline/#{user}.json?count=" <<
|
119
|
+
count.to_s
|
120
|
+
|
121
|
+
if value = cache[url]
|
122
|
+
return value
|
123
|
+
end
|
124
|
+
|
125
|
+
tweets = []
|
126
|
+
|
127
|
+
Timeout.timeout(Config.twitter['request_timeout'], StandardError) do
|
128
|
+
tweets = JSON.parse(open(url).read)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Weed out replies if necessary.
|
132
|
+
unless Config.twitter['include_replies']
|
133
|
+
tweets.delete_if do |tweet|
|
134
|
+
!tweet['in_reply_to_status_id'].nil? ||
|
135
|
+
!tweet['in_reply_to_user_id'].nil?
|
136
|
+
end
|
137
|
+
|
138
|
+
tweets = tweets.slice(0, options[:count].to_i)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Parse the tweets into an easier-to-use format.
|
142
|
+
tweets.map! do |tweet|
|
143
|
+
{
|
144
|
+
:created_at => Time.parse(tweet['created_at']),
|
145
|
+
:html => parse_tweet(tweet['text']),
|
146
|
+
:id => tweet['id'],
|
147
|
+
:source => tweet['source'],
|
148
|
+
:text => tweet['text'],
|
149
|
+
:truncated => tweet['truncated'],
|
150
|
+
:url => "http://twitter.com/#{user}/statuses/#{tweet['id']}"
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
@failures = 0
|
155
|
+
|
156
|
+
return cache.store(url, tweets, :ttl => Config.twitter['cache_ttl'])
|
157
|
+
|
158
|
+
rescue => e
|
159
|
+
Ramaze::Log.error "Thoth::Plugin::Twitter: #{e.message}"
|
160
|
+
|
161
|
+
@failures ||= 0
|
162
|
+
@failures += 1
|
163
|
+
|
164
|
+
if @failures >= Config.twitter['failure_threshold']
|
165
|
+
@skip_until = Time.now + Config.twitter['failure_timeout']
|
166
|
+
Ramaze::Log.error "Thoth::Plugin::Twitter: Twitter failed to respond #{@failures} times. Will retry after #{@skip_until}."
|
167
|
+
end
|
168
|
+
|
169
|
+
return []
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end; end
|
data/lib/thoth/plugin.rb
ADDED
@@ -0,0 +1,59 @@
|
|
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
|
+
|
32
|
+
# Namespace for Thoth plugins. See
|
33
|
+
# http://wiki.github.com/pagojo/rethoth/creating-plugins for more info on
|
34
|
+
# creating and using plugins.
|
35
|
+
module Plugin
|
36
|
+
def self.const_missing(name)
|
37
|
+
self.load(name)
|
38
|
+
self.const_get(name)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Attempts to load the specified plugin, first from the project's
|
42
|
+
# <tt>/plugin</tt> directory, then from Thoth's and then as a gem.
|
43
|
+
def self.load(name)
|
44
|
+
plugin = "thoth_#{name.to_s.downcase.gsub(/^thoth_/, '')}"
|
45
|
+
files = Dir["{#{HOME_DIR}/plugin,#{LIB_DIR}/plugin," <<
|
46
|
+
"#{$:.join(',')}}/#{plugin}.rb"]
|
47
|
+
|
48
|
+
# First try to load a local copy of the plugin, then try the gem.
|
49
|
+
unless (files.any? && require(files.first)) || require(plugin)
|
50
|
+
raise LoadError, "Thoth::Plugin::#{name} not found"
|
51
|
+
end
|
52
|
+
|
53
|
+
Ramaze::Log.info "Loaded plugin: #{plugin}"
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
/* -- Icons ----------------------------------------------------------------- */
|
2
|
+
.adminicon {
|
3
|
+
background: url(../images/admin-sprite.png) no-repeat;
|
4
|
+
display: -moz-inline-box;
|
5
|
+
display: inline-block;
|
6
|
+
height: 16px;
|
7
|
+
vertical-align: middle;
|
8
|
+
width: 16px;
|
9
|
+
}
|
10
|
+
|
11
|
+
.adminicon.comment-delete { background-position: -180px 0; }
|
12
|
+
.adminicon.comment-list { background-position: -196px 0; }
|
13
|
+
.adminicon.logout { background-position: -213px 0; }
|
14
|
+
.adminicon.media-delete { background-position: -147px 0; }
|
15
|
+
.adminicon.media-list { background-position: -164px 0; }
|
16
|
+
.adminicon.media-new { background-position: -130px 0; }
|
17
|
+
.adminicon.page-delete { background-position: -97px 0; }
|
18
|
+
.adminicon.page-edit { background-position: -81px 0; }
|
19
|
+
.adminicon.page-list { background-position: -113px 0; }
|
20
|
+
.adminicon.page-new { background-position: -65px 0; }
|
21
|
+
.adminicon.post-delete { background-position: -32px 0; }
|
22
|
+
.adminicon.post-edit { background-position: -16px 0; }
|
23
|
+
.adminicon.post-list { background-position: -48px 0; }
|
24
|
+
|
25
|
+
.adminicon.sort {
|
26
|
+
height: 5px;
|
27
|
+
width: 7px;
|
28
|
+
}
|
29
|
+
|
30
|
+
.adminicon.sort.asc { background-position: -230px 0; }
|
31
|
+
.adminicon.sort.desc { background-position: -230px -6px; }
|
32
|
+
|
33
|
+
/* -- Toolbar Login Form ---------------------------------------------------- */
|
34
|
+
#adminToolbar form {
|
35
|
+
float: right;
|
36
|
+
margin: 0;
|
37
|
+
}
|
38
|
+
|
39
|
+
#adminToolbar form div { display: inline; }
|
40
|
+
|
41
|
+
/* -- Toolbar Icons --------------------------------------------------------- */
|
42
|
+
.toolbar {
|
43
|
+
background: #f5f5f5;
|
44
|
+
border: 1px solid #dfdfdf;
|
45
|
+
padding: 3px 4px 4px 4px;
|
46
|
+
}
|
47
|
+
|
48
|
+
.toolbar ul {
|
49
|
+
float: left;
|
50
|
+
margin: 0;
|
51
|
+
padding: 0 6px;
|
52
|
+
}
|
53
|
+
|
54
|
+
.toolbar ul.edit { padding-left: 0; }
|
55
|
+
.toolbar ul.tools { border-left: 1px solid #dfdfdf; }
|
56
|
+
|
57
|
+
.toolbar ul:after {
|
58
|
+
clear: left;
|
59
|
+
content: '.';
|
60
|
+
display: block;
|
61
|
+
height: 0;
|
62
|
+
visibility: hidden;
|
63
|
+
}
|
64
|
+
|
65
|
+
.toolbar ul li {
|
66
|
+
float: left;
|
67
|
+
list-style: none;
|
68
|
+
margin-left: 1px;
|
69
|
+
}
|
70
|
+
|
71
|
+
.toolbar ul li:first-child { margin-left: 0; }
|
72
|
+
|
73
|
+
.toolbar a {
|
74
|
+
border: 1px solid transparent;
|
75
|
+
cursor: pointer;
|
76
|
+
display: block;
|
77
|
+
padding: 3px;
|
78
|
+
}
|
79
|
+
|
80
|
+
.toolbar a:hover {
|
81
|
+
background: #dfebff;
|
82
|
+
border: 1px solid #0A46AF;
|
83
|
+
}
|
84
|
+
|
85
|
+
.toolbar a img {
|
86
|
+
border: 0;
|
87
|
+
height: 16px;
|
88
|
+
width: 16px;
|
89
|
+
}
|
90
|
+
|
91
|
+
@media print {
|
92
|
+
.toolbar { display: none; }
|
93
|
+
}
|
94
|
+
|
95
|
+
/* -- Mini-Toolbar ---------------------------------------------------------- */
|
96
|
+
.toolbar.mini {
|
97
|
+
background: none;
|
98
|
+
border: 0;
|
99
|
+
padding: 0;
|
100
|
+
}
|
101
|
+
|
102
|
+
/* -- Comment Mini-Toolbar -------------------------------------------------- */
|
103
|
+
.comment { position: relative; }
|
104
|
+
|
105
|
+
.comment .toolbar {
|
106
|
+
position: absolute;
|
107
|
+
right: -3px;
|
108
|
+
top: 3px;
|
109
|
+
}
|
110
|
+
|
111
|
+
/* -- Misc. Forms ----------------------------------------------------------- */
|
112
|
+
fieldset.default {
|
113
|
+
border: 0;
|
114
|
+
padding: 0;
|
115
|
+
}
|
116
|
+
|
117
|
+
form div.field { margin-bottom: 1em; }
|
118
|
+
form div.field label {
|
119
|
+
display: block;
|
120
|
+
font-weight: bold;
|
121
|
+
}
|
122
|
+
|
123
|
+
form p.buttons { margin: 0.5em 0 1em; }
|
124
|
+
|
125
|
+
ol.suggested-tags {
|
126
|
+
margin: 0.5em 0;
|
127
|
+
padding: 0;
|
128
|
+
}
|
129
|
+
|
130
|
+
ol.suggested-tags li {
|
131
|
+
float: left;
|
132
|
+
list-style: none;
|
133
|
+
margin-right: 0.3em;
|
134
|
+
}
|
135
|
+
|
136
|
+
ol.suggested-tags li:after { content: ', '; }
|
137
|
+
ol.suggested-tags li:last-child:after { content: ''; }
|
138
|
+
ol.suggested-tags li a { white-space: nowrap; }
|
139
|
+
|
140
|
+
#page-form,
|
141
|
+
#post-form { margin-bottom: 2em; }
|
142
|
+
|
143
|
+
#page-form #body,
|
144
|
+
#page-form #title,
|
145
|
+
#post-form #body,
|
146
|
+
#post-form #title,
|
147
|
+
#post-form #tags { width: 99%; }
|
148
|
+
|
149
|
+
#page-form #name,
|
150
|
+
#post-form #name { width: 15em; }
|
151
|
+
|
152
|
+
.tips {
|
153
|
+
background: #eff7ff;
|
154
|
+
border: 1px solid #afd4ff;
|
155
|
+
margin-left: 0;
|
156
|
+
padding: 0.8em;
|
157
|
+
padding-left: 1.8em;
|
158
|
+
}
|
159
|
+
|
160
|
+
.tips li { margin-bottom: 0.5em; }
|
161
|
+
|
162
|
+
.tips kbd {
|
163
|
+
background: #f7f1d8;
|
164
|
+
border: 1px solid #efdb7f;
|
165
|
+
font-size: 11px;
|
166
|
+
padding: 0 3px 1px;
|
167
|
+
white-space: nowrap;
|
168
|
+
}
|
169
|
+
|
170
|
+
/* -- Lists ----------------------------------------------------------------- */
|
171
|
+
table.list {
|
172
|
+
border: 1px solid #dfdfdf;
|
173
|
+
width: 100%;
|
174
|
+
}
|
175
|
+
|
176
|
+
table.list th,
|
177
|
+
table.list td { padding: 1px 2px; }
|
178
|
+
|
179
|
+
table.list thead th {
|
180
|
+
font-weight: bold;
|
181
|
+
text-align: left;
|
182
|
+
white-space: nowrap;
|
183
|
+
}
|
184
|
+
|
185
|
+
table.list thead tr { background: #dfeeff; }
|
186
|
+
table.list tbody tr.empty { font-style: italic; }
|
187
|
+
table.list tbody tr.even { background: #f7fbff; }
|
188
|
+
table.list tbody tr.odd { background: none; }
|
189
|
+
table.list tbody tr:hover { background: #d0dbf7; }
|
190
|
+
|
191
|
+
table.list tbody td { border: 1px solid transparent; }
|
192
|
+
table.list tbody tr:hover td { border: 1px solid #9ab3f7; }
|
193
|
+
|
194
|
+
table.list td.created,
|
195
|
+
table.list td.updated,
|
196
|
+
table.pages td.name { white-space: nowrap; }
|
197
|
+
|
198
|
+
table.media td.size { text-align: right; }
|
199
|
+
|
200
|
+
/* -- Media ----------------------------------------------------------------- */
|
201
|
+
fieldset.replace { padding: 0.3em 1em; }
|
202
|
+
|
203
|
+
table.details th {
|
204
|
+
font-weight: bold;
|
205
|
+
padding-right: 4px;
|
206
|
+
text-align: right;
|
207
|
+
}
|
208
|
+
|
209
|
+
table.details.media tr.filename td,
|
210
|
+
table.details.media tr.path td {
|
211
|
+
font-family: Menlo, 'Bitstream Vera Sans Mono', Consolas, Monaco, 'Courier New', monospaced;
|
212
|
+
}
|
213
|
+
|
214
|
+
/* -- Welcome Page ---------------------------------------------------------- */
|
215
|
+
.welcome .version {
|
216
|
+
color: #666;
|
217
|
+
font-size: 12px;
|
218
|
+
float: right;
|
219
|
+
}
|
220
|
+
|
221
|
+
.welcome dl.customizing { margin-left: 2ex; }
|
222
|
+
.welcome dl.customizing dt { font-weight: bold; }
|
223
|
+
.welcome dl.customizing dd { margin: 0 0 1em 0; }
|