diff --git a/.gitignore b/.gitignore
index 35f5651a4..a6f2826c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,4 +30,5 @@
.DS_Store
.ruby-gemset
-public/sitemap.xml
\ No newline at end of file
+public/sitemap.xml
+public/system/
diff --git a/app/controllers/admin/site_customization/content_blocks_controller.rb b/app/controllers/admin/site_customization/content_blocks_controller.rb
new file mode 100644
index 000000000..2f0843ccc
--- /dev/null
+++ b/app/controllers/admin/site_customization/content_blocks_controller.rb
@@ -0,0 +1,40 @@
+class Admin::SiteCustomization::ContentBlocksController < Admin::SiteCustomization::BaseController
+ load_and_authorize_resource :content_block, class: "SiteCustomization::ContentBlock"
+
+ def index
+ @content_blocks = SiteCustomization::ContentBlock.order(:name, :locale)
+ end
+
+ def create
+ if @content_block.save
+ redirect_to admin_site_customization_content_blocks_path, notice: t('admin.site_customization.content_blocks.create.notice')
+ else
+ flash.now[:error] = t('admin.site_customization.content_blocks.create.error')
+ render :new
+ end
+ end
+
+ def update
+ if @content_block.update(content_block_params)
+ redirect_to admin_site_customization_content_blocks_path, notice: t('admin.site_customization.content_blocks.update.notice')
+ else
+ flash.now[:error] = t('admin.site_customization.content_blocks.update.error')
+ render :edit
+ end
+ end
+
+ def destroy
+ @content_block.destroy
+ redirect_to admin_site_customization_content_blocks_path, notice: t('admin.site_customization.content_blocks.destroy.notice')
+ end
+
+ private
+
+ def content_block_params
+ params.require(:site_customization_content_block).permit(
+ :name,
+ :locale,
+ :body
+ )
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index c449fa9f4..0c23c0d22 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -51,4 +51,8 @@ module ApplicationHelper
def image_path_for(filename)
SiteCustomization::Image.image_path_for(filename) || filename
end
+
+ def content_block(name, locale)
+ SiteCustomization::ContentBlock.block_for(name, locale)
+ end
end
diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb
index c61530772..a99b98fea 100644
--- a/app/models/abilities/administrator.rb
+++ b/app/models/abilities/administrator.rb
@@ -54,6 +54,7 @@ module Abilities
can :manage, SiteCustomization::Page
can :manage, SiteCustomization::Image
+ can :manage, SiteCustomization::ContentBlock
end
end
end
diff --git a/app/models/site_customization/content_block.rb b/app/models/site_customization/content_block.rb
new file mode 100644
index 000000000..c08beb52e
--- /dev/null
+++ b/app/models/site_customization/content_block.rb
@@ -0,0 +1,11 @@
+class SiteCustomization::ContentBlock < ActiveRecord::Base
+ VALID_BLOCKS = %w(top_links footer)
+
+ validates :locale, presence: true, inclusion: { in: I18n.available_locales.map(&:to_s) }
+ validates :name, presence: true, uniqueness: { scope: :locale }, inclusion: { in: VALID_BLOCKS }
+
+ def self.block_for(name, locale)
+ locale ||= I18n.default_locale
+ find_by(name: name, locale: locale).try(:body)
+ end
+end
diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb
index 6a48b493b..65d8bed79 100644
--- a/app/views/admin/_menu.html.erb
+++ b/app/views/admin/_menu.html.erb
@@ -134,5 +134,11 @@
<%= t("admin.menu.site_customization.images") %>
<% end %>
+
+
>
+ <%= link_to admin_site_customization_content_blocks_path do %>
+ <%= t("admin.menu.site_customization.content_blocks") %>
+ <% end %>
+
diff --git a/app/views/admin/site_customization/content_blocks/_form.html.erb b/app/views/admin/site_customization/content_blocks/_form.html.erb
new file mode 100644
index 000000000..9ca4eb36a
--- /dev/null
+++ b/app/views/admin/site_customization/content_blocks/_form.html.erb
@@ -0,0 +1,34 @@
+<%= form_for [:admin, @content_block], html: {class: "edit_page", data: {watch_changes: true}} do |f| %>
+
+ <% if @content_block.errors.any? %>
+
+
+
+
+
+ <%= @content_block.errors.count %>
+ <%= t("admin.site_customization.content_blocks.errors.form.error", count: @content_block.errors.count) %>
+
+
+
+ <% end %>
+
+
+
+ <%= f.label :name %>
+ <%= f.select :name, SiteCustomization::ContentBlock::VALID_BLOCKS, label: false %>
+
+
+ <%= f.label :locale %>
+ <%= f.select :locale, I18n.available_locales, label: false %>
+
+
+
+ <%= f.label :body %>
+ <%= f.text_area :body, label: false, rows: 10 %>
+ <%= f.submit class: "button success" %>
+
+
+<% end %>
diff --git a/app/views/admin/site_customization/content_blocks/edit.html.erb b/app/views/admin/site_customization/content_blocks/edit.html.erb
new file mode 100644
index 000000000..c96f9a810
--- /dev/null
+++ b/app/views/admin/site_customization/content_blocks/edit.html.erb
@@ -0,0 +1,15 @@
+<% provide :title do %>
+ Admin - <%= t("admin.menu.site_customization.content_blocks") %> - <%= @content_block.name %> (<%= @content_block.locale %>)
+<% end %>
+
+<%= link_to admin_site_customization_content_blocks_path, class: "back" do %>
+
+ <%= t("admin.site_customization.content_blocks.edit.back") %>
+<% end %>
+
+<%= button_to t("admin.site_customization.content_blocks.index.delete"), admin_site_customization_content_block_path(@content_block), method: :delete, class: "button hollow alert float-right margin-right" %>
+
+
+
<%= t("admin.site_customization.content_blocks.edit.title") %>
+ <%= render 'form' %>
+
diff --git a/app/views/admin/site_customization/content_blocks/index.html.erb b/app/views/admin/site_customization/content_blocks/index.html.erb
new file mode 100644
index 000000000..653e3e731
--- /dev/null
+++ b/app/views/admin/site_customization/content_blocks/index.html.erb
@@ -0,0 +1,29 @@
+<% provide :title do %>
+ Admin - <%= t("admin.menu.site_customization.content_blocks") %>
+<% end %>
+
+<%= link_to t("admin.site_customization.content_blocks.index.create"), new_admin_site_customization_content_block_path, class: "button float-right margin-right" %>
+<%= t("admin.site_customization.content_blocks.index.title") %>
+
+
+
+
+ | <%= t("admin.site_customization.content_blocks.content_block.name") %> |
+ <%= t("admin.site_customization.content_blocks.content_block.body") %> |
+ |
+
+
+
+ <% @content_blocks.each do |content_block| %>
+
+ | <%= link_to "#{content_block.name} (#{content_block.locale})", edit_admin_site_customization_content_block_path(content_block) %> |
+ <%= content_block.body %> |
+
+ <%= button_to t("admin.site_customization.content_blocks.index.delete"),
+ admin_site_customization_content_block_path(content_block),
+ method: :delete, class: "button hollow alert" %>
+ |
+
+ <% end %>
+
+
diff --git a/app/views/admin/site_customization/content_blocks/new.html.erb b/app/views/admin/site_customization/content_blocks/new.html.erb
new file mode 100644
index 000000000..75ff5f389
--- /dev/null
+++ b/app/views/admin/site_customization/content_blocks/new.html.erb
@@ -0,0 +1,14 @@
+<% provide :title do %>
+ Admin - <%= t("admin.menu.site_customization.content_blocks") %> - <%= t("admin.site_customization.content_blocks.new.title") %>
+<% end %>
+
+<%= link_to admin_site_customization_content_blocks_path, class: "back" do %>
+
+ <%= t("admin.site_customization.content_blocks.new.back") %>
+<% end %>
+
+
+
<%= t("admin.site_customization.content_blocks.new.title") %>
+ <%= render 'form' %>
+
+
diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb
index 4b5e90523..1e5c3dfa5 100644
--- a/app/views/layouts/_footer.html.erb
+++ b/app/views/layouts/_footer.html.erb
@@ -1,5 +1,5 @@
diff --git a/app/views/shared/_top_links.html.erb b/app/views/shared/_top_links.html.erb
index b97499232..8a71130d6 100644
--- a/app/views/shared/_top_links.html.erb
+++ b/app/views/shared/_top_links.html.erb
@@ -1,21 +1,25 @@
-
<%= link_to t("layouts.header.external_link_transparency"),
- t("layouts.header.external_link_transparency_url"),
+ setting['transparency_url'].presence || t("layouts.header.external_link_transparency_url"),
target: "_blank",
title: t('shared.target_blank_html') %>
-
<%= link_to t("layouts.header.external_link_opendata"),
- t("layouts.header.external_link_opendata_url"),
+ setting['opendata_url'].presence || t("layouts.header.external_link_opendata_url"),
target: "_blank",
title: t('shared.target_blank_html') %>
+
<% if setting['blog_url'] %>
-
- <%= link_to setting['blog_url'], title: t('shared.target_blank_html'), target: "_blank" do %>
- <%= t("layouts.header.external_link_blog") %>
- <% end %>
+ <%= link_to t("layouts.header.external_link_blog"),
+ setting['blog_url'],
+ target: "_blank",
+ title: t('shared.target_blank_html') %>
<% end %>
+
+ <%= raw content_block("top_links", I18n.locale) %>
diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml
index bd18422ae..806ddc96f 100644
--- a/config/locales/activerecord.en.yml
+++ b/config/locales/activerecord.en.yml
@@ -46,6 +46,9 @@ en:
site_customization/image:
one: Custom image
other: Custom images
+ site_customization/content_block:
+ one: Custom content block
+ other: Custom content blocks
attributes:
budget:
name: "Name"
@@ -119,6 +122,13 @@ en:
updated_at: Updated at
more_info_flag: Show in more information page
print_content_flag: Print content button
+ site_customization/image:
+ name: Name
+ image: Image
+ site_customization/content_block:
+ name: Name
+ locale: locale
+ body: Body
errors:
models:
user:
diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml
index f3397ef19..43c40110e 100644
--- a/config/locales/activerecord.es.yml
+++ b/config/locales/activerecord.es.yml
@@ -46,6 +46,9 @@ es:
site_customization/image:
one: Imagen
other: Imágenes
+ site_customization/content_block:
+ one: Bloque
+ other: Bloques
attributes:
budget:
name: "Nombre"
@@ -114,6 +117,13 @@ es:
updated_at: última actualización
more_info_flag: Mostrar en la página de más información
print_content_flag: Botón de imprimir contenido
+ site_customization/image:
+ name: Nombre
+ image: Imagen
+ site_customization/content_block:
+ name: Nombre
+ locale: Idioma
+ body: Contenido
errors:
models:
user:
diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml
index 5f84f69d3..12911c410 100755
--- a/config/locales/admin.en.yml
+++ b/config/locales/admin.en.yml
@@ -210,6 +210,7 @@ en:
site_customization:
pages: Custom Pages
images: Custom Images
+ content_blocks: Custom content blocks
moderators:
index:
title: Moderators
@@ -479,6 +480,31 @@ en:
sms_code_not_confirmed: Has not confirmed the sms code
title: Incomplete verifications
site_customization:
+ content_blocks:
+ create:
+ notice: Content block created successfully
+ error: Content block couldn't be created
+ update:
+ notice: Content block updated successfully
+ error: Content block couldn't be updated
+ destroy:
+ notice: Content block deleted successfully
+ edit:
+ title: Editing content block
+ back: Back
+ errors:
+ form:
+ error: Error
+ index:
+ create: Create new content block
+ delete: Delete
+ title: Content blocks
+ new:
+ back: Back
+ title: Create new content block
+ content_block:
+ body: Body
+ name: Name
images:
index:
title: Custom images
@@ -493,7 +519,7 @@ en:
pages:
create:
notice: Page created successfully
- error: Process couldn't be created
+ error: Page couldn't be created
update:
notice: Page updated successfully
error: Page couldn't be updated
diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml
index f4c834e34..feb72e24c 100644
--- a/config/locales/admin.es.yml
+++ b/config/locales/admin.es.yml
@@ -210,6 +210,7 @@ es:
site_customization:
pages: Personalizar páginas
images: Personalizar imágenes
+ content_blocks: Personalizar bloques
moderators:
index:
title: Moderadores
@@ -479,6 +480,31 @@ es:
sms_code_not_confirmed: No ha introducido su código de seguridad
title: Verificaciones incompletas
site_customization:
+ content_blocks:
+ create:
+ notice: Bloque creado correctamente
+ error: No se ha podido crear el bloque
+ update:
+ notice: Bloque actualizado correctamente
+ error: No se ha podido actualizar el bloque
+ destroy:
+ notice: Bloque borrado correctamente
+ edit:
+ title: Editar bloque
+ back: Volver
+ errors:
+ form:
+ error: Error
+ index:
+ create: Crear nuevo bloque
+ delete: Borrar
+ title: Bloques
+ new:
+ back: Volver
+ title: Crear nuevo bloque
+ content_block:
+ body: Contenido
+ name: Nombre
images:
index:
title: Personalizar imágenes
diff --git a/config/routes.rb b/config/routes.rb
index 51d2a47da..2088c48da 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -226,6 +226,7 @@ Rails.application.routes.draw do
namespace :site_customization do
resources :pages, except: [:show]
resources :images, only: [:index, :update, :destroy]
+ resources :content_blocks, except: [:show]
end
end
diff --git a/db/migrate/20170324101716_create_site_customization_content_blocks.rb b/db/migrate/20170324101716_create_site_customization_content_blocks.rb
new file mode 100644
index 000000000..d0e4c3822
--- /dev/null
+++ b/db/migrate/20170324101716_create_site_customization_content_blocks.rb
@@ -0,0 +1,13 @@
+class CreateSiteCustomizationContentBlocks < ActiveRecord::Migration
+ def change
+ create_table :site_customization_content_blocks do |t|
+ t.string :name
+ t.string :locale
+ t.text :body
+
+ t.timestamps null: false
+ end
+
+ add_index :site_customization_content_blocks, [:name, :locale], unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 938fbb9ec..e92a4121c 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170322145702) do
+ActiveRecord::Schema.define(version: 20170324101716) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -439,6 +439,16 @@ ActiveRecord::Schema.define(version: 20170322145702) do
t.datetime "updated_at"
end
+ create_table "site_customization_content_blocks", force: :cascade do |t|
+ t.string "name"
+ t.string "locale"
+ t.text "body"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ add_index "site_customization_content_blocks", ["name", "locale"], name: "index_site_customization_content_blocks_on_name_and_locale", unique: true, using: :btree
+
create_table "site_customization_images", force: :cascade do |t|
t.string "name", null: false
t.string "image_file_name"
diff --git a/db/seeds.rb b/db/seeds.rb
index be7960dff..bbbf0eb01 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -51,6 +51,8 @@ Setting["facebook_handle"] = nil
Setting["youtube_handle"] = nil
Setting["telegram_handle"] = nil
Setting["blog_url"] = nil
+Setting["transparency_url"] = nil
+Setting["opendata_url"] = "/opendata"
# Public-facing URL of the app.
Setting["url"] = "http://example.com"
diff --git a/spec/factories.rb b/spec/factories.rb
index 20a1f463c..116b612d5 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -468,4 +468,10 @@ FactoryGirl.define do
more_info_flag true
end
end
+
+ factory :site_customization_content_block, class: 'SiteCustomization::ContentBlock' do
+ name "top_links"
+ locale "en"
+ body "Some top links content"
+ end
end
diff --git a/spec/features/admin/site_customization/content_blocks_spec.rb b/spec/features/admin/site_customization/content_blocks_spec.rb
new file mode 100644
index 000000000..cc5dca34c
--- /dev/null
+++ b/spec/features/admin/site_customization/content_blocks_spec.rb
@@ -0,0 +1,107 @@
+require 'rails_helper'
+
+feature "Admin custom content blocks" do
+
+ background do
+ admin = create(:administrator)
+ login_as(admin.user)
+ end
+
+ scenario "Index" do
+ block = create(:site_customization_content_block)
+ visit admin_site_customization_content_blocks_path
+
+ expect(page).to have_content(block.name)
+ expect(page).to have_content(block.body)
+ end
+
+ context "Create" do
+ scenario "Valid custom block" do
+ visit admin_root_path
+
+ within("#side_menu") do
+ click_link "Custom content blocks"
+ end
+
+ expect(page).to_not have_content "footer (es)"
+
+ click_link "Create new content block"
+
+ select "footer", from: "site_customization_content_block_name"
+ select "es", from: "site_customization_content_block_locale"
+ fill_in "site_customization_content_block_body", with: "Some custom content"
+
+ click_button "Create Custom content block"
+
+ expect(page).to have_content "footer (es)"
+ expect(page).to have_content "Some custom content"
+ end
+
+ scenario "Invalid custom block" do
+ block = create(:site_customization_content_block)
+
+ visit admin_root_path
+
+ within("#side_menu") do
+ click_link "Custom content blocks"
+ end
+
+ expect(page).to have_content "top_links (en)"
+
+ click_link "Create new content block"
+
+ select "top_links", from: "site_customization_content_block_name"
+ select "en", from: "site_customization_content_block_locale"
+ fill_in "site_customization_content_block_body", with: "Some custom content"
+
+ click_button "Create Custom content block"
+
+ expect(page).to have_content "Content block couldn't be created"
+ expect(page).to have_content "has already been taken"
+ end
+ end
+
+ context "Update" do
+ scenario "Valid custom block" do
+ block = create(:site_customization_content_block)
+ visit admin_root_path
+
+ within("#side_menu") do
+ click_link "Custom content blocks"
+ end
+
+ click_link "top_links (en)"
+
+ fill_in "site_customization_content_block_body", with: "Some other custom content"
+ click_button "Update Custom content block"
+
+ expect(page).to have_content "Content block updated successfully"
+ expect(page).to have_content "Some other custom content"
+ end
+ end
+
+ context "Delete" do
+ scenario "From index page" do
+ block = create(:site_customization_content_block)
+ visit admin_site_customization_content_blocks_path
+
+ expect(page).to have_content("#{block.name} (#{block.locale})")
+ expect(page).to have_content(block.body)
+
+ click_button "Delete"
+
+ expect(page).to_not have_content("#{block.name} (#{block.locale})")
+ expect(page).to_not have_content(block.body)
+ end
+
+ scenario "From edit page" do
+ block = create(:site_customization_content_block)
+ visit edit_admin_site_customization_content_block_path(block)
+
+ click_button "Delete"
+
+ expect(page).to_not have_content("#{block.name} (#{block.locale})")
+ expect(page).to_not have_content(block.body)
+ end
+ end
+end
diff --git a/spec/features/site_customization/content_blocks_spec.rb b/spec/features/site_customization/content_blocks_spec.rb
new file mode 100644
index 000000000..995982063
--- /dev/null
+++ b/spec/features/site_customization/content_blocks_spec.rb
@@ -0,0 +1,33 @@
+require 'rails_helper'
+
+feature "Custom content blocks" do
+ scenario "top links" do
+ create(:site_customization_content_block, name: "top_links", locale: "en", body: "content for top links")
+ create(:site_customization_content_block, name: "top_links", locale: "es", body: "contenido para top links")
+
+ visit "/?locale=en"
+
+ expect(page).to have_content("content for top links")
+ expect(page).to_not have_content("contenido para top links")
+
+ visit "/?locale=es"
+
+ expect(page).to have_content("contenido para top links")
+ expect(page).to_not have_content("content for top links")
+ end
+
+ scenario "footer" do
+ create(:site_customization_content_block, name: "footer", locale: "en", body: "content for footer")
+ create(:site_customization_content_block, name: "footer", locale: "es", body: "contenido para footer")
+
+ visit "/?locale=en"
+
+ expect(page).to have_content("content for footer")
+ expect(page).to_not have_content("contenido para footer")
+
+ visit "/?locale=es"
+
+ expect(page).to have_content("contenido para footer")
+ expect(page).to_not have_content("content for footer")
+ end
+end
diff --git a/spec/models/site_customization/content_block_spec.rb b/spec/models/site_customization/content_block_spec.rb
new file mode 100644
index 000000000..2eb6279f6
--- /dev/null
+++ b/spec/models/site_customization/content_block_spec.rb
@@ -0,0 +1,20 @@
+require 'rails_helper'
+
+RSpec.describe SiteCustomization::ContentBlock, type: :model do
+ let(:block) { build(:site_customization_content_block) }
+
+ it "should be valid" do
+ expect(block).to be_valid
+ end
+
+ it "name is unique per locale" do
+ create(:site_customization_content_block, name: "top_links", locale: "en")
+ invalid_block = build(:site_customization_content_block, name: "top_links", locale: "en")
+
+ expect(invalid_block).to be_invalid
+ expect(invalid_block.errors.full_messages).to include("Name has already been taken")
+
+ valid_block = build(:site_customization_content_block, name: "top_links", locale: "es")
+ expect(valid_block).to be_valid
+ end
+end