なぜcybozuliveではなくGoogleDriveでファイル管理するべきかを非プログラマーに

# 各サービス紹介
えっとまず知らない人もいると思うので
cybozu live
https://cybozulive.com/
Google Drive
https://drive.google.com/
これのことです。

前者はオンラインで情報共有しつつプロジェクトの進行、割り振り、スケジューリングができるツールです。プログラマにはGitHub Issueの劣化版にカレンダーくっつけたといえばいいでしょうか。
後者はクラウドストレージです。

# はじめに

私が所属している[みらい研究室実行委員会](https://miraiken.github.io/web/index.html)ではcybozuliveを使っているのですが、この度ファイル管理をGoogleDriveにしようぜ！ということを言うためにまとめています。
内輪ネタは無いのでご安心ください。

# cybozuliveでのファイル管理が決定的に辛い2つの理由

糞なところは掃いて捨てるほどありますが、それは後述するとしてとりあえず決定的に糞な使用を２つ挙げましょう。

## 1.容量が1GBしかない

正直つらいです。画像とかzipとかあげたら割とすぐ埋まってしまいます。しかしこれだけではまだ糞ではないですよね？

## 2.ファイルを１つずつしかDLできない

こんなのFワード不可避です。

容量少ないならせめて全件DLして移行とかしたいじゃないですか。でも真っ当なやり方ではできません。

### 一括DLできない証拠であるサイボウズサポートの返答

>"サイボウズLive サポートセンター" <support@cybozulive.com>

>To yume-wikijp
○○○○ 様

>サイボウズLive サポートセンター 松田です。
お問合せ頂き、ありがとうございます。

>残念ながら、サイボウズLiveでは、共有ファイルを
一括でダウンロードすることはできません。

>ご要望にお応えできず、申し訳ございません。

>お手数をおかけしますが、1つずつファイルを
ダウンロード頂きますようお願い申し上げます。

>その他にご不明な点がございましたら、お気軽にご連絡ください。
今後ともサイボウズLiveをご愛顧頂きますようお願い申し上げます。


後述する通り``1つずつファイルをダウンロード``とやらを自動化することはできますが、辛いです。

# Google Driveの利点

- Google Docsとの緊密な連携により、MS Officeなくても十分いろいろできる
- 検索で有名なGoogleだけあって検索機能は強い
- デスクトップClientがある
- ファイルを外部に公開できる

# Google Driveとcybozu liveを比較する

## 他人の作ったファイルを見る

| cybozu live            | Google Drive(Desktop Client導入時) |
|-------------------------|------------------------------------|
| 1. ファイルをDLする     | 1. ファイルを開く                  |
| 2. ファイルを閲覧する   |                                    |
| 3. 見終わったら削除する |                                    |

勝手の同期してますからいきなりファイルを見れますね。

## ファイルを編集してアップロードするまで

| cybozu live                                              | Google Drive(Desktop Client導入時) |
|-----------------------------------------------------------|------------------------------------|
| 1. ブラウザでサイトにアクセスしファイルをDLする           | 1. ファイルを編集する              |
| 2. ファイルを編集する                                     | 2. ファイルを保存する              |
| 3. ファイルを保存する                                     |                                    |
| 4. ブラウザでサイトにアクセスしファイルをアップロードする |                                    |

Google Driveならとってもシンプル。いちいちDLするためにブラウザ開かなくていい。


## バージョン管理

| cybozu live                                          | Google Drive(Desktop Client導入時) |
|-------------------------------------------------------|------------------------------------|
| 1. ファイルをDLする                                   | 1. ファイルを編集する              |
| 2. ファイルを編集する                                 |                                    |
| 3. ブラウザでサイボウズのサイトを開く                 |                                    |
| 4. 共有フォルダから対象のファイルを探す               |                                    |
| 5. 「ファイルを上書きする」ボタンからアップロードする |                                    |

Desktop Clientは勝手に版管理してくれる(というより同名は更新扱いする？)ので楽ですね。

ちなみにGoogleDriveの版管理は
![version_manage](https://qiita-image-store.s3.amazonaws.com/0/94177/06022fea-6f75-8af6-bf50-ceeff6fea77d.png)


から見られます。30日間限定の版管理ですがまあ実用上支障ないかと。本当に版管理必要ならgit使えばいいですね。


# Google Driveにするとどう変わるか

## 複数人でファイルを編集する

例えばなんかの解析をする時に複数人でやったとしましょう。

これまでなら、解析範囲を解析担当者に割り振り、それぞれWordでまとめ、それを最終的に一つにまとめるということをしていました。

ほかの解析者が何を書いているかを知るには、途中の物をアップロードしてもらうように頼み、それをダウンロードするぢかありませんでした。

Google Driveは先にいったように、Google Docsと緊密に連携しているので、同じファイルを複数人でブラウザ上で編集できます。編集競合を避けるために解析範囲を割り振るまでは同じですが、ほかの人の進捗も見ることができます。より効率的ですね。

## 議論の過程でファイルを上げる

これに関しては若干面倒です。

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/94177/03114433-429c-1e92-8f8b-43f70edbdf80.png)

図のように、同期しているファイルを右クリックして「Googleドライブ」→「共有」→「詳細設定」から共有リンクを取得し、これをコピペする、という方法になります。

cybozu liveに直接貼る場合
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/94177/527098d0-6c1e-2e1b-ee77-d83dcb0890fe.png)

このように出来たので、若干不便になったかもしれません。
しかし、これをするとcubouzu liveの共有フォルダの添付ファイルがあっという間に膨れ上がって収拾がつかなくなることを考えると、Google Driveにしたほうが結果的に楽かもしれません。


## 作成したファイルを外部の人にみせる

これまではcybozu liveにファイル公開機能が無いため、一旦DLしてそれをメールに添付するしかありませんでした。

Google Driveでは

![公開リンク](https://qiita-image-store.s3.amazonaws.com/0/94177/1d9ba1ab-a60b-d878-5b63-dcfdb283f07f.png)

これのいい所は、ファイルはGoogle Drive上にあるので、相手に大きなファイルを送っていしまうことがなく、また、ファイルに変更があっても送り直す必要が無いというあたりでしょうか。

## ファイルを検索する
cybozu liveでは
![サイボウズLiveの検索](https://qiita-image-store.s3.amazonaws.com/0/94177/3c8defb8-df20-21aa-b82f-c3341a052504.png)
キーワード以外に更新者や更新日で検索できました。**ただしスマホアプリにはその機能がない模様**。

Google Driveはどうでしょうか？
検索で有名なGoogleが作ってるんですから、当然検索機能は優秀です。キーワード検索の他に
![わかりやすいシンプルな絞込](https://qiita-image-store.s3.amazonaws.com/0/94177/2cdcd39f-7521-cd38-b131-3eaea742a58f.png)
ファイル種別で絞り込んだり
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/94177/9e66998f-2c7f-67f5-29f0-e13253b6b23e.png)
もちろん更新日などでも検索できます。

また、[ファイルサイズの大きい順に並べ替えることも出来ます](http://www.lifehacker.jp/2015/01/150104_google_drive.html)

しかし、地味に優秀なのは、**PDFの中身も先頭10ページなら検索してくれる**ということです。

# 複数人プロジェクトにおけるGoogle Driveの運用

基本的には、閲覧者全員がGoogleアカウントを取得し、管理者は閲覧者に対しフォルダーを共有する、ということになると思います。人によってこのフォルダーだけ、とかこのファイルだけ、というように権限を管理できます。

## Google Driveの権限

三段階ありまして、

1. 閲覧できる
2. コメントできる
3. 編集できる

となっています。用法用量を守って権限を管理しましょう。

# プログラマーのためのcybozu live解剖学

「非プログラマーに」というタイトルはどこに行った、という感じですが気にしない。

## cybozu live共有フォルダ上のファイルの扱われ方

まず、これはcubouzu liveを使い込んでいればプログラマでなくてもわかるかもしれませんが、cybozu live共有フォルダ上のファイルには２つの名前があります。

1. アップロード時のファイル名
2. cybozu live共有フォルダでの表示名(デフォルトでは1に同じ)

厄介なことに、この2つは必ずしも一致しません。どういうことかというと、**1の名前が同じファイルが複数同じ階層に存在しうるということです**。これは、全件DLする際に障害になります。

で、これとは別に、各ファイルに**uniqueなコンテンツID(cidと略記される)が振られます**。

## cybozu live での一括DL機能の実装予定について

https://cybozulive.com/ideabox/view?requestId=150
こういうふうに2010年にユーザーから要望が出て、少なくとも2011年に**実装予定**になっています。
さてあれから5年。サイボウズさん、いつ実装するんですか？
zipで固めるなりtarで固めて落とせるようにしていただけませんか・・・？

## cybozu live APIで取れる共有フォルダの情報

cybozu live自体にファイルDL機能がない以上、ダウンローダーを自作しないといけないわけですが、まず考えるのは公式APIでしょう。

https://developer.cybozulive.com/doc/current/
ここにドキュメントがあります。このうち関係するのは
https://developer.cybozulive.com/doc/current/pub/gwCabinet.html
これです。まあごくごくありきたりな内容が取得できますが、**cidは取得できません**。よってAPIは使いものにならないゴミという事になります。

## ならばcokie使ってhtml落としてゴリ押しするしか無い！

さて、cidですが、共有フォルダのファイル一覧のhtmlには実は記載されています。

以下``z_zzzzz``はGroupIDを指します。
![GroupID](https://qiita-image-store.s3.amazonaws.com/0/94177/8bb953e4-69d8-7aa8-8134-1ddf2322ab2f.png)

### とりあえず共有フォルダのファイル一覧を調べる

共有フォルダのファイル一覧のURLですが(というよりGETのパラメータ)

```
https://cybozulive.com/z_zzzzz/gwCabinet/list?currentFolderId=yyyyyy
```

のようになっています。ここでGETのパラメータは

<dl>
    <dt>currentFolderId</dt>
    <dd>各フォルダに固有のid</dd>
</dl>

です。FolderIDが問題ですね。

### FolderIDを取得する

ところで共有フォルダのファイル一覧のhtmlには
![共有フォルダ - みらい研実行委員会 - サイボウズLive.png](https://qiita-image-store.s3.amazonaws.com/0/94177/dba3a4bc-c4c2-b480-7a07-5e9b2bec2722.png)
このような部分があります。フォルダーの一覧がありますね。ここのhtmlは・・・？

```html:共有フォルダのhtml抜粋
<li id="cba_gwCabinetFolder_yyyyyy" class="selfclear active">
    <span class="switch">
        <a href="javascript:void(0);" class="folderOpen" style=""><img src="https://cybozulive.com/static/76ba8adc98/images/plus11.gif" alt="開く" /></a>
        <a href="javascript:void(0);" class="folderClose" style="display:none;"><img src="https://cybozulive.com/static/76ba8adc98/images/minus11.gif" alt="閉じる" /></a>
    </span>

        <img src="https://cybozulive.com/static/76ba8adc98/images/cy_order20.gif" class="icon categoryOrder" style="display: none;"/>
    
    <a href="/z_zzzzz/gwCabinet/list?currentFolderId=yyyyyy" class="iconLink categoryLink" title="広報班">
        <span class="categoryName">広報班</span>
    </a>
    <span class="targetCount">(3)</span>

        <span class="categoryEdit" style="display: none;">
        <a href="javascript:void(0);" class="iconLink modifyCategory" title="編集する"><img src="https://cybozulive.com/static/76ba8adc98/images/edit16.gif" alt="編集する" class="icon" /></a>
        <a href="javascript:void(0);" class="iconLink deleteCategory" title="削除する"><img src="https://cybozulive.com/static/76ba8adc98/images/delete16.gif" alt="削除する" class="icon" /></a>
    </span>
    
    <ul id="cba_gwCabinetFolderList_yyyyyy" class="include" style="display: none;">
    </ul>
</li>  
```

フォルダーごとにこの``li``要素があります。そしてFolderIdが書いてある！

ということは**cokieをヘッダーにつけてhtmlをDLして正規表現で抜けばええやん!!**

### coffset

ところでフォルダー一覧の表示件数は20件です。20件を超えた分はどうやってしゅとくすればいいでしょうか？

ここでFirefoxで次へボタンを押したときの挙動をみると
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/94177/a6e0339a-ddce-d227-794f-f2d84c2cb149.png)

なにやらajaxでGET要求を出してhtmlを取ってきています。つまりフォルダーのファイルリストの正体はこいつです。この要求URLは

```
https://cybozulive.com/z_zzzzz/gwCabinet/ajax/listAjax?currentFolderId=yyyyyy&csort=s&csortOrder=o&coffset=of&dummy=ttttttttttttt
```

のようになっています。ここでGETのパラメータを見ると

<dl>
    <dt>currentFolderId</dt>
    <dd>前述したFolderID</dd>
    <dt>csort</dt>
    <dd>ソート基準(1:タイトル 2:更新日時)</dd>
    <dt>csortOrder</dt>
    <dd>ソート時の並び順(0:降順 1:昇順)</dd>
    <dt>coffset</dt>
    <dd>ページ数×20(1ページあたりの表示数)</dd>
    <dt>dummy</dt>
    <dd>GET要求時点の1 January 1970 00:00:00 UTC (Unix Epoch).からのミリ秒数を表す整数値(なんだけどサーバー側では見てないっぽいどころかこのパラメータなくてもいいみたい)</dd>
</dl>

つまりこのパラメータを指定してhtml落とせばいいということになります。
全件DLの作業中にファイルが追加されることを考え、更新日時(降順)でソートしておくことにしましょう。

### cidを手に入れる

こうして落としたhtmlを見てみましょう。

```html:共有フォルダのhtml抜粋
<tbody>
<tr class="even confirmed subject">
<td class="check">
    <div><input type="checkbox" name="cid" value="xxxxxxx" /></div>
</td>
<td class="title">
    <div class="subject">
        <a href="/z_zzzzz/gwCabinet/view?cid=xxxxxxx&amp;coffset=0&amp;currentFolderId=yyyyyy" class="iconLink" title="ファイルの詳細へ"><img src="https://cybozulive.com/static/76ba8adc98/images/cabinet20.gif" alt="共有フォルダ" class="icon" /><span>ファイル名</span></a>
    </div>
</td>
<td class="download">
    <div>
        <a href="/z_zzzzz/gwCabinet/downloadFileDirect?cid=xxxxxxx" title="ダウンロード" class="download"><img src="https://cybozulive.com/static/76ba8adc98/images/btn_download_s.gif" alt="ダウンロード" /></a>
    </div>
</td>
<td class="filesize"><div>                        xx,xxx KB
</div></td>
</tr>


<tr class="even confirmed contents">
    <td class="check"></td>
    <td colspan="3" class="withFolder">
        <div class="dateformat_mde">
            <div class="contentsParts">
                <div class="contentsPartsWrapper">
                    <div class="contentsPartsBody"></div>
                </div>
            </div>
            <div class="modifyParts">
                <div class="folder">
                    <span class="contentsLabel"><img src="https://cybozulive.com/static/76ba8adc98/images/folder16.gif" alt="フォルダ" /></span>
                    <span class="contentsValue operation"><a href="/z_zzzzz/gwCabinet/list?currentFolderId=yyyyyy">フォルダー名</a></span>
                </div>
                <div class="modifyMember">
                    <span class="contentsLabel">更新者:</span>
                    <span class="contentsValue">○○○○</span>
                </div>
                <div class="modifyDate">
                    <span class="contentsLabel">更新日時:</span>
                    <span class="contentsValue">MM/DD(DATE)</span>
                </div>
            </div>
        </div>
    </td>
</tr>
</tbody>
````

こんな風にxxxxxxxで隠しましたが3箇所に記載されています。

```
<a href="/z_zzzzz/gwCabinet/view?cid=xxxxxxx&amp;coffset=0&amp;currentFolderId=yyyyyy" class="iconLink" title="ファイルの詳細へ">
```

ここに着目するのがいいでしょう。この時、**``cid``**と**``coffset``**と**``currentFolderId``**を手に入れましょう。

さて、ということは**cokieをヘッダーにつけてhtmlをDLして正規表現で抜けばええやん!!**

### coffsetをの終了条件は？

ここまで見ればわかるようにcoffsetを20ずつ増やしてDLしてパースしてというのをループさせればいいわけですが、ループの終了条件は何でしょうか。ここで上記htmlをよく眺めると

```html:次がある時
<div class="contentsInfo listUpdate">
    <div class="pageNavi">
                                    <a href="javascript:void(0);" data-url="/z_zzzzz/gwCabinet/ajax/listAjax?currentFolderId=yyyyyy&amp;csort=s&amp;csortOrder=o&amp;coffset=of" class="cba_commonPageNaviTop">先頭へ</a> <span class="delimiter">|</span>
            <a href="javascript:void(0);" data-url="/z_zzzzz/gwCabinet/ajax/listAjax?currentFolderId=yyyyyy&amp;csort=s&amp;csortOrder=o&amp;coffset=of" class="cba_commonPageNaviPrev">&laquo;&nbsp;前の 20 件へ</a>
                <span class="delimiter">|</span>
                                                            <a href="javascript:void(0);" data-url="/z_zzzzz/gwCabinet/ajax/listAjax?currentFolderId=yyyyyy&amp;csort=s&amp;csortOrder=o&amp;coffset=of" class="cba_commonPageNaviNext">次の 20 件へ&nbsp;&raquo;</a> <span class="delimiter">|</span>
            <a href="javascript:void(0);" data-url="/z_zzzzz/gwCabinet/ajax/listAjax?currentFolderId=yyyyyy&amp;csort=s&amp;csortOrder=o&amp;coffset=of" class="cba_commonPageNaviLast">末尾へ</a>
                </div>
</div>
```

```html:もう終わりの時
<div class="contentsInfo listUpdate">
    <div class="pageNavi">
                                    <a href="javascript:void(0);" data-url="/z_zzzzz/gwCabinet/ajax/listAjax?currentFolderId=yyyyyy&amp;csort=s&amp;csortOrder=o&amp;coffset=of" class="cba_commonPageNaviTop">先頭へ</a> <span class="delimiter">|</span>
            <a href="javascript:void(0);" data-url="/z_zzzzz/gwCabinet/ajax/listAjax?currentFolderId=yyyyyy&amp;csort=s&amp;csortOrder=o&amp;coffset=of" class="cba_commonPageNaviPrev">&laquo;&nbsp;前の 20 件へ</a>
                <span class="delimiter">|</span>
            <span class="disable cba_commonPageNaviNext">次の 20 件へ&nbsp;&raquo;</span> <span class="delimiter">|</span>
        <span class="disable cba_commonPageNaviLast">末尾へ</span>
        </div>
</div>
```

つまり、「末尾へ」という文字のある行に``disable``とあれば終了ですね。

### まって、どうやってファイル名を取得するの？

最初に言ったように、ファイルには2つの名前があります。
共有フォルダの一覧の名前は**拡張子が記載されてないことがあり**、保存する際の名前として採用できません。
したがってこれを取得する方法を考えます。ここで利用するのがファイル詳細画面です。

さてこの詳細画面のURLですが(というよりGETのパラメータ)

```
https://cybozulive.com/z_zzzzz/gwCabinet/view?cid=xxxxxxx&coffset=0&currentFolderId=yyyyyy
```

のようになっています。ここで

<dl>
    <dt>cid</dt>
    <dd>「cidを手に入れる」で手に入れたcid</dd>
    <dt>coffset</dt>
    <dd>「cidを手に入れる」で手に入れたcoffset</dd>
    <dt>currentFolderId</dt>
    <dd>「cidを手に入れる」で手に入れたcurrentFolderId</dd>
</dl>

です。

さて、こうして落としたHTMLには

```html:ファイル詳細画面のhtml(抜粋)
<td class="fileName">
    <div>
        <span class="fileName">ファイル名.拡張子</span>
                                <span class="button--download">
                <a href="/z_zzzzz/gwCabinet/downloadFileDirect?cid=xxxxxxx" class="button--download__operation" title="ダウンロード"><img src="https://cybozulive.com/static/76ba8adc98/images/arrow/download.gif" alt="ダウンロード" /><span>ダウンロード</span></a>
            </span>
            </div>
</td>
```

のような部分があります。さて**勝ち確定ですね！**

### まとめ

1. GroupIDは既知
2. FolderID一覧は共有フォルダのファイル一覧のページどこでもいいのでブラウザで落としておいてそれをパースすれば手に入る(いやそこも自動化してもいいけどさ)
3. cidを各フォルダですべて取得するにはcoffsetを変化させつつDLしてパースすればよい
4. ファイル名は3で手に入れた``cid``,``coffset``,``currentFolderId``を使ってhtmlを落しパースして手に入れる
5. cybozu liveに全件DLという機能がないので全件DLは不可能という常識を捨て去ることにより~~[早苗が奇跡を起こした](http://dic.pixiv.net/a/%E6%9D%B1%E9%A2%A8%E8%B0%B7%E6%97%A9%E8%8B%97#h2_5)~~全件DLへの道がひらけた


## ここまでをC++11で半自動化した

https://github.com/miraiken/cybozu_extract_dl_info

ほい。そのままでは動かないけどここまでみればソースをいじるのは簡単のはず。cokieの話は書いてないけどそれくらいはわかるよね。
VSでしか検証してないけど、別にLinux環境でもSSLが使えるcurlが`std::system``から呼べれば

```cpp
file.imbue(std::locale(std::locale(), new std::codecvt_utf8_utf16<wchar_t>));
```

を全部

```cpp
file.imbue(std::locale(std::locale(), new std::codecvt_utf8<wchar_t>));
```

に置き換えれば使えるはず。

cid_list.txtを生成します。中身は

```
folder_id:yyyyyy cid:xxxxxxx filename:なまえ.拡張子
```

みたいな感じ。folder_idはDLには必要ないんだけど、見やすさを考えてつけました。

## あとはDLするだけだ

ヘッダーを除くとDLする際のcurlのコマンドは

```
curl 'https://cybozulive.com/z_zzzzz/gwCabinet/downloadFileDirect?cid=xxxxxxx' -o 'ファイル名.拡張子' -L
```

こんな感じ。

## それもC++11で自動化してやんよ！

https://github.com/miraiken/cybozu_get_file

ほい。やっぱり手直しは必要です。

なんでC++なんだという声が聞こえてきそうだけど、書く人が一番書きやすい言語で書くのがいいよね。(一応Rubyで書こうとしたんだけど勝手が違うからめんdになって断念した)

# 結論
サイボウズの共有フォルダとか糞だ。滅びろ・・・

さて、新人プログラマーの皆様、なんとなく途中で挫折して最後まで読んでいただけていない気がしますが、ここまで読んでいただきありがとうございます。

今回、htmlを落として正規表現で抜くという**とてつもなくdirtyな**方法を紹介しました。しかもboost.asioを使うでもなく、libcurlを使うでもなく、他言語のネットワークライブラリを使うでもなく、curlをそのまま叩くという・・・。世の中なんでも綺麗にはいかないらしいです。

なるべくこういうことはしたくないものですが、いざというときにはこういう汚い方法もできるようになっておくことは大事かと思います。

ちなみにこれを調査してプログラム書くのに2日、この記事書くのに2日かかっています。皆さんならもっとはやく調査も執筆もできたでしょう。え？できない？・・・うん、がんばれ。

# 課題

- cid_list、オレオレ形式じゃなくてjsonにしたい。(picojson)
- cokieとかGroupIDとかコマンドライン引数で指定できるようにしたい
- GUI作りたい
- Boost.asio使って書き直したい