ワークフロー承認後にファイル管理へファイルを登録する

目次

tips
補足

サイボウズが提供している「ワークフロー×ファイル管理連携プラグイン」は、この記事で紹介しているカスタマイズと同等の機能を持ち、さらに設定が容易になります。

このプラグインはクラウド版Garoonのみ対応しています。
詳細は、次のページを参照してください。
ワークフロー×ファイル管理連携プラグイン (External link)

はじめに

ワークフロー申請を承認したときのイベント を使い、ワークフローの申請内容をファイル管理に登録します(ファイルを登録する部分はGaroon SOAP APIを使用しています)。

前提条件と注意事項

  • このカスタマイズには、クラウド版Garoonまたはパッケージ版Garoon 4.10以降の環境が必要です。
  • ワークフローJavaScriptカスタマイズは、JavaScriptを適用した後に申請されたワークフローが対象です。
    それ以前に申請されたワークフローには適用されません。
  • 代理承認時にカスタマイズが動作しない不具合を確認しています。(2019/2/21追記)

できること

社内規程のような公文書など、登録する際に承認が必要となるファイルの登録を、承認ワークフローと連携させることができます。
申請時にファイルを複数登録することで、最大5つまで同時に承認を申請できます。

完成イメージ

Garoonのワークフローで承認すると、申請内容がファイル管理に登録されます。

  • 「承認する」ボタンをクリックすると、ワークフローの内容がファイル管理の特定のフォルダーに登録されます。

ファイルの登録にはワークフロー側にJavaScriptファイルを設定しカスタマイズを行います。
また、ファイル管理の登録先フォルダーには承認者による書込み権限が必要となります。

Garoonワークフローの設定手順

ワークフローの項目の内容は、会社によって異なります。
ここでは、サンプルということで、ファイル管理へ登録可能な項目をおおむね網羅した申請フォームにJavaScript/CSSカスタマイズを設定する流れを説明します。

ワークフローの申請フォームを作成する

まずは以下の項目を配置して、ワークフローの申請フォームを作成していきます。
申請フォームの作成方法については、Garoonヘルプ - 申請フォームの作成の流れ クラウド版 (External link) パッケージ版 (External link) を参照してください。

申請フォームは、ファイル管理の項目と対応付けます。それぞれの項目は以下のとおり設定してください。
項目コードは、JavaScriptコード内でそれぞれの項目を指定するための一意の文字列になります。

項目名 項目タイプ 項目コード 必須 備考
ファイル1 ファイル添付 Attach1 最大ファイル数を1に設定します。
タイトル1 文字列(1行) Title1
説明1 文字列(1行) Description1
ファイル2 ファイル添付 Attach2 最大ファイル数を1に設定します。
タイトル2 文字列(1行) Title2
説明2 文字列(1行) Description2
ファイル3 ファイル添付 Attach3 最大ファイル数を1に設定します。
タイトル3 文字列(1行) Title3
説明3 文字列(1行) Description3
ファイル4 ファイル添付 Attach4 最大ファイル数を1に設定します。
タイトル4 文字列(1行) Title4
説明4 文字列(1行) Description4
ファイル5 ファイル添付 Attach5 最大ファイル数を1に設定します。
タイトル5 文字列(1行) Title5
説明5 文字列(1行) Description5

上記のとおり設定が完了したら、土台となる申請フォームの作成は完了です。

JavaScript/CSSファイルを適用する

申請フォームの作成が完了したので、ここから作成した申請フォームにJavaScriptファイルを適用していきます。

適用ファイルの準備

今回はサンプルということで、登録先のフォルダーを固定しています。まずは登録先のフォルダーを決定します。
ファイル管理から、登録したいフォルダーにアクセスします。URLに含まれるhid(フォルダーID)を確認します。のちほどプログラムの書き換えに使いますので、メモしておきましょう。

例:以下のイメージでは、登録するフォルダー「改定資料」にアクセスした際のURLがhttps://{subdomain}.cybozu.com/g/cabinet/index.csp?hid=20のため、フォルダーIDは[20]になります。

次のサンプルコードをエディタにコピーします。
15行目のHIDを先ほどメモしたフォルダーIDに書き換えます。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/**
 * Garoon JavaScript、SOAP APIを使ったサンプルプログラム
 *
 * 「wf_to_doc.js」ファイル
 *
 * Copyright (c) 2024 Cybozu
 *
 * Licensed under the MIT License
 */
(($) => {
  'use strict';
  /**
   * 登録先フォルダID
   */
  const HID = 2;

  /**
   * 共通SOAPコンテンツ
   * ${XXXX}の箇所は実施処理等に合わせて置換して使用
   */
  const SOAP_TEMPLATE =
        '<?xml version="1.0" encoding="UTF-8"?>' +
        '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">' +
          '<soap:Header>' +
            '<Action>${ACTION}</Action>' +
            '<Timestamp>' +
              '<Created>${CREATED}</Created>' +
              '<Expires>2037-08-12T14:45:00Z</Expires>' +
            '</Timestamp>' +
            '<Locale>jp</Locale>' +
          '</soap:Header>' +
          '<soap:Body>' +
            '<${ACTION}>' +
              '${PARAMETERS}' +
            '</${ACTION}>' +
          '</soap:Body>' +
        '</soap:Envelope>';

  /**
   * ドキュメント登録パラメータテンプレート
   * ${XXXX}の箇所は入力値等で置換して使用
   */
  const DOC_ADD_TEMPLATE =
        '<parameters hid="${HID}" name="${FILE_NAME}" title="${TITLE}" description="${DESCRIPTION}">' +
          '<request_token>${REQUEST_TOKEN}</request_token>' +
          '<content xmlns="">' +
            '${FILE_CONTENT}' +
          '</content>' +
        '</parameters>';

  // 文字列をHTMLエスケープ
  const escapeHtml = (str) => {
    return str
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  };

  // エラーハンドリング関数
  const handleError = (error, customMessage) => {
    const errorMessage = customMessage || 'エラーが発生しました';
    console.error(errorMessage, error);
    alert(`ワークフローの承認は正常に完了しましたが、ファイル管理への登録に失敗しました。\n${errorMessage}\n詳細: ${error}`);
  };

  // リクエストトークン取得
  const getRequestToken = () => {
    const defer = $.Deferred();

    // リクエストトークンの取得
    let tokenRequest = SOAP_TEMPLATE;
    tokenRequest = tokenRequest.replace('${PARAMETERS}', '');
    tokenRequest = tokenRequest.split('${ACTION}').join('UtilGetRequestToken');
    tokenRequest = tokenRequest.replace('${CREATED}', luxon.DateTime.utc().startOf('second').toISO({suppressMilliseconds: true}));
    $.ajax({
      type: 'post',
      url: '/g/util_api/util/api.csp',
      cache: false,
      async: false,
      data: tokenRequest
    })
      .then((respForToken) => {
        defer.resolve($(respForToken).find('request_token').text());
      })
      .catch((e) => {
        handleError(e.statusText, 'リクエストトークン取得時にエラーが発生しました');
        defer.reject(e.statusText);
      });
    return defer.promise();
  };
  // 指定した申請情報から添付ファイル情報を取得
  const getRequestFiles = (requestId) => {
    const defer = $.Deferred();
    let request = SOAP_TEMPLATE;
    request = request.replace('${PARAMETERS}',
      '<parameters><application_id>' + requestId + '</application_id></parameters>');
    request = request.split('${ACTION}').join('WorkflowGetReceivedApplicationsById');
    request = request.replace(
      '${CREATED}', luxon.DateTime.utc().startOf('second').toISO({suppressMilliseconds: true}));
    $.ajax({
      type: 'post',
      url: '/g/cbpapi/workflow/api.csp',
      cache: false,
      async: false,
      data: request
    })
      .then((response) => {
        defer.resolve($(response).find('files_item'));
      })
      .catch((e) => {
        handleError(e.statusText, '指定した申請情報からの添付ファイル情報取得時にエラーが発生しました');
        defer.reject(e.statusText);
      });
    return defer.promise();
  };
  // 指定IDのファイルを取得
  const downloadFile = (fileElements) => {
    const defer = $.Deferred();

    let request = SOAP_TEMPLATE;
    request = request.replace('${PARAMETERS}',
      '<parameters file_id="' + fileElements.fileId + '"></parameters>');
    request = request.split('${ACTION}').join('WorkflowFileDownload');
    request = request.replace(
      '${CREATED}', luxon.DateTime.utc().startOf('second').toISO({suppressMilliseconds: true}));
    $.ajax({
      type: 'post',
      url: '/g/cbpapi/workflow/api.csp',
      cache: false,
      async: false,
      data: request
    })
      .then((response) => {
        fileElements.fileContent = $(response).find('content').text();
        defer.resolve(fileElements);
      })
      .catch((e) => {
        handleError(e.statusText, '添付ファイル情報取得時にエラーが発生しました');
        defer.reject(e.statusText);
      });
    return defer.promise();
  };

  // ワークフロー承認イベントで起動する
  // 申請内容をファイル管理に登録する
  garoon.events.on('workflow.request.approve.submit.success', (event) => {
    // 申請内容を取得する
    const request = event.request;

    return getRequestToken().then((requestToken) => {
      const requestId = request.id;
      // 申請 ID から添付されたファイル情報を取得
      return getRequestFiles(requestId).then(($requestFileItem) => {
        const files = {};
        $requestFileItem.each((_index, fileItem) => {
          const itemName = $(fileItem).attr('name');
          const file = $(fileItem).find('file');
          files[itemName] = {
            id: $(file).attr('file_id'),
            name: $(file).attr('name')
          };
        });
        for (let i = 1; i <= 5; i++) {
          const file = files['ファイル' + i];
          if (!file || !file.id) {
            continue;
          }
          // 申請内容から登録内容を取得
          const fileElements = {
            title: request.items['Title' + i].value,
            description: request.items['Description' + i].value,
            fileName: file.name,
            fileId: file.id
          };

          downloadFile(fileElements).then((fileElementsPlus) => {
            const title = fileElementsPlus.title;
            const description = fileElementsPlus.description;
            const fileName = fileElementsPlus.fileName;
            const fileContent = fileElementsPlus.fileContent;

            let docAddParam = DOC_ADD_TEMPLATE;
            docAddParam = docAddParam.replace('${REQUEST_TOKEN}', escapeHtml(requestToken));
            docAddParam = docAddParam.replace('${HID}', HID); // フォルダID
            docAddParam = docAddParam.replace('${TITLE}', title); // タイトル
            docAddParam = docAddParam.replace('${DESCRIPTION}', description); // 説明
            docAddParam = docAddParam.replace('${FILE_NAME}', fileName); // ファイル名
            docAddParam = docAddParam.replace('${FILE_CONTENT}', fileContent); // 添付ファイルのbase64Binary表現

            let docAddRequest = SOAP_TEMPLATE;
            // SOAPパラメータを完成させる
            docAddRequest = docAddRequest.replace('${PARAMETERS}', docAddParam);
            // 実行処理を指定
            docAddRequest = docAddRequest.split('${ACTION}').join('CabinetAddFile');
            docAddRequest = docAddRequest.replace(
              '${CREATED}', luxon.DateTime.utc().startOf('second').toISO({suppressMilliseconds: true}));

            // ファイル管理への登録実行
            $.ajax({
              type: 'post',
              url: '/g/cbpapi/cabinet/api.csp',
              cache: false,
              async: false,
              data: docAddRequest
            })
              .catch((e) => {
                handleError(e.statusText, 'ファイル管理への登録実行時にエラーが発生しました');
                defer.reject(e.statusText);
              });
          });
        }
      });

    });
  });
})(jQuery.noConflict(true));

ファイル名を「wf_to_doc.js」、文字コードを「UTF-8」で保存します。
ファイル名は任意ですが、ファイルの拡張子は「js」にしてください。

tips
補足

ワークフロー申請フォームでは、ユーザを選択する項目タイプがありません。
そのため、今回のサンプルでは、文字列(複数行)項目に対して選択ユーザを追加するカスタマイズを行います。

JavaScript/CSSファイルとして使用するファイルおよびリンクの追加
  1. 「申請フォーム情報」部分の右端にある「JavaScript / CSSによるカスタマイズ」をクリックします。

    申請フォームの詳細画面に「JavaScript / CSSによるカスタマイズ」というリンクが表示されない場合、ワークフローのカスタマイズが許可されていません。
    Garoonヘルプ - ワークフローのカスタマイズを許可する( クラウド版 (External link) パッケージ版 (External link) )を参考に、カスタマイズを許可してください。

  2. [カスタマイズ]項目に「適用する」を選択します。wf_to_doc.jsが使用するjQuery、Luxon、および作成した「wf_to_doc.js」ファイルを追加し、「設定する」をクリックします。

本カスタマイズでは、 Cybozu CDN の以下のライブラリーを使用します。

  • jQuery
    https://js.cybozu.com/jquery/3.6.4/jquery.min.js
  • Luxon
    https://js.cybozu.com/luxon/3.3.0/luxon.min.js

jQueryとLuxonはwf_to_doc.jsより上位に登録してください。

以上ですべての設定は完了です。最初にお見せした完成イメージのとおり、動けば成功です。

おわりに

Garoon APIのカスタマイズサンプル ワークフローとファイル管理との連携方法を紹介しました。
ワークフローの承認実行のタイミングでGaroon内の別アプリにデータを登録することが簡単にできます。

更新履歴

  • 2022年2月1日
    添付ファイルのIDを取得する処理を、workflow.request.approve.submit.successイベントのワークフローオブジェクトを使う方法から、 SOAP API(受信した申請を取得する) を使う方法に変更
information

このTipsは、2024年9月版Garoonで動作を確認しています。