Merge pull request #3334 from consul/admin-polish
Improve UX on admin section
This commit is contained in:
@@ -7,13 +7,11 @@ App.Banners =
|
||||
$(selector).removeClass($(selector).attr("class"), true)
|
||||
.addClass(style, true)
|
||||
|
||||
update_background_color: (selector, text_selector, background_color) ->
|
||||
update_background_color: (selector, background_color) ->
|
||||
$(selector).css("background-color", background_color)
|
||||
$(text_selector).val(background_color)
|
||||
|
||||
update_font_color: (selector, text_selector, font_color) ->
|
||||
update_font_color: (selector, font_color) ->
|
||||
$(selector).css("color", font_color)
|
||||
$(text_selector).val(font_color)
|
||||
|
||||
initialize: ->
|
||||
$("[data-js-banner-title]").on
|
||||
@@ -24,20 +22,11 @@ App.Banners =
|
||||
change: ->
|
||||
App.Banners.update_banner("#js-banner-description", $(this).val())
|
||||
|
||||
$("#banner_background_color_picker").on
|
||||
$("[name='banner[background_color]']").on
|
||||
change: ->
|
||||
App.Banners.update_background_color("#js-banner-background", "#banner_background_color", $(this).val())
|
||||
App.Banners.update_background_color("#js-banner-background", $(this).val())
|
||||
|
||||
$("#banner_background_color").on
|
||||
$("[name='banner[font_color]']").on
|
||||
change: ->
|
||||
App.Banners.update_background_color("#js-banner-background", "#banner_background_color_picker", $(this).val())
|
||||
|
||||
$("#banner_font_color_picker").on
|
||||
change: ->
|
||||
App.Banners.update_font_color("#js-banner-title", "#banner_font_color", $(this).val())
|
||||
App.Banners.update_font_color("#js-banner-description", "#banner_font_color", $(this).val())
|
||||
|
||||
$("#banner_font_color").on
|
||||
change: ->
|
||||
App.Banners.update_font_color("#js-banner-title", "#banner_font_color_picker", $(this).val())
|
||||
App.Banners.update_font_color("#js-banner-description", "#banner_font_color_picker", $(this).val())
|
||||
App.Banners.update_font_color("#js-banner-title", $(this).val())
|
||||
App.Banners.update_font_color("#js-banner-description", $(this).val())
|
||||
|
||||
@@ -26,9 +26,11 @@ App.Forms =
|
||||
synchronizeInputs: ->
|
||||
progress_bar = "[name='progress_bar[percentage]']"
|
||||
process_background = "[name='legislation_process[background_color]']"
|
||||
process_font = "[name='legislation_process[font_color]']"
|
||||
process_font = ", [name='legislation_process[font_color]']"
|
||||
processes = process_background + process_font
|
||||
banners = "[name='banner[background_color]'], [name='banner[font_color]']"
|
||||
|
||||
inputs = $("#{progress_bar}, #{process_background}, #{process_font}")
|
||||
inputs = $("#{progress_bar}, #{processes}, #{banners}")
|
||||
inputs.on
|
||||
input: ->
|
||||
$("[name='#{this.name}']").val($(this).val())
|
||||
|
||||
@@ -2766,15 +2766,15 @@ table {
|
||||
display: inline-block;
|
||||
height: rem-calc(120);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
height: rem-calc(96);
|
||||
}
|
||||
|
||||
img {
|
||||
margin-left: rem-calc(-15);
|
||||
max-width: none;
|
||||
width: rem-calc(120);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,12 +37,9 @@ module AdminHelper
|
||||
["spending_proposals"].include?(controller_name)
|
||||
end
|
||||
|
||||
def menu_poll?
|
||||
%w[polls active_polls recounts results].include?(controller_name)
|
||||
end
|
||||
|
||||
def menu_polls?
|
||||
menu_poll? || %w[questions answers].include?(controller_name)
|
||||
%w[polls active_polls recounts results questions answers].include?(controller_name) ||
|
||||
controller.class.parent == Admin::Poll::Questions::Answers
|
||||
end
|
||||
|
||||
def menu_booths?
|
||||
@@ -54,7 +51,8 @@ module AdminHelper
|
||||
end
|
||||
|
||||
def menu_settings?
|
||||
["settings", "tags", "geozones", "images", "content_blocks"].include?(controller_name)
|
||||
["settings", "tags", "geozones", "images", "content_blocks"].include?(controller_name) &&
|
||||
controller.class.parent != Admin::Poll::Questions::Answers
|
||||
end
|
||||
|
||||
def menu_customization?
|
||||
|
||||
@@ -4,4 +4,20 @@ module BannersHelper
|
||||
@banners.present? && @banners.count > 0
|
||||
end
|
||||
|
||||
def banner_default_bg_color
|
||||
"#e7f2fc"
|
||||
end
|
||||
|
||||
def banner_default_font_color
|
||||
"#222222"
|
||||
end
|
||||
|
||||
def banner_bg_color_or_default
|
||||
@banner.background_color.present? ? @banner.background_color : banner_default_bg_color
|
||||
end
|
||||
|
||||
def banner_font_color_or_default
|
||||
@banner.font_color.present? ? @banner.font_color : banner_default_font_color
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -10,21 +10,11 @@
|
||||
<% end %>
|
||||
|
||||
<% if feature?(:polls) %>
|
||||
<li class="section-title">
|
||||
<a href="#">
|
||||
<li class="section-title <%= "is-active" if menu_polls? %>">
|
||||
<%= link_to admin_polls_path do %>
|
||||
<span class="icon-checkmark-circle"></span>
|
||||
<strong><%= t("admin.menu.title_polls") %></strong>
|
||||
</a>
|
||||
<ul id="polls_menu" <%= "class=is-active" if menu_polls? || controller.class.parent == Admin::Poll::Questions::Answers %>>
|
||||
<li <%= "class=is-active" if menu_poll? %>>
|
||||
<%= link_to t("admin.menu.polls"), admin_polls_path %>
|
||||
</li>
|
||||
|
||||
<li <%= "class=is-active" if %w(questions answers).include?(controller_name) ||
|
||||
controller.class.parent == Admin::Poll::Questions::Answers %>>
|
||||
<%= link_to t("admin.menu.poll_questions"), admin_questions_path %>
|
||||
</li>
|
||||
</ul>
|
||||
<strong><%= t("admin.menu.polls") %></strong>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
@@ -75,6 +65,15 @@
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<% if feature?(:signature_sheets) %>
|
||||
<li class="section-title <%= "is-active" if controller_name == "signature_sheets" %>">
|
||||
<%= link_to admin_signature_sheets_path do %>
|
||||
<span class="icon-file-text-o"></span>
|
||||
<strong><%= t("admin.menu.signature_sheets") %></strong>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% if feature?(:spending_proposals) %>
|
||||
<li class="section-title">
|
||||
<a href="#">
|
||||
@@ -138,15 +137,6 @@
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<% if feature?(:signature_sheets) %>
|
||||
<li class="section-title <%= "is-active" if controller_name == "signature_sheets" %>">
|
||||
<%= link_to admin_signature_sheets_path do %>
|
||||
<span class="icon-file-text-o"></span>
|
||||
<strong><%= t("admin.menu.signature_sheets") %></strong>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<li class="section-title">
|
||||
<a href="#">
|
||||
<span class="icon-eye"></span>
|
||||
|
||||
@@ -64,15 +64,32 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="small-3 column">
|
||||
<%= f.label :sections, t("admin.banners.banner.background_color") %>
|
||||
<%= color_field(:banner, :background_color, id: 'banner_background_color_picker') %>
|
||||
<%= f.text_field :background_color, label: false %>
|
||||
<div class="small-12 medium-6 large-3 column">
|
||||
<%= f.label :background_color, nil, for: "background_color_input" %>
|
||||
<p class="help-text"><%= t("admin.shared.color_help") %></p>
|
||||
<div class="row collapse">
|
||||
<div class="small-12 medium-6 column">
|
||||
<%= f.text_field :background_color, label: false, type: :color,
|
||||
value: banner_bg_color_or_default %>
|
||||
</div>
|
||||
<div class="small-12 medium-6 column">
|
||||
<%= f.text_field :background_color, label: false, id: "background_color_input" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small-3 column end">
|
||||
<%= f.label :sections, t("admin.banners.banner.font_color") %>
|
||||
<%= color_field(:banner, :font_color, id: 'banner_font_color_picker') %>
|
||||
<%= f.text_field :font_color, label: false %>
|
||||
|
||||
<div class="small-12 medium-6 large-3 column end">
|
||||
<%= f.label :font_color, nil, for: "font_color_input" %>
|
||||
<p class="help-text"><%= t("admin.shared.color_help") %></p>
|
||||
<div class="row collapse">
|
||||
<div class="small-12 medium-6 column">
|
||||
<%= f.text_field :font_color, label: false, type: :color,
|
||||
value: banner_font_color_or_default %>
|
||||
</div>
|
||||
<div class="small-12 medium-6 column">
|
||||
<%= f.text_field :font_color, label: false, id: "font_color_input" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -80,11 +97,15 @@
|
||||
<div class="actions small-12 medium-3 column">
|
||||
<%= f.submit(class: "button expanded", value: t("admin.banners.edit.form.submit_button")) %>
|
||||
</div>
|
||||
</div>
|
||||
<div id="js-banner-background" class="banner" style="background-color:<%= @banner.background_color %>">
|
||||
<%= link_to @banner.target_url do %>
|
||||
<h2 id="js-banner-title" style="color:<%= @banner.font_color %>"><%= @banner.title %></h2>
|
||||
<h3 id="js-banner-description" style="color:<%= @banner.font_color %>"><%= @banner.description %></h3>
|
||||
<% end %>
|
||||
|
||||
<div id="js-banner-background" class="banner clear"
|
||||
style="background-color:<%= @banner.background_color %>">
|
||||
<%= link_to @banner.target_url do %>
|
||||
<h2 id="js-banner-title" style="color:<%= @banner.font_color %>"><%= @banner.title %></h2>
|
||||
<h3 id="js-banner-description" style="color:<%= @banner.font_color %>">
|
||||
<%= @banner.description %>
|
||||
</h3>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
<%= form_for [:admin, @geozone] do |f| %>
|
||||
|
||||
<%= render 'errors' %>
|
||||
<%= render "errors" %>
|
||||
|
||||
<div class="small-12 medium-6 large-4 column">
|
||||
<%= f.label :name, t("admin.geozones.geozone.name") %>
|
||||
<%= f.text_field :name, label: false %>
|
||||
<div class="small-12 large-8 column">
|
||||
<%= f.text_field :name %>
|
||||
</div>
|
||||
<div class="small-12 medium-6 large-4 column">
|
||||
<%= f.label :html_map_coordinates, t("admin.geozones.geozone.coordinates") %>
|
||||
|
||||
<div class="clear">
|
||||
<div class="small-12 medium-6 large-4 column">
|
||||
<%= f.label :census_code %>
|
||||
<p class="help-text"><%= t("admin.geozones.geozone.code_help") %></p>
|
||||
<%= f.text_field :census_code, label: false %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 medium-6 large-4 column end">
|
||||
<%= f.label :external_code %>
|
||||
<p class="help-text"><%= t("admin.geozones.geozone.code_help") %></p>
|
||||
<%= f.text_field :external_code, label: false %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="small-12 large-8 column">
|
||||
<%= f.label :html_map_coordinates %>
|
||||
<p class="help-text"><%= t("admin.geozones.geozone.coordinates_help") %></p>
|
||||
<%= f.text_field :html_map_coordinates, label: false %>
|
||||
</div>
|
||||
<div class="small-12 medium-6 large-2 column">
|
||||
<%= f.label :external_code, t("admin.geozones.geozone.external_code") %>
|
||||
<%= f.text_field :external_code, label: false %>
|
||||
</div>
|
||||
<div class="small-12 medium-6 large-2 column">
|
||||
<%= f.label :census_code, t("admin.geozones.geozone.census_code") %>
|
||||
<%= f.text_field :census_code, label: false %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 large-3 medium-3 column end">
|
||||
<%= f.submit(class: "button success expanded", value: t("admin.geozones.edit.form.submit_button")) %>
|
||||
<div class="small-12 column">
|
||||
<%= f.submit(value: t("admin.geozones.edit.form.submit_button"),
|
||||
class: "button success") %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -193,7 +193,7 @@
|
||||
|
||||
<div class="small-6 large-3 column">
|
||||
<%= f.label :background_color, nil, for: "background_color_input" %>
|
||||
<p class="help-text"><%= t("admin.legislation.processes.form.color_help") %></p>
|
||||
<p class="help-text"><%= t("admin.shared.color_help") %></p>
|
||||
<div class="row collapse">
|
||||
<div class="small-12 medium-6 column">
|
||||
<%= f.text_field :background_color, label: false, type: :color,
|
||||
@@ -207,7 +207,7 @@
|
||||
|
||||
<div class="small-6 large-3 column end">
|
||||
<%= f.label :font_color, nil, for: "font_color_input" %>
|
||||
<p class="help-text"><%= t("admin.legislation.processes.form.color_help") %></p>
|
||||
<p class="help-text"><%= t("admin.shared.color_help") %></p>
|
||||
<div class="row collapse">
|
||||
<div class="small-12 medium-6 column">
|
||||
<%= f.text_field :font_color, label: false, type: :color, value: font_color_or_default %>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<h2><%= t("admin.officials.index.title") %></h2>
|
||||
<p><%= t("admin.officials.index.help") %></p>
|
||||
|
||||
<%= render 'admin/shared/user_search', url: search_admin_officials_path %>
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<h3><%= t("admin.polls.show.questions_title") %></h3>
|
||||
<h3 class="inline-block"><%= t("admin.polls.show.questions_title") %></h3>
|
||||
|
||||
<%= link_to t("admin.questions.index.create"), new_admin_question_path(poll_id: @poll.id),
|
||||
class: "button float-right" %>
|
||||
|
||||
<% if @poll.questions.empty? %>
|
||||
<div class="callout primary margin-top">
|
||||
@@ -9,6 +12,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= t("admin.polls.show.table_title") %></th>
|
||||
<th class="small-6"><%= t("admin.actions.actions") %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<% @poll.questions.each do |question| %>
|
||||
@@ -17,6 +21,28 @@
|
||||
<strong>
|
||||
<%= link_to question.title, admin_question_path(question) %>
|
||||
</strong>
|
||||
<% if question.proposal.present? %>
|
||||
<small>
|
||||
<%= link_to t("admin.polls.show.see_proposal"),
|
||||
proposal_path(question.proposal),
|
||||
target: "_blank" %>
|
||||
</small>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<div class="small-4 column">
|
||||
<%= link_to t("admin.polls.show.edit_answers"), admin_question_path(question),
|
||||
class: "button hollow expanded" %>
|
||||
</div>
|
||||
<div class="small-4 column">
|
||||
<%= link_to t("shared.edit"), edit_admin_question_path(question),
|
||||
class: "button hollow expanded" %>
|
||||
</div>
|
||||
<div class="small-4 column">
|
||||
<%= link_to t("shared.delete"), admin_question_path(question),
|
||||
class: "button hollow alert expanded",
|
||||
method: :delete %>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -7,13 +7,19 @@
|
||||
<%= f.hidden_field :proposal_id %>
|
||||
|
||||
<div class="small-12">
|
||||
<div class="small-12 medium-6 large-4">
|
||||
<% select_options = Poll.all.map { |p| [p.name, p.id] } %>
|
||||
<%= f.select :poll_id,
|
||||
options_for_select(select_options),
|
||||
prompt: t("admin.questions.index.select_poll"),
|
||||
label: t("admin.questions.new.poll_label") %>
|
||||
</div>
|
||||
<% if @poll.present? %>
|
||||
<%= f.hidden_field :poll_id, value: @poll.id %>
|
||||
<% elsif @question.poll.present? %>
|
||||
<%= f.hidden_field :poll_id, value: @question.poll.id %>
|
||||
<% else %>
|
||||
<div class="small-12 medium-6 large-4">
|
||||
<% select_options = Poll.all.map { |p| [p.name, p.id] } %>
|
||||
<%= f.select :poll_id,
|
||||
options_for_select(select_options),
|
||||
prompt: t("admin.questions.index.select_poll"),
|
||||
label: t("admin.questions.new.poll_label") %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= f.translatable_fields do |translations_form| %>
|
||||
<%= translations_form.text_field :title %>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<%= back_link_to %>
|
||||
|
||||
<h2 class="margin-top"><%= t("admin.questions.new.title") %></h2>
|
||||
<h2 class="margin-top">
|
||||
<% if @poll.present? %>
|
||||
<%= t("admin.questions.new.title", poll: @poll.name) %>
|
||||
<% else %>
|
||||
<%= t("admin.questions.new.title_proposal") %>
|
||||
<% end %>
|
||||
</h2>
|
||||
|
||||
<div class="poll-question-form">
|
||||
<%= render "form", form_url: admin_questions_path %>
|
||||
|
||||
@@ -3,18 +3,24 @@
|
||||
<% end %>
|
||||
|
||||
<div class="proposal-show">
|
||||
<h2><%= @proposal.title %></h2>
|
||||
<h2 class="inline-block"><%= @proposal.title %></h2>
|
||||
|
||||
<% if @proposal.successful? %>
|
||||
<div class="callout success">
|
||||
<%= t("proposals.proposal.successful") %>
|
||||
|
||||
<div class="float-right">
|
||||
<%= link_to t("poll_questions.create_question"),
|
||||
<%= link_to t("admin.proposals.show.create_question"),
|
||||
new_admin_question_path(proposal_id: @proposal.id),
|
||||
class: "button medium" %>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="float-right">
|
||||
<%= link_to t("admin.proposals.show.create_question"),
|
||||
new_admin_question_path(proposal_id: @proposal.id),
|
||||
class: "button hollow medium" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render "proposals/info", proposal: @proposal %>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<% banner = @banners.sample %>
|
||||
<div class="banner" style="background-color:<%= banner.background_color %>">
|
||||
<div class="banner" style="background-color:<%= banner.background_color %>;">
|
||||
<%= link_to banner.target_url do %>
|
||||
<h2 style="color:<%= banner.font_color %>"><%=banner.title %></h2>
|
||||
<h3 style="color:<%= banner.font_color %>"><%=banner.description %></h3>
|
||||
<h2 style="color:<%= banner.font_color %>;"><%= banner.title %></h2>
|
||||
<h3 style="color:<%= banner.font_color %>;"><%= banner.description %></h3>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -46,9 +46,7 @@
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<%= t("verification.letter.new.office",
|
||||
office: link_to(t("verification.letter.new.offices"), setting['verification_offices_url'],
|
||||
target: "blank")).html_safe %>
|
||||
<%= t("verification.letter.new.office") %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to t("verification.letter.new.send_letter"), letter_path, method: :post %>
|
||||
|
||||
@@ -7,12 +7,7 @@
|
||||
<h1><%= t("verification.letter.edit.title") %></h1>
|
||||
|
||||
<div data-alert class="callout success">
|
||||
<%= t("verification.letter.create.flash.success_html",
|
||||
offices: link_to(t("verification.letter.create.flash.offices"),
|
||||
setting['verification_offices_url'],
|
||||
title: t('shared.target_blank_html'),
|
||||
target: "_blank")).html_safe
|
||||
%>
|
||||
<%= t("verification.letter.create.flash.success_html") %>
|
||||
</div>
|
||||
|
||||
<%= link_to t("verification.letter.edit.see_all"), proposals_path, class: "button warning" %>
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
<button class="close-button" aria-label="<%= t("application.close") %>" type="button" data-close>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<%= t("verification.residence.new.error_verifying_census",
|
||||
offices: link_to( t("verification.residence.new.error_verifying_census_offices"),
|
||||
setting['verification_offices_url'], title: t('shared.target_blank_html'), target: "_blank")).html_safe %>
|
||||
<%= t("verification.residence.new.error_verifying_census") %>
|
||||
</div>
|
||||
|
||||
<% else %>
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
|
||||
<% feed.items.each do |item| %>
|
||||
<div class="<%= item.class.to_s.parameterize('_') %> row">
|
||||
<div class="feed-description small-12 column
|
||||
<%= 'medium-6 large-9' if item.image.present? %>">
|
||||
<strong><%= link_to item.title, url_for(item) %></strong><br>
|
||||
<p><%= item.summary %></p>
|
||||
</div>
|
||||
<% if item.image.present? %>
|
||||
<div class="small-12 medium-6 large-3 column">
|
||||
<div class="feed-image">
|
||||
@@ -17,11 +22,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="feed-description small-12 medium-6 large-9 column
|
||||
<%= item.image.present? ? '' : 'medium-offset-6 large-offset-3' %>">
|
||||
<strong><%= link_to item.title, url_for(item) %></strong><br>
|
||||
<p><%= item.summary %></p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -131,6 +131,11 @@ en:
|
||||
organization_name: "If you are proposing in the name of a collective/organization, or on behalf of more people, write its name"
|
||||
image: "Proposal descriptive image"
|
||||
image_title: "Image title"
|
||||
geozone:
|
||||
name: Name
|
||||
external_code: "External code (optional)"
|
||||
census_code: "Census code (optional)"
|
||||
html_map_coordinates: "HTML <map> Coordinates (optional)"
|
||||
milestone:
|
||||
status_id: "Current status (optional)"
|
||||
title: "Title"
|
||||
@@ -229,6 +234,9 @@ en:
|
||||
name: Name
|
||||
locale: locale
|
||||
body: Body
|
||||
banner:
|
||||
background_color: Background color
|
||||
font_color: Font color
|
||||
legislation/process:
|
||||
title: Process Title
|
||||
summary: Summary
|
||||
|
||||
@@ -38,8 +38,6 @@ en:
|
||||
proposals: Proposals
|
||||
budgets: Participatory budgeting
|
||||
help_page: Help page
|
||||
background_color: Background colour
|
||||
font_color: Font colour
|
||||
edit:
|
||||
editing: Edit banner
|
||||
form:
|
||||
@@ -432,7 +430,6 @@ en:
|
||||
homepage_description: Here you can explain the content of the process
|
||||
homepage_enabled: Homepage enabled
|
||||
banner_title: Header colors
|
||||
color_help: Hexadecimal format
|
||||
index:
|
||||
create: New process
|
||||
delete: Delete
|
||||
@@ -480,7 +477,7 @@ en:
|
||||
selected: Selected
|
||||
form:
|
||||
custom_categories: Categories
|
||||
custom_categories_description: Categories that users can select creating the proposal. Max 160 characteres by category.
|
||||
custom_categories_description: Categories that users can select creating the proposal. Max 160 characteres.
|
||||
custom_categories_placeholder: Enter the tags you would like to use, separated by commas (,) and between quotes ("")
|
||||
draft_versions:
|
||||
create:
|
||||
@@ -585,7 +582,6 @@ en:
|
||||
activity: Moderator activity
|
||||
admin: Admin menu
|
||||
banner: Manage banners
|
||||
poll_questions: Questions
|
||||
proposals: Proposals
|
||||
proposals_topics: Proposals topics
|
||||
budgets: Participatory budgets
|
||||
@@ -637,7 +633,6 @@ en:
|
||||
update: "Update Block"
|
||||
title_moderated_content: Moderated content
|
||||
title_budgets: Budgets
|
||||
title_polls: Polls
|
||||
title_profiles: Profiles
|
||||
title_settings: Settings
|
||||
title_site_customization: Site content
|
||||
@@ -979,6 +974,8 @@ en:
|
||||
no_questions: "There are no questions assigned to this poll."
|
||||
questions_title: "List of questions"
|
||||
table_title: "Title"
|
||||
edit_answers: Edit answers
|
||||
see_proposal: "(See proposal)"
|
||||
flash:
|
||||
question_added: "Question added to this poll"
|
||||
error_on_question_added: "Question could not be assigned to this poll"
|
||||
@@ -999,7 +996,8 @@ en:
|
||||
edit:
|
||||
title: "Edit Question"
|
||||
new:
|
||||
title: "Create Question"
|
||||
title: "Create question to poll %{poll}"
|
||||
title_proposal: "Create question"
|
||||
poll_label: "Poll"
|
||||
answers:
|
||||
images:
|
||||
@@ -1100,6 +1098,7 @@ en:
|
||||
official_updated: Details of official saved
|
||||
index:
|
||||
title: Officials
|
||||
help: User accounts marked as officials will have their interventions on the platform highlighted. You can define the label that appears next to the user names of these accounts from most important (level 1) to least (level 5).
|
||||
no_officials: There are no officials.
|
||||
name: Name
|
||||
official_position: Official position
|
||||
@@ -1150,6 +1149,8 @@ en:
|
||||
author: Author
|
||||
milestones: Milestones
|
||||
no_proposals: There are no proposals.
|
||||
show:
|
||||
create_question: Add this proposal to a poll to be voted
|
||||
hidden_proposals:
|
||||
index:
|
||||
filter: Filter
|
||||
@@ -1228,6 +1229,7 @@ en:
|
||||
content: Content
|
||||
created_at: Created at
|
||||
delete: Delete
|
||||
color_help: Hexadecimal format
|
||||
spending_proposals:
|
||||
index:
|
||||
geozone_filter_all: All zones
|
||||
@@ -1294,7 +1296,9 @@ en:
|
||||
name: Name
|
||||
external_code: External code
|
||||
census_code: Census code
|
||||
code_help: Response code for this geozone on the census API
|
||||
coordinates: Coordinates
|
||||
coordinates_help: Coordinates that will generate a clickable area on an HTML image map
|
||||
errors:
|
||||
form:
|
||||
error:
|
||||
|
||||
@@ -508,7 +508,6 @@ en:
|
||||
title: "Questions"
|
||||
most_voted_answer: "Most voted answer: "
|
||||
poll_questions:
|
||||
create_question: "Create question"
|
||||
show:
|
||||
vote_answer: "Vote %{answer}"
|
||||
voted: "You have voted %{answer}"
|
||||
|
||||
@@ -71,7 +71,6 @@ en:
|
||||
min_age_to_participate: Minimum age needed to participate
|
||||
min_age_to_participate_description: "Users over this age can participate in all processes where a user verified account is needed"
|
||||
analytics_url: "Analytics URL"
|
||||
verification_offices_url: Verification offices URL
|
||||
proposal_notification_minimum_interval_in_days: "Minimum interval (in days) for authors of proposals to send new proposal notifications"
|
||||
proposal_notification_minimum_interval_in_days_description: "The number of days user can send a notification for all supporters of their proposal"
|
||||
direct_message_max_per_day: "Direct Message max number per day"
|
||||
@@ -95,7 +94,7 @@ en:
|
||||
polls_description: "Citizens' polls are a participatory mechanism by which citizens with voting rights can make direct decisions"
|
||||
signature_sheets: "Signature sheets"
|
||||
signature_sheets_description: "It allows adding from the Administration panel signatures collected on-site to Proposals and investment projects of the Participative Budgets"
|
||||
legislation: "Legislation"
|
||||
legislation: "Collaborative Legislation"
|
||||
legislation_description: "In participatory processes, citizens are offered the opportunity to participate in the drafting and modification of regulations and to give their opinion on certain actions that are planned to be carried out"
|
||||
spending_proposals: "Spending proposals"
|
||||
spending_proposals_description: "⚠️ NOTE: This functionality has been replaced by Participatory Budgeting and will disappear in new versions"
|
||||
|
||||
@@ -19,8 +19,7 @@ en:
|
||||
unconfirmed_code: You have not yet entered the confirmation code
|
||||
create:
|
||||
flash:
|
||||
offices: Citizen Support Offices
|
||||
success_html: Thank you for requesting your <b>maximum security code (only required for the final votes)</b>. In a few days we will send it to the address featuring in the data we have on file. Please remember that, if you prefer, you can collect your code from any of the %{offices}.
|
||||
success_html: Thank you for requesting your <b>maximum security code (only required for the final votes)</b>. In a few days we will send it to the address featuring in the data we have on file. Please remember that, if you prefer, you can collect your code from any of the Citizen Support Offices.
|
||||
edit:
|
||||
see_all: See proposals
|
||||
title: Letter requested
|
||||
@@ -29,8 +28,7 @@ en:
|
||||
new:
|
||||
explanation: 'For participate on final voting you can:'
|
||||
go_to_index: See proposals
|
||||
office: Verify in any %{office}
|
||||
offices: Citizen Support Offices
|
||||
office: Verify in any Citizen Support Offices
|
||||
send_letter: Send me a letter with the code
|
||||
title: Congratulations!
|
||||
user_permission_info: With your account you can...
|
||||
@@ -60,8 +58,7 @@ en:
|
||||
document_type_label: Document type
|
||||
error_not_allowed_age: You don't have the required age to participate
|
||||
error_not_allowed_postal_code: In order to be verified, you must be registered.
|
||||
error_verifying_census: The Census was unable to verify your information. Please confirm that your census details are correct by calling to City Council or visit one %{offices}.
|
||||
error_verifying_census_offices: Citizen Support Office
|
||||
error_verifying_census: The Census was unable to verify your information. Please confirm that your census details are correct by calling to City Council or visit one Citizen Support Office.
|
||||
form_errors: prevented the verification of your residence
|
||||
postal_code: Postcode
|
||||
postal_code_note: To verify your account you must be registered
|
||||
|
||||
@@ -131,6 +131,11 @@ es:
|
||||
organization_name: "Si estás proponiendo en nombre de una organización o colectivo, o en nombre de más gente, escribe su nombre"
|
||||
image: "Imagen descriptiva del proyecto de gasto"
|
||||
image_title: "Título de la imagen"
|
||||
geozone:
|
||||
name: Nombre
|
||||
external_code: "Código externo (opcional)"
|
||||
census_code: "Código del censo (opcional)"
|
||||
html_map_coordinates: "Coordenadas HTML <map> (opcional)"
|
||||
milestone:
|
||||
status_id: "Estado actual (opcional)"
|
||||
title: "Título"
|
||||
@@ -229,6 +234,9 @@ es:
|
||||
name: Nombre
|
||||
locale: Idioma
|
||||
body: Contenido
|
||||
banner:
|
||||
background_color: Color del fondo
|
||||
font_color: Color del texto
|
||||
legislation/process:
|
||||
title: Título del proceso
|
||||
summary: Resumen
|
||||
|
||||
@@ -38,8 +38,6 @@ es:
|
||||
proposals: Propuestas
|
||||
budgets: Presupuestos participativos
|
||||
help_page: Página de ayuda
|
||||
background_color: Color de fondo
|
||||
font_color: Color del texto
|
||||
edit:
|
||||
editing: Editar el banner
|
||||
form:
|
||||
@@ -432,7 +430,6 @@ es:
|
||||
homepage_description: Aquí puedes explicar el contenido del proceso
|
||||
homepage_enabled: Homepage activada
|
||||
banner_title: Colores del encabezado
|
||||
color_help: Formato hexadecimal
|
||||
index:
|
||||
create: Nuevo proceso
|
||||
delete: Borrar
|
||||
@@ -479,7 +476,7 @@ es:
|
||||
selected: Seleccionada
|
||||
form:
|
||||
custom_categories: Categorías
|
||||
custom_categories_description: Categorías que el usuario puede seleccionar al crear la propuesta. Máximo 160 caracteres por categoría.
|
||||
custom_categories_description: Categorías que el usuario puede seleccionar al crear la propuesta. Máximo 160 caracteres.
|
||||
custom_categories_placeholder: Escribe las etiquetas que desees separadas por una coma (,) y entrecomilladas ("")
|
||||
draft_versions:
|
||||
create:
|
||||
@@ -584,11 +581,10 @@ es:
|
||||
activity: Actividad de moderadores
|
||||
admin: Menú de administración
|
||||
banner: Gestionar banners
|
||||
poll_questions: Preguntas
|
||||
proposals: Propuestas
|
||||
proposals_topics: Temas de propuestas
|
||||
budgets: Presupuestos participativos
|
||||
geozones: Gestionar distritos
|
||||
geozones: Gestionar zonas
|
||||
hidden_comments: Comentarios ocultos
|
||||
hidden_debates: Debates ocultos
|
||||
hidden_proposals: Propuestas ocultas
|
||||
@@ -636,7 +632,6 @@ es:
|
||||
update: "Actualizar Bloque"
|
||||
title_moderated_content: Contenido moderado
|
||||
title_budgets: Presupuestos
|
||||
title_polls: Votaciones
|
||||
title_profiles: Perfiles
|
||||
title_settings: Configuración
|
||||
title_site_customization: Contenido del sitio
|
||||
@@ -978,6 +973,8 @@ es:
|
||||
no_questions: "No hay preguntas asignadas a esta votación."
|
||||
questions_title: "Listado de preguntas asignadas"
|
||||
table_title: "Título"
|
||||
edit_answers: Editar respuestas
|
||||
see_proposal: "(Ver propuesta)"
|
||||
flash:
|
||||
question_added: "Pregunta añadida a esta votación"
|
||||
error_on_question_added: "No se pudo asignar la pregunta"
|
||||
@@ -998,7 +995,8 @@ es:
|
||||
edit:
|
||||
title: "Editar pregunta ciudadana"
|
||||
new:
|
||||
title: "Crear pregunta ciudadana"
|
||||
title: "Crear pregunta ciudadana para la votación %{poll}"
|
||||
title_proposal: "Crear pregunta ciudadana"
|
||||
poll_label: "Votación"
|
||||
answers:
|
||||
images:
|
||||
@@ -1099,6 +1097,7 @@ es:
|
||||
official_updated: Datos del cargo público guardados
|
||||
index:
|
||||
title: Cargos Públicos
|
||||
help: Las cuentas de usuario marcadas como cargos públicos tendrán resaltadas sus intervenciones en la plataforma. Puedes definir la etiqueta que aparece al lado de los nombres de usuario de estas cuentas de mayor importancia (nivel 1) a menor (nivel 5).
|
||||
no_officials: No hay cargos públicos.
|
||||
name: Nombre
|
||||
official_position: Cargo público
|
||||
@@ -1149,6 +1148,8 @@ es:
|
||||
author: Autor
|
||||
milestones: Hitos
|
||||
no_proposals: No hay propuestas.
|
||||
show:
|
||||
create_question: Añadir esta propuesta a una votación para ser votada
|
||||
hidden_proposals:
|
||||
index:
|
||||
filter: Filtro
|
||||
@@ -1227,6 +1228,7 @@ es:
|
||||
content: Contenido
|
||||
created_at: Fecha de creación
|
||||
delete: Eliminar
|
||||
color_help: Formato hexadecimal
|
||||
spending_proposals:
|
||||
index:
|
||||
geozone_filter_all: Todos los ámbitos de actuación
|
||||
@@ -1285,31 +1287,33 @@ es:
|
||||
cost_for_geozone: Coste total
|
||||
geozones:
|
||||
index:
|
||||
title: Distritos
|
||||
create: Crear un distrito
|
||||
title: Zonas
|
||||
create: Crear una zona
|
||||
edit: Editar
|
||||
delete: Borrar
|
||||
geozone:
|
||||
name: Nombre
|
||||
external_code: Código externo
|
||||
census_code: Código del censo
|
||||
code_help: Código de respuesta para esta zona en la API del censo
|
||||
coordinates: Coordenadas
|
||||
coordinates_help: Coordenadas que generarán una zona clicable en un mapa de imagen HTML
|
||||
errors:
|
||||
form:
|
||||
error:
|
||||
one: "error impidió guardar el distrito"
|
||||
other: 'errores impidieron guardar el distrito.'
|
||||
one: "error impidió guardar la zona."
|
||||
other: 'errores impidieron guardar la zona.'
|
||||
edit:
|
||||
form:
|
||||
submit_button: Guardar cambios
|
||||
editing: Editando distrito
|
||||
editing: Editando zona
|
||||
back: Volver
|
||||
new:
|
||||
back: Volver
|
||||
creating: Crear distrito
|
||||
creating: Crear zona
|
||||
delete:
|
||||
success: Distrito borrado correctamente
|
||||
error: No se puede borrar el distrito porque ya tiene elementos asociados
|
||||
success: Zona borrada correctamente
|
||||
error: No se puede borrar la zona porque ya tiene elementos asociados
|
||||
signature_sheets:
|
||||
author: Autor
|
||||
created_at: Fecha de creación
|
||||
|
||||
@@ -508,7 +508,6 @@ es:
|
||||
title: "Preguntas"
|
||||
most_voted_answer: "Respuesta más votada: "
|
||||
poll_questions:
|
||||
create_question: "Crear pregunta"
|
||||
show:
|
||||
vote_answer: "Votar %{answer}"
|
||||
voted: "Has votado %{answer}"
|
||||
|
||||
@@ -71,7 +71,6 @@ es:
|
||||
min_age_to_participate: Edad mínima para participar
|
||||
min_age_to_participate_description: "Los usuarios mayores de esta edad podrán participar en todos los procesos donde se necesite una cueta verificada"
|
||||
analytics_url: "URL de estadísticas externas"
|
||||
verification_offices_url: URL oficinas verificación
|
||||
proposal_notification_minimum_interval_in_days: "Intervalo mínimo (en días) para que los autores de propuestas puedan enviar nuevas notificaciones de propuesta"
|
||||
proposal_notification_minimum_interval_in_days_description: "El número de días en los que un usuario puede enviar una notificación a todos los que apoyan su propuesta"
|
||||
direct_message_max_per_day: "Mensajes directos máximos por día"
|
||||
@@ -95,7 +94,7 @@ es:
|
||||
polls_description: "Las votaciones ciudadanas son un mecanismo de participación por el que la ciudadanía con derecho a voto puede tomar decisiones de forma directa"
|
||||
signature_sheets: "Hojas de firmas"
|
||||
signature_sheets_description: "Permite añadir desde el panel de Administración firmas recogidas de forma presencial a Propuestas y proyectos de gasto de los Presupuestos participativos"
|
||||
legislation: "Legislación"
|
||||
legislation: "Legislación colaborativa"
|
||||
legislation_description: "En los procesos participativos se ofrece a la ciudadanía la oportunidad de participar en la elaboración y modificación de normativa y de dar su opinión sobre ciertas actuaciones que se tiene previsto llevar a cabo"
|
||||
spending_proposals: "Propuestas de inversión"
|
||||
spending_proposals_description: "⚠️ NOTA: Esta funcionalidad ha sido sustituida por Pesupuestos Participativos y desaparecerá en nuevas versiones"
|
||||
|
||||
@@ -19,8 +19,7 @@ es:
|
||||
unconfirmed_code: Todavía no has introducido el código de confirmación
|
||||
create:
|
||||
flash:
|
||||
offices: Oficinas de Atención al Ciudadano
|
||||
success_html: Antes de las votaciones recibirás una carta con las instrucciones para verificar tu cuenta.<br> Recuerda que puedes ahorrar el envío verificándote presencialmente en cualquiera de las %{offices}.
|
||||
success_html: Antes de las votaciones recibirás una carta con las instrucciones para verificar tu cuenta.<br> Recuerda que puedes ahorrar el envío verificándote presencialmente en cualquiera de las Oficinas de Atención al Ciudadano.
|
||||
edit:
|
||||
see_all: Ver propuestas
|
||||
title: Carta solicitada
|
||||
@@ -29,8 +28,7 @@ es:
|
||||
new:
|
||||
explanation: 'Para participar en las votaciones finales puedes:'
|
||||
go_to_index: Ver propuestas
|
||||
office: Verificarte presencialmente en cualquier %{office}
|
||||
offices: Oficina de Atención al Ciudadano
|
||||
office: Verificarte presencialmente en cualquier Oficina de Atención al Ciudadano
|
||||
send_letter: Solicitar una carta por correo postal
|
||||
title: '¡Felicidades!'
|
||||
user_permission_info: Con tu cuenta ya puedes...
|
||||
@@ -60,8 +58,7 @@ es:
|
||||
document_type_label: Tipo de documento
|
||||
error_not_allowed_age: No tienes la edad mínima para participar
|
||||
error_not_allowed_postal_code: Para verificarte debes estar empadronado.
|
||||
error_verifying_census: El Padrón no pudo verificar tu información. Por favor, confirma que tus datos de empadronamiento sean correctos llamando al Ayuntamiento o visitando una %{offices}.
|
||||
error_verifying_census_offices: oficina de Atención al ciudadano
|
||||
error_verifying_census: El Padrón no pudo verificar tu información. Por favor, confirma que tus datos de empadronamiento sean correctos llamando al Ayuntamiento o visitando una Oficina de Atención al Ciudadano.
|
||||
form_errors: evitaron verificar tu residencia
|
||||
postal_code: Código postal
|
||||
postal_code_note: Para verificar tus datos debes estar empadronado
|
||||
|
||||
@@ -28,7 +28,7 @@ section "Creating Settings" do
|
||||
|
||||
Setting.create(key: "feature.debates", value: "true")
|
||||
Setting.create(key: "feature.proposals", value: "true")
|
||||
Setting.create(key: "feature.featured_proposals", value: "true")
|
||||
Setting.create(key: "feature.featured_proposals", value: nil)
|
||||
Setting.create(key: "feature.polls", value: "true")
|
||||
Setting.create(key: "feature.spending_proposals", value: nil)
|
||||
Setting.create(key: "feature.spending_proposal_features.voting_allowed", value: nil)
|
||||
@@ -58,7 +58,6 @@ section "Creating Settings" do
|
||||
Setting.create(key: "meta_description", value: "Citizen participation tool for an open, "\
|
||||
"transparent and democratic government")
|
||||
Setting.create(key: "meta_keywords", value: "citizen participation, open government")
|
||||
Setting.create(key: "verification_offices_url", value: "http://oficinas-atencion-ciudadano.url/")
|
||||
Setting.create(key: "min_age_to_participate", value: "16")
|
||||
Setting.create(key: "map_latitude", value: 40.41)
|
||||
Setting.create(key: "map_longitude", value: -3.7)
|
||||
|
||||
97
db/seeds.rb
97
db/seeds.rb
@@ -1,7 +1,9 @@
|
||||
# coding: utf-8
|
||||
# Default admin user (change password after first deploy to a server!)
|
||||
if Administrator.count == 0 && !Rails.env.test?
|
||||
admin = User.create!(username: 'admin', email: 'admin@consul.dev', password: '12345678', password_confirmation: '12345678', confirmed_at: Time.current, terms_of_service: "1")
|
||||
admin = User.create!(username: "admin", email: "admin@consul.dev", password: "12345678",
|
||||
password_confirmation: "12345678", confirmed_at: Time.current,
|
||||
terms_of_service: "1")
|
||||
admin.create_administrator
|
||||
end
|
||||
|
||||
@@ -23,10 +25,10 @@ Setting["max_votes_for_debate_edit"] = 1000
|
||||
Setting["max_votes_for_proposal_edit"] = 1000
|
||||
|
||||
# Max length for comments
|
||||
Setting['comments_body_max_length'] = 1000
|
||||
Setting["comments_body_max_length"] = 1000
|
||||
|
||||
# Prefix for the Proposal codes
|
||||
Setting["proposal_code_prefix"] = 'CONSUL'
|
||||
Setting["proposal_code_prefix"] = "CONSUL"
|
||||
|
||||
# Number of votes needed for proposal success
|
||||
Setting["votes_for_proposal_success"] = 53726
|
||||
@@ -36,13 +38,13 @@ Setting["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
|
||||
Setting["email_domain_for_officials"] = ''
|
||||
Setting["email_domain_for_officials"] = ""
|
||||
|
||||
# Code to be included at the top (inside <head>) of every page (useful for tracking)
|
||||
Setting['per_page_code_head'] = ''
|
||||
Setting["per_page_code_head"] = ""
|
||||
|
||||
# Code to be included at the top (inside <body>) of every page
|
||||
Setting['per_page_code_body'] = ''
|
||||
Setting["per_page_code_body"] = ""
|
||||
|
||||
# Social settings
|
||||
Setting["twitter_handle"] = nil
|
||||
@@ -64,64 +66,63 @@ Setting["meta_description"] = nil
|
||||
Setting["meta_keywords"] = nil
|
||||
|
||||
# Feature flags
|
||||
Setting['feature.debates'] = true
|
||||
Setting['feature.proposals'] = true
|
||||
Setting['feature.featured_proposals'] = true
|
||||
Setting['feature.spending_proposals'] = nil
|
||||
Setting['feature.polls'] = true
|
||||
Setting['feature.twitter_login'] = true
|
||||
Setting['feature.facebook_login'] = true
|
||||
Setting['feature.google_login'] = true
|
||||
Setting['feature.public_stats'] = true
|
||||
Setting['feature.budgets'] = true
|
||||
Setting['feature.signature_sheets'] = true
|
||||
Setting['feature.legislation'] = true
|
||||
Setting['feature.user.recommendations'] = true
|
||||
Setting['feature.user.recommendations_on_debates'] = true
|
||||
Setting['feature.user.recommendations_on_proposals'] = true
|
||||
Setting['feature.community'] = true
|
||||
Setting['feature.map'] = nil
|
||||
Setting['feature.allow_images'] = true
|
||||
Setting['feature.allow_attached_documents'] = true
|
||||
Setting['feature.help_page'] = true
|
||||
Setting["feature.debates"] = true
|
||||
Setting["feature.proposals"] = true
|
||||
Setting["feature.featured_proposals"] = nil
|
||||
Setting["feature.spending_proposals"] = nil
|
||||
Setting["feature.polls"] = true
|
||||
Setting["feature.twitter_login"] = true
|
||||
Setting["feature.facebook_login"] = true
|
||||
Setting["feature.google_login"] = true
|
||||
Setting["feature.public_stats"] = true
|
||||
Setting["feature.budgets"] = true
|
||||
Setting["feature.signature_sheets"] = true
|
||||
Setting["feature.legislation"] = true
|
||||
Setting["feature.user.recommendations"] = true
|
||||
Setting["feature.user.recommendations_on_debates"] = true
|
||||
Setting["feature.user.recommendations_on_proposals"] = true
|
||||
Setting["feature.community"] = true
|
||||
Setting["feature.map"] = nil
|
||||
Setting["feature.allow_images"] = true
|
||||
Setting["feature.allow_attached_documents"] = true
|
||||
Setting["feature.help_page"] = true
|
||||
|
||||
# Spending proposals feature flags
|
||||
Setting['feature.spending_proposal_features.voting_allowed'] = nil
|
||||
Setting["feature.spending_proposal_features.voting_allowed"] = nil
|
||||
|
||||
# Proposal notifications
|
||||
Setting['proposal_notification_minimum_interval_in_days'] = 3
|
||||
Setting['direct_message_max_per_day'] = 3
|
||||
Setting["proposal_notification_minimum_interval_in_days"] = 3
|
||||
Setting["direct_message_max_per_day"] = 3
|
||||
|
||||
# Email settings
|
||||
Setting['mailer_from_name'] = 'CONSUL'
|
||||
Setting['mailer_from_address'] = 'noreply@consul.dev'
|
||||
Setting["mailer_from_name"] = "CONSUL"
|
||||
Setting["mailer_from_address"] = "noreply@consul.dev"
|
||||
|
||||
# Verification settings
|
||||
Setting['verification_offices_url'] = 'http://oficinas-atencion-ciudadano.url/'
|
||||
Setting['min_age_to_participate'] = 16
|
||||
Setting["min_age_to_participate"] = 16
|
||||
|
||||
# Featured proposals
|
||||
Setting['featured_proposals_number'] = 3
|
||||
Setting["featured_proposals_number"] = 3
|
||||
|
||||
# City map feature default configuration (Greenwich)
|
||||
Setting['map_latitude'] = 51.48
|
||||
Setting['map_longitude'] = 0.0
|
||||
Setting['map_zoom'] = 10
|
||||
Setting["map_latitude"] = 51.48
|
||||
Setting["map_longitude"] = 0.0
|
||||
Setting["map_zoom"] = 10
|
||||
|
||||
# Related content
|
||||
Setting['related_content_score_threshold'] = -0.3
|
||||
Setting["related_content_score_threshold"] = -0.3
|
||||
|
||||
Setting["feature.user.skip_verification"] = 'true'
|
||||
Setting["feature.user.skip_verification"] = "true"
|
||||
|
||||
Setting['feature.homepage.widgets.feeds.proposals'] = true
|
||||
Setting['feature.homepage.widgets.feeds.debates'] = true
|
||||
Setting['feature.homepage.widgets.feeds.processes'] = true
|
||||
Setting["feature.homepage.widgets.feeds.proposals"] = true
|
||||
Setting["feature.homepage.widgets.feeds.debates"] = true
|
||||
Setting["feature.homepage.widgets.feeds.processes"] = true
|
||||
|
||||
# Votes hot_score configuration
|
||||
Setting['hot_score_period_in_days'] = 31
|
||||
Setting["hot_score_period_in_days"] = 31
|
||||
|
||||
WebSection.create(name: 'homepage')
|
||||
WebSection.create(name: 'debates')
|
||||
WebSection.create(name: 'proposals')
|
||||
WebSection.create(name: 'budgets')
|
||||
WebSection.create(name: 'help_page')
|
||||
WebSection.create(name: "homepage")
|
||||
WebSection.create(name: "debates")
|
||||
WebSection.create(name: "proposals")
|
||||
WebSection.create(name: "budgets")
|
||||
WebSection.create(name: "help_page")
|
||||
|
||||
@@ -2,38 +2,57 @@ namespace :settings do
|
||||
|
||||
desc "Changes Setting key per_page_code for per_page_code_head"
|
||||
task per_page_code_migration: :environment do
|
||||
per_page_code_setting = Setting.where(key: 'per_page_code').first
|
||||
per_page_code = Setting.where(key: "per_page_code").first
|
||||
per_page_code_head = Setting.where(key: "per_page_code_head").first
|
||||
|
||||
Setting['per_page_code_head'] = per_page_code_setting&.value.to_s if Setting.where(key: 'per_page_code_head').first.blank?
|
||||
per_page_code_setting.destroy if per_page_code_setting.present?
|
||||
Setting["per_page_code_head"] = per_page_code&.value.to_s if per_page_code_head.blank?
|
||||
per_page_code.destroy if per_page_code.present?
|
||||
end
|
||||
|
||||
desc "Create new Attached Documents feature setting"
|
||||
task create_attached_documents_setting: :environment do
|
||||
Setting['feature.allow_attached_documents'] = true
|
||||
Setting["feature.allow_attached_documents"] = true
|
||||
end
|
||||
|
||||
desc "Enable recommendations settings"
|
||||
task enable_recommendations: :environment do
|
||||
Setting['feature.user.recommendations'] = true
|
||||
Setting['feature.user.recommendations_on_debates'] = true
|
||||
Setting['feature.user.recommendations_on_proposals'] = true
|
||||
Setting["feature.user.recommendations"] = true
|
||||
Setting["feature.user.recommendations_on_debates"] = true
|
||||
Setting["feature.user.recommendations_on_proposals"] = true
|
||||
end
|
||||
|
||||
desc "Enable Help page"
|
||||
task enable_help_page: :environment do
|
||||
Setting['feature.help_page'] = true
|
||||
Setting["feature.help_page"] = true
|
||||
end
|
||||
|
||||
desc "Enable Featured proposals"
|
||||
task enable_featured_proposals: :environment do
|
||||
Setting['feature.featured_proposals'] = true
|
||||
Setting['featured_proposals_number'] = 3
|
||||
Setting["feature.featured_proposals"] = true
|
||||
Setting["featured_proposals_number"] = 3
|
||||
end
|
||||
|
||||
desc "Create new period to calculate hot_score"
|
||||
task create_hot_score_period_setting: :environment do
|
||||
Setting['hot_score_period_in_days'] = 31
|
||||
Setting["hot_score_period_in_days"] = 31
|
||||
end
|
||||
|
||||
desc "Remove deprecated settings"
|
||||
task remove_deprecated_settings: :environment do
|
||||
deprecated_keys = [
|
||||
"place_name",
|
||||
"banner-style.banner-style-one",
|
||||
"banner-style.banner-style-two",
|
||||
"banner-style.banner-style-three",
|
||||
"banner-img.banner-img-one",
|
||||
"banner-img.banner-img-two",
|
||||
"banner-img.banner-img-three",
|
||||
"verification_offices_url"
|
||||
]
|
||||
|
||||
deprecated_keys.each do |key|
|
||||
Setting.where(key: key).first&.destroy
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@ feature "Admin banners magement" do
|
||||
font_color: "#FFFFFF")
|
||||
|
||||
@banner3 = create(:banner, title: "Banner number three",
|
||||
description: "This is the text of banner number three and has style banner-three",
|
||||
description: "This is the text of banner number three",
|
||||
target_url: "http://www.url.com",
|
||||
post_started_at: (Time.current - 1.day),
|
||||
post_ended_at: (Time.current + 10.days),
|
||||
@@ -38,7 +38,7 @@ feature "Admin banners magement" do
|
||||
font_color: "#FFFFFF")
|
||||
|
||||
@banner4 = create(:banner, title: "Banner number four",
|
||||
description: "This is the text of banner number four and has style banner-one",
|
||||
description: "This is the text of banner number four",
|
||||
target_url: "http://www.url.com",
|
||||
post_started_at: (DateTime.current - 10.days),
|
||||
post_ended_at: (DateTime.current + 10.days),
|
||||
@@ -46,7 +46,7 @@ feature "Admin banners magement" do
|
||||
font_color: "#FFFFFF")
|
||||
|
||||
@banner5 = create(:banner, title: "Banner number five",
|
||||
description: "This is the text of banner number five and has style banner-two",
|
||||
description: "This is the text of banner number five",
|
||||
target_url: "http://www.url.com",
|
||||
post_started_at: (DateTime.current - 10.days),
|
||||
post_ended_at: (DateTime.current + 10.days),
|
||||
@@ -140,29 +140,22 @@ feature "Admin banners magement" do
|
||||
scenario "Update banner color when changing from color picker or text_field", :js do
|
||||
visit new_admin_banner_path
|
||||
|
||||
fill_in "banner_background_color", with: "#850000"
|
||||
fill_in "banner_font_color", with: "#ffb2b2"
|
||||
fill_in "background_color_input", with: "#850000"
|
||||
fill_in "font_color_input", with: "#ffb2b2"
|
||||
fill_in "Title", with: "Fun with flags"
|
||||
|
||||
# This last step simulates the blur event on the page. The color pickers and the text_fields
|
||||
# has onChange events that update each one when the other changes, but this is only fired when
|
||||
# the text_field loses the focus (color picker update when text_field changes). The first one
|
||||
# works because when the test fills in the second one, the first loses the focus
|
||||
# (so the onChange is fired). The second one never loses the focus, so the onChange is not been fired.
|
||||
# The `fill_in` action clicks out of the text_field and makes the field to lose the focus.
|
||||
|
||||
expect(find("#banner_background_color_picker").value).to eq("#850000")
|
||||
expect(find("#banner_font_color_picker").value).to eq("#ffb2b2")
|
||||
expect(find("#background_color_input").value).to eq("#850000")
|
||||
expect(find("#font_color_input").value).to eq("#ffb2b2")
|
||||
end
|
||||
|
||||
scenario "Edit banner with live refresh", :js do
|
||||
banner1 = create(:banner, title: "Hello",
|
||||
description: "Wrong text",
|
||||
target_url: "http://www.url.com",
|
||||
post_started_at: (Time.current + 4.days),
|
||||
post_ended_at: (Time.current + 10.days),
|
||||
background_color: "#FF0000",
|
||||
font_color: "#FFFFFF")
|
||||
create(:banner, title: "Hello",
|
||||
description: "Wrong text",
|
||||
target_url: "http://www.url.com",
|
||||
post_started_at: (Time.current + 4.days),
|
||||
post_ended_at: (Time.current + 10.days),
|
||||
background_color: "#FF0000",
|
||||
font_color: "#FFFFFF")
|
||||
|
||||
visit admin_root_path
|
||||
|
||||
|
||||
@@ -12,13 +12,10 @@ feature 'Admin polls' do
|
||||
"edit_admin_poll_path",
|
||||
%w[name summary description]
|
||||
|
||||
scenario 'Index empty', :js do
|
||||
scenario "Index empty", :js do
|
||||
visit admin_root_path
|
||||
|
||||
click_link "Polls"
|
||||
within('#polls_menu') do
|
||||
click_link "Polls"
|
||||
end
|
||||
|
||||
expect(page).to have_content "There are no polls"
|
||||
end
|
||||
@@ -31,9 +28,6 @@ feature 'Admin polls' do
|
||||
visit admin_root_path
|
||||
|
||||
click_link "Polls"
|
||||
within("#polls_menu") do
|
||||
click_link "Polls"
|
||||
end
|
||||
|
||||
expect(page).to have_content "List of polls"
|
||||
expect(page).to have_css ".poll", count: 3
|
||||
|
||||
@@ -14,19 +14,41 @@ feature "Admin poll questions" do
|
||||
scenario "Index" do
|
||||
poll1 = create(:poll)
|
||||
poll2 = create(:poll)
|
||||
poll3 = create(:poll)
|
||||
proposal = create(:proposal)
|
||||
question1 = create(:poll_question, poll: poll1)
|
||||
question2 = create(:poll_question, poll: poll2)
|
||||
question3 = create(:poll_question, poll: poll3, proposal: proposal)
|
||||
|
||||
visit admin_questions_path
|
||||
visit admin_poll_path(poll1)
|
||||
expect(page).to have_content(poll1.name)
|
||||
|
||||
within("#poll_question_#{question1.id}") do
|
||||
expect(page).to have_content(question1.title)
|
||||
expect(page).to have_content(poll1.name)
|
||||
expect(page).to have_content("Edit answers")
|
||||
expect(page).to have_content("Edit")
|
||||
expect(page).to have_content("Delete")
|
||||
end
|
||||
|
||||
visit admin_poll_path(poll2)
|
||||
expect(page).to have_content(poll2.name)
|
||||
|
||||
within("#poll_question_#{question2.id}") do
|
||||
expect(page).to have_content(question2.title)
|
||||
expect(page).to have_content(poll2.name)
|
||||
expect(page).to have_content("Edit answers")
|
||||
expect(page).to have_content("Edit")
|
||||
expect(page).to have_content("Delete")
|
||||
end
|
||||
|
||||
visit admin_poll_path(poll3)
|
||||
expect(page).to have_content(poll3.name)
|
||||
|
||||
within("#poll_question_#{question3.id}") do
|
||||
expect(page).to have_content(question3.title)
|
||||
expect(page).to have_link("(See proposal)", href: proposal_path(question3.proposal))
|
||||
expect(page).to have_content("Edit answers")
|
||||
expect(page).to have_content("Edit")
|
||||
expect(page).to have_content("Delete")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,7 +57,8 @@ feature "Admin poll questions" do
|
||||
poll = create(:poll, geozone_restricted: true, geozone_ids: [geozone.id])
|
||||
question = create(:poll_question, poll: poll)
|
||||
|
||||
visit admin_question_path(question)
|
||||
visit admin_poll_path(poll)
|
||||
click_link "#{question.title}"
|
||||
|
||||
expect(page).to have_content(question.title)
|
||||
expect(page).to have_content(question.author.name)
|
||||
@@ -44,17 +67,13 @@ feature "Admin poll questions" do
|
||||
scenario "Create" do
|
||||
poll = create(:poll, name: "Movies")
|
||||
title = "Star Wars: Episode IV - A New Hope"
|
||||
description = %{
|
||||
During the battle, Rebel spies managed to steal secret plans to the Empire's ultimate weapon, the DEATH STAR, an armored space station
|
||||
with enough power to destroy an entire planet.
|
||||
Pursued by the Empire's sinister agents, Princess Leia races home aboard her starship, custodian of the stolen plans that can save her
|
||||
people and restore freedom to the galaxy....
|
||||
}
|
||||
|
||||
visit admin_questions_path
|
||||
visit admin_poll_path(poll)
|
||||
click_link "Create question"
|
||||
|
||||
select "Movies", from: "poll_question_poll_id"
|
||||
expect(page).to have_content("Create question to poll Movies")
|
||||
expect(page).to have_selector("input[id='poll_question_poll_id'][value='#{poll.id}']",
|
||||
visible: false)
|
||||
fill_in "Question", with: title
|
||||
|
||||
click_button "Save"
|
||||
@@ -62,13 +81,33 @@ feature "Admin poll questions" do
|
||||
expect(page).to have_content(title)
|
||||
end
|
||||
|
||||
scenario "Create from successful proposal" do
|
||||
poll = create(:poll, name: "Proposals")
|
||||
proposal = create(:proposal, :successful)
|
||||
scenario "Create from proposal" do
|
||||
create(:poll, name: "Proposals")
|
||||
proposal = create(:proposal)
|
||||
|
||||
visit admin_proposal_path(proposal)
|
||||
|
||||
click_link "Create question"
|
||||
expect(page).not_to have_content("This proposal has reached the required supports")
|
||||
click_link "Add this proposal to a poll to be voted"
|
||||
|
||||
expect(page).to have_current_path(new_admin_question_path, ignore_query: true)
|
||||
expect(page).to have_field("Question", with: proposal.title)
|
||||
|
||||
select "Proposals", from: "poll_question_poll_id"
|
||||
|
||||
click_button "Save"
|
||||
|
||||
expect(page).to have_content(proposal.title)
|
||||
end
|
||||
|
||||
scenario "Create from successful proposal" do
|
||||
create(:poll, name: "Proposals")
|
||||
proposal = create(:proposal, :successful)
|
||||
|
||||
visit admin_proposal_path(proposal)
|
||||
|
||||
expect(page).to have_content("This proposal has reached the required supports")
|
||||
click_link "Add this proposal to a poll to be voted"
|
||||
|
||||
expect(page).to have_current_path(new_admin_question_path, ignore_query: true)
|
||||
expect(page).to have_field("Question", with: proposal.title)
|
||||
@@ -78,14 +117,14 @@ feature "Admin poll questions" do
|
||||
click_button "Save"
|
||||
|
||||
expect(page).to have_content(proposal.title)
|
||||
expect(page).to have_link(proposal.title, href: proposal_path(proposal))
|
||||
expect(page).to have_link(proposal.author.name, href: user_path(proposal.author))
|
||||
end
|
||||
|
||||
scenario "Update" do
|
||||
question1 = create(:poll_question)
|
||||
poll = create(:poll)
|
||||
question1 = create(:poll_question, poll: poll)
|
||||
|
||||
visit admin_poll_path(poll)
|
||||
|
||||
visit admin_questions_path
|
||||
within("#poll_question_#{question1.id}") do
|
||||
click_link "Edit"
|
||||
end
|
||||
@@ -98,18 +137,15 @@ feature "Admin poll questions" do
|
||||
|
||||
expect(page).to have_content "Changes saved"
|
||||
expect(page).to have_content new_title
|
||||
|
||||
visit admin_questions_path
|
||||
|
||||
expect(page).to have_content(new_title)
|
||||
expect(page).not_to have_content(old_title)
|
||||
end
|
||||
|
||||
scenario "Destroy" do
|
||||
question1 = create(:poll_question)
|
||||
question2 = create(:poll_question)
|
||||
poll = create(:poll)
|
||||
question1 = create(:poll_question, poll: poll)
|
||||
question2 = create(:poll_question, poll: poll)
|
||||
|
||||
visit admin_questions_path
|
||||
visit admin_poll_path(poll)
|
||||
|
||||
within("#poll_question_#{question1.id}") do
|
||||
click_link "Delete"
|
||||
@@ -123,29 +159,20 @@ feature "Admin poll questions" do
|
||||
|
||||
context "Poll select box" do
|
||||
|
||||
let(:poll) { create(:poll, name_en: "Name in English",
|
||||
name_es: "Nombre en Español",
|
||||
summary_en: "Summary in English",
|
||||
summary_es: "Resumen en Español",
|
||||
description_en: "Description in English",
|
||||
description_es: "Descripción en Español") }
|
||||
|
||||
let(:question) { create(:poll_question, poll: poll,
|
||||
title_en: "Question in English",
|
||||
title_es: "Pregunta en Español") }
|
||||
|
||||
before do
|
||||
@edit_question_url = edit_admin_question_path(question)
|
||||
end
|
||||
|
||||
scenario "translates the poll name in options", :js do
|
||||
visit @edit_question_url
|
||||
|
||||
expect(page).to have_select("poll_question_poll_id", options: [poll.name_en])
|
||||
poll = create(:poll, name_en: "Name in English", name_es: "Nombre en Español")
|
||||
proposal = create(:proposal)
|
||||
|
||||
visit admin_proposal_path(proposal)
|
||||
click_link "Add this proposal to a poll to be voted"
|
||||
|
||||
expect(page).to have_select("poll_question_poll_id", options: ["Select Poll", poll.name_en])
|
||||
|
||||
select("Español", from: "locale-switcher")
|
||||
|
||||
expect(page).to have_select("poll_question_poll_id", options: [poll.name_es])
|
||||
expect(page).to have_select("poll_question_poll_id",
|
||||
options: ["Seleccionar votación", poll.name_es])
|
||||
end
|
||||
|
||||
scenario "uses fallback if name is not translated to current locale", :js do
|
||||
@@ -153,13 +180,18 @@ feature "Admin poll questions" do
|
||||
skip("Spec only useful when French falls back to Spanish")
|
||||
end
|
||||
|
||||
visit @edit_question_url
|
||||
poll = create(:poll, name_en: "Name in English", name_es: "Nombre en Español")
|
||||
proposal = create(:proposal)
|
||||
|
||||
expect(page).to have_select("poll_question_poll_id", options: [poll.name_en])
|
||||
visit admin_proposal_path(proposal)
|
||||
click_link "Add this proposal to a poll to be voted"
|
||||
|
||||
expect(page).to have_select("poll_question_poll_id", options: ["Select Poll", poll.name_en])
|
||||
|
||||
select("Français", from: "locale-switcher")
|
||||
|
||||
expect(page).to have_select("poll_question_poll_id", options: [poll.name_es])
|
||||
expect(page).to have_select("poll_question_poll_id",
|
||||
options: ["Sélectionner un vote", poll.name_es])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ feature "Admin proposals" do
|
||||
successful_proposals.each do |proposal|
|
||||
visit admin_proposal_path(proposal)
|
||||
expect(page).to have_content "This proposal has reached the required supports"
|
||||
expect(page).to have_link "Create question"
|
||||
expect(page).to have_link "Add this proposal to a poll to be voted"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,8 +18,8 @@ feature "Banner" do
|
||||
|
||||
within(".banner") do
|
||||
expect(page).to have_content("Banner description")
|
||||
expect(find("h2")[:style]).to eq("color:#{banner.font_color}")
|
||||
expect(find("h3")[:style]).to eq("color:#{banner.font_color}")
|
||||
expect(find("h2")[:style]).to eq("color:#{banner.font_color};")
|
||||
expect(find("h3")[:style]).to eq("color:#{banner.font_color};")
|
||||
end
|
||||
|
||||
visit debates_path
|
||||
|
||||
@@ -521,6 +521,7 @@ feature "Proposals" do
|
||||
end
|
||||
|
||||
scenario "Index do not list retired proposals by default" do
|
||||
Setting["feature.featured_proposals"] = true
|
||||
create_featured_proposals
|
||||
not_retired = create(:proposal)
|
||||
retired = create(:proposal, retired_at: Time.current)
|
||||
@@ -881,8 +882,10 @@ feature "Proposals" do
|
||||
end
|
||||
|
||||
scenario "do not show in featured proposals section" do
|
||||
Setting["feature.featured_proposals"] = true
|
||||
featured_proposal = create(:proposal, :with_confidence_score, cached_votes_up: 100)
|
||||
archived_proposal = create(:proposal, :archived, :with_confidence_score, cached_votes_up: 10000)
|
||||
archived_proposal = create(:proposal, :archived, :with_confidence_score,
|
||||
cached_votes_up: 10000)
|
||||
|
||||
visit proposals_path
|
||||
|
||||
|
||||
@@ -20,17 +20,6 @@ feature "Verify Letter" do
|
||||
expect(user.letter_verification_code).to be
|
||||
end
|
||||
|
||||
scenario "Go to office instead of send letter" do
|
||||
Setting["verification_offices_url"] = "http://offices.consul"
|
||||
user = create(:user, residence_verified_at: Time.current,
|
||||
confirmed_phone: "611111111")
|
||||
|
||||
login_as(user)
|
||||
visit new_letter_path
|
||||
|
||||
expect(page).to have_link "Citizen Support Offices", href: "http://offices.consul"
|
||||
end
|
||||
|
||||
scenario "Deny access unless verified residence" do
|
||||
user = create(:user)
|
||||
|
||||
|
||||
@@ -1,6 +1,39 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe "Settings Rake" do
|
||||
describe Setting do
|
||||
|
||||
context "Remove deprecated settings" do
|
||||
|
||||
let :run_remove_deprecated_settings_task do
|
||||
Rake::Task["settings:remove_deprecated_settings"].reenable
|
||||
Rake.application.invoke_task "settings:remove_deprecated_settings"
|
||||
end
|
||||
|
||||
before do
|
||||
Setting.create(key: "place_name", value: "City")
|
||||
Setting.create(key: "banner-style.banner-style-one", value: "Style one")
|
||||
Setting.create(key: "banner-style.banner-style-two", value: "Style two")
|
||||
Setting.create(key: "banner-style.banner-style-three", value: "Style three")
|
||||
Setting.create(key: "banner-img.banner-img-one", value: "Image 1")
|
||||
Setting.create(key: "banner-img.banner-img-two", value: "Image 2")
|
||||
Setting.create(key: "banner-img.banner-img-three", value: "Image 3")
|
||||
Setting.create(key: "verification_offices_url", value: "http://offices.url")
|
||||
Setting.create(key: "not_deprecated", value: "Setting not deprecated")
|
||||
run_remove_deprecated_settings_task
|
||||
end
|
||||
|
||||
it "Rake only removes deprecated settings" do
|
||||
expect(Setting.where(key: "place_name").count).to eq(0)
|
||||
expect(Setting.where(key: "banner-style.banner-style-one").count).to eq(0)
|
||||
expect(Setting.where(key: "banner-style.banner-style-two").count).to eq(0)
|
||||
expect(Setting.where(key: "banner-style.banner-style-three").count).to eq(0)
|
||||
expect(Setting.where(key: "banner-img.banner-img-one").count).to eq(0)
|
||||
expect(Setting.where(key: "banner-img.banner-img-two").count).to eq(0)
|
||||
expect(Setting.where(key: "banner-img.banner-img-three").count).to eq(0)
|
||||
expect(Setting.where(key: "verification_offices_url").count).to eq(0)
|
||||
expect(Setting.where(key: "not_deprecated").count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#per_page_code_migration" do
|
||||
|
||||
|
||||
Reference in New Issue
Block a user