Merge branch 'master' into dashboard-master

This commit is contained in:
decabeza
2018-10-19 01:48:37 +02:00
1586 changed files with 126348 additions and 9884 deletions

1
.gitignore vendored
View File

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

View File

@@ -25,6 +25,12 @@ Layout/IndentationConsistency:
Layout/EndOfLine: Layout/EndOfLine:
EnforcedStyle: lf EnforcedStyle: lf
Layout/TrailingBlankLines:
Enabled: true
Layout/TrailingWhitespace:
Enabled: true
Bundler/DuplicatedGem: Bundler/DuplicatedGem:
Enabled: true Enabled: true
@@ -420,3 +426,6 @@ Security/MarshalLoad:
Security/YAMLLoad: Security/YAMLLoad:
Enabled: true Enabled: true
Style/PercentLiteralDelimiters:
Enabled: true

View File

@@ -34,21 +34,12 @@ If you want to contribute code to solve an issue:
* Fork the project. * Fork the project.
* Create a topic branch based on master. * Create a topic branch based on master.
* Commit there your code to solve the issue. * Commit there your code to solve the issue.
* Make sure all test are passing (and add specs to test any new feature if needed). * Make sure all test are passing (and add specs to test any new feature you've added).
* Follow these [best practices](https://github.com/styleguide/ruby) * Follow these [best practices](https://github.com/styleguide/ruby)
* Open a *pull request* to the main repository describing what issue you are addressing. * Open a *pull request* to the main repository describing what issue you are addressing.
**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) **Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github)
## Cleaning up
In the rush of time sometimes things get messy, you can help us cleaning things up:
* implement [pending specs](https://travis-ci.org/consul/consul)
* increase [code coverage](https://coveralls.io/github/consul/consul?branch=master)
* improve [code quality](https://codeclimate.com/github/consul/consul)
* make [code consistent](https://github.com/bbatsov/rubocop)
## Other ways of contributing without coding ## Other ways of contributing without coding
* If you think there's a feature missing, or find a bug, create an issue (make sure it has not already been reported). * If you think there's a feature missing, or find a bug, create an issue (make sure it has not already been reported).

View File

@@ -1,7 +1,11 @@
# Use Ruby 2.3.6 as base image
FROM ruby:2.3.6 FROM ruby:2.3.6
ENV DEBIAN_FRONTEND noninteractive
# Install essential Linux packages # Install essential Linux packages
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev postgresql-client nodejs imagemagick sudo RUN apt-get update -qq
RUN apt-get install -y build-essential libpq-dev postgresql-client nodejs imagemagick sudo libxss1 libappindicator1 libindicator7 unzip memcached
# Files created inside the container repect the ownership # Files created inside the container repect the ownership
RUN adduser --shell /bin/bash --disabled-password --gecos "" consul \ RUN adduser --shell /bin/bash --disabled-password --gecos "" consul \
@@ -34,9 +38,18 @@ COPY Gemfile_custom Gemfile_custom
# Prevent bundler warnings; ensure that the bundler version executed is >= that which created Gemfile.lock # Prevent bundler warnings; ensure that the bundler version executed is >= that which created Gemfile.lock
RUN gem install bundler RUN gem install bundler
# Finish establishing our Ruby enviornment # Finish establishing our Ruby environment
RUN bundle install --full-index RUN bundle install --full-index
# Install Chromium and ChromeDriver for E2E integration tests
RUN apt-get update -qq && apt-get install -y chromium
RUN wget -N http://chromedriver.storage.googleapis.com/2.38/chromedriver_linux64.zip
RUN unzip chromedriver_linux64.zip
RUN chmod +x chromedriver
RUN mv -f chromedriver /usr/local/share/chromedriver
RUN ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver
RUN ln -s /usr/local/share/chromedriver /usr/bin/chromedriver
# Copy the Rails application into place # Copy the Rails application into place
COPY . . COPY . .

31
Gemfile
View File

@@ -5,17 +5,17 @@ gem 'rails', '4.2.10'
gem 'acts-as-taggable-on', '~> 5.0.0' gem 'acts-as-taggable-on', '~> 5.0.0'
gem 'acts_as_votable', '~> 0.11.1' gem 'acts_as_votable', '~> 0.11.1'
gem 'ahoy_matey', '~> 1.6.0' gem 'ahoy_matey', '~> 1.6.0'
gem 'ancestry', '~> 3.0.1' gem 'ancestry', '~> 3.0.2'
gem 'autoprefixer-rails', '~> 8.2.0' gem 'autoprefixer-rails', '~> 9.1.4'
gem 'best_in_place', '~> 3.0.1' gem 'best_in_place', '~> 3.0.1'
gem 'browser', '~> 2.5.2' gem 'browser', '~> 2.5.3'
gem 'cancancan', '~> 2.1.2' gem 'cancancan', '~> 2.1.2'
gem 'ckeditor', '~> 4.2.3' gem 'ckeditor', '~> 4.2.3'
gem 'cocoon', '~> 1.2.9' gem 'cocoon', '~> 1.2.9'
gem 'coffee-rails', '~> 4.2.1' gem 'coffee-rails', '~> 4.2.2'
gem 'daemons', '~> 1.2.4' gem 'daemons', '~> 1.2.4'
gem 'dalli', '~> 2.7.6' gem 'dalli', '~> 2.7.6'
gem 'delayed_job_active_record', '~> 4.1.0' gem 'delayed_job_active_record', '~> 4.1.3'
gem 'devise', '~> 3.5.7' gem 'devise', '~> 3.5.7'
gem 'devise-async', '~> 0.10.2' gem 'devise-async', '~> 0.10.2'
gem 'devise_security_extension', '~> 0.10.0' gem 'devise_security_extension', '~> 0.10.0'
@@ -28,7 +28,7 @@ gem 'groupdate', '~> 3.2.0'
gem 'initialjs-rails', '~> 0.2.0.5' gem 'initialjs-rails', '~> 0.2.0.5'
gem 'invisible_captcha', '~> 0.10.0' gem 'invisible_captcha', '~> 0.10.0'
gem 'jquery-fileupload-rails' gem 'jquery-fileupload-rails'
gem 'jquery-rails', '~> 4.3.1' gem 'jquery-rails', '~> 4.3.3'
gem 'jquery-ui-rails', '~> 6.0.1' gem 'jquery-ui-rails', '~> 6.0.1'
gem 'kaminari', '~> 1.1.1' gem 'kaminari', '~> 1.1.1'
gem 'newrelic_rpm', '~> 4.1.0.333' gem 'newrelic_rpm', '~> 4.1.0.333'
@@ -37,7 +37,7 @@ gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-google-oauth2', '~> 0.4.0' gem 'omniauth-google-oauth2', '~> 0.4.0'
gem 'omniauth-twitter', '~> 1.4.0' gem 'omniauth-twitter', '~> 1.4.0'
gem 'paperclip', '~> 5.2.1' gem 'paperclip', '~> 5.2.1'
gem 'paranoia', '~> 2.4.0' gem 'paranoia', '~> 2.4.1'
gem 'pg', '~> 0.21.0' gem 'pg', '~> 0.21.0'
gem 'pg_search', '~> 2.0.1' gem 'pg_search', '~> 2.0.1'
gem 'redcarpet', '~> 3.4.0' gem 'redcarpet', '~> 3.4.0'
@@ -45,14 +45,14 @@ gem 'responders', '~> 2.4.0'
gem 'rinku', '~> 2.0.2', require: 'rails_rinku' gem 'rinku', '~> 2.0.2', require: 'rails_rinku'
gem 'rollbar', '~> 2.15.5' gem 'rollbar', '~> 2.15.5'
gem 'sass-rails', '~> 5.0', '>= 5.0.4' gem 'sass-rails', '~> 5.0', '>= 5.0.4'
gem 'savon', '~> 2.11.1' gem 'savon', '~> 2.12.0'
gem 'sitemap_generator', '~> 6.0.1' gem 'sitemap_generator', '~> 6.0.1'
gem 'social-share-button', '~> 1.1' gem 'social-share-button', '~> 1.1'
gem 'sprockets', '~> 3.7.2' gem 'sprockets', '~> 3.7.2'
gem 'turbolinks', '~> 2.5.3' gem 'turbolinks', '~> 2.5.3'
gem 'turnout', '~> 2.4.0' gem 'turnout', '~> 2.4.0'
gem 'uglifier', '~> 4.1.2' gem 'uglifier', '~> 4.1.2'
gem 'unicorn', '~> 5.4.0' gem 'unicorn', '~> 5.4.1'
gem 'whenever', '~> 0.10.0', require: false gem 'whenever', '~> 0.10.0', require: false
gem 'globalize', '~> 5.0.0' gem 'globalize', '~> 5.0.0'
gem 'globalize-accessors', '~> 0.2.1' gem 'globalize-accessors', '~> 0.2.1'
@@ -68,7 +68,8 @@ group :development, :test do
gem 'bullet', '~> 5.7.0' gem 'bullet', '~> 5.7.0'
gem 'byebug', '~> 10.0.0' gem 'byebug', '~> 10.0.0'
gem 'factory_bot_rails', '~> 4.8.2' gem 'factory_bot_rails', '~> 4.8.2'
gem 'i18n-tasks', '~> 0.9.20' gem 'faker', '~> 1.8.7'
gem 'i18n-tasks', '~> 0.9.25'
gem 'knapsack_pro', '~> 0.53.0' gem 'knapsack_pro', '~> 0.53.0'
gem 'launchy', '~> 2.4.3' gem 'launchy', '~> 2.4.3'
gem 'letter_opener_web', '~> 1.3.2' gem 'letter_opener_web', '~> 1.3.2'
@@ -79,7 +80,7 @@ end
group :test do group :test do
gem 'capybara', '~> 2.17.0' gem 'capybara', '~> 2.17.0'
gem 'coveralls', '~> 0.8.21', require: false gem 'coveralls', '~> 0.8.22', require: false
gem 'database_cleaner', '~> 1.6.1' gem 'database_cleaner', '~> 1.6.1'
gem 'email_spec', '~> 2.1.0' gem 'email_spec', '~> 2.1.0'
gem 'rspec-rails', '~> 3.6' gem 'rspec-rails', '~> 3.6'
@@ -89,13 +90,13 @@ end
group :development do group :development do
gem 'capistrano', '~> 3.10.1', require: false gem 'capistrano', '~> 3.10.1', require: false
gem 'capistrano-bundler', '~> 1.2', require: false gem 'capistrano-bundler', '~> 1.2', require: false
gem 'capistrano-rails', '~> 1.3.1', require: false gem 'capistrano-rails', '~> 1.4.0', require: false
gem 'capistrano3-delayed-job', '~> 1.7.3' gem 'capistrano3-delayed-job', '~> 1.7.3'
gem 'mdl', '~> 0.4.0', require: false gem 'mdl', '~> 0.5.0', require: false
gem 'rubocop', '~> 0.54.0', require: false gem 'rubocop', '~> 0.54.0', require: false
gem 'rubocop-rspec', '~> 1.24.0', require: false gem 'rubocop-rspec', '~> 1.26.0', require: false
gem 'rvm1-capistrano3', '~> 1.4.0', require: false gem 'rvm1-capistrano3', '~> 1.4.0', require: false
gem 'scss_lint', '~> 0.54.0', require: false gem 'scss_lint', '~> 0.55.0', require: false
gem 'web-console', '~> 3.3.0' gem 'web-console', '~> 3.3.0'
end end

View File

@@ -57,11 +57,11 @@ GEM
akami (1.3.1) akami (1.3.1)
gyoku (>= 0.4.0) gyoku (>= 0.4.0)
nokogiri nokogiri
ancestry (3.0.1) ancestry (3.0.2)
activerecord (>= 3.2.0) activerecord (>= 3.2.0)
arel (6.0.4) arel (6.0.4)
ast (2.4.0) ast (2.4.0)
autoprefixer-rails (8.2.0) autoprefixer-rails (9.1.4)
execjs execjs
babel-source (5.8.35) babel-source (5.8.35)
babel-transpiler (0.7.0) babel-transpiler (0.7.0)
@@ -71,7 +71,7 @@ GEM
best_in_place (3.0.3) best_in_place (3.0.3)
actionpack (>= 3.2) actionpack (>= 3.2)
railties (>= 3.2) railties (>= 3.2)
browser (2.5.2) browser (2.5.3)
builder (3.2.3) builder (3.2.3)
bullet (5.7.1) bullet (5.7.1)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
@@ -86,7 +86,7 @@ GEM
capistrano-bundler (1.3.0) capistrano-bundler (1.3.0)
capistrano (~> 3.1) capistrano (~> 3.1)
sshkit (~> 1.2) sshkit (~> 1.2)
capistrano-rails (1.3.1) capistrano-rails (1.4.0)
capistrano (~> 3.1) capistrano (~> 3.1)
capistrano-bundler (~> 1.1) capistrano-bundler (~> 1.1)
capistrano3-delayed-job (1.7.5) capistrano3-delayed-job (1.7.5)
@@ -117,21 +117,21 @@ GEM
execjs execjs
coffee-script-source (1.12.2) coffee-script-source (1.12.2)
concurrent-ruby (1.0.5) concurrent-ruby (1.0.5)
coveralls (0.8.21) coveralls (0.8.22)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov (~> 0.14.1) simplecov (~> 0.16.1)
term-ansicolor (~> 1.3) term-ansicolor (~> 1.3)
thor (~> 0.19.4) thor (~> 0.19.4)
tins (~> 1.6) tins (~> 1.6)
crass (1.0.3) crass (1.0.4)
daemons (1.2.6) daemons (1.2.6)
dalli (2.7.6) dalli (2.7.6)
database_cleaner (1.6.2) database_cleaner (1.6.2)
debug_inspector (0.0.3) debug_inspector (0.0.3)
delayed_job (4.1.3) delayed_job (4.1.5)
activesupport (>= 3.0, < 5.2) activesupport (>= 3.0, < 5.3)
delayed_job_active_record (4.1.2) delayed_job_active_record (4.1.3)
activerecord (>= 3.0, < 5.2) activerecord (>= 3.0, < 5.3)
delayed_job (>= 3.0, < 5) delayed_job (>= 3.0, < 5)
devise (3.5.10) devise (3.5.10)
bcrypt (~> 3.0) bcrypt (~> 3.0)
@@ -146,16 +146,13 @@ GEM
devise (>= 3.0.0, < 4.0) devise (>= 3.0.0, < 4.0)
railties (>= 3.2.6, < 5.0) railties (>= 3.2.6, < 5.0)
diff-lcs (1.3) diff-lcs (1.3)
docile (1.1.5) docile (1.3.1)
easy_translate (0.5.1)
thread
thread_safe
email_spec (2.1.1) email_spec (2.1.1)
htmlentities (~> 4.3.3) htmlentities (~> 4.3.3)
launchy (~> 2.1) launchy (~> 2.1)
mail (~> 2.6) mail (~> 2.6)
errbase (0.0.3) errbase (0.0.3)
erubi (1.7.0) erubi (1.7.1)
erubis (2.7.0) erubis (2.7.0)
execjs (2.7.0) execjs (2.7.0)
factory_bot (4.8.2) factory_bot (4.8.2)
@@ -167,7 +164,7 @@ GEM
i18n (>= 0.7) i18n (>= 0.7)
faraday (0.12.1) faraday (0.12.1)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
ffi (1.9.23) ffi (1.9.25)
foundation-rails (6.4.3.0) foundation-rails (6.4.3.0)
railties (>= 3.1.0) railties (>= 3.1.0)
sass (>= 3.3.0, < 3.5) sass (>= 3.3.0, < 3.5)
@@ -194,22 +191,21 @@ GEM
gyoku (1.3.1) gyoku (1.3.1)
builder (>= 2.1.2) builder (>= 2.1.2)
hashie (3.5.7) hashie (3.5.7)
highline (1.7.10) highline (2.0.0)
htmlentities (4.3.4) htmlentities (4.3.4)
httpi (2.4.2) httpi (2.4.4)
rack rack
socksify socksify
i18n (0.9.5) i18n (0.9.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n-tasks (0.9.20) i18n-tasks (0.9.25)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
easy_translate (>= 0.5.1)
erubi erubi
highline (>= 1.7.3) highline (>= 2.0.0)
i18n i18n
parser (>= 2.2.3.0) parser (>= 2.2.3.0)
rainbow (~> 2.2) rainbow (>= 2.2.2, < 4.0)
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
initialjs-rails (0.2.0.5) initialjs-rails (0.2.0.5)
railties (>= 3.1, < 6.0) railties (>= 3.1, < 6.0)
@@ -219,7 +215,7 @@ GEM
actionpack (>= 3.1) actionpack (>= 3.1)
railties (>= 3.1) railties (>= 3.1)
sass (>= 3.2) sass (>= 3.2)
jquery-rails (4.3.1) jquery-rails (4.3.3)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
@@ -239,10 +235,10 @@ GEM
activerecord activerecord
kaminari-core (= 1.1.1) kaminari-core (= 1.1.1)
kaminari-core (1.1.1) kaminari-core (1.1.1)
kgio (2.11.1) kgio (2.11.2)
knapsack_pro (0.53.0) knapsack_pro (0.53.0)
rake rake
kramdown (1.14.0) kramdown (1.17.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.6.0) letter_opener (1.6.0)
@@ -256,7 +252,7 @@ GEM
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.0) mail (2.7.0)
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
mdl (0.4.0) mdl (0.5.0)
kramdown (~> 1.12, >= 1.12.0) kramdown (~> 1.12, >= 1.12.0)
mixlib-cli (~> 1.7, >= 1.7.0) mixlib-cli (~> 1.7, >= 1.7.0)
mixlib-config (~> 2.2, >= 2.2.1) mixlib-config (~> 2.2, >= 2.2.1)
@@ -268,15 +264,16 @@ GEM
mini_portile2 (2.3.0) mini_portile2 (2.3.0)
minitest (5.11.3) minitest (5.11.3)
mixlib-cli (1.7.0) mixlib-cli (1.7.0)
mixlib-config (2.2.4) mixlib-config (2.2.13)
tomlrb
multi_json (1.12.2) multi_json (1.12.2)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
net-scp (1.2.1) net-scp (1.2.1)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (4.2.0) net-ssh (5.0.2)
newrelic_rpm (4.1.0.333) newrelic_rpm (4.1.0.333)
nokogiri (1.8.2) nokogiri (1.8.4)
mini_portile2 (~> 2.3.0) mini_portile2 (~> 2.3.0)
nori (2.6.0) nori (2.6.0)
oauth (0.5.3) oauth (0.5.3)
@@ -313,16 +310,16 @@ GEM
mime-types mime-types
mimemagic (~> 0.3.0) mimemagic (~> 0.3.0)
parallel (1.12.1) parallel (1.12.1)
paranoia (2.4.0) paranoia (2.4.1)
activerecord (>= 4.0, < 5.2) activerecord (>= 4.0, < 5.3)
parser (2.5.0.5) parser (2.5.1.2)
ast (~> 2.4.0) ast (~> 2.4.0)
pg (0.21.0) pg (0.21.0)
pg_search (2.0.1) pg_search (2.0.1)
activerecord (>= 4.2) activerecord (>= 4.2)
activesupport (>= 4.2) activesupport (>= 4.2)
arel (>= 6) arel (>= 6)
powerpack (0.1.1) powerpack (0.1.2)
public_suffix (3.0.1) public_suffix (3.0.1)
quiet_assets (1.1.0) quiet_assets (1.1.0)
railties (>= 3.1, < 5.0) railties (>= 3.1, < 5.0)
@@ -359,8 +356,7 @@ GEM
activesupport (= 4.2.10) activesupport (= 4.2.10)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.2.2) rainbow (3.0.0)
rake
raindrops (0.19.0) raindrops (0.19.0)
rake (12.3.1) rake (12.3.1)
redcarpet (3.4.0) redcarpet (3.4.0)
@@ -396,10 +392,10 @@ GEM
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-rspec (1.24.0) rubocop-rspec (1.26.0)
rubocop (>= 0.53.0) rubocop (>= 0.53.0)
ruby-progressbar (1.9.0) ruby-progressbar (1.10.0)
rubyzip (1.2.1) rubyzip (1.2.2)
rvm1-capistrano3 (1.4.0) rvm1-capistrano3 (1.4.0)
capistrano (~> 3.0) capistrano (~> 3.0)
sshkit (>= 1.2) sshkit (>= 1.2)
@@ -412,25 +408,25 @@ GEM
sprockets (>= 2.8, < 4.0) sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0) sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3) tilt (>= 1.1, < 3)
savon (2.11.2) savon (2.12.0)
akami (~> 1.2) akami (~> 1.2)
builder (>= 2.1.2) builder (>= 2.1.2)
gyoku (~> 1.2) gyoku (~> 1.2)
httpi (~> 2.3) httpi (~> 2.3)
nokogiri (>= 1.4.0) nokogiri (>= 1.8.1)
nori (~> 2.4) nori (~> 2.4)
wasabi (~> 3.4) wasabi (~> 3.4)
scss_lint (0.54.0) scss_lint (0.55.0)
rake (>= 0.9, < 13) rake (>= 0.9, < 13)
sass (~> 3.4.20) sass (~> 3.4.20)
selenium-webdriver (3.10.0) selenium-webdriver (3.10.0)
childprocess (~> 0.5) childprocess (~> 0.5)
rubyzip (~> 1.2) rubyzip (~> 1.2)
simplecov (0.14.1) simplecov (0.16.1)
docile (~> 1.1.0) docile (~> 1.1)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov-html (~> 0.10.0) simplecov-html (~> 0.10.0)
simplecov-html (0.10.1) simplecov-html (0.10.2)
sitemap_generator (6.0.1) sitemap_generator (6.0.1)
builder (~> 3.0) builder (~> 3.0)
social-share-button (1.1.0) social-share-button (1.1.0)
@@ -451,7 +447,7 @@ GEM
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sshkit (1.15.1) sshkit (1.17.0)
net-scp (>= 1.1.2) net-scp (>= 1.1.2)
net-ssh (>= 2.8.0) net-ssh (>= 2.8.0)
term-ansicolor (1.6.0) term-ansicolor (1.6.0)
@@ -459,10 +455,10 @@ GEM
terminal-table (1.8.0) terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1, >= 1.1.1)
thor (0.19.4) thor (0.19.4)
thread (0.2.2)
thread_safe (0.3.6) thread_safe (0.3.6)
tilt (2.0.8) tilt (2.0.8)
tins (1.15.0) tins (1.16.3)
tomlrb (1.2.7)
turbolinks (2.5.3) turbolinks (2.5.3)
coffee-rails coffee-rails
turnout (2.4.0) turnout (2.4.0)
@@ -474,8 +470,8 @@ GEM
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (4.1.3) uglifier (4.1.3)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unicode-display_width (1.3.0) unicode-display_width (1.4.0)
unicorn (5.4.0) unicorn (5.4.1)
kgio (~> 2.6) kgio (~> 2.6)
raindrops (~> 0.7) raindrops (~> 0.7)
uniform_notifier (1.11.0) uniform_notifier (1.11.0)
@@ -504,26 +500,26 @@ DEPENDENCIES
acts-as-taggable-on (~> 5.0.0) acts-as-taggable-on (~> 5.0.0)
acts_as_votable (~> 0.11.1) acts_as_votable (~> 0.11.1)
ahoy_matey (~> 1.6.0) ahoy_matey (~> 1.6.0)
ancestry (~> 3.0.1) ancestry (~> 3.0.2)
autoprefixer-rails (~> 8.2.0) autoprefixer-rails (~> 9.1.4)
best_in_place (~> 3.0.1) best_in_place (~> 3.0.1)
browser (~> 2.5.2) browser (~> 2.5.3)
bullet (~> 5.7.0) bullet (~> 5.7.0)
byebug (~> 10.0.0) byebug (~> 10.0.0)
cancancan (~> 2.1.2) cancancan (~> 2.1.2)
capistrano (~> 3.10.1) capistrano (~> 3.10.1)
capistrano-bundler (~> 1.2) capistrano-bundler (~> 1.2)
capistrano-rails (~> 1.3.1) capistrano-rails (~> 1.4.0)
capistrano3-delayed-job (~> 1.7.3) capistrano3-delayed-job (~> 1.7.3)
capybara (~> 2.17.0) capybara (~> 2.17.0)
ckeditor (~> 4.2.3) ckeditor (~> 4.2.3)
cocoon (~> 1.2.9) cocoon (~> 1.2.9)
coffee-rails (~> 4.2.1) coffee-rails (~> 4.2.2)
coveralls (~> 0.8.21) coveralls (~> 0.8.22)
daemons (~> 1.2.4) daemons (~> 1.2.4)
dalli (~> 2.7.6) dalli (~> 2.7.6)
database_cleaner (~> 1.6.1) database_cleaner (~> 1.6.1)
delayed_job_active_record (~> 4.1.0) delayed_job_active_record (~> 4.1.3)
devise (~> 3.5.7) devise (~> 3.5.7)
devise-async (~> 0.10.2) devise-async (~> 0.10.2)
devise_security_extension (~> 0.10.0) devise_security_extension (~> 0.10.0)
@@ -537,24 +533,24 @@ DEPENDENCIES
graphiql-rails (~> 1.4.1) graphiql-rails (~> 1.4.1)
graphql (~> 1.7.8) graphql (~> 1.7.8)
groupdate (~> 3.2.0) groupdate (~> 3.2.0)
i18n-tasks (~> 0.9.20) i18n-tasks (~> 0.9.25)
initialjs-rails (~> 0.2.0.5) initialjs-rails (~> 0.2.0.5)
invisible_captcha (~> 0.10.0) invisible_captcha (~> 0.10.0)
jquery-fileupload-rails jquery-fileupload-rails
jquery-rails (~> 4.3.1) jquery-rails (~> 4.3.3)
jquery-ui-rails (~> 6.0.1) jquery-ui-rails (~> 6.0.1)
kaminari (~> 1.1.1) kaminari (~> 1.1.1)
knapsack_pro (~> 0.53.0) knapsack_pro (~> 0.53.0)
launchy (~> 2.4.3) launchy (~> 2.4.3)
letter_opener_web (~> 1.3.2) letter_opener_web (~> 1.3.2)
mdl (~> 0.4.0) mdl (~> 0.5.0)
newrelic_rpm (~> 4.1.0.333) newrelic_rpm (~> 4.1.0.333)
omniauth (~> 1.8.1) omniauth (~> 1.8.1)
omniauth-facebook (~> 4.0.0) omniauth-facebook (~> 4.0.0)
omniauth-google-oauth2 (~> 0.4.0) omniauth-google-oauth2 (~> 0.4.0)
omniauth-twitter (~> 1.4.0) omniauth-twitter (~> 1.4.0)
paperclip (~> 5.2.1) paperclip (~> 5.2.1)
paranoia (~> 2.4.0) paranoia (~> 2.4.1)
pg (~> 0.21.0) pg (~> 0.21.0)
pg_search (~> 2.0.1) pg_search (~> 2.0.1)
quiet_assets (~> 1.1.0) quiet_assets (~> 1.1.0)
@@ -567,11 +563,11 @@ DEPENDENCIES
rollbar (~> 2.15.5) rollbar (~> 2.15.5)
rspec-rails (~> 3.6) rspec-rails (~> 3.6)
rubocop (~> 0.54.0) rubocop (~> 0.54.0)
rubocop-rspec (~> 1.24.0) rubocop-rspec (~> 1.26.0)
rvm1-capistrano3 (~> 1.4.0) rvm1-capistrano3 (~> 1.4.0)
sass-rails (~> 5.0, >= 5.0.4) sass-rails (~> 5.0, >= 5.0.4)
savon (~> 2.11.1) savon (~> 2.12.0)
scss_lint (~> 0.54.0) scss_lint (~> 0.55.0)
selenium-webdriver (~> 3.10) selenium-webdriver (~> 3.10)
sitemap_generator (~> 6.0.1) sitemap_generator (~> 6.0.1)
social-share-button (~> 1.1) social-share-button (~> 1.1)
@@ -581,7 +577,7 @@ DEPENDENCIES
turbolinks (~> 2.5.3) turbolinks (~> 2.5.3)
turnout (~> 2.4.0) turnout (~> 2.4.0)
uglifier (~> 4.1.2) uglifier (~> 4.1.2)
unicorn (~> 5.4.0) unicorn (~> 5.4.1)
web-console (~> 3.3.0) web-console (~> 3.3.0)
whenever (~> 0.10.0) whenever (~> 0.10.0)
wicked_pdf (~> 1.1.0) wicked_pdf (~> 1.1.0)

View File

View File

@@ -34,6 +34,7 @@
//= require moderator_comment //= require moderator_comment
//= require moderator_debates //= require moderator_debates
//= require moderator_proposals //= require moderator_proposals
//= require moderator_budget_investments
//= require moderator_proposal_notifications //= require moderator_proposal_notifications
//= require prevent_double_submission //= require prevent_double_submission
//= require gettext //= require gettext

View File

@@ -5,35 +5,19 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
CKEDITOR.editorConfig = function( config ) CKEDITOR.editorConfig = function( config )
{ {
// Define changes to default configuration here. For example:
// config.language = 'fr';
// config.uiColor = '#AADC6E';
config.forcePasteAsPlainText = true; config.forcePasteAsPlainText = true;
/* Filebrowser routes */
// The location of an external file browser, that should be launched when "Browse Server" button is pressed.
config.filebrowserBrowseUrl = "/ckeditor/attachment_files"; config.filebrowserBrowseUrl = "/ckeditor/attachment_files";
// The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Flash dialog.
config.filebrowserFlashBrowseUrl = "/ckeditor/attachment_files"; config.filebrowserFlashBrowseUrl = "/ckeditor/attachment_files";
// The location of a script that handles file uploads in the Flash dialog.
config.filebrowserFlashUploadUrl = "/ckeditor/attachment_files"; config.filebrowserFlashUploadUrl = "/ckeditor/attachment_files";
// The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Link tab of Image dialog.
config.filebrowserImageBrowseLinkUrl = "/ckeditor/pictures"; config.filebrowserImageBrowseLinkUrl = "/ckeditor/pictures";
// The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Image dialog.
config.filebrowserImageBrowseUrl = "/ckeditor/pictures"; config.filebrowserImageBrowseUrl = "/ckeditor/pictures";
// The location of a script that handles file uploads in the Image dialog.
config.filebrowserImageUploadUrl = "/ckeditor/pictures"; config.filebrowserImageUploadUrl = "/ckeditor/pictures";
// The location of a script that handles file uploads.
config.filebrowserUploadUrl = "/ckeditor/attachment_files"; config.filebrowserUploadUrl = "/ckeditor/attachment_files";
config.allowedContent = true; config.allowedContent = true;
config.format_tags = "p;h2;h3";
// Rails CSRF token // Rails CSRF token
config.filebrowserParams = function(){ config.filebrowserParams = function(){
@@ -111,7 +95,14 @@ CKEDITOR.editorConfig = function( config )
config.toolbar_mini = [ config.toolbar_mini = [
{ name: 'paragraph', groups: [ 'list' ], items: [ 'NumberedList', 'BulletedList' ] }, { name: 'paragraph', groups: [ 'list' ], items: [ 'NumberedList', 'BulletedList' ] },
{ name: 'links', items: [ 'Link', 'Unlink' ] },
{ name: 'styles', items: [ 'Format' ] },
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ], items: [ 'Bold', 'Italic', 'Underline', 'Strike' ] } { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ], items: [ 'Bold', 'Italic', 'Underline', 'Strike' ] }
]; ];
config.toolbar_admin = config.toolbar_mini.concat([
{ name: 'insert', items: [ 'Image' ] }
]);
config.toolbar = "mini"; config.toolbar = "mini";
}; };

View File

View File

@@ -1,6 +1,7 @@
App.Globalize = App.Globalize =
display_locale: (locale) -> display_locale: (locale) ->
App.Globalize.enable_locale(locale)
$(".js-globalize-locale-link").each -> $(".js-globalize-locale-link").each ->
if $(this).data("locale") == locale if $(this).data("locale") == locale
$(this).show() $(this).show()
@@ -22,12 +23,25 @@ App.Globalize =
element.addClass('is-active'); element.addClass('is-active');
remove_language: (locale) -> remove_language: (locale) ->
$(".js-globalize-attribute[data-locale=" + locale + "]").val('').hide() $(".js-globalize-attribute[data-locale=" + locale + "]").each ->
$(this).val('').hide()
if CKEDITOR.instances[$(this).attr('id')]
CKEDITOR.instances[$(this).attr('id')].setData('')
$(".js-globalize-locale-link[data-locale=" + locale + "]").hide() $(".js-globalize-locale-link[data-locale=" + locale + "]").hide()
next = $(".js-globalize-locale-link:visible").first() next = $(".js-globalize-locale-link:visible").first()
App.Globalize.highlight_locale(next) App.Globalize.highlight_locale(next)
App.Globalize.display_translations(next.data("locale")) App.Globalize.display_translations(next.data("locale"))
$("#delete_translations_" + locale).val(1) App.Globalize.disable_locale(locale)
enable_locale: (locale) ->
$("#enabled_translations_" + locale).val(1)
disable_locale: (locale) ->
$("#enabled_translations_" + locale).val(0)
refresh_visible_translations: ->
locale = $('.js-globalize-locale-link.is-active').data("locale")
App.Globalize.display_translations(locale)
initialize: -> initialize: ->
$('.js-globalize-locale').on 'change', -> $('.js-globalize-locale').on 'change', ->

View File

@@ -12,3 +12,5 @@ App.LegislationAdmin =
else else
$(this).val("") $(this).val("")
$("#nested-question-options").on "cocoon:after-insert", ->
App.Globalize.refresh_visible_translations()

View File

@@ -1,10 +1,15 @@
App.MarkdownEditor = App.MarkdownEditor =
refresh_preview: (element, md) -> refresh_preview: (element, md) ->
textarea_content = element.find('textarea').val() textarea_content = App.MarkdownEditor.find_textarea(element).val()
result = md.render(textarea_content) result = md.render(textarea_content)
element.find('#markdown-preview').html(result) element.find('#markdown-preview').html(result)
# Multi-locale (translatable) form fields work by hiding inputs of locales
# which are not "active".
find_textarea: (editor) ->
editor.find('textarea:visible')
initialize: -> initialize: ->
$('.markdown-editor').each -> $('.markdown-editor').each ->
md = window.markdownit({ md = window.markdownit({
@@ -13,18 +18,18 @@ App.MarkdownEditor =
typographer: true, typographer: true,
}) })
App.MarkdownEditor.refresh_preview($(this), md) editor = $(this)
$(this).on 'change input paste keyup', -> editor.on 'input', ->
App.MarkdownEditor.refresh_preview($(this), md) App.MarkdownEditor.refresh_preview($(this), md)
$('.legislation-draft-versions-edit .warning').show() $('.legislation-draft-versions-edit .warning').show()
return return
$(this).find('textarea').on 'scroll', -> editor.find('textarea').on 'scroll', ->
$('#markdown-preview').scrollTop($(this).scrollTop()) $('#markdown-preview').scrollTop($(this).scrollTop())
$(this).find('.fullscreen-toggle').on 'click', -> editor.find('.fullscreen-toggle').on 'click', ->
$('.markdown-editor').toggleClass('fullscreen') editor.toggleClass('fullscreen')
$('.fullscreen-container').toggleClass('medium-8', 'medium-12') $('.fullscreen-container').toggleClass('medium-8', 'medium-12')
span = $(this).find('span') span = $(this).find('span')
current_html = span.html() current_html = span.html()
@@ -33,7 +38,8 @@ App.MarkdownEditor =
else else
span.html(span.data('open-text')) span.html(span.data('open-text'))
if $('.markdown-editor').hasClass('fullscreen') if editor.hasClass('fullscreen')
$('.markdown-editor textarea').height($(window).height() - 100) App.MarkdownEditor.find_textarea(editor).height($(window).height() - 100)
App.MarkdownEditor.refresh_preview(editor, md)
else else
$('.markdown-editor textarea').height("10em") App.MarkdownEditor.find_textarea(editor).height("10em")

View File

@@ -0,0 +1,8 @@
App.ModeratorBudgetInvestments =
add_class_faded: (id) ->
$("##{id}").addClass("faded")
$("#comments").addClass("faded")
hide_moderator_actions: (id) ->
$("##{id} .js-moderator-investment-actions:first").hide()

View File

@@ -46,6 +46,10 @@ $sidebar-active: #f4fcd0;
.top-links { .top-links {
background: #000; background: #000;
a {
line-height: rem-calc($line-height * 1.5);
}
} }
.admin-top-bar { .admin-top-bar {
@@ -190,6 +194,11 @@ $sidebar-active: #f4fcd0;
&.with-button { &.with-button {
line-height: $line-height * 2; line-height: $line-height * 2;
.button {
background: #fff;
color: $brand;
}
} }
} }
@@ -204,6 +213,19 @@ $sidebar-active: #f4fcd0;
table { table {
thead {
color: #fff;
}
th {
background: $brand;
color: #fff;
label {
color: #fff;
}
}
.break { .break {
word-break: break-word; word-break: break-word;
} }
@@ -220,7 +242,6 @@ $sidebar-active: #f4fcd0;
@include breakpoint(medium) { @include breakpoint(medium) {
margin-left: $line-height / 2; margin-left: $line-height / 2;
margin-right: $line-height / 2;
margin-top: 0; margin-top: 0;
} }
} }
@@ -335,6 +356,14 @@ $sidebar-active: #f4fcd0;
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.enabled {
color: $color-success;
}
.disabled {
color: $text-medium;
}
// 02. Sidebar // 02. Sidebar
// ----------- // -----------

View File

@@ -878,11 +878,6 @@ footer {
h1 { h1 {
margin-top: $line-height; margin-top: $line-height;
img {
height: rem-calc(80);
width: rem-calc(80);
}
a { a {
color: #fff; color: #fff;
display: block; display: block;

View File

@@ -10,14 +10,24 @@
// ---------------------- // ----------------------
.jumbo { .jumbo {
background: $highlight;
margin-bottom: $line-height; margin-bottom: $line-height;
margin-top: rem-calc(-24); margin-top: rem-calc(-24);
padding-bottom: $line-height; padding-bottom: $line-height;
padding-top: $line-height; padding-top: $line-height;
@include breakpoint(medium) {
padding: rem-calc(24) 0;
}
&.light { &.light {
background: #ecf0f1; background: #ecf0f1;
} }
h1,
p {
color: $text;
}
} }
.lead { .lead {

View File

@@ -9,7 +9,6 @@
// 07. Proposals successful // 07. Proposals successful
// 08. Polls // 08. Polls
// 09. Polls results and stats // 09. Polls results and stats
// 10. Guides
// //
// 01. Votes and supports // 01. Votes and supports
@@ -685,7 +684,8 @@
} }
.budget-investments-list .budget-investment, .budget-investments-list .budget-investment,
.proposals-list .proposal { .proposals-list .proposal,
.legislation-proposals .proposal {
@include breakpoint(medium) { @include breakpoint(medium) {
@@ -886,6 +886,13 @@
} }
} }
.legislation-proposals {
.votes {
min-height: $line-height * 8;
}
}
.proposal-show .votes, .proposal-show .votes,
.debate-show .votes { .debate-show .votes {
border: 0; border: 0;
@@ -2087,94 +2094,3 @@
line-height: rem-calc(60); line-height: rem-calc(60);
} }
} }
// 10. Guides
// ----------------------
.guides {
h2 {
margin: $line-height 0 $line-height * 2;
}
.guide-budget,
.guide-proposal {
border-radius: rem-calc(3);
margin: $line-height 0;
padding: $line-height;
position: relative;
.button {
color: $text;
font-weight: bold;
}
&::before {
border-radius: 100%;
color: #fff;
font-family: "icons" !important;
height: rem-calc(80);
left: 50%;
line-height: rem-calc(80);
margin-left: rem-calc(-40);
position: absolute;
text-align: center;
top: -40px;
width: rem-calc(80);
z-index: 99;
}
}
.guide-budget {
border: 1px solid $budget;
&::before {
background: $budget;
content: '\53';
font-size: rem-calc(40);
}
.button {
background: #f8f5f9;
border: 1px solid $budget;
}
}
.guide-proposal {
border: 1px solid $proposals;
&::before {
background: $proposals;
content: '\68';
font-size: rem-calc(40);
}
.button {
background: #fffaf4;
border: 1px solid $proposals;
}
}
ul {
@include breakpoint(medium) {
min-height: $line-height * 14;
}
li {
margin-bottom: $line-height;
padding-left: $line-height;
position: relative;
&::before {
color: #37af65;
content: '\56';
font-family: "icons" !important;
font-size: $small-font-size;
left: 0;
position: absolute;
top: 1px;
}
}
}
}

View File

@@ -1,5 +1,5 @@
class Admin::ActivityController < Admin::BaseController class Admin::ActivityController < Admin::BaseController
has_filters %w{all on_users on_proposals on_debates on_comments} has_filters %w{all on_users on_proposals on_debates on_comments on_system_emails}
def show def show
@activity = Activity.for_render.send(@current_filter) @activity = Activity.for_render.send(@current_filter)

View File

@@ -1,4 +1,5 @@
class Admin::AdminNotificationsController < Admin::BaseController class Admin::AdminNotificationsController < Admin::BaseController
include Translatable
def index def index
@admin_notifications = AdminNotification.all @admin_notifications = AdminNotification.all
@@ -62,6 +63,13 @@ class Admin::AdminNotificationsController < Admin::BaseController
private private
def admin_notification_params def admin_notification_params
params.require(:admin_notification).permit(:title, :body, :link, :segment_recipient) attributes = [:title, :body, :link, :segment_recipient,
*translation_params(AdminNotification)]
params.require(:admin_notification).permit(attributes)
end
def resource
AdminNotification.find(params[:id])
end end
end end

View File

@@ -1,4 +1,5 @@
class Admin::BannersController < Admin::BaseController class Admin::BannersController < Admin::BaseController
include Translatable
has_filters %w{all with_active with_inactive}, only: :index has_filters %w{all with_active with_inactive}, only: :index
@@ -40,6 +41,7 @@ class Admin::BannersController < Admin::BaseController
attributes = [:title, :description, :target_url, attributes = [:title, :description, :target_url,
:post_started_at, :post_ended_at, :post_started_at, :post_ended_at,
:background_color, :font_color, :background_color, :font_color,
*translation_params(Banner),
web_section_ids: []] web_section_ids: []]
params.require(:banner).permit(*attributes) params.require(:banner).permit(*attributes)
end end
@@ -59,4 +61,9 @@ class Admin::BannersController < Admin::BaseController
def banner_sections def banner_sections
@banner_sections = WebSection.all @banner_sections = WebSection.all
end end
def resource
@banner = Banner.find(params[:id]) unless @banner
@banner
end
end end

View File

@@ -47,9 +47,10 @@ class Admin::BudgetInvestmentMilestonesController < Admin::BaseController
image_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy] image_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]
documents_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy] documents_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]
attributes = [:title, :description, :publication_date, :budget_investment_id, :status_id, attributes = [:title, :description, :publication_date, :budget_investment_id, :status_id,
*translation_params(Budget::Investment::Milestone),
image_attributes: image_attributes, documents_attributes: documents_attributes] image_attributes: image_attributes, documents_attributes: documents_attributes]
params.require(:budget_investment_milestone).permit(*attributes, translation_params(params[:budget_investment_milestone])) params.require(:budget_investment_milestone).permit(*attributes)
end end
def load_budget_investment def load_budget_investment
@@ -64,10 +65,6 @@ class Admin::BudgetInvestmentMilestonesController < Admin::BaseController
Budget::Investment::Milestone.find(params[:id]) Budget::Investment::Milestone.find(params[:id])
end end
def resource_model
Budget::Investment::Milestone
end
def resource def resource
get_milestone get_milestone
end end

View File

@@ -75,25 +75,17 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
resource_model.parameterize('_') resource_model.parameterize('_')
end end
def sort_by(params)
if params.present? && Budget::Investment::SORTING_OPTIONS.include?(params)
"#{params == 'supports' ? 'cached_votes_up' : params} ASC"
else
"cached_votes_up DESC, created_at DESC"
end
end
def load_investments def load_investments
@investments = Budget::Investment.scoped_filter(params, @current_filter) @investments = Budget::Investment.scoped_filter(params, @current_filter)
.order(sort_by(params[:sort_by])) @investments = @investments.order_filter(params[:sort_by]) if params[:sort_by].present?
@investments = @investments.page(params[:page]) unless request.format.csv? @investments = @investments.page(params[:page]) unless request.format.csv?
end end
def budget_investment_params def budget_investment_params
params.require(:budget_investment) params.require(:budget_investment)
.permit(:title, :description, :external_url, :heading_id, :administrator_id, :tag_list, .permit(:title, :description, :external_url, :heading_id, :administrator_id, :tag_list,
:valuation_tag_list, :incompatible, :visible_to_valuators, :selected, valuator_ids: [], :valuation_tag_list, :incompatible, :visible_to_valuators, :selected,
valuator_group_ids: []) valuator_ids: [], valuator_group_ids: [])
end end
def load_budget def load_budget

View File

@@ -0,0 +1,34 @@
class Admin::HiddenBudgetInvestmentsController < Admin::BaseController
include FeatureFlags
has_filters %w{all with_confirmed_hide without_confirmed_hide}, only: :index
feature_flag :budgets
before_action :load_investment, only: [:confirm_hide, :restore]
def index
@investments = Budget::Investment.only_hidden.send(@current_filter)
.order(hidden_at: :desc)
.page(params[:page])
end
def confirm_hide
@investment.confirm_hide
redirect_to request.query_parameters.merge(action: :index)
end
def restore
@investment.restore
@investment.ignore_flag
Activity.log(current_user, :restore, @investment)
redirect_to request.query_parameters.merge(action: :index)
end
private
def load_investment
@investment = Budget::Investment.with_hidden.find(params[:id])
end
end

View File

@@ -1,6 +1,8 @@
class Admin::Legislation::DraftVersionsController < Admin::Legislation::BaseController class Admin::Legislation::DraftVersionsController < Admin::Legislation::BaseController
load_and_authorize_resource :process, class: "Legislation::Process" include Translatable
load_and_authorize_resource :draft_version, class: "Legislation::DraftVersion", through: :process
load_and_authorize_resource :draft_version, class: "Legislation::DraftVersion", through: :process, prepend: true
load_and_authorize_resource :process, class: "Legislation::Process", prepend: true
def index def index
@draft_versions = @process.draft_versions @draft_versions = @process.draft_versions
@@ -44,7 +46,12 @@ class Admin::Legislation::DraftVersionsController < Admin::Legislation::BaseCont
:status, :status,
:final_version, :final_version,
:body, :body,
:body_html :body_html,
*translation_params(Legislation::DraftVersion)
) )
end end
def resource
@draft_version
end
end end

View File

@@ -1,4 +1,6 @@
class Admin::Legislation::ProcessesController < Admin::Legislation::BaseController class Admin::Legislation::ProcessesController < Admin::Legislation::BaseController
include Translatable
has_filters %w{open next past all}, only: :index has_filters %w{open next past all}, only: :index
load_and_authorize_resource :process, class: "Legislation::Process" load_and_authorize_resource :process, class: "Legislation::Process"
@@ -61,6 +63,7 @@ class Admin::Legislation::ProcessesController < Admin::Legislation::BaseControll
:result_publication_enabled, :result_publication_enabled,
:published, :published,
:custom_list, :custom_list,
*translation_params(Legislation::Process),
documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy] documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]
) )
end end
@@ -69,4 +72,8 @@ class Admin::Legislation::ProcessesController < Admin::Legislation::BaseControll
@process.set_tag_list_on(:customs, process_params[:custom_list]) @process.set_tag_list_on(:customs, process_params[:custom_list])
@process.save @process.save
end end
def resource
@process || Legislation::Process.find(params[:id])
end
end end

View File

@@ -1,7 +1,16 @@
class Admin::Legislation::ProposalsController < Admin::Legislation::BaseController class Admin::Legislation::ProposalsController < Admin::Legislation::BaseController
has_orders %w[id title supports], only: :index
load_and_authorize_resource :process, class: "Legislation::Process" load_and_authorize_resource :process, class: "Legislation::Process"
load_and_authorize_resource :proposal, class: "Legislation::Proposal", through: :process load_and_authorize_resource :proposal, class: "Legislation::Proposal", through: :process
def index def index
@proposals = @proposals.send("sort_by_#{@current_order}").page(params[:page])
end
def toggle_selection
@proposal.toggle :selected
@proposal.save!
end end
end end

View File

@@ -1,4 +1,6 @@
class Admin::Legislation::QuestionsController < Admin::Legislation::BaseController class Admin::Legislation::QuestionsController < Admin::Legislation::BaseController
include Translatable
load_and_authorize_resource :process, class: "Legislation::Process" load_and_authorize_resource :process, class: "Legislation::Process"
load_and_authorize_resource :question, class: "Legislation::Question", through: :process load_and_authorize_resource :question, class: "Legislation::Question", through: :process
@@ -46,7 +48,13 @@ class Admin::Legislation::QuestionsController < Admin::Legislation::BaseControll
def question_params def question_params
params.require(:legislation_question).permit( params.require(:legislation_question).permit(
:title, :title,
question_options_attributes: [:id, :value, :_destroy] *translation_params(::Legislation::Question),
question_options_attributes: [:id, :value,
*translation_params(::Legislation::QuestionOption)]
) )
end end
def resource
@question || ::Legislation::Question.find(params[:id])
end
end end

View File

@@ -48,10 +48,7 @@ class Admin::NewslettersController < Admin::BaseController
@newsletter = Newsletter.find(params[:id]) @newsletter = Newsletter.find(params[:id])
if @newsletter.valid? if @newsletter.valid?
@newsletter.list_of_recipient_emails.each do |recipient_email| @newsletter.delay.deliver
Mailer.newsletter(@newsletter, recipient_email).deliver_later
end
@newsletter.update(sent_at: Time.current) @newsletter.update(sent_at: Time.current)
flash[:notice] = t("admin.newsletters.send_success") flash[:notice] = t("admin.newsletters.send_success")
else else

View File

@@ -1,4 +1,5 @@
class Admin::Poll::PollsController < Admin::Poll::BaseController class Admin::Poll::PollsController < Admin::Poll::BaseController
include Translatable
load_and_authorize_resource load_and_authorize_resource
before_action :load_search, only: [:search_booths, :search_officers] before_action :load_search, only: [:search_booths, :search_officers]
@@ -63,7 +64,7 @@ class Admin::Poll::PollsController < Admin::Poll::BaseController
attributes = [:name, :starts_at, :ends_at, :geozone_restricted, :summary, :description, attributes = [:name, :starts_at, :ends_at, :geozone_restricted, :summary, :description,
:results_enabled, :stats_enabled, geozone_ids: [], :results_enabled, :stats_enabled, geozone_ids: [],
image_attributes: image_attributes] image_attributes: image_attributes]
params.require(:poll).permit(*attributes) params.require(:poll).permit(*attributes, *translation_params(Poll))
end end
def search_params def search_params
@@ -74,4 +75,7 @@ class Admin::Poll::PollsController < Admin::Poll::BaseController
@search = search_params[:search] @search = search_params[:search]
end end
def resource
@poll ||= Poll.find(params[:id])
end
end end

View File

@@ -1,4 +1,6 @@
class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController
include Translatable
before_action :load_answer, only: [:show, :edit, :update, :documents] before_action :load_answer, only: [:show, :edit, :update, :documents]
load_and_authorize_resource :question, class: "::Poll::Question" load_and_authorize_resource :question, class: "::Poll::Question"
@@ -49,11 +51,15 @@ class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController
def answer_params def answer_params
documents_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy] documents_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]
attributes = [:title, :description, :question_id, documents_attributes: documents_attributes] attributes = [:title, :description, :question_id, documents_attributes: documents_attributes]
params.require(:poll_question_answer).permit(*attributes) params.require(:poll_question_answer).permit(*attributes, *translation_params(Poll::Question::Answer))
end end
def load_answer def load_answer
@answer = ::Poll::Question::Answer.find(params[:id] || params[:answer_id]) @answer = ::Poll::Question::Answer.find(params[:id] || params[:answer_id])
end end
def resource
load_answer unless @answer
@answer
end
end end

View File

@@ -1,5 +1,6 @@
class Admin::Poll::QuestionsController < Admin::Poll::BaseController class Admin::Poll::QuestionsController < Admin::Poll::BaseController
include CommentableActions include CommentableActions
include Translatable
load_and_authorize_resource :poll load_and_authorize_resource :poll
load_and_authorize_resource :question, class: 'Poll::Question' load_and_authorize_resource :question, class: 'Poll::Question'
@@ -55,11 +56,15 @@ class Admin::Poll::QuestionsController < Admin::Poll::BaseController
private private
def question_params def question_params
params.require(:poll_question).permit(:poll_id, :title, :question, :proposal_id) attributes = [:poll_id, :title, :question, :proposal_id]
params.require(:poll_question).permit(*attributes, *translation_params(Poll::Question))
end end
def search_params def search_params
params.permit(:poll_id, :search) params.permit(:poll_id, :search)
end end
def resource
@poll_question ||= Poll::Question.find(params[:id])
end
end end

View File

@@ -5,7 +5,9 @@ class Admin::ProposalNotificationsController < Admin::BaseController
before_action :load_proposal, only: [:confirm_hide, :restore] before_action :load_proposal, only: [:confirm_hide, :restore]
def index def index
@proposal_notifications = ProposalNotification.only_hidden.send(@current_filter).order(hidden_at: :desc) @proposal_notifications = ProposalNotification.only_hidden
.send(@current_filter)
.order(hidden_at: :desc)
.page(params[:page]) .page(params[:page])
end end

View File

@@ -1,7 +1,7 @@
class Admin::SignatureSheetsController < Admin::BaseController class Admin::SignatureSheetsController < Admin::BaseController
def index def index
@signature_sheets = SignatureSheet.all @signature_sheets = SignatureSheet.all.order(created_at: :desc)
end end
def new def new

View File

@@ -0,0 +1,82 @@
class Admin::SiteCustomization::InformationTextsController < Admin::SiteCustomization::BaseController
include Translatable
def index
fetch_existing_keys
append_or_create_keys
@content = @content[@tab.to_s]
end
def update
content_params.each do |content|
values = content[:values].slice(*translation_params(I18nContent))
unless values.empty?
values.each do |key, value|
locale = key.split("_").last
if value == t(content[:id], locale: locale) || value.match(/translation missing/)
next
else
text = I18nContent.find_or_create_by(key: content[:id])
Globalize.locale = locale
text.update(value: value)
end
end
end
end
redirect_to admin_site_customization_information_texts_path,
notice: t('flash.actions.update.translation')
end
private
def resource
I18nContent.find(content_params[:id])
end
def content_params
params.require(:contents).values
end
def delete_translations
languages_to_delete = params[:enabled_translations].select { |_, v| v == '0' }
.keys
languages_to_delete.each do |locale|
I18nContentTranslation.destroy_all(locale: locale)
end
end
def fetch_existing_keys
@existing_keys = {}
@tab = params[:tab] || :debates
I18nContent.begins_with_key(@tab)
.all
.map{ |content| @existing_keys[content.key] = content }
end
def append_or_create_keys
@content = {}
I18n.backend.send(:init_translations) unless I18n.backend.initialized?
locale = params[:locale] || I18n.locale
translations = I18n.backend.send(:translations)[locale.to_sym]
translations.each do |k, v|
@content[k.to_s] = flat_hash(v).keys
.map { |s| @existing_keys["#{k.to_s}.#{s}"].nil? ?
I18nContent.new(key: "#{k.to_s}.#{s}") :
@existing_keys["#{k.to_s}.#{s}"] }
end
end
def flat_hash(h, f = nil, g = {})
return g.update({ f => h }) unless h.is_a? Hash
h.each { |k, r| flat_hash(r, [f,k].compact.join('.'), g) }
return g
end
end

View File

@@ -1,4 +1,5 @@
class Admin::SiteCustomization::PagesController < Admin::SiteCustomization::BaseController class Admin::SiteCustomization::PagesController < Admin::SiteCustomization::BaseController
include Translatable
load_and_authorize_resource :page, class: "SiteCustomization::Page" load_and_authorize_resource :page, class: "SiteCustomization::Page"
def index def index
@@ -34,15 +35,21 @@ class Admin::SiteCustomization::PagesController < Admin::SiteCustomization::Base
private private
def page_params def page_params
params.require(:site_customization_page).permit( attributes = [:slug,
:slug,
:title, :title,
:subtitle, :subtitle,
:content, :content,
:more_info_flag, :more_info_flag,
:print_content_flag, :print_content_flag,
:status, :status,
:locale :locale]
params.require(:site_customization_page).permit(*attributes,
*translation_params(SiteCustomization::Page)
) )
end end
def resource
SiteCustomization::Page.find(params[:id])
end
end end

View File

@@ -18,8 +18,13 @@ class Admin::StatsController < Admin::BaseController
@verified_users = User.active.level_two_or_three_verified.count @verified_users = User.active.level_two_or_three_verified.count
@unverified_users = User.active.unverified.count @unverified_users = User.active.unverified.count
@users = User.active.count @users = User.active.count
@user_ids_who_voted_proposals = ActsAsVotable::Vote.where(votable_type: 'Proposal').distinct.count(:voter_id)
@user_ids_who_voted_proposals = ActsAsVotable::Vote.where(votable_type: 'Proposal')
.distinct
.count(:voter_id)
@user_ids_who_didnt_vote_proposals = @verified_users - @user_ids_who_voted_proposals @user_ids_who_didnt_vote_proposals = @verified_users - @user_ids_who_voted_proposals
@spending_proposals = SpendingProposal.count @spending_proposals = SpendingProposal.count
budgets_ids = Budget.where.not(phase: 'finished').pluck(:id) budgets_ids = Budget.where.not(phase: 'finished').pluck(:id)
@budgets = budgets_ids.size @budgets = budgets_ids.size

View File

@@ -0,0 +1,50 @@
class Admin::SystemEmailsController < Admin::BaseController
before_action :load_system_email, only: [:view, :preview_pending, :moderate_pending]
def index
@system_emails = {
proposal_notification_digest: %w(view preview_pending)
}
end
def view
case @system_email
when "proposal_notification_digest"
@notifications = Notification.where(notifiable_type: "ProposalNotification").limit(2)
@subject = t('mailers.proposal_notification_digest.title', org_name: Setting['org_name'])
end
end
def preview_pending
case @system_email
when "proposal_notification_digest"
@previews = ProposalNotification.where(id: unsent_proposal_notifications_ids)
.page(params[:page])
end
end
def moderate_pending
ProposalNotification.find(params[:id]).moderate_system_email(current_user)
redirect_to admin_system_email_preview_pending_path("proposal_notification_digest")
end
def send_pending
Notification.delay.send_pending
flash[:notice] = t("admin.system_emails.preview_pending.send_pending_notification")
redirect_to admin_system_emails_path
end
private
def load_system_email
@system_email = params[:system_email_id]
end
def unsent_proposal_notifications_ids
Notification.where(notifiable_type: "ProposalNotification", emailed_at: nil)
.group(:notifiable_id).count.keys
end
end

View File

@@ -5,7 +5,9 @@ class Admin::VerificationsController < Admin::BaseController
end end
def search def search
@users = User.incomplete_verification.search(params[:name_or_email]).page(params[:page]).for_render @users = User.incomplete_verification.search(params[:name_or_email])
.page(params[:page])
.for_render
render :index render :index
end end

View File

@@ -1,4 +1,5 @@
class Admin::Widget::CardsController < Admin::BaseController class Admin::Widget::CardsController < Admin::BaseController
include Translatable
def new def new
@card = ::Widget::Card.new(header: header_card?) @card = ::Widget::Card.new(header: header_card?)
@@ -39,13 +40,21 @@ class Admin::Widget::CardsController < Admin::BaseController
private private
def card_params def card_params
params.require(:widget_card).permit(:label, :title, :description, :link_text, :link_url, image_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]
params.require(:widget_card).permit(
:label, :title, :description, :link_text, :link_url,
:button_text, :button_url, :alignment, :header, :button_text, :button_url, :alignment, :header,
image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) *translation_params(Widget::Card),
image_attributes: image_attributes
)
end end
def header_card? def header_card?
params[:header_card].present? params[:header_card].present?
end end
def resource
Widget::Card.find(params[:id])
end
end end

View File

@@ -3,18 +3,32 @@ module FlagActions
def flag def flag
Flag.flag(current_user, flaggable) Flag.flag(current_user, flaggable)
if controller_name == 'investments'
respond_with flaggable, template: "budgets/#{controller_name}/_refresh_flag_actions"
else
respond_with flaggable, template: "#{controller_name}/_refresh_flag_actions" respond_with flaggable, template: "#{controller_name}/_refresh_flag_actions"
end end
end
def unflag def unflag
Flag.unflag(current_user, flaggable) Flag.unflag(current_user, flaggable)
if controller_name == 'investments'
respond_with flaggable, template: "budgets/#{controller_name}/_refresh_flag_actions"
else
respond_with flaggable, template: "#{controller_name}/_refresh_flag_actions" respond_with flaggable, template: "#{controller_name}/_refresh_flag_actions"
end end
end
private private
def flaggable def flaggable
if resource_model.to_s == 'Budget::Investment'
instance_variable_get("@investment")
else
instance_variable_get("@#{resource_model.to_s.downcase}") instance_variable_get("@#{resource_model.to_s.downcase}")
end end
end
end end

View File

@@ -3,8 +3,12 @@ module Polymorphic
private private
def resource def resource
if resource_model.to_s == 'Budget::Investment'
@resource ||= instance_variable_get("@investment")
else
@resource ||= instance_variable_get("@#{resource_name}") @resource ||= instance_variable_get("@#{resource_name}")
end end
end
def resource_name def resource_name
@resource_name ||= resource_model.to_s.downcase @resource_name ||= resource_model.to_s.downcase

View File

@@ -7,13 +7,18 @@ module Translatable
private private
def translation_params(params) def translation_params(resource_model)
resource_model.globalize_attribute_names.select { |k, v| params.include?(k.to_sym) && params[k].present? } return [] unless params[:enabled_translations]
resource_model.translated_attribute_names.product(enabled_translations).map do |attr_name, loc|
resource_model.localized_attr_name_for(attr_name, loc)
end
end end
def delete_translations def delete_translations
locales = resource_model.globalize_locales. locales = resource.translated_locales
select { |k, v| params[:delete_translations].include?(k.to_sym) && params[:delete_translations][k] == "1" } .select { |l| params.dig(:enabled_translations, l) == "0" }
locales.each do |l| locales.each do |l|
Globalize.with_locale(l) do Globalize.with_locale(l) do
resource.translation.destroy resource.translation.destroy
@@ -21,4 +26,9 @@ module Translatable
end end
end end
def enabled_translations
params.fetch(:enabled_translations, {})
.select { |_, v| v == '1' }
.keys
end
end end

View File

@@ -1,8 +0,0 @@
class GuidesController < ApplicationController
skip_authorization_check
def new
end
end

View File

@@ -1,7 +1,11 @@
class Legislation::ProcessesController < Legislation::BaseController class Legislation::ProcessesController < Legislation::BaseController
has_filters %w{open next past}, only: :index has_filters %w[open next past], only: :index
has_filters %w[random winners], only: :proposals
load_and_authorize_resource load_and_authorize_resource
before_action :set_random_seed, only: :proposals
def index def index
@current_filter ||= 'open' @current_filter ||= 'open'
@processes = ::Legislation::Process.send(@current_filter).published.page(params[:page]) @processes = ::Legislation::Process.send(@current_filter).published.page(params[:page])
@@ -25,7 +29,7 @@ class Legislation::ProcessesController < Legislation::BaseController
set_process set_process
@phase = :debate_phase @phase = :debate_phase
if @process.debate_phase.started? if @process.debate_phase.started? || (current_user && current_user.administrator?)
render :debate render :debate
else else
render :phase_not_open render :phase_not_open
@@ -87,8 +91,14 @@ class Legislation::ProcessesController < Legislation::BaseController
set_process set_process
@phase = :proposals_phase @phase = :proposals_phase
if @process.proposals_phase.started? @proposals = ::Legislation::Proposal.where(process: @process)
legislation_proposal_votes(@process.proposals) @proposals = @proposals.search(params[:search]) if params[:search].present?
@current_filter = "winners" if params[:filter].blank? && @proposals.winners.any?
@proposals = @proposals.send(@current_filter).page(params[:page])
if @process.proposals_phase.started? || (current_user && current_user.administrator?)
legislation_proposal_votes(@proposals)
render :proposals render :proposals
else else
render :phase_not_open render :phase_not_open
@@ -105,4 +115,15 @@ class Legislation::ProcessesController < Legislation::BaseController
return if member_method? return if member_method?
@process = ::Legislation::Process.find(params[:process_id]) @process = ::Legislation::Process.find(params[:process_id])
end end
def set_random_seed
seed = begin
Float(params[:random_seed] || session[:random_seed] || (rand(99) / 100.0))
rescue
0
end
session[:random_seed], params[:random_seed] = seed
seed = (-1..1).cover?(seed) ? seed : 1
::Legislation::Proposal.connection.execute "select setseed(#{seed})"
end
end end

View File

@@ -2,13 +2,13 @@ class Legislation::ProposalsController < Legislation::BaseController
include CommentableActions include CommentableActions
include FlagActions include FlagActions
load_and_authorize_resource :process, class: "Legislation::Process"
load_and_authorize_resource :proposal, class: "Legislation::Proposal", through: :process
before_action :parse_tag_filter, only: :index before_action :parse_tag_filter, only: :index
before_action :load_categories, only: [:index, :new, :create, :edit, :map, :summary] before_action :load_categories, only: [:index, :new, :create, :edit, :map, :summary]
before_action :load_geozones, only: [:edit, :map, :summary] before_action :load_geozones, only: [:edit, :map, :summary]
before_action :authenticate_user!, except: [:index, :show, :map, :summary] before_action :authenticate_user!, except: [:index, :show, :map, :summary]
load_and_authorize_resource :process, class: "Legislation::Process"
load_and_authorize_resource :proposal, class: "Legislation::Proposal", through: :process
invisible_captcha only: [:create, :update], honeypot: :subtitle invisible_captcha only: [:create, :update], honeypot: :subtitle
@@ -54,6 +54,7 @@ class Legislation::ProposalsController < Legislation::BaseController
params.require(:legislation_proposal).permit(:legislation_process_id, :title, params.require(:legislation_proposal).permit(:legislation_process_id, :title,
:question, :summary, :description, :video_url, :tag_list, :question, :summary, :description, :video_url, :tag_list,
:terms_of_service, :geozone_id, :terms_of_service, :geozone_id,
image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy],
documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id]) documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id])
end end

View File

@@ -6,6 +6,7 @@ class Management::BaseController < ActionController::Base
helper_method :managed_user helper_method :managed_user
helper_method :current_user helper_method :current_user
helper_method :manager_logged_in
private private
@@ -22,7 +23,10 @@ class Management::BaseController < ActionController::Base
end end
def managed_user def managed_user
@managed_user ||= Verification::Management::ManagedUser.find(session[:document_type], session[:document_number]) @managed_user ||= Verification::Management::ManagedUser.find(
session[:document_type],
session[:document_number]
)
end end
def check_verified_user(alert_msg) def check_verified_user(alert_msg)
@@ -49,4 +53,11 @@ class Management::BaseController < ActionController::Base
def clear_password def clear_password
session[:new_password] = nil session[:new_password] = nil
end end
def manager_logged_in
if current_manager
@manager_logged_in = User.find_by_manager_login(session[:manager]["login"])
end
end
end end

View File

@@ -0,0 +1,24 @@
class Moderation::Budgets::InvestmentsController < Moderation::BaseController
include FeatureFlags
include ModerateActions
has_filters %w{pending_flag_review all with_ignored_flag}, only: :index
has_orders %w{flags created_at}, only: :index
feature_flag :budgets
before_action :load_resources, only: [:index, :moderate]
load_and_authorize_resource class: 'Budget::Investment'
private
def resource_name
'budget_investment'
end
def resource_model
Budget::Investment
end
end

View File

@@ -36,19 +36,11 @@ class NotificationsController < ApplicationController
end end
private private
def linkable_resource_path(notification) def linkable_resource_path(notification)
case notification.linkable_resource.class.name if notification.linkable_resource.is_a?(AdminNotification)
when "Budget::Investment" notification.linkable_resource.link || notifications_path
budget_investment_path @notification.linkable_resource.budget, @notification.linkable_resource
when "Topic"
community_topic_path @notification.linkable_resource.community, @notification.linkable_resource
else else
if @notification.linkable_resource.is_a?(AdminNotification) polymorphic_hierarchy_path(notification.linkable_resource)
@notification.linkable_resource.link || notifications_path
else
url_for @notification.linkable_resource
end
end end
end end

View File

@@ -1,6 +1,9 @@
class PagesController < ApplicationController class PagesController < ApplicationController
include FeatureFlags
skip_authorization_check skip_authorization_check
feature_flag :help_page, if: lambda { params[:id] == "help/index" }
def show def show
@custom_page = SiteCustomization::Page.published.find_by(slug: params[:id]) @custom_page = SiteCustomization::Page.published.find_by(slug: params[:id])
@banners = Banner.in_section('help_page').with_active @banners = Banner.in_section('help_page').with_active

View File

@@ -14,6 +14,7 @@ class StatsController < ApplicationController
@debate_votes = daily_cache('debate_votes') { Vote.where(votable_type: 'Debate').count } @debate_votes = daily_cache('debate_votes') { Vote.where(votable_type: 'Debate').count }
@proposal_votes = daily_cache('proposal_votes') { Vote.where(votable_type: 'Proposal').count } @proposal_votes = daily_cache('proposal_votes') { Vote.where(votable_type: 'Proposal').count }
@comment_votes = daily_cache('comment_votes') { Vote.where(votable_type: 'Comment').count } @comment_votes = daily_cache('comment_votes') { Vote.where(votable_type: 'Comment').count }
@investment_votes = daily_cache('budget_investment_votes') { Vote.where(votable_type: 'Budget::Investment').count }
@votes = daily_cache('votes') { Vote.count } @votes = daily_cache('votes') { Vote.count }
@verified_users = daily_cache('verified_users') { User.with_hidden.level_two_or_three_verified.count } @verified_users = daily_cache('verified_users') { User.with_hidden.level_two_or_three_verified.count }

View File

@@ -11,7 +11,7 @@ class Users::SessionsController < Devise::SessionsController
end end
def after_sign_out_path_for(resource) def after_sign_out_path_for(resource)
request.referer.present? ? request.referer : super request.referer.present? && !request.referer.match("management") ? request.referer : super
end end
def verifying_via_email? def verifying_via_email?

View File

@@ -1,7 +1,7 @@
module AdminBudgetInvestmentsHelper module AdminBudgetInvestmentsHelper
def advanced_menu_visibility def advanced_menu_visibility
(params[:advanced_filters].empty? && params["max_per_heading"].blank?) ? 'hide' : '' (params[:advanced_filters].empty? && params["min_total_supports"].blank?) ? 'hide' : ''
end end
def init_advanced_menu def init_advanced_menu

View File

@@ -1,19 +1,31 @@
module AdminHelper module AdminHelper
def side_menu def side_menu
if namespace == 'moderation/budgets'
render "/moderation/menu"
else
render "/#{namespace}/menu" render "/#{namespace}/menu"
end end
end
def namespaced_root_path def namespaced_root_path
if namespace == 'moderation/budgets'
"/moderation"
else
"/#{namespace}" "/#{namespace}"
end end
end
def namespaced_header_title def namespaced_header_title
if namespace == 'moderation/budgets'
t("moderation.header.title")
else
t("#{namespace}.header.title") t("#{namespace}.header.title")
end end
end
def menu_moderated_content? def menu_moderated_content?
["proposals", "debates", "comments", "hidden_users", "activity"].include?(controller_name) && controller.class.parent != Admin::Legislation ["proposals", "debates", "comments", "hidden_users", "activity", "hidden_budget_investments"].include?(controller_name) && controller.class.parent != Admin::Legislation
end end
def menu_budget? def menu_budget?
@@ -21,11 +33,11 @@ module AdminHelper
end end
def menu_polls? def menu_polls?
%w[polls questions answers].include?(controller_name) %w[polls questions answers recounts results].include?(controller_name)
end end
def menu_booths? def menu_booths?
%w[officers booths officer_assignments booth_assignments recounts results shifts].include?(controller_name) %w[officers booths shifts booth_assignments officer_assignments].include?(controller_name)
end end
def menu_profiles? def menu_profiles?
@@ -37,7 +49,7 @@ module AdminHelper
end end
def menu_customization? def menu_customization?
["pages", "banners"].include?(controller_name) || menu_homepage? ["pages", "banners", "information_texts"].include?(controller_name) || menu_homepage?
end end
def menu_homepage? def menu_homepage?

View File

@@ -41,22 +41,7 @@ module CommentsHelper
end end
def commentable_path(comment) def commentable_path(comment)
commentable = comment.commentable polymorphic_hierarchy_path(comment.commentable)
case comment.commentable_type
when "Budget::Investment"
budget_investment_path(commentable.budget_id, commentable)
when "Legislation::Question"
legislation_process_question_path(commentable.process, commentable)
when "Legislation::Annotation"
legislation_process_draft_version_annotation_path(commentable.draft_version.process, commentable.draft_version, commentable)
when "Topic"
community_topic_path(commentable.community, commentable)
when "Legislation::Proposal"
legislation_process_proposal_path(commentable.legislation_process_id, commentable)
else
commentable
end
end end
def user_level_class(comment) def user_level_class(comment)

View File

@@ -6,16 +6,24 @@ module GlobalizeHelper
def locale_options def locale_options
I18n.available_locales.map do |locale| I18n.available_locales.map do |locale|
[name_for_locale(locale), neutral_locale(locale)] [name_for_locale(locale), locale]
end end
end end
def display_translation?(locale) def display_translation?(locale)
same_locale?(neutral_locale(I18n.locale), neutral_locale(locale)) ? "" : "display: none" same_locale?(I18n.locale, locale) ? "" : "display: none;"
end
def translation_enabled_tag(locale, enabled)
hidden_field_tag("enabled_translations[#{locale}]", (enabled ? 1 : 0))
end end
def css_to_display_translation?(resource, locale) def css_to_display_translation?(resource, locale)
resource.translated_locales.include?(neutral_locale(locale)) || locale == I18n.locale ? "" : "display: none" enable_locale?(resource, locale) ? "" : "display: none;"
end
def enable_locale?(resource, locale)
resource.translated_locales.include?(locale) || locale == I18n.locale
end end
def highlight_current?(locale) def highlight_current?(locale)
@@ -26,10 +34,6 @@ module GlobalizeHelper
display_translation?(locale) display_translation?(locale)
end end
def neutral_locale(locale)
locale.to_s.downcase.underscore.to_sym
end
def globalize(locale, &block) def globalize(locale, &block)
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
yield yield

View File

@@ -1,19 +0,0 @@
module GuidesHelper
def new_proposal_guide
if feature?('guides') && Budget.current&.accepting?
new_guide_path
else
new_proposal_path
end
end
def new_budget_investment_guide
if feature?('guides')
new_guide_path
else
new_budget_investment_path(current_budget)
end
end
end

View File

@@ -6,4 +6,24 @@ module LegislationHelper
def format_date_for_calendar_form(date) def format_date_for_calendar_form(date)
l(date, format: "%d/%m/%Y") if date l(date, format: "%d/%m/%Y") if date
end end
def new_legislation_proposal_link_text(process)
t("proposals.index.start_proposal")
end
def link_to_toggle_legislation_proposal_selection(proposal)
if proposal.selected?
button_text = t("admin.legislation.proposals.index.selected")
html_class = 'button expanded'
else
button_text = t("admin.legislation.proposals.index.select")
html_class = 'button hollow expanded'
end
link_to button_text,
toggle_selection_admin_legislation_process_proposal_path(proposal.process, proposal),
remote: true,
method: :patch,
class: html_class
end
end end

View File

@@ -1,10 +1,7 @@
module LocalesHelper module LocalesHelper
def name_for_locale(locale) def name_for_locale(locale)
default = I18n.t("locale", locale: locale) I18n.t("i18n.language.name", locale: locale, fallback: false, default: locale.to_s)
I18n.backend.translate(locale, "i18n.language.name", default: default)
rescue
nil
end end
end end

View File

@@ -46,7 +46,7 @@ module PollsHelper
end end
def voted_before_sign_in(question) def voted_before_sign_in(question)
question.answers.where(author: current_user).any? { |vote| current_user.current_sign_in_at >= vote.updated_at } question.answers.where(author: current_user).any? { |vote| current_user.current_sign_in_at > vote.updated_at }
end end
end end

View File

@@ -0,0 +1,9 @@
module SiteCustomizationHelper
def site_customization_enable_translation?(locale)
I18nContentTranslation.existing_languages.include?(locale) || locale == I18n.locale
end
def site_customization_display_translation?(locale)
site_customization_enable_translation?(locale) ? "" : "display: none;"
end
end

View File

@@ -0,0 +1,67 @@
module TranslatableFormHelper
def translatable_form_for(record_or_record_path, options = {})
object = record_or_record_path.is_a?(Array) ? record_or_record_path.last : record_or_record_path
form_for(record_or_record_path, options.merge(builder: TranslatableFormBuilder)) do |f|
object.globalize_locales.each do |locale|
concat translation_enabled_tag(locale, enable_locale?(object, locale))
end
yield(f)
end
end
def merge_translatable_field_options(options, locale)
options.merge(
class: "#{options[:class]} js-globalize-attribute".strip,
style: "#{options[:style]} #{display_translation?(locale)}".strip,
data: options.fetch(:data, {}).merge(locale: locale),
label_options: {
class: "#{options.dig(:label_options, :class)} js-globalize-attribute".strip,
style: "#{options.dig(:label_options, :style)} #{display_translation?(locale)}".strip,
data: (options.dig(:label_options, :data) || {}) .merge(locale: locale)
}
)
end
class TranslatableFormBuilder < FoundationRailsHelper::FormBuilder
def translatable_text_field(method, options = {})
translatable_field(:text_field, method, options)
end
def translatable_text_area(method, options = {})
translatable_field(:text_area, method, options)
end
def translatable_cktext_area(method, options = {})
translatable_field(:cktext_area, method, options)
end
private
def translatable_field(field_type, method, options = {})
@template.capture do
@object.globalize_locales.each do |locale|
Globalize.with_locale(locale) do
localized_attr_name = @object.localized_attr_name_for(method, locale)
label_without_locale = @object.class.human_attribute_name(method)
final_options = @template.merge_translatable_field_options(options, locale)
.reverse_merge(label: label_without_locale)
if field_type == :cktext_area
@template.concat content_tag :div, send(field_type, localized_attr_name, final_options),
class: "js-globalize-attribute",
style: @template.display_translation?(locale),
data: { locale: locale }
else
@template.concat send(field_type, localized_attr_name, final_options)
end
end
end
end
end
end
end

View File

@@ -52,8 +52,8 @@ module UsersHelper
current_user && current_user.manager? current_user && current_user.manager?
end end
def show_admin_menu? def show_admin_menu?(user = nil)
current_administrator? || current_moderator? || current_valuator? || current_manager? current_administrator? || current_moderator? || current_valuator? || current_manager? || (user && user.administrator?)
end end
def interests_title_text(user) def interests_title_text(user)

View File

@@ -14,9 +14,16 @@ module Abilities
can :restore, Proposal can :restore, Proposal
cannot :restore, Proposal, hidden_at: nil cannot :restore, Proposal, hidden_at: nil
can :create, Legislation::Proposal
can :show, Legislation::Proposal
can :proposals, ::Legislation::Process
can :restore, Legislation::Proposal can :restore, Legislation::Proposal
cannot :restore, Legislation::Proposal, hidden_at: nil cannot :restore, Legislation::Proposal, hidden_at: nil
can :restore, Budget::Investment
cannot :restore, Budget::Investment, hidden_at: nil
can :restore, User can :restore, User
cannot :restore, User, hidden_at: nil cannot :restore, User, hidden_at: nil
@@ -32,6 +39,9 @@ module Abilities
can :confirm_hide, Legislation::Proposal can :confirm_hide, Legislation::Proposal
cannot :confirm_hide, Legislation::Proposal, hidden_at: nil cannot :confirm_hide, Legislation::Proposal, hidden_at: nil
can :confirm_hide, Budget::Investment
cannot :confirm_hide, Budget::Investment, hidden_at: nil
can :confirm_hide, User can :confirm_hide, User
cannot :confirm_hide, User, hidden_at: nil cannot :confirm_hide, User, hidden_at: nil
@@ -51,8 +61,7 @@ module Abilities
can :manage, Dashboard::Action can :manage, Dashboard::Action
can [:read, :update, :valuate, :destroy, :summary], SpendingProposal can [:read, :update, :valuate, :destroy, :summary], SpendingProposal
can [:index, :read, :new, :create, :update, :destroy, :calculate_winners], Budget
can [:index, :read, :new, :create, :update, :destroy, :calculate_winners, :read_results], Budget
can [:read, :create, :update, :destroy], Budget::Group can [:read, :create, :update, :destroy], Budget::Group
can [:read, :create, :update, :destroy], Budget::Heading can [:read, :create, :update, :destroy], Budget::Heading
can [:hide, :update, :toggle_selection], Budget::Investment can [:hide, :update, :toggle_selection], Budget::Investment
@@ -75,6 +84,9 @@ module Abilities
can :manage, SiteCustomization::Image can :manage, SiteCustomization::Image
can :manage, SiteCustomization::ContentBlock can :manage, SiteCustomization::ContentBlock
can :access, :ckeditor
can :manage, Ckeditor::Picture
can [:manage], ::Legislation::Process can [:manage], ::Legislation::Process
can [:manage], ::Legislation::DraftVersion can [:manage], ::Legislation::DraftVersion
can [:manage], ::Legislation::Question can [:manage], ::Legislation::Question

View File

@@ -66,6 +66,9 @@ module Abilities
can [:flag, :unflag], Legislation::Proposal can [:flag, :unflag], Legislation::Proposal
cannot [:flag, :unflag], Legislation::Proposal, author_id: user.id cannot [:flag, :unflag], Legislation::Proposal, author_id: user.id
can [:flag, :unflag], Budget::Investment
cannot [:flag, :unflag], Budget::Investment, author_id: user.id
can [:create, :destroy], Follow can [:create, :destroy], Follow
can [:destroy], Document do |document| can [:destroy], Document do |document|

View File

@@ -63,6 +63,15 @@ module Abilities
cannot :moderate, ProposalNotification, author_id: user.id cannot :moderate, ProposalNotification, author_id: user.id
can :index, ProposalNotification can :index, ProposalNotification
can :hide, Budget::Investment, hidden_at: nil
cannot :hide, Budget::Investment, author_id: user.id
can :ignore_flag, Budget::Investment, ignored_flag_at: nil, hidden_at: nil
cannot :ignore_flag, Budget::Investment, author_id: user.id
can :moderate, Budget::Investment
cannot :moderate, Budget::Investment, author_id: user.id
end end
end end
end end

View File

@@ -2,7 +2,7 @@ class Activity < ActiveRecord::Base
belongs_to :actionable, -> { with_hidden }, polymorphic: true belongs_to :actionable, -> { with_hidden }, polymorphic: true
belongs_to :user, -> { with_hidden } belongs_to :user, -> { with_hidden }
VALID_ACTIONS = %w(hide block restore valuate) VALID_ACTIONS = %w(hide block restore valuate email)
validates :action, inclusion: {in: VALID_ACTIONS} validates :action, inclusion: {in: VALID_ACTIONS}
@@ -11,6 +11,7 @@ class Activity < ActiveRecord::Base
scope :on_users, -> { where(actionable_type: 'User') } scope :on_users, -> { where(actionable_type: 'User') }
scope :on_comments, -> { where(actionable_type: 'Comment') } scope :on_comments, -> { where(actionable_type: 'Comment') }
scope :on_budget_investments, -> { where(actionable_type: 'Budget::Investment') } scope :on_budget_investments, -> { where(actionable_type: 'Budget::Investment') }
scope :on_system_emails, -> { where(actionable_type: 'ProposalNotification') }
scope :for_render, -> { includes(user: [:moderator, :administrator]).includes(:actionable) } scope :for_render, -> { includes(user: [:moderator, :administrator]).includes(:actionable) }
def self.log(user, action, actionable) def self.log(user, action, actionable)

View File

@@ -1,6 +1,10 @@
class AdminNotification < ActiveRecord::Base class AdminNotification < ActiveRecord::Base
include Notifiable include Notifiable
translates :title, touch: true
translates :body, touch: true
globalize_accessors
validates :title, presence: true validates :title, presence: true
validates :body, presence: true validates :body, presence: true
validates :segment_recipient, presence: true validates :segment_recipient, presence: true

View File

@@ -3,6 +3,10 @@ class Banner < ActiveRecord::Base
acts_as_paranoid column: :hidden_at acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases include ActsAsParanoidAliases
translates :title, touch: true
translates :description, touch: true
globalize_accessors
validates :title, presence: true, validates :title, presence: true,
length: { minimum: 2 } length: { minimum: 2 }
validates :description, presence: true validates :description, presence: true

View File

@@ -23,6 +23,7 @@ class Budget
include Relationable include Relationable
include Notifiable include Notifiable
include Filterable include Filterable
include Flaggable
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id' belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
belongs_to :heading belongs_to :heading
@@ -57,6 +58,10 @@ class Budget
scope :sort_by_price, -> { reorder(price: :desc, confidence_score: :desc, id: :desc) } scope :sort_by_price, -> { reorder(price: :desc, confidence_score: :desc, id: :desc) }
scope :sort_by_random, ->(seed) { reorder("budget_investments.id % #{seed.to_f.nonzero? ? seed.to_f : 1}, budget_investments.id") } scope :sort_by_random, ->(seed) { reorder("budget_investments.id % #{seed.to_f.nonzero? ? seed.to_f : 1}, budget_investments.id") }
scope :sort_by_id, -> { order("id DESC") }
scope :sort_by_title, -> { order("title ASC") }
scope :sort_by_supports, -> { order("cached_votes_up DESC") }
scope :valuation_open, -> { where(valuation_finished: false) } scope :valuation_open, -> { where(valuation_finished: false) }
scope :without_admin, -> { valuation_open.where(administrator_id: nil) } scope :without_admin, -> { valuation_open.where(administrator_id: nil) }
scope :without_valuator, -> { valuation_open.where(valuator_assignments_count: 0) } scope :without_valuator, -> { valuation_open.where(valuator_assignments_count: 0) }
@@ -77,6 +82,8 @@ class Budget
scope :winners, -> { selected.compatible.where(winner: true) } scope :winners, -> { selected.compatible.where(winner: true) }
scope :unselected, -> { not_unfeasible.where(selected: false) } scope :unselected, -> { not_unfeasible.where(selected: false) }
scope :last_week, -> { where("created_at >= ?", 7.days.ago)} scope :last_week, -> { where("created_at >= ?", 7.days.ago)}
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
scope :sort_by_created_at, -> { reorder(created_at: :desc) }
scope :by_budget, ->(budget) { where(budget: budget) } scope :by_budget, ->(budget) { where(budget: budget) }
scope :by_group, ->(group_id) { where(group_id: group_id) } scope :by_group, ->(group_id) { where(group_id: group_id) }
@@ -109,7 +116,8 @@ class Budget
budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id]) budget = Budget.find_by(slug: params[:budget_id]) || Budget.find_by(id: params[:budget_id])
results = Investment.by_budget(budget) results = Investment.by_budget(budget)
results = limit_results(budget, params, results) if params[:max_per_heading].present? results = results.where("cached_votes_up + physical_votes >= ?",
params[:min_total_supports]) if params[:min_total_supports].present?
results = results.where(group_id: params[:group_id]) if params[:group_id].present? results = results.where(group_id: params[:group_id]) if params[:group_id].present?
results = results.by_tag(params[:tag_name]) if params[:tag_name].present? results = results.by_tag(params[:tag_name]) if params[:tag_name].present?
results = results.by_heading(params[:heading_id]) if params[:heading_id].present? results = results.by_heading(params[:heading_id]) if params[:heading_id].present?
@@ -132,6 +140,12 @@ class Budget
results.where("budget_investments.id IN (?)", ids) results.where("budget_investments.id IN (?)", ids)
end end
def self.order_filter(sorting_param)
if sorting_param.present? && SORTING_OPTIONS.include?(sorting_param)
send("sort_by_#{sorting_param}")
end
end
def self.limit_results(budget, params, results) def self.limit_results(budget, params, results)
max_per_heading = params[:max_per_heading].to_i max_per_heading = params[:max_per_heading].to_i
return results if max_per_heading <= 0 return results if max_per_heading <= 0

View File

@@ -8,7 +8,7 @@ class Budget
accepted_content_types: [ "application/pdf" ] accepted_content_types: [ "application/pdf" ]
translates :title, :description, touch: true translates :title, :description, touch: true
globalize_accessors locales: [:en, :es, :fr, :nl, :val, :pt_br] globalize_accessors
belongs_to :investment belongs_to :investment
belongs_to :status, class_name: 'Budget::Investment::Status' belongs_to :status, class_name: 'Budget::Investment::Status'

View File

@@ -0,0 +1,4 @@
class Ckeditor::Asset < ActiveRecord::Base
include Ckeditor::Orm::ActiveRecord::AssetBase
include Ckeditor::Backend::Paperclip
end

View File

@@ -0,0 +1,14 @@
class Ckeditor::Picture < Ckeditor::Asset
has_attached_file :data,
url: '/ckeditor_assets/pictures/:id/:style_:basename.:extension',
path: ':rails_root/public/ckeditor_assets/pictures/:id/:style_:basename.:extension',
styles: { content: '800>', thumb: '118x100#' }
validates_attachment_presence :data
validates_attachment_size :data, less_than: 2.megabytes
validates_attachment_content_type :data, content_type: /\Aimage/
def url_content
url(:content)
end
end

View File

@@ -0,0 +1,11 @@
class I18nContent < ActiveRecord::Base
scope :by_key, -> (key){ where(key: key) }
scope :begins_with_key, -> (key){ where("key ILIKE ?", "#{key}?%") }
validates :key, uniqueness: true
translates :value, touch: true
globalize_accessors locales: [:en, :es, :fr, :nl]
end

View File

@@ -0,0 +1,5 @@
class I18nContentTranslation < ActiveRecord::Base
def self.existing_languages
self.select(:locale).uniq.map{ |l| l.locale.to_sym }.to_a
end
end

View File

@@ -2,7 +2,7 @@ class Image < ActiveRecord::Base
include ImagesHelper include ImagesHelper
include ImageablesHelper include ImageablesHelper
TITLE_LEGHT_RANGE = 4..80 TITLE_LENGTH_RANGE = 4..80
MIN_SIZE = 475 MIN_SIZE = 475
MAX_IMAGE_SIZE = 1.megabyte MAX_IMAGE_SIZE = 1.megabyte
ACCEPTED_CONTENT_TYPE = %w(image/jpeg image/jpg).freeze ACCEPTED_CONTENT_TYPE = %w(image/jpeg image/jpg).freeze
@@ -23,7 +23,7 @@ class Image < ActiveRecord::Base
validate :attachment_presence validate :attachment_presence
validate :validate_attachment_content_type, if: -> { attachment.present? } validate :validate_attachment_content_type, if: -> { attachment.present? }
validate :validate_attachment_size, if: -> { attachment.present? } validate :validate_attachment_size, if: -> { attachment.present? }
validates :title, presence: true, length: { in: TITLE_LEGHT_RANGE } validates :title, presence: true, length: { in: TITLE_LENGTH_RANGE }
validates :user_id, presence: true validates :user_id, presence: true
validates :imageable_id, presence: true, if: -> { persisted? } validates :imageable_id, presence: true, if: -> { persisted? }
validates :imageable_type, presence: true, if: -> { persisted? } validates :imageable_type, presence: true, if: -> { persisted? }

View File

@@ -4,6 +4,13 @@ class Legislation::DraftVersion < ActiveRecord::Base
acts_as_paranoid column: :hidden_at acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases include ActsAsParanoidAliases
translates :title, touch: true
translates :changelog, touch: true
translates :body, touch: true
translates :body_html, touch: true
translates :toc_html, touch: true
globalize_accessors
belongs_to :process, class_name: 'Legislation::Process', foreign_key: 'legislation_process_id' belongs_to :process, class_name: 'Legislation::Process', foreign_key: 'legislation_process_id'
has_many :annotations, class_name: 'Legislation::Annotation', foreign_key: 'legislation_draft_version_id', dependent: :destroy has_many :annotations, class_name: 'Legislation::Annotation', foreign_key: 'legislation_draft_version_id', dependent: :destroy
@@ -19,10 +26,19 @@ class Legislation::DraftVersion < ActiveRecord::Base
renderer = Redcarpet::Render::HTML.new(with_toc_data: true) renderer = Redcarpet::Render::HTML.new(with_toc_data: true)
toc_renderer = Redcarpet::Render::HTML_TOC.new(with_toc_data: true) toc_renderer = Redcarpet::Render::HTML_TOC.new(with_toc_data: true)
if body_changed?
self.body_html = Redcarpet::Markdown.new(renderer).render(body) self.body_html = Redcarpet::Markdown.new(renderer).render(body)
self.toc_html = Redcarpet::Markdown.new(toc_renderer).render(body) self.toc_html = Redcarpet::Markdown.new(toc_renderer).render(body)
end end
translations.each do |translation|
if translation.body_changed?
translation.body_html = Redcarpet::Markdown.new(renderer).render(translation.body)
translation.toc_html = Redcarpet::Markdown.new(toc_renderer).render(translation.body)
end
end
end
def display_title def display_title
status == 'draft' ? "#{title} *" : title status == 'draft' ? "#{title} *" : title
end end

View File

@@ -9,6 +9,12 @@ class Legislation::Process < ActiveRecord::Base
acts_as_paranoid column: :hidden_at acts_as_paranoid column: :hidden_at
acts_as_taggable_on :customs acts_as_taggable_on :customs
translates :title, touch: true
translates :summary, touch: true
translates :description, touch: true
translates :additional_info, touch: true
globalize_accessors
PHASES_AND_PUBLICATIONS = %i(debate_phase allegations_phase proposals_phase draft_publication result_publication).freeze PHASES_AND_PUBLICATIONS = %i(debate_phase allegations_phase proposals_phase draft_publication result_publication).freeze
has_many :draft_versions, -> { order(:id) }, class_name: 'Legislation::DraftVersion', has_many :draft_versions, -> { order(:id) }, class_name: 'Legislation::DraftVersion',

View File

@@ -11,6 +11,7 @@ class Legislation::Proposal < ActiveRecord::Base
include Communitable include Communitable
include Documentable include Documentable
include Notifiable include Notifiable
include Imageable
documentable max_documents_allowed: 3, documentable max_documents_allowed: 3,
max_file_size: 3.megabytes, max_file_size: 3.megabytes,
@@ -44,9 +45,15 @@ class Legislation::Proposal < ActiveRecord::Base
scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc) } scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc) }
scope :sort_by_created_at, -> { reorder(created_at: :desc) } scope :sort_by_created_at, -> { reorder(created_at: :desc) }
scope :sort_by_most_commented, -> { reorder(comments_count: :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_up: :desc) }
scope :sort_by_random, -> { reorder("RANDOM()") } scope :sort_by_random, -> { reorder("RANDOM()") }
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) } scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
scope :last_week, -> { where("proposals.created_at >= ?", 7.days.ago)} scope :last_week, -> { where("proposals.created_at >= ?", 7.days.ago)}
scope :selected, -> { where(selected: true) }
scope :random, -> { sort_by_random }
scope :winners, -> { selected.sort_by_confidence_score }
def to_param def to_param
"#{id}-#{title}".parameterize "#{id}-#{title}".parameterize

View File

@@ -3,6 +3,9 @@ class Legislation::Question < ActiveRecord::Base
include ActsAsParanoidAliases include ActsAsParanoidAliases
include Notifiable include Notifiable
translates :title, touch: true
globalize_accessors
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id' belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
belongs_to :process, class_name: 'Legislation::Process', foreign_key: 'legislation_process_id' belongs_to :process, class_name: 'Legislation::Process', foreign_key: 'legislation_process_id'
@@ -11,7 +14,7 @@ class Legislation::Question < ActiveRecord::Base
has_many :answers, class_name: 'Legislation::Answer', foreign_key: 'legislation_question_id', dependent: :destroy, inverse_of: :question has_many :answers, class_name: 'Legislation::Answer', foreign_key: 'legislation_question_id', dependent: :destroy, inverse_of: :question
has_many :comments, as: :commentable, dependent: :destroy has_many :comments, as: :commentable, dependent: :destroy
accepts_nested_attributes_for :question_options, reject_if: proc { |attributes| attributes[:value].blank? }, allow_destroy: true accepts_nested_attributes_for :question_options, reject_if: proc { |attributes| attributes.all? { |k, v| v.blank? } }, allow_destroy: true
validates :process, presence: true validates :process, presence: true
validates :title, presence: true validates :title, presence: true

View File

@@ -2,6 +2,9 @@ class Legislation::QuestionOption < ActiveRecord::Base
acts_as_paranoid column: :hidden_at acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases include ActsAsParanoidAliases
translates :value, touch: true
globalize_accessors
belongs_to :question, class_name: 'Legislation::Question', foreign_key: 'legislation_question_id', inverse_of: :question_options belongs_to :question, class_name: 'Legislation::Question', foreign_key: 'legislation_question_id', inverse_of: :question_options
has_many :answers, class_name: 'Legislation::Answer', foreign_key: 'legislation_question_id', dependent: :destroy, inverse_of: :question has_many :answers, class_name: 'Legislation::Answer', foreign_key: 'legislation_question_id', dependent: :destroy, inverse_of: :question

View File

@@ -8,6 +8,9 @@ class Newsletter < ActiveRecord::Base
validates_format_of :from, :with => /@/ validates_format_of :from, :with => /@/
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases
def list_of_recipient_emails def list_of_recipient_emails
UserSegments.user_segment_emails(segment_recipient) if valid_segment_recipient? UserSegments.user_segment_emails(segment_recipient) if valid_segment_recipient?
end end
@@ -20,9 +23,47 @@ class Newsletter < ActiveRecord::Base
sent_at.nil? sent_at.nil?
end end
def deliver
run_at = first_batch_run_at
list_of_recipient_emails_in_batches.each do |recipient_emails|
recipient_emails.each do |recipient_email|
if valid_email?(recipient_email)
Mailer.delay(run_at: run_at).newsletter(self, recipient_email)
log_delivery(recipient_email)
end
end
run_at += batch_interval
end
end
def batch_size
10000
end
def batch_interval
20.minutes
end
def first_batch_run_at
Time.current
end
def list_of_recipient_emails_in_batches
list_of_recipient_emails.in_groups_of(batch_size, false)
end
private private
def validate_segment_recipient def validate_segment_recipient
errors.add(:segment_recipient, :invalid) unless valid_segment_recipient? errors.add(:segment_recipient, :invalid) unless valid_segment_recipient?
end end
def valid_email?(email)
email.match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i)
end
def log_delivery(recipient_email)
user = User.where(email: recipient_email).first
Activity.log(user, :email, self)
end
end end

View File

@@ -68,4 +68,29 @@ class Notification < ActiveRecord::Base
end end
end end
def self.send_pending
run_at = first_batch_run_at
User.email_digest.find_in_batches(batch_size: batch_size) do |users|
users.each do |user|
email_digest = EmailDigest.new(user)
email_digest.deliver(run_at)
end
run_at += batch_interval
end
end
private
def self.batch_size
10000
end
def self.batch_interval
20.minutes
end
def self.first_batch_run_at
Time.current
end
end end

View File

@@ -4,6 +4,11 @@ class Poll < ActiveRecord::Base
include ActsAsParanoidAliases include ActsAsParanoidAliases
include Notifiable include Notifiable
translates :name, touch: true
translates :summary, touch: true
translates :description, touch: true
globalize_accessors
RECOUNT_DURATION = 1.week RECOUNT_DURATION = 1.week
has_many :booth_assignments, class_name: "Poll::BoothAssignment" has_many :booth_assignments, class_name: "Poll::BoothAssignment"

View File

@@ -9,7 +9,9 @@ class Poll::Answer < ActiveRecord::Base
validates :author, presence: true validates :author, presence: true
validates :answer, presence: true validates :answer, presence: true
validates :answer, inclusion: { in: ->(a) { a.question.question_answers.pluck(:title) }}, validates :answer, inclusion: { in: ->(a) { a.question.question_answers
.joins(:translations)
.pluck("poll_question_answer_translations.title") }},
unless: ->(a) { a.question.blank? } unless: ->(a) { a.question.blank? }
scope :by_author, ->(author_id) { where(author_id: author_id) } scope :by_author, ->(author_id) { where(author_id: author_id) }

View File

@@ -10,7 +10,9 @@ class Poll::PartialResult < ActiveRecord::Base
validates :question, presence: true validates :question, presence: true
validates :author, presence: true validates :author, presence: true
validates :answer, presence: true validates :answer, presence: true
validates :answer, inclusion: { in: ->(a) { a.question.question_answers.pluck(:title) }}, validates :answer, inclusion: { in: ->(a) { a.question.question_answers
.joins(:translations)
.pluck("poll_question_answer_translations.title") }},
unless: ->(a) { a.question.blank? } unless: ->(a) { a.question.blank? }
validates :origin, inclusion: { in: VALID_ORIGINS } validates :origin, inclusion: { in: VALID_ORIGINS }

View File

@@ -5,6 +5,9 @@ class Poll::Question < ActiveRecord::Base
acts_as_paranoid column: :hidden_at acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases include ActsAsParanoidAliases
translates :title, touch: true
globalize_accessors
belongs_to :poll belongs_to :poll
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id' belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'

View File

@@ -1,6 +1,11 @@
class Poll::Question::Answer < ActiveRecord::Base class Poll::Question::Answer < ActiveRecord::Base
include Galleryable include Galleryable
include Documentable include Documentable
translates :title, touch: true
translates :description, touch: true
globalize_accessors
documentable max_documents_allowed: 3, documentable max_documents_allowed: 3,
max_file_size: 3.megabytes, max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ] accepted_content_types: [ "application/pdf" ]
@@ -15,7 +20,7 @@ class Poll::Question::Answer < ActiveRecord::Base
before_validation :set_order, on: :create before_validation :set_order, on: :create
def description def description
super.try :html_safe self[:description].try :html_safe
end end
def self.order_answers(ordered_array) def self.order_answers(ordered_array)

View File

@@ -223,7 +223,7 @@ class Proposal < ActiveRecord::Base
end end
def users_to_notify def users_to_notify
(voters + followers).uniq (voters + followers).uniq - [author]
end end
def self.proposals_orders(user) def self.proposals_orders(user)

View File

@@ -37,6 +37,11 @@ class ProposalNotification < ActiveRecord::Base
proposal proposal
end end
def moderate_system_email(moderator)
Notification.where(notifiable_type: 'ProposalNotification', notifiable: self).destroy_all
Activity.log(moderator, :hide, self)
end
def ignore_flag def ignore_flag
update(ignored_at: Time.current) update(ignored_at: Time.current)
end end

View File

@@ -1,5 +1,5 @@
class SiteCustomization::ContentBlock < ActiveRecord::Base class SiteCustomization::ContentBlock < ActiveRecord::Base
VALID_BLOCKS = %w(top_links footer) VALID_BLOCKS = %w(top_links footer subnavigation_left subnavigation_right)
validates :locale, presence: true, inclusion: { in: I18n.available_locales.map(&:to_s) } validates :locale, presence: true, inclusion: { in: I18n.available_locales.map(&:to_s) }
validates :name, presence: true, uniqueness: { scope: :locale }, inclusion: { in: VALID_BLOCKS } validates :name, presence: true, uniqueness: { scope: :locale }, inclusion: { in: VALID_BLOCKS }

View File

@@ -1,7 +1,7 @@
class SiteCustomization::Image < ActiveRecord::Base class SiteCustomization::Image < ActiveRecord::Base
VALID_IMAGES = { VALID_IMAGES = {
"icon_home" => [330, 240], "icon_home" => [330, 240],
"logo_header" => [80, 80], "logo_header" => [260, 80],
"social_media_icon" => [470, 246], "social_media_icon" => [470, 246],
"social_media_icon_twitter" => [246, 246], "social_media_icon_twitter" => [246, 246],
"apple-touch-icon-200" => [200, 200] "apple-touch-icon-200" => [200, 200]

View File

@@ -6,11 +6,15 @@ class SiteCustomization::Page < ActiveRecord::Base
format: { with: /\A[0-9a-zA-Z\-_]*\Z/, message: :slug_format } format: { with: /\A[0-9a-zA-Z\-_]*\Z/, message: :slug_format }
validates :title, presence: true validates :title, presence: true
validates :status, presence: true, inclusion: { in: VALID_STATUSES } validates :status, presence: true, inclusion: { in: VALID_STATUSES }
validates :locale, presence: true
translates :title, touch: true
translates :subtitle, touch: true
translates :content, touch: true
globalize_accessors
scope :published, -> { where(status: 'published').order('id DESC') } scope :published, -> { where(status: 'published').order('id DESC') }
scope :with_more_info_flag, -> { where(status: 'published', more_info_flag: true).order('id ASC') } scope :with_more_info_flag, -> { where(status: 'published', more_info_flag: true).order('id ASC') }
scope :with_same_locale, -> { where(locale: I18n.locale).order('id ASC') } scope :with_same_locale, -> { joins(:translations).where("site_customization_page_translations.locale": I18n.locale) }
def url def url
"/#{slug}" "/#{slug}"

View File

@@ -182,6 +182,7 @@ class User < ActiveRecord::Base
debates_ids = Debate.where(author_id: id).pluck(:id) debates_ids = Debate.where(author_id: id).pluck(:id)
comments_ids = Comment.where(user_id: id).pluck(:id) comments_ids = Comment.where(user_id: id).pluck(:id)
proposal_ids = Proposal.where(author_id: id).pluck(:id) proposal_ids = Proposal.where(author_id: id).pluck(:id)
investment_ids = Budget::Investment.where(author_id: id).pluck(:id)
proposal_notification_ids = ProposalNotification.where(author_id: id).pluck(:id) proposal_notification_ids = ProposalNotification.where(author_id: id).pluck(:id)
hide hide
@@ -189,6 +190,7 @@ class User < ActiveRecord::Base
Debate.hide_all debates_ids Debate.hide_all debates_ids
Comment.hide_all comments_ids Comment.hide_all comments_ids
Proposal.hide_all proposal_ids Proposal.hide_all proposal_ids
Budget::Investment.hide_all investment_ids
ProposalNotification.hide_all proposal_notification_ids ProposalNotification.hide_all proposal_notification_ids
end end
@@ -327,6 +329,10 @@ class User < ActiveRecord::Base
where(conditions.to_hash).where(["username = ?", login]).first where(conditions.to_hash).where(["username = ?", login]).first
end end
def self.find_by_manager_login(manager_login)
find_by(id: manager_login.split("_").last)
end
def interests def interests
followables = follows.map(&:followable) followables = follows.map(&:followable)
followables.compact.map { |followable| followable.tags.map(&:name) }.flatten.compact.uniq followables.compact.map { |followable| followable.tags.map(&:name) }.flatten.compact.uniq

View File

@@ -1,8 +1,15 @@
class Widget::Card < ActiveRecord::Base class Widget::Card < ActiveRecord::Base
include Imageable include Imageable
# table_name must be set before calls to 'translates'
self.table_name = "widget_cards" self.table_name = "widget_cards"
translates :label, touch: true
translates :title, touch: true
translates :description, touch: true
translates :link_text, touch: true
globalize_accessors
def self.header def self.header
where(header: true) where(header: true)
end end

View File

@@ -7,7 +7,7 @@
<strong><%= t("admin.menu.title_polls") %></strong> <strong><%= t("admin.menu.title_polls") %></strong>
</a> </a>
<ul id="polls_menu" <%= "class=is-active" if menu_polls? || controller.class.parent == Admin::Poll::Questions::Answers %>> <ul id="polls_menu" <%= "class=is-active" if menu_polls? || controller.class.parent == Admin::Poll::Questions::Answers %>>
<li <%= "class=is-active" if controller_name == "polls" && action_name != "booth_assignments" %>> <li <%= "class=is-active" if %w(polls recounts results).include?(controller_name) %>>
<%= link_to t("admin.menu.polls"), admin_polls_path %> <%= link_to t("admin.menu.polls"), admin_polls_path %>
</li> </li>
@@ -25,7 +25,7 @@
<strong><%= t("admin.menu.title_booths") %></strong> <strong><%= t("admin.menu.title_booths") %></strong>
</a> </a>
<ul id="booths_menu" <%= "class=is-active" if menu_booths? || controller_name == "polls" && action_name == "booth_assignments" %>> <ul id="booths_menu" <%= "class=is-active" if menu_booths? || controller_name == "polls" && action_name == "booth_assignments" %>>
<li <%= "class=is-active" if controller_name == "officers" %>> <li <%= "class=is-active" if %w(officers officer_assignments).include?(controller_name) %>>
<%= link_to t("admin.menu.poll_officers"), admin_officers_path %> <%= link_to t("admin.menu.poll_officers"), admin_officers_path %>
</li> </li>
@@ -35,12 +35,12 @@
</li> </li>
<li <%= "class=is-active" if (controller_name == "polls" && action_name == "booth_assignments") || <li <%= "class=is-active" if (controller_name == "polls" && action_name == "booth_assignments") ||
(controller_name == "booth_assignments" && action_name == "manage") %>> controller_name == "booth_assignments" %>>
<%= link_to t("admin.menu.poll_booth_assignments"), booth_assignments_admin_polls_path %> <%= link_to t("admin.menu.poll_booth_assignments"), booth_assignments_admin_polls_path %>
</li> </li>
<li <%= "class=is-active" if %w(shifts booths).include?(controller_name) && <li <%= "class=is-active" if %w(shifts booths).include?(controller_name) &&
action_name == "available" %>> %w(available new).include?(action_name) %>>
<%= link_to t("admin.menu.poll_shifts"), available_admin_booths_path %> <%= link_to t("admin.menu.poll_shifts"), available_admin_booths_path %>
</li> </li>
</ul> </ul>
@@ -79,21 +79,24 @@
</li> </li>
<% end %> <% end %>
<% newsletters_notifications_sections = %w(newsletters emails_download admin_notifications) %> <% messages_sections = %w(newsletters emails_download admin_notifications system_emails) %>
<% newsletters_menu_active = newsletters_notifications_sections.include?(controller_name) %> <% messages_menu_active = messages_sections.include?(controller_name) %>
<li class="section-title" <%= "class=active" if newsletters_menu_active %>> <li class="section-title" <%= "class=is-active" if messages_menu_active %>>
<a href="#"> <a href="#">
<span class="icon-zip"></span> <span class="icon-zip"></span>
<strong><%= t("admin.menu.newsletters_and_notifications") %></strong> <strong><%= t("admin.menu.messaging_users") %></strong>
</a> </a>
<ul id="newsletters_and_notifications_menu" <%= "class=is-active" if newsletters_menu_active %>> <ul id="messaging_users_menu" <%= "class=is-active" if messages_menu_active %>>
<li <%= "class=active" if controller_name == "newsletters" %>> <li <%= "class=is-active" if controller_name == "newsletters" %>>
<%= link_to t("admin.menu.newsletters"), admin_newsletters_path %> <%= link_to t("admin.menu.newsletters"), admin_newsletters_path %>
</li> </li>
<li <%= "class=active" if controller_name == "admin_notifications" %>> <li <%= "class=is-active" if controller_name == "admin_notifications" %>>
<%= link_to t("admin.menu.admin_notifications"), admin_admin_notifications_path %> <%= link_to t("admin.menu.admin_notifications"), admin_admin_notifications_path %>
</li> </li>
<li <%= "class=active" if controller_name == "emails_download" %>> <li <%= "class=is-active" if controller_name == "system_emails" %>>
<%= link_to t("admin.menu.system_emails"), admin_system_emails_path %>
</li>
<li <%= "class=is-active" if controller_name == "emails_download" %>>
<%= link_to t("admin.menu.emails_download"), admin_emails_download_index_path %> <%= link_to t("admin.menu.emails_download"), admin_emails_download_index_path %>
</li> </li>
</ul> </ul>
@@ -107,7 +110,7 @@
<ul <%= "class=is-active" if menu_customization? && <ul <%= "class=is-active" if menu_customization? &&
controller.class.parent != Admin::Poll::Questions::Answers %>> controller.class.parent != Admin::Poll::Questions::Answers %>>
<li <%= "class=active" if menu_homepage? %>> <li <%= "class=is-active" if menu_homepage? %>>
<%= link_to t("admin.menu.site_customization.homepage"), admin_homepage_path %> <%= link_to t("admin.menu.site_customization.homepage"), admin_homepage_path %>
</li> </li>
@@ -118,6 +121,10 @@
<li <%= "class=is-active" if controller_name == "banners" %>> <li <%= "class=is-active" if controller_name == "banners" %>>
<%= link_to t("admin.menu.banner"), admin_banners_path %> <%= link_to t("admin.menu.banner"), admin_banners_path %>
</li> </li>
<li <%= "class=is-active" if controller_name == "information_texts" %>>
<%= link_to t("admin.menu.site_customization.information_texts"), admin_site_customization_information_texts_path %>
</li>
</ul> </ul>
</li> </li>
@@ -148,6 +155,12 @@
</li> </li>
<% end %> <% end %>
<% if feature?(:budgets) %>
<li <%= "class=is-active" if controller_name == "hidden_budget_investments" %>>
<%= link_to t("admin.menu.hidden_budget_investments"), admin_hidden_budget_investments_path %>
</li>
<% end %>
<li <%= "class=is-active" if controller_name == "comments" %>> <li <%= "class=is-active" if controller_name == "comments" %>>
<%= link_to t("admin.menu.hidden_comments"), admin_comments_path %> <%= link_to t("admin.menu.hidden_comments"), admin_comments_path %>
</li> </li>

View File

@@ -33,12 +33,16 @@
<%= activity.actionable.username %> (<%= activity.actionable.email %>) <%= activity.actionable.username %> (<%= activity.actionable.email %>)
<% when "Comment" %> <% when "Comment" %>
<%= activity.actionable.body %> <%= activity.actionable.body %>
<% when "Newsletter" %>
<strong><%= activity.actionable.subject %></strong>
<% when "ProposalNotification" %>
<strong><%= activity.actionable.title %></strong>
<br>
<%= activity.actionable.body %>
<% else %> <% else %>
<strong><%= activity.actionable.title %></strong> <strong><%= activity.actionable.title %></strong>
<br> <br>
<div class="proposal-description">
<%= activity.actionable.description %> <%= activity.actionable.description %>
</div>
<% end %> <% end %>
<td class="align-top"> <td class="align-top">
<%= activity.user.name %> (<%= activity.user.email %>) <%= activity.user.name %> (<%= activity.user.email %>)

View File

@@ -1,13 +1,19 @@
<%= form_for [:admin, @admin_notification] do |f| %> <%= render "admin/shared/globalize_locales", resource: @admin_notification %>
<%= translatable_form_for [:admin, @admin_notification] do |f| %>
<%= render 'shared/errors', resource: @admin_notification %> <%= render 'shared/errors', resource: @admin_notification %>
<%= f.select :segment_recipient, options_for_select(user_segments_options, <%= f.select :segment_recipient, options_for_select(user_segments_options,
@admin_notification[:segment_recipient]) %> @admin_notification[:segment_recipient]) %>
<%= f.text_field :title %>
<%= f.translatable_text_field :title %>
<%= f.text_field :link %> <%= f.text_field :link %>
<%= f.text_area :body %>
<%= f.translatable_text_area :body %>
<div class="margin-top"> <div class="margin-top">
<%= f.submit class: "button success" %> <%= f.submit t("admin.admin_notifications.#{admin_submit_action(@admin_notification)}.submit_button"),
class: "button success" %>
</div> </div>
<% end %> <% end %>

View File

@@ -9,7 +9,7 @@
<th><%= t("admin.admin_notifications.index.title") %></th> <th><%= t("admin.admin_notifications.index.title") %></th>
<th><%= t("admin.admin_notifications.index.segment_recipient") %></th> <th><%= t("admin.admin_notifications.index.segment_recipient") %></th>
<th><%= t("admin.admin_notifications.index.sent") %></th> <th><%= t("admin.admin_notifications.index.sent") %></th>
<th class="small-5 text-right"><%= t("admin.admin_notifications.index.actions") %></th> <th class="small-5"><%= t("admin.admin_notifications.index.actions") %></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -28,21 +28,29 @@
<%= l admin_notification.sent_at.to_date %> <%= l admin_notification.sent_at.to_date %>
<% end %> <% end %>
</td> </td>
<td class="text-right"> <td>
<% if admin_notification.draft? %> <% if admin_notification.draft? %>
<div class="small-4 column">
<%= link_to t("admin.admin_notifications.index.edit"), <%= link_to t("admin.admin_notifications.index.edit"),
edit_admin_admin_notification_path(admin_notification), edit_admin_admin_notification_path(admin_notification),
method: :get, class: "button hollow" %> method: :get, class: "button expanded hollow" %>
</div>
<div class="small-4 column">
<%= link_to t("admin.admin_notifications.index.delete"), <%= link_to t("admin.admin_notifications.index.delete"),
admin_admin_notification_path(admin_notification), admin_admin_notification_path(admin_notification),
method: :delete, class: "button hollow alert" %> method: :delete, class: "button expanded hollow alert" %>
</div>
<div class="small-4 column">
<%= link_to t("admin.admin_notifications.index.preview"), <%= link_to t("admin.admin_notifications.index.preview"),
admin_admin_notification_path(admin_notification), admin_admin_notification_path(admin_notification),
class: "button" %> class: "button expanded" %>
</div>
<% else %> <% else %>
<div class="small-4 column">
<%= link_to t("admin.admin_notifications.index.view"), <%= link_to t("admin.admin_notifications.index.view"),
admin_admin_notification_path(admin_notification), admin_admin_notification_path(admin_notification),
class: "button" %> class: "button expanded" %>
</div>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -2,9 +2,7 @@
<h2><%= t("admin.admin_notifications.show.section_title") %></h2> <h2><%= t("admin.admin_notifications.show.section_title") %></h2>
<div class="small-12 column"> <div class="small-12 column callout highlight">
<div class="callout highlight">
<div class="row">
<div class="small-12 medium-6 column"> <div class="small-12 medium-6 column">
<strong><%= t("admin.admin_notifications.show.sent_at") %></strong><br> <strong><%= t("admin.admin_notifications.show.sent_at") %></strong><br>
<% if @admin_notification.draft? %> <% if @admin_notification.draft? %>
@@ -13,23 +11,22 @@
<%= l(@admin_notification.sent_at.to_date) %> <%= l(@admin_notification.sent_at.to_date) %>
<% end %> <% end %>
</div> </div>
<div class="small-12 medium-6 column"> <div class="small-12 medium-6 column">
<strong><%= t("admin.admin_notifications.show.title") %></strong><br> <strong><%= t("admin.admin_notifications.show.title") %></strong><br>
<%= @admin_notification.title %> <%= @admin_notification.title %>
</div> </div>
</div>
<div class="row">
<div class="small-12 medium-6 column"> <div class="small-12 medium-6 column">
<strong><%= t("admin.admin_notifications.show.body") %></strong><br> <strong><%= t("admin.admin_notifications.show.body") %></strong><br>
<%= @admin_notification.body %> <%= @admin_notification.body %>
</div> </div>
<div class="small-12 medium-6 column"> <div class="small-12 medium-6 column">
<strong><%= t("admin.admin_notifications.show.link") %></strong><br> <strong><%= t("admin.admin_notifications.show.link") %></strong><br>
<%= @admin_notification.link %> <%= @admin_notification.link %>
</div> </div>
</div>
<div class="row">
<div class="small-12 column"> <div class="small-12 column">
<strong><%= t("admin.admin_notifications.show.segment_recipient") %></strong><br> <strong><%= t("admin.admin_notifications.show.segment_recipient") %></strong><br>
<%= segment_name(@admin_notification.segment_recipient) %> <%= segment_name(@admin_notification.segment_recipient) %>
@@ -42,7 +39,6 @@
<% end %> <% end %>
</div> </div>
</div> </div>
</div>
<p class="help-text" id="phase-description-help-text"> <p class="help-text" id="phase-description-help-text">
<% if @admin_notification.draft? %> <% if @admin_notification.draft? %>
@@ -65,13 +61,15 @@
</ul> </ul>
</div> </div>
<hr> <hr>
</div>
<% if @admin_notification.draft? && @admin_notification.valid_segment_recipient? %> <% if @admin_notification.draft? && @admin_notification.valid_segment_recipient? %>
<div class="small-12 medium-6 large-3 column end">
<%= link_to t("admin.admin_notifications.show.send"), <%= link_to t("admin.admin_notifications.show.send"),
deliver_admin_admin_notification_path(@admin_notification), deliver_admin_admin_notification_path(@admin_notification),
"data-alert": t("admin.admin_notifications.show.send_alert", "data-alert": t("admin.admin_notifications.show.send_alert",
n: @admin_notification.list_of_recipients_count), n: @admin_notification.list_of_recipients_count),
method: :post, method: :post,
id: "js-send-admin_notification-alert", id: "js-send-admin_notification-alert",
class: "button success" %> class: "button success expanded" %>
</div>
<% end %> <% end %>

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