Redmineで問い合わせ管理

Redmineで問い合わせ管理

週2でSES契約している常駐先の名刺を作ってもらいました。
肩書きは「CTO」!!

大したことしているわけではないですが、こういう肩書きを名乗らせてもらえるだけの信頼関係を築くことができたのが何より嬉しいですね。

そんな常駐先では問い合わせ対応が一つの課題で、解決手段としてRedmineを導入したので備忘録。

前提

記載した方がよさげな前提は下記の通り

  • OS: CentOS 7系
  • Redmine: 3.4
  • MTA: Postfix 2.6.6
  • 認証が必須
  • 全てのプロジェクトは非公開

問い合わせフロー

構築するフローはこんな感じ

問い合わせフロー

問い合わせフロー

メールからのチケット作成

Redmineの設定

グループの作成

問い合わせ元のメール送信者はRedmineに登録されていないことを前提にしている。

前提でも明示した通り、このRedmineでは認証を必須としていて、全てのプロジェクトは非公開となっているため、問い合わせ元のメールアドレスをRedmineユーザとして登録する必要がある。
(メールの送信元を登録する方法は後述)

問い合わせからの登録は一つのグループにまとめておきたいのでグループを作成する。
なお、ロールは特に設定しなくても良い。

「問い合わせ」グループの作成

メール受信時の設定

「管理」→「設定」から「受信メール」タブを開いて「受信メール用のWebサービスを有効にする」にチェックを入れる。

「APIキー」は後述のサーバーの設定で利用するのでメモしておく。

受信メール設定

受信メール設定

サーバーの設定

受信アカウントの設定

チケット登録用のメールアカウントを設定します。

今回は「suppurt@」にメールが届いたら、「サポート窓口」プロジェクトにチケットが作成されるように設定。

pass: /etc/aliases

support: "|/var/lib/redmine/extra/mail_handler/rdm-mailhandler.rb --url https://redmine.example.com --key exampleapikeyexampleapikey --unknown-user create --no-permission-check --no-notification --no-account-notice --project support --default-group 問い合わせ --tracker サポート --allow-override project"

今回利用しているオプションは下記の通り。

オプション 説明
–url 必須オプション
RedmineサーバのURL
–key 必須オプション
Redmineの受信メール用のAPIキー
–unknown-user 未登録のアドレスからのメールの処理方法
下記を指定可能
* ignore: メールを無視 (デフォルト)
* accept: 匿名ユーザーとして受け付ける
* create: ユーザーアカウントを作成
–no-permission-check 権限のチェックを行わない
–no-notification 新たに作成したユーザーに対してメール通知を行わない
–no-account-notice 新たに作成したユーザーに対してアカウント情報を送信しない
–project 登録先のプロジェクトの識別子
–default-group 作成したユーザーのグループ (デフォルト: なし)
コンマ区切りで複数のグループを指定可能
–tracker チケット登録時のトラッカー
–allow-override ATTRSで指定した属性をメール本文内で指定することを許可
ATTRはコンマ区切りで指定するか、’all’ですべての属性を許可することができる

利用できるオプションは下記サイトに詳しく載っています。
システム管理者向けガイド — 高度な設定 » メールによるチケット登録

Postfixの読み直し

サーバーを設定したらPostfixに設定を読み込ませます。

/etc/aliases直後は下記を実行

newaliases

エイリアス情報をPostfixに読み込ませるためにPostfixを再起動

systemctl restart postfix.service 
# CentOS 6系は /etc/init.d/postfixd restart
# postfix reload だけでもいいっぽい

完了!

以上で設定は完了。

Redmineに登録されていないメールアドレスからsupport@宛にテストメールを送って、チケットが作成されていれば無事に設定されています。

Google Form からのチケット作成

フォームの作成

まずはフォームの作成。

今回は下記のようなフォームを作成しました。

問い合わせフォーム

問い合わせフォーム

スクリプトの作成

RedmineとAPI連携するためにはAPI KEYが必要なので、チケットの作成者となるアカウントを作成しておきAPIアクセスキーを取得しておく。

フォームの編集画面で右上の3点メニューから「スクリプトエディタ」を開きます

スクリプトエディタの軌道

スクリプトエディタの軌道

スクリプトエディタが開くとこんな画面が表示されます。

スクリプトエディタ画面

スクリプトエディタ画面

スクリプトはこんな感じ。

// RedmineのURLをAPIKEY付きで定義
var REDMINE_URL = "https://redmine.example.com/issues.json?key=チケットを作成するユーザのAPIキー";

// チケットを作成するプロジェクトのIDを定義
var PROJECT_ID = 1;

function sendToRedmine(subject, body) {
  var url = REDMINE_URL;
  var data = { "issue":{"project_id": PROJECT_ID, "subject": subject, "description" : body}};
    
  var payload = JSON.stringify(data);
  var options = {
    "method" : "POST",
    "contentType" : "application/json",
    "payload" : payload
  };
  var response = UrlFetchApp.fetch(url, options);
}

function test() {
  sendToRedmine("メールテスト件名","メールテスト本文");
}

function onFormSubmit(e){

  var subject = "";
  var body = "";
  var formData = null; 
  var itemResponse = null;
  var title = ""; 
  var response = null; 

  for (var i = 0; i < itemResponse.length; i++){
    formData = itemResponse[i];
    title = formData.getItem().getTitle();
    response = formData.getResponse();
    
    switch (title) {
      case "件名":
        subject = response;
        break;
    }

    if ("お問い合わせ内容" == title) {
      title = title + " : \n";
    } else {
      title = title + " : ";
    }

    body = body + "\n" + title + response;
  }

  sendToRedmine(subject, body);
}

このスクリプトにはテスト関数が用意されています。
上記スクリプトを保存したら「実行」→「関数のデバッグ」から「test」関数を実行してテストしましょう。

実行確認

実行確認

初回実行時には、実行権限などを聞かれると思いますがキャプチャをとれないのでここでは説明を省略。

実行してRedmineにチケットが作成されれば成功。

トリガーの設定

フォーム送信時に特定の関数を実行するためにはトリガーの設定が必要です。

「編集」から「現在のプロジェクトのトリガー」を選択してください。

トリガーの設定

下記画像のように「実行」と「イベント」を設定します。

トリガーの設定

トリガーの設定

また、「通知」をクリックして送信失敗時にはメールにて通知してもらうように設定しましょう。

失敗時の通知設定

失敗時の通知設定

運用をサポートする設定

概要

問い合わせにはメールで回答するのですが、その時にsupport@をCCに入れてメールを送信します。

また、メールの件名にはチケットIDを入れることでチケットのコメントとして集約することができるのですが、このチケットIDは入力ミスや入力漏れが発生すると、結構大変なことになります。
(クライアントも返信時にそのまま利用するため)

入力漏れやミスを少しでも軽減するための工夫として「件名をコピー」リンクを作成しています。

件名をコピー

件名をコピー

クリックすると下記のような文字列がクリップボードにコピーされます。

Re: [#105] パスワード再発行について

利用するプラグイン

リンクの作成には下記プラグインを利用します。

View customize plugin for Redmine

利用方法は上記githubに説明があるので省略します。

ViewCustomizeの設定

「New view customize」から下記のとおり設定します。

Javascript

項目 設定値
Path pattern /issues/[0-9]+
Type JavaScript
Code 後述
Enabled チェック入れる
プライベート チェック入れない

コードには下記を入力。
注:Redmineのバージョンによってはセレクタが変わっている可能性がある。

function prepared_subject_support() {
  $('.subject').prepend('<a class="js-codecopy" href="javascript: void 0;" onclick="copy_subject_support(this);">件名をコピー</a>');
  var issue_id_obj = document.querySelector('#content h2');
  var subject = document.querySelector('#content .subject h3');
  var issue_id = get_issue_id_support(issue_id_obj);
  subject.textContent = issue_id + subject.textContent;
}

function get_issue_id_support(issue_id_obj) {
  if (!issue_id_obj) {
    return '';
  }
  
  var match = issue_id_obj.textContent.match(/#[0-9]+/);

  if (!match) {
    return '';
  }
  
  return "[" + match[0] + "] ";
}

$(function() {
  prepared_subject_support();
});

var copy_timeout_support = 0;
function copy_subject_support(obj) {
  if (!document.querySelector) {
    alert("コピーできませんでした。\nブラウザが対応していない可能性があります。");
    return;
  }

  var subject = $(obj).parents('.subject');
  var isSuccess = false;

  if (!subject[0]) {
    alert('コピーに失敗しました。');
    return;
  }

  $(obj).removeClass('js-codecopy-ok');

  if (copy_timeout_support) {
    clearTimeout(copy_timeout_support);
  }

  try {
    var subject = document.querySelector('#content .subject h3');
    var backupText = subject.textContent;
    subject.textContent = "Re: " + subject.textContent;
    
    var range = document.createRange();
    range.selectNode(subject);
    window.getSelection().removeAllRanges();
    window.getSelection().addRange(range);
    isSuccess = document.execCommand('copy');
    
    subject.textContent = backupText;

    if (true !== isSuccess) {
      alert('コピーできませんでした。\nブラウザが対応していない可能性があります。');
    }

    window.getSelection().removeAllRanges();
  } catch(e) {
    window.getSelection().removeAllRanges();
    alert('コピーできませんでした。\nブラウザが対応していない可能性があります。');
  }
  
  if (isSuccess) {
    $(obj).addClass('js-codecopy-ok');
    console.log(obj);
    copy_timeout_support = setTimeout(function() {$(obj).removeClass('js-codecopy-ok');}, 3000);
  }
}

CSS

項目 設定値
Path pattern /issues/[0-9]+
Type StyleSheet
Code 後述
Enabled チェック入れる
プライベート チェック入れない

コードには下記を入力。
注:Redmineのバージョンによってはセレクタが変わっている可能性がある。

.subject .js-codecopy-ok:after {
  content : '[OK]';
}

シェアする

  • このエントリーをはてなブックマークに追加

フォローする