murlsh 0.2.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.
- data/.gitignore +1 -0
- data/.htaccess +7 -0
- data/COPYING +674 -0
- data/README.textile +32 -0
- data/Rakefile +192 -0
- data/VERSION +1 -0
- data/bin/murlsh +16 -0
- data/config.ru +11 -0
- data/config.yaml +18 -0
- data/lib/murlsh/atom_feed.rb +76 -0
- data/lib/murlsh/auth.rb +33 -0
- data/lib/murlsh/dispatch.rb +54 -0
- data/lib/murlsh/get_content_type.rb +84 -0
- data/lib/murlsh/get_title.rb +65 -0
- data/lib/murlsh/markup.rb +97 -0
- data/lib/murlsh/plugin.rb +23 -0
- data/lib/murlsh/referrer.rb +47 -0
- data/lib/murlsh/sqlite3_adapter.rb +12 -0
- data/lib/murlsh/time.rb +18 -0
- data/lib/murlsh/url.rb +44 -0
- data/lib/murlsh/url_body.rb +155 -0
- data/lib/murlsh/url_server.rb +82 -0
- data/lib/murlsh/xhtml_response.rb +19 -0
- data/lib/murlsh.rb +16 -0
- data/murlsh.gemspec +109 -0
- data/plugins/update_feed.rb +23 -0
- data/public/css/jquery.jgrowl.css +127 -0
- data/public/css/phone.css +14 -0
- data/public/css/screen.css +94 -0
- data/public/js/jquery-1.3.2.min.js +19 -0
- data/public/js/jquery.cookie.js +96 -0
- data/public/js/jquery.jgrowl_compressed.js +100 -0
- data/public/js/js.js +229 -0
- data/public/swf/player_mp3_mini.swf +0 -0
- data/test/auth_test.rb +34 -0
- data/test/get_charset_test.rb +25 -0
- data/test/get_content_type_test.rb +63 -0
- data/test/get_title_test.rb +43 -0
- data/test/markup_test.rb +144 -0
- data/test/referrer_test.rb +71 -0
- data/test/xhtml_response_test.rb +139 -0
- metadata +170 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Murlsh
|
2
|
+
|
3
|
+
class Plugin
|
4
|
+
|
5
|
+
def self.inherited(child)
|
6
|
+
registered << child
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.hooks(name)
|
10
|
+
matches = registered.select { |p| p::Hook == name }
|
11
|
+
|
12
|
+
if block_given?
|
13
|
+
matches.each { |p| yield p }
|
14
|
+
end
|
15
|
+
matches
|
16
|
+
end
|
17
|
+
|
18
|
+
@registered = []
|
19
|
+
class << self; attr_reader :registered end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Murlsh
|
5
|
+
|
6
|
+
class Referrer
|
7
|
+
|
8
|
+
def initialize(url)
|
9
|
+
@url = url
|
10
|
+
|
11
|
+
begin
|
12
|
+
url_parsed = URI(url)
|
13
|
+
@hostpath = url_parsed.host + url_parsed.path
|
14
|
+
@query_string =
|
15
|
+
url_parsed.query.nil? ? {} : CGI::parse(url_parsed.query)
|
16
|
+
rescue Exception => e
|
17
|
+
@hostpath = ''
|
18
|
+
@query_string = {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def search_query(qmap=Qmap)
|
23
|
+
if hostpath and query_string
|
24
|
+
qmap.each_pair do |r,v|
|
25
|
+
if hostpath[r] and !query_string[v].empty?
|
26
|
+
if block_given?
|
27
|
+
yield query_string[v].first
|
28
|
+
else
|
29
|
+
return query_string[v].first
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
Qmap = {
|
38
|
+
/^www\.google\.(bs|ca|com|cz|dk|es|fi|nl)(\/m)?\/search$/ => 'q',
|
39
|
+
/^www\.bing\.com\/search$/ => 'q',
|
40
|
+
}
|
41
|
+
|
42
|
+
attr_accessor :url
|
43
|
+
attr_reader :hostpath
|
44
|
+
attr_reader :query_string
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'active_record/connection_adapters/sqlite3_adapter'
|
2
|
+
|
3
|
+
class ActiveRecord::ConnectionAdapters::SQLite3Adapter
|
4
|
+
|
5
|
+
def initialize(connection, logger, config)
|
6
|
+
super
|
7
|
+
@connection.create_function('MATCH', 2) do |func,search_in,search_for|
|
8
|
+
func.result = search_in.to_s.match(/#{search_for}/i) ? 1 : nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
data/lib/murlsh/time.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
class Time
|
4
|
+
|
5
|
+
def fuzzy
|
6
|
+
days_ago = (Time.now.to_i - to_i) / 86400
|
7
|
+
|
8
|
+
case days_ago
|
9
|
+
when 0 then 'today'
|
10
|
+
when 1 then 'yesterday'
|
11
|
+
when (2..4) then "#{days_ago} days ago"
|
12
|
+
when (5..7) then strftime('%a %e %b')
|
13
|
+
when (8..180) then strftime('%e %b').strip
|
14
|
+
else strftime('%e %b %Y').strip
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/lib/murlsh/url.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Murlsh
|
7
|
+
|
8
|
+
class Url < ActiveRecord::Base
|
9
|
+
|
10
|
+
def title
|
11
|
+
read_attribute(:title) || read_attribute(:url) || 'title missing'
|
12
|
+
end
|
13
|
+
|
14
|
+
def same_author?(other)
|
15
|
+
other and other.email and other.name and
|
16
|
+
email and name and email == other.email and name == other.name
|
17
|
+
end
|
18
|
+
|
19
|
+
Widely_known = %w{
|
20
|
+
wikipedia.org
|
21
|
+
flickr.com
|
22
|
+
github.com
|
23
|
+
twitter.com
|
24
|
+
vimeo.com
|
25
|
+
youtube.com
|
26
|
+
}
|
27
|
+
|
28
|
+
def hostrec
|
29
|
+
begin
|
30
|
+
domain = URI(url).host[/[a-z\d-]+\.[a-z]{2,}(\.[a-z]{2})?$/].downcase
|
31
|
+
rescue Exception => e
|
32
|
+
domain = nil
|
33
|
+
end
|
34
|
+
yield domain unless !domain or title.downcase.index(domain) or
|
35
|
+
Widely_known.include?(domain)
|
36
|
+
end
|
37
|
+
|
38
|
+
def is_image?
|
39
|
+
%w{image/gif image/jpeg image/png}.include?(content_type)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module Murlsh
|
2
|
+
|
3
|
+
class UrlBody < Builder::XmlMarkup
|
4
|
+
include Murlsh::Markup
|
5
|
+
|
6
|
+
def initialize(config, db, req)
|
7
|
+
@config, @db, @req, @q = config, db, req, req.params['q']
|
8
|
+
super(:indent => 2)
|
9
|
+
end
|
10
|
+
|
11
|
+
def urls
|
12
|
+
Murlsh::Url.all(:conditions => search_conditions, :order => 'id DESC',
|
13
|
+
:limit => @req.params['n'] ? @req.params['n'].to_i :
|
14
|
+
@config.fetch('num_posts_page', 100))
|
15
|
+
end
|
16
|
+
|
17
|
+
def search_conditions
|
18
|
+
if @q
|
19
|
+
search_cols = %w{name title url}
|
20
|
+
[search_cols.collect { |x| "MATCH(#{x}, ?)" }.join(' OR ')].push(
|
21
|
+
*[@q] * search_cols.size)
|
22
|
+
else
|
23
|
+
[]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def each
|
28
|
+
instruct! :xml
|
29
|
+
declare! :DOCTYPE, :html, :PUBLIC, '-//W3C//DTD XHTML 1.1//EN',
|
30
|
+
'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
|
31
|
+
|
32
|
+
yield html(:xmlns => 'http://www.w3.org/1999/xhtml',
|
33
|
+
:'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
34
|
+
:'xsi:schemaLocation' => 'http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd',
|
35
|
+
:'xml:lang' => 'en') {
|
36
|
+
headd
|
37
|
+
body {
|
38
|
+
ul(:id => 'urls') {
|
39
|
+
li { feed_icon ; search_form }
|
40
|
+
|
41
|
+
gravatar_size = @config.fetch('gravatar_size', 0)
|
42
|
+
|
43
|
+
last = nil
|
44
|
+
urls.each do |mu|
|
45
|
+
li {
|
46
|
+
unless mu.same_author?(last)
|
47
|
+
div(:class => 'icon') {
|
48
|
+
gravatar(mu.email, 's' => gravatar_size, :text => mu.name)
|
49
|
+
} if mu.email and gravatar_size > 0
|
50
|
+
div(mu.name, :class => 'name') if mu.name
|
51
|
+
end
|
52
|
+
|
53
|
+
a(mu.title.strip.gsub(/\s+/, ' '), :href => mu.url)
|
54
|
+
|
55
|
+
mu.hostrec { |hostrec| span(hostrec, :class => 'host') }
|
56
|
+
span(", #{mu.time.fuzzy}", :class => 'date') if
|
57
|
+
@config.fetch('show_dates', true) and mu.time
|
58
|
+
last = mu
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
li { add_form }
|
63
|
+
}
|
64
|
+
|
65
|
+
clear
|
66
|
+
powered_by
|
67
|
+
js
|
68
|
+
}
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def headd
|
73
|
+
head {
|
74
|
+
titlee
|
75
|
+
metas(:description => @config.fetch('description', ''),
|
76
|
+
:viewport =>
|
77
|
+
'width=device-width,minimum-scale=1.0,maximum-scale=1.0')
|
78
|
+
google_verify
|
79
|
+
css(@config.fetch('css_files', []),
|
80
|
+
:prefix => @config.fetch('css_prefix', ''))
|
81
|
+
css('phone.css', :media => 'only screen and (max-device-width: 480px)',
|
82
|
+
:prefix => @config.fetch('css_prefix', ''))
|
83
|
+
atom(@config.fetch('feed_file'))
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def titlee
|
88
|
+
title(@config.fetch('page_title', '') + (@q ? " /#{@q}" : ''))
|
89
|
+
end
|
90
|
+
|
91
|
+
def google_verify
|
92
|
+
(gv = @config.fetch('google_verify')) and metas('verify-v1' => gv)
|
93
|
+
end
|
94
|
+
|
95
|
+
def feed_icon
|
96
|
+
div(:class => 'icon') {
|
97
|
+
a('feed', :href => @config.fetch('feed_file'), :class => 'feed')
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def search_form
|
102
|
+
form(:action => '', :method => 'get') {
|
103
|
+
value = @q
|
104
|
+
Murlsh::Referrer.new(@req.referrer).search_query do |refq|
|
105
|
+
re_parts = refq.split.collect { |x| Regexp.escape(x) }
|
106
|
+
value = "\\b(#{re_parts.join('|')})\\b"
|
107
|
+
end
|
108
|
+
fieldset {
|
109
|
+
input(:type => 'text', :id => 'q', :name => 'q', :size => 32,
|
110
|
+
:value => value)
|
111
|
+
input(:type => 'submit', :value=> 'Regex Search')
|
112
|
+
}
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
def add_form
|
117
|
+
form(:action => '', :method => 'post') {
|
118
|
+
fieldset(:id => 'add') {
|
119
|
+
self.p { add_form_input('Add URL:', 'url', 32) }
|
120
|
+
self.p {
|
121
|
+
add_form_input('Password:', 'auth', 16, 'password')
|
122
|
+
input(:type => 'button', :id => 'submit', :value => 'Add')
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_form_input(label, id, size, tipe='text')
|
129
|
+
label(label, :for => id)
|
130
|
+
input(:type => tipe, :id => id, :name => id, :size => size)
|
131
|
+
end
|
132
|
+
|
133
|
+
def clear
|
134
|
+
div(:style => 'clear : both')
|
135
|
+
end
|
136
|
+
|
137
|
+
def powered_by
|
138
|
+
self.p {
|
139
|
+
text! 'powered by '
|
140
|
+
a('murlsh', :href => 'http://github.com/mmb/murlsh/')
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
def js
|
145
|
+
javascript(%w{
|
146
|
+
jquery-1.3.2.min.js
|
147
|
+
jquery.cookie.js
|
148
|
+
jquery.jgrowl_compressed.js
|
149
|
+
js.js
|
150
|
+
}, :prefix => @config.fetch('js_prefix', ''))
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
%w{
|
2
|
+
rubygems
|
3
|
+
active_record
|
4
|
+
rack
|
5
|
+
}.each { |m| require m }
|
6
|
+
|
7
|
+
module Murlsh
|
8
|
+
|
9
|
+
class UrlServer
|
10
|
+
|
11
|
+
def initialize(config, db)
|
12
|
+
@config = config
|
13
|
+
@db = db
|
14
|
+
ActiveRecord::Base.default_timezone = :utc
|
15
|
+
|
16
|
+
Dir['plugins/*.rb'].each { |p| load p }
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(req)
|
20
|
+
resp = Murlsh::XhtmlResponse.new
|
21
|
+
|
22
|
+
resp.set_content_type(req.env['HTTP_ACCEPT'], req.env['HTTP_USER_AGENT'])
|
23
|
+
|
24
|
+
last_db_update = File::Stat.new(@config['db_file']).mtime
|
25
|
+
resp['Last-Modified'] = last_db_update.httpdate
|
26
|
+
resp['ETag'] = "#{last_db_update.to_i}#{req.params.sort}"
|
27
|
+
|
28
|
+
resp.body = Murlsh::UrlBody.new(@config, @db, req)
|
29
|
+
|
30
|
+
resp
|
31
|
+
end
|
32
|
+
|
33
|
+
def post(req)
|
34
|
+
resp = Rack::Response.new
|
35
|
+
|
36
|
+
url = req.params['url']
|
37
|
+
|
38
|
+
unless url.empty?
|
39
|
+
auth = req.params['auth']
|
40
|
+
if user = auth.empty? ? nil : Murlsh::Auth.new(
|
41
|
+
@config.fetch('auth_file')).auth(auth)
|
42
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3',
|
43
|
+
:database => @config.fetch('db_file'))
|
44
|
+
|
45
|
+
content_type = Murlsh.get_content_type(url)
|
46
|
+
mu = Murlsh::Url.new do |u|
|
47
|
+
u.time = Time.now.gmtime
|
48
|
+
u.url = url
|
49
|
+
u.email = user[:email]
|
50
|
+
u.name = user[:name]
|
51
|
+
u.title = Murlsh.get_title(url, :content_type => content_type)
|
52
|
+
u.content_type = content_type
|
53
|
+
end
|
54
|
+
|
55
|
+
mu.save
|
56
|
+
|
57
|
+
Murlsh::Plugin.hooks('add_post') { |p| p.run(@config) }
|
58
|
+
|
59
|
+
resp['Content-Type'] = 'application/json'
|
60
|
+
|
61
|
+
resp.set_cookie('auth',
|
62
|
+
:expires => Time.mktime(2015, 6, 22),
|
63
|
+
:path => '/',
|
64
|
+
:value => auth)
|
65
|
+
|
66
|
+
resp.body = [mu].to_json
|
67
|
+
else
|
68
|
+
resp.status = 403
|
69
|
+
resp['Content-Type'] = 'text/plain'
|
70
|
+
resp.write('Permission denied')
|
71
|
+
end
|
72
|
+
else
|
73
|
+
resp.status = 500
|
74
|
+
resp['Content-Type'] = 'text/plain'
|
75
|
+
resp.write('No url')
|
76
|
+
end
|
77
|
+
resp
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Murlsh
|
2
|
+
|
3
|
+
class XhtmlResponse < Rack::Response
|
4
|
+
|
5
|
+
# set the content type to application/xhtml+xml for anything that
|
6
|
+
# claims to accept it, for anything else or IE use text/html
|
7
|
+
def set_content_type(http_accept, http_user_agent)
|
8
|
+
self['Content-Type'] = if http_accept and
|
9
|
+
http_accept[/((\*|application)\/\*|application\/xhtml\+xml)/i] and
|
10
|
+
(!http_user_agent or !http_user_agent[/ msie /i])
|
11
|
+
'application/xhtml+xml'
|
12
|
+
else
|
13
|
+
'text/html'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/lib/murlsh.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'murlsh/auth'
|
2
|
+
require 'murlsh/dispatch'
|
3
|
+
require 'murlsh/get_content_type'
|
4
|
+
require 'murlsh/get_title'
|
5
|
+
require 'murlsh/plugin'
|
6
|
+
require 'murlsh/referrer'
|
7
|
+
require 'murlsh/sqlite3_adapter'
|
8
|
+
require 'murlsh/time'
|
9
|
+
require 'murlsh/url_server'
|
10
|
+
require 'murlsh/url'
|
11
|
+
require 'murlsh/xhtml_response'
|
12
|
+
|
13
|
+
# requiring builder before active_record blows up
|
14
|
+
require 'murlsh/atom_feed'
|
15
|
+
require 'murlsh/markup'
|
16
|
+
require 'murlsh/url_body'
|
data/murlsh.gemspec
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{murlsh}
|
8
|
+
s.version = "0.2.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Matthew M. Boedicker"]
|
12
|
+
s.date = %q{2009-10-23}
|
13
|
+
s.default_executable = %q{murlsh}
|
14
|
+
s.description = %q{url sharing site framework with easy adding, title lookup, atom feed, thumbnails and embedding}
|
15
|
+
s.email = %q{matthewm@boedicker.org}
|
16
|
+
s.executables = ["murlsh"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"README.textile"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".gitignore",
|
22
|
+
".htaccess",
|
23
|
+
"COPYING",
|
24
|
+
"README.textile",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"bin/murlsh",
|
28
|
+
"config.ru",
|
29
|
+
"config.yaml",
|
30
|
+
"lib/murlsh.rb",
|
31
|
+
"lib/murlsh/atom_feed.rb",
|
32
|
+
"lib/murlsh/auth.rb",
|
33
|
+
"lib/murlsh/dispatch.rb",
|
34
|
+
"lib/murlsh/get_content_type.rb",
|
35
|
+
"lib/murlsh/get_title.rb",
|
36
|
+
"lib/murlsh/markup.rb",
|
37
|
+
"lib/murlsh/plugin.rb",
|
38
|
+
"lib/murlsh/referrer.rb",
|
39
|
+
"lib/murlsh/sqlite3_adapter.rb",
|
40
|
+
"lib/murlsh/time.rb",
|
41
|
+
"lib/murlsh/url.rb",
|
42
|
+
"lib/murlsh/url_body.rb",
|
43
|
+
"lib/murlsh/url_server.rb",
|
44
|
+
"lib/murlsh/xhtml_response.rb",
|
45
|
+
"murlsh.gemspec",
|
46
|
+
"plugins/update_feed.rb",
|
47
|
+
"public/css/jquery.jgrowl.css",
|
48
|
+
"public/css/phone.css",
|
49
|
+
"public/css/screen.css",
|
50
|
+
"public/js/jquery-1.3.2.min.js",
|
51
|
+
"public/js/jquery.cookie.js",
|
52
|
+
"public/js/jquery.jgrowl_compressed.js",
|
53
|
+
"public/js/js.js",
|
54
|
+
"public/swf/player_mp3_mini.swf",
|
55
|
+
"test/auth_test.rb",
|
56
|
+
"test/get_charset_test.rb",
|
57
|
+
"test/get_content_type_test.rb",
|
58
|
+
"test/get_title_test.rb",
|
59
|
+
"test/markup_test.rb",
|
60
|
+
"test/referrer_test.rb",
|
61
|
+
"test/xhtml_response_test.rb"
|
62
|
+
]
|
63
|
+
s.homepage = %q{http://github.com/mmb/murlsh}
|
64
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
65
|
+
s.require_paths = ["lib"]
|
66
|
+
s.rubygems_version = %q{1.3.5}
|
67
|
+
s.summary = %q{url sharing site framework}
|
68
|
+
s.test_files = [
|
69
|
+
"test/xhtml_response_test.rb",
|
70
|
+
"test/markup_test.rb",
|
71
|
+
"test/referrer_test.rb",
|
72
|
+
"test/get_charset_test.rb",
|
73
|
+
"test/get_content_type_test.rb",
|
74
|
+
"test/auth_test.rb",
|
75
|
+
"test/get_title_test.rb"
|
76
|
+
]
|
77
|
+
|
78
|
+
if s.respond_to? :specification_version then
|
79
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
80
|
+
s.specification_version = 3
|
81
|
+
|
82
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
83
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 2.3.4"])
|
84
|
+
s.add_runtime_dependency(%q<bcrypt>, [">= 2.1.2"])
|
85
|
+
s.add_runtime_dependency(%q<builder>, [">= 2.1.2"])
|
86
|
+
s.add_runtime_dependency(%q<hpricot>, [">= 0.8.1"])
|
87
|
+
s.add_runtime_dependency(%q<htmlentities>, [">= 4.2.0"])
|
88
|
+
s.add_runtime_dependency(%q<rack>, [">= 1.0.1"])
|
89
|
+
s.add_runtime_dependency(%q<sqlite3>, [">= 1.2.5"])
|
90
|
+
else
|
91
|
+
s.add_dependency(%q<activerecord>, [">= 2.3.4"])
|
92
|
+
s.add_dependency(%q<bcrypt>, [">= 2.1.2"])
|
93
|
+
s.add_dependency(%q<builder>, [">= 2.1.2"])
|
94
|
+
s.add_dependency(%q<hpricot>, [">= 0.8.1"])
|
95
|
+
s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
|
96
|
+
s.add_dependency(%q<rack>, [">= 1.0.1"])
|
97
|
+
s.add_dependency(%q<sqlite3>, [">= 1.2.5"])
|
98
|
+
end
|
99
|
+
else
|
100
|
+
s.add_dependency(%q<activerecord>, [">= 2.3.4"])
|
101
|
+
s.add_dependency(%q<bcrypt>, [">= 2.1.2"])
|
102
|
+
s.add_dependency(%q<builder>, [">= 2.1.2"])
|
103
|
+
s.add_dependency(%q<hpricot>, [">= 0.8.1"])
|
104
|
+
s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
|
105
|
+
s.add_dependency(%q<rack>, [">= 1.0.1"])
|
106
|
+
s.add_dependency(%q<sqlite3>, [">= 1.2.5"])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'murlsh'
|
2
|
+
|
3
|
+
module Murlsh
|
4
|
+
|
5
|
+
# regenerate atom feed after a new url has been added
|
6
|
+
class UpdateFeed < Plugin
|
7
|
+
|
8
|
+
Hook = 'add_post'
|
9
|
+
|
10
|
+
def self.run(config)
|
11
|
+
latest = Murlsh::Url.all(:order => 'id DESC',
|
12
|
+
:limit => config.fetch('num_posts_feed', 25))
|
13
|
+
|
14
|
+
feed = Murlsh::AtomFeed.new(config.fetch('root_url'),
|
15
|
+
:filename => config.fetch('feed_file'),
|
16
|
+
:title => config.fetch('page_title', ''))
|
17
|
+
|
18
|
+
feed.write(latest, config.fetch('feed_file'))
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
|
2
|
+
div.jGrowl {
|
3
|
+
padding: 10px;
|
4
|
+
z-index: 9999;
|
5
|
+
}
|
6
|
+
|
7
|
+
/** Special IE6 Style Positioning **/
|
8
|
+
div.ie6 {
|
9
|
+
position: absolute;
|
10
|
+
}
|
11
|
+
|
12
|
+
div.ie6.top-right {
|
13
|
+
right: auto;
|
14
|
+
bottom: auto;
|
15
|
+
left: expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
|
16
|
+
top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
|
17
|
+
}
|
18
|
+
|
19
|
+
div.ie6.top-left {
|
20
|
+
left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
|
21
|
+
top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
|
22
|
+
}
|
23
|
+
|
24
|
+
div.ie6.bottom-right {
|
25
|
+
left: expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
|
26
|
+
top: expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
|
27
|
+
}
|
28
|
+
|
29
|
+
div.ie6.bottom-left {
|
30
|
+
left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
|
31
|
+
top: expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
|
32
|
+
}
|
33
|
+
|
34
|
+
div.ie6.center {
|
35
|
+
left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
|
36
|
+
top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
|
37
|
+
width: 100%;
|
38
|
+
}
|
39
|
+
|
40
|
+
/** Normal Style Positions **/
|
41
|
+
body > div.jGrowl {
|
42
|
+
position: fixed;
|
43
|
+
}
|
44
|
+
|
45
|
+
body > div.jGrowl.top-left {
|
46
|
+
left: 0px;
|
47
|
+
top: 0px;
|
48
|
+
}
|
49
|
+
|
50
|
+
body > div.jGrowl.top-right {
|
51
|
+
right: 0px;
|
52
|
+
top: 0px;
|
53
|
+
}
|
54
|
+
|
55
|
+
body > div.jGrowl.bottom-left {
|
56
|
+
left: 0px;
|
57
|
+
bottom: 0px;
|
58
|
+
}
|
59
|
+
|
60
|
+
body > div.jGrowl.bottom-right {
|
61
|
+
right: 0px;
|
62
|
+
bottom: 0px;
|
63
|
+
}
|
64
|
+
|
65
|
+
body > div.jGrowl.center {
|
66
|
+
top: 0px;
|
67
|
+
width: 50%;
|
68
|
+
left: 25%;
|
69
|
+
}
|
70
|
+
|
71
|
+
/** Cross Browser Styling **/
|
72
|
+
div.center div.jGrowl-notification, div.center div.jGrowl-closer {
|
73
|
+
margin-left: auto;
|
74
|
+
margin-right: auto;
|
75
|
+
}
|
76
|
+
|
77
|
+
div.jGrowl div.jGrowl-notification, div.jGrowl div.jGrowl-closer {
|
78
|
+
background-color: #000;
|
79
|
+
color: #fff;
|
80
|
+
opacity: .85;
|
81
|
+
filter: alpha(opacity = 85);
|
82
|
+
zoom: 1;
|
83
|
+
width: 235px;
|
84
|
+
padding: 10px;
|
85
|
+
margin-top: 5px;
|
86
|
+
margin-bottom: 5px;
|
87
|
+
font-family: Tahoma, Arial, Helvetica, sans-serif;
|
88
|
+
font-size: 12px;
|
89
|
+
text-align: left;
|
90
|
+
display: none;
|
91
|
+
-moz-border-radius: 5px;
|
92
|
+
-webkit-border-radius: 5px;
|
93
|
+
}
|
94
|
+
|
95
|
+
div.jGrowl div.jGrowl-notification {
|
96
|
+
min-height: 40px;
|
97
|
+
}
|
98
|
+
|
99
|
+
div.jGrowl div.jGrowl-notification div.header {
|
100
|
+
font-weight: bold;
|
101
|
+
font-size: 10px;
|
102
|
+
}
|
103
|
+
|
104
|
+
div.jGrowl div.jGrowl-notification div.close {
|
105
|
+
z-index: 99;
|
106
|
+
float: right;
|
107
|
+
font-weight: bold;
|
108
|
+
font-size: 12px;
|
109
|
+
cursor: pointer;
|
110
|
+
}
|
111
|
+
|
112
|
+
div.jGrowl div.jGrowl-closer {
|
113
|
+
height: 15px;
|
114
|
+
padding-top: 4px;
|
115
|
+
padding-bottom: 4px;
|
116
|
+
cursor: pointer;
|
117
|
+
font-size: 11px;
|
118
|
+
font-weight: bold;
|
119
|
+
text-align: center;
|
120
|
+
}
|
121
|
+
|
122
|
+
/** Hide jGrowl when printing **/
|
123
|
+
@media print {
|
124
|
+
div.jGrowl {
|
125
|
+
display: none;
|
126
|
+
}
|
127
|
+
}
|