diff options
-rw-r--r-- | app/controllers/admin_censor_rule_controller.rb | 72 | ||||
-rw-r--r-- | app/controllers/request_controller.rb | 12 | ||||
-rw-r--r-- | app/models/censor_rule.rb | 33 | ||||
-rw-r--r-- | app/models/incoming_message.rb | 25 | ||||
-rw-r--r-- | app/models/info_request.rb | 3 | ||||
-rw-r--r-- | app/views/admin_censor_rule/_form.rhtml | 14 | ||||
-rw-r--r-- | app/views/admin_censor_rule/_show.rhtml | 27 | ||||
-rw-r--r-- | app/views/admin_censor_rule/edit.rhtml | 16 | ||||
-rw-r--r-- | app/views/admin_censor_rule/new.rhtml | 9 | ||||
-rw-r--r-- | app/views/admin_request/show.rhtml | 2 | ||||
-rw-r--r-- | config/routes.rb | 3 | ||||
-rw-r--r-- | db/migrate/068_add_censor_table.rb | 29 | ||||
-rw-r--r-- | db/schema.rb | 14 | ||||
-rw-r--r-- | todo.txt | 13 |
14 files changed, 254 insertions, 18 deletions
diff --git a/app/controllers/admin_censor_rule_controller.rb b/app/controllers/admin_censor_rule_controller.rb new file mode 100644 index 000000000..36c700cbb --- /dev/null +++ b/app/controllers/admin_censor_rule_controller.rb @@ -0,0 +1,72 @@ +# app/controllers/admin_censor_rule_controller.rb: +# For modifying requests. +# +# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. +# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# +# $Id: admin_censor_rule_controller.rb,v 1.1 2008-10-27 18:18:30 francis Exp $ + +class AdminCensorRuleController < ApplicationController + layout "admin" + before_filter :assign_http_auth_user + + def new + @info_request = InfoRequest.find(params[:info_request_id]) + end + + def create + params[:censor_rule][:last_edit_editor] = admin_http_auth_user() + @censor_rule = CensorRule.new(params[:censor_rule]) + if @censor_rule.save + expire_for_request(@censor_rule.info_request) + flash[:notice] = 'CensorRule was successfully created.' + redirect_to admin_url('request/show/' + @censor_rule.info_request.id.to_s) + else + render :action => 'new' + end + end + + def edit + @censor_rule = CensorRule.find(params[:id]) + end + + def update + params[:censor_rule][:last_edit_editor] = admin_http_auth_user() + @censor_rule = CensorRule.find(params[:id]) + if @censor_rule.update_attributes(params[:censor_rule]) + expire_for_request(@censor_rule.info_request) + flash[:notice] = 'CensorRule was successfully updated.' + redirect_to admin_url('request/show/' + @censor_rule.info_request.id.to_s) + else + render :action => 'edit' + end + end + + def destroy + censor_rule = CensorRule.find(params[:censor_rule_id]) + info_request = censor_rule.info_request + + censor_rule.destroy + expire_for_request(info_request) + + flash[:notice] = "CensorRule was successfully destroyed." + + redirect_to admin_url('request/show/' + info_request.id.to_s) + end + + + def expire_for_request(info_request) + # clear out cached entries + for incoming_message in info_request.incoming_messages + for attachment in incoming_message.get_attachments_for_display + expire_page :controller => 'request', :action => "get_attachment", :id => info_request.id, + :incoming_message_id => incoming_message.id, + :part => attachment.url_part_number, :file_name => attachment.display_filename + end + end + end + + private + +end + diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index afb980c87..bb0d7f9bd 100644 --- a/app/controllers/request_controller.rb +++ b/app/controllers/request_controller.rb @@ -4,7 +4,7 @@ # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: francis@mysociety.org; WWW: http://www.mysociety.org/ # -# $Id: request_controller.rb,v 1.129 2008-10-25 12:01:06 francis Exp $ +# $Id: request_controller.rb,v 1.130 2008-10-27 18:18:30 francis Exp $ class RequestController < ApplicationController @@ -406,6 +406,10 @@ class RequestController < ApplicationController get_attachment_internal html = @attachment.body_as_html + # Mask any more emails that have now been exposed (e.g. in PDFs - ones in + # .doc will have been got in get_attachment_internal below) + html = @incoming_message.binary_mask_stuff(html) + view_html_stylesheet = render_to_string :partial => "request/view_html_stylesheet" html.sub!(/<head>/i, "<head>" + view_html_stylesheet) html.sub!(/<body[^>]*>/i, '<body><prefix-here><div id="wrapper"><div id="view_html_content">' + view_html_stylesheet) @@ -414,10 +418,6 @@ class RequestController < ApplicationController view_html_prefix = render_to_string :partial => "request/view_html_prefix" html.sub!("<prefix-here>", view_html_prefix) - # Mask any more emails that have now been exposed (e.g. in PDFs - ones in - # .doc will have been got in get_attachment_internal below) - html = IncomingMessage.binary_mask_all_emails(html) - response.content_type = 'text/html' render :text => html end @@ -436,7 +436,7 @@ class RequestController < ApplicationController # Prevent spam to magic request address. # XXX Bit dodgy modifying a binary like this but hey. Maybe only do for some mime types? - @attachment.body = IncomingMessage.binary_mask_all_emails(@attachment.body) + @attachment.body = @incoming_message.binary_mask_stuff(@attachment.body) end # FOI officers can upload a response diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb new file mode 100644 index 000000000..acc4349e6 --- /dev/null +++ b/app/models/censor_rule.rb @@ -0,0 +1,33 @@ +# models/censor_rule.rb: +# Stores alterations to remove specific data from requests. +# +# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. +# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# +# $Id: censor_rule.rb,v 1.1 2008-10-27 18:18:30 francis Exp $ + +class CensorRule < ActiveRecord::Base + belongs_to :info_request + belongs_to :user + belongs_to :public_body + + def apply_to_text(text) + text.gsub!(self.text, self.replacement) + return text + end + def apply_to_binary(binary) + replacement = self.text.gsub(/./, 'x') + binary.gsub!(self.text, replacement) + return binary + end + + + def validate + if self.info_request.nil? && self.user.nil? && self.public_body.nil? + errors.add("Censor must apply to an info request a user or a body; ") + end + end +end + + + diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb index a6ca692c8..eb63ee6fa 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -19,7 +19,7 @@ # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: francis@mysociety.org; WWW: http://www.mysociety.org/ # -# $Id: incoming_message.rb,v 1.154 2008-10-17 20:43:25 francis Exp $ +# $Id: incoming_message.rb,v 1.155 2008-10-27 18:18:30 francis Exp $ # TODO # Move some of the (e.g. quoting) functions here into rblib, as they feel @@ -288,7 +288,8 @@ class IncomingMessage < ActiveRecord::Base end # Replaces all email addresses in (possibly binary data) with equal length alternative ones. - def IncomingMessage.binary_mask_all_emails(text) + # Also replaces censor items + def binary_mask_stuff(text) orig_size = text.size # Replace ASCII email addresses... @@ -311,7 +312,12 @@ class IncomingMessage < ActiveRecord::Base text.gsub!(email, mask) end - raise "internal error in binary_mask_all_emails" if text.size != orig_size + # Replace censor items + for censor_rule in self.info_request.censor_rules + text = censor_rule.apply_to_binary(text) + end + + raise "internal error in binary_mask_stuff" if text.size != orig_size return text end @@ -337,7 +343,7 @@ class IncomingMessage < ActiveRecord::Base end # Remove emails, mobile phones and other details FOI officers ask us to remove. - def self.remove_privacy_sensitive_things(text) + def remove_privacy_sensitive_things(text) text = text.dup # Remove any email addresses - we don't want bounce messages to leak out @@ -362,6 +368,11 @@ class IncomingMessage < ActiveRecord::Base # Remove WhatDoTheyKnow signup links text.gsub!(/http:\/\/www.whatdotheyknow.com\/c\/[^\s]+/, "[WDTK login link]") + # Remove things from censor rules + for censor_rule in self.info_request.censor_rules + text = censor_rule.apply_to_text(text) + end + return text end @@ -704,7 +715,7 @@ class IncomingMessage < ActiveRecord::Base # Find the body text and remove emails for privacy/anti-spam reasons text = get_main_body_text text = self.mask_special_emails(text) - text = IncomingMessage.remove_privacy_sensitive_things(text) + text = self.remove_privacy_sensitive_things(text) # Remove quoted sections, adding HTML. XXX The FOLDED_QUOTED_SECTION is # a nasty hack so we can escape other HTML before adding the unfold @@ -745,7 +756,7 @@ class IncomingMessage < ActiveRecord::Base # Find the body text and remove emails for privacy/anti-spam reasons text = get_main_body_text text = self.mask_special_emails(text) - text = IncomingMessage.remove_privacy_sensitive_things(text) + text = self.remove_privacy_sensitive_things(text) # Remove existing quoted sections text = self.remove_lotus_quoting(text, '') @@ -763,7 +774,7 @@ class IncomingMessage < ActiveRecord::Base # Remove any privacy things text = self.cached_attachment_text text = self.mask_special_emails(text) - text = IncomingMessage.remove_privacy_sensitive_things(text) + text = self.remove_privacy_sensitive_things(text) return text end def IncomingMessage.get_attachment_text_internal_one_file(content_type, body) diff --git a/app/models/info_request.rb b/app/models/info_request.rb index 0c99dadbf..ebf06913c 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -23,7 +23,7 @@ # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: francis@mysociety.org; WWW: http://www.mysociety.org/ # -# $Id: info_request.rb,v 1.146 2008-10-14 12:48:49 francis Exp $ +# $Id: info_request.rb,v 1.147 2008-10-27 18:18:30 francis Exp $ require 'digest/sha1' require File.join(File.dirname(__FILE__),'../../vendor/plugins/acts_as_xapian/lib/acts_as_xapian') @@ -44,6 +44,7 @@ class InfoRequest < ActiveRecord::Base has_many :user_info_request_sent_alerts has_many :track_things, :order => 'created_at desc' has_many :comments, :order => 'created_at' + has_many :censor_rules, :order => 'created_at desc' # user described state (also update in info_request_event, admin_request/edit.rhtml) validates_inclusion_of :described_state, :in => [ diff --git a/app/views/admin_censor_rule/_form.rhtml b/app/views/admin_censor_rule/_form.rhtml new file mode 100644 index 000000000..4034e9859 --- /dev/null +++ b/app/views/admin_censor_rule/_form.rhtml @@ -0,0 +1,14 @@ +<%= error_messages_for 'censor_rule' %> + +<p><label for="censor_rule_text">Text</label> (that you want to remove)<br/> +<%= text_field 'censor_rule', 'text', :size => 60 %></p> + +<p><label for="censor_rule_replacement">Replacement</label> (put is in <strong>[square brackets]</strong>, e.g. [personal information removed]. applies only to text in emails; binaries must stay the same length and the replacement is just a bunch of 'x's)<br/> +<%= text_field 'censor_rule', 'replacement', :size => 60 %></p> + +<p><label for="censor_rule_last_edit_comment">Comment for this edit</label> (put purpose of the rule, and why the change)<br/> +<%= text_area 'censor_rule', 'last_edit_comment', :rows => 2, :cols => 60 %></p> + +<p>Rule is for request '<%=request_link(info_request)%>' + <%= hidden_field 'censor_rule', 'info_request_id', { :value => info_request.id } %> +</p> diff --git a/app/views/admin_censor_rule/_show.rhtml b/app/views/admin_censor_rule/_show.rhtml new file mode 100644 index 000000000..4d6432a9e --- /dev/null +++ b/app/views/admin_censor_rule/_show.rhtml @@ -0,0 +1,27 @@ + +<table> + <tr> + <th>Id</th> + <% for column in CensorRule.content_columns %> + <th><%= column.human_name %></th> + <% end %> + <th>Actions</th> + </tr> + +<% for censor_rule in censor_rules %> + <tr class="<%= cycle('odd', 'even') %>"> + <td><%=h censor_rule.id %></td> + <% for column in CensorRule.content_columns.map { |c| c.name } %> + <td><%=h censor_rule.send(column) %></td> + <% end %> + <td> + <%= link_to "Edit", '../../censor/edit/' + censor_rule.id.to_s %> + </td> + </tr> +<% end %> +</table> + +<p> + <%= link_to "New censor rule", '../../censor/new?info_request_id=' + info_request.id.to_s %> +</p> + diff --git a/app/views/admin_censor_rule/edit.rhtml b/app/views/admin_censor_rule/edit.rhtml new file mode 100644 index 000000000..c22a61021 --- /dev/null +++ b/app/views/admin_censor_rule/edit.rhtml @@ -0,0 +1,16 @@ +<% @title = 'Edit censor rule' %> + +<h1><%=@title%></h1> + +<% form_tag '../update/' + @censor_rule.id.to_s do %> + <%= render :partial => 'form', :locals => { :info_request => @censor_rule.info_request } %> + <p><%= submit_tag 'Save', :accesskey => 's' %></p> +<% end %> + +<% form_tag('../destroy/' + @censor_rule.id.to_s) do %> + <p> + <%= hidden_field_tag(:censor_rule_id, @censor_rule.id) %> + Permanent! --> <%= submit_tag "Destroy rule" %> + </p> +<% end %> + diff --git a/app/views/admin_censor_rule/new.rhtml b/app/views/admin_censor_rule/new.rhtml new file mode 100644 index 000000000..36de5f0f1 --- /dev/null +++ b/app/views/admin_censor_rule/new.rhtml @@ -0,0 +1,9 @@ +<% @title = 'New censor rule' %> + +<h1><%=@title%></h1> + +<% form_tag 'create' do %> + <%= render :partial => 'form', :locals => { :info_request => @info_request } %> + <p><%= submit_tag "Create" %></p> +<% end %> + diff --git a/app/views/admin_request/show.rhtml b/app/views/admin_request/show.rhtml index 7a5deb7d7..4efd90b11 100644 --- a/app/views/admin_request/show.rhtml +++ b/app/views/admin_request/show.rhtml @@ -128,4 +128,6 @@ <% end %> </table> +<h2>Censor rules</h2> +<%= render :partial => 'admin_censor_rule/show', :locals => { :censor_rules => @info_request.censor_rules, :info_request => @info_request } %> diff --git a/config/routes.rb b/config/routes.rb index 6d27b2dd7..1f70bee8e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,7 +4,7 @@ # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: francis@mysociety.org; WWW: http://www.mysociety.org/ # -# $Id: routes.rb,v 1.76 2008-10-17 20:32:43 francis Exp $ +# $Id: routes.rb,v 1.77 2008-10-27 18:18:31 francis Exp $ ActionController::Routing::Routes.draw do |map| @@ -96,6 +96,7 @@ ActionController::Routing::Routes.draw do |map| map.connect '/admin/request/:action/:id', :controller => 'admin_request' map.connect '/admin/user/:action/:id', :controller => 'admin_user' map.connect '/admin/track/:action/:id', :controller => 'admin_track' + map.connect '/admin/censor/:action/:id', :controller => 'admin_censor_rule' # Sample of named route: # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' diff --git a/db/migrate/068_add_censor_table.rb b/db/migrate/068_add_censor_table.rb new file mode 100644 index 000000000..92a17d2bc --- /dev/null +++ b/db/migrate/068_add_censor_table.rb @@ -0,0 +1,29 @@ +class AddCensorTable < ActiveRecord::Migration + def self.up + create_table :censor_rules do |t| + t.column :info_request_id, :integer + t.column :user_id, :integer + t.column :public_body_id, :integer + + t.column :text, :text, :null => false + t.column :replacement, :text, :null => false + + t.column :last_edit_editor, :string, :null => false + t.column :last_edit_comment, :text, :null => false + + t.column :created_at, :datetime, :null => false + t.column :updated_at, :datetime, :null => false + end + + if ActiveRecord::Base.connection.adapter_name == "PostgreSQL" + execute "ALTER TABLE censor_rules ADD CONSTRAINT fk_censor_rules_info_request FOREIGN KEY (info_request_id) REFERENCES info_requests(id)" + execute "ALTER TABLE censor_rules ADD CONSTRAINT fk_censor_rules_user FOREIGN KEY (user_id) REFERENCES users(id)" + execute "ALTER TABLE censor_rules ADD CONSTRAINT fk_censor_rules_public_body FOREIGN KEY (public_body_id) REFERENCES public_bodies(id)" + end + end + + def self.down + drop_table :censor_rules + end +end + diff --git a/db/schema.rb b/db/schema.rb index 6939d7c41..e33f36c38 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -9,7 +9,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 67) do +ActiveRecord::Schema.define(:version => 68) do create_table "acts_as_xapian_jobs", :force => true do |t| t.string "model", :null => false @@ -19,6 +19,18 @@ ActiveRecord::Schema.define(:version => 67) do add_index "acts_as_xapian_jobs", ["model", "model_id"], :name => "index_acts_as_xapian_jobs_on_model_and_model_id", :unique => true + create_table "censor_rules", :force => true do |t| + t.integer "info_request_id" + t.integer "user_id" + t.integer "public_body_id" + t.text "text", :null => false + t.text "replacement", :null => false + t.string "last_edit_editor", :null => false + t.text "last_edit_comment", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "comments", :force => true do |t| t.integer "user_id", :null => false t.string "comment_type", :default => "internal_error", :null => false @@ -1,3 +1,14 @@ +Test data for Tony + +Censor stuff +============ + +Thing about case insensitivity +Add rules for users / bodies? + +Internal review +=============== + Where to offer button to enter internal review state: - always offer option on request to have internal review, except when in waiting_response earlier than 20 days, and except when in waiting_clarification (call button "make clarification"!) @@ -24,8 +35,6 @@ might want to go back to internal review, not to general awaiting response? When you select "waiting_response" might want to convert to internal_review in case where current state is already internal_review. -Test data for Tony - Next ==== |