aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/admin.js3
-rw-r--r--app/assets/javascripts/admin/category-order.js42
-rw-r--r--app/assets/stylesheets/admin.scss15
-rw-r--r--app/assets/stylesheets/responsive/_user_layout.scss4
-rw-r--r--app/controllers/admin_public_body_categories_controller.rb84
-rw-r--r--app/controllers/admin_public_body_controller.rb2
-rw-r--r--app/controllers/admin_public_body_headings_controller.rb113
-rw-r--r--app/controllers/admin_spam_addresses_controller.rb4
-rw-r--r--app/controllers/public_body_controller.rb4
-rw-r--r--app/controllers/user_controller.rb15
-rw-r--r--app/helpers/admin_public_body_category_helper.rb14
-rw-r--r--app/models/public_body.rb14
-rw-r--r--app/models/public_body_category.rb90
-rw-r--r--app/models/public_body_category/category_collection.rb54
-rw-r--r--app/models/public_body_category_link.rb34
-rw-r--r--app/models/public_body_heading.rb75
-rw-r--r--app/views/admin_general/_admin_navbar.html.erb1
-rw-r--r--app/views/admin_public_body/_tag_help.html.erb2
-rw-r--r--app/views/admin_public_body/import_csv.html.erb2
-rw-r--r--app/views/admin_public_body_categories/_category_list_item.html.erb5
-rw-r--r--app/views/admin_public_body_categories/_form.html.erb66
-rw-r--r--app/views/admin_public_body_categories/_heading_list.html.erb26
-rw-r--r--app/views/admin_public_body_categories/edit.html.erb30
-rw-r--r--app/views/admin_public_body_categories/index.html.erb28
-rw-r--r--app/views/admin_public_body_categories/new.html.erb21
-rw-r--r--app/views/admin_public_body_headings/_form.html.erb41
-rw-r--r--app/views/admin_public_body_headings/edit.html.erb30
-rw-r--r--app/views/admin_public_body_headings/new.html.erb21
-rw-r--r--app/views/admin_request/_incoming_message_actions.html.erb2
-rw-r--r--app/views/admin_spam_addresses/index.html.erb4
-rw-r--r--app/views/public_body/list.html.erb2
-rw-r--r--app/views/user/show.html.erb3
32 files changed, 832 insertions, 19 deletions
diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js
index 0b5d56525..4925a65a4 100644
--- a/app/assets/javascripts/admin.js
+++ b/app/assets/javascripts/admin.js
@@ -1,7 +1,10 @@
// ...
//= require jquery
//= require jquery.ui.tabs
+//= require jquery.ui.sortable
+//= require jquery.ui.effect-highlight
//= require admin/bootstrap-collapse
//= require admin/bootstrap-tab
//= require admin/admin
+//= require admin/category-order
//= require jquery_ujs
diff --git a/app/assets/javascripts/admin/category-order.js b/app/assets/javascripts/admin/category-order.js
new file mode 100644
index 000000000..3be82e46f
--- /dev/null
+++ b/app/assets/javascripts/admin/category-order.js
@@ -0,0 +1,42 @@
+$(function() {
+ $('.save-order').each(function(index){
+
+ // identify the elements that will work together
+ var save_button = $(this);
+ var save_notice = save_button.next();
+ var save_panel = save_button.parent();
+ var list_element = $(save_button.data('list-id'));
+ var endpoint = save_button.data('endpoint');
+
+ // on the first list change, show that there are unsaved changes
+ list_element.sortable({
+ update: function (event, ui) {
+ if (save_button.is('.disabled')){
+ save_button.removeClass("disabled");
+ save_notice.html(save_notice.data('unsaved-text'));
+ save_panel.effect('highlight', {}, 2000);
+ }
+ }
+ });
+ // on save, POST to endpoint
+ save_button.click(function(){
+ if (!save_button.is('.disabled')){
+ var data = list_element.sortable('serialize', {'attribute': 'data-id'});
+ var update_call = $.ajax({ data: data, type: 'POST', url: endpoint });
+
+ // on success, disable the save button again, and show success notice
+ update_call.done(function(msg) {
+ save_button.addClass('disabled');
+ save_panel.effect('highlight', {}, 2000);
+ save_notice.html(save_notice.data('success-text'));
+ })
+ // on failure, show error message
+ update_call.fail(function(jqXHR, msg) {
+ save_panel.effect('highlight', {'color': '#cc0000'}, 2000);
+ save_notice.html(save_notice.data('error-text') + jqXHR.responseText);
+ });
+ }
+ return false;
+ })
+ });
+});
diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss
index b0de2eb7b..863a6c808 100644
--- a/app/assets/stylesheets/admin.scss
+++ b/app/assets/stylesheets/admin.scss
@@ -27,6 +27,9 @@ body.admin {
}
.admin {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 20px;
@import "compass/css3";
@import "bootstrap";
@@ -47,6 +50,9 @@ body.admin {
.accordion-group {
border: none;
+ div {
+ clear: both;
+ }
}
.accordion-heading {
.btn {
@@ -104,5 +110,14 @@ body.admin {
width: 750px;
}
+ .save-notice {
+ display: inline-block;
+ padding-left: 1em;
+ }
+
+ .category-list-item {
+ padding: 3px 0;
+ }
+
}
diff --git a/app/assets/stylesheets/responsive/_user_layout.scss b/app/assets/stylesheets/responsive/_user_layout.scss
index a568a5fa3..84ddbf562 100644
--- a/app/assets/stylesheets/responsive/_user_layout.scss
+++ b/app/assets/stylesheets/responsive/_user_layout.scss
@@ -4,4 +4,8 @@
#search_form {
margin-top: 2rem;
}
+
+ #request_latest_status {
+ width: 300px;
+ }
}
diff --git a/app/controllers/admin_public_body_categories_controller.rb b/app/controllers/admin_public_body_categories_controller.rb
new file mode 100644
index 000000000..fda09fa4a
--- /dev/null
+++ b/app/controllers/admin_public_body_categories_controller.rb
@@ -0,0 +1,84 @@
+class AdminPublicBodyCategoriesController < AdminController
+ def index
+ @locale = self.locale_from_params
+ @category_headings = PublicBodyHeading.all
+ @without_heading = PublicBodyCategory.without_headings
+ end
+
+ def new
+ @category = PublicBodyCategory.new
+ render :formats => [:html]
+ end
+
+ def edit
+ @category = PublicBodyCategory.find(params[:id])
+ @tagged_public_bodies = PublicBody.find_by_tag(@category.category_tag)
+ end
+
+ def update
+ @category = PublicBodyCategory.find(params[:id])
+ @tagged_public_bodies = PublicBody.find_by_tag(@category.category_tag)
+ heading_ids = []
+
+ I18n.with_locale(I18n.default_locale) do
+ if params[:public_body_category][:category_tag] && PublicBody.find_by_tag(@category.category_tag).count > 0 && @category.category_tag != params[:public_body_category][:category_tag]
+ flash[:notice] = 'There are authorities associated with this category, so the tag can\'t be renamed'
+ else
+ if params[:headings]
+ heading_ids = params[:headings].values
+ removed_headings = @category.public_body_heading_ids - heading_ids
+ added_headings = heading_ids - @category.public_body_heading_ids
+
+ unless removed_headings.empty?
+ # remove the link objects
+ deleted_links = PublicBodyCategoryLink.where(
+ :public_body_category_id => @category.id,
+ :public_body_heading_id => [removed_headings]
+ )
+ deleted_links.delete_all
+
+ #fix the category object
+ @category.public_body_heading_ids = heading_ids
+ end
+
+ added_headings.each do |heading_id|
+ PublicBodyHeading.find(heading_id).add_category(@category)
+ end
+ end
+
+ if @category.update_attributes(params[:public_body_category])
+ flash[:notice] = 'Category was successfully updated.'
+ end
+ end
+
+ render :action => 'edit'
+ end
+ end
+
+ def create
+ I18n.with_locale(I18n.default_locale) do
+ @category = PublicBodyCategory.new(params[:public_body_category])
+ if @category.save
+ if params[:headings]
+ params[:headings].values.each do |heading_id|
+ PublicBodyHeading.find(heading_id).add_category(@category)
+ end
+ end
+ flash[:notice] = 'Category was successfully created.'
+ redirect_to admin_categories_path
+ else
+ render :action => 'new'
+ end
+ end
+ end
+
+ def destroy
+ @locale = self.locale_from_params
+ I18n.with_locale(@locale) do
+ category = PublicBodyCategory.find(params[:id])
+ category.destroy
+ flash[:notice] = "Category was successfully destroyed."
+ redirect_to admin_categories_path
+ end
+ end
+end
diff --git a/app/controllers/admin_public_body_controller.rb b/app/controllers/admin_public_body_controller.rb
index 120419a27..f7a80476c 100644
--- a/app/controllers/admin_public_body_controller.rb
+++ b/app/controllers/admin_public_body_controller.rb
@@ -4,8 +4,6 @@
# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
# Email: hello@mysociety.org; WWW: http://www.mysociety.org/
-require "public_body_categories"
-
class AdminPublicBodyController < AdminController
def index
list
diff --git a/app/controllers/admin_public_body_headings_controller.rb b/app/controllers/admin_public_body_headings_controller.rb
new file mode 100644
index 000000000..c7c80e802
--- /dev/null
+++ b/app/controllers/admin_public_body_headings_controller.rb
@@ -0,0 +1,113 @@
+class AdminPublicBodyHeadingsController < AdminController
+
+ def edit
+ @heading = PublicBodyHeading.find(params[:id])
+ render :formats => [:html]
+ end
+
+ def update
+ I18n.with_locale(I18n.default_locale) do
+ @heading = PublicBodyHeading.find(params[:id])
+ if @heading.update_attributes(params[:public_body_heading])
+ flash[:notice] = 'Category heading was successfully updated.'
+ end
+ render :action => 'edit'
+ end
+ end
+
+ def reorder
+ transaction = reorder_headings(params[:headings])
+ if transaction[:success]
+ render :nothing => true, :status => :ok
+ else
+ render :text => transaction[:error], :status => :unprocessable_entity
+ end
+ end
+
+ def reorder_categories
+ transaction = reorder_categories_for_heading(params[:id], params[:categories])
+ if transaction[:success]
+ render :nothing => true, :status => :ok and return
+ else
+ render :text => transaction[:error], :status => :unprocessable_entity
+ end
+ end
+
+ def new
+ @heading = PublicBodyHeading.new
+ render :formats => [:html]
+ end
+
+ def create
+ I18n.with_locale(I18n.default_locale) do
+ @heading = PublicBodyHeading.new(params[:public_body_heading])
+ if @heading.save
+ flash[:notice] = 'Category heading was successfully created.'
+ redirect_to admin_categories_url
+ else
+ render :action => 'new'
+ end
+ end
+ end
+
+ def destroy
+ @locale = self.locale_from_params()
+ I18n.with_locale(@locale) do
+ heading = PublicBodyHeading.find(params[:id])
+
+ if heading.public_body_categories.count > 0
+ flash[:notice] = "There are categories associated with this heading, so can't destroy it"
+ redirect_to edit_admin_heading_url(heading)
+ return
+ end
+
+ heading.destroy
+ flash[:notice] = "Category heading was successfully destroyed."
+ redirect_to admin_categories_url
+ end
+ end
+
+ protected
+
+ def reorder_headings(headings)
+ error = nil
+ ActiveRecord::Base.transaction do
+ headings.each_with_index do |heading_id, index|
+ begin
+ heading = PublicBodyHeading.find(heading_id)
+ rescue ActiveRecord::RecordNotFound => e
+ error = e.message
+ raise ActiveRecord::Rollback
+ end
+ heading.display_order = index
+ unless heading.save
+ error = heading.errors.full_messages.join(",")
+ raise ActiveRecord::Rollback
+ end
+ end
+ end
+ { :success => error.nil? ? true : false, :error => error }
+ end
+
+ def reorder_categories_for_heading(heading_id, categories)
+ error = nil
+ ActiveRecord::Base.transaction do
+ categories.each_with_index do |category_id, index|
+ conditions = { :public_body_category_id => category_id,
+ :public_body_heading_id => heading_id }
+ link = PublicBodyCategoryLink.where(conditions).first
+ unless link
+ error = "Couldn't find PublicBodyCategoryLink for category #{category_id}, heading #{heading_id}"
+ raise ActiveRecord::Rollback
+ end
+ link.category_display_order = index
+ unless link.save
+ error = link.errors.full_messages.join(",")
+ raise ActiveRecord::Rollback
+ end
+ end
+ end
+ { :success => error.nil? ? true : false, :error => error }
+ end
+
+end
diff --git a/app/controllers/admin_spam_addresses_controller.rb b/app/controllers/admin_spam_addresses_controller.rb
index f5c7e93da..fff7e2a4a 100644
--- a/app/controllers/admin_spam_addresses_controller.rb
+++ b/app/controllers/admin_spam_addresses_controller.rb
@@ -10,7 +10,7 @@ class AdminSpamAddressesController < AdminController
if @spam_address.save
notice = "#{ @spam_address.email } has been added to the spam addresses list"
- redirect_to spam_addresses_path, :notice => notice
+ redirect_to admin_spam_addresses_path, :notice => notice
else
@spam_addresses = SpamAddress.all
render :index
@@ -21,7 +21,7 @@ class AdminSpamAddressesController < AdminController
@spam_address = SpamAddress.find(params[:id])
@spam_address.destroy
notice = "#{ @spam_address.email } has been removed from the spam addresses list"
- redirect_to spam_addresses_path, :notice => notice
+ redirect_to admin_spam_addresses_path, :notice => notice
end
end
diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb
index d2c84d820..e64644a1b 100644
--- a/app/controllers/public_body_controller.rb
+++ b/app/controllers/public_body_controller.rb
@@ -111,7 +111,7 @@ class PublicBodyController < ApplicationController
if @tag.nil? || @tag == 'all'
@tag = 'all'
elsif @tag == 'other'
- category_list = PublicBodyCategories.get.tags.map{ |c| %Q('#{ c }') }.join(",")
+ category_list = PublicBodyCategory.get.tags.map{ |c| %Q('#{ c }') }.join(",")
where_condition += base_tag_condition + " AND has_tag_string_tags.name in (#{category_list})) = 0"
elsif @tag.scan(/./mu).size == 1
@tag = Unicode.upcase(@tag)
@@ -132,7 +132,7 @@ class PublicBodyController < ApplicationController
elsif @tag.size == 1
@description = _("beginning with ‘{{first_letter}}’", :first_letter => @tag)
else
- category_name = PublicBodyCategories.get.by_tag[@tag]
+ category_name = PublicBodyCategory.get.by_tag[@tag]
if category_name.nil?
@description = _("matching the tag ‘{{tag_name}}’", :tag_name => @tag)
else
diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb
index f23343ddb..baeaab18a 100644
--- a/app/controllers/user_controller.rb
+++ b/app/controllers/user_controller.rb
@@ -49,13 +49,28 @@ class UserController < ApplicationController
# TODO: really should just use SQL query here rather than Xapian.
if @show_requests
begin
+
+ request_states = @display_user.info_requests.pluck(:described_state).uniq
+
+ option_item = Struct.new(:value, :text)
+ @request_states = request_states.map do |state|
+ option_item.new(state, InfoRequest.get_status_description(state))
+ end
+
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])
+
+ unless params[:request_latest_status].blank?
+ requests_query << ' latest_status:' << params[:request_latest_status]
+ comments_query << ' latest_status:' << params[:request_latest_status]
+ @match_phrase << _(" filtered by status: '{{status}}'", :status => params[:request_latest_status])
+ end
end
+
@xapian_requests = perform_search([InfoRequestEvent], requests_query, 'newest', 'request_collapse')
@xapian_comments = perform_search([InfoRequestEvent], comments_query, 'newest', nil)
diff --git a/app/helpers/admin_public_body_category_helper.rb b/app/helpers/admin_public_body_category_helper.rb
new file mode 100644
index 000000000..9c5e5cc5e
--- /dev/null
+++ b/app/helpers/admin_public_body_category_helper.rb
@@ -0,0 +1,14 @@
+module AdminPublicBodyCategoryHelper
+ def heading_is_selected?(heading)
+ if params[:headings]
+ if params[:headings]["heading_#{heading.id}"]
+ return true
+ else
+ return false
+ end
+ elsif @category.public_body_headings.include?(heading)
+ return true
+ end
+ false
+ end
+end
diff --git a/app/models/public_body.rb b/app/models/public_body.rb
index 87b5c2227..f61a3f449 100644
--- a/app/models/public_body.rb
+++ b/app/models/public_body.rb
@@ -132,14 +132,14 @@ class PublicBody < ActiveRecord::Base
end
def translated_versions=(translation_attrs)
- def skip?(attrs)
- valueless = attrs.inject({}) { |h, (k, v)| h[k] = v if v != '' and k != 'locale'; h } # because we want to fall back to alternative translations where there are empty values
- return valueless.length == 0
+ def empty_translation?(attrs)
+ attrs_with_values = attrs.select{ |key, value| value != '' and key != 'locale' }
+ attrs_with_values.empty?
end
if translation_attrs.respond_to? :each_value # Hash => updating
translation_attrs.each_value do |attrs|
- next if skip?(attrs)
+ next if empty_translation?(attrs)
t = translation_for(attrs[:locale]) || PublicBody::Translation.new
t.attributes = attrs
calculate_cached_fields(t)
@@ -147,7 +147,7 @@ class PublicBody < ActiveRecord::Base
end
else # Array => creating
translation_attrs.each do |attrs|
- next if skip?(attrs)
+ next if empty_translation?(attrs)
new_translation = PublicBody::Translation.new(attrs)
calculate_cached_fields(new_translation)
translations << new_translation
@@ -335,8 +335,8 @@ class PublicBody < ActiveRecord::Base
types = []
first = true
for tag in self.tags
- if PublicBodyCategories::get().by_tag().include?(tag.name)
- desc = PublicBodyCategories::get().singular_by_tag()[tag.name]
+ if PublicBodyCategory.get().by_tag().include?(tag.name)
+ desc = PublicBodyCategory.get().singular_by_tag()[tag.name]
if first
# terrible that Ruby/Rails doesn't have an equivalent of ucfirst
# (capitalize shockingly converts later characters to lowercase)
diff --git a/app/models/public_body_category.rb b/app/models/public_body_category.rb
new file mode 100644
index 000000000..8eaecd596
--- /dev/null
+++ b/app/models/public_body_category.rb
@@ -0,0 +1,90 @@
+# == Schema Information
+#
+# Table name: public_body_categories
+#
+# id :integer not null, primary key
+# title :text not null
+# category_tag :text not null
+# description :text not null
+# display_order :integer
+#
+
+require 'forwardable'
+
+class PublicBodyCategory < ActiveRecord::Base
+ attr_accessible :locale, :category_tag, :title, :description,
+ :translated_versions, :display_order
+
+ has_many :public_body_category_links, :dependent => :destroy
+ has_many :public_body_headings, :through => :public_body_category_links
+
+ translates :title, :description
+ validates_uniqueness_of :category_tag, :message => N_('Tag is already taken')
+ validates_presence_of :title, :message => N_("Title can't be blank")
+ validates_presence_of :category_tag, :message => N_("Tag can't be blank")
+
+ def self.get
+ locale = I18n.locale.to_s || default_locale.to_s || ""
+ categories = CategoryCollection.new
+ I18n.with_locale(locale) do
+ headings = PublicBodyHeading.all
+ headings.each do |heading|
+ categories << heading.name
+ heading.public_body_categories.each do |category|
+ categories << [
+ category.category_tag,
+ category.title,
+ category.description
+ ]
+ end
+ end
+ end
+ categories
+ end
+
+ def self.without_headings
+ sql = %Q| SELECT * FROM public_body_categories pbc
+ WHERE pbc.id NOT IN (
+ SELECT public_body_category_id AS id
+ FROM public_body_category_links
+ ) |
+ PublicBodyCategory.find_by_sql(sql)
+ end
+
+ # Called from the old-style public_body_categories_[locale].rb data files
+ def self.add(locale, data_list)
+ CategoryAndHeadingMigrator.add_categories_and_headings_from_list(locale, data_list)
+ end
+
+ # Convenience methods for creating/editing translations via forms
+ def find_translation_by_locale(locale)
+ translations.find_by_locale(locale)
+ end
+
+ def translated_versions
+ translations
+ end
+
+ def translated_versions=(translation_attrs)
+ def empty_translation?(attrs)
+ attrs_with_values = attrs.select{ |key, value| value != '' and key != 'locale' }
+ attrs_with_values.empty?
+ end
+ if translation_attrs.respond_to? :each_value # Hash => updating
+ translation_attrs.each_value do |attrs|
+ next if empty_translation?(attrs)
+ t = translation_for(attrs[:locale]) || PublicBodyCategory::Translation.new
+ t.attributes = attrs
+ t.save!
+ end
+ else # Array => creating
+ translation_attrs.each do |attrs|
+ next if empty_translation?(attrs)
+ new_translation = PublicBodyCategory::Translation.new(attrs)
+ translations << new_translation
+ end
+ end
+ end
+end
+
+
diff --git a/app/models/public_body_category/category_collection.rb b/app/models/public_body_category/category_collection.rb
new file mode 100644
index 000000000..8286e2710
--- /dev/null
+++ b/app/models/public_body_category/category_collection.rb
@@ -0,0 +1,54 @@
+# replicate original file-based PublicBodyCategories functionality
+class PublicBodyCategory::CategoryCollection
+ include Enumerable
+ extend Forwardable
+ def_delegators :@categories, :each, :<<
+
+ def initialize
+ @categories = []
+ end
+
+ def with_headings
+ @categories
+ end
+
+ def with_description
+ @categories.select() { |a| a.instance_of?(Array) }
+ end
+
+ def tags
+ tags = with_description.map() { |a| a[0] }
+ end
+
+ def by_tag
+ Hash[*with_description.map() { |a| a[0..1] }.flatten]
+ end
+
+ def singular_by_tag
+ Hash[*with_description.map() { |a| [a[0],a[2]] }.flatten]
+ end
+
+ def by_heading
+ output = {}
+ heading = nil
+ @categories.each do |row|
+ if row.is_a?(Array)
+ output[heading] << row[0]
+ else
+ heading = row
+ output[heading] = []
+ end
+ end
+ output
+ end
+
+ def headings
+ output = []
+ @categories.each do |row|
+ unless row.is_a?(Array)
+ output << row
+ end
+ end
+ output
+ end
+end
diff --git a/app/models/public_body_category_link.rb b/app/models/public_body_category_link.rb
new file mode 100644
index 000000000..eb233b56f
--- /dev/null
+++ b/app/models/public_body_category_link.rb
@@ -0,0 +1,34 @@
+# == Schema Information
+#
+# Table name: public_body_category_link
+#
+# public_body_category_id :integer not null
+# public_body_heading_id :integer not null
+# category_display_order :integer
+#
+
+class PublicBodyCategoryLink < ActiveRecord::Base
+ attr_accessible :public_body_category_id, :public_body_heading_id, :category_display_order
+
+ belongs_to :public_body_category
+ belongs_to :public_body_heading
+ validates_presence_of :public_body_category
+ validates_presence_of :public_body_heading
+ validates :category_display_order, :numericality => { :only_integer => true,
+ :message => N_('Display order must be a number') }
+
+ before_validation :on => :create do
+ unless self.category_display_order
+ self.category_display_order = PublicBodyCategoryLink.next_display_order(public_body_heading_id)
+ end
+ end
+
+ def self.next_display_order(heading_id)
+ if last = where(:public_body_heading_id => heading_id).order(:category_display_order).last
+ last.category_display_order + 1
+ else
+ 0
+ end
+ end
+
+end
diff --git a/app/models/public_body_heading.rb b/app/models/public_body_heading.rb
new file mode 100644
index 000000000..c38800561
--- /dev/null
+++ b/app/models/public_body_heading.rb
@@ -0,0 +1,75 @@
+# == Schema Information
+#
+# Table name: public_body_headings
+#
+# id :integer not null, primary key
+# name :text not null
+# display_order :integer
+#
+
+class PublicBodyHeading < ActiveRecord::Base
+ attr_accessible :name, :display_order, :translated_versions
+
+ has_many :public_body_category_links, :dependent => :destroy
+ has_many :public_body_categories, :order => :category_display_order, :through => :public_body_category_links
+ default_scope order('display_order ASC')
+
+ translates :name
+
+ validates_uniqueness_of :name, :message => N_('Name is already taken')
+ validates_presence_of :name, :message => N_('Name can\'t be blank')
+ validates :display_order, :numericality => { :only_integer => true,
+ :message => N_('Display order must be a number') }
+
+ before_validation :on => :create do
+ unless self.display_order
+ self.display_order = PublicBodyHeading.next_display_order
+ end
+ end
+
+ # Convenience methods for creating/editing translations via forms
+ def find_translation_by_locale(locale)
+ translations.find_by_locale(locale)
+ end
+
+ def translated_versions
+ translations
+ end
+
+ def translated_versions=(translation_attrs)
+ def empty_translation?(attrs)
+ attrs_with_values = attrs.select{ |key, value| value != '' and key != 'locale' }
+ attrs_with_values.empty?
+ end
+
+ if translation_attrs.respond_to? :each_value # Hash => updating
+ translation_attrs.each_value do |attrs|
+ next if empty_translation?(attrs)
+ t = translation_for(attrs[:locale]) || PublicBodyHeading::Translation.new
+ t.attributes = attrs
+ t.save!
+ end
+ else # Array => creating
+ translation_attrs.each do |attrs|
+ next if empty_translation?(attrs)
+ new_translation = PublicBodyHeading::Translation.new(attrs)
+ translations << new_translation
+ end
+ end
+ end
+
+ def add_category(category)
+ unless public_body_categories.include?(category)
+ public_body_categories << category
+ end
+ end
+
+ def self.next_display_order
+ if max = maximum(:display_order)
+ max + 1
+ else
+ 0
+ end
+ end
+
+end
diff --git a/app/views/admin_general/_admin_navbar.html.erb b/app/views/admin_general/_admin_navbar.html.erb
index 5cc740f70..14fc06092 100644
--- a/app/views/admin_general/_admin_navbar.html.erb
+++ b/app/views/admin_general/_admin_navbar.html.erb
@@ -10,6 +10,7 @@
<li><%= link_to 'Stats', admin_stats_path %></li>
<li><%= link_to 'Debug', admin_debug_path %></li>
<li><%= link_to 'Authorities', admin_body_list_path %></li>
+ <li><%= link_to 'Categories', admin_categories_path %></li>
<li><%= link_to 'Requests', admin_request_list_path %></li>
<li><%= link_to 'Users', admin_user_list_path %></li>
<li><%= link_to 'Tracks', admin_track_list_path %></li>
diff --git a/app/views/admin_public_body/_tag_help.html.erb b/app/views/admin_public_body/_tag_help.html.erb
index b64e65877..5d6990400 100644
--- a/app/views/admin_public_body/_tag_help.html.erb
+++ b/app/views/admin_public_body/_tag_help.html.erb
@@ -1,6 +1,6 @@
<h2>List of tags</h2>
<% first_row = true %>
-<% for row in PublicBodyCategories::get().with_headings() %>
+<% for row in PublicBodyCategory.get().with_headings() %>
<% if row.instance_of?(Array) %>
<% if row[0] != 'other' %>
<strong><%= row[0] %></strong>=<%= row[1] %>
diff --git a/app/views/admin_public_body/import_csv.html.erb b/app/views/admin_public_body/import_csv.html.erb
index c690f0fc2..4b14226d1 100644
--- a/app/views/admin_public_body/import_csv.html.erb
+++ b/app/views/admin_public_body/import_csv.html.erb
@@ -76,7 +76,7 @@ Another One,another@example.com,Otro organismo,a_tag
<hr>
<p>Standard tags:
- <% for category, description in PublicBodyCategories::get().by_tag() %>
+ <% for category, description in PublicBodyCategory.get().by_tag() %>
<% if category != "other" %>
<strong><%= category %></strong>=<%= description %>;
<% end %>
diff --git a/app/views/admin_public_body_categories/_category_list_item.html.erb b/app/views/admin_public_body_categories/_category_list_item.html.erb
new file mode 100644
index 000000000..056ab6148
--- /dev/null
+++ b/app/views/admin_public_body_categories/_category_list_item.html.erb
@@ -0,0 +1,5 @@
+<div class="category-list-item" <% if heading %> data-id="categories_<%= category.id %>"<% end %>>
+ <%= link_to(category.title, edit_admin_category_path(category), :title => "view full details") %>
+</div>
+
+
diff --git a/app/views/admin_public_body_categories/_form.html.erb b/app/views/admin_public_body_categories/_form.html.erb
new file mode 100644
index 000000000..b0778d371
--- /dev/null
+++ b/app/views/admin_public_body_categories/_form.html.erb
@@ -0,0 +1,66 @@
+<%= error_messages_for 'category' %>
+
+<!--[form:public_body_category]-->
+
+<div id="div-locales">
+ <ul class="locales nav nav-tabs">
+ <% I18n.available_locales.each_with_index do |locale, i| %>
+ <li><a href="#div-locale-<%=locale.to_s%>" data-toggle="tab" ><%=locale_name(locale.to_s) || "Default locale"%></a></li>
+ <% end %>
+ </ul>
+ <div class="tab-content">
+<%
+ I18n.available_locales.each do |locale|
+ if locale==I18n.default_locale # The default locale is submitted as part of the bigger object...
+ prefix = 'public_body_category'
+ object = @category
+ else # ...but additional locales go "on the side"
+ prefix = "public_body_category[translated_versions][]"
+ object = @category.new_record? ?
+ PublicBodyCategory::Translation.new :
+ @category.find_translation_by_locale(locale.to_s) || PublicBodyCategory::Translation.new
+ end
+%>
+ <%= fields_for prefix, object do |t| %>
+ <div class="tab-pane" id="div-locale-<%=locale.to_s%>">
+ <div class="control-group">
+ <%= t.hidden_field :locale, :value => locale.to_s %>
+ <label for="<%= form_tag_id(t.object_name, :title, locale) %>" class="control-label">Title</label>
+ <div class="controls">
+ <%= t.text_field :title, :id => form_tag_id(t.object_name, :title, locale), :class => "span4" %>
+ </div>
+ </div>
+ <div class="control-group">
+ <label for="<%= form_tag_id(t.object_name, :description, locale) %>" class="control-label">Description</label>
+ <div class="controls">
+ <%= t.text_field :description, :id => form_tag_id(t.object_name, :description, locale), :class => "span4" %>
+ </div>
+ </div>
+ </div>
+ <%
+ end
+end
+%>
+ </div>
+</div>
+
+<% if PublicBody.find_by_tag(@category.category_tag).count == 0 or @category.errors.messages.keys.include?(:category_tag) %>
+ <h3>Common Fields</h3>
+
+ <div class="control-group">
+ <label for="public_body_category_category_tag" class="control-label">Category tag</label>
+ <div class="controls">
+ <%= f.text_field :category_tag, :class => "span4" %>
+ </div>
+ </div>
+<% end %>
+
+<h3>Headings</h3>
+<div class="control-group">
+ <% PublicBodyHeading.all.each do |heading| %>
+ <div class="span3">
+ &nbsp;<%= check_box_tag "headings[heading_#{heading.id}]", heading.id, heading_is_selected?(heading) %> <label for="headings_heading_<%= heading.id %>" class="control-label"><%= heading.name %></label>
+ </div>
+ <% end %>
+</div>
+<!--[eoform:public_body_category]-->
diff --git a/app/views/admin_public_body_categories/_heading_list.html.erb b/app/views/admin_public_body_categories/_heading_list.html.erb
new file mode 100644
index 000000000..4bd8bdc90
--- /dev/null
+++ b/app/views/admin_public_body_categories/_heading_list.html.erb
@@ -0,0 +1,26 @@
+<div class="accordion" id="category_list">
+ <% for heading in category_headings %>
+ <div class="accordion-group" data-id="headings_<%=heading.id%>">
+ <div class="accordion-heading accordion-toggle row">
+ <span class="item-title span6">
+ <a href="#heading_<%=heading.id%>_categories" data-toggle="collapse" data-parent="#categories" ><%= chevron_right %></a>
+ <strong><%= link_to(heading.name, edit_admin_heading_path(heading), :title => "view full details") %></strong>
+ </span>
+ </div>
+
+ <div id="heading_<%= heading.id %>_categories" class="accordion-body collapse row ">
+ <div class="well">
+ <div class="span12" id="heading_<%= heading.id %>_category_list" class="category-list">
+ <% heading.public_body_categories.each do |category| %>
+ <%= render :partial => 'category_list_item', :locals => { :category => category, :heading => heading } %>
+ <% end %>
+ </div>
+
+ <div class="form-actions save-panel">
+ <%= link_to "Save", '#', :class => "btn btn-primary disabled save-order", "data-heading-id" => heading.id, "data-list-id" => "#heading_#{heading.id}_category_list", 'data-endpoint' => reorder_categories_admin_heading_path(heading) %><p class="save-notice" data-unsaved-text="There are unsaved changes to the order of categories." data-success-text="Changes saved." data-error-text="There was an error saving your changes: ">Drag and drop to change the order of categories.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ <% end %>
+</div>
diff --git a/app/views/admin_public_body_categories/edit.html.erb b/app/views/admin_public_body_categories/edit.html.erb
new file mode 100644
index 000000000..95988d688
--- /dev/null
+++ b/app/views/admin_public_body_categories/edit.html.erb
@@ -0,0 +1,30 @@
+<h1><%=@title%></h1>
+
+<div class="row">
+ <div class="span8">
+ <div id="public_body_category_form">
+ <%= form_for @category, :url => admin_category_path(@category), :html => { :class => "form form-horizontal" } do |f| %>
+ <%= render :partial => 'form', :locals => {:f => f} %>
+ <div class="form-actions">
+ <%= f.submit 'Save', :accesskey => 's', :class => "btn btn-success" %></p>
+ </div>
+ <% end %>
+ </div>
+</div>
+
+<div class="row">
+ <div class="span8 well">
+ <%= link_to 'List all', admin_categories_path, :class => "btn" %>
+ </div>
+</div>
+
+<% if @tagged_public_bodies.empty? %>
+ <div class="row">
+ <div class="span8">
+ <%= form_tag(admin_category_path(@category), :method => 'delete', :class => "form form-inline") do %>
+ <%= hidden_field_tag(:public_body_id, { :value => @category.id } ) %>
+ <%= submit_tag "Destroy #{@category.title}", :title => @category.title, :class => "btn btn-danger" %> (this is permanent!)
+ <% end %>
+ </div>
+ </div>
+<% end %>
diff --git a/app/views/admin_public_body_categories/index.html.erb b/app/views/admin_public_body_categories/index.html.erb
new file mode 100644
index 000000000..62ec4623d
--- /dev/null
+++ b/app/views/admin_public_body_categories/index.html.erb
@@ -0,0 +1,28 @@
+<% @title = 'Listing public authority categories' %>
+
+<h1><%=@title%></h1>
+
+<div class="btn-toolbar">
+ <div class="btn-group">
+ <%= link_to 'New category', new_admin_category_path, :class => "btn btn-primary" %>
+ </div>
+ <div class="btn-group">
+ <%= link_to 'New category heading', new_admin_heading_path, :class => "btn" %>
+ </div>
+</div>
+
+<h2>All category headings</h2>
+<div>
+<%= render :partial => 'heading_list', :locals => { :category_headings => @category_headings, :table_name => 'exact' } %>
+
+<% if @without_heading.count > 0 %>
+
+ <h3>Categories with no heading</h3>
+
+ <% @without_heading.each do |category| %>
+ <%= render :partial => 'category_list_item', :locals => { :category => category, :heading => nil } %>
+ <% end %>
+<% end %>
+<div class="form-actions save-panel">
+<%= link_to "Save", '#', :class => "btn btn-primary disabled save-order", "data-list-id" => '#category_list', 'data-endpoint' => reorder_admin_headings_path %><p class="save-notice" data-unsaved-text="There are unsaved changes to the order of category headings." data-success-text="Changes saved." data-error-text="There was an error saving your changes: ">Drag and drop to change the order of category headings.</p>
+</div>
diff --git a/app/views/admin_public_body_categories/new.html.erb b/app/views/admin_public_body_categories/new.html.erb
new file mode 100644
index 000000000..8b1b1103f
--- /dev/null
+++ b/app/views/admin_public_body_categories/new.html.erb
@@ -0,0 +1,21 @@
+<% @title = 'New category' %>
+
+<h1><%=@title%></h1>
+<div class="row">
+ <div class="span8">
+ <div id="public_category_form">
+ <%= form_for @category, :url => admin_categories_path, :html => {:class => "form form-horizontal"} do |f| %>
+ <%= render :partial => 'form', :locals => {:f => f} %>
+
+ <div class="form-actions">
+ <%= f.submit "Create", :class => "btn btn-primary" %>
+ </div>
+ <% end %>
+ <div class="row">
+ <div class="span8 well">
+ <%= link_to 'List all', admin_categories_path, :class => "btn" %>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/app/views/admin_public_body_headings/_form.html.erb b/app/views/admin_public_body_headings/_form.html.erb
new file mode 100644
index 000000000..d4e914ca1
--- /dev/null
+++ b/app/views/admin_public_body_headings/_form.html.erb
@@ -0,0 +1,41 @@
+<%= error_messages_for 'heading' %>
+
+<!--[form:public_body_heading]-->
+
+<div id="div-locales">
+ <ul class="locales nav nav-tabs">
+ <% I18n.available_locales.each_with_index do |locale, i| %>
+ <li><a href="#div-locale-<%=locale.to_s%>" data-toggle="tab" ><%=locale_name(locale.to_s) || "Default locale"%></a></li>
+ <% end %>
+ </ul>
+ <div class="tab-content">
+<%
+ for locale in I18n.available_locales do
+ if locale==I18n.default_locale # The default locale is submitted as part of the bigger object...
+ prefix = 'public_body_heading'
+ object = @heading
+ else # ...but additional locales go "on the side"
+ prefix = "public_body_heading[translated_versions][]"
+ object = @heading.new_record? ?
+ PublicBodyHeading::Translation.new :
+ @heading.find_translation_by_locale(locale.to_s) || PublicBodyHeading::Translation.new
+ end
+%>
+ <%= fields_for prefix, object do |t| %>
+ <div class="tab-pane" id="div-locale-<%=locale.to_s%>">
+ <div class="control-group">
+ <%= t.hidden_field :locale, :value => locale.to_s %>
+ <label for="<%= form_tag_id(t.object_name, :name, locale) %>" class="control-label">Name</label>
+ <div class="controls">
+ <%= t.text_field :name, :id => form_tag_id(t.object_name, :name, locale), :class => "span4" %>
+ </div>
+ </div>
+ </div>
+ <%
+ end
+end
+%>
+ </div>
+</div>
+
+<!--[eoform:public_body_heading]-->
diff --git a/app/views/admin_public_body_headings/edit.html.erb b/app/views/admin_public_body_headings/edit.html.erb
new file mode 100644
index 000000000..eff89285a
--- /dev/null
+++ b/app/views/admin_public_body_headings/edit.html.erb
@@ -0,0 +1,30 @@
+<h1><%=@title%></h1>
+
+<div class="row">
+ <div class="span8">
+ <div id="public_body_heading_form">
+ <%= form_for @heading, :url => admin_heading_path(@heading), :html => { :class => "form form-horizontal" } do |f| %>
+ <%= render :partial => 'form', :locals => {:f => f} %>
+ <div class="form-actions">
+ <%= f.submit 'Save', :accesskey => 's', :class => "btn btn-success" %></p>
+ </div>
+ <% end %>
+ </div>
+</div>
+
+<div class="row">
+ <div class="span8 well">
+ <%= link_to 'List all', admin_categories_path, :class => "btn" %>
+ </div>
+</div>
+
+<% if @heading.public_body_categories.empty? %>
+ <div class="row">
+ <div class="span8">
+ <%= form_tag(admin_heading_path(@heading), :method => 'delete', :class => "form form-inline") do %>
+ <%= hidden_field_tag(:public_body_heading_id, { :value => @heading.id } ) %>
+ <%= submit_tag "Destroy #{@heading.name}", :name => @heading.name, :class => "btn btn-danger" %> (this is permanent!)
+ <% end %>
+ </div>
+ </div>
+<% end %>
diff --git a/app/views/admin_public_body_headings/new.html.erb b/app/views/admin_public_body_headings/new.html.erb
new file mode 100644
index 000000000..91d5d4a9d
--- /dev/null
+++ b/app/views/admin_public_body_headings/new.html.erb
@@ -0,0 +1,21 @@
+<% @title = 'New category heading' %>
+
+<h1><%=@title%></h1>
+<div class="row">
+ <div class="span8">
+ <div id="public_heading_form">
+ <%= form_for @heading, :url => admin_headings_path, :html => {:class => "form form-horizontal"} do |f| %>
+ <%= render :partial => 'form', :locals => {:f => f} %>
+
+ <div class="form-actions">
+ <%= f.submit "Create", :class => "btn btn-primary" %>
+ </div>
+ <% end %>
+ <div class="row">
+ <div class="span8 well">
+ <%= link_to 'List all', admin_categories_path, :class => "btn" %>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/app/views/admin_request/_incoming_message_actions.html.erb b/app/views/admin_request/_incoming_message_actions.html.erb
index dd50eb047..22effcce5 100644
--- a/app/views/admin_request/_incoming_message_actions.html.erb
+++ b/app/views/admin_request/_incoming_message_actions.html.erb
@@ -25,7 +25,7 @@
<div class="control-group">
<label class="control-label">Mark <code>To:</code> address as spam</label>
<div class="controls">
- <%= link_to 'Spam Addresses', spam_addresses_path %>
+ <%= link_to 'Spam Addresses', admin_spam_addresses_path %>
</div>
</div>
diff --git a/app/views/admin_spam_addresses/index.html.erb b/app/views/admin_spam_addresses/index.html.erb
index 9846bc017..7a11f70e1 100644
--- a/app/views/admin_spam_addresses/index.html.erb
+++ b/app/views/admin_spam_addresses/index.html.erb
@@ -16,7 +16,7 @@
<div class="row">
<div class="span12">
- <%= form_for(@spam_address, :html => { :class => 'form-inline' }) do |f| -%>
+ <%= form_for(@spam_address, :url => admin_spam_addresses_path, :html => { :class => 'form-inline' }) do |f| -%>
<%= error_messages_for @spam_address %>
<%= f.text_field :email, :class => 'input-xxlarge', :placeholder => 'Enter email' %>
<%= f.submit 'Add Spam Address', :class => 'btn btn-warning' %>
@@ -39,7 +39,7 @@
<% @spam_addresses.each do |spam| %>
<tr>
<td><%= spam.email %></td>
- <td><%= link_to 'Remove', spam,
+ <td><%= link_to 'Remove', admin_spam_address_path(spam),
:method => :delete,
:confirm => 'This is permanent! Are you sure?',
:class => 'btn btn-mini btn-danger' %></td>
diff --git a/app/views/public_body/list.html.erb b/app/views/public_body/list.html.erb
index ce24daaf9..0750c7655 100644
--- a/app/views/public_body/list.html.erb
+++ b/app/views/public_body/list.html.erb
@@ -7,7 +7,7 @@
</li>
</ul>
<% first_row = true %>
- <% for row in PublicBodyCategories::get().with_headings() %>
+ <% for row in PublicBodyCategory.get().with_headings() %>
<% if row.instance_of?(Array) %>
<li>
<%= link_to_unless (@tag == row[0]), row[1], list_public_bodies_path(:tag => row[0]) %>
diff --git a/app/views/user/show.html.erb b/app/views/user/show.html.erb
index 7ae577565..b23f74326 100644
--- a/app/views/user/show.html.erb
+++ b/app/views/user/show.html.erb
@@ -121,6 +121,9 @@
<%= form_tag(show_user_url, :method => "get", :id=>"search_form") do %>
<div>
<%= text_field_tag(:user_query, params[:user_query], {:title => "type your search term here" }) %>
+ <%= select_tag :request_latest_status,
+ options_from_collection_for_select(@request_states, 'value', 'text', params[:request_latest_status]),
+ :prompt => _('Filter by Request Status (optional)') %>
<% if @is_you %>
<%= submit_tag(_("Search your contributions")) %>
<% else %>