Commit bac18c77 by Mai Hoang Thai Ha

fix some bugs

parent 3a2d621e
// Place all the styles related to the admins controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/
# frozen_string_literal: true
class Admins::ConfirmationsController < Devise::ConfirmationsController
include Accessible
# GET /resource/confirmation/new
# def new
# super
# end
# POST /resource/confirmation
# def create
# super
# end
# GET /resource/confirmation?confirmation_token=abcdef
# def show
# super
# end
# protected
# The path used after resending confirmation instructions.
# def after_resending_confirmation_instructions_path_for(resource_name)
# super(resource_name)
# end
# The path used after confirmation.
# def after_confirmation_path_for(resource_name, resource)
# super(resource_name, resource)
# end
end
# frozen_string_literal: true
class Admins::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# You should configure your model like this:
# devise :omniauthable, omniauth_providers: [:twitter]
# You should also create an action method in this controller like this:
# def twitter
# end
# More info at:
# https://github.com/heartcombo/devise#omniauth
# GET|POST /resource/auth/twitter
# def passthru
# super
# end
# GET|POST /users/auth/twitter/callback
# def failure
# super
# end
# protected
# The path used when OmniAuth fails
# def after_omniauth_failure_path_for(scope)
# super(scope)
# end
end
# frozen_string_literal: true
class Admins::PasswordsController < Devise::PasswordsController
# 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
# frozen_string_literal: true
class Admins::RegistrationsController < Devise::RegistrationsController
include Accessible
skip_before_action :check_user, except: %i[new create]
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
# def new
# super
# end
# POST /resource
# def create
# super
# end
# GET /resource/edit
# def edit
# super
# end
# PUT /resource
# def update
# super
# end
# DELETE /resource
# def destroy
# super
# end
# GET /resource/cancel
# Forces the session data which is usually expired after sign
# in to be expired now. This is useful if the user wants to
# cancel oauth signing in/up in the middle of the process,
# removing all OAuth session data.
# def cancel
# super
# end
# protected
# If you have extra params to permit, append them to the sanitizer.
# def configure_sign_up_params
# devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
# end
# If you have extra params to permit, append them to the sanitizer.
# def configure_account_update_params
# devise_parameter_sanitizer.permit(:account_update, keys: [:attribute])
# end
# The path used after sign up.
# def after_sign_up_path_for(resource)
# super(resource)
# end
# The path used after sign up for inactive accounts.
# def after_inactive_sign_up_path_for(resource)
# super(resource)
# end
end
# frozen_string_literal: true
class Admins::SessionsController < Devise::SessionsController
include Accessible
skip_before_action :check_user, only: :destroy
# before_action :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.permit(:sign_in, keys: [:attribute])
# end
end
# frozen_string_literal: true
class Admins::UnlocksController < Devise::UnlocksController
# GET /resource/unlock/new
# def new
# super
# end
# POST /resource/unlock
# def create
# super
# end
# GET /resource/unlock?unlock_token=abcdef
# def show
# super
# end
# protected
# The path used after sending unlock password instructions
# def after_sending_unlock_instructions_path_for(resource)
# super(resource)
# end
# The path used after unlocking the resource
# def after_unlock_path_for(resource)
# super(resource)
# end
end
class AdminsController < ApplicationController
before_action :authenticate_admin!
def applies
@apply_jobs = ApplyJob.includes(:job, cv_attachment: :blob).all
.page(params[:page]).per(Job::JOB_PER_PAGE)
end
end
module Accessible
extend ActiveSupport::Concern
included do
before_action :check_user
end
protected
def check_user
if current_admin
flash.clear
# if you have rails_admin. You can redirect anywhere really
# redirect_to(rails_admin.dashboard_path) and return
elsif current_user
flash.clear
set_flash_message!(:notice, :signed_in)
# The authenticated root path can be defined in your routes.rb in: devise_scope :user do...
redirect_to(root_path) and return
end
end
end
# frozen_string_literal: true
class Users::ConfirmationsController < Devise::ConfirmationsController
include Accessible
# GET /resource/confirmation/new
# POST /resource/confirmation
......
# frozen_string_literal: true
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
include Accessible
# You should configure your model like this:
# devise :omniauthable, omniauth_providers: [:twitter]
......
# frozen_string_literal: true
class Users::PasswordsController < Devise::PasswordsController
include Accessible
# GET /resource/password/new
# def new
# super
......
# frozen_string_literal: true
class Users::RegistrationsController < Devise::RegistrationsController
include Accessible
skip_before_action :check_user, except: %i[new, create]
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
......
# frozen_string_literal: true
class Users::SessionsController < Devise::SessionsController
include Accessible
skip_before_action :check_user, only: :destroy
# before_action :configure_sign_in_params, only: [:create]
# GET /resource/sign_in
......
# frozen_string_literal: true
class Users::UnlocksController < Devise::UnlocksController
include Accessible
# GET /resource/unlock/new
# def new
# super
......
module AdminsHelper
end
class Admin < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
end
- provide(:title, 'All applies jobs')
.container
h2.my-5.text-center
| All applies jobs
.container
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info @apply_jobs
.page-info.p-2
= paginate @apply_jobs
.container
- @apply_jobs.each do |apply|
.apply-item
.apply-job-title
= link_to apply.job.title, apply.job, class: 'job-title fs-3 text-decoration-none text-reset'
.apply-user-name.text-secondary
p.fw-bold
| Candidate Name:
span.fw-normal.text-dark
= apply.user_name
.apply-cv
p.fw-bold.text-secondary
| Candidate' CV:
- if apply.cv.attached?
span.fw-normal
= link_to apply.cv.filename, rails_blob_path(apply.cv, disposition: "attachment")
- else
span.fw-normal.text-dark
= 'cv file not uploaded yet'
.d-flex.align-items-center.justify-content-between
.apply-user-email
p.fw-bold.text-secondary
| Candidate's Email:
span.fw-normal.text-dark
= apply.email
.apply-date
p.fst-italic.text-secondary
| Applied at:
span.fw-normal.text-dark
= apply.created_at.strftime('%d/%m/%Y')
hr
.container
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info @apply_jobs
.page-info.p-2
= paginate @apply_jobs
<h2>Resend confirmation instructions</h2>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= render "admins/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 "admins/shared/links" %>
<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>
<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 %>
<p>Hello <%= @resource.email %>!</p>
<p>We're contacting you to notify you that your password has been changed.</p>
<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>
<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>
- provide(:title, 'Admin Change password')
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Set your password
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f|
= render 'admins/shared/error_messages', resource: resource
= f.hidden_field :reset_password_token
.field.row.mb-5.form-group
.col-4
= f.label :password, 'New password', class: 'form-label'
.col-8
= f.password_field :password, autofocus: true, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.mb-5.form-group
.col-4
= f.label :password_confirmation, 'Password Confirmation', class: 'form-label'
.col-8
= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.actions.row.justify-content-end
.col-8
= f.submit 'Set my password', class: 'btn btn-primary w-50 my-4', data: { disable_with: false }
javascript:
Validator({
form: '#new_admin',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#admin_password'),
Validator.minLenght('#admin_password', 6, 'Use 6 characters or more for your password'),
Validator.isRequired('#admin_password_confirmation'),
Validator.isConfirmed('#admin_password_confirmation', function() {
return document.querySelector('#new_admin #admin_password').value;
}, 'Those passwords didn’t match.')
]
})
- provide(:title, 'Admin Forgot password')
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Forgot password
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
= render 'admins/shared/error_messages', resource: resource
.field.row.form-group.mb-4
.col-2
= f.label :email, class: 'form-label'
.col-10
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
- if devise_mapping.confirmable? && controller_name != 'confirmations'
p
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: 'text-decoration-none'
.actions.row.justify-content-end
.col-10.d-flex.justify-content-center
= f.submit 'Confirm your email', class: 'btn btn-primary w-75 my-4', data: { disable_with: false }
javascript:
Validator({
form: '#new_admin',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#admin_email'),
Validator.isEmail('#admin_email')
]
})
- provide(:title, 'Admin Edit profile')
.container
.row.justify-content-center
.col-8
h2.text-center.my-5
| Edit
= resource_name.to_s.humanize
= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f|
= render 'admins/shared/error_messages', resource: resource
.field.row.mb-2.form-group
.col-3
= f.label :email, class: 'form-label'
.col-9
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
div
| Currently waiting confirmation for:
= resource.unconfirmed_email
.row.my-4.justify-content-end
.col-9
hr
p
|(leave password blank if you don't want to change it)
.field.row.mb-2.form-group
.col-3
= f.label :password, class: 'form-label'
.col-9
= f.password_field :password, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :password_confirmation, class: 'form-label'
.col-9
= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field
.col-3
= f.label :current_password, class: 'form-label'
.col-9
= f.password_field :current_password, autocomplete: "current-password", class: 'form-control mb-2'
.actions.row.justify-content-end
.col-9
= f.submit 'Done', class: 'btn btn-primary w-50 my-4', data: { disable_with: false }
hr
h3
| Delete my account
p
| Click the button below to delete account
= button_to 'Delete', registration_path(resource_name), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-primary w-100 my-4'
= link_to 'Home', :back
javascript:
Validator({
form: '#edit_admin',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#admin_email'),
Validator.isEmail('#admin_email'),
Validator.isRequired('#admin_name')
]
})
\ No newline at end of file
- provide(:title, 'Admin Sign Up')
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= render 'users/shared/error_messages', resource: resource
.field.row.form-group.mb-4
.col-2
= f.label :email, class: 'form-label'
.col-10
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
.field.row.form-group.mb-4
.col-2
= f.label :password, class: 'form-label'
.col-10
= f.password_field :password, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.form-group.mb-4
.col-2
= f.label :password_confirmation, class: 'form-label'
.col-10
= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.actions
= f.submit 'Sign up', class: 'btn btn-primary w-100 my-5'
javascript:
Validator({
form: '#new_admin',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#admin_email'),
Validator.isEmail('#admin_email'),
Validator.isRequired('#admin_password'),
Validator.isRequired('#admin_password_confirmation')
Validator.isConfirmed('#admin_password_confirmation', function() {
return document.querySelector('#new_admin #admin_password').value;
}, 'Those passwords didn’t match.')
]
})
- provide(:title, 'Log in')
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Log in Admin
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
.field.row.form-group.mb-4
.col-2
= f.label :email, class: 'form-label'
.col-10
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
.field.row.form-group.mb-4
.col-2
= f.label :password, class: 'form-label'
.col-10
= f.password_field :password, autocomplete: 'current-password', class: 'form-control mb-2'
span.form-msg
- if devise_mapping.rememberable?
.field
= f.check_box :remember_me
= f.label :remember_me, class: 'form-label mx-2'
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
= link_to 'Forgot password?', forgot_password_path, class: 'text-decoration-none'
.actions.row.justify-content-end
.col-10.d-flex.justify-content-around
= f.submit 'Log in', class: 'btn btn-primary w-25', data: { disable_with: false }
= link_to 'Sign up', new_registration_path(resource_name), class: 'text-decoration-none w-25 btn btn-secondary'
javascript:
Validator({
form: '#new_admin',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#admin_email'),
Validator.isEmail('#admin_email'),
Validator.isRequired('#admin_password')
]
})
- if resource.errors.any?
#error_explanation.bg_danger
.alert.alert-danger
= I18n.t("errors.messages.not_saved", count: resource.errors.count, resource: resource.class.model_name.human.downcase)
ul.mb-3
- resource.errors.full_messages.each do |message|
li.text-danger
= message
\ No newline at end of file
- if controller_name != 'sessions'
= link_to 'Log in', new_session_path(resource_name), class: 'text-decoration-none'
br/
- if devise_mapping.registerable? && controller_name != 'registrations'
= link_to 'Sign up', new_registration_path(resource_name), class: 'text-decoration-none btn btn-primary'
br/
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
= link_to 'Forgot your password?', new_password_path(resource_name), class: 'text-decoration-none'
br/
- if devise_mapping.confirmable? && controller_name != 'confirmations'
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: 'text-decoration-none'
br/
- 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), class: 'text-decoration-none'
br/
- 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), method: :post, class: 'text-decoration-none'
br/
<h2>Resend unlock instructions</h2>
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
<%= render "admins/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 "admins/shared/links" %>
......@@ -15,8 +15,17 @@ header.navbar.navbar-fixed-top.navbar-inverse
= link_to "Profile", user_profile_path
li
= link_to "Log out", destroy_user_session_path, method: :delete
- elsif admin_signed_in?
li
= link_to "Applies job", admin_applies_jobs_path
li
= link_to "Profile", '#'
li
= link_to "Log out", destroy_admin_session_path, method: :delete
- else
li
= link_to "Admin", admin_login_path
li
= link_to "Log in", login_path
li
= link_to "Sign up", signup_path
\ No newline at end of file
<h2><%= t "devise.invitations.edit.header" %></h2>
<%= form_for(resource, as: resource_name, url: invitation_path(resource_name), html: { method: :put }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<%= f.hidden_field :invitation_token, readonly: true %>
<% if f.object.class.require_password_on_accepting %>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<% end %>
<div class="actions">
<%= f.submit t("devise.invitations.edit.submit_button") %>
</div>
<% end %>
<h2><%= t "devise.invitations.new.header" %></h2>
<%= form_for(resource, as: resource_name, url: invitation_path(resource_name), html: { method: :post }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<% resource.class.invite_key_fields.each do |field| -%>
<div class="field">
<%= f.label field %><br />
<%= f.text_field field %>
</div>
<% end -%>
<div class="actions">
<%= f.submit t("devise.invitations.new.submit_button") %>
</div>
<% end %>
......@@ -3,6 +3,8 @@
en:
devise:
confirmations:
admin_user:
confirmed: "Your ADMIN email address has been successfully confirmed."
confirmed: "Your email address has been successfully confirmed."
send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
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."
......
Rails.application.routes.draw do
devise_for :admins, controllers: {
sessions: 'admins/sessions',
confirmations: 'admins/confirmations',
registrations: 'admins/registrations'
}
devise_scope :admin do
get 'admin/login', to: 'admins/sessions#new', as: :admin_login
end
devise_for :users, controllers: {
sessions: 'users/sessions',
confirmations: 'users/confirmations',
......@@ -27,4 +36,5 @@ Rails.application.routes.draw do
post '/confirm', to: 'applies#confirm', as: :confirm_job
post '/done', to: 'applies#create', as: :done_job
get '/history', to: 'history_jobs#index', as: :history_jobs
get '/admin/applies', to: 'admins#applies', as: :admin_applies_jobs
end
# frozen_string_literal: true
class DeviseCreateAdmins < ActiveRecord::Migration[6.1]
def change
create_table :admins 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.string :current_sign_in_ip
# t.string :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 :admins, :email, unique: true
add_index :admins, :reset_password_token, unique: true
# add_index :admins, :confirmation_token, unique: true
# add_index :admins, :unlock_token, unique: true
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_09_07_064156) do
ActiveRecord::Schema.define(version: 2021_09_14_062751) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name", null: false
......@@ -40,6 +40,22 @@ ActiveRecord::Schema.define(version: 2021_09_07_064156) do
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
create_table "admins", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", 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.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_admins_on_email", unique: true
t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true
end
create_table "apply_jobs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.bigint "job_id", null: false
t.bigint "user_id", null: false
......
require "test_helper"
class AdminsControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
# 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
require "test_helper"
class AdminTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment