friendly_format 0.5.1 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +14 -1
- data/README +8 -2
- data/Rakefile +19 -32
- data/TODO +1 -0
- data/friendly_format.gemspec +18 -16
- data/lib/friendly_format/adapter/abstract.rb +31 -0
- data/lib/friendly_format/adapter/hpricot_adapter.rb +34 -0
- data/lib/friendly_format/adapter/libxml_adapter.rb +25 -0
- data/lib/friendly_format/adapter/nokogiri_adapter.rb +21 -0
- data/lib/friendly_format/set_common.rb +5 -5
- data/lib/friendly_format/set_strict.rb +2 -2
- data/lib/friendly_format/version.rb +1 -1
- data/lib/friendly_format.rb +162 -99
- data/tasks/ann.rake +1 -1
- data/tasks/gem.rake +11 -2
- data/tasks/post_load.rake +2 -7
- data/tasks/rdoc.rake +4 -3
- data/tasks/setup.rb +35 -22
- data/test/sample/complex_article_result.txt +46 -46
- data/test/test_friendly_format.rb +92 -26
- metadata +22 -10
- data/tasks/manifest.rake +0 -48
data/CHANGES
CHANGED
@@ -1,4 +1,17 @@
|
|
1
|
-
=
|
1
|
+
= friendly_format changes history
|
2
|
+
|
3
|
+
== friendly_format 0.6.1 / 2009-04-05
|
4
|
+
|
5
|
+
* added nokogiri and libxml-ruby support.
|
6
|
+
* drop support for hpricot < 0.7
|
7
|
+
* you can explicitly choose adapter, or auto-choose:
|
8
|
+
1. hpricot or falls back to:
|
9
|
+
2. nokogiri or falls back to:
|
10
|
+
3. libxml-ruby
|
11
|
+
* now internally, we store string instead of symbol
|
12
|
+
for accepted tags to prevent from symbol explosion.
|
13
|
+
* ruby 1.9 compliant
|
14
|
+
* escape gt(>) in pre tag and forbidden tag as well.
|
2
15
|
|
3
16
|
== friendly_format 0.5.1 / 2008-12-11
|
4
17
|
|
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= friendly_format 0.
|
1
|
+
= friendly_format 0.6
|
2
2
|
by Lin Jen-Shin (a.k.a. godfat-真常[http://godfat.org])
|
3
3
|
godfat (XD) godfat.org
|
4
4
|
|
@@ -18,9 +18,15 @@ by Lin Jen-Shin (a.k.a. godfat-真常[http://godfat.org])
|
|
18
18
|
FriendlyFormat.format_article(text, FriendlyFormat::SetStrict.new)
|
19
19
|
FriendlyFormat.format_article(text, FriendlyFormat::SetCommon.new)
|
20
20
|
|
21
|
+
FriendlyFormat.adapter = FriendlyFormat::NokogiriAdapter
|
22
|
+
FriendlyFormat.adapter = FriendlyFormat::LibxmlAdapter
|
23
|
+
FriendlyFormat.adapter = FriendlyFormat::HpricotAdapter # default
|
24
|
+
|
21
25
|
== REQUIREMENTS:
|
22
26
|
|
23
|
-
* hpricot
|
27
|
+
* hpricot >=0.6 or
|
28
|
+
* nokogiri >=1.1 or
|
29
|
+
* libxml-ruby >=0.9
|
24
30
|
|
25
31
|
== INSTALL:
|
26
32
|
|
data/Rakefile
CHANGED
@@ -1,44 +1,26 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
Bones.setup
|
6
|
-
rescue LoadError
|
7
|
-
load 'tasks/setup.rb' # this line should already be there
|
8
|
-
end
|
3
|
+
require 'bones'
|
4
|
+
Bones.setup
|
9
5
|
|
10
6
|
PROJ.name = 'friendly_format'
|
11
|
-
# supress warnings, there's too many warnings in dm-core
|
12
|
-
# PROJ.ruby_opts.delete '-w'
|
13
|
-
|
14
|
-
PROJ.gem.dependencies << ['hpricot', '>=0.6.0']
|
15
|
-
PROJ.gem.development_dependencies << ['minitest', '>=1.3.0']
|
16
|
-
# PROJ.gem.executables = ["bin/#{PROJ.name}"]
|
17
|
-
|
18
|
-
task :default do
|
19
|
-
Rake.application.options.show_task_pattern = /./
|
20
|
-
Rake.application.display_tasks_and_comments
|
21
|
-
end
|
22
|
-
|
23
|
-
namespace :gem do
|
24
|
-
desc "create #{PROJ.name}.gemspec"
|
25
|
-
task 'gemspec' do
|
26
|
-
puts "rake gem:debug > #{PROJ.name}.gemspec"
|
27
|
-
File.open("#{PROJ.name}.gemspec", 'w'){|spec| spec << `rake gem:debug`.sub(/.*/, '')}
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
7
|
PROJ.authors = 'Lin Jen-Shin (a.k.a. godfat 真常)'
|
32
8
|
PROJ.email = 'godfat (XD) godfat.org'
|
33
9
|
PROJ.url = "http://github.com/godfat/#{PROJ.name}"
|
10
|
+
PROJ.rubyforge.name = 'ludy'
|
11
|
+
|
12
|
+
PROJ.gem.development_dependencies << ['hpricot', '>=0.6'] <<
|
13
|
+
['nokogiri', '>=1.1'] <<
|
14
|
+
['libxml-ruby', '>=0.9']
|
15
|
+
|
16
|
+
# PROJ.ruby_opts.delete '-w'
|
17
|
+
|
34
18
|
PROJ.description = PROJ.summary = paragraphs_of('README', 'description').join("\n\n")
|
35
19
|
PROJ.changes = paragraphs_of('CHANGES', 0..1).join("\n\n")
|
36
|
-
PROJ.rubyforge.name = 'ludy'
|
37
20
|
PROJ.version = File.read("lib/#{PROJ.name}/version.rb").gsub(/.*VERSION = '(.*)'.*/m, '\1')
|
38
21
|
|
39
|
-
PROJ.
|
40
|
-
|
41
|
-
'^\.gitignore$', '^ann-', '\.sqlite3$', '\.db$']
|
22
|
+
PROJ.exclude += ['^tmp', 'tmp$', '^pkg', '^\.gitignore$',
|
23
|
+
'^ann-', '\.sqlite3$', '\.db$']
|
42
24
|
|
43
25
|
PROJ.rdoc.remote_dir = PROJ.name
|
44
26
|
|
@@ -46,8 +28,8 @@ PROJ.readme_file = 'README'
|
|
46
28
|
PROJ.rdoc.main = 'README'
|
47
29
|
PROJ.rdoc.exclude += ['Rakefile', '^tasks', '^test']
|
48
30
|
PROJ.rdoc.include << '\w+'
|
49
|
-
PROJ.rdoc.opts << '--diagram' if !
|
50
|
-
PROJ.rdoc.opts += ['--charset=utf-8', '--inline-source',
|
31
|
+
# PROJ.rdoc.opts << '--diagram' if !Rake::Win32 and `which dot` =~ %r/\/dot/
|
32
|
+
PROJ.rdoc.opts += ['--charset=utf-8', '--inline-source',
|
51
33
|
'--line-numbers', '--promiscuous']
|
52
34
|
|
53
35
|
PROJ.spec.opts << '--color'
|
@@ -56,3 +38,8 @@ PROJ.ann.file = "ann-#{PROJ.name}-#{PROJ.version}"
|
|
56
38
|
PROJ.ann.paragraphs.concat %w[LINKS SYNOPSIS REQUIREMENTS INSTALL LICENSE]
|
57
39
|
|
58
40
|
CLEAN.include Dir['**/*.rbc']
|
41
|
+
|
42
|
+
task :default do
|
43
|
+
Rake.application.options.show_task_pattern = /./
|
44
|
+
Rake.application.display_tasks_and_comments
|
45
|
+
end
|
data/TODO
CHANGED
data/friendly_format.gemspec
CHANGED
@@ -1,24 +1,23 @@
|
|
1
|
-
|
2
1
|
# -*- encoding: utf-8 -*-
|
3
2
|
|
4
3
|
Gem::Specification.new do |s|
|
5
4
|
s.name = %q{friendly_format}
|
6
|
-
s.version = "0.
|
5
|
+
s.version = "0.6.1"
|
7
6
|
|
8
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
8
|
s.authors = ["Lin Jen-Shin (a.k.a. godfat \347\234\237\345\270\270)"]
|
10
|
-
s.date = %q{
|
11
|
-
s.description = %q{}
|
9
|
+
s.date = %q{2009-04-05}
|
10
|
+
s.description = %q{make user input be valid xhtml and format it with gsub("\n", "<br/>") etc. you can partially allow some tags and don't escape them.}
|
12
11
|
s.email = %q{godfat (XD) godfat.org}
|
13
12
|
s.extra_rdoc_files = ["CHANGES", "LICENSE", "NOTICE", "README", "TODO", "friendly_format.gemspec"]
|
14
|
-
s.files = ["CHANGES", "LICENSE", "NOTICE", "README", "Rakefile", "TODO", "friendly_format.gemspec", "lib/friendly_format.rb", "lib/friendly_format/
|
13
|
+
s.files = ["CHANGES", "LICENSE", "NOTICE", "README", "Rakefile", "TODO", "friendly_format.gemspec", "lib/friendly_format.rb", "lib/friendly_format/adapter/abstract.rb", "lib/friendly_format/adapter/hpricot_adapter.rb", "lib/friendly_format/adapter/libxml_adapter.rb", "lib/friendly_format/adapter/nokogiri_adapter.rb", "lib/friendly_format/set_common.rb", "lib/friendly_format/set_strict.rb", "lib/friendly_format/version.rb", "test/sample/complex_article.txt", "test/sample/complex_article_result.txt", "test/test_friendly_format.rb"]
|
15
14
|
s.has_rdoc = true
|
16
15
|
s.homepage = %q{http://github.com/godfat/friendly_format}
|
17
|
-
s.rdoc_options = ["--
|
16
|
+
s.rdoc_options = ["--charset=utf-8", "--inline-source", "--line-numbers", "--promiscuous", "--main", "README"]
|
18
17
|
s.require_paths = ["lib"]
|
19
18
|
s.rubyforge_project = %q{ludy}
|
20
19
|
s.rubygems_version = %q{1.3.1}
|
21
|
-
s.summary = %q{}
|
20
|
+
s.summary = %q{make user input be valid xhtml and format it with gsub("\n", "<br/>") etc. you can partially allow some tags and don't escape them.}
|
22
21
|
s.test_files = ["test/test_friendly_format.rb"]
|
23
22
|
|
24
23
|
if s.respond_to? :specification_version then
|
@@ -26,17 +25,20 @@ Gem::Specification.new do |s|
|
|
26
25
|
s.specification_version = 2
|
27
26
|
|
28
27
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
29
|
-
s.
|
30
|
-
s.add_development_dependency(%q<
|
31
|
-
s.add_development_dependency(%q<
|
28
|
+
s.add_development_dependency(%q<bones>, [">= 2.4.2"])
|
29
|
+
s.add_development_dependency(%q<hpricot>, [">= 0.6"])
|
30
|
+
s.add_development_dependency(%q<nokogiri>, [">= 1.1"])
|
31
|
+
s.add_development_dependency(%q<libxml-ruby>, [">= 0.9"])
|
32
32
|
else
|
33
|
-
s.add_dependency(%q<
|
34
|
-
s.add_dependency(%q<
|
35
|
-
s.add_dependency(%q<
|
33
|
+
s.add_dependency(%q<bones>, [">= 2.4.2"])
|
34
|
+
s.add_dependency(%q<hpricot>, [">= 0.6"])
|
35
|
+
s.add_dependency(%q<nokogiri>, [">= 1.1"])
|
36
|
+
s.add_dependency(%q<libxml-ruby>, [">= 0.9"])
|
36
37
|
end
|
37
38
|
else
|
38
|
-
s.add_dependency(%q<
|
39
|
-
s.add_dependency(%q<
|
40
|
-
s.add_dependency(%q<
|
39
|
+
s.add_dependency(%q<bones>, [">= 2.4.2"])
|
40
|
+
s.add_dependency(%q<hpricot>, [">= 0.6"])
|
41
|
+
s.add_dependency(%q<nokogiri>, [">= 1.1"])
|
42
|
+
s.add_dependency(%q<libxml-ruby>, [">= 0.9"])
|
41
43
|
end
|
42
44
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
module FriendlyFormat
|
3
|
+
module Abstract
|
4
|
+
|
5
|
+
def method_name node
|
6
|
+
# discard body
|
7
|
+
node.children.to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_xhtml node
|
11
|
+
node.to_xhtml
|
12
|
+
end
|
13
|
+
|
14
|
+
def content node
|
15
|
+
node.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def element? node
|
19
|
+
node.element?
|
20
|
+
end
|
21
|
+
|
22
|
+
def text? node
|
23
|
+
node.text?
|
24
|
+
end
|
25
|
+
|
26
|
+
def empty? node
|
27
|
+
node.children.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
end # of Abstract
|
31
|
+
end # of FriendlyFormat
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
module FriendlyFormat
|
5
|
+
class HpricotAdapter
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def parse html
|
9
|
+
Hpricot.parse(html)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_xhtml node
|
13
|
+
node.to_html
|
14
|
+
end
|
15
|
+
|
16
|
+
def content node
|
17
|
+
node.content
|
18
|
+
end
|
19
|
+
|
20
|
+
def element? node
|
21
|
+
node.kind_of?(Hpricot::Elem)
|
22
|
+
end
|
23
|
+
|
24
|
+
def text? node
|
25
|
+
node.kind_of?(Hpricot::Text)
|
26
|
+
end
|
27
|
+
|
28
|
+
def empty? node
|
29
|
+
node.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
end # of class method for HpricotAdapter
|
33
|
+
end # of HpricotAdapter
|
34
|
+
end # of FriendlyFormat
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
require 'friendly_format/adapter/abstract'
|
3
|
+
require 'libxml'
|
4
|
+
|
5
|
+
module FriendlyFormat
|
6
|
+
class LibxmlAdapter
|
7
|
+
extend Abstract
|
8
|
+
|
9
|
+
def self.parse html
|
10
|
+
parser = LibXML::XML::HTMLParser.string(
|
11
|
+
"<zzz>#{html}</zzz>",
|
12
|
+
:options => LibXML::XML::HTMLParser::Options::RECOVER)
|
13
|
+
|
14
|
+
# root is html, children is [body], first is body
|
15
|
+
# same as nokogiri
|
16
|
+
# drop zzz with .children.first since it would wrap a tag p for the article
|
17
|
+
parser.parse.root.children.first.children.first
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.to_xhtml node
|
21
|
+
node.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
end # of LibxmlAdapter
|
25
|
+
end # of FriendlyFormat
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
require 'friendly_format/adapter/abstract'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module FriendlyFormat
|
6
|
+
class NokogiriAdapter
|
7
|
+
extend Abstract
|
8
|
+
|
9
|
+
def self.parse html
|
10
|
+
# root is html, children is [body], first is body
|
11
|
+
# same as libxml
|
12
|
+
# drop zzz with .children.first since it would wrap a tag p for the article
|
13
|
+
Nokogiri::HTML.parse(
|
14
|
+
"<zzz>#{html}</zzz>",
|
15
|
+
nil, # url?
|
16
|
+
html.respond_to?(:encoding) ? html.encoding.name : 'utf-8'
|
17
|
+
).root.children.first.children.first
|
18
|
+
end
|
19
|
+
|
20
|
+
end # of NokogiriAdapter
|
21
|
+
end # of FriendlyFormat
|
@@ -6,11 +6,11 @@ module FriendlyFormat
|
|
6
6
|
# a few people have permission to post or edit.
|
7
7
|
class SetCommon < Set
|
8
8
|
def initialize
|
9
|
-
super([
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
super(%w[ a area b big blockquote br
|
10
|
+
center cite code del div em
|
11
|
+
font h1 h2 h3 h4 h5 h6 hr
|
12
|
+
i img li map object ol p
|
13
|
+
pre small span strong u ul ])
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
data/lib/friendly_format.rb
CHANGED
@@ -5,21 +5,45 @@ require 'friendly_format/set_strict'
|
|
5
5
|
|
6
6
|
# 2008-05-09 godfat
|
7
7
|
module FriendlyFormat
|
8
|
+
autoload(:LibxmlAdapter, 'friendly_format/adapter/libxml_adapter')
|
9
|
+
autoload(:HpricotAdapter, 'friendly_format/adapter/hpricot_adapter')
|
10
|
+
autoload(:NokogiriAdapter, 'friendly_format/adapter/nokogiri_adapter')
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_writer(:adapter)
|
14
|
+
def adapter
|
15
|
+
@adapter ||= begin
|
16
|
+
HpricotAdapter
|
17
|
+
rescue LoadError
|
18
|
+
begin
|
19
|
+
NokogiriAdapter
|
20
|
+
rescue LoadError
|
21
|
+
LibxmlAdapter
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
8
27
|
module_function
|
9
28
|
# format entire article for you, passing allowed tags to it.
|
10
29
|
# you can use Set or Symbol to specify which tags would be allowed.
|
11
30
|
# default was no tags at all, all tags would be escaped.
|
12
31
|
# it uses Hpricot to parse input.
|
13
32
|
def format_article html, *args
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
33
|
+
return html if html.strip == ''
|
34
|
+
|
35
|
+
FriendlyFormat.force_encoding(
|
36
|
+
FriendlyFormat.format_article_entrance(html,
|
37
|
+
args.inject(Set.new){ |allowed_tags, arg|
|
38
|
+
case arg
|
39
|
+
when String; allowed_tags << arg
|
40
|
+
when Symbol; allowed_tags << arg.to_s
|
41
|
+
when Set; allowed_tags += Set.new(arg.map{|a|a.to_s})
|
42
|
+
else; raise(TypeError.new("expected String|Symbol|Set, got #{arg.class}"))
|
43
|
+
end
|
44
|
+
allowed_tags
|
45
|
+
}),
|
46
|
+
html)
|
23
47
|
end
|
24
48
|
|
25
49
|
# automaticly add "a href" tag on text starts from
|
@@ -27,14 +51,12 @@ module FriendlyFormat
|
|
27
51
|
# regexp translated from drupal to find where's the target.
|
28
52
|
# it uses simplified regexp to do the task. see format_url.
|
29
53
|
def format_autolink html, attrs = {}
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
}
|
37
|
-
doc.to_html
|
54
|
+
return html if html.strip == ''
|
55
|
+
|
56
|
+
FriendlyFormat.force_encoding(
|
57
|
+
FriendlyFormat.format_autolink_rec(
|
58
|
+
FriendlyFormat.adapter.parse(html), attrs),
|
59
|
+
html)
|
38
60
|
end
|
39
61
|
|
40
62
|
# translated from drupal-6.2/modules/filter/filter.module
|
@@ -65,27 +87,6 @@ module FriendlyFormat
|
|
65
87
|
}[1..-1]
|
66
88
|
end
|
67
89
|
|
68
|
-
# same as format_autolink_regexp, but it's simplified and
|
69
|
-
# cannot process text composed with html and plain text.
|
70
|
-
# used in format_autolink.
|
71
|
-
def format_url text, attrs = {}
|
72
|
-
# translated from drupal-6.2/modules/filter/filter.module
|
73
|
-
# Match absolute URLs.
|
74
|
-
text.gsub(
|
75
|
-
%r{((http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://|www\.)([a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+*~#&=/;-]))([.,?!]*?)}i){ |match|
|
76
|
-
url = $1 # is there any other way to get this variable?
|
77
|
-
|
78
|
-
caption = FriendlyFormat.trim url
|
79
|
-
attrs = attrs.map{ |k,v| " #{k}=\"#{v}\""}.join
|
80
|
-
|
81
|
-
# Match www domains/addresses.
|
82
|
-
url = "http://#{url}" unless url =~ %r{^http://}
|
83
|
-
"<a href=\"#{url}\" title=\"#{url}\"#{attrs}>#{caption}</a>"
|
84
|
-
# Match e-mail addresses.
|
85
|
-
}.gsub( %r{([A-Za-z0-9._-]+@[A-Za-z0-9._+-]+\.[A-Za-z]{2,4})([.,?!]*?)}i,
|
86
|
-
'<a href="mailto:\1">\1</a>')
|
87
|
-
end
|
88
|
-
|
89
90
|
# convert newline character(s) to <br />
|
90
91
|
def format_newline text
|
91
92
|
# windows: \r\n
|
@@ -93,79 +94,141 @@ module FriendlyFormat
|
|
93
94
|
text.gsub("\r\n", "\n").tr("\r", "\n").gsub("\n", '<br />')
|
94
95
|
end
|
95
96
|
|
96
|
-
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
97
|
+
|
98
|
+
# private below
|
99
|
+
|
100
|
+
class << self
|
101
|
+
# extract it to public?
|
102
|
+
# @api private
|
103
|
+
def trim text, length = 75
|
104
|
+
# Use +3 for '...' string length.
|
105
|
+
if text.size <= 3
|
106
|
+
'...'
|
107
|
+
elsif text.size > length
|
108
|
+
"#{text[0...length-3]}..."
|
109
|
+
else
|
110
|
+
text
|
111
|
+
end
|
106
112
|
end
|
107
|
-
end
|
108
113
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
#
|
113
|
-
|
114
|
-
|
115
|
-
#
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
114
|
+
# same as format_autolink_regexp, but it's simplified and
|
115
|
+
# cannot process text composed with html and plain text.
|
116
|
+
# used in format_autolink.
|
117
|
+
# @api private
|
118
|
+
def format_url text, attrs = {}
|
119
|
+
# translated from drupal-6.2/modules/filter/filter.module
|
120
|
+
# Match absolute URLs.
|
121
|
+
text.gsub(
|
122
|
+
%r{((http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://|www\.)([a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+*~#&=/;-]))([.,?!]*?)}i){ |match|
|
123
|
+
url = $1 # is there any other way to get this variable?
|
124
|
+
caption = trim(url)
|
125
|
+
html_attrs = attrs.map{ |k,v| " #{k}=\"#{v}\""}.join
|
126
|
+
|
127
|
+
# Match www domains/addresses.
|
128
|
+
url = "http://#{url}" unless url =~ %r{^http://}
|
129
|
+
"<a href=\"#{url}\" title=\"#{url}\"#{html_attrs}>#{caption}</a>"
|
130
|
+
# Match e-mail addresses.
|
131
|
+
}.gsub( %r{([A-Za-z0-9._-]+@[A-Za-z0-9._+-]+\.[A-Za-z]{2,4})([.,?!]*?)}i,
|
132
|
+
'<a href="mailto:\1">\1</a>')
|
133
|
+
end
|
120
134
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
135
|
+
# perhaps we should escape all inside code instead of pre?
|
136
|
+
# @api private
|
137
|
+
def escape_ltgt_inside_pre html, allowed_tags
|
138
|
+
return html unless allowed_tags.member?('pre')
|
139
|
+
# don't bother nested pre, because we escape all tags in pre
|
140
|
+
html = html + '</pre>' unless html =~ %r{</pre>}i
|
141
|
+
html.gsub(%r{<pre>(.*)</pre>}mi){
|
142
|
+
# stop escaping for '>' because drupal's url filter would make > into url...
|
143
|
+
# is there any other way to get matched group?
|
144
|
+
"<pre>#{escape_ltgt($1)}</pre>"
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
# @api private
|
149
|
+
def format_autolink_rec elem, attrs = {}
|
150
|
+
elem.children.map{ |e|
|
151
|
+
if adapter.text?(e)
|
152
|
+
format_url(e.content, attrs)
|
153
|
+
|
154
|
+
elsif adapter.element?(e)
|
155
|
+
if adapter.empty?(e)
|
156
|
+
adapter.to_xhtml(e)
|
157
|
+
else
|
158
|
+
"<#{e.name}>" +
|
159
|
+
format_autolink_rec(e, attrs) +
|
160
|
+
"</#{e.name}>"
|
161
|
+
end
|
127
162
|
|
128
|
-
# recursion
|
129
|
-
def self.format_article_elems elems, allowed_tags = Set.new, no_format_newline = false
|
130
|
-
elems.children.map{ |e|
|
131
|
-
if e.kind_of?(Hpricot::Text)
|
132
|
-
if no_format_newline
|
133
|
-
format_url(e.content)
|
134
163
|
else
|
135
|
-
|
164
|
+
e
|
165
|
+
|
136
166
|
end
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
167
|
+
|
168
|
+
}.join
|
169
|
+
end
|
170
|
+
|
171
|
+
# recursion entrance
|
172
|
+
# @api private
|
173
|
+
def format_article_entrance html, allowed_tags = Set.new
|
174
|
+
format_article_rec(
|
175
|
+
adapter.parse(escape_ltgt_inside_pre(html, allowed_tags)),
|
176
|
+
allowed_tags)
|
177
|
+
end
|
178
|
+
|
179
|
+
# recursion
|
180
|
+
# @api private
|
181
|
+
def format_article_rec elem, allowed_tags = Set.new, no_format_newline = false
|
182
|
+
elem.children.map{ |e|
|
183
|
+
if adapter.text?(e)
|
184
|
+
if no_format_newline
|
185
|
+
format_url(adapter.content(e))
|
141
186
|
else
|
142
|
-
e
|
143
|
-
FriendlyFormat.format_article_elems(e, allowed_tags, e.stag.name == 'pre') +
|
144
|
-
(e.etag || Hpricot::ETag.new(e.stag.name)).inspect
|
187
|
+
format_newline(format_url(adapter.content(e)))
|
145
188
|
end
|
146
|
-
|
147
|
-
|
148
|
-
|
189
|
+
|
190
|
+
elsif adapter.element?(e)
|
191
|
+
if allowed_tags.member?(e.name)
|
192
|
+
if adapter.empty?(e) || e.name == 'a'
|
193
|
+
adapter.to_xhtml(e)
|
194
|
+
else
|
195
|
+
"<#{e.name}>" +
|
196
|
+
format_article_rec(
|
197
|
+
e, allowed_tags, e.name == 'pre') +
|
198
|
+
"</#{e.name}>"
|
199
|
+
end
|
149
200
|
else
|
150
|
-
|
151
|
-
|
152
|
-
|
201
|
+
if adapter.empty?(e)
|
202
|
+
"<#{e.name}>"
|
203
|
+
else
|
204
|
+
"<#{e.name}>" +
|
205
|
+
format_article_rec(e, allowed_tags) +
|
206
|
+
"</#{e.name}>"
|
207
|
+
end
|
153
208
|
end
|
209
|
+
|
154
210
|
end
|
155
|
-
|
156
|
-
|
157
|
-
end
|
211
|
+
}.join
|
212
|
+
end
|
158
213
|
|
159
|
-
|
160
|
-
|
161
|
-
|
214
|
+
# i cannot find a way to escape both lt and gt,
|
215
|
+
# so it's a trick that just escape lt and no browser
|
216
|
+
# would treat complex lt and gt structure to be a tag
|
217
|
+
# wraping content.
|
218
|
+
# @api private
|
219
|
+
def escape_ltgt text
|
220
|
+
text.gsub('<', '<').gsub('>', '>')
|
221
|
+
end
|
162
222
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
223
|
+
# force encoding for ruby 1.9
|
224
|
+
# @api private
|
225
|
+
def force_encoding output, input
|
226
|
+
if output.respond_to?(:force_encoding)
|
227
|
+
output.force_encoding(input.encoding)
|
228
|
+
else
|
229
|
+
output
|
230
|
+
end
|
231
|
+
end
|
170
232
|
|
233
|
+
end
|
171
234
|
end
|
data/tasks/ann.rake
CHANGED
@@ -42,7 +42,7 @@ namespace :ann do
|
|
42
42
|
desc "Send an email announcement"
|
43
43
|
task :email => ['ann:prereqs', PROJ.ann.file] do
|
44
44
|
ann = PROJ.ann
|
45
|
-
from = ann.email[:from] || PROJ.email
|
45
|
+
from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
|
46
46
|
to = Array(ann.email[:to])
|
47
47
|
|
48
48
|
### build a mail header for RFC 822
|