diff --git a/.gitignore b/.gitignore index eb917c637..5cc519787 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,6 @@ /spec/examples.txt /config/database.yml /config/secrets.yml +/config/deploy-secrets.yml /coverage diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e46777e3d..af11c122b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,3 +43,5 @@ Cuando quieras resolver una incidencia mediante código: * Lo que se esperaba que pasara * Lo que ha pasado * También es buena idea que incluyas tu sistema operativo, navegador, versión de navegador y plugins instalados. + +¡Gracias! :heart: :heart: :heart: diff --git a/CONTRIBUTING_EN.md b/CONTRIBUTING_EN.md new file mode 100644 index 000000000..26e08fc11 --- /dev/null +++ b/CONTRIBUTING_EN.md @@ -0,0 +1,46 @@ +## Team members + +* Raimond Garcia [github](https://github.com/voodoorai2000) | [twitter](https://twitter.com/voodoorai2000) +* Juanjo Bazán [github](https://github.com/xuanxu) | [twitter](https://twitter.com/xuanxu) +* Enrique García Cota [github](https://github.com/kikito) | [twitter](https://twitter.com/otikik) +* Alberto Garcia Cabeza [github](https://github.com/decabeza) | [twitter](https://twitter.com/decabeza) + +## Report an issue + +The prefered way to report any bug is [opening an issue in the project's Github repo](https://github.com/AyuntamientoMadrid/participacion/issues/new). + +For more informal communication, contact team members via twitter + +## Resolve an issue + +Admins tag issues using two label related with collaboration availability: + +* `PRs-welcome`: [issues labeled with PRs-welcome](https://github.com/AyuntamientoMadrid/participacion/labels/PRs-welcome) are well defined features ready to be implemented by whoever wants to do it. + +* `Not-ready`: with this label admins mark features or changes that are not well defined yet or subject to an internal decision. Is not a good idea to start implementation of these isuues. + +If you want to contribute code to solve an issue: + +* Add a comment to tell everyone you are working on the issue. +* If an issue has someone assigned it means that person is already working on it. +* Fork the project. +* Create a topic branch based on master. +* Commit there your code to solve the issue. +* Make sure all test are passing (and add specs to test any new feature if needed). +* Open a *pull request* to the main repository describing what issue you are addressing. + +## Other ways of contributing without coding + +* If you think there's a feature missing, or find a bug, create an issue (make sure it has not already been reported). +* You can also help promoting the project talking about it in your social networks. + +## How to report an issue + +* Try to use a descriptive and to-the-point title +* Is a good idea to include some of there sections: + * Steps to reproduce the bug + * Expected behaviour/response + * Actual response +* Sometimes it is also helpful if you mention your operating system, browser version and installed plugins. + +Thanks! :heart: :heart: :heart: \ No newline at end of file diff --git a/Capfile b/Capfile new file mode 100644 index 000000000..b565fae23 --- /dev/null +++ b/Capfile @@ -0,0 +1,14 @@ +# Load DSL and set up stages +require 'capistrano/setup' + +# Include default deployment tasks +require 'capistrano/deploy' + +require 'capistrano/rvm' +require 'capistrano/bundler' +require 'capistrano/rails/assets' +require 'capistrano/rails/migrations' +require 'capistrano/passenger' + +# Load custom tasks from `lib/capistrano/tasks` if you have any defined +Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } diff --git a/Gemfile b/Gemfile index 00e2a98eb..7340871bb 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,5 @@ source 'https://rubygems.org' - # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.2.3' # Use PostgreSQL @@ -26,11 +25,6 @@ gem 'devise' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' gem 'acts_as_commentable_with_threading' -# Use Unicorn as the app server -# gem 'unicorn' - -# Use Capistrano for deployment -# gem 'capistrano-rails', group: :development gem 'acts-as-taggable-on' gem "responders" gem 'foundation-rails' @@ -53,6 +47,11 @@ group :development, :test do gem 'quiet_assets' gem 'letter_opener_web', '~> 1.2.0' gem 'i18n-tasks' + gem 'capistrano', '3.4.0', require: false + gem "capistrano-bundler", '1.1.4', require: false + gem "capistrano-rails", '1.1.3', require: false + gem "capistrano-rvm", require: false + gem "capistrano-passenger", require: false end group :test do @@ -61,3 +60,7 @@ group :test do gem 'coveralls', require: false end +group :test do + gem 'email_spec' +end + diff --git a/Gemfile.lock b/Gemfile.lock index 67f4a7616..cf6709f66 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -54,6 +54,21 @@ GEM byebug (5.0.0) columnize (= 0.9.0) cancancan (1.12.0) + capistrano (3.4.0) + i18n + rake (>= 10.0.0) + sshkit (~> 1.3) + capistrano-bundler (1.1.4) + capistrano (~> 3.1) + sshkit (~> 1.2) + capistrano-passenger (0.1.1) + capistrano (~> 3.0) + capistrano-rails (1.1.3) + capistrano (~> 3.1) + capistrano-bundler (~> 1.1) + capistrano-rvm (0.1.2) + capistrano (~> 3.0) + sshkit (~> 1.2) capybara (2.4.4) mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -75,6 +90,7 @@ GEM coffee-script-source execjs coffee-script-source (1.9.1.1) + colorize (0.7.7) columnize (0.9.0) coveralls (0.8.2) json (~> 1.8) @@ -99,6 +115,9 @@ GEM json thread thread_safe + email_spec (1.6.0) + launchy (~> 2.1) + mail (~> 2.2) erubis (2.7.0) execjs (2.5.2) factory_girl (4.5.0) @@ -146,6 +165,9 @@ GEM mini_portile (0.6.2) minitest (5.7.0) multi_json (1.11.2) + net-scp (1.2.1) + net-ssh (>= 2.6.5) + net-ssh (2.9.2) netrc (0.10.3) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) @@ -233,6 +255,10 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) + sshkit (1.7.1) + colorize (>= 0.7.0) + net-scp (>= 1.1.2) + net-ssh (>= 2.8.0) term-ansicolor (1.3.2) tins (~> 1.0) terminal-table (1.5.2) @@ -273,12 +299,18 @@ DEPENDENCIES acts_as_votable byebug cancancan + capistrano (= 3.4.0) + capistrano-bundler (= 1.1.4) + capistrano-passenger + capistrano-rails (= 1.1.3) + capistrano-rvm capybara ckeditor coffee-rails (~> 4.1.0) coveralls database_cleaner devise + email_spec factory_girl_rails foundation-rails i18n-tasks diff --git a/README.md b/README.md index 1df4987c8..66c1853dc 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ cd participacion bundle install cp config/database.yml.example config/database.yml cp config/secrets.yml.example config/secrets.yml -bundle exec bin/rake db:create db:schema:load -RAILS_ENV=test bundle exec rake db:create db:schema:load +bundle exec bin/rake db:setup +RAILS_ENV=test bundle exec rake db:setup ``` Para ejecutar la aplicación en local: @@ -47,4 +47,4 @@ El código de este proyecto está publicado bajo la licencia MIT (ver MIT-licens ## Contribuciones -Ver fichero CONTRIBUTING.md +Ver fichero [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 000000000..b1b1521ff --- /dev/null +++ b/README_EN.md @@ -0,0 +1,51 @@ +# Ayuntamiento de Madrid (Madrid city's government) eParticipation application + +[![Build Status](https://travis-ci.org/AyuntamientoMadrid/participacion.svg?branch=master)](https://travis-ci.org/AyuntamientoMadrid/participacion) +[![Code Climate](https://codeclimate.com/github/AyuntamientoMadrid/participacion/badges/gpa.svg)](https://codeclimate.com/github/AyuntamientoMadrid/participacion) +[![Dependency Status](https://gemnasium.com/AyuntamientoMadrid/participacion.svg)](https://gemnasium.com/AyuntamientoMadrid/participacion) +[![Coverage Status](https://coveralls.io/repos/AyuntamientoMadrid/participacion/badge.svg?branch=master&service=github)](https://coveralls.io/github/AyuntamientoMadrid/participacion?branch=master) + +This is the opensource code repository of Madrid City government eParticipation website + +## Current state + +Development started on [2015 July 15th](https://github.com/AyuntamientoMadrid/participacion/commit/8db36308379accd44b5de4f680a54c41a0cc6fc6) + +The project is in its early stages. Features currently present in the code (and their names) are subject to change. + +## 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 + +Prerequisites: install git, Ruby 2.2.2, bundler gem and PostgreSQL. + +``` +cd participacion +bundle install +cp config/database.yml.example config/database.yml +cp config/secrets.yml.example config/secrets.yml +bundle exec bin/rake db:setup +RAILS_ENV=test bundle exec rake db:setup +``` + +Run the app locally: +``` +bundle exec bin/rails s +``` + +Run the tests with: +``` +bundle exec bin/rspec +``` + +## Licence + +Code published under MIT license (see [MIT-license.md](MIT-license.md)) + +## Contributions + +See [CONTRIBUTING_EN.md](CONTRIBUTING_EN.md) diff --git a/app/assets/images/home_header_bg.jpg b/app/assets/images/home_header_bg.jpg index f264aa06f..3f5065f9b 100644 Binary files a/app/assets/images/home_header_bg.jpg and b/app/assets/images/home_header_bg.jpg differ diff --git a/app/assets/stylesheets/debates.scss b/app/assets/stylesheets/debates.scss index d8fabd59d..c74ef5057 100644 --- a/app/assets/stylesheets/debates.scss +++ b/app/assets/stylesheets/debates.scss @@ -96,7 +96,49 @@ header { .button { color: white; font-family: inherit; - margin-top: $line-height*2; + margin-top: $line-height; + } + + .home-page { + .button { + color: white; + font-family: inherit; + margin-top: $line-height*2; + } + } + + .selected { + position: relative; + + &:before { + top: -14px; + left: 50%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + border-top-color: #fff; + border-width: 8px; + margin-left: -8px; + } + } + + .language { + float: none; + text-align: center; + + @media (min-width: 480px) { + float: left; + } + } + + .external-links { + @extend .language; + + @media (min-width: 480px) { + float: right; + } } } @@ -105,13 +147,13 @@ header { } .top-bar { - background: white; - color: $header-color; + background: rgba(0,0,0,.5); + color: white; //$header-color; height: $line-height*4; max-width: 1170px !important; .name a { - color: black; + color: white; font-family: 'Lato'; font-size: rem-calc(36); font-weight: lighter; @@ -119,7 +161,7 @@ header { padding-left: 0; span { - color: $brand; + // color: $brand; font-size: rem-calc(24); font-weight: normal; } @@ -131,14 +173,29 @@ header { } .top-bar-section { + margin-right: $line-height; + + ul li > a { + font-size: rem-calc(14); + } + + ul li, ul li:hover:not(.has-form) > a { + background: none; + } li:not(.has-form) a:not(.button) { - background: white; - color: $brand; + background: none; + color: white; line-height: $line-height*4; + + &:hover { + background: none; + color: $link-hover; + } } li.active:not(.has-form) a:not(.button) { + background: none; height: $line-height*4; line-height: $line-height*4; } @@ -147,9 +204,13 @@ header { .top-links { color: white; font-size: rem-calc(14); - height: $line-height*2; + height: $line-height*3; padding: $line-height/2 0; + @media (min-width: 480px) { + height: $line-height*2; + } + a { color: white; } @@ -179,7 +240,7 @@ header { .icon-like { background: white; - border: 2px solid white; + border: 2px solid $votes-border; border-radius: rem-calc(3); color: $votes-neutral; display: inline-block; @@ -187,10 +248,10 @@ header { line-height: rem-calc(30); padding: rem-calc(3) rem-calc(6); position: relative; - //when active => color: $votes-like; &:hover { background: $votes-like; + border-color: white; color: white; cursor: pointer; } @@ -202,7 +263,7 @@ header { .icon-unlike { background: white; - border: 2px solid white; + border: 2px solid $votes-border; border-radius: rem-calc(3); color: $votes-neutral; display: inline-block; @@ -210,10 +271,10 @@ header { line-height: rem-calc(30); padding: rem-calc(3) rem-calc(6); position: relative; - //when active => color: $votes-unlike; &:hover { background: $votes-unlike; + border-color: white; color: white; cursor: pointer; } diff --git a/app/assets/stylesheets/variables.scss b/app/assets/stylesheets/variables.scss index f9607af9c..37123d553 100644 --- a/app/assets/stylesheets/variables.scss +++ b/app/assets/stylesheets/variables.scss @@ -26,7 +26,8 @@ $comments-info: #A5B2B9; $comments-text: #3F4549; $header-color: #292B33; -$link: #0077B9; +$link: #2895F1; +$link-hover: #2178BF; $tags-bg: #FAFAFA; $tags-border: #F0F0F0; @@ -37,8 +38,8 @@ $text-medium: #999999; $text-light: #A3A6AD; $votes: #31708f; -$votes-background: #0081B3; -$votes-border: #005b80; +$votes-background: #26AEEE;//#0081B3; +$votes-border: #1F94CB;//#005b80; $votes-like: #7BD2A8; $votes-like-act: #5D9E7F; $votes-neutral: #CCCCCC; diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index 494068476..e9b5b6c99 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -17,7 +17,7 @@ class AccountController < ApplicationController end def account_params - params.require(:account).permit(:first_name, :last_name, :nickname, :use_nickname) + params.require(:account).permit(:first_name, :last_name, :nickname, :use_nickname, :email_on_debate_comment, :email_on_comment_reply) end end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 409bb7c9b..bbad3ca3b 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -7,6 +7,10 @@ class CommentsController < ApplicationController @comment = Comment.build(@debate, current_user, params[:comment][:body]) @comment.save! @comment.move_to_child_of(@parent) if reply? + + Mailer.comment(@comment).deliver_now if email_on_debate_comment? + Mailer.reply(@comment).deliver_now if email_on_comment_reply? + respond_with @comment end @@ -32,4 +36,12 @@ class CommentsController < ApplicationController def reply? @parent.class == Comment end + + def email_on_debate_comment? + @comment.debate.author.email_on_debate_comment? + end + + def email_on_comment_reply? + reply? && @parent.author.email_on_comment_reply? + end end \ No newline at end of file diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 000000000..e5d793e1e --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: "participacion@madrid.es" + layout 'mailer' +end diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb new file mode 100644 index 000000000..fa53c7c1a --- /dev/null +++ b/app/mailers/mailer.rb @@ -0,0 +1,16 @@ +class Mailer < ApplicationMailer + + def comment(comment) + @comment = comment + @debate = comment.debate + mail(to: @debate.author.email, subject: t('mailer.comment.subject')) + end + + def reply(reply) + @reply = reply + @debate = @reply.debate + parent = Comment.find(@reply.parent_id) + mail(to: parent.author.email, subject: t('mailer.reply.subject')) + end + +end diff --git a/app/models/comment.rb b/app/models/comment.rb index e99ef15e3..c2cf75292 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -17,4 +17,13 @@ class Comment < ActiveRecord::Base def self.find_parent(params) params[:commentable_type].constantize.find(params[:commentable_id]) end + + def debate + commentable if commentable.class == Debate + end + + def author + user + end + end diff --git a/app/views/account/show.html.erb b/app/views/account/show.html.erb index da568a9b9..d130121f3 100644 --- a/app/views/account/show.html.erb +++ b/app/views/account/show.html.erb @@ -15,6 +15,16 @@ <%= f.label :nickname, t("account.show.nickname_label") %> <%= f.text_field :nickname %> +
+ <%= f.check_box :email_on_debate_comment %> + <%= f.label :email_on_debate_comment, t("account.show.email_on_debate_comment_label") %> +
+ +
+ <%= f.check_box :email_on_comment_reply %> + <%= f.label :email_on_comment_reply, t("account.show.email_on_comment_reply_label") %> +
+ <%= f.submit t("account.show.save_changes_submit"), class: "button radius" %> <% end %> diff --git a/app/views/devise/menu/_login_items.html.erb b/app/views/devise/menu/_login_items.html.erb index c075d23aa..4959553f9 100644 --- a/app/views/devise/menu/_login_items.html.erb +++ b/app/views/devise/menu/_login_items.html.erb @@ -1,14 +1,17 @@ \ No newline at end of file diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index b76b2735e..48ffa41d5 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -1,53 +1,48 @@
- + -
- -
- - <% if home_page? %> -
-
-

<%= t("layouts.header.open_city") %>

-

<%= t("layouts.header.open_city_slogan") %>

- <%= link_to t("layouts.header.create_debate"), new_debate_path, class: 'button radius' %> -
-
- <% end %> -
+ <% if home_page? %> +
+
+

<%= t("layouts.header.open_city") %>

+

<%= t("layouts.header.open_city_slogan") %>

+ <%= link_to t("layouts.header.create_debate"), new_debate_path, class: 'button radius' %> +
+
+ <% end %> + \ No newline at end of file diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 000000000..991cf0ffa --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,5 @@ + + + <%= yield %> + + diff --git a/app/views/mailer/comment.html.erb b/app/views/mailer/comment.html.erb new file mode 100644 index 000000000..f469d50b8 --- /dev/null +++ b/app/views/mailer/comment.html.erb @@ -0,0 +1,7 @@ +Hello, + +
<%= @comment.author.name %>
+ +
<%= @comment.body %>
+ +
<%= link_to @debate.title, debate_url(@debate) %>
\ No newline at end of file diff --git a/app/views/mailer/reply.html.erb b/app/views/mailer/reply.html.erb new file mode 100644 index 000000000..7714d7c28 --- /dev/null +++ b/app/views/mailer/reply.html.erb @@ -0,0 +1,7 @@ +Hello, + +
<%= @reply.author.name %>
+ +
<%= @reply.body %>
+ +
<%= link_to @debate.title, debate_url(@debate) %>
\ No newline at end of file diff --git a/config/deploy-secrets.yml.example b/config/deploy-secrets.yml.example new file mode 100644 index 000000000..b69fea225 --- /dev/null +++ b/config/deploy-secrets.yml.example @@ -0,0 +1,18 @@ +staging: + deploy_to: "/var/www/participacion" + ssh_port: 21 + server: staging.participacion.madrid.es + user: xxxxx + +preproduction: + deploy_to: "/var/www/participacion" + ssh_port: 2222 + server: xxx.xxx.xxx.xxx + user: xxxxx + +production: + deploy_to: "/var/www/participacion" + ssh_port: 2222 + server: xxx.xxx.xxx.xxx + user: xxxxx + diff --git a/config/deploy.rb b/config/deploy.rb new file mode 100644 index 000000000..8104d742f --- /dev/null +++ b/config/deploy.rb @@ -0,0 +1,38 @@ +# config valid only for current version of Capistrano +lock '3.4.0' + +def deploysecret(key) + @deploy_secrets_yml ||= YAML.load_file('config/deploy-secrets.yml')[fetch(:stage).to_s] + @deploy_secrets_yml[key.to_s] +end + +set :rails_env, fetch(:stage) +set :rvm_ruby_version, '2.2.2' + +set :application, 'participacion' +set :repo_url, 'git@github.com:AyuntamientoMadrid/participacion.git' + +set :scm, :git +set :revision, `git rev-parse --short #{fetch(:branch)}`.strip + +set :log_level, :info + +set :linked_files, %w{config/database.yml config/secrets.yml} +set :linked_dirs, %w{log tmp public/system public/assets} + +set :keep_releases, 5 + +set :local_user, ENV['USER'] + +namespace :deploy do + + after :restart, :clear_cache do + on roles(:web), in: :groups, limit: 3, wait: 10 do + # Here we can do anything such as: + # within release_path do + # execute :rake, 'cache:clear' + # end + end + end + +end diff --git a/config/deploy/preproduction.rb b/config/deploy/preproduction.rb new file mode 100644 index 000000000..fb9033c03 --- /dev/null +++ b/config/deploy/preproduction.rb @@ -0,0 +1,5 @@ +set :deploy_to, deploysecret(:deploy_to) +set :branch, :production +set :ssh_options, port: deploysecret(:ssh_port) + +server deploysecret(:server), user: deploysecret(:user), roles: %w(web app db importer) \ No newline at end of file diff --git a/config/deploy/production.rb b/config/deploy/production.rb new file mode 100644 index 000000000..fb9033c03 --- /dev/null +++ b/config/deploy/production.rb @@ -0,0 +1,5 @@ +set :deploy_to, deploysecret(:deploy_to) +set :branch, :production +set :ssh_options, port: deploysecret(:ssh_port) + +server deploysecret(:server), user: deploysecret(:user), roles: %w(web app db importer) \ No newline at end of file diff --git a/config/deploy/staging.rb b/config/deploy/staging.rb new file mode 100644 index 000000000..5ca436c42 --- /dev/null +++ b/config/deploy/staging.rb @@ -0,0 +1,7 @@ +set :deploy_to, deploysecret(:deploy_to) +set :branch, :master +set :ssh_options, port: deploysecret(:ssh_port) + +set :passenger_restart_with_sudo, false + +server deploysecret(:server), user: deploysecret(:user), roles: %w(web app db importer) diff --git a/config/locales/en.yml b/config/locales/en.yml index fbb77d5a4..79e4e1129 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -11,6 +11,7 @@ en: open_city_slogan: So the citizens can decide what kind of city they want. create_debate: Create a debate my_account_link: My account + language: Site language admin: dashboard: index: @@ -70,6 +71,8 @@ en: show: title: "My account" save_changes_submit: "Save changes" + email_on_debate_comment_label: "Receive email when someone comments on my debates" + email_on_comment_reply_label: "Receive email when someone replies to my comments" change_credentials_link: "Change my credentials" first_name_label: "First Name" last_name_label: "Last Name" @@ -81,3 +84,8 @@ en: shared: tags_cloud: tags: Tags + mailer: + comment: + subject: Someone has commented on your debate + reply: + subject: Someone has replied to your comment diff --git a/config/locales/es.yml b/config/locales/es.yml index c53d01547..8daf07d42 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -11,6 +11,7 @@ es: open_city_slogan: Para que todos los madrileños decidamos que ciudad queremos tener. create_debate: Crea un debate my_account_link: Mi cuenta + language: Idioma de la página admin: dashboard: index: @@ -70,6 +71,8 @@ es: show: title: "Mi cuenta" save_changes_submit: "Guardar cambios" + email_on_debate_comment_label: "Recibir un email cuando alguien commenta en mis debates" + email_on_comment_reply_label: "Recibir un email cuando alguien contesta a mis comentarios" change_credentials_link: "Cambiar mi contraseña" first_name_label: "Nombre" last_name_label: "Apellidos" @@ -81,4 +84,8 @@ es: shared: tags_cloud: tags: Etiquetas - + mailer: + comment: + subject: Alguien ha comentado en tu debate + reply: + subject: Alguien ha respondido a tu comentario diff --git a/db/migrate/20150806163142_add_preferences_to_users.rb b/db/migrate/20150806163142_add_preferences_to_users.rb new file mode 100644 index 000000000..8c3ae57cc --- /dev/null +++ b/db/migrate/20150806163142_add_preferences_to_users.rb @@ -0,0 +1,6 @@ +class AddPreferencesToUsers < ActiveRecord::Migration + def change + add_column :users, :email_on_debate_comment, :boolean, default: false + add_column :users, :email_on_comment_reply, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 7833fc34c..6ee012197 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -94,6 +94,8 @@ ActiveRecord::Schema.define(version: 20150807140346) do t.string "unconfirmed_email" t.string "nickname" t.boolean "use_nickname", default: false, null: false + t.boolean "email_on_debate_comment", default: false + t.boolean "email_on_comment_reply", default: false end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree diff --git a/spec/features/account_spec.rb b/spec/features/account_spec.rb index 7179b6c6b..e622c83c1 100644 --- a/spec/features/account_spec.rb +++ b/spec/features/account_spec.rb @@ -21,6 +21,8 @@ feature 'Account' do fill_in 'account_first_name', with: 'Larry' fill_in 'account_last_name', with: 'Bird' + check 'account_email_on_debate_comment' + check 'account_email_on_comment_reply' click_button 'Save changes' expect(page).to have_content "Saved" @@ -29,5 +31,7 @@ feature 'Account' do expect(page).to have_selector("input[value='Larry']") expect(page).to have_selector("input[value='Bird']") + expect(page).to have_selector("input[id='account_email_on_debate_comment'][value='1']") + expect(page).to have_selector("input[id='account_email_on_comment_reply'][value='1']") end end \ No newline at end of file diff --git a/spec/features/comments_spec.rb b/spec/features/comments_spec.rb index 3d215ef00..1ec6a8c10 100644 --- a/spec/features/comments_spec.rb +++ b/spec/features/comments_spec.rb @@ -41,11 +41,11 @@ feature 'Comments' do login_as(user) visit debate_path(debate) - fill_in 'comment_body', with: '¿Has pensado en esto...?' + fill_in 'comment_body', with: 'Have you thought about...?' click_button 'Publish comment' within "#comments" do - expect(page).to have_content '¿Has pensado en esto...?' + expect(page).to have_content 'Have you thought about...?' end end @@ -60,12 +60,12 @@ feature 'Comments' do click_link "Reply" within "#js-comment-form-comment_#{comment.id}" do - fill_in 'comment_body', with: 'La semana que viene está hecho.' + fill_in 'comment_body', with: 'It will be done next week.' click_button 'Publish reply' end within "#comment-#{comment.id}" do - expect(page).to have_content 'La semana que viene está hecho.' + expect(page).to have_content 'It will be done next week.' end end diff --git a/spec/features/emails_spec.rb b/spec/features/emails_spec.rb new file mode 100644 index 000000000..86c794fac --- /dev/null +++ b/spec/features/emails_spec.rb @@ -0,0 +1,63 @@ +require 'rails_helper' + +feature 'Emails' do + + background do + reset_mailer + end + + scenario "Signup Email" do + sign_up + + email = open_last_email + expect(email).to have_subject('Confirmation instructions') + expect(email).to deliver_to('manuela@madrid.es') + expect(email).to have_body_text(user_confirmation_path) + end + + scenario "Reset password" do + reset_password + + email = open_last_email + expect(email).to have_subject('Reset password instructions') + expect(email).to deliver_to('manuela@madrid.es') + expect(email).to have_body_text(edit_user_password_path) + end + + scenario "Debate comment", :js do + user = create(:user, email_on_debate_comment: true) + debate = create(:debate, author: user) + comment_on(debate) + + email = open_last_email + expect(email).to have_subject('Someone has commented on your debate') + expect(email).to deliver_to(debate.author) + expect(email).to have_body_text(debate_path(debate)) + end + + scenario "Comment reply", :js do + user = create(:user, email_on_comment_reply: true) + reply_to(user) + + email = open_last_email + expect(email).to have_subject('Someone has replied to your comment') + expect(email).to deliver_to(user) + expect(email).to have_body_text(debate_path(Comment.first.debate)) + end + + scenario 'Do not send email about debate comment unless set in preferences', :js do + user = create(:user, email_on_debate_comment: false) + debate = create(:debate, author: user) + comment_on(debate) + + expect { open_last_email }.to raise_error "No email has been sent!" + end + + scenario "Do not send email about comment reply unless set in preferences", :js do + user = create(:user, email_on_comment_reply: false) + reply_to(user) + + expect { open_last_email }.to raise_error "No email has been sent!" + end + +end \ No newline at end of file diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index d2f01ec84..c15869bae 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -44,4 +44,26 @@ feature 'Users' do expect(page).to have_content 'Signed out successfully.' end + scenario 'Reset password' do + create(:user, email: 'manuela@madrid.es') + + visit '/' + click_link 'Log in' + click_link 'Forgot your password?' + + fill_in 'user_email', with: 'manuela@madrid.es' + click_button 'Send me reset password instructions' + + expect(page).to have_content "You will receive an email with instructions on how to reset your password in a few minutes." + + sent_token = /.*reset_password_token=(.*)".*/.match(ActionMailer::Base.deliveries.last.body.to_s)[1] + visit edit_user_password_path(reset_password_token: sent_token) + + fill_in 'user_password', with: 'new password' + fill_in 'user_password_confirmation', with: 'new password' + click_button 'Change my password' + + expect(page).to have_content "Your password has been changed successfully. You are now signed in." + end + end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index e53871fe2..9387c2bed 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -34,6 +34,20 @@ describe User do expect(subject).to be_valid end + describe 'preferences' do + describe 'email_on_debate_comment' do + it 'should be false by default' do + expect(subject.email_on_debate_comment).to eq(false) + end + end + + describe 'email_on_comment_reply' do + it 'should be false by default' do + expect(subject.email_on_comment_reply).to eq(false) + end + end + end + describe 'use_nickname' do describe 'when true' do before { subject.use_nickname = true } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 156073f78..640f9e96c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ require 'factory_girl_rails' require 'database_cleaner' +require "email_spec" +Dir["./spec/support/**/*.rb"].sort.each { |f| require f} RSpec.configure do |config| config.use_transactional_fixtures = false @@ -7,7 +9,9 @@ RSpec.configure do |config| config.filter_run :focus config.run_all_when_everything_filtered = true config.include FactoryGirl::Syntax::Methods - + config.include(EmailSpec::Helpers) + config.include(EmailSpec::Matchers) + config.include(CommonActions) config.before(:suite) do DatabaseCleaner.clean_with :truncation end diff --git a/spec/support/common_actions.rb b/spec/support/common_actions.rb new file mode 100644 index 000000000..d07f60261 --- /dev/null +++ b/spec/support/common_actions.rb @@ -0,0 +1,54 @@ +module CommonActions + + def sign_up + visit '/' + click_link 'Sign up' + + fill_in 'user_first_name', with: 'Manuela' + fill_in 'user_last_name', with: 'Carmena' + fill_in 'user_email', with: 'manuela@madrid.es' + fill_in 'user_password', with: 'judgementday' + fill_in 'user_password_confirmation', with: 'judgementday' + + click_button 'Sign up' + end + + def reset_password + create(:user, email: 'manuela@madrid.es') + + visit '/' + click_link 'Log in' + click_link 'Forgot your password?' + + fill_in 'user_email', with: 'manuela@madrid.es' + click_button 'Send me reset password instructions' + end + + def comment_on(debate) + user2 = create(:user) + + login_as(user2) + visit debate_path(debate) + + fill_in 'comment_body', with: 'Have you thought about...?' + click_button 'Publish comment' + expect(page).to have_content 'Have you thought about...?' + end + + def reply_to(user) + manuela = create(:user) + debate = create(:debate) + comment = create(:comment, commentable: debate, user: user) + + login_as(manuela) + visit debate_path(debate) + + click_link "Reply" + within "#js-comment-form-comment_#{comment.id}" do + fill_in 'comment_body', with: 'It will be done next week.' + click_button 'Publish reply' + end + expect(page).to have_content 'It will be done next week.' + end + +end \ No newline at end of file