murlsh 1.4.1 → 1.5.0
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.
- data/.gitignore +4 -0
- data/.htaccess +0 -3
- data/Gemfile +41 -0
- data/README.textile +68 -6
- data/Rakefile +65 -152
- data/config.ru +13 -2
- data/config.yaml +9 -4
- data/db/migrate/20110213023123_init.rb +20 -0
- data/lib/murlsh/atom_body.rb +78 -0
- data/lib/murlsh/atom_server.rb +38 -0
- data/lib/murlsh/auth.rb +12 -0
- data/lib/murlsh/build_query.rb +1 -1
- data/lib/murlsh/cat_files.rb +17 -0
- data/lib/murlsh/delicious_parse.rb +1 -0
- data/lib/murlsh/dispatch.rb +25 -11
- data/lib/murlsh/etag_add_encoding.rb +1 -1
- data/lib/murlsh/feed_body.rb +36 -0
- data/lib/murlsh/img_store.rb +27 -28
- data/lib/murlsh/install.rb +1 -0
- data/lib/murlsh/json_body.rb +5 -2
- data/lib/murlsh/json_server.rb +9 -13
- data/lib/murlsh/m3u_body.rb +28 -0
- data/lib/murlsh/m3u_server.rb +50 -0
- data/lib/murlsh/markup.rb +1 -1
- data/lib/murlsh/plugin.rb +5 -0
- data/lib/murlsh/podcast_server.rb +44 -0
- data/lib/murlsh/pop_server.rb +78 -0
- data/lib/murlsh/random_server.rb +41 -0
- data/lib/murlsh/rss_body.rb +46 -0
- data/lib/murlsh/rss_server.rb +38 -0
- data/lib/murlsh/search_conditions.rb +2 -2
- data/lib/murlsh/uri_ask.rb +2 -2
- data/lib/murlsh/url_body.rb +21 -6
- data/lib/murlsh/url_result_set.rb +2 -2
- data/lib/murlsh/url_server.rb +19 -16
- data/lib/murlsh/write_ordered_hash.rb +17 -0
- data/lib/murlsh.rb +13 -2
- data/murlsh.gemspec +41 -194
- data/plugins/add_post_60_notify_hubs.rb +3 -2
- data/plugins/add_pre_30_unajax_twitter.rb +1 -1
- data/plugins/add_pre_40_thumbnail_shortcuts.rb +23 -0
- data/plugins/add_pre_45_supplied_thumbnail.rb +4 -9
- data/plugins/add_pre_50_media_thumbnail.rb +4 -9
- data/plugins/add_pre_50_open_graph_image.rb +4 -8
- data/plugins/add_pre_60_github_title.rb +1 -1
- data/plugins/add_pre_65_html_thumb.rb +3 -8
- data/plugins/add_pre_65_img_thumb.rb +4 -9
- data/plugins/avatar_50_gravatar.rb +2 -1
- data/plugins/store_asset_40_s3.rb +40 -0
- data/plugins/store_asset_50_local.rb +22 -0
- data/public/js/js.js +0 -7
- data/spec/auth_spec.rb +7 -0
- data/spec/cat_files_spec.rb +49 -0
- data/spec/img_store_spec.rb +24 -8
- metadata +119 -76
- data/VERSION +0 -1
- data/lib/murlsh/build_md5.rb +0 -12
- data/lib/murlsh/head_from_get.rb +0 -15
- data/plugins/add_post_50_update_feed.rb +0 -84
- data/plugins/add_post_50_update_m3u.rb +0 -35
- data/plugins/add_post_50_update_podcast.rb +0 -44
- data/plugins/add_post_50_update_rss.rb +0 -51
- data/plugins/add_pre_60_s3_image.rb +0 -35
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
require 'murlsh'
|
6
|
+
|
7
|
+
module Murlsh
|
8
|
+
|
9
|
+
# Serve Atom feed.
|
10
|
+
class AtomServer
|
11
|
+
|
12
|
+
def initialize(config); @config = config; end
|
13
|
+
|
14
|
+
# Respond to a GET request for Atom feed.
|
15
|
+
def get(req)
|
16
|
+
conditions = Murlsh::SearchConditions.new(req['q']).conditions
|
17
|
+
page = 1
|
18
|
+
per_page = config.fetch('num_posts_feed', 25)
|
19
|
+
|
20
|
+
result_set = Murlsh::UrlResultSet.new(conditions, page, per_page)
|
21
|
+
urls = result_set.results
|
22
|
+
|
23
|
+
feed_url = URI.join(config.fetch('root_url'), config.fetch('feed_file'))
|
24
|
+
body = Murlsh::AtomBody.new(config, req, feed_url, urls)
|
25
|
+
|
26
|
+
resp = Rack::Response.new(body, 200,
|
27
|
+
'Cache-Control' => 'must-revalidate, max-age=0',
|
28
|
+
'Content-Type' => 'application/atom+xml')
|
29
|
+
if u = body.updated
|
30
|
+
resp['Last-Modified'] = u.httpdate
|
31
|
+
end
|
32
|
+
resp
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :config
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/murlsh/auth.rb
CHANGED
@@ -44,6 +44,18 @@ module Murlsh
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
# Look up a user by email address.
|
48
|
+
#
|
49
|
+
# Return nil if not found.
|
50
|
+
def by_email(email)
|
51
|
+
hash = Digest::MD5.hexdigest(email)
|
52
|
+
|
53
|
+
self.class.csv_iter(@file) do |row|
|
54
|
+
return { :name => row[0], :email => row[1] } if hash == row[1]
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
47
59
|
end
|
48
60
|
|
49
61
|
end
|
data/lib/murlsh/build_query.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Murlsh
|
2
|
+
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# Concatenate some files and return the result as a string.
|
6
|
+
def cat_files(files, sep=nil)
|
7
|
+
result = ''
|
8
|
+
files.each do |fname|
|
9
|
+
open(fname) do |h|
|
10
|
+
while (line = h.gets) do; result << line; end
|
11
|
+
result << sep if sep
|
12
|
+
end
|
13
|
+
end
|
14
|
+
result
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/murlsh/dispatch.rb
CHANGED
@@ -14,25 +14,34 @@ module Murlsh
|
|
14
14
|
def initialize(config)
|
15
15
|
@config = config
|
16
16
|
|
17
|
-
|
17
|
+
atom_server = Murlsh::AtomServer.new(config)
|
18
18
|
json_server = Murlsh::JsonServer.new(config)
|
19
|
+
m3u_server = Murlsh::M3uServer.new(config)
|
20
|
+
podcast_server = Murlsh::PodcastServer.new(config)
|
21
|
+
pop_server = Murlsh::PopServer.new(config)
|
22
|
+
random_server = Murlsh::RandomServer.new(config)
|
23
|
+
rss_server = Murlsh::RssServer.new(config)
|
24
|
+
url_server = Murlsh::UrlServer.new(config)
|
25
|
+
|
19
26
|
root_path = URI(config.fetch('root_url')).path
|
20
27
|
|
21
28
|
@routes = [
|
22
|
-
[%r{^HEAD #{root_path}
|
23
|
-
[%r{^GET #{root_path}
|
29
|
+
[%r{^(?:HEAD|GET) #{root_path}atom\.atom$}, atom_server.method(:get)],
|
30
|
+
[%r{^(?:HEAD|GET) #{root_path}json\.json$}, json_server.method(:get)],
|
31
|
+
[%r{^(?:HEAD|GET) #{root_path}m3u\.m3u$}, m3u_server.method(:get)],
|
32
|
+
[%r{^(?:HEAD|GET) #{root_path}podcast\.rss$}, podcast_server.method(:get)],
|
33
|
+
[%r{^POST #{root_path}pop$}, pop_server.method(:post)],
|
34
|
+
[%r{^(?:HEAD|GET) #{root_path}random$}, random_server.method(:get)],
|
35
|
+
[%r{^(?:HEAD|GET) #{root_path}rss\.rss$}, rss_server.method(:get)],
|
36
|
+
[%r{^(?:HEAD|GET) #{root_path}(url)?$}, url_server.method(:get)],
|
24
37
|
[%r{^POST #{root_path}(url)?$}, url_server.method(:post)],
|
25
|
-
[%r{^HEAD #{root_path}json\.json$}, json_server.method(:head)],
|
26
|
-
[%r{^GET #{root_path}json\.json$}, json_server.method(:get)],
|
27
38
|
]
|
28
39
|
|
29
40
|
db_init
|
30
41
|
end
|
31
42
|
|
32
43
|
def db_init
|
33
|
-
ActiveRecord::Base.establish_connection(
|
34
|
-
:adapter => 'sqlite3', :database => @config.fetch('db_file'))
|
35
|
-
|
44
|
+
ActiveRecord::Base.establish_connection config.fetch('db')
|
36
45
|
ActiveRecord::Base.default_timezone = :utc
|
37
46
|
ActiveRecord::Base.include_root_in_json = false
|
38
47
|
# ActiveRecord::Base.logger = Logger.new(STDERR)
|
@@ -55,13 +64,18 @@ module Murlsh
|
|
55
64
|
|
56
65
|
# Called if the request is not found.
|
57
66
|
def not_found(req)
|
58
|
-
|
67
|
+
if req.head?
|
68
|
+
Rack::Response.new([], 404)
|
69
|
+
else
|
70
|
+
Rack::Response.new("<p>#{req.url} not found</p>
|
59
71
|
|
60
|
-
<p><a href=\"#{
|
72
|
+
<p><a href=\"#{config.fetch('root_url')}\">root<a></p>
|
61
73
|
",
|
62
|
-
|
74
|
+
404, { 'Content-Type' => 'text/html' })
|
75
|
+
end
|
63
76
|
end
|
64
77
|
|
78
|
+
attr_reader :config
|
65
79
|
attr_accessor :routes
|
66
80
|
end
|
67
81
|
|
@@ -14,7 +14,7 @@ module Murlsh
|
|
14
14
|
headers = Rack::Utils::HeaderHash.new(headers)
|
15
15
|
|
16
16
|
if headers['ETag']
|
17
|
-
headers['ETag'].sub!
|
17
|
+
headers['ETag'].sub! /(")?$/, "#{headers['Content-Encoding']}\\1"
|
18
18
|
end
|
19
19
|
|
20
20
|
[status, headers, body]
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Murlsh
|
2
|
+
|
3
|
+
# Feed body mixin.
|
4
|
+
module FeedBody
|
5
|
+
|
6
|
+
# Content types to add an enclosure for.
|
7
|
+
EnclosureContentTypes = %w{
|
8
|
+
application/pdf
|
9
|
+
audio/mpeg
|
10
|
+
image/gif
|
11
|
+
image/jpeg
|
12
|
+
image/png
|
13
|
+
}
|
14
|
+
|
15
|
+
def initialize(config, req, feed_url, urls)
|
16
|
+
@config, @req, @feed_url, @urls = config, req, feed_url, urls
|
17
|
+
@updated = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Yield body for Rack.
|
21
|
+
def each; yield build; end
|
22
|
+
|
23
|
+
# Build feed title.
|
24
|
+
def feed_title
|
25
|
+
result = "#{config['page_title']}"
|
26
|
+
req['q'] ? "#{result} /#{req['q']}" : result
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :config
|
30
|
+
attr_reader :req
|
31
|
+
attr_reader :feed_url
|
32
|
+
attr_reader :urls
|
33
|
+
attr_reader :updated
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/murlsh/img_store.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
require 'cgi'
|
2
1
|
require 'digest/md5'
|
3
2
|
require 'open-uri'
|
4
|
-
require 'uri'
|
5
3
|
|
6
4
|
require 'RMagick'
|
7
5
|
|
@@ -9,66 +7,67 @@ require 'murlsh'
|
|
9
7
|
|
10
8
|
module Murlsh
|
11
9
|
|
12
|
-
#
|
10
|
+
# Store images from various sources in asset storage.
|
11
|
+
#
|
12
|
+
# Storage is determined by store_asset plugins.
|
13
13
|
class ImgStore
|
14
14
|
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
def initialize(
|
19
|
-
@
|
20
|
-
@user_agent = options[:user_agent]
|
15
|
+
# Store images from various sources in asset storage.
|
16
|
+
#
|
17
|
+
# Storage is determined by store_asset plugins.
|
18
|
+
def initialize(config)
|
19
|
+
@config = config
|
21
20
|
end
|
22
21
|
|
23
22
|
# Build headers to send with request.
|
24
23
|
def headers
|
25
24
|
result = {}
|
26
|
-
result['User-Agent'] =
|
25
|
+
result['User-Agent'] = config['user_agent'] if config['user_agent']
|
27
26
|
result
|
28
27
|
end
|
29
28
|
|
30
|
-
# Fetch an image from a url and store it
|
31
|
-
#
|
32
|
-
# The filename will be the md5sum of the contents plus the correct
|
33
|
-
# extension.
|
29
|
+
# Fetch an image from a url and store it in asset storage.
|
34
30
|
#
|
35
31
|
# If a block is given the Magick::ImageList created will be yielded
|
36
32
|
# before storage.
|
33
|
+
#
|
34
|
+
# Returns image url.
|
37
35
|
def store_url(url, &block)
|
38
|
-
open(url, headers) { |fin| store_img_data
|
36
|
+
open(url, headers) { |fin| store_img_data(fin.read, &block) }
|
39
37
|
end
|
40
38
|
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# The filename will be the md5sum of the contents plus the correct
|
44
|
-
# extension.
|
39
|
+
# Store a blob of image data in asset storage.
|
45
40
|
#
|
46
41
|
# If a block is given the Magick::ImageList created will be yielded
|
47
42
|
# before storage.
|
43
|
+
#
|
44
|
+
# Returns image url.
|
48
45
|
def store_img_data(img_data, &block)
|
49
46
|
img = Magick::ImageList.new.from_blob(img_data)
|
50
47
|
yield img if block_given?
|
51
48
|
store_img img
|
52
49
|
end
|
53
50
|
|
54
|
-
#
|
51
|
+
# Store a Magick::ImageList in asset storage.
|
55
52
|
#
|
56
|
-
#
|
57
|
-
# extension.
|
53
|
+
# Returns image url.
|
58
54
|
def store_img(img)
|
59
55
|
img.extend(Murlsh::ImageList) unless img.is_a?(Murlsh::ImageList)
|
60
56
|
img_data = img.to_blob
|
61
57
|
md5 = Digest::MD5.hexdigest(img_data)
|
62
58
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
59
|
+
name = "img/thumb/#{md5}#{img.preferred_extension}"
|
60
|
+
|
61
|
+
Murlsh::Plugin.hooks('store_asset') do |p|
|
62
|
+
# run until one returns something
|
63
|
+
if url = p.run(name, img_data, config)
|
64
|
+
return url
|
65
|
+
end
|
67
66
|
end
|
68
|
-
|
67
|
+
nil
|
69
68
|
end
|
70
69
|
|
71
|
-
attr_reader :
|
70
|
+
attr_reader :config
|
72
71
|
end
|
73
72
|
|
74
73
|
end
|
data/lib/murlsh/install.rb
CHANGED
data/lib/murlsh/json_body.rb
CHANGED
@@ -4,7 +4,6 @@ module Murlsh
|
|
4
4
|
|
5
5
|
# Recent urls json response builder.
|
6
6
|
class JsonBody
|
7
|
-
include Murlsh::BuildMd5
|
8
7
|
|
9
8
|
def initialize(config, req, result_set)
|
10
9
|
@config, @req, @result_set = config, req, result_set
|
@@ -19,6 +18,10 @@ module Murlsh
|
|
19
18
|
@body
|
20
19
|
else
|
21
20
|
urls = @result_set.results.map do |mu|
|
21
|
+
Murlsh::Plugin.hooks('url_display_pre') do |p|
|
22
|
+
p.run mu, @req, @config
|
23
|
+
end
|
24
|
+
|
22
25
|
h = mu.attributes
|
23
26
|
|
24
27
|
h['title'] = mu.title_stripped
|
@@ -26,7 +29,7 @@ module Murlsh
|
|
26
29
|
# add site root url to relative thumbnail urls
|
27
30
|
if h['thumbnail_url'] and
|
28
31
|
not URI(h['thumbnail_url']).scheme.to_s.downcase[/https?/]
|
29
|
-
h['thumbnail_url'] = URI.join(@config
|
32
|
+
h['thumbnail_url'] = URI.join(@config.fetch('root_url'),
|
30
33
|
h['thumbnail_url']).to_s
|
31
34
|
end
|
32
35
|
|
data/lib/murlsh/json_server.rb
CHANGED
@@ -5,8 +5,6 @@ module Murlsh
|
|
5
5
|
# Serve most recent urls in json and jsonp.
|
6
6
|
class JsonServer
|
7
7
|
|
8
|
-
include Murlsh::HeadFromGet
|
9
|
-
|
10
8
|
def initialize(config); @config = config; end
|
11
9
|
|
12
10
|
# Respond to a GET request. Return json of recent urls or jsonp if
|
@@ -14,26 +12,24 @@ module Murlsh
|
|
14
12
|
def get(req)
|
15
13
|
conditions = Murlsh::SearchConditions.new(req['q']).conditions
|
16
14
|
page = 1
|
17
|
-
per_page =
|
15
|
+
per_page = config.fetch('num_posts_feed', 25)
|
18
16
|
|
19
17
|
result_set = Murlsh::UrlResultSet.new(conditions, page, per_page)
|
20
18
|
|
21
|
-
resp = Rack::Response.new
|
22
|
-
|
23
19
|
if req['callback']
|
24
|
-
|
25
|
-
|
20
|
+
content_type = 'application/javascript'
|
21
|
+
body = Murlsh::JsonpBody.new(config, req, result_set)
|
26
22
|
else
|
27
|
-
|
28
|
-
|
23
|
+
content_type = 'application/json'
|
24
|
+
body = Murlsh::JsonBody.new(config, req, result_set)
|
29
25
|
end
|
30
26
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
resp
|
27
|
+
Rack::Response.new body, 200,
|
28
|
+
'Cache-Control' => 'must-revalidate, max-age=0',
|
29
|
+
'Content-Type' => content_type
|
35
30
|
end
|
36
31
|
|
32
|
+
attr_reader :config
|
37
33
|
end
|
38
34
|
|
39
35
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Murlsh
|
2
|
+
|
3
|
+
# m3u builder.
|
4
|
+
class M3uBody
|
5
|
+
include Murlsh::FeedBody
|
6
|
+
|
7
|
+
# m3u builder.
|
8
|
+
def build
|
9
|
+
if defined?(@body)
|
10
|
+
@body
|
11
|
+
else
|
12
|
+
result = "# #{feed_url}\r\n\r\n"
|
13
|
+
urls.each do |mu|
|
14
|
+
Murlsh::Plugin.hooks('url_display_pre') do |p|
|
15
|
+
p.run mu, req, config
|
16
|
+
end
|
17
|
+
|
18
|
+
result << "#{mu.url}\r\n"
|
19
|
+
@updated = @updated ? [@updated, mu.time].max : mu.time
|
20
|
+
end
|
21
|
+
|
22
|
+
@body = result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
require 'murlsh'
|
6
|
+
|
7
|
+
module Murlsh
|
8
|
+
|
9
|
+
# Serve m3u file of audio urls.
|
10
|
+
class M3uServer
|
11
|
+
|
12
|
+
AudioContentTypes = %w{
|
13
|
+
application/ogg
|
14
|
+
audio/mpeg
|
15
|
+
audio/ogg
|
16
|
+
}
|
17
|
+
|
18
|
+
def initialize(config); @config = config; end
|
19
|
+
|
20
|
+
# Respond to a GET request for m3u file.
|
21
|
+
def get(req)
|
22
|
+
conditions = ['content_type IN (?)', AudioContentTypes]
|
23
|
+
search_conditions = Murlsh::SearchConditions.new(req['q']).conditions
|
24
|
+
unless search_conditions.empty?
|
25
|
+
conditions[0] << " AND (#{search_conditions[0]})"
|
26
|
+
conditions.push(*search_conditions[1..-1])
|
27
|
+
end
|
28
|
+
|
29
|
+
page = 1
|
30
|
+
per_page = config.fetch('num_posts_feed', 25)
|
31
|
+
|
32
|
+
result_set = Murlsh::UrlResultSet.new(conditions, page, per_page)
|
33
|
+
urls = result_set.results
|
34
|
+
|
35
|
+
feed_url = URI.join(config.fetch('root_url'), 'm3u.m3u')
|
36
|
+
body = Murlsh::M3uBody.new(config, req, feed_url, urls)
|
37
|
+
|
38
|
+
resp = Rack::Response.new(body, 200,
|
39
|
+
'Cache-Control' => 'must-revalidate, max-age=0',
|
40
|
+
'Content-Type' => 'audio/x-mpegurl')
|
41
|
+
if u = body.updated
|
42
|
+
resp['Last-Modified'] = u.httpdate
|
43
|
+
end
|
44
|
+
resp
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :config
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/murlsh/markup.rb
CHANGED
@@ -70,7 +70,7 @@ module Murlsh
|
|
70
70
|
if options[:id]
|
71
71
|
if options[:label]
|
72
72
|
label_suffix = options[:label_suffix] || ':'
|
73
|
-
label
|
73
|
+
label "#{options[:label]}#{label_suffix}", :for => options[:id]
|
74
74
|
end
|
75
75
|
options[:name] ||= options[:id]
|
76
76
|
end
|
data/lib/murlsh/plugin.rb
CHANGED
@@ -9,8 +9,13 @@ module Murlsh
|
|
9
9
|
# run arguments (config hash)
|
10
10
|
# * avatar - called to get an avatar url from an email md5 sum
|
11
11
|
# run arguments (avatar url, url, config hash)
|
12
|
+
# * store_asset - store an asset somewhere where it can be loaded by url
|
13
|
+
# run arguments (name, data, config hash)
|
12
14
|
# * url_display_add - called to display additional information after urls
|
13
15
|
# run arguments (markup builder, url, config hash)
|
16
|
+
# * url_display_pre - called to modify a url on-the-fly before display, does
|
17
|
+
# not change database
|
18
|
+
# run arguments (url, rack request, config hash)
|
14
19
|
class Plugin
|
15
20
|
|
16
21
|
# Called when a plugin class inherits from this class (the way plugins
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
require 'murlsh'
|
6
|
+
|
7
|
+
module Murlsh
|
8
|
+
|
9
|
+
# Serve podcast RSS feed.
|
10
|
+
class PodcastServer
|
11
|
+
|
12
|
+
def initialize(config); @config = config; end
|
13
|
+
|
14
|
+
# Respond to a GET request for podcast RSS feed.
|
15
|
+
def get(req)
|
16
|
+
conditions = ['content_type = ?', 'audio/mpeg']
|
17
|
+
search_conditions = Murlsh::SearchConditions.new(req['q']).conditions
|
18
|
+
unless search_conditions.empty?
|
19
|
+
conditions[0] << " AND (#{search_conditions[0]})"
|
20
|
+
conditions.push(*search_conditions[1..-1])
|
21
|
+
end
|
22
|
+
|
23
|
+
page = 1
|
24
|
+
per_page = config.fetch('num_posts_feed', 25)
|
25
|
+
|
26
|
+
result_set = Murlsh::UrlResultSet.new(conditions, page, per_page)
|
27
|
+
urls = result_set.results
|
28
|
+
|
29
|
+
feed_url = URI.join(config.fetch('root_url'), 'podcast.rss')
|
30
|
+
body = Murlsh::RssBody.new(config, req, feed_url, urls)
|
31
|
+
|
32
|
+
resp = Rack::Response.new(body, 200,
|
33
|
+
'Cache-Control' => 'must-revalidate, max-age=0',
|
34
|
+
'Content-Type' => 'application/rss+xml')
|
35
|
+
if u = body.updated
|
36
|
+
resp['Last-Modified'] = u.httpdate
|
37
|
+
end
|
38
|
+
resp
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :config
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'net/pop'
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
require 'json'
|
5
|
+
require 'postrank-uri'
|
6
|
+
require 'rack'
|
7
|
+
require 'rmail'
|
8
|
+
|
9
|
+
module Murlsh
|
10
|
+
|
11
|
+
# Pop mail from a pop3 server and add all urls in messages from murlsh users.
|
12
|
+
class PopServer
|
13
|
+
|
14
|
+
def initialize(config); @config = config; end
|
15
|
+
|
16
|
+
def post(req)
|
17
|
+
response_body = []
|
18
|
+
|
19
|
+
if req['secret'] == config['pop_secret']
|
20
|
+
Net::POP3.enable_ssl
|
21
|
+
Net::POP3.start(config.fetch('pop_server'), config.fetch('pop_port'),
|
22
|
+
config.fetch('pop_user'), config.fetch('pop_password')) do |pop|
|
23
|
+
pop.each_mail do |mail|
|
24
|
+
begin
|
25
|
+
response_body << process_mail(mail.pop)
|
26
|
+
rescue Exception
|
27
|
+
ensure
|
28
|
+
mail.delete
|
29
|
+
end
|
30
|
+
end
|
31
|
+
pop.finish
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Rack::Response.new response_body.to_json, 200,
|
36
|
+
'Content-Type' => 'application/json'
|
37
|
+
end
|
38
|
+
|
39
|
+
# Authenticate the sender and add all urls extracted from the the email
|
40
|
+
# body.
|
41
|
+
def process_mail(mail)
|
42
|
+
parsed_mail = parse_mail(mail)
|
43
|
+
|
44
|
+
if user = Murlsh::Auth.new(config.fetch('auth_file')).by_email(
|
45
|
+
parsed_mail[:from])
|
46
|
+
parsed_mail[:uris].each do |uri|
|
47
|
+
mu = Murlsh::Url.new do |u|
|
48
|
+
u.url = uri
|
49
|
+
u.email = user[:email]
|
50
|
+
u.name = user[:name]
|
51
|
+
u.time = parsed_mail[:date]
|
52
|
+
end
|
53
|
+
|
54
|
+
# validate before add_pre plugins have run and also after (in save!)
|
55
|
+
raise ActiveRecord::RecordInvalid.new(mu) unless mu.valid?
|
56
|
+
Murlsh::Plugin.hooks('add_pre') { |p| p.run mu, config }
|
57
|
+
mu.save!
|
58
|
+
Murlsh::Plugin.hooks('add_post') { |p| p.run mu, config }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
parsed_mail
|
62
|
+
end
|
63
|
+
|
64
|
+
# Parse date, from address and urls found in body from an email message.
|
65
|
+
def parse_mail(mail)
|
66
|
+
parsed_mail = RMail::Parser.read(mail.gsub(/\r/, ''))
|
67
|
+
|
68
|
+
{
|
69
|
+
:date => parsed_mail.header.date.utc,
|
70
|
+
:from => parsed_mail.header.from.first.address.downcase,
|
71
|
+
:uris => PostRank::URI.extract(parsed_mail.body),
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_reader :config
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
require 'murlsh'
|
4
|
+
|
5
|
+
module Murlsh
|
6
|
+
|
7
|
+
# Redirect to a random url from the database.
|
8
|
+
class RandomServer
|
9
|
+
|
10
|
+
def initialize(config); @config = config; end
|
11
|
+
|
12
|
+
# Redirect to a random url from the database optionally matching a query.
|
13
|
+
#
|
14
|
+
# Redirect to root url if no urls match.
|
15
|
+
def get(req)
|
16
|
+
if choice = random_url(Murlsh::SearchConditions.new(req['q']).conditions)
|
17
|
+
url = choice.url
|
18
|
+
else
|
19
|
+
url = config.fetch('root_url')
|
20
|
+
end
|
21
|
+
|
22
|
+
resp = Rack::Response.new("<a href=\"#{url}\">#{url}</a>")
|
23
|
+
resp.redirect(url)
|
24
|
+
|
25
|
+
resp
|
26
|
+
end
|
27
|
+
|
28
|
+
# Select a random url from the database optionally matching a query.
|
29
|
+
#
|
30
|
+
# Return nil if no urls match.
|
31
|
+
def random_url(conditions=[])
|
32
|
+
count = Murlsh::Url.count(:conditions => conditions)
|
33
|
+
if count > 0
|
34
|
+
Murlsh::Url.first(:conditions => conditions, :offset => rand(count))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :config
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|