Add user model through devise. Add some basic layout. Start testing in travis.

This commit is contained in:
2020-08-20 11:01:27 +01:00
parent 1bc17c462a
commit b7b1e2b3fd
45 changed files with 599 additions and 155 deletions

2
.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
## Dependency directories
node_modules/

84
.idea/workspace.xml generated
View File

@@ -1,28 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="d6389a56-ae12-4279-bc1d-76334fb6e4f6" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/app/assets/stylesheets/pages.scss" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/controllers/pages_controller.rb" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/helpers/pages_helper.rb" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/javascript/stylesheets/application.scss" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/views/pages/home.html.erb" afterDir="false" />
<change afterPath="$PROJECT_DIR$/test/controllers/pages_controller_test.rb" afterDir="false" />
<list default="true" id="d6389a56-ae12-4279-bc1d-76334fb6e4f6" name="Default Changelist" comment="First working version with devise+rails_admin">
<change afterPath="$PROJECT_DIR$/.dockerignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.travis.yml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/javascript/stylesheets/bootstrap.scss" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/views/layouts/_content.html.erb" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/views/layouts/_footer.html.erb" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/views/layouts/_top_navigation.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Dockerfile" beforeDir="false" afterPath="$PROJECT_DIR$/Dockerfile" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Gemfile" beforeDir="false" afterPath="$PROJECT_DIR$/Gemfile" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Gemfile.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Gemfile.lock" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/javascript/packs/application.js" beforeDir="false" afterPath="$PROJECT_DIR$/app/javascript/packs/application.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/javascript/stylesheets/application.scss" beforeDir="false" afterPath="$PROJECT_DIR$/app/javascript/stylesheets/application.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/models/admin.rb" beforeDir="false" afterPath="$PROJECT_DIR$/app/models/admin.rb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/devise/confirmations/new.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/devise/confirmations/new.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/devise/passwords/edit.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/devise/passwords/edit.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/devise/passwords/new.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/devise/passwords/new.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/devise/registrations/edit.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/devise/registrations/edit.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/devise/registrations/new.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/devise/registrations/new.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/devise/sessions/new.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/devise/sessions/new.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/devise/shared/_links.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/devise/shared/_links.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/devise/unlocks/new.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/devise/unlocks/new.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/views/layouts/application.html.erb" beforeDir="false" afterPath="$PROJECT_DIR$/app/views/layouts/application.html.erb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config/initializers/assets.rb" beforeDir="false" afterPath="$PROJECT_DIR$/config/initializers/assets.rb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config/initializers/devise.rb" beforeDir="false" afterPath="$PROJECT_DIR$/config/initializers/devise.rb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config/environments/development.rb" beforeDir="false" afterPath="$PROJECT_DIR$/config/environments/development.rb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config/routes.rb" beforeDir="false" afterPath="$PROJECT_DIR$/config/routes.rb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/db/migrate/20200817200957_devise_create_admins.rb" beforeDir="false" afterPath="$PROJECT_DIR$/db/migrate/20200817200957_devise_create_admins.rb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/db/schema.rb" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/db/seeds.rb" beforeDir="false" afterPath="$PROJECT_DIR$/db/seeds.rb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config/webpack/environment.js" beforeDir="false" afterPath="$PROJECT_DIR$/config/webpack/environment.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/db/schema.rb" beforeDir="false" afterPath="$PROJECT_DIR$/db/schema.rb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ss-calculator.iml" beforeDir="false" afterPath="$PROJECT_DIR$/ss-calculator.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/yarn.lock" beforeDir="false" afterPath="$PROJECT_DIR$/yarn.lock" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@@ -37,6 +42,7 @@
<option name="RECENT_TEMPLATES">
<list>
<option value="SCSS File" />
<option value="ERB File" />
</list>
</option>
</component>
@@ -63,20 +69,18 @@
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="project.structure.last.edited" value="Project" />
<property name="project.structure.proportion" value="0.0" />
<property name="project.structure.side.proportion" value="0.0" />
<property name="project.structure.proportion" value="0.15" />
<property name="project.structure.side.proportion" value="0.2" />
<property name="settings.editor.selected.configurable" value="vcs.Git" />
</component>
<component name="RunManager">
<configuration default="true" type="ArquillianTestNG" factoryName="" nameIsGenerated="true">
<configuration default="true" type="ArquillianJUnit" factoryName="" nameIsGenerated="true">
<option name="arquillianRunConfiguration">
<value>
<option name="containerStateName" value="" />
</value>
</option>
<option name="TEST_OBJECT" value="CLASS" />
<properties />
<listeners />
<option name="TEST_OBJECT" value="class" />
<method v="2">
<option name="Make" enabled="true" />
</method>
@@ -98,7 +102,6 @@
<configuration name="docker-compose.yml[db, web]: Compose Deployment" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker">
<deployment type="docker-compose.yml">
<settings>
<option name="commandLineOptions" value="--build" />
<option name="services">
<list>
<option value="db" />
@@ -121,7 +124,8 @@
<option name="presentableId" value="Default" />
<updated>1597686467739</updated>
<workItem from="1597686468787" duration="5768000" />
<workItem from="1597747634273" duration="9261000" />
<workItem from="1597747634273" duration="16339000" />
<workItem from="1597855104277" duration="5953000" />
</task>
<task id="LOCAL-00001" summary="Install rails_admin and devise. Start setting it up.">
<created>1597695591871</created>
@@ -130,7 +134,14 @@
<option name="project" value="LOCAL" />
<updated>1597695591871</updated>
</task>
<option name="localTasksCounter" value="2" />
<task id="LOCAL-00002" summary="First working version with devise+rails_admin">
<created>1597818691547</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1597818691547</updated>
</task>
<option name="localTasksCounter" value="3" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -138,25 +149,30 @@
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Install rails_admin and devise. Start setting it up." />
<option name="LAST_COMMIT_MESSAGE" value="Install rails_admin and devise. Start setting it up." />
<MESSAGE value="First working version with devise+rails_admin" />
<option name="LAST_COMMIT_MESSAGE" value="First working version with devise+rails_admin" />
</component>
<component name="WindowStateProjectService">
<state x="552" y="178" key="#Docker" timestamp="1597687559241">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="552" y="178" key="#Docker/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597687559241" />
<state x="440" y="94" key="#Project_Structure" timestamp="1597761418307">
<state x="440" y="94" key="#Project_Structure" timestamp="1597855191047">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="440" y="94" key="#Project_Structure/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597761418307" />
<state x="414" y="175" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1597761256987">
<state x="440" y="94" key="#Project_Structure/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597855191047" />
<state x="414" y="175" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1597855201875">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="414" y="175" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597761256987" />
<state x="585" y="107" key="CommitChangelistDialog2" timestamp="1597695591093">
<state x="414" y="175" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597855201875" />
<state x="708" y="322" key="#com.intellij.fileTypes.FileTypeChooser" timestamp="1597832335080">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="585" y="107" key="CommitChangelistDialog2/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597695591093" />
<state x="708" y="322" key="#com.intellij.fileTypes.FileTypeChooser/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597832335080" />
<state x="585" y="107" key="CommitChangelistDialog2" timestamp="1597917079584">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="585" y="107" key="CommitChangelistDialog2/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597917079584" />
<state x="798" y="388" key="DockerComposeFileDialog" timestamp="1597687575523">
<screen x="0" y="0" width="1920" height="1040" />
</state>
@@ -169,10 +185,10 @@
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="740" y="275" key="FileChooserDialogImpl/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597687573558" />
<state x="552" y="253" key="Vcs.Push.Dialog.v2" timestamp="1597695595550">
<state x="552" y="253" key="Vcs.Push.Dialog.v2" timestamp="1597818693333">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="552" y="253" key="Vcs.Push.Dialog.v2/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597695595550" />
<state x="552" y="253" key="Vcs.Push.Dialog.v2/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597818693333" />
<state x="832" y="364" key="cloudConfig.SelfSettingsDialog" timestamp="1597761380237">
<screen x="0" y="0" width="1920" height="1040" />
</state>
@@ -181,9 +197,9 @@
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="565" y="130" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597694693345" />
<state x="667" y="253" width="585" height="534" key="find.popup" timestamp="1597750172554">
<state x="667" y="253" width="585" height="534" key="find.popup" timestamp="1597831549088">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="667" y="253" width="585" height="534" key="find.popup/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597750172554" />
<state x="667" y="253" width="585" height="534" key="find.popup/0.0.1920.1040/-1920.0.1740.1040@0.0.1920.1040" timestamp="1597831549088" />
</component>
</project>

5
.travis.yml Normal file
View File

@@ -0,0 +1,5 @@
language: ruby
rvm:
- 2.5
- 2.6
bundler_args: --without production

View File

@@ -58,7 +58,8 @@ gem 'rails_admin', '~> 2.0'
# Auth with devise
gem 'devise', '~> 4.7'
gem 'devise-bootstrap-views', '~> 1.0'
gem 'omniauth-github'
gem 'omniauth-openid'
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'omniauth-facebook'

View File

@@ -85,6 +85,7 @@ GEM
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise-bootstrap-views (1.1.0)
erubi (1.9.0)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
@@ -303,6 +304,7 @@ DEPENDENCIES
byebug
capybara (>= 2.15)
devise (~> 4.7)
devise-bootstrap-views (~> 1.0)
jbuilder (~> 2.7)
listen (~> 3.2)
omniauth-facebook

View File

@@ -8,7 +8,11 @@ require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
global.toastr = require("toastr")
import "../stylesheets/bootstrap"
import "../stylesheets/application"
import 'jquery'
import 'popper.js'
import 'bootstrap'
// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)

View File

@@ -1 +1,6 @@
@import 'toastr'
@import 'toastr';
body {
height: 100%;
padding-top: 5rem;
}

View File

@@ -0,0 +1,2 @@
@import '~bootstrap/scss/bootstrap.scss';

View File

@@ -1,7 +1,6 @@
class Admin < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable,
:lockable, :timeoutable, :trackable, :omniauthable
devise :database_authenticatable, :recoverable, :rememberable, :validatable, :confirmable,
:lockable, :timeoutable, :trackable
end

7
app/models/user.rb Normal file
View File

@@ -0,0 +1,7 @@
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable,
:lockable, :timeoutable, :trackable, :omniauthable
end

View File

@@ -1,16 +1,16 @@
<h2>Resend confirmation instructions</h2>
<h1><%= t('.resend_confirmation_instructions') %></h1>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= bootstrap_devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email), class: 'form-control' %>
</div>
<div class="actions">
<%= f.submit "Resend confirmation instructions" %>
<div class="form-group">
<%= f.submit t('.resend_confirmation_instructions'), class: 'btn btn-primary' %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<%= render 'devise/shared/links' %>

View File

@@ -1,25 +1,26 @@
<h2>Change your password</h2>
<h1><%= t('.change_your_password') %></h1>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= bootstrap_devise_error_messages! %>
<%= f.hidden_field :reset_password_token %>
<div class="field">
<%= f.label :password, "New password" %><br />
<div class="form-group">
<%= f.label :password, t('.new_password') %>
<%= f.password_field :password, autofocus: true, class: 'form-control' %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em><br />
<small class="form-text text-muted"><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></small>
<% end %>
<%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
<div class="form-group">
<%= f.label :password_confirmation, t('.confirm_new_password') %>
<%= f.password_field :password_confirmation, autocomplete: 'off', class: 'form-control' %>
</div>
<div class="actions">
<%= f.submit "Change my password" %>
<div class="form-group">
<%= f.submit t('.change_my_password'), class: 'btn btn-primary' %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<%= render 'devise/shared/links' %>

View File

@@ -1,16 +1,16 @@
<h2>Forgot your password?</h2>
<h1><%= t('.forgot_your_password') %></h1>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= bootstrap_devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
</div>
<div class="actions">
<%= f.submit "Send me reset password instructions" %>
<div class="form-group">
<%= f.submit t('.send_me_reset_password_instructions'), class: 'btn btn-primary' %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<%= render 'devise/shared/links' %>

View File

@@ -1,43 +1,37 @@
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<h1><%= t('.title', resource: resource_name.to_s.humanize) %></h1>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= bootstrap_devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
</div>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %>
<div class="field">
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "new-password" %>
<% if @minimum_password_length %>
<br />
<em><%= @minimum_password_length %> characters minimum</em>
<% end %>
<small class="form-text text-muted"><%= t('.leave_blank_if_you_don_t_want_to_change_it') %></small>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control' %>
</div>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "current-password" %>
<div class="form-group">
<%= f.label :current_password %>
<%= f.password_field :current_password, autocomplete: 'current-password', class: 'form-control' %>
<small class="form-text text-muted"><%= t('.we_need_your_current_password_to_confirm_your_changes') %></small>
</div>
<div class="actions">
<%= f.submit "Update" %>
<div class="form-group">
<%= f.submit t('.update'), class: 'btn btn-primary' %>
</div>
<% end %>
<h3>Cancel my account</h3>
<p><%= t('.unhappy') %>? <%= link_to t('.cancel_my_account'), registration_path(resource_name), data: { confirm: t('.are_you_sure') }, method: :delete %>.</p>
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
<%= link_to "Back", :back %>
<%= link_to t('.back'), :back %>

View File

@@ -1,29 +1,30 @@
<h2>Sign up</h2>
<h1><%= t('.sign_up') %></h1>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= bootstrap_devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
</div>
<div class="field">
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, autocomplete: 'current-password', class: 'form-control' %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
<small class="form-text text-muted"><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></small>
<% end %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, autocomplete: 'current-password', class: 'form-control' %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
<div class="form-group">
<%= f.submit t('.sign_up'), class: 'btn btn-primary' %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<%= render 'devise/shared/links' %>

View File

@@ -1,26 +1,28 @@
<h2>Log in</h2>
<h1><%= t('.sign_in') %></h1>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "current-password" %>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, autocomplete: 'current-password', class: 'form-control' %>
</div>
<% if devise_mapping.rememberable? %>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
<div class="form-group form-check">
<%= f.check_box :remember_me, class: 'form-check-input' %>
<%= f.label :remember_me, class: 'form-check-label' do %>
<%= resource.class.human_attribute_name('remember_me') %>
<% end %>
</div>
<% end %>
<div class="actions">
<%= f.submit "Log in" %>
<div class="form-group">
<%= f.submit t('.sign_in'), class: 'btn btn-primary' %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<%= render 'devise/shared/links' %>

View File

@@ -1,25 +1,27 @@
<%- if controller_name != 'sessions' %>
<%= link_to "Log in", new_session_path(resource_name) %><br />
<% end %>
<div class="form-group">
<%- if controller_name != 'sessions' %>
<%= link_to t(".sign_in"), new_session_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to t(".sign_up"), new_registration_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to t(".forgot_your_password"), new_password_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<%= link_to t('.didn_t_receive_confirmation_instructions'), new_confirmation_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<%= link_to t('.didn_t_receive_unlock_instructions'), new_unlock_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.omniauthable? %>
<%- resource_class.omniauth_providers.each do |provider| %>
<%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %><br />
<% end %>
<% end %>
<%- if devise_mapping.omniauthable? %>
<%- resource_class.omniauth_providers.each do |provider| %>
<%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider) %><br />
<% end -%>
<% end -%>
</div>

View File

@@ -1,16 +1,16 @@
<h2>Resend unlock instructions</h2>
<h1><%= t('.resend_unlock_instructions') %></h1>
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= bootstrap_devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
</div>
<div class="actions">
<%= f.submit "Resend unlock instructions" %>
<div class="form-group">
<%= f.submit t('.resend_unlock_instructions'), class: 'btn btn-primary'%>
</div>
<% end %>
<%= render "devise/shared/links" %>
<%= render 'devise/shared/links' %>

View File

@@ -0,0 +1,7 @@
<main role="main" class="container">
<div class="starter-template text-center">
<%= yield %>
</div>
</main>

View File

View File

@@ -0,0 +1,32 @@
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu" aria-labelledby="dropdown01">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
<button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>

View File

@@ -18,8 +18,10 @@
<% end %>
</script>
<% end %>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
<%= render 'layouts/top_navigation' %>
<%= render 'layouts/content' %>
<%= render 'layouts/footer' %>
</body>
</html>

View File

@@ -0,0 +1,16 @@
<h2>Resend confirmation instructions</h2>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
</div>
<div class="actions">
<%= f.submit "Resend confirmation instructions" %>
</div>
<% end %>
<%= render "users/shared/links" %>

View File

@@ -0,0 +1,5 @@
<p>Welcome <%= @email %>!</p>
<p>You can confirm your account email through the link below:</p>
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>

View File

@@ -0,0 +1,7 @@
<p>Hello <%= @email %>!</p>
<% if @resource.try(:unconfirmed_email?) %>
<p>We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.</p>
<% else %>
<p>We're contacting you to notify you that your email has been changed to <%= @resource.email %>.</p>
<% end %>

View File

@@ -0,0 +1,3 @@
<p>Hello <%= @resource.email %>!</p>
<p>We're contacting you to notify you that your password has been changed.</p>

View File

@@ -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>

View File

@@ -0,0 +1,7 @@
<p>Hello <%= @resource.email %>!</p>
<p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
<p>Click the link below to unlock your account:</p>
<p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %></p>

View File

@@ -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| %>
<%= render "users/shared/error_messages", resource: resource %>
<%= 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: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Change my password" %>
</div>
<% end %>
<%= render "users/shared/links" %>

View File

@@ -0,0 +1,16 @@
<h2>Forgot your password?</h2>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="actions">
<%= f.submit "Send me reset password instructions" %>
</div>
<% end %>
<%= render "users/shared/links" %>

View File

@@ -0,0 +1,43 @@
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div class="field">
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "new-password" %>
<% if @minimum_password_length %>
<br />
<em><%= @minimum_password_length %> characters minimum</em>
<% end %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "current-password" %>
</div>
<div class="actions">
<%= f.submit "Update" %>
</div>
<% end %>
<h3>Cancel my account</h3>
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
<%= link_to "Back", :back %>

View File

@@ -0,0 +1,29 @@
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "users/shared/links" %>

View File

@@ -0,0 +1,26 @@
<h2>Log in</h2>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "current-password" %>
</div>
<% if devise_mapping.rememberable? %>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end %>
<div class="actions">
<%= f.submit "Log in" %>
</div>
<% end %>
<%= render "users/shared/links" %>

View File

@@ -0,0 +1,15 @@
<% if resource.errors.any? %>
<div id="error_explanation">
<h2>
<%= I18n.t("errors.messages.not_saved",
count: resource.errors.count,
resource: resource.class.model_name.human.downcase)
%>
</h2>
<ul>
<% resource.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>

View File

@@ -0,0 +1,25 @@
<%- if controller_name != 'sessions' %>
<%= link_to "Log in", new_session_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.omniauthable? %>
<%- resource_class.omniauth_providers.each do |provider| %>
<%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %><br />
<% end %>
<% end %>

View File

@@ -0,0 +1,16 @@
<h2>Resend unlock instructions</h2>
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="actions">
<%= f.submit "Resend unlock instructions" %>
</div>
<% end %>
<%= render "users/shared/links" %>

View File

@@ -61,4 +61,7 @@ Rails.application.configure do
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
# Reload everything besides classes
config.reload_classes_only_on_change = false
end

View File

@@ -1,4 +1,5 @@
Rails.application.routes.draw do
devise_for :users
get 'pages/home'
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html

View File

@@ -1,3 +1,17 @@
const { environment } = require('@rails/webpacker')
const webpack = require("webpack");
// Add an additional plugin of your choosing : ProvidePlugin
environment.plugins.prepend(
"Provide",
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
jquery: "jquery",
"window.Tether": "tether",
Popper: ["popper.js", "default"] // for Bootstrap 4
})
);
module.exports = environment

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.inet :current_sign_in_ip
# t.inet :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_08_17_200957) do
ActiveRecord::Schema.define(version: 2020_08_19_085432) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -21,10 +21,36 @@ ActiveRecord::Schema.define(version: 2020_08_17_200957) do
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["confirmation_token"], name: "index_admins_on_confirmation_token", unique: true
t.index ["email"], name: "index_admins_on_email", unique: true
t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_admins_on_unlock_token", unique: true
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end

View File

@@ -6,6 +6,9 @@
"@rails/activestorage": "^6.0.0",
"@rails/ujs": "^6.0.0",
"@rails/webpacker": "4.3.0",
"bootstrap": "^4.5.2",
"jquery": "^3.5.1",
"popper.js": "^1.16.1",
"toastr": "^2.1.4",
"turbolinks": "^5.2.0"
},

11
test/fixtures/users.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
# This model initially had no columns defined. If you add columns to the
# model remove the '{}' from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
one: {}
# column: value
#
two: {}
# column: value

7
test/models/user_test.rb Normal file
View File

@@ -0,0 +1,7 @@
require 'test_helper'
class UserTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

View File

@@ -1506,6 +1506,11 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
bootstrap@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.2.tgz#a85c4eda59155f0d71186b6e6ad9b875813779ab"
integrity sha512-vlGn0bcySYl/iV+BGA544JkkZP5LB3jsmkeKLFQakCOwCM3AOk7VkldBz4jrzSe+Z0Ezn99NVXa1o45cQY4R6A==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -3991,7 +3996,7 @@ jest-worker@^25.4.0:
merge-stream "^2.0.0"
supports-color "^7.0.0"
jquery@>=1.12.0:
jquery@>=1.12.0, jquery@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
@@ -5176,6 +5181,11 @@ pnp-webpack-plugin@^1.5.0:
dependencies:
ts-pnp "^1.1.6"
popper.js@^1.16.1:
version "1.16.1"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
portfinder@^1.0.26:
version "1.0.28"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"