Box のアクティビティ/ Member Timeline
同じ記録を、業務の履歴として読み下せる形で出す
3 つの読み口のうち 2 つ目です。データの元は全社の監査ログとまったく同じ 1 本ですが、こちらは業務の履歴として読める形に整えて Box のメンバー全員へ出します。1 つの Box には社内の担当者と取引先の担当者が同時に入るため、何を出すかの線引きがこの章の主題です。
何が見えるかWhat Members See
2 つのビューがあります。メンバー全員が見る 48 種と、Owner だけが見る 4 種です。合わせて 52 種で、この数は自動テストで固定しています。
| ビュー | 分類 | 数 | 中身 |
|---|---|---|---|
| アクティビティ メンバー全員 | ファイル | 14 | アップロード・差し替え・削除・復元・改名・移動・版の復元・完全削除・アーカイブとその解除・保持の設定と解除・メタデータの編集・ゴミ箱を空にする |
| フォルダ | 11 | 作成・改名・削除・移動・復元・アーカイブとその解除・保持の設定と解除・完全削除・メタデータの編集 | |
| メンバー | 7 | 追加と招待・招待の受諾・ロールの変更・除外・自分から退出・オーナーの引き継ぎ・参加申請の承認 | |
| 共有・受け渡し | 7 | 共有リンクの発行と無効化・全社側からの強制失効・ファイルリクエストの発行と無効化・同じく強制失効・提出の取り込み | |
| コメント | 3 | 投稿・編集・削除 | |
| Box 設定 | 6 | 作成・基本情報の変更・公開範囲の変更・保持期間の変更・版の保持期間の変更・Drive 同期の切り替え | |
| アクセス履歴 Owner のみ | 読み取り | 4 | ダウンロード・プレビュー・一括ダウンロード・共有リンク経由のアクセス |
あえて出していないもの
163 種のうち、ここに出るのは 52 種だけです。残りを出さない理由は 3 つに分かれます。
- システム内部の配管(ウイルス検査の進行・Drive 同期の各段・通知の作成と送信)。業務として意味を持つ場面では、それぞれの画面が状態を出しています。ここに並べると本来読みたい行が埋もれます。
- 読み取り系(ダウンロード・プレビュー)。これはアクセス履歴の側にあります。
- Box が見えなくなる操作(Box の無効化・削除)。その操作が済んだ時点で、この画面自体が開けません。
- メンバー系のうち招待の辞退・取り消し・期限切れも外しました。誰が辞退したかを Box の全員に見せる必要はなく、行数だけが増えるためです。
権限をデータベース側で固定するAuthorization in the Database
Box のメンバーは、記録のテーブルを直接参照できません。通り道は box_activity_for_member という関数 1 本だけです。この関数は「持ち主の権限で動く関数」(SECURITY DEFINER)で、参照の制限を越えてテーブルを読める代わりに、関数の中で 4 つの検証を済ませます。
①〜④ はすべて「空の値なら拒否」の向きに倒してあります。判定に使う値が取れなかったときに通してしまうと、条件が 1 つ崩れるだけで全部が開くためです。この 4 通りの弾かれ方は、実際のデータベースに対してロール別に走らせる自動テストで固定しています。
表示対象を関数の中に固定する
「メンバー全員に見せる 48 種」「Owner だけの 4 種」の一覧は、関数の中に配列として直接書いてあります。アプリ側の定数はその写しで、画面の選択肢と入力の検証にだけ使います。
こうしている理由は、「ダウンロード履歴を一般のメンバーに見せない」という境界をデータベース層で固定するためです。アプリ側の条件分岐だけで守っていると、実装の 1 か所を間違えただけで漏れます。増減させるときは「関数の配列」「共有の定数」「表示文」の 3 か所を同時に直す必要があり、ずれたら自動テストが落ちます(定数の全項目が関数から返り得るか、定数の外が混ざっていないかを突き合わせています)。
内部情報を構造的に出さないStructural Masking
操作ごとの自由項目には、IP・端末情報・トークン・内部の識別子まで入り得ます。これを「出さないもののリストを作って除く」方式では守れません。出してよいものの対応表を通す方式にしています。
対応表は操作ごとに持っています。記録の自由項目のキーの綴りは実装の時期によって不統一(英字の区切り方が 2 通り混在)で、単純な「許可するキーの一覧」では受け止められなかったためです。表に載っていない操作は「追加の項目なし」になり、表示は固定の文と対象名だけになります。
メールの伏せ字Masked Emails
ここは実装のあいだに 2 回直した箇所です。1 回目で塞いだつもりが塞がっておらず、2 回目で塞がりました。何が抜けたのかを書き残します。
| 段 | やったこと | 残っていた穴 |
|---|---|---|
| 1 回目 | 記録の自由項目に入っているアドレスそのものを、対象名の欄へ流し込まないようにした。専用の欄へ隔離し、伏せ字にしてから対象名へ畳む形にした | 未承諾の招待相手が対象の行では、アドレスの「@より前」がそのまま出続けていた |
| 2 回目 | 「@より前」が出る発生源を塞ぎ、5 つの経路すべてを伏せ字に畳んだ | なし(機械検査を、完全なアドレス形だけでなく「@より前」の一致・部分一致まで見る形に強化) |
なぜ「@より前」が出てしまったのか
Box に社外の相手を招待すると、その相手のアカウントが先に作られます。そのとき表示名の欄には、招待先アドレスの「@より前」がそのまま入ります(まだ本人が名前を入れていないので、他に入れるものがない)。この行はログインできないため、本人が受諾するまで表示名は一生「@より前」のままです。
アクティビティの側は「対象の名前が取れたらそれを出し、取れなければアドレスを伏せ字にして出す」という順で組んでいました。ところが上記の行では名前が取れてしまうので、伏せ字にする道に入らず、そのまま表示されていました。
対処は、「まだ一度もログインしていない、確認も済んでいない」アカウントの表示名を、名前として採らないようにしたことです(この条件は、ほかの画面でメンバーを検索するときに使っているものと同じ形にそろえました)。名前として採らないので伏せ字の道に入り、t***@example.com の形で表示されます。
機械検査の作り方
応答の JSON を文字列にして「@を含むアドレス形が入っていないか」を見る検査は、1 回目のときに通っていました(@が無いので当たらない)。現在の検査は、投入したアドレスの「@より前」と一致も部分一致もしないことまで見ます。
伏せ字にする規則そのものは実装 1 本にしてあり、画面向けの応答と CSV の両方が同じ関数を通ります。「同じ 1 行を両方へ流して結果が一致すること」と「その関数の外に独自の実装が生えていないこと」の 2 つを、それぞれ自動テストで見ています。
日本語の表示文Message Builder
API が返すのは構造化された行だけで、文章は作りません。「○○さんが △△ をアップロードしました」という文の組み立ては、画面側の純粋な関数 1 本が担っています。
分けている理由は、文章を作る場所が 2 つになると必ず食い違うからです。API 側でも文を作り、画面側でも作ると、片方だけ直された状態が生まれます。文章の置き場は画面側の 1 か所、というのを規律にしています(CSV では文章の代わりに「項目名=値」を並べた列を出しており、こちらも日本語の文は作りません)。
| 操作 | 表示される文 | 値の出どころ |
|---|---|---|
| アップロード | 見積書_0807.pdf をアップロードしました | 対象名 |
| 差し替え | 見積書_0807.pdf を差し替えました(Rev.3) | 結合で導出 |
| 改名 | 旧名.pdf を 新名.pdf に変更しました | 対応表 |
| 移動 | 見積書.pdf を 見積 へ移動しました | 結合で導出 |
| 版の復元 | 見積書.pdf を以前のバージョンに戻しました(Rev.2) | 対応表 |
| ロールの変更 | 田中 太郎 さんのロールを Uploader から Editor に変更しました | 対応表 |
| 共有リンクの発行 | 見積書.pdf の共有リンクを発行しました(期限: 7 日) | 対応表 |
| コメント | 見積書.pdf にコメントしました: 「承認しました」 | 専用の欄 |
| ゴミ箱を空にする | ゴミ箱を空にしました(12 件) | 対応表 |
| 共有リンク経由のアクセス | 共有リンク経由でアクセスがありました(受取人: t***@example.com) | 引き直し + 伏せ字 |
52 種すべてに文があり、網羅は自動テストで固定しています。値が取れなかったときは例外を投げず、退化した文に落とします(「別のフォルダへ移動しました」のように、取れた情報だけで成り立つ文)。未知の操作が来たときも、「○○さんが操作を行いました(操作名)」の受け皿の文になります。行が消えるより、情報が少なくても行が並ぶほうがよいという判断です。
タイムラインの画面Timeline UI
Box 詳細の 3 番目のタブです。1 行は「誰が・何をした」の文と、その下に小さくアイコンと相対時刻が並ぶ 2 段構成です。
行の作り
- 1 行目: 操作した人の名前(社外なら社外バッジ)+ 文 + 対象へのリンク。リンクを押すと、ファイル詳細・そのフォルダ・メンバータブ・共有リンクタブ・コメントの位置へ飛びます。
- 2 行目: 操作の種別を表すアイコン(14px)と相対時刻。時刻にカーソルを載せると絶対時刻が出ます。
- アバター: 人以外が操作した行(共有リンク・ファイルリクエスト・システム)は、アバターの代わりに丸いアイコンのタイルに置き換わります。表示名は「共有リンク受取人(
t***@example.com)」のように、種類と伏せ字を併記します。 - 削除済みの利用者: 頭文字を出さないニュートラルなアバターと「削除済みユーザー」。消えた名前を頭文字で復元しません。
日付の区切り
「今日」「昨日」「8月7日(木)」「2025年12月3日」(年を跨ぐときだけ年付き)で区切ります。区切りは日本時間です。見出しは画面上部に貼り付いて追従するので、長くスクロールしても「いつの話か」を見失いません。
絞り込み 3 種
| 絞り込み | 選択肢と挙動 |
|---|---|
| 期間 | 全期間(既定)/直近 7 日/直近 30 日/期間を指定(カレンダー)。既定を「直近 7 日」にしない理由は、静かな Box で初めて開いた人が空を見るほうが害が大きいためです。量は続きの読み込みが吸収します |
| 操作の種別 | カテゴリ見出し付きのチェックリスト。カテゴリごとの一括チェックができ、一部だけ選んだ状態は中間表示になります。選択肢の日本語は共有の定数から導出し、画面側に並行の一覧を作りません |
| 操作者 | Box のメンバーから検索して選択。アバター・名前・社外バッジを表示。タイムラインのアバターを押すと、その人で絞り込みが入ります |
絞り込みが 1 つでも効いているときは、ツールバーのすぐ下に取り外せるラベルが並び、「すべてクリア」も置きます。いま何で絞っているかが分かり、1 タップで戻せる形です。脱退したメンバーの行から絞り込んだ場合など、名前を解決できない相手は「指定の操作者」と表示します。
URL に何を載せ、何を載せないか
表示中のビュー・期間・操作の種別・操作者は URL に載ります。URL を渡せば「あの期間の、あの人の操作」を別のメンバーにそのまま見せられます。
一方、続きの位置を示す印は載せません。載せると「途中から始まる不完全な一覧」を共有してしまうためです。
続きの読み込みと終端
- 1 ページ 30 件。末端に近づくと自動で次を読み、押せるボタンも常設します(キーボードだけで操作する場合に、自動発火に頼らせないため)。
- これ以上ない場合は「これ以上表示できるアクティビティはありません」と中立に示します。「2 年より前は保管庫にあります」といった説明は置いていません(Box のメンバーにとっては操作できない話で、置くと不要な問い合わせを生むため)。
- 500 件を超えても表示のための特別な仕組み(仮想スクロール)は入れていません。実測で十分に動いており、必要が出た時点で入れる方針です。
アクセス履歴を Owner 限定にする理由Owner-Only Access Log
ダウンロード・プレビュー・一括ダウンロード・共有リンク経由のアクセスの 4 種は、Box の Owner だけが見られます。
1 つの Box には、社内の担当者と、複数の取引先の担当者が同時に入ります。ここでダウンロードの履歴を全員に見せると、取引先 A が取引先 B の行動を観察できる状態になります。「どの見積を、いつ、何回開いたか」は商談の状況を推測させる情報で、業務上も心理的にも共有すべきものではありません。
一方で、Box の中で何が持ち出されたかを把握する責任は Owner にあります。だから Owner には出します。この境界は関数の中で固定されており、システム管理者であってもその Box の Owner でなければ開けません(全社の立場で見る場合は、監査ログの側が正しい経路です)。
- サブタブは Owner にしか描画しません。非 Owner が URL で直接開こうとした場合は、エラーを見せずにメインのビューへ静かに寄せます(「そこに何かある」ことすら匂わせない)。
- 共有リンク経由のアクセスの受取人は、常に伏せ字です。完全な開示はシステム管理者の経路だけです。
- サブタブの下には「ダウンロード・プレビュー・共有リンク経由のアクセスの記録です。Box オーナーのみが閲覧できます。」という 1 行を常設しています。誰に見えているかを、見ている本人にも明示する意図です。
CSV の取り出しOwner Export
Owner は、いま見ている条件をそのまま持ち出せます。理由の入力は要りません(自分の Box の業務データだからです)。
10 列。表計算で開ける形にする
全社の監査ログの CSV(19 列)とは意図的に別物にしています。あちらは機械で読み直す監査の成果物、こちらは人が表計算で開いて確認する業務の記録です。
| 項目 | 全社の監査ログ | Box のアクティビティ |
|---|---|---|
| 列数 | 19 | 10 |
| 時刻 | 世界標準時(ISO 表記) | 日本時間(yyyy-MM-dd HH:mm:ss) |
| 文字コードの目印 | 付けない | 付ける |
| 圧縮 | する | しない |
| 個人情報 | 生のまま(IP・端末情報を含む) | 伏せ字のみ。IP・端末情報は列が存在しない |
| 理由の入力 | 必須(10〜256 文字) | 不要 |
| 想定する使い方 | 別のシステムへ取り込む・長期保管 | その場で開いて確認する・関係者へ渡す |
日本時間・目印つき・非圧縮にしているのは、ダブルクリックで開いて文字化けしないためです。列名にも時刻の帯を書き込んで(occurred_at_jst)、どの時刻で読むべきかがファイル単体で分かるようにしています。
10 列の中身は、時刻・操作名・操作名の日本語・操作者名・社内か社外か・対象の種類・対象名・受取人(伏せ字)・提出者(伏せ字)・詳細です。最後の「詳細」には、対応表を通った項目を「項目名=値」の形でつないだ文字列が入ります。日本語の文章は作りません(文章の置き場を 2 つにしないため)。
その場で流すか、メールで届けるか
- 期間を指定していて、その長さが 31 日以下なら、その場でダウンロードが始まります。
- それ以外(全期間を含む)は、バッチへ渡します。202 で受け付けたあとダイアログは進行の表示に変わり、閉じても構いません(完了時にメールとアプリ内通知が届きます)。
- 完了メールから戻ると、URL に依頼の識別子が付いていて同じダイアログが再開します。成功していればその場でダウンロードできます。
- 保存期間(7 日)を過ぎた依頼から戻った場合は、期限切れの表示と再取り出しの導線を出します。何も起きない、という終わり方にしません。
- 1 時間の上限(5 回)に達した場合は、ダイアログの中に「1 時間にエクスポートできる回数の上限(5 回)に達しました」と出します。
読み上げと操作への配慮Accessibility
この画面は「読む」ことが主目的なので、読み上げ環境での辿りやすさに寄せています。
| 対象 | どうしたか | なぜ |
|---|---|---|
| タイムライン全体 | 更新され続ける記事の列として扱い、各行を記事として印を付ける。行には表示文をそのまま読み上げ用の名前として持たせる | 行の途中で読み飛ばされず、1 行ずつ辿れる |
| 日付の区切り | 見出しの要素で構成し、区切りそのものに名前を付けない | 名前を付けると「領域」として扱われ、日付の数だけ領域が増殖します。日付の移動は見出しの移動で足りる |
| 続きの読み込み | 自動の発火に加えて、常にボタンを置く | キーボードだけの操作でも、末端まで到達できる |
| 相対時刻 | 機械が読める時刻の属性を付け、絶対時刻はカーソルを載せたときの説明として関連付ける | 「3 分前」だけでは正確な時刻が分からない。正確な時刻が要る用途は CSV が担う |
| サブタブ | 左右キーで移動でき、選択状態を明示する | ほかのタブ(Box のタブ・ファイル詳細)と同じ操作感にそろえる |
| 種類の伝え方 | 色だけで伝えない。アイコンと文章が常に伴う | 色を識別しにくい環境でも、種類が読み取れる |
画面の幅が狭いときは、3 つの絞り込みを 1 つのボタンに畳んで下から出るシートにまとめます。シートの中身は横並びのときとまったく同じ実装で、幅ごとに別の作りを持たせていません。CSV のボタンだけはツールバーの右端に残します。