Collaborative legislation summary

This commit is contained in:
lalo
2019-06-11 14:30:34 +02:00
200 changed files with 2879 additions and 714 deletions

1
.gitignore vendored
View File

@@ -38,4 +38,3 @@
public/sitemap.xml
public/system/
/public/ckeditor_assets/

View File

@@ -3,6 +3,77 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
## [1.0.0](https://github.com/consul/consul/tree/1.0.0) (2019-06-10)
[Full Changelog](https://github.com/consul/consul/compare/1.0.0-beta...1.0.0)
### Added
- **Accounts:** Add description field to administrator users like evaluators description [\#3389](https://github.com/consul/consul/pull/3389)
- **Admin:** Add document uploads from admin section [\#3466](https://github.com/consul/consul/pull/3466)
- **Admin:** Images and documents settings [\#3585](https://github.com/consul/consul/pull/3585)
- **Budgets:** notify by email new evaluation comments [\#3413](https://github.com/consul/consul/pull/3413)
- **Installation:** Add deploy-secrets.yml.example file [\#3516](https://github.com/consul/consul/pull/3516)
- **Installation:** Add new settings automatically on every deployment [\#3576](https://github.com/consul/consul/pull/3576)
- **Installation:** Add task to upgrade to a new release [\#3590](https://github.com/consul/consul/pull/3590)
- **Legislations:** Create Legislation::PeopleProposal model [\#3591](https://github.com/consul/consul/pull/3591)
- **Translations:** Update translations from Crowdin [\#3378](https://github.com/consul/consul/pull/3378)
- **Translations:** Admin basic customization texts [\#3488](https://github.com/consul/consul/pull/3488)
- **Translations:** Add Bosnian, Croatian, Czech, Danish, Greek, and Turkish locales [\#3571](https://github.com/consul/consul/pull/3571)
- **Newsletters:** Proposals authors user segment [\#3507](https://github.com/consul/consul/pull/3507)
- **Polls:** Add slug to polls [\#3504](https://github.com/consul/consul/pull/3504)
- **Statistics:** Add budget stats [\#3438](https://github.com/consul/consul/pull/3438)
- **Statistics:** Add admin budget stats [\#3499](https://github.com/consul/consul/pull/3499)
- **Statistics:** Add options to show advanced stats [\#3520](https://github.com/consul/consul/pull/3520)
### Changed
- **Accounts:** Change devise configuration [\#3561](https://github.com/consul/consul/pull/3561)
- **Admin:** Show count of votes associated to verified signatures [\#2616](https://github.com/consul/consul/pull/2616)
- **Budgets:** Don't destroy budgets with an associated poll [\#3492](https://github.com/consul/consul/pull/3492)
- **Budgets:** Add task to regenerate ballot\_lines\_count cache [\#3563](https://github.com/consul/consul/pull/3563)
- **Dashboard:** Hide polls created by users from proposals dashboard on admin poll index [\#3572](https://github.com/consul/consul/pull/3572)
- **Dashboard:** Allow users to delete dashboard polls [\#3574](https://github.com/consul/consul/pull/3574)
- **Maintenance:** Add Rails 5.1 compatibility [\#3562](https://github.com/consul/consul/pull/3562)
- **Maintenance:** Update migrations and schema file [\#3598](https://github.com/consul/consul/pull/3598)
- **Maintenance-Refactoring:** Refactor admin/debates and admin/comments to hidden [\#3376](https://github.com/consul/consul/pull/3376)
- **Maintenance-Refactoring:** Simplify stats caching [\#3510](https://github.com/consul/consul/pull/3510)
- **Maintenance-Refactoring:** Refactor gender and age stats methods [\#3511](https://github.com/consul/consul/pull/3511)
- **Maintenance-Refactoring:** Simplify link to poll [\#3519](https://github.com/consul/consul/pull/3519)
- **Maintenance-Refactoring:** Extract partial with mobile sticky content [\#3577](https://github.com/consul/consul/pull/3577)
- **Maintenance-Refactoring:** Use find instead of find by [\#3580](https://github.com/consul/consul/pull/3580)
- **Maintenance-Rubocop:** Allow lines to be 110 characters long by Rubocop [\#3529](https://github.com/consul/consul/pull/3529)
- **Maintenance-Seeds:** Simplify settings seeds [\#3564](https://github.com/consul/consul/pull/3564)
- **Polls:** Display all polls for current booth [\#3361](https://github.com/consul/consul/pull/3361)
- **Polls:** Allow delete polls with associated questions and answers [\#3476](https://github.com/consul/consul/pull/3476)
- **Polls:** Remove redirect for poll officers [\#3506](https://github.com/consul/consul/pull/3506)
- **Polls:** Remove token on views [\#3539](https://github.com/consul/consul/pull/3539)
- **Proposals:** Remove question and external_url fields from proposals and legislation proposals [\#3397](https://github.com/consul/consul/pull/3397)
- **Proposals:** Proposals support on mobile [\#3515](https://github.com/consul/consul/pull/3515)
- **Proposals:** Make proposals to be selected by administrators [\#3567](https://github.com/consul/consul/pull/3567)
- **Statistics:** Improve poll stats [\#3503](https://github.com/consul/consul/pull/3503)
- **Statistics:** Change stats layout [\#3512](https://github.com/consul/consul/pull/3512)
- **UX/UI:** Improve help texts on Admin UI [\#3508](https://github.com/consul/consul/pull/3508)
- **UX/UI:** Users menu [\#3509](https://github.com/consul/consul/pull/3509)
- **UX/UI:** Add help texs, links and new message section to improve UX [\#3573](https://github.com/consul/consul/pull/3573)
### Fixed
- **Budgets:** Don't show links to disabled budget results [\#3592](https://github.com/consul/consul/pull/3592)
- **Legislations:** Fix order in annotation comments with same score [\#3565](https://github.com/consul/consul/pull/3565)
- **Maintenance:** Fix obsolete `respond\_with\_bip` usage [\#3483](https://github.com/consul/consul/pull/3483)
- **Maintenance:** Remove Rspec deprecation warning [\#3530](https://github.com/consul/consul/pull/3530)
- **Maintenance:** Fix column order in schema file [\#3533](https://github.com/consul/consul/pull/3533)
- **Maintenance:** Fix indentation in schema file [\#3595](https://github.com/consul/consul/pull/3595)
- **Maintenance-Specs:** Fix typo in budget executions spec [\#3486](https://github.com/consul/consul/pull/3486)
- **Maintenance-Specs:** Remove unused \(and flaky\) card code and its spec [\#3487](https://github.com/consul/consul/pull/3487)
- **Maintenance-Specs:** Resize Capybara window back to its original size [\#3534](https://github.com/consul/consul/pull/3534)
- **Maintenance-Specs:** Check the comment is present after commenting [\#3596](https://github.com/consul/consul/pull/3596)
- **Maintenance-Specs:** Reset globalize fallbacks before every test [\#3601](https://github.com/consul/consul/pull/3601)
- **Multi-language:** Avoid duplicate records using translations [\#3581](https://github.com/consul/consul/pull/3581)
- **Polls:** Fix valid votes labels [\#3570](https://github.com/consul/consul/pull/3570)
- **Polls:** Show name and email for deleted poll officer's user account [\#3587](https://github.com/consul/consul/pull/3587)
- **UX/UI:** Always use map image from admin site customization images [\#3472](https://github.com/consul/consul/pull/3472)
### Removed
- **Maintenance-Deprecated:** Delete spending proposals [\#3569](https://github.com/consul/consul/pull/3569)
## [1.0.0-beta](https://github.com/consul/consul/compare/v0.19...1.0.0-beta) (2019-04-29)
### Added
@@ -782,7 +853,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Rails 4.2.6
- Ruby 2.2.3
[Unreleased]: https://github.com/consul/consul/compare/1.0.0-beta...consul:master
[Unreleased]: https://github.com/consul/consul/compare/1.0.0...consul:master
[1.0.0]: https://github.com/consul/consul/compare/1.0.0-beta...1.0.0
[1.0.0-beta]: https://github.com/consul/consul/compare/v0.19...1.0.0-beta
[0.19.0]: https://github.com/consul/consul/compare/v0.18...v.019
[0.18.0]: https://github.com/consul/consul/compare/v0.17...v.018

View File

@@ -414,6 +414,15 @@ code {
word-break: break-all;
}
.content-type {
white-space: nowrap;
padding-right: $line-height;
label {
margin-left: 0 !important;
}
}
// 02. Sidebar
// -----------

View File

@@ -28,4 +28,22 @@ class Admin::AdministratorsController < Admin::BaseController
redirect_to admin_administrators_path
end
def edit
end
def update
if @administrator.update(update_administrator_params)
notice = t("admin.administrators.form.updated")
redirect_to admin_administrators_path, notice: notice
else
render :edit
end
end
private
def update_administrator_params
params.require(:administrator).permit(:description)
end
end

View File

@@ -46,11 +46,11 @@ class Admin::BudgetGroupsController < Admin::BaseController
private
def load_budget
@budget = Budget.includes(:groups).find(params[:budget_id])
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def load_group
@group = @budget.groups.find(params[:id])
@group = @budget.groups.find_by_slug_or_id! params[:id]
end
def groups_index

View File

@@ -47,15 +47,15 @@ class Admin::BudgetHeadingsController < Admin::BaseController
private
def load_budget
@budget = Budget.includes(:groups).find(params[:budget_id])
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def load_group
@group = @budget.groups.find(params[:group_id])
@group = @budget.groups.find_by_slug_or_id! params[:group_id]
end
def load_heading
@heading = @group.headings.find(params[:id])
@heading = @group.headings.find_by_slug_or_id! params[:id]
end
def headings_index

View File

@@ -83,11 +83,11 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
params.require(:budget_investment)
.permit(:title, :description, :external_url, :heading_id, :administrator_id, :tag_list,
:valuation_tag_list, :incompatible, :visible_to_valuators, :selected,
valuator_ids: [], valuator_group_ids: [])
:milestone_tag_list, valuator_ids: [], valuator_group_ids: [])
end
def load_budget
@budget = Budget.includes(:groups).find(params[:budget_id])
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def load_investment

View File

@@ -6,6 +6,7 @@ class Admin::BudgetsController < Admin::BaseController
has_filters %w{open finished}, only: :index
before_action :load_budget, except: [:index, :new, :create]
load_and_authorize_resource
def index
@@ -66,4 +67,8 @@ class Admin::BudgetsController < Admin::BaseController
params.require(:budget).permit(*valid_attributes, *report_attributes, translation_params(Budget))
end
def load_budget
@budget = Budget.find_by_slug_or_id! params[:id]
end
end

View File

@@ -1,4 +1,4 @@
class Admin::CommentsController < Admin::BaseController
class Admin::HiddenCommentsController < Admin::BaseController
has_filters %w{without_confirmed_hide all with_confirmed_hide}
before_action :load_comment, only: [:confirm_hide, :restore]

View File

@@ -1,4 +1,4 @@
class Admin::DebatesController < Admin::BaseController
class Admin::HiddenDebatesController < Admin::BaseController
include FeatureFlags
feature_flag :debates
@@ -29,4 +29,4 @@ class Admin::DebatesController < Admin::BaseController
@debate = Debate.with_hidden.find(params[:id])
end
end
end

View File

@@ -14,6 +14,7 @@ class Admin::SettingsController < Admin::BaseController
@participation_processes_settings = all_settings["process"]
@map_configuration_settings = all_settings["map"]
@proposals_settings = all_settings["proposals"]
@uploads_settings = all_settings["uploads"]
end
def update
@@ -29,10 +30,24 @@ class Admin::SettingsController < Admin::BaseController
redirect_to admin_settings_path, notice: t("admin.settings.index.map.flash.update")
end
def update_content_types
setting = Setting.find(params[:id])
group = setting.content_type_group
mime_type_values = content_type_params.keys.map do |content_type|
Setting.mime_types[group][content_type]
end
setting.update value: mime_type_values.join(" ")
redirect_to admin_settings_path, notice: t("admin.settings.flash.updated")
end
private
def settings_params
params.require(:setting).permit(:value)
end
def content_type_params
params.permit(:jpg, :png, :gif, :pdf, :doc, :docx, :xls, :xlsx, :csv, :zip)
end
end

View File

@@ -14,7 +14,8 @@ class Admin::SystemEmailsController < Admin::BaseController
direct_message_for_receiver: %w[view edit_info],
direct_message_for_sender: %w[view edit_info],
email_verification: %w[view edit_info],
user_invite: %w[view edit_info]
user_invite: %w[view edit_info],
evaluation_comment: %w[view edit_info]
}
end
@@ -34,6 +35,8 @@ class Admin::SystemEmailsController < Admin::BaseController
load_sample_user
when "user_invite"
@subject = t("mailers.user_invite.subject", org_name: Setting["org_name"])
when "evaluation_comment"
load_sample_valuation_comment
end
end
@@ -97,6 +100,17 @@ class Admin::SystemEmailsController < Admin::BaseController
end
end
def load_sample_valuation_comment
comment = Comment.where(commentable_type: "Budget::Investment").last
if comment
@email = EvaluationCommentEmail.new(comment)
@email_to = @email.to.first
else
redirect_to admin_system_emails_path,
alert: t("admin.system_emails.alert.no_evaluation_comments")
end
end
def load_sample_user
@user = User.last
@token = @user.email_verification_token || SecureRandom.hex

View File

@@ -37,7 +37,7 @@ module Budgets
end
def load_budget
@budget = Budget.find(params[:budget_id])
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def load_ballot

View File

@@ -1,6 +1,7 @@
module Budgets
class BallotsController < ApplicationController
before_action :authenticate_user!
before_action :load_budget
load_and_authorize_resource :budget
before_action :load_ballot
after_action :store_referer, only: [:show]
@@ -13,6 +14,10 @@ module Budgets
private
def load_budget
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def load_ballot
query = Budget::Ballot.where(user: current_user, budget: @budget)
@ballot = @budget.balloting? ? query.first_or_create : query.first_or_initialize

View File

@@ -12,15 +12,15 @@ module Budgets
private
def investments_by_heading
base = @budget.investments.winners
base = base.joins(milestones: :translations).includes(:milestones)
base = base.tagged_with(params[:milestone_tag]) if params[:milestone_tag].present?
if params[:status].present?
@budget.investments.winners
.with_milestone_status_id(params[:status])
.uniq
.group_by(&:heading)
base = base.with_milestone_status_id(params[:status])
base.uniq.group_by(&:heading)
else
@budget.investments.winners
.joins(milestones: :translations)
.distinct.group_by(&:heading)
base.distinct.group_by(&:heading)
end
end

View File

@@ -1,5 +1,7 @@
module Budgets
class GroupsController < ApplicationController
before_action :load_budget
before_action :load_group
load_and_authorize_resource :budget
load_and_authorize_resource :group, class: "Budget::Group"
@@ -9,5 +11,14 @@ module Budgets
def show
end
private
def load_budget
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def load_group
@group = @budget.groups.find_by_slug_or_id! params[:id]
end
end
end

View File

@@ -10,6 +10,7 @@ module Budgets
PER_PAGE = 10
before_action :authenticate_user!, except: [:index, :show, :json_data]
before_action :load_budget, except: :json_data
load_and_authorize_resource :budget, except: :json_data
load_and_authorize_resource :investment, through: :budget, class: "Budget::Investment",
@@ -136,7 +137,7 @@ module Budgets
def load_heading
if params[:heading_id].present?
@heading = @budget.headings.find(params[:heading_id])
@heading = @budget.headings.find_by_slug_or_id! params[:heading_id]
@assigned_heading = @ballot.try(:heading_for_group, @heading.try(:group))
load_map
end
@@ -154,6 +155,10 @@ module Budgets
TagCloud.new(Budget::Investment, params[:search])
end
def load_budget
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def set_view
@view = (params[:view] == "minimal") ? "minimal" : "default"
end

View File

@@ -14,15 +14,14 @@ module Budgets
private
def load_budget
@budget = Budget.find_by(id: params[:budget_id])
@budget = Budget.find_by_slug_or_id(params[:budget_id]) || Budget.first
end
def load_heading
@heading = if params[:heading_id].present?
@budget.headings.find(params[:heading_id])
else
@budget.headings.first
end
if @budget.present?
headings = @budget.headings
@heading = headings.find_by_slug_or_id(params[:heading_id]) || headings.first
end
end
end

View File

@@ -13,7 +13,7 @@ module Budgets
private
def load_budget
@budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id])
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
end

View File

@@ -3,6 +3,7 @@ class BudgetsController < ApplicationController
include BudgetsHelper
feature_flag :budgets
before_action :load_budget, only: :show
load_and_authorize_resource
before_action :set_default_budget_filter, only: :show
has_filters %w[not_unfeasible feasible unfeasible unselected selected winners], only: :show
@@ -19,4 +20,10 @@ class BudgetsController < ApplicationController
@banners = Banner.in_section("budgets").with_active
end
private
def load_budget
@budget = Budget.find_by_slug_or_id! params[:id]
end
end

View File

@@ -14,6 +14,7 @@ class CommentsController < ApplicationController
if @comment.save
CommentNotifier.new(comment: @comment).process
add_notification @comment
EvaluationCommentNotifier.new(comment: @comment).process if send_evaluation_notification?
else
render :new
end
@@ -107,4 +108,7 @@ class CommentsController < ApplicationController
end
end
def send_evaluation_notification?
@comment.valuation && Setting["feature.valuation_comment_notification"]
end
end

View File

@@ -12,7 +12,7 @@ class InstallationController < ApplicationController
def consul_installation_details
{
release: "1.0.0-beta"
release: "1.0.0"
}.merge(features: settings_feature_flags)
end

View File

@@ -1,4 +1,5 @@
class Management::Budgets::InvestmentsController < Management::BaseController
before_action :load_budget
load_resource :budget
load_resource :investment, through: :budget, class: "Budget::Investment"
@@ -60,6 +61,10 @@ class Management::Budgets::InvestmentsController < Management::BaseController
check_verified_user t("management.budget_investments.alert.unverified_user")
end
def load_budget
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def load_categories
@categories = ActsAsTaggableOn::Tag.category.order(:name)
end

View File

@@ -45,5 +45,4 @@ class Officing::BaseController < ApplicationController
def current_booth
Poll::Booth.where(id: session[:booth_id]).first
end
end

View File

@@ -12,8 +12,9 @@ class PollsController < ApplicationController
::Poll::Answer # trigger autoload
def index
@polls = @polls.not_budget.public_polls.send(@current_filter).includes(:geozones)
.sort_for_list.page(params[:page])
@polls = Kaminari.paginate_array(
@polls.public_polls.not_budget.send(@current_filter).includes(:geozones).sort_for_list
).page(params[:page])
end
def show

View File

@@ -65,7 +65,7 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController
end
def load_budget
@budget = Budget.find(params[:budget_id])
@budget = Budget.find_by_slug_or_id! params[:budget_id]
end
def load_investment
@@ -75,9 +75,7 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController
def heading_filters
investments = @budget.investments.by_valuator(current_user.valuator.try(:id))
.visible_to_valuators.distinct
investment_headings = Budget::Heading.joins(:translations)
.where(id: investments.pluck(:heading_id).uniq)
.order(name: :asc)
investment_headings = Budget::Heading.where(id: investments.pluck(:heading_id)).sort_by(&:name)
all_headings_filter = [
{

View File

@@ -25,7 +25,7 @@ module AdminHelper
end
def moderated_sections
["hidden_proposals", "debates", "comments", "hidden_users", "activity",
["hidden_proposals", "hidden_debates", "hidden_comments", "hidden_users", "activity",
"hidden_budget_investments"]
end
@@ -77,7 +77,9 @@ module AdminHelper
end
def admin_select_options
Administrator.all.order("users.username asc").includes(:user).collect { |v| [ v.name, v.id ] }
Administrator.with_user
.collect { |v| [ v.description_or_name, v.id ] }
.sort_by { |a| a[0] }
end
def admin_submit_action(resource)

View File

@@ -4,6 +4,12 @@ module BudgetExecutionsHelper
@budget.investments.winners.with_milestone_status_id(status).count
end
def options_for_milestone_tags
@budget.milestone_tags.map do |tag|
["#{tag} (#{@budget.investments.winners.tagged_with(tag).count})", tag]
end
end
def first_milestone_with_image(investment)
investment.milestones.order_by_publication_date
.select{ |milestone| milestone.image.present? }.last

View File

@@ -105,4 +105,18 @@ module BudgetsHelper
ends_at: balloting_phase.ends_at }),
method: :post
end
def budget_subnav_items_for(budget)
{
results: t("budgets.results.link"),
stats: t("stats.budgets.link"),
executions: t("budgets.executions.link")
}.select { |section, _| can?(:"read_#{section}", budget) }.map do |section, text|
{
text: text,
url: send("budget_#{section}_path", budget),
active: controller_name == section.to_s
}
end
end
end

View File

@@ -17,15 +17,11 @@ module DocumentablesHelper
end
def accepted_content_types_extensions(documentable_class)
documentable_class.accepted_content_types
.collect{ |content_type| ".#{content_type.split("/").last}" }
.join(",")
Setting.accepted_content_types_for("documents").map { |content_type| ".#{content_type}" }.join(",")
end
def documentable_humanized_accepted_content_types(documentable_class)
documentable_class.accepted_content_types
.collect{ |content_type| content_type.split("/").last }
.join(", ")
Setting.accepted_content_types_for("documents").join(", ")
end
def documentables_note(documentable)

View File

@@ -9,7 +9,7 @@ module ImageablesHelper
end
def imageable_max_file_size
bytes_to_megabytes(Image::MAX_IMAGE_SIZE)
bytes_to_megabytes(Setting["uploads.images.max_size"].to_i.megabytes)
end
def bytes_to_megabytes(bytes)
@@ -17,19 +17,21 @@ module ImageablesHelper
end
def imageable_accepted_content_types
Image::ACCEPTED_CONTENT_TYPE
Setting["uploads.images.content_types"]&.split(" ") || [ "image/jpeg" ]
end
def imageable_accepted_content_types_extensions
Image::ACCEPTED_CONTENT_TYPE
.collect{ |content_type| ".#{content_type.split("/").last}" }
.join(",")
Setting.accepted_content_types_for("images").map do |content_type|
if content_type == "jpg"
".jpg,.jpeg"
else
".#{content_type}"
end
end.join(",")
end
def imageable_humanized_accepted_content_types
Image::ACCEPTED_CONTENT_TYPE
.collect{ |content_type| content_type.split("/").last }
.join(", ")
Setting.accepted_content_types_for("images").join(", ")
end
def imageables_note(_imageable)

View File

@@ -8,4 +8,16 @@ module MailerHelper
return budget_investment_url(commentable.budget_id, commentable) if commentable.is_a?(Budget::Investment)
end
def valuation_comments_url(commentable)
admin_budget_budget_investment_url(commentable.budget, commentable, anchor: "comments")
end
def valuation_comments_link(commentable)
link_to(
commentable.title,
valuation_comments_url(@email.commentable),
target: :blank,
style: "color: #2895F1; text-decoration:none;"
)
end
end

View File

@@ -120,6 +120,13 @@ class Mailer < ApplicationMailer
mail(to: @email_to, from: @newsletter.from, subject: @newsletter.subject)
end
def evaluation_comment(comment, to)
@email = EvaluationCommentEmail.new(comment)
@email_to = to
mail(to: @email_to.email, subject: @email.subject) if @email.can_be_sent?
end
private
def with_user(user, &block)

View File

@@ -51,7 +51,7 @@ module Abilities
can :comment_as_administrator, [Debate, Comment, Proposal, Poll::Question, Budget::Investment,
Legislation::Question, Legislation::Proposal, Legislation::Annotation, Topic]
can [:search, :create, :index, :destroy], ::Administrator
can [:search, :create, :index, :destroy, :edit, :update], ::Administrator
can [:search, :create, :index, :destroy], ::Moderator
can [:search, :show, :edit, :update, :create, :index, :destroy, :summary], ::Valuator
can [:search, :create, :index, :destroy], ::Manager

View File

@@ -3,4 +3,14 @@ class Administrator < ApplicationRecord
delegate :name, :email, :name_and_email, to: :user
validates :user_id, presence: true, uniqueness: true
scope :with_user, -> { includes(:user) }
def description_or_name
description.presence || name
end
def description_or_name_and_email
"#{description_or_name} (#{email})"
end
end

View File

@@ -195,6 +195,10 @@ class Budget < ApplicationRecord
investments.winners.any?
end
def milestone_tags
investments.winners.map(&:milestone_tag_list).flatten.uniq.sort
end
private
def sanitize_descriptions

View File

@@ -28,7 +28,9 @@ class Budget
validates :budget_id, presence: true
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
scope :sort_by_name, -> { joins(:translations).order(:name) }
def self.sort_by_name
all.sort_by(&:name)
end
def single_heading_group?
headings.count == 1

View File

@@ -40,8 +40,7 @@ class Budget
delegate :budget, :budget_id, to: :group, allow_nil: true
scope :i18n, -> { joins(:translations) }
scope :allow_custom_content, -> { i18n.where(allow_custom_content: true).order("budget_heading_translations.name") }
scope :allow_custom_content, -> { where(allow_custom_content: true).sort_by(&:name) }
def self.sort_by_name
all.sort do |heading, other_heading|

View File

@@ -13,9 +13,6 @@ class Budget
include Imageable
include Mappable
include Documentable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
acts_as_votable
acts_as_paranoid column: :hidden_at
@@ -113,7 +110,7 @@ class Budget
end
def self.scoped_filter(params, current_filter)
budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id])
budget = Budget.find_by_slug_or_id params[:budget_id]
results = Investment.by_budget(budget)
results = results.where("cached_votes_up + physical_votes >= ?",
@@ -377,6 +374,12 @@ class Budget
milestones.published.with_status.order_by_publication_date.last&.status_id
end
def admin_and_valuator_users_associated
valuator_users = (valuator_groups.map(&:valuators) + valuators).flatten
all_users = valuator_users << administrator
all_users.compact.uniq
end
private
def set_denormalized_ids

View File

@@ -4,8 +4,9 @@ class Comment < ApplicationRecord
include Graphqlable
include Notifiable
COMMENTABLE_TYPES = %w(Debate Proposal Budget::Investment Poll Topic Legislation::Question
Legislation::Annotation Legislation::Proposal).freeze
COMMENTABLE_TYPES = %w[Debate Proposal Budget::Investment Poll Topic
Legislation::Question Legislation::Annotation
Legislation::Proposal Legislation::PeopleProposal].freeze
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases

View File

@@ -7,16 +7,17 @@ module Documentable
end
module ClassMethods
attr_reader :max_documents_allowed, :max_file_size, :accepted_content_types
private
def documentable(options = {})
@max_documents_allowed = options[:max_documents_allowed]
@max_file_size = options[:max_file_size]
@accepted_content_types = options[:accepted_content_types]
def max_documents_allowed
Setting["uploads.documents.max_amount"].to_i
end
def max_file_size
Setting["uploads.documents.max_size"].to_i.megabytes
end
def accepted_content_types
Setting["uploads.documents.content_types"]&.split(" ") || [ "application/pdf" ]
end
end
end

View File

@@ -8,6 +8,8 @@ module Milestoneable
has_many :progress_bars, as: :progressable
acts_as_taggable_on :milestone_tags
def primary_progress_bar
progress_bars.primary.first
end

View File

@@ -7,6 +7,10 @@ module Sluggable
def self.find_by_slug_or_id(slug_or_id)
find_by_slug(slug_or_id) || find_by_id(slug_or_id)
end
def self.find_by_slug_or_id!(slug_or_id)
find_by_slug(slug_or_id) || find(slug_or_id)
end
end
def generate_slug

View File

@@ -1,13 +1,5 @@
class Dashboard::Action < ApplicationRecord
include Documentable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf",
"image/jpeg",
"image/jpg",
"image/png",
"application/zip" ]
include Linkable
acts_as_paranoid column: :hidden_at

View File

@@ -0,0 +1,16 @@
class EvaluationCommentNotifier
def initialize(args = {})
@comment = args.fetch(:comment)
end
def process
send_evaluation_comment_email
end
private
def send_evaluation_comment_email
EvaluationCommentEmail.new(@comment).to.each do |to|
Mailer.evaluation_comment(@comment, to).deliver_later
end
end
end

View File

@@ -2,12 +2,11 @@ class Image < ApplicationRecord
include ImagesHelper
include ImageablesHelper
TITLE_LENGTH_RANGE = 4..80
MIN_SIZE = 475
MAX_IMAGE_SIZE = 1.megabyte
ACCEPTED_CONTENT_TYPE = %w(image/jpeg image/jpg).freeze
has_attached_file :attachment, styles: { large: "x#{MIN_SIZE}", medium: "300x300#", thumb: "140x245#" },
has_attached_file :attachment, styles: {
large: "x#{Setting["uploads.images.min_height"]}",
medium: "300x300#",
thumb: "140x245#"
},
url: "/system/:class/:prefix/:style/:hash.:extension",
hash_data: ":class/:style",
use_timestamp: false,
@@ -23,7 +22,8 @@ class Image < ApplicationRecord
validate :attachment_presence
validate :validate_attachment_content_type, if: -> { attachment.present? }
validate :validate_attachment_size, if: -> { attachment.present? }
validates :title, presence: true, length: { in: TITLE_LENGTH_RANGE }
validates :title, presence: true
validate :validate_title_length
validates :user_id, presence: true
validates :imageable_id, presence: true, if: -> { persisted? }
validates :imageable_type, presence: true, if: -> { persisted? }
@@ -71,20 +71,38 @@ class Image < ApplicationRecord
return true if imageable_class == Widget::Card
dimensions = Paperclip::Geometry.from_file(attachment.queued_for_write[:original].path)
errors.add(:attachment, :min_image_width, required_min_width: MIN_SIZE) if dimensions.width < MIN_SIZE
errors.add(:attachment, :min_image_height, required_min_height: MIN_SIZE) if dimensions.height < MIN_SIZE
min_width = Setting["uploads.images.min_width"].to_i
min_height = Setting["uploads.images.min_height"].to_i
errors.add(:attachment, :min_image_width, required_min_width: min_width) if dimensions.width < min_width
errors.add(:attachment, :min_image_height, required_min_height: min_height) if dimensions.height < min_height
end
end
def validate_attachment_size
if imageable_class &&
attachment_file_size > 1.megabytes
attachment_file_size > Setting["uploads.images.max_size"].to_i.megabytes
errors.add(:attachment, I18n.t("images.errors.messages.in_between",
min: "0 Bytes",
max: "#{imageable_max_file_size} MB"))
end
end
def validate_title_length
if title.present?
title_min_length = Setting["uploads.images.title.min_length"].to_i
title_max_length = Setting["uploads.images.title.max_length"].to_i
if title.size < title_min_length
errors.add(:title, I18n.t("errors.messages.too_short", count: title_min_length))
end
if title.size > title_max_length
errors.add(:title, I18n.t("errors.messages.too_long", count: title_max_length))
end
end
end
def validate_attachment_content_type
if imageable_class && !attachment_of_valid_content_type?
message = I18n.t("images.errors.messages.wrong_content_type",

View File

@@ -0,0 +1,152 @@
class Legislation::PeopleProposal < ApplicationRecord
include ActsAsParanoidAliases
include Flaggable
include Taggable
include Conflictable
include Measurable
include Sanitizable
include Searchable
include Filterable
include Followable
include Communitable
include Documentable
include Notifiable
include Imageable
include Randomizable
accepts_nested_attributes_for :documents, allow_destroy: true
acts_as_votable
acts_as_paranoid column: :hidden_at
belongs_to :process, class_name: "Legislation::Process", foreign_key: "legislation_process_id"
belongs_to :author, -> { with_hidden }, class_name: "User", foreign_key: "author_id"
has_many :comments, as: :commentable
validates :title, presence: true
validates :summary, presence: true
validates :author, presence: true
validates :process, presence: true
validates :title, length: { in: 4..Legislation::PeopleProposal.title_max_length }
validates :description, length: { maximum: Legislation::PeopleProposal.description_max_length }
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
before_validation :set_responsible_name
before_save :calculate_hot_score, :calculate_confidence_score
scope :for_render, -> { includes(:tags) }
scope :sort_by_hot_score, -> { reorder(hot_score: :desc) }
scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc) }
scope :sort_by_created_at, -> { reorder(created_at: :desc) }
scope :sort_by_most_commented, -> { reorder(comments_count: :desc) }
scope :sort_by_title, -> { reorder(title: :asc) }
scope :sort_by_id, -> { reorder(id: :asc) }
scope :sort_by_supports, -> { reorder(cached_votes_score: :desc) }
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
scope :last_week, -> { where("people_proposals.created_at >= ?", 7.days.ago)}
scope :validated, -> { where(validated: true) }
scope :selected, -> { where(selected: true) }
scope :winners, -> { selected.sort_by_confidence_score }
def to_param
"#{id}-#{title}".parameterize
end
def searchable_values
{ title => "A",
author.username => "B",
tag_list.join(" ") => "B",
summary => "C",
description => "D"}
end
def self.search(terms)
by_code = search_by_code(terms.strip)
by_code.present? ? by_code : pg_search(terms)
end
def self.search_by_code(terms)
matched_code = match_code(terms)
results = where(id: matched_code[1]) if matched_code
return results if results.present? && results.first.code == terms
end
def self.match_code(terms)
/\A#{Setting["proposal_code_prefix"]}-\d\d\d\d-\d\d-(\d*)\z/.match(terms)
end
def likes
cached_votes_up
end
def dislikes
cached_votes_down
end
def total_votes
cached_votes_total
end
def votes_score
cached_votes_score
end
def voters
User.active.where(id: votes_for.voters)
end
def editable?
total_votes <= Setting["max_votes_for_people_proposal_edit"].to_i
end
def editable_by?(user)
author_id == user.id && editable?
end
def votable_by?(user)
user && user.level_two_or_three_verified?
end
def register_vote(user, vote_value)
vote_by(voter: user, vote: vote_value) if votable_by?(user)
end
def code
"#{Setting["proposal_code_prefix"]}-#{created_at.strftime("%Y-%m")}-#{id}"
end
def after_commented
save # update cache when it has a new comment
end
def calculate_hot_score
self.hot_score = ScoreCalculator.hot_score(self)
end
def calculate_confidence_score
self.confidence_score = ScoreCalculator.confidence_score(total_votes, total_votes)
end
def after_hide
tags.each{ |t| t.decrement_custom_counter_for("LegislationPeopleProposal") }
end
def after_restore
tags.each{ |t| t.increment_custom_counter_for("LegislationPeopleProposal") }
end
def contact_info
[phone, email, website].compact
end
protected
def set_responsible_name
if author && author.document_number?
self.responsible_name = author.document_number
end
end
end

View File

@@ -4,9 +4,6 @@ class Legislation::Process < ApplicationRecord
include Milestoneable
include Imageable
include Documentable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
acts_as_paranoid column: :hidden_at
acts_as_taggable_on :customs
@@ -20,7 +17,8 @@ class Legislation::Process < ApplicationRecord
include Globalizable
PHASES_AND_PUBLICATIONS = %i[homepage_phase draft_phase debate_phase allegations_phase
proposals_phase draft_publication result_publication].freeze
proposals_phase people_proposals_phase draft_publication
result_publication].freeze
CSS_HEX_COLOR = /\A#?(?:[A-F0-9]{3}){1,2}\z/i
@@ -34,6 +32,8 @@ class Legislation::Process < ApplicationRecord
foreign_key: "legislation_process_id", dependent: :destroy
has_many :proposals, -> { order(:id) }, class_name: "Legislation::Proposal",
foreign_key: "legislation_process_id", dependent: :destroy
has_many :people_proposals, -> { order(:id) }, class_name: "Legislation::PeopleProposal",
foreign_key: "legislation_process_id", dependent: :destroy
validates_translation :title, presence: true
validates :start_date, presence: true
@@ -45,6 +45,8 @@ class Legislation::Process < ApplicationRecord
validates :allegations_start_date, presence: true, if: :allegations_end_date?
validates :allegations_end_date, presence: true, if: :allegations_start_date?
validates :proposals_phase_end_date, presence: true, if: :proposals_phase_start_date?
validates :people_proposals_phase_end_date, presence: true,
if: :people_proposals_phase_start_date?
validate :valid_date_ranges
validates :background_color, format: { allow_blank: true, with: CSS_HEX_COLOR }
validates :font_color, format: { allow_blank: true, with: CSS_HEX_COLOR }
@@ -84,6 +86,11 @@ class Legislation::Process < ApplicationRecord
proposals_phase_end_date, proposals_phase_enabled)
end
def people_proposals_phase
Legislation::Process::Phase.new(people_proposals_phase_start_date,
people_proposals_phase_end_date, people_proposals_phase_enabled)
end
def draft_publication
Legislation::Process::Publication.new(draft_publication_date, draft_publication_enabled)
end

View File

@@ -14,9 +14,6 @@ class Legislation::Proposal < ApplicationRecord
include Imageable
include Randomizable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
accepts_nested_attributes_for :documents, allow_destroy: true
acts_as_votable

View File

@@ -1,9 +1,6 @@
class Milestone < ApplicationRecord
include Imageable
include Documentable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
translates :title, :description, touch: true
include Globalizable

View File

@@ -49,7 +49,19 @@ class Poll < ApplicationRecord
scope :not_budget, -> { where(budget_id: nil) }
scope :created_by_admin, -> { where(related_type: nil) }
scope :sort_for_list, -> { joins(:translations).order(:geozone_restricted, :starts_at, "poll_translations.name") }
def self.sort_for_list
all.sort do |poll, another_poll|
if poll.geozone_restricted? == another_poll.geozone_restricted?
[poll.starts_at, poll.name] <=> [another_poll.starts_at, another_poll.name]
else
if poll.geozone_restricted?
1
else
-1
end
end
end
end
def self.overlaping_with(poll)
where("? < ends_at and ? >= starts_at", poll.starts_at.beginning_of_day,

View File

@@ -12,7 +12,7 @@ class Poll
end
def self.available
where(polls: { id: Poll.current_or_recounting }).joins(polls: :translations)
where(polls: { id: Poll.current_or_recounting }).joins(:polls)
end
def assignment_on_poll(poll)

View File

@@ -7,7 +7,13 @@ class Poll
validates :user_id, presence: true, uniqueness: true
delegate :name, :email, to: :user
def name
user&.name || I18n.t("shared.author_info.author_deleted")
end
def email
user&.email || I18n.t("shared.author_info.email_deleted")
end
def voting_days_assigned_polls
officer_assignments.voting_days.includes(booth_assignment: :poll).

View File

@@ -6,9 +6,6 @@ class Poll::Question::Answer < ApplicationRecord
translates :description, touch: true
include Globalizable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
accepts_nested_attributes_for :documents, allow_destroy: true
belongs_to :question, class_name: "Poll::Question", foreign_key: "question_id"

View File

@@ -15,9 +15,6 @@ class Proposal < ApplicationRecord
include Mappable
include Notifiable
include Documentable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
include EmbedVideosHelper
include Relationable
include Milestoneable

View File

@@ -3,9 +3,12 @@ class Setting < ApplicationRecord
default_scope { order(id: :asc) }
def prefix
key.split(".").first
end
def type
prefix = key.split(".").first
if %w[feature process proposals map html homepage].include? prefix
if %w[feature process proposals map html homepage uploads].include? prefix
prefix
else
"configuration"
@@ -16,6 +19,14 @@ class Setting < ApplicationRecord
value.present?
end
def content_type?
key.split(".").last == "content_types"
end
def content_type_group
key.split(".").second
end
class << self
def [](key)
where(key: key).pluck(:value).first.presence
@@ -40,5 +51,132 @@ class Setting < ApplicationRecord
setting = where(key: key).first
setting.destroy if setting.present?
end
def accepted_content_types_for(group)
mime_content_types = Setting["uploads.#{group}.content_types"]&.split(" ") || []
Setting.mime_types[group].select { |_, content_type| mime_content_types.include?(content_type) }.keys
end
def mime_types
{
"images" => {
"jpg" => "image/jpeg",
"png" => "image/png",
"gif" => "image/gif"
},
"documents" => {
"pdf" => "application/pdf",
"doc" => "application/msword",
"docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"xls" => "application/x-ole-storage",
"xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"csv" => "text/plain",
"zip" => "application/zip"
}
}
end
def defaults
{
"feature.featured_proposals": nil,
"feature.facebook_login": true,
"feature.google_login": true,
"feature.twitter_login": true,
"feature.public_stats": true,
"feature.signature_sheets": true,
"feature.user.recommendations": true,
"feature.user.recommendations_on_debates": true,
"feature.user.recommendations_on_proposals": true,
"feature.user.skip_verification": "true",
"feature.community": true,
"feature.map": nil,
"feature.allow_attached_documents": true,
"feature.allow_images": true,
"feature.help_page": true,
"feature.valuation_comment_notification": true,
"homepage.widgets.feeds.debates": true,
"homepage.widgets.feeds.processes": true,
"homepage.widgets.feeds.proposals": true,
# Code to be included at the top (inside <body>) of every page
"html.per_page_code_body": "",
# Code to be included at the top (inside <head>) of every page (useful for tracking)
"html.per_page_code_head": "",
"map.latitude": 51.48,
"map.longitude": 0.0,
"map.zoom": 10,
"process.debates": true,
"process.proposals": true,
"process.polls": true,
"process.budgets": true,
"process.legislation": true,
"proposals.successful_proposal_id": nil,
"proposals.poll_short_title": nil,
"proposals.poll_description": nil,
"proposals.poll_link": nil,
"proposals.email_short_title": nil,
"proposals.email_description": nil,
"proposals.poster_short_title": nil,
"proposals.poster_description": nil,
# Images and Documents
"uploads.images.title.min_length": 4,
"uploads.images.title.max_length": 80,
"uploads.images.min_width": 0,
"uploads.images.min_height": 475,
"uploads.images.max_size": 1,
"uploads.images.content_types": "image/jpeg",
"uploads.documents.max_amount": 3,
"uploads.documents.max_size": 3,
"uploads.documents.content_types": "application/pdf",
# Names for the moderation console, as a hint for moderators
# to know better how to assign users with official positions
"official_level_1_name": I18n.t("seeds.settings.official_level_1_name"),
"official_level_2_name": I18n.t("seeds.settings.official_level_2_name"),
"official_level_3_name": I18n.t("seeds.settings.official_level_3_name"),
"official_level_4_name": I18n.t("seeds.settings.official_level_4_name"),
"official_level_5_name": I18n.t("seeds.settings.official_level_5_name"),
"max_ratio_anon_votes_on_debates": 50,
"max_votes_for_debate_edit": 1000,
"max_votes_for_proposal_edit": 1000,
"max_votes_for_people_proposal_edit": 1000,
"comments_body_max_length": 1000,
"proposal_code_prefix": "CONSUL",
"votes_for_proposal_success": 10000,
"months_to_archive_proposals": 12,
# Users with this email domain will automatically be marked as level 1 officials
# Emails under the domain's subdomains will also be included
"email_domain_for_officials": "",
"facebook_handle": nil,
"instagram_handle": nil,
"telegram_handle": nil,
"twitter_handle": nil,
"twitter_hashtag": nil,
"youtube_handle": nil,
"url": "http://example.com", # Public-facing URL of the app.
# CONSUL installation's organization name
"org_name": "CONSUL",
"meta_title": nil,
"meta_description": nil,
"meta_keywords": nil,
"proposal_notification_minimum_interval_in_days": 3,
"direct_message_max_per_day": 3,
"mailer_from_name": "CONSUL",
"mailer_from_address": "noreply@consul.dev",
"min_age_to_participate": 16,
"hot_score_period_in_days": 31,
"related_content_score_threshold": -0.3,
"featured_proposals_number": 3,
"dashboard.emails": nil
}
end
def reset_defaults
defaults.each { |name, value| self[name] = value }
end
def add_new_settings
defaults.each do |name, value|
self[name] = value unless find_by(key: name)
end
end
end
end

View File

@@ -23,6 +23,7 @@ class User < ApplicationRecord
has_many :identities, dependent: :destroy
has_many :debates, -> { with_hidden }, foreign_key: :author_id
has_many :proposals, -> { with_hidden }, foreign_key: :author_id
has_many :people_proposals, -> { with_hidden }, foreign_key: :author_id
has_many :budget_investments, -> { with_hidden }, foreign_key: :author_id, class_name: "Budget::Investment"
has_many :comments, -> { with_hidden }
has_many :failed_census_calls
@@ -131,9 +132,7 @@ class User < ApplicationRecord
end
def headings_voted_within_group(group)
Budget::Heading.joins(:translations)
.order("name")
.where(id: voted_investments.by_group(group).pluck(:heading_id))
Budget::Heading.where(id: voted_investments.by_group(group).pluck(:heading_id))
end
def voted_investments

View File

@@ -141,8 +141,8 @@
<% end %>
<% if feature?(:debates) %>
<li <%= "class=is-active" if controller_name == "debates" %>>
<%= link_to t("admin.menu.hidden_debates"), admin_debates_path %>
<li <%= "class=is-active" if controller_name == "hidden_debates" %>>
<%= link_to t("admin.menu.hidden_debates"), admin_hidden_debates_path %>
</li>
<% end %>
@@ -152,8 +152,8 @@
</li>
<% end %>
<li <%= "class=is-active" if controller_name == "comments" %>>
<%= link_to t("admin.menu.hidden_comments"), admin_comments_path %>
<li <%= "class=is-active" if controller_name == "hidden_comments" %>>
<%= link_to t("admin.menu.hidden_comments"), admin_hidden_comments_path %>
</li>
<li <%= "class=is-active" if controller_name == "proposal_notifications" %>>

View File

@@ -0,0 +1,15 @@
<%= back_link_to admin_administrators_path %>
<h2><%= t("admin.administrators.form.edit_title") %></h2>
<div class="callout highlight">
<strong><%= @administrator.name %></strong><br>
<%= @administrator.email %>
</div>
<div class="margin-top">
<%= form_for [:admin, @administrator] do |f| %>
<%= f.text_field :description %>
<%= f.submit class: "button success" %>
<% end %>
</div>

View File

@@ -11,6 +11,7 @@
<th scope="col" class="text-center"><%= t("admin.administrators.index.id") %></th>
<th scope="col"><%= t("admin.administrators.index.name") %></th>
<th scope="col"><%= t("admin.administrators.index.email") %></th>
<th scope="col"><%= t("admin.administrators.index.description") %></th>
<th scope="col" class="small-3"><%= t("admin.shared.actions") %></th>
</thead>
<% @administrators.each do |administrator| %>
@@ -24,13 +25,18 @@
<td>
<%= administrator.email %>
</td>
<td>
<%= administrator.description %>
</td>
<td>
<% if administrator.persisted? %>
<%= link_to t("admin.actions.edit"),
edit_admin_administrator_path(administrator),
class: "button hollow" %>
<%= link_to t("admin.administrators.administrator.delete"),
admin_administrator_path(administrator),
method: :delete,
class: "button hollow alert expanded"
%>
class: "button hollow alert" %>
<% else %>
<%= link_to t("admin.administrators.administrator.add"),
{ controller: "admin/administrators", action: :create,

View File

@@ -2,6 +2,7 @@
admin_budget_budget_investments_path(csv_params),
class: "float-right small clear" %>
<% if params[:advanced_filters].include?("winners") %>
<% if display_calculate_winners_button?(@budget) %>
<%= link_to calculate_winner_button_text(@budget),

View File

@@ -13,9 +13,9 @@
</td>
<td class="small">
<% if investment.administrator.present? %>
<span title="<%= t("admin.budget_investments.index.assigned_admin") %>">
<%= investment.administrator.name %>
</span>
<span title="<%= t("admin.budget_investments.index.assigned_admin") %>">
<%= investment.administrator.description_or_name %>
</span>
<% else %>
<%= t("admin.budget_investments.index.no_admin_assigned") %>
<% end %>

View File

@@ -46,7 +46,7 @@
<div class="small-12 medium-6">
<%= f.select(:administrator_id,
@admins.collect{ |a| [a.name_and_email, a.id ] },
@admins.collect{ |a| [a.description_or_name_and_email, a.id ] },
{ include_blank: t("admin.budget_investments.edit.undefined") }) %>
</div>
</div>
@@ -105,6 +105,13 @@
</div>
</div>
<div class="small-12 column">
<%= f.text_field :milestone_tag_list,
value: @investment.milestone_tag_list.sort.join(", "),
label: t("admin.budget_investments.edit.milestone_tags") %>
</div>
<div class="small-12 column margin-top">
<%= f.submit(class: "button", value: t("admin.budget_investments.edit.submit_button")) %>
</div>

View File

@@ -1,7 +1,8 @@
<h2><%= t("admin.comments.index.title") %></h2>
<h2><%= t("admin.hidden_comments.index.title") %></h2>
<p><%= t("admin.shared.moderated_content") %></p>
<%= render "shared/filter_subnav", i18n_namespace: "admin.comments.index" %>
<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_comments.index" %>
<% if @comments.any? %>
<h3 class="margin"><%= page_entries_info @comments %></h3>
@@ -17,20 +18,20 @@
<td>
<%= text_with_links comment.body %><br>
<% if comment.commentable.hidden? %>
(<%= t("admin.comments.index.hidden_#{comment.commentable_type.downcase}") %>: <%= comment.commentable.title %>)
(<%= t("admin.hidden_comments.index.hidden_#{comment.commentable_type.downcase}") %>: <%= comment.commentable.title %>)
<% else %>
<%= link_to comment.commentable.title, commentable_path(comment) %>
<% end %>
</td>
<td>
<%= link_to t("admin.actions.restore"),
restore_admin_comment_path(comment, request.query_parameters),
restore_admin_hidden_comment_path(comment, request.query_parameters),
method: :put,
data: { confirm: t("admin.actions.confirm") },
class: "button hollow warning" %>
<% unless comment.confirmed_hide? %>
<%= link_to t("admin.actions.confirm_hide"),
confirm_hide_admin_comment_path(comment, request.query_parameters),
confirm_hide_admin_hidden_comment_path(comment, request.query_parameters),
method: :put,
class: "button" %>
<% end %>
@@ -43,6 +44,6 @@
<%= paginate @comments %>
<% else %>
<div class="callout primary margin">
<%= t("admin.comments.index.no_hidden_comments") %>
<%= t("admin.hidden_comments.index.no_hidden_comments") %>
</div>
<% end %>

View File

@@ -1,7 +1,8 @@
<h2><%= t("admin.debates.index.title") %></h2>
<h2><%= t("admin.hidden_debates.index.title") %></h2>
<p><%= t("admin.shared.moderated_content") %></p>
<%= render "shared/filter_subnav", i18n_namespace: "admin.debates.index" %>
<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_debates.index" %>
<% if @debates.any? %>
<h3 class="margin"><%= page_entries_info @debates %></h3>
@@ -25,13 +26,13 @@
</td>
<td class="align-top">
<%= link_to t("admin.actions.restore"),
restore_admin_debate_path(debate, request.query_parameters),
restore_admin_hidden_debate_path(debate, request.query_parameters),
method: :put,
data: { confirm: t("admin.actions.confirm") },
class: "button hollow warning" %>
<% unless debate.confirmed_hide? %>
<%= link_to t("admin.actions.confirm_hide"),
confirm_hide_admin_debate_path(debate, request.query_parameters),
confirm_hide_admin_hidden_debate_path(debate, request.query_parameters),
method: :put,
class: "button" %>
<% end %>
@@ -44,6 +45,6 @@
<%= paginate @debates %>
<% else %>
<div class="callout primary margin">
<%= t("admin.debates.index.no_hidden_debates") %>
<%= t("admin.hidden_debates.index.no_hidden_debates") %>
</div>
<% end %>

View File

@@ -4,6 +4,14 @@
polymorphic_path([:admin, *resource_hierarchy_for(milestoneable.progress_bars.new)]),
class: "button hollow float-right" %>
<% if milestoneable.milestone_tag_list.any? %>
<div>
<strong>
<%= t("admin.milestones.index.milestone_tags") %>:
</strong> <%= milestoneable.milestone_tag_list.sort.join(", ") %>
</div>
<% end %>
<% if milestoneable.milestones.any? %>
<table>
<thead>

View File

@@ -0,0 +1,19 @@
<%= form_tag admin_update_content_types_path, method: :put, id: "edit_#{dom_id(setting)}" do %>
<%= hidden_field_tag "id", setting.id %>
<div class="small-12 medium-6 large-8 column">
<% group = setting.content_type_group %>
<% Setting.mime_types[group].each do |content_type, mime_type_value| %>
<span class="content-type">
<%= check_box_tag content_type,
setting.value.split(" ").include?(mime_type_value),
setting.value.split(" ").include?(mime_type_value) %>
<%= label_tag content_type, content_type.upcase %>
</span>
<% end %>
</div>
<div class="small-12 medium-6 large-4 column">
<%= submit_tag t("admin.settings.index.update_setting"), class: "button hollow expanded" %>
</div>
<% end %>

View File

@@ -0,0 +1,6 @@
<%= form_for(feature, url: admin_setting_path(feature), html: { id: "edit_#{dom_id(feature)}"}) do |f| %>
<%= f.hidden_field :value, id: dom_id(feature), value: (feature.enabled? ? "" : "active") %>
<%= f.submit(t("admin.settings.index.features.#{feature.enabled? ? "disable" : "enable"}"),
class: "button expanded #{feature.enabled? ? "hollow alert" : "success"}",
data: {confirm: t("admin.actions.confirm")}) %>
<% end %>

View File

@@ -32,13 +32,7 @@
</td>
<td class="text-right">
<%= form_for(feature, url: admin_setting_path(feature), html: { id: "edit_#{dom_id(feature)}"}) do |f| %>
<%= f.hidden_field :value, id: dom_id(feature), value: (feature.enabled? ? "" : "active") %>
<%= f.submit(t("admin.settings.index.features.#{feature.enabled? ? "disable" : "enable"}"),
class: "button expanded #{feature.enabled? ? "hollow alert" : "success"}",
data: {confirm: t("admin.actions.confirm")}) %>
<% end %>
<%= render "admin/settings/featured_settings_form", feature: feature %>
</td>
</tr>
<% end %>

View File

@@ -29,6 +29,12 @@
<% end %>
</li>
<li class="tabs-title" id="images-and-documents-tab">
<%= link_to "#tab-images-and-documents" do %>
<%= t("admin.settings.index.images_and_documents") %>
<% end %>
</li>
<li class="tabs-title" id="proposals-tab">
<%= link_to "#tab-proposals" do %>
<%= t("admin.settings.index.dashboard.title") %>

View File

@@ -0,0 +1,3 @@
<h2><%= t("admin.settings.index.images_and_documents") %></h2>
<%= render "settings_table", settings: @uploads_settings %>

View File

@@ -0,0 +1,8 @@
<%= form_for(setting, url: admin_setting_path(setting), html: { id: "edit_#{dom_id(setting)}"}) do |f| %>
<div class="small-12 medium-6 large-8 column">
<%= f.text_area :value, label: false, id: dom_id(setting), lines: 1 %>
</div>
<div class="small-12 medium-6 large-4 column">
<%= f.submit(t("admin.settings.index.update_setting"), class: "button hollow expanded") %>
</div>
<% end %>

View File

@@ -16,13 +16,10 @@
</span>
</td>
<td class="small-6">
<%= form_for(setting, url: admin_setting_path(setting), html: { id: "edit_#{dom_id(setting)}"}) do |f| %>
<div class="small-12 medium-6 large-8 column">
<%= f.text_area :value, label: false, id: dom_id(setting), lines: 1 %>
</div>
<div class="small-12 medium-6 large-4 column">
<%= f.submit(t("admin.settings.index.update_setting"), class: "button hollow expanded") %>
</div>
<% if setting.content_type? %>
<%= render "admin/settings/content_types_settings_form", setting: setting %>
<% else %>
<%= render "admin/settings/settings_form", setting: setting %>
<% end %>
</td>
</tr>

View File

@@ -18,6 +18,10 @@
<%= render "map_configuration_tab" %>
</div>
<div class="tabs-panel" id="tab-images-and-documents">
<%= render "images_and_documents_tab" %>
</div>
<div class="tabs-panel" id="tab-proposals">
<%= render "proposals_dashboard" %>
</div>

View File

@@ -0,0 +1,35 @@
<div class="row margin-top">
<div class="small-12 medium-9 column">
<h2><%= t("budgets.index.finished_budgets") %></h2>
<div id="finished_budgets" class="budget-investments-list">
<% budgets.each do |budget| %>
<div class="budget-investment clear">
<div class="panel past-budgets">
<div class="row" data-equalizer data-equalizer-on="medium">
<div class="small-12 medium-6 column table" data-equalizer-watch>
<div class="table-cell align-middle">
<h3><%= budget.name %></h3>
</div>
</div>
<div class="small-12 medium-6 column table" data-equalizer-watch>
<div id="budget_<%= budget.id %>_results" class="table-cell align-middle">
<% if can?(:read_results, budget) %>
<%= link_to t("budgets.index.see_results"),
budget_results_path(budget),
class: "button" %>
<% end %>
<%= link_to t("budgets.index.milestones"),
budget_executions_path(budget),
class: "button" %>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
</div>

View File

@@ -0,0 +1,17 @@
<div class="row margin-top">
<div class="small-12 column">
<ul class="tabs">
<% budget_subnav_items_for(budget).each do |item| %>
<% if item[:active] %>
<li class="tabs-title is-active">
<span class="show-for-sr"><%= t("shared.you_are_in") %></span>
<%= link_to item[:text], item[:url], class: "is-active" %>
</li>
<% else %>
<li class="tabs-title"><%= link_to item[:text], item[:url] %></li>
<% end %>
<% end %>
</ul>
</div>
</div>

View File

@@ -25,21 +25,7 @@
</div>
</div>
<div class="row margin-top">
<div class="small-12 column">
<ul class="tabs">
<li class="tabs-title">
<%= link_to t("budgets.results.link"), budget_results_path(@budget) %>
</li>
<li class="tabs-title">
<%= link_to t("stats.budgets.link"), budget_stats_path(@budget) %>
</li>
<li class="tabs-title is-active">
<%= link_to t("budgets.executions.link"), budget_executions_path(@budget), class: "is-active" %>
</li>
</ul>
</div>
</div>
<%= render "budgets/subnav", budget: @budget %>
<div class="row">
<div class="small-12 medium-3 large-2 column">
@@ -58,13 +44,24 @@
<div class="small-12 medium-9 large-10 column">
<%= form_tag(budget_executions_path(@budget), method: :get) do %>
<div class="small-12 medium-3 column">
<%= label_tag :status, t("budgets.executions.filters.label") %>
<%= label_tag :milestone_tag, t("budgets.executions.filters.milestone_tag.label") %>
<%= select_tag :milestone_tag,
options_for_select(
options_for_milestone_tags,
params[:milestone_tag]
),
class: "js-submit-on-change",
prompt: t("budgets.executions.filters.milestone_tag.all",
count: @budget.investments.winners.with_milestones.count) %>
</div>
<div class="small-12 medium-3 column">
<%= label_tag :status, t("budgets.executions.filters.status.label") %>
<%= select_tag :status,
options_from_collection_for_select(@statuses,
:id, lambda { |s| "#{s.name} (#{filters_select_counts(s.id)})" },
params[:status]),
class: "js-submit-on-change",
prompt: t("budgets.executions.filters.all",
prompt: t("budgets.executions.filters.status.all",
count: @budget.investments.winners.with_milestones.count) %>
</div>
<% end %>

View File

@@ -53,7 +53,7 @@
<% end %>
<% end %>
<% if current_budget.finished? %>
<% if can?(:read_results, current_budget) %>
<%= link_to t("budgets.show.see_results"),
budget_results_path(current_budget, heading_id: current_budget.headings.first),
class: "button margin-top expanded" %>
@@ -126,38 +126,7 @@
</div>
<% if @finished_budgets.present? %>
<div class="row margin-top">
<div class="small-12 medium-9 column">
<h2><%= t("budgets.index.finished_budgets") %></h2>
<div id="finished_budgets" class="budget-investments-list">
<% @finished_budgets.each do |budget| %>
<div class="budget-investment clear">
<div class="panel past-budgets">
<div class="row" data-equalizer data-equalizer-on="medium">
<div class="small-12 medium-6 column table" data-equalizer-watch>
<div class="table-cell align-middle">
<h3><%= budget.name %></h3>
</div>
</div>
<div class="small-12 medium-6 column table" data-equalizer-watch>
<div id="budget_<%= budget.id %>_results" class="table-cell align-middle">
<%= link_to t("budgets.index.see_results"),
budget_results_path(budget.id),
class: "button" %>
<%= link_to t("budgets.index.milestones"),
budget_executions_path(budget.id),
class: "button" %>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
</div>
<%= render "finished", budgets: @finished_budgets %>
<% end %>
</div>
<% else %>

View File

@@ -36,7 +36,7 @@
verify_account: link_to(t("votes.verify_account"), verification_path),
signin: link_to(t("votes.signin"), new_user_session_path),
signup: link_to(t("votes.signup"), new_user_registration_path),
supported_headings: (current_user && current_user.headings_voted_within_group(investment.group).map(&:name).to_sentence)
supported_headings: (current_user && current_user.headings_voted_within_group(investment.group).map(&:name).sort.to_sentence)
).html_safe %>
</small>
</p>

View File

@@ -24,22 +24,7 @@
</div>
</div>
<div class="row margin-top">
<div class="small-12 column">
<ul class="tabs">
<li class="tabs-title is-active">
<span class="show-for-sr"><%= t("shared.you_are_in") %></span>
<%= link_to t("budgets.results.link"), budget_results_path(@budget), class: "is-active" %>
</li>
<li class="tabs-title">
<%= link_to t("stats.budgets.link"), budget_stats_path(@budget) %>
</li>
<li class="tabs-title">
<%= link_to t("budgets.executions.link"), budget_executions_path(@budget) %>
</li>
</ul>
</div>
</div>
<%= render "budgets/subnav", budget: @budget %>
<div class="row">
<div class="small-12 medium-3 large-2 column">

View File

@@ -36,7 +36,7 @@
<% end %>
<% end %>
<% if @budget.finished? %>
<% if can?(:read_results, @budget) %>
<%= link_to t("budgets.show.see_results"),
budget_results_path(@budget),
class: "button margin-top expanded" %>

View File

@@ -21,22 +21,7 @@
</div>
</div>
<div class="row margin-top">
<div class="small-12 column">
<ul class="tabs">
<li class="tabs-title">
<span class="show-for-sr"><%= t("shared.you_are_in") %></span>
<%= link_to t("budgets.results.link"), budget_results_path(@budget) %>
</li>
<li class="tabs-title is-active">
<%= link_to t("stats.budgets.link"), budget_stats_path(@budget), class: "is-active" %>
</li>
<li class="tabs-title">
<%= link_to t("budgets.executions.link"), budget_executions_path(@budget) %>
</li>
</ul>
</div>
</div>
<%= render "budgets/subnav", budget: @budget %>
<div class="row margin">
<div class="small-12 medium-3 column sidebar">

View File

@@ -3,7 +3,8 @@
<% allow_votes = local_assigns.fetch(:allow_votes, true) %>
<% allow_actions = local_assigns.fetch(:allow_actions, true) %>
<% allow_comments = local_assigns.fetch(:allow_comments, true) %>
<% cache [locale_and_user_status(comment), comment, commentable_cache_key(comment.commentable), comment.author, (comment_flags[comment.id] if comment_flags)] do %>
<% admin_layout = local_assigns.fetch(:admin_layout, false) %>
<% cache [locale_and_user_status(comment), comment, commentable_cache_key(comment.commentable), comment.author, (comment_flags[comment.id] if comment_flags), (admin_layout if admin_layout)] do %>
<ul id="<%= dom_id(comment) %>" class="comment no-bullet small-12">
<li class="comment-body">
<% if comment.hidden? || comment.user.hidden? %>
@@ -30,7 +31,14 @@
<div class="comment-info">
<% if comment.as_administrator? %>
<span class="user-name"><%= t("comments.comment.admin") %> #<%= comment.administrator_id %></span>
<span class="user-name">
<%= t("comments.comment.admin") %>
<% if admin_layout %>
<%= Administrator.find(comment.administrator_id).description_or_name %>
<% else %>
#<%= comment.administrator_id %>
<% end %>
</span>
<% elsif comment.as_moderator? %>
<span class="user-name"><%= t("comments.comment.moderator") %> #<%= comment.moderator_id %></span>
<% else %>

View File

@@ -1,7 +1,8 @@
<% commentable = comment_tree.commentable %>
<% valuation = local_assigns.fetch(:valuation, false) %>
<% allow_comments = local_assigns.fetch(:allow_comments, true) %>
<% cache [locale_and_user_status, comment_tree.order, commentable_cache_key(commentable), comment_tree.comments, comment_tree.comment_authors, commentable.comments_count, comment_flags] do %>
<% admin_layout = local_assigns.fetch(:admin_layout, false) %>
<% cache [locale_and_user_status, comment_tree.order, commentable_cache_key(commentable), comment_tree.comments, comment_tree.comment_authors, commentable.comments_count, comment_flags, admin_layout] do %>
<section class="expanded comments">
<div class="row">
<div id="comments" class="small-12 column">
@@ -46,7 +47,8 @@
valuation: valuation,
allow_votes: !valuation,
allow_actions: !valuation,
allow_comments: allow_comments } %>
allow_comments: allow_comments,
admin_layout: admin_layout } %>
<% end %>
<%= paginate comment_tree.root_comments %>
</div>

View File

@@ -2,5 +2,5 @@
<h2 class="sidebar-title"><%= t("shared.tags_cloud.districts") %></h2>
<br>
<%= link_to map_proposals_path, id: "map", title: t("shared.tags_cloud.districts_list") do %>
<%= image_tag(image_path_for("map.jpg", alt: t("shared.tags_cloud.districts_list")) %>
<%= image_tag(image_path_for("map.jpg", alt: t("shared.tags_cloud.districts_list"))) %>
<% end %>

View File

@@ -0,0 +1,19 @@
<td style="padding-bottom: 20px; padding-left: 10px;">
<h1 style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;">
<%= t("mailers.evaluation_comment.title", investment: @email.commentable.title) %>
</h1>
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
<%= t("mailers.evaluation_comment.hi") %> <strong><%= @email_to.name %></strong>,
</p>
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
<%= t("mailers.evaluation_comment.new_comment_by_html", commenter: @email.comment.author.name, investment: valuation_comments_link(@email.commentable)) %>
</p>
<%= t("mailers.evaluation_comment.commenter_info", commenter: @email.comment.author.name, time: l(@email.comment.created_at)) %>
<div style="border-left: 2px solid #DEE0E3;font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-style: italic;font-weight: normal;line-height: 24px;margin-left: 20px;padding: 10px;">
<%= simple_format text_with_links(@email.comment.body), {}, sanitize: false %>
</div>
</td>

View File

@@ -0,0 +1,43 @@
<% if proposal.selected? %>
<div class="callout success">
<strong><%= t("proposals.proposal.selected") %></strong>
</div>
<% else %>
<div id="proposal_sticky" data-sticky-container>
<div class="sticky fixed-mobile-content"
data-sticky
data-stick-to="bottom"
data-sticky-on="small"
data-top-anchor="0"
data-btm-anchor="sticky_stop"
data-check-every="0">
<div class="fixed-mobile-content">
<div class="sidebar-divider"></div>
<h2><%= t("votes.supports") %></h2>
<div id="<%= dom_id(proposal) %>_votes">
<% if proposal.draft? %>
<div class="callout primary">
<p class="text-center small"><strong><%= t("proposals.show.draft") %></strong></p>
</div>
<% elsif proposal.successful? %>
<div class="supports text-center">
<%= render "supports", proposal: proposal %>
</div>
<% elsif proposal.archived? %>
<div class="padding text-center">
<p>
<strong><%= t("proposals.proposal.supports", count: proposal.total_votes) %></strong>
</p>
<p><%= t("proposals.proposal.archived") %></p>
</div>
<% else %>
<%= render "votes", { proposal: proposal, vote_url: vote_proposal_path(proposal, value: "yes") } %>
<% end %>
</div>
</div>
</div>
</div>
<div id="sticky_stop"></div>
<% end %>

View File

@@ -2,12 +2,12 @@
<% provide :title do %><%= @proposal.title %><% end %>
<% content_for :meta_description do %><%= @proposal.summary %><% end %>
<% provide :social_media_meta_tags do %>
<%= render "shared/social_media_meta_tags",
social_url: proposal_url(@proposal),
social_title: @proposal.title,
social_description: @proposal.summary,
twitter_image_url: (@proposal.image.present? ? @proposal.image_url(:thumb) : nil),
og_image_url: (@proposal.image.present? ? @proposal.image_url(:thumb) : nil) %>
<%= render "shared/social_media_meta_tags",
social_url: proposal_url(@proposal),
social_title: @proposal.title,
social_description: @proposal.summary,
twitter_image_url: (@proposal.image.present? ? @proposal.image_url(:thumb) : nil),
og_image_url: (@proposal.image.present? ? @proposal.image_url(:thumb) : nil) %>
<% end %>
<% content_for :canonical do %>
<%= render "shared/canonical", href: proposal_url(@proposal) %>
@@ -62,50 +62,7 @@
</div>
<% end %>
<% if @proposal.selected? %>
<div class="callout success">
<strong><%= t("proposals.proposal.selected") %></strong>
</div>
<% else %>
<div id="proposal_sticky" data-sticky-container>
<div class="sticky fixed-mobile-content"
data-sticky
data-stick-to="bottom"
data-sticky-on="small"
data-top-anchor="0"
data-btm-anchor="sticky_stop"
data-check-every="0">
<div class="fixed-mobile-content">
<div class="sidebar-divider"></div>
<h2><%= t("votes.supports") %></h2>
<div id="<%= dom_id(@proposal) %>_votes">
<% if @proposal.draft? %>
<div class="callout primary">
<p class=text-center><strong><%= t(".draft") %></strong></p>
</div>
<% elsif @proposal.successful? %>
<div class="supports text-center">
<%= render "supports", proposal: @proposal %>
</div>
<% elsif @proposal.archived? %>
<div class="padding text-center">
<p>
<strong><%= t("proposals.proposal.supports", count: @proposal.total_votes) %></strong>
</p>
<p><%= t("proposals.proposal.archived") %></p>
</div>
<% else %>
<%= render "votes",
{ proposal: @proposal, vote_url: vote_proposal_path(@proposal, value: "yes") } %>
<% end %>
</div>
</div>
</div>
</div>
<div id="sticky_stop"></div>
<% end %>
<%= render "proposals/support_status", proposal: @proposal %>
<%= render "proposals/social_share", proposal: @proposal, share_title: t("proposals.show.share") %>

View File

@@ -1,6 +1,6 @@
{
"<%= t("stats.index.visits") %>" : "<%= number_with_delimiter(@visits) %>",
"<%= t("stats.index.debates") %>" : <%= number_with_delimiter(@debates) %>",
"<%= t("stats.index.debates") %>" : "<%= number_with_delimiter(@debates) %>",
"<%= t("stats.index.proposals") %>" : "<%= number_with_delimiter(@proposals) %>",
"<%= t("stats.index.comments") %>" : "<%= number_with_delimiter(@comments) %>",
"<%= t("stats.index.proposal_votes") %>" : "<%= number_with_delimiter(@proposal_votes) %>",
@@ -9,4 +9,4 @@
"<%= t("stats.index.votes") %>" : "<%= number_with_delimiter(@votes) %>",
"<%= t("stats.index.verified_users") %>" : "<%= number_with_delimiter(@verified_users) %>",
"<%= t("stats.index.unverified_users") %>" : "<%= number_with_delimiter(@unverified_users) %>"
}
}

View File

@@ -1,8 +1,10 @@
<h2><%= t("valuation.budget_investments.valuation_comments") %></h2>
<% unless @comment_tree.nil? %>
<%= render partial: "/comments/comment_tree", locals: { comment_tree: @comment_tree,
comment_flags: @comment_flags,
display_comments_count: false,
valuation: true,
allow_comments: !@budget.finished? } %>
<%= render partial: "/comments/comment_tree", locals: {
comment_tree: @comment_tree,
comment_flags: @comment_flags,
display_comments_count: false,
valuation: true,
allow_comments: !@budget.finished?,
admin_layout: true } %>
<% end %>

View File

@@ -45,11 +45,19 @@ namespace :deploy do
#before :starting, "rvm1:install:ruby" # install Ruby and create gemset
#before :starting, "install_bundler_gem" # install bundler gem
after "deploy:migrate", "add_new_settings"
after :publishing, "deploy:restart"
after :published, "delayed_job:restart"
after :published, "refresh_sitemap"
after :finishing, "deploy:cleanup"
desc "Deploys and runs the tasks needed to upgrade to a new release"
task :upgrade do
after "add_new_settings", "execute_release_tasks"
invoke "deploy"
end
end
task :install_bundler_gem do
@@ -67,3 +75,23 @@ task :refresh_sitemap do
end
end
end
task :add_new_settings do
on roles(:db) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "settings:add_new_settings"
end
end
end
end
task :execute_release_tasks do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "consul:execute_release_tasks"
end
end
end
end

View File

@@ -48,6 +48,7 @@ Rails.application.configure do
# config.action_cable.url = 'wss://example.com/cable'
# config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true

View File

@@ -115,9 +115,12 @@ ignore_missing:
- "activerecord.errors.models.direct_message.*"
- "errors.messages.blank"
- "errors.messages.taken"
- "errors.messages.too_short"
- "errors.messages.too_long"
- "devise.failure.invalid"
- "devise.registrations.destroyed"
- "devise.password_expired.*"
- "seeds.settings.*"
## Consider these keys used:
ignore_unused:
@@ -130,9 +133,9 @@ ignore_unused:
- "date.order"
- "unauthorized.*"
- "admin.officials.level_*"
- "admin.comments.index.filter*"
- "admin.hidden_comments.index.filter*"
- "admin.banners.index.filters.*"
- "admin.debates.index.filter*"
- "admin.hidden_debates.index.filter*"
- "admin.hidden_proposals.index.filter*"
- "admin.proposal_notifications.index.filter*"
- "admin.budgets.index.filter*"
@@ -147,7 +150,7 @@ ignore_unused:
- "admin.legislation.processes.proposals.select_order"
- "admin.legislation.draft_versions.*.submit_button"
- "admin.legislation.questions.*.submit_button"
- "admin.comments.index.hidden_*"
- "admin.hidden_comments.index.hidden_*"
- "admin.settings.index.features.*"
- "admin.polls.*.submit_button"
- "admin.booths.*.submit_button"
@@ -206,7 +209,7 @@ ignore_unused:
- admin.stats.polls.expired
- "stats.polls.*_percentage"
- landings.cambia_tu_ciudad.*
- 'seeds.settings.*'
- "seeds.settings.*"
- "dashboard.polls.*.submit"
####
## Exclude these keys from the `i18n-tasks eq-base" report:

View File

@@ -168,8 +168,9 @@ ar:
heading_selection_title: "جسب المنطقة"
no_winner_investments: "لا يوجد استثمارات فائزة"
filters:
label: "حالة المشروع الحالية"
all: "الكل (%{count})"
status:
label: "حالة المشروع الحالية"
all: "الكل (%{count})"
phases:
errors:
dates_range_invalid: "تاريخ البدأ لا يمكن ان يكون مساو او يتجاوز تاريخ الانتهاء"

View File

@@ -3,7 +3,7 @@ ar:
no_reply: "تم إرسال هذه الرسالة من عنوان بريد إلكتروني لا يقبل الردود."
comment:
hi: مرحبا
new_comment_by_html: هناك تعليق جديد من <b>%{commenter}</b>
new_comment_by_html: هناك تعليق جديد من <strong>%{commenter}</strong>
subject: تم التعليق على %{commentable}
title: تعليق جديد
config:
@@ -16,7 +16,7 @@ ar:
title: أكد حسابك باستخدام الرابط التالي
reply:
hi: مرحبا
new_reply_by_html: وهناك رد جديد من <b>%{commenter}</b> على التعليق الخاص بك
new_reply_by_html: وهناك رد جديد من <strong>%{commenter}</strong> على التعليق الخاص بك
subject: يوجد رد على تعليقك
title: اجابة جديدة على تعليقك
unfeasible_spending_proposal:

View File

@@ -3,7 +3,7 @@ ast:
no_reply: "Este mensaje se ha enviado desde una dirección de correo electrónico que no admite respuestas."
comment:
hi: Hola
new_comment_by_html: Hay un nuevo comentario de <b>%{commenter}</b> en
new_comment_by_html: Hay un nuevo comentario de <strong>%{commenter}</strong> en
subject: Alguien ha comentado en tu %{commentable}
title: Nuevo comentario
config:
@@ -16,7 +16,7 @@ ast:
title: Verifica tu cuenta con el siguiente enlace
reply:
hi: Hola
new_reply_by_html: Hay una nueva respuesta de <b>%{commenter}</b> a tu comentario en
new_reply_by_html: Hay una nueva respuesta de <strong>%{commenter}</strong> a tu comentario en
subject: Alguien ha respondido a tu comentario
title: Nueva respuesta a tu comentario
unfeasible_spending_proposal:

View File

@@ -3,7 +3,7 @@ ca:
no_reply: "Aquest missatge s'ha enviat des d'una adreça de correu electrònic que no admet respostes."
comment:
hi: Hola
new_comment_by_html: Hi ha un nou comentari de <b>%{commenter}</b> en
new_comment_by_html: Hi ha un nou comentari de <strong>%{commenter}</strong> en
subject: Algú ha comentat en el teu %{commentable}
title: Nou comentari
config:
@@ -16,7 +16,7 @@ ca:
title: Verifica el teu compte amb el següent enllaç
reply:
hi: Hola
new_reply_by_html: Hi ha una nova resposta de <b>%{commenter}</b> al teu comentari en
new_reply_by_html: Hi ha una nova resposta de <strong>%{commenter}</strong> al teu comentari en
subject: Algú ha respost al teu comentari
title: Nova resposta al teu comentari
unfeasible_spending_proposal:

View File

@@ -182,8 +182,9 @@ de:
heading_selection_title: "Nach Bezirk"
no_winner_investments: "Keine erfolgreichen Ausgabenvorschläge in diesem Status"
filters:
label: "Aktueller Stand des Projekts"
all: "Alle (%{count})"
status:
label: "Aktueller Stand des Projekts"
all: "Alle (%{count})"
phases:
errors:
dates_range_invalid: "Das Anfangsdatum kann nicht gleich oder später als das Enddatum sein"

View File

@@ -3,7 +3,7 @@ de:
no_reply: "Diese Nachricht wurde von einer E-Mail-Adresse gesendet, die keine Antworten akzeptiert."
comment:
hi: Hallo
new_comment_by_html: Es gibt einen neuen Kommentar von <b>%{commenter}</b>
new_comment_by_html: Es gibt einen neuen Kommentar von <strong>%{commenter}</strong>
subject: Jemand hat Ihre %{commentable} kommentiert.
title: Neuer Kommentar
config:
@@ -16,7 +16,7 @@ de:
title: Bestätigen Sie Ihr Konto über den folgenden Link
reply:
hi: Hallo
new_reply_by_html: Es gibt eine neue Antwort von <b>%{commenter}</b> auf Ihren Kommentar
new_reply_by_html: Es gibt eine neue Antwort von <strong>%{commenter}</strong> auf Ihren Kommentar
subject: Jemand hat auf Ihren Kommentar geantwortet
title: Neue Antwort auf Ihren Kommentar
unfeasible_spending_proposal:

View File

@@ -281,6 +281,7 @@ en:
tags_placeholder: "Write the tags you want separated by commas (,)"
undefined: Undefined
user_groups: "Groups"
milestone_tags: Milestone tags
search_unfeasible: Search unfeasible
milestones:
index:
@@ -297,6 +298,7 @@ en:
documents: "Documents"
milestone: Milestone
new_milestone: Create new milestone
milestone_tags: Milestone Tags
form:
admin_statuses: Manage statuses
no_statuses_defined: There are no defined statuses yet
@@ -355,7 +357,7 @@ en:
notice: "Progress bar updated successfully"
delete:
notice: "Progress bar deleted successfully"
comments:
hidden_comments:
index:
filter: Filter
filters:
@@ -430,7 +432,7 @@ en:
request: Requested resource
update:
success: The task has been marked as solved.
debates:
hidden_debates:
index:
filter: Filter
filters:
@@ -728,6 +730,7 @@ en:
title: Administrators
name: Name
email: Email
description: Description
id: Administrator ID
no_administrators: There are no administrators.
administrator:
@@ -736,6 +739,9 @@ en:
restricted_removal: "Sorry, you can't remove yourself from the administrators"
search:
title: "Administrators: User search"
form:
edit_title: "Edit administrator"
updated: "Administrator updated successfully"
moderators:
index:
title: Moderators
@@ -875,6 +881,9 @@ en:
user_invite:
title: "User Invitation"
description: "Sent to the person that has been invited to register an account."
evaluation_comment:
title: "New evaluation comment"
description: "Sent to administrators and evaluators related to commented investment"
edit_info: "You can edit this email in"
message_title: "Message's Title"
message_body: "This is a sample of message's content."
@@ -882,6 +891,7 @@ en:
no_investments: "There aren't any budget investment created. Some example data is needed in order to preview the email."
no_comments: "There aren't any comments created. Some example data is needed in order to preview the email."
no_replies: "There aren't any replies created. Some example data is needed in order to preview the email."
no_evaluation_comments: "There aren't any evaluation comments created. Some example data is needed in order to preview the email."
emails_download:
index:
title: Emails download
@@ -1261,6 +1271,7 @@ en:
title: Configuration settings
update_setting: Update
participation_processes: "Participation processes"
images_and_documents: "Images and documents"
feature_flags: Features
features:
enabled: "Feature enabled"

Some files were not shown because too many files have changed in this diff Show More