RedmineにExcelBookを表示する(ViewCustomizePlugin)

# やったこと
RedemineのViewCustomizePluginを使って、ExcelBook(html形式)をチケット画面やガントチャート画面に表示する様にしました。
（Bitnami Redmine 3.3.0, ie11）
# 概要
## やろうと思った経緯
良くあるのですが、Redmineでチケット駆動開発をしている中で、上司は、中日程をエクセルで管理して共有しています。

エクセル資料をサーバーに置いて、メールでも添付して資料を見てください。

・・・という事をやっているのですが、
部下はサーバーの資料をあまり見に行ってくれません。

メールに添付した資料も、他のメールに埋もれてしまって、見ようと思いません。

上司も、部下がほとんど見ない資料をメンテする事になるので、そのうちサーバー資料のメンテを放置してしまいます。

![図1.png](https://qiita-image-store.s3.amazonaws.com/0/150778/af3310c6-65da-5711-b14a-5a647bdb7d5c.png)

## 結果
Redmineに、サーバーのエクセル資料を表示する様にしました。
チケット画面やガントチャート画面の下にエクセルブックを表示しています。

![図2.png](https://qiita-image-store.s3.amazonaws.com/0/150778/5dd1ed0c-d526-caf5-91b6-f2c3aef16680.png)
ViewCustomizePluginの機能で実装しています。

いつも見ているチケット画面に表示されるので、ちょいちょい目に入り、中日程が気になったら、すぐに見られる様になりました。

![図3.png](https://qiita-image-store.s3.amazonaws.com/0/150778/aabf02cb-c2aa-00d7-9c7b-57a359490afb.png)

上司も今までと同様に、サーバー上のエクセルファイルを修正するだけで良いので、情報共有が楽になった様です。

# RedmineにExcelBookを表示する
VewCustomizePluginでExcelBook(html形式)をチケット画面やガントチャート画面に表示します。
## ExcelBook(html形式)を置いた場所
Redmineサーバー側にフォルダを作成
⇒C:\Bitnami\redmine\apache2\htdocs配下に置き場所のフォルダ（ShareInfo\mySchedule）を作成し、ExcelBook(html形式)が設置された状態

```bat
C:\Bitnami\redmine\apache2\htdocs\ShareInfo\mySchedule
■ExcelBook(html形式)
mySchedule.files
mySchedule.html
```
## ViewCustomizePlugin設定
指定したプロジェクトでExcelデータを表示する設定です。
### Path pattern:
```
/issues 
```
### Type:
```
JavaScript 
```
### Code:
```js
/* 他のページを取り込む */
$(function(){
  /* TESTプロジェクトのみ対象 */
  if($('body').hasClass('project-test'))
  {
    var $hideExcelLink = $("<a href='#' >hideExcel</a>"); 
    var $myFrame = $("<iframe class='autoSizeAdjust' width='100%' height='400px' frameborder='0' src='http://127.0.0.1/ShareInfo/mySchedule/mySchedule.html' scrolling='auto'></iframe>");
	
    /* Excelページを最後に追加 */
    $('#content').after(
      $hideExcelLink, //非表示ボタン
      $myFrame        //Excelページ
    );
    
    /* Excelページの非表示 */
    var func = function(){
      $myFrame.hide();
      $(this).hide(); //hideExcelのリンクを非表示に
      return false;
    };
    
    $hideExcelLink.click(func);
  }
  else
  {
    //DO NOTHING
  }
});
```
# 共有サーバーのExcelBook(html形式)をRedmineサーバーへコピーする
共有サーバー側のフォルダ:

```bat
\\NAS\home\Tool\ShareInfo\mySchedule
```
をRedmineサーバーへフォルダごとコピー

```bat
C:\Bitnami\redmine\apache2\htdocs\ShareInfo\mySchedule`
```

## コピー方法はバッチファイルで実装

下記バッチファイルをRedmineサーバー側から起動して実施

```bat:myScheduleCopy.bat
::--- 上書き確認を行わずにディレクトリ/ファイルをコピーする ---

::/S 	ファイルが存在する場合のみディレクトリごとコピーする
::/R 	読み取り専用属性のファイルも上書きコピーできるようにする
::/I 	コピー先のディレクトリが存在しない場合は新規にディレクトリを作成する
::/Y 	同名のファイルが存在する場合、上書きの確認を行わない
::/Z 	ネットワーク経由のコピーでネットワーク切断が発生した場合、コピーを再開できるようにする
::/K 	通常は解除される読み取り専用属性を維持したままコピーする

xcopy \\NAS\home\Tool\ShareInfo\mySchedule C:\Bitnami\redmine\apache2\htdocs\ShareInfo\mySchedule /S /R /I /Y /Z /K
```

# ExcelBookをhtml形式で保存するExcelマクロ
htmlで保存するためのExcelマクロBookを別に設置しておいて、
iniファイル設定で、対象のエクセル資料を指定する様にしました。
※対象のエクセル資料は閉じている状態が前提になります。（同じファイル名を開くエラーになります。。。）
## ファイル構成
AutoHtmlSetup.xlsmを起動すると、日程.xlsxを開いてhtml形式で保存して閉じる。

```bat
AutoHtmlSetup.ini
AutoHtmlSetup.xlsm
日程.xlsx
```

### AutoHtmlSetup.ini
AutoHtmlSetup.iniファイルには、Excelマクロファイル（AutoHtmlSetup.xlsm）の動作設定などを記載。
[INPUT_FILE]
・開くエクセルファイル（日程.xlsx）の指定
[OUTPUT_FILE]
・htmlファイルの保存先
[終了OPTION]
・AutoClose=1：htmlファイルの保存後、ExcelマクロBOOKを自動クローズする。
・AutoClose=0：自動クローズしない
　※Excelマクロファイルを編集したい場合、クローズしないに設定する。

```ini:AutoHtmlSetup.ini
[INPUT_FILE]
InputFilePath=\\NAS\home\Tool\ShareInfo\日程.xlsx

[OUTPUT_FILE]
OutputFilePath=\\NAS\home\Tool\ShareInfo\mySchedule\mySchedule.html

[終了OPTION]
AutoClose=1
```

### AutoHtmlSetup.xlsm

```vbnet:ThisWorkbook
'Bookが開いたら自動実行
Private Sub Workbook_Open()
 AutoSaveHtml
End Sub

'エクセルをHTML形式で保存する
'　iniファイルから、エクセルパスとHTML出力パスを取得
'　iniファイルでこの自動実行を自動終了するかどうか分岐可能
Private Sub AutoSaveHtml()

  Dim mybook As Workbook
  Dim isAutoClose, InputFilePath, OutputFilePath, FileName, FileFolder As String
  Dim myFSO As Object
  Dim IsFolderExist As Boolean
  
  '監視するエクセルファイルをiniファイルから取得
  InputFilePath = Readini(ThisWorkbook.Path & "\AutoHtmlSetup.ini", "INPUT_FILE", "InputFilePath")
  '- BOOK OPEN
  Set mybook = Workbooks.Open(FileName:=InputFilePath)
  'ActiveWorkbook

  'htmlファイルで出力する
  OutputFilePath = Readini(ThisWorkbook.Path & "\AutoHtmlSetup.ini", "OUTPUT_FILE", "OutputFilePath")
  
  Set myFSO = CreateObject("Scripting.FileSystemObject")
  FileName = myFSO.GetFileName(OutputFilePath)
  FileFolder = myFSO.GetParentFolderName(OutputFilePath)
  IsFolderExist = IsFolderExistsFunc(FileFolder)
  
  If (IsFolderExist) Then
    '- HTML OUT
    Application.DisplayAlerts = False '（上書き）警告を無効に
    mybook.SaveAs FileName:=OutputFilePath, FileFormat:=xlHtml
    Application.DisplayAlerts = True  '警告を有効に
    '- 出力したhtmlファイルが開いたままにならない様にclose
    mybook.Close
  End If

  'iniファイルで自動終了するかどうか分岐する
  isAutoClose = Readini(ThisWorkbook.Path & "\AutoHtmlSetup.ini", "終了OPTION", "AutoClose")
  If (isAutoClose = "1") Then
    '自動終了する
  
    '- 他にエクセル開いていなければエクセルを終了する
    If (Workbooks.Count = 1) Then
      Application.Quit
    End If
    
    '- 最初にiniファイルから開いたBOOKが開いているのを閉じる
    ActiveWorkbook.Close
    
  ElseIf (IsFolderExist = False) Then
    '開いたエクセルファイルを閉じておく
    mybook.Close
    '自動終了しない 出力先フォルダがない
    MsgBox "HTMLの出力先フォルダを先に作成してください：" & FileFolder
  End If
  
End Sub

'フォルダがあるかどうか
' ある:True
' ない:False
Private Function IsFolderExistsFunc(folder_path As String) As Boolean

  If (Dir(folder_path, vbDirectory) = "") Then
    IsFolderExistsFunc = False
  Else
    IsFolderExistsFunc = True
  End If

End Function
```

```vbnet:標準モジュールTool
Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
 
Private Const STRING_MAX_SIZE As Integer = 4096
 
'  【入出力】 striniFile      ： (I)  iniファイル名（ファイル名はフルパスとする事）
'             strSection      ： (I)  iniファイルセクション名
'             strKey          ： (I)  iniファイルキー名
'  【戻り値】 読み込んだiniファイルのvalue値。
Public Function Readini(ByVal striniFile As String, _
                        ByVal strSection As String, _
                        ByVal strKey As String) As String
     
  Dim nret As Long    '復帰値格納用
  '---------------
  ' iniファイル読み込み時の設定文字（固定長）
  ' STRING_MAX_SIZE分の固定領域
  ' iniファイル読み込み時、可変長ではアボートする。
  '---------------
  Dim strValue As String * STRING_MAX_SIZE
  
   strValue = Space(STRING_MAX_SIZE) 'スペース埋めしておかないとサイズ指定の文字列はゴミが入っている。(初期化されていない。)
   nret = GetPrivateProfileString(strSection, strKey, "", strValue, STRING_MAX_SIZE, striniFile)
     
  'iniファイル読み込みで取得した文字列はNULLも取ってしまっているので、NULLの直前まで取得
  Readini = Left(strValue, (InStr(strValue, Chr(0)) - 1))
End Function
```

# 定期実行設定にする
設置したExcelマクロを自動実行させて、
サーバーにあるExcel資料（日程.xlsx）を、html形式で保存する作業を自動処理させます。
これをするには、Excelマクロを起動するPCにExcelがインストールされている必要があります。
結局、これは自分の個人PCから実行する様にしました。

理由：
・Redmineや、自動実行ツールJenkinsのPCにエクセルが入っていない。
・日程.xlsxにマクロを入れたくない。（マクロを有効にしますかボタンが表示されるのが嫌らしい）
・日程.xlsxは今まで通り、サーバーの資料を修正して保存するだけがいい。
・上司は資料をsubversionで管理してくれない。（コミットトリガ設定できない）
などなど。。。

残念。何か良い方法ございましたら、どなたか教えて下さい。。。