commit: 6045b6cb1880b27e8b21799b9501a794a5f5b88b
parent: 7e93da3f8d31041034ba4eece5ee7a2cec5cfd2b
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 5 Mar 2016 22:43:05 +0100
Customizing devise views and controllers
Diffstat:
19 files changed, 398 insertions(+), 17 deletions(-)
diff --git a/app/assets/images/background-photo.jpg b/app/assets/images/background-photo.jpg
Binary files differ.
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
@@ -7,15 +7,73 @@ $darker-background-color: #e3dede;
$text-color: #333030;
$lighter-text-color: #8b8687;
-@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700,400italic");
+@import url(https://fonts.googleapis.com/css?family=Roboto:400,500,400italic);
+@import url(https://fonts.googleapis.com/css?family=Roboto+Mono);
@import "font-awesome-sprockets";
@import "font-awesome";
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+
+body {
+ line-height: 1;
+}
+
+ol, ul {
+ list-style: none;
+}
+
+blockquote, q {
+ quotes: none;
+}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
body {
- font-family: 'Noto Sans', sans-serif;
- background: $background-color image-url('background-pattern.png');
+ font-family: 'Roboto', sans-serif;
+ background: $background-color image-url('background-photo.jpeg');
+ background-size: cover;
font-size: 13px;
line-height: 18px;
+ font-weight: 400;
color: $text-color;
}
@@ -25,19 +83,143 @@ body {
margin-top: 40px;
}
-.footer {
- text-align: center;
- padding: 100px 0;
- font-size: 12px;
- color: $text-color;
+.logo-container {
+ width: 400px;
+ margin: 100px auto;
+ cursor: default;
+
+ h1 {
+ display: block;
+ text-align: center;
+ color: #fff;
+ font-size: 48px;
+ line-height: 48px;
+ font-weight: 500;
+
+ small {
+ display: block;
+ font-size: 12px;
+ font-weight: 400;
+ font-family: 'Roboto Mono', monospace;
+ }
+ }
+}
+
+.form-container {
+ width: 400px;
+ margin: 0 auto;
+
+ .field {
+ margin-bottom: 15px;
+ }
+
+ input[type=text], input[type=email], input[type=password] {
+ background: transparent;
+ border: 0;
+ border-bottom: 2px solid #9baec8;
+ padding: 7px 0;
+ font-size: 16px;
+ color: #fff;
+ display: block;
+ width: 100%;
+ outline: 0;
+
+ &:invalid {
+ box-shadow: none;
+ }
+
+ &:focus:invalid {
+ border-bottom-color: #df405a;
+ }
+
+ &:required:valid {
+ border-bottom-color: #79bd9a;
+ }
+
+ &:active, &:focus {
+ border-bottom-color: #2b90d9;
+ }
+ }
+
+ .field_with_error {
+ input[type=text], input[type=email], input[type=password] {
+ border-bottom-color: #df405a;
+ }
+ }
+
+ .actions {
+ margin-top: 30px;
+
+ button {
+ display: block;
+ width: 100%;
+ border: 0;
+ border-radius: 4px;
+ background: #2b90d9;
+ color: #fff;
+ font-size: 18px;
+ padding: 10px;
+ text-transform: uppercase;
+ cursor: pointer;
+ font-weight: 500;
+ outline: 0;
+
+ &:hover {
+ background-color: lighten(#2b90d9, 5%);
+ }
- .mastodon-link {
- color: $quaternary-color;
- text-decoration: none;
- font-weight: bold;
+ &:active, &:focus {
+ position: relative;
+ top: 1px;
+ background-color: darken(#2b90d9, 5%);
+ }
+ }
+ }
+
+ .form-footer {
+ margin-top: 30px;
+ text-align: center;
+
+
+ a {
+ color: #9baec8;
+ text-decoration: none;
+
+ &:hover {
+ color: #d9e1e8;
+ text-decoration: underline;
+ }
+ }
+ }
+
+ #error_explanation {
+ background: #282c37;
+ color: #9baec8;
+ border-radius: 4px;
+ padding: 15px 10px;
+ margin-bottom: 30px;
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
+
+ h2 {
+ font-weight: 500;
+ margin-bottom: 5px;
+ }
+
+ li {
+ margin-left: 15px;
+ list-style: circle;
+ }
}
}
+.no-list {
+ list-style: none;
+
+ li {
+ display: inline-block;
+ margin: 0 5px;
+ }
+}
@import 'home';
@import 'accounts';
diff --git a/app/controllers/auth/passwords_controller.rb b/app/controllers/auth/passwords_controller.rb
@@ -0,0 +1,34 @@
+class Auth::PasswordsController < Devise::PasswordsController
+ layout 'auth'
+
+ # GET /resource/password/new
+ # def new
+ # super
+ # end
+
+ # POST /resource/password
+ # def create
+ # super
+ # end
+
+ # GET /resource/password/edit?reset_password_token=abcdef
+ # def edit
+ # super
+ # end
+
+ # PUT /resource/password
+ # def update
+ # super
+ # end
+
+ # protected
+
+ # def after_resetting_password_path_for(resource)
+ # super(resource)
+ # end
+
+ # The path used after sending reset password instructions
+ # def after_sending_reset_password_instructions_path_for(resource_name)
+ # super(resource_name)
+ # end
+end
diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb
@@ -0,0 +1,22 @@
+class Auth::RegistrationsController < Devise::RegistrationsController
+ layout 'auth'
+
+ before_filter :configure_sign_up_params, only: [:create]
+
+ protected
+
+ def build_resource(hash = nil)
+ super(hash)
+ self.resource.build_account if self.resource.account.nil?
+ end
+
+ def configure_sign_up_params
+ devise_parameter_sanitizer.for(:sign_up) do |u|
+ u.permit(:email, :password, :password_confirmation, account_attributes: [:username])
+ end
+ end
+
+ def after_sign_up_path_for(resource)
+ account_path(resource.account)
+ end
+end
diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb
@@ -0,0 +1,27 @@
+class Auth::SessionsController < Devise::SessionsController
+ layout 'auth'
+
+ # before_filter :configure_sign_in_params, only: [:create]
+
+ # GET /resource/sign_in
+ # def new
+ # super
+ # end
+
+ # POST /resource/sign_in
+ # def create
+ # super
+ # end
+
+ # DELETE /resource/sign_out
+ # def destroy
+ # super
+ # end
+
+ # protected
+
+ # If you have extra params to permit, append them to the sanitizer.
+ # def configure_sign_in_params
+ # devise_parameter_sanitizer.for(:sign_in) << :attribute
+ # end
+end
diff --git a/app/models/account.rb b/app/models/account.rb
@@ -1,6 +1,7 @@
class Account < ActiveRecord::Base
# Local users
has_one :user, inverse_of: :account
+ validates :username, uniqueness: { scope: :domain }
# Avatar upload
attr_reader :avatar_remote_url
diff --git a/app/models/user.rb b/app/models/user.rb
@@ -2,6 +2,7 @@ class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
belongs_to :account, inverse_of: :user
+ accepts_nested_attributes_for :account
validates :account, presence: true
end
diff --git a/app/views/auth/mailer/password_change.html.erb b/app/views/auth/mailer/password_change.html.erb
@@ -0,0 +1,3 @@
+<p>Hello <%= @resource.email %>!</p>
+
+<p>We're contacting you to notify you that your password has been changed.</p>
diff --git a/app/views/auth/mailer/reset_password_instructions.html.erb b/app/views/auth/mailer/reset_password_instructions.html.erb
@@ -0,0 +1,8 @@
+<p>Hello <%= @resource.email %>!</p>
+
+<p>Someone has requested a link to change your password. You can do this through the link below.</p>
+
+<p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
+
+<p>If you didn't request this, please ignore this email.</p>
+<p>Your password won't change until you access the link above and create a new one.</p>
diff --git a/app/views/auth/passwords/edit.html.erb b/app/views/auth/passwords/edit.html.erb
@@ -0,0 +1,25 @@
+<h2>Change your password</h2>
+
+<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
+ <%= devise_error_messages! %>
+ <%= f.hidden_field :reset_password_token %>
+
+ <div class="field">
+ <%= f.label :password, "New password" %><br />
+ <% if @minimum_password_length %>
+ <em>(<%= @minimum_password_length %> characters minimum)</em><br />
+ <% end %>
+ <%= f.password_field :password, autofocus: true, autocomplete: "off" %>
+ </div>
+
+ <div class="field">
+ <%= f.label :password_confirmation, "Confirm new password" %><br />
+ <%= f.password_field :password_confirmation, autocomplete: "off" %>
+ </div>
+
+ <div class="actions">
+ <%= f.submit "Change my password" %>
+ </div>
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/auth/passwords/new.html.haml b/app/views/auth/passwords/new.html.haml
@@ -0,0 +1,9 @@
+= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
+ = devise_error_messages!
+
+ .field
+ = f.email_field :email, autofocus: true, required: true, placeholder: 'E-mail address'
+ .actions
+ = f.button "Reset password", type: 'submit'
+
+.form-footer= render "auth/shared/links"
diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml
@@ -0,0 +1,11 @@
+= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f|
+ = devise_error_messages!
+
+ .field
+ = f.password_field :password, autocomplete: "off", placeholder: 'New password'
+ .field
+ = f.password_field :password_confirmation, autocomplete: "off", placeholder: 'Confirm new password'
+ .field
+ = f.password_field :current_password, autocomplete: "off", placeholder: 'Current password'
+ .actions
+ = f.button "Save changes", type: 'submit'
diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml
@@ -0,0 +1,17 @@
+= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
+ = devise_error_messages!
+
+ = f.fields_for :account do |ff|
+ .field
+ = ff.text_field :username, autofocus: true, placeholder: 'Username', required: true
+
+ .field
+ = f.email_field :email, placeholder: 'E-mail address', required: true
+ .field
+ = f.password_field :password, autocomplete: "off", placeholder: 'Password', required: true
+ .field
+ = f.password_field :password_confirmation, autocomplete: "off", placeholder: 'Confirm password', required: true
+ .actions
+ = f.button "Sign up", type: 'submit'
+
+.form-footer= render "auth/shared/links"
diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml
@@ -0,0 +1,9 @@
+= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
+ .field
+ = f.email_field :email, autofocus: true, placeholder: 'E-mail address', required: true
+ .field
+ = f.password_field :password, autocomplete: "off", placeholder: 'Password', required: true
+ .actions
+ = f.button "Log in", type: 'submit'
+
+.form-footer= render "auth/shared/links"
diff --git a/app/views/auth/shared/_links.html.haml b/app/views/auth/shared/_links.html.haml
@@ -0,0 +1,19 @@
+%ul.no-list
+ - if controller_name != 'sessions'
+ %li= link_to "Log in", new_session_path(resource_name)
+
+ - if devise_mapping.registerable? && controller_name != 'registrations'
+ %li= link_to "Sign up", new_registration_path(resource_name)
+
+ - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
+ %li= link_to "Forgot your password?", new_password_path(resource_name)
+
+ - if devise_mapping.confirmable? && controller_name != 'confirmations'
+ %li= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
+
+ - if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
+ %li= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name)
+
+ - if devise_mapping.omniauthable?
+ - resource_class.omniauth_providers.each do |provider|
+ %li= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider)
diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml
@@ -1 +1,3 @@
Mastodon
+
+= link_to 'Logout', destroy_user_session_path, method: :delete
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
@@ -9,7 +9,4 @@
= yield :header_tags
%body
.container
- = yield
- .footer
- Powered by
- = link_to 'Mastodon', 'https://github.com/Gargron/mastodon', class: 'mastodon-link'
+ = content_for?(:content) ? yield(:content) : yield
diff --git a/app/views/layouts/auth.html.haml b/app/views/layouts/auth.html.haml
@@ -0,0 +1,10 @@
+- content_for :content do
+ .logo-container
+ %h1
+ Mastodon
+ %small= Rails.configuration.x.local_domain
+
+ .form-container
+ = yield
+
+= render template: "layouts/application"
diff --git a/config/routes.rb b/config/routes.rb
@@ -2,7 +2,11 @@ Rails.application.routes.draw do
get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta
get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger
- devise_for :users, path: 'auth'
+ devise_for :users, path: 'auth', controllers: {
+ sessions: 'auth/sessions',
+ registrations: 'auth/registrations',
+ passwords: 'auth/passwords'
+ }
resources :accounts, path: 'users', only: [:show], param: :username do
resources :stream_entries, path: 'updates', only: [:show]