Added feature to add content block to headings in sidebar -- rebase

This commit is contained in:
Milber Champutiz Burbano
2018-11-27 09:37:35 -05:00
parent e23fbc8d58
commit 9f455b9165
25 changed files with 289 additions and 48 deletions

View File

@@ -33,7 +33,7 @@ class Admin::BudgetHeadingsController < Admin::BaseController
private
def budget_heading_params
params.require(:budget_heading).permit(:name, :price, :population)
params.require(:budget_heading).permit(:name, :price, :population, :allow_custom_content)
end
end

View File

@@ -1,12 +1,23 @@
class Admin::SiteCustomization::ContentBlocksController < Admin::SiteCustomization::BaseController
load_and_authorize_resource :content_block, class: "SiteCustomization::ContentBlock"
load_and_authorize_resource :content_block, class: "SiteCustomization::ContentBlock",
except: [:delete_heading_content_block, :edit_heading_content_block, :update_heading_content_block]
def index
@content_blocks = SiteCustomization::ContentBlock.order(:name, :locale)
@headings_content_blocks = Budget::ContentBlock.all
end
def create
if @content_block.save
if is_heading_content_block?(@content_block.name)
heading_content_block = new_heading_content_block
if heading_content_block.save
notice = t('admin.site_customization.content_blocks.create.notice')
redirect_to admin_site_customization_content_blocks_path, notice: notice
else
flash.now[:error] = t('admin.site_customization.content_blocks.create.error')
render :new
end
elsif @content_block.save
notice = t('admin.site_customization.content_blocks.create.notice')
redirect_to admin_site_customization_content_blocks_path, notice: notice
else
@@ -15,8 +26,22 @@ class Admin::SiteCustomization::ContentBlocksController < Admin::SiteCustomizati
end
end
def edit
@selected_content_block = (@content_block.is_a? SiteCustomization::ContentBlock) ? @content_block.name : "hcb_#{ @content_block.heading_id }"
end
def update
if @content_block.update(content_block_params)
if is_heading_content_block?(params[:site_customization_content_block][:name])
heading_content_block = new_heading_content_block
if heading_content_block.save
@content_block.destroy
notice = t('admin.site_customization.content_blocks.create.notice')
redirect_to admin_site_customization_content_blocks_path, notice: notice
else
flash.now[:error] = t('admin.site_customization.content_blocks.create.error')
render :new
end
elsif @content_block.update(content_block_params)
notice = t('admin.site_customization.content_blocks.update.notice')
redirect_to admin_site_customization_content_blocks_path, notice: notice
else
@@ -31,6 +56,48 @@ class Admin::SiteCustomization::ContentBlocksController < Admin::SiteCustomizati
redirect_to admin_site_customization_content_blocks_path, notice: notice
end
def delete_heading_content_block
heading_content_block = Budget::ContentBlock.find(params[:id])
heading_content_block.destroy if heading_content_block
notice = t('admin.site_customization.content_blocks.destroy.notice')
redirect_to admin_site_customization_content_blocks_path, notice: notice
end
def edit_heading_content_block
@content_block = Budget::ContentBlock.find(params[:id])
@selected_content_block = (@content_block.is_a? Budget::ContentBlock) ? "hcb_#{ @content_block.heading_id }" : @content_block.heading.name
@is_heading_content_block = true
render :edit
end
def update_heading_content_block
heading_content_block = Budget::ContentBlock.find(params[:id])
if is_heading_content_block?(params[:name])
heading_content_block.locale = params[:locale]
heading_content_block.body = params[:body]
if heading_content_block.save
notice = t('admin.site_customization.content_blocks.update.notice')
redirect_to admin_site_customization_content_blocks_path, notice: notice
else
flash.now[:error] = t('admin.site_customization.content_blocks.update.error')
render :edit
end
else
@content_block = SiteCustomization::ContentBlock.new
@content_block.name = params[:name]
@content_block.locale = params[:locale]
@content_block.body = params[:body]
if @content_block.save
heading_content_block.destroy
notice = t('admin.site_customization.content_blocks.update.notice')
redirect_to admin_site_customization_content_blocks_path, notice: notice
else
flash.now[:error] = t('admin.site_customization.content_blocks.update.error')
render :edit
end
end
end
private
def content_block_params
@@ -40,4 +107,16 @@ class Admin::SiteCustomization::ContentBlocksController < Admin::SiteCustomizati
:body
)
end
def is_heading_content_block?(name)
name.start_with?('hcb_')
end
def new_heading_content_block
heading_content_block = Budget::ContentBlock.new
heading_content_block.body = params[:site_customization_content_block][:body]
heading_content_block.locale = params[:site_customization_content_block][:locale]
heading_content_block.heading_id = params[:site_customization_content_block][:name].sub('hcb_', '').to_i
heading_content_block
end
end

View File

@@ -17,6 +17,7 @@ module Budgets
before_action :load_categories, only: [:index, :new, :create]
before_action :set_default_budget_filter, only: :index
before_action :set_view, only: :index
before_action :load_content_blocks, only: :index
skip_authorization_check only: :json_data
@@ -149,6 +150,10 @@ module Budgets
@categories = ActsAsTaggableOn::Tag.category.order(:name)
end
def load_content_blocks
@heading_content_blocks = @heading.content_blocks.where(locale: I18n.locale) if @heading
end
def tag_cloud
TagCloud.new(Budget::Investment, params[:search])
end

View File

@@ -0,0 +1,9 @@
module ContentBlocksHelper
def valid_blocks
options = SiteCustomization::ContentBlock::VALID_BLOCKS.map { |key| [t("admin.site_customization.content_blocks.content_block.names.#{key}"), key] }
Budget::Heading.allow_custom_content.each do |heading|
options.push([heading.name, "hcb_#{heading.id}"])
end
options
end
end

View File

@@ -0,0 +1,9 @@
class Budget
class ContentBlock < ActiveRecord::Base
validates :locale, presence: true, inclusion: { in: I18n.available_locales.map(&:to_s) }
validates :heading, presence: true
validates_uniqueness_of :heading, scope: :locale
belongs_to :heading
end
end

View File

@@ -5,6 +5,7 @@ class Budget
belongs_to :group
has_many :investments
has_many :content_blocks
validates :group_id, presence: true
validates :name, presence: true, uniqueness: { if: :name_exists_in_budget_headings }
@@ -15,6 +16,7 @@ class Budget
delegate :budget, :budget_id, to: :group, allow_nil: true
scope :order_by_group_name, -> { includes(:group).order('budget_groups.name', 'budget_headings.name') }
scope :allow_custom_content, -> { where(allow_custom_content: true).order(:name) }
def name_scoped_by_group
group.single_heading_group? ? name : "#{group.name}: #{name}"

View File

@@ -1,7 +1,7 @@
<table>
<thead>
<tr>
<th colspan="4" class="with-button">
<th colspan="5" class="with-button">
<%= content_tag(:span, group.name, class:"group-toggle-#{group.id}", id:"group-name-#{group.id}") %>
<%= content_tag(:span, (render 'admin/budgets/max_headings_label', current: group.max_votable_headings, max: group.headings.count, group: group if group.max_votable_headings), class:"small group-toggle-#{group.id}", id:"max-heading-label-#{group.id}") %>
@@ -17,6 +17,7 @@
<th><%= t("admin.budgets.form.table_heading") %></th>
<th class="text-right"><%= t("admin.budgets.form.table_amount") %></th>
<th class="text-right"><%= t("admin.budgets.form.table_population") %></th>
<th class="text-right"><%= t("admin.budgets.form.table_allow_custom_contents") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
<% end %>
@@ -26,7 +27,7 @@
<% if headings.blank? %>
<tr>
<td colspan="4">
<td colspan="5">
<div class="callout primary">
<%= t("admin.budgets.form.no_heading") %>
</div>
@@ -35,7 +36,7 @@
<% end %>
<tr id="group-<%= group.id %>-new-heading-form" style="display:none">
<td colspan="4">
<td colspan="5">
<%= render "admin/budgets/heading_form", group: group, budget: @budget, heading: Budget::Heading.new %>
</td>
</tr>

View File

@@ -8,6 +8,9 @@
<td class="text-right">
<%= heading.population %>
</td>
<td class="text-right">
<%= heading.allow_custom_content ? t("true_value") : t("false_value") %>
</td>
<td>
<%= link_to t("admin.actions.edit"),
edit_admin_budget_budget_group_budget_heading_path(budget_id: group.budget.id, budget_group_id: group.id, id: heading.id),

View File

@@ -35,5 +35,11 @@
</div>
</div>
<div class="row">
<div class="small-12 medium-6 column">
<%= f.check_box :allow_custom_content, label: t('admin.budgets.form.allow_content_block') %>
</div>
</div>
<%= f.submit t("admin.budgets.form.save_heading"), class: "button success" %>
<% end %>

View File

@@ -1,35 +1,5 @@
<%= form_for [:admin, @content_block], html: {class: "edit_page", data: {watch_changes: true}} do |f| %>
<% if @content_block.errors.any? %>
<div id="error_explanation" data-alert class="callout alert" data-closable>
<button class="close-button" aria-label="<%= t("application.close") %>" type="button" data-close>
<span aria-hidden="true">&times;</span>
</button>
<strong>
<%= @content_block.errors.count %>
<%= t("admin.site_customization.content_blocks.errors.form.error", count: @content_block.errors.count) %>
</strong>
</div>
<% end %>
<div class="small-12 medium-6 column">
<%= f.label :name %>
<%= f.select :name, SiteCustomization::ContentBlock::VALID_BLOCKS.map { |key| [t("admin.site_customization.content_blocks.content_block.names.#{key}"), key] }, label: false %>
</div>
<div class="small-12 medium-6 column">
<%= f.label :locale %>
<%= f.select :locale, I18n.available_locales, label: false %>
</div>
<div class="small-12 column">
<%= f.label :body %>
<%= f.text_area :body, label: false, rows: 10 %>
<div class="small-12 medium-6 large-3">
<%= f.submit class: "button success expanded" %>
</div>
</div>
<% if @is_heading_content_block %>
<%= render 'form_heading_content_block' %>
<% else %>
<%= render 'form_content_block' %>
<% end %>

View File

@@ -0,0 +1,35 @@
<%= form_for [:admin, @content_block], html: {class: "edit_page", data: {watch_changes: true}} do |f| %>
<% if @content_block.errors.any? %>
<div id="error_explanation" data-alert class="callout alert" data-closable>
<button class="close-button" aria-label="<%= t("application.close") %>" type="button" data-close>
<span aria-hidden="true">&times;</span>
</button>
<strong>
<%= @content_block.errors.count %>
<%= t("admin.site_customization.content_blocks.errors.form.error", count: @content_block.errors.count) %>
</strong>
</div>
<% end %>
<div class="small-12 medium-6 column">
<%= f.label :name %>
<%= f.select :name, options_for_select(valid_blocks, @selected_content_block), label: false %>
</div>
<div class="small-12 medium-6 column">
<%= f.label :locale %>
<%= f.select :locale, I18n.available_locales, label: false %>
</div>
<div class="small-12 column">
<%= f.label :body %>
<%= f.text_area :body, label: false, rows: 10 %>
<div class="small-12 medium-6 large-3">
<%= f.submit class: "button success expanded" %>
</div>
</div>
<% end %>

View File

@@ -0,0 +1,29 @@
<%= form_tag(admin_site_customization_update_heading_content_block_path(@content_block.id), method: "put") do %>
<% if @content_block.errors.any? %>
<div id="error_explanation" data-alert class="callout alert" data-closable>
<button class="close-button" aria-label="<%= t("application.close") %>" type="button" data-close>
<span aria-hidden="true">&times;</span>
</button>
<strong>
<%= @content_block.errors.count %>
<%= t("admin.site_customization.content_blocks.errors.form.error", count: @content_block.errors.count) %>
</strong>
</div>
<% end %>
<div class="small-12 medium-6 column">
<%= label_tag :name %>
<%= select_tag :name, options_for_select(valid_blocks, @selected_content_block), label: false %>
</div>
<div class="small-12 medium-6 column">
<%= label_tag :locale %>
<%= select_tag :locale, options_for_select(I18n.available_locales, @content_block.locale.to_sym), label: false %>
</div>
<div class="small-12 column">
<%= label_tag :body %>
<%= text_area_tag :body, @content_block.body, rows: 10 %>
<div class="small-12 medium-6 large-3">
<%= button_tag t("admin.menu.site_customization.buttons.content_block.update"), class: "button success expanded" %>
</div>
</div>
<% end %>

View File

@@ -1,11 +1,11 @@
<% provide :title do %>
<%= t("admin.header.title") %> - <%= t("admin.menu.site_customization.content_blocks") %> - <%= @content_block.name %> (<%= @content_block.locale %>)
<%= t("admin.header.title") %> - <%= t("admin.menu.site_customization.content_blocks") %> - <%= @content_block.try(:name) || @content_block.heading.try(:name) %> (<%= @content_block.locale %>)
<% end %>
<%= back_link_to admin_site_customization_content_blocks_path %>
<%= link_to t("admin.site_customization.content_blocks.index.delete"),
admin_site_customization_content_block_path(@content_block),
(@is_heading_content_block ? admin_site_customization_delete_heading_content_block_path(@content_block.id) : admin_site_customization_content_block_path(@content_block)),
method: :delete,
class: "delete float-right" %>

View File

@@ -17,7 +17,7 @@
<p class="margin-top"><%= t("admin.site_customization.content_blocks.footer_html") %></p>
<% if @content_blocks.any? %>
<% if @content_blocks.any? || @headings_content_blocks.any? %>
<table class="cms-page-list">
<thead>
<tr>
@@ -38,10 +38,21 @@
</td>
</tr>
<% end %>
<% @headings_content_blocks.each do |content_block| %>
<tr id="<%= dom_id(content_block) %>">
<td><%= link_to "#{content_block.heading.name} (#{content_block.locale})", admin_site_customization_edit_heading_content_block_path(content_block) %></td>
<td><%= content_block.body.html_safe %></td>
<td>
<%= link_to t("admin.site_customization.content_blocks.index.delete"),
admin_site_customization_delete_heading_content_block_path(content_block.id),
method: :delete, class: "button hollow alert" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<div class="callout primary">
<%= t("admin.site_customization.content_blocks.no_blocks") %>
</div>
</div-->
<% end %>

View File

@@ -0,0 +1,9 @@
<% if @heading.allow_custom_content %>
<br>
<ul id="content_block" class="no-bullet categories">
<% @heading_content_blocks.each do |content_block| %>
<%= raw content_block.body %>
<% end %>
</ul>
<% end %>

View File

@@ -21,9 +21,13 @@
</p>
<% end %>
<% if @heading && !@heading.content_blocks.where(locale: I18n.locale).empty? %>
<%= render 'budgets/investments/content_blocks' %>
<% end %>
<%= render "shared/tag_cloud", taggable: 'budget/investment' %>
<%= render 'budgets/investments/categories' %>
<% if @heading && can?(:show, @ballot) %>
<div class="sidebar-divider"></div>

View File

@@ -1,4 +1,6 @@
en:
true_value: 'Yes'
false_value: 'No'
admin:
header:
title: Administration
@@ -128,9 +130,11 @@ en:
table_heading: Heading
table_amount: Amount
table_population: Population
table_allow_custom_contents: Custom content allowed
population_info: "Budget Heading population field is used for Statistic purposes at the end of the Budget to show for each Heading that represents an area with population what percentage voted. The field is optional so you can leave it empty if it doesn't apply."
max_votable_headings: "Maximum number of headings in which a user can vote"
current_of_max_headings: "%{current} of %{max}"
allow_content_block: Allow content block
winners:
calculate: Calculate Winner Investments
calculated: Winners being calculated, it may take a minute.
@@ -576,6 +580,8 @@ en:
welcome: "Welcome"
buttons:
save: "Save"
content_block:
update: "Update Block"
title_moderated_content: Moderated content
title_budgets: Budgets
title_polls: Polls

View File

@@ -1,4 +1,6 @@
es:
true_value: 'Si'
false_value: 'No'
admin:
header:
title: Administración
@@ -131,6 +133,7 @@ es:
population_info: "El campo población de las partidas presupuestarias se usa con fines estadísticos únicamente, con el objetivo de mostrar el porcentaje de votos habidos en cada partida que represente un área con población. Es un campo opcional, así que puedes dejarlo en blanco si no aplica."
max_votable_headings: "Máximo número de partidas en que un usuario puede votar"
current_of_max_headings: "%{current} de %{max}"
allow_content_block: Permite bloque de contenidos
winners:
calculate: Calcular proyectos ganadores
calculated: Calculando ganadores, puede tardar un minuto.
@@ -575,6 +578,8 @@ es:
welcome: "Bienvenido/a"
buttons:
save: "Guardar cambios"
content_block:
update: "Actualizar Bloque"
title_moderated_content: Contenido moderado
title_budgets: Presupuestos
title_polls: Votaciones

View File

@@ -211,6 +211,9 @@ namespace :admin do
resources :pages, except: [:show]
resources :images, only: [:index, :update, :destroy]
resources :content_blocks, except: [:show]
delete '/heading_content_blocks/:id', to: 'content_blocks#delete_heading_content_block', as: 'delete_heading_content_block'
get '/edit_heading_content_blocks/:id', to: 'content_blocks#edit_heading_content_block', as: 'edit_heading_content_block'
put '/update_heading_content_blocks/:id', to: 'content_blocks#update_heading_content_block', as: 'update_heading_content_block'
resources :information_texts, only: [:index] do
post :update, on: :collection
end

View File

@@ -0,0 +1,5 @@
class AddAllowCustomContentToHeadings < ActiveRecord::Migration
def change
add_column :budget_headings, :allow_custom_content, :boolean, default: false
end
end

View File

@@ -0,0 +1,10 @@
class CreateHeadingContentBlocks < ActiveRecord::Migration
def change
create_table :budget_content_blocks do |t|
t.integer :heading_id, index: true, foreign_key: true
t.text :body
t.string :locale
t.timestamps null: false
end
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20181016204729) do
ActiveRecord::Schema.define(version: 20181108142513) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -141,6 +141,16 @@ ActiveRecord::Schema.define(version: 20181016204729) do
t.datetime "updated_at", null: false
end
create_table "budget_content_blocks", force: :cascade do |t|
t.integer "heading_id"
t.text "body"
t.string "locale"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "budget_content_blocks", ["heading_id"], name: "index_budget_content_blocks_on_heading_id", using: :btree
create_table "budget_groups", force: :cascade do |t|
t.integer "budget_id"
t.string "name", limit: 50
@@ -156,6 +166,7 @@ ActiveRecord::Schema.define(version: 20181016204729) do
t.integer "price", limit: 8
t.integer "population"
t.string "slug"
t.boolean "allow_custom_content", default: false
end
add_index "budget_headings", ["group_id"], name: "index_budget_headings_on_group_id", using: :btree

View File

@@ -209,4 +209,10 @@ FactoryBot.define do
factory :valuator_group, class: ValuatorGroup do
sequence(:name) { |n| "Valuator Group #{n}" }
end
factory :heading_content_block, class: 'Budget::ContentBlock' do
association :heading, factory: :budget_heading
locale 'en'
body 'Some heading contents'
end
end

View File

@@ -9,10 +9,13 @@ feature "Admin custom content blocks" do
scenario "Index" do
block = create(:site_customization_content_block)
heading_block = create(:heading_content_block)
visit admin_site_customization_content_blocks_path
expect(page).to have_content(block.name)
expect(page).to have_content(block.body)
expect(page).to have_content(heading_block.heading.name)
expect(page).to have_content(heading_block.body)
end
context "Create" do

View File

@@ -0,0 +1,20 @@
require 'rails_helper'
RSpec.describe Budget::ContentBlock do
let(:block) { build(:heading_content_block) }
it "is valid" do
expect(block).to be_valid
end
it "Heading is unique per locale" do
heading_content_block_en = create(:heading_content_block, locale: "en")
invalid_block = build(:heading_content_block, heading: heading_content_block_en.heading, locale: "en")
expect(invalid_block).to be_invalid
expect(invalid_block.errors.full_messages).to include("Heading has already been taken")
valid_block = build(:heading_content_block, heading: heading_content_block_en.heading, locale: "es")
expect(valid_block).to be_valid
end
end