以下の追加と修正を実施
authorOHASHI, Norikazu <katz@neko-mori.sakura.ne.jp>
Sun, 21 Apr 2019 14:06:55 +0000 (23:06 +0900)
committerOHASHI, Norikazu <katz@neko-mori.sakura.ne.jp>
Sun, 21 Apr 2019 14:06:55 +0000 (23:06 +0900)
* ユーザ情報編集機能などを追加
* パスワードのフォームチェックをjava-script
* フォームのデザインを修正

12 files changed:
readme.md
sinatra/app/controllers/web_gui.rb
sinatra/app/models/users_db.rb
sinatra/app/views/error.haml
sinatra/app/views/layout.haml
sinatra/app/views/login.haml
sinatra/app/views/main.haml
sinatra/app/views/scss/style.scss
sinatra/app/views/signup.haml
sinatra/app/views/user_edit.haml [new file with mode: 0644]
sinatra/app/views/user_home.haml
sinatra/public/scripts/default.js [new file with mode: 0644]

index 1df7530..6b16fb5 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -28,7 +28,7 @@
         login.haml         # user login view
         logout.haml        # user logout view
         user_home.haml     # user default home view
-        account.haml       # setup account view
+        user_edit.haml     # change user info view
         list.haml          # list of books view
         search.haml        # search books view
         detail.haml        # detail of book parameter view
@@ -56,7 +56,7 @@
 
 | 項番 | カラム名 | 型 | 属性 | 概要 |
 | --: | --- | --- | --- | --- |
-|  1 | isbn | VARCHAR(14) | NOT NULL | ISBNコード |
+|  1 | isbn | VARCHAR(14) | NOT NULL, PRIMARY KEY | ISBNコード |
 |  2 | title | VARCHAR(255) | NOT NULL | 書名 |
 |  3 | volume | INTEGER | | 巻数 | 
 |  4 | author | VARCHAR(127) | | 著者名 |
 |  5 | creat_at | DATETIME | NOT NULL | 登録日時 |
 |  6 | update_at | DATETIME | NOT NULL | 更新日時 |
 注: isdn + user_id で複合キーとする。
+
+
+### 書影管理テーブル (book\_shadows)
+| 項番 | カラム名 | 型 | 属性 | 概要 |
+| --: | --- | --- | --- | --- |
+|  1 | key_hash | VARCHAR(60) | NOT NULL, PRIMARY KEY | SHA125 HASH (アクセスキー) |
+|  2 | isbn | VARCHAR(14) | NOT NULL, UNIQUE | 対象ISBN |
+|  3 | mime_type | VARCHAR(255) | NOT NULL | 画像のMIME TYPE |
+|  4 | shadow | LARGETEXT | NOT NULL | 画像のデータ(BASE64) |
+|  5 | creat_at | DATETIME | NOT NULL | 登録日時 |
+|  6 | update_at | DATETIME | NOT NULL | 更新日時 |
+
+注: 画像をロードした場合の管理用
index 5238d77..7093cea 100644 (file)
@@ -13,27 +13,28 @@ require_relative '../models/users_db'
 class WebGui < Sinatra::Base
 
   # エラーステータス設定
-  # @attr_reader [Integer] status HTTTP レスポンスステータス
+  # @attr_reader [Hash] params エラー処理用パラメータ
   class WebError < StandardError
-    attr_reader :statu
+    attr_reader :param
 
-    # @param [Integer] status HTTP レスポンスステータス
-    def initialize(status)
-      @status = status
+    # @param [Hash] params エラー処理用パラメータ
+    def initialize(params)
+      @params = params
     end
   end
   
   set :root, File.join(File.dirname(__FILE__), '..')
   set :views, Proc.new {File.join(root, "views")}
+  set :public, Proc.new {File.join(root, "../public")}
   set :haml, :format => :html5
   set :environment, :production
   
   use Rack::Session::Cookie,
-      :expire_after => 300,
+      :expire_after => 1200,
       :secret => 'change'
   
   # スタイルシート
-  get '/style.css' do
+  get '/css/style.css' do
     scss :'scss/style'
   end
   
@@ -65,8 +66,7 @@ class WebGui < Sinatra::Base
       session[:userId] = id;
       redirect "/user_home"
     rescue UserAccount::AlreadyInstanceError 
-      raise WebError.new(406),
-            "すでに登録済みのアカウント名が指定されています。"
+      raise WebError.new(status: 406, message: "すでに登録済みのアカウント名が指定されています。")
     end
   end
 
@@ -90,8 +90,7 @@ class WebGui < Sinatra::Base
       redirect "/user_home"
     rescue UserAccount::NotFoundInstanceError,
            UserAccount::AuthenticationError
-      raise WebError.new(401),
-            "認証に失敗しました アカウント、 パスワードを確認してください。"
+      raise WebError.new(status: 401, message: "認証に失敗しました アカウント、 パスワードを確認してください。")
     end
   end
 
@@ -100,16 +99,70 @@ class WebGui < Sinatra::Base
   get '/user_home' do
     id = session[:userId]
     if (id == nil)
-      raize WebError.new(408),
-            "セッション期限切れです。再ログインをしてください。"
+      raize WebError.new(status: 408, message: "セッション期限切れです。再ログインをしてください。")
     end
     user = UserAccount.getUser(id)
     @id = id
     @username = user.full_name
+    @is_addmin = user.isAddmin
     @newest_list = [ "書籍1", "書籍2", "書籍3"]
     haml :user_home
   end
 
+  # ユーザ情報編集ページ
+  # @raise [WebError] セッションの期限切れ
+  # @raise [WebError] ユーザID不正
+  get '/user_edit' do
+    id = session[:userId]
+    if (id == nil)
+      raize WebError.new(status: 408, message: "セッション期限切れです。再ログインをしてください。")
+    end
+    begin
+      user = UserAccount.getUser(id)
+      @id = id
+      @username = user.full_name
+      @account = user.user_name
+      @email = user.email
+    rescue UserAccount::NotFoundInstanceError
+      raize WebError.new(status: 400, message: "該当するユーザが存在しません。")
+    end
+    haml :user_edit
+  end
+
+  # ユーザ情報編集ページ(POST)
+  # @post_param name [String] ログインユーザ名,
+  # @post_param try_pass [String] 旧パスワード
+  # @post_param new_pass [String] 新パスワード
+  # @post_param full_name [String] フルネーム
+  # @post_param email [String] Eメール
+  # @raise [WebError] ユーザ情報編集失敗
+  post '/user_edit' do
+    id = session[:userId]
+    name = params[:name]
+    full_name = params[:full_name]
+    email = params[:email]
+    try_pass = params[:try_pass]
+    new_pass = params[:new_pass]
+    
+    if (id == nil)
+      raise WebError.new(staut: 408, message: "セッション期限切れです。再ログインをしてください。")
+    end
+    begin
+      if (new_pass != "") 
+        check_id = UserAccount.checkPasswd(name, try_pass)
+        if (check_id != id) 
+          raise StandardError, "アカウント名とIDが不正"
+        end
+      end
+      UserAccount.changeUser(id, new_pass, full_name, email)
+      redirect "/user_home"
+    rescue StandardError,
+           UserAccount::NotFoundInstanceError,
+           UserAccount::AuthenticationError
+      raise WebError.new(statu: 400, message: "ユーザ情報の編集に失敗しました。", refs: "/user_edit")
+    end
+  end
+  
   # ログアウトページ
   get '/logout' do
     # 最終ログイン情報登録
@@ -119,8 +172,9 @@ class WebGui < Sinatra::Base
   # エラーページ
   error WebError do
     e = env['sinatra.error']
-    status e.status
-    @error_message = e.message
+    status e.params[:status]
+    @refs = e.params[:refs]
+    @error_message = e.params[:message]
     haml :error
   end
 end
index d4ade87..5d17800 100644 (file)
@@ -13,6 +13,14 @@ ActiveRecord::Base.establish_connection(:development)
 
 # ユーザ情報
 class User < ActiveRecord::Base
+  # ユーザロール
+  #管理者権限
+  ROLE_ADMIN  = 0
+  #一般権限
+  ROLE_NORMAL = 8
+  #パスワード忘れ中
+  ROLE_FORGOT = 10
+  
   # 正規表現によるレコード探索
   # @param [Symbol] key カラム名
   # @param [String] pattern 正規表現パターン
@@ -21,15 +29,18 @@ class User < ActiveRecord::Base
     column = columns_hash[key.to_s].name
     where("`#{table_name}`.`#{column}` REGEXP ?", pattern)
   end
+  
+  # 管理者権限か否か
+  # @return [Boolean] true:  管理者
+  # @return [Boolean] false: 管理者ではない
+  def isAddmin
+    return user_role == ROLE_ADMIN
+  end
 end
 
 # ユーザ管理
 class UserAccount
 
-  # ユーザロール
-  ROLE_ADMIN  = 0  #管理者権限
-  ROLE_NORMAL = 8  #一般権限
-  ROLE_FORGOT = 10 #パスワード忘れ中
   
   # ユーザ認証エラー
   class AuthenticationError < SecurityError
@@ -47,6 +58,19 @@ class UserAccount
   class DbAccessError < StandardError
   end
 
+  # ユーザが管理者であるかをチェック
+  # @param [Integer] id チェック対象ユーザID
+  # @return [Boolean] true:  管理者
+  # @return [Boolean] false: 管理者ではない
+  def self.checkAdmin(id)
+    user = User.find_by(user_id: id)
+    if (user == nil)
+      return false
+    end
+    return user.isAddmin()
+  end
+
+  private_class_method :checkAdmin
     
   # ユーザアカウントを作成
   # @param [String] name ユーザ名
@@ -74,7 +98,7 @@ class UserAccount
     user.email = email
     user.passwd_salt = passwd_salt
     user.passwd_hash = passwd_hash
-    user.user_role = ROLE_NORMAL
+    user.user_role = User::ROLE_NORMAL
     user.create_at = DateTime.now
     user.update_at = DateTime.now
     user.save
@@ -119,4 +143,53 @@ class UserAccount
     end
     return user
   end
+
+  # ユーザ一覧の取得
+  # @param [Integer] id 取得対象ユーザID
+  # @return [Array<User>] 全ユーザ情報の一覧
+  # @raise [AuthenticationError] ユーザ権限不正
+  def self.getUserList(id)
+    if (checkAdmin(id))
+      raise AuthenticationError
+    end
+    return User.all
+  end
+
+  # ユーザ情報の変更
+  # @param [Integer] id 変更対象のユーザID
+  # @param [String] passwd 変更するパスワード (未修正の場合は空文字列)
+  # @param [String] full_name 変更するフルネーム (未修正の場合は空文字列)
+  # @param [String] email 変更するEメールアドレス (未修正の場合は空文字列)
+  def self.changeUser(id, passwd, full_name, email)
+    user = User.find_by(user_id: id)
+    change_f = false
+    if (user == nil) 
+      raise NotFoundInstanceError
+    end
+
+    # パスワード更新
+    if (passwd != "") 
+      user.passwd_hash = BCrypt::Engine.hash_secret(passwd, user.passwd_salt)
+      change_f = true
+    end
+
+    # フルネーム更新
+    if ((full_name != "") && (full_name != user.full_name) )
+      user.full_name = full_name
+      change_f = true
+    end
+
+    # Eメール更新
+    if ((email != "") && (email != user.email))
+      user.email = email
+      change_f = true
+    end
+
+    # 更新内容の適用
+    if (change_f)
+      user.update_at = DateTime.now
+      user.save
+    end
+  end
+
 end
index 06a5929..907acc0 100644 (file)
@@ -1,6 +1,6 @@
 - # encoding: utf-8
 - error_message =  @error_message
-
+- refs = @refs
 .error
   %h3
     エラーが発生しました。
@@ -8,5 +8,9 @@
     
   .buttoms
     .buttoms__push
-      %a{ :href => '/'}
-        メインにもどる
+      - if refs == nil
+        %a{ :href => '/'}
+          メインにもどる
+      - else
+        %a{ :href => refs}
+          もどる
index 077b8a7..c92384d 100644 (file)
@@ -3,6 +3,7 @@
   - title_name = @username+"書籍"
   - id = @id
   - user_name = @username
+  - is_admin = @is_admin
 - else
   - title_name = "書籍一覧管理"
 !!!
@@ -11,7 +12,8 @@
     %meta{ :charset => 'utf-8'}
     %title
       #{title_name}
-    %link{ :rel => 'stylesheet', :href => '/style.css'}
+    %link{ :rel => 'stylesheet', :href => '/css/style.css'}
+    %script{ :src => '/scripts/default.js'}
   %body
       
     - if  user_name == nil
           %a{ :href => "/book_list" }
             書籍一覧
           %br
-          %a{ :href => "/user_info" }
-            ユーザ情報
+          - if is_admin
+            %a{ :href => "/user_list" }
+              ユーザ情報変更
+          - else
+            %a{ :href => "/user_edit" }
+              ユーザ情報変更
           %br
           %a{ :href => "/logout" }
             ログアウト
index 7a0612e..2a4c72f 100644 (file)
@@ -6,16 +6,21 @@
 %hr
 
 %form{ :action => "/login", :method => "post"}
-  アカウント:
-  %input{ :name => 'name', :size => 10, :maxlength => 10}
-  %br
-
-  パスワード:
-  %input{ :name => 'passwd', :type => 'password', :pattern => '.{6,}' }
-  %br
-
+  .formstyle
+    .params
+      .item 
+        %label{ :for => 'name' }
+          %span
+            アカウント:
+        %input{ :name => 'name', :size => 10, :maxlength => 10, :id => 'name', :required => true}
+      .item
+        %label{ :for => 'passwd' }
+          %span
+            パスワード:
+        %input{ :name => 'passwd', :type => 'password', :id => 'passwd', :pattern => '.{6,}',  :required => true}
   %hr
-
-  %input{ :type => 'submit', :value => '送信'}
-  %input{ :type => 'reset', :value => 'リセット'}
+  
+  .buttons
+    %input{ :type => 'submit', :value => '送信'}
+    %input{ :type => 'button', :onclick =>"location.href='/'", :value => 'もどる' }
   
index 1719528..1089886 100644 (file)
@@ -1,7 +1,7 @@
 - # encoding: utf-8
 
 %h1
-  書籍管理サーバ
+  蔵書管理サーバ
 
 .buttoms
   .buttoms__push
index 89c02ee..aaa3eca 100644 (file)
@@ -63,10 +63,33 @@ ul {
     }
 }
 
-.forms {
+.message {
+    margin-left: 10px;
+    margin-right: 10px;
     text-align: left;
 }
 
+.formstyle {
+    margin: 0, auto;
+    .params {
+        margin-left: 20px;
+        text-align: left;
+        display: block;
+        .item {
+            label {
+                margin-right: 10px;
+                span {
+                    width:220px;
+                    display: inline-block;
+                }
+            }
+        }
+    }
+    .buttoms {
+        display: block;
+    }
+}
+
 .error {
     background: #cd5e3c
 }
index 528d1a4..e7ceb8c 100644 (file)
@@ -9,27 +9,41 @@
 %hr
 
 %form{ :action => "/signup", :method => "post"}
-  .forms
-    %p
-      アカウント:
-      %input{ :name => 'name', :size => 10, :maxlength => 10}
+  .formstyle
+    .params
+      .item
+        %label{ :for => 'name' }
+          %span
+            アカウント:
+        %input{ :name => 'name', :id => 'name', :size => 10, :maxlength => 10, :required => true}
+      
+      .item  
+        %label{ :for => 'full_name' }
+          %span
+            フルネーム:
+        %input{ :name => 'full_name', :id => 'full_name', :size => 30, :maxlength => 127}
+        
+      .item
+        %label{ :for => 'passwd' }
+          %span
+            パスワード:
+        %input{ :name => 'passwd', :type => 'password', :id => 'passwd', :pattern => '.{6,}', :required => true }
 
-    %p
-      フルネーム:
-      %input{ :name => 'full_name', :size => 30, :maxlength => 127}
+      .item
+        %label{ :for => 'passwdConfirm'}
+          %span
+            パスワード(確認用):
+        %input{ :type => 'password', :id => 'passwdConfirm', :pattern => '.{6,}', :required => true, :oninput => 'checkPasswd(this)' }
 
-    %p
-      パスワード:
-      %input{ :name => 'passwd', :type => 'password', :pattern => '.{6,}' }
-    
+      .item
+        %label{ :for => 'email' }
+          %span
+            Eメールアドレス:
+        %input{ :name => 'email', :type => 'email', :id => 'email', :size => 30, :maxlength => 127, :required => true}
       
-    %p
-      Eメールアドレス:
-      %input{ :name => 'email', :type => 'email', :size => 30, :maxlength => 127}
-    
-  %hr
-
-  %input{ :type => 'submit', :value => '送信'}
-  %input{ :type => 'reset', :value => 'リセット'}
+    %hr
+    .buttons
+      %input{ :type => 'submit', :value => '送信'}
+      %input{ :type => 'button', :onclick =>"location.href='/'", :value => 'もどる' }
   
   
diff --git a/sinatra/app/views/user_edit.haml b/sinatra/app/views/user_edit.haml
new file mode 100644 (file)
index 0000000..21ce8cc
--- /dev/null
@@ -0,0 +1,60 @@
+- # encoding: utf-8
+- id = @id
+- account = @account
+- username = @username
+- email = @email
+
+%h3
+  #{username} のユーザデータを変更できます。
+
+.message
+  パスワードを変更する際には、現在のパスワードと
+  新しいパスワードを入力してください。
+
+%hr
+
+%form{ :action => "/user_edit", :method => "post"}
+  .formstyle
+    .params
+      .item
+        %label{ :for => 'name' }
+          %span
+            アカウント:
+        %input{ :name => 'name', :id => 'name', :value => account, :readonly => true}
+      
+      .item  
+        %label{ :for => 'full_name' }
+          %span
+            フルネーム:
+        %input{ :name => 'full_name', :id => 'full_name', :size => 30, :value => username, :maxlength => 127}
+        
+      .item
+        %label{ :for => 'old_passwd' }
+          %span
+            現在のパスワード:
+        %input{ :name => 'try_pass', :type => 'password', :id => 'old_passwd'}
+        
+      .item
+        %label{ :for => 'passwd' }
+          %span
+            新しいパスワード:
+        %input{ :name => 'new_pass', :type => 'password', :id => 'passwd', :pattern => '.{6,}', :oninput => 'checkOldPass(this)' }
+
+      .item
+        %label{ :for => 'passwdConfirm'}
+          %span
+            新しいパスワード(確認用):
+        %input{ :type => 'password', :id => 'passwdConfirm', :pattern => '.{6,}', :oninput => 'checkPasswd(this)' }
+
+      .item
+        %label{ :for => 'email' }
+          %span
+            Eメールアドレス:
+        %input{ :name => 'email', :type => 'email', :id => 'email', :size => 30, :maxlength => 127, :value => email }
+      
+    %hr
+    .buttons
+      %input{ :type => 'submit', :value => '送信'}
+      %input{ :type => 'button', :onclick =>"location.href='/user_home'", :value => 'もどる' }
+  
+  
index 9bb1415..fbed01a 100644 (file)
@@ -5,7 +5,7 @@
 %h2
   ここは #{user_name} さんのホームページです
 
-%p
+.message
   あなたの最近登録した本は以下になります。
 
   %ul
diff --git a/sinatra/public/scripts/default.js b/sinatra/public/scripts/default.js
new file mode 100644 (file)
index 0000000..0d7fe2f
--- /dev/null
@@ -0,0 +1,39 @@
+// メールのフォームチェック用
+
+function checkEmail(input){
+  var mail = document.getElementById("email").value; //メールの値を取得
+  var mailConfirm = input.value; //メール確認用の値を取得(引数input)
+
+  // パスワードの一致確認
+  if(mail != mailConfirm){
+    input.setCustomValidity('メールアドレスが一致しません'); // エラーメッセージのセット
+  }else{
+    input.setCustomValidity(''); // エラーメッセージのクリア
+  }
+}
+
+// パスワードのフォームチェック用 (新旧の確認)
+function checkOldPass(input){
+  var old_passwd = document.getElementById("old_passwd").value; //旧パスワードの値を取得
+  var new_passwd = input.value; //新パスワードの値を取得
+
+  // パスワードの一致確認
+  if((old_passwd != "")  && (old_passwd == new_passwd)){
+    input.setCustomValidity('現在のパスワードと新しいパスワードが同じです。'); // エラーメッセージのセット
+  }else{
+    input.setCustomValidity(''); // エラーメッセージのクリア
+  }
+}
+
+// パスワードのフォームチェック用 
+function checkPasswd(input){
+  var passwd = document.getElementById("passwd").value; //パスワードの値を取得
+  var passwdConfirm = input.value; //パスワード(確認用)の値を取得
+
+  // パスワードの一致確認
+  if(passwd != passwdConfirm){
+    input.setCustomValidity('パスワードが一致しません。'); // エラーメッセージのセット
+  }else{
+    input.setCustomValidity(''); // エラーメッセージのクリア
+  }
+}