【続編】Lotus Notes CalendarからGoogle Calendarへイベントを登録

1年ほど前に"Lotus Notes CalendarからGoogle Calendarへイベントを登録する"という記事でコードを書いたんだけど、ときどきリクエストを投げると"Bad Request Error 400"となって帰ってくることがあって困ってました。

今日はちょっと時間があったので、原因究明とコードの改修に挑戦。

今まではGoogle CalendarのDescriptionにあたるフィールド、NotesカレンダーのBody部分を一度空の状態にしてリクエストを投げると成功するので、あとは手作業でコピペしていたため、ここが問題なんだろうということは察しがついてました。

なので原因として推測されるのは
1.開こうとしているURLの長さの問題
2.Body部のURLエンコードの問題
のどちらか、または両方かなと。

まず、1については"Bad Request"となったリクエストのURLの長さを数えてみると720バイトありました。これは特定のスケジュールからだけでなく、複数の"Bad Request"となるスケジュールからも確認できたので、おそらくHTTPで受け付けられるURLの長さは720バイトだろうと判断。

でも720バイトちょうどのリクエストを送っても"Bad Request"となるということは、URLエンコードの問題かなと思って、720バイトから何文字か削ってみたところ、ちゃんと登録画面が開けました。"日本語"という文字をURLエンコードすると"%E6%97%A5%E6%9C%AC%E8%AA%9E"というように"%"+"99"とう形式でエンコードされるのに、"Bad Request"となったURLは"%"で終わっていたり、"%"の後の数字が1桁だったりと、この条件を満たしてませんでした。

上記のことから、
1.開くURLを@Left()をつかって720バイト以内に収める
2.そのURLから@LeftBack()を使って、最後の"%"から左の文字列を@URLOpen()関数に渡す
ということで実現できそうです。

ちなみにIEでのURLの長さの制限は 2,083 文字だそうです。
[IE] URL に使用可能な文字数は最大 2,083 文字

ついでにちょっとだけコードをいじってみました。

このコードをツールバーにボタンを追加して、埋め込んであげればOK。
本当はボタンを押したときに、選択されてる文書のフォームをチェックして、カレンダーの文書じゃなかったらメッセージを返して〜なんてこともしたかったんだけど、めんどくさいのでやめました。

ちなみにLotus Notesは7.0の日本語版、Google Calendarの言語は英語、タイムゾーンは日本(GMT +09:00)で使ってます。

REM {@TimeMerge()で、Googleタイムゾーンにあわせる};
gtimezone := "Z=-18$DO=0";
gStartDateTime := @TimeMerge( StartDateTime; StartDateTime ; gtimezone );
gEndDateTime := @TimeMerge( EndDateTime; EndDateTime ; gtimezone );

REM {月、日、時、分、秒の1桁を@Replace()で2桁に変換するためのリスト};
short := "0" :"1" :"2" : "3" : "4" : "5" : "6" : "7" : "8" : "9";
long := "00" :"01" :"02" :"03" :"04" :"05" :"06" :"07" :"08" :"09";

REM {開始年月日・時刻をyyyymmdd、hhmmss形式にフォーマット};
syear := @Text(@Year(gStartDateTime));
smonth := @Replace(@Text(@Month(gStartDateTime)); short; long);
sday := @Replace(@Text(@Day(gStartDateTime)); short; long);
shour := @Replace(@Text(@Hour(gStartDateTime)); short; long);
sminute := @Replace(@Text(@Minute(gStartDateTime)); short; long);
ssecond := @Replace(@Text(@Second(gStartDateTime)); short; long);

startDate := syear + smonth + sday;
startTime := shour + sminute + ssecond;

REM {終了年月日・時刻をyyyymmdd、hhmmss形式にフォーマット};
eyear := @Text(@Year(gEndDateTime));
emonth := @Replace(@Text(@Month(gEndDateTime)); short; long);
eday := @Replace(@Text(@Day(gEndDateTime)); short; long);
ehour := @Replace(@Text(@Hour(gEndDateTime)); short; long);
eminute := @Replace(@Text(@Minute(gEndDateTime)); short; long);
esecond := @Replace(@Text(@Second(gEndDateTime)); short; long);

endDate := eyear + emonth + eday;
endTime := ehour + eminute + esecond;

REM {SubjectフィールドをURLエンコード};
subject := @URLEncode ("Domino"; Subject);

REM {開始終了日時を作成};
REM {AppointmentTypeがAnniversary、All Day Eventは時間指定なしでリクエスト};
dates := @If(
AppointmentType="1" | AppointmentType="2" ;
startDate + "/" + endDate ;
startDate +"T" + startTime + "Z/" + endDate + "T" + endTime +"Z"
);

REM {リッチテキストのBodyフィールドをテキストに変換してURLエンコード};
details := @URLEncode("Domino"; @Trim(@Text(Body)));

REM {LocationフィールドをURLエンコード};
location := @URLEncode ("Domino"; @Text(Location));

REM {Google CalendarのベースとなるURLを変数にセット};
REM {将来URLが変更したら、ここを修正する};
REM {イベントの公開属性を変えるには &icc=PUBLIC に変更(ここではPRIVATE)};
REM {スケジュールの空き状態を変えるには &trp=FALSE に変更(ここではBusy)};
gcalurl := "https://www.google.com/calendar/event?action=TEMPLATE&icc=PRIVATE&trp=TRUE";

REM {URLを組み立て};
url := gcalurl + "&text=" + subject + "&dates=" + dates + "&location=" + location + "&details=" + details;

REM {URLは720バイト以下で、URLエンコードが途中で切れないように調整};
accessurl := @LeftBack(@Left(url ;720);"%");

REM {@URLOpen()でHTTPSリクエストを作成};
@URLOpen(accessurl; 1);

@All;

ブラウザはFirefoxを使っているので、このボタンから開いた画面の"Submit"ボタンを勝手に押してくれるようなGreasemonkeyスクリプトを書こうと思って、現在あれこれ格闘中です。
それができたら、最後の1アクションが不要になるのに!!!