gluttonberg-core 2.5.5 → 2.5.6

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 (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