2019年12月26日木曜日

FileMaker 標準機能で Google Calendar 追加・編集・削除

FileMaker 標準機能で Google Calendar 追加・編集・削除
Google Calendar API v3

動作検証
Windows 10
FileMaker 16,17,18

Mac OS 10.13.3
FileMaker Pro 18

iOS13
FileMaker Go 18

有料サンプル
ご支援頂ける場合、ご購入頂けると幸いです。
https://fm-aid.stores.jp/items/5e037b41d790db3198885824
Q&A 不明な点は以下で問い合わせ下さい。
https://fm-aid.com/community/viewforum.php?id=6

準備
Google API プロジェクトの作成(2019.12.2x現在)
Google Developer Console にアクセス。
https://console.developers.google.com/

■プロジェクトの作成
(1)の部分をクリック。
 ※プロジェクトがない場合、[プロジェクトの選択]と表示されています。
(2)「新しいプロジェクト」をクリック

任意のプロジェクト名を設定し、[作成]をクリック

作成した「プロジェクト」を選択

■ライブラリ
「ライブラリ」をクリック

「Calendar」で検索
「Google Calendar API」を選択

[有効にする]をクリック

■認証情報
[認証情報]をクリック

[同意画面を構成]をクリック

[外部]を選択
[作成]をクリック

「アプリケーション名」を設定し、
[保存]をクリック


OAuth 同意画面 2021/04/05 追記

[アプリを公開]をクリック 
[確認]をクリック 2021/04/05 追記

公開ステータス が、「本番環境」でエラーとなる場合、
 テスト環境へ戻してみてください。 2022/03/07 追記
 テスト環境でエラーとなる場合、テストユーザーを追加してください。2022/05/11 追記
 (FileMakerで「[Get_AuthorizationCode]ボタンをクリック」したとき)

(2022/11/16 追記)
OAuth 同意画面
公開ステータス:本番環境 に設定



[認証情報]をクリック
[認証情報を作成]をクリック
[OAuth クライアント ID]をクリック

[その他]を選択
[作成]をクリック

「クライアント ID」
「クライアント シークレット」をコピーし、控えておく


■FileMaker(2022/11/16 追記)
ファイル:GoogleCalendarFM_Lib.fmp12 を修正してください。
スクリプト:Get_AuthorizationCode
redirect_uri=urn:ietf:wg:oauth:2.0:oob を変更
redirect_uri=http://localhost
URL を開く [ Let([ ¢URi="https://accounts.google.com/o/oauth2/auth?scope={scope}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id={ClientId}" ]; Substitute ( ¢URi ; ["{scope}" ; AppPermission::Scope] ; ["{ClientId}" ; GoogleApiProject::ClientId] ) ) ] [ ダイアログなし ]
URL を開く [ Let([ ¢URi="https://accounts.google.com/o/oauth2/auth?scope={scope}&redirect_uri=http://localhost&response_type=code&client_id={ClientId}" ]; Substitute ( ¢URi ; ["{scope}" ; AppPermission::Scope] ; ["{ClientId}" ; GoogleApiProject::ClientId] ) ) ] [ ダイアログなし ]

スクリプト:Get_Token
--data redirect_uri=urn:ietf:wg:oauth:2.0:oob を変更
--data redirect_uri=http://localhost
変数を設定 [ $Option; 値:Let( ¢option=" --request POST --data code={Authorization_Code} --data client_id={client_id} --data client_secret={client_secret} --data redirect_uri=urn:ietf:wg:oauth:2.0:oob --data grant_type=authorization_code " ; Substitute ( ¢option ; ["{Authorization_Code}" ; GLOBAL::gAuthorization_Code] ; ["{client_id}" ; GoogleApiProject::ClientId ] ; ["{client_secret}" ; GoogleApiProject::ClientSecret ] ) ) ]
変数を設定 [ $Option; 値:Let( ¢option=" --request POST --data code={Authorization_Code} --data client_id={client_id} --data client_secret={client_secret} --data redirect_uri=http://localhost --data grant_type=authorization_code " ; Substitute ( ¢option ; ["{Authorization_Code}" ; GLOBAL::gAuthorization_Code] ; ["{client_id}" ; GoogleApiProject::ClientId ] ; ["{client_secret}" ; GoogleApiProject::ClientSecret ] ) ) ]
変数を設定 [ $URi; 値:"https://accounts.google.com/o/oauth2/token" ]







■FileMaker
サンプルファイル「GoogleCalendarFM_Lib.fmp12」
レイアウト:GoogleApiProject を開く

「クライアント ID」
「クライアント シークレット」をそれぞれ設定

レイアウト:Application Permission を開く
Title:
(任意) calendar/Googleアカウント の様な入力がおすすめ
例:calendar/xxxxx@gmail.com

FMid_GoogleApiProject:
レイアウト:GoogleApiProject で作成した APIプロジェクトを選択

Scope:
https://www.googleapis.com/auth/calendar


[Get_AuthorizationCode]ボタンをクリック

ブラウザが開きます
操作するカレンダーのGoogleアカウントでログインor選択


[詳細]をクリック

[アプリ名(安全ではないページ)に移動]をクリック

[許可]をクリック

[許可]をクリック
コードをコピー
(2022/11/16 追記)
クエリパラメータ code= をコピー




FileMaker に戻り、
サンプルファイル「GoogleCalendarFM_Lib.fmp12」
レイアウト:Application Permission を開く

[Get_Token]ボタンをクリック


先程コピーしたコードを貼り付け
[OK]をクリック


「RefreshToken」「AccessToken」が取得できれば準備完了

■FileMakerからGoogle Calendar イベント、追加・編集・削除

サンプルファイル:Example_Calendar.fmp12

フィールド:GoogleCalendarFM_Lib_AppPermission_id
「GoogleCalendarFM_Lib.fmp12」で作成したレコードを設定。

必要な情報を入力して、[Insert][update][get][delete]をクリック



2019年12月10日火曜日

FileMaker 検索条件を保存、保存した検索条件で検索を実行。SearchRequestMaker

FileMaker 検索条件を保存、保存した検索条件で検索を実行。
ファイル:SearchRequestMaker 一つで、複数のファイル・テーブル・レイアウトに対応。


Windows 10
FileMaker 16,17,18

Mac OS 10.13.3
FileMaker Pro 18

iOS13
FileMaker Go 18

完成品 ※Ver.2.0になっています。

ご支援頂ける場合、ご購入頂けると幸いです。
SearchRequestMaker.zip(500円)
https://fm-aid.stores.jp/items/5defaa7c7d31c9050a865abb
Ver.2.0
https://qbxxdp.blogspot.com/2020/01/filemaker-searchrequestmaker-ver20.html

作成方法

 テーブル:          



フィールド(テーブル:SearchRequest)

フィールド(テーブル:SpecialValue)
 *フィールド: _k |タイプ: テキスト|式: GET(UUID)

 リレーション:     


 スクリプト:     


-----
スクリプト:Receive_Card(FileName,LayoutName)
変数を設定[$arg; 値: Get(スクリプト引数) ]
変数を設定[$$TargetFileName; 値: GetValue($arg ; 1 ) ]
変数を設定[$LayoutName; 値: GetValue($arg ; 2 ) ]
レイアウト切り替え[「SearchRequest」 (SearchRequest) ]
検索モードに切り替え[]
フィールド設定[SearchRequest:: FileName; "==" & $$TargetFileName ]
フィールド設定[SearchRequest:: LayoutName; "==" & $LayoutName ]
エラー処理[オン]
検索実行[]
エラー処理[オフ]
新規ウインドウ[スタイル: カード; 名前: "Card_SearchRequest"; 使用するレイアウト: <現在のレイアウト>; 閉じる: はい; 最小化: いいえ; 最大化: いいえ; サイズ変更: いいえ; メニューバー: いいえ; 親ウインドウを淡色表示: はい; ツールバー: いいえ ]
-----
スクリプト: Receive_AddEditSearchRequest(JSON)
変数を設定[$json; 値: Get(スクリプト引数) ]
変数を設定[$FileName; 値: JSONGetElement($json; "file") ]
変数を設定[$LayoutName; 値: JSONGetElement($json; "layout") ]
変数を設定[$_kSearchRequest; 値: JSONGetElement($json; "_kSearchRequest") ]
If[not IsEmpty($_kSearchRequest) ]
検索モードに切り替え[]
フィールド設定[SearchRequest:: _k; "==" & $_kSearchRequest ]
エラー処理[オン]
検索実行[]
エラー処理[オフ]
Else
新規レコード / 検索条件
変数を設定[$_kSearchRequest; 値: SearchRequest:: _k ]
End If
フィールド設定[SearchRequest:: FileName; $FileName ]
フィールド設定[SearchRequest:: LayoutName; $LayoutName ]
フィールド設定[SearchRequest:: JSON; JSONDeleteElement($json ; "_kSearchRequest") ]
フィールドへ移動[]
スクリプト実行[「Receive_Card(FileName, LayoutName) 」; 引数: $FileName & ¶ & $LayoutName ]
レコード / 検索条件 / ページへ移動[最初の]
Loop
Exit Loop If[$_kSearchRequest = SearchRequest:: _k ]
レコード / 検索条件 / ページへ移動[次の; 最後まできたら終了 ]
End Loop
フィールドへ移動[SearchRequest:: Title ]
-----
スクリプト: Fire_Search
If[SearchRequest:: gPauseSearch = 1 ]
変数を設定[$json; 値: JSONSetElement(SearchRequest:: JSON ; "PauseSearch"; "1"; JSONString ) ]
Else
変数を設定[$json; 値: SearchRequest:: JSON ]
End If
#SpecialValue
スクリプト実行[「_SpecialValue(json) 」; 引数: $json ]
変数を設定[$json; 値: Get(スクリプトの結果) ]
変数を設定[$URi; 値: Let([_URi = "fmp://$/{FileName}?script=SetSearchRequestFromJSON&$json={json}"]; Substitute(_URi ;["{FileName}" ; SearchRequest:: FileName];["{json}" ; GetAsURLEncoded($json)] ) ) ]
URL を開く[$URi][ダイアログなし]
変数を設定[$WindowStyle; 値: Get(ウインドウスタイル) /* 「0」(ゼロ) - ウインドウがドキュメントウインドウの場合 「1」- ウインドウがフローティングドキュメントウインドウの場合 「2」- ウインドウがダイアログウインドウの場合 「3」- ウインドウがカードの場合 */ ]
If[$WindowStyle = 3]
ウインドウを閉じる[現在のウインドウ]
End If
-----
スクリプト: _SpecialValue(json)
変数を設定 [ $json; 値:Get(スクリプト引数) ]
If [ IsEmpty ( $json ) ]
現在のスクリプト終了 [ ]
End If
変数を設定 [ $record_array; 値:JSONGetElement ( $json ; "record" ) ]
変数を設定 [ $record_ListKeys; 値: JSONListKeys ($record_array; "") ]
変数を設定 [ $MAX; 値:ValueCount ( $record_ListKeys ) ]
Loop
Exit Loop If [ Let([$n=$n+1];$n>$MAX) ]
変数を設定 [ $record_Key; 値:GetValue ( $record_ListKeys ; $n ) ]
変数を設定 [ $record; 値:JSONGetElement ($record_array ; $record_Key) ]
変数を設定 [ $record_data; 値:JSONGetElement ($record; "data") ]
変数を設定 [ $field_ListKeys; 値: JSONListKeys ($record_data; "") ]
変数を設定 [ $MAX2; 値:ValueCount ( $field_ListKeys ) ]
変数を設定 [ $k; 値:0 ]
Loop
Exit Loop If [ Let([$k=$k+1];$k>$MAX2) ]
変数を設定 [ $field_Key; 値:GetValue ( $field_ListKeys ; $k ) ]
変数を設定 [ $TEXT; 値:JSONGetElement ( $record_data ; $field_Key ) ]
変数を設定 [ $val; 値:Substitute ( $TEXT 
; [GetNthRecord ( SpecialValue::SpecialValue ; 1 ) ; Evaluate (GetNthRecord (SpecialValue::Formula ; 1 ))] 
; [GetNthRecord ( SpecialValue::SpecialValue ; 2 ) ; Evaluate (GetNthRecord (SpecialValue::Formula ; 2 ))] 
; [GetNthRecord ( SpecialValue::SpecialValue ; 3 ) ; Evaluate (GetNthRecord (SpecialValue::Formula ; 3 ))]
; [GetNthRecord ( SpecialValue::SpecialValue ; 4 ) ; Evaluate (GetNthRecord (SpecialValue::Formula ; 4 ))] 
; [GetNthRecord ( SpecialValue::SpecialValue ; 5 ) ; Evaluate (GetNthRecord (SpecialValue::Formula ; 5 ))] 
; [GetNthRecord ( SpecialValue::SpecialValue ; 6 ) ; Evaluate (GetNthRecord (SpecialValue::Formula ; 6 ))] 
.....
; [GetNthRecord ( SpecialValue::SpecialValue ; 256 ) ; Evaluate (GetNthRecord (SpecialValue::Formula ; 256 ))] ) ]
変数を設定 [ $json; 値:JSONSetElement ( $json ; "record[" & $record_Key & "].data." & $field_Key ; $val ; JSONString ) ]
End Loop
End Loop
現在のスクリプト終了 [ 結果: $json ]
-----
スクリプト: Fire_EditSearchRequest
#SearchRequest
変数を設定 [ $URi; 値:Let([ _URi= "fmp://$/{FileName}?script=GetSearchRequestAsJSON&$_kSearchRequest={_kSearchRequest}" ];
Substitute ( _URi ; ["{FileName}" ; SearchRequest::FileName] ; ["{_kSearchRequest}" ; SearchRequest::_k] ) ) ]
URL を開く [ $URi ] [ ダイアログなし ]
-----
スクリプト: Fire_AddSearchRequest
#SearchRequest
If [ IsEmpty ( $$TargetFileName ) ]
カスタムダイアログを表示 [ タイトル: "!"; メッセージ: "対象ファイルが設定されていません。¶" & "[$$TargetFileName=null]"; デフォルトボタン: 「OK」, 確定: 「はい」 ]
現在のスクリプト終了 [ ]
End If
変数を設定 [ $URi; 値:Let([ _URi= "fmp://$/{FileName}?script=GetSearchRequestAsJSON" ]; Substitute ( _URi ; ["{FileName}" ;
$$TargetFileName] ) ) ]
URL を開く [ $URi ] [ ダイアログなし ]
-----
スクリプト: Close_Card
ウインドウを閉じる [ 現在のウインドウ ]
全スクリプト終了
-----
スクリプト: OnLastWindowClose
フィールド設定 [ SearchRequest::gPauseSearch; "" ]
フィールドへ移動 [ ]
────────────────────────────────────────

対象ファイルで使用するスクリプト


スクリプト: GetSearchRequestAsJSON
変数を設定 [ $WindowMode; 値:Get (ウインドウモード) /* 「0」- ブラウズモード 「1」- 検索モード 「2」- プレビューモード 「3」-印刷中の場合 */ ]
If [ $WindowMode<>1 ]
カスタムダイアログを表示 [ タイトル: "!"; メッセージ: "ERROR:検索モードでは有りません。"; デフォルトボタン: 「OK」, 確定: 「はい」 ]
現在のスクリプト終了 [ 結果: "ERROR:検索モードでは有りません。" ]
End If
変数を設定 [ $TableName; 値:Get ( レイアウトテーブル名 ) ]
変数を設定 [ $FieldNameList; 値:Let ( [ $query = " SELECT TableName ,FieldName FROM FILEMAKER_FIELDS WHERE TableName = ? AND FieldClass = 'Normal' AND FieldType NOT LIKE 'global%' /*除外 グローバルフィールド*/ AND FieldType <> 'binary' /*除外 オブジェクトフィールド*/ " ]; ExecuteSQL ( $query ; "::" ; "";$TableName ) ) ]
変数を設定 [ $FieldNameList; 値:FieldNames (Get ( ファイル名 ) ; Get ( レイアウト名 )) ]
レコード/検索条件/ページへ移動 [ 最初の ]
変数を設定 [ $i; 値:0 ]
Loop
変数を設定 [ $MAX; 値:ValueCount ( $FieldNameList ) ]
変数を設定 [ $n; 値:0 ]
変数を設定 [ $record; 値:"" ]
#2019/12/13|$json_d 初期化を追加
変数を設定 [ $json_d; 値:"" ]
Loop
Exit Loop If [ Let([$n=$n+1]; $n>$MAX) ]
変数を設定 [ $FieldName; 値:GetValue ($FieldNameList ; $n ) ]
変数を設定 [ $val; 値:Evaluate ( $FieldName ) ]
#2019/12/13|If [ not IsEmpty ( $val ) ]を追加
If [ not IsEmpty ( $val ) ]
#$FieldName内の[]は、()に置換
変数を設定 [ $json_d; 値:Let([ _val=$val ;_key=Substitute ( $FieldName ; ["[" ; "("] ; ["]" ; ")"]) ]; JSONSetElement ( $json_d; _key ; _val ; JSONString ) ) ]
End If
End Loop
変数を設定 [ $RequestOmitState; 値:Get (検索条件除外状態) ]
変数を設定 [ $record; 値:JSONSetElement ($record; "RequestOmitState" ; $RequestOmitState ; JSONString ) ]
変数を設定 [ $record; 値:JSONSetElement ($record; "data" ; $json_d ; JSONObject ) ]
変数を設定 [ $record_array; 値:JSONSetElement ($record_array; "["&$i&"]" ; $record ; JSONObject ) ]
レコード/検索条件/ページへ移動 [ 次の; 最後まできたら終了 ]
変数を設定 [ $i; 値:$i+1 ]
End Loop
変数を設定 [ $json; 値:JSONSetElement ($json ; "file" ; Get ( ファイル名 ) ; JSONString ) ]
変数を設定 [ $json; 値:JSONSetElement ($json ; "table" ; $TableName ; JSONString ) ]
変数を設定 [ $json; 値:JSONSetElement ($json ; "layout" ; Get ( レイアウト名 ) ; JSONString ) ]
変数を設定 [ $json; 値:JSONSetElement ($json ; "record" ;$record_array ; JSONArray ) ]
If [ not IsEmpty ( $_kSearchRequest ) ]
変数を設定 [ $json; 値:JSONSetElement ($json ; "_kSearchRequest" ; $_kSearchRequest ; JSONString ) ]
End If
スクリプト実行 [ 「Receive_AddEditSearchRequest(JSON)」; 引数: $json ]
現在のスクリプト終了 [ 結果: $json ]
-----
スクリプト: SetSearchRequestFromJSON
If [ IsEmpty ( $json ) ]
変数を設定 [ $json; 値:Get(スクリプト引数) ]
End If
If [ IsEmpty ( $json ) ]
現在のスクリプト終了 [ ]
End If
変数を設定 [ $layout; 値:JSONGetElement ( $json ; "layout" ) ]
変数を設定 [ $table; 値:JSONGetElement ( $json ; "table" ) ]
変数を設定 [ $PauseSearch; 値:JSONGetElement ( $json ; "PauseSearch" ) ]
変数を設定 [ $record_array; 値:JSONGetElement ( $json ; "record" ) ]
変数を設定 [ $record_ListKeys; 値: JSONListKeys ($record_array; "") ]
レイアウト切り替え [ $layout ]
検索モードに切り替え [ ]
変数を設定 [ $MAX; 値:ValueCount ( $record_ListKeys ) ]
Loop
Exit Loop If [ Let([$n=$n+1];$n>$MAX) ]
If [ Get ( 対象レコード数 )<$n ] 新規レコード/検索条件 End If レコード/検索条件/ページへ移動 [ $n ] [ ダイアログなし ] 変数を設定 [ $record_Key; 値:GetValue (
    $record_ListKeys ; $n ) ] 変数を設定 [ $record; 値:JSONGetElement ($record_array ; $record_Key) ] 変数を設定 [ $record_data;
    値:JSONGetElement ($record; "data" ) ] 変数を設定 [ $field_ListKeys; 値: JSONListKeys ($record_data; "" ) ] 変数を設定 [
    $RequestOmitState; 値:JSONGetElement ( $record ; "RequestOmitState" ) ] If [ $RequestOmitState ] レコードを対象外に End If
    変数を設定 [ $MAX2; 値:ValueCount ( $field_ListKeys ) ] 変数を設定 [ $k; 値:0 ] Loop Exit Loop If [ Let([$k=$k+1];$k>$MAX2) ]
    変数を設定 [ $field_Key; 値:GetValue ( $field_ListKeys ; $k ) ]
    変数を設定 [ $FieldName; 値:Substitute ( $field_Key ; ["(" ; "]"]; [")" ; ")"] ) ]
    変数を設定 [ $FieldName; 値:If ( PatternCount ( $field_Key ; "::" ) ; $field_Key; $table & "::" & $field_Key) ]
    エラー処理 [ オン ]
    フィールドを名前で設定 [ $FieldName; JSONGetElement ( $record_data ; $field_Key ) ]
    エラー処理 [ オフ ]
    End Loop
    End Loop
    If [ $PauseSearch <> 1 ]
        エラー処理 [ オン ]
        検索実行 [ ]
        エラー処理 [ オフ ]
    End If
現在のスクリプト終了 [ ]
-----

 レイアウト:     

レイアウト名:SearchRequest

レイアウト名:SpecialValue
[+]ボタン:単一ステップ 新規レコード/検索条件
[×]ボタン:単一ステップ レコード/検索条件削除



-----

2019年12月4日水曜日

FileMakerでGoogle Route Mapを表示、Google Maps JavaScript API v3使用

FileMakerでGoogle Route Mapを表示
距離を取得
所要時間を取得
動作検証
Windows 10
FileMaker 13,14,15,16,17,18

Mac OS 10.13.3
FileMaker Pro 18

iOS13
FileMaker Go 18

サンプルファイル
GRouteMap_Limited.zip(無料)管理者権限無し
https://fm-aid.stores.jp/items/5de7b105b080e01d0f22c8d9


有料サンプル
ご支援頂ける場合、ご購入頂けると幸いです。
GRouteMap_Unlimited.zip(500円)管理者権限あり
https://fm-aid.stores.jp/items/5de7b36cd20039790920e1d6


Google Maps JavaScript API
APIキー取得方法は、以下を参照して下さい。
https://qbxxdp.blogspot.com/2019/11/filemakergoogle-mapgoogle-maps.html


Google Route Map を利用するには、「Maps JavaScript API」と「Directions API」を有効にする必要があります。
「APIキー取得方法」ですでに「Maps JavaScript API」は有効になっていると思います。
※Google Route Mapでは「Geocoding API」はいりません。


「API の制限」を設定している場合は、「Directions API」も追加。


APIキーをコピー

FileMaker
[歯車]アイコンをクリック


APIキーを貼り付け

「出発」「目的地」を入力し、[Go]ボタンをクリックでルートマップが表示されます