2017年2月24日 星期五

FileMaker 實作 Password Field

FileMaker 聽說在 15 之前並沒有密碼欄可以使用,由於工作上使用 13 和 14 兩版,所以並沒辦法確認 15 版是否真的有這個功能,以及是否有任何其他問題。

這邊單就以 15 之前的版本來說, 若要再 15 前的版本使用密碼輸入欄位,必須要用模擬的方式處理。是的別懷疑,要用模擬的, FileMaker 並不像現在常見的程式語言提供這些東西,他比較類似於 Scratch , 很多功能可能都要自己硬刻出來。

我這邊有作出三種版本,可以參考自己需要哪種,文中都會有介紹:

其中前兩種方法不適用於 Web Direct
  1. Web Direct 不支援 onObjectKeyStroke
  2. Web Direct 取得 Get( ActiveSelectionStart ) 有問題,無法計算

※ KeyStroke Trigger 版

特性:動作模式和一般輸入都相同,有考慮到選取文字取代、刪除等功能
優點:可以偵測按下 Enter ,並立刻觸發事件(如登入等等)
缺點:不支援貼上(貼上文字無法被 mask )
原理:

  這個方法的原理是有兩個文字欄位,一個是輸入顯示用,另一個是實際儲存。
  假設輸入欄位稱為 ipt , 實際密碼欄位是 pswd。

  首先設定 ipt 輸入區的 onObjectKeyStroke, Script 取得輸入的字元後計算應該
  放在哪個位置;將這個字元放入 pswd 中應該出現的位置。 再從 pswd 欄位取
  得字元數後,建立 mask 放到 ipt 內,最後 Trigger Script return false 讓輸入按鍵
  事件被吃掉即可。
   
  效果預覽 GIF:

    ########################################################################################
    # 仿製密碼輸入欄位 By darkk6 2017.02.20
    # 參數:
    # 1. 從 KeyStroke Trigger 取得的 Code ( Get ( TriggerKeystroke ) )
    # 2. 使用者輸入欄位完整名稱 tableName::fieldName
    # 3. 密碼實際儲存欄位完整名稱 tableName::fieldName
    # 4. 使用者輸入欄位「物件名稱」
    #
    # Usage : ( in KeyStroke Trigger Script )
    # Perform Script ["仿製密碼輸入_by_darkk6" ; Parameter: ...... ] // 參數參考說明
    # Exit Script [ Result: Get(ScriptResult) ]
    #--------------------------------------------------------------------------------------------------------------------------------
    # 支援度較低:選取文字後刪除或選取文字後取代;支援按下 Enter 立刻觸發自訂動作
    #--------------------------------------------------------------------------------------------------------------------------------
    # 限制一:Web Direct 不支援 onKeyStroke , 無法用於 Web Direct 上
    # 限制二:不支援貼上、剪下、復原等快捷鍵功能
    ########################################################################################
    If [ ValueCount ( Get ( ScriptParameter ) ) ≠ 4 ]
        Show Custom Dialog [ Title: "參數數量不足"; Message: "參數數量必須要四個"; Default Button: “確定”, Commit: “Yes” ]
        Exit Script [ Result: False ]
    End If
    Set Variable [ $IptFieldName; Value:GetValue(Get ( ScriptParameter );2) ]
    Set Variable [ $PswdFieldName; Value:GetValue(Get ( ScriptParameter );3) ]
    Set Variable [ $IptObjectName; Value:GetValue(Get ( ScriptParameter );4) ]
    Set Variable [ $PswdValue; Value:GetField ( $PswdFieldName ) /* 取得目前的密碼欄位資料供下計算用 - by darkk6 */ ]
    #========================
    Set Variable [ $keycode; Value:GetAsNumber ( GetValue(Get ( ScriptParameter );1) ) ]
    Set Variable [ $keychar; Value:Char ( $keycode ) ]
    Set Variable [ $~idx; Value:Get ( ActiveSelectionStart ) ]
    Set Variable [ $~len; Value: Get ( ActiveSelectionSize ) ]
    Set Variable [ $~left; Value:Left ( $PswdValue ; $~idx-1 ) ]
    Set Variable [ $~right; Value:Right( $PswdValue ; Length( $PswdValue) - ( $~idx + $~len ) + 1 ) ]
    If [ $keycode = 10 or $keycode = 13 ]
        Commit Records/Requests [ No dialog ]
        # @TODO 請修改這裡:按下 Enter 要執行的事件
        Show Custom Dialog [ Title: "記得修改這裡"; Message: "按下 Enter 了,可以執行事件或者忽略,密碼為:" & $PswdValue; Default Button: “確定”, Commit: “Yes” ]
        Exit Script [ Result: False ]
    Else If [ $keycode ≥ 32 and $keycode < 127 ]
        # 當按下可接受按鍵 , idx + 1
        Set Field By Name [ $PswdFieldName; $~left & $keychar & $~right ]
        Set Variable [ $~idx; Value:$~idx + 1 ]
    Else If [ $~len > 0 and ( $keycode = 8 or $keycode = 127 ) ]
        # 有選取文字的情況下按下 backspace 或 delete , idx 不變
        Set Field By Name [ $PswdFieldName; $~left & $~right ]
    Else If [ $keycode = 8 ]
        # 沒有選取文字的情況下按下 backspace , idx-1
        Set Field By Name [ $PswdFieldName; Left ( $~left ; Length ( $~left ) -1 ) & $~right ]
        Set Variable [ $~idx; Value:$~idx - 1 ]
    Else If [ $keycode = 127 ]
        # 沒有選取文字的情況下按下 delete , idx 不變
        Set Field By Name [ $~left & Right ( $~right ; Length ( $~right ) -1 ); Left ( $~left ; Length ( $~left ) -1 ) & $~right ]
    Else If [ $keycode = 28 or $keycode = 30 or $keycode = 1 or $keycode = 4 ]
        # 當按下 ←,→,home,end
        Exit Script [ Result: True ]
    Else
        Exit Script [ Result: False ]
    End If
    #---- 建立 Password Mask ----
    Set Variable [ $n; Value:Length( GetField( $PswdFieldName ) ) /* 這裡要重新取得,因為上面有將新的內容寫入了 - by darkk6 */ ]
    Set Variable [ $i; Value:1 ]
    Set Variable [ $mask; Value:"" ]
    Loop
        Exit Loop If [ $i > $n ]
        Set Variable [ $mask; Value:$mask & "*" ]
        Set Variable [ $i; Value:$i+1 ]
    End Loop
    #--------------------------
    Set Field By Name [ $IptFieldName; $mask ]
    Go to Object [ Object Name: $IptObjectName ]
    Set Selection [ Start Position: $~idx ]
    Exit Script [ Result: False ]
---------

※ OnModify Trigger 版

特性:動作模式和一般輸入都相同,有考慮到選取文字取代、刪除等功能
優點:支援貼上,其餘和 KeyStoke 版本相同
缺點:通常會先看到該文字明碼後,再變成遮罩字元
限制:輸入的密碼,不能有遮罩字元(如遮罩字元是 * , 那麼密碼就不能有 *)
原理:

  原理和 onObjectKeyStroke 版本相同,但這邊完全透過游標位置,和抓取是
  否有非遮罩字元出現計算輸入的文字應有的位置。計算稍微複雜,但效果佳。

  由於是透過尋找非遮罩字元,因此密碼中不能包含遮罩字元,因此不建議用「*」
  可以改用全形符號的 「●」,並限制密碼必須是半形文字。

   使用時,一樣在 ipt 欄位設定 trigger , 但這邊是 onObjectModify。

   效果預覽 GIF:
    ########################################################################################
    # 仿製密碼輸入欄位 onModify version By darkk6 2017.02.20
    # 若要用在 WebDirect 上面,要做作一些修改和限制
    #
    # 參數:
    # 1. 使用者輸入欄位完整名稱 tableName::fieldName
    # 2. 密碼實際儲存欄位完整名稱 tableName::fieldName
    # 3. 使用者輸入欄位「物件名稱」
    #
    # Usage : ( in onModify Trigger Script )
    # Perform Script ["仿製密碼輸入_ver.onModify_by_darkk6" ; Parameter: ...... ] // 參數參考說明
    #--------------------------------------------------------------------------------------------------------------------------------
    # 支援度較廣:選取文字後刪除或選取文字後取代。也支援貼上功能
    #--------------------------------------------------------------------------------------------------------------------------------
    # 限制一:計算會尋找非 Mask 字元,因此該使用者密碼不能含有 mask 字元。 因此這裡使用的 mask 字元為 ● 而非 *
    # 限制二:執行 Trigger 需要時間,在網路較慢的情況下輸入太快會造成錯誤 (噴出文字)
    ########################################################################################
    If [ ValueCount ( Get ( ScriptParameter ) ) ≠ 3 ]
        Show Custom Dialog [ Title: "參數數量不足"; Message: "參數數量必須要三個"; Default Button: “確定”, Commit: “Yes” ]
        Exit Script [ Result: False ]
    End If
    Set Variable [ $IptFieldName; Value:GetValue(Get ( ScriptParameter );1) ]
    Set Variable [ $PswdFieldName; Value:GetValue(Get ( ScriptParameter );2) ]
    Set Variable [ $IptObjectName; Value:GetValue(Get ( ScriptParameter );3) ]
    Set Variable [ $IptValue; Value:GetField ( $IptFieldName ) /* 取得目前的輸入欄位資料供底下用 - by darkk6 */ ]
    Set Variable [ $PswdValue; Value:GetField ( $PswdFieldName ) /* 取得目前的密碼欄位資料供下計算用 - by darkk6 */ ]
    #========================
    Set Variable [ $~idx; Value:Get ( ActiveSelectionStart ) ]
    Set Variable [ $found; Value:"" ]
    Set Variable [ $n; Value:Length( $IptValue ) ]
    Set Variable [ $i; Value:1 ]
    Loop
        Exit Loop If [ $i > $n ]
        If [ Middle ( $IptValue ; $i ; 1) ≠ "●" ]
            Set Variable [ $found; Value:$found & Middle ($IptValue ; $i ; 1) ]
        End If
        Set Variable [ $i; Value:$i+1 ]
    End Loop
    #=========== 這是最重要的計算方式 ===========
    If [ IsEmpty ( $found ) ]
        # 沒有輸入文字,是刪除文字觸發的事件
        Set Variable [ $delta; Value:Abs(Length ( $PswdValue ) - Length ( $IptValue )) /* 這邊正常來說不可能出現負數,為避免特殊狀況還是加上 Abs() - by darkk6 */ ]
        Set Variable [ $~left; Value:Left ( $PswdValue; $~idx-1 ) ]
        If [ $delta = 0 ]
            Exit Script [ Result: True /* 沒找到文字的情況下相差 0 代表沒有刪除任何文字,直接結束即可 - by darkk6 */ ]
        Else If [ $delta = 1 /* 沒找到文字的情況下相差 1 代表正常的刪除文字 - by darkk6 */ ]
            Set Variable [ $~right; Value:Right( $PswdValue; Length( $PswdValue) - $~idx ) ]
        Else
            Set Variable [ $~right; Value:Right( $PswdValue; Length( $PswdValue) - $delta - $~idx + 1 ) /* 沒找到文字的情況下相差 >1 代表有選取文字的刪除,要調整計算法 - by darkk6 */ ]
        End If
    Else
        # 有找到輸入文字,是輸入文字的觸發事件
        Set Variable [ $delta; Value:Length ( $PswdValue ) - Length ( $IptValue ) /* 因為有可能是負數,所以不能加 Abs() - by darkk6 */ ]
        Set Variable [ $~left; Value:Left ( $PswdValue; $~idx - Length ( $found ) - 1 ) ]
        Set Variable [ $~right; Value:$found & Right( $PswdValue; Length( $PswdValue) - $~idx + 1 - $delta ) ]
    End If
    #===============================================
    Set Field By Name [ $PswdFieldName; $~left & $~right ]
    #---- 建立 Password Mask ----
    Set Variable [ $n; Value:Length( GetField ( $PswdFieldName ) ) /* 這邊要重新取得,因為有改動過資料 by darkk6 */ ]
    Set Variable [ $i; Value:1 ]
    Set Variable [ $mask; Value:"" ]
    Loop
        Exit Loop If [ $i > $n ]
        Set Variable [ $mask; Value:$mask & "●" ]
        Set Variable [ $i; Value:$i+1 ]
    End Loop
    #--------------------------
    Set Field By Name [ $IptFieldName; $mask ]
    Go to Object [ Object Name: $IptObjectName ]
    Set Selection [ Start Position: $~idx ]
---------

※ Web Inject 版

特性:並非用模擬的方式達成,不會有任何問題
缺點:(無)
限制:只適用於 Web Direct 上,必須搭配網頁
原理:
  
  此方式原理非常簡單,就是透過字體來處理

  先建立一個 「所有文字都是 ● 的 Web Font」,並撰寫 CSS,
  接著透過 Injection 設定密碼輸入區的字體為這個字體,如此一來
  無論怎麼輸入,都是呈現 ●,即可達成目的。
 

  這個方法必須要用到一個同 Domain 的網頁,但由於要使用 Web Direct
  勢必有安裝 IIS,因此完全不需另外架設 Web Server , 直接使用 IIS 即可。


網頁原理:

  在 FileMaker 中建立的 WebViewer 元件會在 Web Direct 中轉換成 iframe,
  因此只要在 iframe 中透過 parent.document 先對上層網頁注入上面提到寫
  好的 CSS 檔案,接著找出輸入密碼區的 DOM,設定  CSS Style 即可。
  至於 inject 的程式碼這邊由於有自己有保留版權,暫不提供。不過原理知道
  了相信應該不算難。


操作:

  除了輸入密碼欄位和其他需要的東西外,再拉一個 WebViewer,位置指向
  準備好的網頁 URL,特別注意這個 WebViewer 不可隱藏,但可以用別的物
  件蓋在上面將他藏起來。 URL 最後要帶密碼欄位在 Web Direct 中的 DOM ID
  這個必須要發布後,透過 WebConsole 去找。

效果預覽 GIF:


CSS
---
@font-face {
    font-family: 'DotOnlyFont';
    src: url('fonts/DotOnlyFont.eot');
    src: url('fonts/DotOnlyFont.eot?#iefix') format('embedded-opentype'),
         url('fonts/DotOnlyFont.woff') format('woff'),
         url('fonts/DotOnlyFont.ttf') format('truetype'),
         url('fonts/DotOnlyFont.svg#DotOnlyFont') format('svg');
    font-weight: normal;
    font-style: normal;
}
.passwd{
 -webkit-text-security:disc !important; 
 font-family: 'DotOnlyFont' !important;
}
---

沒有留言:

張貼留言