FullCalendarにGoogle Calendarを複数表示させる

かなり久しぶりの投稿です…(汗)

随分前にサイトへスケジュールカレンダーを表示させるためにFull Calendarを使う手法を投稿したところ、ちょいちょいアクセスがあります。ありがとうございます。…ほんとに役に立ってるかはわかりませんが…

さて、今回は本店 島勇の教室スケジュールを表示させるために、複数のGoogle Calendarを利用して、それぞれの管理を別の人(講師)がしつつ、わかりやすく表示できないかと模索してみました。

島勇では、和紙人形、漆芸、一閑張りの教室を常設しています。
今まではwordpressの「Event Manager」を利用していたのですが、突如カレンダーが巨大化して、それのCSSをいじるのに萎えてしまったため、それならばシステムも変えてもっと簡単にしようと思い立ったのです。

仕様としては

  • 複数のGoogle Calendarを読み込む
  • Google Calendarは個別に管理
  • 教室ごとに色分けして表示
  • 教室の参加申込は別に考える

という感じです。

まずはGoogle Calendarを用意します。
FullCalendarにGoogle Calendarを表示させる時点ですでにGoogleアカウントは持っているとは思いますが、もし持っていなければ作りましょう。そしてデベロッパー登録しましょう。
詳しくは …すみません。検索して頑張って自己解決してください!私も苦労した口で、正直他人に教えられるほど理解してませんので(。>﹏<。)スミマセン

Google CalendarはGoogleアカウントさえ持っていれば誰でも管理者として新しいカレンダーを製作し、公開することができます。
そしてその公開カレンダーは、複数の利用者で管理ができます。
管理権限は

  1. 予定の表示(時間枠のみ。詳細は非表示)
  2. 予定の表示(すべての予定の詳細)
  3. 予定の変更
  4. 変更および共有の管理権限

の4つあります。
1. は公開するなら誰が見るかわからないので、時間割だけ見れればいいという場合ですね。でも使い所がないような気もします。
2. が普通の公開レベルです。開始から終了の時間、内容の詳細は閲覧可能ですが、予定の追加と変更はできません。教室なら生徒さんをこれにしておくのが普通です。
3. は予定の追加や変更ができます。講師をこの権限にしておいて、その教室のスケジュールを書き込んでもらいます。急な追加や休校などになっても、いちいち管理者に連絡しなくても自分で変更できるわけです。
4.言葉がわかりにくいですが、管理者のことです。3. の権限以外に、カレンダーに関わる人の権限を指定して招待することができます。
権限の変更、共有者の削除など、人事的な仕事は管理者にしかできません。なので、この権限は1人にしておくことが普通です。

—- [ 追記 ] —-

実は自分の使い方は、iPhoneのディフォルトカレンダーアプリとGoogle Calendarアプリを連動させていて、ディフォルトカレンダーで予定を追加してました。
この場合、3. 予定の変更 権限では、ディフォルトカレンダーが連動してくれませんでした。(Google Calendarアプリからは通常通り予定の追加、変更が可能でした)
AndroidならGoogle Calendarアプリが標準なのかな?それならば問題ないのですが、iPhoneユーザーで、ディフォルトカレンダーを使いたい人の場合は、4.変更および共有の管理権限 で権限を与える必要があります。
ややこしいですねぇ…

—- [ 追記 終わり ] —-

これら権限について細かくできることやできないことについては、ここでは必要ないので割愛します。ネットで探すとわかりやすいのがいくつもあるので、そっちで調べる方が絶対わかりやすいです(汗)

さて、教室の数だけカレンダーを作ります。
それぞれのカレンダーの管理者は島勇サイトの管理者に設定します。
それぞれのカレンダーに講師を共有者として3.の権限を与えて招待します。
講師の方には、Google Calendarを導入してもらい(PCでもスマホでもどっちでもOK)、招待メールのURLからGoogle Calendarに追加してもらいます。余談ですが、iPhoneの標準カレンダーにGoogle Calendarを読み込むことができ、標準カレンダーにスケジュール追加すればFullCalendarにきちんと反映されます。(要 標準カレンダーとGoogleアカウントの連携)

さて、製作したカレンダーにはそれぞれカレンダーIDが存在します。
カレンダーIDは、PCのGoogle Calendarページ(スマホ等のアプリでは見れません)から、対象のカレンダー名の右にあるメニューから「設定と共有」を選びます。
設定ページの下の方に「カレンダーの統合」があり、そのすぐ下にカレンダーIDが表示されています。

さて、FullCalendarの方の設定を書き換えます。
※ここでの設定ファイルは https://advicehp.com/archives/307 で作ったものを使います。その他の場合では、もしかしたら変数名が違うかもしれません。まあ多分大丈夫。見比べればわかるでしょう…

変更前は

document.addEventListener('DOMContentLoaded', function() {
 var calendarEl = document.getElementById('calendar');
 var calendar = new FullCalendar.Calendar(calendarEl, {
   initialView: 'dayGridMonth',
   // GoogleカレンダーのAPIキーを設定
   googleCalendarApiKey: '---自分のAPIキーを入れてね---',
   // カレンダーIDを設定
   events: '---使用するカレンダーIDを入れてね---',
   // 日本語化
   locale: 'ja',
   // 高さ自動調整
   height: 'auto',
   // ヘッダー部レイアウト
   headerToolbar: {
     left: 'prev,today,next',
     center: 'title',
     right: 'dayGridMonth,listMonth'
   },
   // 24時間表示
   eventTimeFormat: {
     hour: 'numeric',
     minute: '2-digit'
   },
   // 日本語化により各日付の後ろにつく「日」を取る
   dayCellContent: function(e) {
     e.dayNumberText = e.dayNumberText.replace('日', '');
   },
   // ボタン文字列の日本語化
   buttonText: {
     today: '今日',
     month: '月',
     week: '週',
     day: '日',
     list: 'リスト'
   },
   // all-day表示を終日にする
   allDayText: '終日',
   // デフォルトの6週間表示を自動調整
   fixedWeekCount: false,
   // イベントをクリックしたらグーグルカレンダーへ飛ばずアラート表示
   eventClick: function(info) {
     var eventObj = info.event;
     if (eventObj.allDay == false) {
       var daytext = moment(eventObj.start).format('YYYY-MM-DD');
       var starttext = moment(eventObj.start).format('HH:mm');
       var endtext = moment(eventObj.end).format('HH:mm');
       if (eventObj.url) {
         alert( '【'+ daytext +'】\n開店 ' + starttext + ' 〜 閉店 ' + endtext );
       }
       } else {
         alert( '【'+ daytext +'】\n' + eventObj.title);
       } info.jsEvent.preventDefault();
     },
   });
   calendar.render();
});

です。

修正箇所は

  1. 8行目 カレンダーIDの部分
  2. 48行目 アラート表示のところで「開店」とか「閉店」になってるから「開始」「終了」に。まあこれは適宜…

検索して探し出した記事では、カレンダーIDを入れる変数が「events」で配列じゃないから不安だったけど、まあとりあえず真似して作ってみて、動かなかったら考えればいいやって感じで当て込みました。結果、一発でうまくいきました♪

修正後はこんな感じ。

document.addEventListener('DOMContentLoaded', function() {
    var calendarEl = document.getElementById('calendar');
    var calendar = new FullCalendar.Calendar(calendarEl, {
        initialView: 'dayGridMonth',
        // GoogleカレンダーのAPIキーを設定
        googleCalendarApiKey: '---自分のAPIキーを入れてね---',
        // カレンダーIDを設定
        eventSources: [
            {
                // 和紙人形教室
                googleCalendarId: '---対象のAPIキーを入れてね。例では和紙人形教室---',
                className: 'washiningyo'
            },
            {
                // 漆芸教室
                googleCalendarId: '---対象のAPIキーを入れてね。例では漆芸教室---',
                className: 'shitsugei'
            },
            {
                // 一閑張り教室
                googleCalendarId: '---対象のAPIキーを入れてね。例では一閑張り教室---',
                className: 'ikkannbari'
            },
            {
                // ワークショップ(その他の教室)
                googleCalendarId: '---対象のAPIキーを入れてね。例ではワークショップ---',
                className: 'workshop'
            },
        ],            
        // 日本語化
        locale: 'ja',
        // 高さ自動調整
        height: 'auto',
        // ヘッダー部レイアウト
        headerToolbar: {
            left: 'today,prev',
            center: 'title',
            right: 'next,dayGridMonth,listMonth'
        },
        // 24時間表示
        eventTimeFormat: { hour: 'numeric', minute: '2-digit' },
        // 日本語化により各日付の後ろにつく「日」を取る
        dayCellContent: function(e) {
            e.dayNumberText = e.dayNumberText.replace('日', '');
        },
        // ボタン文字列の日本語化
        buttonText: {
            today:    '今日',
            month:    '月',
            week:     '週',
            day:      '日',
            list:       'リスト'
        },
        // all-day表示を終日にする
        allDayText: '終日',
        // デフォルトの6週間表示を自動調整
        fixedWeekCount: false,
        // イベントをクリックしたらグーグルカレンダーへ飛ばずアラート表示
        eventClick: function(info) {
            var eventObj = info.event;
            var daytext = moment(eventObj.start).format('YYYY-MM-DD'); 
            if (eventObj.allDay == false) {
                var starttext = moment(eventObj.start).format('HH:mm'); 
                var endtext = moment(eventObj.end).format('HH:mm'); 
                if (eventObj.url) {
                    alert( '【'+ daytext +'】\n'  + eventObj.title + '\n開始 ' + starttext + ' 〜 終了 ' + endtext );
                }
            } else {
                alert('【'+ daytext +'】\n' + eventObj.title);
            }
            info.jsEvent.preventDefault(); // prevents browser from following link in current tab.
        },
    });
    calendar.render();
});

eventSources: [] の中に各カレンダーを入れ込めば、ひとつのカレンダーに表示できるわけです。
また、classNameに区別しやすい名前をつけておけばCSSも指定しやすくなります。

CSSは

fc-daygrid-event-dot { display: none !important; }
.fc-daygrid-dot-event { padding: 1px 3px !important; }
.washiningyo { background-color: #ff4a04; color: #fff; }
.ikkannbari { background-color: #2196f3; color: #fff; }
.shitsugei { background-color: #4caf50; color: #fff; }
.workshop { background-color: #948db6; color: #fff; }

これをテンプレートのcssファイルに追加します。
上から、各項目の●記号を削除。
次が背景色の塗り範囲を調整。
以下4行はそれぞれの背景色の指定。背景色を濃いめにしたので、文字はいずれも白を指定しました。要素名=className です。

これでサイト管理者がそれぞれの予定を聞いて集めて、いちいち入力する手間が省けます。
なんなら教室の生徒さんは、それぞれのカレンダーを照会して自分のカレンダーに登録もできるので、スケジュールは自動で更新されます。

EventManagerでは、カレンダーから参加申込が受け付けられたのですが、その分入力項目も多く(コピーで省けるとはいえ…大変)頻繁に申し込みがなければちょっと機能過多に感じてしまいます。
今回FullCalendarに変えたことで随分とシンプルになり、より見やすくなったのではないかと思います。
参加申込はContactForm7とかを利用して、極シンプルにしてあります。

実際にはここで稼働しています。

ということで、「FullCalendarにGoogle Calendarを複数表示させる」でした♪
参考になれば幸いです!