rad_kit 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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