diff options
author | David Cabo <david@calibea.com> | 2012-01-06 18:54:37 +0100 |
---|---|---|
committer | David Cabo <david@calibea.com> | 2012-01-06 18:54:37 +0100 |
commit | d4f3ca2a2f7cc68663a9ca005cf379533032d232 (patch) | |
tree | f6d2a07fd1e4f8e1091d5df765b68140db5e47ca | |
parent | 7ed887f0989425d9e412890800df05637b08c025 (diff) | |
parent | 766d696d5b914520b0b9367e9b9a9decab87ea5f (diff) |
Merge branch 'release/0.5' of github.com:sebbacon/alaveteli into release/0.5
29 files changed, 1418 insertions, 558 deletions
diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb index 62229a441..c31134641 100644 --- a/app/controllers/public_body_controller.rb +++ b/app/controllers/public_body_controller.rb @@ -129,7 +129,7 @@ class PublicBodyController < ApplicationController end PublicBody.with_locale(@locale) do @public_bodies = PublicBody.paginate( - :order => "public_body_translations.name", :page => params[:page], :per_page => 1000, # fit all councils on one page + :order => "public_body_translations.name", :page => params[:page], :per_page => 100, :conditions => conditions, :joins => :translations ) diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index dad5e81cd..f3bbd6708 100644 --- a/app/controllers/request_controller.rb +++ b/app/controllers/request_controller.rb @@ -35,9 +35,8 @@ class RequestController < ApplicationController # do nothing - as "authenticated?" has done the redirect to signin page for us return end - if !params[:query].nil? - query = params[:query] + '*' + query = params[:query] query = query.split(' ').join(' OR ') # XXX: HACK for OR instead of default AND! @xapian_requests = perform_search([PublicBody], query, 'relevant', nil, 5) end @@ -815,7 +814,8 @@ class RequestController < ApplicationController for message in info_request.incoming_messages attachments = message.get_attachments_for_display for attachment in attachments - zipfile.get_output_stream(attachment.display_filename) { |f| + filename = "#{attachment.url_part_number}_#{attachment.display_filename}" + zipfile.get_output_stream(filename) { |f| f.puts(attachment.body) } end diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index 96dbfba74..fc29a847c 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -23,7 +23,17 @@ class UserController < ApplicationController redirect_to :url_name => MySociety::Format.simplify_url_part(params[:url_name], 'user', 32), :status => :moved_permanently return end - + if params[:view].nil? + @show_requests = true + @show_profile = true + elsif params[:view] == 'profile' + @show_profile = true + @show_requests = false + elsif params[:view] == 'requests' + @show_profile = false + @show_requests = true + end + @display_user = User.find(:first, :conditions => [ "url_name = ? and email_confirmed = ?", params[:url_name], true ]) if not @display_user raise ActiveRecord::RecordNotFound.new("user not found, url_name=" + params[:url_name]) @@ -34,31 +44,33 @@ class UserController < ApplicationController # Use search query for this so can collapse and paginate easily # XXX really should just use SQL query here rather than Xapian. - begin - requests_query = 'requested_by:' + @display_user.url_name - comments_query = 'commented_by:' + @display_user.url_name - if !params[:user_query].nil? - requests_query += " " + params[:user_query] - comments_query += " " + params[:user_query] - @match_phrase = _("{{search_results}} matching '{{query}}'", :search_results => "", :query => params[:user_query]) - end - @xapian_requests = perform_search([InfoRequestEvent], requests_query, 'newest', 'request_collapse') - @xapian_comments = perform_search([InfoRequestEvent], comments_query, 'newest', nil) - - if (@page > 1) - @page_desc = " (page " + @page.to_s + ")" - else - @page_desc = "" + if @show_requests + begin + requests_query = 'requested_by:' + @display_user.url_name + comments_query = 'commented_by:' + @display_user.url_name + if !params[:user_query].nil? + requests_query += " " + params[:user_query] + comments_query += " " + params[:user_query] + @match_phrase = _("{{search_results}} matching '{{query}}'", :search_results => "", :query => params[:user_query]) + end + @xapian_requests = perform_search([InfoRequestEvent], requests_query, 'newest', 'request_collapse') + @xapian_comments = perform_search([InfoRequestEvent], comments_query, 'newest', nil) + + if (@page > 1) + @page_desc = " (page " + @page.to_s + ")" + else + @page_desc = "" + end + rescue + @xapian_requests = nil + @xapian_comments = nil end - rescue - @xapian_requests = nil - @xapian_comments = nil - end - # Track corresponding to this page - @track_thing = TrackThing.create_track_for_user(@display_user) - @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => @track_thing.params[:title_in_rss], :has_json => true } ] + # Track corresponding to this page + @track_thing = TrackThing.create_track_for_user(@display_user) + @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => @track_thing.params[:title_in_rss], :has_json => true } ] + end # All tracks for the user if @is_you @track_things = TrackThing.find(:all, :conditions => ["tracking_user_id = ? and track_medium = ?", @display_user.id, 'email_daily'], :order => 'created_at desc') diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb index a4519a17d..20989d641 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -44,275 +44,6 @@ module TMail end end -# This is the type which is used to send data about attachments to the view -class FOIAttachment - attr_accessor :body - attr_accessor :content_type - attr_accessor :filename - attr_accessor :url_part_number - attr_accessor :within_rfc822_subject # we use the subject as the filename for email attachments - - # List of DSN codes taken from RFC 3463 - # http://tools.ietf.org/html/rfc3463 - DsnToMessage = { - 'X.1.0' => 'Other address status', - 'X.1.1' => 'Bad destination mailbox address', - 'X.1.2' => 'Bad destination system address', - 'X.1.3' => 'Bad destination mailbox address syntax', - 'X.1.4' => 'Destination mailbox address ambiguous', - 'X.1.5' => 'Destination mailbox address valid', - 'X.1.6' => 'Mailbox has moved', - 'X.1.7' => 'Bad sender\'s mailbox address syntax', - 'X.1.8' => 'Bad sender\'s system address', - 'X.2.0' => 'Other or undefined mailbox status', - 'X.2.1' => 'Mailbox disabled, not accepting messages', - 'X.2.2' => 'Mailbox full', - 'X.2.3' => 'Message length exceeds administrative limit.', - 'X.2.4' => 'Mailing list expansion problem', - 'X.3.0' => 'Other or undefined mail system status', - 'X.3.1' => 'Mail system full', - 'X.3.2' => 'System not accepting network messages', - 'X.3.3' => 'System not capable of selected features', - 'X.3.4' => 'Message too big for system', - 'X.4.0' => 'Other or undefined network or routing status', - 'X.4.1' => 'No answer from host', - 'X.4.2' => 'Bad connection', - 'X.4.3' => 'Routing server failure', - 'X.4.4' => 'Unable to route', - 'X.4.5' => 'Network congestion', - 'X.4.6' => 'Routing loop detected', - 'X.4.7' => 'Delivery time expired', - 'X.5.0' => 'Other or undefined protocol status', - 'X.5.1' => 'Invalid command', - 'X.5.2' => 'Syntax error', - 'X.5.3' => 'Too many recipients', - 'X.5.4' => 'Invalid command arguments', - 'X.5.5' => 'Wrong protocol version', - 'X.6.0' => 'Other or undefined media error', - 'X.6.1' => 'Media not supported', - 'X.6.2' => 'Conversion required and prohibited', - 'X.6.3' => 'Conversion required but not supported', - 'X.6.4' => 'Conversion with loss performed', - 'X.6.5' => 'Conversion failed', - 'X.7.0' => 'Other or undefined security status', - 'X.7.1' => 'Delivery not authorized, message refused', - 'X.7.2' => 'Mailing list expansion prohibited', - 'X.7.3' => 'Security conversion required but not possible', - 'X.7.4' => 'Security features not supported', - 'X.7.5' => 'Cryptographic failure', - 'X.7.6' => 'Cryptographic algorithm not supported', - 'X.7.7' => 'Message integrity failure' - } - - # Returns HTML, of extra comment to put by attachment - def extra_note - # For delivery status notification attachments, extract the status and - # look up what it means in the DSN table. - if @content_type == 'message/delivery-status' - if !@body.match(/Status:\s+([0-9]+\.([0-9]+\.[0-9]+))\s+/) - return "" - end - dsn = $1 - dsn_part = 'X.' + $2 - - dsn_message = "" - if DsnToMessage.include?(dsn_part) - dsn_message = " (" + DsnToMessage[dsn_part] + ")" - end - - return "<br><em>DSN: " + dsn + dsn_message + "</em>" - end - return "" - end - - # Called by controller so old filenames still work - def old_display_filename - filename = self._internal_display_filename - - # Convert weird spaces (e.g. \n) to normal ones - filename = filename.gsub(/\s/, " ") - # Remove slashes, they mess with URLs - filename = filename.gsub(/\//, "-") - - return filename - end - - # XXX changing this will break existing URLs, so have a care - maybe - # make another old_display_filename see above - def display_filename - filename = self._internal_display_filename - - # Sometimes filenames have e.g. %20 in - no point butchering that - # (without unescaping it, this would remove the % and leave 20s in there) - filename = CGI.unescape(filename) - - # Remove weird spaces - filename = filename.gsub(/\s+/, " ") - # Remove non-alphabetic characters - filename = filename.gsub(/[^A-Za-z0-9.]/, " ") - # Remove spaces near dots - filename = filename.gsub(/\s*\.\s*/, ".") - # Compress adjacent spaces down to a single one - filename = filename.gsub(/\s+/, " ") - filename = filename.strip - - return filename - end - - def _internal_display_filename - calc_ext = AlaveteliFileTypes.mimetype_to_extension(@content_type) - - if @filename - # Put right extension on if missing - if !filename.match(/\.#{calc_ext}$/) && calc_ext - filename + "." + calc_ext - else - filename - end - else - if !calc_ext - calc_ext = "bin" - end - if @within_rfc822_subject - @within_rfc822_subject + "." + calc_ext - else - "attachment." + calc_ext - end - end - end - - # Size to show next to the download link for the attachment - def display_size - s = self.body.size - - if s > 1024 * 1024 - return sprintf("%.1f", s.to_f / 1024 / 1024) + 'M' - else - return (s / 1024).to_s + 'K' - end - end - - # Whether this type can be shown in the Google Docs Viewer. - # The full list of supported types can be found at - # https://docs.google.com/support/bin/answer.py?hl=en&answer=1189935 - def has_google_docs_viewer? - return !! { - "application/pdf" => true, # .pdf - "image/tiff" => true, # .tiff - - "application/vnd.ms-word" => true, # .doc - "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => true, # .docx - - "application/vnd.ms-powerpoint" => true, # .ppt - "application/vnd.openxmlformats-officedocument.presentationml.presentation" => true, # .pptx - - "application/vnd.ms-excel" => true, # .xls - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => true, # .xlsx - - } [self.content_type] - end - - # Whether this type has a "View as HTML" - def has_body_as_html? - return ( - !!{ - "text/plain" => true, - "application/rtf" => true, - }[self.content_type] or - self.has_google_docs_viewer? - ) - end - - # Name of type of attachment type - only valid for things that has_body_as_html? - def name_of_content_type - return { - "text/plain" => "Text file", - 'application/rtf' => "RTF file", - - 'application/pdf' => "PDF file", - 'image/tiff' => "TIFF image", - - 'application/vnd.ms-word' => "Word document", - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => "Word document", - - 'application/vnd.ms-powerpoint' => "PowerPoint presentation", - 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => "PowerPoint presentation", - - 'application/vnd.ms-excel' => "Excel spreadsheet", - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => "Excel spreadsheet", - }[self.content_type] - end - - # For "View as HTML" of attachment - def body_as_html(dir) - html = nil - wrapper_id = "wrapper" - - # simple cases, can never fail - if self.content_type == 'text/plain' - text = self.body.strip - text = CGI.escapeHTML(text) - text = MySociety::Format.make_clickable(text) - html = text.gsub(/\n/, '<br>') - return '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"><html><head><title></title></head><body>' + html + "</body></html>", wrapper_id - end - - # the extractions will also produce image files, which go in the - # current directory, so change to the directory the function caller - # wants everything in - Dir.chdir(dir) do - tempfile = Tempfile.new('foiextract', '.') - tempfile.print self.body - tempfile.flush - - if self.content_type == 'application/pdf' - IO.popen("#{`which pdftohtml`.chomp} -nodrm -zoom 1.0 -stdout -enc UTF-8 -noframes " + tempfile.path + "", "r") do |child| - html = child.read() - end - elsif self.content_type == 'application/rtf' - IO.popen("/usr/bin/unrtf --html " + tempfile.path + "", "r") do |child| - html = child.read() - end - elsif self.has_google_docs_viewer? - html = '' # force error and using Google docs viewer - else - raise "No HTML conversion available for type " + self.content_type - end - - tempfile.close - tempfile.delete - end - - # We need to look at: - # a) Any error code - # b) The output size, as pdftohtml does not return an error code upon error. - # c) For cases when there is no text in the body of the HTML, or - # images, so nothing will be rendered. This is to detect some bug in - # pdftohtml, which sometimes makes it return just <hr>s and no other - # content. - html.match(/(\<body[^>]*\>.*)/mi) - body = $1.to_s - body_without_tags = body.gsub(/\s+/,"").gsub(/\<[^\>]*\>/, "") - contains_images = html.match(/<img/mi) ? true : false - if !$?.success? || html.size == 0 || (body_without_tags.size == 0 && !contains_images) - ret = "<html><head></head><body>"; - if self.has_google_docs_viewer? - wrapper_id = "wrapper_google_embed" - ret = ret + "<iframe src='http://docs.google.com/viewer?url=<attachment-url-here>&embedded=true' width='100%' height='100%' style='border: none;'></iframe>"; - else - ret = ret + "<p>Sorry, we were unable to convert this file to HTML. Please use the download link at the top right.</p>" - end - ret = ret + "</body></html>" - return ret, wrapper_id - end - - return html, wrapper_id - end - -end - - class IncomingMessage < ActiveRecord::Base belongs_to :info_request validates_presence_of :info_request @@ -380,7 +111,7 @@ class IncomingMessage < ActiveRecord::Base if !self.mail['return-path'].nil? && self.mail['return-path'].addr == "<>" return false end - if !self.mail['auto-submitted'].nil? && !self.mail['auto-submitted'].keys.empty? + if !self.mail['auto-submitted'].nil? return false end return true @@ -792,7 +523,7 @@ class IncomingMessage < ActiveRecord::Base # it into conflict with ensure_parts_counted which it has to be # called both before and after. It will fail with cases of # attachments of attachments etc. - + charset = curr_mail.charset # save this, because overwriting content_type also resets charset # Don't allow nil content_types if curr_mail.content_type.nil? curr_mail.content_type = 'application/octet-stream' @@ -822,7 +553,6 @@ class IncomingMessage < ActiveRecord::Base curr_mail.content_type = 'application/octet-stream' end end - # If the part is an attachment of email if curr_mail.content_type == 'message/rfc822' || curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef' ensure_parts_counted # fills in rfc822_attachment variable @@ -832,6 +562,8 @@ class IncomingMessage < ActiveRecord::Base curr_mail.within_rfc822_attachment = within_rfc822_attachment leaves_found += [curr_mail] end + # restore original charset + curr_mail.charset = charset end return leaves_found end @@ -887,64 +619,58 @@ class IncomingMessage < ActiveRecord::Base end # Returns body text from main text part of email, converted to UTF-8 def get_main_body_text_internal + parse_raw_email! main_part = get_main_body_text_part return _convert_part_body_to_text(main_part) end + # Given a main text part, converts it to text def _convert_part_body_to_text(part) if part.nil? text = "[ Email has no body, please see attachments ]" - text_charset = "utf-8" + source_charset = "utf-8" else - text = part.body - text_charset = part.charset + text = part.body # by default, TMail converts to UT8 in this call + source_charset = part.charset if part.content_type == 'text/html' # e.g. http://www.whatdotheyknow.com/request/35/response/177 - # XXX This is a bit of a hack as it is calling a convert to text routine. - # Could instead call a sanitize HTML one. - text = self.class._get_attachment_text_internal_one_file(part.content_type, text) - end - end - - # Charset conversion, turn everything into UTF-8 - if not text_charset.nil? - begin - # XXX specially convert unicode pound signs, was needed here - # http://www.whatdotheyknow.com/request/88/response/352 - text = text.gsub("£", Iconv.conv(text_charset, 'utf-8', '£')) - # Try proper conversion - text = Iconv.conv('utf-8', text_charset, text) - rescue Iconv::IllegalSequence, Iconv::InvalidEncoding - # Clearly specified charset was nonsense - text_charset = nil + # XXX This is a bit of a hack as it is calling a + # convert to text routine. Could instead call a + # sanitize HTML one. + + # If the text isn't UTF8, it means TMail had a problem + # converting it (invalid characters, etc), and we + # should instead tell elinks to respect the source + # charset + use_charset = "utf-8" + begin + text = Iconv.conv('utf-8', 'utf-8', text) + rescue Iconv::IllegalSequence + use_charset = source_charset + end + text = self.class._get_attachment_text_internal_one_file(part.content_type, text, use_charset) end end - if text_charset.nil? - # No specified charset, so guess - - # Could use rchardet here, but it had trouble with - # http://www.whatdotheyknow.com/request/107/response/144 - # So I gave up - most likely in UK we'll only get windows-1252 anyway. + # If TMail can't convert text, it just returns it, so we sanitise it. + begin + # Test if it's good UTF-8 + text = Iconv.conv('utf-8', 'utf-8', text) + rescue Iconv::IllegalSequence + # Text looks like unlabelled nonsense, + # strip out anything that isn't UTF-8 begin - # See if it is good UTF-8 anyway - text = Iconv.conv('utf-8', 'utf-8', text) - rescue Iconv::IllegalSequence - begin - # Or is it good windows-1252, most likely - text = Iconv.conv('utf-8', 'windows-1252', text) - rescue Iconv::IllegalSequence - # Text looks like unlabelled nonsense, strip out anything that isn't UTF-8 - text = Iconv.conv('utf-8//IGNORE', 'utf-8', text) + - _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]", - :site_name => MySociety::Config.get('SITE_NAME', 'Alaveteli')) + text = Iconv.conv('utf-8//IGNORE', source_charset, text) + + _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]", + :site_name => MySociety::Config.get('SITE_NAME', 'Alaveteli')) + rescue Iconv::InvalidEncoding, Iconv::IllegalSequence + if source_charset != "utf-8" + source_charset = "utf-8" + retry end end end - # An assertion that we have ended up with UTF-8 XXX can remove as this should - # always be fine if code above is - Iconv.conv('utf-8', 'utf-8', text) # Fix DOS style linefeeds to Unix style ones (or other later regexps won't work) # Needed for e.g. http://www.whatdotheyknow.com/request/60/response/98 @@ -1192,7 +918,9 @@ class IncomingMessage < ActiveRecord::Base return self.cached_attachment_text_clipped end - def IncomingMessage._get_attachment_text_internal_one_file(content_type, body) + def IncomingMessage._get_attachment_text_internal_one_file(content_type, body, charset = 'utf-8') + # note re. charset: TMail always tries to convert email bodies + # to UTF8 by default, so normally it should already be that. text = '' # XXX - tell all these command line tools to return utf-8 if content_type == 'text/plain' @@ -1214,9 +942,10 @@ class IncomingMessage < ActiveRecord::Base # catdoc on RTF prodcues less comments and extra bumf than --text option to unrtf AlaveteliExternalCommand.run(`which catdoc`.chomp, tempfile.path, :append_to => text) elsif content_type == 'text/html' - # lynx wordwraps links in its output, which then don't get formatted properly - # by Alaveteli. We use elinks instead, which doesn't do that. - AlaveteliExternalCommand.run(`which elinks`.chomp, "-eval", "'set document.codepage.assume = \"utf-8\"'", "-dump-charset", "utf-8", "-force-html", "-dump", + # lynx wordwraps links in its output, which then don't + # get formatted properly by Alaveteli. We use elinks + # instead, which doesn't do that. + AlaveteliExternalCommand.run(`which elinks`.chomp, "-eval", "'set document.codepage.assume = \"#{charset}\"'", "-eval", "'set document.codepage.force_assumed = 1'", "-dump-charset", "utf-8", "-force-html", "-dump", tempfile.path, :append_to => text) elsif content_type == 'application/vnd.ms-excel' # Bit crazy using /usr/bin/strings - but xls2csv, xlhtml and @@ -1283,7 +1012,7 @@ class IncomingMessage < ActiveRecord::Base text = '' attachments = self.get_attachments_for_display for attachment in attachments - text += IncomingMessage._get_attachment_text_internal_one_file(attachment.content_type, attachment.body) + text += IncomingMessage._get_attachment_text_internal_one_file(attachment.content_type, attachment.body, attachment.charset) end # Remove any bad characters text = Iconv.conv('utf-8//IGNORE', 'utf-8', text) @@ -1376,7 +1105,7 @@ class IncomingMessage < ActiveRecord::Base if !self.mail['return-path'].nil? && self.mail['return-path'].addr == "<>" return false end - if !self.mail['auto-submitted'].nil? && !self.mail['auto-submitted'].keys.empty? + if !self.mail['auto-submitted'].nil? return false end return true diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb index 4ea89bf81..3514702da 100644 --- a/app/models/info_request_event.rb +++ b/app/models/info_request_event.rb @@ -147,6 +147,7 @@ class InfoRequestEvent < ActiveRecord::Base return event.calculated_state end end + return end def waiting_classification diff --git a/app/views/layouts/default.rhtml b/app/views/layouts/default.rhtml index 2f8e0bf36..ad0560baa 100644 --- a/app/views/layouts/default.rhtml +++ b/app/views/layouts/default.rhtml @@ -102,7 +102,8 @@ <%= _('Hello, {{username}}!', :username => h(@user.name))%> <% if @user %> - <%=link_to _("My profile"), user_url(@user) %> + <%=link_to _("My requests"), show_user_requests_path(:url_name => @user.url_name) %> + <%=link_to _("My profile"), show_user_profile_path(:url_name => @user.url_name) %> <% end %> diff --git a/app/views/public_body/list.rhtml b/app/views/public_body/list.rhtml index af91d8ed2..8cb207bd4 100644 --- a/app/views/public_body/list.rhtml +++ b/app/views/public_body/list.rhtml @@ -44,7 +44,7 @@ </div> <% end %> -<h2 class="publicbody_results"><%= _('Found {{count}} public bodies {{description}}', :count=>@public_bodies.size, :description=>@description) %></h2> +<h2 class="publicbody_results"><%= _('Found {{count}} public bodies {{description}}', :count=>@public_bodies.total_entries, :description=>@description) %></h2> <%= render :partial => 'body_listing', :locals => { :public_bodies => @public_bodies } %> <%= will_paginate(@public_bodies) %><br/> diff --git a/app/views/request/list.rhtml b/app/views/request/list.rhtml index 3890fa28b..63faf3643 100644 --- a/app/views/request/list.rhtml +++ b/app/views/request/list.rhtml @@ -18,7 +18,7 @@ <% if @list_results.empty? %> <p> <%= _('No requests of this sort yet.')%></p> <% else %> - <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @list_results.size) %></h2> + <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @matches_estimated) %></h2> <div class="results_block"> <% for result in @list_results%> <% if result.class.to_s == 'InfoRequestEvent' %> diff --git a/app/views/user/show.rhtml b/app/views/user/show.rhtml index baf6621df..9ac203541 100644 --- a/app/views/user/show.rhtml +++ b/app/views/user/show.rhtml @@ -1,4 +1,8 @@ -<% @title = h(@display_user.name) + " - Freedom of Information requests" %> +<% if @show_requests %> + <% @title = h(@display_user.name) + " - Freedom of Information requests" %> +<% else %> + <% @title = h(@display_user.name) + " - user profile" %> +<% end %> <% if (@same_name_users.size >= 1) %> <p><%= _('There is <strong>more than one person</strong> who uses this site and has this name. @@ -7,7 +11,7 @@ <% end %> <% end%> -<% if @is_you && @undescribed_requests.size > 0 %> +<% if @show_profile && @is_you && @undescribed_requests.size > 0 %> <div class="undescribed_requests"> <p><%= _('Please <strong>go to the following requests</strong>, and let us know if there was information in the recent responses to them.')%></p> @@ -24,17 +28,18 @@ </div> <% end %> +<% if @show_profile %> <div id="user_profile_header"> <div id="header_right"> - <h2><%= _('Track this person')%></h2> - <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'sidebar' } %> - - <h2><%= _('On this page')%></h2> - <a href="#foi_requests"><%= _('FOI requests')%></a> - <br><a href="#annotations"><%= _('Annotations')%></a> - <% if @is_you %> - <br><a href="#email_subscriptions"><%= _('Email subscriptions')%></a> - <% end %> + <% if !@track_thing.nil? %> + <h2><%= _('Track this person')%></h2> + <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'sidebar' } %> + <% end %> + <% if !@xapian_requests.nil? %> + <h2><%= _('On this page')%></h2> + <a href="#foi_requests"><%= _('FOI requests')%></a> + <br><a href="#annotations"><%= _('Annotations')%></a> + <% end %> </div> <div class="header_left"> @@ -116,7 +121,9 @@ </div> </div> <div style="clear:both"></div> +<% end %> +<% if @show_requests %> <div id="user_profile_search"> <% form_tag(show_user_url, :method => "get", :id=>"search_form") do %> <div> @@ -154,8 +161,10 @@ <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @display_user.info_requests.size) %> <% end %> <% else %> + <% if @show_requests %> <h2 class="foi_results" id="foi_requests"><%= @is_you ? _('Freedom of Information requests made by you') : _('Freedom of Information requests made by this person') %> </h2> <p><%= _('The search index is currently offline, so we can\'t show the Freedom of Information requests this person has made.')%></p> + <% end %> <% end %> <% if !@xapian_comments.nil? %> @@ -181,7 +190,7 @@ <% end %> <% end %> - <% if @is_you %> + <% if @show_profile && @is_you %> <% if @track_things.empty? %> <h2 id="email_subscriptions"> <%= _('Your email subscriptions')%></h2> <p><%= _('None made.')%></p> @@ -232,4 +241,5 @@ <% end %> <% end %> <% end %> -</div>
\ No newline at end of file +</div> +<% end %> diff --git a/config/crontab.ugly b/config/crontab.ugly index 5f2fbdb3b..43b191fd4 100644 --- a/config/crontab.ugly +++ b/config/crontab.ugly @@ -10,7 +10,7 @@ PATH=/usr/local/bin:/usr/bin:/bin MAILTO=cron-!!(*= $site *)!!@mysociety.org # Every 5 minutes -*/5 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/update-xapian-index.lock /data/vhost/!!(*= $vhost *)!!/!!(*= $vcspath *)!!/script/update-xapian-index || echo "stalled?" +*/5 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/update-xapian-index.lock "/data/vhost/!!(*= $vhost *)!!/!!(*= $vcspath *)!!/script/update-xapian-index verbose=true" >> /data/vhost/!!(*= $vhost *)!!/logs/update-xapian-index.log || echo "stalled?" # Every 10 minutes 5,15,25,35,45,55 * * * * !!(*= $user *)!! /etc/init.d/foi-alert-tracks check diff --git a/config/general.yml b/config/general.yml deleted file mode 100644 index 7c70f8e71..000000000 --- a/config/general.yml +++ /dev/null @@ -1,118 +0,0 @@ -# general.yml-example: -# Example values for the "general" config file. -# -# Configuration parameters, in YAML syntax. -# -# Copy this file to one called "general.yml" in the same directory. Or -# have multiple config files and use a symlink to change between them. - -# Site name appears in various places throughout the site -SITE_NAME: 'Alaveteli' - -# Domain used in URLs generated by scripts (e.g. for going in some emails) -DOMAIN: 'localhost:3000' - -# ISO country code of country currrently deployed in -# (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) -ISO_COUNTRY_CODE: DE - -# These feeds are displayed accordingly on the Alaveteli "blog" page: -BLOG_FEED: 'http://www.mysociety.org/category/projects/whatdotheyknow/feed/' -TWITTER_USERNAME: 'alaveteli_foi' - -# Locales we wish to support in this app, space-delimited -AVAILABLE_LOCALES: 'en' -DEFAULT_LOCALE: 'en' - -# if 'true', respect the user's choice of language in the browser -USE_DEFAULT_BROWSER_LANGUAGE: true - -# How many days should have passed before an answer to a request is officially late? -REPLY_LATE_AFTER_DAYS: 20 -REPLY_VERY_LATE_AFTER_DAYS: 40 -# We give some types of authority like schools a bit longer than everyone else -SPECIAL_REPLY_VERY_LATE_AFTER_DAYS: 60 - -# example searches for the home page, semicolon delimited. -FRONTPAGE_SEARCH_EXAMPLES: 'Geraldine Quango; Department for Humpadinking' - -# example public bodies for the home page, semicolon delimited - short_names -FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq' - -# URL of theme to install (when running rails-post-deploy script) -THEME_URL: 'git://github.com/sebbacon/alavetelitheme.git' - - -## Incoming email -# Your email domain, e.g. 'foifa.com' -INCOMING_EMAIL_DOMAIN: 'localhost' - -# An optional prefix to help you distinguish FOI requests, e.g. 'foi+' -INCOMING_EMAIL_PREFIX: '' - -# used for hash in request email address -INCOMING_EMAIL_SECRET: 'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx' - -# used as envelope from at the incoming email domain for cases where we don't care about failure -BLACKHOLE_PREFIX: 'do-not-reply-to-this-address' - -## Administration - -# Leave these two blank to skip admin authorisation -ADMIN_USERNAME: 'asd' -ADMIN_PASSWORD: 'qwe' - -# Email "from" details -CONTACT_EMAIL: 'postmaster@localhost' -CONTACT_NAME: 'Alaveteli Webmaster' - -# Where the raw incoming email data gets stored; make sure you back -# this up! -RAW_EMAILS_LOCATION: 'files/raw_emails' - -# The base URL for admin pages. You probably don't want to change this. -ADMIN_BASE_URL: '/admin/' - -# Where /stylesheets sits under for admin pages. See asset_host in -# config/environment.rb. Can be full domain or relative path (not an -# absolute path beginning with /). Again, unlikely to want to change -# this. -ADMIN_PUBLIC_URL: '' - -# Secret key for signing cookie_store sessions -COOKIE_STORE_SESSION_SECRET: 'your secret key here, make it long and random' - -# If present, puts the site in read only mode, and uses the text as reason -# (whole paragraph). Please use a read-only database user as well, as it only -# checks in a few obvious places. -READ_ONLY: '' - -# Doesn't do anything right now. -STAGING_SITE: 1 - -# Recaptcha, for detecting humans. Get keys here: http://recaptcha.net/whyrecaptcha.html -RECAPTCHA_PUBLIC_KEY: '6LcsnMcSAAAAAAL4FqMix7IOsEIwdMh42MuOFztv' -RECAPTCHA_PRIVATE_KEY: '6LcsnMcSAAAAAFjbWcf2dI874as0fmYSAiC9Jgvx' - -# For debugging memory problems. If true, the app logs -# the memory use increase of the Ruby process due to the -# request (Linux only). Since Ruby never returns memory to the OS, if the -# existing process previously served a larger request, this won't -# show any consumption for the later request. -DEBUG_RECORD_MEMORY: false - -# If you have Alaveteli set up behind an HTTP caching proxy -# (accelerator) like Varnish or Squid, you can cause the application -# to purge selected URLs by setting these two variables (see -# `../doc/CACHING.md` for details) -ACCELERATOR_HOST: 'localhost' -ACCELERATOR_PORT: '6081' - -# mySociety's gazeteer service. Shouldn't change. -GAZE_URL: http://gaze.mysociety.org - -# Path to a program that converts a page at a URL to HTML. It should -# take two arguments: the URL, and a path to an output file. A static -# binary of wkhtmltopdf is recommended: -# http://code.google.com/p/wkhtmltopdf/downloads/list -HTML_TO_PDF_COMMAND: /usr/local/bin/wkhtmltopdf-amd64
\ No newline at end of file diff --git a/config/general.yml-example b/config/general.yml-example index 8c59b1b0e..ec4529b2b 100644 --- a/config/general.yml-example +++ b/config/general.yml-example @@ -35,7 +35,7 @@ SPECIAL_REPLY_VERY_LATE_AFTER_DAYS: 60 FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq' # URL of theme to install (when running rails-post-deploy script) -THEME_URL: 'git://github.com/mysociety/whatdotheyknow-theme.git' +THEME_URL: 'git://github.com/sebbacon/alavetelitheme.git' # Whether a user needs to sign in to start the New Request process FORCE_REGISTRATION_ON_NEW_REQUEST: false diff --git a/config/routes.rb b/config/routes.rb index 511b5fc1e..39c6ba70f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -78,6 +78,8 @@ ActionController::Routing::Routes.draw do |map| user.confirm '/c/:email_token', :action => 'confirm' user.show_user '/user/:url_name.:format', :action => 'show' + user.show_user_profile '/user/:url_name/profile.:format', :action => 'show', :view => 'profile' + user.show_user_requests '/user/:url_name/requests.:format', :action => 'show', :view => 'requests' user.contact_user '/user/contact/:id', :action => 'contact' user.signchangepassword '/profile/change_password', :action => 'signchangepassword' diff --git a/doc/CHANGES.md b/doc/CHANGES.md index 758e6b56e..b6e901fa9 100644 --- a/doc/CHANGES.md +++ b/doc/CHANGES.md @@ -6,14 +6,16 @@ * It is now possible to rebuild the xapian index for specific terms, rather than having to drop and rebuild the entire database every time (as previously). See rake xapian:rebuild_index for more info. * When listing authorities, show all authorities in default locale, rather than only those in the currently selected locale. * Ensure incoming emails are only ever parsed once (should give a performance boost) +* [Full list of changes on github](https://github.com/sebbacon/alaveteli/issues?state=closed&milestone=9) ## Upgrade notes -* **IMPORTANT! We now depend on Xapian 1.2**, which means you may need to install Xapian from backports. See [issue #159] for more info. +* **IMPORTANT! We now depend on Xapian 1.2**, which means you may need to install Xapian from backports. See [issue #159](https://github.com/sebbacon/alaveteli/issues/159) for more info. * Themes created for 0.4 and below should be changed to match the new format (although the old way should continue to work): * You should create a resources folder at `<yourtheme>/public/` and symlink to it from the main rails app. See the `install.rb` in `alaveteli-theme` example theme for details. * Your styles should be moved from `general/custom_css.rhtml` to a standalone stylesheet in `<yourtheme>/public/stylesheets/` * The partial at `general/_before_head_end.rhtml` should be changed in the theme to include this stylesheet - +* [issue #281](https://github.com/sebbacon/alaveteli/issues/281) fixes some bugs relating to display of internationalised emails. To fix any wrongly displayed emails, you'll need to run the script at `script/clear-caches` so that the caches can be regenerated +* During this release, a bug was discovered in pdftk 1.44 which caused it to loop forever. Until it's incorporated into an official release, you'll need to patch it yourself or use the Debian package compiled by mySociety (see link in [issue 305](https://github.com/sebbacon/alaveteli/issues/305)) # Version 0.4 diff --git a/doc/INSTALL.md b/doc/INSTALL.md index a666ac2f0..b95534e4f 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -51,6 +51,12 @@ graphical interface running) on Linux. If you do install `wkhtmltopdf`, you need to edit a setting in the config file to point to it (see below). +Version 1.44 of `pdftk` contains a bug which makes it to loop forever +in certain edge conditions. Until it's incorporated into an official +release, you can either hope you don't encounter the bug (it ties up a +rails process until you kill it) you'll need to patch it yourself or +use the Debian package compiled by mySociety (see link in +[issue 305](https://github.com/sebbacon/alaveteli/issues/305)) # Configure Database @@ -77,8 +83,8 @@ constraints whilst running the tests they also need to be a superuser. The following command will set up a user 'foi' with password 'foi': - echo "CREATE DATABASE foi_development encoding = 'UTF8'; - CREATE DATABASE foi_test encoding = 'UTF8'; + echo "CREATE DATABASE foi_development encoding 'SQL_ASCII' template template0; + CREATE DATABASE foi_test encoding 'SQL_ASCII' template template0; CREATE USER foi WITH CREATEUSER; ALTER USER foi WITH PASSWORD 'foi'; ALTER USER foi WITH CREATEDB; @@ -86,6 +92,12 @@ The following command will set up a user 'foi' with password 'foi': GRANT ALL PRIVILEGES ON DATABASE foi_test TO foi; ALTER DATABASE foi_development OWNER TO foi; ALTER DATABASE foi_test OWNER TO foi;" | psql + +We create using the ``SQL_ASCII`` encoding, because in postgres this +is means "no encoding"; and because we handle and store all kinds of +data that may not be valid UTF (for example, data originating from +various broken email clients that's not 8-bit clean), it's safer to be +able to store *anything*, than reject data at runtime. # Configure email @@ -295,8 +307,9 @@ is supplied in `../conf/varnish-alaveteli.vcl`. to `/etc/elinks/elinks.conf`: set document.codepage.assume = "utf-8" - - You should also check that your locale is set up wrongly. See + set document.codepage.force_assumed = 1 + + You should also check that your locale is set up correctly. See [https://github.com/sebbacon/alaveteli/issues/128#issuecomment-1814845](this issue followup) for further discussion. diff --git a/lib/tmail_extensions.rb b/lib/tmail_extensions.rb index 6a5044cdb..f35565352 100644 --- a/lib/tmail_extensions.rb +++ b/lib/tmail_extensions.rb @@ -30,7 +30,7 @@ module TMail # Monkeypatch! Return the name part of from address, or nil if there isn't one def from_name_if_present if self.from && self.from_addrs[0].name - return self.from_addrs[0].name + return TMail::Unquoter.unquote_and_convert_to(self.from_addrs[0].name, "utf-8") else return nil end diff --git a/script/clear-caches b/script/clear-caches index 2d91774ef..be1d3d017 100755 --- a/script/clear-caches +++ b/script/clear-caches @@ -4,7 +4,7 @@ LOC=`dirname $0` -"$LOC/runner" "ActiveRecord::Base.connection.execute(\"update incoming_messages set cached_attachment_text_clipped = null, cached_main_body_text_unfolded = null, cached_main_body_text_folded = null, sent_at = null, subject = null, safe_mail_from = null, mail_from_domain = null, valid_to_reply_to = null\")" +"$LOC/runner" "ActiveRecord::Base.connection.execute(\"update incoming_messages set cached_attachment_text_clipped = null, cached_main_body_text_unfolded = null, cached_main_body_text_folded = null, sent_at = null, subject = null, mail_from = null, mail_from_domain = null, valid_to_reply_to = null, last_parsed = null\")" # Remove page cache (do it in two stages so live site gets cache cleared faster) rm -fr $LOC/../old-cache diff --git a/script/handle-mail-replies b/script/handle-mail-replies index 9451bc9f2..68cab9035 100755 --- a/script/handle-mail-replies +++ b/script/handle-mail-replies @@ -120,12 +120,21 @@ def is_oof?(message) end end + if message.header_string("Auto-Submitted") == "auto-generated" + if subject =~ /out of( the)? office/ + return true + end + end + if subject.start_with? "out of office autoreply:" return true end if subject == "out of office" return true end + if subject == "out of office reply" + return true + end if subject.end_with? "is out of the office" return true end diff --git a/script/update-xapian-index b/script/update-xapian-index index 8d1fa7d0c..6ece02de0 100755 --- a/script/update-xapian-index +++ b/script/update-xapian-index @@ -1,5 +1,5 @@ #!/bin/bash cd `dirname $0` -rake --silent xapian:update_index +rake --silent xapian:update_index "$@" diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb index 4994c2a8f..f25ebe339 100644 --- a/spec/controllers/request_controller_spec.rb +++ b/spec/controllers/request_controller_spec.rb @@ -287,7 +287,7 @@ describe RequestController, "when showing one request" do old_path = assigns[:url_path] response.location.should have_text(/#{assigns[:url_path]}$/) zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", old_path)) { |zipfile| - zipfile.count.should == 2 + zipfile.count.should == 1 # just the message } receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) get :download_entire_request, :url_title => title @@ -295,14 +295,14 @@ describe RequestController, "when showing one request" do old_path = assigns[:url_path] response.location.should have_text(/#{assigns[:url_path]}$/) zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", old_path)) { |zipfile| - zipfile.count.should == 2 + zipfile.count.should == 3 # the message plus two "hello.txt" files } receive_incoming_mail('incoming-request-attachment-unknown-extension.email', ir.incoming_email) get :download_entire_request, :url_title => title assigns[:url_path].should have_text(/#{title}.zip$/) response.location.should have_text(/#{assigns[:url_path]}/) zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", assigns[:url_path])) { |zipfile| - zipfile.count.should == 4 + zipfile.count.should == 5 # the message, two hello.txt, the unknown attachment, and its empty message } assigns[:url_path].should_not == old_path end diff --git a/spec/controllers/user_controller_spec.rb b/spec/controllers/user_controller_spec.rb index 399b275a7..c13d7c9fc 100644 --- a/spec/controllers/user_controller_spec.rb +++ b/spec/controllers/user_controller_spec.rb @@ -28,6 +28,16 @@ describe UserController, "when showing a user" do response.should render_template('show') end + it "should distinguish between 'my profile' and 'my requests' for logged in users" do + session[:user_id] = users(:bob_smith_user).id + get :show, :url_name => "bob_smith", :view => 'requests' + response.body.should_not include("Change your password") + response.body.should match(/Your [0-9]+ Freedom of Information requests/) + get :show, :url_name => "bob_smith", :view => 'profile' + response.body.should include("Change your password") + response.body.should_not match(/Your [0-9]+ Freedom of Information requests/) + end + it "should assign the user" do get :show, :url_name => "bob_smith" assigns[:display_user].should == users(:bob_smith_user) diff --git a/spec/fixtures/files/quoted-subject-iso8859-1.email b/spec/fixtures/files/quoted-subject-iso8859-1.email new file mode 100644 index 000000000..6ada69905 --- /dev/null +++ b/spec/fixtures/files/quoted-subject-iso8859-1.email @@ -0,0 +1,462 @@ +From: =?iso-8859-1?Q?Coordena=E7=E3o_de_Relacionamento=2C_Pesquisa_e_Informa=E7?= + =?iso-8859-1?Q?=E3o/CEDI?= <geraldinequango@localhost> +To: FOI Person <EMAIL_TO> +MIME-Version: 1.0 +Content-Type: multipart/related; + type="multipart/alternative"; + boundary="----_=_NextPart_001_01CCB66F.F38B15FC" +Subject: =?iso-8859-1?Q?C=E2mara_Responde=3A__Banco_de_ideias?= +Date: Fri, 9 Dec 2011 10:42:02 -0200 + +This is a multi-part message in MIME format. + +------_=_NextPart_001_01CCB66F.F38B15FC +Content-Type: multipart/alternative; + boundary="----_=_NextPart_002_01CCB66F.F38B15FC" + + +------_=_NextPart_002_01CCB66F.F38B15FC +Content-Type: text/plain; + charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable + +=20 + +Senhor Benedito, + +=20 + +O Centro de Documenta=E7=E3o e Informa=E7=E3o (Cedi) da C=E2mara dos = +Deputados agradece o seu contato. + +=20 + +Em aten=E7=E3o ao solicitado, informamos que a C=E2mara dos Deputados, = +por iniciativa da Comiss=E3o de Legisla=E7=E3o Participativa - CLP, = +criou um "Banco de Id=E9ias" com o objetivo de registrar e reunir = +id=E9ias de interesse da popula=E7=E3o. As sugest=F5es s=E3o organizadas = +por temas e ficam =E0s disposi=E7=E3o para consulta de entidades da = +sociedade civil e parlamentares, que poder=E3o adot=E1-las, = +aprimorando-as ou n=E3o, para serem transformadas em sugest=E3o de = +iniciativa legislativa, no caso das entidades da sociedade civil, ou em = +proposi=E7=E3o legislativa, no caso dos parlamentares. Cabe ressaltar = +que a Comiss=E3o reserva-se o direito de editar ou resumir os textos = +recebidos. + +A seguir, o endere=E7o eletr=F4nico do Banco de Id=E9ias:=20 + +=20 + +http://www2.camara.gov.br/atividade-legislativa/comissoes/comissoes-perma= +nentes/clp/banideias.htm/banco-de-ideias + +=20 + +Atenciosamente, +***************************************************** +Coordena=E7=E3o de Relacionamento, Pesquisa e Informa=E7=E3o - Corpi +Centro de Documenta=E7=E3o e Informa=E7=E3o - Cedi +C=E2mara dos Deputados - Anexo II +Pra=E7a dos Tr=EAs Poderes - Bras=EDlia - DF=20 +70160-900=20 +Tel.: 0-XX-61- 3216-5777; fax: 0-XX-61- 3216-5757=20 +informa.cedi@camara.gov.br <mailto:informa.cedi@camara.gov.br>=20 +***************************************************** + +mbb + +=20 + +Solicita=E7=E3o:=20 + +=20 + + Prezado(a) C=E2mara dos Deputados, + + =20 + + Gostaria de sugerir que o sal=E1rio de quem trabalha com pol=EDtica + + seja vinculado ao sal=E1rio m=EDnimo. + + =20 + + Atenciosamente, + + Benedito P.B.Neto + + =20 + + ------------------------------------------------------------------- + + =20 + + Por favor use esse endere=E7o de email em todas as repostas para = +este + + pedido: + + leideacesso+request-120-9702221c@queremossaber.org.br + + =20 + + Caso este email - informa.cedi@camara.gov.br - seja o endere=E7o + + errado para fazer acesso a informa=E7=E3o por favor nos contate e + + aponte o endere=E7o correto atrav=E9s desse formul=E1rio: + + http://queremossaber.org.br/pt/help/contact + + =20 + + Aviso: Esta mensagem e todas as respostas ser=E3o publicadas na + + internet. Leia sobre nossa pol=EDtica de privacidade: + + http://queremossaber.org.br/pt/help/officers + + =20 + + Caso voc=EA ache esse servi=E7o =FAtil, por favor entre em contato. + + =20 + + =20 + + ------------------------------------------------------------------- + + +------_=_NextPart_002_01CCB66F.F38B15FC +Content-Type: text/html; + charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable + +<html xmlns:v=3D"urn:schemas-microsoft-com:vml" = +xmlns:o=3D"urn:schemas-microsoft-com:office:office" = +xmlns:w=3D"urn:schemas-microsoft-com:office:word" = +xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml" = +xmlns=3D"http://www.w3.org/TR/REC-html40"><head><meta = +http-equiv=3DContent-Type content=3D"text/html; = +charset=3Diso-8859-1"><meta name=3DGenerator content=3D"Microsoft Word = +14 (filtered medium)"><!--[if !mso]><style>v\:* = +{behavior:url(#default#VML);} +o\:* {behavior:url(#default#VML);} +w\:* {behavior:url(#default#VML);} +.shape {behavior:url(#default#VML);} +</style><![endif]--><style><!-- +/* Font Definitions */ +@font-face + {font-family:Calibri; + panose-1:2 15 5 2 2 2 4 3 2 4;} +@font-face + {font-family:Tahoma; + panose-1:2 11 6 4 3 5 4 4 2 4;} +/* Style Definitions */ +p.MsoNormal, li.MsoNormal, div.MsoNormal + {margin:0cm; + margin-bottom:.0001pt; + font-size:11.0pt; + font-family:"Calibri","sans-serif"; + mso-fareast-language:EN-US;} +a:link, span.MsoHyperlink + {mso-style-priority:99; + color:blue; + text-decoration:underline;} +a:visited, span.MsoHyperlinkFollowed + {mso-style-priority:99; + color:purple; + text-decoration:underline;} +p.MsoPlainText, li.MsoPlainText, div.MsoPlainText + {mso-style-priority:99; + mso-style-link:"Texto sem Formata=E7=E3o Char"; + margin:0cm; + margin-bottom:.0001pt; + font-size:11.0pt; + font-family:"Calibri","sans-serif"; + mso-fareast-language:EN-US;} +p.MsoAcetate, li.MsoAcetate, div.MsoAcetate + {mso-style-priority:99; + mso-style-link:"Texto de bal=E3o Char"; + margin:0cm; + margin-bottom:.0001pt; + font-size:8.0pt; + font-family:"Tahoma","sans-serif"; + mso-fareast-language:EN-US;} +span.TextosemFormataoChar + {mso-style-name:"Texto sem Formata=E7=E3o Char"; + mso-style-priority:99; + mso-style-link:"Texto sem Formata=E7=E3o"; + font-family:"Calibri","sans-serif";} +span.TextodebaloChar + {mso-style-name:"Texto de bal=E3o Char"; + mso-style-priority:99; + mso-style-link:"Texto de bal=E3o"; + font-family:"Tahoma","sans-serif";} +.MsoChpDefault + {mso-style-type:export-only; + font-family:"Calibri","sans-serif"; + mso-fareast-language:EN-US;} +@page WordSection1 + {size:612.0pt 792.0pt; + margin:70.85pt 3.0cm 70.85pt 3.0cm;} +div.WordSection1 + {page:WordSection1;} +--></style><!--[if gte mso 9]><xml> +<o:shapedefaults v:ext=3D"edit" spidmax=3D"1026" /> +</xml><![endif]--><!--[if gte mso 9]><xml> +<o:shapelayout v:ext=3D"edit"> +<o:idmap v:ext=3D"edit" data=3D"1" /> +</o:shapelayout></xml><![endif]--></head><body lang=3DPT-BR link=3Dblue = +vlink=3Dpurple><div class=3DWordSection1><p class=3DMsoNormal><span = +style=3D'mso-fareast-language:PT-BR'><img width=3D566 height=3D58 = +id=3D"Imagem_x0020_1" src=3D"cid:image001.png@01CCB65F.2FD5C970" = +alt=3D"Descri=E7=E3o: Logo Corpi_html_1cd70d8a"><o:p></o:p></span></p><p = +class=3DMsoPlainText> <o:p></o:p></p><p class=3DMsoPlainText>Senhor = +Benedito,<o:p></o:p></p><p class=3DMsoPlainText><o:p> </o:p></p><p = +class=3DMsoPlainText>O Centro de Documenta=E7=E3o e Informa=E7=E3o = +(Cedi) da C=E2mara dos Deputados agradece o seu = +contato.<o:p></o:p></p><p class=3DMsoPlainText><o:p> </o:p></p><p = +class=3DMsoPlainText>Em aten=E7=E3o ao solicitado, informamos que a = +C=E2mara dos Deputados, por iniciativa da Comiss=E3o de Legisla=E7=E3o = +Participativa – CLP, criou um “Banco de Id=E9ias” com = +o objetivo de registrar e reunir id=E9ias de interesse da popula=E7=E3o. = +As sugest=F5es s=E3o organizadas por temas e ficam =E0s disposi=E7=E3o = +para consulta de entidades da sociedade civil e parlamentares, que = +poder=E3o adot=E1-las, aprimorando-as ou n=E3o, para serem transformadas = +em sugest=E3o de iniciativa legislativa, no caso das entidades da = +sociedade civil, ou em proposi=E7=E3o legislativa, no caso dos = +parlamentares. Cabe ressaltar que a Comiss=E3o reserva-se o direito de = +editar ou resumir os textos recebidos.<o:p></o:p></p><p = +class=3DMsoPlainText>A seguir, o endere=E7o eletr=F4nico do Banco de = +Id=E9ias: <o:p></o:p></p><p class=3DMsoPlainText><o:p> </o:p></p><p = +class=3DMsoPlainText>http://www2.camara.gov.br/atividade-legislativa/comi= +ssoes/comissoes-permanentes/clp/banideias.htm/banco-de-ideias<o:p></o:p><= +/p><p class=3DMsoPlainText><o:p> </o:p></p><p = +class=3DMsoNormal><span = +style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-la= +nguage:PT-BR'>Atenciosamente,<br>****************************************= +*************<br>Coordena=E7=E3o de Relacionamento, Pesquisa e = +Informa=E7=E3o – Corpi<br>Centro de Documenta=E7=E3o e = +Informa=E7=E3o – Cedi<br>C=E2mara dos Deputados – Anexo = +II<br>Pra=E7a dos Tr=EAs Poderes – Bras=EDlia – DF = +<br>70160-900 <br>Tel.: 0-XX-61- 3216-5777; fax: 0-XX-61- 3216-5757 = +<br><a href=3D"mailto:informa.cedi@camara.gov.br"><span = +style=3D'color:windowtext'>informa.cedi@camara.gov.br</span></a><br>*****= +************************************************<o:p></o:p></span></p><p = +class=3DMsoNormal><span = +style=3D'font-size:8.0pt;font-family:"Arial","sans-serif";mso-fareast-lan= +guage:PT-BR'>mbb<o:p></o:p></span></p><p = +class=3DMsoPlainText><o:p> </o:p></p><p = +class=3DMsoPlainText>Solicita=E7=E3o: <o:p></o:p></p><p = +class=3DMsoPlainText><o:p> </o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 Prezado(a) C=E2mara dos = +Deputados,<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0 = +<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0=A0Gostaria de = +sugerir que o sal=E1rio de quem trabalha com pol=EDtica<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 seja vinculado ao sal=E1rio = +m=EDnimo.<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0 = +<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0=A0Atenciosamente,<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 Benedito P.B.Neto<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0=A0-------------------------------------= +------------------------------<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0=A0Por favor use esse endere=E7o de = +email em todas as repostas para este<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 pedido:<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 = +leideacesso+request-120-9702221c@queremossaber.org.br<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0=A0Caso este email - = +informa.cedi@camara.gov.br - seja o endere=E7o<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 errado para fazer acesso a = +informa=E7=E3o por favor nos contate e<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 aponte o endere=E7o correto atrav=E9s = +desse formul=E1rio:<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0 = +http://queremossaber.org.br/pt/help/contact<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0=A0Aviso: Esta mensagem e todas as = +respostas ser=E3o publicadas na<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 internet. Leia sobre nossa pol=EDtica = +de privacidade:<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0 = +http://queremossaber.org.br/pt/help/officers<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0=A0Caso voc=EA ache esse servi=E7o = +=FAtil, por favor entre em contato.<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0=A0<o:p></o:p></p><p = +class=3DMsoPlainText>=A0=A0=A0=A0=A0-------------------------------------= +------------------------------<o:p></o:p></p></div></body></html> +------_=_NextPart_002_01CCB66F.F38B15FC-- + +------_=_NextPart_001_01CCB66F.F38B15FC +Content-Type: image/png; + name="image001.png" +Content-Transfer-Encoding: base64 +Content-ID: <image001.png@01CCB65F.2FD5C970> +Content-Description: image001.png +Content-Location: image001.png + +iVBORw0KGgoAAAANSUhEUgAAAjYAAAA6CAIAAAAsiYQwAAAjEUlEQVR4nO2dB3gc1bn336nbm7pV +rWZZkiU3ucmWcQMMGBsbguFiMARM5wMSQhIuaf4INyEkfOAvBG4IJU8uJU5CQlxkXHDFvajYlixZ +stUsraTVStt3p9wzK1vI2tVom4rx/J71ejRzznvec2b2/OedOXOG5Hkerjd48LjdLp7v6eg4eKil +utr21NMzdDpVxMt58slDb789O+JmJSQkJK4TyNF2YETgwNhqbbf0dHa7a+vOXjjTve1Ia6PLxtT2 +YOCeV5wql5dEvEye544f/xOAJFESEhISIUKyLEsQxGi7EXm6Ozvr66znu7u6WhpPNeDlZ6qbWxos +1tgeM9B0N01NpDEdF63p5OpWr1spkw1HC2A0rRgGsxISEhLXC6TFYtFqtTiOj7Yn4dLGgOXc+bNV +5hMnjp902C4Z7W2tqYzNbOjp6WJcGKVUEuPieZVbaSYZttN2AjxUjjJ+6dLxJZPVo+27hISEhIQf +BImSyWQKxTV4vs+7zpg7vjrN7tlf4d5daoSoSme6zeHOtVpO82Q6rm/BPZMxeRUpswIFzl3gxFPj +49MTYm4riC0snlyQNylaFaXRYAqVWN3dYG2zNtW26fft33fvzEnZubkjVj8JCQmJ6xxSLpezLDva +bgRHp7E1fd1HCS5bS22djSKAYPPxJVWYy8Db1Tx2GtcU8vvLSRIIrhUfn8elFk5z3nrbf86YgSWM +SyEppbhxj4exOaytra2fNTTtKd/ZRDXWwDmADoqO/WnKwZGpoISEhIQEglQqlWVlZcXFxaPtSRD8 +9jfvWE63OFXj4nQ3UsDlcthBRwtOGLioTlqrydfZp1E5zxWXTJmSmplDavXJIqbcLt7E28w95nZo +O2w5e7Su63jTkfNdh4B0A0GBigYghEElLH17zApQC5dDOY7DvIxUdSUkJCSuU0iapnmedzgc18q1 +PjvAH76oK9Le2I67bOBKZvFOueu5NSkTycycfENGujo1c5zISEXGbW+zQQN/qb6pvsl5sbazu5xr +rK6rMrOnAZcDgQvKpNYAXH1zjnGsnbIEvPrUO8BEkigJCQmJ4YakKCo2NtZoNKalpY22MwHxt7+d +MssID8ZcBK4QCYnjQuGD+W88e69YHgcc7Gw/6yi/dPHSWeeZGkdXC9/e1NIAhAlwEjACaPQdN3h+ +Bhzxy9Nm9v6B9OlbMLpEQkJCYuwjRBtRUVEXzp9PTkwkKGq0/RkS+46NNTnAVAMkA3QDxXa1vLLq +Md90RmisaTRt9Bw1VXeUmU42e6wWtsftcALuAcwjXL5TorrrAyrTbfn13B/0BmaSOElISEiMGEK/ +G20wnNRoqru782JiRtufQeB58F5Y++wvxz6r3eYm587CPBgQZbaq79wbn5OYIKTh3MAzQCj/q3br +J9terCjHoAgD5srcGUJ2HGjs8r2lIIp2azxZD0xYFfE6SUhISEiII3TWGEFMUCguVVUxs2eTZG8n +PsZgu4HUG43wyy3lbld+gZwGYM5xHiC45x54SLh7hMDRSvpM85lf7vq/NhTqzCSBQSu9d4ywy/+C +hwc393zRwoQ4kcuAEhISEhLDwuV4Ii09/UhtbUxDc1ZG/FiUKFIP4Ppwx66KKnsRHVMHzRQkm2x1 +K1dkp+Xl9E/46xObbW470DSwWIiq1B/OlSYvWDPhobANSUhISEgEzTeXvObOnb/3zOaMlJU8Phan +RGqub930wYEcJpakeQIYl8eeksz8dNWUKPk3QrSxav+nbZuA5EONmQbAggu/f8pt2fHxkbAWLhj2 +C57/2Wh7ISEhITFyfCNRiUrZzKTJn1V/eu8k0dFxo8TqX/3rQENDpsbAARfFpTaD46lbCqfMLkKb +XC6XTCazdJne3vemm2kHYoiHcwOC54DBc9NueGLugghYCwwkQt+Uf7Ua9erTWFCpXifDd6N/ZSNi +cLBSArccqapJSEhEiqsGDmSMyzxTdnZfx76SBSVjanrZ+95648BXddGaNAPYCd5Q7Xbrki8++sLT +vVuRPqHv9Sff3O2sBFlE9IkH3iOjYt6Z+qNEMiECBgOgf/+Ilgf0rb3L377ec7D6RoQBKighIXHN +MXBs27Kly35/9Peq88ppmdN7XyU16s+objhy8ONfnsKSFxMYR8I5K9sJxJmvX/mdQejg+F733jZ+ ++PqJP4E2PkKX+FAIRXy46jfzE0dBn8BfCNW33LepL0vf1r7l/tl98wayZkgPxX0L0Gb/BANUKpAq ++673TQNXB1LiZoejahISEuHgZ/j1g9kPfv7lVgr0BRkZMFr6xLm9I/TgzW3nvv/8/0QlLUrBPD2A +WZhxp7kz7z+zJq/IICRjWSDJ4+cOPPX5i2BIjVTZ4DA9n/niPYlFETIYFgN62AHRRt8FwP7LfWlE +8vZfL2K/bz1c3e8PWD8gr3hEGEiVRbL7VtMvfpXMt6YjXDUJCYmg8CNRKr3qhhuX7NyzW+F2Z43W +xN44Dx54/69HXt/w70nUJAVut0GjjSPqSetzD9320LrFvakwkvzk4rmndzwO+qQIFcyBTf7g5KW/ +W/RUhAyGi2+g47vVdzmQvIGn6Z848Ktnvd13xHvwkA0O2ZKjXjUJCYkB+H+INcWgWTVv9ptbdy5S +9sxNmzXCPnmRPf3OV++9/1EGl0tTJAasBTijqf37jyx//albgbcBJrzH/V9njz+/d70JIwCL0KQP +tu510xb/6ob/jIy1CDHYmX6k8oZj3y/9AzsIVVSG405S+DWNSNUkJCQCZLB5FnBtTMKLa+776L0P +Ghacvzf7P4bdkStzQ/T+VfjdtypONEZDTBPFZQMcY6mUjoRn7pr7+svLOJbFvckOna1Yt+sn7Xgj +EHQEbkHxLDg61xYseO2GH+kDnBhpRAi/lxfPO0xdbf+QJbSrYcM0eiJ8s+FXTUJCIkD8SxTvfbJI +xtseXffQF5tLf3j4Lz9fdadCqeibiCjyeOeGQP//+eOKn7z4ywZdbLRsei7OHgVwOZ286sRr/7Vm +9ZqFQkJCeO7pwZb3/rzpZWF8BBYJfUIC6SF/nvfqz5asCb8qIeB7DwnG5Bl6gMFHn/8Bxisi9fU7 +AER8TEdoDFPVJCQkwsG/RHEcB8KU3sLFtOW3LU07V7Oh9PObsibEjM/RsIxcqewd5x1BWLfn5Imz +7/7lq/d3nU6JvSkO74nGuGqOZ60mfYb5r7/+3Xcm65BbgOP17S33nfjtwcqNoE0KY2YjL0JWDlwO +nSr6g+wnVi6+L1LVCYE+ler70+/6cGyGn2ZAvxygzyJa6zeNeHbfcRBDutc3ZN+3psNXNQkJifDx +L1EDnoiaPCE7Pln11cGvzBdborTRs2JjWjpa8/PzddERmHa2k4Hj24/8ed+R0s3nHR4iUT7VgbNa +3sg5OlyE8pGbdY+/9Pzkcd5Z+FjXf1/8x893fHbJVQOa2HCDJ5S72wM2Ys6sWb+d9Mic5Hnh1yVM +Buvv/K4fbKCEb4c+pLVA+tnBBsQH5XPgCYLdGqB74i0ZqapJSEhEisDm/OaZBGXCykV31ZVVbXac +LD25a65m8c5N+/F2a1F+8c23JAFcHVSxNvBGYKJ4jtSe++Dfx45Ud9Udwwhrp1KZnk4ZyoCVuVxG +d/fC4qRXVhWvXDqPoAQnN5858Zfa/7/xUjnLuYBWhn1xjwfGDjLdT6Y99b0Ft+vxMXTzSUJCQkKi +lwBfS4GjLl2OyfKmTE52pX2kb9l5fmMSHZc/667N/7P7Fz99JTk19abFN5aUJCWPV6k0MYPok8fj +sNpt3YePwFtf7SQrT5/yWKzGJAOrT6HVemVUPd9sdFd6zA554dT3X/zxPbPiFTIheLL0tK78/Hf7 +3AfcbhOQCiDCucbI984cAQ7L/fqFT9z/f+aQU8OwJjGiSOGLhMT1RmAS1W9It1amfyZX/0juCx8e +eW/D18/e80hh0Zo3z+0/8oMvvjS/3ZjqcdPMDHYuFsfwE3CMw4Q3NO0GXEZgRDNVc7HGTRgBIzEs +fyqG6bA5eozHif0nPdOB2B2FkwvTsz/53qLiG2YLJTGe3TV1rx78cHvPP4S3tBMkUENGZiLwwrR7 +LAsYn8rk/X3V94pGZzC9hISEhESgBPNyv34oQPXEzGefyOc2dO56ccOyWXPnvnjDfKWyZM8B07Gd +5bgZ6hm+3ORwuO1gxYDrAtABkQV4XAI/TsWzaqLerZzcLnM5qZZxcRnfU9hm3Prs0rum6QFvc3Sd +NFVv2Ltje837TbJ2QZYohXcYYchX9pAyMUjwoihDXtSk+8c/9OjsOaGakpAYSP9nra7nOE9qB4nh +IESJuowKf0a15JnfLPnnmU2fHP2qprnVMDlh/t3x2nSV0pzgvOjs5rXq8yqcq7hEYZiTUmpoGcZa +CXURvciSwidGcRnKhUVTFEatsvlizRcnPtnXVrGlvrLFXQm0DHRKgHBeJMgD5wHWA4wmNjZ5Ser8 +J5WF86YuBmyszI3rS6Qmf4tgHxHs8Hff8XIRcWO4CbPFhmkAeoCNP3aGF0oD8SUiTngSdYU78pbd +kXdrVVNLhfH81srNm3bv7Sb1bKwzPio3LVWlwOkiKtvjpJVqWomzdkJJcJVOtulrjtjuNq3ffrHW +w3a0XmAIs/AQLkGBMjr0mAkXBneA2wUMlqDOnZmZfot6Vn5mQUncxIjUdPiI1ORvY6GPuLamsAuz +xfpGtEfIneAYO4/QjW47SHxbCVKimB4gtT4ru4HUIXGYmJyMPndOLW5sa67ounCk48BfT5/4ynIA +SHILEQ0kDjgOHIEJ86dzPFgFHcJwb1iDgRJ9DzmsjvV+E0KE1Evv/yQPDO+dn8IN1XZKn1k8f+66 +6GlzUufHajQaKhKv5xhmfB/08d06YNOAuXwGTEA34Cng/nkDidUG6/iCivMGPJYknn0wP0WW+0cP +fp/wHcygeIuJ+CmyIwJZ6Uvg7Rmm80O2hq9xEfeCqnKkrg1IXJ8EKVGExt/Kq0QLx6i0hPHoswwW +rC8Bh9VR2rzr0wtHzTXnd1TXc4ntvBYHHikTdTlU6r3PhDRLUCDv29wHRlBIfsC7NQ3ADXDJu46/ +/I30aQ8Hc4g56iXzJ86fd9fCJXFR8uBqNaYRj66wq6f99ttZg08/Ih7l+HZegecNrSIhWO7/sO2A +RhiyLPEWG5BY3KbfxAFWJ5xaizgvYtPvvh6sGcNvh9DqKCHRH38SxbOD3rDxO/uR6JRICrViZc5t +6AM3I5FxdPRc+rD1rN1hP1txphEaGrAOYB12BuMxmwMIHHM67QxwWD+VQvqkkmswnAcZ30nRNF4W +R0+i59Lz3Jw7KTVxXuzCuY8qErlkiNBEstcWw/qDH5VbCwHWSLznDce+b6wpYiSQxBEk4rtbpBnH +cjtIXD/4kyjOGcCDt6FAgCJem/FDbYbwx2QAJ+fAcZujra4bc2DnGoCiiJa6OrPT2f81iizDTCgo +JOUMpPBclCJWsUIRx8bh+qufjvq269PY+fGPHU+GJGRXB4sjw088WPaxyUi2g4SEX/xJ1PDokx/k +uAKFWXR8jA6+GbyXOEKFX1uMncsjY8eTIQlnvEmAeYNK7Jcx254j3A4SEn4Z8l5U74CEMGcb6oX1 +jnQINpMTeJd3OMZVcF5IMjIjEkcdkVsp/RnhjkDkdDgQT0LzVnzgQwgMR6NF3OY12sVfo25LXEN8 +08WfONa69fM9u5w2wHGWxwmCxc4AY+JxGYbNAmAEhcm1ee76/rrFmYJimUzG997bb75k3k9SFNos +QGDCwAakajgLWAqw6UB8DdgUljHGqTY8WrxtW90fD1UzNIUDzgA/HrBk4AjgDwDkAN4BWCaSHp7H +eCJ7gnNSwe0zi8cBgaxpPE77S6++ddiaQhHQYzv04WPfzZ88Fcfxnp6esrKyadOmWq226urq8ePH +YxiWlJRkNps7OjqMRmN0dBRBkCjZxIkT1Wr1qVOn9Ho9SoZ8Rem7urqmTZvW2dlZU1NjMBgKCgpG +ZR/00qdSfX+Krx/Sjt+UgVgbcM88qLy9+E0jkt3vpsHcCISgGm1Aiw3IG6CTIZQe7J4NhAjaDL8d +IuuPxPWJIFE95q4XX/h/724pB908wDQ4WNOwS/UwEQjPRJyosjrgC++booDYA+XvfHHPc9+55Y03 +HnS73Qf37flnVTZQJwGKvcp0GmCcMJEEtCIJGwf5rVDOQ9sOZnZCUoXpzhsry5nt/3KCEulZBUDK +HrAAjPe6ge0Hkww6ScjqBoqFfbCJA9v2yVOK/rHh8Yx0hZMj39hvY5v+DeQ0MNn2P8LkA7S3t5eW +lmZkZGzduqO+vmbGjBmxsXFr1tz3wgvfP3mybOHCBbt3750/f/6hQ18vW7bs5Zd/snr13RZLz549 +e5YvX56env7aa6899NBD27dv37lz18qVdzAMM6o7QkCkLxNfOdj4Pb95A+kmBtwGDzzvkGIQ1Ca/ +bgSy7Ndg4InFRUXclLiFoJKFUOvA/QnNeN/6ANtBZL2ERCAIEvXpriN/31Y7L3HBMdCSvMfqsOtY +Qxw0tbP6KnBMhVN2KMRJppWOcuFF8RTzQdmRHzXdRispmYwcr+AS6JIuzqZw2Xk2vgZkTmjJhppW +mB4HHVbQWSGbZ20xtD06pgePpaZocTlFV0NRjMfhYZouQA2APh1YK1Dt4AI4mwAxVjong0zUaxrL +aw8/9rxh28ZHSQKTy2ilojCVGnfcWV3sdd1s7omNjUfK5HA4/vjH2paWlubmlscee+zQoUNmsy03 +N7ekpC0vb+KZM6f37duXnp7V2WmeN69YLpejGAtlz8zMnDdv3v79XyNxamlBKQdeS5SQ8EUaDtCL +1A4SI4PwBtv9NV1WVTIPejd/MccRPftuxURtHMcxOCShWIWFFAfOnKl1nTxqNLNkN045HVBR0TV3 +rrYV4tuBNHAOULcvv60wJo5gXCqSNLqZDBLXsEBgnJEis1wuT3x8ulqt5Si2Dqez+XqKyOSnKOZk +FS1wqyiy0+6Ji6JMbteEC+Bxy03E7todXY4sUpevnvTVoTM2t4uicRVoDKDuBg8Ik+4Jz0mlpCS1 +traUln5JUQSKilQqNU2TCoXigQfWvv766ygBTcs8Hndubt4NN5S8+eYb06dOPH78ZHv7pVtumYK2 +qtXqzZu3pKamzJkzOzU1qaOja3T3hMTYR7r10ovUDhIjBolxQBtxnECdPsY5L6TdWvzOz28A8L5C +ULh2d3mgREer/bbHf1V1+GA6JecJS0NjU45iCkckZYPVwzmVGvvj3711XPoQj8wyTbzWdZZXRBmx +inWLV76ytoRzA064WZYmCNbKEG6UiPa0fadt5tI/WeO0POhY2T6LoyeaNniAd4Dw9JMV2At8+mQA +FA+VoECpzRgbG2O1Wu12O1Kd6OholUq1fv16ZAkFWDiOozVoee0DD+oNBqU6iqZnIRlDa9asWeNw +OJOSEidOnOjN7oArrxtGuYTK83zfoAxM9NkviesEqVPuRWoHiRFDuNBH4leWHfiSe3VX9An6DeRj +YhKwTR+96Oi2AM8ROBkbH2PpbjaxjPAiKUx+2j7+51v+lZ5OMAyKcmYDHPLmzcDAxEEHwVK3JqdO +mTEH59lmSEsEFfAKGhfu/eA0+qK97/gl1JeH+1FReclTM+FkV3UqOfE8htttjhhDlBbA4b3H1Qp4 +PN4GgOI8buvWrfX1DU8//YRWq/3007/PnTs7Pz/38OHDlZWV999/f2dnZ1xcnMVirag4uGDBgp4e +i8lkqqmpOX369KpVq5DCxcfHVVRUVFdXo/Xz589vbcWjopCG0R9//LHBYLjlllu2bNmi0+nQpt27 +d1+8eHHt2rUjt2euffo/KCN1ahIS4XDd/ppIJCnlgjqRLFQCdMVhk/wmQ59YJF46dd+qZkqWRmJb +AKajOKjb8t9vHwKW9D5D+6VwKU7QORQU5QsPPDm/rF98zx9nAJZL8Lsrj3iKCkDp5jkRt+QLZjCf +baVJHkU0bW1t6akpnV4nEgDqAGes7ShNQ0MDim/uumvlW2+9qVDoVt99x8lTZb/4xd+XLFm0YsWK +Tz75BIVTc+fOPXXq1BtvvJGennHgwL59+75+9NF1zz13Y2dnx8MPP/Lss8/GxsYWFBSUlZWhWOqF +F15AsoSkCGU/fvy42Wyura3NyMjYsuVLvV5z5513vvrqqy+99FKE90A/BgyfG74iRuwQD/OORbDe ++h1RFtkiws84woTTo4XQnhFhZNo28FLGyIjEYbr/N0zdTqR2IomUJN07FK/x8ox5/MAkPILhMQrH +XP1fAB/rZlvYU6mQRmPkbF7rgFgMyCqIdQIrh+o8yGsApgOYSXxLpSxXXSjcQ4KzDFiKchRYFZhX +sGJuZXOwHUxOMAKPabUa5AGSPjOSUGEjx6svP99LEER8fLzL5dZoOZ1ej+EIPjMzIyYmZtq0ourq +s0J9eP6ee+4tLf1Sq1WjGOvYsWPl5WU33njjypXfQTo0c+bMcePGoZipsbEJWWtrM6JviqJmz57d +3Nzs8XhQrFZbe3b16nvQglqtFnM6PL59s5n1DUcelXJhmJvxmtg7EenRRqY9/ZY4Fhgj5yLD9Gsa +vm4nUnZQ8AQmYYHLgphmnPS0IyEYODU4z3lwkjpdYTzdZuIpHGf4otR4IkGmw6gLgLs4JkZpXH5T +XlyCClgWE25hpbHA4t6QiuF5gsdmTssRDAlzmvNtgEVDD+BicxZVIzfw3EsoauJrZRTNeyeNRfLo +vdnF2zjBQyQYPT09W7ZsQaGSTqdDIkTT1KpVq1BIdOzocYNeidTrwIEDWVlZKpXGYulWq3UorlKr +NdHRBrvdlpc30eVKM5lMeXl5wgD6g4eeeuqpqqoaggCUq6WlBWW86aabjEbjtGnTKyrKkUoWFxdH +pNF9GfAzGLB3RU5z/G7yXSn+LLDf7H4HFgdYXAiJfb3yuzXMM74hswdelwFORrAl+58s++YNvKC+ +lNjVc7yKt0DghHAw+PVQfDnYgob0UCSlSGtjVz9WP6QzIe/KYOsbzg4NodsZsqa+x2RQLvlCEt75 +w0uB64YYoAurdh+Em+4cUA+cFCThnxu3/XRLBy/Xk46uV1elrv3RigYMGBSHce5unePpJx9ITKX8 +F/INqLSabJhwAeNkctlgiTjOdmjfkWR6Mo/M87xaq+V7NeoKrHeWChQqzZ8/3263p6amoj+R/CQk +JHjvMMWbu7qzsjPdHk9TY2NGhjAlIBIhmqY5DlpamiwWS2xsXGZmFoZhvYZRFJWRka5UqlJSUtxu +149//NIPf/hDZAdtYhiGJMnOzg6TyZydnRVKG4eHyGmO+BmQSMcXSHbM3/ThvifUgxkJKrFfr3wj +gDDP+IbM7tfnADOG35K+6X3zBlWQeK8R5imzX1MiBwOEep1qsIKGjO1CqKzvnhLX1MF+cb4GfY0H +dbCFfGSGTMg1jaAPvZCoi/Z4UJnYKWCyZKk7d+6w/iHqjsxsT79HWSmCOGG8tPl4Zb4rq4Kt9Niy +LLYeHcuqWL4eThZg02h797Yth9MzMnH8jNuT6Z01ibvyeifSyWPNCuaxhfEUhaQlHsfwNI969+fb +zzV02TyeWQDHhEd/uQZgGgmgz9AO814D2Gw40ECQbkYnl/VKlAbpkLc1DFgXQBJSndOnTzc1XZo5 +05Wdna1UKuvr6xMTE3Ec1xt0n37695iYqFmzirZv3ymX0yUlJX/b+HFMtLzkhpV1dftyc3Pb29vN +ZrNCoUDSFRcXd/bs2fLy8ptvvtlmsy1YsADpk9HYvmPHzsWLF6Ll6OgY9Ilgu48KQfURvseZ71mk +SJYhEwfiQGjZQz6vDMTnEH5+wbakeF8QWd+Ggz6B9N0Uzj7tbzP8o8svEW9AkV0Z1ME2TPWNIMN3 +7JEYAYUoXPk3k6EgG/Hjarf7zbf/8jlAg6Az6NM7qI+LxZLdxLTx9BHg8yn10cL8h1sIsJEzUADG +4PKD3UV7/rADuPeuTDnOeaeFzfZmPwqMKzFBuTR1nXdKPe1hjJ+MJ++saoEzGzN54h8Ak4D7J+j1 +kC/DmCYeK8Rz2mREEc+VOc9OL0yW0QoPzyFLvGAN2ccUYEdltLW1NTc3FxYWIAlhWfbkyZN79+5F +EVVmZqZFwLRixa1bt27Nyso2Gts2bdrUabIkp2SUl5965plnysrK3n//z/n5OT09PSaT6fHHH//i +iy8efviRxsbm7m4T2rp8+fJ3333nySefLC0tnTdvXlpa2jDtgFEnqOM+qNPhcM6dhzQrgvjvWTz7 +MPkc2YJGuKsSkfwBnvSPGHwTh8ZgNgNswLHcrY+FX9PYRxh0vuyW4rdLd1nLq1Px8RyOA8Zd4rFc +1BqgBnB69YZngOMZo5UdDww7ZVrykqXj8S6LzN4KrvyjXIcgZrzG+4INbAJw5wQhQbnM8cKbdHOs +HO7gPW4cGMYOzp5MvNwCOdnCAPLpLArdAG3ldBiYeVMqYJkYdLNcqhu3EF3T9d2/f+1ZUkY6bC5X +l9vtMhlZClwec7cFvE8vNTU1O51Og8Gg0Wg8Hs/MmbN6err37v160aKSo0ePlZVVymQKuVy+aNGi +9evXu1yu1tbW1atX33vvvRs2bFAqaRQwIdWsrj53/PiJnJychAQkdUxFRdm5c+fq6uqQzejoaBRj +oYyjvZuGkcD7kaD6nQh2UgMI0OZglx1Esg+fz5EtaISjJZHixAPESF3z8bUZeAOOkcjSlxH7NfVJ +2phtCnEEicpMSNn2yvqNG3fsudQY1W0Gq47ArDywnZDkgjpUNRno9JxDrVbRCdH/QU747sNrddGa +7s62oiJDXEyNjAAGk7MYxWM8zVp6yJibGCQ/mIUgSY5xEPFxrktUvN4lN0yYcGlJSbeHnmgn9TJO +xoGdwWQKtstGxiaxXTypUoCNF6RxXJzONkVXsGLVggkTxgle8thNaZrGqXYt69b2JEcZhKdxFQpF +fv4klUqJQigMwxISErq7LUuXLn3ttV/r9Ya1ax9AqlNRUXHhQv3Ro0eefvrpysrKCRMmIJVCUdHR +o0fvvPPO/fv3z549a+rU6Z2dHUiu3n33XaVSOXPmzAceeOCjjz4qKirasmVLbGxs77Szw4rvvQoY +hkNK5PwrIiVG1u0Aw51wjIeQfcRkbCQ9iVRj9i0MdrIfQmg7pM2gPAwt+2gREbfFzzAi1e1E/BZU +H5dnOh+fHf+Dl+77ntveYTLZLXoMs+DAd4DTKcybRyGJikYSpVGpxkUrruTUGmJef2GN3G0ieNaD +I4mieYyQsRYradB5jCjI6SEJOcc6iRiVx9hDy5Plqgl3x9+6bCbKayNjUEqU3oPJlazJSsYomG6O +UquRNArPSxlkV7+ySqGmPvrj3RZcr2NcBGNVaAxoZVRU1IIFJVarrXcs+NSpU0EYho6//PLLfaMr +CgoKMjIyULyF9KykpIQgiLi4OLQVqRTaevfdd6M1SN6yszMnTy4sLS1duXJlb8Y1a9agTWazWavV +4qKDDwcFibzLGUTy4Gc6F9nk1/KAH3mA2QdLH5TbgZc4mLfhONz/1rdIdr8+h5AxtJYMhOFugTA9 +GfJgGOwIFJGfQAoKPGOYDF8DihcR8pEZrBuBdDsidkLzQZyr3rdE0Mr4BKXwfKxwiQ9SRXNi4IlX +8qCMunq1QSt8C+8n7Kcycd6VQBMKmhY0TgNXvgRivEu9s7gONpcrFq2Jib7s8DeGVSo1+lx2nrg8 +OwWGQf/5ilQqVf8E/V8x1X8Zadjtt9/e92dvYr1eP4g/Q8NjkHTr8uCyDK4xQWUZ7PKL76YhsweY +3u/RGaBvImlEKiKecbA1ISQIJ2MILRnUcggFie9BEbMBbg32GPa7ryN+IA25NfwGDMG4+Dll4In9 +rg+coCwH3hdFSqvCeCUgJus3h9+3gci+IBHHsI9/tiyCBscyImfBEmOf4btKIxEa0g+qj3Ak6luk +TsMDdT21kNTHXbuM5L6TjpMAuVYaarj9/Ja8WF1CQkJC4tuHJFESEhISEmMUSaIkJCQkJMYokkRJ +SEhISIxR/heHXcIrMvkS1gAAAABJRU5ErkJggg== + +------_=_NextPart_001_01CCB66F.F38B15FC--
\ No newline at end of file diff --git a/spec/fixtures/files/track-response-abcmail-oof.email b/spec/fixtures/files/track-response-abcmail-oof.email new file mode 100644 index 000000000..5d1733143 --- /dev/null +++ b/spec/fixtures/files/track-response-abcmail-oof.email @@ -0,0 +1,80 @@ +Delivered-To: mysociety.robin@gmail.com +Received: by 10.216.154.212 with SMTP id h62cs265517wek; + Fri, 30 Dec 2011 02:03:17 -0800 (PST) +Received: by 10.227.208.129 with SMTP id gc1mr47630338wbb.4.1325239396543; + Fri, 30 Dec 2011 02:03:16 -0800 (PST) +Return-Path: <Name.Removed@example.gov.uk> +Received: from wildfire.ukcod.org.uk (wildfire.ukcod.org.uk. [89.238.145.74]) + by mx.google.com with ESMTPS id ei10si9596065wbb.20.2011.12.30.02.03.16 + (version=TLSv1/SSLv3 cipher=OTHER); + Fri, 30 Dec 2011 02:03:16 -0800 (PST) +Received-SPF: neutral (google.com: 89.238.145.74 is neither permitted nor denied by best guess record for domain of Name.Removed@example.gov.uk) client-ip=89.238.145.74; +Authentication-Results: mx.google.com; spf=neutral (google.com: 89.238.145.74 is neither permitted nor denied by best guess record for domain of Name.Removed@example.gov.uk) smtp.mail=Name.Removed@example.gov.uk +Received: from foi by wildfire.ukcod.org.uk with local (Exim 4.72) + (envelope-from <Name.Removed@example.gov.uk>) + id 1RgZIs-0000ME-1T + for team_delivery@whatdotheyknow.com; Fri, 30 Dec 2011 10:03:10 +0000 +Received: from truro.icritical.com ([93.95.13.13]:51540) + by wildfire.ukcod.org.uk with smtp (Exim 4.72) + (envelope-from <Name.Removed@example.gov.uk>) + id 1RgZIq-0000M6-St + for track@whatdotheyknow.com; Fri, 30 Dec 2011 10:03:09 +0000 +Received: (qmail 19136 invoked from network); 30 Dec 2011 10:03:08 -0000 +Received: from localhost (127.0.0.1) + by truro.icritical.com with SMTP; 30 Dec 2011 10:03:08 -0000 +Received: from truro.icritical.com ([127.0.0.1]) + by localhost (truro.icritical.com [127.0.0.1]) (amavisd-new, port 10024) + with SMTP id 19122-01 for <track@whatdotheyknow.com>; + Fri, 30 Dec 2011 10:03:07 +0000 (GMT) +Received: (qmail 19112 invoked by uid 599); 30 Dec 2011 10:03:06 -0000 +Received: from unknown (HELO abcmail.example.gov.uk) (213.185.212.82) + by truro.icritical.com (qpsmtpd/0.28) with ESMTP; Fri, 30 Dec 2011 10:03:06 +0000 +Subject: AUTO: Name Removed is out of the office (returning 03/01/2012) +Auto-Submitted: auto-generated +From: Name.Removed@example.gov.uk +To: track@whatdotheyknow.com +Message-ID: <OFF4E36F18.ED02EFA3-ON80257976.00373794-80257976.00373794@example.gov.uk> +Date: Fri, 30 Dec 2011 10:03:07 +0000 +X-MIMETrack: Serialize by Router on ABCMail/SVR/ABC(Release 8.5.2FP1|November 29, 2010) at + 30/12/2011 10:03:07 +MIME-Version: 1.0 +Content-type: text/plain; charset=US-ASCII +X-Virus-Scanned: by iCritical at truro.icritical.com + + +I am out of the office until 03/01/2012. + +I will be out of the office until 3rd January December 2012. I will deal +with all emails upon my return. If your query is urgent please contact +Colleague Name on colleague.name@example.gov.uk or 01234 567890. + +If you are requesting information under the Freedom of Information Act, the +Environmental Information Regulations or the Data Protection Act, please +forward your enquiry to colleague.name@example.gov.uk The Council +will begin processing your request once it is received by that address. + + +Thanks + +Name + + + + + +Note: This is an automated response to your message "Your WhatDoTheyKnow +email alert" sent on 30/12/2011 06:54:19. + +This is the only notification you will receive while this person is away. + + +This e-mail and any files transmitted with it are confidential and +intended solely for the use of the individual or entity to whom +they are addressed. +If you have received this e-mail in error please notify the +originator of the message. This footer also confirms that this +e-mail message has been scanned for the presence of computer viruses. + +Any views expressed in this message are those of the individual +sender, except where the sender specifies and with authority, +states them to be the views of Organisation Name. diff --git a/spec/fixtures/files/track-response-outlook-oof.email b/spec/fixtures/files/track-response-outlook-oof.email new file mode 100644 index 000000000..ee5a28b15 --- /dev/null +++ b/spec/fixtures/files/track-response-outlook-oof.email @@ -0,0 +1,587 @@ +Delivered-To: mysociety.robin@gmail.com +Received: by 10.152.24.138 with SMTP id u10cs341636laf; + Thu, 8 Dec 2011 02:39:53 -0800 (PST) +Received: by 10.180.103.131 with SMTP id fw3mr4246912wib.57.1323340792168; + Thu, 08 Dec 2011 02:39:52 -0800 (PST) +Return-Path: <peter@kentadvice.co.uk> +Received: from wildfire.ukcod.org.uk (wildfire.ukcod.org.uk. [89.238.145.74]) + by mx.google.com with ESMTPS id ft12si3357577wbb.14.2011.12.08.02.39.51 + (version=TLSv1/SSLv3 cipher=OTHER); + Thu, 08 Dec 2011 02:39:52 -0800 (PST) +Received-SPF: neutral (google.com: 89.238.145.74 is neither permitted nor denied by best guess record for domain of peter@kentadvice.co.uk) client-ip=89.238.145.74; +Authentication-Results: mx.google.com; spf=neutral (google.com: 89.238.145.74 is neither permitted nor denied by best guess record for domain of peter@kentadvice.co.uk) smtp.mail=peter@kentadvice.co.uk +Received: from foi by wildfire.ukcod.org.uk with local (Exim 4.72) + (envelope-from <peter@kentadvice.co.uk>) + id 1RYbOC-00034X-Vm + for team_delivery@whatdotheyknow.com; Thu, 08 Dec 2011 10:39:45 +0000 +Received: from mail-ey0-f173.google.com ([209.85.215.173]:38997) + by wildfire.ukcod.org.uk with esmtp (Exim 4.72) + (envelope-from <peter@kentadvice.co.uk>) + id 1RYbOC-00034L-GF + for track@whatdotheyknow.com; Thu, 08 Dec 2011 10:39:44 +0000 +Received: by eaai10 with SMTP id i10so1168752eaa.32 + for <track@whatdotheyknow.com>; Thu, 08 Dec 2011 02:39:33 -0800 (PST) +Received: by 10.213.21.148 with SMTP id j20mr131258ebb.87.1323340773446; + Thu, 08 Dec 2011 02:39:33 -0800 (PST) +Received: from PRWin7 (cpc2-tilb7-2-0-cust982.basl.cable.virginmedia.com. [94.168.103.215]) + by mx.google.com with ESMTPS id 49sm16411187eec.1.2011.12.08.02.39.31 + (version=TLSv1/SSLv3 cipher=OTHER); + Thu, 08 Dec 2011 02:39:32 -0800 (PST) +From: "Name Removed" <name-removed@example.co.uk> +To: <track@whatdotheyknow.com> +Subject: Out of Office reply +Date: Thu, 8 Dec 2011 10:39:24 -0000 +Message-ID: <00ab01ccb595$aada0070$008e0150$@co.uk> +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="----=_NextPart_000_00AC_01CCB595.AADA0070" +X-Mailer: Microsoft Office Outlook 12.0 +Thread-Index: Acy1laTpNPAp9QuHRu2X59T70yzpQw== +Content-Language: en-gb + +This is a multi-part message in MIME format. + +------=_NextPart_000_00AC_01CCB595.AADA0070 +Content-Type: text/plain; + charset="US-ASCII" +Content-Transfer-Encoding: 7bit + +Thank you for your message. I am currently out of the office, with [limited] +[no] access to e-mail. + +I will be returning on [day, date]. + +If you need assistance before then, you may reach me at [phone number]. +For urgent issues, please contact [name] at [e-mail address] or [telephone +number]. + +[Signature] + +[Optional: Type your favorite quotation or saying here along with the author +or source] + +------=_NextPart_000_00AC_01CCB595.AADA0070 +Content-Type: text/html; + charset="US-ASCII" +Content-Transfer-Encoding: quoted-printable + +<html xmlns:v=3D"urn:schemas-microsoft-com:vml" = +xmlns:o=3D"urn:schemas-microsoft-com:office:office" = +xmlns:w=3D"urn:schemas-microsoft-com:office:word" = +xmlns:x=3D"urn:schemas-microsoft-com:office:excel" = +xmlns:p=3D"urn:schemas-microsoft-com:office:powerpoint" = +xmlns:a=3D"urn:schemas-microsoft-com:office:access" = +xmlns:dt=3D"uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" = +xmlns:s=3D"uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" = +xmlns:rs=3D"urn:schemas-microsoft-com:rowset" xmlns:z=3D"#RowsetSchema" = +xmlns:b=3D"urn:schemas-microsoft-com:office:publisher" = +xmlns:ss=3D"urn:schemas-microsoft-com:office:spreadsheet" = +xmlns:c=3D"urn:schemas-microsoft-com:office:component:spreadsheet" = +xmlns:oa=3D"urn:schemas-microsoft-com:office:activation" = +xmlns:html=3D"http://www.w3.org/TR/REC-html40" = +xmlns:q=3D"http://schemas.xmlsoap.org/soap/envelope/" xmlns:D=3D"DAV:" = +xmlns:x2=3D"http://schemas.microsoft.com/office/excel/2003/xml" = +xmlns:ois=3D"http://schemas.microsoft.com/sharepoint/soap/ois/" = +xmlns:dir=3D"http://schemas.microsoft.com/sharepoint/soap/directory/" = +xmlns:ds=3D"http://www.w3.org/2000/09/xmldsig#" = +xmlns:dsp=3D"http://schemas.microsoft.com/sharepoint/dsp" = +xmlns:udc=3D"http://schemas.microsoft.com/data/udc" = +xmlns:xsd=3D"http://www.w3.org/2001/XMLSchema" = +xmlns:sub=3D"http://schemas.microsoft.com/sharepoint/soap/2002/1/alerts/"= + xmlns:ec=3D"http://www.w3.org/2001/04/xmlenc#" = +xmlns:sp=3D"http://schemas.microsoft.com/sharepoint/" = +xmlns:sps=3D"http://schemas.microsoft.com/sharepoint/soap/" = +xmlns:xsi=3D"http://www.w3.org/2001/XMLSchema-instance" = +xmlns:udcxf=3D"http://schemas.microsoft.com/data/udc/xmlfile" = +xmlns:wf=3D"http://schemas.microsoft.com/sharepoint/soap/workflow/" = +xmlns:mver=3D"http://schemas.openxmlformats.org/markup-compatibility/2006= +" xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml" = +xmlns:mrels=3D"http://schemas.openxmlformats.org/package/2006/relationshi= +ps" = +xmlns:ex12t=3D"http://schemas.microsoft.com/exchange/services/2006/types"= + = +xmlns:ex12m=3D"http://schemas.microsoft.com/exchange/services/2006/messag= +es" xmlns=3D"http://www.w3.org/TR/REC-html40"> + +<head> +<META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; = +charset=3Dus-ascii"> + + +<meta name=3DProgId content=3DWord.Document> +<meta name=3DGenerator content=3D"Microsoft Word 12"> +<meta name=3DOriginator content=3D"Microsoft Word 12"> +<link rel=3DFile-List href=3D"cid:filelist.xml@01C895B2.35BC4F70"> +<!--[if gte mso 9]><xml> + <o:OfficeDocumentSettings> + <o:AllowPNG/> + <o:TargetScreenSize>1024x768</o:TargetScreenSize> + </o:OfficeDocumentSettings> +</xml><![endif]--> +<link rel=3DthemeData href=3D"~~themedata~~"> +<link rel=3DcolorSchemeMapping href=3D"~~colorschememapping~~"> +<!--[if gte mso 9]><xml> + <w:WordDocument> + <w:SpellingState>Clean</w:SpellingState> + <w:TrackMoves/> + <w:TrackFormatting/> + <w:EnvelopeVis/> + <w:ValidateAgainstSchemas/> + <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> + <w:IgnoreMixedContent>false</w:IgnoreMixedContent> + <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> + <w:DoNotPromoteQF/> + <w:LidThemeOther>EN-US</w:LidThemeOther> + <w:LidThemeAsian>X-NONE</w:LidThemeAsian> + <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> + <w:Compatibility> + <w:DoNotExpandShiftReturn/> + <w:BreakWrappedTables/> + <w:SnapToGridInCell/> + <w:WrapTextWithPunct/> + <w:UseAsianBreakRules/> + <w:DontGrowAutofit/> + <w:SplitPgBreakAndParaMark/> + <w:DontVertAlignCellWithSp/> + <w:DontBreakConstrainedForcedTables/> + <w:DontVertAlignInTxbx/> + <w:Word11KerningPairs/> + <w:CachedColBalance/> + </w:Compatibility> + <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> + <m:mathPr> + <m:mathFont m:val=3D"Cambria Math"/> + <m:brkBin m:val=3D"before"/> + <m:brkBinSub m:val=3D"--"/> + <m:smallFrac m:val=3D"off"/> + <m:dispDef/> + <m:lMargin m:val=3D"0"/> + <m:rMargin m:val=3D"0"/> + <m:defJc m:val=3D"centerGroup"/> + <m:wrapIndent m:val=3D"1440"/> + <m:intLim m:val=3D"subSup"/> + <m:naryLim m:val=3D"undOvr"/> + </m:mathPr></w:WordDocument> +</xml><![endif]--><!--[if gte mso 9]><xml> + <w:LatentStyles DefLockedState=3D"false" DefUnhideWhenUsed=3D"true"=20 + DefSemiHidden=3D"true" DefQFormat=3D"false" DefPriority=3D"99"=20 + LatentStyleCount=3D"267"> + <w:LsdException Locked=3D"false" Priority=3D"0" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Normal"/> + <w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"heading 1"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" = +Name=3D"heading 2"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" = +Name=3D"heading 3"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" = +Name=3D"heading 4"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" = +Name=3D"heading 5"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" = +Name=3D"heading 6"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" = +Name=3D"heading 7"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" = +Name=3D"heading 8"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" = +Name=3D"heading 9"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 1"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 2"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 3"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 4"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 5"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 6"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 7"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 8"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 9"/> + <w:LsdException Locked=3D"false" Priority=3D"35" QFormat=3D"true" = +Name=3D"caption"/> + <w:LsdException Locked=3D"false" Priority=3D"10" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Title"/> + <w:LsdException Locked=3D"false" Priority=3D"1" Name=3D"Default = +Paragraph Font"/> + <w:LsdException Locked=3D"false" Priority=3D"11" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtitle"/> + <w:LsdException Locked=3D"false" Priority=3D"22" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Strong"/> + <w:LsdException Locked=3D"false" Priority=3D"20" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Emphasis"/> + <w:LsdException Locked=3D"false" Priority=3D"59" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Table Grid"/> + <w:LsdException Locked=3D"false" UnhideWhenUsed=3D"false" = +Name=3D"Placeholder Text"/> + <w:LsdException Locked=3D"false" Priority=3D"1" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"No Spacing"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Shading"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light List"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Grid"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 1"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 2"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Dark List"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful List"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 1"/> + <w:LsdException Locked=3D"false" UnhideWhenUsed=3D"false" = +Name=3D"Revision"/> + <w:LsdException Locked=3D"false" Priority=3D"34" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"List Paragraph"/> + <w:LsdException Locked=3D"false" Priority=3D"29" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Quote"/> + <w:LsdException Locked=3D"false" Priority=3D"30" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Quote"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"19" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtle Emphasis"/> + <w:LsdException Locked=3D"false" Priority=3D"21" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Emphasis"/> + <w:LsdException Locked=3D"false" Priority=3D"31" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtle Reference"/> + <w:LsdException Locked=3D"false" Priority=3D"32" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense = +Reference"/> + <w:LsdException Locked=3D"false" Priority=3D"33" SemiHidden=3D"false"=20 + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Book Title"/> + <w:LsdException Locked=3D"false" Priority=3D"37" = +Name=3D"Bibliography"/> + <w:LsdException Locked=3D"false" Priority=3D"39" QFormat=3D"true" = +Name=3D"TOC Heading"/> + </w:LatentStyles> +</xml><![endif]--> +<style> +<!-- + /* Font Definitions */ + @font-face + {font-family:"Cambria Math"; + panose-1:2 4 5 3 5 4 6 3 2 4; + mso-font-charset:1; + mso-generic-font-family:roman; + mso-font-format:other; + mso-font-pitch:variable; + mso-font-signature:0 0 0 0 0 0;} +@font-face + {font-family:Calibri; + panose-1:2 15 5 2 2 2 4 3 2 4; + mso-font-charset:0; + mso-generic-font-family:swiss; + mso-font-pitch:variable; + mso-font-signature:-1610611985 1073750139 0 0 159 0;} + /* Style Definitions */ + p.MsoNormal, li.MsoNormal, div.MsoNormal + {mso-style-unhide:no; + mso-style-qformat:yes; + mso-style-parent:""; + margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:11.0pt; + font-family:"Calibri","sans-serif"; + mso-fareast-font-family:Calibri; + mso-fareast-theme-font:minor-latin; + mso-bidi-font-family:"Times New Roman";} +a:link, span.MsoHyperlink + {mso-style-noshow:yes; + mso-style-priority:99; + color:blue; + text-decoration:underline; + text-underline:single;} +a:visited, span.MsoHyperlinkFollowed + {mso-style-noshow:yes; + mso-style-priority:99; + color:purple; + text-decoration:underline; + text-underline:single;} +span.EmailStyle17 + {mso-style-type:personal; + mso-style-noshow:yes; + mso-style-unhide:no; + font-family:"Calibri","sans-serif"; + mso-ascii-font-family:Calibri; + mso-hansi-font-family:Calibri; + color:windowtext;} +span.EmailStyle18 + {mso-style-type:personal-reply; + mso-style-noshow:yes; + mso-style-unhide:no; + mso-ansi-font-size:11.0pt; + mso-bidi-font-size:11.0pt; + font-family:"Calibri","sans-serif"; + mso-ascii-font-family:Calibri; + mso-ascii-theme-font:minor-latin; + mso-hansi-font-family:Calibri; + mso-hansi-theme-font:minor-latin; + mso-bidi-font-family:"Times New Roman"; + mso-bidi-theme-font:minor-bidi; + color:#5F497A; + mso-themecolor:accent4; + mso-themeshade:191;} +.MsoChpDefault + {mso-style-type:export-only; + mso-default-props:yes; + font-size:10.0pt; + mso-ansi-font-size:10.0pt; + mso-bidi-font-size:10.0pt;} +@page Section1 + {size:8.5in 11.0in; + margin:1.0in 1.0in 1.0in 1.0in; + mso-header-margin:.5in; + mso-footer-margin:.5in; + mso-paper-source:0;} +div.Section1 + {page:Section1;} +--> +</style> +<!--[if gte mso 10]> +<style> + /* Style Definitions */=20 + table.MsoNormalTable + {mso-style-name:"Table Normal"; + mso-tstyle-rowband-size:0; + mso-tstyle-colband-size:0; + mso-style-noshow:yes; + mso-style-priority:99; + mso-style-qformat:yes; + mso-style-parent:""; + mso-padding-alt:0in 5.4pt 0in 5.4pt; + mso-para-margin:0in; + mso-para-margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:10.0pt; + font-family:"Times New Roman","serif";} +</style> +<![endif]--><!--[if gte mso 9]><xml> + <o:shapedefaults v:ext=3D"edit" spidmax=3D"1026" /> +</xml><![endif]--><!--[if gte mso 9]><xml> + <o:shapelayout v:ext=3D"edit"> + <o:idmap v:ext=3D"edit" data=3D"1" /> + </o:shapelayout></xml><![endif]--> +</head> + +<body lang=3DEN-US link=3Dblue vlink=3Dpurple = +style=3D'tab-interval:.5in'> + +<div class=3DSection1> + +<p class=3DMsoNormal>Thank you for your message. I am currently out of = +the +office, with [limited] [no] access to e-mail.<o:p></o:p></p> + +<p class=3DMsoNormal><o:p> </o:p></p> + +<p class=3DMsoNormal>I will be returning on [day, date].<o:p></o:p></p> + +<p class=3DMsoNormal><o:p> </o:p></p> + +<p class=3DMsoNormal>If you need assistance before then, you may reach = +me at +[phone number].<o:p></o:p></p> + +<p class=3DMsoNormal>For urgent issues, please contact [name] at [e-mail = +address] +or [telephone number].<o:p></o:p></p> + +<p class=3DMsoNormal><o:p> </o:p></p> + +<p class=3DMsoNormal>[Signature]<o:p></o:p></p> + +<p class=3DMsoNormal><o:p> </o:p></p> + +<p class=3DMsoNormal><i style=3D'mso-bidi-font-style:normal'>[Optional: = +Type your +favorite quotation or saying here along with the author or = +source]<o:p></o:p></i></p> + +</div> + +</body> + +</html> + +------=_NextPart_000_00AC_01CCB595.AADA0070-- + diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb index ed31b7c5c..f514d6546 100644 --- a/spec/models/incoming_message_spec.rb +++ b/spec/models/incoming_message_spec.rb @@ -9,6 +9,10 @@ describe IncomingMessage, " when dealing with incoming mail" do load_raw_emails_data(raw_emails) end + after(:all) do + ActionMailer::Base.deliveries.clear + end + it "should return the mail Date header date for sent at" do @im.sent_at.should == @im.mail.date end @@ -27,6 +31,31 @@ describe IncomingMessage, " when dealing with incoming mail" do end end + it "should ensure cached body text has been parsed correctly" do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('quoted-subject-iso8859-1.email', ir.incoming_email) + message = ir.incoming_messages[1] + message.get_main_body_text_unfolded.should_not include("Email has no body") + end + + it "should correctly convert HTML even when there's a meta tag asserting that it is iso-8859-1 which would normally confuse elinks" do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('quoted-subject-iso8859-1.email', ir.incoming_email) + message = ir.incoming_messages[1] + message.parse_raw_email! + message.get_main_body_text_part.charset.should == "iso-8859-1" + message.get_main_body_text_internal.should include("política") + end + + it "should unquote RFC 2047 headers" do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('quoted-subject-iso8859-1.email', ir.incoming_email) + message = ir.incoming_messages[1] + message.mail_from.should == "Coordenação de Relacionamento, Pesquisa e Informação/CEDI" + message.subject.should == "Câmara Responde: Banco de ideias" + end + + it "should fold multiline sections" do { "foo\n--------\nconfidential" => "foo\nFOLDED_QUOTED_SECTION\n", # basic test @@ -102,16 +131,15 @@ describe IncomingMessage, " folding quoted parts of emails" do end describe IncomingMessage, " checking validity to reply to" do - def test_email(result, email, return_path, autosubmitted) + def test_email(result, email, return_path, autosubmitted = nil) @address = mock(TMail::Address) @address.stub!(:spec).and_return(email) @return_path = mock(TMail::ReturnPathHeader) @return_path.stub!(:addr).and_return(return_path) - - @autosubmitted = mock(TMail::KeywordsHeader) - @autosubmitted.stub!(:keys).and_return(autosubmitted) - + if !autosubmitted.nil? + @autosubmitted = TMail::UnstructuredHeader.new("auto-submitted", autosubmitted) + end @mail = mock(TMail::Mail) @mail.stub!(:from_addrs).and_return( [ @address ] ) @mail.stub!(:[]).with("return-path").and_return(@return_path) @@ -123,39 +151,39 @@ describe IncomingMessage, " checking validity to reply to" do end it "says a valid email is fine" do - test_email(true, "team@mysociety.org", nil, []) + test_email(true, "team@mysociety.org", nil) end it "says postmaster email is bad" do - test_email(false, "postmaster@mysociety.org", nil, []) + test_email(false, "postmaster@mysociety.org", nil) end it "says Mailer-Daemon email is bad" do - test_email(false, "Mailer-Daemon@mysociety.org", nil, []) + test_email(false, "Mailer-Daemon@mysociety.org", nil) end it "says case mangled MaIler-DaemOn email is bad" do - test_email(false, "MaIler-DaemOn@mysociety.org", nil, []) + test_email(false, "MaIler-DaemOn@mysociety.org", nil) end it "says Auto_Reply email is bad" do - test_email(false, "Auto_Reply@mysociety.org", nil, []) + test_email(false, "Auto_Reply@mysociety.org", nil) end it "says DoNotReply email is bad" do - test_email(false, "DoNotReply@tube.tfl.gov.uk", nil, []) + test_email(false, "DoNotReply@tube.tfl.gov.uk", nil) end it "says a filled-out return-path is fine" do - test_email(true, "team@mysociety.org", "Return-path: <foo@baz.com>", []) + test_email(true, "team@mysociety.org", "Return-path: <foo@baz.com>") end it "says an empty return-path is bad" do - test_email(false, "team@mysociety.org", "<>", []) + test_email(false, "team@mysociety.org", "<>") end it "says an auto-submitted keyword is bad" do - test_email(false, "team@mysociety.org", nil, ["auto-replied"]) + test_email(false, "team@mysociety.org", nil, "auto-replied") end end diff --git a/spec/script/handle-mail-replies_spec.rb b/spec/script/handle-mail-replies_spec.rb index eae0b516b..8ed83b31f 100644 --- a/spec/script/handle-mail-replies_spec.rb +++ b/spec/script/handle-mail-replies_spec.rb @@ -54,5 +54,15 @@ describe "When filtering" do r = mail_reply_test("track-response-messageclass-oof.email") r.status.should == 2 end + + it "should detect an Outlook(?)-style out-of-office" do + r = mail_reply_test("track-response-outlook-oof.email") + r.status.should == 2 + end + + it "should detect an ABCMail-style out-of-office" do + r = mail_reply_test("track-response-abcmail-oof.email") + r.status.should == 2 + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5c5cd9a7f..29ce6bca5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -78,7 +78,6 @@ def load_file_fixture(file_name) end def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst = true) - parse_all_incoming_messages if dropfirst begin ActsAsXapian.readable_init @@ -86,7 +85,9 @@ def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst = rescue RuntimeError end ActsAsXapian.writable_init + 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. @@ -96,7 +97,7 @@ end def update_xapian_index verbose = false - ActsAsXapian.update_index(flush_to_disk=true, verbose) + ActsAsXapian.update_index(flush_to_disk=false, verbose) end # Validate an entire HTML page 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 fb6a08979..59b3777da 100644 --- a/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb +++ b/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb @@ -219,6 +219,7 @@ module ActsAsXapian # for indexing @@writable_db = Xapian::flint_open(full_path, Xapian::DB_CREATE_OR_OPEN) + @@enquire = Xapian::Enquire.new(@@writable_db) @@term_generator = Xapian::TermGenerator.new() @@term_generator.set_flags(Xapian::TermGenerator::FLAG_SPELLING, 0) @@term_generator.database = @@writable_db @@ -524,8 +525,6 @@ module ActsAsXapian # If there are no models in the queue, then nothing to do return if model_classes.size == 0 - ActsAsXapian.writable_init - # Abort if full rebuild is going on new_path = ActsAsXapian.db_path + ".new" if File.exist?(new_path) @@ -533,6 +532,7 @@ module ActsAsXapian end ids_to_refresh = ActsAsXapianJob.find(:all).map() { |i| i.id } + ActsAsXapian.writable_init for id in ids_to_refresh job = nil begin @@ -548,6 +548,7 @@ module ActsAsXapian next end STDOUT.puts("ActsAsXapian.update_index #{job.action} #{job.model} #{job.model_id.to_s} #{Time.now.to_s}") if verbose + begin if job.action == 'update' # XXX Index functions may reference other models, so we could eager load here too? @@ -566,47 +567,57 @@ module ActsAsXapian job.action = 'destroy' retry end - job.destroy - if flush ActsAsXapian.writable_db.flush end + job.destroy end rescue => detail # print any error, and carry on so other things are indexed 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 + ActsAsXapian.writable_db.flush ActsAsXapian.writable_db.close end + def ActsAsXapian._is_xapian_db(path) + return File.exist?(File.join(temp_path, "iamflint")) or File.exist?(File.join(temp_path, "iamchert")) + 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. # # Incremental update_index calls above are suspended while this rebuild # happens (i.e. while the .new database is there) - any index update jobs # are left in the database, and will run after the rebuild has finished. + def ActsAsXapian.rebuild_index(model_classes, verbose = false, terms = true, values = true, texts = true, safe_rebuild = true) #raise "when rebuilding all, please call as first and only thing done in process / task" if not ActsAsXapian.writable_db.nil? - prepare_environment - - # Delete any existing .new database, and open a new one + + update_existing = !(terms == true && values == true && texts == true) + # Delete any existing .new database, and open a new one which is a copy of the current one new_path = ActsAsXapian.db_path + ".new" + old_path = ActsAsXapian.db_path if File.exist?(new_path) - raise "found existing " + new_path + " which is not Xapian flint database, please delete for me" if not File.exist?(File.join(new_path, "iamflint")) + raise "found existing " + new_path + " which is not Xapian flint database, please delete for me" if not ActsAsXapian._is_xapian_db(new_path) FileUtils.rm_r(new_path) end - + if update_existing + FileUtils.cp_r(old_path, new_path) + end + ActsAsXapian.writable_init + ActsAsXapian.writable_db.close # just to make an empty one to read # Index everything if safe_rebuild _rebuild_index_safely(model_classes, verbose, terms, values, texts) else + @@db_path = ActsAsXapian.db_path + ".new" + ActsAsXapian.writable_init # Save time by running the indexing in one go and in-process - ActsAsXapian.writable_init(".new") for model_class in model_classes STDOUT.puts("ActsAsXapian.rebuild_index: Rebuilding #{model_class.to_s}") if verbose model_class.find(:all).each do |model| @@ -614,16 +625,14 @@ module ActsAsXapian model.xapian_index(terms, values, texts) end end - # make sure everything is written and close ActsAsXapian.writable_db.flush ActsAsXapian.writable_db.close end # Rename into place - old_path = ActsAsXapian.db_path - temp_path = ActsAsXapian.db_path + ".tmp" + temp_path = old_path + ".tmp" if File.exist?(temp_path) - raise "temporary database found " + temp_path + " which is not Xapian flint database, please delete for me" if not File.exist?(File.join(temp_path, "iamflint")) + raise "temporary database found " + temp_path + " which is not Xapian flint database, please delete for me" if not ActsAsXapian._is_xapian_db(temp_path) FileUtils.rm_r(temp_path) end if File.exist?(old_path) @@ -633,12 +642,13 @@ module ActsAsXapian # Delete old database if File.exist?(temp_path) - raise "old database now at " + temp_path + " is not Xapian flint database, please delete for me" if not File.exist?(File.join(temp_path, "iamflint")) + raise "old database now at " + temp_path + " is not Xapian flint database, please delete for me" if not ActsAsXapian._is_xapian_db(temp_path) FileUtils.rm_r(temp_path) end # You'll want to restart your FastCGI or Mongrel processes after this, # so they get the new db + @@db_path = old_path end def ActsAsXapian._rebuild_index_safely(model_classes, verbose, terms, values, texts) @@ -658,18 +668,18 @@ module ActsAsXapian # database connection doesn't survive a fork, rebuild it ActiveRecord::Base.connection.reconnect! else + # fully reopen the database each time (with a new object) # (so doc ids and so on aren't preserved across the fork) - ActsAsXapian.writable_init(".new") + @@db_path = ActsAsXapian.db_path + ".new" + ActsAsXapian.writable_init STDOUT.puts("ActsAsXapian.rebuild_index: New batch. #{model_class.to_s} from #{i} to #{i + batch_size} of #{model_class_count} pid #{Process.pid.to_s}") if verbose model_class.find(:all, :limit => batch_size, :offset => i, :order => :id).each do |model| STDOUT.puts("ActsAsXapian.rebuild_index #{model_class} #{model.id}") if verbose model.xapian_index(terms, values, texts) end - # make sure everything is written ActsAsXapian.writable_db.flush - # close database - 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) @@ -739,7 +749,7 @@ module ActsAsXapian # Store record in the Xapian database def xapian_index(terms = true, values = true, texts = true) - # if we have a conditional function for indexing, call it and destory object if failed + # if we have a conditional function for indexing, call it and destroy object if failed if self.class.xapian_options.include?(:if) if_value = xapian_value(self.class.xapian_options[:if], :boolean) if not if_value @@ -748,13 +758,6 @@ module ActsAsXapian end end - if self.class.to_s == "PublicBody" and self.url_name == "tgq" - -#require 'ruby-debug' -#debugger - end - # otherwise (re)write the Xapian record for the object - ActsAsXapian.readable_init existing_query = Xapian::Query.new("I" + self.xapian_document_term) ActsAsXapian.enquire.query = existing_query match = ActsAsXapian.enquire.mset(0,1,1).matches[0] @@ -767,8 +770,8 @@ module ActsAsXapian doc.add_term("M" + self.class.to_s) doc.add_term("I" + doc.data) end - ActsAsXapian.term_generator.document = doc - # work out what to index. XXX for now, this is only selective on "terms". + # work out what to index + # 1. Which terms to index? We allow the user to specify particular ones terms_to_index = [] drop_all_terms = false if terms and self.xapian_options[:terms] @@ -782,16 +785,18 @@ module ActsAsXapian drop_all_terms = true end end + # 2. Texts to index? Currently, it's all or nothing texts_to_index = [] if texts and self.xapian_options[:texts] texts_to_index = self.xapian_options[:texts] end + # 3. Values to index? Currently, it's all or nothing values_to_index = [] if values and self.xapian_options[:values] values_to_index = self.xapian_options[:values] end - # clear any existing values that we might want to replace + # clear any existing data that we might want to replace if drop_all_terms && texts # as an optimisation, if we're reindexing all of both, we remove everything doc.clear_terms @@ -801,17 +806,17 @@ module ActsAsXapian term_prefixes_to_index = terms_to_index.map {|x| x[1]} for existing_term in doc.terms first_letter = existing_term.term[0...1] - if !"MI".include?(first_letter) - if first_letter.match("^[A-Z]+") && terms_to_index.include?(first_letter) - doc.remove_term(existing_term.term) + if !"MI".include?(first_letter) # it's not one of the reserved value + if first_letter.match("^[A-Z]+") # it's a "value" (rather than indexed text) + if term_prefixes_to_index.include?(first_letter) # it's a value that we've been asked to index + doc.remove_term(existing_term.term) + end elsif texts - doc.remove_term(existing_term.term) + doc.remove_term(existing_term.term) # it's text and we've been asked to reindex it end end end end - # for now, we always clear values - doc.clear_values for term in terms_to_index value = xapian_value(term[0]) @@ -823,15 +828,20 @@ module ActsAsXapian doc.add_term(term[1] + value) end end - # values - for value in values_to_index - doc.add_value(value[1], xapian_value(value[0], value[3])) + + if values + doc.clear_values + for value in values_to_index + doc.add_value(value[1], xapian_value(value[0], value[3])) + end end - # texts - 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) + if texts + ActsAsXapian.term_generator.document = doc + 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) + end end ActsAsXapian.writable_db.replace_document("I" + doc.data, doc) diff --git a/vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake b/vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake index d18cd07d5..470016420 100644 --- a/vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake +++ b/vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake @@ -30,12 +30,23 @@ namespace :xapian do desc 'Completely rebuilds Xapian search index (must specify all models)' task :rebuild_index => :environment do + def coerce_arg(arg, default) + if arg == "false" + return false + elsif arg == "true" + return true + elsif arg.nil? + return default + else + return arg + end + end raise "specify ALL your models with models=\"ModelName1 ModelName2\" as parameter" if ENV['models'].nil? ActsAsXapian.rebuild_index(ENV['models'].split(" ").map{|m| m.constantize}, - ENV['verbose'] ? true : false, - ENV['terms'] == "false" ? false : ENV['terms'], - ENV['values'] == "false" ? false : ENV['values'], - ENV['texts'] == "false" ? false : true) + coerce_arg(ENV['verbose'], false), + coerce_arg(ENV['terms'], true), + coerce_arg(ENV['values'], true), + coerce_arg(ENV['texts'], true)) end # Parameters - are models, query, offset, limit, sort_by_prefix, |