rad_kit 0.0.6 → 0.0.7
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/lib/components/captcha.rb +24 -0
- data/lib/components/captcha.yml +2 -0
- data/lib/kit/{http_controller → controller}/authorized.rb +1 -1
- data/lib/kit/controller/captcha.rb +23 -0
- data/lib/kit/{http_controller → controller}/localized.rb +1 -1
- data/lib/kit/controller.rb +5 -0
- data/lib/kit/gems.rb +10 -6
- data/lib/kit/i18n/locales/ru/pluralization.rb +62 -0
- data/lib/kit/i18n.rb +16 -0
- data/lib/kit/kit.rb +10 -7
- data/lib/kit/kit_text_utils.rb +30 -0
- data/lib/kit/misc/prepare_model.rb +16 -0
- data/lib/kit/misc/user_error.rb +12 -0
- data/lib/kit/models/attachments_uploader_helper.rb +1 -1
- data/lib/kit/models/authorized.rb +6 -8
- data/lib/kit/models/authorized_object.rb +3 -4
- data/lib/kit/models/{micelaneous.rb → miscellaneous.rb} +0 -0
- data/lib/kit/models_after.rb +1 -1
- data/lib/kit/mongoid/{rad_micelaneous.rb → rad_miscellaneous.rb} +1 -1
- data/lib/kit/mongoid/text_processor.rb +1 -1
- data/lib/kit/mongoid.rb +2 -2
- data/lib/kit/spec.rb +1 -2
- data/lib/kit/tasks.rb +1 -1
- data/lib/text_utils/code_highlighter.rb +75 -0
- data/lib/text_utils/custom_markdown.rb +64 -0
- data/lib/text_utils/ensure_utf.rb +14 -0
- data/lib/text_utils/format_qualifier.rb +13 -0
- data/lib/{kit/text_utils → text_utils}/html_sanitizer.rb +8 -6
- data/lib/text_utils/markdown.rb +33 -0
- data/lib/text_utils/pipe.rb +16 -0
- data/lib/text_utils/processor.rb +14 -0
- data/lib/text_utils/support.rb +15 -0
- data/lib/text_utils/truncate.rb +30 -0
- data/lib/text_utils.rb +23 -0
- data/readme.md +1 -8
- data/spec/controller/authorization_spec.rb +1 -1
- data/spec/controller/captcha_spec.rb +66 -0
- data/spec/i18n/i18n_spec/locales/en/general.yml +5 -0
- data/spec/i18n/i18n_spec/locales/ru/general.yml +7 -0
- data/spec/i18n/i18n_spec.rb +32 -0
- data/spec/misc/kit_text_utils_spec.rb +29 -0
- data/spec/misc/prepare_model_spec.rb +37 -0
- data/spec/misc/user_error_spec.rb +38 -0
- data/spec/models/authorized_object_spec.rb +10 -3
- data/spec/spec_helper/factories.rb +4 -0
- data/spec/spec_helper/user.rb +2 -3
- data/spec/spec_helper.rb +0 -5
- data/spec/text_utils/code_highlighter_spec.rb +38 -0
- data/spec/text_utils/custom_markdown_spec.rb +82 -0
- data/spec/text_utils/format_qualifier_spec.rb +37 -0
- data/spec/text_utils/html_sanitizer_spec.rb +88 -0
- data/spec/text_utils/markdown_spec.rb +114 -0
- data/spec/text_utils/pipe_spec.rb +35 -0
- data/spec/text_utils/spec_helper.rb +26 -0
- data/spec/text_utils/text_processor_shared.rb +9 -0
- data/spec/text_utils/truncate_spec.rb +22 -0
- metadata +57 -47
- data/lib/kit/http_controller.rb +0 -4
- data/lib/kit/text_utils/code_highlighter.rb +0 -58
- data/lib/kit/text_utils/custom_markdown.rb +0 -90
- data/lib/kit/text_utils/ensure_utf.rb +0 -8
- data/lib/kit/text_utils/github_flavoured_markdown.rb +0 -32
- data/lib/kit/text_utils/image_box.rb +0 -35
- data/lib/kit/text_utils/markup.rb +0 -43
- data/lib/kit/text_utils/processor.rb +0 -25
- data/lib/kit/text_utils/tag_shortcuts.rb +0 -14
- data/lib/kit/text_utils/truncate.rb +0 -29
- data/lib/kit/text_utils/truncator.rb +0 -15
- data/lib/kit/text_utils/urls.rb +0 -13
- data/lib/kit/text_utils.rb +0 -43
- data/spec/utils/text_utils_spec.rb +0 -280
@@ -0,0 +1,24 @@
|
|
1
|
+
class Rad::Captcha
|
2
|
+
attr_accessor :public_key, :private_key, :timeout, :enabled, :verify_url
|
3
|
+
attr_required :private_key, :public_key
|
4
|
+
def enabled?; !!enabled end
|
5
|
+
|
6
|
+
def verify request, params
|
7
|
+
recaptcha = nil
|
8
|
+
Timeout::timeout(timeout || 3) do
|
9
|
+
recaptcha = Net::HTTP.post_form URI.parse(verify_url), {
|
10
|
+
'privatekey' => private_key,
|
11
|
+
'remoteip' => request.ip,
|
12
|
+
'challenge' => params.recaptcha_challenge_field,
|
13
|
+
'response' => params.recaptcha_response_field
|
14
|
+
}
|
15
|
+
end
|
16
|
+
result = recaptcha.body.split.map { |s| s.chomp }
|
17
|
+
answer, error = result
|
18
|
+
answer == 'true'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
rad.register :captcha, depends_on: [:template, :controller] do
|
23
|
+
Rad::Captcha.new
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rad::Controller::Captcha
|
2
|
+
inherited do
|
3
|
+
around :ensure_registered_or_human
|
4
|
+
end
|
5
|
+
|
6
|
+
protected
|
7
|
+
def ensure_registered_or_human
|
8
|
+
if rad.captcha.enabled? and rad.user.anonymous? and !request.get?
|
9
|
+
# right now we support capcha for :js format only
|
10
|
+
if request.from_browser? and params.format == 'js'
|
11
|
+
if rad.captcha.verify request, params
|
12
|
+
yield
|
13
|
+
else
|
14
|
+
render '/kit/captcha/action'
|
15
|
+
end
|
16
|
+
else
|
17
|
+
raise_user_error t(:registered_user_or_human_required)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/kit/gems.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
# core gems
|
2
|
-
gem '
|
2
|
+
gem 'i18n', '0.5.0'
|
3
|
+
gem 'redcarpet', '1.17.2'
|
3
4
|
gem 'sanitize', '1.2.1'
|
4
5
|
gem 'stringex', '1.2.0'
|
5
6
|
gem 'state_machine', '0.10.4'
|
6
|
-
# gem '
|
7
|
-
gem '
|
8
|
-
gem 'coderay', '0.9.7'
|
7
|
+
# gem 'factory_girl', '1.3.3'
|
8
|
+
gem 'recaptcha', '0.3.1'
|
9
9
|
|
10
10
|
if respond_to? :fake_gem
|
11
11
|
fake_gem 'rad_core'
|
12
|
-
fake_gem 'rad_ext'
|
13
12
|
fake_gem 'rad_common_interface'
|
14
13
|
fake_gem 'rad_assets'
|
15
14
|
fake_gem 'mongoid_misc'
|
16
|
-
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# old
|
18
|
+
# gem 'bluecloth', '2.0.9'
|
19
|
+
# gem 'paperclip', '2.3.1.1'
|
20
|
+
# gem 'coderay', '0.9.7'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# {
|
2
|
+
# :'ru' => {
|
3
|
+
# pluralize: lambda { |n|
|
4
|
+
# # Правило плюрализации для русского языка, взято из CLDR, http://unicode.org/cldr/
|
5
|
+
# #
|
6
|
+
# #
|
7
|
+
# # Russian language pluralization rules, taken from CLDR project, http://unicode.org/cldr/
|
8
|
+
# #
|
9
|
+
# # one -> n mod 10 is 1 and n mod 100 is not 11;
|
10
|
+
# # few -> n mod 10 in 2..4 and n mod 100 not in 12..14;
|
11
|
+
# # many -> n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
|
12
|
+
# # other -> everything else
|
13
|
+
# #
|
14
|
+
# # Пример
|
15
|
+
# #
|
16
|
+
# # :one = 1, 21, 31, 41, 51, 61...
|
17
|
+
# # :few = 2-4, 22-24, 32-34...
|
18
|
+
# # :many = 0, 5-20, 25-30, 35-40...
|
19
|
+
# # :other = 1.31, 2.31, 5.31...
|
20
|
+
# modulo10 = n.modulo(10)
|
21
|
+
# modulo100 = n.modulo(100)
|
22
|
+
#
|
23
|
+
# if modulo10 == 1 && modulo100 != 11
|
24
|
+
# :one
|
25
|
+
# elsif (modulo10 == 2 || modulo10 == 3 || modulo10 == 4) && !(modulo100 == 12 || modulo100 == 13 || modulo100 == 14)
|
26
|
+
# :few
|
27
|
+
# elsif modulo10 == 0 || (modulo10 == 5 || modulo10 == 6 || modulo10 == 7 || modulo10 == 8 || modulo10 == 9) || (modulo100 == 11 || modulo100 == 12 || modulo100 == 13 || modulo100 == 14)
|
28
|
+
# :many
|
29
|
+
# else
|
30
|
+
# :other
|
31
|
+
# end
|
32
|
+
# }
|
33
|
+
# }
|
34
|
+
# }
|
35
|
+
|
36
|
+
{
|
37
|
+
ru: {
|
38
|
+
i18n: {
|
39
|
+
plural: {
|
40
|
+
rule: lambda{|n|
|
41
|
+
# Правило плюрализации для русского языка, взято из CLDR, http://unicode.org/cldr/
|
42
|
+
#
|
43
|
+
#
|
44
|
+
# Russian language pluralization rules, taken from CLDR project, http://unicode.org/cldr/
|
45
|
+
#
|
46
|
+
# one -> n mod 10 is 1 and n mod 100 is not 11;
|
47
|
+
# few -> n mod 10 in 2..4 and n mod 100 not in 12..14;
|
48
|
+
# many -> n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
|
49
|
+
# other -> everything else
|
50
|
+
#
|
51
|
+
# Пример
|
52
|
+
#
|
53
|
+
# :one = 1, 21, 31, 41, 51, 61...
|
54
|
+
# :few = 2-4, 22-24, 32-34...
|
55
|
+
# :many = 0, 5-20, 25-30, 35-40...
|
56
|
+
# :other = 1.31, 2.31, 5.31...
|
57
|
+
n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
data/lib/kit/i18n.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Hack, ActiveSupport currently uses differrent version
|
2
|
+
# gem 'i18n', '>= 0.4.1'
|
3
|
+
# require 'i18n'
|
4
|
+
|
5
|
+
require "i18n/backend/pluralization"
|
6
|
+
I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
|
7
|
+
|
8
|
+
I18n.load_path += Dir["#{__FILE__.dirname}/i18n/locales/*/*.{rb,yml}"]
|
9
|
+
|
10
|
+
|
11
|
+
#
|
12
|
+
# Helpers for Rad
|
13
|
+
#
|
14
|
+
[Rad::Controller::Abstract, Rad::Controller::Context].each do |klass|
|
15
|
+
klass.delegate :t, to: I18n
|
16
|
+
end
|
data/lib/kit/kit.rb
CHANGED
@@ -2,14 +2,14 @@ require 'kit/support'
|
|
2
2
|
|
3
3
|
# Configs
|
4
4
|
class Rad::Kit
|
5
|
-
attr_accessor :default_item, :fs_prefix, :fs_type, :fs_cache_path, :fs_path, :tags_count
|
6
|
-
|
5
|
+
attr_accessor :default_item, :fs_prefix, :fs_type, :fs_cache_path, :fs_path, :tags_count, :use_code_highlighter
|
6
|
+
attr_required :tags_count, :fs_prefix, :fs_type, :fs_path, :fs_cache_path
|
7
7
|
def items; @items ||= [] end
|
8
8
|
end
|
9
9
|
|
10
10
|
rad.router.class.class_eval do
|
11
11
|
attr_accessor :default_url
|
12
|
-
|
12
|
+
attr_required :default_url
|
13
13
|
end
|
14
14
|
|
15
15
|
rad.config.class.class_eval do
|
@@ -18,12 +18,15 @@ rad.config.class.class_eval do
|
|
18
18
|
def default_viewers; @default_viewers ||= [] end
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
21
|
# Kit
|
23
22
|
#
|
24
|
-
# TODO3 move :text_utils to standalone
|
23
|
+
# TODO3 move :text_utils to standalone gem
|
25
24
|
%w(
|
26
25
|
support
|
27
|
-
|
28
|
-
|
26
|
+
controller
|
27
|
+
i18n
|
28
|
+
kit_text_utils
|
29
|
+
|
30
|
+
misc/prepare_model
|
31
|
+
misc/user_error
|
29
32
|
).each{|f| require "kit/#{f}"}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'text_utils'
|
2
|
+
|
3
|
+
module TextUtils
|
4
|
+
class << self
|
5
|
+
# TODO2 rename
|
6
|
+
def markup data
|
7
|
+
ps = []
|
8
|
+
ps << EnsureUtf
|
9
|
+
ps << HtmlSanitizer
|
10
|
+
ps << FormatQualifier
|
11
|
+
ps << CodeHighlighter if rad.config.use_code_highlighter
|
12
|
+
ps << CustomMarkdown
|
13
|
+
ps << Markdown
|
14
|
+
|
15
|
+
markup = Pipe.new *ps
|
16
|
+
markup.call data
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def truncate data, length
|
21
|
+
truncate = Pipe.new(
|
22
|
+
EnsureUtf,
|
23
|
+
HtmlSanitizer,
|
24
|
+
FormatQualifier,
|
25
|
+
[Truncate, length]
|
26
|
+
)
|
27
|
+
truncate.call data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Rad::Controller::Abstract::ClassMethods.class_eval do
|
2
|
+
def prepare_model aclass, opt = {}
|
3
|
+
opt = opt.symbolize_keys
|
4
|
+
id = opt.delete(:id) || :id
|
5
|
+
variable = opt.delete(:variable) || aclass.alias.underscore
|
6
|
+
|
7
|
+
finder = opt.delete(:finder) || :find!
|
8
|
+
|
9
|
+
method = "prepare_#{variable}"
|
10
|
+
define_method method do
|
11
|
+
model = aclass.send finder, params[id]
|
12
|
+
instance_variable_set "@#{variable}", model
|
13
|
+
end
|
14
|
+
before method, opt
|
15
|
+
end
|
16
|
+
end
|
@@ -43,9 +43,8 @@ module Mongoid::Authorized
|
|
43
43
|
#
|
44
44
|
# Roles
|
45
45
|
#
|
46
|
-
def anonymous?
|
47
|
-
|
48
|
-
end
|
46
|
+
def self.anonymous? name; name == 'anonymous' end
|
47
|
+
def anonymous?; Mongoid::Authorized.anonymous?(name) end
|
49
48
|
|
50
49
|
def registered?
|
51
50
|
!anonymous?
|
@@ -120,8 +119,8 @@ module Mongoid::Authorized
|
|
120
119
|
operation = operation.to_s
|
121
120
|
|
122
121
|
return true if has_role?(:admin)
|
123
|
-
|
124
|
-
custom_method = "able_#{operation}?"
|
122
|
+
|
123
|
+
custom_method = "able_#{operation}?"
|
125
124
|
return object.send custom_method, self if object.respond_to? custom_method
|
126
125
|
|
127
126
|
(
|
@@ -155,10 +154,9 @@ module Mongoid::Authorized
|
|
155
154
|
end
|
156
155
|
|
157
156
|
protected
|
158
|
-
def calculate_effective_roles_for roles
|
157
|
+
def calculate_effective_roles_for roles
|
159
158
|
effective_permissions = {}
|
160
|
-
permissions = ::Mongoid::Authorized.permissions
|
161
|
-
# permissions = rad.config.permissions(DEFAULT_PERMISSIONS).to_h #.to_h(to_s: true)
|
159
|
+
permissions = ::Mongoid::Authorized.permissions
|
162
160
|
permissions.each do |operation, allowed_roles|
|
163
161
|
operation = operation.to_s
|
164
162
|
effective_permissions[operation.to_s] = roles.any?{|role| allowed_roles.include? role}
|
@@ -13,7 +13,7 @@ module Mongoid::AuthorizedObject
|
|
13
13
|
default: [],
|
14
14
|
protected: true
|
15
15
|
|
16
|
-
# Contains the role and all upper roles. So complex becouse we need it in indexes.
|
16
|
+
# Contains the role and all upper roles. So complex becouse we need it in indexes.
|
17
17
|
field :viewers,
|
18
18
|
type: Array,
|
19
19
|
default: lambda{(
|
@@ -128,14 +128,13 @@ module Mongoid::AuthorizedObject
|
|
128
128
|
def normalized_collaborators
|
129
129
|
unless normalized_collaborators = cache[:normalized_collaborators]
|
130
130
|
normalized_collaborators = Role.denormalize_to_higher_roles collaborators
|
131
|
-
normalized_collaborators << "user:#{owner_name}"
|
131
|
+
normalized_collaborators << "user:#{owner_name}" unless Mongoid::Authorized.anonymous?(owner_name)
|
132
132
|
normalized_collaborators.sort!
|
133
133
|
cache[:normalized_collaborators] = normalized_collaborators
|
134
134
|
end
|
135
135
|
normalized_collaborators
|
136
136
|
end
|
137
|
-
|
138
|
-
|
137
|
+
|
139
138
|
#
|
140
139
|
# Special Permissions
|
141
140
|
#
|
File without changes
|
data/lib/kit/models_after.rb
CHANGED
@@ -22,7 +22,7 @@ module Mongoid::TextProcessor
|
|
22
22
|
|
23
23
|
define_method "#{original_attr_name}=" do |value|
|
24
24
|
send "#{original_attr_name}_without_markup=", value
|
25
|
-
send "#{attr_name}_without_markup=",
|
25
|
+
send "#{attr_name}_without_markup=", TextUtils.markup(value)
|
26
26
|
end
|
27
27
|
|
28
28
|
define_method "#{attr_name}_as_text" do
|
data/lib/kit/mongoid.rb
CHANGED
@@ -14,9 +14,9 @@ end
|
|
14
14
|
|
15
15
|
%w(
|
16
16
|
text_processor
|
17
|
-
|
17
|
+
rad_miscellaneous
|
18
18
|
).each{|n| require "kit/mongoid/#{n}"}
|
19
19
|
|
20
|
-
(rad.extension(:mm_plugins){[]} + [Mongoid::
|
20
|
+
(rad.extension(:mm_plugins){[]} + [Mongoid::RadMiscellaneous]).each do |plugin|
|
21
21
|
Mongoid::Document.include plugin
|
22
22
|
end
|
data/lib/kit/spec.rb
CHANGED
data/lib/kit/tasks.rb
CHANGED
@@ -0,0 +1,75 @@
|
|
1
|
+
class TextUtils::CodeHighlighter < TextUtils::Processor
|
2
|
+
# highlights code inside of <code lang/language='java'> ... code ... </code>
|
3
|
+
def call data, env
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
snippets = {}
|
7
|
+
|
8
|
+
# processign html :code tags
|
9
|
+
data = data.gsub /(<code.*?>)(.+?)(<\/code\s*>)/im do
|
10
|
+
node = Nokogiri::HTML($1 + $3).css('code').first
|
11
|
+
language = node.attributes['lang'].try(:value) || node.attributes['language'].try(:value)
|
12
|
+
code = $2
|
13
|
+
|
14
|
+
if language and code
|
15
|
+
attributes = {}; node.attributes.each{|name, value| attributes[name] = value.value}
|
16
|
+
code = colorize code, language, attributes
|
17
|
+
cut_snippet snippets, code
|
18
|
+
else
|
19
|
+
$&
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# processign markdown ``` tags
|
24
|
+
data = data.gsub /^```\s*([a-z\-_0-9]+)\s*\n(.+?)^```\s*$/im do
|
25
|
+
language, code = $1, $2
|
26
|
+
|
27
|
+
if language and code
|
28
|
+
code = colorize code, language, {language: language}
|
29
|
+
cut_snippet snippets, code
|
30
|
+
else
|
31
|
+
$&
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
data = call_next data, env
|
36
|
+
|
37
|
+
restore_snippet snippets, data
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
# temporarilly removing all highlighted code from html to prevent it's beed damaged by next processors
|
42
|
+
def cut_snippet snippets, code
|
43
|
+
key = "CODESNIPPET#{snippets.size}"
|
44
|
+
snippets[key] = code
|
45
|
+
key
|
46
|
+
end
|
47
|
+
|
48
|
+
# inserting cutted code back to html
|
49
|
+
def restore_snippet snippets, data
|
50
|
+
data = data.gsub /(CODESNIPPET[0-9]+)/ do |key|
|
51
|
+
snippets[key]
|
52
|
+
end
|
53
|
+
data
|
54
|
+
end
|
55
|
+
|
56
|
+
def colorize code, language, attributes
|
57
|
+
require 'albino'
|
58
|
+
code = Albino.colorize(code, language.to_sym)
|
59
|
+
code = "<code #{attributes.to_a.collect{|k, v| "#{k}='#{v}'"}.join(' ')}>\n#{code}\n</code>"
|
60
|
+
code = rewrite_styles code
|
61
|
+
end
|
62
|
+
|
63
|
+
# adding prefix 'hl_' to all class names
|
64
|
+
def rewrite_styles html
|
65
|
+
node = Nokogiri::HTML(html).css('code').first
|
66
|
+
node.css("*").each do |e|
|
67
|
+
classes = e.attribute 'class'
|
68
|
+
if classes and classes.value
|
69
|
+
classes = classes.value.strip.split(/\s+/).collect{|c| "hl_#{c}"}.join(' ')
|
70
|
+
e['class'] = classes
|
71
|
+
end
|
72
|
+
end
|
73
|
+
node.to_s
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class TextUtils::CustomMarkdown < TextUtils::Processor
|
2
|
+
def call data, env
|
3
|
+
if env[:format] == :markdown
|
4
|
+
hide_html_tags data do |data|
|
5
|
+
image_box data do |data|
|
6
|
+
call_next data, env
|
7
|
+
end
|
8
|
+
end
|
9
|
+
else
|
10
|
+
call_next data, env
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
def hide_html_tags data, &block
|
16
|
+
snippets, counter = {}, 0
|
17
|
+
data = data.gsub /<.+?>/ do
|
18
|
+
key = "HTMLSNIPPET#{counter}"; counter += 1
|
19
|
+
snippets[key] = $&
|
20
|
+
key
|
21
|
+
end
|
22
|
+
|
23
|
+
data = block.call data
|
24
|
+
|
25
|
+
data = data.gsub /HTMLSNIPPET\d+/ do
|
26
|
+
snippets[$&]
|
27
|
+
end
|
28
|
+
data
|
29
|
+
end
|
30
|
+
|
31
|
+
# !![img] => [![img_thumb]][img]
|
32
|
+
def image_box data, &block
|
33
|
+
img_urls = {}
|
34
|
+
data = data.gsub(/!!\[(.+?)\]/) do
|
35
|
+
img_key = $1 || ''
|
36
|
+
if url = data.scan(/\[#{img_key}\]:\s*([^\s]+)$/).first.try(:first)
|
37
|
+
unless url =~ /\.[^\.]+\.[^\.]+$/ # image.png
|
38
|
+
thumb_img_key = "#{img_key}_thumb"
|
39
|
+
|
40
|
+
# building url with thumb (foo.png => foo.thumb.png)
|
41
|
+
img_urls[thumb_img_key] = url.sub(/\.([^\.]+)$/){".thumb.#{$1}"}
|
42
|
+
|
43
|
+
"[![][#{thumb_img_key}]][#{img_key}]"
|
44
|
+
else # image.(icon|thumb|...).png
|
45
|
+
img_key_full = "#{img_key}_full"
|
46
|
+
|
47
|
+
# building url with thumb (foo.png => foo.thumb.png)
|
48
|
+
img_urls[img_key_full] = url.sub(/\.([^\.]+)\.([^\.]+)$/){".#{$2}"}
|
49
|
+
|
50
|
+
"[![][#{img_key}]][#{img_key_full}]"
|
51
|
+
end
|
52
|
+
else
|
53
|
+
$& || ''
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
unless img_urls.blank?
|
58
|
+
data << "\n"
|
59
|
+
data << img_urls.to_a.collect{|k, v| "[#{k}]: #{v}"}.join("\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
block.call data
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class TextUtils::EnsureUtf < TextUtils::Processor
|
2
|
+
def call data, env
|
3
|
+
data = call_next data, env
|
4
|
+
|
5
|
+
# Escape all non-word unicode symbols, otherwise it will raise error when converting to BSON
|
6
|
+
data = Iconv.conv('UTF-8//IGNORE//TRANSLIT', 'UTF-8', data)
|
7
|
+
|
8
|
+
unless data.encoding == Encoding::UTF_8
|
9
|
+
raise "something wrong happens, invalid encoding (#{data.encoding} instead of utf-8)!"
|
10
|
+
end
|
11
|
+
|
12
|
+
data
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class TextUtils::FormatQualifier < TextUtils::Processor
|
2
|
+
def call data, env
|
3
|
+
env[:format] = (
|
4
|
+
(data =~ /\A\s*<[a-z_\-0-9]+>.*<\/[a-z_\-0-9]+>\s*\z/im) or
|
5
|
+
(data =~ /\A\s*<[a-z_\-0-9]+\/>\s*\z/i)
|
6
|
+
) ? :html : :markdown
|
7
|
+
|
8
|
+
data = call_next data, env
|
9
|
+
|
10
|
+
raise "some processor in pipe clear the data format!" unless env[:format]
|
11
|
+
data
|
12
|
+
end
|
13
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
class
|
1
|
+
class TextUtils::HtmlSanitizer < TextUtils::Processor
|
2
2
|
RELAXED = {
|
3
3
|
elements: [
|
4
4
|
'a', 'b', 'blockquote', 'br', 'caption', 'cite', 'code', 'col',
|
5
5
|
'colgroup', 'dd', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
6
6
|
'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'small', 'strike', 'strong',
|
7
7
|
'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'u',
|
8
|
-
'ul', 'div', 'font', 'span'],
|
8
|
+
'ul', 'div', 'font', 'span', 'iframe'],
|
9
9
|
|
10
10
|
attributes: {
|
11
11
|
:all => ['class', 'style'],
|
@@ -20,7 +20,9 @@ class Rad::TextUtils::HtmlSanitizer < Rad::TextUtils::Processor
|
|
20
20
|
'td' => ['abbr', 'axis', 'colspan', 'rowspan', 'width'],
|
21
21
|
'th' => ['abbr', 'axis', 'colspan', 'rowspan', 'scope', 'width'],
|
22
22
|
'ul' => ['type'],
|
23
|
-
'code' => ['lang', 'language']
|
23
|
+
'code' => ['lang', 'language'],
|
24
|
+
|
25
|
+
'iframe' => ['height', 'scrolling', 'src', 'width']
|
24
26
|
},
|
25
27
|
|
26
28
|
protocols: {
|
@@ -76,10 +78,10 @@ class Rad::TextUtils::HtmlSanitizer < Rad::TextUtils::Processor
|
|
76
78
|
{:whitelist_nodes => [node, parent]}
|
77
79
|
end
|
78
80
|
|
79
|
-
def
|
80
|
-
|
81
|
+
def call data, env
|
82
|
+
data = call_next data, env
|
81
83
|
|
82
|
-
Sanitize.clean(
|
84
|
+
Sanitize.clean(data, RELAXED.merge(
|
83
85
|
transformers: [EMBEDDED_VIDEO],
|
84
86
|
:add_attributes => {
|
85
87
|
all: [:class]
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class TextUtils::Markdown < TextUtils::Processor
|
2
|
+
DEFAULT_OPTIONS = [:autolink, :lax_htmlblock, :smart, :tables, :xhtml, :fenced_code, :strikethrough, :hard_wrap]
|
3
|
+
|
4
|
+
def initialize processor = nil, options = nil
|
5
|
+
super processor
|
6
|
+
@options = options || DEFAULT_OPTIONS
|
7
|
+
end
|
8
|
+
|
9
|
+
def call data, env
|
10
|
+
if env[:format] == :markdown
|
11
|
+
require 'redcarpet'
|
12
|
+
|
13
|
+
data = fix_new_lines data do |data|
|
14
|
+
markdown = Redcarpet.new(data, *@options)
|
15
|
+
markdown.to_html
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
call_next data, env
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
# remove line breaks after images
|
24
|
+
def fix_new_lines data, &block
|
25
|
+
|
26
|
+
|
27
|
+
data = block.call data
|
28
|
+
|
29
|
+
data.gsub /(<img.+?\/>)\s*(<br\s*\/>)\s*\n/ do
|
30
|
+
$1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|