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.
Files changed (71) hide show
  1. data/lib/components/captcha.rb +24 -0
  2. data/lib/components/captcha.yml +2 -0
  3. data/lib/kit/{http_controller → controller}/authorized.rb +1 -1
  4. data/lib/kit/controller/captcha.rb +23 -0
  5. data/lib/kit/{http_controller → controller}/localized.rb +1 -1
  6. data/lib/kit/controller.rb +5 -0
  7. data/lib/kit/gems.rb +10 -6
  8. data/lib/kit/i18n/locales/ru/pluralization.rb +62 -0
  9. data/lib/kit/i18n.rb +16 -0
  10. data/lib/kit/kit.rb +10 -7
  11. data/lib/kit/kit_text_utils.rb +30 -0
  12. data/lib/kit/misc/prepare_model.rb +16 -0
  13. data/lib/kit/misc/user_error.rb +12 -0
  14. data/lib/kit/models/attachments_uploader_helper.rb +1 -1
  15. data/lib/kit/models/authorized.rb +6 -8
  16. data/lib/kit/models/authorized_object.rb +3 -4
  17. data/lib/kit/models/{micelaneous.rb → miscellaneous.rb} +0 -0
  18. data/lib/kit/models_after.rb +1 -1
  19. data/lib/kit/mongoid/{rad_micelaneous.rb → rad_miscellaneous.rb} +1 -1
  20. data/lib/kit/mongoid/text_processor.rb +1 -1
  21. data/lib/kit/mongoid.rb +2 -2
  22. data/lib/kit/spec.rb +1 -2
  23. data/lib/kit/tasks.rb +1 -1
  24. data/lib/text_utils/code_highlighter.rb +75 -0
  25. data/lib/text_utils/custom_markdown.rb +64 -0
  26. data/lib/text_utils/ensure_utf.rb +14 -0
  27. data/lib/text_utils/format_qualifier.rb +13 -0
  28. data/lib/{kit/text_utils → text_utils}/html_sanitizer.rb +8 -6
  29. data/lib/text_utils/markdown.rb +33 -0
  30. data/lib/text_utils/pipe.rb +16 -0
  31. data/lib/text_utils/processor.rb +14 -0
  32. data/lib/text_utils/support.rb +15 -0
  33. data/lib/text_utils/truncate.rb +30 -0
  34. data/lib/text_utils.rb +23 -0
  35. data/readme.md +1 -8
  36. data/spec/controller/authorization_spec.rb +1 -1
  37. data/spec/controller/captcha_spec.rb +66 -0
  38. data/spec/i18n/i18n_spec/locales/en/general.yml +5 -0
  39. data/spec/i18n/i18n_spec/locales/ru/general.yml +7 -0
  40. data/spec/i18n/i18n_spec.rb +32 -0
  41. data/spec/misc/kit_text_utils_spec.rb +29 -0
  42. data/spec/misc/prepare_model_spec.rb +37 -0
  43. data/spec/misc/user_error_spec.rb +38 -0
  44. data/spec/models/authorized_object_spec.rb +10 -3
  45. data/spec/spec_helper/factories.rb +4 -0
  46. data/spec/spec_helper/user.rb +2 -3
  47. data/spec/spec_helper.rb +0 -5
  48. data/spec/text_utils/code_highlighter_spec.rb +38 -0
  49. data/spec/text_utils/custom_markdown_spec.rb +82 -0
  50. data/spec/text_utils/format_qualifier_spec.rb +37 -0
  51. data/spec/text_utils/html_sanitizer_spec.rb +88 -0
  52. data/spec/text_utils/markdown_spec.rb +114 -0
  53. data/spec/text_utils/pipe_spec.rb +35 -0
  54. data/spec/text_utils/spec_helper.rb +26 -0
  55. data/spec/text_utils/text_processor_shared.rb +9 -0
  56. data/spec/text_utils/truncate_spec.rb +22 -0
  57. metadata +57 -47
  58. data/lib/kit/http_controller.rb +0 -4
  59. data/lib/kit/text_utils/code_highlighter.rb +0 -58
  60. data/lib/kit/text_utils/custom_markdown.rb +0 -90
  61. data/lib/kit/text_utils/ensure_utf.rb +0 -8
  62. data/lib/kit/text_utils/github_flavoured_markdown.rb +0 -32
  63. data/lib/kit/text_utils/image_box.rb +0 -35
  64. data/lib/kit/text_utils/markup.rb +0 -43
  65. data/lib/kit/text_utils/processor.rb +0 -25
  66. data/lib/kit/text_utils/tag_shortcuts.rb +0 -14
  67. data/lib/kit/text_utils/truncate.rb +0 -29
  68. data/lib/kit/text_utils/truncator.rb +0 -15
  69. data/lib/kit/text_utils/urls.rb +0 -13
  70. data/lib/kit/text_utils.rb +0 -43
  71. 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,2 @@
1
+ verify_url: "http://www.google.com/recaptcha/api/verify"
2
+ enabled: false
@@ -1,4 +1,4 @@
1
- module Rad::Controller::Http::Authorized
1
+ module Rad::Controller::Authorized
2
2
  inherited do
3
3
  helper_method :can?, :owner?
4
4
  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
@@ -1,4 +1,4 @@
1
- module Rad::Controller::Http::Localized
1
+ module Rad::Controller::Localized
2
2
  inherited do
3
3
  before :prepare_locale
4
4
  end
@@ -0,0 +1,5 @@
1
+ %w(
2
+ authorized
3
+ localized
4
+ captcha
5
+ ).each{|n| require "kit/controller/#{n}"}
data/lib/kit/gems.rb CHANGED
@@ -1,16 +1,20 @@
1
1
  # core gems
2
- gem 'bluecloth', '2.0.9'
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 'paperclip', '2.3.1.1'
7
- gem 'factory_girl', '1.3.3'
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
- require_attr :default_item, :tags_count, :fs_prefix, :fs_type, :fs_path, :fs_cache_path
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
- require_attr :default_url
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 text_utils gem
23
+ # TODO3 move :text_utils to standalone gem
25
24
  %w(
26
25
  support
27
- text_utils
28
- http_controller
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
@@ -0,0 +1,12 @@
1
+ class UserError < StandardError
2
+ end
3
+
4
+ Object.class_eval do
5
+ def raise_user_error msg
6
+ raise UserError, msg
7
+ end
8
+
9
+ def self.raise_user_error msg
10
+ raise UserError, msg
11
+ end
12
+ end
@@ -46,7 +46,7 @@ module Mongoid::AttachmentsUploaderHelper
46
46
  model.send(field_name).name == h.name
47
47
  end
48
48
  end
49
-
49
+
50
50
  add.each{|file| association.build field_name => file}
51
51
  update.each do |file|
52
52
  h = FileHelper.new file
@@ -43,9 +43,8 @@ module Mongoid::Authorized
43
43
  #
44
44
  # Roles
45
45
  #
46
- def anonymous?
47
- name == 'anonymous'
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
@@ -13,7 +13,7 @@ end
13
13
 
14
14
 
15
15
  %w(
16
- micelaneous
16
+ miscellaneous
17
17
  role
18
18
  authorized
19
19
  authorized_object
@@ -1,4 +1,4 @@
1
- module Mongoid::RadMicelaneous
1
+ module Mongoid::RadMiscellaneous
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  delegate :t, to: I18n
@@ -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=", Rad::TextUtils.markup(value)
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
- rad_micelaneous
17
+ rad_miscellaneous
18
18
  ).each{|n| require "kit/mongoid/#{n}"}
19
19
 
20
- (rad.extension(:mm_plugins){[]} + [Mongoid::RadMicelaneous]).each do |plugin|
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
@@ -1,5 +1,4 @@
1
1
  require 'rad'
2
-
3
2
  require 'rad/spec'
4
3
 
5
4
  require 'mongoid_misc'
@@ -55,7 +54,7 @@ rspec.extend Rad::CarrierWaveSpecHelper
55
54
 
56
55
 
57
56
  #
58
- # Micelaneous
57
+ # Miscellaneous
59
58
  #
60
59
  rspec do
61
60
  alias_method :call, :wcall
data/lib/kit/tasks.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'rake_ext'
2
2
 
3
- require 'rad_ext/tasks'
3
+ require 'rad/tasks'
4
4
  require 'rad/assets/tasks'
5
5
  require 'mongo_migration/tasks'
6
6
 
@@ -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 Rad::TextUtils::HtmlSanitizer < Rad::TextUtils::Processor
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 process html, env
80
- html = call_next html, env
81
+ def call data, env
82
+ data = call_next data, env
81
83
 
82
- Sanitize.clean(html, RELAXED.merge(
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