Merge branch 'master' into feature/3153

This commit is contained in:
Javier Martín
2019-02-15 16:25:50 +01:00
committed by GitHub
1259 changed files with 48644 additions and 16791 deletions

View File

@@ -35,3 +35,6 @@ Metrics/LineLength:
RSpec/NotToNot:
Enabled: true
Style/StringLiterals:
EnforcedStyle: double_quotes

View File

@@ -175,7 +175,7 @@ linters:
StringQuotes:
enabled: true
style: single_quotes
style: double_quotes
TrailingSemicolon:
enabled: true

View File

@@ -3,6 +3,30 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
## [0.18.1](https://github.com/consul/consul/tree/v0.18.1) (2019-01-17)
### Added
- **Legislation:** Legislation process homepage phase [\#3188](https://github.com/consul/consul/pull/3188)
- **Legislation:** Show documents on processes proposals phase [\#3136](https://github.com/consul/consul/pull/3136)
- **Maintenance-Refactorings:** Remove semicolons from controllers [\#3160](https://github.com/consul/consul/pull/3160)
- **Maintenance-Refactorings:** Remove before action not used [\#3167](https://github.com/consul/consul/pull/3167)
- **Maintenance-Rubocop:** Enable double quotes rubocop rule [\#3175](https://github.com/consul/consul/pull/3175)
- **Maintenance-Rubocop:** Enable line length rubocop rule [\#3165](https://github.com/consul/consul/pull/3165)
- **Maintenance-Rubocop:** Add rubocop rule to indent private methods [\#3134](https://github.com/consul/consul/pull/3134)
### Changed
- **Admin:** Improve CRUD budgets and content blocks [\#3173](https://github.com/consul/consul/pull/3173)
- **Design/UX:** new CRUD budgets, content blocks and heading map [\#3150](https://github.com/consul/consul/pull/3150)
- **Design/UX:** Processes key dates [\#3137](https://github.com/consul/consul/pull/3137)
### Fixed
- **Admin:** checks for deleted proposals [\#3154](https://github.com/consul/consul/pull/3154)
- **Admin:** Add default order for admin budget investments list [\#3151](https://github.com/consul/consul/pull/3151)
- **Budgets:** Bug Management Cannot create Budget Investment without a map location [\#3133](https://github.com/consul/consul/pull/3133)
## [0.18.0](https://github.com/consul/consul/compare/v0.17...v0.18) (2018-12-27)
### Added
@@ -47,6 +71,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- **Admin:** Improve visualization for small resolution [\#3025](https://github.com/consul/consul/pull/3025)
- **Admin:** Budgets admin [\#3012](https://github.com/consul/consul/pull/3012)
- **Budgets:** Budget investments social share [\#3053](https://github.com/consul/consul/pull/3053)
- **Design/UX:** Documents title [\#3131](https://github.com/consul/consul/pull/3131)
- **Design/UX:** Proposal create question [\#3122](https://github.com/consul/consul/pull/3122)
- **Design/UX:** Budget investments price explanation [\#3121](https://github.com/consul/consul/pull/3121)
- **Design/UX:** Change CRUD for budget groups and headings [\#3106](https://github.com/consul/consul/pull/3106)

View File

@@ -54,6 +54,7 @@ gem 'unicorn', '~> 5.4.1'
gem 'whenever', '~> 0.10.0', require: false
gem 'globalize', '~> 5.0.0'
gem 'globalize-accessors', '~> 0.2.1'
gem 'recipient_interceptor', '~> 0.2.0'
source 'https://rails-assets.org' do
gem 'rails-assets-leaflet'

View File

@@ -356,6 +356,8 @@ GEM
rainbow (3.0.0)
raindrops (0.19.0)
rake (12.3.1)
recipient_interceptor (0.2.0)
mail
redcarpet (3.4.0)
referer-parser (0.3.0)
request_store (1.3.2)
@@ -551,6 +553,7 @@ DEPENDENCIES
rails (= 4.2.11)
rails-assets-leaflet!
rails-assets-markdown-it (~> 8.2.1)!
recipient_interceptor (~> 0.2.0)
redcarpet (~> 3.4.0)
responders (~> 2.4.0)
rinku (~> 2.0.2)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

@@ -23,8 +23,36 @@ App.Forms =
false
)
synchronizeInputs: ->
progress_bar = "[name='progress_bar[percentage]']"
process_background = "[name='legislation_process[background_color]']"
process_font = "[name='legislation_process[font_color]']"
inputs = $("#{progress_bar}, #{process_background}, #{process_font}")
inputs.on
input: ->
$("[name='#{this.name}']").val($(this).val())
inputs.trigger("input")
hideOrShowFieldsAfterSelection: ->
$("[name='progress_bar[kind]']").on
change: ->
title_field = $("[name^='progress_bar'][name$='[title]']").parent()
if this.value == "primary"
title_field.hide()
$("#globalize_locales").hide()
else
title_field.show()
$("#globalize_locales").show()
$("[name='progress_bar[kind]']").change()
initialize: ->
App.Forms.disableEnter()
App.Forms.submitOnChange()
App.Forms.toggleLink()
App.Forms.synchronizeInputs()
App.Forms.hideOrShowFieldsAfterSelection()
false

View File

@@ -8,7 +8,7 @@ App.Tags =
unless $this.data('initialized') is 'yes'
$this.on('click', ->
name = $(this).text()
name = '"' + $(this).text() + '"'
current_tags = $tag_input.val().split(',').filter(Boolean)
if $.inArray(name, current_tags) >= 0

View File

@@ -71,7 +71,7 @@ $color-alert: #a94442;
$black: #222;
$white: #fff;
$body-font-family: 'Source Sans Pro', 'Helvetica', 'Arial', sans-serif !important;
$body-font-family: "Source Sans Pro", "Helvetica", "Arial", sans-serif !important;
$header-font-family: $body-font-family;
@@ -79,24 +79,24 @@ $global-radius: rem-calc(3);
$button-radius: $global-radius;
$font-family-serif: Georgia, 'Times New Roman', Times, serif;
$font-family-serif: Georgia, "Times New Roman", Times, serif;
$header-styles: (
small: (
'h1': ('font-size': 34),
'h2': ('font-size': 24),
'h3': ('font-size': 20),
'h4': ('font-size': 18),
'h5': ('font-size': 16),
'h6': ('font-size': 14),
"h1": ("font-size": 34),
"h2": ("font-size": 24),
"h3": ("font-size": 20),
"h4": ("font-size": 18),
"h5": ("font-size": 16),
"h6": ("font-size": 14),
),
medium: (
'h1': ('font-size': 44),
'h2': ('font-size': 34),
'h3': ('font-size': 24),
'h4': ('font-size': 19),
'h5': ('font-size': 16),
'h6': ('font-size': 13),
"h1": ("font-size": 44),
"h2": ("font-size": 34),
"h3": ("font-size": 24),
"h4": ("font-size": 19),
"h5": ("font-size": 16),
"h6": ("font-size": 13),
),
);

View File

@@ -60,7 +60,7 @@
// 55. Top Bar
// 56. Xy Grid
@import 'util/util';
@import "util/util";
// 1. Global
// ---------
@@ -82,7 +82,7 @@ $black: #0a0a0a;
$white: #fefefe;
$body-background: $white;
$body-font-color: $black;
$body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;
$body-font-family: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif;
$body-antialiased: true;
$global-margin: 1rem;
$global-padding: 1rem;
@@ -124,7 +124,7 @@ $grid-column-gutter: (
medium: 30px,
);
$grid-column-align-edge: true;
$grid-column-alias: 'columns';
$grid-column-alias: "columns";
$block-grid-max: 8;
// 4. Base Typography
@@ -133,26 +133,26 @@ $block-grid-max: 8;
$header-font-family: $body-font-family;
$header-font-weight: $global-weight-normal;
$header-font-style: normal;
$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace;
$font-family-monospace: Consolas, "Liberation Mono", Courier, monospace;
$header-color: inherit;
$header-lineheight: 1.4;
$header-margin-bottom: 0.5rem;
$header-styles: (
small: (
'h1': ('font-size': 24),
'h2': ('font-size': 20),
'h3': ('font-size': 19),
'h4': ('font-size': 18),
'h5': ('font-size': 17),
'h6': ('font-size': 16),
"h1": ("font-size": 24),
"h2": ("font-size": 20),
"h3": ("font-size": 19),
"h4": ("font-size": 18),
"h5": ("font-size": 17),
"h6": ("font-size": 16),
),
medium: (
'h1': ('font-size': 48),
'h2': ('font-size': 40),
'h3': ('font-size': 31),
'h4': ('font-size': 25),
'h5': ('font-size': 20),
'h6': ('font-size': 16),
"h1": ("font-size": 48),
"h2": ("font-size": 40),
"h3": ("font-size": 31),
"h4": ("font-size": 25),
"h5": ("font-size": 20),
"h6": ("font-size": 16),
),
);
$header-text-rendering: optimizeLegibility;
@@ -188,7 +188,7 @@ $blockquote-padding: rem-calc(9 20 0 19);
$blockquote-border: 1px solid $medium-gray;
$cite-font-size: rem-calc(13);
$cite-color: $dark-gray;
$cite-pseudo-content: '\2014 \0020';
$cite-pseudo-content: "\2014 \0020";
$keystroke-font: $font-family-monospace;
$keystroke-color: $black;
$keystroke-background: $light-gray;
@@ -271,8 +271,8 @@ $breadcrumbs-item-color-disabled: $medium-gray;
$breadcrumbs-item-margin: 0.75rem;
$breadcrumbs-item-uppercase: true;
$breadcrumbs-item-separator: true;
$breadcrumbs-item-separator-item: '/';
$breadcrumbs-item-separator-item-rtl: '\\';
$breadcrumbs-item-separator-item: "/";
$breadcrumbs-item-separator-item-rtl: "\\";
$breadcrumbs-item-separator-color: $medium-gray;
// 11. Button
@@ -305,7 +305,7 @@ $button-transition: background-color 0.25s ease-out, color 0.25s ease-out;
$buttongroup-margin: 1rem;
$buttongroup-spacing: 1px;
$buttongroup-child-selector: '.button';
$buttongroup-child-selector: ".button";
$buttongroup-expand-max: 6;
$buttongroup-radius-on-each: true;
@@ -511,7 +511,7 @@ $offcanvas-transition-length: 0.5s;
$offcanvas-transition-timing: ease;
$offcanvas-fixed-reveal: true;
$offcanvas-exit-background: rgba($white, 0.25);
$maincontent-class: 'off-canvas-content';
$maincontent-class: "off-canvas-content";
// 26. Orbit
// ---------

View File

@@ -251,6 +251,13 @@ $sidebar-active: #f4fcd0;
max-width: none;
}
form {
.input-group-label {
height: $line-height * 2;
}
}
.menu.simple {
margin-bottom: $line-height / 2;
@@ -424,7 +431,7 @@ $sidebar-active: #f4fcd0;
> a::after {
border: 0;
content: '\61' !important;
content: "\61" !important;
font-family: "icons" !important;
height: auto;
position: absolute !important;
@@ -1106,7 +1113,7 @@ table {
}
.map-icon::after {
content: '';
content: "";
width: 14px;
height: 14px;
margin: 8px 0 0 8px;
@@ -1166,7 +1173,7 @@ table {
&.enabled::before,
&.disabled::before {
font-family: 'icons';
font-family: "icons";
left: 0;
position: absolute;
}
@@ -1176,7 +1183,7 @@ table {
&::before {
color: $check;
content: '\6c';
content: "\6c";
}
}
@@ -1185,7 +1192,7 @@ table {
&::before {
color: #000;
content: '\76';
content: "\76";
}
}
}

View File

@@ -14,7 +14,7 @@
}
.annotator-adder {
background-image: image-url('annotator_adder.png');
background-image: image-url("annotator_adder.png");
margin-top: -52px;
}
@@ -43,7 +43,7 @@
.annotator-widget::after,
.annotator-editor.annotator-invert-y .annotator-widget::after {
background-image: image-url('annotator_items.png');
background-image: image-url("annotator_items.png");
}
.annotator-editor a,

View File

@@ -1,22 +1,23 @@
@import 'social-share-button';
@import 'foundation_and_overrides';
@import 'fonts';
@import 'icons';
@import 'mixins';
@import 'admin';
@import 'layout';
@import 'participation';
@import 'pages';
@import 'legislation';
@import 'legislation_process';
@import 'community';
@import 'custom';
@import 'c3';
@import 'annotator.min';
@import 'annotator_overrides';
@import 'jquery-ui/datepicker';
@import 'datepicker_overrides';
@import 'jquery-ui/autocomplete';
@import 'autocomplete_overrides';
@import 'jquery-ui/sortable';
@import 'leaflet';
@import "social-share-button";
@import "foundation_and_overrides";
@import "fonts";
@import "icons";
@import "mixins";
@import "admin";
@import "layout";
@import "participation";
@import "milestones";
@import "pages";
@import "legislation";
@import "legislation_process";
@import "community";
@import "custom";
@import "c3";
@import "annotator.min";
@import "annotator_overrides";
@import "jquery-ui/datepicker";
@import "datepicker_overrides";
@import "jquery-ui/autocomplete";
@import "autocomplete_overrides";
@import "jquery-ui/sortable";
@import "leaflet";

View File

@@ -51,11 +51,11 @@
}
.ui-datepicker-prev::after {
content: '\62';
content: "\62";
}
.ui-datepicker-next::after {
content: '\63';
content: "\63";
}
table {

View File

@@ -8,88 +8,88 @@
// - - - - - - - - - - - - - - - - - - - - - - - - -
@font-face {
font-family: 'Source Sans Pro';
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 300;
src: font-url('sourcesanspro-light-webfont.eot');
src: font-url('sourcesanspro-light-webfont.eot?#iefix') format('embedded-opentype'),
font-url('sourcesanspro-light-webfont.woff2') format('woff2'),
font-url('sourcesanspro-light-webfont.woff') format('woff'),
font-url('sourcesanspro-light-webfont.ttf') format('truetype'),
font-url('sourcesanspro-light-webfont.svg#source_sans_prolight') format('svg');
src: font-url("sourcesanspro-light-webfont.eot");
src: font-url("sourcesanspro-light-webfont.eot?#iefix") format("embedded-opentype"),
font-url("sourcesanspro-light-webfont.woff2") format("woff2"),
font-url("sourcesanspro-light-webfont.woff") format("woff"),
font-url("sourcesanspro-light-webfont.ttf") format("truetype"),
font-url("sourcesanspro-light-webfont.svg#source_sans_prolight") format("svg");
}
@font-face {
font-family: 'Source Sans Pro';
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
src: font-url('sourcesanspro-regular-webfont.eot');
src: font-url('sourcesanspro-regular-webfont.eot?#iefix') format('embedded-opentype'),
font-url('sourcesanspro-regular-webfont.woff2') format('woff2'),
font-url('sourcesanspro-regular-webfont.woff') format('woff'),
font-url('sourcesanspro-regular-webfont.ttf') format('truetype'),
font-url('sourcesanspro-regular-webfont.svg#source_sans_proregular') format('svg');
src: font-url("sourcesanspro-regular-webfont.eot");
src: font-url("sourcesanspro-regular-webfont.eot?#iefix") format("embedded-opentype"),
font-url("sourcesanspro-regular-webfont.woff2") format("woff2"),
font-url("sourcesanspro-regular-webfont.woff") format("woff"),
font-url("sourcesanspro-regular-webfont.ttf") format("truetype"),
font-url("sourcesanspro-regular-webfont.svg#source_sans_proregular") format("svg");
}
@font-face {
font-family: 'Source Sans Pro';
font-family: "Source Sans Pro";
font-style: italic;
font-weight: 400;
src: font-url('sourcesanspro-italic-webfont.eot');
src: font-url('sourcesanspro-italic-webfont.eot?#iefix') format('embedded-opentype'),
font-url('sourcesanspro-italic-webfont.woff2') format('woff2'),
font-url('sourcesanspro-italic-webfont.woff') format('woff'),
font-url('sourcesanspro-italic-webfont.ttf') format('truetype'),
font-url('sourcesanspro-italic-webfont.svg#source_sans_proitalic') format('svg');
src: font-url("sourcesanspro-italic-webfont.eot");
src: font-url("sourcesanspro-italic-webfont.eot?#iefix") format("embedded-opentype"),
font-url("sourcesanspro-italic-webfont.woff2") format("woff2"),
font-url("sourcesanspro-italic-webfont.woff") format("woff"),
font-url("sourcesanspro-italic-webfont.ttf") format("truetype"),
font-url("sourcesanspro-italic-webfont.svg#source_sans_proitalic") format("svg");
}
@font-face {
font-family: 'Source Sans Pro';
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 700;
src: font-url('sourcesanspro-bold-webfont.eot');
src: font-url('sourcesanspro-bold-webfont.eot?#iefix') format('embedded-opentype'),
font-url('sourcesanspro-bold-webfont.woff2') format('woff2'),
font-url('sourcesanspro-bold-webfont.woff') format('woff'),
font-url('sourcesanspro-bold-webfont.ttf') format('truetype'),
font-url('sourcesanspro-bold-webfont.svg#source_sans_probold') format('svg');
src: font-url("sourcesanspro-bold-webfont.eot");
src: font-url("sourcesanspro-bold-webfont.eot?#iefix") format("embedded-opentype"),
font-url("sourcesanspro-bold-webfont.woff2") format("woff2"),
font-url("sourcesanspro-bold-webfont.woff") format("woff"),
font-url("sourcesanspro-bold-webfont.ttf") format("truetype"),
font-url("sourcesanspro-bold-webfont.svg#source_sans_probold") format("svg");
}
// 02. Lato
// - - - - - - - - - - - - - - - - - - - - - - - - -
@font-face {
font-family: 'Lato';
src: font-url('lato-light.eot');
src: font-url('lato-light.eot?#iefix') format('embedded-opentype'),
font-url('lato-light.woff2') format('woff2'),
font-url('lato-light.woff') format('woff'),
font-url('lato-light.ttf') format('truetype'),
font-url('lato-light.svg#latolight') format('svg');
font-family: "Lato";
src: font-url("lato-light.eot");
src: font-url("lato-light.eot?#iefix") format("embedded-opentype"),
font-url("lato-light.woff2") format("woff2"),
font-url("lato-light.woff") format("woff"),
font-url("lato-light.ttf") format("truetype"),
font-url("lato-light.svg#latolight") format("svg");
font-weight: lighter;
font-style: normal;
}
@font-face {
font-family: 'Lato';
src: font-url('lato-regular.eot');
src: font-url('lato-regular.eot?#iefix') format('embedded-opentype'),
font-url('lato-regular.woff2') format('woff2'),
font-url('lato-regular.woff') format('woff'),
font-url('lato-regular.ttf') format('truetype'),
font-url('lato-regular.svg#latoregular') format('svg');
font-family: "Lato";
src: font-url("lato-regular.eot");
src: font-url("lato-regular.eot?#iefix") format("embedded-opentype"),
font-url("lato-regular.woff2") format("woff2"),
font-url("lato-regular.woff") format("woff"),
font-url("lato-regular.ttf") format("truetype"),
font-url("lato-regular.svg#latoregular") format("svg");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Lato';
src: font-url('lato-bold.eot');
src: font-url('lato-bold.eot?#iefix') format('embedded-opentype'),
font-url('lato-bold.woff2') format('woff2'),
font-url('lato-bold.woff') format('woff'),
font-url('lato-bold.ttf') format('truetype'),
font-url('lato-bold.svg#latobold') format('svg');
font-family: "Lato";
src: font-url("lato-bold.eot");
src: font-url("lato-bold.eot?#iefix") format("embedded-opentype"),
font-url("lato-bold.woff2") format("woff2"),
font-url("lato-bold.woff") format("woff"),
font-url("lato-bold.ttf") format("truetype"),
font-url("lato-bold.svg#latobold") format("svg");
font-weight: bold;
font-style: normal;
}

View File

@@ -1,11 +1,11 @@
@charset 'utf-8';
@charset "utf-8";
@import 'settings';
@import 'consul_settings';
@import 'custom_settings';
@import 'foundation';
@import "settings";
@import "consul_settings";
@import "custom_settings";
@import "foundation";
@import 'motion-ui/motion-ui';
@import "motion-ui/motion-ui";
@include foundation-global-styles;
// @include foundation-xy-grid-classes;

View File

@@ -1,12 +1,12 @@
@charset "UTF-8";
@font-face {
font-family: 'icons';
src: font-url('icons.eot');
src: font-url('icons.eot?#iefix') format('embedded-opentype'),
font-url('icons.woff') format('woff'),
font-url('icons.ttf') format('truetype'),
font-url('icons.svg#icons') format('svg');
font-family: "icons";
src: font-url("icons.eot");
src: font-url("icons.eot?#iefix") format("embedded-opentype"),
font-url("icons.woff") format("woff"),
font-url("icons.ttf") format("truetype"),
font-url("icons.svg#icons") format("svg");
font-weight: normal;
font-style: normal;
}
@@ -38,257 +38,257 @@
}
.icon-angle-down::before {
content: '\61';
content: "\61";
}
.icon-angle-left::before {
content: '\62';
content: "\62";
}
.icon-angle-right::before {
content: '\63';
content: "\63";
}
.icon-angle-up::before {
content: '\64';
content: "\64";
}
.icon-comments::before {
content: '\65';
content: "\65";
}
.icon-twitter::before {
content: '\66';
content: "\66";
}
.icon-calendar::before {
content: '\67';
content: "\67";
}
.icon-debates::before {
content: '\69';
content: "\69";
}
.icon-unlike::before {
content: '\6a';
content: "\6a";
}
.icon-like::before {
content: '\6b';
content: "\6b";
}
.icon-check::before {
content: '\6c';
content: "\6c";
}
.icon-edit::before {
content: '\6d';
content: "\6d";
}
.icon-user::before {
content: '\6f';
content: "\6f";
}
.icon-settings::before {
content: '\71';
content: "\71";
}
.icon-stats::before {
content: '\72';
content: "\72";
}
.icon-proposals::before {
content: '\68';
content: "\68";
}
.icon-organizations::before {
content: '\73';
content: "\73";
}
.icon-deleted::before {
content: '\74';
content: "\74";
}
.icon-tag::before {
content: '\75';
content: "\75";
}
.icon-eye::before {
content: '\70';
content: "\70";
}
.icon-x::before {
content: '\76';
content: "\76";
}
.icon-flag::before {
content: '\77';
content: "\77";
}
.icon-comment::before {
content: '\79';
content: "\79";
}
.icon-reply::before {
content: '\7a';
content: "\7a";
}
.icon-facebook::before {
content: '\41';
content: "\41";
}
.icon-google-plus::before {
content: '\42';
content: "\42";
}
.icon-search::before {
content: '\45';
content: "\45";
}
.icon-external::before {
content: '\46';
content: "\46";
}
.icon-video::before {
content: '\44';
content: "\44";
}
.icon-document::before {
content: '\47';
content: "\47";
}
.icon-print::before {
content: '\48';
content: "\48";
}
.icon-blog::before {
content: '\4a';
content: "\4a";
}
.icon-box::before {
content: '\49';
content: "\49";
}
.icon-youtube::before {
content: '\4b';
content: "\4b";
}
.icon-letter::before {
content: '\4c';
content: "\4c";
}
.icon-circle::before {
content: '\43';
content: "\43";
}
.icon-circle-o::before {
content: '\4d';
content: "\4d";
}
.icon-help::before {
content: '\4e';
content: "\4e";
}
.icon-budget::before {
content: '\53';
content: "\53";
}
.icon-notification::before {
content: '\6e';
content: "\6e";
}
.icon-no-notification::before {
content: '\78';
content: "\78";
}
.icon-whatsapp::before {
content: '\50';
content: "\50";
}
.icon-zip::before {
content: '\4f';
content: "\4f";
}
.icon-banner::before {
content: '\51';
content: "\51";
}
.icon-arrow-down::before {
content: '\52';
content: "\52";
}
.icon-arrow-left::before {
content: '\54';
content: "\54";
}
.icon-arrow-right::before {
content: '\55';
content: "\55";
}
.icon-check-circle::before {
content: '\56';
content: "\56";
}
.icon-arrow-top::before {
content: '\57';
content: "\57";
}
.icon-checkmark-circle::before {
content: '\59';
content: "\59";
}
.icon-minus-square::before {
content: '\58';
content: "\58";
}
.icon-plus-square::before {
content: '\5a';
content: "\5a";
}
.icon-expand::before {
content: '\30';
content: "\30";
}
.icon-telegram::before {
content: '\31';
content: "\31";
}
.icon-instagram::before {
content: '\32';
content: "\32";
}
.icon-image::before {
content: '\33';
content: "\33";
}
.icon-search-plus::before {
content: '\34';
content: "\34";
}
.icon-search-minus::before {
content: '\35';
content: "\35";
}
.icon-calculator::before {
content: '\36';
content: "\36";
}
.icon-map-marker::before {
content: '\37';
content: "\37";
}
.icon-user-plus::before {
content: '\38';
content: "\38";
}
.icon-file-text-o::before {
content: '\39';
content: "\39";
}
.icon-file-text::before {
content: '\21';
content: "\21";
}
.icon-bars::before {
content: '\22';
content: "\22";
}

View File

@@ -372,7 +372,7 @@ a {
text-decoration: none;
}
&[aria-selected='true'],
&[aria-selected="true"],
&.is-active {
border-bottom: 0;
color: $brand;
@@ -382,7 +382,7 @@ a {
background: $brand;
border-bottom: 2px solid $brand;
bottom: 0;
content: '';
content: "";
left: 0;
position: absolute;
width: 100%;
@@ -459,7 +459,7 @@ header {
&::after {
color: #808080;
content: '\61';
content: "\61";
font-family: "icons" !important;
font-size: $small-font-size;
pointer-events: none;
@@ -501,7 +501,7 @@ header {
a {
color: #fff;
display: inline-block;
font-family: 'Lato' !important;
font-family: "Lato" !important;
font-size: rem-calc(24);
font-weight: lighter;
line-height: $line-height * 2;
@@ -646,7 +646,7 @@ header {
display: inline-block;
&::after {
content: '|';
content: "|";
}
&:last-child::after {
@@ -794,7 +794,7 @@ footer {
color: $text;
.logo a {
font-family: 'Lato' !important;
font-family: "Lato" !important;
text-decoration: none;
&:hover {
@@ -906,7 +906,7 @@ footer {
}
.auth-image {
background: $brand image-url('auth_bg.jpg');
background: $brand image-url("auth_bg.jpg");
background-repeat: no-repeat;
background-size: cover;
@@ -980,7 +980,7 @@ footer {
}
.locale-switcher {
background: #1a1a1a;
background: #001d33;
border: 0;
border-radius: rem-calc(4);
color: #fff;
@@ -1279,7 +1279,7 @@ form {
&::before {
background: $border;
content: '';
content: "";
height: 100%;
left: 7px;
position: absolute;
@@ -1325,14 +1325,14 @@ form {
}
&::before {
content: '\43';
content: "\43";
}
}
&::before {
background: #fff;
color: $brand;
content: '\4d';
content: "\4d";
font-family: "icons" !important;
font-size: $small-font-size;
height: rem-calc(20);
@@ -1487,7 +1487,7 @@ table {
&::before {
color: #45b0e3;
content: 'f';
content: "f";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 0;
@@ -1507,7 +1507,7 @@ table {
width: $line-height * 2 !important;
&::before {
content: 'f';
content: "f";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
@@ -1530,7 +1530,7 @@ table {
&::before {
color: #3b5998;
content: 'A';
content: "A";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 0;
@@ -1550,7 +1550,7 @@ table {
width: rem-calc(48) !important;
&::before {
content: 'A';
content: "A";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
@@ -1573,7 +1573,7 @@ table {
&::before {
color: #de4c34;
content: 'B';
content: "B";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 0;
@@ -1593,7 +1593,7 @@ table {
width: $line-height * 2 !important;
&::before {
content: 'B';
content: "B";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
@@ -1616,7 +1616,7 @@ table {
&::before {
color: #08c;
content: '1';
content: "1";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 0;
@@ -1636,7 +1636,7 @@ table {
width: $line-height * 2 !important;
&::before {
content: '1';
content: "1";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
@@ -1689,7 +1689,7 @@ table {
width: $line-height * 2;
&::before {
content: 'f';
content: "f";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
@@ -1714,7 +1714,7 @@ table {
width: rem-calc(48);
&::before {
content: 'A';
content: "A";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
@@ -1739,7 +1739,7 @@ table {
width: rem-calc(48);
&::before {
content: 'B';
content: "B";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
@@ -1764,7 +1764,7 @@ table {
width: $line-height * 2;
&::before {
content: '1';
content: "1";
font-family: "icons" !important;
font-size: rem-calc(24);
left: 50%;
@@ -1808,7 +1808,7 @@ table {
top: 24px;
@include breakpoint(medium) {
content: 'c';
content: "c";
}
}
}
@@ -2260,15 +2260,15 @@ table {
@include breakpoint(large) {
.banner-img-one {
background-image: image-url('banners/banner1.png');
background-image: image-url("banners/banner1.png");
}
.banner-img-two {
background-image: image-url('banners/banner2.png');
background-image: image-url("banners/banner2.png");
}
.banner-img-three {
background-image: image-url('banners/banner3.png');
background-image: image-url("banners/banner3.png");
}
}
@@ -2385,7 +2385,7 @@ table {
}
.card .orbit .orbit-wrapper .truncate {
background: image-url('truncate.png');
background: image-url("truncate.png");
background-repeat: repeat-x;
bottom: 0;
height: rem-calc(20);
@@ -2668,7 +2668,7 @@ table {
&.score-positive::before,
&.score-negative::before {
font-family: 'icons';
font-family: "icons";
left: 0;
position: absolute;
}
@@ -2678,7 +2678,7 @@ table {
&::before {
color: $color-success;
content: '\6c';
content: "\6c";
}
}
@@ -2687,7 +2687,7 @@ table {
&::before {
color: $color-alert;
content: '\76';
content: "\76";
}
}
}
@@ -2728,7 +2728,8 @@ table {
// 24. Homepage
// ------------
.home-page {
.home-page,
.custom-page {
a {
@@ -2868,11 +2869,19 @@ table {
.figure-card {
display: flex;
margin: 0 0 $line-height;
overflow: hidden;
position: relative;
@include breakpoint(medium down) {
min-height: $line-height * 4;
}
@include breakpoint(medium) {
max-height: rem-calc(185);
overflow: hidden;
}
@include breakpoint(large) {
min-height: rem-calc(185);
}
a {
@@ -2904,8 +2913,16 @@ table {
h3,
.title {
font-size: rem-calc(24);
line-height: rem-calc(24);
font-size: $base-font-size;
@include breakpoint(medium) {
font-size: rem-calc(20);
}
@include breakpoint(large) {
font-size: rem-calc(24);
line-height: rem-calc(24);
}
}
span {

View File

@@ -24,7 +24,7 @@
&::before {
color: #8aa8be;
content: '';
content: "";
padding-right: $line-height / 4;
vertical-align: text-bottom;
}
@@ -60,12 +60,14 @@
border: 1px solid $border;
display: block;
margin: rem-calc(-1) 0;
min-height: $line-height * 3;
position: relative;
vertical-align: top;
@include breakpoint(large down) {
&::after {
content: '\63';
content: "\63";
font-family: "icons" !important;
font-size: rem-calc(24);
pointer-events: none;
@@ -90,7 +92,7 @@
}
&::after {
content: '';
content: "";
}
}
@@ -136,7 +138,7 @@
}
&::after {
content: '';
content: "";
}
}
}
@@ -466,8 +468,8 @@
cursor: pointer;
position: absolute;
margin-left: rem-calc(-20);
font-family: 'icons';
content: '\58';
font-family: "icons";
content: "\58";
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@@ -476,8 +478,8 @@
cursor: pointer;
position: absolute;
margin-left: rem-calc(-20);
font-family: 'icons';
content: '\5a';
font-family: "icons";
content: "\5a";
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@@ -505,7 +507,7 @@
.anchor::before {
display: none;
content: '#';
content: "#";
color: $text-medium;
position: absolute;
left: 0;
@@ -757,7 +759,7 @@
display: inline-block;
&::after {
content: '|';
content: "|";
color: #838383;
}
}
@@ -778,7 +780,7 @@
&::after {
margin-left: rem-calc(4);
content: '|';
content: "|";
}
}
@@ -828,7 +830,7 @@
&::before {
margin-right: rem-calc(4);
content: '';
content: "";
}
}
}
@@ -979,4 +981,4 @@
font-size: rem-calc(20);
margin-top: 0;
}
}
}

View File

@@ -0,0 +1,127 @@
$progress-bar-background: #fef0e2;
$progress-bar-color: #fea230;
.tab-milestones {
.progress-bars {
margin-bottom: $line-height * 2;
margin-top: $line-height;
h5 {
font-size: rem-calc(24);
}
.progress {
background: $progress-bar-background;
border-radius: rem-calc(6);
position: relative;
}
.progress-meter {
background: $progress-bar-color;
border-radius: rem-calc(6);
}
.progress-meter-text {
color: #000;
right: 12px;
text-align: right;
transform: translate(0%, -50%);
}
.milestone-progress .row {
margin-bottom: $line-height / 2;
}
}
}
.tab-milestones .timeline li {
margin: 0 auto;
position: relative;
width: 0;
@include breakpoint(small only) {
width: 100%;
}
&::before {
background: $budget;
border-radius: rem-calc(20);
content: "";
height: rem-calc(20);
position: absolute;
top: 5px;
transform: translateX(-50%);
width: rem-calc(20);
z-index: 2;
}
&::after {
background: $light-gray;
bottom: 100%;
content: "";
height: 100%;
position: absolute;
top: 25px;
width: 1px;
z-index: 1;
}
.milestone-content {
padding: $line-height / 6 $line-height / 2;
position: relative;
@include breakpoint(medium) {
width: rem-calc(300);
}
@include breakpoint(large) {
width: rem-calc(450);
}
h3 {
margin-bottom: 0;
}
.milestone-date {
color: $text-medium;
font-size: $small-font-size;
}
}
&:nth-child(odd) {
.milestone-content {
text-align: right;
@include breakpoint(medium) {
margin-left: rem-calc(-315);
}
@include breakpoint(large) {
margin-left: rem-calc(-465);
}
@include breakpoint(small only) {
left: 15px;
text-align: left;
}
}
}
&:nth-child(even) {
.milestone-content {
left: 15px;
}
}
}
.milestone-status {
background: $budget;
border-radius: rem-calc(4);
color: #fff;
display: inline-block;
margin-top: $line-height / 6;
padding: $line-height / 4 $line-height / 2;
}

View File

@@ -11,7 +11,7 @@
@mixin logo {
color: #fff;
display: inline-block;
font-family: 'Lato' !important;
font-family: "Lato" !important;
font-size: rem-calc(24);
font-weight: lighter;

View File

@@ -23,11 +23,6 @@
&.light {
background: #ecf0f1;
}
h1,
p {
color: $text;
}
}
.lead {

View File

@@ -287,7 +287,7 @@
margin: $line-height / 2 0;
&::before {
content: 'l ';
content: "l ";
font-family: "icons" !important;
}
}
@@ -523,118 +523,6 @@
}
}
.tab-milestones ul {
margin-top: rem-calc(40);
position: relative;
li {
margin: 0 auto;
position: relative;
width: 0;
}
li::before {
background: $budget;
border-radius: rem-calc(20);
content: '';
height: rem-calc(20);
position: absolute;
top: 5px;
transform: translateX(-50%);
width: rem-calc(20);
z-index: 2;
}
li::after {
background: $light-gray;
bottom: 100%;
content: '';
height: 100%;
position: absolute;
top: 25px;
width: 1px;
z-index: 1;
}
}
.tab-milestones ul .milestone-content {
padding: $line-height / 6 $line-height / 2;
position: relative;
h3 {
margin-bottom: 0;
}
.milestone-date {
color: $text-medium;
font-size: $small-font-size;
}
}
.tab-milestones .timeline ul li:nth-child(odd),
.tab-milestones .timeline ul li:nth-child(even) {
.milestone-content {
@include breakpoint(medium) {
width: rem-calc(300);
}
@include breakpoint(large) {
width: rem-calc(450);
}
}
}
.tab-milestones .timeline ul li:nth-child(odd) {
.milestone-content {
text-align: right;
@include breakpoint(medium) {
margin-left: rem-calc(-315);
}
@include breakpoint(large) {
margin-left: rem-calc(-465);
}
}
}
.tab-milestones .timeline ul li:nth-child(even) {
.milestone-content {
left: 15px;
}
}
.tab-milestones {
@include breakpoint(small only) {
.timeline ul li {
width: 100%;
&:nth-child(odd),
&:nth-child(even) {
.milestone-content {
left: 15px;
text-align: left;
}
}
}
}
}
.milestone-status {
background: $budget;
border-radius: rem-calc(4);
color: #fff;
display: inline-block;
margin-top: $line-height / 6;
padding: $line-height / 4 $line-height / 2;
}
.show-actions-menu {
[class^="icon-"] {
@@ -845,7 +733,7 @@
}
.truncate {
background: image-url('truncate.png');
background: image-url("truncate.png");
background-repeat: repeat-x;
bottom: 0;
height: rem-calc(24);
@@ -1050,7 +938,7 @@
&::before {
color: $text;
font-family: 'icons';
font-family: "icons";
}
}
@@ -1059,7 +947,7 @@
.button {
&::before {
content: '\51';
content: "\51";
}
}
}
@@ -1069,7 +957,7 @@
.button {
&::before {
content: '\22';
content: "\22";
}
}
}
@@ -1078,8 +966,8 @@
position: relative;
&::before {
content: '\22';
font-family: 'icons';
content: "\22";
font-family: "icons";
left: 0;
position: absolute;
top: 6px;
@@ -1090,8 +978,8 @@
position: relative;
&::before {
content: '\51';
font-family: 'icons';
content: "\51";
font-family: "icons";
left: 0;
position: absolute;
top: 6px;
@@ -1102,8 +990,8 @@
color: $brand;
&::after {
content: '\6c';
font-family: 'icons';
content: "\6c";
font-family: "icons";
font-size: $tiny-font-size;
}
}
@@ -1365,16 +1253,12 @@
display: inline-block;
margin-bottom: $line-height / 2;
&:hover {
background: $highlight;
text-decoration: none;
}
a {
display: block;
padding: $line-height / 2;
&:hover {
background: $highlight;
text-decoration: none;
}
}
@@ -1422,8 +1306,8 @@
&::before {
color: #a5a1ff;
content: '\57';
font-family: 'icons';
content: "\57";
font-family: "icons";
font-size: $small-font-size;
position: absolute;
right: -6px;
@@ -1566,8 +1450,8 @@
font-weight: bold;
&::after {
content: '\56';
font-family: 'icons';
content: "\56";
font-family: "icons";
font-size: $small-font-size;
font-weight: normal;
line-height: $line-height;
@@ -1642,7 +1526,7 @@
background-color: #fff;
border: 4px solid $budget;
border-radius: 100%;
content: '';
content: "";
height: 16px;
left: -22px;
position: absolute;
@@ -1784,7 +1668,7 @@
&::after {
color: #1b254c;
content: '\59';
content: "\59";
font-family: "icons" !important;
left: 34px;
position: absolute;
@@ -1977,7 +1861,7 @@
&::after {
color: $color-info;
content: '\6c';
content: "\6c";
}
}
@@ -1986,7 +1870,7 @@
&::after {
color: $color-alert;
content: '\74';
content: "\74";
}
}
@@ -1995,7 +1879,7 @@
&::after {
color: $color-info;
content: '\6f';
content: "\6f";
}
}
@@ -2004,7 +1888,7 @@
&::after {
color: $color-warning;
content: '\6f';
content: "\6f";
}
}
@@ -2013,7 +1897,7 @@
&::after {
color: $color-success;
content: '\59';
content: "\59";
}
}
}
@@ -2079,7 +1963,7 @@
&::after {
background: #92ba48;
border-radius: rem-calc(20);
content: '\6c';
content: "\6c";
color: #fff;
font-family: "icons" !important;
font-size: rem-calc(12);

View File

@@ -1,4 +1,5 @@
class Admin::BudgetGroupsController < Admin::BaseController
include Translatable
include FeatureFlags
feature_flag :budgets
@@ -57,7 +58,8 @@ class Admin::BudgetGroupsController < Admin::BaseController
end
def budget_group_params
params.require(:budget_group).permit(:name, :max_votable_headings)
valid_attributes = [:max_votable_headings]
params.require(:budget_group).permit(*valid_attributes, translation_params(Budget::Group))
end
end

View File

@@ -1,4 +1,5 @@
class Admin::BudgetHeadingsController < Admin::BaseController
include Translatable
include FeatureFlags
feature_flag :budgets
@@ -62,8 +63,8 @@ class Admin::BudgetHeadingsController < Admin::BaseController
end
def budget_heading_params
params.require(:budget_heading).permit(:name, :price, :population, :allow_custom_content,
:latitude, :longitude)
valid_attributes = [:price, :population, :allow_custom_content, :latitude, :longitude]
params.require(:budget_heading).permit(*valid_attributes, translation_params(Budget::Heading))
end
end

View File

@@ -0,0 +1,8 @@
class Admin::BudgetInvestmentProgressBarsController < Admin::ProgressBarsController
private
def progressable
Budget::Investment.find(params[:budget_investment_id])
end
end

View File

@@ -1,4 +1,5 @@
class Admin::BudgetPhasesController < Admin::BaseController
include Translatable
before_action :load_phase, only: [:edit, :update]
@@ -21,8 +22,8 @@ class Admin::BudgetPhasesController < Admin::BaseController
end
def budget_phase_params
valid_attributes = [:starts_at, :ends_at, :summary, :description, :enabled]
params.require(:budget_phase).permit(*valid_attributes)
valid_attributes = [:starts_at, :ends_at, :enabled]
params.require(:budget_phase).permit(*valid_attributes, translation_params(Budget::Phase))
end
end

View File

@@ -1,4 +1,5 @@
class Admin::BudgetsController < Admin::BaseController
include Translatable
include FeatureFlags
feature_flag :budgets
@@ -56,8 +57,8 @@ class Admin::BudgetsController < Admin::BaseController
def budget_params
descriptions = Budget::Phase::PHASE_KINDS.map{|p| "description_#{p}"}.map(&:to_sym)
valid_attributes = [:name, :phase, :currency_symbol] + descriptions
params.require(:budget).permit(*valid_attributes)
valid_attributes = [:phase, :currency_symbol] + descriptions
params.require(:budget).permit(*valid_attributes, translation_params(Budget))
end
end

View File

@@ -1,12 +1,13 @@
class Admin::Legislation::ProcessesController < Admin::Legislation::BaseController
include Translatable
has_filters %w{open next past all}, only: :index
has_filters %w[open all], only: :index
load_and_authorize_resource :process, class: "Legislation::Process"
def index
@processes = ::Legislation::Process.send(@current_filter).order('id DESC').page(params[:page])
@processes = ::Legislation::Process.send(@current_filter).order(start_date: :desc)
.page(params[:page])
end
def create
@@ -66,8 +67,11 @@ class Admin::Legislation::ProcessesController < Admin::Legislation::BaseControll
:result_publication_enabled,
:published,
:custom_list,
:background_color,
:font_color,
translation_params(::Legislation::Process),
documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]
documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy],
image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]
]
end

View File

@@ -0,0 +1,14 @@
class Admin::Legislation::ProgressBarsController < Admin::ProgressBarsController
include FeatureFlags
feature_flag :legislation
def index
@process = progressable
end
private
def progressable
::Legislation::Process.find(params[:process_id])
end
end

View File

@@ -7,6 +7,7 @@ class Admin::Poll::PollsController < Admin::Poll::BaseController
before_action :load_geozones, only: [:new, :create, :edit, :update]
def index
@polls = Poll.order(starts_at: :desc)
end
def show
@@ -51,7 +52,7 @@ class Admin::Poll::PollsController < Admin::Poll::BaseController
end
def booth_assignments
@polls = Poll.current_or_incoming
@polls = Poll.current
end
private

View File

@@ -6,8 +6,8 @@ class Admin::Poll::ShiftsController < Admin::Poll::BaseController
def new
load_shifts
@shift = ::Poll::Shift.new
@voting_polls = @booth.polls.current_or_incoming
@recount_polls = @booth.polls.current_or_recounting_or_incoming
@voting_polls = @booth.polls.current
@recount_polls = @booth.polls.current_or_recounting
end
def create
@@ -26,9 +26,14 @@ class Admin::Poll::ShiftsController < Admin::Poll::BaseController
def destroy
@shift = Poll::Shift.find(params[:id])
@shift.destroy
notice = t("admin.poll_shifts.flash.destroy")
redirect_to new_admin_booth_shift_path(@booth), notice: notice
if @shift.unable_to_destroy?
alert = t("admin.poll_shifts.flash.unable_to_destroy")
redirect_to new_admin_booth_shift_path(@booth), alert: alert
else
@shift.destroy
notice = t("admin.poll_shifts.flash.destroy")
redirect_to new_admin_booth_shift_path(@booth), notice: notice
end
end
def search_officers

View File

@@ -0,0 +1,69 @@
class Admin::ProgressBarsController < Admin::BaseController
include Translatable
before_action :load_progressable
before_action :load_progress_bar, only: [:edit, :update, :destroy]
helper_method :progress_bars_index
def index
end
def new
@progress_bar = @progressable.progress_bars.new
end
def create
@progress_bar = @progressable.progress_bars.new(progress_bar_params)
if @progress_bar.save
redirect_to progress_bars_index, notice: t("admin.progress_bars.create.notice")
else
render :new
end
end
def edit
end
def update
if @progress_bar.update(progress_bar_params)
redirect_to progress_bars_index, notice: t('admin.progress_bars.update.notice')
else
render :edit
end
end
def destroy
@progress_bar.destroy
redirect_to progress_bars_index, notice: t('admin.progress_bars.delete.notice')
end
private
def progress_bar_params
params.require(:progress_bar).permit(allowed_params)
end
def allowed_params
[
:kind,
:percentage,
translation_params(ProgressBar)
]
end
def load_progressable
@progressable = progressable
end
def progressable
raise "This method must be implemented in subclass #{self.class.name}"
end
def load_progress_bar
@progress_bar = progressable.progress_bars.find(params[:id])
end
def progress_bars_index
polymorphic_path([:admin, *resource_hierarchy_for(@progressable), ProgressBar.new])
end
end

View File

@@ -0,0 +1,7 @@
class Admin::ProposalProgressBarsController < Admin::ProgressBarsController
private
def progressable
Proposal.find(params[:proposal_id])
end
end

View File

@@ -0,0 +1,9 @@
class Admin::SiteCustomization::CardsController < Admin::SiteCustomization::BaseController
skip_authorization_check
def index
@page = ::SiteCustomization::Page.find(params[:page_id])
@cards = @page.cards
end
end

View File

@@ -3,14 +3,17 @@ class Admin::Widget::CardsController < Admin::BaseController
include ImageAttributes
def new
@card = ::Widget::Card.new(header: header_card?)
if header_card?
@card = ::Widget::Card.new(header: header_card?)
else
@card = ::Widget::Card.new(site_customization_page_id: params[:page_id])
end
end
def create
@card = ::Widget::Card.new(card_params)
if @card.save
notice = "Success"
redirect_to admin_homepage_url, notice: notice
redirect_to_customization_page_cards_or_homepage
else
render :new
end
@@ -23,8 +26,7 @@ class Admin::Widget::CardsController < Admin::BaseController
def update
@card = ::Widget::Card.find(params[:id])
if @card.update(card_params)
notice = "Updated"
redirect_to admin_homepage_url, notice: notice
redirect_to_customization_page_cards_or_homepage
else
render :edit
end
@@ -34,25 +36,39 @@ class Admin::Widget::CardsController < Admin::BaseController
@card = ::Widget::Card.find(params[:id])
@card.destroy
notice = "Removed"
redirect_to admin_homepage_url, notice: notice
redirect_to_customization_page_cards_or_homepage
end
private
def card_params
params.require(:widget_card).permit(
:link_url, :button_text, :button_url, :alignment, :header,
translation_params(Widget::Card),
image_attributes: image_attributes
)
end
def card_params
params.require(:widget_card).permit(
:link_url, :button_text, :button_url, :alignment, :header, :site_customization_page_id,
:columns,
translation_params(Widget::Card),
image_attributes: image_attributes
)
end
def header_card?
params[:header_card].present?
end
def header_card?
params[:header_card].present?
end
def resource
Widget::Card.find(params[:id])
end
def redirect_to_customization_page_cards_or_homepage
notice = t("admin.site_customization.pages.cards.#{params[:action]}.notice")
if @card.site_customization_page_id
redirect_to admin_site_customization_page_cards_path(page), notice: notice
else
redirect_to admin_homepage_url, notice: notice
end
end
def page
::SiteCustomization::Page.find(@card.site_customization_page_id)
end
def resource
Widget::Card.find(params[:id])
end
end

View File

@@ -1,40 +0,0 @@
class AnnotationsController < ApplicationController
skip_before_action :verify_authenticity_token
load_and_authorize_resource
def create
@annotation = Annotation.new(annotation_params)
@annotation.user = current_user
if @annotation.save
render json: @annotation.to_json(methods: :permissions)
end
end
def update
@annotation = Annotation.find(params[:id])
if @annotation.update_attributes(annotation_params)
render json: @annotation.to_json(methods: :permissions)
end
end
def destroy
@annotation = Annotation.find(params[:id])
@annotation.destroy
render json: { status: :ok }
end
def search
@annotations = Annotation.where(legacy_legislation_id: params[:legacy_legislation_id])
annotations_hash = { total: @annotations.size, rows: @annotations }
render json: annotations_hash.to_json(methods: :permissions)
end
private
def annotation_params
params
.require(:annotation)
.permit(:quote, :text, ranges: [:start, :startOffset, :end, :endOffset])
.merge(legacy_legislation_id: params[:legacy_legislation_id])
end
end

View File

@@ -120,6 +120,8 @@ class ApplicationController < ActionController::Base
def set_default_budget_filter
if @budget.try(:balloting?) || @budget.try(:publishing_prices?)
params[:filter] ||= "selected"
elsif @budget.try(:finished?)
params[:filter] ||= "winners"
end
end

View File

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

View File

@@ -4,7 +4,7 @@ module Budgets
load_and_authorize_resource :group, class: "Budget::Group"
before_action :set_default_budget_filter, only: :show
has_filters %w{not_unfeasible feasible unfeasible unselected selected}, only: [:show]
has_filters %w[not_unfeasible feasible unfeasible unselected selected winners], only: [:show]
def show
end

View File

@@ -27,7 +27,9 @@ module Budgets
has_orders %w{most_voted newest oldest}, only: :show
has_orders ->(c) { c.instance_variable_get(:@budget).investments_orders }, only: :index
has_filters %w{not_unfeasible feasible unfeasible unselected selected}, only: [:index, :show, :suggest]
valid_filters = %w[not_unfeasible feasible unfeasible unselected selected winners]
has_filters valid_filters, only: [:index, :show, :suggest]
invisible_captcha only: [:create, :update], honeypot: :subtitle, scope: :budget_investment
@@ -35,18 +37,10 @@ module Budgets
respond_to :html, :js
def index
all_investments = if @budget.finished?
investments.winners
else
investments
end
@investments = all_investments.page(params[:page]).per(10).for_render
@investments = investments.page(params[:page]).per(10).for_render
@investment_ids = @investments.pluck(:id)
@investments_map_coordinates = MapLocation.where(investment: all_investments).map do |loc|
loc.json_data
end
@investments_map_coordinates = MapLocation.where(investment: investments).map(&:json_data)
load_investment_votes(@investments)
@tag_cloud = tag_cloud

View File

@@ -5,7 +5,7 @@ class BudgetsController < ApplicationController
load_and_authorize_resource
before_action :set_default_budget_filter, only: :show
has_filters %w{not_unfeasible feasible unfeasible unselected selected}, only: :show
has_filters %w[not_unfeasible feasible unfeasible unselected selected winners], only: :show
respond_to :html, :js

View File

@@ -11,7 +11,7 @@ class DocumentsController < ApplicationController
else
flash[:alert] = t "documents.actions.destroy.alert"
end
redirect_to params[:from]
redirect_to request.referer
end
format.js do
if @document.destroy

View File

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

View File

@@ -1,8 +0,0 @@
class LegacyLegislationsController < ApplicationController
load_and_authorize_resource
def show
@legacy_legislation = LegacyLegislation.find(params[:id])
end
end

View File

@@ -1,5 +1,5 @@
class Legislation::ProcessesController < Legislation::BaseController
has_filters %w[open next past], only: :index
has_filters %w[open past], only: :index
has_filters %w[random winners], only: :proposals
load_and_authorize_resource
@@ -9,7 +9,7 @@ class Legislation::ProcessesController < Legislation::BaseController
def index
@current_filter ||= 'open'
@processes = ::Legislation::Process.send(@current_filter).published
.not_in_draft.page(params[:page])
.not_in_draft.order(start_date: :desc).page(params[:page])
end
def show

View File

@@ -4,7 +4,6 @@ class Management::Budgets::InvestmentsController < Management::BaseController
load_resource :investment, through: :budget, class: 'Budget::Investment'
before_action :only_verified_users, except: :print
before_action :load_heading, only: [:index, :show, :print]
def index
@investments = @investments.apply_filters_and_search(@budget, params).page(params[:page])
@@ -61,10 +60,6 @@ class Management::Budgets::InvestmentsController < Management::BaseController
check_verified_user t("management.budget_investments.alert.unverified_user")
end
def load_heading
@heading = @budget.headings.find(params[:heading_id]) if params[:heading_id].present?
end
def load_categories
@categories = ActsAsTaggableOn::Tag.category.order(:name)
end

View File

@@ -9,6 +9,7 @@ class PagesController < ApplicationController
@banners = Banner.in_section('help_page').with_active
if @custom_page.present?
@cards = @custom_page.cards
render action: :custom_page
else
render action: params[:id]

View File

@@ -3,7 +3,7 @@ class PollsController < ApplicationController
load_and_authorize_resource
has_filters %w{current expired incoming}
has_filters %w[current expired]
has_orders %w{most_voted newest oldest}, only: :show
::Poll::Answer # trigger autoload

View File

@@ -31,7 +31,7 @@ class RelatedContentsController < ApplicationController
private
def score(action)
@related = RelatedContent.find_by(id: params[:id])
@related = RelatedContent.find params[:id]
@related.send("score_#{action}", current_user)
render template: 'relationable/_refresh_score_actions'

View File

@@ -75,8 +75,8 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController
def heading_filters
investments = @budget.investments.by_valuator(current_user.valuator.try(:id))
.visible_to_valuators.distinct
investment_headings = Budget::Heading.where(id: investments.pluck(:heading_id).uniq)
investment_headings = Budget::Heading.joins(:translations)
.where(id: investments.pluck(:heading_id).uniq)
.order(name: :asc)
all_headings_filter = [

View File

@@ -29,6 +29,10 @@ module AdminHelper
"hidden_budget_investments"]
end
def menu_budgets?
controller_name.starts_with?("budget")
end
def menu_budget?
["spending_proposals"].include?(controller_name)
end
@@ -50,11 +54,16 @@ module AdminHelper
end
def menu_customization?
["pages", "banners", "information_texts"].include?(controller_name) || menu_homepage?
["pages", "banners", "information_texts"].include?(controller_name) ||
menu_homepage? || menu_pages?
end
def menu_homepage?
["homepage", "cards"].include?(controller_name)
["homepage", "cards"].include?(controller_name) && params[:page_id].nil?
end
def menu_pages?
["pages", "cards"].include?(controller_name) && params[:page_id].present?
end
def official_level_options

View File

@@ -1,7 +1,7 @@
module BudgetHeadingsHelper
def budget_heading_select_options(budget)
budget.headings.order_by_group_name.map do |heading|
budget.headings.sort_by_name.map do |heading|
[heading.name_scoped_by_group, heading.id]
end
end

View File

@@ -48,8 +48,8 @@ module BudgetsHelper
end
def css_for_ballot_heading(heading)
return '' if current_ballot.blank?
current_ballot.has_lines_in_heading?(heading) ? 'is-active' : ''
return "" if current_ballot.blank? || @current_filter == "unfeasible"
current_ballot.has_lines_in_heading?(heading) ? "is-active" : ""
end
def current_ballot
@@ -60,13 +60,17 @@ module BudgetsHelper
Budget::Investment.by_budget(budget).tags_on(:valuation).order(:name).select(:name).distinct
end
def unfeasible_or_unselected_filter
["unselected", "unfeasible"].include?(@current_filter)
end
def budget_published?(budget)
!budget.drafting? || current_user&.administrator?
end
def current_budget_map_locations
return unless current_budget.present?
if current_budget.valuating_or_later?
if current_budget.publishing_prices_or_later? && current_budget.investments.selected.any?
investments = current_budget.investments.selected
else
investments = current_budget.investments
@@ -86,4 +90,10 @@ module BudgetsHelper
t("admin.budgets.winners.recalculate")
end
end
def display_support_alert?(investment)
current_user &&
!current_user.voted_in_group?(investment.group) &&
investment.group.headings.count > 1
end
end

View File

@@ -12,8 +12,20 @@ module FeedsHelper
feed.kind == "processes"
end
def feed_debates_enabled?
Setting["feature.homepage.widgets.feeds.debates"].present?
end
def feed_proposals_enabled?
Setting["feature.homepage.widgets.feeds.proposals"].present?
end
def feed_processes_enabled?
Setting['feature.homepage.widgets.feeds.processes'].present?
Setting["feature.homepage.widgets.feeds.processes"].present?
end
def feed_debates_and_proposals_enabled?
feed_debates_enabled? && feed_proposals_enabled?
end
end

View File

@@ -37,4 +37,15 @@ module LegislationHelper
"milestones" => admin_legislation_process_milestones_path(process)
}
end
def banner_color?
@process.background_color.present? && @process.font_color.present?
end
def css_for_process_header
if banner_color?
"background:" + @process.background_color + ";color:" + @process.font_color + ";"
end
end
end

View File

@@ -0,0 +1,18 @@
module MilestonesHelper
def progress_tag_for(progress_bar)
text = number_to_percentage(progress_bar.percentage, precision: 0)
content_tag :div, class: "progress",
role: "progressbar",
"aria-valuenow": "#{progress_bar.percentage}",
"aria-valuetext": "#{progress_bar.percentage}%",
"aria-valuemax": ProgressBar::RANGE.max,
"aria-valuemin": "0",
tabindex: "0" do
content_tag(:span, "",
class: "progress-meter",
style: "width: #{progress_bar.percentage}%;") +
content_tag(:p, text, class: "progress-meter-text")
end
end
end

View File

@@ -49,4 +49,19 @@ module PollsHelper
question.answers.where(author: current_user).any? { |vote| current_user.current_sign_in_at > vote.updated_at }
end
def show_stats_or_results?
@poll.expired? && (@poll.results_enabled? || @poll.stats_enabled?)
end
def results_menu?
controller_name == "polls" && action_name == "results"
end
def stats_menu?
controller_name == "polls" && action_name == "stats"
end
def info_menu?
controller_name == "polls" && action_name == "show"
end
end

View File

@@ -57,8 +57,6 @@ module Abilities
can [:search, :create, :index, :destroy], ::Manager
can [:search, :index], ::User
can :manage, Annotation
can [:read, :update, :valuate, :destroy, :summary], SpendingProposal
can [:index, :read, :new, :create, :update, :destroy, :calculate_winners], Budget
can [:read, :create, :update, :destroy], Budget::Group
@@ -93,6 +91,7 @@ module Abilities
cannot :comment_as_moderator, [::Legislation::Question, Legislation::Annotation, ::Legislation::Proposal]
can [:create], Document
can [:destroy], Document, documentable_type: "Poll::Question::Answer"
can [:create, :destroy], DirectUpload
can [:deliver], Newsletter, hidden_at: nil

View File

@@ -92,9 +92,6 @@ module Abilities
can [:create, :show], ProposalNotification, proposal: { author_id: user.id }
can :create, Annotation
can [:update, :destroy], Annotation, user_id: user.id
can [:create], Topic
can [:update, :destroy], Topic, author_id: user.id

View File

@@ -16,9 +16,7 @@ module Abilities
can :read, Poll::Question
can [:read, :welcome], Budget
can :read, SpendingProposal
can :read, LegacyLegislation
can :read, User
can [:search, :read], Annotation
can [:read], Budget
can [:read], Budget::Group
can [:read, :print, :json_data], Budget::Investment

View File

@@ -1,10 +0,0 @@
class Annotation < ActiveRecord::Base
serialize :ranges, Array
belongs_to :legacy_legislation
belongs_to :user
def permissions
{ update: [user_id], delete: [user_id], admin: [] }
end
end

View File

@@ -3,9 +3,14 @@ class Budget < ActiveRecord::Base
include Measurable
include Sluggable
translates :name, touch: true
include Globalizable
CURRENCY_SYMBOLS = %w(€ $ £ ¥).freeze
validates :name, presence: true, uniqueness: true
before_validation :assign_model_to_translations
validates_translation :name, presence: true
validates :phase, inclusion: { in: Budget::Phase::PHASE_KINDS }
validates :currency_symbol, presence: true
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
@@ -105,8 +110,8 @@ class Budget < ActiveRecord::Base
Budget::Phase::PUBLISHED_PRICES_PHASES.include?(phase)
end
def valuating_or_later?
valuating? || publishing_prices? || balloting_or_later?
def publishing_prices_or_later?
publishing_prices? || balloting_or_later?
end
def balloting_process?

View File

@@ -2,14 +2,21 @@ class Budget
class Group < ActiveRecord::Base
include Sluggable
translates :name, touch: true
include Globalizable
belongs_to :budget
has_many :headings, dependent: :destroy
before_validation :assign_model_to_translations
validates_translation :name, presence: true
validates :budget_id, presence: true
validates :name, presence: true, uniqueness: { scope: :budget }
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
scope :sort_by_name, -> { includes(:translations).order(:name) }
def single_heading_group?
headings.count == 1
end

View File

@@ -0,0 +1,13 @@
class Budget::Group::Translation < Globalize::ActiveRecord::Translation
delegate :budget, to: :globalized_model
validate :name_uniqueness_by_budget
def name_uniqueness_by_budget
if budget.groups.joins(:translations)
.where(name: name)
.where.not("budget_group_translations.budget_group_id": budget_group_id).any?
errors.add(:name, I18n.t("errors.messages.taken"))
end
end
end

View File

@@ -4,13 +4,18 @@ class Budget
include Sluggable
translates :name, touch: true
include Globalizable
belongs_to :group
has_many :investments
has_many :content_blocks
before_validation :assign_model_to_translations
validates_translation :name, presence: true
validates :group_id, presence: true
validates :name, presence: true, uniqueness: { if: :name_exists_in_budget_headings }
validates :price, presence: true
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
validates :population, numericality: { greater_than: 0 }, allow_nil: true
@@ -21,17 +26,19 @@ class Budget
delegate :budget, :budget_id, to: :group, allow_nil: true
scope :order_by_group_name, -> { includes(:group).order('budget_groups.name', 'budget_headings.name') }
scope :allow_custom_content, -> { where(allow_custom_content: true).order(:name) }
scope :i18n, -> { includes(:translations) }
scope :allow_custom_content, -> { i18n.where(allow_custom_content: true).order(:name) }
def self.sort_by_name
all.sort do |heading, other_heading|
[other_heading.group.name, heading.name] <=> [heading.group.name, other_heading.name]
end
end
def name_scoped_by_group
group.single_heading_group? ? name : "#{group.name}: #{name}"
end
def name_exists_in_budget_headings
group.budget.headings.where(name: name).where.not(id: id).any?
end
def can_be_deleted?
investments.empty?
end

View File

@@ -0,0 +1,14 @@
class Budget::Heading::Translation < Globalize::ActiveRecord::Translation
delegate :budget, to: :globalized_model
validate :name_uniqueness_by_budget
def name_uniqueness_by_budget
if budget.headings
.joins(:translations)
.where(name: name)
.where.not("budget_heading_translations.budget_heading_id": budget_heading_id).any?
errors.add(:name, I18n.t("errors.messages.taken"))
end
end
end

View File

@@ -257,7 +257,7 @@ class Budget
end
def can_vote_in_another_heading?(user)
headings_voted_by_user(user).count < group.max_votable_headings
user.headings_voted_within_group(group).count < group.max_votable_headings
end
def headings_voted_by_user(user)
@@ -265,7 +265,7 @@ class Budget
end
def voted_in?(heading, user)
headings_voted_by_user(user).include?(heading.id)
user.headings_voted_within_group(group).where(id: heading.id).exists?
end
def ballotable_by?(user)

View File

@@ -6,20 +6,22 @@ class Budget
SUMMARY_MAX_LENGTH = 1000
DESCRIPTION_MAX_LENGTH = 2000
translates :summary, touch: true
translates :description, touch: true
include Globalizable
belongs_to :budget
belongs_to :next_phase, class_name: 'Budget::Phase', foreign_key: :next_phase_id
has_one :prev_phase, class_name: 'Budget::Phase', foreign_key: :next_phase_id
validates_translation :summary, length: { maximum: SUMMARY_MAX_LENGTH }
validates_translation :description, length: { maximum: DESCRIPTION_MAX_LENGTH }
validates :budget, presence: true
validates :kind, presence: true, uniqueness: { scope: :budget }, inclusion: { in: PHASE_KINDS }
validates :summary, length: { maximum: SUMMARY_MAX_LENGTH }
validates :description, length: { maximum: DESCRIPTION_MAX_LENGTH }
validate :invalid_dates_range?
validate :prev_phase_dates_valid?
validate :next_phase_dates_valid?
before_validation :sanitize_description
after_save :adjust_date_ranges
after_save :touch_budget
@@ -87,8 +89,5 @@ class Budget
end
end
def sanitize_description
self.description = WYSIWYGSanitizer.new.sanitize(description)
end
end
end

View File

@@ -0,0 +1,9 @@
class Budget::Phase::Translation < Globalize::ActiveRecord::Translation
before_validation :sanitize_description
private
def sanitize_description
self.description = WYSIWYGSanitizer.new.sanitize(description)
end
end

View File

@@ -0,0 +1,11 @@
class Budget::Translation < Globalize::ActiveRecord::Translation
validate :name_uniqueness_by_budget
def name_uniqueness_by_budget
if Budget.joins(:translations)
.where(name: name)
.where.not("budget_translations.budget_id": budget_id).any?
errors.add(:name, I18n.t("errors.messages.taken"))
end
end
end

View File

@@ -8,6 +8,10 @@ module Globalizable
def locales_not_marked_for_destruction
translations.reject(&:_destroy).map(&:locale)
end
def assign_model_to_translations
translations.each { |translation| translation.globalized_model = self }
end
end
class_methods do

View File

@@ -5,5 +5,15 @@ module Milestoneable
has_many :milestones, as: :milestoneable, dependent: :destroy
scope :with_milestones, -> { joins(:milestones).distinct }
has_many :progress_bars, as: :progressable
def primary_progress_bar
progress_bars.primary.first
end
def secondary_progress_bars
progress_bars.secondary
end
end
end

View File

@@ -3,6 +3,10 @@ module Sluggable
included do
before_validation :generate_slug, if: :generate_slug?
def self.find_by_slug_or_id(slug_or_id)
find_by_slug(slug_or_id) || find_by_id(slug_or_id)
end
end
def generate_slug

View File

@@ -88,6 +88,10 @@ class Debate < ActiveRecord::Base
cached_votes_total
end
def votes_score
cached_votes_score
end
def total_anonymous_votes
cached_anonymous_votes_total
end

View File

@@ -1,3 +0,0 @@
class LegacyLegislation < ActiveRecord::Base
has_many :annotations
end

View File

@@ -2,6 +2,7 @@ class Legislation::Process < ActiveRecord::Base
include ActsAsParanoidAliases
include Taggable
include Milestoneable
include Imageable
include Documentable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
@@ -18,8 +19,10 @@ class Legislation::Process < ActiveRecord::Base
translates :homepage, touch: true
include Globalizable
PHASES_AND_PUBLICATIONS = %i[draft_phase debate_phase allegations_phase proposals_phase
draft_publication result_publication].freeze
PHASES_AND_PUBLICATIONS = %i[homepage_phase draft_phase debate_phase allegations_phase
proposals_phase draft_publication result_publication].freeze
CSS_HEX_COLOR = /\A#?(?:[A-F0-9]{3}){1,2}\z/i
has_many :draft_versions, -> { order(:id) }, class_name: 'Legislation::DraftVersion',
foreign_key: 'legislation_process_id',
@@ -43,17 +46,21 @@ class Legislation::Process < ActiveRecord::Base
validates :allegations_end_date, presence: true, if: :allegations_start_date?
validates :proposals_phase_end_date, presence: true, if: :proposals_phase_start_date?
validate :valid_date_ranges
validates :background_color, format: { allow_blank: true, with: CSS_HEX_COLOR }
validates :font_color, format: { allow_blank: true, with: CSS_HEX_COLOR }
scope :open, -> { where("start_date <= ? and end_date >= ?", Date.current, Date.current)
.order('id DESC') }
scope :next, -> { where("start_date > ?", Date.current).order('id DESC') }
scope :past, -> { where("end_date < ?", Date.current).order('id DESC') }
scope :open, -> { where("start_date <= ? and end_date >= ?", Date.current, Date.current) }
scope :past, -> { where("end_date < ?", Date.current) }
scope :published, -> { where(published: true) }
scope :not_in_draft, -> { where("draft_phase_enabled = false or (draft_start_date IS NOT NULL and
draft_end_date IS NOT NULL and (draft_start_date > ? or
draft_end_date < ?))", Date.current, Date.current) }
def homepage_phase
Legislation::Process::Phase.new(start_date, end_date, homepage_enabled)
end
def draft_phase
Legislation::Process::Phase.new(draft_start_date, draft_end_date, draft_phase_enabled)
end

View File

@@ -47,7 +47,7 @@ class Legislation::Proposal < ActiveRecord::Base
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_supports, -> { reorder(cached_votes_score: :desc) }
scope :sort_by_random, -> { reorder("RANDOM()") }
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
scope :last_week, -> { where("proposals.created_at >= ?", 7.days.ago)}
@@ -96,6 +96,10 @@ class Legislation::Proposal < ActiveRecord::Base
cached_votes_total
end
def votes_score
cached_votes_score
end
def voters
User.active.where(id: votes_for.voters)
end

View File

@@ -14,7 +14,7 @@ class Milestone < ActiveRecord::Base
validates :milestoneable, presence: true
validates :publication_date, presence: true
before_validation :assign_milestone_to_translations
before_validation :assign_model_to_translations
validates_translation :description, presence: true, unless: -> { status_id.present? }
scope :order_by_publication_date, -> { order(publication_date: :asc, created_at: :asc) }
@@ -25,9 +25,4 @@ class Milestone < ActiveRecord::Base
80
end
private
def assign_milestone_to_translations
translations.each { |translation| translation.globalized_model = self }
end
end

View File

@@ -28,7 +28,6 @@ class Poll < ActiveRecord::Base
validate :date_range
scope :current, -> { where('starts_at <= ? and ? <= ends_at', Date.current.beginning_of_day, Date.current.beginning_of_day) }
scope :incoming, -> { where('? < starts_at', Date.current.beginning_of_day) }
scope :expired, -> { where('ends_at < ?', Date.current.beginning_of_day) }
scope :recounting, -> { Poll.where(ends_at: (Date.current.beginning_of_day - RECOUNT_DURATION)..Date.current.beginning_of_day) }
scope :published, -> { where('published = ?', true) }
@@ -45,20 +44,12 @@ class Poll < ActiveRecord::Base
starts_at <= timestamp && timestamp <= ends_at
end
def incoming?(timestamp = Date.current.beginning_of_day)
timestamp < starts_at
end
def expired?(timestamp = Date.current.beginning_of_day)
ends_at < timestamp
end
def self.current_or_incoming
current + incoming
end
def self.current_or_recounting_or_incoming
current + recounting + incoming
def self.current_or_recounting
current + recounting
end
def answerable_by?(user)

View File

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

View File

@@ -15,6 +15,10 @@ class Poll
shifts.empty? ? false : true
end
def unable_to_destroy?
(partial_results.count + recounts.count).positive?
end
private
def shifts

View File

@@ -35,6 +35,10 @@ class Poll
end
end
def unable_to_destroy?
booth.booth_assignments.map(&:unable_to_destroy?).any?
end
def destroy_officer_assignments
Poll::OfficerAssignment.where(booth_assignment: booth.booth_assignments,
officer: officer,

View File

@@ -0,0 +1,23 @@
class ProgressBar < ActiveRecord::Base
self.inheritance_column = nil
RANGE = 0..100
enum kind: %i[primary secondary]
belongs_to :progressable, polymorphic: true
translates :title, touch: true
include Globalizable
validates :progressable, presence: true
validates :kind, presence: true,
uniqueness: {
scope: [:progressable_type, :progressable_id],
conditions: -> { primary }
}
validates :percentage, presence: true, inclusion: RANGE, numericality: { only_integer: true }
before_validation :assign_model_to_translations
validates_translation :title, presence: true, unless: :primary?
end

View File

@@ -0,0 +1,3 @@
class ProgressBar::Translation < Globalize::ActiveRecord::Translation
delegate :primary?, to: :globalized_model
end

View File

@@ -4,13 +4,14 @@ class SiteCustomization::Image < ActiveRecord::Base
"social_media_icon" => [470, 246],
"social_media_icon_twitter" => [246, 246],
"apple-touch-icon-200" => [200, 200],
"budget_execution_no_image" => [800, 600]
"budget_execution_no_image" => [800, 600],
"map" => [420, 500]
}
has_attached_file :image
validates :name, presence: true, uniqueness: true, inclusion: { in: VALID_IMAGES.keys }
validates_attachment_content_type :image, content_type: ["image/png"]
validates_attachment_content_type :image, content_type: ["image/png", "image/jpeg"]
validate :check_image
def self.all_images

View File

@@ -1,5 +1,6 @@
class SiteCustomization::Page < ActiveRecord::Base
VALID_STATUSES = %w(draft published)
VALID_STATUSES = %w[draft published]
has_many :cards, class_name: "Widget::Card", foreign_key: "site_customization_page_id"
translates :title, touch: true
translates :subtitle, touch: true
@@ -12,9 +13,12 @@ class SiteCustomization::Page < ActiveRecord::Base
format: { with: /\A[0-9a-zA-Z\-_]*\Z/, message: :slug_format }
validates :status, presence: true, inclusion: { in: VALID_STATUSES }
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_same_locale, -> { joins(:translations).where("site_customization_page_translations.locale": I18n.locale) }
scope :published, -> { where(status: "published").sort_desc }
scope :sort_asc, -> { order("id ASC") }
scope :sort_desc, -> { order("id DESC") }
scope :with_more_info_flag, -> { where(status: "published", more_info_flag: true).sort_asc }
scope :with_same_locale, -> { joins(:translations).locale }
scope :locale, -> { where("site_customization_page_translations.locale": I18n.locale) }
def url
"/#{slug}"

2
app/models/tag.rb Normal file
View File

@@ -0,0 +1,2 @@
class Tag < ActsAsTaggableOn::Tag
end

View File

@@ -127,6 +127,14 @@ class User < ActiveRecord::Base
votes.for_budget_investments(Budget::Investment.where(group: group)).exists?
end
def headings_voted_within_group(group)
Budget::Heading.order("name").where(id: voted_investments.by_group(group).pluck(:heading_id))
end
def voted_investments
Budget::Investment.where(id: votes.for_budget_investments.pluck(:votable_id))
end
def administrator?
administrator.present?
end

View File

@@ -1,5 +1,6 @@
class Widget::Card < ActiveRecord::Base
include Imageable
belongs_to :page, class_name: "SiteCustomization::Page", foreign_key: "site_customization_page_id"
# table_name must be set before calls to 'translates'
self.table_name = "widget_cards"
@@ -15,6 +16,12 @@ class Widget::Card < ActiveRecord::Base
end
def self.body
where(header: false).order(:created_at)
where(header: false, site_customization_page_id: nil).order(:created_at)
end
#add widget cards to custom pages
def self.page(page_id)
where(site_customization_page_id: page_id)
end
end

View File

@@ -56,8 +56,7 @@
<% end %>
<% if feature?(:budgets) %>
<li class="section-title <%= "is-active" if controller_name == "budgets" ||
controller_name == "milestone_statuses" %>">
<li class="section-title <%= "is-active" if menu_budgets? %>">
<%= link_to admin_budgets_path do %>
<span class="icon-budget"></span>
<strong><%= t("admin.menu.budgets") %></strong>
@@ -123,7 +122,7 @@
<%= link_to t("admin.menu.site_customization.homepage"), admin_homepage_path %>
</li>
<li <%= "class=is-active" if controller_name == "pages" %>>
<li <%= "class=is-active" if menu_pages? || controller_name == "pages" %>>
<%= link_to t("admin.menu.site_customization.pages"), admin_site_customization_pages_path %>
</li>

View File

@@ -1,10 +1,16 @@
<div class="small-12 medium-6">
<%= form_for [:admin, @budget, @group], url: path do |f| %>
<%= render "admin/shared/globalize_locales", resource: @group %>
<%= f.text_field :name,
label: t("admin.budget_groups.form.name"),
maxlength: 50,
placeholder: t("admin.budget_groups.form.name") %>
<div class="small-12 medium-6">
<%= translatable_form_for [:admin, @budget, @group], url: path do |f| %>
<%= render 'shared/errors', resource: @group %>
<%= f.translatable_fields do |translations_form| %>
<%= translations_form.text_field :name,
label: t("admin.budget_groups.form.name"),
maxlength: 50,
placeholder: t("admin.budget_groups.form.name") %>
<% end %>
<% if @group.persisted? %>
<%= f.select :max_votable_headings,

View File

@@ -1,3 +1,7 @@
<%= back_link_to admin_budgets_path, t("admin.budget_groups.index.back") %>
<div class="clear"></div>
<h2 class="inline-block"><%= @budget.name %></h2>
<%= link_to t("admin.budget_groups.form.create"),

View File

@@ -1,11 +1,17 @@
<%= render "admin/shared/globalize_locales", resource: @heading %>
<div class="small-12 medium-6">
<%= form_for [:admin, @budget, @group, @heading], url: path do |f| %>
<%= translatable_form_for [:admin, @budget, @group, @heading], url: path do |f| %>
<%= f.text_field :name,
label: t("admin.budget_headings.form.name"),
maxlength: 50,
placeholder: t("admin.budget_headings.form.name") %>
<%= render 'shared/errors', resource: @heading %>
<%= f.translatable_fields do |translations_form| %>
<%= translations_form.text_field :name,
label: t("admin.budget_headings.form.name"),
maxlength: 50,
placeholder: t("admin.budget_headings.form.name") %>
<% end %>
<%= f.text_field :price,
label: t("admin.budget_headings.form.amount"),

View File

@@ -1,4 +1,4 @@
<%= back_link_to admin_budget_groups_path(@budget) %>
<%= back_link_to admin_budget_groups_path(@budget), t("admin.budget_headings.index.back") %>
<div class="clear"></div>
<h2 class="inline-block"><%= "#{@budget.name} / #{@group.name}" %></h2>

View File

@@ -1,4 +1,8 @@
<%= form_for [:admin, @phase.budget, @phase] do |f| %>
<%= render "admin/shared/globalize_locales", resource: @phase %>
<%= translatable_form_for [:admin, @phase.budget, @phase] do |f| %>
<%= render 'shared/errors', resource: @phase %>
<div class="small-12 medium-6 column">
<%= f.label :starts_at, t("admin.budget_phases.edit.start_date") %>
@@ -18,32 +22,34 @@
</div>
<div class="small-12 column">
<%= f.label :description, t("admin.budget_phases.edit.description") %>
<%= f.translatable_fields do |translations_form| %>
<span class="help-text" id="phase-description-help-text">
<%= t("admin.budget_phases.edit.description_help_text") %>
</span>
<%= f.label :description, t("admin.budget_phases.edit.description") %>
<%= f.cktext_area :description,
maxlength: Budget::Phase::DESCRIPTION_MAX_LENGTH,
ckeditor: { language: I18n.locale },
label: false %>
<span class="help-text" id="phase-description-help-text">
<%= t("admin.budget_phases.edit.description_help_text") %>
</span>
<div class="ckeditor">
<%= translations_form.cktext_area :description,
maxlength: Budget::Phase::DESCRIPTION_MAX_LENGTH,
label: false %>
</div>
<%= f.label :summary, t("admin.budget_phases.edit.summary") %>
<span class="help-text" id="phase-summary-help-text">
<%= t("admin.budget_phases.edit.summary_help_text") %>
</span>
<div class="ckeditor">
<%= translations_form.cktext_area :summary,
maxlength: Budget::Phase::SUMMARY_MAX_LENGTH,
label: false%>
</div>
<% end %>
</div>
<div class="small-12 column margin-top">
<%= f.label :summary, t("admin.budget_phases.edit.summary") %>
<span class="help-text" id="phase-summary-help-text">
<%= t("admin.budget_phases.edit.summary_help_text") %>
</span>
<%= f.cktext_area :summary,
maxlength: Budget::Phase::SUMMARY_MAX_LENGTH,
ckeditor: { language: I18n.locale },
label: false %>
</div>
<div class="small-12 column margin-top">
<%= f.check_box :enabled, label: t("admin.budget_phases.edit.enabled") %>

View File

@@ -1,12 +1,21 @@
<%= form_for [:admin, @budget] do |f| %>
<%= render "admin/shared/globalize_locales", resource: @budget %>
<%= translatable_form_for [:admin, @budget] do |f| %>
<%= render 'shared/errors', resource: @budget %>
<div class="small-12 medium-9 column">
<%= f.text_field :name, maxlength: Budget.title_max_length %>
<%= f.translatable_fields do |translations_form| %>
<%= translations_form.text_field :name,
label: t("activerecord.attributes.budget.name"),
maxlength: Budget.title_max_length,
placeholder: t("activerecord.attributes.budget.name") %>
<% end %>
</div>
<div class="margin-top">
<div class="small-12 medium-6 column">
<%= f.select :phase, budget_phases_select_options, selected: "drafting" %>
<%= f.select :phase, budget_phases_select_options %>
</div>
<div class="small-12 medium-3 column end">
<%= f.select :currency_symbol, budget_currency_symbol_select_options %>
@@ -62,8 +71,8 @@
</div>
<div class="float-right">
<% if @budget.balloting_process? %>
<%= link_to t("admin.budgets.winners.calculate"),
<% if display_calculate_winners_button?(@budget) %>
<%= link_to calculate_winner_button_text(@budget),
calculate_winners_admin_budget_path(@budget),
method: :put,
class: "button hollow" %>

View File

@@ -201,6 +201,52 @@
<hr>
</div>
<div class="images small-12 column">
<%= render 'images/nested_image', imageable: @process, f: f %>
</div>
<div class="small-12 column">
<hr>
</div>
<div class="small-12 column">
<h3><%= t("admin.legislation.processes.form.banner_title") %></h3>
</div>
<div class="small-6 large-3 column">
<%= f.label :background_color, nil, for: "background_color_input" %>
<p class="help-text"><%= t("admin.legislation.processes.form.color_help") %></p>
<div class="row collapse">
<div class="small-12 medium-6 column">
<%= f.text_field :background_color, label: false, type: :color %>
</div>
<div class="small-12 medium-6 column">
<%= f.text_field :background_color, label: false,
placeholder: "#e7f2fc",
id: "background_color_input" %>
</div>
</div>
</div>
<div class="small-6 large-3 column end">
<%= f.label :font_color, nil, for: "font_color_input" %>
<p class="help-text"><%= t("admin.legislation.processes.form.color_help") %></p>
<div class="row collapse">
<div class="small-12 medium-6 column">
<%= f.text_field :font_color, label: false, type: :color %>
</div>
<div class="small-12 medium-6 column">
<%= f.text_field :font_color, label: false,
placeholder: "#222222",
id: "font_color_input" %>
</div>
</div>
</div>
<div class="small-12 column">
<hr>
</div>
<%= f.translatable_fields do |translations_form| %>
<div class="small-12 medium-9 column">
<%= translations_form.text_field :title,

View File

@@ -0,0 +1,12 @@
<% provide :title do %>
<%= "#{t("admin.header.title")} - #{t("admin.menu.legislation")}" %> -
<%= "#{@process.title} - #{t("admin.progress_bars.index.title")}" %>
<% end %>
<%= back_link_to admin_legislation_process_milestones_path(@progressable),
t("admin.legislation.processes.edit.back") %>
<h2><%= @process.title %></h2>
<%= render "admin/legislation/processes/subnav", process: @process, active: "milestones" %>
<%= render "admin/progress_bars/progress_bars", progressable: @process %>

View File

@@ -18,7 +18,7 @@
<tr id="<%= dom_id(proposal) %>" class="legislation_proposal">
<td class="text-center"><%= proposal.id %></td>
<td><%= proposal.title %></td>
<td class="text-center"><%= proposal.cached_votes_up %></td>
<td class="text-center"><%= proposal.votes_score %></td>
<td class="select"><%= render "select_proposal", proposal: proposal %></td>
</tr>
<% end %>

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