diff --git a/.gitignore b/.gitignore index a6f2826c3..7bb7a3090 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ public/sitemap.xml public/system/ + +#Netbeans projects files +/nbproject diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index bcadd2a25..4704696e5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -42,7 +42,6 @@ Layout/DotPosition: - 'app/controllers/admin/poll/officer_assignments_controller.rb' - 'app/controllers/admin/poll/polls_controller.rb' - 'app/controllers/admin/poll/recounts_controller.rb' - - 'app/controllers/officing/final_recounts_controller.rb' - 'app/controllers/officing/residence_controller.rb' - 'app/controllers/officing/results_controller.rb' - 'app/models/poll/officer.rb' @@ -191,7 +190,6 @@ Layout/MultilineMethodCallIndentation: - 'app/controllers/admin/poll/officer_assignments_controller.rb' - 'app/controllers/admin/poll/polls_controller.rb' - 'app/controllers/admin/poll/recounts_controller.rb' - - 'app/controllers/officing/final_recounts_controller.rb' - 'app/controllers/officing/residence_controller.rb' - 'app/controllers/officing/results_controller.rb' - 'app/models/poll/officer.rb' @@ -289,6 +287,7 @@ Lint/StringConversionInInterpolation: - 'app/models/poll/null_result.rb' - 'app/models/poll/partial_result.rb' - 'app/models/poll/white_result.rb' + - 'app/models/poll/total_result.rb' # Offense count: 15 # Cop supports --auto-correct. @@ -473,7 +472,6 @@ Style/ConditionalAssignment: - 'app/controllers/admin/poll/questions_controller.rb' - 'app/controllers/comments_controller.rb' - 'app/controllers/management/spending_proposals_controller.rb' - - 'app/controllers/officing/final_recounts_controller.rb' - 'app/controllers/spending_proposals_controller.rb' - 'app/controllers/verification/sms_controller.rb' - 'lib/graph_ql/api_types_creator.rb' @@ -565,6 +563,7 @@ Style/MutableConstant: - 'app/models/poll/null_result.rb' - 'app/models/poll/partial_result.rb' - 'app/models/poll/white_result.rb' + - 'app/models/poll/total_result.rb' - 'app/models/proposal.rb' - 'app/models/signature_sheet.rb' - 'app/models/site_customization/content_block.rb' diff --git a/CUSTOMIZE_EN.md b/CUSTOMIZE_EN.md deleted file mode 100644 index 3a97b96de..000000000 --- a/CUSTOMIZE_EN.md +++ /dev/null @@ -1,221 +0,0 @@ -# Customization - -You can modify your own CONSUL to have your custom visual style, but first you'll have to create a fork from [https://github.com/consul/consul](https://github.com/consul/consul) using Github's "fork" button on top right corner. You can use any other service like Gitlab, but don't forget to put a reference link back to CONSUL on the footer to comply with project's license (GPL Affero 3). - -We've created an specific structure where you can overwrite and customize the application in a way that will let you keep updating it from CONSUL's main repository, without having conflicts on code merging or risking loosing your customization changes. We try to make CONSUL as vanilla as possible to help other developers onboard the codebase. - -## Special Folders and Files - -In order to customize your CONSUL fork, you'll make use of some `custom` folders on the following paths: - -* `config/locales/custom/` -* `app/assets/images/custom/` -* `app/views/custom/` -* `app/controllers/custom/` -* `app/models/custom/` - -Also these are the files where you can apply some customization: - -* `app/assets/stylesheets/custom.css` -* `app/assets/stylesheets/_custom_settings.css` -* `app/assets/javascripts/custom.js` -* `Gemfile_custom` -* `config/application.custom.rb` - -### Internationalization - -If you want to add a new language translation of the user-facing texts you can find them organized in YML files under `config/locales/` folder. Take a look at the official Ruby on Rails [internationalization guide](http://guides.rubyonrails.org/i18n.html) to better understand the translations system. - -If you just want to change some of the existing texts, you can just drop your changes at the `config/locales/custom/` folder, we strongly recommend to include only those text that you want to change instead of a whole copy of the original file. For example if you want to customize the text "Ayuntamiento de Madrid, 2016" that appears on every page's footer, firstly you want to locate where it's used (`app/views/layouts/_footer.html.erb`), we can see code is: - -```ruby -<%= t("layouts.footer.copyright", year: Time.current.year) %> -``` - -And that the text its located at the file `config/locales/es/general.yml` following this structure (we're only displaying in the following snippet the relevant parts): - -```yml -es: - layouts: - footer: - copyright: Ayuntamiento de Madrid, %{year} - -``` - -So in order to customize it, we would create a new file `config/locales/custom/es/general.yml` with just that content, and change "Ayuntamiento de Madrid" for our organization name. We strongly recommend to make copies from `config/locales/` and modify or delete the lines as needed to keep the indentation structure and avoid issues. - -### Images - -If you want to overwrite any image, firstly you need to findout the filename, and by defaul it will be located under `app/assets/images`. For example if you want to change the header logo (`app/assets/images/logo_header.png`) you must create another file with the exact same file name under `app/assets/images/custom` folder. The images and icons that you will most likely want to change are: - -* apple-touch-icon-200.png -* icon_home.png -* logo_email.png -* logo_header.png -* map.jpg -* social_media_icon.png -* social_media_icon_twitter.png - -### Views (HTML) - -If you want to change any page HTML you can just find the correct file under the `app/views` folder and put a copy at `app/views/custom` keeping as well any sub-folder structure, and then apply your customizations. For example if you want to customize `app/views/pages/conditions.html` you'll have to make a copy at `app/views/custom/pages/conditions.html.erb` (note the `pages` subdirectory). - -### CSS - -In order to make changes to any CSS selector (custom style sheets), you can add them directly at `app/assets/stylesheets/custom.scss`. For example to change the header color (`.top-links`) you can just add: - -```css -.top-links { - background: red; -} -``` - -If you want to change any [foundation](http://foundation.zurb.com/) variable, you can do it at the `app/assets/stylesheets/_custom_settings.scss` file. For example to change the main application color just add: - -```css -$brand: #446336; -``` - -We use [SASS, with SCSS syntax](http://sass-lang.com/guide) as CSS preprocessor. - -### Javascript - -If you want to add some custom Javascript code, `app/assets/javascripts/custom.js` is the file to do it. For example to create a new alert just add: - -```js -$(function(){ - alert('foobar'); -}); -``` - -### Models - -If you need to create new models or customize existent ones, you can do it so at the `app/models/custom` folder. Keep in mind that for old models you'll need to firstly require the dependency. - -For example for Madrid's City Hall fork its required to check the zip code's format (it always starts with 280 followed by 2 digits). That check is at `app/models/custom/verification/residence.rb`: - -```ruby -require_dependency Rails.root.join('app', 'models', 'verification', 'residence').to_s - -class Verification::Residence - - validate :postal_code_in_madrid - validate :residence_in_madrid - - def postal_code_in_madrid - errors.add(:postal_code, I18n.t('verification.residence.new.error_not_allowed_postal_code')) unless valid_postal_code? - end - - def residence_in_madrid - return if errors.any? - - unless residency_valid? - errors.add(:residence_in_madrid, false) - store_failed_attempt - Lock.increase_tries(user) - end - end - - private - - def valid_postal_code? - postal_code =~ /^280/ - end - -end -``` - -Do not forget to cover your changes with a test at the `spec/models/custom` folder. Following the example we could create `spec/models/custom/residence_spec.rb`: - -```ruby -require 'rails_helper' - -describe Verification::Residence do - - let(:residence) { build(:verification_residence, document_number: "12345678Z") } - - describe "verification" do - - describe "postal code" do - it "should be valid with postal codes starting with 280" do - residence.postal_code = "28012" - residence.valid? - expect(residence.errors[:postal_code].size).to eq(0) - - residence.postal_code = "28023" - residence.valid? - expect(residence.errors[:postal_code].size).to eq(0) - end - - it "should not be valid with postal codes not starting with 280" do - residence.postal_code = "12345" - residence.valid? - expect(residence.errors[:postal_code].size).to eq(1) - - residence.postal_code = "13280" - residence.valid? - expect(residence.errors[:postal_code].size).to eq(1) - expect(residence.errors[:postal_code]).to include("In order to be verified, you must be registered in the municipality of Madrid.") - end - end - - end - -end -``` - -### Controllers - -TODO! - -### Gemfile - -To add new gems (libraries) you can edit the `Gemfile_custom` file. For example to add [rails-footnotes](https://github.com/josevalim/rails-footnotes) gem you would just add: - -```ruby -gem 'rails-footnotes', '~> 4.0' -``` - -And then just do the classic Ruby on Rails flow `bundle install` and following any gem specific install steps from it's own documentation. - -### application.rb - -If you need to extend or modify the `config/application.rb` just do it at the `config/application_custom.rb` file. For example if you want to change de default language to English, just add: - -```ruby -module Consul - class Application < Rails::Application - config.i18n.default_locale = :en - config.i18n.available_locales = [:en, :es] - end -end -``` - -Remeber that in order to see this changes live you'll need to restart the server. - -### lib/ - -TODO - -### public/ - -TODO - -### Seeds - -TODO - -## Updating - -We recommend you to add CONSUL as remote: - -``` -git remote add consul https://github.com/consul/consul -``` - -And then just grab lastest changes on to a branch of your own repo with: - -``` -git checkout -b consul_update -git pull consul master -``` diff --git a/CUSTOMIZE_ES.md b/CUSTOMIZE_ES.md deleted file mode 100644 index 26204c4e4..000000000 --- a/CUSTOMIZE_ES.md +++ /dev/null @@ -1,221 +0,0 @@ -# Personalización - -Puedes modificar CONSUL y ponerle tu propia imagen, para esto debes primero hacer un fork de [https://github.com/consul/consul](https://github.com/consul/consul) creando un repositorio nuevo en Github. Puedes usar otro servicio como Gitlab, pero no te olvides de poner el enlace en el footer a tu repositorio en cumplimiento con la licencia de este proyecto (GPL Affero 3). - -Hemos creado una estructura específica donde puedes sobreescribir y personalizar la aplicación para que puedas actualizar sin que tengas problemas al hacer merge y se sobreescriban por error tus cambios. Intentamos que CONSUL sea una aplicación Ruby on Rails lo más plain vanilla posible para facilitar el acceso de nuevas desarrolladoras. - -## Ficheros y directorios especiales - -Para adaptar tu fork de CONSUL puedes utilizar alguno de los directorios `custom` que están en las rutas: - -* `config/locales/custom/` -* `app/assets/images/custom/` -* `app/views/custom/` -* `app/controllers/custom/` -* `app/models/custom/` - -Aparte de estos directorios también cuentas con ciertos ficheros para: - -* `app/assets/stylesheets/custom.css` -* `app/assets/stylesheets/_custom_settings.css` -* `app/assets/javascripts/custom.js` -* `Gemfile_custom` -* `config/application.custom.rb` - -### Internacionalización - -Si quieres modificar algún texto de la web deberías encontrarlos en los ficheros formato YML disponibles en `config/locales/`. Puedes leer la [guía de internacionalización](http://guides.rubyonrails.org/i18n.html) de Ruby on Rails sobre como funciona este sistema. - -Las adaptaciones los debes poner en el directorio `config/locales/custom/`, recomendamos poner solo los textos que quieras personalizar. Por ejemplo si quieres personalizar el texto de "Ayuntamiento de Madrid, 2016" que se encuentra en el footer en todas las páginas, primero debemos ubicar en que plantilla se encuentra (`app/views/layouts/_footer.html.erb`), vemos que en el código pone lo siguiente: - -```ruby -<%= t("layouts.footer.copyright", year: Time.current.year) %> -``` - -Y que en el fichero `config/locales/es/general.yml` sigue esta estructura (solo ponemos lo relevante para este caso): - -```yml -es: - layouts: - footer: - copyright: Ayuntamiento de Madrid, %{year} - -``` - -Si creamos el fichero `config/locales/custom/es/general.yml` y modificamos "Ayuntamiento de Madrid" por el nombre de la organización que se este haciendo la modificación. Recomendamos directamente copiar los ficheros `config/locales/` e ir revisando y corrigiendo las que querramos, borrando las líneas que no querramos traducir. - -### Imágenes - -Si quieres sobreescribir alguna imagen debes primero fijarte el nombre que tiene, por defecto se encuentran en `app/assets/images`. Por ejemplo si quieres modificar `app/assets/images/logo_header.png` debes poner otra con ese mismo nombre en el directorio `app/assets/images/custom`. Los iconos que seguramente quieras modificar son: - -* apple-touch-icon-200.png -* icon_home.png -* logo_email.png -* logo_header.png -* map.jpg -* social_media_icon.png -* social_media_icon_twitter.png - -### Vistas (HTML) - -Si quieres modificar el HTML de alguna página puedes hacerlo copiando el HTML de `app/views` y poniendolo en `app/views/custom` respetando los subdirectorios que encuentres ahí. Por ejemplo si quieres modificar `app/views/pages/conditions.html` debes copiarlo y modificarla en `app/views/custom/pages/conditions.html.erb` - -### CSS - -Si quieres cambiar algun selector CSS (de las hojas de estilo) puedes hacerlo en el fichero `app/assets/stylesheets/custom.scss`. Por ejemplo si quieres cambiar el color del header (`.top-links`) puedes hacerlo agregando: - -```css -.top-links { - background: red; -} -``` - -Si quieres cambiar alguna variable de [foundation](http://foundation.zurb.com/) puedes hacerlo en el fichero `app/assets/stylesheets/_custom_settings.scss`. Por ejemplo para cambiar el color general de la aplicación puedes hacerlo agregando: - -```css -$brand: #446336; -``` - -Usamos un preprocesador de CSS, [SASS, con la sintaxis SCSS](http://sass-lang.com/guide). - -### Javascript - -Si quieres agregar código Javascript puedes hacerlo en el fichero `app/assets/javascripts/custom.js`. Por ejemplo si quieres que salga una alerta puedes poner lo siguiente: - -```js -$(function(){ - alert('foobar'); -}); -``` - -### Modelos - -Si quieres agregar modelos nuevos, o modificar o agregar métodos a uno ya existente puedes hacerlo en `app/models/custom`. En el caso de los modelos antiguos debes primero hacer un require de la dependencia. - -Por ejemplo en el caso del Ayuntamiento de Madrid se requiere comprobar que el código postal durante la verificación sigue un cierto formato (empieza con 280). Esto se realiza creando este fichero en `app/models/custom/verification/residence.rb`: - -```ruby -require_dependency Rails.root.join('app', 'models', 'verification', 'residence').to_s - -class Verification::Residence - - validate :postal_code_in_madrid - validate :residence_in_madrid - - def postal_code_in_madrid - errors.add(:postal_code, I18n.t('verification.residence.new.error_not_allowed_postal_code')) unless valid_postal_code? - end - - def residence_in_madrid - return if errors.any? - - unless residency_valid? - errors.add(:residence_in_madrid, false) - store_failed_attempt - Lock.increase_tries(user) - end - end - - private - - def valid_postal_code? - postal_code =~ /^280/ - end - -end -``` - -No olvides poner los tests relevantes en `spec/models/custom`, siguiendo con el ejemplo pondriamos lo siguiente en `spec/models/custom/residence_spec.rb`: - -```ruby -require 'rails_helper' - -describe Verification::Residence do - - let(:residence) { build(:verification_residence, document_number: "12345678Z") } - - describe "verification" do - - describe "postal code" do - it "should be valid with postal codes starting with 280" do - residence.postal_code = "28012" - residence.valid? - expect(residence.errors[:postal_code].size).to eq(0) - - residence.postal_code = "28023" - residence.valid? - expect(residence.errors[:postal_code].size).to eq(0) - end - - it "should not be valid with postal codes not starting with 280" do - residence.postal_code = "12345" - residence.valid? - expect(residence.errors[:postal_code].size).to eq(1) - - residence.postal_code = "13280" - residence.valid? - expect(residence.errors[:postal_code].size).to eq(1) - expect(residence.errors[:postal_code]).to include("In order to be verified, you must be registered in the municipality of Madrid.") - end - end - - end - -end -``` - -### Controladores - -TODO - -### Gemfile - -Para agregar librerías (gems) nuevas puedes hacerlo en el fichero `Gemfile_custom`. Por ejemplo si quieres agregar la gema [rails-footnotes](https://github.com/josevalim/rails-footnotes) debes hacerlo agregandole - -```ruby -gem 'rails-footnotes', '~> 4.0' -``` - -Y siguiendo el flujo clásico en Ruby on Rails (`bundle install` y seguir con los pasos específicos de la gema en la documentación) - -### application.rb - -Cuando necesites extender o modificar el `config/application.rb` puedes hacerlo a través del fichero `config/application_custom.rb`. Por ejemplo si quieres modificar el idioma por defecto al inglés pondrías lo siguiente: - -```ruby -module Consul - class Application < Rails::Application - config.i18n.default_locale = :en - config.i18n.available_locales = [:en, :es] - end -end -``` - -Recuerda que para ver reflejado estos cambios debes reiniciar el servidor de desarrollo. - -### lib/ - -TODO - -### public/ - -TODO - -### Seeds - -TODO - -## Actualizar - -Te recomendamos que agregues el remote de CONSUL para facilitar este proceso de merge: - -``` -git remote add consul https://github.com/consul/consul -``` - -Con esto puedes actualizarte con - -``` -git checkout -b consul_update -git pull consul master -``` diff --git a/Gemfile b/Gemfile index a1a0ecf7a..3f17d753b 100644 --- a/Gemfile +++ b/Gemfile @@ -35,7 +35,7 @@ gem 'omniauth-twitter', '~> 1.4.0' gem 'paperclip', '~> 5.1.0' gem 'jquery-fileupload-rails' gem 'paranoia', '~> 2.3.1' -gem 'pg', '~> 0.20.0' +gem 'pg', '~> 0.21.0' gem 'pg_search', '~> 2.0.1' gem 'rails-assets-markdown-it', '~> 8.2.1', source: 'https://rails-assets.org' gem 'redcarpet', '~> 3.4.0' @@ -72,7 +72,7 @@ end group :test do gem 'capybara', '~> 2.14.0' gem 'coveralls', '~> 0.8.21', require: false - gem 'database_cleaner', '~> 1.5.3' + gem 'database_cleaner', '~> 1.6.1' gem 'email_spec', '~> 2.1.0' gem 'poltergeist', '~> 1.15.0' gem 'rspec-rails', '~> 3.6' @@ -85,7 +85,7 @@ group :development do gem 'capistrano3-delayed-job', '~> 1.7.3' gem 'mdl', '~> 0.4.0', require: false gem 'rvm1-capistrano3', '~> 1.4.0', require: false - gem 'scss_lint', '~> 0.53.0', require: false + gem 'scss_lint', '~> 0.54.0', require: false gem 'web-console', '~> 3.3.0' end diff --git a/Gemfile.lock b/Gemfile.lock index c33459f75..f15f0c6e9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -119,7 +119,7 @@ GEM tins (~> 1.6) daemons (1.2.4) dalli (2.7.6) - database_cleaner (1.5.3) + database_cleaner (1.6.1) debug_inspector (0.0.3) delayed_job (4.1.3) activesupport (>= 3.0, < 5.2) @@ -149,6 +149,7 @@ GEM launchy (~> 2.1) mail (~> 2.6) errbase (0.0.3) + erubi (1.6.1) erubis (2.7.0) execjs (2.7.0) factory_girl (4.8.0) @@ -187,11 +188,11 @@ GEM rack socksify i18n (0.8.6) - i18n-tasks (0.9.15) + i18n-tasks (0.9.18) activesupport (>= 4.0.2) ast (>= 2.1.0) easy_translate (>= 0.5.0) - erubis + erubi highline (>= 1.7.3) i18n parser (>= 2.2.3.0) @@ -302,7 +303,7 @@ GEM activerecord (>= 4.0, < 5.2) parser (2.4.0.0) ast (~> 2.2) - pg (0.20.0) + pg (0.21.0) pg_search (2.0.1) activerecord (>= 4.2) activesupport (>= 4.2) @@ -357,7 +358,7 @@ GEM responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) - rinku (2.0.2) + rinku (2.0.3) rollbar (2.14.1) multi_json rspec-core (3.6.0) @@ -406,7 +407,7 @@ GEM nokogiri (>= 1.4.0) nori (~> 2.4) wasabi (~> 3.4) - scss_lint (0.53.0) + scss_lint (0.54.0) rake (>= 0.9, < 13) sass (~> 3.4.20) simplecov (0.14.1) @@ -505,7 +506,7 @@ DEPENDENCIES coveralls (~> 0.8.21) daemons (~> 1.2.4) dalli (~> 2.7.6) - database_cleaner (~> 1.5.3) + database_cleaner (~> 1.6.1) delayed_job_active_record (~> 4.1.0) devise (~> 3.5.7) devise-async (~> 0.10.2) @@ -536,7 +537,7 @@ DEPENDENCIES omniauth-twitter (~> 1.4.0) paperclip (~> 5.1.0) paranoia (~> 2.3.1) - pg (~> 0.20.0) + pg (~> 0.21.0) pg_search (~> 2.0.1) poltergeist (~> 1.15.0) quiet_assets (~> 1.1.0) @@ -552,7 +553,7 @@ DEPENDENCIES rvm1-capistrano3 (~> 1.4.0) sass-rails (~> 5.0, >= 5.0.4) savon (~> 2.11.1) - scss_lint (~> 0.53.0) + scss_lint (~> 0.54.0) sitemap_generator (~> 5.3.1) social-share-button (~> 0.10) spring (~> 2.0.1) diff --git a/README.md b/README.md index 6b8eed086..48f6bfcb8 100644 --- a/README.md +++ b/README.md @@ -23,25 +23,20 @@ This is the opensource code repository of the eParticipation website CONSUL, ori Development started on [2015 July 15th](https://github.com/consul/consul/commit/8db36308379accd44b5de4f680a54c41a0cc6fc6). Code was deployed to production on 2015 september 7th to [decide.madrid.es](https://decide.madrid.es). Since then new features are added often. You can take a look at the current features in [features]( http://www.decide.es/en/) or [docs](https://github.com/consul/consul/tree/master/doc) and future features in the [open issues list](https://github.com/consul/consul/issues). For current status on upcoming features go to [Roadmap](https://github.com/consul/consul/projects/6) -## Tech stack - -The application backend is written in the [Ruby language](https://www.ruby-lang.org/) using the [Ruby on Rails](http://rubyonrails.org/) framework. - -Frontend tools used include [SCSS](http://sass-lang.com/) over [Foundation](http://foundation.zurb.com/) for the styles. - ## Configuration for development and test environments -**NOTE**: For more detailed instructions check the [docs](https://github.com/consul/consul/tree/master/doc/en/dev_test_setup.md) +**NOTE**: For more detailed instructions check the [docs](https://github.com/consul/docs/tree/master/en/getting_started/prerequisites) Prerequisites: install git, Ruby 2.3.2, bundler gem, and PostgreSQL (>=9.4). -``` +```bash git clone https://github.com/consul/consul.git cd consul bundle install cp config/database.yml.example config/database.yml cp config/secrets.yml.example config/secrets.yml -bin/rake db:setup +bin/rake db:create +bin/rake db:migrate bin/rake db:dev_seed RAILS_ENV=test rake db:setup ``` @@ -60,20 +55,6 @@ Run the tests with: bin/rspec ``` -If you add SCSS code you can check it with: - -``` -scss-lint -``` - -To maintain accesibility level, if you add new colors use a [Color contrast checker](http://webaim.org/resources/contrastchecker/) (WCAG AA is mandatory, WCAG AAA is recommended) - -If you work on Coffeescript code you can check it with [coffeelint](http://www.coffeelint.org/) (install with `npm install -g coffeelint`) : - -``` -coffeelint . -``` - You can use the default admin user from the seeds file: **user:** admin@consul.dev @@ -84,18 +65,9 @@ But for some actions like voting, you will need a verified user, the seeds file **user:** verified@consul.dev **pass:** 12345678 -### Customization +## Documentation -Read more on documentation: - -* English: [CUSTOMIZE_EN.md](CUSTOMIZE_EN.md) -* Spanish: [CUSTOMIZE_ES.md](CUSTOMIZE_ES.md) - -### OAuth - -To test authentication services with external OAuth suppliers - right now Twitter, Facebook and Google - you'll need to create an "application" in each of the supported platforms and set the *key* and *secret* provided in your *secrets.yml* - -In the case of Google, verify that the APIs *Contacts API* and *Google+ API* are enabled for the application. +Please check the ongoing documentation at https://consul_docs.gitbooks.io/docs/content/ to learn more about how to start your own CONSUL fork, install it, customize it and learn to use it from an administrator/maintainer perspective. You can contribute to it at https://github.com/consul/docs ## License @@ -104,7 +76,3 @@ Code published under AFFERO GPL v3 (see [LICENSE-AGPLv3.txt](LICENSE-AGPLv3.txt) ## Contributions See [CONTRIBUTING.md](CONTRIBUTING.md) - -## Brand guidelines - -If you want to use CONSUL logo you can [download the guidelines](https://raw.githubusercontent.com/consul/consul/master/public/consul_brand.zip) which contains a use guide and different versions and sizes of the logo. \ No newline at end of file diff --git a/README_ES.md b/README_ES.md index 155ab957f..b22312e29 100644 --- a/README_ES.md +++ b/README_ES.md @@ -23,14 +23,9 @@ Este es el repositorio de código abierto de la Aplicación de Participación Ci El desarrollo de esta aplicación comenzó el [15 de Julio de 2015](https://github.com/consul/consul/commit/8db36308379accd44b5de4f680a54c41a0cc6fc6) y el código fue puesto en producción el día 7 de Septiembre de 2015 en [decide.madrid.es](https://decide.madrid.es). Desde entonces se le añaden mejoras y funcionalidades constantemente. Las funcionalidades actuales se pueden consultar en [características](http://www.decide.es/es/) o en la [documentación](https://github.com/consul/consul/tree/master/doc) y las siguientes funcionaliades en la lista de [tareas por hacer](https://github.com/consul/consul/issues). Para conocer el estado actual de las próximas caracteristicas, vaya a [Roadmap](https://github.com/consul/consul/projects/6) -## Tecnología - -El backend de esta aplicación se desarrolla con el lenguaje de programación [Ruby](https://www.ruby-lang.org/) sobre el *framework* [Ruby on Rails](http://rubyonrails.org/). -Las herramientas utilizadas para el frontend no están cerradas aún. Los estilos de la página usan [SCSS](http://sass-lang.com/) sobre [Foundation](http://foundation.zurb.com/) - ## Configuración para desarrollo y tests -**NOTA**: para unas instrucciones más detalladas consulta la [documentación](https://github.com/consul/consul/tree/master/doc/es/dev_test_setup.md) +**NOTA**: para unas instrucciones más detalladas consulta la [documentación](https://github.com/consul/docs/tree/master/es/getting_started/prerequisites) Prerequisitos: tener instalado git, Ruby 2.3.2, la gema `bundler` y PostgreSQL (9.4 o superior). @@ -41,7 +36,8 @@ cd consul bundle install cp config/database.yml.example config/database.yml cp config/secrets.yml.example config/secrets.yml -bin/rake db:setup +bin/rake db:create +bin/rake db:migrate bin/rake db:dev_seed RAILS_ENV=test rake db:setup ``` @@ -60,20 +56,6 @@ Para ejecutar los tests: bin/rspec ``` -Si añades código SCSS puedes revisarlo con: - -``` -scss-lint -``` - -Para mantener el nivel de accesibilidad, si añades colores nuevos utiliza un [Comprobador de contraste de color](http://webaim.org/resources/contrastchecker/) (WCAG AA es obligatorio, WCAG AAA es recomendable) - -Si trabajas en código coffeescript puedes revisarlo con [coffeelint](http://www.coffeelint.org/) (instalalo con `npm install -g coffeelint`) : - -``` -coffeelint . -``` - Puedes usar el usuario administrador por defecto del fichero seeds: **user:** admin@consul.dev @@ -84,15 +66,9 @@ Pero para ciertas acciones, como apoyar, necesitarás un usuario verificado, el **user:** verified@consul.dev **pass:** 12345678 -### Customización +## Documentación -Ver fichero [CUSTOMIZE_ES.md](CUSTOMIZE_ES.md) - -### OAuth - -Para probar los servicios de autenticación mediante proveedores externos OAuth — en este momento Twitter, Facebook y Google —, necesitas crear una "aplicación" en cada una de las plataformas soportadas y configurar la *key* y el *secret* proporcionados en tu *secrets.yml* - -En el caso de Google, comprueba que las APIs *Contacts API* y *Google+ API* están habilitadas para la aplicación. +Por favor visita la documentación que está siendo completada en https://consul_docs.gitbooks.io/docs/content/ para conocer más sobre este proyecto, como comenzar tu propio fork, instalarlo, customizarlo y usarlo como administrador/mantenedor. Puedes colaborar en ella en https://github.com/consul/docs ## Licencia @@ -101,7 +77,3 @@ El código de este proyecto está publicado bajo la licencia AFFERO GPL v3 (ver ## Contribuciones Ver fichero [CONTRIBUTING_ES.md](CONTRIBUTING_ES.md) - -## Guía de estilo - -Si quieres usar el logo de CONSUL puedes [descargar la guía de estilo](https://raw.githubusercontent.com/consul/consul/master/public/consul_brand.zip) que contiene una guía de uso y diferentes versiones y tamaños del logo. \ No newline at end of file diff --git a/app/assets/javascripts/comments.js.coffee b/app/assets/javascripts/comments.js.coffee index fefe85544..94845ca78 100644 --- a/app/assets/javascripts/comments.js.coffee +++ b/app/assets/javascripts/comments.js.coffee @@ -5,6 +5,8 @@ App.Comments = this.update_comments_count() add_reply: (parent_id, response_html) -> + if $("##{parent_id} .comment-children").length == 0 + $("##{parent_id}").append("
  • ") $("##{parent_id} .comment-children:first").prepend($(response_html)) this.update_comments_count() diff --git a/app/assets/javascripts/embed_video.js.coffee b/app/assets/javascripts/embed_video.js.coffee index 85c18ac8e..0825d1b80 100644 --- a/app/assets/javascripts/embed_video.js.coffee +++ b/app/assets/javascripts/embed_video.js.coffee @@ -1,7 +1,6 @@ App.EmbedVideo = - + initialize: -> $('#js-embedded-video').each -> - code = $(this).data("video-code") + code = $(this).data("video-code") $('#js-embedded-video').html(code) - \ No newline at end of file diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index a2897e6f9..6ac728e2c 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -54,7 +54,7 @@ $admin-color: #cf3638; } } - th { + th, td { text-align: left; &.text-center { diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index 2170387df..9d7b31a12 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -206,7 +206,7 @@ a { .menu.simple { border-bottom: 1px solid $border; - margin: $line-height 0; + margin-bottom: $line-height; li { padding-bottom: rem-calc(7); diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index e10c0327f..84e80f01f 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -314,6 +314,7 @@ .debate-show, .proposal-show, +.poll-question-show, .investment-project-show, .budget-investment-show, .polls-show, @@ -895,9 +896,14 @@ } .help-header { + background: #fafafa; + border-bottom: 1px solid #eee; + padding-bottom: $line-height / 2; + padding-top: $line-height; h1 { font-size: rem-calc(24); + text-transform: uppercase; } } diff --git a/app/controllers/admin/budget_investment_milestones_controller.rb b/app/controllers/admin/budget_investment_milestones_controller.rb index a4b8acc5d..fb5f42384 100644 --- a/app/controllers/admin/budget_investment_milestones_controller.rb +++ b/app/controllers/admin/budget_investment_milestones_controller.rb @@ -48,7 +48,7 @@ class Admin::BudgetInvestmentMilestonesController < Admin::BaseController end def load_budget_investment_milestone - @milestone = Budget::Investment::Milestone.find params[:id] + @milestone = Budget::Investment::Milestone.find params[:id] end diff --git a/app/controllers/admin/poll/booths_controller.rb b/app/controllers/admin/poll/booths_controller.rb index ff3700436..4b322f0b2 100644 --- a/app/controllers/admin/poll/booths_controller.rb +++ b/app/controllers/admin/poll/booths_controller.rb @@ -30,6 +30,11 @@ class Admin::Poll::BoothsController < Admin::BaseController end end + def available + @booths = Poll::Booth.available.order(name: :asc).page(params[:page]) + render :index + end + private def booth_params diff --git a/app/controllers/admin/poll/questions_controller.rb b/app/controllers/admin/poll/questions_controller.rb index ab7297a95..3bbe1c395 100644 --- a/app/controllers/admin/poll/questions_controller.rb +++ b/app/controllers/admin/poll/questions_controller.rb @@ -1,4 +1,6 @@ class Admin::Poll::QuestionsController < Admin::BaseController + include CommentableActions + load_and_authorize_resource :poll load_and_authorize_resource :question, class: 'Poll::Question' @@ -20,6 +22,7 @@ class Admin::Poll::QuestionsController < Admin::BaseController def create @question.author = @question.proposal.try(:author) || current_user + recover_documents_from_cache(@question) if @question.save redirect_to admin_question_path(@question) @@ -29,6 +32,7 @@ class Admin::Poll::QuestionsController < Admin::BaseController end def show + @document = Document.new(documentable: @question) end def edit @@ -54,7 +58,8 @@ class Admin::Poll::QuestionsController < Admin::BaseController private def question_params - params.require(:poll_question).permit(:poll_id, :title, :question, :description, :proposal_id, :valid_answers) + params.require(:poll_question).permit(:poll_id, :title, :question, :description, :proposal_id, :valid_answers, :video_url, + documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id]) end def search_params diff --git a/app/controllers/admin/poll/shifts_controller.rb b/app/controllers/admin/poll/shifts_controller.rb index 8a808a7a9..168284474 100644 --- a/app/controllers/admin/poll/shifts_controller.rb +++ b/app/controllers/admin/poll/shifts_controller.rb @@ -2,20 +2,21 @@ class Admin::Poll::ShiftsController < Admin::BaseController before_action :load_booth before_action :load_polls + before_action :load_officer def new - load_officers load_shifts @shift = ::Poll::Shift.new end def create @shift = ::Poll::Shift.new(shift_params) + @officer = @shift.officer + if @shift.save notice = t("admin.poll_shifts.flash.create") redirect_to new_admin_booth_shift_path(@shift.booth), notice: notice else - load_officers load_shifts render :new end @@ -28,6 +29,10 @@ class Admin::Poll::ShiftsController < Admin::BaseController redirect_to new_admin_booth_shift_path(@booth), notice: notice end + def search_officers + @officers = User.search(params[:search]).order(username: :asc) + end + private def load_booth @@ -38,14 +43,16 @@ class Admin::Poll::ShiftsController < Admin::BaseController @polls = ::Poll.current_or_incoming end - def load_officers - @officers = ::Poll::Officer.all - end - def load_shifts @shifts = @booth.shifts end + def load_officer + if params[:officer_id].present? + @officer = ::Poll::Officer.find(params[:officer_id]) + end + end + def shift_params params.require(:shift).permit(:booth_id, :officer_id, :date) end diff --git a/app/controllers/communities_controller.rb b/app/controllers/communities_controller.rb index 3462c8425..ff74f208b 100644 --- a/app/controllers/communities_controller.rb +++ b/app/controllers/communities_controller.rb @@ -1,8 +1,8 @@ class CommunitiesController < ApplicationController + TOPIC_ORDERS = %w{newest most_commented oldest}.freeze + before_action :set_order, :set_community, :load_topics, :load_participants - before_action :set_order, :set_community, :load_topics, :load_participants, only: :show - - has_orders %w{newest most_commented oldest}, only: :show + has_orders TOPIC_ORDERS skip_authorization_check @@ -13,7 +13,7 @@ class CommunitiesController < ApplicationController private def set_order - @order = params[:order].present? ? params[:order] : "newest" + @order = valid_order? ? params[:order] : "newest" end def set_community @@ -27,4 +27,8 @@ class CommunitiesController < ApplicationController def load_participants @participants = @community.participants end + + def valid_order? + params[:order].present? && TOPIC_ORDERS.include?(params[:order]) + end end diff --git a/app/controllers/management/sessions_controller.rb b/app/controllers/management/sessions_controller.rb index 85ed59eae..6db303a39 100644 --- a/app/controllers/management/sessions_controller.rb +++ b/app/controllers/management/sessions_controller.rb @@ -20,7 +20,7 @@ class Management::SessionsController < ActionController::Base def destroy_session session[:manager] = nil - session[:document_type] = nil + session[:document_type] = nil session[:document_number] = nil end diff --git a/app/controllers/officing/final_recounts_controller.rb b/app/controllers/officing/final_recounts_controller.rb deleted file mode 100644 index 3efd6a682..000000000 --- a/app/controllers/officing/final_recounts_controller.rb +++ /dev/null @@ -1,49 +0,0 @@ -class Officing::FinalRecountsController < Officing::BaseController - before_action :load_poll - before_action :load_officer_assignment, only: :create - - def new - @officer_assignments = ::Poll::OfficerAssignment. - includes(:final_recounts, booth_assignment: [:booth]). - joins(:booth_assignment). - final. - where(id: current_user.poll_officer.officer_assignment_ids). - where("poll_booth_assignments.poll_id = ?", @poll.id). - order(date: :asc) - - @final_recounts = @officer_assignments.select {|oa| oa.final_recounts.any?}.map(&:final_recounts).flatten - end - - def create - @final_recount = ::Poll::FinalRecount.find_or_initialize_by(booth_assignment_id: @officer_assignment.booth_assignment_id, - date: final_recount_params[:date]) - @final_recount.officer_assignment_id = @officer_assignment.id - @final_recount.count = final_recount_params[:count] - - if @final_recount.save - msg = { notice: t("officing.final_recounts.flash.create") } - else - msg = { alert: t("officing.final_recounts.flash.error_create") } - end - redirect_to new_officing_poll_final_recount_path(@poll), msg - end - - private - - def load_poll - @poll = Poll.expired.find(params[:poll_id]) - end - - def load_officer_assignment - @officer_assignment = current_user.poll_officer. - officer_assignments.final.find_by(id: final_recount_params[:officer_assignment_id]) - if @officer_assignment.blank? - redirect_to new_officing_poll_final_recount_path(@poll), alert: t("officing.final_recounts.flash.error_create") - end - end - - def final_recount_params - params.permit(:officer_assignment_id, :count, :date) - end - -end diff --git a/app/controllers/officing/polls_controller.rb b/app/controllers/officing/polls_controller.rb index e122284ec..ba2a5c01a 100644 --- a/app/controllers/officing/polls_controller.rb +++ b/app/controllers/officing/polls_controller.rb @@ -6,10 +6,11 @@ class Officing::PollsController < Officing::BaseController end def final - @polls = current_user.poll_officer? ? current_user.poll_officer.final_days_assigned_polls : [] - return unless current_user.poll_officer? - - @polls = @polls.select {|poll| poll.ends_at > 1.week.ago && poll.expired?} + @polls = if current_user.poll_officer? + current_user.poll_officer.final_days_assigned_polls.select {|poll| poll.ends_at > 1.week.ago && poll.expired?} + else + [] + end end -end \ No newline at end of file +end diff --git a/app/controllers/officing/results_controller.rb b/app/controllers/officing/results_controller.rb index 3a921a0d9..65a6deac5 100644 --- a/app/controllers/officing/results_controller.rb +++ b/app/controllers/officing/results_controller.rb @@ -28,6 +28,7 @@ class Officing::ResultsController < Officing::BaseController where(date: index_params[:date]) @whites = ::Poll::WhiteResult.where(booth_assignment_id: @booth_assignment.id, date: index_params[:date]).sum(:amount) @nulls = ::Poll::NullResult.where(booth_assignment_id: @booth_assignment.id, date: index_params[:date]).sum(:amount) + @total = ::Poll::TotalResult.where(booth_assignment_id: @booth_assignment.id, date: index_params[:date]).sum(:amount) end end @@ -70,6 +71,7 @@ class Officing::ResultsController < Officing::BaseController build_white_results build_null_results + build_total_results end def build_white_results @@ -96,6 +98,18 @@ class Officing::ResultsController < Officing::BaseController end end + def build_total_results + if results_params[:total].present? + total_result = ::Poll::TotalResult.find_or_initialize_by(booth_assignment_id: @officer_assignment.booth_assignment_id, + date: results_params[:date]) + total_result.officer_assignment_id = @officer_assignment.id + total_result.amount = results_params[:total].to_i + total_result.author = current_user + total_result.origin = 'booth' + @results << total_result + end + end + def go_back_to_new(alert = nil) params[:d] = results_params[:date] params[:oa] = results_params[:officer_assignment_id] @@ -132,7 +146,7 @@ class Officing::ResultsController < Officing::BaseController end def results_params - params.permit(:officer_assignment_id, :date, :questions, :whites, :nulls) + params.permit(:officer_assignment_id, :date, :questions, :whites, :nulls, :total) end def index_params diff --git a/app/controllers/polls/questions_controller.rb b/app/controllers/polls/questions_controller.rb index 1849dff97..bb1560f54 100644 --- a/app/controllers/polls/questions_controller.rb +++ b/app/controllers/polls/questions_controller.rb @@ -10,6 +10,8 @@ class Polls::QuestionsController < ApplicationController @comment_tree = CommentTree.new(@commentable, params[:page], @current_order) set_comment_flags(@comment_tree.comments) + @document = Document.new(documentable: @question) + question_answer = @question.answers.where(author_id: current_user.try(:id)).first @answers_by_question_id = {@question.id => question_answer.try(:answer)} end diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index f0e57a383..0e63685bd 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -1,13 +1,13 @@ class TopicsController < ApplicationController include CommentableActions - include FlagActions before_action :load_community - before_action :load_topic, only: [:show, :edit, :update] + before_action :load_topic, only: [:show, :edit, :update, :destroy] has_orders %w{most_voted newest oldest}, only: :show - skip_authorization_check + skip_authorization_check only: :show + load_and_authorize_resource except: :show def new @topic = Topic.new @@ -39,6 +39,11 @@ class TopicsController < ApplicationController end end + def destroy + @topic.destroy + redirect_to community_path(@community), notice: I18n.t('flash.actions.destroy.topic') + end + private def topic_params diff --git a/app/controllers/verification/letter_controller.rb b/app/controllers/verification/letter_controller.rb index 0e726a683..db04002b4 100644 --- a/app/controllers/verification/letter_controller.rb +++ b/app/controllers/verification/letter_controller.rb @@ -44,7 +44,7 @@ class Verification::LetterController < ApplicationController end def verify_phone! - unless current_user.confirmed_phone? + unless current_user.sms_verified? redirect_to verified_user_path, alert: t('verification.letter.alert.unconfirmed_code') end end diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index 9fadfea39..d56c658bd 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -25,7 +25,7 @@ module AdminHelper end def menu_polls? - ["polls", "questions", "officers", "booths", "officer_assignments", "booth_assignments", "recounts", "results"].include? controller_name + ["polls", "questions", "officers", "booths", "officer_assignments", "booth_assignments", "recounts", "results", "shifts"].include? controller_name end def menu_profiles? diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb index 1ab6c9826..bdd2ccb68 100644 --- a/app/helpers/comments_helper.rb +++ b/app/helpers/comments_helper.rb @@ -42,7 +42,7 @@ module CommentsHelper def commentable_path(comment) commentable = comment.commentable - + case comment.commentable_type when "Budget::Investment" budget_investment_path(commentable.budget_id, commentable) @@ -51,7 +51,7 @@ module CommentsHelper when "Legislation::Annotation" legislation_process_draft_version_annotation_path(commentable.draft_version.process, commentable.draft_version, commentable) when "Topic" - community_topic_path(comment.commentable.community, comment.commentable) + community_topic_path(commentable.community, commentable) else commentable end diff --git a/app/helpers/communities_helper.rb b/app/helpers/communities_helper.rb index 1d50c5388..bb770a948 100644 --- a/app/helpers/communities_helper.rb +++ b/app/helpers/communities_helper.rb @@ -1,12 +1,7 @@ module CommunitiesHelper def community_title(community) - if community.from_proposal? - community.proposal.title - else - investment = Budget::Investment.where(community_id: community.id).first - investment.title - end + community.from_proposal? ? community.proposal.title : community.investment.title end def community_text(community) @@ -21,8 +16,7 @@ module CommunitiesHelper if community.from_proposal? community.proposal.author_id == participant.id else - investment = Budget::Investment.where(community_id: community.id).first - investment.author_id == participant.id + community.investment.author_id == participant.id end end @@ -30,8 +24,7 @@ module CommunitiesHelper if community.from_proposal? proposal_path(community.proposal) else - investment = Budget::Investment.where(community_id: community.id).first - budget_investment_path(investment.budget_id, investment) + budget_investment_path(community.investment.budget_id, community.investment) end end diff --git a/app/helpers/embed_videos_helper.rb b/app/helpers/embed_videos_helper.rb index 1034a7a59..f1750bf8b 100644 --- a/app/helpers/embed_videos_helper.rb +++ b/app/helpers/embed_videos_helper.rb @@ -1,5 +1,8 @@ module EmbedVideosHelper + VIMEO_REGEX = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/ + YOUTUBE_REGEX = /youtu.*(be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/ + def embedded_video_code link = @proposal.video_url title = t('proposals.show.embed_video_title', proposal: @proposal.title) @@ -10,10 +13,10 @@ module EmbedVideosHelper end if server == "Vimeo" - reg_exp = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/ + reg_exp = VIMEO_REGEX src = "https://player.vimeo.com/video/" elsif server == "YouTube" - reg_exp = /youtu.*(be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/ + reg_exp = YOUTUBE_REGEX src = "https://www.youtube.com/embed/" end @@ -28,4 +31,11 @@ module EmbedVideosHelper end end -end \ No newline at end of file + def valid_video_url? + return if video_url.blank? + return if video_url.match(VIMEO_REGEX) + return if video_url.match(YOUTUBE_REGEX) + errors.add(:video_url, :invalid) + end + +end diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index db4cee09d..773dabf34 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -57,7 +57,7 @@ module Abilities can [:index, :create, :edit, :update, :destroy], Geozone can [:read, :create, :update, :destroy, :add_question, :remove_question, :search_booths, :search_questions, :search_officers], Poll - can [:read, :create, :update, :destroy], Poll::Booth + can [:read, :create, :update, :destroy, :available], Poll::Booth can [:search, :create, :index, :destroy], ::Poll::Officer can [:create, :destroy], ::Poll::BoothAssignment can [:create, :destroy], ::Poll::OfficerAssignment diff --git a/app/models/abilities/common.rb b/app/models/abilities/common.rb index 311b0dade..620cfb212 100644 --- a/app/models/abilities/common.rb +++ b/app/models/abilities/common.rb @@ -71,6 +71,9 @@ module Abilities can :create, Annotation can [:update, :destroy], Annotation, user_id: user.id + + can [:create], Topic + can [:update, :destroy], Topic, author_id: user.id end end end diff --git a/app/models/community.rb b/app/models/community.rb index 710982200..3fa1cebaa 100644 --- a/app/models/community.rb +++ b/app/models/community.rb @@ -1,11 +1,11 @@ class Community < ActiveRecord::Base has_one :proposal - has_one :investment + has_one :investment, class_name: Budget::Investment has_many :topics def participants - users_participants = users_who_commented_by + - users_who_topics_author_by + + users_participants = users_who_commented + + users_who_topics_author + author_from_community users_participants.uniq end @@ -16,24 +16,19 @@ class Community < ActiveRecord::Base private - def users_who_commented_by + def users_who_commented topics_ids = topics.pluck(:id) query = "comments.commentable_id IN (?)and comments.commentable_type = 'Topic'" User.by_comments(query, topics_ids) end - def users_who_topics_author_by + def users_who_topics_author author_ids = topics.pluck(:author_id) User.by_authors(author_ids) end def author_from_community - if from_proposal? - User.where(id: proposal.author_id) - else - investment = Budget::Investment.where(community_id: id).first - User.where(id: investment.author_id) - end + from_proposal? ? User.where(id: proposal.author_id) : User.where(id: investment.author_id) end end diff --git a/app/models/concerns/communitable.rb b/app/models/concerns/communitable.rb index 6f0a10f43..415a7a3f0 100644 --- a/app/models/concerns/communitable.rb +++ b/app/models/concerns/communitable.rb @@ -7,8 +7,8 @@ module Communitable end def associate_community - community = Community.create - self.community_id = community.id + community = Community.create + self.community_id = community.id end end diff --git a/app/models/poll.rb b/app/models/poll.rb index 4ba313963..3873ac5e0 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -4,6 +4,7 @@ class Poll < ActiveRecord::Base has_many :partial_results, through: :booth_assignments has_many :white_results, through: :booth_assignments has_many :null_results, through: :booth_assignments + has_many :total_results, through: :booth_assignments has_many :voters has_many :officer_assignments, through: :booth_assignments has_many :officers, through: :officer_assignments diff --git a/app/models/poll/booth.rb b/app/models/poll/booth.rb index 9edbcbaf0..0aca6eecb 100644 --- a/app/models/poll/booth.rb +++ b/app/models/poll/booth.rb @@ -5,10 +5,15 @@ class Poll has_many :shifts validates :name, presence: true, uniqueness: true - + def self.search(terms) return Booth.none if terms.blank? Booth.where("name ILIKE ? OR location ILIKE ?", "%#{terms}%", "%#{terms}%") end + + def self.available + where(polls: { id: Poll.current_or_incoming }).includes(:polls) + end + end end \ No newline at end of file diff --git a/app/models/poll/booth_assignment.rb b/app/models/poll/booth_assignment.rb index 5ef9d687e..47cb01bfe 100644 --- a/app/models/poll/booth_assignment.rb +++ b/app/models/poll/booth_assignment.rb @@ -10,5 +10,6 @@ class Poll has_many :partial_results has_many :white_results has_many :null_results + has_many :total_results end end diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index 28dd8ed4a..8e02dcfe2 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -1,6 +1,11 @@ class Poll::Question < ActiveRecord::Base include Measurable include Searchable + include Documentable + documentable max_documents_allowed: 1, + max_file_size: 3.megabytes, + accepted_content_types: [ "application/pdf" ] + accepts_nested_attributes_for :documents, allow_destroy: true acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases diff --git a/app/models/poll/shift.rb b/app/models/poll/shift.rb index 8ee646ea4..cc6a43425 100644 --- a/app/models/poll/shift.rb +++ b/app/models/poll/shift.rb @@ -8,6 +8,7 @@ class Poll validates :date, presence: true validates :date, uniqueness: { scope: [:officer_id, :booth_id] } + before_create :persist_data after_create :create_officer_assignments def create_officer_assignments @@ -18,5 +19,11 @@ class Poll Poll::OfficerAssignment.create!(attrs) end end + + def persist_data + self.officer_name = officer.name + self.officer_email = officer.email + end + end end \ No newline at end of file diff --git a/app/models/poll/total_result.rb b/app/models/poll/total_result.rb new file mode 100644 index 000000000..2df01929e --- /dev/null +++ b/app/models/poll/total_result.rb @@ -0,0 +1,23 @@ +class Poll::TotalResult < ActiveRecord::Base + + VALID_ORIGINS = %w{web booth} + + belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id' + belongs_to :booth_assignment + belongs_to :officer_assignment + + validates :author, presence: true + validates :origin, inclusion: {in: VALID_ORIGINS} + + scope :by_author, ->(author_id) { where(author_id: author_id) } + + before_save :update_logs + + def update_logs + if amount_changed? && amount_was.present? + self.amount_log += ":#{amount_was.to_s}" + self.officer_assignment_id_log += ":#{officer_assignment_id_was.to_s}" + self.author_id_log += ":#{author_id_was.to_s}" + end + end +end diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 82aa627cc..e3c7a8c57 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -15,6 +15,7 @@ class Proposal < ActiveRecord::Base max_file_size: 3.megabytes, accepted_content_types: [ "application/pdf" ] accepts_nested_attributes_for :documents, allow_destroy: true + include EmbedVideosHelper acts_as_votable acts_as_paranoid column: :hidden_at @@ -41,6 +42,8 @@ class Proposal < ActiveRecord::Base validates :terms_of_service, acceptance: { allow_nil: false }, on: :create + validate :valid_video_url? + before_validation :set_responsible_name before_save :calculate_hot_score, :calculate_confidence_score diff --git a/app/models/topic.rb b/app/models/topic.rb index 8c8f3f6c9..34abed0e3 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -1,6 +1,4 @@ class Topic < ActiveRecord::Base - include Flaggable - acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index b42e717b4..e629f5807 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -73,12 +73,15 @@ <%= link_to t('admin.menu.poll_officers'), admin_officers_path %> -
  • > +
  • > <%= link_to t('admin.menu.poll_booths'), admin_booths_path %>
  • -
  • > - <%= link_to t('admin.menu.poll_shifts'), admin_booths_path %> +
  • > + <%= link_to t('admin.menu.poll_shifts'), available_admin_booths_path %>
  • diff --git a/app/views/admin/poll/booths/index.html.erb b/app/views/admin/poll/booths/index.html.erb index 0dbb62cdf..05044dd0e 100644 --- a/app/views/admin/poll/booths/index.html.erb +++ b/app/views/admin/poll/booths/index.html.erb @@ -15,8 +15,7 @@ <%= t("admin.booths.index.name") %> <%= t("admin.booths.index.location") %> -   -   + <%= t("admin.actions.actions") %> <% @booths.each do |booth| %> diff --git a/app/views/admin/poll/officer_assignments/index.html.erb b/app/views/admin/poll/officer_assignments/index.html.erb index f6e75c602..f27f6b2ae 100644 --- a/app/views/admin/poll/officer_assignments/index.html.erb +++ b/app/views/admin/poll/officer_assignments/index.html.erb @@ -15,7 +15,6 @@ <%= t("admin.poll_officer_assignments.index.table_name") %> <%= t("admin.poll_officer_assignments.index.table_email") %> - <%= t("admin.actions.actions") %> <% @officers.each do |officer| %> @@ -28,11 +27,6 @@ <%= officer.email %> - - <%= link_to t("admin.poll_officer_assignments.index.edit_officer_assignments"), - by_officer_admin_poll_officer_assignments_path(@poll, officer_id: officer.id), - class: "button hollow" %> - <% end %> @@ -40,4 +34,4 @@ <%= paginate @officers %> <% end %> - \ No newline at end of file + diff --git a/app/views/admin/poll/questions/_form.html.erb b/app/views/admin/poll/questions/_form.html.erb index 959aa4ddf..2b32ed680 100644 --- a/app/views/admin/poll/questions/_form.html.erb +++ b/app/views/admin/poll/questions/_form.html.erb @@ -26,6 +26,17 @@ ckeditor: { language: I18n.locale } %> +
    + <%= render 'documents/nested_documents', documentable: @question %> +
    + +
    + <%= f.label :video_url, t("proposals.form.proposal_video_url") %> +

    <%= t("proposals.form.proposal_video_url_note") %>

    + <%= f.text_field :video_url, placeholder: t("proposals.form.proposal_video_url"), label: false, + aria: {describedby: "video-url-help-text"} %> +
    +
    <%= f.submit(class: "button expanded", value: t("shared.save")) %> diff --git a/app/views/admin/poll/questions/show.html.erb b/app/views/admin/poll/questions/show.html.erb index 3be660f79..3f776c5c0 100644 --- a/app/views/admin/poll/questions/show.html.erb +++ b/app/views/admin/poll/questions/show.html.erb @@ -40,6 +40,21 @@ <%= @question.description %>

    + <% if @question.video_url.present? %> +

    + <%= t("admin.questions.show.video_url") %> +
    + <%= @question.video_url %> +

    + <% end %> + + <% if @question.documents.any? %> +

    + <%= t("admin.questions.show.documents") %> +
    + <%= @question.documents.first.title %> +

    + <% end %> <%= link_to t("admin.questions.show.preview"), question_path(@question) %>
    diff --git a/app/views/admin/poll/results/index.html.erb b/app/views/admin/poll/results/index.html.erb index 9d3a6abef..64a93aa10 100644 --- a/app/views/admin/poll/results/index.html.erb +++ b/app/views/admin/poll/results/index.html.erb @@ -14,11 +14,13 @@ <%= t("admin.results.index.table_whites") %> <%= t("admin.results.index.table_nulls") %> + <%= t("admin.results.index.table_total") %> <%= @poll.white_results.sum(:amount) %> <%= @poll.null_results.sum(:amount) %> + <%= @poll.total_results.sum(:amount) %> @@ -47,4 +49,4 @@ <% end %> <% end %> -
    \ No newline at end of file + diff --git a/app/views/admin/poll/shifts/_form.html.erb b/app/views/admin/poll/shifts/_form.html.erb new file mode 100644 index 000000000..f0461855f --- /dev/null +++ b/app/views/admin/poll/shifts/_form.html.erb @@ -0,0 +1,30 @@ +<%= form_for @shift, as: :shift, url: admin_booth_shifts_path do |f| %> + <%= render "shared/errors", resource: @shift %> + +
    + + <%= t("admin.poll_shifts.new.new_shift") %> + + +
    + <%= t("admin.poll_shifts.new.officer") %> +
    <%= @officer.name %> + <%= f.hidden_field :officer_id, value: @officer.id %> +
    + +
    + + <%= f.select :date, + shift_dates_select_options(@polls), + prompt: t("admin.poll_shifts.new.select_date"), + label: false %> +
    + + <%= f.hidden_field :booth_id, value: @booth.id %> + +
    + <%= f.submit t("admin.poll_shifts.new.add_shift"), + class: "button expanded margin-top" %> +
    +
    +<% end %> diff --git a/app/views/admin/poll/shifts/_search_officers.html.erb b/app/views/admin/poll/shifts/_search_officers.html.erb new file mode 100644 index 000000000..e63d09d41 --- /dev/null +++ b/app/views/admin/poll/shifts/_search_officers.html.erb @@ -0,0 +1,19 @@ +
    +
    + <%= form_tag search_officers_admin_booth_shifts_path, + method: :get, remote: true do |f| %> +
    + <%= text_field_tag :search, + @search, + placeholder: t("admin.poll_shifts.new.search_officer_placeholder"), + id: "search-officers" %> +
    + <%= submit_tag t("admin.poll_shifts.new.search_officer_button"), + class: "button" %> +
    +
    + <% end %> +
    +
    + +
    diff --git a/app/views/admin/poll/shifts/_search_officers_results.html.erb b/app/views/admin/poll/shifts/_search_officers_results.html.erb new file mode 100644 index 000000000..70525ba6d --- /dev/null +++ b/app/views/admin/poll/shifts/_search_officers_results.html.erb @@ -0,0 +1,36 @@ +<% if @officers.blank? %> +
    + <%= t('admin.shared.no_search_results') %> +
    +<% else %> +

    <%= t('admin.shared.search_results') %>

    + + + + + + + + + + + <% @officers.each do |user| %> + + + + + + <% end %> + +
    <%= t("admin.poll_shifts.new.table_name") %><%= t("admin.poll_shifts.new.table_email") %> + <%= t("admin.poll_shifts.new.table_shift") %> +
    + <%= user.name %> + + <%= user.email %> + + <%= link_to t("admin.poll_shifts.new.edit_shifts"), + new_admin_booth_shift_path(officer_id: user.poll_officer.id), + class: "button hollow" %> +
    +<% end %> diff --git a/app/views/admin/poll/shifts/_shifts.html.erb b/app/views/admin/poll/shifts/_shifts.html.erb index 800c6944b..f2de53548 100644 --- a/app/views/admin/poll/shifts/_shifts.html.erb +++ b/app/views/admin/poll/shifts/_shifts.html.erb @@ -1,19 +1,19 @@ -

    <%= t("admin.poll_shifts.new.assignments") %>

    +

    <%= t("admin.poll_shifts.new.shifts") %>

    - + <% @shifts.each do |shift| %> - +
    <%= t("admin.poll_shifts.new.date") %> <%= t("admin.poll_shifts.new.officer") %><%= t("admin.poll_shifts.new.assignment") %><%= t("admin.poll_shifts.new.shift") %>
    <%= l(shift.date.to_date, format: :long) %><%= shift.officer.name %><%= shift.officer_name %> - <%= link_to t("admin.poll_shifts.new.remove_assignment"), + <%= link_to t("admin.poll_shifts.new.remove_shift"), admin_booth_shift_path(@booth, shift), method: :delete, class: "button hollow alert" %> diff --git a/app/views/admin/poll/shifts/new.html.erb b/app/views/admin/poll/shifts/new.html.erb index c997dc35f..b975c40a9 100644 --- a/app/views/admin/poll/shifts/new.html.erb +++ b/app/views/admin/poll/shifts/new.html.erb @@ -2,45 +2,21 @@

    <%= @booth.name %>

    -<%= form_for @shift, as: :shift, url: admin_booth_shifts_path do |f| %> - <%= render "shared/errors", resource: @shift %> - -
    - - <%= t("admin.poll_shifts.new.new_assignment") %> - - -
    - - <%= f.select :date, - shift_dates_select_options(@polls), - prompt: t("admin.poll_shifts.new.select_date"), - label: false %> -
    - -
    - - <%= f.select :officer_id, - officer_select_options(@officers), - prompt: t("admin.poll_shifts.new.select_officer"), - label: false %> -
    - - <%= f.hidden_field :booth_id, value: @booth.id %> - -
    - <%= f.submit t("admin.poll_shifts.new.add_assignment"), - class: "button expanded hollow margin-top" %> -
    -
    +<% if @officer.blank? %> +

    + <%= t("admin.poll_shifts.new.search_officer_text") %> +

    + <%= render "search_officers" %> +<% else %> + <%= render "form" %> <% end %>
    <% if @shifts.empty? %> -
    - <%= t("admin.poll_shifts.new.no_assignments") %> +
    + <%= t("admin.poll_shifts.new.no_shifts") %>
    <% else %> <%= render "shifts" %> <% end %> -
    \ No newline at end of file +
    diff --git a/app/views/admin/poll/shifts/search_officers.js.erb b/app/views/admin/poll/shifts/search_officers.js.erb new file mode 100644 index 000000000..ba621d8f7 --- /dev/null +++ b/app/views/admin/poll/shifts/search_officers.js.erb @@ -0,0 +1 @@ +$("#search-officers-results").html("<%= j render 'search_officers_results' %>"); \ No newline at end of file diff --git a/app/views/admin/site_customization/pages/index.html.erb b/app/views/admin/site_customization/pages/index.html.erb index dd9cfd3ad..897549caf 100644 --- a/app/views/admin/site_customization/pages/index.html.erb +++ b/app/views/admin/site_customization/pages/index.html.erb @@ -34,7 +34,7 @@ <% if page.status == "published" %> <%= link_to t("admin.site_customization.pages.index.see_page"), page.url, target: "_blank" %> <% else %> - <%= t("admin.site_customization.pages.index.see_page") %> + <%= t("admin.site_customization.pages.index.see_page") %> <% end %>
    diff --git a/app/views/admin/tags/index.html.erb b/app/views/admin/tags/index.html.erb index 715479420..f22321f38 100644 --- a/app/views/admin/tags/index.html.erb +++ b/app/views/admin/tags/index.html.erb @@ -19,7 +19,7 @@ <% @tags.each do |tag| %> - + +
    + <%= form_for(tag, url: admin_tag_path(tag), as: :tag, diff --git a/app/views/budgets/index.html.erb b/app/views/budgets/index.html.erb index 494568706..8cde29bc7 100644 --- a/app/views/budgets/index.html.erb +++ b/app/views/budgets/index.html.erb @@ -32,6 +32,7 @@

    <%= t("budgets.index.section_footer.title") %>

    +

    <%= t("budgets.index.section_footer.description") %>

    <%= t("budgets.index.section_footer.help_text_1") %>

    <%= t("budgets.index.section_footer.help_text_2") %>

    <%= t("budgets.index.section_footer.help_text_3", diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index c22e7aa19..0000f8cc0 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -93,6 +93,7 @@ <% end %> + <% unless child_comments_of(comment).empty? %>

    • <% child_comments_of(comment).each do |child| %> @@ -102,5 +103,6 @@ <% end %>
  • + <% end %> <% end %> diff --git a/app/views/communities/_participant.html.erb b/app/views/communities/_participant.html.erb new file mode 100644 index 000000000..5e4f02900 --- /dev/null +++ b/app/views/communities/_participant.html.erb @@ -0,0 +1,22 @@ +
    +
    + + <%= avatar_image( participant, seed: participant.id, size: 32, class: 'author-photo') %> + +
    + + + <%= link_to participant.name, user_path(participant)%> + + + <% if is_author?(@community, participant) %> +  •  + + <%= t("comments.comment.author") %> + + <% end %> + +
    + +
    +
    diff --git a/app/views/communities/_participants.html.erb b/app/views/communities/_participants.html.erb index 931ebf105..a153ec624 100644 --- a/app/views/communities/_participants.html.erb +++ b/app/views/communities/_participants.html.erb @@ -14,28 +14,7 @@ <% @participants.each do |participant| %> -
    -
    - - <%= avatar_image( participant, seed: participant.id, size: 32, class: 'author-photo') %> - -
    - - - <%= link_to participant.name, user_path(participant)%> - - - <% if is_author?(@community, participant) %> -  •  - - <%= t("comments.comment.author") %> - - <% end %> - -
    - -
    -
    + <%= render 'participant', participant: participant %> <% end %> diff --git a/app/views/debates/index.html.erb b/app/views/debates/index.html.erb index 231131eac..f06f43923 100644 --- a/app/views/debates/index.html.erb +++ b/app/views/debates/index.html.erb @@ -66,6 +66,7 @@

    <%= t("debates.index.section_footer.title") %>

    +

    <%= t("debates.index.section_footer.description") %>

    <%= t("debates.index.section_footer.help_text_1") %>

    <%= t("debates.index.section_footer.help_text_2", org: link_to(setting['org_name'], new_user_registration_path)).html_safe %>

    diff --git a/app/views/legislation/processes/index.html.erb b/app/views/legislation/processes/index.html.erb index 063e4048f..7630d29ac 100644 --- a/app/views/legislation/processes/index.html.erb +++ b/app/views/legislation/processes/index.html.erb @@ -23,6 +23,7 @@

    <%= t("legislation.processes.index.section_footer.title") %>

    +

    <%= t("legislation.processes.index.section_footer.description") %>

    <%= t("legislation.processes.index.section_footer.help_text_1") %>

    <%= t("legislation.processes.index.section_footer.help_text_2", org: setting['org_name']) %>

    diff --git a/app/views/officing/final_recounts/new.html.erb b/app/views/officing/final_recounts/new.html.erb deleted file mode 100644 index 647bd31b4..000000000 --- a/app/views/officing/final_recounts/new.html.erb +++ /dev/null @@ -1,80 +0,0 @@ -<% if @officer_assignments.any? %> -

    <%= t("officing.final_recounts.new.title", poll: @poll.name) %>

    - - <%= form_tag(officing_poll_final_recounts_path(@poll), {id: "officer_assignment_form"}) do %> -
    -
    - - <%= select_tag :officer_assignment_id, - booths_for_officer_select_options(@officer_assignments), - { prompt: t("officing.final_recounts.new.select_booth"), - label: false } %> -
    -
    - -
    -
    - - <%= select_tag :date, - poll_dates_select_options(@poll), - { prompt: t("officing.final_recounts.new.select_date"), - label: false } %> -
    -
    - -
    -
    - - <%= text_field_tag :count, nil, placeholder: t("officing.final_recounts.new.count_placeholder") %> -
    -
    - -
    -
    - <%= submit_tag t("officing.final_recounts.new.submit"), class: "button expanded" %> -
    -
    - <% end %> -<% else %> -

    <%= @poll.name %>

    -
    - <%= t("officing.final_recounts.new.not_allowed") %> -
    -<% end %> - - -<% if @final_recounts.any? %> -
    -

    <%= t("officing.final_recounts.new.final_recount_list") %>

    - - - - - - - - - - - <% @final_recounts.each do |final_recount| %> - - - - - - - - <% end %> - -
    <%= t("officing.final_recounts.new.date") %><%= t("officing.final_recounts.new.booth") %><%= t("officing.final_recounts.new.count") %><%= t("officing.final_recounts.new.system_count") %> 
    - <%= l(final_recount.date.to_date, format: :long) %> - - <%= final_recount.booth_assignment.booth.name %> - - <%= final_recount.count %> - - <%= system_recount_to_compare_with_final_recount final_recount %> - - <%= link_to t("officing.final_recounts.new.add_results"), new_officing_poll_result_path(@poll, oa: final_recount.officer_assignment.id, d: l(final_recount.date.to_date) )%> -
    -<% end %> \ No newline at end of file diff --git a/app/views/officing/polls/final.html.erb b/app/views/officing/polls/final.html.erb index b46ab0b13..149551d1e 100644 --- a/app/views/officing/polls/final.html.erb +++ b/app/views/officing/polls/final.html.erb @@ -14,9 +14,6 @@
    - <%= link_to t("officing.polls.final.add_recount"), - new_officing_poll_final_recount_path(poll), - class: "button hollow" %> <%= link_to t("officing.polls.final.add_results"), new_officing_poll_result_path(poll), class: "button hollow" %> @@ -29,4 +26,4 @@
    <%= t("officing.polls.final.no_polls") %>
    -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/officing/results/index.html.erb b/app/views/officing/results/index.html.erb index 70da6d8d3..3b14e3c97 100644 --- a/app/views/officing/results/index.html.erb +++ b/app/views/officing/results/index.html.erb @@ -16,12 +16,14 @@
    <%= t("officing.results.index.table_whites") %> <%= t("officing.results.index.table_nulls") %><%= t("officing.results.index.table_total") %>
    <%= @whites %> <%= @nulls %><%= @total %>
    @@ -54,4 +56,4 @@
    <%= t("officing.results.index.no_results") %>
    -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/officing/results/new.html.erb b/app/views/officing/results/new.html.erb index 74af8af10..fa5f449fb 100644 --- a/app/views/officing/results/new.html.erb +++ b/app/views/officing/results/new.html.erb @@ -47,6 +47,11 @@

    <%= t("officing.results.new.ballots_null") %>

    <%= text_field_tag :nulls, params[:nulls].presence, placeholder: "0" %> + +
    +

    <%= t("officing.results.new.ballots_total") %>

    + <%= text_field_tag :total, params[:total].presence, placeholder: "0" %> +

    diff --git a/app/views/polls/_poll_group.html.erb b/app/views/polls/_poll_group.html.erb index 6e0b85f16..e3e5c5976 100644 --- a/app/views/polls/_poll_group.html.erb +++ b/app/views/polls/_poll_group.html.erb @@ -64,7 +64,7 @@ <% if poll.expired? %> <%= t("polls.index.participate_button_expired") %> <% elsif poll.incoming? %> - <%= t("polls.index.participate_button_incoming") %> + <%= t("polls.index.participate_button_incoming") %> <% else %> <%= t("polls.index.participate_button") %> <% end %> diff --git a/app/views/polls/index.html.erb b/app/views/polls/index.html.erb index 55cdae8e2..871b04953 100644 --- a/app/views/polls/index.html.erb +++ b/app/views/polls/index.html.erb @@ -27,10 +27,10 @@

    <%= t("polls.index.section_footer.title") %>

    +

    <%= t("polls.index.section_footer.description") %>

    <%= t("polls.index.section_footer.help_text_1") %>

    <%= t("polls.index.section_footer.help_text_2", org: link_to(setting['org_name'], new_user_registration_path)).html_safe %>

    -

    diff --git a/app/views/polls/questions/_filter_subnav.html.erb b/app/views/polls/questions/_filter_subnav.html.erb new file mode 100644 index 000000000..738fc3700 --- /dev/null +++ b/app/views/polls/questions/_filter_subnav.html.erb @@ -0,0 +1,22 @@ +
    +
    +
      +
    • + <%= link_to "#tab-comments" do %> +

      + <%= t("proposals.show.comments_tab") %> + (<%= @question.comments_count %>) +

      + <% end %> +
    • +
    • + <%= link_to "#tab-documents" do %> +

      + <%= t("documents.tab") %> + (<%= @question.documents.count %>) +

      + <% end %> +
    • +
    +
    +
    diff --git a/app/views/polls/questions/show.html.erb b/app/views/polls/questions/show.html.erb index da46ebbcc..f67633129 100644 --- a/app/views/polls/questions/show.html.erb +++ b/app/views/polls/questions/show.html.erb @@ -55,6 +55,21 @@ +<% if @question.video_url.present? %> +
    +
    + +
    +
    + +<% end %> +

    <%= t('poll_questions.show.more_info') %>

    @@ -62,4 +77,16 @@
    -<%= render "comments" %> +
    + <%= render "polls/questions/filter_subnav" %> + +
    + <%= render "polls/questions/comments" %> +
    + +
    + <%= render 'documents/documents', + documents: @question.documents, + max_documents_allowed: Poll::Question.max_documents_allowed %> +
    +
    diff --git a/app/views/proposals/index.html.erb b/app/views/proposals/index.html.erb index 154c5f43f..b5710dfc7 100644 --- a/app/views/proposals/index.html.erb +++ b/app/views/proposals/index.html.erb @@ -80,6 +80,7 @@

    <%= t("proposals.index.section_footer.title") %>

    +

    <%= t("proposals.index.section_footer.description") %>

    <%= t("proposals.index.section_footer.help_text_1") %>

    <%= t("proposals.index.section_footer.help_text_2", org: link_to(setting['org_name'], new_user_registration_path)).html_safe %>

    diff --git a/app/views/proposals/show.html.erb b/app/views/proposals/show.html.erb index 177228d76..1dc918e8e 100644 --- a/app/views/proposals/show.html.erb +++ b/app/views/proposals/show.html.erb @@ -23,7 +23,8 @@ <% end %> <% if author_of?(@proposal, current_user) %> - <%= link_to t("proposals.show.send_notification"), new_proposal_notification_path(proposal_id: @proposal.id), + <%= link_to t("proposals.show.send_notification"), + new_proposal_notification_path(proposal_id: @proposal.id), class: 'button hollow float-right' %> <% end %> diff --git a/app/views/shared/_admin_login_items.html.erb b/app/views/shared/_admin_login_items.html.erb index 85a367e97..5ba498523 100644 --- a/app/views/shared/_admin_login_items.html.erb +++ b/app/views/shared/_admin_login_items.html.erb @@ -1,37 +1,37 @@ <% if show_admin_menu? %> -
  • - <%= link_to t("layouts.header.administration_menu"), "#", rel: "nofollow" %> - +
  • <% end %> diff --git a/app/views/shared/_section_header.html.erb b/app/views/shared/_section_header.html.erb index ca0476bb9..9408b663e 100644 --- a/app/views/shared/_section_header.html.erb +++ b/app/views/shared/_section_header.html.erb @@ -1,12 +1,9 @@ -
    +
    -
    +
    <%= image_tag "help/help_icon_#{image}.png", alt: t("#{i18n_namespace}.icon_alt"), class: "align-top" %>

    <%= t("#{i18n_namespace}.title") %>

    -

    - <%= t("#{i18n_namespace}.description") %>
    - <%= link_to t("#{i18n_namespace}.help"), "#section_help" %> -

    + <%= link_to t("#{i18n_namespace}.help"), "#section_help", class: "float-right" %>
    diff --git a/app/views/shared/_subnavigation.html.erb b/app/views/shared/_subnavigation.html.erb index 487edeb39..dd9809dd3 100644 --- a/app/views/shared/_subnavigation.html.erb +++ b/app/views/shared/_subnavigation.html.erb @@ -20,7 +20,7 @@
  • <%= layout_menu_link_to t("layouts.header.poll_questions"), polls_path, - controller_name == "polls", + controller_name == "polls" || controller_name == "questions", accesskey: "3", title: t("shared.go_to_page") + t("layouts.header.poll_questions") %>
  • diff --git a/app/views/topics/_informative_text.html.erb b/app/views/topics/_informative_text.html.erb new file mode 100644 index 000000000..aeb5ad773 --- /dev/null +++ b/app/views/topics/_informative_text.html.erb @@ -0,0 +1,11 @@ +

    <%= t("community.show.create_first_community_topic.first_theme") %>

    + +<% if user_signed_in? %> + <%= t("community.show.create_first_community_topic.first_theme_not_logged_in") %> +<% else %> +
    + <%= t("community.show.create_first_community_topic.sub_first_theme", + link: link_to(t("community.show.create_first_community_topic.sign_link", + org_name: setting['org_name']), new_user_session_path)).html_safe %> +
    +<% end %> diff --git a/app/views/topics/_topic.html.erb b/app/views/topics/_topic.html.erb new file mode 100644 index 000000000..fe225d501 --- /dev/null +++ b/app/views/topics/_topic.html.erb @@ -0,0 +1,25 @@ +
    + +
    + +

    <%= link_to topic.title, community_topic_path(@community, topic) %>

    + +

    +   + <%= link_to t("community.show.topic.comments", count: topic.comments_count), community_topic_path(@community, topic, anchor: "comments") %> +  •  + <%= I18n.l topic.created_at.to_date %> +  •  + <%= topic.author.name %> +

    + +
    + +
    + <% if topic.author == current_user %> + <%= link_to t("community.show.topic.edit_button"), edit_community_topic_path(@community.id, topic), class: 'button small hollow' %> + <%= link_to t("community.show.topic.destroy_button"), community_topic_path(@community.id, topic), method: :delete, class: 'button hollow alert small' %> + <% end %> +
    + +
    diff --git a/app/views/topics/_topics.html.erb b/app/views/topics/_topics.html.erb index f74fa573f..42fa4f319 100644 --- a/app/views/topics/_topics.html.erb +++ b/app/views/topics/_topics.html.erb @@ -1,48 +1,13 @@ <% if topics.any? %> -
    <%= render 'shared/wide_order_selector', i18n_namespace: "comments" %>
    - - <% topics.each do |topic| %> - -
    -
    - -

    <%= link_to topic.title, community_topic_path(@community, topic) %>

    - -

    -   - <%= link_to t("community.show.topic.comments", count: topic.comments_count), community_topic_path(@community, topic, anchor: "comments") %> -  •  - <%= I18n.l topic.created_at.to_date %> -  •  - <%= topic.author.name %> -

    - -
    - -
    - <% if topic.author == current_user %> - <%= link_to t("community.show.topic.edit_button"), edit_community_topic_path(@community.id, topic), class: 'button small hollow' %> - <% end %> -
    - -
    - - <% end %> - <% else %> -

    <%= t("community.show.create_first_community_topic.first_theme") %>

    - <% if user_signed_in? %> - <%= t("community.show.create_first_community_topic.first_theme_not_logged_in") %> - <% else %> -
    - <%= t("community.show.create_first_community_topic.sub_first_theme", - link: link_to(t("community.show.create_first_community_topic.sign_link", - org_name: setting['org_name']), new_user_session_path)).html_safe %> -
    - <% end %> + <%= render 'topics/informative_text', topics: topics %> +<% end %> + +<% topics.each do |topic| %> + <%= render 'topics/topic', topic: topic %> <% end %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 3986b1b89..9357927ba 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -21,6 +21,7 @@ data: # - config/locales/**/*.%{locale}.yml ## Another gem (replace %#= with %=): # - "<%#= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml" + - config/locales/custom/%{locale}/custom.yml - config/locales/%{locale}/general.yml - config/locales/%{locale}/activerecord.yml - config/locales/%{locale}/activemodel.yml diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index c91719104..91b493ffa 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -498,16 +498,22 @@ en: final_recount: "Final recount (by officer)" poll_shifts: new: - new_assignment: "New shift" + add_shift: "Add shift" + shift: "Assignment" + shifts: "Shifts in this booth" date: "Date" + edit_shifts: Edit shifts + new_shift: "New shift" + no_shifts: "This booth has no shifts" officer: "Officer" - assignment: "Assignment" + remove_shift: "Remove" + search_officer_button: Search + search_officer_placeholder: Search officer + search_officer_text: Search for an officer to assign a new shift select_date: "Select day" - select_officer: "Select officer" - add_assignment: "Add shift" - remove_assignment: "Remove" - assignments: "Shifts in this booth" - no_assignments: "This booth has no shifts" + table_shift: "Shift" + table_email: "Email" + table_name: "Name" flash: create: "Shift added" destroy: "Shift removed" @@ -590,6 +596,8 @@ en: title: Title valid_answers: Valid answers description: Description + video_url: External video + documents: Documents preview: View on website recounts: index: @@ -604,6 +612,7 @@ en: no_results: "There are no results" table_whites: "Blank ballots" table_nulls: "Invalid ballots" + table_total: "Total ballots" table_answer: Answer table_votes: Votes booths: diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index 59406499f..c1879df16 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -41,10 +41,10 @@ en: section_header: icon_alt: Participatory budgets icon title: Participatory budgets - description: With the participatory budgets the citizens decide to which projects presented by the neighbors is destined a part of the municipal budget. help: Help about participatory budgets section_footer: title: Help about participatory budgets + description: With the participatory budgets the citizens decide to which projects presented by the neighbors is destined a part of the municipal budget. help_text_1: "Participatory budgets are processes in which citizens decide directly on what is spent part of the municipal budget. Any registered person over 16 years old can propose an investment project that is preselected in a phase of citizen supports." help_text_2: "The most voted projects are evaluated and passed to a final vote in which they decide the actions to be carried out by the City Council once the municipal budgets of the next year are approved." help_text_3: "The presentation of participatory budgeting projects takes place from January and over a period of one and a half months. To participate and propose proposals for the entire city and / or districts, you must sign up on %{org} and verify your account." diff --git a/config/locales/en/community.yml b/config/locales/en/community.yml index 37b7ea74f..068c98998 100644 --- a/config/locales/en/community.yml +++ b/config/locales/en/community.yml @@ -26,6 +26,7 @@ en: disabled_info_title: You need to be logged to create a new topic topic: edit_button: Edit + destroy_button: Destroy comments: one: 1 comment other: "%{count} comments" diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 9681c4140..d8b9c2a78 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -126,10 +126,10 @@ en: section_header: icon_alt: Debates icon title: Debates - description: Start a debate to share opinions with others about the topics you are concerned about. help: Help about debates section_footer: title: Help about debates + description: Start a debate to share opinions with others about the topics you are concerned about. help_text_1: "The space for citizen debates is aimed at anyone who can expose issues of their concern and those who want to share opinions with other people." help_text_2: 'To open a debate you need to sign up on %{org}. Users can also comment on open debates and rate them with the "I agree" or "I disagree" buttons found in each of them.' help_text_3: "Keep in mind that a debate does not start any specific action. If you want to make a %{proposal} for the city or raise a investment project of %{budget} when the phase is open, go to the corresponding section." @@ -179,6 +179,7 @@ en: proposal_notification: "Notification" spending_proposal: Spending proposal budget/investment: Investment + poll/shift: Shift user: Account verification/sms: phone signature_sheet: Signature sheet @@ -376,10 +377,10 @@ en: section_header: icon_alt: Proposals icon title: Proposals - description: Make a citizen proposal. If it gets enough supports it will go to voting phase, so you can get all the citizens to decide how they want their city to be. help: Help about proposals section_footer: title: Help about proposals + description: Make a citizen proposal. If it gets enough supports it will go to voting phase, so you can get all the citizens to decide how they want their city to be. help_text_1: "The citizen proposals are an opportunity for neighbours and collectives to decide directly how they want to shape their city. Any person can make a proposal about a topic or concern of their interest, for the City Council to make it, after it gets enough supports to be put to a citizens vote." help_text_2: "To create a proposal, you must sign up on %{org}. The proposals that get the support of 1% of the users in the web, goes to voting phase. To support proposals it is necessary to have a verified account." help_text_3: "A citizen vote is celebrated when the proposals get the necessary supports. Once celebrated, if there are more people in favor than against, the City Council assumes the proposal and carries it out." @@ -468,10 +469,10 @@ en: section_header: icon_alt: Voting icon title: Voting - description: Sign up to vote on citizen proposals and questions the City Council ask to the neighbors. Make municipal decisions directly. help: Help about voting section_footer: title: Help about voting + description: Sign up to vote on citizen proposals and questions the City Council ask to the neighbors. Make municipal decisions directly. help_text_1: "Voting takes place when a citizen proposal supports reaches 1% of the census with voting rights. Voting can also include questions that the City Council ask to the citizens decision." help_text_2: "To participate in the next vote you have to sign up on %{org} and verify your account. All registered voters in the city over 16 years old can vote. The results of all votes are binding on the government." show: diff --git a/config/locales/en/legislation.yml b/config/locales/en/legislation.yml index 7c3b4a236..37f9c02aa 100644 --- a/config/locales/en/legislation.yml +++ b/config/locales/en/legislation.yml @@ -66,10 +66,10 @@ en: section_header: icon_alt: Legislation processes icon title: Legislation processes - description: Participate in the debates and processes prior to the approval of a ordinance or a municipal action. Your opinion will be consider by the City Council. help: Help about legislation processes section_footer: title: Help about legislation processes + description: Participate in the debates and processes prior to the approval of a ordinance or a municipal action. Your opinion will be consider by the City Council. help_text_1: "In participatory processes, the City Council offers to its citizens the opportunity to participate in the drafting and modification of regulations, affecting the city and to be able to give their opinion on certain actions that it plans to carry out." help_text_2: "People registered in %{org} can participate with contributions in the public consultation of new ordinances, regulations and guidelines, among others. Your comments are analyzed by the corresponding area and considered for the final drafting of the ordinances." help_text_3: "The City Council also opens processes to receive contributions and opinions on municipal actions." diff --git a/config/locales/en/officing.yml b/config/locales/en/officing.yml index 897c51b2a..72b3812ea 100644 --- a/config/locales/en/officing.yml +++ b/config/locales/en/officing.yml @@ -15,25 +15,7 @@ en: title: Polls ready for final recounting no_polls: You are not officing final recounts in any active poll select_poll: Select poll - add_recount: Add final recount add_results: Add results - final_recounts: - flash: - create: "Data added" - error_create: "Final counts NOT added. Error in data." - new: - title: "%{poll} - Add final recount" - not_allowed: "You are allowed to add final recounts for this poll" - booth: "Booth" - date: "Date" - select_booth: "Select booth" - select_date: "Select date" - count: "Final vote count" - count_placeholder: "Final vote count" - submit: Save - final_recount_list: "Your final recounts" - system_count: "System recount" - add_results: "Add results" results: flash: create: "Results saved" @@ -49,6 +31,7 @@ en: select_date: "Select date" ballots_white: "Blank ballots" ballots_null: "Invalid ballots" + ballots_total: "Total ballots" submit: "Save" results_list: "Your results" see_results: "See results" @@ -59,6 +42,7 @@ en: table_votes: Votes table_whites: "Blank ballots" table_nulls: "Invalid ballots" + table_total: "Total ballots" residence: flash: create: "Document verified with Census" diff --git a/config/locales/en/pages.yml b/config/locales/en/pages.yml index 83db1a8e4..d1884cbad 100644 --- a/config/locales/en/pages.yml +++ b/config/locales/en/pages.yml @@ -63,9 +63,9 @@ en: text: |- Use it in your local government or help us to improve it, it is free software. - This Open Government Portal use the [CONSUL app](https://github.com/ayuntamientomadrid 'consul github') that is free software, with [licence AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ), that means in simple words that anyone can use the code freely, copy it, see it in detail, modify it and redistribute it to the word with the modifications he wants (allowing others to do the same). Because we think culture is better and richer when it is released. + This Open Government Portal use the [CONSUL app](https://github.com/consul/consul 'consul github') that is free software, with [licence AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ), that means in simple words that anyone can use the code freely, copy it, see it in detail, modify it and redistribute it to the word with the modifications he wants (allowing others to do the same). Because we think culture is better and richer when it is released. - If you are a programmer, you can see the code and help us to improve it at [CONSUL app](https://github.com/ayuntamientomadrid 'consul github'). + If you are a programmer, you can see the code and help us to improve it at [CONSUL app](https://github.com/consul/consul 'consul github'). titles: how_to_use: Use it in your local government privacy: Privacy Policy diff --git a/config/locales/en/responders.yml b/config/locales/en/responders.yml index 17de27b74..203d5700e 100644 --- a/config/locales/en/responders.yml +++ b/config/locales/en/responders.yml @@ -29,3 +29,4 @@ en: spending_proposal: "Spending proposal deleted succesfully." budget_investment: "Investment project deleted succesfully." error: "Could not delete" + topic: "Topic deleted successfully." diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 58fd31aa1..2db094999 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -498,18 +498,24 @@ es: final_recount: "Recuento final (presidente de mesa)" poll_shifts: new: - new_assignment: "Nuevo turno" + add_shift: "Añadir turno" + shift: "Asignación" + shifts: "Turnos en esta urna" date: "Fecha" + edit_shifts: Asignar turno + new_shift: "Nuevo turno" + no_shifts: "Esta urna no tiene turnos asignados" officer: "Presidente de mesa" - assignment: "Asignación" + remove_shift: "Eliminar turno" + search_officer_button: Buscar + search_officer_placeholder: Buscar presidentes de mesa + search_officer_text: Busca al presidente de mesa para asignar un turno select_date: "Seleccionar día" - select_officer: "Seleccionar presidente de mesa" - add_assignment: "Añadir turno" - remove_assignment: "Eliminar turno" - assignments: "Turnos en esta urna" - no_assignments: "Esta urna no tiene turnos asignados" + table_shift: "Turno" + table_email: "Email" + table_name: "Nombre" flash: - create: "Añadido turno de presidente de mesa" + create: "Añadido turno de presidente de mesa" destroy: "Eliminado turno de presidente de mesa" poll_booth_assignments: flash: @@ -590,6 +596,8 @@ es: title: Título valid_answers: Respuestas válidas description: Descripción + video_url: Video externo + documents: Documentos preview: Ver en la web recounts: index: @@ -604,6 +612,7 @@ es: no_results: "No hay resultados" table_whites: Papeletas en blanco table_nulls: Papeletas nulas + table_total: Papeletas totales table_answer: Respuesta table_votes: Votos booths: diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index cc6b27e2f..78b81fbd3 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -41,10 +41,10 @@ es: section_header: icon_alt: Icono de Presupuestos participativos title: Presupuestos participativos - description: Con los presupuestos participativos la ciudadanía decide a qué proyectos presentados por los vecinos y vecinas va destinada una parte del presupuesto municipal. help: Ayuda sobre presupuestos participativos section_footer: title: Ayuda sobre presupuestos participativos + description: Con los presupuestos participativos la ciudadanía decide a qué proyectos presentados por los vecinos y vecinas va destinada una parte del presupuesto municipal. help_text_1: "Los presupuestos participativos son unos procesos en los que la ciudadanía decide de forma directa en qué se gasta una parte del presupuesto municipal. Cualquier persona empadronada mayor de 16 años puede proponer un proyecto de gasto que se preselecciona en una fase de apoyos ciudadanos." help_text_2: "Los proyectos más votados se evalúan y pasan a una votación final en la que se deciden las actuaciones que llevará a cabo el Ayuntamiento una vez se aprueben los presupuestos municipales del año próximo." help_text_3: "La presentación de proyectos de presupuestos participativos se lleva a cabo desde enero y a lo largo de un periodo de mes y medio, aproximadamente. Para participar y plantear propuestas para toda la ciudad y/ los distritos hay que registrarse en %{org} y verificar la cuenta." diff --git a/config/locales/es/community.yml b/config/locales/es/community.yml index 196cd4c50..8013c116c 100644 --- a/config/locales/es/community.yml +++ b/config/locales/es/community.yml @@ -26,6 +26,7 @@ es: disabled_info_title: Necesitas estar logueado para crear un nuevo tema topic: edit_button: Editar + destroy_button: Eliminar comments: one: 1 Comentario other: "%{count} Comentarios" diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index dc2af84bb..f90dbc25c 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -126,10 +126,10 @@ es: section_header: icon_alt: Icono de Debates title: Debates - description: Inicia un debate para compartir puntos de vista con otras personas sobre los temas que te preocupan. help: Ayuda sobre los debates section_footer: title: Ayuda sobre los debates + description: Inicia un debate para compartir puntos de vista con otras personas sobre los temas que te preocupan. help_text_1: "El espacio de debates ciudadanos está dirigido a que cualquier persona pueda exponer temas que le preocupan y sobre los que quiera compartir puntos de vista con otras personas." help_text_2: 'Para abrir un debate es necesario registrarse en %{org}. Los usuarios ya registrados también pueden comentar los debates abiertos y valorarlos con los botones de "Estoy de acuerdo" o "No estoy de acuerdo" que se encuentran en cada uno de ellos.' help_text_3: "Ten en cuenta que un debate no activa ningún mecanismo de actuación concreto. Si quieres hacer una %{proposal} para la ciudad o plantear un proyecto de %{budget} cuando se abra la convocatoria, ve a la sección correspondiente." @@ -179,6 +179,7 @@ es: proposal_notification: "la notificación" spending_proposal: la propuesta de gasto budget/investment: la propuesta de inversión + poll/shift: el turno user: la cuenta verification/sms: el teléfono signature_sheet: la hoja de firmas @@ -376,10 +377,10 @@ es: section_header: icon_alt: Icono de Propuestas title: Propuestas - description: Haz una propuesta ciudadana. Si obtiene los apoyos suficientes y pasa a votación, puedes conseguir que todos los habitantes decidan cómo quieren que sea nuestra ciudad. help: Ayuda sobre las propuestas section_footer: title: Ayuda sobre las propuestas + description: Haz una propuesta ciudadana. Si obtiene los apoyos suficientes y pasa a votación, puedes conseguir que todos los habitantes decidan cómo quieren que sea nuestra ciudad. help_text_1: "Las propuestas ciudadanas son una oportunidad para que los vecinos y colectivos decidan directamente cómo quieren que sea su ciudad. Cualquier persona puede hacer una propuesta sobre un tema que le interese o preocupe para que el ayuntamiento la lleve a cabo, después de conseguir los apoyos suficientes y de someterse a votación ciudadana." help_text_2: "Para crear una propuesta hay que registrarse en %{org}. Las propuestas que consigan el apoyo del 1% de la gente en la web, pasan a votación. Para apoyar propuestas es necesario tener una cuenta verificada." help_text_3: "Se convoca una votación ciudadana cuando las propuestas consiguen los apoyos necesarios. Una vez celebrada, si hay más gente a favor que en contra, el Consistorio asume la propuesta y la lleva a cabo." @@ -468,10 +469,10 @@ es: section_header: icon_alt: Icono de Votaciones title: Votaciones - description: Regístrate para poder votar propuestas ciudadanas y las cuestiones que pregunta a sus vecinos el Ayuntamiento. Toma decisiones municipales de forma directa. help: Ayuda sobre las votaciones section_footer: title: Ayuda sobre las votaciones + description: Regístrate para poder votar propuestas ciudadanas y las cuestiones que pregunta a sus vecinos el Ayuntamiento. Toma decisiones municipales de forma directa. help_text_1: "Las votaciones se convocan cuando una propuesta ciudadana alcanza el 1% de apoyos del censo con derecho a voto. En las votaciones también se pueden incluir cuestiones que el Ayuntamiento somete a decisión directa de la ciudadanía." help_text_2: "Para participar en la próxima votación tienes que registrarte en %{org} y verificar tu cuenta. Pueden votar todas las personas empadronadas en la ciudad mayores de 16 años. Los resultados de todas las votaciones serán vinculantes para el gobierno." show: diff --git a/config/locales/es/legislation.yml b/config/locales/es/legislation.yml index 454422046..4d2a72cf5 100644 --- a/config/locales/es/legislation.yml +++ b/config/locales/es/legislation.yml @@ -66,10 +66,10 @@ es: section_header: icon_alt: Icono de Procesos legislativos title: Procesos legislativos - description: Participa en los debates y procesos previos a la aprobación de una norma o de una actuación municipal. Tu opinión será tenida en cuenta por el Ayuntamiento. help: Ayuda sobre procesos legislativos section_footer: title: Ayuda sobre procesos legislativos + description: Participa en los debates y procesos previos a la aprobación de una norma o de una actuación municipal. Tu opinión será tenida en cuenta por el Ayuntamiento. help_text_1: "En los procesos participativos, el Ayuntamiento ofrece a la ciudadanía la oportunidad de participar en la elaboración y modificación de normativa que afecta a la ciudad y de dar su opinión sobre ciertas actuaciones que tiene previsto llevar a cabo." help_text_2: "Las personas registradas en %{org} pueden participar con aportaciones en la consulta pública de nuevas ordenanzas, reglamentos y directrices, entre otros. Sus comentarios son analizados por el área correspondiente y tenidos en cuenta de cara a la redacción final de las normas." help_text_3: "El Ayuntamiento también abre procesos para recibir aportaciones y opiniones sobre actuaciones municipales." diff --git a/config/locales/es/officing.yml b/config/locales/es/officing.yml index bd18f1ca7..81dd70492 100644 --- a/config/locales/es/officing.yml +++ b/config/locales/es/officing.yml @@ -15,24 +15,6 @@ es: title: "Listado de votaciones finalizadas" no_polls: "No tienes permiso para recuento final en ninguna votación reciente" select_poll: "Selecciona votación" - add_recount: "Añadir recuentos finales" - add_results: "Añadir resultados" - final_recounts: - flash: - create: "Datos añadidos" - error_create: "Recuento final NO añadido. Error en los datos" - new: - title: "%{poll} - Añadir recuento final" - not_allowed: "No tienes permiso para introducir recountos finales" - booth: "Urna" - date: "Día" - select_booth: "Elige urna" - select_date: "Elige día" - count: "Recuento final" - count_placeholder: "Número final de votos" - submit: "Guardar" - final_recount_list: "Tus recuentos finales" - system_count: "Recuento del sistema" add_results: "Añadir resultados" results: flash: @@ -49,6 +31,7 @@ es: select_date: "Elige día" ballots_white: "Papeletas en blanco" ballots_null: "Papeletas nulas" + ballots_total: "Papeletas totales" submit: "Guardar" results_list: "Tus resultados" see_results: "Ver resultados" @@ -59,6 +42,7 @@ es: table_votes: Votos table_whites: Papeletas en blanco table_nulls: Papeletas nulas + table_total: Papeletas totales residence: flash: create: "Documento verificado con el Padrón" diff --git a/config/locales/es/pages.yml b/config/locales/es/pages.yml index 81c916b5a..d67568b2d 100644 --- a/config/locales/es/pages.yml +++ b/config/locales/es/pages.yml @@ -63,9 +63,9 @@ es: text: |- Utilízalo en tu municipio libremente o ayúdanos a mejorarlo, es software libre. - Este Portal de Gobierno Abierto usa la [aplicación CONSUL](https://github.com/ayuntamientomadrid 'github consul') que es software libre, con [licencia AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ), esto significa en palabras sencillas, que cualquiera puede libremente usar el código, copiarlo, verlo en detalle, modificarlo, y redistribuirlo al mundo con las modificaciones que quiera (manteniendo el que otros puedan a su vez hacer lo mismo). Porque creemos que la cultura es mejor y más rica cuando se libera. + Este Portal de Gobierno Abierto usa la [aplicación CONSUL](https://github.com/consul/consul 'github consul') que es software libre, con [licencia AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ), esto significa en palabras sencillas, que cualquiera puede libremente usar el código, copiarlo, verlo en detalle, modificarlo, y redistribuirlo al mundo con las modificaciones que quiera (manteniendo el que otros puedan a su vez hacer lo mismo). Porque creemos que la cultura es mejor y más rica cuando se libera. - Si eres programador, puedes ver el código y ayudarnos a mejorarlo en [aplicación CONSUL](https://github.com/ayuntamientomadrid 'github consul'). + Si eres programador, puedes ver el código y ayudarnos a mejorarlo en [aplicación CONSUL](https://github.com/consul/consul 'github consul'). titles: how_to_use: Utilízalo en tu municipio privacy: Política de Privacidad @@ -81,4 +81,4 @@ es: info_code: 'Ahora introduce el código que has recibido en tu carta:' password: Contraseña submit: Verificar mi cuenta - title: Verifica tu cuenta \ No newline at end of file + title: Verifica tu cuenta diff --git a/config/locales/es/responders.yml b/config/locales/es/responders.yml index d3d4ff163..74e3ea20a 100644 --- a/config/locales/es/responders.yml +++ b/config/locales/es/responders.yml @@ -29,3 +29,4 @@ es: spending_proposal: "Propuesta de inversión eliminada." budget_investment: "Propuesta de inversión eliminada." error: "No se pudo borrar" + topic: "Tema eliminado." diff --git a/config/locales/fr/general.yml b/config/locales/fr/general.yml index e4d0f9a51..cf1488375 100644 --- a/config/locales/fr/general.yml +++ b/config/locales/fr/general.yml @@ -1196,7 +1196,7 @@ fr: how_to_use: text: "Utilisez cet outil dans votre collectivité ou aidez-nous àl'améliorer, c'est un logiciel libre.\r\n\r\nCe portail de gouvernement ouvert utilise - cette [application Consul ](https://github.com/ayuntamientomadrid 'consul + cette [application Consul ](https://github.com/consul/consul 'consul github') qui est un logiciel libre, avec une [license AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ), ce qui signifie en fait que n'importe qui peut utiliser le code librement, le copier, l'étudier en détail, le modifier et le redistribuer @@ -1207,7 +1207,7 @@ fr: la Mairie de Madrid autant que possible pour le faire. Si vous êtes intéressé, n'hésitez pas à nous contacter : ag.gobiernoabierto@consul.dev\r\n\r\nSi vous êtes développeur, vous pouvez voir le code et nous aider à l'améliorer - sur [Consul app](https://github.com/ayuntamientomadrid 'consul github ' + sur [Consul app](https://github.com/consul/consul 'consul github ' )." titles: faq: Solution pour les problèmes techniques (FAQ) diff --git a/config/locales/fr/pages.yml b/config/locales/fr/pages.yml index 42a4e3f2b..dcb57d400 100644 --- a/config/locales/fr/pages.yml +++ b/config/locales/fr/pages.yml @@ -59,9 +59,9 @@ fr: text: |- Utilisez le librement pour votre gouvernement ou aider nous à l'améliorer, c'est un logiciel libre. - Ce portail de Gouvernement Ouvert utilise l'[application Consul](https://github.com/ayuntamientomadrid 'consul github') qui est un logiciel libre, sous [licence AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ), ce qui, en quelques mots, signifie que tout le monde peut utiliser le code librement, le copier, le voir en détail, le modifier et le redistribuer avec ces modifications (permettant à d'autres d'en faire de même). Car nous pensons que la culture est meilleure et plus riche quand elle est libre. + Ce portail de Gouvernement Ouvert utilise l'[application Consul](https://github.com/consul/consul 'consul github') qui est un logiciel libre, sous [licence AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ), ce qui, en quelques mots, signifie que tout le monde peut utiliser le code librement, le copier, le voir en détail, le modifier et le redistribuer avec ces modifications (permettant à d'autres d'en faire de même). Car nous pensons que la culture est meilleure et plus riche quand elle est libre. - Si vous êtes un développeur, vous pouvez voir le code et nous aider à l'améliorer en allant sur l'[applicationConsul](https://github.com/ayuntamientomadrid 'consul github'). + Si vous êtes un développeur, vous pouvez voir le code et nous aider à l'améliorer en allant sur l'[applicationConsul](https://github.com/consul/consul 'consul github'). titles: how_to_use: Utilisez-le pour votre gouvernement privacy: Politique de confidentialité diff --git a/config/locales/nl/pages.yml b/config/locales/nl/pages.yml index 71edb19fe..a07151243 100644 --- a/config/locales/nl/pages.yml +++ b/config/locales/nl/pages.yml @@ -60,9 +60,9 @@ nl: text: |- Gebruik het in uw gemeente en help ons het te verbeteren, het is gratis software. - Deze Open Overheid site gebruikt de [Consul web-app](https://github.com/ayuntamientomadrid 'consul github'). Gratis software, onder de [AGPLv3 Licentie](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ). Simpel gezegd betekend dit dat u de app kunt inspecteren, gebruiken, aanpassen en herdistribueren (onder voorwaarde dat anderen hetzelfde kunnen doen met uw versie). + Deze Open Overheid site gebruikt de [Consul web-app](https://github.com/consul/consul 'consul github'). Gratis software, onder de [AGPLv3 Licentie](http://www.gnu.org/licenses/agpl-3.0.html 'AGPLv3 gnu' ). Simpel gezegd betekend dit dat u de app kunt inspecteren, gebruiken, aanpassen en herdistribueren (onder voorwaarde dat anderen hetzelfde kunnen doen met uw versie). - Voor programmeurs: help ons het te verbeteren via [Consul app](https://github.com/ayuntamientomadrid 'consul github'). + Voor programmeurs: help ons het te verbeteren via [Consul app](https://github.com/consul/consul 'consul github'). titles: how_to_use: Gebruik het in uw gemeente privacy: Privacy Verklaring @@ -78,4 +78,4 @@ nl: info_code: 'en de code die u heeft ontvangen:' password: Wachtwoord submit: Verifieer mijn account - title: Verifieer uw account \ No newline at end of file + title: Verifieer uw account diff --git a/config/locales/pt-BR/general.yml b/config/locales/pt-BR/general.yml index 69ca72c02..816c14fa2 100644 --- a/config/locales/pt-BR/general.yml +++ b/config/locales/pt-BR/general.yml @@ -1212,7 +1212,7 @@ pt-BR: how_to_use: text: "Use este programa em seu governo local ou nos ajude a melhorá-lo, ele é um software livre.\r\n\r\nEste Portal de Governo Aberto usa o [app Consul] - (https://github.com/ayuntamientomadrid 'cônsul github'), que é um software + (https://github.com/consul/consul 'cônsul github'), que é um software livre, com [licença AGPLv3] (http://www.gnu.org/licenses/agpl- 3.0.html 'AGPLv3 gnu'), o que significa, em palavras simples que qualquer um pode usar o código livremente, copiá-lo, vê-lo em detalhes, modificá-lo e redistribuí-lo @@ -1223,7 +1223,7 @@ pt-BR: Madrid, o tanto quanto possível para fazê-lo, por isso, se você estiver interessado escreva-nos: ag.gobiernoabierto@consul.dev \r\n\r\nSe você é um programador, você - pode ver o código e nos ajudar a melhorá-lo em [app Consul] (https://github.com/ayuntamientomadrid + pode ver o código e nos ajudar a melhorá-lo em [app Consul] (https://github.com/consul/consul 'cônsul github')." titles: faq: Solução para problemas técnicos (FAQ) diff --git a/config/routes.rb b/config/routes.rb index eaf6b98f4..a02384ced 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -287,7 +287,11 @@ Rails.application.routes.draw do end resources :booths do - resources :shifts + get :available, on: :collection + + resources :shifts do + get :search_officers, on: :collection + end end resources :questions @@ -416,7 +420,6 @@ Rails.application.routes.draw do resources :polls, only: [:index] do get :final, on: :collection - resources :final_recounts, only: [:new, :create] resources :results, only: [:new, :create, :index] end resource :residence, controller: "residence", only: [:new, :create] diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb index 9c7eb800f..d41595683 100644 --- a/db/dev_seeds.rb +++ b/db/dev_seeds.rb @@ -90,6 +90,7 @@ valuator.update(residence_verified_at: Time.current, confirmed_phone: Faker::Pho poll_officer = create_user('poll_officer@consul.dev', 'Paul O. Fisher') poll_officer.create_poll_officer +poll_officer.update(residence_verified_at: Time.current, confirmed_phone: Faker::PhoneNumber.phone_number, document_type: "1", verified_at: Time.current, document_number: "2211111111") level_2 = create_user('leveltwo@consul.dev', 'level 2') level_2.update(residence_verified_at: Time.current, confirmed_phone: Faker::PhoneNumber.phone_number, document_number: "2222222222", document_type: "1") diff --git a/db/migrate/20170905111444_add_video_url_to_poll_questions.rb b/db/migrate/20170905111444_add_video_url_to_poll_questions.rb new file mode 100644 index 000000000..800ca915e --- /dev/null +++ b/db/migrate/20170905111444_add_video_url_to_poll_questions.rb @@ -0,0 +1,5 @@ +class AddVideoUrlToPollQuestions < ActiveRecord::Migration + def change + add_column :poll_questions, :video_url, :string + end +end diff --git a/db/migrate/20170908175149_add_officer_data_to_poll_shifts.rb b/db/migrate/20170908175149_add_officer_data_to_poll_shifts.rb new file mode 100644 index 000000000..0982345b4 --- /dev/null +++ b/db/migrate/20170908175149_add_officer_data_to_poll_shifts.rb @@ -0,0 +1,6 @@ +class AddOfficerDataToPollShifts < ActiveRecord::Migration + def change + add_column :poll_shifts, :officer_name, :string + add_column :poll_shifts, :officer_email, :string + end +end diff --git a/db/migrate/20170914114427_create_poll_total_results.rb b/db/migrate/20170914114427_create_poll_total_results.rb new file mode 100644 index 000000000..1a733dce4 --- /dev/null +++ b/db/migrate/20170914114427_create_poll_total_results.rb @@ -0,0 +1,18 @@ +class CreatePollTotalResults < ActiveRecord::Migration + def change + create_table :poll_total_results do |t| + t.integer :author_id + t.integer :amount + t.string :origin + t.date :date + t.integer :booth_assignment_id + t.integer :officer_assignment_id + t.text :amount_log, default: "" + t.text :officer_assignment_id_log, default: "" + t.text :author_id_log, default: "" + end + + add_index :poll_total_results, :officer_assignment_id + add_index :poll_total_results, :booth_assignment_id + end +end diff --git a/db/migrate/20170914154743_fix_password_changed_at_default.rb b/db/migrate/20170914154743_fix_password_changed_at_default.rb new file mode 100644 index 000000000..dd3eb9aea --- /dev/null +++ b/db/migrate/20170914154743_fix_password_changed_at_default.rb @@ -0,0 +1,10 @@ +class FixPasswordChangedAtDefault < ActiveRecord::Migration + def up + change_column_default :users, :password_changed_at, Time.new(2015, 1, 1, 1, 1, 1) + end + + def down + change_column_default :users, :password_changed_at, Time.now + end + +end diff --git a/db/schema.rb b/db/schema.rb index b87332817..45598ff4e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170822144743) do +ActiveRecord::Schema.define(version: 20170914154743) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -669,6 +669,7 @@ ActiveRecord::Schema.define(version: 20170822144743) do t.datetime "created_at" t.datetime "updated_at" t.tsvector "tsv" + t.string "video_url" end add_index "poll_questions", ["author_id"], name: "index_poll_questions_on_author_id", using: :btree @@ -682,12 +683,29 @@ ActiveRecord::Schema.define(version: 20170822144743) do t.date "date" t.datetime "created_at" t.datetime "updated_at" + t.string "officer_name" + t.string "officer_email" end add_index "poll_shifts", ["booth_id", "officer_id"], name: "index_poll_shifts_on_booth_id_and_officer_id", using: :btree add_index "poll_shifts", ["booth_id"], name: "index_poll_shifts_on_booth_id", using: :btree add_index "poll_shifts", ["officer_id"], name: "index_poll_shifts_on_officer_id", using: :btree + create_table "poll_total_results", force: :cascade do |t| + t.integer "author_id" + t.integer "amount" + t.string "origin" + t.date "date" + t.integer "booth_assignment_id" + t.integer "officer_assignment_id" + t.text "amount_log", default: "" + t.text "officer_assignment_id_log", default: "" + t.text "author_id_log", default: "" + end + + add_index "poll_total_results", ["booth_assignment_id"], name: "index_poll_total_results_on_booth_assignment_id", using: :btree + add_index "poll_total_results", ["officer_assignment_id"], name: "index_poll_total_results_on_officer_assignment_id", using: :btree + create_table "poll_voters", force: :cascade do |t| t.string "document_number" t.string "document_type" @@ -971,7 +989,7 @@ ActiveRecord::Schema.define(version: 20170822144743) do t.boolean "email_digest", default: true t.boolean "email_on_direct_message", default: true t.boolean "official_position_badge", default: false - t.datetime "password_changed_at", default: '2017-08-07 11:14:09', null: false + t.datetime "password_changed_at", default: '2015-01-01 00:01:01', null: false t.boolean "created_from_signature", default: false t.integer "failed_email_digests_count", default: 0 t.text "former_users_data_log", default: "" diff --git a/db/seeds.rb b/db/seeds.rb index 478771474..e271e319a 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -39,10 +39,10 @@ Setting["months_to_archive_proposals"] = 12 Setting["email_domain_for_officials"] = '' # Code to be included at the top (inside ) of every page (useful for tracking) -Setting['per_page_code_head'] = '' +Setting['per_page_code_head'] = '' # Code to be included at the top (inside ) of every page -Setting['per_page_code_body'] = '' +Setting['per_page_code_body'] = '' # Social settings Setting["twitter_handle"] = nil diff --git a/doc/en/dev_test_setup.md b/doc/en/dev_test_setup.md deleted file mode 100644 index d5fa28fe0..000000000 --- a/doc/en/dev_test_setup.md +++ /dev/null @@ -1,11 +0,0 @@ -# Configuration for development and test environments (Mac OS X) - -## Linux - -See [here](dev_test_setup_linux.md) - -## Mac OS X - -See [here](dev_test_setup_osx.md) - -## Windows diff --git a/doc/en/dev_test_setup_linux.md b/doc/en/dev_test_setup_linux.md deleted file mode 100644 index 5cb89d166..000000000 --- a/doc/en/dev_test_setup_linux.md +++ /dev/null @@ -1,122 +0,0 @@ -# Configuration for development and test environments (GNU/Linux) - -## Git - -Git is officially maintained in Debian/Ubuntu: - -``` -sudo apt-get install git -``` - -## Ruby - -Ruby versions packaged in official repositories are not suitable to work with consul (at least Debian 7 and 8), so we'll have to install it manually. - -The preferred method is via rvm: - -(only the multi user option installs all dependencies automatically, as we use 'sudo'.) - -### As local user - -``` -curl -L https://get.rvm.io | bash -s stable -``` - -### For all system users - -``` -curl -L https://get.rvm.io | sudo bash -s stable -``` - -and then add your user to rvm group - -``` -sudo usermod -a -G rvm -``` - -and finally, add rvm script source to user's bash (~/.bashrc) (this step it's only necessary if you still can't execute rvm command) - -``` -[[ -s /usr/local/rvm/scripts/rvm ]] && source /usr/local/rvm/scripts/rvm -``` - -with all this, you are suppose to be able to install a ruby version from rvm, as for example version 2.3.0: - -``` -sudo rvm install 2.3.0 -``` - -## Bundler - -with - -``` -gem install bundler -``` - -or there is more methods [here](https://rvm.io/integration/bundler) that should be better as: - -``` -gem install rubygems-bundler -``` - -## PostgreSQL (>=9.4) - -PostgreSQL version 9.4 is not official in debian 7 (wheezy), in 8 it seems to be officially maintained. - -So you have to add a repository, the official postgresql works fine. - -Add the repository to apt, for example creating file */etc/apt/sources.list.d/pgdg.list* with: - -``` -deb http://apt.postgresql.org/pub/repos/apt/ wheezy-pgdg main -``` - -afterwards you'll have to download the key, and install it, by: - -``` -wget https://www.postgresql.org/media/keys/ACCC4CF8.asc -apt-key add ACCC4CF8.asc -``` - -and install postgresql - -``` -apt-get update -apt-get install postgresql-9.4 -``` - -## Cloning the repository - -Now, with all the dependencies installed, clone the CONSUL repository: - -``` -git clone https://github.com/consul/consul.git -cd consul -bundle install -cp config/database.yml.example config/database.yml -cp config/secrets.yml.example config/secrets.yml -``` - -Perhaps it's needed to create a superuser rol with password in postgresql, and write it in */config/database.yml* 'user:' and 'password:' fields. - -Also, it seems that postgresql use as default an unix socket for localhost communications. If we encounter problems creating database (connection problems) we can change in */config/database.yml* the line: - -``` -host: localhost -``` - -for: - -``` -host: /var/run/postgresql -``` - -After this: - -``` -rake db:create -rake db:setup -rake db:dev_seed -RAILS_ENV=test bin/rake db:setup -``` diff --git a/doc/en/dev_test_setup_osx.md b/doc/en/dev_test_setup_osx.md deleted file mode 100644 index 82f6f430e..000000000 --- a/doc/en/dev_test_setup_osx.md +++ /dev/null @@ -1,128 +0,0 @@ -# Configuration for development and test environments (Mac OS X) - -## Homebrew - -Homebrew is a very popular package manager for OS X. It's advised to use it since it makes the installation of some of the dependencies much easier. - -You can find the installation instructions at: [brew.sh](http://brew.sh) - -## XCode and XCode Command Line Tools - -To install *git* you'll first need to install *Xcode* (download it from the Mac App Store) and its *Xcode Command Line Tools* (you can install them from the Xcode's app menu) - -## Git - -You can download git from: [git-scm.com/download/mac](https://git-scm.com/download/mac) - -## Ruby y rbenv - -OS X already comes with a preinstalled Ruby version, but it's quite old and we need a newer one (2.3.2). One of the multiple ways of installing Ruby in OS X is through *rbenv*. The installation instructions are in its GitHub repository and are pretty straight-forward: - -[github.com/rbenv/rbenv](https://github.com/rbenv/rbenv) - -## Bundler - -``` -gem install bundler -``` - -## PostgreSQL (>=9.4) - -``` -brew install postgres -``` - -Once installed, we need to *initialize* it: - -``` -initdb /usr/local/var/postgres -``` - -Now we're going to configure some things related to the *default user*. First we start postgres server with: - -``` -postgres -D /usr/local/var/postgres -``` - -At this point we're supposed to have postgres correctly installed and a default user will automatically be created (whose name will match our username). This user hasn't got a password yet. - -If we run `psql` we'll login into the postgres console with the default user. Probably it will fail since its required that a default database exists for that user. We can create it by typing: - -``` -createdb 'your_username' -``` - -If we run `psql` again we should now get access to postgres console. With `\du` you can see the current users list. - -In case you want to set a password for your user you can make it throught postgres console by: - -``` -ALTER USER your_username WITH PASSWORD 'your_password'; -``` - -Now we'll create the *consul* user, the one the application is using. Run in postgres console: - -``` -CREATE ROLE consul WITH PASSWORD '000'; -ALTER ROLE consul WITH SUPERUSER; -ALTER ROLE consul WITH login; -``` - -If at any point during PostgreSQL installation you feel you have messed things up, you can uninstall it and start again by running: - -``` -brew uninstall postgres -``` - -You'll have to delete also this directory (otherwise the new installation will generate conflicts, source: [gist.github.com/lxneng/741932](https://gist.github.com/lxneng/741932)): - -``` -rm -rf /usr/local/var/postgres -``` - -## Postgis - -``` -brew install postgis -``` - -## PhantomJS - -``` -brew install phantomjs -``` - -## Imagemagick - -``` -brew install imagemagick -``` - -## Cloning the repository - -Now that we have all the dependencies installed we can download the repository: - -``` -git clone https://github.com/consul/consul.git -cd consul -bundle install -cp config/database.yml.example config/database.yml -cp config/secrets.yml.example config/secrets.yml -``` - -Now copy in `database.yml` the database user and password you used for *consul*. - -After this: - -``` -rake db:create -rake db:setup -rake db:dev_seed -RAILS_ENV=test bin/rake db:setup -``` - -To run the tests: - -``` -bundle exec rspec -``` diff --git a/doc/es/dev_test_setup.md b/doc/es/dev_test_setup.md deleted file mode 100644 index cda2613e7..000000000 --- a/doc/es/dev_test_setup.md +++ /dev/null @@ -1,11 +0,0 @@ -# Configuración para los entornos de desarrollo y pruebas - -## Linux - -Consultar [aqui](dev_test_setup_linux.md) - -## Mac OS X - -Consultar [aquí](dev_test_setup_osx.md) - -## Windows diff --git a/doc/es/dev_test_setup_linux.md b/doc/es/dev_test_setup_linux.md deleted file mode 100644 index 6f3b2e47a..000000000 --- a/doc/es/dev_test_setup_linux.md +++ /dev/null @@ -1,141 +0,0 @@ -# Configuración para los entornos de desarrollo y pruebas (GNU/Linux) - -## Git - -Git es mantenido oficialmente en Debian/Ubuntu: - -``` -sudo apt-get install git -``` - -## Ruby - -Las versiones de Ruby versions empaquetadas en repositorios oficiales no son aptas para trabajar con consul (al menos Debian 7 y 8), así que debemos instalar manualmente. - -El método recomendado es via rvm: - -(sólo la opción multiusuario instala todas las dependencias automáticamente, al usar 'sudo'.) - -### Como usuario local - -``` -curl -L https://get.rvm.io | bash -s stable -``` - -### Para todos los usuarios del sistema - -``` -curl -L https://get.rvm.io | sudo bash -s stable -``` - -añadismos nuestro usuario al grupo de rvm - -``` -sudo usermod -a -G rvm -``` - -y finalmente, añadimos el script rvm a nuestro bash (~/.bashrc) (este paso sólo es necesario si no puedes ejecutar el comando rvm) - -``` -[[ -s /usr/local/rvm/scripts/rvm ]] && source /usr/local/rvm/scripts/rvm -``` - -con todo esto, deberías poder instalar la versión de ruby con rvm, por ejemplo la 2.3.2: - -``` -sudo rvm install 2.3.2 -``` - -## Bundler - -usando - -``` -gem install bundler -``` - -hay varios métodos alternativos [aquí](https://rvm.io/integration/bundler) que podrían ser mejores como: - -``` -gem install rubygems-bundler -``` - -## PostgreSQL (>=9.4) - -La versión 9.4 de PostgreSQL no es oficial en Debian 7 (wheezy), pero en Debian 8 parece ser mantenida oficialmente. - -Así que debemos añadir el respositorio oficial de postgresql a apt, por ejemplo creando el fichero */etc/apt/sources.list.d/pgdg.list* con: - -``` -deb http://apt.postgresql.org/pub/repos/apt/ wheezy-pgdg main -``` - -después deberás descargar la key e instalarla: - -``` -wget https://www.postgresql.org/media/keys/ACCC4CF8.asc -apt-key add ACCC4CF8.asc -``` - -y finalmente instalar postgresql - -``` -apt-get update -apt-get install postgresql-9.4 -``` - -## Clonar el repositorio - -Ahora que ya tenemos todas las dependencias instalado podemos bajarnos el proyecto: - -``` -git clone https://github.com/consul/consul.git -cd consul -bundle install -cp config/database.yml.example config/database.yml -cp config/secrets.yml.example config/secrets.yml -``` - -Ahora copia en `database.yml` el usuario y la contraseña que pusiste para *consul*. Cuando ya lo hayas hecho: - -``` -rake db:create -rake db:setup -rake db:dev_seed -RAILS_ENV=test bin/rake db:setup -``` - -Para ejecutar los tests: - -``` -bundle exec rspec -``` - -Quizás necesites crear un rol de superusuario en postgresql, y completar en el fichero*/config/database.yml* los campos 'user:' y 'password:'. - -Además, parece que postgresql usa un socket unix por defecto para las comunicaciones en local. Si te encuentras este problema creando la base de datos, cambia en */config/database.yml* la linea: - -``` -host: localhost -``` - -por: - -``` -host: /var/run/postgresql -``` - -Tras esto en el terminal ejecutaremos: - -``` -rake db:create -rake db:setup -rake db:dev_seed -RAILS_ENV=test bin/rake db:setup -``` - -Y por último para comprobar que todo esta bien, lanza los tests: - -``` -bundle exec rspec -``` diff --git a/doc/es/dev_test_setup_osx.md b/doc/es/dev_test_setup_osx.md deleted file mode 100644 index 01cea3923..000000000 --- a/doc/es/dev_test_setup_osx.md +++ /dev/null @@ -1,128 +0,0 @@ -# Configuración para los entornos de desarrollo y pruebas (Mac OS X) - -## Homebrew - -Homebrew es un gestor de paquetes para OS X muy popular. Es recomendable usarlo pues facilita enormemente la instalación de algunos de los paquetes necesarios. - -Puedes encontrar las instrucciones de instalación en: [brew.sh](http://brew.sh) - -## XCode y XCode Command Line Tools - -Para utilizar git necesitarás instalar *Xcode* (está en la Mac App Store) y las *Xcode Command Line Tools* (se instalan desde el menú de Xcode). - -## Git y Github - -Puedes descargar git desde: [git-scm.com/download/mac](https://git-scm.com/download/mac) - -## Ruby y rbenv - -OS X ya viene con una versión preinstalada de ruby, pero es bastante vieja y en nuestro caso no nos sirve. Una de las formas de instalar Ruby es a través de rbenv. Las instrucciones de instalación están en su GitHub y son bastante claras: - -[github.com/rbenv/rbenv](https://github.com/rbenv/rbenv) - -Después instala la versión de Ruby 2.3.2 - -## Bundler - -``` -gem install bundler -``` - -## PostgreSQL (>=9.4) - -``` -brew install postgres -``` - -Una vez instalado es necesario *inicializar* la instalación: - -``` -initdb /usr/local/var/postgres -``` - -Ahora vamos a configurar algunos aspectos del usuario por defecto. Primero iniciamos el servidor de postgres con: - -``` -postgres -D /usr/local/var/postgres -``` - -Llegados a este punto se supone que tenemos postgres correctamente instalado y se nos habrá creado un usuario por defecto (cuyo nombre es nuestro nombre de usuario), y que (todavía) no tiene contraseña. - -Si ejecutamos `psql` accederemos a la consola de postgres con el usuario por defecto. Probablemente fallará porque es necesario que de antemano exista una base de datos por defecto para dicho usuario. Podemos crearla ejecutando sobre la terminal: - -``` -createdb 'tu_nombre_de_usuario' -``` - -Si ahora ejecutamos `psql` de nuevo deberíamos poder acceder correctamente a la consola de postgres. Si sobre la consola de postgres ejecutas `\du` puede ver la lista de usuarios actual. - -En el caso de que quieras asignarte una contraseña puedes hacerlo desde la consola de postgres con: - -``` -ALTER USER tu_nombre_usuario WITH PASSWORD 'tu_contraseña'; -``` - -Ahora vamos a crear el usuario *consul*, que es el que utiliza la aplicación. Ejecuta sobre la consola de postgres: - -``` -CREATE ROLE consul WITH PASSWORD '000'; -ALTER ROLE consul WITH SUPERUSER; -ALTER ROLE consul WITH login; -``` - -Si en algún momento durante la instalación de PostgreSQL y postgis sospechas que te has equivocado y deseas desinstalarlo y volver a empezar desde cero: - -``` -brew uninstall postgres -``` - -También tendrás que borrar el siguiente directorio para que no de conflictos cuando intentes volver a instalarlo (fuente: [gist.github.com/lxneng/741932](https://gist.github.com/lxneng/741932)): - -``` -rm -rf /usr/local/var/postgres -``` - -## Postgis - -``` -brew install postgis -``` - -## PhantomJS - -``` -brew install phantomjs -``` - -## Imagemagick - -``` -brew install imagemagick -``` - -## Clonar el repositorio - -Ahora que ya tenemos todas las dependencias instalado podemos bajarnos el proyecto: - -``` -git clone https://github.com/consul/consul.git -cd consul -bundle install -cp config/database.yml.example config/database.yml -cp config/secrets.yml.example config/secrets.yml -``` - -Ahora copia en `database.yml` el usuario y la contraseña que pusiste para *consul*. Cuando ya lo hayas hecho: - -``` -rake db:create -rake db:setup -rake db:dev_seed -RAILS_ENV=test bin/rake db:setup -``` - -Para ejecutar los tests: - -``` -bundle exec rspec -``` diff --git a/lib/tasks/communities.rake b/lib/tasks/communities.rake index ad08e4bd9..261f4dbee 100644 --- a/lib/tasks/communities.rake +++ b/lib/tasks/communities.rake @@ -5,14 +5,14 @@ namespace :communities do Proposal.all.each do |proposal| if proposal.community.blank? - community = Community.create + community = Community.create proposal.update(community_id: community.id) end end Budget::Investment.all.each do |investment| if investment.community.blank? - community = Community.create + community = Community.create investment.update(community_id: community.id) end end diff --git a/public/consul_brand.zip b/public/consul_brand.zip deleted file mode 100644 index 6664d406f..000000000 Binary files a/public/consul_brand.zip and /dev/null differ diff --git a/spec/factories.rb b/spec/factories.rb index 226723fe3..b1c6e7174 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -155,7 +155,7 @@ FactoryGirl.define do description 'Proposal description' question 'Proposal question' external_url 'http://external_documention.es' - video_url 'http://video_link.com' + video_url 'https://youtu.be/nhuNb0XtRhQ' responsible_name 'John Snow' terms_of_service '1' association :author, factory: :user @@ -380,6 +380,10 @@ FactoryGirl.define do trait :budget_investment_document do association :documentable, factory: :budget_investment end + + trait :poll_question_document do + association :documentable, factory: :poll_question + end end factory :comment do @@ -552,6 +556,11 @@ FactoryGirl.define do origin { 'web' } end + factory :poll_total_result, class: 'Poll::TotalResult' do + association :author, factory: :user + origin { 'web' } + end + factory :officing_residence, class: 'Officing::Residence' do user association :officer, factory: :poll_officer diff --git a/spec/features/admin/poll/booths_spec.rb b/spec/features/admin/poll/booths_spec.rb index ec8c10d58..fd8fb9e84 100644 --- a/spec/features/admin/poll/booths_spec.rb +++ b/spec/features/admin/poll/booths_spec.rb @@ -36,6 +36,32 @@ feature 'Admin booths' do expect(page).to_not have_content "There are no booths" end + scenario "Available" do + booth_for_current_poll = create(:poll_booth) + booth_for_incoming_poll = create(:poll_booth) + booth_for_expired_poll = create(:poll_booth) + + current_poll = create(:poll, :current) + incoming_poll = create(:poll, :incoming) + expired_poll = create(:poll, :expired) + + create(:poll_booth_assignment, poll: current_poll, booth: booth_for_current_poll) + create(:poll_booth_assignment, poll: incoming_poll, booth: booth_for_incoming_poll) + create(:poll_booth_assignment, poll: expired_poll, booth: booth_for_expired_poll) + + visit admin_root_path + + within('#side_menu') do + click_link "Manage shifts" + end + + expect(page).to have_css(".booth", count: 2) + + expect(page).to have_content booth_for_current_poll.name + expect(page).to have_content booth_for_incoming_poll.name + expect(page).to_not have_content booth_for_expired_poll.name + end + scenario 'Show' do booth = create(:poll_booth) diff --git a/spec/features/admin/poll/questions_spec.rb b/spec/features/admin/poll/questions_spec.rb index e9b9f277a..392b2aabf 100644 --- a/spec/features/admin/poll/questions_spec.rb +++ b/spec/features/admin/poll/questions_spec.rb @@ -38,6 +38,7 @@ feature 'Admin poll questions' do Pursued by the Empire's sinister agents, Princess Leia races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the galaxy.... } + video_url = "https://puppyvideos.com" visit admin_questions_path click_link "Create question" @@ -45,11 +46,13 @@ feature 'Admin poll questions' do select 'Movies', from: 'poll_question_poll_id' fill_in 'poll_question_title', with: title fill_in 'poll_question_description', with: description + fill_in 'poll_question_video_url', with: video_url click_button 'Save' expect(page).to have_content(title) expect(page).to have_content(description) + expect(page).to have_content(video_url) end scenario 'Create from successful proposal index' do diff --git a/spec/features/admin/poll/shifts_spec.rb b/spec/features/admin/poll/shifts_spec.rb index fd9b9f4ca..761a79536 100644 --- a/spec/features/admin/poll/shifts_spec.rb +++ b/spec/features/admin/poll/shifts_spec.rb @@ -30,7 +30,7 @@ feature 'Admin shifts' do expect(page).to have_content officer.name end - scenario "Create" do + scenario "Create", :js do poll = create(:poll) booth = create(:poll_booth) officer = create(:poll_officer) @@ -41,10 +41,13 @@ feature 'Admin shifts' do click_link "Manage shifts" end - select I18n.l(poll.starts_at.to_date, format: :long), from: 'shift_date' - select officer.name, from: 'shift_officer_id' - click_button "Add shift" + fill_in "search", with: officer.email + click_button "Search" + click_link "Edit shifts" + select I18n.l(poll.starts_at.to_date, format: :long), from: 'shift_date' + click_button "Add shift" + expect(page).to have_content "Shift added" within("#shifts") do @@ -54,6 +57,25 @@ feature 'Admin shifts' do end end + scenario "Erros on create", :js do + poll = create(:poll) + booth = create(:poll_booth) + officer = create(:poll_officer) + + visit admin_booths_path + + within("#booth_#{booth.id}") do + click_link "Manage shifts" + end + + fill_in "search", with: officer.email + click_button "Search" + click_link "Edit shifts" + click_button "Add shift" + + expect(page).to have_content "can't be blank" + end + scenario "Destroy" do poll = create(:poll) booth = create(:poll_booth) @@ -76,6 +98,20 @@ feature 'Admin shifts' do expect(page).to have_css(".shift", count: 0) end + scenario "Destroy an officer" do + poll = create(:poll) + booth = create(:poll_booth) + officer = create(:poll_officer) + + shift = create(:poll_shift, officer: officer, booth: booth) + officer.destroy + + visit new_admin_booth_shift_path(booth) + + expect(page).to have_css(".shift", count: 1) + expect(page).to have_content(officer.name) + end + scenario "Empty" do poll = create(:poll) booth = create(:poll_booth) diff --git a/spec/features/comments/budget_investments_spec.rb b/spec/features/comments/budget_investments_spec.rb index 587a1c517..ae8fc935a 100644 --- a/spec/features/comments/budget_investments_spec.rb +++ b/spec/features/comments/budget_investments_spec.rb @@ -33,6 +33,10 @@ feature 'Commenting Budget::Investments' do expect(page).to have_content second_child.body expect(page).to have_link "Go back to #{investment.title}", href: budget_investment_path(investment.budget, investment) + + expect(page).to have_selector("ul#comment_#{parent_comment.id}>li", count: 2) + expect(page).to have_selector("ul#comment_#{first_child.id}>li", count: 1) + expect(page).to have_selector("ul#comment_#{second_child.id}>li", count: 1) end scenario 'Collapsable comments', :js do diff --git a/spec/features/comments/debates_spec.rb b/spec/features/comments/debates_spec.rb index 76320a848..28166784a 100644 --- a/spec/features/comments/debates_spec.rb +++ b/spec/features/comments/debates_spec.rb @@ -33,6 +33,10 @@ feature 'Commenting debates' do expect(page).to have_content second_child.body expect(page).to have_link "Go back to #{debate.title}", href: debate_path(debate) + + expect(page).to have_selector("ul#comment_#{parent_comment.id}>li", count: 2) + expect(page).to have_selector("ul#comment_#{first_child.id}>li", count: 1) + expect(page).to have_selector("ul#comment_#{second_child.id}>li", count: 1) end scenario 'Collapsable comments', :js do diff --git a/spec/features/comments/legislation_annotations_spec.rb b/spec/features/comments/legislation_annotations_spec.rb index 87896df03..aad2b90b2 100644 --- a/spec/features/comments/legislation_annotations_spec.rb +++ b/spec/features/comments/legislation_annotations_spec.rb @@ -38,6 +38,10 @@ feature 'Commenting legislation questions' do expect(page).to have_content second_child.body expect(page).to have_link "Go back to #{legislation_annotation.title}", href: href + + expect(page).to have_selector("ul#comment_#{parent_comment.id}>li", count: 2) + expect(page).to have_selector("ul#comment_#{first_child.id}>li", count: 1) + expect(page).to have_selector("ul#comment_#{second_child.id}>li", count: 1) end scenario 'Collapsable comments', :js do diff --git a/spec/features/comments/legislation_questions_spec.rb b/spec/features/comments/legislation_questions_spec.rb index 50eb60172..d21cefc4e 100644 --- a/spec/features/comments/legislation_questions_spec.rb +++ b/spec/features/comments/legislation_questions_spec.rb @@ -35,6 +35,10 @@ feature 'Commenting legislation questions' do expect(page).to have_content second_child.body expect(page).to have_link "Go back to #{legislation_question.title}", href: href + + expect(page).to have_selector("ul#comment_#{parent_comment.id}>li", count: 2) + expect(page).to have_selector("ul#comment_#{first_child.id}>li", count: 1) + expect(page).to have_selector("ul#comment_#{second_child.id}>li", count: 1) end scenario 'Collapsable comments', :js do diff --git a/spec/features/comments/proposals_spec.rb b/spec/features/comments/proposals_spec.rb index 475e018f3..acba0bec9 100644 --- a/spec/features/comments/proposals_spec.rb +++ b/spec/features/comments/proposals_spec.rb @@ -32,6 +32,10 @@ feature 'Commenting proposals' do expect(page).to have_content first_child.body expect(page).to have_content second_child.body expect(page).to have_link "Go back to #{proposal.title}", href: proposal_path(proposal) + + expect(page).to have_selector("ul#comment_#{parent_comment.id}>li", count: 2) + expect(page).to have_selector("ul#comment_#{first_child.id}>li", count: 1) + expect(page).to have_selector("ul#comment_#{second_child.id}>li", count: 1) end scenario 'Collapsable comments', :js do diff --git a/spec/features/comments/topics_spec.rb b/spec/features/comments/topics_spec.rb index c72e8028d..38971fcff 100644 --- a/spec/features/comments/topics_spec.rb +++ b/spec/features/comments/topics_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' include ActionView::Helpers::DateHelper -feature 'Commenting topics' do +feature 'Commenting topics from proposals' do let(:user) { create :user } let(:proposal) { create :proposal } @@ -319,7 +319,7 @@ feature 'Commenting topics' do page.find("#flag-expand-comment-#{comment.id}").click expect(page).to have_selector("#flag-comment-#{comment.id}") end - + Setting['feature.community'] = nil end @@ -550,3 +550,553 @@ feature 'Commenting topics' do end end + +feature 'Commenting topics from budget investments' do + let(:user) { create :user } + let(:investment) { create :budget_investment } + + scenario 'Index', :js do + + community = investment.community + topic = create(:topic, community: community) + 3.times { create(:comment, commentable: topic) } + + visit community_topic_path(community, topic) + + expect(page).to have_css('.comment', count: 3) + + comment = Comment.last + within first('.comment') do + expect(page).to have_content comment.user.name + expect(page).to have_content I18n.l(comment.created_at, format: :datetime) + expect(page).to have_content comment.body + end + end + + scenario 'Show', :js do + community = investment.community + topic = create(:topic, community: community) + parent_comment = create(:comment, commentable: topic) + first_child = create(:comment, commentable: topic, parent: parent_comment) + second_child = create(:comment, commentable: topic, parent: parent_comment) + + visit comment_path(parent_comment) + + expect(page).to have_css(".comment", count: 3) + expect(page).to have_content parent_comment.body + expect(page).to have_content first_child.body + expect(page).to have_content second_child.body + + expect(page).to have_link "Go back to #{topic.title}", href: community_topic_path(community, topic) + end + + scenario 'Collapsable comments', :js do + community = investment.community + topic = create(:topic, community: community) + parent_comment = create(:comment, body: "Main comment", commentable: topic) + child_comment = create(:comment, body: "First subcomment", commentable: topic, parent: parent_comment) + grandchild_comment = create(:comment, body: "Last subcomment", commentable: topic, parent: child_comment) + + visit community_topic_path(community, topic) + + expect(page).to have_css('.comment', count: 3) + + find("#comment_#{child_comment.id}_children_arrow").trigger('click') + + expect(page).to have_css('.comment', count: 2) + expect(page).to_not have_content grandchild_comment.body + + find("#comment_#{child_comment.id}_children_arrow").trigger('click') + + expect(page).to have_css('.comment', count: 3) + expect(page).to have_content grandchild_comment.body + + find("#comment_#{parent_comment.id}_children_arrow").trigger('click') + + expect(page).to have_css('.comment', count: 1) + expect(page).to_not have_content child_comment.body + expect(page).to_not have_content grandchild_comment.body + end + + scenario 'Comment order' do + community = investment.community + topic = create(:topic, community: community) + c1 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 100, + cached_votes_total: 120, created_at: Time.current - 2) + c2 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 10, + cached_votes_total: 12, created_at: Time.current - 1) + c3 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 1, + cached_votes_total: 2, created_at: Time.current) + + visit community_topic_path(community, topic, order: :most_voted) + + expect(c1.body).to appear_before(c2.body) + expect(c2.body).to appear_before(c3.body) + + visit community_topic_path(community, topic, order: :newest) + + expect(c3.body).to appear_before(c2.body) + expect(c2.body).to appear_before(c1.body) + + visit community_topic_path(community, topic, order: :oldest) + + expect(c1.body).to appear_before(c2.body) + expect(c2.body).to appear_before(c3.body) + end + + scenario 'Creation date works differently in roots and in child comments, when sorting by confidence_score' do + community = investment.community + topic = create(:topic, community: community) + old_root = create(:comment, commentable: topic, created_at: Time.current - 10) + new_root = create(:comment, commentable: topic, created_at: Time.current) + old_child = create(:comment, commentable: topic, parent_id: new_root.id, created_at: Time.current - 10) + new_child = create(:comment, commentable: topic, parent_id: new_root.id, created_at: Time.current) + + visit community_topic_path(community, topic, order: :most_voted) + + expect(new_root.body).to appear_before(old_root.body) + expect(old_child.body).to appear_before(new_child.body) + + visit community_topic_path(community, topic, order: :newest) + + expect(new_root.body).to appear_before(old_root.body) + expect(new_child.body).to appear_before(old_child.body) + + visit community_topic_path(community, topic, order: :oldest) + + expect(old_root.body).to appear_before(new_root.body) + expect(old_child.body).to appear_before(new_child.body) + end + + scenario 'Turns links into html links' do + community = investment.community + topic = create(:topic, community: community) + create :comment, commentable: topic, body: 'Built with http://rubyonrails.org/' + + visit community_topic_path(community, topic) + + within first('.comment') do + expect(page).to have_content 'Built with http://rubyonrails.org/' + expect(page).to have_link('http://rubyonrails.org/', href: 'http://rubyonrails.org/') + expect(find_link('http://rubyonrails.org/')[:rel]).to eq('nofollow') + expect(find_link('http://rubyonrails.org/')[:target]).to eq('_blank') + end + end + + scenario 'Sanitizes comment body for security' do + community = investment.community + topic = create(:topic, community: community) + create :comment, commentable: topic, + body: " click me http://www.url.com" + + visit community_topic_path(community, topic) + + within first('.comment') do + expect(page).to have_content "click me http://www.url.com" + expect(page).to have_link('http://www.url.com', href: 'http://www.url.com') + expect(page).not_to have_link('click me') + end + end + + scenario 'Paginated comments' do + community = investment.community + topic = create(:topic, community: community) + per_page = 10 + (per_page + 2).times { create(:comment, commentable: topic)} + + visit community_topic_path(community, topic) + + expect(page).to have_css('.comment', count: per_page) + within("ul.pagination") do + expect(page).to have_content("1") + expect(page).to have_content("2") + expect(page).to_not have_content("3") + click_link "Next", exact: false + end + + expect(page).to have_css('.comment', count: 2) + end + + feature 'Not logged user' do + scenario 'can not see comments forms' do + community = investment.community + topic = create(:topic, community: community) + create(:comment, commentable: topic) + + visit community_topic_path(community, topic) + + expect(page).to have_content 'You must Sign in or Sign up to leave a comment' + within('#comments') do + expect(page).to_not have_content 'Write a comment' + expect(page).to_not have_content 'Reply' + end + end + end + + scenario 'Create', :js do + login_as(user) + community = investment.community + topic = create(:topic, community: community) + visit community_topic_path(community, topic) + + fill_in "comment-body-topic_#{topic.id}", with: 'Have you thought about...?' + click_button 'Publish comment' + + within "#comments" do + expect(page).to have_content 'Have you thought about...?' + end + + within "#tab-comments-label" do + expect(page).to have_content 'Comments (1)' + end + end + + scenario 'Errors on create', :js do + login_as(user) + community = investment.community + topic = create(:topic, community: community) + visit community_topic_path(community, topic) + + click_button 'Publish comment' + + expect(page).to have_content "Can't be blank" + end + + scenario 'Reply', :js do + community = investment.community + topic = create(:topic, community: community) + citizen = create(:user, username: 'Ana') + manuela = create(:user, username: 'Manuela') + comment = create(:comment, commentable: topic, user: citizen) + + login_as(manuela) + visit community_topic_path(community, topic) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + fill_in "comment-body-comment_#{comment.id}", with: 'It will be done next week.' + click_button 'Publish reply' + end + + within "#comment_#{comment.id}" do + expect(page).to have_content 'It will be done next week.' + end + + expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true) + end + + scenario 'Errors on reply', :js do + community = investment.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic, user: user) + + login_as(user) + visit community_topic_path(community, topic) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + click_button 'Publish reply' + expect(page).to have_content "Can't be blank" + end + + end + + scenario "N replies", :js do + community = investment.community + topic = create(:topic, community: community) + parent = create(:comment, commentable: topic) + + 7.times do + create(:comment, commentable: topic, parent: parent) + parent = parent.children.first + end + + visit community_topic_path(community, topic) + expect(page).to have_css(".comment.comment.comment.comment.comment.comment.comment.comment") + end + + scenario "Flagging as inappropriate", :js do + community = investment.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic) + + login_as(user) + visit community_topic_path(community, topic) + + within "#comment_#{comment.id}" do + page.find("#flag-expand-comment-#{comment.id}").click + page.find("#flag-comment-#{comment.id}").click + + expect(page).to have_css("#unflag-expand-comment-#{comment.id}") + end + + expect(Flag.flagged?(user, comment)).to be + end + + scenario "Undoing flagging as inappropriate", :js do + community = investment.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic) + Flag.flag(user, comment) + + login_as(user) + visit community_topic_path(community, topic) + + within "#comment_#{comment.id}" do + page.find("#unflag-expand-comment-#{comment.id}").click + page.find("#unflag-comment-#{comment.id}").click + + expect(page).to have_css("#flag-expand-comment-#{comment.id}") + end + + expect(Flag.flagged?(user, comment)).to_not be + end + + scenario "Flagging turbolinks sanity check", :js do + Setting['feature.community'] = true + + community = investment.community + topic = create(:topic, community: community, title: "Should we change the world?") + comment = create(:comment, commentable: topic) + + login_as(user) + visit community_path(community) + click_link "Should we change the world?" + + within "#comment_#{comment.id}" do + page.find("#flag-expand-comment-#{comment.id}").click + expect(page).to have_selector("#flag-comment-#{comment.id}") + end + + Setting['feature.community'] = nil + end + + scenario "Erasing a comment's author" do + community = investment.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic, body: "this should be visible") + comment.user.erase + + visit community_topic_path(community, topic) + + within "#comment_#{comment.id}" do + expect(page).to have_content('User deleted') + expect(page).to have_content('this should be visible') + end + end + + feature "Moderators" do + scenario "can create comment as a moderator", :js do + community = investment.community + topic = create(:topic, community: community) + moderator = create(:moderator) + + login_as(moderator.user) + visit community_topic_path(community, topic) + + fill_in "comment-body-topic_#{topic.id}", with: "I am moderating!" + check "comment-as-moderator-topic_#{topic.id}" + click_button "Publish comment" + + within "#comments" do + expect(page).to have_content "I am moderating!" + expect(page).to have_content "Moderator ##{moderator.id}" + expect(page).to have_css "div.is-moderator" + expect(page).to have_css "img.moderator-avatar" + end + end + + scenario "can create reply as a moderator", :js do + community = investment.community + topic = create(:topic, community: community) + citizen = create(:user, username: "Ana") + manuela = create(:user, username: "Manuela") + moderator = create(:moderator, user: manuela) + comment = create(:comment, commentable: topic, user: citizen) + + login_as(manuela) + visit community_topic_path(community, topic) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + fill_in "comment-body-comment_#{comment.id}", with: "I am moderating!" + check "comment-as-moderator-comment_#{comment.id}" + click_button 'Publish reply' + end + + within "#comment_#{comment.id}" do + expect(page).to have_content "I am moderating!" + expect(page).to have_content "Moderator ##{moderator.id}" + expect(page).to have_css "div.is-moderator" + expect(page).to have_css "img.moderator-avatar" + end + + expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true) + end + + scenario "can not comment as an administrator" do + community = investment.community + topic = create(:topic, community: community) + moderator = create(:moderator) + + login_as(moderator.user) + visit community_topic_path(community, topic) + + expect(page).to_not have_content "Comment as administrator" + end + end + + feature "Administrators" do + scenario "can create comment as an administrator", :js do + community = investment.community + topic = create(:topic, community: community) + admin = create(:administrator) + + login_as(admin.user) + visit community_topic_path(community, topic) + + fill_in "comment-body-topic_#{topic.id}", with: "I am your Admin!" + check "comment-as-administrator-topic_#{topic.id}" + click_button "Publish comment" + + within "#comments" do + expect(page).to have_content "I am your Admin!" + expect(page).to have_content "Administrator ##{admin.id}" + expect(page).to have_css "div.is-admin" + expect(page).to have_css "img.admin-avatar" + end + end + + scenario "can create reply as an administrator", :js do + community = investment.community + topic = create(:topic, community: community) + citizen = create(:user, username: "Ana") + manuela = create(:user, username: "Manuela") + admin = create(:administrator, user: manuela) + comment = create(:comment, commentable: topic, user: citizen) + + login_as(manuela) + visit community_topic_path(community, topic) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + fill_in "comment-body-comment_#{comment.id}", with: "Top of the world!" + check "comment-as-administrator-comment_#{comment.id}" + click_button 'Publish reply' + end + + within "#comment_#{comment.id}" do + expect(page).to have_content "Top of the world!" + expect(page).to have_content "Administrator ##{admin.id}" + expect(page).to have_css "div.is-admin" + expect(page).to have_css "img.admin-avatar" + end + + expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true) + end + + scenario "can not comment as a moderator" do + community = investment.community + topic = create(:topic, community: community) + admin = create(:administrator) + + login_as(admin.user) + visit community_topic_path(community, topic) + + expect(page).to_not have_content "Comment as moderator" + end + end + + feature 'Voting comments' do + + background do + @manuela = create(:user, verified_at: Time.current) + @pablo = create(:user) + @investment = create(:budget_investment) + @topic = create(:topic, community: @investment.community) + @comment = create(:comment, commentable: @topic) + + login_as(@manuela) + end + + scenario 'Show' do + create(:vote, voter: @manuela, votable: @comment, vote_flag: true) + create(:vote, voter: @pablo, votable: @comment, vote_flag: false) + + visit community_topic_path(@investment.community, @topic) + + within("#comment_#{@comment.id}_votes") do + within(".in_favor") do + expect(page).to have_content "1" + end + + within(".against") do + expect(page).to have_content "1" + end + + expect(page).to have_content "2 votes" + end + end + + scenario 'Create', :js do + visit community_topic_path(@investment.community, @topic) + + within("#comment_#{@comment.id}_votes") do + find(".in_favor a").click + + within(".in_favor") do + expect(page).to have_content "1" + end + + within(".against") do + expect(page).to have_content "0" + end + + expect(page).to have_content "1 vote" + end + end + + scenario 'Update', :js do + visit community_topic_path(@investment.community, @topic) + + within("#comment_#{@comment.id}_votes") do + find('.in_favor a').click + find('.against a').click + + within('.in_favor') do + expect(page).to have_content "0" + end + + within('.against') do + expect(page).to have_content "1" + end + + expect(page).to have_content "1 vote" + end + end + + scenario 'Trying to vote multiple times', :js do + visit community_topic_path(@investment.community, @topic) + + within("#comment_#{@comment.id}_votes") do + find('.in_favor a').click + find('.in_favor a').click + + within('.in_favor') do + expect(page).to have_content "1" + end + + within('.against') do + expect(page).to have_content "0" + end + + expect(page).to have_content "1 vote" + end + end + end + +end diff --git a/spec/features/communities_spec.rb b/spec/features/communities_spec.rb index b9d14c790..2909f416e 100644 --- a/spec/features/communities_spec.rb +++ b/spec/features/communities_spec.rb @@ -15,7 +15,7 @@ feature 'Communities' do scenario 'Should display default content' do proposal = create(:proposal) community = proposal.community - user = create(:user) + user = create(:user) login_as(user) visit community_path(community) @@ -63,10 +63,47 @@ feature 'Communities' do end end + scenario "Topic order" do + proposal = create(:proposal) + community = proposal.community + topic1 = create(:topic, community: community) + topic2 = create(:topic, community: community) + topic2_comment = create(:comment, :with_confidence_score, commentable: topic2) + topic3 = create(:topic, community: community) + topic3_comment = create(:comment, :with_confidence_score, commentable: topic3) + topic3_comment = create(:comment, :with_confidence_score, commentable: topic3) + + visit community_path(community, order: :most_commented) + + expect(topic3.title).to appear_before(topic2.title) + expect(topic2.title).to appear_before(topic1.title) + + visit community_path(community, order: :oldest) + + expect(topic1.title).to appear_before(topic2.title) + expect(topic2.title).to appear_before(topic3.title) + + visit community_path(community, order: :newest) + + expect(topic3.title).to appear_before(topic2.title) + expect(topic2.title).to appear_before(topic1.title) + end + + scenario "Should order by newest when order param is invalid" do + proposal = create(:proposal) + community = proposal.community + topic1 = create(:topic, community: community) + topic2 = create(:topic, community: community) + + visit community_path(community, order: "invalid_param") + + expect(topic2.title).to appear_before(topic1.title) + end + scenario 'Should display topic edit button when author is logged' do proposal = create(:proposal) community = proposal.community - user = create(:user) + user = create(:user) topic1 = create(:topic, community: community, author: user) topic2 = create(:topic, community: community) login_as(user) diff --git a/spec/features/management/proposals_spec.rb b/spec/features/management/proposals_spec.rb index 8f63146e9..349a4f904 100644 --- a/spec/features/management/proposals_spec.rb +++ b/spec/features/management/proposals_spec.rb @@ -26,7 +26,7 @@ feature 'Proposals' do fill_in 'proposal_summary', with: 'In summary, what we want is...' fill_in 'proposal_description', with: 'This is very important because...' fill_in 'proposal_external_url', with: 'http://rescue.org/refugees' - fill_in 'proposal_video_url', with: 'http://youtube.com' + fill_in 'proposal_video_url', with: 'https://www.youtube.com/watch?v=yRYFKcMa_Ek' check 'proposal_terms_of_service' click_button 'Create proposal' @@ -38,7 +38,7 @@ feature 'Proposals' do expect(page).to have_content 'In summary, what we want is...' expect(page).to have_content 'This is very important because...' expect(page).to have_content 'http://rescue.org/refugees' - expect(page).to have_content 'http://youtube.com' + expect(page).to have_content 'https://www.youtube.com/watch?v=yRYFKcMa_Ek' expect(page).to have_content user.name expect(page).to have_content I18n.l(Proposal.last.created_at.to_date) diff --git a/spec/features/officing/final_recount_spec.rb b/spec/features/officing/final_recount_spec.rb deleted file mode 100644 index ab1c39dc0..000000000 --- a/spec/features/officing/final_recount_spec.rb +++ /dev/null @@ -1,149 +0,0 @@ -require 'rails_helper' - -feature 'Officing Final Recount' do - - background do - @poll_officer = create(:poll_officer) - @officer_assignment = create(:poll_officer_assignment, :final, officer: @poll_officer) - @poll = @officer_assignment.booth_assignment.poll - @poll.update(ends_at: 1.day.ago) - login_as(@poll_officer.user) - end - - scenario 'Only polls where user is officer for final recounts are accessible' do - regular_officer_assignment_1 = create(:poll_officer_assignment, officer: @poll_officer) - regular_officer_assignment_2 = create(:poll_officer_assignment, officer: @poll_officer) - - not_allowed_poll_1 = create(:poll, :expired) - not_allowed_poll_2 = regular_officer_assignment_1.booth_assignment.poll - not_allowed_poll_2.update(ends_at: 1.day.ago) - not_allowed_poll_3 = regular_officer_assignment_2.booth_assignment.poll - - visit root_path - click_link 'Polling officers' - - expect(page).to have_content('Poll officing') - within('#side_menu') do - click_link 'Final recounts and results' - end - - expect(page).to_not have_content(not_allowed_poll_1.name) - expect(page).to_not have_content(not_allowed_poll_2.name) - expect(page).to_not have_content(not_allowed_poll_3.name) - expect(page).to have_content(@poll.name) - - visit new_officing_poll_final_recount_path(not_allowed_poll_1) - expect(page).to have_content('You are allowed to add final recounts for this poll') - end - - scenario 'Add final recount' do - visit officing_root_path - - within('#side_menu') do - click_link 'Final recounts and results' - end - - within("#poll_#{@poll.id}") do - expect(page).to have_content(@poll.name) - click_link 'Add final recount' - end - - expect(page).to_not have_content('Your recounts') - - booth_name = @officer_assignment.booth_assignment.booth.name - date = I18n.l(@poll.starts_at.to_date, format: :long) - select booth_name, from: 'officer_assignment_id' - select date, from: 'date' - fill_in :count, with: '33' - click_button 'Save' - - expect(page).to have_content('Your final recounts') - - within("#poll_final_recount_#{@officer_assignment.booth_assignment.final_recounts.first.id}") do - expect(page).to have_content(date) - expect(page).to have_content(booth_name) - expect(page).to have_content('33') - end - end - - scenario 'Edit final recount' do - final_recount = create(:poll_final_recount, - officer_assignment: @officer_assignment, - booth_assignment: @officer_assignment.booth_assignment, - date: @poll.starts_at, - count: 100) - - booth_name = @officer_assignment.booth_assignment.booth.name - date = I18n.l(final_recount.date.to_date, format: :long) - - visit new_officing_poll_final_recount_path(@poll) - - expect(page).to have_content('Your final recounts') - - within("#poll_final_recount_#{final_recount.id}") do - expect(page).to have_content(date) - expect(page).to have_content(booth_name) - expect(page).to have_content('100') - end - - select booth_name, from: 'officer_assignment_id' - select date, from: 'date' - fill_in :count, with: '42' - click_button 'Save' - - expect(page).to have_content "Data added" - - within("#poll_final_recount_#{final_recount.id}") do - expect(page).to have_content(date) - expect(page).to have_content(booth_name) - expect(page).to have_content('42') - end - expect(page).to_not have_content('100') - end - - scenario 'Show final and system recounts to compare' do - final_officer_assignment = create(:poll_officer_assignment, :final, officer: @poll_officer) - poll = final_officer_assignment.booth_assignment.poll - poll.update(ends_at: 1.day.ago) - final_recount = create(:poll_final_recount, - officer_assignment: final_officer_assignment, - booth_assignment: final_officer_assignment.booth_assignment, - date: 7.days.ago, - count: 100) - 33.times do - create(:poll_voter, :valid_document, - poll: poll, - booth_assignment: final_officer_assignment.booth_assignment, - created_at: final_recount.date) - end - - visit new_officing_poll_final_recount_path(poll) - within("#poll_final_recount_#{final_recount.id}") do - expect(page).to have_content(I18n.l(final_recount.date.to_date, format: :long)) - expect(page).to have_content(final_officer_assignment.booth_assignment.booth.name) - expect(page).to have_content('100') - expect(page).to have_content('33') - end - end - - scenario "Show link to add results for same booth/date" do - final_officer_assignment = create(:poll_officer_assignment, :final, officer: @poll_officer) - poll = final_officer_assignment.booth_assignment.poll - poll.update(ends_at: 1.day.ago) - final_recount = create(:poll_final_recount, - officer_assignment: final_officer_assignment, - booth_assignment: final_officer_assignment.booth_assignment, - date: 7.days.ago, - count: 100) - visit new_officing_poll_final_recount_path(poll) - within("#poll_final_recount_#{final_recount.id}") do - click_link "Add results" - end - - expected_path = new_officing_poll_result_path(poll, oa: final_recount.officer_assignment.id, d: I18n.l(final_recount.date.to_date)) - expect(page).to have_current_path(expected_path) - expect(page).to have_select('officer_assignment_id', selected: final_recount.booth_assignment.booth.name) - expect(page).to have_select('date', selected: I18n.l(final_recount.date.to_date, format: :long)) - end - -end diff --git a/spec/features/officing/results_spec.rb b/spec/features/officing/results_spec.rb index 1de3f4bdb..676ac7d68 100644 --- a/spec/features/officing/results_spec.rb +++ b/spec/features/officing/results_spec.rb @@ -65,6 +65,7 @@ feature 'Officing Results' do fill_in "whites", with: '66' fill_in "nulls", with: '77' + fill_in "total", with: '88' click_button 'Save' @@ -101,6 +102,7 @@ feature 'Officing Results' do fill_in "questions[#{@question_1.id}][1]", with: '200' fill_in "whites", with: '6' fill_in "nulls", with: '7' + fill_in "total", with: '8' click_button 'Save' @@ -113,6 +115,7 @@ feature 'Officing Results' do expect(page).to_not have_content('7777') within("#white_results") { expect(page).to have_content('6') } within("#null_results") { expect(page).to have_content('7') } + within("#total_results") { expect(page).to have_content('8') } within("#question_#{@question_1.id}_0_result") { expect(page).to have_content('5555') } within("#question_#{@question_1.id}_1_result") { expect(page).to have_content('200') } end @@ -134,6 +137,11 @@ feature 'Officing Results' do booth_assignment: @officer_assignment.booth_assignment, date: @poll.ends_at, amount: 44) + total_result = create(:poll_total_result, + officer_assignment: @officer_assignment, + booth_assignment: @officer_assignment.booth_assignment, + date: @poll.ends_at, + amount: 66) visit officing_poll_results_path(@poll, date: I18n.l(@poll.ends_at.to_date), @@ -154,6 +162,7 @@ feature 'Officing Results' do within('#white_results') { expect(page).to have_content('21') } within('#null_results') { expect(page).to have_content('44') } + within('#total_results') { expect(page).to have_content('66') } end -end \ No newline at end of file +end diff --git a/spec/features/polls/questions_spec.rb b/spec/features/polls/questions_spec.rb index 6f2225175..1563ca81a 100644 --- a/spec/features/polls/questions_spec.rb +++ b/spec/features/polls/questions_spec.rb @@ -25,6 +25,25 @@ feature 'Poll Questions' do expect(page).to have_content(question_with_author_visible_name.author_visible_name) end + scenario '#show view has video_url present' do + poll = create(:poll) + normal_question = create(:poll_question, poll: poll, video_url: "https://puppyvideos.com") + + visit question_path(normal_question) + + expect(page).to have_link(normal_question.video_url) + end + + scenario '#show view has document present' do + poll = create(:poll) + normal_question = create(:poll_question, poll: poll) + document = create(:document, documentable: normal_question) + + visit question_path(normal_question) + + expect(page).to have_content(document.title) + end + context 'Answering' do let(:geozone) { create(:geozone) } let(:poll) { create(:poll, geozone_restricted: true, geozone_ids: [geozone.id]) } diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index e7d000f94..304fcb612 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -151,7 +151,7 @@ feature 'Proposals' do fill_in 'proposal_summary', with: 'In summary, what we want is...' fill_in 'proposal_description', with: 'This is very important because...' fill_in 'proposal_external_url', with: 'http://rescue.org/refugees' - fill_in 'proposal_video_url', with: 'http://youtube.com' + fill_in 'proposal_video_url', with: 'https://www.youtube.com/watch?v=yPQfcG-eimk' fill_in 'proposal_responsible_name', with: 'Isabel Garcia' fill_in 'proposal_tag_list', with: 'Refugees, Solidarity' check 'proposal_terms_of_service' @@ -169,7 +169,7 @@ feature 'Proposals' do expect(page).to have_content 'In summary, what we want is...' expect(page).to have_content 'This is very important because...' expect(page).to have_content 'http://rescue.org/refugees' - expect(page).to have_content 'http://youtube.com' + expect(page).to have_content 'https://www.youtube.com/watch?v=yPQfcG-eimk' expect(page).to have_content author.name expect(page).to have_content 'Refugees' expect(page).to have_content 'Solidarity' @@ -187,7 +187,7 @@ feature 'Proposals' do fill_in 'proposal_summary', with: 'In summary, what we want is...' fill_in 'proposal_description', with: 'This is very important because...' fill_in 'proposal_external_url', with: 'http://rescue.org/refugees' - fill_in 'proposal_video_url', with: 'http://youtube.com' + fill_in 'proposal_video_url', with: 'https://www.youtube.com/watch?v=yPQfcG-eimk' fill_in 'proposal_responsible_name', with: 'Isabel Garcia' fill_in 'proposal_tag_list', with: 'Refugees, Solidarity' check 'proposal_terms_of_service' @@ -393,7 +393,7 @@ feature 'Proposals' do fill_in 'proposal_summary', with: 'In summary, what we want is...' fill_in 'proposal_description', with: 'This is very important because...' fill_in 'proposal_external_url', with: 'http://rescue.org/refugees' - fill_in 'proposal_video_url', with: 'http://youtube.com' + fill_in 'proposal_video_url', with: 'https://www.youtube.com/watch?v=yPQfcG-eimk' fill_in 'proposal_responsible_name', with: 'Isabel Garcia' check 'proposal_terms_of_service' @@ -421,7 +421,7 @@ feature 'Proposals' do fill_in 'proposal_summary', with: 'In summary, what we want is...' fill_in 'proposal_description', with: 'This is very important because...' fill_in 'proposal_external_url', with: 'http://rescue.org/refugees' - fill_in 'proposal_video_url', with: 'http://youtube.com' + fill_in 'proposal_video_url', with: 'https://www.youtube.com/watch?v=yPQfcG-eimk' fill_in 'proposal_responsible_name', with: 'Isabel Garcia' check 'proposal_terms_of_service' diff --git a/spec/features/tags/proposals_spec.rb b/spec/features/tags/proposals_spec.rb index cf5017d4f..d2a16b8f7 100644 --- a/spec/features/tags/proposals_spec.rb +++ b/spec/features/tags/proposals_spec.rb @@ -101,7 +101,7 @@ feature 'Tags' do fill_in 'proposal_summary', with: 'In summary, what we want is...' fill_in_ckeditor 'proposal_description', with: 'A description with enough characters' fill_in 'proposal_external_url', with: 'http://rescue.org/refugees' - fill_in 'proposal_video_url', with: 'http://youtube.com' + fill_in 'proposal_video_url', with: 'https://www.youtube.com/watch?v=Ae6gQmhaMn4' fill_in 'proposal_responsible_name', with: 'Isabel Garcia' check 'proposal_terms_of_service' diff --git a/spec/features/topics_specs.rb b/spec/features/topics_specs.rb index 0d668d44b..0ba674844 100644 --- a/spec/features/topics_specs.rb +++ b/spec/features/topics_specs.rb @@ -13,10 +13,10 @@ feature 'Topics' do expect(page).to have_selector(".button.expanded.disabled") end - scenario 'Should can access to new topic page with user logged', :js do + scenario 'Can access to new topic page with user logged', :js do proposal = create(:proposal) community = proposal.community - user = create(:user) + user = create(:user) login_as(user) visit community_path(community) @@ -28,7 +28,7 @@ feature 'Topics' do scenario 'Should have content on new topic page', :js do proposal = create(:proposal) community = proposal.community - user = create(:user) + user = create(:user) login_as(user) visit community_path(community) @@ -47,10 +47,10 @@ feature 'Topics' do context 'Create' do - scenario 'Should can create a new topic', :js do + scenario 'Can create a new topic', :js do proposal = create(:proposal) community = proposal.community - user = create(:user) + user = create(:user) login_as(user) visit new_community_topic_path(community) @@ -62,14 +62,23 @@ feature 'Topics' do expect(current_path).to eq(community_path(community)) end + scenario 'Can not create a new topic when user not logged', :js do + proposal = create(:proposal) + community = proposal.community + + visit new_community_topic_path(community) + + expect(page).to have_content "You do not have permission to carry out the action 'new' on topic." + end + end context 'Edit' do - scenario 'Should can edit a topic' do + scenario 'Can edit a topic' do proposal = create(:proposal) community = proposal.community - user = create(:user) + user = create(:user) topic = create(:topic, community: community, author: user) login_as(user) visit edit_community_topic_path(community, topic) @@ -82,11 +91,23 @@ feature 'Topics' do expect(current_path).to eq(community_path(community)) end + scenario 'Can not edit a topic when user logged is not an author' do + proposal = create(:proposal) + community = proposal.community + topic = create(:topic, community: community) + user = create(:user) + login_as(user) + + visit edit_community_topic_path(community, topic) + + expect(page).to have_content "You do not have permission to carry out the action 'edit' on topic." + end + end context 'Show' do - scenario 'Should can show topic' do + scenario 'Can show topic' do proposal = create(:proposal) community = proposal.community topic = create(:topic, community: community) @@ -99,4 +120,35 @@ feature 'Topics' do end + context 'Destroy' do + + scenario 'Can destroy a topic' do + proposal = create(:proposal) + community = proposal.community + user = create(:user) + topic = create(:topic, community: community, author: user) + login_as(user) + visit community_path(community) + + click_link "Destroy" + + expect(page).to have_content "Topic deleted successfully." + expect(page).not_to have_content topic.title + expect(current_path).to eq(community_path(community)) + end + + scenario 'Can not destroy a topic when user logged is not an author' do + proposal = create(:proposal) + community = proposal.community + topic = create(:topic, community: community) + user = create(:user) + login_as(user) + + visit community_path(community) + + expect(page).not_to have_link "Destroy" + end + + end + end diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index bd2aefa17..ae2bac6d1 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -235,7 +235,7 @@ feature 'Users' do end scenario 'Not display interests when proposal has been destroyed' do - proposal = create(:proposal, tag_list: "Sport") + proposal = create(:proposal, tag_list: "Sport") create(:follow, :followed_proposal, followable: proposal, user: @user) proposal.destroy diff --git a/spec/models/abilities/administrator_spec.rb b/spec/models/abilities/administrator_spec.rb index bbec927a0..dcb099ecd 100644 --- a/spec/models/abilities/administrator_spec.rb +++ b/spec/models/abilities/administrator_spec.rb @@ -14,9 +14,11 @@ describe "Abilities::Administrator" do let(:proposal) { create(:proposal) } let(:budget_investment) { create(:budget_investment) } let(:legislation_question) { create(:legislation_question) } + let(:poll_question) { create(:poll_question) } let(:proposal_document) { build(:document, documentable: proposal) } let(:budget_investment_document) { build(:document, documentable: budget_investment) } + let(:poll_question_document) { build(:document, documentable: poll_question) } let(:hidden_debate) { create(:debate, :hidden) } let(:hidden_comment) { create(:comment, :hidden) } @@ -83,4 +85,8 @@ describe "Abilities::Administrator" do it { should be_able_to(:new, budget_investment_document) } it { should be_able_to(:create, budget_investment_document) } it { should be_able_to(:destroy, budget_investment_document) } + + it { should be_able_to(:new, poll_question_document) } + it { should be_able_to(:create, poll_question_document) } + it { should be_able_to(:destroy, poll_question_document) } end diff --git a/spec/models/poll/booth_spec.rb b/spec/models/poll/booth_spec.rb index c095c62cd..d340d8197 100644 --- a/spec/models/poll/booth_spec.rb +++ b/spec/models/poll/booth_spec.rb @@ -24,4 +24,25 @@ describe :booth do end end + describe "#available" do + + it "returns booths associated to current or incoming polls" do + booth_for_current_poll = create(:poll_booth) + booth_for_incoming_poll = create(:poll_booth) + booth_for_expired_poll = create(:poll_booth) + + current_poll = create(:poll, :current) + incoming_poll = create(:poll, :incoming) + expired_poll = create(:poll, :expired) + + create(:poll_booth_assignment, poll: current_poll, booth: booth_for_current_poll) + create(:poll_booth_assignment, poll: incoming_poll, booth: booth_for_incoming_poll) + create(:poll_booth_assignment, poll: expired_poll, booth: booth_for_expired_poll) + + expect(Poll::Booth.available).to include(booth_for_current_poll) + expect(Poll::Booth.available).to include(booth_for_incoming_poll) + expect(Poll::Booth.available).to_not include(booth_for_expired_poll) + end + + end end \ No newline at end of file diff --git a/spec/models/poll/poll_spec.rb b/spec/models/poll/poll_spec.rb index 2eb78841b..ac0cc44ea 100644 --- a/spec/models/poll/poll_spec.rb +++ b/spec/models/poll/poll_spec.rb @@ -67,31 +67,15 @@ describe :poll do current = create(:poll, :current) incoming = create(:poll, :incoming) expired = create(:poll, :expired) - + current_or_incoming = Poll.current_or_incoming - + expect(current_or_incoming).to include(current) expect(current_or_incoming).to include(incoming) expect(current_or_incoming).to_not include(expired) end end - describe "#document_has_voted?" do - it "returns true if Poll::Voter with document exists" do - poll = create(:poll) - voter = create(:poll_voter, :valid_document, poll: poll) - - expect(poll.document_has_voted?(voter.document_number, voter.document_type)).to eq(true) - end - - it "returns false if Poll::Voter with document does not exists" do - poll_2 = create(:poll) - voter = create(:poll_voter, :valid_document, poll: poll_2) - - expect(poll.document_has_voted?(voter.document_number, voter.document_type)).to eq(false) - end - end - describe "answerable_by" do let(:geozone) {create(:geozone) } diff --git a/spec/models/poll/shift_spec.rb b/spec/models/poll/shift_spec.rb index e70b5f9a0..409e38f93 100644 --- a/spec/models/poll/shift_spec.rb +++ b/spec/models/poll/shift_spec.rb @@ -57,5 +57,27 @@ describe :shift do end end + + describe "#persist_data" do + + let(:user) { create(:user, username: "Ana", email: "ana@example.com") } + let(:officer) { create(:poll_officer, user: user) } + let(:shift) { create(:poll_shift, officer: officer) } + + it "should maintain officer data after destroying associated user" do + shift.officer.user.destroy + + expect(shift.officer_name).to eq "Ana" + expect(shift.officer_email).to eq "ana@example.com" + end + + it "should maintain officer data after destroying officer role" do + shift.officer.destroy + + expect(shift.officer_name).to eq "Ana" + expect(shift.officer_email).to eq "ana@example.com" + end + + end end diff --git a/spec/models/poll/total_result_spec.rb b/spec/models/poll/total_result_spec.rb new file mode 100644 index 000000000..baa877227 --- /dev/null +++ b/spec/models/poll/total_result_spec.rb @@ -0,0 +1,70 @@ +require 'rails_helper' + +describe Poll::TotalResult do + + describe "logging changes" do + it "should update amount_log if amount changes" do + total_result = create(:poll_total_result, amount: 33) + + expect(total_result.amount_log).to eq("") + + total_result.amount = 33 + total_result.save + total_result.amount = 32 + total_result.save + total_result.amount = 34 + total_result.save + + expect(total_result.amount_log).to eq(":33:32") + end + + it "should update officer_assignment_id_log if amount changes" do + total_result = create(:poll_total_result, amount: 33) + + expect(total_result.amount_log).to eq("") + expect(total_result.officer_assignment_id_log).to eq("") + + total_result.amount = 33 + total_result.officer_assignment = create(:poll_officer_assignment, id: 101) + total_result.save + + total_result.amount = 32 + total_result.officer_assignment = create(:poll_officer_assignment, id: 102) + total_result.save + + total_result.amount = 34 + total_result.officer_assignment = create(:poll_officer_assignment, id: 103) + total_result.save + + expect(total_result.amount_log).to eq(":33:32") + expect(total_result.officer_assignment_id_log).to eq(":101:102") + end + + it "should update author_id if amount changes" do + total_result = create(:poll_total_result, amount: 33) + + expect(total_result.amount_log).to eq("") + expect(total_result.author_id_log).to eq("") + + author_A = create(:poll_officer).user + author_B = create(:poll_officer).user + author_C = create(:poll_officer).user + + total_result.amount = 33 + total_result.author_id = author_A.id + total_result.save! + + total_result.amount = 32 + total_result.author_id = author_B.id + total_result.save! + + total_result.amount = 34 + total_result.author_id = author_C.id + total_result.save! + + expect(total_result.amount_log).to eq(":33:32") + expect(total_result.author_id_log).to eq(":#{author_A.id}:#{author_B.id}") + end + end + +end diff --git a/spec/models/proposal_spec.rb b/spec/models/proposal_spec.rb index 72d2b6489..588bd4057 100644 --- a/spec/models/proposal_spec.rb +++ b/spec/models/proposal_spec.rb @@ -67,6 +67,18 @@ describe Proposal do end end + describe "#video_url" do + it "should not be valid when URL is not from Youtube or Vimeo" do + proposal.video_url = "https://twitter.com" + expect(proposal).to_not be_valid + end + + it "should be valid when URL is from Youtube or Vimeo" do + proposal.video_url = "https://vimeo.com/112681885" + expect(proposal).to be_valid + end + end + describe "#responsible_name" do it "should be mandatory" do proposal.responsible_name = nil diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 6db1a2a25..3d4217ea3 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -668,8 +668,8 @@ describe User do end it "should discard followed objects duplicated tags" do - proposal1 = create(:proposal, tag_list: "Sport") - proposal2 = create(:proposal, tag_list: "Sport") + proposal1 = create(:proposal, tag_list: "Sport") + proposal2 = create(:proposal, tag_list: "Sport") budget_investment = create(:budget_investment, tag_list: "Sport") create(:follow, followable: proposal1, user: user) diff --git a/vendor/assets/javascripts/annotator.js b/vendor/assets/javascripts/annotator.js index b9019a6b2..dfbc0a0bf 100644 --- a/vendor/assets/javascripts/annotator.js +++ b/vendor/assets/javascripts/annotator.js @@ -2054,7 +2054,7 @@ setDocument = Sizzle.setDocument = function( node ) { // getElementById is not reliable as a find shortcut delete Expr.find["ID"]; - Expr.filter["ID"] = function( id ) { + Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");