gluttonberg-core 2.5.5 → 2.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/app/assets/javascripts/gb_application.js +34 -0
  2. data/app/assets/stylesheets/gb_admin-override.sass +17 -1
  3. data/app/controllers/gluttonberg/admin/asset_library/assets_controller.rb +4 -2
  4. data/app/controllers/gluttonberg/admin/content/articles_controller.rb +22 -22
  5. data/app/controllers/gluttonberg/admin/content/comments_controller.rb +80 -23
  6. data/app/controllers/gluttonberg/admin/main_controller.rb +1 -1
  7. data/app/controllers/gluttonberg/public/comments_controller.rb +9 -8
  8. data/app/helpers/gluttonberg/admin.rb +3 -3
  9. data/app/helpers/gluttonberg/asset_library.rb +14 -11
  10. data/app/models/gluttonberg/asset.rb +8 -6
  11. data/app/models/gluttonberg/comment.rb +57 -3
  12. data/app/models/gluttonberg/comment_subscription.rb +2 -0
  13. data/app/models/gluttonberg/setting.rb +11 -5
  14. data/app/views/gluttonberg/admin/asset_library/assets/_browser_root.html.haml +20 -3
  15. data/app/views/gluttonberg/admin/asset_library/assets/search.json.haml +1 -1
  16. data/app/views/gluttonberg/admin/content/comments/index.html.haml +36 -10
  17. data/app/views/gluttonberg/admin/content/main/_sidebar.html.haml +6 -3
  18. data/app/views/gluttonberg/admin/main/index.html.haml +4 -4
  19. data/app/views/gluttonberg/admin/settings/generic_settings/index.html.haml +11 -7
  20. data/config/routes.rb +10 -0
  21. data/db/migrate/20130201025800_spam_flag_for_comments.rb +6 -0
  22. data/lib/engine.rb +3 -2
  23. data/lib/generators/gluttonberg/installer/installer_generator.rb +33 -32
  24. data/lib/gluttonberg/content/block.rb +2 -0
  25. data/lib/gluttonberg/content/clean_html.rb +16 -14
  26. data/lib/gluttonberg/content/despamilator/conf/unusual_characters.txt +6674 -0
  27. data/lib/gluttonberg/content/despamilator/filter/emails.rb +49 -0
  28. data/lib/gluttonberg/content/despamilator/filter/gtubs_test_filter.rb +25 -0
  29. data/lib/gluttonberg/content/despamilator/filter/html_tags.rb +134 -0
  30. data/lib/gluttonberg/content/despamilator/filter/ip_address_url.rb +27 -0
  31. data/lib/gluttonberg/content/despamilator/filter/long_words.rb +29 -0
  32. data/lib/gluttonberg/content/despamilator/filter/mixed_case.rb +25 -0
  33. data/lib/gluttonberg/content/despamilator/filter/naughty_words.rb +80 -0
  34. data/lib/gluttonberg/content/despamilator/filter/no_vowels.rb +28 -0
  35. data/lib/gluttonberg/content/despamilator/filter/numbers_and_words.rb +55 -0
  36. data/lib/gluttonberg/content/despamilator/filter/obfuscated_urls.rb +45 -0
  37. data/lib/gluttonberg/content/despamilator/filter/prices.rb +23 -0
  38. data/lib/gluttonberg/content/despamilator/filter/script_tag.rb +25 -0
  39. data/lib/gluttonberg/content/despamilator/filter/shouting.rb +38 -0
  40. data/lib/gluttonberg/content/despamilator/filter/spammy_tlds.rb +26 -0
  41. data/lib/gluttonberg/content/despamilator/filter/square_brackets.rb +27 -0
  42. data/lib/gluttonberg/content/despamilator/filter/trailing_number.rb +25 -0
  43. data/lib/gluttonberg/content/despamilator/filter/unusual_characters.rb +51 -0
  44. data/lib/gluttonberg/content/despamilator/filter/urls.rb +45 -0
  45. data/lib/gluttonberg/content/despamilator/filter/very_long_domain_name.rb +31 -0
  46. data/lib/gluttonberg/content/despamilator/filter/weird_punctuation.rb +48 -0
  47. data/lib/gluttonberg/content/despamilator/filter.rb +57 -0
  48. data/lib/gluttonberg/content/despamilator/subject/text.rb +36 -0
  49. data/lib/gluttonberg/content/despamilator/subject.rb +34 -0
  50. data/lib/gluttonberg/content/despamilator/version.rb +7 -0
  51. data/lib/gluttonberg/content/despamilator.rb +79 -0
  52. data/lib/gluttonberg/content.rb +12 -11
  53. data/lib/gluttonberg/library/attachment_mixin.rb +52 -269
  54. data/lib/gluttonberg/library/config/image_sizes.rb +61 -0
  55. data/lib/gluttonberg/library/config.rb +10 -0
  56. data/lib/gluttonberg/library/processor/audio.rb +42 -0
  57. data/lib/gluttonberg/library/processor/image.rb +134 -0
  58. data/lib/gluttonberg/library/processor.rb +11 -0
  59. data/lib/gluttonberg/library/storage/filesystem.rb +76 -0
  60. data/lib/gluttonberg/library/storage/s3.rb +196 -0
  61. data/lib/gluttonberg/library/storage.rb +11 -0
  62. data/lib/gluttonberg/library.rb +87 -86
  63. data/lib/gluttonberg/record_history.rb +14 -15
  64. data/lib/gluttonberg/tasks/asset.rake +25 -3
  65. data/lib/gluttonberg/version.rb +1 -1
  66. metadata +53 -2
@@ -0,0 +1,51 @@
1
+ module Gluttonberg
2
+ module Content
3
+ require 'despamilator/filter'
4
+
5
+ module DespamilatorFilter
6
+
7
+ class UnusualCharacters < Despamilator::Filter
8
+
9
+ def name
10
+ 'Unusual Characters'
11
+ end
12
+
13
+ def description
14
+ 'Detects and scores each occurrence of an unusual 2 or 3 character combination'
15
+ end
16
+
17
+ def parse subject
18
+ initialize_combos
19
+ tokenize(subject.text.without_uris).each do |token|
20
+ subject.register_match!({:score => 0.05, :filter => self}) if @@combos[token.to_sym]
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def tokenize text
27
+ tokens = []
28
+ text.downcase.split(/[^a-z]/).each do |word|
29
+ word.chars.each_with_index do |c, i|
30
+ substr = word[i,i+3]
31
+ tokens << substr.to_sym if substr.length == 3
32
+ tokens << substr[0,2].to_sym if substr.length > 1
33
+ end
34
+ end
35
+ tokens
36
+ end
37
+
38
+ def initialize_combos
39
+ @@combos ||= {}
40
+ return @@combos unless @@combos.empty?
41
+
42
+ File.open(File.join(File.dirname(__FILE__), %w{.. conf unusual_characters.txt}), 'r').each do |line|
43
+ @@combos[line.strip.to_sym] = true
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end #Content
51
+ end #Gluttonberg
@@ -0,0 +1,45 @@
1
+ module Gluttonberg
2
+ module Content
3
+ require 'despamilator/filter'
4
+
5
+ module DespamilatorFilter
6
+
7
+ class URLs < Despamilator::Filter
8
+
9
+ def name
10
+ 'URLs'
11
+ end
12
+
13
+ def description
14
+ 'Detects each url in a string'
15
+ end
16
+
17
+ def parse subject
18
+ text = subject.text.downcase.gsub(/http:\/\/\d+\.\d+\.\d+\.\d+/, '')
19
+ matches = text.count(/https?:\/\//)
20
+ comment_number_of_urls_allowed = Gluttonberg::Setting.get_setting("comment_number_of_urls_allowed")
21
+ score_for_one_url = 0.4
22
+ if !comment_number_of_urls_allowed.blank? && comment_number_of_urls_allowed.to_i > 0
23
+ comment_number_of_urls_allowed = comment_number_of_urls_allowed.to_i
24
+ score_for_one_url = 1.0 / comment_number_of_urls_allowed.to_i
25
+ end
26
+ 1.upto(matches > 2 ? 2 : matches) do
27
+ subject.register_match!({:score => score_for_one_url, :filter => self})
28
+ end
29
+
30
+ comment_email_as_spam = Gluttonberg::Setting.get_setting("comment_email_as_spam")
31
+ if comment_email_as_spam == "Yes"
32
+ text_temp = text.strip
33
+ extracted_urls = URI.extract(text_temp)
34
+ subject.register_match!({
35
+ :score => 1.0, :filter => self
36
+ }) if extracted_urls.length > 0 && extracted_urls[0] == text_temp
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end #Content
45
+ end #Gluttonberg
@@ -0,0 +1,31 @@
1
+ module Gluttonberg
2
+ module Content
3
+ require 'despamilator/filter'
4
+ require 'domainatrix'
5
+
6
+ module DespamilatorFilter
7
+
8
+ class VeryLongDomainName < Despamilator::Filter
9
+
10
+ def name
11
+ 'Very Long Domain Name'
12
+ end
13
+
14
+ def description
15
+ 'Detects unusually long domain names.'
16
+ end
17
+
18
+ def parse subject
19
+ subject.text.scan(URI.regexp).each do |url_parts|
20
+ url_parts.compact!
21
+ next if !url_parts[1] or url_parts[1] !~ /(\w|-){5,}\.\w{2,5}/
22
+ url = Domainatrix.parse('http://' + url_parts[1])
23
+ subject.register_match!({:score => 0.4, :filter => self}) if url.domain.length > 20
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end #Content
31
+ end #Gluttonberg
@@ -0,0 +1,48 @@
1
+ module Gluttonberg
2
+ module Content
3
+ require 'despamilator/filter'
4
+
5
+ module DespamilatorFilter
6
+
7
+ class WeirdPunctuation < Despamilator::Filter
8
+
9
+ def name
10
+ 'Weird Punctuation'
11
+ end
12
+
13
+ def description
14
+ 'Detects unusual use of punctuation.'
15
+ end
16
+
17
+ def parse subject
18
+ text = subject.text.without_uris.downcase
19
+
20
+ text.gsub!(/\w&\w/, 'xx')
21
+ text.gsub!(/[a-z](!|\?)(\s|$)/, 'x')
22
+ text.gsub!(/(?:#{punctuation}){20,}/, '')
23
+ matches = text.remove_and_count!(/(?:\W|\s|^)(#{punctuation})/)
24
+ matches += text.remove_and_count!(/\w,\w/)
25
+ matches += text.remove_and_count!(/\w\w\.\w/)
26
+ matches += text.remove_and_count!(/\w\.\w\w/)
27
+ matches += text.remove_and_count!(/(#{punctuation})(#{punctuation})/)
28
+ matches += text.remove_and_count!(/(#{punctuation})$/)
29
+ matches += text.remove_and_count!(/(?:\W|\s|^)\d+(#{punctuation})/)
30
+
31
+ subject.register_match!({:score => 0.03 * matches, :filter => self}) if matches > 0
32
+ end
33
+
34
+ private
35
+
36
+ def punctuation
37
+ @punctuation ||= %w{~ ` ! @ # $ % ^ & * _ - + = , / ? | \\ : ;}.map do |punctuation_character|
38
+ Regexp.escape(punctuation_character)
39
+ end.join('|')
40
+
41
+ @punctuation
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end #Content
48
+ end #Gluttonberg
@@ -0,0 +1,57 @@
1
+ module Gluttonberg
2
+ module Content
3
+ class Despamilator
4
+
5
+ #This class is the base class of all the despamilator filters.
6
+ #
7
+ #== EXAMPLE:
8
+ #
9
+ #This example is to detect the letter "a". Put the code in
10
+ #lib/despamilator/filter/detect_letter_a.rb:
11
+ #
12
+ # require 'despamilator/filter_base'
13
+ #
14
+ # module DespamilatorFilter
15
+ #
16
+ # class DetectLetterA < Despamilator::FilterBase
17
+ #
18
+ # def name
19
+ # 'Detecting the letter A'
20
+ # end
21
+ #
22
+ # def description
23
+ # 'Detects the letter "a" in a string for no reason other than a demo'
24
+ # end
25
+ #
26
+ # def parse text
27
+ # if text.downcase.scan(/a/)
28
+ # # add 0.1 to the score of the text
29
+ # self.append_score = 0.1
30
+ # end
31
+ # end
32
+ # end
33
+
34
+ class Filter
35
+
36
+ # The nice description of the filter. Usually no more than a sentence.
37
+
38
+ def description
39
+ raise "No description defined for #{self.class}"
40
+ end
41
+
42
+ # This method parses some text. The score is assigned to the same instance.
43
+
44
+ def parse text
45
+ raise "No parser defined for #{self.class}"
46
+ end
47
+
48
+ # The one or two word name for the filter.
49
+
50
+ def name
51
+ raise "No name defined for #{self.class}"
52
+ end
53
+
54
+ end
55
+ end
56
+ end #content
57
+ end #Gluttonberg
@@ -0,0 +1,36 @@
1
+ module Gluttonberg
2
+ module Content
3
+ require 'uri'
4
+
5
+ class Despamilator
6
+ class Subject
7
+ class Text < String
8
+
9
+ def initialize text
10
+ super text
11
+ freeze
12
+ end
13
+
14
+ def without_uris
15
+ gsub(/\b(?:https?|mailto|ftp):.+?(\s|$)/i, '')
16
+ end
17
+
18
+ def words
19
+ split(/\W+/)
20
+ end
21
+
22
+ def count pattern
23
+ scan(pattern).flatten.compact.length
24
+ end
25
+
26
+ def remove_and_count! pattern
27
+ count = count(pattern)
28
+ gsub!(pattern, '')
29
+ count
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end #Content
36
+ end #Gluttonberg
@@ -0,0 +1,34 @@
1
+ module Gluttonberg
2
+ module Content
3
+ require 'despamilator/subject/text'
4
+
5
+ class Despamilator
6
+ class Subject
7
+ attr_reader :score, :text
8
+
9
+ def initialize text
10
+ @score = 0.0
11
+ @matches = {}
12
+ @text = Despamilator::Subject::Text.new(text)
13
+ end
14
+
15
+ def register_match! details
16
+ @score += details[:score] || raise('A score must be supplied')
17
+ filter = details[:filter] || raise('A filter must be supplied')
18
+
19
+ @matches[filter] ||= 0.0
20
+ @matches[filter] += details[:score]
21
+ end
22
+
23
+ def matches
24
+ @matches.map do |filter, score|
25
+ {:filter => filter, :score => score}
26
+ end.sort do |a, b|
27
+ b[:score] <=> a[:score]
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end #Content
34
+ end #Gluttonberg
@@ -0,0 +1,7 @@
1
+ module Gluttonberg
2
+ module Content
3
+ class Despamilator
4
+ VERSION = '2.1.4'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,79 @@
1
+ module Gluttonberg
2
+ module Content
3
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
4
+ require 'despamilator/filter'
5
+ Dir.glob(File.join(File.dirname(__FILE__), 'despamilator', 'filter', '*.rb')).each do |filter_file|
6
+ require filter_file
7
+ end
8
+
9
+ require 'despamilator/subject'
10
+ require 'ostruct'
11
+
12
+ #== SYNOPSIS:
13
+ #
14
+ # require 'despamilator'
15
+ #
16
+ # # some time later...
17
+ #
18
+ # dspam = Despamilator.new('some text with an <h2> tag qthhg')
19
+ #
20
+ # dspam.score #=> the total score for this string (1 is normally my threshold).
21
+ # dspam.matches #=> array of hashes containing matching filters and their score.
22
+
23
+ class Despamilator
24
+
25
+ # Constructor. Takes the text you which to parse and score.
26
+
27
+ def initialize text
28
+ @subject = Despamilator::Subject.new text
29
+ run_filters @subject
30
+ end
31
+
32
+ # Returns the total score as a Float.
33
+
34
+ def score
35
+ @subject.score
36
+ end
37
+
38
+ def matched_by
39
+ warn 'Despamilator.matched_by is deprecated, please use Despamilator.matches by 2011-12-31.'
40
+
41
+ matches.map do |match|
42
+ filter = match[:filter]
43
+
44
+ OpenStruct.new(
45
+ :name => filter.name,
46
+ :description => filter.description,
47
+ :score => match[:score]
48
+ )
49
+ end
50
+ end
51
+
52
+ # Returns an array of scores and filters that have matched and contributed to the score.
53
+ # Each element is a a child of the Despamilator::FilterBase class.
54
+
55
+ def matches
56
+ @subject.matches
57
+ end
58
+
59
+ # Generic Test for Unsolicited Bulk Submissions. Similar to SpamAssassin's GTUBE.
60
+ # A string that will result in a spam score of at least 100. Handy for testing.
61
+
62
+ def self.gtubs_test_string
63
+ '89913b8a065b7092721fe995877e097681683af9d3ab767146d5d6fd050fc0bda7ab99f4232d94a1'
64
+ end
65
+
66
+ private
67
+
68
+ def run_filters subject
69
+ filter_namespace = Gluttonberg::Content.const_get('DespamilatorFilter')
70
+
71
+ filter_namespace.constants.each do |filter_class|
72
+ filter = filter_namespace.const_get(filter_class).new
73
+ filter.parse(subject)
74
+ end
75
+ end
76
+
77
+ end
78
+ end #content
79
+ end # Gluttonberg
@@ -1,6 +1,6 @@
1
1
  content = Pathname(__FILE__).dirname.expand_path
2
2
 
3
- require File.join(content, 'content', 'slug_management')
3
+ require File.join(content, "content", "slug_management")
4
4
  require File.join(content, "content", "block")
5
5
  require File.join(content, "content", "block_localization")
6
6
  require File.join(content, "content", "localization")
@@ -8,9 +8,10 @@ require File.join(content, "content", "publishable")
8
8
  require File.join(content, "content", "versioning")
9
9
  require File.join(content, "content", "clean_html")
10
10
  require File.join(content, "content", "import_export_csv")
11
+ require File.join(content, "content", "despamilator")
11
12
 
12
13
  module Gluttonberg
13
- # The content module contains a whole bunch classes and mixins related to the
14
+ # The content module contains a whole bunch classes and mixins related to the
14
15
  # pages, localizations, content models and helpers for rendering content
15
16
  # inside of views.
16
17
  module Content
@@ -19,7 +20,7 @@ module Gluttonberg
19
20
  @@localizations = {}
20
21
  @@localization_associations = nil
21
22
  @@localization_classes = nil
22
-
23
+
23
24
  # This is called after the application loads so that we can define any
24
25
  # extra associations or do house-keeping once everything is required and
25
26
  # running
@@ -29,10 +30,10 @@ module Gluttonberg
29
30
  @@localization_classes = @@localizations.values
30
31
  @@content_associations = Block.classes.collect { |k| k.association_name }
31
32
  end
32
-
33
+
33
34
  # For each content class that is registered, a corresponding association is
34
35
  # declared against the Page model. We need to keep track of these, which
35
- # is what this method does. It just returns an array of the association
36
+ # is what this method does. It just returns an array of the association
36
37
  # names.
37
38
  def self.non_localized_associations
38
39
  @@non_localized_associations ||= begin
@@ -40,24 +41,24 @@ module Gluttonberg
40
41
  non_localized.collect {|c| c.association_name }
41
42
  end
42
43
  end
43
-
44
+
44
45
  # Return the collection of content association names.
45
46
  def self.content_associations
46
47
  @@content_associations
47
48
  end
48
-
49
- # If a content class has the is_localized declaration, this method is used
49
+
50
+ # If a content class has the is_localized declaration, this method is used
50
51
  # to register it so we can keep track of all localized content.
51
52
  def self.register_localization(assoc_name, klass)
52
53
  @@localizations[assoc_name] = klass
53
54
  end
54
-
55
- # Returns a hash of content classes that are localized, keyed to the
55
+
56
+ # Returns a hash of content classes that are localized, keyed to the
56
57
  # association name.
57
58
  def self.localizations
58
59
  @@localizations
59
60
  end
60
-
61
+
61
62
  # Returns an array of the localization association names.
62
63
  def self.localization_associations
63
64
  @@localization_associations