parlement 0.5 → 0.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 (56) hide show
  1. data/CHANGES +26 -0
  2. data/README +2 -1
  3. data/app/controllers/account_controller.rb +27 -18
  4. data/app/controllers/application.rb +1 -3
  5. data/app/controllers/elt_controller.rb +92 -42
  6. data/app/helpers/elt_helper.rb +7 -18
  7. data/app/models/elt.rb +76 -22
  8. data/app/models/mail.rb +13 -10
  9. data/app/models/mail_notify.rb +11 -27
  10. data/app/models/person.rb +1 -1
  11. data/app/views/account/_login.rhtml +14 -9
  12. data/app/views/account/_show.rhtml +23 -9
  13. data/app/views/elt/_choice.rhtml +32 -0
  14. data/app/views/elt/_elt.rhtml +90 -78
  15. data/app/views/elt/_list.rhtml +83 -50
  16. data/app/views/elt/_listByDate.rhtml +60 -0
  17. data/app/views/elt/_listByVote.rhtml +71 -0
  18. data/app/views/elt/choices.rhtml +29 -0
  19. data/app/views/elt/new.rhtml +11 -26
  20. data/app/views/elt/rss.rxml +14 -12
  21. data/app/views/elt/show.rhtml +23 -29
  22. data/app/views/elt/vote_rss.rxml +40 -0
  23. data/app/views/layouts/top.rhtml +5 -13
  24. data/config/environment.rb +3 -1
  25. data/config/routes.rb +4 -0
  26. data/db/development_structure.sql +5 -2
  27. data/db/migrate/002_nested_set.rb +22 -0
  28. data/db/migrate/003_elt_children_count.rb +12 -0
  29. data/public/images/ParlementLogo.png +0 -0
  30. data/public/images/indicator.gif +0 -0
  31. data/public/javascripts/behaviour.js +254 -0
  32. data/public/javascripts/mybehaviour.js +96 -0
  33. data/public/javascripts/slider.js +188 -163
  34. data/test/fixtures/elts.yml +19 -0
  35. data/test/fixtures/mail/mail_ruby +4 -0
  36. data/test/fixtures/mail/mail_rubyChild +1 -1
  37. data/test/fixtures/mail/mail_rubyChild2 +1 -1
  38. data/test/functional/account_controller_test.rb +24 -0
  39. data/test/functional/elt_controller_test.rb +64 -4
  40. data/test/unit/elt_test.rb +28 -4
  41. data/test/unit/mail_notify_test.rb +7 -5
  42. data/test/unit/mail_test.rb +1 -1
  43. data/vendor/plugins/google_analytics/README +19 -0
  44. data/vendor/plugins/google_analytics/Rakefile +22 -0
  45. data/vendor/plugins/google_analytics/init.rb +3 -0
  46. data/vendor/plugins/google_analytics/lib/rubaidh/google_analytics.rb +70 -0
  47. data/vendor/plugins/google_analytics/test/google_analytics_test.rb +8 -0
  48. data/vendor/plugins/output_compression/README +4 -0
  49. data/vendor/plugins/output_compression/Rakefile +22 -0
  50. data/vendor/plugins/output_compression/init.rb +2 -0
  51. data/vendor/plugins/output_compression/lib/output_compression.rb +66 -0
  52. data/vendor/plugins/output_compression/tasks/output_compression_tasks.rake +4 -0
  53. data/vendor/plugins/output_compression/test/output_compression_test.rb +8 -0
  54. metadata +32 -4
  55. data/public/images/smile.svg +0 -257
  56. data/public/javascripts/borders.js +0 -687
data/CHANGES CHANGED
@@ -1,5 +1,31 @@
1
1
  - parlement changelog
2
2
 
3
+ == Version 0.6
4
+
5
+ Voting using V and Λ buttons (picturing -1 and +1 values, down and up).
6
+ Clicking on an existing vote resets it (equivalent to a 0 vote).
7
+
8
+ Votes recorded and replicated as mails. Elements also listed by their approval
9
+ (rss feed available).
10
+
11
+ * elements are now acts_as_nested_set AND acts_as_tree
12
+ * list by date, dynamic and managing all sub elements
13
+ * rss now manages _all_ sub elements
14
+ * compress_output
15
+ * google analytics plugin
16
+ * visual_effect queues
17
+ * close or open elements and their sub elements
18
+ * optimisations
19
+ * voting on the web page
20
+ * logging in and out changes appropriately +1 and -1 votes
21
+ * element id now generated from element subject
22
+ * element body now cached
23
+ * some spam tests when creating a new element
24
+ * list by vote
25
+ * possibility to see individual votes when clicking on a vote result
26
+ * Correcting a painful "null character" mishandling when mail is quoted printable
27
+
28
+
3
29
  == Version 0.5
4
30
 
5
31
  Voting. Mails containing -1, 0 or +1 trigger a vote
data/README CHANGED
@@ -69,9 +69,10 @@ Parameters
69
69
  >> elts=Mail.find_all.select{|m|m.file and mail=TMail::Mail.parse(m.file) and reply=mail.in_reply_to and mailObject=Mail.find_by_message(reply) and mailObject.elt.id!=m.elt.parent_id}.collect{|m| {:elt=>m.elt.id, :parent=>Mail.find_by_message(TMail::Mail.parse(m.file).in_reply_to).elt.id}}; puts elts.size
70
70
  >> elts.each{|e|print e[:elt], ' ', e[:parent], ' '; puts Elt.update_all("parent_id='#{e[:parent]}'", "id='#{e[:elt]}'")}; puts elts.size
71
71
 
72
- # To get duplicate messages
72
+ # To get and remove duplicate messages
73
73
  >> messages=Mail.find_by_sql "select message from mails group by message having count(message) > 1"; puts messages.length
74
74
  >> messages.each{ |m| mail=Mail.find_all_by_message m.message; mail.each{|m| print m.elt.id, ' ', m.elt.subject, ' ', m.elt.children.size; puts } }; puts messages.length
75
+ >> messages.each{ |m| mail=Mail.find_all_by_message m.message; mail.each_index{|i| m=mail[i]; print i, ' ', m.elt.id, ' ', m.elt.subject, ' ', m.elt.children.size; m.elt.destroy if i>0 and m.elt.children.size==0; puts } }; puts messages.length
75
76
 
76
77
  # To get all existing votes
77
78
  elts=Elt.find(:all, :conditions => ["body LIKE '%%+1%%' OR body LIKE '%%0%%' OR body LIKE '%%-1%%'"]); puts elts.size
@@ -7,7 +7,7 @@ class AccountController < UserController
7
7
 
8
8
  def login
9
9
  # Cleaning up
10
- @session[:person] = @session[:user] = nil
10
+ session[:person] = session[:user] = nil
11
11
 
12
12
  login = @params[:person][:name]
13
13
  email = @params[:person][:email]
@@ -27,7 +27,7 @@ class AccountController < UserController
27
27
  logger.info "person: "+@person.to_s
28
28
  logger.info "person.id: "+@person.id.to_s
29
29
  flash.now[:notice] = "Pseudo created"
30
- @session[:person] = @person
30
+ session[:person] = @person
31
31
  else
32
32
  flash.now[:error] = 'Error creating account'
33
33
  end
@@ -38,12 +38,12 @@ class AccountController < UserController
38
38
  end
39
39
  end
40
40
 
41
- # Second we record the password or try to authenticate
42
41
  if @person and @person.errors.empty?
42
+ # Second we record the password or try to authenticate
43
43
  if password.empty?
44
44
  if not @user or not @user.salted_password \
45
45
  or @user.salted_password.empty?
46
- @session[:person] = @person
46
+ session[:person] = @person
47
47
  else
48
48
  flash.now[:error] = "This pseudo is protected with a password"
49
49
  end
@@ -61,8 +61,8 @@ class AccountController < UserController
61
61
  if @user.save
62
62
  flash['notice 2'] = 'Password recorded'
63
63
 
64
- @session[:person] = @person
65
- @session[:user] = @user
64
+ session[:person] = @person
65
+ session[:user] = @user
66
66
  end
67
67
  end
68
68
  rescue
@@ -71,8 +71,8 @@ class AccountController < UserController
71
71
 
72
72
  elsif User.authenticate(login, password)
73
73
  # There is a password protecting this pseudo
74
- @session[:person] = @person
75
- @session[:user] = @user
74
+ session[:person] = @person
75
+ session[:user] = @user
76
76
 
77
77
  elsif not email.empty?
78
78
  begin
@@ -82,8 +82,8 @@ class AccountController < UserController
82
82
  @user.security_token = nil
83
83
  if @user.save
84
84
  flash.now['notice 2'] = 'Password successfully modified!'
85
- @session[:person] = @person
86
- @session[:user] = @user
85
+ session[:person] = @person
86
+ session[:user] = @user
87
87
  end
88
88
  end
89
89
  end
@@ -96,7 +96,7 @@ class AccountController < UserController
96
96
 
97
97
  # Third we record the email or send a check_key for a password reset
98
98
  if not email.empty?
99
- if @session[:person]
99
+ if session[:person]
100
100
  if email == @person.email
101
101
  flash.now['notice 3'] = "Email already recorded and verified"
102
102
  else
@@ -114,15 +114,16 @@ class AccountController < UserController
114
114
  end
115
115
  end
116
116
 
117
- render :partial => 'show', :locals => { :divId => params[:divId] }
118
- #render :partial => 'show', :locals => { :divId => 'test' }
117
+ render :partial => 'show', :locals => {
118
+ :divId => params[:divId], :choices => getAllVotes }
119
119
  end
120
120
 
121
121
 
122
122
  def logout
123
- @session[:person] = @person = nil
124
- @session[:user] = @user = nil
125
- render :partial => 'login', :locals => { :divId => params[:divId] }
123
+ session[:person] = @person = nil
124
+ session[:user] = @user = nil
125
+ render :partial => 'show', :locals => {
126
+ :divId => params[:divId], :choices => getAllVotes }
126
127
  end
127
128
 
128
129
  def check_key
@@ -132,8 +133,8 @@ class AccountController < UserController
132
133
  @person.email = @user.email
133
134
  @person.save
134
135
  flash.now[:notice] = "Email #{@person.email} verified."
135
- @session[:person] = @person
136
- @session[:user] = @user
136
+ session[:person] = @person
137
+ session[:user] = @user
137
138
  render :text => "<h1>Email verified!</h1> \
138
139
  <br/> \
139
140
  <a href='#{url_for :controller => 'elt', :action => 'show', :id => nil }'>\
@@ -183,5 +184,13 @@ class AccountController < UserController
183
184
  logger.error e
184
185
  end
185
186
  end
187
+
188
+ def getAllVotes
189
+ return if !params[:divId]
190
+ elt = Elt.find params[:divId].gsub(/author_/, '')
191
+ Choice.find_all_by_person_id((session[:person] ? session[:person].id : nil),
192
+ :include => 'elt',
193
+ :conditions => "elts.lft >= '#{elt.lft}' AND elts.rgt <= '#{elt.rgt}'")
194
+ end
186
195
  end
187
196
 
@@ -9,9 +9,7 @@ class ApplicationController < ActionController::Base
9
9
 
10
10
  before_filter :set_charset
11
11
  after_filter :fix_unicode_for_safari
12
-
13
- include LiveTree
14
- helper LiveTreeHelper
12
+ after_filter :compress_output
15
13
 
16
14
  # automatically and transparently fixes utf-8 bug
17
15
  # with Safari when using xmlhttp
@@ -4,73 +4,123 @@
4
4
  # An element is just the name for a poll/message/issue
5
5
  #
6
6
  class EltController < ApplicationController
7
- def index
7
+
8
+ def index
8
9
  params[:id] = params[:id].gsub(/.html/, '')
9
- show
10
- end
10
+ show
11
+ end
11
12
 
12
- def show
13
+ def show
13
14
  params[:id] = params[:id].gsub(/.html/, '')
14
- @elt = Elt.find(params[:id])
15
- @title = @elt.subject+' (parlement)'
15
+ @elt = Elt.find(params[:id])
16
+ @title = @elt.subject
17
+ @title += " (parlement)" if !@title.downcase.include? "parlement"
18
+ if request.env['REQUEST_URI'].match '.dyndns'
19
+ headers["Status"] = "301 Moved Permanently"
20
+ redirect_to request.env['REQUEST_URI'].gsub(/.dyndns/, '')
21
+ end
16
22
  render :layout => 'top'
17
23
 
18
24
  # TODO
19
- rescue ActiveRecord::RecordNotFound => e
20
- flash[:error] = "Element '#{params[:id]}' does not exist"
21
- headers["Status"] = "301 Moved Permanently"
22
- redirect_to '/'
23
- end
25
+ rescue ActiveRecord::RecordNotFound => e
26
+ flash[:error] = "Element '#{params[:id]}' does not exist"
27
+ headers["Status"] = "301 Moved Permanently"
28
+ redirect_to '/'
29
+ end
24
30
 
25
- def list
26
- @elt = Elt.find(params[:id]) if @elt == nil
31
+ def list
32
+ @elt = Elt.find(params[:id]) if @elt == nil
27
33
  render :partial => '/elt/list', :locals => { :elt => @elt }
28
- end
34
+ end
35
+
36
+ def listByDate
37
+ @elt = Elt.find params[:id] unless @elt
38
+ render :partial => 'listByDate'
39
+ end
40
+
41
+ def listByVote
42
+ @elt = Elt.find params[:id] unless @elt
43
+ render :partial => 'listByVote'
44
+ end
29
45
 
30
- def rss
46
+ def rss
31
47
  params[:id] = params[:id].gsub(/.rss/, '')
32
48
  @headers["Content-Type"] = "text/xml; charset=utf-8"
33
- @elt = Elt.find(params[:id]) if @elt == nil
34
- end
49
+ @elt = Elt.find(params[:id]) if @elt == nil
50
+ end
51
+
52
+ def vote_rss
53
+ params[:id] = params[:id].gsub(/.rss/, '')
54
+ @headers["Content-Type"] = "text/xml; charset=utf-8"
55
+ @elt = Elt.find(params[:id]) if @elt == nil
56
+ end
35
57
 
36
58
  # Used to initialise the elt, its subject mainly
37
- def new
38
- @elt = Elt.new
59
+ def new
60
+ @elt = Elt.new
39
61
  @elt.parent = Elt.find(params[:id])
40
- @elt.position = 0
41
- @elt.person = Person.find_by_name(cookies[:person])
42
- @elt.build_person if @elt.person == nil
43
-
44
- @person = @elt.person
45
62
 
46
63
  if @elt.parent.subject.include? 'Re: '
47
64
  @elt.subject = @elt.parent.subject
48
65
  else
49
66
  @elt.subject = 'Re: '+@elt.parent.subject
50
67
  end
51
- end
68
+ end
52
69
 
53
- def preview
70
+ def preview
54
71
  render :inline => h(format(params[:elt][:body]))
55
- end
56
-
57
- def create
58
- @elt = Elt.new(params[:elt])
72
+ end
59
73
 
74
+ def create
75
+ @elt = Elt.new(params[:elt])
60
76
  @elt.person = @session[:person]
61
77
 
62
- if params[:submit] == 'preview' or @elt.save
63
- render :partial => '/elt/elt', :locals => { :elt => @elt, :eltTop => false }
64
- else
65
- flash[:notice] = 'Error'
66
- render :controller => 'elt', :action => 'new'
67
- end
68
- end
69
-
70
- def raw_elt
71
- @mail = Elt.find(params[:id]).mail
72
- #@elt = TMail::Mail.parse(Elt.find(params[:id]).mail.id)
78
+ if @elt.subject.match(/([<>\/]|href)/) then
79
+ logger.error "SPAM! '#{@elt.subject}'"
80
+ logger.error "SPAM! '#{@elt.body}'"
81
+ flash[:error] = 'Sorry, to fight spam "<" ">" or "href" are forbidden in the subject!'
82
+ headers["Status"] = "404 Post considered as spam"
83
+ render :controller => 'elt', :action => 'new', :status => 404
84
+ elsif params[:submit] == "preview" or (@elt.publish and @elt.parent.add_child(@elt)) then
85
+ headers["Status"] = "201 Created"
86
+ render :partial => '/elt/elt', :status => 201, :locals => { :elt => @elt, :eltTop => false }
87
+ else
88
+ flash[:notice] = 'Error'
89
+ render :controller => 'elt', :action => 'new'
90
+ end
91
+ end
92
+
93
+ def raw_elt
94
+ @mail = Elt.find(params[:id]).mail
95
+ #@elt = TMail::Mail.parse(Elt.find(params[:id]).mail.id)
73
96
  render :inline => "<pre><%= @mail.file %></pre>", :layout => 'top'
74
- end
97
+ end
98
+
99
+ def vote
100
+ @elt = Elt.find params[:id]
101
+ vote = @elt.children.build
102
+ vote.person = session[:person]
103
+ vote.subject = @elt.subject
104
+ vote.subject = 'Re: '+vote.subject if vote.subject and !vote.subject.include? 'Re: '
105
+ vote.body = params[:choice][:value]
106
+
107
+ choice = Choice.find_by_elt_id_and_person_id @elt.id, (session[:person] ? session[:person].id : nil)
108
+
109
+ if choice and choice.value == vote.body.to_i then
110
+ logger.info "#{(session[:person] ? session[:person].name : 'null')} voting 0"
111
+ vote.body = "0"
112
+ else
113
+ logger.info "#{(session[:person] ? session[:person].name : 'null')} voting #{params[:choice][:value]}"
114
+ end
115
+
116
+ vote.publish
117
+ @elt.add_child vote
118
+
119
+ render :partial => '/elt/choice', :locals => { :elt => @elt }
120
+ end
121
+
122
+ def choices
123
+ @elt = Elt.find params[:id]
124
+ end
75
125
  end
76
126
 
@@ -9,6 +9,7 @@ module EltHelper
9
9
  # /italic/
10
10
  # - listElement
11
11
  # 1. numbered list
12
+ # chat log lines become list elements
12
13
  # short lines to which are added a break
13
14
  text = auto_link data \
14
15
  .gsub(/^\b(.*:)$(\n)^>\s/, '\\1<br/>\\2\\2> ') \
@@ -17,9 +18,11 @@ module EltHelper
17
18
  .gsub(/(\s+)\/([\w\s]*)\//, '\\1_\\2_') \
18
19
  .gsub(/^-\s/, '* ') \
19
20
  .gsub(/^\d+[\.-]\s+/, '# ') \
21
+ .gsub(/^(\d\d:\d\d\s)/, '* \\1') \
20
22
  .gsub(/^\b(.{2,50})$(\n)\b/, '\\1<br/>\\2') \
21
23
  if data != nil
22
24
 
25
+ #hard_breaks = true
23
26
  textiled = text.blank? ? "" : RedCloth.new(text)
24
27
  begin
25
28
  textiled = textiled.to_html if !textiled.blank?
@@ -28,28 +31,13 @@ module EltHelper
28
31
  textiled = textiled.gsub(/[->\[]/, ' ')
29
32
  textiled = textiled.to_html
30
33
  end
31
- if ! textiled.match(/[.\s]<p>/) then
34
+ if ! textiled.match(/[.\s]<p>/) and textiled.size < 200 then
32
35
  # Remove any leading and finishing <p> </p> as they are not required
33
36
  if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
34
37
  if textiled[-4..-1] == "</p>" then textiled = textiled[0..-5] end
35
38
  end
36
39
 
37
40
  textiled
38
-
39
- # in reply to:\n>
40
- # .gsub(/(:)(\n)(^>\s)/, '\\1\\2\\2\\3') \
41
- # ^here: data
42
- # .gsub(/^(\w*):\s/, ' \\1: ') \
43
-
44
- # hard_breaks = false
45
- # auto_link textilize_without_paragraph(data \
46
- # .gsub(/\&\#8211\;/, '* ') \
47
- # .gsub(/^-\s/, '* ') \
48
- # .gsub(/[^\n]\n^>/, '') \
49
- # .gsub(/^\d+[ \.-]+/, '# ') \
50
- # # ).gsub(/(.{65,182})<br\s\/>/, \
51
- # # ).gsub(/([\r\n\<\>=\w\d\s.\:;,$*?!\-����������\"\&#;()\/]{65,182})<br\s\/>([^>])/,
52
- # ) if data != nil
53
41
  end
54
42
 
55
43
  #
@@ -60,10 +48,11 @@ module EltHelper
60
48
  #
61
49
  def displayTitle? elt
62
50
  #elt.id and elt.subject.reverse.index(elt.parent.subject.reverse) != 0
63
- elt.id and elt.parent and !elt.subject.empty? \
51
+ elt.id and elt.parent and elt.subject and !elt.subject.empty? \
64
52
  and elt.subject != elt.parent.subject \
53
+ and elt.parent.subject \
65
54
  and elt.subject.downcase != elt.parent.subject.downcase \
66
- and not elt.subject =~ /^((\[[\w]*\] )*(r�f|re|sv|aw)*(.)*:[ \t]*)+(#{Regexp.escape(elt.parent.subject)})/
55
+ and not elt.subject.sub(/(Re|r�f|re|sv|aw):\s*/, '') == elt.parent.subject
67
56
  end
68
57
  end
69
58
 
data/app/models/elt.rb CHANGED
@@ -7,7 +7,8 @@
7
7
  class Elt < ActiveRecord::Base
8
8
  usesguid
9
9
 
10
- acts_as_tree :order => "position is not null, position, created_on"
10
+ acts_as_nested_set :order => "position is not null, position, created_on"
11
+ acts_as_tree :order => "position is not null, position, created_on", :counter_cache => true
11
12
 
12
13
  has_one :mail, :dependent => true
13
14
  has_many :attachments, :dependent => true
@@ -16,14 +17,9 @@ class Elt < ActiveRecord::Base
16
17
 
17
18
  has_and_belongs_to_many :subscribers, :class_name => "Person", :join_table => "subscribers"
18
19
 
19
- has_many :choices
20
+ has_many :choices, :dependent => true, :order => "choices.created_on"
20
21
  has_many :voters, :through => :choices, :source => :person
21
22
 
22
- # for live_tree
23
- def name
24
- subject
25
- end
26
-
27
23
  # Just a quick method to get all subscribers as a simple list
28
24
  def all_recipients
29
25
  if parent
@@ -33,36 +29,94 @@ class Elt < ActiveRecord::Base
33
29
  end
34
30
  end
35
31
 
36
- alias_method :simple_save, :save
37
-
38
- # Save, publish and vote this elt
39
- def save
40
- publish
41
- simple_save
42
- vote
43
- end
44
-
45
32
  def vote(value = 1, person = self.person)
46
33
  logger.info "#{person.name if person} vote #{value} on #{subject} #{self.choices}"
47
- choices.each{ |c| logger.info c if c.person == person; c.destroy if c.person == person }
34
+ choices.each{ |c| c.destroy if c.person == person }
48
35
  choice = choices.build :value => value, :person => person
49
36
  choice.save!
50
37
  end
51
38
 
52
39
  # Get the vote results
53
40
  def result(electoralList = nil)
54
- (Choice.count("elt_id = '#{self.id}' AND value = 1") \
55
- - \
56
- Choice.count("elt_id = '#{self.id}' AND value = -1"))
41
+ #(Choice.count("elt_id = '#{self.id}' AND value = 1") \
42
+ # - \
43
+ # Choice.count("elt_id = '#{self.id}' AND value = -1"))
44
+ Choice.sum(:value, :conditions => "elt_id = '#{self.id}'")
57
45
  end
58
46
 
59
- private
60
-
61
47
  # Mail this elt to all subscribers
62
48
  def publish
63
49
  logger.info "Publish #{subject}"
50
+
64
51
  build_mail(:elt => self) unless mail
65
52
  mail.publish
53
+ save!
54
+
55
+ parent.vote Regexp.last_match(1), person if body =~ /^\s*(-1|0|\+1)(\s|$)/
56
+ if self.body.gsub(/(-1|0|\+1)/, '').strip.empty?
57
+ # Hide simple votes
58
+ vote -1
59
+ else
60
+ vote if person
61
+ end
62
+ true
63
+ end
64
+
65
+ def save
66
+ if new_record? and subject and not subject.empty?
67
+ # Let's generate a nice looking id
68
+ self.id = new_id = subject.gsub(/\[[\w-]*\]/, '').strip.gsub(/[\s\?\&\#\\\/:]+/, '_')
69
+
70
+ discrim = 0
71
+ self.id = "#{new_id}_#{discrim}" while discrim += 1 and self.class.find_by_id self.id
72
+ end
73
+
74
+ super
75
+ end
76
+
77
+ #
78
+ # ___________________________________________________________________
79
+ # | Root |
80
+ # | ____________________________ ____________________________ |
81
+ # | | Child 1 | | Child 2 | |
82
+ # | | __________ _________ | | __________ _________ | |
83
+ # | | | C 1.1 | | C 1.2 | | | | C 2.1 | | C 2.2 | | |
84
+ # 1 2 3_________4 5________6 7 8 9_________10 11_______12 13 14
85
+ # | |___________________________| |___________________________| |
86
+ # |___________________________________________________________________|
87
+ #
88
+ def move(destination)
89
+ return if self[right_col_name].nil? || self[left_col_name].nil? \
90
+ || destination[right_col_name].nil? || destination[left_col_name].nil? \
91
+ || (self[left_col_name] < destination[left_col_name] && destination[right_col_name] < self[right_col_name])
92
+
93
+ dif = self[right_col_name] - self[left_col_name] + 1
94
+ moveBy = destination[right_col_name] - self[left_col_name]
95
+
96
+ self.class.transaction {
97
+ # Take out of range this very node
98
+ self.class.update_all( "#{left_col_name} = -#{left_col_name}, #{right_col_name} = -#{right_col_name}",
99
+ "#{scope_condition} AND #{self[left_col_name]} <= #{left_col_name} AND #{right_col_name} <= #{self[right_col_name]}" )
100
+
101
+ # Shift intermediary elements to the left
102
+ self.class.update_all( "#{left_col_name} = (#{left_col_name} - #{dif})",
103
+ "#{scope_condition} AND #{self[right_col_name]} < #{left_col_name} AND #{left_col_name} < #{destination[right_col_name]}" )
104
+ self.class.update_all( "#{right_col_name} = (#{right_col_name} - #{dif})",
105
+ "#{scope_condition} AND #{self[right_col_name]} < #{right_col_name} AND #{right_col_name} < #{destination[right_col_name]}" )
106
+
107
+ # Shift intermediary elements to the right
108
+ self.class.update_all( "#{left_col_name} = (#{left_col_name} + #{dif})",
109
+ "#{scope_condition} AND #{destination[right_col_name]} < #{left_col_name} AND #{left_col_name} < #{self[left_col_name]}" )
110
+ self.class.update_all( "#{right_col_name} = (#{right_col_name} + #{dif})",
111
+ "#{scope_condition} AND #{destination[right_col_name]} <= #{right_col_name} AND #{right_col_name} < #{self[left_col_name]}" )
112
+
113
+ # Puts back this element where it should be
114
+ self.class.update_all( "#{left_col_name} = -#{left_col_name} + #{moveBy}, #{right_col_name} = -#{right_col_name} + #{moveBy}",
115
+ "#{scope_condition} AND #{self[left_col_name]} <= -#{left_col_name} AND #{right_col_name} <= -#{self[right_col_name]}" )
116
+
117
+ self.parent = destination
118
+ self.save!
119
+ }
66
120
  end
67
121
  end
68
122
 
data/app/models/mail.rb CHANGED
@@ -103,7 +103,9 @@ class Mail < ActiveRecord::Base
103
103
  rescue ActiveRecord::RecordNotFound
104
104
  elt.build_parent :parent_id => 'mail', :subject => parentId, :body => ''
105
105
  elt.parent.id = parentId
106
- elt.parent.save!
106
+ elt.parent.publish
107
+ elt.save
108
+ elt.parent.parent.add_child elt.parent
107
109
  end
108
110
 
109
111
  mngAttachment(mail) if mail
@@ -111,16 +113,10 @@ class Mail < ActiveRecord::Base
111
113
  self.message = mail.message_id
112
114
  self.mail_parents = mail.references
113
115
  self.file = mail.encoded
114
- elt.save!
115
-
116
- if elt.body =~ /^\s*(-1|0|\+1)(\s*|$)/ then
117
- #print '(', $`, ': ', Regexp.last_match(1), ')'
118
- #puts
119
- elt.parent.vote Regexp.last_match(1)
120
- end
116
+ elt.publish
117
+ elt.parent.add_child elt
121
118
  end
122
119
 
123
-
124
120
  """ _ _ _ _
125
121
  _ __ _ _| |__ | (_)___| |__
126
122
  | '_ \| | | | '_ \| | / __| '_ \
@@ -201,7 +197,14 @@ class Mail < ActiveRecord::Base
201
197
  if attachment.content_type == 'text/plain'
202
198
  charset = attachment.type_param 'charset'
203
199
  if charset and !charset.empty?
204
- elt.body += Iconv.new(charset, 'utf-8').iconv(attachment.body)
200
+ if attachment.transfer_encoding == 'quoted-printable'
201
+ # Here to correct a null character which can occur in some mails
202
+ # It looks like ==0 or ^@ !!!
203
+ # Otherwise the elt can not be saved in the db :(
204
+ elt.body += Iconv.new(charset, 'utf-8').iconv(attachment.body).gsub(/\0/, '')
205
+ else
206
+ elt.body += Iconv.new(charset, 'utf-8').iconv(attachment.body)
207
+ end
205
208
  else
206
209
  elt.body += attachment.body
207
210
  end
@@ -9,40 +9,24 @@ class MailNotify < ActionMailer::Base
9
9
  logger.info "Create a mail for publication"
10
10
  subject elt.subject
11
11
 
12
- recipients (mailing_list(elt.parent).subject.blank? ? '' : mailing_list(elt.parent).subject ) \
13
- + ' <' \
14
- + mailing_list(elt.parent).id + '@' + ActionMailer::Base.server_settings[:domain] \
15
- + '>'
12
+ ml = mailing_list(elt.parent)
13
+
14
+ recipients (ml.subject.blank? ? '' : ml.subject ) \
15
+ + " <#{ml.id}@#{ActionMailer::Base.server_settings[:domain]}>"
16
16
 
17
17
  from ((elt.person and elt.person.name) ? elt.person.name : ANONYMOUS_POSTER) \
18
- + ' <' \
18
+ + " <" \
19
19
  + ((elt.person and elt.person.email) \
20
- ? elt.person.email : ANONYMOUS_POSTER + '@' + ActionMailer::Base.server_settings[:domain]) \
21
- + '> '
20
+ ? elt.person.email : "#{ANONYMOUS_POSTER}@#{ActionMailer::Base.server_settings[:domain]}") \
21
+ + ">"
22
22
 
23
23
  # Try to render the element as html
24
- body(:elt => elt)
25
-
26
- #content_type 'multipart/alternative'
27
- #part "text/html" do |a|
28
- # a.body = render(:template => "/home/manu/develop/parlement/trunk/app/views/mail_notify/publish.text.html.rhtml",
29
- # :locals => { :elt => elt })
30
- #end
31
- #part "text/plain" do |a|
32
- # a.body = render(:template => "mail_notify/publish.text.plain",
33
- # :locals => { :elt => elt })
34
- #end
35
- # :body => email_builder.render(:file => "mail_notify/publish.text.html.rhtml")
36
- #attachment :content_type => "text/html",
37
- # :body => email_builder.render(:file => "mail_notify/publish.text.html.rhtml")
38
- #attachment :content_type => "text/plain",
39
- # :body => email_builder.render(:file => "mail_notify/publish.text.plain.rhtml")
40
-
24
+ body :elt => elt
41
25
 
42
26
  # This is the essential of a mailing list, you reply to the mailing list,
43
27
  # where every body sends their mail.
44
28
  # This very mail can be a mailing list all by itself...
45
- @headers['Reply-to'] = mailing_list(elt).id + '@' + ActionMailer::Base.server_settings[:domain]
29
+ @headers['Reply-to'] = "#{ml.id}@#{ActionMailer::Base.server_settings[:domain]}"
46
30
 
47
31
  @headers['In-Reply-To'] = elt.parent.mail.message \
48
32
  if elt.parent and elt.parent.mail and elt.parent.mail.message
@@ -56,7 +40,7 @@ class MailNotify < ActionMailer::Base
56
40
  @headers['references'] << parentMsg.message if parentMsg.message
57
41
  end
58
42
 
59
- @headers['X-Mailer'] = ActionMailer::Base.server_settings[:domain] + " v" + PARLEMENT_VERSION
43
+ @headers['X-Mailer'] = "#{ActionMailer::Base.server_settings[:domain]} v#{PARLEMENT_VERSION}"
60
44
 
61
45
  logger.info "Mail created"
62
46
  end
@@ -70,7 +54,7 @@ class MailNotify < ActionMailer::Base
70
54
  # generated ids
71
55
  #
72
56
  def mailing_list(elt)
73
- if (elt.id.size > 21 and elt.parent_id != 'ROOT') then
57
+ if (elt.id.size > 21 and elt.parent_id != 'ROOT' and !elt.subject.match 'Re: ') then
74
58
  mailing_list elt.parent
75
59
  else
76
60
  elt
data/app/models/person.rb CHANGED
@@ -6,7 +6,7 @@ class Person < ActiveRecord::Base
6
6
  has_many :issues, :through => :choices, :source => :elt
7
7
 
8
8
  validates_presence_of :name, :on => :create
9
- validates_length_of :name, :within => 3..40, :on => :create
9
+ validates_length_of :name, :within => 3..80, :on => :create
10
10
  validates_uniqueness_of :name, :on => :create
11
11
  validates_uniqueness_of :email, :on => :create,
12
12
  :if => Proc.new { |p| p.email and p.email.length > 0 }