aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock2
-rw-r--r--app/controllers/admin_request_controller.rb3
-rw-r--r--app/controllers/application_controller.rb11
-rw-r--r--app/controllers/general_controller.rb6
-rw-r--r--app/helpers/application_helper.rb7
-rw-r--r--app/models/incoming_message.rb47
-rw-r--r--app/models/info_request.rb12
-rw-r--r--app/models/info_request_event.rb2
-rw-r--r--app/models/outgoing_mailer.rb3
-rw-r--r--app/models/request_mailer.rb7
-rw-r--r--app/models/user.rb2
-rw-r--r--app/views/layouts/default.rhtml4
-rw-r--r--app/views/request/show.rhtml3
-rw-r--r--app/views/user/sign.rhtml17
-rw-r--r--config/environment.rb5
-rw-r--r--config/routes.rb2
-rw-r--r--db/migrate/098_fix_public_body_translations.rb9
-rw-r--r--lib/mail_handler/backends/mail_backend.rb51
-rw-r--r--lib/mail_handler/backends/mail_extensions.rb7
-rw-r--r--lib/mail_handler/backends/tmail_backend.rb62
-rw-r--r--lib/mail_handler/backends/tmail_extensions.rb (renamed from lib/tmail_extensions.rb)26
-rw-r--r--lib/mail_handler/mail_handler.rb (renamed from lib/tnef.rb)34
-rw-r--r--lib/tasks/translation.rake4
-rwxr-xr-xscript/handle-mail-replies.rb4
-rw-r--r--spec/controllers/general_controller_spec.rb6
-rw-r--r--spec/controllers/public_body_controller_spec.rb18
-rw-r--r--spec/controllers/request_controller_spec.rb2
-rw-r--r--spec/controllers/track_controller_spec.rb14
-rw-r--r--spec/controllers/user_controller_spec.rb70
-rw-r--r--spec/integration/create_request_spec.rb35
-rw-r--r--spec/integration/search_request_spec.rb2
-rw-r--r--spec/lib/mail_handler/mail_handler_spec.rb23
-rw-r--r--spec/lib/tmail_extensions_spec.rb45
-rw-r--r--spec/models/incoming_message_spec.rb56
-rw-r--r--spec/models/info_request_event_spec.rb47
-rw-r--r--spec/models/request_mailer_spec.rb2
-rw-r--r--spec/models/track_mailer_spec.rb2
-rw-r--r--spec/models/xapian_spec.rb22
-rw-r--r--spec/script/handle-mail-replies_spec.rb10
-rw-r--r--spec/spec_helper.rb56
-rw-r--r--vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb91
42 files changed, 491 insertions, 341 deletions
diff --git a/Gemfile b/Gemfile
index 1da116ae2..61eae74f8 100644
--- a/Gemfile
+++ b/Gemfile
@@ -16,6 +16,7 @@ gem 'gettext_i18n_rails', '>= 0.7.1'
gem 'gettext', '~> 2.3.3'
gem 'json', '~> 1.5.1'
gem 'mahoro'
+gem 'mail', :platforms => :ruby_19
gem 'memcache-client', :require => 'memcache'
gem 'locale', '>= 2.0.5'
gem 'net-http-local'
diff --git a/Gemfile.lock b/Gemfile.lock
index f2cdd9d40..c43136893 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -21,6 +21,7 @@ GEM
net-ssh (>= 2.0.14)
net-ssh-gateway (>= 1.1.0)
chunky_png (1.2.6)
+ columnize (0.3.6)
compass (0.12.2)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
@@ -156,6 +157,7 @@ DEPENDENCIES
json (~> 1.5.1)
locale (>= 2.0.5)
mahoro
+ mail
mailcatcher
memcache-client
net-http-local
diff --git a/app/controllers/admin_request_controller.rb b/app/controllers/admin_request_controller.rb
index 6b6a8525e..1de63be59 100644
--- a/app/controllers/admin_request_controller.rb
+++ b/app/controllers/admin_request_controller.rb
@@ -207,8 +207,7 @@ class AdminRequestController < AdminController
end
raw_email_data = incoming_message.raw_email.data
- mail = TMail::Mail.parse(raw_email_data)
- mail.base64_decode
+ mail = MailHandler.mail_from_raw_email(raw_email_data)
destination_request.receive(mail, raw_email_data, true)
incoming_message_id = incoming_message.id
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 3f3c169ae..f9649c868 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -117,17 +117,14 @@ class ApplicationController < ActionController::Base
# Override default error handler, for production sites.
def rescue_action_in_public(exception)
- # Call `set_view_paths` from the theme, if it exists.
+ # Looks for before_filters called something like `set_view_paths_{themename}`. These
+ # are set by the themes.
# Normally, this is called by the theme itself in a
# :before_filter, but when there's an error, this doesn't
# happen. By calling it here, we can ensure error pages are
# still styled according to the theme.
- begin
- set_view_paths
- rescue NameError => e
- if !(e.message =~ /undefined local variable or method `set_view_paths'/)
- raise
- end
+ ActionController::Base.before_filters.select{|f| f.to_s =~ /set_view_paths/}.each do |f|
+ self.send(f)
end
# Make sure expiry time for session is set (before_filters are
# otherwise missed by this override)
diff --git a/app/controllers/general_controller.rb b/app/controllers/general_controller.rb
index 0cde238cd..3ba636e29 100644
--- a/app/controllers/general_controller.rb
+++ b/app/controllers/general_controller.rb
@@ -226,12 +226,6 @@ class GeneralController < ApplicationController
redirect_to request_url(info_request)
end
- # For debugging
- def fai_test
- sleep 10
- render :text => "awake\n"
- end
-
def custom_css
long_cache
@locale = self.locale_from_params()
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 5c856383b..6411cf27e 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -112,5 +112,12 @@ module ApplicationHelper
return "#{exact_date} (#{ago_text})"
end
+ # Note that if the admin interface is proxied via another server, we can't
+ # rely on a sesssion being shared between the front end and admin interface,
+ # so need to check the status of the user.
+ def is_admin?
+ return !session[:using_admin].nil? || (!@user.nil? && @user.super?)
+ end
+
end
diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb
index 5205d0a2d..123319125 100644
--- a/app/models/incoming_message.rb
+++ b/app/models/incoming_message.rb
@@ -38,14 +38,6 @@ require 'zip/zip'
require 'mapi/msg'
require 'mapi/convert'
-# Monkeypatch! Adding some extra members to store extra info in.
-module TMail
- class Mail
- attr_accessor :url_part_number
- attr_accessor :rfc822_attachment # when a whole email message is attached as text
- attr_accessor :within_rfc822_attachment # for parts within a message attached as text (for getting subject mainly)
- end
-end
class IncomingMessage < ActiveRecord::Base
belongs_to :info_request
@@ -70,17 +62,10 @@ class IncomingMessage < ActiveRecord::Base
'application/zip' => 1,
}
- # Return the structured TMail::Mail object
- # Documentation at http://i.loveruby.net/en/projects/tmail/doc/
+ # Return a cached structured mail object
def mail(force = nil)
if (!force.nil? || @mail.nil?) && !self.raw_email.nil?
- # Hack round bug in TMail's MIME decoding.
- # Report of TMail bug:
- # http://rubyforge.org/tracker/index.php?func=detail&aid=21810&group_id=4512&atid=17370
- copy_of_raw_data = self.raw_email.data.gsub(/; boundary=\s+"/im,'; boundary="')
-
- @mail = TMail::Mail.parse(copy_of_raw_data)
- @mail.base64_decode
+ @mail = MailHandler.mail_from_raw_email(self.raw_email.data)
end
@mail
end
@@ -207,9 +192,9 @@ class IncomingMessage < ActiveRecord::Base
# Number the attachments in depth first tree order, for use in URLs.
# XXX This fills in part.rfc822_attachment and part.url_part_number within
- # all the parts of the email (see TMail monkeypatch above for how these
- # attributes are added). ensure_parts_counted must be called before using
- # the attributes.
+ # all the parts of the email (see monkeypatches in lib/mail_handler/tmail_extensions and
+ # lib/mail_handler/mail_extensions for how these attributes are added). ensure_parts_counted
+ # must be called before using the attributes.
def ensure_parts_counted
@count_parts_count = 0
_count_parts_recursive(self.mail)
@@ -222,20 +207,20 @@ class IncomingMessage < ActiveRecord::Base
_count_parts_recursive(p)
end
else
- part_filename = TMail::Mail.get_part_file_name(part)
+ part_filename = MailHandler.get_part_file_name(part)
begin
if part.content_type == 'message/rfc822'
# An email attached as text
# e.g. http://www.whatdotheyknow.com/request/64/response/102
- part.rfc822_attachment = TMail::Mail.parse(part.body)
+ part.rfc822_attachment = MailHandler.mail_from_raw_email(part.body, decode=false)
elsif part.content_type == 'application/vnd.ms-outlook' || part_filename && AlaveteliFileTypes.filename_to_mimetype(part_filename) == 'application/vnd.ms-outlook'
# An email attached as an Outlook file
# e.g. http://www.whatdotheyknow.com/request/chinese_names_for_british_politi
msg = Mapi::Msg.open(StringIO.new(part.body))
- part.rfc822_attachment = TMail::Mail.parse(msg.to_mime.to_s)
+ part.rfc822_attachment = MailHandler.mail_from_raw_email(msg.to_mime.to_s, decode=false)
elsif part.content_type == 'application/ms-tnef'
# A set of attachments in a TNEF file
- part.rfc822_attachment = TNEF.as_tmail(part.body)
+ part.rfc822_attachment = MailHandler.mail_from_tnef(part.body)
end
rescue
# If attached mail doesn't parse, treat it as text part
@@ -473,16 +458,6 @@ class IncomingMessage < ActiveRecord::Base
return text
end
- # Internal function
- def _get_part_file_name(mail)
- part_file_name = TMail::Mail.get_part_file_name(mail)
- if part_file_name.nil?
- return nil
- end
- part_file_name = part_file_name.dup
- return part_file_name
- end
-
# (This risks losing info if the unchosen alternative is the only one to contain
# useful info, but let's worry about that another time)
def get_attachment_leaves
@@ -534,7 +509,7 @@ class IncomingMessage < ActiveRecord::Base
end
# PDFs often come with this mime type, fix it up for view code
if curr_mail.content_type == 'application/octet-stream'
- part_file_name = self._get_part_file_name(curr_mail)
+ part_file_name = MailHandler.get_part_file_name(curr_mail)
calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(part_file_name, curr_mail.body)
if calc_mime
curr_mail.content_type = calc_mime
@@ -814,7 +789,7 @@ class IncomingMessage < ActiveRecord::Base
attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => hexdigest)
attachment.update_attributes(:url_part_number => leaf.url_part_number,
:content_type => leaf.content_type,
- :filename => _get_part_file_name(leaf),
+ :filename => MailHandler.get_part_file_name(leaf),
:charset => leaf.charset,
:within_rfc822_subject => within_rfc822_subject,
:body => body)
diff --git a/app/models/info_request.rb b/app/models/info_request.rb
index e1885dee6..194f8e105 100644
--- a/app/models/info_request.rb
+++ b/app/models/info_request.rb
@@ -275,7 +275,7 @@ public
return self.magic_email("request-")
end
def incoming_name_and_email
- return TMail::Address.address_from_name_and_email(self.user_name, self.incoming_email).to_s
+ return MailHandler.address_from_name_and_email(self.user_name, self.incoming_email)
end
# Subject lines for emails about the request
@@ -707,11 +707,11 @@ public
return self.public_body.is_followupable?
end
def recipient_name_and_email
- return TMail::Address.address_from_name_and_email(
+ return MailHandler.address_from_name_and_email(
_("{{law_used}} requests at {{public_body}}",
:law_used => self.law_used_short,
:public_body => self.public_body.short_or_long_name),
- self.recipient_email).to_s
+ self.recipient_email)
end
# History of some things that have happened
@@ -1124,7 +1124,11 @@ public
}
if deep
- ret[:user] = self.user.json_for_api
+ if self.user
+ ret[:user] = self.user.json_for_api
+ else
+ ret[:user_name] = self.user_name
+ end
ret[:public_body] = self.public_body.json_for_api
ret[:info_request_events] = self.info_request_events.map { |e| e.json_for_api(false) }
end
diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb
index 5a8e3416f..09eba31ab 100644
--- a/app/models/info_request_event.rb
+++ b/app/models/info_request_event.rb
@@ -384,7 +384,7 @@ class InfoRequestEvent < ActiveRecord::Base
if prev_addr.nil? || curr_addr.nil?
return false
end
- return TMail::Address.parse(prev_addr).address == TMail::Address.parse(curr_addr).address
+ return MailHandler.address_from_string(prev_addr) == MailHandler.address_from_string(curr_addr)
end
def json_for_api(deep, snippet_highlight_proc = nil)
diff --git a/app/models/outgoing_mailer.rb b/app/models/outgoing_mailer.rb
index 277794c69..503166b8a 100644
--- a/app/models/outgoing_mailer.rb
+++ b/app/models/outgoing_mailer.rb
@@ -47,7 +47,8 @@ class OutgoingMailer < ApplicationMailer
return info_request.recipient_name_and_email
else
# calling safe_mail_from from so censor rules are run
- return TMail::Address.address_from_name_and_email(incoming_message_followup.safe_mail_from, incoming_message_followup.from_email).to_s
+ return MailHandler.address_from_name_and_email(incoming_message_followup.safe_mail_from,
+ incoming_message_followup.from_email)
end
end
# Used in the preview of followup
diff --git a/app/models/request_mailer.rb b/app/models/request_mailer.rb
index 90c4c6b53..493d6961c 100644
--- a/app/models/request_mailer.rb
+++ b/app/models/request_mailer.rb
@@ -204,15 +204,14 @@ class RequestMailer < ApplicationMailer
#
# That is because we want to be sure we properly record the actual message
# received in its raw form - so any information won't be lost in a round
- # trip via TMail, or by bugs in it, and so we can use something other than
- # TMail at a later date. And so we can offer an option to download the
+ # trip via the mail handler, or by bugs in it, and so we can use something
+ # other than TMail at a later date. And so we can offer an option to download the
# actual original mail sent by the authority in the admin interface (so
# can check that attachment decoding failures are problems in the message,
# not in our code). ]
def self.receive(raw_email)
logger.info "Received mail:\n #{raw_email}" unless logger.nil?
- mail = TMail::Mail.parse(raw_email)
- mail.base64_decode
+ mail = MailHandler.mail_from_raw_email(raw_email)
new.receive(mail, raw_email)
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 70386f7e4..6e1e21481 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -203,7 +203,7 @@ class User < ActiveRecord::Base
# For use in to/from in email messages
def name_and_email
- return TMail::Address.address_from_name_and_email(self.name, self.email).to_s
+ return MailHandler.address_from_name_and_email(self.name, self.email)
end
# The "internal admin" is a special user for internal use.
diff --git a/app/views/layouts/default.rhtml b/app/views/layouts/default.rhtml
index 29ff209b9..8c4ae588b 100644
--- a/app/views/layouts/default.rhtml
+++ b/app/views/layouts/default.rhtml
@@ -12,7 +12,7 @@
<link rel="shortcut icon" href="/favicon.ico">
<%= render :partial => 'general/stylesheet_includes' %>
- <% if session[:using_admin] %>
+ <% if is_admin? %>
<%= stylesheet_link_tag "/adminbootstraptheme/stylesheets/admin", :title => "Main", :rel => "stylesheet" %>
<% end %>
@@ -63,7 +63,7 @@
</script>
<% end %>
-<% if session[:using_admin] %>
+<% if is_admin? %>
<%= render :partial => 'admin_general/admin_navbar' %>
<% end %>
diff --git a/app/views/request/show.rhtml b/app/views/request/show.rhtml
index cf1f971d9..7aff1aeab 100644
--- a/app/views/request/show.rhtml
+++ b/app/views/request/show.rhtml
@@ -22,8 +22,6 @@
</div>
<% end %>
-<%= render :partial => 'sidebar' %>
-
<div id="left_column">
<h1><%=h(@info_request.title)%></h1>
@@ -148,3 +146,4 @@
<%= render :partial => 'after_actions' %>
</div>
+<%= render :partial => 'sidebar' %>
diff --git a/app/views/user/sign.rhtml b/app/views/user/sign.rhtml
index 4704ea95a..6a1979155 100644
--- a/app/views/user/sign.rhtml
+++ b/app/views/user/sign.rhtml
@@ -1,16 +1,16 @@
+
<% if !@post_redirect.nil? && @post_redirect.reason_params[:user_name] %>
<% @title = _("Sign in") %>
-
<div id="sign_alone">
<p id="sign_in_reason">
<% if @post_redirect.reason_params[:web].empty? %>
<%= _('Please sign in as ')%><%= link_to h(@post_redirect.reason_params[:user_name]), @post_redirect.reason_params[:user_url] %>.
<% else %>
- <%= @post_redirect.reason_params[:web] %>,
+ <%= @post_redirect.reason_params[:web] %>,
<%= _('please sign in as ')%><%= link_to h(@post_redirect.reason_params[:user_name]), @post_redirect.reason_params[:user_url] %>.
<% end %>
- </p>
+ </p>
<% if @post_redirect.post_params["controller"] == "admin_general" %>
<p id="superuser_message">Don't have a superuser account yet? <%= link_to "Sign in as the emergency user", @post_redirect.uri + "?emergency=1" %></p>
<% end %>
@@ -22,7 +22,16 @@
<% else %>
<% @title = _('Sign in or make a new account') %>
- <div id="sign_together">
+ <div id="sign_together">
+ <% if !@post_redirect.nil? %>
+ <p id="sign_in_reason">
+ <% if @post_redirect.reason_params[:web].empty? %>
+ <%= _('Please sign in or make a new account.') %>
+ <% else %>
+ <%= _('{{reason}}, please sign in or make a new account.', :reason => @post_redirect.reason_params[:web]) %>
+ <% end %>
+ </p>
+ <% end %>
<div id="left_half">
<h1><%= _('Sign in') %></h1>
<%= render :partial => 'signin', :locals => { :sign_in_as_existing_user => false } %>
diff --git a/config/environment.rb b/config/environment.rb
index 492446a43..e79efdcfa 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -92,6 +92,8 @@ Rails::Initializer.run do |config|
require 'routing_filters.rb'
end
+ config.autoload_paths << "#{RAILS_ROOT}/lib/mail_handler"
+
# See Rails::Configuration for more options
ENV['RECAPTCHA_PUBLIC_KEY'] = Configuration::recaptcha_public_key
ENV['RECAPTCHA_PRIVATE_KEY'] = Configuration::recaptcha_private_key
@@ -140,7 +142,6 @@ WillPaginate::ViewHelpers.pagination_options[:renderer] = 'WillPaginateExtension
# Load monkey patches and other things from lib/
require 'ruby19.rb'
-require 'tmail_extensions.rb'
require 'activesupport_cache_extensions.rb'
require 'timezone_fixes.rb'
require 'use_spans_for_errors.rb'
@@ -148,12 +149,12 @@ require 'make_html_4_compliant.rb'
require 'activerecord_errors_extensions.rb'
require 'willpaginate_extension.rb'
require 'sendmail_return_path.rb'
-require 'tnef.rb'
require 'i18n_fixes.rb'
require 'rack_quote_monkeypatch.rb'
require 'world_foi_websites.rb'
require 'alaveteli_external_command.rb'
require 'quiet_opener.rb'
+require 'mail_handler'
if !Configuration.exception_notifications_from.blank? && !Configuration.exception_notifications_to.blank?
ExceptionNotification::Notifier.sender_address = Configuration::exception_notifications_from
diff --git a/config/routes.rb b/config/routes.rb
index 5fc0075a4..3512b4cd4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -32,8 +32,6 @@ ActionController::Routing::Routes.draw do |map|
general.advanced_search '/advancedsearch', :action => 'search_redirect', :advanced => true
general.random_request '/random', :action => 'random_request'
-
- general.fai_test '/test', :action => 'fai_test'
end
map.with_options :controller => 'request' do |request|
diff --git a/db/migrate/098_fix_public_body_translations.rb b/db/migrate/098_fix_public_body_translations.rb
index 9bcefb1c0..25f36336f 100644
--- a/db/migrate/098_fix_public_body_translations.rb
+++ b/db/migrate/098_fix_public_body_translations.rb
@@ -15,13 +15,12 @@ class FixPublicBodyTranslations < ActiveRecord::Migration
where first_letter is null
;
SQL
-
+
execute <<-SQL
update public_body_translations
- set publication_scheme = public_bodies.publication_scheme
- from public_bodies
- where public_body_translations.public_body_id = public_bodies.id
- and public_body_translations.publication_scheme is null
+ set publication_scheme = (SELECT public_bodies.publication_scheme FROM public_bodies WHERE
+public_body_translations.public_body_id = public_bodies.id )
+ where public_body_translations.publication_scheme is null
;
SQL
end
diff --git a/lib/mail_handler/backends/mail_backend.rb b/lib/mail_handler/backends/mail_backend.rb
new file mode 100644
index 000000000..0e198adf0
--- /dev/null
+++ b/lib/mail_handler/backends/mail_backend.rb
@@ -0,0 +1,51 @@
+require 'mail'
+
+module MailHandler
+ module Backends
+ module MailBackend
+
+ def backend()
+ 'Mail'
+ end
+
+ # Note that the decode flag is not yet used
+ def mail_from_raw_email(data, decode=true)
+ Mail.new(data)
+ end
+
+ # Extracts all attachments from the given TNEF file as a Mail object
+ def mail_from_tnef(content)
+ main = Mail.new
+ tnef_attachments(content).each do |attachment|
+ main.add_file(attachment)
+ end
+ main.ready_to_send!
+ main
+ end
+
+ # Return a copy of the file name for the mail part
+ def get_part_file_name(mail_part)
+ part_file_name = mail_part.filename
+ part_file_name.nil? ? nil : part_file_name.dup
+ end
+
+ # Format
+ def address_from_name_and_email(name, email)
+ if !MySociety::Validate.is_valid_email(email)
+ raise "invalid email " + email + " passed to address_from_name_and_email"
+ end
+ if name.nil?
+ return Mail::Address.new(email)
+ end
+ address = Mail::Address.new
+ address.display_name = name
+ address.address = email
+ address.to_s
+ end
+
+ def address_from_string(string)
+ Mail::Address.new(string).address
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/mail_handler/backends/mail_extensions.rb b/lib/mail_handler/backends/mail_extensions.rb
new file mode 100644
index 000000000..cbe0491ed
--- /dev/null
+++ b/lib/mail_handler/backends/mail_extensions.rb
@@ -0,0 +1,7 @@
+module Mail
+ class Message
+ attr_accessor :url_part_number
+ attr_accessor :rfc822_attachment # when a whole email message is attached as text
+ attr_accessor :within_rfc822_attachment # for parts within a message attached as text (for getting subject mainly)
+ end
+end \ No newline at end of file
diff --git a/lib/mail_handler/backends/tmail_backend.rb b/lib/mail_handler/backends/tmail_backend.rb
new file mode 100644
index 000000000..87aba73d7
--- /dev/null
+++ b/lib/mail_handler/backends/tmail_backend.rb
@@ -0,0 +1,62 @@
+module MailHandler
+ module Backends
+ module TmailBackend
+
+ def backend()
+ 'TMail'
+ end
+
+ # Turn raw data into a structured TMail::Mail object
+ # Documentation at http://i.loveruby.net/en/projects/tmail/doc/
+ def mail_from_raw_email(data, decode=true)
+ # Hack round bug in TMail's MIME decoding.
+ # Report of TMail bug:
+ # http://rubyforge.org/tracker/index.php?func=detail&aid=21810&group_id=4512&atid=17370
+ copy_of_raw_data = data.gsub(/; boundary=\s+"/im,'; boundary="')
+ mail = TMail::Mail.parse(copy_of_raw_data)
+ mail.base64_decode if decode
+ mail
+ end
+
+ # Extracts all attachments from the given TNEF file as a TMail::Mail object
+ def mail_from_tnef(content)
+ main = TMail::Mail.new
+ main.set_content_type 'multipart', 'mixed', { 'boundary' => TMail.new_boundary }
+ tnef_attachments(content).each do |attachment|
+ tmail_attachment = TMail::Mail.new
+ tmail_attachment['content-location'] = attachment[:filename]
+ tmail_attachment.body = attachment[:content]
+ main.parts << tmail_attachment
+ end
+ main
+ end
+
+ # Return a copy of the file name for the mail part
+ def get_part_file_name(mail_part)
+ part_file_name = TMail::Mail.get_part_file_name(mail_part)
+ if part_file_name.nil?
+ return nil
+ end
+ part_file_name = part_file_name.dup
+ return part_file_name
+ end
+
+ def address_from_name_and_email(name, email)
+ if !MySociety::Validate.is_valid_email(email)
+ raise "invalid email " + email + " passed to address_from_name_and_email"
+ end
+ if name.nil?
+ return TMail::Address.parse(email).to_s
+ end
+ # Botch an always quoted RFC address, then parse it
+ name = name.gsub(/(["\\])/, "\\\\\\1")
+ TMail::Address.parse('"' + name + '" <' + email + '>').to_s
+ end
+
+ def address_from_string(string)
+ TMail::Address.parse(string).address
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/tmail_extensions.rb b/lib/mail_handler/backends/tmail_extensions.rb
index 6a533e658..9359dfeea 100644
--- a/lib/tmail_extensions.rb
+++ b/lib/mail_handler/backends/tmail_extensions.rb
@@ -15,6 +15,12 @@ require 'tmail/interface'
# These mainly used in app/models/incoming_message.rb
module TMail
class Mail
+ # Monkeypatch! Adding some extra members to store extra info in.
+
+ attr_accessor :url_part_number
+ attr_accessor :rfc822_attachment # when a whole email message is attached as text
+ attr_accessor :within_rfc822_attachment # for parts within a message attached as text (for getting subject mainly)
+
# Monkeypatch! (check to see if this becomes a standard function in
# TMail::Mail, then use that, whatever it is called)
def Mail.get_part_file_name(part)
@@ -68,22 +74,6 @@ module TMail
end
- class Address
- # Monkeypatch! Constructor which makes a TMail::Address given
- # a name and an email
- def Address.address_from_name_and_email(name, email)
- if !MySociety::Validate.is_valid_email(email)
- raise "invalid email " + email + " passed to address_from_name_and_email"
- end
- if name.nil?
- return TMail::Address.parse(email)
- end
- # Botch an always quoted RFC address, then parse it
- name = name.gsub(/(["\\])/, "\\\\\\1")
- return TMail::Address.parse('"' + name + '" <' + email + '>')
- end
- end
-
module TextUtils
# Monkeypatch! Much more aggressive list of characters to cause quoting
# than in normal TMail. e.g. Have found real cases where @ needs quoting.
@@ -95,8 +85,8 @@ module TMail
end
end
-# Monkeypatch! TMail 1.2.7.1 will parse only one address out of a list of addresses with
-# unquoted display parts https://github.com/mikel/tmail/issues#issue/9 - this monkeypatch
+# Monkeypatch! TMail 1.2.7.1 will parse only one address out of a list of addresses with
+# unquoted display parts https://github.com/mikel/tmail/issues#issue/9 - this monkeypatch
# fixes this issue.
module TMail
diff --git a/lib/tnef.rb b/lib/mail_handler/mail_handler.rb
index 1c941f8b0..24d14b5c8 100644
--- a/lib/tnef.rb
+++ b/lib/mail_handler/mail_handler.rb
@@ -1,13 +1,23 @@
+# Handles the parsing of email
require 'tmpdir'
-class TNEF
+module MailHandler
- # Extracts all attachments from the given TNEF file as a TMail::Mail object
- # The TNEF file also contains the message body, but in general this is the
+ if RUBY_VERSION.to_f >= 1.9
+ require 'backends/mail_extensions'
+ require 'backends/mail_backend'
+ include Backends::MailBackend
+ else
+ require 'backends/tmail_extensions'
+ require 'backends/tmail_backend'
+ include Backends::TmailBackend
+ end
+
+ # Returns a set of attachments from the given TNEF contents
+ # The TNEF contents also contains the message body, but in general this is the
# same as the message body in the message proper.
- def self.as_tmail(content)
- main = TMail::Mail.new
- main.set_content_type 'multipart', 'mixed', { 'boundary' => TMail.new_boundary }
+ def tnef_attachments(content)
+ attachments = []
Dir.mktmpdir do |dir|
IO.popen("#{`which tnef`.chomp} -K -C #{dir}", "w") do |f|
f.write(content)
@@ -23,10 +33,8 @@ class TNEF
Dir.new(dir).sort.each do |file| # sort for deterministic behaviour
if file != "." && file != ".."
file_content = File.open("#{dir}/#{file}", "r").read
- attachment = TMail::Mail.new
- attachment['content-location'] = file
- attachment.body = file_content
- main.parts << attachment
+ attachments << { :content => file_content,
+ :filename => file }
found += 1
end
end
@@ -34,7 +42,11 @@ class TNEF
raise IOError, "tnef produced no attachments"
end
end
- main
+ attachments
end
+ # Turn instance methods into class methods
+ extend self
+
end
+
diff --git a/lib/tasks/translation.rake b/lib/tasks/translation.rake
index f6611cc80..273c12bfa 100644
--- a/lib/tasks/translation.rake
+++ b/lib/tasks/translation.rake
@@ -4,7 +4,7 @@ namespace :translation do
include Usage
def write_email(email, email_description, output_file)
- mail_object = TMail::Mail.parse(email.to_s)
+ mail_object = MailHandler.mail_from_raw_email(email.to_s, decode=false)
output_file.write("\n")
output_file.write("Description of email: #{email_description}\n")
output_file.write("Subject line: #{mail_object.subject}\n")
@@ -86,7 +86,7 @@ namespace :translation do
'fixtures',
'files',
'incoming-request-plain.email'))
- response_mail = TMail::Mail.parse(content)
+ response_mail = MailHandler.mail_from_raw_email(content, decode=false)
response_mail.from = "authority@example.com"
stopped_responses_email = RequestMailer.create_stopped_responses(info_request,
diff --git a/script/handle-mail-replies.rb b/script/handle-mail-replies.rb
index f4ffb61f8..c05dca453 100755
--- a/script/handle-mail-replies.rb
+++ b/script/handle-mail-replies.rb
@@ -16,17 +16,19 @@ $alaveteli_dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
$:.push(File.join($alaveteli_dir, "commonlib", "rblib"))
load "config.rb"
$:.push(File.join($alaveteli_dir, "lib"))
+$:.push(File.join($alaveteli_dir, "lib", "mail_handler"))
require "configuration"
MySociety::Config.set_file(File.join($alaveteli_dir, 'config', 'general'), true)
MySociety::Config.load_default
require 'action_mailer'
+require 'mail_handler'
def main(in_test_mode)
Dir.chdir($alaveteli_dir) do
raw_message = $stdin.read
begin
- message = TMail::Mail.parse(raw_message)
+ message = MailHandler.mail_from_raw_email(raw_message, decode=false)
rescue
# Error parsing message. Just pass it on, to be on the safe side.
forward_on(raw_message) unless in_test_mode
diff --git a/spec/controllers/general_controller_spec.rb b/spec/controllers/general_controller_spec.rb
index 935f8eab6..830486493 100644
--- a/spec/controllers/general_controller_spec.rb
+++ b/spec/controllers/general_controller_spec.rb
@@ -144,7 +144,7 @@ describe GeneralController, "when showing the front page with fixture data" do
describe 'when constructing the list of recent requests' do
before(:each) do
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
describe 'when there are fewer than five successful requests' do
@@ -189,8 +189,8 @@ describe GeneralController, 'when using xapian search' do
# rebuild xapian index after fixtures loaded
before(:each) do
- load_raw_emails_data
- rebuild_xapian_index
+ load_raw_emails_data
+ get_fixtures_xapian_index
end
it "should redirect from search query URL to pretty URL" do
diff --git a/spec/controllers/public_body_controller_spec.rb b/spec/controllers/public_body_controller_spec.rb
index d12818a1c..29ece18cb 100644
--- a/spec/controllers/public_body_controller_spec.rb
+++ b/spec/controllers/public_body_controller_spec.rb
@@ -6,7 +6,7 @@ describe PublicBodyController, "when showing a body" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should be successful" do
@@ -29,14 +29,14 @@ describe PublicBodyController, "when showing a body" do
assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ InfoRequest.all(
:conditions => ["public_body_id = ?", public_bodies(:geraldine_public_body).id])
end
-
+
it "should assign the requests (2)" do
get :show, :url_name => "tgq", :view => 'successful'
assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ InfoRequest.all(
:conditions => ["described_state = ? and public_body_id = ?",
"successful", public_bodies(:geraldine_public_body).id])
end
-
+
it "should assign the requests (3)" do
get :show, :url_name => "dfh", :view => 'all'
assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ InfoRequest.all(
@@ -66,7 +66,7 @@ describe PublicBodyController, "when showing a body" do
ActionController::Routing::Routes.filters = old_filters
end
-
+
it "should redirect to newest name if you use historic name of public body in URL" do
get :show, :url_name => "hdink", :view => 'all'
response.should redirect_to(:controller => 'public_body', :action => 'show', :url_name => "dfh")
@@ -148,7 +148,7 @@ describe PublicBodyController, "when listing bodies" do
get :list, :tag => "other"
response.should render_template('list')
assigns[:public_bodies].should =~ PublicBody.all(:conditions => "id not in (#{public_bodies(:humpadink_public_body).id}, #{PublicBody.internal_admin_body.id})")
-
+
get :list
response.should render_template('list')
assigns[:public_bodies].should =~ PublicBody.all(:conditions => "id <> #{PublicBody.internal_admin_body.id}")
@@ -194,10 +194,10 @@ end
describe PublicBodyController, "when doing type ahead searches" do
integrate_views
-
+
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should return nothing for the empty query string" do
@@ -205,7 +205,7 @@ describe PublicBodyController, "when doing type ahead searches" do
response.should render_template('public_body/_search_ahead')
assigns[:xapian_requests].should be_nil
end
-
+
it "should return a body matching the given keyword, but not users with a matching description" do
get :search_typeahead, :query => "Geraldine"
response.should render_template('public_body/_search_ahead')
@@ -230,7 +230,7 @@ describe PublicBodyController, "when doing type ahead searches" do
end
it "should not return matches for short words" do
- get :search_typeahead, :query => "b"
+ get :search_typeahead, :query => "b"
response.should render_template('public_body/_search_ahead')
assigns[:xapian_requests].should be_nil
end
diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb
index b0223588e..e898fb91b 100644
--- a/spec/controllers/request_controller_spec.rb
+++ b/spec/controllers/request_controller_spec.rb
@@ -5,7 +5,7 @@ describe RequestController, "when listing recent requests" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should be successful" do
diff --git a/spec/controllers/track_controller_spec.rb b/spec/controllers/track_controller_spec.rb
index 7daa23769..c785960b5 100644
--- a/spec/controllers/track_controller_spec.rb
+++ b/spec/controllers/track_controller_spec.rb
@@ -38,7 +38,7 @@ describe TrackController, "when making a new track on a request" do
get :track_request, :url_title => @ir.url_title, :feed => 'track'
response.should redirect_to(:controller => 'request', :action => 'show', :url_title => @ir.url_title)
end
-
+
it "should 404 for non-existent requests" do
session[:user_id] = @user.id
lambda {
@@ -61,9 +61,9 @@ describe TrackController, "when sending alerts for a track" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
-
+
it "should send alerts" do
# Don't do clever locale-insertion-unto-URL stuff
old_filters = ActionController::Routing::Routes.filters
@@ -138,7 +138,7 @@ describe TrackController, "when viewing RSS feed for a track" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should get the RSS feed" do
@@ -168,7 +168,7 @@ describe TrackController, "when viewing JSON version of a track feed" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should get the feed" do
@@ -210,9 +210,9 @@ describe TrackController, "when tracking a public body" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
-
+
it "should work" do
geraldine = public_bodies(:geraldine_public_body)
get :track_public_body, :feed => 'feed', :url_name => geraldine.url_name
diff --git a/spec/controllers/user_controller_spec.rb b/spec/controllers/user_controller_spec.rb
index 386d1b04b..23006803b 100644
--- a/spec/controllers/user_controller_spec.rb
+++ b/spec/controllers/user_controller_spec.rb
@@ -8,9 +8,9 @@ describe UserController, "when showing a user" do
integrate_views
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
-
+
it "should be successful" do
get :show, :url_name => "bob_smith"
response.should be_success
@@ -45,7 +45,7 @@ describe UserController, "when showing a user" do
get :show, :url_name => "bob_smith"
assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ InfoRequest.all(
:conditions => "user_id = #{users(:bob_smith_user).id}")
-
+
get :show, :url_name => "bob_smith", :user_query => "money"
assigns[:xapian_requests].results.map{|x|x[:model].info_request}.should =~ [
info_requests(:naughty_chicken_request),
@@ -218,7 +218,7 @@ describe UserController, "when signing in" do
# Get the confirmation URL, and check we’re still Joe
get :confirm, :email_token => post_redirect.email_token
session[:user_id].should == users(:admin_user).id
-
+
# And the redirect should still work, of course
response.should redirect_to(:controller => 'request', :action => 'list', :post_redirect => 1)
@@ -232,21 +232,21 @@ describe UserController, "when signing up" do
it "should be an error if you type the password differently each time" do
post :signup, { :user_signup => { :email => 'new@localhost', :name => 'New Person',
- :password => 'sillypassword', :password_confirmation => 'sillypasswordtwo' }
+ :password => 'sillypassword', :password_confirmation => 'sillypasswordtwo' }
}
assigns[:user_signup].errors[:password].should == 'Please enter the same password twice'
end
it "should be an error to sign up with a misformatted email" do
post :signup, { :user_signup => { :email => 'malformed-email', :name => 'Mr Malformed',
- :password => 'sillypassword', :password_confirmation => 'sillypassword' }
+ :password => 'sillypassword', :password_confirmation => 'sillypassword' }
}
assigns[:user_signup].errors[:email].should_not be_nil
end
it "should send confirmation mail if you fill in the form right" do
post :signup, { :user_signup => { :email => 'new@localhost', :name => 'New Person',
- :password => 'sillypassword', :password_confirmation => 'sillypassword' }
+ :password => 'sillypassword', :password_confirmation => 'sillypassword' }
}
response.should render_template('confirm')
@@ -270,13 +270,13 @@ describe UserController, "when signing up" do
it "should send special 'already signed up' mail if you fill the form in with existing registered email" do
post :signup, { :user_signup => { :email => 'silly@localhost', :name => 'New Person',
- :password => 'sillypassword', :password_confirmation => 'sillypassword' }
+ :password => 'sillypassword', :password_confirmation => 'sillypassword' }
}
response.should render_template('confirm')
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
-
+
# This text may span a line break, depending on the length of the SITE_NAME
deliveries[0].body.should match(/when\s+you\s+already\s+have\s+an/)
end
@@ -377,7 +377,7 @@ describe UserController, "when changing password" do
get :signchangepassword
response.should render_template('signchangepassword')
end
-
+
it "should change the password, if you have right to do so" do
session[:user_id] = users(:bob_smith_user).id
session[:user_circumstance] = "change_password"
@@ -437,8 +437,8 @@ describe UserController, "when changing email address" do
it "should be an error if the password is wrong, everything else right" do
@user = users(:bob_smith_user)
session[:user_id] = @user.id
-
- post :signchangeemail, { :signchangeemail => { :old_email => 'bob@localhost',
+
+ post :signchangeemail, { :signchangeemail => { :old_email => 'bob@localhost',
:password => 'donotknowpassword', :new_email => 'newbob@localhost' },
:submitted_signchangeemail_do => 1
}
@@ -455,8 +455,8 @@ describe UserController, "when changing email address" do
it "should be an error if old email is wrong, everything else right" do
@user = users(:bob_smith_user)
session[:user_id] = @user.id
-
- post :signchangeemail, { :signchangeemail => { :old_email => 'bob@moo',
+
+ post :signchangeemail, { :signchangeemail => { :old_email => 'bob@moo',
:password => 'jonespassword', :new_email => 'newbob@localhost' },
:submitted_signchangeemail_do => 1
}
@@ -473,8 +473,8 @@ describe UserController, "when changing email address" do
it "should work even if the old email had a case difference" do
@user = users(:bob_smith_user)
session[:user_id] = @user.id
-
- post :signchangeemail, { :signchangeemail => { :old_email => 'BOB@localhost',
+
+ post :signchangeemail, { :signchangeemail => { :old_email => 'BOB@localhost',
:password => 'jonespassword', :new_email => 'newbob@localhost' },
:submitted_signchangeemail_do => 1
}
@@ -485,8 +485,8 @@ describe UserController, "when changing email address" do
it "should send confirmation email if you get all the details right" do
@user = users(:bob_smith_user)
session[:user_id] = @user.id
-
- post :signchangeemail, { :signchangeemail => { :old_email => 'bob@localhost',
+
+ post :signchangeemail, { :signchangeemail => { :old_email => 'bob@localhost',
:password => 'jonespassword', :new_email => 'newbob@localhost' },
:submitted_signchangeemail_do => 1
}
@@ -521,16 +521,16 @@ describe UserController, "when changing email address" do
post_redirect = PostRedirect.find_by_email_token(mail_token)
post_redirect.circumstance.should == 'change_email'
post_redirect.user.should == users(:bob_smith_user)
- post_redirect.post_params.should == {"submitted_signchangeemail_do"=>"1",
- "action"=>"signchangeemail",
+ post_redirect.post_params.should == {"submitted_signchangeemail_do"=>"1",
+ "action"=>"signchangeemail",
"signchangeemail"=>{
- "old_email"=>"bob@localhost",
- "new_email"=>"newbob@localhost"},
+ "old_email"=>"bob@localhost",
+ "new_email"=>"newbob@localhost"},
"controller"=>"user"}
post :signchangeemail, post_redirect.post_params
response.should redirect_to(:controller => 'user', :action => 'show', :url_name => 'bob_smith')
- flash[:notice].should match(/You have now changed your email address/)
+ flash[:notice].should match(/You have now changed your email address/)
@user.reload
@user.email.should == 'newbob@localhost'
@user.email_confirmed.should == true
@@ -539,8 +539,8 @@ describe UserController, "when changing email address" do
it "should send special 'already signed up' mail if you try to change your email to one already used" do
@user = users(:bob_smith_user)
session[:user_id] = @user.id
-
- post :signchangeemail, { :signchangeemail => { :old_email => 'bob@localhost',
+
+ post :signchangeemail, { :signchangeemail => { :old_email => 'bob@localhost',
:password => 'jonespassword', :new_email => 'silly@localhost' },
:submitted_signchangeemail_do => 1
}
@@ -572,9 +572,9 @@ describe UserController, "when using profile photos" do
@uploadedfile_2 = File.open(file_fixture_name("parrot.jpg"))
@uploadedfile_2.stub!(:original_filename).and_return('parrot.jpg')
end
-
+
it "should not let you change profile photo if you're not logged in as the user" do
- post :set_profile_photo, { :id => @user.id, :file => @uploadedfile, :submitted_draft_profile_photo => 1, :automatically_crop => 1 }
+ post :set_profile_photo, { :id => @user.id, :file => @uploadedfile, :submitted_draft_profile_photo => 1, :automatically_crop => 1 }
end
it "should return a 404 not a 500 when a profile photo has not been set" do
@@ -588,10 +588,10 @@ describe UserController, "when using profile photos" do
@user.profile_photo.should be_nil
session[:user_id] = @user.id
- post :set_profile_photo, { :id => @user.id, :file => @uploadedfile, :submitted_draft_profile_photo => 1, :automatically_crop => 1 }
+ post :set_profile_photo, { :id => @user.id, :file => @uploadedfile, :submitted_draft_profile_photo => 1, :automatically_crop => 1 }
response.should redirect_to(:controller => 'user', :action => 'show', :url_name => "bob_smith")
- flash[:notice].should match(/Thank you for updating your profile photo/)
+ flash[:notice].should match(/Thank you for updating your profile photo/)
@user.reload
@user.profile_photo.should_not be_nil
@@ -601,13 +601,13 @@ describe UserController, "when using profile photos" do
@user.profile_photo.should be_nil
session[:user_id] = @user.id
- post :set_profile_photo, { :id => @user.id, :file => @uploadedfile, :submitted_draft_profile_photo => 1, :automatically_crop => 1 }
+ post :set_profile_photo, { :id => @user.id, :file => @uploadedfile, :submitted_draft_profile_photo => 1, :automatically_crop => 1 }
response.should redirect_to(:controller => 'user', :action => 'show', :url_name => "bob_smith")
- flash[:notice].should match(/Thank you for updating your profile photo/)
+ flash[:notice].should match(/Thank you for updating your profile photo/)
- post :set_profile_photo, { :id => @user.id, :file => @uploadedfile_2, :submitted_draft_profile_photo => 1, :automatically_crop => 1 }
+ post :set_profile_photo, { :id => @user.id, :file => @uploadedfile_2, :submitted_draft_profile_photo => 1, :automatically_crop => 1 }
response.should redirect_to(:controller => 'user', :action => 'show', :url_name => "bob_smith")
- flash[:notice].should match(/Thank you for updating your profile photo/)
+ flash[:notice].should match(/Thank you for updating your profile photo/)
@user.reload
@user.profile_photo.should_not be_nil
@@ -617,7 +617,7 @@ describe UserController, "when using profile photos" do
end
describe UserController, "when showing JSON version for API" do
-
+
it "should be successful" do
get :show, :url_name => "bob_smith", :format => "json"
@@ -634,7 +634,7 @@ describe UserController, "when viewing the wall" do
integrate_views
before(:each) do
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should show users stuff on their wall, most recent first" do
diff --git a/spec/integration/create_request_spec.rb b/spec/integration/create_request_spec.rb
index 56757c7e0..4efbf94ee 100644
--- a/spec/integration/create_request_spec.rb
+++ b/spec/integration/create_request_spec.rb
@@ -1,23 +1,30 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "When creating requests" do
- it "should associate the request with the requestor, even if it is approved by an admin" do
- # This is a test for https://github.com/mysociety/alaveteli/issues/446
- params = { :info_request => { :public_body_id => public_bodies(:geraldine_public_body).id,
- :title => "Why is your quango called Geraldine?", :tag_string => "" },
- :outgoing_message => { :body => "This is a silly letter. It is too short to be interesting." },
- :submitted_new_request => 1, :preview => 0
- }
+ def create_request_unregistered
+ params = { :info_request => { :public_body_id => public_bodies(:geraldine_public_body).id,
+ :title => "Why is your quango called Geraldine?",
+ :tag_string => "" },
+ :outgoing_message => { :body => "This is a silly letter. It is too short to be interesting." },
+ :submitted_new_request => 1,
+ :preview => 0
+ }
- # Initially we are not logged in. Try to create a new request.
- post "/new", params
- # We expect to be redirected to the login page
- post_redirect = PostRedirect.get_last_post_redirect
- response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
- follow_redirect!
- response.should render_template("user/sign")
+ # Initially we are not logged in. Try to create a new request.
+ post "/new", params
+ # We expect to be redirected to the login page
+ post_redirect = PostRedirect.get_last_post_redirect
+ response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
+ follow_redirect!
+ response.should render_template("user/sign")
+ response.body.should match(/To send your FOI request, please sign in or make a new account./)
+ end
+ it "should associate the request with the requestor, even if it is approved by an admin" do
+ # This is a test for https://github.com/mysociety/alaveteli/issues/446
+ create_request_unregistered
+ post_redirect = PostRedirect.get_last_post_redirect
# Now log in as an unconfirmed user.
post "/profile/sign_in", :user_signin => {:email => users(:unconfirmed_user).email, :password => "jonespassword"}, :token => post_redirect.token
# This will trigger a confirmation mail. Get the PostRedirect for later.
diff --git a/spec/integration/search_request_spec.rb b/spec/integration/search_request_spec.rb
index 17a7b4aaa..c564032a6 100644
--- a/spec/integration/search_request_spec.rb
+++ b/spec/integration/search_request_spec.rb
@@ -4,7 +4,7 @@ describe "When searching" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should not strip quotes from quoted query" do
diff --git a/spec/lib/mail_handler/mail_handler_spec.rb b/spec/lib/mail_handler/mail_handler_spec.rb
new file mode 100644
index 000000000..a3fba0698
--- /dev/null
+++ b/spec/lib/mail_handler/mail_handler_spec.rb
@@ -0,0 +1,23 @@
+# coding: utf-8
+require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
+
+describe 'when creating a mail object from raw data' do
+
+ it 'should correctly parse a multipart email with a linebreak in the boundary' do
+ mail = get_fixture_mail('space-boundary.email')
+ mail.parts.size.should == 2
+ mail.multipart?.should == true
+ end
+
+ it 'should parse multiple to addresses with unqoted display names' do
+ mail = get_fixture_mail('multiple-unquoted-display-names.email')
+ mail.to.should == ["request-66666-caa77777@whatdotheyknow.com", "foi@example.com"]
+ end
+
+ it 'should convert an iso8859 email to utf8' do
+ mail = get_fixture_mail('iso8859_2_raw_email.email')
+ mail.subject.should have_text(/gjatë/u)
+ mail.body.is_utf8?.should == true
+ end
+
+end
diff --git a/spec/lib/tmail_extensions_spec.rb b/spec/lib/tmail_extensions_spec.rb
deleted file mode 100644
index bd89e6a84..000000000
--- a/spec/lib/tmail_extensions_spec.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# coding: utf-8
-# This is a test of the set_content_type monkey patch in
-# lib/tmail_extensions.rb
-
-require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-
-describe "when using TMail" do
-
- before(:each) do
- ActionMailer::Base.deliveries.clear
- end
-
- it "should load an email with funny MIME settings" do
- # just send it to the holding pen
- InfoRequest.holding_pen_request.incoming_messages.size.should == 0
- receive_incoming_mail("humberside-police-odd-mime-type.email", 'dummy')
- InfoRequest.holding_pen_request.incoming_messages.size.should == 1
-
- # clear the notification of new message in holding pen
- deliveries = ActionMailer::Base.deliveries
- deliveries.size.should == 1
- deliveries.clear
-
- incoming_message = InfoRequest.holding_pen_request.incoming_messages[0]
-
- # This will raise an error if the bug in TMail hasn't been fixed
- incoming_message.get_body_for_html_display()
- end
-
- it 'should parse multiple to addresses with unqoted display names' do
- mail = TMail::Mail.parse(load_file_fixture('multiple-unquoted-display-names.email'))
- mail.to.should == ["request-66666-caa77777@whatdotheyknow.com", "foi@example.com"]
- end
-
- it 'should convert to utf8' do
- # NB this isn't actually a TMail extension, but is core TMail;
- # this was just a convenient place to assert the UTF8
- # conversion is working
- mail = TMail::Mail.parse(load_file_fixture('iso8859_2_raw_email.email'))
- mail.subject.should have_text(/gjatë/u)
- mail.body.is_utf8?.should == true
- end
-
-end
-
diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb
index b038c43d9..fdbcd1e23 100644
--- a/spec/models/incoming_message_spec.rb
+++ b/spec/models/incoming_message_spec.rb
@@ -85,6 +85,26 @@ describe IncomingMessage, " when dealing with incoming mail" do
end
end
+
+ it "should load an email with funny MIME settings" do
+ ActionMailer::Base.deliveries.clear
+ # just send it to the holding pen
+ InfoRequest.holding_pen_request.incoming_messages.size.should == 0
+ receive_incoming_mail("humberside-police-odd-mime-type.email", 'dummy')
+ InfoRequest.holding_pen_request.incoming_messages.size.should == 1
+
+ # clear the notification of new message in holding pen
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ deliveries.clear
+
+ incoming_message = InfoRequest.holding_pen_request.incoming_messages[0]
+
+ # This will raise an error if the bug in TMail hasn't been fixed
+ incoming_message.get_body_for_html_display()
+ end
+
+
end
describe IncomingMessage, "when parsing HTML mail" do
@@ -399,14 +419,8 @@ end
describe IncomingMessage, " when uudecoding bad messages" do
- before(:each) do
- load_raw_emails_data
- end
-
it "should be able to do it at all" do
- mail_body = load_file_fixture('incoming-request-bad-uuencoding.email')
- mail = TMail::Mail.parse(mail_body)
- mail.base64_decode
+ mail = get_fixture_mail('incoming-request-bad-uuencoding.email')
im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
im.extract_attachments!
@@ -418,9 +432,7 @@ describe IncomingMessage, " when uudecoding bad messages" do
end
it "should apply censor rules" do
- mail_body = load_file_fixture('incoming-request-bad-uuencoding.email')
- mail = TMail::Mail.parse(mail_body)
- mail.base64_decode
+ mail = get_fixture_mail('incoming-request-bad-uuencoding.email')
im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
@@ -443,14 +455,8 @@ end
describe IncomingMessage, "when messages are attached to messages" do
- before(:each) do
- load_raw_emails_data
- end
-
it "should flatten all the attachments out" do
- mail_body = load_file_fixture('incoming-request-attach-attachments.email')
- mail = TMail::Mail.parse(mail_body)
- mail.base64_decode
+ mail = get_fixture_mail('incoming-request-attach-attachments.email')
im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
@@ -468,14 +474,8 @@ end
describe IncomingMessage, "when Outlook messages are attached to messages" do
- before(:each) do
- load_raw_emails_data
- end
-
it "should flatten all the attachments out" do
- mail_body = load_file_fixture('incoming-request-oft-attachments.email')
- mail = TMail::Mail.parse(mail_body)
- mail.base64_decode
+ mail = get_fixture_mail('incoming-request-oft-attachments.email')
im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
@@ -490,14 +490,8 @@ end
describe IncomingMessage, "when TNEF attachments are attached to messages" do
- before(:each) do
- load_raw_emails_data
- end
-
it "should flatten all the attachments out" do
- mail_body = load_file_fixture('incoming-request-tnef-attachments.email')
- mail = TMail::Mail.parse(mail_body)
- mail.base64_decode
+ mail = get_fixture_mail('incoming-request-tnef-attachments.email')
im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
diff --git a/spec/models/info_request_event_spec.rb b/spec/models/info_request_event_spec.rb
index 7352f3be0..796f8b840 100644
--- a/spec/models/info_request_event_spec.rb
+++ b/spec/models/info_request_event_spec.rb
@@ -54,36 +54,71 @@ describe InfoRequestEvent do
end
- describe "doing search/index stuff" do
+ describe "doing search/index stuff" do
before(:each) do
load_raw_emails_data
parse_all_incoming_messages
end
- it 'should get search text for outgoing messages' do
+ it 'should get search text for outgoing messages' do
event = info_request_events(:useless_outgoing_message_event)
message = outgoing_messages(:useless_outgoing_message).body
event.search_text_main.should == message + "\n\n"
end
- it 'should get search text for incoming messages' do
+ it 'should get search text for incoming messages' do
event = info_request_events(:useless_incoming_message_event)
event.search_text_main.strip.should == "No way! I'm not going to tell you that in a month of Thursdays.\n\nThe Geraldine Quango"
end
- it 'should get clipped text for incoming messages, and cache it too' do
+ it 'should get clipped text for incoming messages, and cache it too' do
event = info_request_events(:useless_incoming_message_event)
-
+
event.incoming_message_selective_columns("cached_main_body_text_folded").cached_main_body_text_folded = nil
event.search_text_main(true).strip.should == "No way! I'm not going to tell you that in a month of Thursdays.\n\nThe Geraldine Quango"
event.incoming_message_selective_columns("cached_main_body_text_folded").cached_main_body_text_folded.should_not == nil
end
-
end
+ describe 'when asked if it has the same email as a previous send' do
+
+ before do
+ @info_request_event = InfoRequestEvent.new
+ end
+
+ it 'should return true if the email in its params and the previous email the request was sent to are both nil' do
+ @info_request_event.stub!(:params).and_return({})
+ @info_request_event.stub_chain(:info_request, :get_previous_email_sent_to).and_return(nil)
+ @info_request_event.same_email_as_previous_send?.should be_true
+ end
+
+ it 'should return false if one email address exists and the other does not' do
+ @info_request_event.stub!(:params).and_return(:email => 'test@example.com')
+ @info_request_event.stub_chain(:info_request, :get_previous_email_sent_to).and_return(nil)
+ @info_request_event.same_email_as_previous_send?.should be_false
+ end
+ it 'should return true if the addresses are identical' do
+ @info_request_event.stub!(:params).and_return(:email => 'test@example.com')
+ @info_request_event.stub_chain(:info_request, :get_previous_email_sent_to).and_return('test@example.com')
+ @info_request_event.same_email_as_previous_send?.should be_true
+ end
+
+ it 'should return false if the addresses are different' do
+ @info_request_event.stub!(:params).and_return(:email => 'test@example.com')
+ @info_request_event.stub_chain(:info_request, :get_previous_email_sent_to).and_return('different@example.com')
+ @info_request_event.same_email_as_previous_send?.should be_false
+ end
+
+ it 'should return true if the addresses have different formats' do
+ @info_request_event.stub!(:params).and_return(:email => 'A Test <test@example.com>')
+ @info_request_event.stub_chain(:info_request, :get_previous_email_sent_to).and_return('test@example.com')
+ @info_request_event.same_email_as_previous_send?.should be_true
+ end
+
+ end
end
diff --git a/spec/models/request_mailer_spec.rb b/spec/models/request_mailer_spec.rb
index 906756784..0f09e6926 100644
--- a/spec/models/request_mailer_spec.rb
+++ b/spec/models/request_mailer_spec.rb
@@ -98,7 +98,7 @@ describe RequestMailer, " when receiving incoming mail" do
mail.multipart?.should == true
mail.parts.size.should == 2
message_part = mail.parts[0].to_s
- bounced_mail = TMail::Mail.parse(mail.parts[1].body)
+ bounced_mail = MailHandler.mail_from_raw_email(mail.parts[1].body, decode=false)
bounced_mail.to.should == [ ir.incoming_email ]
bounced_mail.from.should == [ 'geraldinequango@localhost' ]
bounced_mail.body.include?("That's so totally a rubbish question").should be_true
diff --git a/spec/models/track_mailer_spec.rb b/spec/models/track_mailer_spec.rb
index 1bf77dab5..9bf03c3d0 100644
--- a/spec/models/track_mailer_spec.rb
+++ b/spec/models/track_mailer_spec.rb
@@ -169,7 +169,7 @@ describe TrackMailer do
it 'should deliver one email, with right headers' do
@user = mock_model(User,
- :name_and_email => TMail::Address.address_from_name_and_email('Tippy Test', 'tippy@localhost'),
+ :name_and_email => MailHandler.address_from_name_and_email('Tippy Test', 'tippy@localhost'),
:url_name => 'tippy_test'
)
diff --git a/spec/models/xapian_spec.rb b/spec/models/xapian_spec.rb
index 195b39eee..8c99d550f 100644
--- a/spec/models/xapian_spec.rb
+++ b/spec/models/xapian_spec.rb
@@ -4,9 +4,9 @@ describe User, " when indexing users with Xapian" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
-
+
it "should search by name" do
# def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
xapian_object = InfoRequest.full_search([User], "Silly", 'created_at', true, nil, 100, 1)
@@ -21,7 +21,7 @@ describe User, " when indexing users with Xapian" do
xapian_object = InfoRequest.full_search([User], "stuff", 'created_at', true, nil, 100, 1)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == user
-
+
user.about_me = "I am really an aardvark, true story."
user.save!
update_xapian_index
@@ -38,7 +38,7 @@ end
describe PublicBody, " when indexing public bodies with Xapian" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should search index the main name field" do
@@ -71,7 +71,7 @@ describe PublicBody, " when indexing requests by body they are to" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should find requests to the body" do
@@ -126,7 +126,7 @@ end
describe User, " when indexing requests by user they are from" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should find requests from the user" do
@@ -204,7 +204,7 @@ end
describe User, " when indexing comments by user they are by" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should find requests from the user" do
@@ -239,7 +239,7 @@ end
describe InfoRequest, " when indexing requests by their title" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should find events for the request" do
@@ -268,7 +268,7 @@ end
describe InfoRequest, " when indexing requests by tag" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should find request by tag, even when changes" do
@@ -289,7 +289,7 @@ end
describe PublicBody, " when indexing authorities by tag" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should find request by tag, even when changes" do
@@ -313,7 +313,7 @@ end
describe PublicBody, " when only indexing selected things on a rebuild" do
before(:each) do
load_raw_emails_data
- rebuild_xapian_index
+ get_fixtures_xapian_index
end
it "should only index what we ask it to" do
diff --git a/spec/script/handle-mail-replies_spec.rb b/spec/script/handle-mail-replies_spec.rb
index 406af9ee3..90a8de27c 100644
--- a/spec/script/handle-mail-replies_spec.rb
+++ b/spec/script/handle-mail-replies_spec.rb
@@ -5,7 +5,7 @@ def mail_reply_test(email_filename)
Dir.chdir Rails.root do
xc = ExternalCommand.new("script/handle-mail-replies", "--test")
xc.run(load_file_fixture(email_filename))
-
+
xc.err.should == ""
return xc
end
@@ -14,7 +14,7 @@ end
describe "When filtering" do
it "should not fail when not in test mode" do
xc = ExternalCommand.new("script/handle-mail-replies")
- xc.run(load_file_fixture("track-response-exim-bounce.email"))
+ xc.run(load_file_fixture("track-response-exim-bounce.email"))
xc.err.should == ""
end
@@ -23,19 +23,19 @@ describe "When filtering" do
r.status.should == 1
r.out.should == "user@example.com\n"
end
-
+
it "should detect a WebShield delivery error message" do
r = mail_reply_test("track-response-webshield-bounce.email")
r.status.should == 1
r.out.should == "failed.user@example.co.uk\n"
end
-
+
it "should detect a MS Exchange non-permanent delivery error message" do
r = mail_reply_test("track-response-ms-bounce.email")
r.status.should == 1
r.out.should == ""
end
-
+
it "should pass on a non-bounce message" do
r = mail_reply_test("incoming-request-bad-uuencoding.email")
r.status.should == 0
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 248dff70e..d4dad591d 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -23,7 +23,6 @@ FakeWeb.register_uri(:purge, %r|varnish.localdomain|, :body => "OK")
# Use test-specific translations
FastGettext.add_text_domain 'app', :path => File.join(File.dirname(__FILE__), 'fixtures', 'locale'), :type => :po
FastGettext.default_text_domain = 'app'
-
Spec::Runner.configure do |config|
# If you're not using ActiveRecord you should remove these
# lines, delete config/database.yml and disable :active_record
@@ -47,6 +46,7 @@ Spec::Runner.configure do |config|
:holidays,
:track_things_sent_emails
+
# == Fixtures
#
# You can declare fixtures for each example_group like this:
@@ -99,6 +99,19 @@ def load_file_fixture(file_name)
return content
end
+def parse_all_incoming_messages
+ IncomingMessage.find(:all).each{ |x| x.parse_raw_email! }
+end
+
+def load_raw_emails_data
+ raw_emails_yml = File.join(Spec::Runner.configuration.fixture_path, "raw_emails.yml")
+ for raw_email_id in YAML::load_file(raw_emails_yml).map{|k,v| v["id"]} do
+ raw_email = RawEmail.find(raw_email_id)
+ raw_email.data = load_file_fixture("raw_emails/%d.email" % [raw_email_id])
+ end
+end
+
+# Rebuild the current xapian index
def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst = true)
if dropfirst
begin
@@ -110,16 +123,35 @@ def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst =
ActsAsXapian.writable_db.close
end
parse_all_incoming_messages
- verbose = false
# safe_rebuild=true, which involves forking to avoid memory leaks, doesn't work well with rspec.
# unsafe is significantly faster, and we can afford possible memory leaks while testing.
- safe_rebuild = false
- ActsAsXapian.rebuild_index(["PublicBody", "User", "InfoRequestEvent"].map{|m| m.constantize}, verbose, terms, values, texts, safe_rebuild)
+ models = [PublicBody, User, InfoRequestEvent]
+ ActsAsXapian.rebuild_index(models, verbose=false, terms, values, texts, safe_rebuild=false)
+end
+
+# Create a clean xapian index based on the fixture files and the raw_email data.
+def create_fixtures_xapian_index
+ load_raw_emails_data
+ rebuild_xapian_index
end
def update_xapian_index
- verbose = false
- ActsAsXapian.update_index(flush_to_disk=false, verbose)
+ ActsAsXapian.update_index(flush_to_disk=false, verbose=false)
+end
+
+# Copy the xapian index created in create_fixtures_xapian_index to a temporary
+# copy at the same level and point xapian at the copy
+def get_fixtures_xapian_index()
+ # Create a base index for the fixtures if not already created
+ $existing_xapian_db ||= create_fixtures_xapian_index
+ # Store whatever the xapian db path is originally
+ $original_xapian_path ||= ActsAsXapian.db_path
+ path_array = $original_xapian_path.split(File::Separator)
+ path_array.pop
+ temp_path = File.join(path_array, 'test.temp')
+ FileUtils.remove_entry_secure(temp_path, force=true)
+ FileUtils.cp_r($original_xapian_path, temp_path)
+ ActsAsXapian.db_path = temp_path
end
# Validate an entire HTML page
@@ -200,16 +232,8 @@ def safe_mock_model(model, args = {})
mock
end
-def load_raw_emails_data
- raw_emails_yml = File.join(Spec::Runner.configuration.fixture_path, "raw_emails.yml")
- for raw_email_id in YAML::load_file(raw_emails_yml).map{|k,v| v["id"]} do
- raw_email = RawEmail.find(raw_email_id)
- raw_email.data = load_file_fixture("raw_emails/%d.email" % [raw_email_id])
- end
-end
-
-def parse_all_incoming_messages
- IncomingMessage.find(:all).each{|x| x.parse_raw_email!}
+def get_fixture_mail(filename)
+ MailHandler.mail_from_raw_email(load_file_fixture(filename))
end
def load_test_categories
diff --git a/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb b/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
index d5c0e89c6..374fcd65b 100644
--- a/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
+++ b/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
@@ -16,7 +16,7 @@ begin
require 'xapian'
$acts_as_xapian_bindings_available = true
rescue LoadError
- STDERR.puts "acts_as_xapian: No Ruby bindings for Xapian installed"
+ STDERR.puts "acts_as_xapian: No Ruby bindings for Xapian installed"
$acts_as_xapian_bindings_available = false
end
@@ -46,6 +46,9 @@ module ActsAsXapian
def ActsAsXapian.db
@@db
end
+ def ActsAsXapian.db_path=(db_path)
+ @@db_path = db_path
+ end
def ActsAsXapian.db_path
@@db_path
end
@@ -110,14 +113,14 @@ module ActsAsXapian
end
# Opens / reopens the db for reading
- # XXX we perhaps don't need to rebuild database and enquire and queryparser -
+ # XXX we perhaps don't need to rebuild database and enquire and queryparser -
# but db.reopen wasn't enough by itself, so just do everything it's easier.
def ActsAsXapian.readable_init
raise NoXapianRubyBindingsError.new("Xapian Ruby bindings not installed") unless ActsAsXapian.bindings_available
raise "acts_as_xapian hasn't been called in any models" if @@init_values.empty?
-
+
prepare_environment
-
+
# We need to reopen the database each time, so Xapian gets changes to it.
# Calling reopen() does not always pick up changes for reasons that I can
# only speculate about at the moment. (It is easy to reproduce this by
@@ -126,7 +129,7 @@ module ActsAsXapian
if !@@db.nil?
@@db.close
end
-
+
# basic Xapian objects
begin
@@db = Xapian::Database.new(@@db_path)
@@ -188,7 +191,7 @@ module ActsAsXapian
# If making acts_as_xapian generic, would really need to make the :terms have
# another option that lets people choose non-boolean for terms that need it
# (i.e. searching explicitly within a free text field)
- @@query_parser.add_boolean_prefix(term[2], term[1])
+ @@query_parser.add_boolean_prefix(term[2], term[1])
end
end
if options[:values]
@@ -198,9 +201,9 @@ module ActsAsXapian
# date types are special, mark them so the first model they're seen for
if !@@values_by_number.include?(value[1])
- if value[3] == :date
+ if value[3] == :date
value_range = Xapian::DateValueRangeProcessor.new(value[1])
- elsif value[3] == :string
+ elsif value[3] == :string
value_range = Xapian::StringValueRangeProcessor.new(value[1])
elsif value[3] == :number
value_range = Xapian::NumberValueRangeProcessor.new(value[1])
@@ -212,7 +215,7 @@ module ActsAsXapian
# stop it being garbage collected, as
# add_valuerangeprocessor ref is outside Ruby's GC
- @@value_ranges_store.push(value_range)
+ @@value_ranges_store.push(value_range)
end
@@values_by_number[value[1]] = value[2]
@@ -230,7 +233,7 @@ module ActsAsXapian
# again XXX reopen it each time, xapian_spec.rb needs this so database
# gets written twice correctly.
# return unless @@writable_db.nil?
-
+
prepare_environment
full_path = @@db_path + suffix
@@ -246,7 +249,7 @@ module ActsAsXapian
######################################################################
# Search with a query or for similar models
-
+
# Base class for Search and Similar below
class QueryBase
attr_accessor :offset
@@ -271,12 +274,12 @@ module ActsAsXapian
# Set self.query before calling this
def initialize_query(options)
#raise options.to_yaml
-
+
self.runtime += Benchmark::realtime {
offset = options[:offset] || 0; offset = offset.to_i
limit = options[:limit]
raise "please specifiy maximum number of results to return with parameter :limit" if not limit
- limit = limit.to_i
+ limit = limit.to_i
sort_by_prefix = options[:sort_by_prefix] || nil
sort_by_ascending = options[:sort_by_ascending].nil? ? true : options[:sort_by_ascending]
collapse_by_prefix = options[:collapse_by_prefix] || nil
@@ -313,7 +316,7 @@ module ActsAsXapian
tries += 1
delay *= 2
delay = MSET_MAX_DELAY if delay > MSET_MAX_DELAY
-
+
ActsAsXapian.db.reopen()
retry
else
@@ -336,7 +339,7 @@ module ActsAsXapian
for t in self.query.terms
term = t.term
#x = x + term.to_yaml + term.size.to_s + term[0..0] + "*"
- if term.size >= 2 && term[0..0] == 'Z'
+ if term.size >= 2 && term[0..0] == 'Z'
# normal terms begin Z (for stemmed), then have no capital letter prefix
if term[1..1] == term[1..1].downcase
ret = true
@@ -372,8 +375,8 @@ module ActsAsXapian
# Pull out all the results
iter = self.matches._begin
while not iter.equals(self.matches._end)
- docs.push({:data => iter.document.data,
- :percent => iter.percent,
+ docs.push({:data => iter.document.data,
+ :percent => iter.percent,
:weight => iter.weight,
:collapse_count => iter.collapse_count})
iter.next
@@ -403,14 +406,14 @@ module ActsAsXapian
end
# now get them in right order again
results = []
- docs.each do |doc|
+ docs.each do |doc|
k = doc[:data].split('-')
model_instance = chash[[k[0], k[1].to_i]]
if model_instance
results << { :model => model_instance,
- :percent => doc[:percent],
- :weight => doc[:weight],
- :collapse_count => doc[:collapse_count] }
+ :percent => doc[:percent],
+ :weight => doc[:weight],
+ :collapse_count => doc[:collapse_count] }
end
end
self.cached_results = results
@@ -428,7 +431,7 @@ module ActsAsXapian
# essential to make sure the classes have been loaded, and thus
# acts_as_xapian called on them, so we know the fields for the query
# parser.
-
+
# model_classes - model classes to search within, e.g. [PublicBody,
# User]. Can take a single model class, or you can express the model
# class names in strings if you like.
@@ -443,7 +446,7 @@ module ActsAsXapian
new_model_classes.push(model_class)
end
model_classes = new_model_classes
-
+
# Set things up
self.initialize_db
@@ -518,7 +521,7 @@ module ActsAsXapian
# object. This explains what exactly it does, which is to exclude
# terms in the existing query.
# http://thread.gmane.org/gmane.comp.search.xapian.general/3673/focus=3681
- eset = ActsAsXapian.enquire.eset(40, selection)
+ eset = ActsAsXapian.enquire.eset(40, selection)
# Do main search for them
self.important_terms = []
@@ -548,8 +551,8 @@ module ActsAsXapian
######################################################################
# Index
-
- # Offline indexing job queue model, create with migration made
+
+ # Offline indexing job queue model, create with migration made
# using "script/generate acts_as_xapian" as described in ../README.txt
class ActsAsXapianJob < ActiveRecord::Base
end
@@ -561,7 +564,7 @@ module ActsAsXapian
# logging in the database that it has been.
def ActsAsXapian.update_index(flush = false, verbose = false)
# STDOUT.puts("start of ActsAsXapian.update_index") if verbose
-
+
# Before calling writable_init we have to make sure every model class has been initialized.
# i.e. has had its class code loaded, so acts_as_xapian has been called inside it, and
# we have the info from acts_as_xapian.
@@ -621,17 +624,17 @@ module ActsAsXapian
STDERR.puts(detail.backtrace.join("\n") + "\nFAILED ActsAsXapian.update_index job #{id} #{$!} " + (job.nil? ? "" : "model " + job.model + " id " + job.model_id.to_s))
end
end
- # We close the database when we're finished to remove the lock file. Since writable_init
- # reopens it and recreates the environment every time we don't need to do further cleanup
+ # We close the database when we're finished to remove the lock file. Since writable_init
+ # reopens it and recreates the environment every time we don't need to do further cleanup
ActsAsXapian.writable_db.flush
ActsAsXapian.writable_db.close
end
-
+
def ActsAsXapian._is_xapian_db(path)
is_db = File.exist?(File.join(path, "iamflint")) || File.exist?(File.join(path, "iamchert"))
return is_db
end
-
+
# You must specify *all* the models here, this totally rebuilds the Xapian
# database. You'll want any readers to reopen the database after this.
#
@@ -672,7 +675,7 @@ module ActsAsXapian
end
ActsAsXapian.writable_db.flush
ActsAsXapian.writable_db.close
- end
+ end
# Rename into place
temp_path = old_path + ".tmp"
@@ -728,7 +731,7 @@ module ActsAsXapian
model.xapian_index(terms, values, texts)
end
ActsAsXapian.writable_db.flush
- ActsAsXapian.writable_db.close
+ ActsAsXapian.writable_db.close
# database connection won't survive a fork, so shut it down
ActiveRecord::Base.connection.disconnect!
# brutal exit, so other shutdown code not run (for speed and safety)
@@ -741,7 +744,7 @@ module ActsAsXapian
######################################################################
# Instance methods that get injected into your model.
-
+
module InstanceMethods
# Used internally
def xapian_document_term
@@ -755,7 +758,7 @@ module ActsAsXapian
else
values = []
for locale in self.translations.map{|x| x.locale}
- self.class.with_locale(locale) do
+ self.class.with_locale(locale) do
values << single_xapian_value(field, type=type)
end
end
@@ -866,7 +869,7 @@ module ActsAsXapian
end
end
end
-
+
for term in terms_to_index
value = xapian_value(term[0])
if value.kind_of?(Array)
@@ -877,11 +880,11 @@ module ActsAsXapian
doc.add_term(term[1] + value)
end
end
-
+
if values
- doc.clear_values
+ doc.clear_values
for value in values_to_index
- doc.add_value(value[1], xapian_value(value[0], value[3]))
+ doc.add_value(value[1], xapian_value(value[0], value[3]))
end
end
if texts
@@ -889,7 +892,7 @@ module ActsAsXapian
for text in texts_to_index
ActsAsXapian.term_generator.increase_termpos # stop phrases spanning different text fields
# XXX the "1" here is a weight that could be varied for a boost function
- ActsAsXapian.term_generator.index_text(xapian_value(text, nil, true), 1)
+ ActsAsXapian.term_generator.index_text(xapian_value(text, nil, true), 1)
end
end
@@ -914,13 +917,13 @@ module ActsAsXapian
job.save!
end
end
-
+
# Allow reindexing to be skipped if a flag is set
def xapian_mark_needs_index_if_reindex
return true if (self.respond_to?(:no_xapian_reindex) && self.no_xapian_reindex == true)
xapian_mark_needs_index
end
-
+
def xapian_mark_needs_destroy
model = self.class.base_class.to_s
model_id = self.id
@@ -937,7 +940,7 @@ module ActsAsXapian
######################################################################
# Main entry point, add acts_as_xapian to your model.
-
+
module ActsMethods
# See top of this file for docs
def acts_as_xapian(options)
@@ -957,7 +960,7 @@ module ActsAsXapian
after_destroy :xapian_mark_needs_destroy
end
end
-
+
end
# Reopen ActiveRecord and include the acts_as_xapian method