diff --git a/.travis.yml b/.travis.yml index 48e05b4..025f452 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,14 +7,14 @@ cache: node_js: 9 -sudo: false - +sudo: required # required with chrome addon addons: chrome: stable before_install: - cp config/database.example.yml config/database.yml - cp config/chexpire.test.yml config/chexpire.yml + - cp config/secrets.example.yml config/secrets.yml install: - bundle install @@ -24,3 +24,4 @@ install: script: - bundle exec rubocop - bundle exec rails test + - bundle exec rails test:system diff --git a/Gemfile b/Gemfile index c82704a..3a119d1 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,11 @@ gem 'rails-i18n', '~> 5.1' gem 'mysql2', '>= 0.4.4', '< 0.6.0' # Use Puma as the app server gem 'puma', '~> 3.11' + +gem 'devise', '~> 4.4' +gem 'devise-i18n', '~> 1.6' +gem 'simple_form', '~> 4.0' + # Use SCSS for stylesheets gem 'sass-rails', '~> 5.0' # Use Uglifier as compressor for JavaScript assets @@ -35,7 +40,9 @@ gem 'bootsnap', '>= 1.1.0', require: false group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] + gem 'binding_of_caller' + gem 'pry-byebug' + gem 'pry-rails' end group :development do @@ -48,6 +55,7 @@ group :development do gem 'rubocop', '~> 0.56.0', require: false gem 'annotate', require: false + gem 'letter_opener_web' gem 'capistrano-rails' gem "capistrano", "~> 3.10", require: false @@ -61,6 +69,7 @@ group :test do gem 'selenium-webdriver' # Easy installation and use of chromedriver to run system tests with Chrome gem 'chromedriver-helper' + gem 'launchy' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem diff --git a/Gemfile.lock b/Gemfile.lock index ce54755..978f671 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -55,6 +55,8 @@ GEM ast (2.4.0) bcrypt (3.1.12) bindex (0.5.0) + binding_of_caller (0.8.0) + debug_inspector (>= 0.0.1) bootsnap (1.3.0) msgpack (~> 1.0) builder (3.2.3) @@ -89,8 +91,18 @@ GEM chromedriver-helper (1.2.0) archive-zip (~> 0.10) nokogiri (~> 1.8) + coderay (1.1.2) concurrent-ruby (1.0.5) crass (1.0.4) + debug_inspector (0.0.3) + devise (4.4.3) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 4.1.0, < 6.0) + responders + warden (~> 1.2.3) + devise-i18n (1.6.2) + devise (>= 4.4) erubi (1.7.1) execjs (2.7.0) ffi (1.9.23) @@ -102,6 +114,14 @@ GEM jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) + launchy (2.4.3) + addressable (~> 2.3) + letter_opener (1.6.0) + launchy (~> 2.2) + letter_opener_web (1.3.4) + actionmailer (>= 3.2) + letter_opener (~> 1.0) + railties (>= 3.2) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -127,10 +147,19 @@ GEM nio4r (2.3.1) nokogiri (1.8.2) mini_portile2 (~> 2.3.0) + orm_adapter (0.5.0) parallel (1.12.1) parser (2.5.1.0) ast (~> 2.4.0) powerpack (0.1.1) + pry (0.11.3) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + pry-byebug (3.6.0) + byebug (~> 10.0) + pry (~> 0.10) + pry-rails (0.3.6) + pry (>= 0.10.4) public_suffix (3.0.2) puma (3.11.4) rack (2.0.5) @@ -170,6 +199,9 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) rubocop (0.56.0) parallel (~> 1.10) parser (>= 2.5) @@ -194,6 +226,9 @@ GEM selenium-webdriver (3.12.0) childprocess (~> 0.5) rubyzip (~> 1.2) + simple_form (4.0.1) + actionpack (>= 5.0) + activemodel (>= 5.0) spring (2.0.2) activesupport (>= 4.2) spring-watcher-listen (2.0.1) @@ -220,6 +255,8 @@ GEM uglifier (4.1.10) execjs (>= 0.3.0, < 3) unicode-display_width (1.3.2) + warden (1.2.7) + rack (>= 1.0) web-console (3.6.2) actionview (>= 5.0) activemodel (>= 5.0) @@ -241,23 +278,30 @@ PLATFORMS DEPENDENCIES annotate bcrypt (~> 3.1.7) + binding_of_caller bootsnap (>= 1.1.0) - byebug capistrano (~> 3.10) capistrano-rails capistrano-rbenv capistrano3-puma capybara (>= 2.15, < 4.0) chromedriver-helper + devise (~> 4.4) + devise-i18n (~> 1.6) jbuilder (~> 2.5) + launchy + letter_opener_web listen (>= 3.0.5, < 3.2) mysql2 (>= 0.4.4, < 0.6.0) + pry-byebug + pry-rails puma (~> 3.11) rails (~> 5.2.0) rails-i18n (~> 5.1) rubocop (~> 0.56.0) sass-rails (~> 5.0) selenium-webdriver + simple_form (~> 4.0) spring spring-watcher-listen (~> 2.0.0) turbolinks (~> 5) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d1..37ba566 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,10 @@ class ApplicationController < ActionController::Base + before_action :configure_devise_parameters, if: :devise_controller? + + protected + + def configure_devise_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: [:tos_accepted]) + devise_parameter_sanitizer.permit(:account_update, keys: [:notifications_enabled]) + end end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..7aa200d --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,12 @@ +module UsersHelper + # Inject a devise template inside a same container + # while translation form keys are still valid + # (original partial scope is preserved) + def devise_form_container + content_for(:devise_form_content) do + yield + end + + render "shared/devise_form_container" + end +end diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index a18eca2..6abc989 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -9,3 +9,6 @@ import 'bootstrap' import './src/application.scss' +import Rails from 'rails-ujs'; + +Rails.start() diff --git a/app/javascript/packs/src/application.scss b/app/javascript/packs/src/application.scss index 5de3350..f6121ca 100644 --- a/app/javascript/packs/src/application.scss +++ b/app/javascript/packs/src/application.scss @@ -1 +1,2 @@ @import '~bootstrap/scss/bootstrap'; +@import 'components/users'; diff --git a/app/javascript/packs/src/components/users.scss b/app/javascript/packs/src/components/users.scss new file mode 100644 index 0000000..05ae08f --- /dev/null +++ b/app/javascript/packs/src/components/users.scss @@ -0,0 +1,3 @@ +.form-check-label.optional.boolean { + color: inherit; +} diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..a398135 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,39 @@ +# == Schema Information +# +# Table name: users +# +# id :bigint(8) not null, primary key +# confirmation_sent_at :datetime +# confirmation_token :string(255) +# confirmed_at :datetime +# current_sign_in_at :datetime +# current_sign_in_ip :string(255) +# email :string(255) default(""), not null +# encrypted_password :string(255) default(""), not null +# last_sign_in_at :datetime +# last_sign_in_ip :string(255) +# notifications_enabled :boolean default(TRUE), not null +# remember_created_at :datetime +# reset_password_sent_at :datetime +# reset_password_token :string(255) +# sign_in_count :integer default(0), not null +# tos_accepted :boolean default(FALSE), not null +# unconfirmed_email :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_users_on_confirmation_token (confirmation_token) UNIQUE +# index_users_on_email (email) UNIQUE +# index_users_on_reset_password_token (reset_password_token) UNIQUE +# + +class User < ApplicationRecord + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :validatable, :confirmable + + validates :tos_accepted, acceptance: true +end diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 0000000..474e5a0 --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb @@ -0,0 +1,12 @@ +<%= devise_form_container do %> +
<%= t('.greeting', recipient: @email) %>
+ +<%= t('.instruction') %>
+<%= link_to t('.action'), confirmation_url(@resource, confirmation_token: @token) %>
diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb new file mode 100644 index 0000000..0402428 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +<%= t('.greeting', recipient: @email) %>
+ +<% if @resource.try(:unconfirmed_email?) %> +<%= t('.message', email: @resource.unconfirmed_email) %>
+<% else %> +<%= t('.message', email: @resource.email) %>
+<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb new file mode 100644 index 0000000..a30313b --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +<%= t('.greeting', recipient: @resource.email) %>
+ +<%= t('.message') %>
diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 0000000..6c22abf --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +<%= t('.greeting', recipient: @resource.email) %>
+ +<%= t('.instruction') %>
+ +<%= link_to t('.action'), edit_password_url(@resource, reset_password_token: @token) %>
+ +<%= t('.instruction_2') %>
+<%= t('.instruction_3') %>
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 0000000..84d0981 --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +<%= t('.greeting', recipient: @resource.email) %>
+ +<%= t('.message') %>
+ +<%= t('.instruction') %>
+ +<%= link_to t('.action'), unlock_url(@resource, unlock_token: @token) %>
diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 0000000..964ded5 --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,16 @@ +<%= devise_form_container do %> +<%= t('.unhappy') %> <%= + button_to t('.cancel_my_account'), registration_path(resource_name), + class: "btn btn-danger", + data: { confirm: t('.are_you_sure') }, method: :delete %>
+ + <%= link_to t('devise.shared.links.back'), :back %> +<% end %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 0000000..69a0e09 --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,18 @@ +<%= devise_form_container do %> +