diff options
36 files changed, 1046 insertions, 7 deletions
| diff --git a/Gemfile b/Gemfile index e2147ef..32a78dc 100644 --- a/Gemfile +++ b/Gemfile | |||
| @@ -55,3 +55,4 @@ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] | |||
| 55 | 55 | ||
| 56 | gem 'haml' | 56 | gem 'haml' | 
| 57 | gem 'normalize-rails' | 57 | gem 'normalize-rails' | 
| 58 | gem 'devise' | ||
| diff --git a/Gemfile.lock b/Gemfile.lock index 60b7136..41f4d51 100644 --- a/Gemfile.lock +++ b/Gemfile.lock | |||
| @@ -41,6 +41,7 @@ GEM | |||
| 41 | addressable (2.5.1) | 41 | addressable (2.5.1) | 
| 42 | public_suffix (~> 2.0, >= 2.0.2) | 42 | public_suffix (~> 2.0, >= 2.0.2) | 
| 43 | arel (8.0.0) | 43 | arel (8.0.0) | 
| 44 | bcrypt (3.1.11) | ||
| 44 | bindex (0.5.0) | 45 | bindex (0.5.0) | 
| 45 | builder (3.2.3) | 46 | builder (3.2.3) | 
| 46 | byebug (9.0.6) | 47 | byebug (9.0.6) | 
| @@ -61,6 +62,12 @@ GEM | |||
| 61 | execjs | 62 | execjs | 
| 62 | coffee-script-source (1.12.2) | 63 | coffee-script-source (1.12.2) | 
| 63 | concurrent-ruby (1.0.5) | 64 | concurrent-ruby (1.0.5) | 
| 65 | devise (4.3.0) | ||
| 66 | bcrypt (~> 3.0) | ||
| 67 | orm_adapter (~> 0.1) | ||
| 68 | railties (>= 4.1.0, < 5.2) | ||
| 69 | responders | ||
| 70 | warden (~> 1.2.3) | ||
| 64 | erubi (1.6.0) | 71 | erubi (1.6.0) | 
| 65 | execjs (2.7.0) | 72 | execjs (2.7.0) | 
| 66 | ffi (1.9.18) | 73 | ffi (1.9.18) | 
| @@ -92,6 +99,7 @@ GEM | |||
| 92 | nokogiri (1.8.0) | 99 | nokogiri (1.8.0) | 
| 93 | mini_portile2 (~> 2.2.0) | 100 | mini_portile2 (~> 2.2.0) | 
| 94 | normalize-rails (4.1.1) | 101 | normalize-rails (4.1.1) | 
| 102 | orm_adapter (0.5.0) | ||
| 95 | public_suffix (2.0.5) | 103 | public_suffix (2.0.5) | 
| 96 | puma (3.9.1) | 104 | puma (3.9.1) | 
| 97 | rack (2.0.3) | 105 | rack (2.0.3) | 
| @@ -124,6 +132,9 @@ GEM | |||
| 124 | rb-fsevent (0.9.8) | 132 | rb-fsevent (0.9.8) | 
| 125 | rb-inotify (0.9.10) | 133 | rb-inotify (0.9.10) | 
| 126 | ffi (>= 0.5.0, < 2) | 134 | ffi (>= 0.5.0, < 2) | 
| 135 | responders (2.4.0) | ||
| 136 | actionpack (>= 4.2.0, < 5.3) | ||
| 137 | railties (>= 4.2.0, < 5.3) | ||
| 127 | ruby_dep (1.5.0) | 138 | ruby_dep (1.5.0) | 
| 128 | rubyzip (1.2.1) | 139 | rubyzip (1.2.1) | 
| 129 | sass (3.4.24) | 140 | sass (3.4.24) | 
| @@ -160,6 +171,8 @@ GEM | |||
| 160 | thread_safe (~> 0.1) | 171 | thread_safe (~> 0.1) | 
| 161 | uglifier (3.2.0) | 172 | uglifier (3.2.0) | 
| 162 | execjs (>= 0.3.0, < 3) | 173 | execjs (>= 0.3.0, < 3) | 
| 174 | warden (1.2.7) | ||
| 175 | rack (>= 1.0) | ||
| 163 | web-console (3.5.1) | 176 | web-console (3.5.1) | 
| 164 | actionview (>= 5.0) | 177 | actionview (>= 5.0) | 
| 165 | activemodel (>= 5.0) | 178 | activemodel (>= 5.0) | 
| @@ -178,6 +191,7 @@ DEPENDENCIES | |||
| 178 | byebug | 191 | byebug | 
| 179 | capybara (~> 2.13) | 192 | capybara (~> 2.13) | 
| 180 | coffee-rails (~> 4.2) | 193 | coffee-rails (~> 4.2) | 
| 194 | devise | ||
| 181 | haml | 195 | haml | 
| 182 | jbuilder (~> 2.5) | 196 | jbuilder (~> 2.5) | 
| 183 | listen (>= 3.0.5, < 3.2) | 197 | listen (>= 3.0.5, < 3.2) | 
| diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 40358bb..5ddb0bb 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css | |||
| @@ -17,7 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | @import url('https://fonts.googleapis.com/css?family=Inconsolata'); | 18 | @import url('https://fonts.googleapis.com/css?family=Inconsolata'); | 
| 19 | 19 | ||
| 20 | body { | 20 | body#main-body { | 
| 21 | background-color: #bfefff; | 21 | background-color: #bfefff; | 
| 22 | } | 22 | } | 
| 23 | 23 | ||
| @@ -76,3 +76,140 @@ body { | |||
| 76 | #sidebar h2 { | 76 | #sidebar h2 { | 
| 77 | font-size: 1em; | 77 | font-size: 1em; | 
| 78 | } | 78 | } | 
| 79 | |||
| 80 | #flash { | ||
| 81 | width: 100%; | ||
| 82 | text-align: center; | ||
| 83 | padding: .25em; | ||
| 84 | font-weight: bold | ||
| 85 | } | ||
| 86 | |||
| 87 | .flash-notice { | ||
| 88 | background-color: #fffde8; | ||
| 89 | } | ||
| 90 | |||
| 91 | .flash-alert { | ||
| 92 | background-color: #ff6a6a; | ||
| 93 | } | ||
| 94 | |||
| 95 | html { | ||
| 96 | height: 100%; | ||
| 97 | } | ||
| 98 | |||
| 99 | body#userdata-body { | ||
| 100 | background-color: #eeeeee; | ||
| 101 | height: 100%; | ||
| 102 | } | ||
| 103 | |||
| 104 | #userdata-container { | ||
| 105 | margin: 4em auto 0; | ||
| 106 | width: 25%; | ||
| 107 | } | ||
| 108 | |||
| 109 | #userdata-form { | ||
| 110 | background-color: #fefefe; | ||
| 111 | -webkit-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.75); | ||
| 112 | -moz-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.75); | ||
| 113 | box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.75); | ||
| 114 | border-radius: 2px 2px 2px 2px; | ||
| 115 | -moz-border-radius: 2px 2px 2px 2px; | ||
| 116 | -webkit-border-radius: 2px 2px 2px 2px; | ||
| 117 | padding: 1em; | ||
| 118 | } | ||
| 119 | |||
| 120 | #userdata-form .field { | ||
| 121 | margin-bottom: 1em; | ||
| 122 | } | ||
| 123 | |||
| 124 | #userdata-form .field label { | ||
| 125 | display: block; | ||
| 126 | margin-bottom: .25em; | ||
| 127 | } | ||
| 128 | |||
| 129 | #userdata-form input[type=text], #userdata-form input[type=password] { | ||
| 130 | font-size: 24px; | ||
| 131 | padding: 3px; | ||
| 132 | width: 100%; | ||
| 133 | box-sizing: border-box; | ||
| 134 | -webkit-box-shadow: inset 0px 1px 2px 0px rgba(0,0,0,0.7); | ||
| 135 | -moz-box-shadow: inset 0px 1px 2px 0px rgba(0,0,0,0.7); | ||
| 136 | box-shadow: inset 0px 1px 2px 0px rgba(0,0,0,0.7); | ||
| 137 | border: 1px solid #ddd; | ||
| 138 | outline: 0; | ||
| 139 | border-radius: 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | .remember-me-field { | ||
| 143 | float: left; | ||
| 144 | } | ||
| 145 | |||
| 146 | .submit-field { | ||
| 147 | text-align: right; | ||
| 148 | } | ||
| 149 | |||
| 150 | .userdata-link { | ||
| 151 | margin: 1em; | ||
| 152 | } | ||
| 153 | |||
| 154 | .userdata-link a { | ||
| 155 | text-decoration: none; | ||
| 156 | } | ||
| 157 | |||
| 158 | .userdata-link a:hover { | ||
| 159 | text-decoration: underline; | ||
| 160 | } | ||
| 161 | |||
| 162 | #userdata-form, .userdata-link a, .userdata-link a:visited { | ||
| 163 | color: #555d66; | ||
| 164 | } | ||
| 165 | |||
| 166 | #userdata-form label, .userdata-link a { | ||
| 167 | font-size: .75em; | ||
| 168 | } | ||
| 169 | |||
| 170 | #userdata-flash { | ||
| 171 | color: black; | ||
| 172 | background-color: white; | ||
| 173 | padding: .75em; | ||
| 174 | margin-bottom: 2em; | ||
| 175 | font-size: .8em; | ||
| 176 | -webkit-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.75); | ||
| 177 | -moz-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.75); | ||
| 178 | box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.75); | ||
| 179 | } | ||
| 180 | |||
| 181 | .userdata-flash-alert { | ||
| 182 | border-left: 5px solid red; | ||
| 183 | } | ||
| 184 | |||
| 185 | .userdata-flash-notice { | ||
| 186 | border-left: 5px solid green; | ||
| 187 | } | ||
| 188 | |||
| 189 | .userdata-flash-tag { | ||
| 190 | font-weight: bold; | ||
| 191 | } | ||
| 192 | |||
| 193 | .sidebar-module + .sidebar-module { | ||
| 194 | margin-top: 2em; | ||
| 195 | } | ||
| 196 | |||
| 197 | .sidebar-module ul { | ||
| 198 | padding-left: 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | .sidebar-module li { | ||
| 202 | list-style-type: none; | ||
| 203 | } | ||
| 204 | |||
| 205 | .sidebar-module a { | ||
| 206 | text-decoration: none; | ||
| 207 | } | ||
| 208 | |||
| 209 | .sidebar-module a:hover { | ||
| 210 | text-decoration: underline; | ||
| 211 | } | ||
| 212 | |||
| 213 | .sidebar-module a:visited { | ||
| 214 | color: #352712; | ||
| 215 | } | ||
| diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1c07694..0174cae 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb | |||
| @@ -1,3 +1,9 @@ | |||
| 1 | class ApplicationController < ActionController::Base | 1 | class ApplicationController < ActionController::Base | 
| 2 | protect_from_forgery with: :exception | 2 | protect_from_forgery with: :exception | 
| 3 | |||
| 4 | private | ||
| 5 | |||
| 6 | def after_sign_out_path_for(resource) | ||
| 7 | new_session_path(resource) | ||
| 8 | end | ||
| 3 | end | 9 | end | 
| diff --git a/app/controllers/users/confirmations_controller.rb b/app/controllers/users/confirmations_controller.rb new file mode 100644 index 0000000..1126e23 --- /dev/null +++ b/app/controllers/users/confirmations_controller.rb | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | class Users::ConfirmationsController < Devise::ConfirmationsController | ||
| 2 | # GET /resource/confirmation/new | ||
| 3 | # def new | ||
| 4 | # super | ||
| 5 | # end | ||
| 6 | |||
| 7 | # POST /resource/confirmation | ||
| 8 | # def create | ||
| 9 | # super | ||
| 10 | # end | ||
| 11 | |||
| 12 | # GET /resource/confirmation?confirmation_token=abcdef | ||
| 13 | # def show | ||
| 14 | # super | ||
| 15 | # end | ||
| 16 | |||
| 17 | # protected | ||
| 18 | |||
| 19 | # The path used after resending confirmation instructions. | ||
| 20 | # def after_resending_confirmation_instructions_path_for(resource_name) | ||
| 21 | # super(resource_name) | ||
| 22 | # end | ||
| 23 | |||
| 24 | # The path used after confirmation. | ||
| 25 | # def after_confirmation_path_for(resource_name, resource) | ||
| 26 | # super(resource_name, resource) | ||
| 27 | # end | ||
| 28 | end | ||
| diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb new file mode 100644 index 0000000..1907e5b --- /dev/null +++ b/app/controllers/users/omniauth_callbacks_controller.rb | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController | ||
| 2 | # You should configure your model like this: | ||
| 3 | # devise :omniauthable, omniauth_providers: [:twitter] | ||
| 4 | |||
| 5 | # You should also create an action method in this controller like this: | ||
| 6 | # def twitter | ||
| 7 | # end | ||
| 8 | |||
| 9 | # More info at: | ||
| 10 | # https://github.com/plataformatec/devise#omniauth | ||
| 11 | |||
| 12 | # GET|POST /resource/auth/twitter | ||
| 13 | # def passthru | ||
| 14 | # super | ||
| 15 | # end | ||
| 16 | |||
| 17 | # GET|POST /users/auth/twitter/callback | ||
| 18 | # def failure | ||
| 19 | # super | ||
| 20 | # end | ||
| 21 | |||
| 22 | # protected | ||
| 23 | |||
| 24 | # The path used when OmniAuth fails | ||
| 25 | # def after_omniauth_failure_path_for(scope) | ||
| 26 | # super(scope) | ||
| 27 | # end | ||
| 28 | end | ||
| diff --git a/app/controllers/users/passwords_controller.rb b/app/controllers/users/passwords_controller.rb new file mode 100644 index 0000000..53cc34e --- /dev/null +++ b/app/controllers/users/passwords_controller.rb | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | class Users::PasswordsController < Devise::PasswordsController | ||
| 2 | # GET /resource/password/new | ||
| 3 | # def new | ||
| 4 | # super | ||
| 5 | # end | ||
| 6 | |||
| 7 | # POST /resource/password | ||
| 8 | # def create | ||
| 9 | # super | ||
| 10 | # end | ||
| 11 | |||
| 12 | # GET /resource/password/edit?reset_password_token=abcdef | ||
| 13 | # def edit | ||
| 14 | # super | ||
| 15 | # end | ||
| 16 | |||
| 17 | # PUT /resource/password | ||
| 18 | # def update | ||
| 19 | # super | ||
| 20 | # end | ||
| 21 | |||
| 22 | # protected | ||
| 23 | |||
| 24 | # def after_resetting_password_path_for(resource) | ||
| 25 | # super(resource) | ||
| 26 | # end | ||
| 27 | |||
| 28 | # The path used after sending reset password instructions | ||
| 29 | # def after_sending_reset_password_instructions_path_for(resource_name) | ||
| 30 | # super(resource_name) | ||
| 31 | # end | ||
| 32 | end | ||
| diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb new file mode 100644 index 0000000..4d6fbad --- /dev/null +++ b/app/controllers/users/registrations_controller.rb | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | class Users::RegistrationsController < Devise::RegistrationsController | ||
| 2 | # before_action :configure_sign_up_params, only: [:create] | ||
| 3 | # before_action :configure_account_update_params, only: [:update] | ||
| 4 | |||
| 5 | # GET /resource/sign_up | ||
| 6 | # def new | ||
| 7 | # super | ||
| 8 | # end | ||
| 9 | |||
| 10 | # POST /resource | ||
| 11 | # def create | ||
| 12 | # super | ||
| 13 | # end | ||
| 14 | |||
| 15 | # GET /resource/edit | ||
| 16 | # def edit | ||
| 17 | # super | ||
| 18 | # end | ||
| 19 | |||
| 20 | # PUT /resource | ||
| 21 | # def update | ||
| 22 | # super | ||
| 23 | # end | ||
| 24 | |||
| 25 | # DELETE /resource | ||
| 26 | # def destroy | ||
| 27 | # super | ||
| 28 | # end | ||
| 29 | |||
| 30 | # GET /resource/cancel | ||
| 31 | # Forces the session data which is usually expired after sign | ||
| 32 | # in to be expired now. This is useful if the user wants to | ||
| 33 | # cancel oauth signing in/up in the middle of the process, | ||
| 34 | # removing all OAuth session data. | ||
| 35 | # def cancel | ||
| 36 | # super | ||
| 37 | # end | ||
| 38 | |||
| 39 | # protected | ||
| 40 | |||
| 41 | # If you have extra params to permit, append them to the sanitizer. | ||
| 42 | # def configure_sign_up_params | ||
| 43 | # devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute]) | ||
| 44 | # end | ||
| 45 | |||
| 46 | # If you have extra params to permit, append them to the sanitizer. | ||
| 47 | # def configure_account_update_params | ||
| 48 | # devise_parameter_sanitizer.permit(:account_update, keys: [:attribute]) | ||
| 49 | # end | ||
| 50 | |||
| 51 | # The path used after sign up. | ||
| 52 | # def after_sign_up_path_for(resource) | ||
| 53 | # super(resource) | ||
| 54 | # end | ||
| 55 | |||
| 56 | # The path used after sign up for inactive accounts. | ||
| 57 | # def after_inactive_sign_up_path_for(resource) | ||
| 58 | # super(resource) | ||
| 59 | # end | ||
| 60 | end | ||
| diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb new file mode 100644 index 0000000..889fba5 --- /dev/null +++ b/app/controllers/users/sessions_controller.rb | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | class Users::SessionsController < Devise::SessionsController | ||
| 2 | layout "userdata" | ||
| 3 | # before_action :configure_sign_in_params, only: [:create] | ||
| 4 | |||
| 5 | # GET /resource/sign_in | ||
| 6 | # def new | ||
| 7 | # super | ||
| 8 | # end | ||
| 9 | |||
| 10 | # POST /resource/sign_in | ||
| 11 | # def create | ||
| 12 | # super | ||
| 13 | # end | ||
| 14 | |||
| 15 | # DELETE /resource/sign_out | ||
| 16 | # def destroy | ||
| 17 | # super | ||
| 18 | # end | ||
| 19 | |||
| 20 | # protected | ||
| 21 | |||
| 22 | # If you have extra params to permit, append them to the sanitizer. | ||
| 23 | # def configure_sign_in_params | ||
| 24 | # devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute]) | ||
| 25 | # end | ||
| 26 | end | ||
| diff --git a/app/controllers/users/unlocks_controller.rb b/app/controllers/users/unlocks_controller.rb new file mode 100644 index 0000000..8b9ef86 --- /dev/null +++ b/app/controllers/users/unlocks_controller.rb | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | class Users::UnlocksController < Devise::UnlocksController | ||
| 2 | # GET /resource/unlock/new | ||
| 3 | # def new | ||
| 4 | # super | ||
| 5 | # end | ||
| 6 | |||
| 7 | # POST /resource/unlock | ||
| 8 | # def create | ||
| 9 | # super | ||
| 10 | # end | ||
| 11 | |||
| 12 | # GET /resource/unlock?unlock_token=abcdef | ||
| 13 | # def show | ||
| 14 | # super | ||
| 15 | # end | ||
| 16 | |||
| 17 | # protected | ||
| 18 | |||
| 19 | # The path used after sending unlock password instructions | ||
| 20 | # def after_sending_unlock_instructions_path_for(resource) | ||
| 21 | # super(resource) | ||
| 22 | # end | ||
| 23 | |||
| 24 | # The path used after unlocking the resource | ||
| 25 | # def after_unlock_path_for(resource) | ||
| 26 | # super(resource) | ||
| 27 | # end | ||
| 28 | end | ||
| diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..b8bdae1 --- /dev/null +++ b/app/models/user.rb | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | class User < ApplicationRecord | ||
| 2 | # Include default devise modules. Others available are: | ||
| 3 | # :confirmable, :lockable, :timeoutable and :omniauthable | ||
| 4 | devise :database_authenticatable, | ||
| 5 | :recoverable, :rememberable, :trackable, :validatable | ||
| 6 | 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..2dc668f --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | <h2>Resend confirmation instructions</h2> | ||
| 2 | |||
| 3 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> | ||
| 4 | <%= devise_error_messages! %> | ||
| 5 | |||
| 6 | <div class="field"> | ||
| 7 | <%= f.label :email %><br /> | ||
| 8 | <%= f.email_field :email, autofocus: true, value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> | ||
| 9 | </div> | ||
| 10 | |||
| 11 | <div class="actions"> | ||
| 12 | <%= f.submit "Resend confirmation instructions" %> | ||
| 13 | </div> | ||
| 14 | <% end %> | ||
| 15 | |||
| 16 | <%= render "devise/shared/links" %> | ||
| diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 0000000..dc55f64 --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | <p>Welcome <%= @email %>!</p> | ||
| 2 | |||
| 3 | <p>You can confirm your account email through the link below:</p> | ||
| 4 | |||
| 5 | <p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p> | ||
| 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..32f4ba8 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | <p>Hello <%= @email %>!</p> | ||
| 2 | |||
| 3 | <% if @resource.try(:unconfirmed_email?) %> | ||
| 4 | <p>We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.</p> | ||
| 5 | <% else %> | ||
| 6 | <p>We're contacting you to notify you that your email has been changed to <%= @resource.email %>.</p> | ||
| 7 | <% 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..b41daf4 --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | <p>Hello <%= @resource.email %>!</p> | ||
| 2 | |||
| 3 | <p>We're contacting you to notify you that your password has been changed.</p> | ||
| 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..f667dc1 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | <p>Hello <%= @resource.email %>!</p> | ||
| 2 | |||
| 3 | <p>Someone has requested a link to change your password. You can do this through the link below.</p> | ||
| 4 | |||
| 5 | <p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p> | ||
| 6 | |||
| 7 | <p>If you didn't request this, please ignore this email.</p> | ||
| 8 | <p>Your password won't change until you access the link above and create a new one.</p> | ||
| 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..41e148b --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | <p>Hello <%= @resource.email %>!</p> | ||
| 2 | |||
| 3 | <p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p> | ||
| 4 | |||
| 5 | <p>Click the link below to unlock your account:</p> | ||
| 6 | |||
| 7 | <p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %></p> | ||
| diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 0000000..6a796b0 --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | <h2>Change your password</h2> | ||
| 2 | |||
| 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> | ||
| 4 | <%= devise_error_messages! %> | ||
| 5 | <%= f.hidden_field :reset_password_token %> | ||
| 6 | |||
| 7 | <div class="field"> | ||
| 8 | <%= f.label :password, "New password" %><br /> | ||
| 9 | <% if @minimum_password_length %> | ||
| 10 | <em>(<%= @minimum_password_length %> characters minimum)</em><br /> | ||
| 11 | <% end %> | ||
| 12 | <%= f.password_field :password, autofocus: true, autocomplete: "off" %> | ||
| 13 | </div> | ||
| 14 | |||
| 15 | <div class="field"> | ||
| 16 | <%= f.label :password_confirmation, "Confirm new password" %><br /> | ||
| 17 | <%= f.password_field :password_confirmation, autocomplete: "off" %> | ||
| 18 | </div> | ||
| 19 | |||
| 20 | <div class="actions"> | ||
| 21 | <%= f.submit "Change my password" %> | ||
| 22 | </div> | ||
| 23 | <% end %> | ||
| 24 | |||
| 25 | <%= render "devise/shared/links" %> | ||
| diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb new file mode 100644 index 0000000..3d6d11a --- /dev/null +++ b/app/views/devise/passwords/new.html.erb | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | <h2>Forgot your password?</h2> | ||
| 2 | |||
| 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> | ||
| 4 | <%= devise_error_messages! %> | ||
| 5 | |||
| 6 | <div class="field"> | ||
| 7 | <%= f.label :email %><br /> | ||
| 8 | <%= f.email_field :email, autofocus: true %> | ||
| 9 | </div> | ||
| 10 | |||
| 11 | <div class="actions"> | ||
| 12 | <%= f.submit "Send me reset password instructions" %> | ||
| 13 | </div> | ||
| 14 | <% end %> | ||
| 15 | |||
| 16 | <%= render "devise/shared/links" %> | ||
| diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb new file mode 100644 index 0000000..1e66f3d --- /dev/null +++ b/app/views/devise/registrations/edit.html.erb | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | <h2>Edit <%= resource_name.to_s.humanize %></h2> | ||
| 2 | |||
| 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> | ||
| 4 | <%= devise_error_messages! %> | ||
| 5 | |||
| 6 | <div class="field"> | ||
| 7 | <%= f.label :email %><br /> | ||
| 8 | <%= f.email_field :email, autofocus: true %> | ||
| 9 | </div> | ||
| 10 | |||
| 11 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> | ||
| 12 | <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div> | ||
| 13 | <% end %> | ||
| 14 | |||
| 15 | <div class="field"> | ||
| 16 | <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br /> | ||
| 17 | <%= f.password_field :password, autocomplete: "off" %> | ||
| 18 | <% if @minimum_password_length %> | ||
| 19 | <br /> | ||
| 20 | <em><%= @minimum_password_length %> characters minimum</em> | ||
| 21 | <% end %> | ||
| 22 | </div> | ||
| 23 | |||
| 24 | <div class="field"> | ||
| 25 | <%= f.label :password_confirmation %><br /> | ||
| 26 | <%= f.password_field :password_confirmation, autocomplete: "off" %> | ||
| 27 | </div> | ||
| 28 | |||
| 29 | <div class="field"> | ||
| 30 | <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br /> | ||
| 31 | <%= f.password_field :current_password, autocomplete: "off" %> | ||
| 32 | </div> | ||
| 33 | |||
| 34 | <div class="actions"> | ||
| 35 | <%= f.submit "Update" %> | ||
| 36 | </div> | ||
| 37 | <% end %> | ||
| 38 | |||
| 39 | <h3>Cancel my account</h3> | ||
| 40 | |||
| 41 | <p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p> | ||
| 42 | |||
| 43 | <%= link_to "Back", :back %> | ||
| diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 0000000..5a238ce --- /dev/null +++ b/app/views/devise/registrations/new.html.erb | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | <h2>Sign up</h2> | ||
| 2 | |||
| 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> | ||
| 4 | <%= devise_error_messages! %> | ||
| 5 | |||
| 6 | <div class="field"> | ||
| 7 | <%= f.label :email %><br /> | ||
| 8 | <%= f.email_field :email, autofocus: true %> | ||
| 9 | </div> | ||
| 10 | |||
| 11 | <div class="field"> | ||
| 12 | <%= f.label :password %> | ||
| 13 | <% if @minimum_password_length %> | ||
| 14 | <em>(<%= @minimum_password_length %> characters minimum)</em> | ||
| 15 | <% end %><br /> | ||
| 16 | <%= f.password_field :password, autocomplete: "off" %> | ||
| 17 | </div> | ||
| 18 | |||
| 19 | <div class="field"> | ||
| 20 | <%= f.label :password_confirmation %><br /> | ||
| 21 | <%= f.password_field :password_confirmation, autocomplete: "off" %> | ||
| 22 | </div> | ||
| 23 | |||
| 24 | <div class="actions"> | ||
| 25 | <%= f.submit "Sign up" %> | ||
| 26 | </div> | ||
| 27 | <% end %> | ||
| 28 | |||
| 29 | <%= render "devise/shared/links" %> | ||
| diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb new file mode 100644 index 0000000..e6a3e41 --- /dev/null +++ b/app/views/devise/shared/_links.html.erb | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | <%- if controller_name != 'sessions' %> | ||
| 2 | <%= link_to "Log in", new_session_path(resource_name) %><br /> | ||
| 3 | <% end -%> | ||
| 4 | |||
| 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> | ||
| 6 | <%= link_to "Sign up", new_registration_path(resource_name) %><br /> | ||
| 7 | <% end -%> | ||
| 8 | |||
| 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> | ||
| 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %><br /> | ||
| 11 | <% end -%> | ||
| 12 | |||
| 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> | ||
| 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br /> | ||
| 15 | <% end -%> | ||
| 16 | |||
| 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> | ||
| 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br /> | ||
| 19 | <% end -%> | ||
| 20 | |||
| 21 | <%- if devise_mapping.omniauthable? %> | ||
| 22 | <%- resource_class.omniauth_providers.each do |provider| %> | ||
| 23 | <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %><br /> | ||
| 24 | <% end -%> | ||
| 25 | <% end -%> | ||
| diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb new file mode 100644 index 0000000..16586bc --- /dev/null +++ b/app/views/devise/unlocks/new.html.erb | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | <h2>Resend unlock instructions</h2> | ||
| 2 | |||
| 3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> | ||
| 4 | <%= devise_error_messages! %> | ||
| 5 | |||
| 6 | <div class="field"> | ||
| 7 | <%= f.label :email %><br /> | ||
| 8 | <%= f.email_field :email, autofocus: true %> | ||
| 9 | </div> | ||
| 10 | |||
| 11 | <div class="actions"> | ||
| 12 | <%= f.submit "Resend unlock instructions" %> | ||
| 13 | </div> | ||
| 14 | <% end %> | ||
| 15 | |||
| 16 | <%= render "devise/shared/links" %> | ||
| diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 55d7805..e9d8dfd 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml | |||
| @@ -5,12 +5,24 @@ | |||
| 5 | = csrf_meta_tags | 5 | = csrf_meta_tags | 
| 6 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' | 6 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' | 
| 7 | = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' | 7 | = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' | 
| 8 | %body | 8 | %body#main-body | 
| 9 | - if flash[:alert] | ||
| 10 | %div#flash.flash-alert= flash[:alert] | ||
| 11 | - if flash[:notice] | ||
| 12 | %div#flash.flash-notice= flash[:notice] | ||
| 9 | #container | 13 | #container | 
| 10 | #banner | 14 | #banner | 
| 11 | %h1= link_to "Thoughts", root_url | 15 | %h1= link_to "Thoughts", root_url | 
| 12 | #main | 16 | #main | 
| 13 | #content= yield | 17 | #content= yield | 
| 14 | #sidebar | 18 | #sidebar | 
| 15 | %h2 I'm Fef. | 19 | .sidebar-module | 
| 16 | %p I'm a computer science undergrad at Carnegie Mellon University. I make a lot of things, including Twitter bots. I really like Pokémon, and I write about it a lot. | 20 | %h2 I'm Fef. | 
| 21 | %p I'm a computer science undergrad at Carnegie Mellon University. I make a lot of things, including Twitter bots. I really like Pokémon, and I write about it a lot. | ||
| 22 | .sidebar-module | ||
| 23 | %h2 Meta | ||
| 24 | %ul | ||
| 25 | - if not user_signed_in? | ||
| 26 | %li= link_to "Log in", new_user_session_path | ||
| 27 | - if user_signed_in? | ||
| 28 | %li= link_to "Log out", destroy_user_session_path, method: :delete | ||
| diff --git a/app/views/layouts/userdata.html.haml b/app/views/layouts/userdata.html.haml new file mode 100644 index 0000000..f6e3b57 --- /dev/null +++ b/app/views/layouts/userdata.html.haml | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | !!! 5 | ||
| 2 | %html | ||
| 3 | %head | ||
| 4 | %title Thoughts | ||
| 5 | = csrf_meta_tags | ||
| 6 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' | ||
| 7 | = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' | ||
| 8 | %body#userdata-body | ||
| 9 | #userdata-container | ||
| 10 | - if flash[:alert] | ||
| 11 | #userdata-flash.userdata-flash-alert | ||
| 12 | %span.userdata-flash-tag ERROR: | ||
| 13 | = flash.alert | ||
| 14 | - if flash[:notice] | ||
| 15 | #userdata-flash.userdata-flash-notice | ||
| 16 | %span.userdata-flash-tag NOTICE: | ||
| 17 | = flash.notice | ||
| 18 | = yield | ||
| diff --git a/app/views/users/sessions/.new.html.haml.swo b/app/views/users/sessions/.new.html.haml.swo new file mode 100644 index 0000000..85950d9 --- /dev/null +++ b/app/views/users/sessions/.new.html.haml.swo | |||
| Binary files differ | |||
| diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml new file mode 100644 index 0000000..fbdf8be --- /dev/null +++ b/app/views/users/sessions/new.html.haml | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #userdata-form | ||
| 2 | = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| | ||
| 3 | .field | ||
| 4 | = f.label :login | ||
| 5 | = f.text_field :login, autofocus: true | ||
| 6 | .field | ||
| 7 | = f.label :password | ||
| 8 | = f.password_field :password, autocomplete: "off" | ||
| 9 | .remember-me-field | ||
| 10 | = f.check_box :remember_me | ||
| 11 | = f.label :remember_me | ||
| 12 | .submit-field | ||
| 13 | = f.submit "Log in" | ||
| 14 | .userdata-link= link_to "Forgot your password?", new_password_path(resource_name) | ||
| 15 | .userdata-link= link_to "← Back to thoughts", root_url | ||
| diff --git a/config/environments/development.rb b/config/environments/development.rb index 5187e22..1b0c4b3 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb | |||
| @@ -51,4 +51,6 @@ Rails.application.configure do | |||
| 51 | # Use an evented file watcher to asynchronously detect changes in source code, | 51 | # Use an evented file watcher to asynchronously detect changes in source code, | 
| 52 | # routes, locales, etc. This feature depends on the listen gem. | 52 | # routes, locales, etc. This feature depends on the listen gem. | 
| 53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker | 53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker | 
| 54 | |||
| 55 | config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } | ||
| 54 | end | 56 | end | 
| diff --git a/config/environments/production.rb b/config/environments/production.rb index 3606f65..03d15d1 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb | |||
| @@ -45,7 +45,7 @@ Rails.application.configure do | |||
| 45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] | 45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] | 
| 46 | 46 | ||
| 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. | 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. | 
| 48 | # config.force_ssl = true | 48 | config.force_ssl = true | 
| 49 | 49 | ||
| 50 | # Use the lowest log level to ensure availability of diagnostic information | 50 | # Use the lowest log level to ensure availability of diagnostic information | 
| 51 | # when problems arise. | 51 | # when problems arise. | 
| @@ -66,6 +66,8 @@ Rails.application.configure do | |||
| 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. | 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. | 
| 67 | # config.action_mailer.raise_delivery_errors = false | 67 | # config.action_mailer.raise_delivery_errors = false | 
| 68 | 68 | ||
| 69 | config.action_mailer.default_url_options = { host: 'feffernoo.se' } | ||
| 70 | |||
| 69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to | 71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to | 
| 70 | # the I18n.default_locale when a translation cannot be found). | 72 | # the I18n.default_locale when a translation cannot be found). | 
| 71 | config.i18n.fallbacks = true | 73 | config.i18n.fallbacks = true | 
| diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 0000000..f85c0a6 --- /dev/null +++ b/config/initializers/devise.rb | |||
| @@ -0,0 +1,278 @@ | |||
| 1 | # Use this hook to configure devise mailer, warden hooks and so forth. | ||
| 2 | # Many of these configuration options can be set straight in your model. | ||
| 3 | Devise.setup do |config| | ||
| 4 | # The secret key used by Devise. Devise uses this key to generate | ||
| 5 | # random tokens. Changing this key will render invalid all existing | ||
| 6 | # confirmation, reset password and unlock tokens in the database. | ||
| 7 | # Devise will use the `secret_key_base` as its `secret_key` | ||
| 8 | # by default. You can change it below and use your own secret key. | ||
| 9 | # config.secret_key = 'f85d289a63b03808f899604ae6f3b1ba93430a8a7e9a5c0ee817471e4d4e201f000e8d42386392d4c24add7b4e2f0e011ea491c1febb57b37301e63b67f0ed60' | ||
| 10 | |||
| 11 | # ==> Mailer Configuration | ||
| 12 | # Configure the e-mail address which will be shown in Devise::Mailer, | ||
| 13 | # note that it will be overwritten if you use your own mailer class | ||
| 14 | # with default "from" parameter. | ||
| 15 | config.mailer_sender = 'no-reply@feffernoo.se' | ||
| 16 | |||
| 17 | # Configure the class responsible to send e-mails. | ||
| 18 | # config.mailer = 'Devise::Mailer' | ||
| 19 | |||
| 20 | # Configure the parent class responsible to send e-mails. | ||
| 21 | # config.parent_mailer = 'ActionMailer::Base' | ||
| 22 | |||
| 23 | # ==> ORM configuration | ||
| 24 | # Load and configure the ORM. Supports :active_record (default) and | ||
| 25 | # :mongoid (bson_ext recommended) by default. Other ORMs may be | ||
| 26 | # available as additional gems. | ||
| 27 | require 'devise/orm/active_record' | ||
| 28 | |||
| 29 | # ==> Configuration for any authentication mechanism | ||
| 30 | # Configure which keys are used when authenticating a user. The default is | ||
| 31 | # just :email. You can configure it to use [:username, :subdomain], so for | ||
| 32 | # authenticating a user, both parameters are required. Remember that those | ||
| 33 | # parameters are used only when authenticating and not when retrieving from | ||
| 34 | # session. If you need permissions, you should implement that in a before filter. | ||
| 35 | # You can also supply a hash where the value is a boolean determining whether | ||
| 36 | # or not authentication should be aborted when the value is not present. | ||
| 37 | # config.authentication_keys = [:email] | ||
| 38 | config.authentication_keys = [:login] | ||
| 39 | |||
| 40 | # Configure parameters from the request object used for authentication. Each entry | ||
| 41 | # given should be a request method and it will automatically be passed to the | ||
| 42 | # find_for_authentication method and considered in your model lookup. For instance, | ||
| 43 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. | ||
| 44 | # The same considerations mentioned for authentication_keys also apply to request_keys. | ||
| 45 | # config.request_keys = [] | ||
| 46 | |||
| 47 | # Configure which authentication keys should be case-insensitive. | ||
| 48 | # These keys will be downcased upon creating or modifying a user and when used | ||
| 49 | # to authenticate or find a user. Default is :email. | ||
| 50 | config.case_insensitive_keys = [] | ||
| 51 | |||
| 52 | # Configure which authentication keys should have whitespace stripped. | ||
| 53 | # These keys will have whitespace before and after removed upon creating or | ||
| 54 | # modifying a user and when used to authenticate or find a user. Default is :email. | ||
| 55 | config.strip_whitespace_keys = [:login] | ||
| 56 | |||
| 57 | # Tell if authentication through request.params is enabled. True by default. | ||
| 58 | # It can be set to an array that will enable params authentication only for the | ||
| 59 | # given strategies, for example, `config.params_authenticatable = [:database]` will | ||
| 60 | # enable it only for database (email + password) authentication. | ||
| 61 | # config.params_authenticatable = true | ||
| 62 | |||
| 63 | # Tell if authentication through HTTP Auth is enabled. False by default. | ||
| 64 | # It can be set to an array that will enable http authentication only for the | ||
| 65 | # given strategies, for example, `config.http_authenticatable = [:database]` will | ||
| 66 | # enable it only for database authentication. The supported strategies are: | ||
| 67 | # :database = Support basic authentication with authentication key + password | ||
| 68 | # config.http_authenticatable = false | ||
| 69 | |||
| 70 | # If 401 status code should be returned for AJAX requests. True by default. | ||
| 71 | # config.http_authenticatable_on_xhr = true | ||
| 72 | |||
| 73 | # The realm used in Http Basic Authentication. 'Application' by default. | ||
| 74 | # config.http_authentication_realm = 'Application' | ||
| 75 | |||
| 76 | # It will change confirmation, password recovery and other workflows | ||
| 77 | # to behave the same regardless if the e-mail provided was right or wrong. | ||
| 78 | # Does not affect registerable. | ||
| 79 | # config.paranoid = true | ||
| 80 | |||
| 81 | # By default Devise will store the user in session. You can skip storage for | ||
| 82 | # particular strategies by setting this option. | ||
| 83 | # Notice that if you are skipping storage for all authentication paths, you | ||
| 84 | # may want to disable generating routes to Devise's sessions controller by | ||
| 85 | # passing skip: :sessions to `devise_for` in your config/routes.rb | ||
| 86 | config.skip_session_storage = [:http_auth] | ||
| 87 | |||
| 88 | # By default, Devise cleans up the CSRF token on authentication to | ||
| 89 | # avoid CSRF token fixation attacks. This means that, when using AJAX | ||
| 90 | # requests for sign in and sign up, you need to get a new CSRF token | ||
| 91 | # from the server. You can disable this option at your own risk. | ||
| 92 | # config.clean_up_csrf_token_on_authentication = true | ||
| 93 | |||
| 94 | # When false, Devise will not attempt to reload routes on eager load. | ||
| 95 | # This can reduce the time taken to boot the app but if your application | ||
| 96 | # requires the Devise mappings to be loaded during boot time the application | ||
| 97 | # won't boot properly. | ||
| 98 | # config.reload_routes = true | ||
| 99 | |||
| 100 | # ==> Configuration for :database_authenticatable | ||
| 101 | # For bcrypt, this is the cost for hashing the password and defaults to 11. If | ||
| 102 | # using other algorithms, it sets how many times you want the password to be hashed. | ||
| 103 | # | ||
| 104 | # Limiting the stretches to just one in testing will increase the performance of | ||
| 105 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use | ||
| 106 | # a value less than 10 in other environments. Note that, for bcrypt (the default | ||
| 107 | # algorithm), the cost increases exponentially with the number of stretches (e.g. | ||
| 108 | # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). | ||
| 109 | config.stretches = Rails.env.test? ? 1 : 11 | ||
| 110 | |||
| 111 | # Set up a pepper to generate the hashed password. | ||
| 112 | # config.pepper = '371cc1e0f4d476a1969326be1ae6e43e8959b187b04be1b39225325a9429541174c0de9ddb3cf270dbf03d7df0711c744b82ca8d9fd27f69a09bf8162d262f80' | ||
| 113 | |||
| 114 | # Send a notification to the original email when the user's email is changed. | ||
| 115 | # config.send_email_changed_notification = false | ||
| 116 | |||
| 117 | # Send a notification email when the user's password is changed. | ||
| 118 | # config.send_password_change_notification = false | ||
| 119 | |||
| 120 | # ==> Configuration for :confirmable | ||
| 121 | # A period that the user is allowed to access the website even without | ||
| 122 | # confirming their account. For instance, if set to 2.days, the user will be | ||
| 123 | # able to access the website for two days without confirming their account, | ||
| 124 | # access will be blocked just in the third day. Default is 0.days, meaning | ||
| 125 | # the user cannot access the website without confirming their account. | ||
| 126 | # config.allow_unconfirmed_access_for = 2.days | ||
| 127 | |||
| 128 | # A period that the user is allowed to confirm their account before their | ||
| 129 | # token becomes invalid. For example, if set to 3.days, the user can confirm | ||
| 130 | # their account within 3 days after the mail was sent, but on the fourth day | ||
| 131 | # their account can't be confirmed with the token any more. | ||
| 132 | # Default is nil, meaning there is no restriction on how long a user can take | ||
| 133 | # before confirming their account. | ||
| 134 | # config.confirm_within = 3.days | ||
| 135 | |||
| 136 | # If true, requires any email changes to be confirmed (exactly the same way as | ||
| 137 | # initial account confirmation) to be applied. Requires additional unconfirmed_email | ||
| 138 | # db field (see migrations). Until confirmed, new email is stored in | ||
| 139 | # unconfirmed_email column, and copied to email column on successful confirmation. | ||
| 140 | config.reconfirmable = true | ||
| 141 | |||
| 142 | # Defines which key will be used when confirming an account | ||
| 143 | # config.confirmation_keys = [:email] | ||
| 144 | |||
| 145 | # ==> Configuration for :rememberable | ||
| 146 | # The time the user will be remembered without asking for credentials again. | ||
| 147 | # config.remember_for = 2.weeks | ||
| 148 | |||
| 149 | # Invalidates all the remember me tokens when the user signs out. | ||
| 150 | config.expire_all_remember_me_on_sign_out = true | ||
| 151 | |||
| 152 | # If true, extends the user's remember period when remembered via cookie. | ||
| 153 | # config.extend_remember_period = false | ||
| 154 | |||
| 155 | # Options to be passed to the created cookie. For instance, you can set | ||
| 156 | # secure: true in order to force SSL only cookies. | ||
| 157 | # config.rememberable_options = {} | ||
| 158 | |||
| 159 | # ==> Configuration for :validatable | ||
| 160 | # Range for password length. | ||
| 161 | config.password_length = 6..128 | ||
| 162 | |||
| 163 | # Email regex used to validate email formats. It simply asserts that | ||
| 164 | # one (and only one) @ exists in the given string. This is mainly | ||
| 165 | # to give user feedback and not to assert the e-mail validity. | ||
| 166 | config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ | ||
| 167 | |||
| 168 | # ==> Configuration for :timeoutable | ||
| 169 | # The time you want to timeout the user session without activity. After this | ||
| 170 | # time the user will be asked for credentials again. Default is 30 minutes. | ||
| 171 | # config.timeout_in = 30.minutes | ||
| 172 | |||
| 173 | # ==> Configuration for :lockable | ||
| 174 | # Defines which strategy will be used to lock an account. | ||
| 175 | # :failed_attempts = Locks an account after a number of failed attempts to sign in. | ||
| 176 | # :none = No lock strategy. You should handle locking by yourself. | ||
| 177 | # config.lock_strategy = :failed_attempts | ||
| 178 | |||
| 179 | # Defines which key will be used when locking and unlocking an account | ||
| 180 | # config.unlock_keys = [:email] | ||
| 181 | |||
| 182 | # Defines which strategy will be used to unlock an account. | ||
| 183 | # :email = Sends an unlock link to the user email | ||
| 184 | # :time = Re-enables login after a certain amount of time (see :unlock_in below) | ||
| 185 | # :both = Enables both strategies | ||
| 186 | # :none = No unlock strategy. You should handle unlocking by yourself. | ||
| 187 | # config.unlock_strategy = :both | ||
| 188 | |||
| 189 | # Number of authentication tries before locking an account if lock_strategy | ||
| 190 | # is failed attempts. | ||
| 191 | # config.maximum_attempts = 20 | ||
| 192 | |||
| 193 | # Time interval to unlock the account if :time is enabled as unlock_strategy. | ||
| 194 | # config.unlock_in = 1.hour | ||
| 195 | |||
| 196 | # Warn on the last attempt before the account is locked. | ||
| 197 | # config.last_attempt_warning = true | ||
| 198 | |||
| 199 | # ==> Configuration for :recoverable | ||
| 200 | # | ||
| 201 | # Defines which key will be used when recovering the password for an account | ||
| 202 | # config.reset_password_keys = [:email] | ||
| 203 | |||
| 204 | # Time interval you can reset your password with a reset password key. | ||
| 205 | # Don't put a too small interval or your users won't have the time to | ||
| 206 | # change their passwords. | ||
| 207 | config.reset_password_within = 6.hours | ||
| 208 | |||
| 209 | # When set to false, does not sign a user in automatically after their password is | ||
| 210 | # reset. Defaults to true, so a user is signed in automatically after a reset. | ||
| 211 | # config.sign_in_after_reset_password = true | ||
| 212 | |||
| 213 | # ==> Configuration for :encryptable | ||
| 214 | # Allow you to use another hashing or encryption algorithm besides bcrypt (default). | ||
| 215 | # You can use :sha1, :sha512 or algorithms from others authentication tools as | ||
| 216 | # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 | ||
| 217 | # for default behavior) and :restful_authentication_sha1 (then you should set | ||
| 218 | # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). | ||
| 219 | # | ||
| 220 | # Require the `devise-encryptable` gem when using anything other than bcrypt | ||
| 221 | # config.encryptor = :sha512 | ||
| 222 | |||
| 223 | # ==> Scopes configuration | ||
| 224 | # Turn scoped views on. Before rendering "sessions/new", it will first check for | ||
| 225 | # "users/sessions/new". It's turned off by default because it's slower if you | ||
| 226 | # are using only default views. | ||
| 227 | # config.scoped_views = false | ||
| 228 | |||
| 229 | # Configure the default scope given to Warden. By default it's the first | ||
| 230 | # devise role declared in your routes (usually :user). | ||
| 231 | # config.default_scope = :user | ||
| 232 | |||
| 233 | # Set this configuration to false if you want /users/sign_out to sign out | ||
| 234 | # only the current scope. By default, Devise signs out all scopes. | ||
| 235 | # config.sign_out_all_scopes = true | ||
| 236 | |||
| 237 | # ==> Navigation configuration | ||
| 238 | # Lists the formats that should be treated as navigational. Formats like | ||
| 239 | # :html, should redirect to the sign in page when the user does not have | ||
| 240 | # access, but formats like :xml or :json, should return 401. | ||
| 241 | # | ||
| 242 | # If you have any extra navigational formats, like :iphone or :mobile, you | ||
| 243 | # should add them to the navigational formats lists. | ||
| 244 | # | ||
| 245 | # The "*/*" below is required to match Internet Explorer requests. | ||
| 246 | # config.navigational_formats = ['*/*', :html] | ||
| 247 | |||
| 248 | # The default HTTP method used to sign out a resource. Default is :delete. | ||
| 249 | config.sign_out_via = :delete | ||
| 250 | |||
| 251 | # ==> OmniAuth | ||
| 252 | # Add a new OmniAuth provider. Check the wiki for more information on setting | ||
| 253 | # up on your models and hooks. | ||
| 254 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' | ||
| 255 | |||
| 256 | # ==> Warden configuration | ||
| 257 | # If you want to use other strategies, that are not supported by Devise, or | ||
| 258 | # change the failure app, you can configure them inside the config.warden block. | ||
| 259 | # | ||
| 260 | # config.warden do |manager| | ||
| 261 | # manager.intercept_401 = false | ||
| 262 | # manager.default_strategies(scope: :user).unshift :some_external_strategy | ||
| 263 | # end | ||
| 264 | |||
| 265 | # ==> Mountable engine configurations | ||
| 266 | # When using Devise inside an engine, let's call it `MyEngine`, and this engine | ||
| 267 | # is mountable, there are some extra configurations to be taken into account. | ||
| 268 | # The following options are available, assuming the engine is mounted as: | ||
| 269 | # | ||
| 270 | # mount MyEngine, at: '/my_engine' | ||
| 271 | # | ||
| 272 | # The router that invoked `devise_for`, in the example above, would be: | ||
| 273 | # config.router_name = :my_engine | ||
| 274 | # | ||
| 275 | # When using OmniAuth, Devise cannot automatically set OmniAuth path, | ||
| 276 | # so you need to do it manually. For the users scope, it would be: | ||
| 277 | # config.omniauth_path_prefix = '/my_engine/users/auth' | ||
| 278 | end | ||
| diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml new file mode 100644 index 0000000..0b8f130 --- /dev/null +++ b/config/locales/devise.en.yml | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n | ||
| 2 | |||
| 3 | en: | ||
| 4 | devise: | ||
| 5 | confirmations: | ||
| 6 | confirmed: "Your email address has been successfully confirmed." | ||
| 7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." | ||
| 8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." | ||
| 9 | failure: | ||
| 10 | already_authenticated: "You are already signed in." | ||
| 11 | inactive: "Your account is not activated yet." | ||
| 12 | invalid: "Invalid %{authentication_keys} or password." | ||
| 13 | locked: "Your account is locked." | ||
| 14 | last_attempt: "You have one more attempt before your account is locked." | ||
| 15 | not_found_in_database: "Invalid %{authentication_keys} or password." | ||
| 16 | timeout: "Your session expired. Please sign in again to continue." | ||
| 17 | unauthenticated: "You need to sign in or sign up before continuing." | ||
| 18 | unconfirmed: "You have to confirm your email address before continuing." | ||
| 19 | mailer: | ||
| 20 | confirmation_instructions: | ||
| 21 | subject: "Confirmation instructions" | ||
| 22 | reset_password_instructions: | ||
| 23 | subject: "Reset password instructions" | ||
| 24 | unlock_instructions: | ||
| 25 | subject: "Unlock instructions" | ||
| 26 | email_changed: | ||
| 27 | subject: "Email Changed" | ||
| 28 | password_change: | ||
| 29 | subject: "Password Changed" | ||
| 30 | omniauth_callbacks: | ||
| 31 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." | ||
| 32 | success: "Successfully authenticated from %{kind} account." | ||
| 33 | passwords: | ||
| 34 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." | ||
| 35 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." | ||
| 36 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." | ||
| 37 | updated: "Your password has been changed successfully. You are now signed in." | ||
| 38 | updated_not_active: "Your password has been changed successfully." | ||
| 39 | registrations: | ||
| 40 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." | ||
| 41 | signed_up: "Welcome! You have signed up successfully." | ||
| 42 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." | ||
| 43 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." | ||
| 44 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." | ||
| 45 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." | ||
| 46 | updated: "Your account has been updated successfully." | ||
| 47 | sessions: | ||
| 48 | signed_in: "Signed in successfully." | ||
| 49 | signed_out: "Signed out successfully." | ||
| 50 | already_signed_out: "Signed out successfully." | ||
| 51 | unlocks: | ||
| 52 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." | ||
| 53 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." | ||
| 54 | unlocked: "Your account has been unlocked successfully. Please sign in to continue." | ||
| 55 | errors: | ||
| 56 | messages: | ||
| 57 | already_confirmed: "was already confirmed, please try signing in" | ||
| 58 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" | ||
| 59 | expired: "has expired, please request a new one" | ||
| 60 | not_found: "not found" | ||
| 61 | not_locked: "was not locked" | ||
| 62 | not_saved: | ||
| 63 | one: "1 error prohibited this %{resource} from being saved:" | ||
| 64 | other: "%{count} errors prohibited this %{resource} from being saved:" | ||
| diff --git a/config/routes.rb b/config/routes.rb index 64b02aa..9c94f8a 100644 --- a/config/routes.rb +++ b/config/routes.rb | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | Rails.application.routes.draw do | 1 | Rails.application.routes.draw do | 
| 2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html | 2 | devise_for :users, controllers: { sessions: 'users/sessions' } | 
| 3 | |||
| 3 | root "records#index" | 4 | root "records#index" | 
| 4 | 5 | ||
| 5 | get 'says/:slug', to: 'entries#show' | 6 | get 'says/:slug', to: 'entries#show' | 
| diff --git a/db/migrate/20170625151955_devise_create_users.rb b/db/migrate/20170625151955_devise_create_users.rb new file mode 100644 index 0000000..799f225 --- /dev/null +++ b/db/migrate/20170625151955_devise_create_users.rb | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | class DeviseCreateUsers < ActiveRecord::Migration[5.1] | ||
| 2 | def change | ||
| 3 | create_table :users do |t| | ||
| 4 | ## Database authenticatable | ||
| 5 | t.string :login, null: false, default: "" | ||
| 6 | t.string :email, null: false, default: "" | ||
| 7 | t.string :encrypted_password, null: false, default: "" | ||
| 8 | |||
| 9 | ## Recoverable | ||
| 10 | t.string :reset_password_token | ||
| 11 | t.datetime :reset_password_sent_at | ||
| 12 | |||
| 13 | ## Rememberable | ||
| 14 | t.datetime :remember_created_at | ||
| 15 | |||
| 16 | ## Trackable | ||
| 17 | t.integer :sign_in_count, default: 0, null: false | ||
| 18 | t.datetime :current_sign_in_at | ||
| 19 | t.datetime :last_sign_in_at | ||
| 20 | t.string :current_sign_in_ip | ||
| 21 | t.string :last_sign_in_ip | ||
| 22 | |||
| 23 | ## Confirmable | ||
| 24 | # t.string :confirmation_token | ||
| 25 | # t.datetime :confirmed_at | ||
| 26 | # t.datetime :confirmation_sent_at | ||
| 27 | # t.string :unconfirmed_email # Only if using reconfirmable | ||
| 28 | |||
| 29 | ## Lockable | ||
| 30 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts | ||
| 31 | # t.string :unlock_token # Only if unlock strategy is :email or :both | ||
| 32 | # t.datetime :locked_at | ||
| 33 | |||
| 34 | |||
| 35 | t.timestamps null: false | ||
| 36 | end | ||
| 37 | |||
| 38 | add_index :users, :login, unique: true | ||
| 39 | add_index :users, :email, unique: true | ||
| 40 | add_index :users, :reset_password_token, unique: true | ||
| 41 | # add_index :users, :confirmation_token, unique: true | ||
| 42 | # add_index :users, :unlock_token, unique: true | ||
| 43 | end | ||
| 44 | end | ||
| diff --git a/db/schema.rb b/db/schema.rb index 6eca6ad..b690393 100644 --- a/db/schema.rb +++ b/db/schema.rb | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | # | 10 | # | 
| 11 | # It's strongly recommended that you check this file into your version control system. | 11 | # It's strongly recommended that you check this file into your version control system. | 
| 12 | 12 | ||
| 13 | ActiveRecord::Schema.define(version: 20170625004938) do | 13 | ActiveRecord::Schema.define(version: 20170625151955) do | 
| 14 | 14 | ||
| 15 | create_table "entries", force: :cascade do |t| | 15 | create_table "entries", force: :cascade do |t| | 
| 16 | t.string "title" | 16 | t.string "title" | 
| @@ -31,4 +31,23 @@ ActiveRecord::Schema.define(version: 20170625004938) do | |||
| 31 | t.index ["recordable_type", "recordable_id"], name: "index_records_on_recordable_type_and_recordable_id" | 31 | t.index ["recordable_type", "recordable_id"], name: "index_records_on_recordable_type_and_recordable_id" | 
| 32 | end | 32 | end | 
| 33 | 33 | ||
| 34 | create_table "users", force: :cascade do |t| | ||
| 35 | t.string "login", default: "", null: false | ||
| 36 | t.string "email", default: "", null: false | ||
| 37 | t.string "encrypted_password", default: "", null: false | ||
| 38 | t.string "reset_password_token" | ||
| 39 | t.datetime "reset_password_sent_at" | ||
| 40 | t.datetime "remember_created_at" | ||
| 41 | t.integer "sign_in_count", default: 0, null: false | ||
| 42 | t.datetime "current_sign_in_at" | ||
| 43 | t.datetime "last_sign_in_at" | ||
| 44 | t.string "current_sign_in_ip" | ||
| 45 | t.string "last_sign_in_ip" | ||
| 46 | t.datetime "created_at", null: false | ||
| 47 | t.datetime "updated_at", null: false | ||
| 48 | t.index ["email"], name: "index_users_on_email", unique: true | ||
| 49 | t.index ["login"], name: "index_users_on_login", unique: true | ||
| 50 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true | ||
| 51 | end | ||
| 52 | |||
| 34 | end | 53 | end | 
| diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml new file mode 100644 index 0000000..80aed36 --- /dev/null +++ b/test/fixtures/users.yml | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html | ||
| 2 | |||
| 3 | # This model initially had no columns defined. If you add columns to the | ||
| 4 | # model remove the '{}' from the fixture names and add the columns immediately | ||
| 5 | # below each fixture, per the syntax in the comments below | ||
| 6 | # | ||
| 7 | one: {} | ||
| 8 | # column: value | ||
| 9 | # | ||
| 10 | two: {} | ||
| 11 | # column: value | ||
| diff --git a/test/models/user_test.rb b/test/models/user_test.rb new file mode 100644 index 0000000..82f61e0 --- /dev/null +++ b/test/models/user_test.rb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | require 'test_helper' | ||
| 2 | |||
| 3 | class UserTest < ActiveSupport::TestCase | ||
| 4 | # test "the truth" do | ||
| 5 | # assert true | ||
| 6 | # end | ||
| 7 | end | ||
