parlement 0.9 → 0.10

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 (52) hide show
  1. data/CHANGES +20 -1
  2. data/MEMORY +51 -0
  3. data/README +11 -38
  4. data/Rakefile +2 -2
  5. data/app/controllers/account_controller.rb +83 -50
  6. data/app/controllers/application.rb +3 -0
  7. data/app/controllers/elt_controller.rb +14 -3
  8. data/app/controllers/subscriber_controller.rb +25 -9
  9. data/app/helpers/elt_helper.rb +1 -1
  10. data/app/models/elt.rb +23 -13
  11. data/app/models/mail.rb +37 -17
  12. data/app/models/mail_notify.rb +6 -5
  13. data/app/models/person.rb +6 -5
  14. data/app/models/person_mail.rb +4 -0
  15. data/app/models/person_notify.rb +13 -0
  16. data/app/models/subscription.rb +4 -0
  17. data/app/views/account/_login.rhtml +9 -12
  18. data/app/views/account/_show.rhtml +35 -16
  19. data/app/views/elt/_elt.rhtml +8 -20
  20. data/app/views/elt/_listByVote.rhtml +9 -9
  21. data/app/views/elt/show.rhtml +6 -33
  22. data/app/views/layouts/top.rhtml +6 -0
  23. data/app/views/person_notify/setEmail.rhtml +24 -0
  24. data/app/views/subscriber/_list.rhtml +18 -0
  25. data/config/environment.rb +6 -4
  26. data/config/environments/development.rb +2 -0
  27. data/db/ROOT/parlement/{news → News}/Version_01.txt +0 -0
  28. data/db/ROOT/parlement/{news → News}/Version_02.txt +0 -0
  29. data/db/ROOT/parlement/{news → News}/Version_03.txt +0 -0
  30. data/db/ROOT/parlement/{news → News}/Version_04.txt +0 -0
  31. data/db/ROOT/parlement/{news → News}/Version_05.txt +0 -0
  32. data/db/ROOT/parlement/{news → News}/Version_06.txt +0 -0
  33. data/db/ROOT/parlement/{news → News}/Version_07.txt +0 -0
  34. data/db/ROOT/parlement/{news → News}/Version_08.txt +0 -0
  35. data/db/ROOT/parlement/{security → Security}/anonymity.txt +0 -0
  36. data/db/ROOT/parlement.txt +1 -1
  37. data/db/development_structure.sql +49 -34
  38. data/db/migrate/003_elt_children_count.rb +1 -1
  39. data/db/migrate/005_filter_mail.rb +22 -0
  40. data/public/javascripts/mybehaviour.js +10 -2
  41. data/public/stylesheets/default.css +21 -15
  42. data/test/unit/choice_test.rb +1 -1
  43. data/test/unit/elt_test.rb +1 -1
  44. data/test/unit/mail_notify_test.rb +24 -8
  45. data/test/unit/mail_test.rb +61 -6
  46. data/test/unit/{notifier_test.rb → person_notify_test.rb} +2 -2
  47. data/test/unit/person_test.rb +1 -1
  48. data/test/unit/subscriber_test.rb +12 -12
  49. metadata +24 -20
  50. data/app/models/notifier.rb +0 -13
  51. data/app/views/notifier/changeEmail.rhtml +0 -10
  52. data/public/images/comments.gif +0 -0
data/CHANGES CHANGED
@@ -1,9 +1,28 @@
1
1
  - parlement changelog
2
2
 
3
+ == Version 0.10
4
+
5
+ Democratically moderated mails.
6
+
7
+ Elements are displayed on the web page or sent by mail when they reach a user
8
+ chosen voting threshold.
9
+
10
+ * one element threads are not automatically displayed anymore
11
+ * better spam fighting
12
+ * unsubscribe now also works when user is subscribed on a parent elt
13
+ * correcting a badly managed reply_to in mails
14
+ * setEmail and setPassword
15
+ * small threads automatically displayed (SUB_THREAD_LENGTH = 5)
16
+ * colored logs
17
+ * persistent cookies for authentication
18
+ * fighting the bacon spammer :-(
19
+ * easier rss auto discovery (html/head/link)
20
+ * moderated mails
21
+
3
22
  == Version 0.9
4
23
 
5
24
  Improved look, particularly the voting and writing part. Long posts and lists
6
- now truncated. Permanent login. Positioned elements. Search form (though
25
+ now truncated. Permanent login. Positioned elements. Search form (through
7
26
  google).
8
27
 
9
28
  * now asking to choose a pseudo
data/MEMORY ADDED
@@ -0,0 +1,51 @@
1
+ What is below is just for future personal reference...
2
+
3
+ http://www.ajaxchat.org/chat/
4
+
5
+
6
+ # Here is my console code to reset threads from their original mails:
7
+ >> 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
8
+ >> elts.each{|e|print e[:elt], ' ', e[:parent], ' '; puts Elt.update_all("parent_id='#{e[:parent]}'", "id='#{e[:elt]}'")}; puts elts.size
9
+
10
+ # To get and remove duplicate messages
11
+ >> messages=Mail.find_by_sql "select message from mails group by message having count(message) > 1"; puts messages.length
12
+ >> 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
13
+ >> 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
14
+
15
+ # To get all existing votes
16
+ elts=Elt.find(:all, :conditions => ["body LIKE '%%+1%%' OR body LIKE '%%0%%' OR body LIKE '%%-1%%'"]); puts elts.size
17
+ elts.each { |e| print '(', Regexp.last_match(1), ')' if e.body =~ /^\s*(-1|0|\+1)(\s*|$)/ }; puts; puts elts.size
18
+ elts.each { |e| e.vote(Regexp.last_match(1), e.person) if e.body =~ /^\s*(-1|0|\+1)(\s*|$)/ }; puts; puts elts.size
19
+
20
+
21
+ UPDATE elts SET elts_count = (SELECT COUNT(*) FROM elts e2 WHERE e2.parent_id = elts.id);
22
+
23
+ # To reconstruct the nested tree set
24
+ def construction(e, counter)
25
+ e.lft = counter
26
+ counter += 1
27
+ e.children.each { |c|
28
+ counter = construction(c, counter)
29
+ }
30
+ e.rgt = counter
31
+ counter += 1
32
+ e.save
33
+ counter
34
+ end
35
+ e=Elt.find('ROOT')
36
+ construction(e, 0)
37
+
38
+ UPDATE elts SET lft = NULL, rgt = NULL;
39
+ ALTER TABLE elts ADD CONSTRAINT elts_unique_lft UNIQUE (lft);
40
+ ALTER TABLE elts ADD CONSTRAINT elts_unique_rgt UNIQUE (rgt);
41
+
42
+ SELECT * FROM person_mails;
43
+ INSERT INTO person_mails (person_id, mail_id, created_on) VALUES ('first', 'aGaGwUHxCr25c5aaVnmTrI', now());
44
+
45
+ select id from mails;
46
+ select count(*) from mails;
47
+ select * from people;
48
+ select * from subscriptions;
49
+ select * from person_mails;
50
+ \dt
51
+
data/README CHANGED
@@ -5,10 +5,11 @@
5
5
  | .__/ \__,_|_| |_|\___|_| |_| |_|\___|_| |_|\__|
6
6
  |_|
7
7
 
8
- I use debian, here are the commands I use to setup my machine for parlement:
8
+ On debian, here are the commands used to setup the parlement machine:
9
9
  # apt-get install postgresql
10
10
  # sudo su - postgres
11
- # createuser /your_login/
11
+ ~ createuser /your_login/
12
+ ~ exit
12
13
  # apt-get install ruby
13
14
  # apt-get install libpgsql-ruby
14
15
  # apt-get install irb
@@ -22,6 +23,7 @@ You need to install "ruby gems":http://docs.rubygems.org
22
23
  And then:
23
24
  # gem install rails
24
25
  # gem install redcloth
26
+ # gem install term-ansicolor
25
27
 
26
28
  Only if you expect to develop and do any release:
27
29
  # gem install meta_project
@@ -40,54 +42,25 @@ filtering tool), define this kind of rule:
40
42
  * ^TO parlement|leparlement.org
41
43
  | /home/manu/parlement/trunk/script/runner 'Mailman.receive STDIN.read'
42
44
 
43
-
44
45
  Once you have everything set up (ruby, postgresql, redcloth, irb), you can
45
46
  initialise and launch parlement this way:
46
47
 
47
- Go into the right folder
48
- ~ cd parlement/trunk
49
-
50
- Create the db
51
- ~ psql -q template1 -f db/schema.sql
52
-
53
- Populate the db
54
- ~ ruby lib/data_import.rb
55
-
56
- Migrate the db to its last version
57
- ~ rake migrate
58
-
59
- Launch the developement web server
60
- ~ ruby ./script/server
48
+ ~ svn checkout svn://rubyforge.org/var/svn/parlement # Get the code source
49
+ ~ cd parlement/trunk # Go into the right folder
50
+ ~ createdb parlement_development # Create database
51
+ ~ psql -q template1 -f db/schema.sql # Initiate the db schema
52
+ ~ rake migrate # Get the db to its last version
53
+ ~ ruby lib/data_import.rb # Populate the db
54
+ ~ ruby ./script/server # Launch the server
61
55
 
62
56
  Now you can go to http://localhost:3000 with your browser.
63
57
 
64
58
  You can check that everything works all right on your machine with the testing suite:
65
59
  ~ rake
66
60
 
67
-
68
61
  __ __ _
69
62
  \ \ / /__| | ___ ___ _ __ ___ ___
70
63
  \ \ /\ / / _ \ |/ __/ _ \| '_ ` _ \ / _ \
71
64
  \ V V / __/ | (_| (_) | | | | | | __/
72
65
  \_/\_/ \___|_|\___\___/|_| |_| |_|\___|
73
-
74
-
75
-
76
-
77
-
78
- What is below is just for future personal reference...
79
-
80
-
81
- # Here is my console code to reset threads from their original mails:
82
- >> 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
83
- >> elts.each{|e|print e[:elt], ' ', e[:parent], ' '; puts Elt.update_all("parent_id='#{e[:parent]}'", "id='#{e[:elt]}'")}; puts elts.size
84
-
85
- # To get and remove duplicate messages
86
- >> messages=Mail.find_by_sql "select message from mails group by message having count(message) > 1"; puts messages.length
87
- >> 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
88
- >> 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
89
66
 
90
- # To get all existing votes
91
- elts=Elt.find(:all, :conditions => ["body LIKE '%%+1%%' OR body LIKE '%%0%%' OR body LIKE '%%-1%%'"]); puts elts.size
92
- elts.each { |e| print '(', Regexp.last_match(1), ')' if e.body =~ /^\s*(-1|0|\+1)(\s*|$)/ }; puts; puts elts.size
93
- elts.each { |e| e.vote(Regexp.last_match(1), e.person) if e.body =~ /^\s*(-1|0|\+1)(\s*|$)/ }; puts; puts elts.size
data/Rakefile CHANGED
@@ -56,7 +56,7 @@ spec = Gem::Specification.new do |s|
56
56
  EOF
57
57
  s.files = PKG_FILES
58
58
  s.require_path = 'lib'
59
- s.autorequire = 'rails redcloth'
59
+ s.autorequire = 'rails redcloth term-ansicolor'
60
60
  s.has_rdoc = false
61
61
  s.requirements << 'none'
62
62
  s.test_files = Dir.glob('test/unit/*')
@@ -72,7 +72,7 @@ EOF
72
72
  #s.signing_key = File.join(ENV['CERT_DIR'], 'gem-private_key.pem')
73
73
  #s.cert_chain = [File.join(ENV['CERT_DIR'], 'gem-public_cert.pem')]
74
74
  #end
75
- s.add_dependency('rails')
75
+ s.add_dependency('rails redcloth term-ansicolor')
76
76
  end
77
77
 
78
78
  desc "Build Gem"
@@ -1,4 +1,4 @@
1
- #
1
+ #
2
2
  # This class manages users inscription, login and logout
3
3
  #
4
4
  class AccountController < UserController
@@ -25,7 +25,7 @@ class AccountController < UserController
25
25
  Person.transaction(@person) do
26
26
  @person = Person.new :id => login.gsub(/\s/, '_'), :name => login
27
27
  if @person.save
28
- logger.info "person.id: #{@person.id}, name: #{@person.name}"
28
+ logger.info yellow { bold { "Person: #{@person.name}, id: #{@person.id}" } }
29
29
  flash.now[:notice] = "Pseudo created"
30
30
  session[:person] = @person
31
31
  else
@@ -34,12 +34,12 @@ class AccountController < UserController
34
34
  end
35
35
  rescue Exception => e
36
36
  flash.now[:error] = 'Error creating account'
37
- logger.error e
37
+ logger.error red { e }
38
38
  end
39
39
  end
40
40
 
41
+ # We record the password or try to authenticate
41
42
  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?
@@ -48,42 +48,28 @@ class AccountController < UserController
48
48
  flash.now[:error] = "This pseudo is protected with a password"
49
49
  end
50
50
  elsif not @user
51
- @user = User.new()
52
- begin
53
- User.transaction(@user) do
54
- @user.login = login
55
- @user.change_password(password)
56
- # To make sure even a non email protected user can use a password
57
- @user.email = login+'@nomailyet'
58
- # This is a hack, to make sure this user can login even if he
59
- # didn't verify his email
60
- @user.verified = true
61
- if @user.save
62
- flash['notice 2'] = 'Password recorded'
63
-
64
- session[:person] = @person
65
- session[:user] = @user
66
- end
67
- end
68
- rescue
69
- flash.now[:error] = 'Error with password'
70
- end
71
-
51
+ # Record password
52
+ session[:person] = @person
53
+ mngPassword
72
54
  elsif User.authenticate(login, password)
73
- # There is a password protecting this pseudo
55
+ logger.info yellow { bold { "#{@person.name} logged in" } }
56
+ # Authenticate
74
57
  session[:person] = @person
75
58
  session[:user] = @user
76
-
77
- elsif not email.empty?
59
+ elsif email and not email.empty?
60
+ # There is a check key, used to change the password
78
61
  begin
79
62
  User.transaction(@user) do
80
63
  if User.authenticate_by_token(@user.id, email)
81
64
  @user.change_password(password)
82
65
  @user.security_token = nil
83
66
  if @user.save
67
+ logger.info yellow { "Person: #{@person.name} changed its password" }
84
68
  flash.now['notice 2'] = 'Password successfully modified!'
85
69
  session[:person] = @person
86
70
  session[:user] = @user
71
+ else
72
+ @user.errors.each_full { |msg| logger.error msg }
87
73
  end
88
74
  end
89
75
  end
@@ -94,45 +80,50 @@ class AccountController < UserController
94
80
  flash.now[:error] = "Wrong password"
95
81
  end
96
82
 
97
- # Third we record the email or send a check_key for a password reset
98
- if not email.empty?
83
+ # Record the email or send a check_key for a password reset
84
+ if email and not email.empty?
99
85
  if session[:person]
100
86
  if email == @person.email
101
87
  flash.now['notice 3'] = "Email already recorded and verified"
102
88
  else
103
- signup
89
+ mngEmail
104
90
  end
105
-
106
91
  elsif email == @person.email
107
92
  # User protected by password and with the same email as entered
108
93
  key = @user.generate_security_token
109
94
  url = url_for(:action => 'check_key')
110
95
  url += "?user[id]=#{@user.id}&key=#{key}"
111
96
  UserNotify::deliver_forgot_password(@user, url)
112
- flash.now['notice 3'] = "Email with a check key sent to "+email
97
+ flash.now['notice 3'] = "Email with a check key sent to #{email}"
113
98
  end
114
99
  end
115
100
  end
116
101
 
117
102
  # Record cookies for re authentication
118
103
  if session[:person]
119
- cookies[:person_name] = session[:person].name
120
- cookies[:salted_password] = @user.salted_password if @user
104
+ cookies[:person_name] = { :value => session[:person].name, :expires => 3.years.from_now }
105
+ cookies[:salted_password] = { :value => @user.salted_password, :expires => 3.years.from_now } if @user
121
106
  end
122
107
 
108
+ # To make sure the logout knows which elt's choices to update
109
+ @elt = Elt.find(params[:elt]) if params[:elt]
110
+
123
111
  render :partial => 'show',
124
- :locals => { :divId => params[:divId], :choices => getAllVotes },
112
+ :locals => { :choices => getAllVotes },
125
113
  :status => (session[:person] ? 200 : 403)
126
114
  end
127
115
 
128
116
  def logout
117
+ logger.info yellow { bold { "Bye bye" } }
129
118
  # Cleaning up
130
119
  session[:person] = @person = session[:user] = @user = nil
131
120
  cookies.delete :person_name
132
121
  cookies.delete :salted_password
133
122
 
134
- render :partial => 'show', :locals => {
135
- :divId => params[:divId], :choices => getAllVotes }
123
+ # To make sure the logout knows which elt's choices to update
124
+ @elt = Elt.find(params[:elt]) if params[:elt]
125
+
126
+ render :partial => 'show', :locals => { :choices => getAllVotes }
136
127
  end
137
128
 
138
129
  def check_key
@@ -158,10 +149,26 @@ class AccountController < UserController
158
149
  end
159
150
  end
160
151
 
152
+ def setPassword
153
+ logger.info yellow { "Person #{session[:person].name} sets/changes his password" } if session[:person]
154
+ @person = session[:person]
155
+ @user = session[:user]
156
+ mngPassword
157
+ render :partial => 'show'
158
+ end
159
+
160
+ def setEmail
161
+ logger.info yellow { "Person #{session[:person].name} set his email to #{params[:person][:email]}" } if session[:person]
162
+ @person = session[:person]
163
+ @user = session[:user]
164
+ mngEmail
165
+ render :partial => 'show'
166
+ end
167
+
161
168
  def setAvatar
162
169
  return render(:text => "Not logged in", :status => 403) \
163
170
  unless session[:person]
164
- logger.info "Setting up an avatar for person #{session[:person].name}"
171
+ logger.info yellow { "Setting up an avatar for person #{session[:person].name}" }
165
172
 
166
173
  @elt = Elt.find_by_id 'people'
167
174
  if !@elt
@@ -192,8 +199,34 @@ class AccountController < UserController
192
199
 
193
200
  protected
194
201
 
202
+ def mngPassword
203
+ if @person and password = @params[:user][:password] and !password.empty?
204
+ @user = @person.user || User.new()
205
+ begin
206
+ User.transaction(@user) do
207
+ @user.login = @person.name
208
+ # To make sure even a non email protected user can use a password
209
+ @user.email ||= @person.name+'@nomailyet'
210
+ # This is a hack, to make sure this user can login even if he
211
+ # didn't verify his email
212
+ @user.verified = true
213
+ @user.change_password(password)
214
+ if @user.save
215
+ logger.info yellow { "Password changed" }
216
+ flash['notice 2'] = 'Password recorded'
217
+ session[:user] = @user
218
+ else
219
+ @user.errors.each_full { |msg| logger.error msg }
220
+ end
221
+ end
222
+ rescue
223
+ flash.now[:error] = 'Error with password'
224
+ end
225
+ end
226
+ end
227
+
195
228
  # Mostly copied from the login engine
196
- def signup
229
+ def mngEmail
197
230
  if @person and not @user
198
231
  @user = User.new
199
232
  @user.login = @person.name
@@ -207,31 +240,31 @@ class AccountController < UserController
207
240
  end
208
241
  if @user.save
209
242
  key = @user.generate_security_token
210
- #url = url_for(:action => 'home', 'user[id]' => @user.id, :key => key)
211
- url = url_for(:action => 'check_key')
212
- url += "?user[id]=#{@user.id}&key=#{key}"
243
+ url = url_for(:action => 'check_key', 'user[id]' => @user.id, :key => key)
244
+ #url = url_for(:action => 'check_key')
245
+ #url += "?user[id]=#{@user.id}&key=#{key}"
213
246
  #flash[:notice] = 'Signup successful!'
214
- flash.now['notice 3'] = 'Confirmation email sent to '+@user.email
215
247
  if LoginEngine.config(:use_email_notification)
216
- UserNotify.deliver_signup(@user, params[:user][:password], url)
217
- flash['notice 4'] = ' Please check your registered email account to verify your account registration.'
248
+ PersonNotify.deliver_setEmail(@person, url)
249
+ flash.now['notice 3'] = "Confirmation email sent to #{@user.email}"
218
250
  end
219
251
  #redirect_to :action => 'login'
220
252
  end
221
253
  end
222
254
  rescue Exception => e
223
255
  flash.now[:notice] = nil
256
+ flash.now['notice 2'] = nil
257
+ flash.now['notice 3'] = nil
224
258
  flash.now[:warning] = 'Error creating account: confirmation email not sent'
225
- logger.error e
259
+ e.backtrace.each { |m| logger.error m }
226
260
  end
227
261
  end
228
262
 
229
263
  def getAllVotes
230
- return if !params[:divId]
231
- elt = Elt.find params[:divId].gsub(/author_/, '')
264
+ return unless @elt
232
265
  Choice.find_all_by_person_id((session[:person] ? session[:person].id : nil),
233
266
  :include => 'elt',
234
- :conditions => "elts.lft >= '#{elt.lft}' AND elts.rgt <= '#{elt.rgt}'")
267
+ :conditions => "elts.lft >= '#{@elt.lft}' AND elts.rgt <= '#{@elt.rgt}'")
235
268
  end
236
269
  end
237
270
 
@@ -2,9 +2,11 @@
2
2
  # Likewese will all the methods added be available for all controllers.
3
3
 
4
4
  require 'login_engine'
5
+ require 'term/ansicolor'
5
6
 
6
7
  class ApplicationController < ActionController::Base
7
8
  include LoginEngine
9
+ include Term::ANSIColor
8
10
  model :user
9
11
 
10
12
  before_filter :set_charset
@@ -32,6 +34,7 @@ class ApplicationController < ActionController::Base
32
34
  ((!person.user and !cookies[:salted_password]) \
33
35
  or (person.user \
34
36
  and person.user.salted_password == cookies[:salted_password]))
37
+ logger.info yellow { bold { "#{person.name} is back" } }
35
38
  session[:person] = person
36
39
  session[:user] = person.user
37
40
  end
@@ -66,10 +66,13 @@ class EltController < ApplicationController
66
66
  @elt = Elt.new(params[:elt])
67
67
  @elt.person = @session[:person]
68
68
 
69
- if @elt.subject.match(/([<>\/]|href)/) then
70
- logger.error "SPAM! '#{@elt.subject}'"
69
+ if @elt.subject =~ /([<>\/]|href)/ \
70
+ or @elt.body =~ /(.*([<>\/]|href).*){3}/ \
71
+ or @elt.body =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i \
72
+ or @elt.body =~ /([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,}.*){3}/i
73
+ logger.error red { underline { "SPAM! '#{@elt.subject}'" } }
71
74
  logger.error "SPAM! '#{@elt.body}'"
72
- flash[:error] = 'Sorry, to fight spam "<" ">" or "href" are forbidden in the subject!'
75
+ flash[:error] = 'Sorry, to fight spam "<" ">" or "href" are forbidden in the subject, and there can not be more than 3 links in the body, you also can\'t input one simple email or more than 3 emails!'
73
76
  headers["Status"] = "404 Post considered as spam"
74
77
  render :controller => 'elt', :action => 'new', :status => 404
75
78
  elsif params[:submit] == "preview" or (@elt.publish and @elt.parent.add_child(@elt)) then
@@ -89,6 +92,14 @@ class EltController < ApplicationController
89
92
 
90
93
  def vote
91
94
  @elt = Elt.find params[:id]
95
+ unless params[:choice][:value] =~ /^\s*(-1|0|\+1)(\s|$)/ then
96
+ logger.error red { underline { "SPAM! through the vote" } }
97
+ logger.error params[:choice][:value]
98
+ flash[:error] = 'Sorry, you can only vote here!'
99
+ render :partial => '/elt/choice', :locals => { :elt => @elt }
100
+ return
101
+ end
102
+
92
103
  vote = @elt.children.build
93
104
  vote.person = session[:person]
94
105
  vote.subject = @elt.subject
@@ -1,21 +1,37 @@
1
+ #
2
+ # Mailing list management
3
+ #
4
+ # This is to add or remove subscribers
5
+ #
1
6
  class SubscriberController < ApplicationController
2
-
3
7
  def subscribe
4
- @elt = Elt.find(params[:id])
8
+ @elt = Elt.find params[:id]
5
9
 
6
10
  if @session[:person]
7
11
  if @elt.subscribers.include?(@session[:person])
8
- @elt.subscribers.delete(@session[:person])
9
- logger.info "Unsubscribed #{@session[:person].name} from #{@elt.subject}..."
10
- render :inline => "Unsubscribed..."
12
+ Subscription.find_by_person_id_and_elt_id(@session[:person].id, @elt.id).destroy
13
+ logger.info yellow { "User #{@session[:person].name} unsubscribed from #{@elt.subject}..." }
14
+ elsif @elt.all_recipients.include?(@session[:person])
15
+ elt2 = @elt
16
+ elt2 = elt2.parent until elt2.subscribers.include?(@session[:person])
17
+ Subscription.find_by_person_id_and_elt_id(@session[:person].id, elt2.id).destroy
18
+ logger.info yellow { "User #{@session[:person].name} unsubscribed from #{elt2.subject}..." }
11
19
  else
12
- @elt.subscribers << @session[:person]
13
- logger.info "Subscribed #{@session[:person].name} to #{@elt.subject}..."
14
- render :inline => "Subscribed..."
20
+ if cookies[:filter]
21
+ filter = cookies[:filter].to_i - 1
22
+ filter = nil unless filter >= 0
23
+ else
24
+ filter = 0
25
+ end
26
+ @elt.subscriptions.create :person => @session[:person], :filter => filter
27
+ logger.info yellow { "User #{@session[:person].name} subscribed to #{@elt.id} with filter #{filter}..." }
15
28
  end
16
29
  else
17
30
  puts "Not logged in!"
18
- render :inline => "First you need to login... it's easy, type a pseudo with at least 3 characters. Then ok. Then subscribe"
31
+ #render :inline => "First you need to login... it's easy, type a pseudo with at least 3 characters. Then OK"
32
+ flash.now[:error] = 'First you need to login!'
19
33
  end
34
+ render :partial => 'list'
20
35
  end
21
36
  end
37
+
@@ -66,7 +66,7 @@ module EltHelper
66
66
  and elt.subject != elt.parent.subject \
67
67
  and elt.parent.subject \
68
68
  and elt.subject.downcase != elt.parent.subject.downcase \
69
- and not elt.subject.sub(/(Re|r�f|re|sv|aw):\s*/, '') == elt.parent.subject
69
+ and not elt.subject.sub(/(Re|RE|r�f|re|sv|aw):\s*/, '') == elt.parent.subject
70
70
  end
71
71
  end
72
72
 
data/app/models/elt.rb CHANGED
@@ -16,12 +16,14 @@
16
16
  # normal characters are used)
17
17
  #
18
18
  class Elt < ActiveRecord::Base
19
- has_one :mail, :dependent => true
20
19
  belongs_to :person
20
+ has_one :mail, :dependent => :destroy
21
+
22
+ has_many :choices, :dependent => :destroy, :order => "choices.created_on"
21
23
  has_many :voters, :through => :choices, :source => :person
22
- has_many :choices, :dependent => true, :order => "choices.created_on"
23
- has_many :attachments, :dependent => true
24
- has_and_belongs_to_many :subscribers, :class_name => "Person", :join_table => "subscribers"
24
+ has_many :attachments, :dependent => :destroy
25
+ has_many :subscriptions, :dependent => :destroy
26
+ has_many :subscribers, :through => :subscriptions, :source => :person
25
27
 
26
28
  acts_as_nested_set :order => "position is not null, position, created_on"
27
29
  acts_as_tree :order => "position is not null, position, created_on", :counter_cache => true
@@ -35,11 +37,20 @@ class Elt < ActiveRecord::Base
35
37
  end
36
38
  end
37
39
 
40
+ def all_subscriptions
41
+ if parent
42
+ (subscriptions + parent.all_subscriptions).uniq
43
+ else
44
+ subscriptions
45
+ end
46
+ end
47
+
38
48
  def vote(value = 1, person = self.person)
39
49
  logger.info "#{person.name if person} vote #{value} on #{subject} #{self.choices}"
40
50
  choices.each{ |c| c.destroy if c.person == person }
41
51
  choice = choices.build :value => value, :person => person
42
52
  choice.save!
53
+ mail.publish if mail
43
54
  end
44
55
 
45
56
  # Get the vote results
@@ -54,10 +65,10 @@ class Elt < ActiveRecord::Base
54
65
  def publish
55
66
  logger.info "Publish #{subject}"
56
67
 
68
+ self.position = Regexp.last_match(1) if body =~ /^\s*position:\s*(\d+(\.\d+)?)(\s|$)/
69
+
57
70
  build_mail(:elt => self) unless mail
58
71
  mail.publish
59
-
60
- self.position = Regexp.last_match(1) if body =~ /^\s*position:\s*(\d+(\.\d+)?)(\s|$)/
61
72
  save!
62
73
 
63
74
  parent.vote Regexp.last_match(1), person if body =~ /^\s*(-1|0|\+1)(\s|$)/
@@ -73,15 +84,14 @@ class Elt < ActiveRecord::Base
73
84
  def save
74
85
  if new_record?
75
86
  # Let's generate a nice looking id
76
- self.id = new_id = subject.gsub(/\[[\w-]*\]/, '').strip \
77
- .gsub(/\s/, '_').gsub(/[^\w]+/, '').gsub(/_+/, '_') \
78
- .gsub(/(^_|_$)/, '') \
79
- if subject
87
+ self.id = new_id =
88
+ subject.gsub(/\[[\w-]*\]/, '').strip \
89
+ .gsub(/\s/, '_').gsub(/[^\w]+/, '') \
90
+ .gsub(/_+/, '_').gsub(/(^_|_$)/, '') if subject
80
91
  self.id ||= "elt"
81
92
 
82
- discrim = 0
83
- self.id = "#{new_id}_#{discrim}" \
84
- while discrim += 1 and self.class.find_by_id self.id
93
+ i = 0
94
+ self.id = "#{new_id}_#{i+=1}" while self.class.find_by_id self.id
85
95
  end
86
96
 
87
97
  super