mediaプラグイン応用(11) アップロードユーザ情報の追加

Mediaプラグインは、「どのユーザがアップロードしたか」の情報を保管しません。
そのため、別のユーザがアップロードしたファイルであっても、IDを正しく指定すれば削除できてしまいます。

attachmentsテーブルにuser_idを追加して、ファイル保存時の登録とファイル削除時の権限チェックを追加しました。

ユーザID追加

user_idのカラムを追加して、attachmentsテーブルを作成しなおします。

mysql> DESCRIBE attachments;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id     | int(10) unsigned | NO   | MUL | NULL    |                |
| model       | varchar(255)     | NO   | MUL | NULL    |                |
| foreign_key | int(10)          | NO   |     | NULL    |                |
| dirname     | varchar(255)     | YES  |     | NULL    |                |
| basename    | varchar(255)     | NO   |     | NULL    |                |
| checksum    | varchar(255)     | NO   |     | NULL    |                |
| alternative | varchar(50)      | YES  |     | NULL    |                |
| group       | varchar(255)     | YES  |     | NULL    |                |
| created     | datetime         | YES  |     | NULL    |                |
| modified    | datetime         | YES  |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+
12 rows in set (0.00 sec)


$fieldListにuser_idカラムを追加します。
app/controllers/users_controller.php

        function edit_image() {
(中略)
            $fieldList = array(
+                'user_id',
                'model',
                'foreign_key',
                'dirname',
                'basename',
                'checksum',
                'group',
                'alternative',
                'file',
            );

            if ($this->User->saveAll($this->data, array(

Mediaプラグインのモデルに、Userとの関連付け($belongsTo)と、beforeSave()でuser_idのセットを追加します。
モデルからAuthコンポーネントの認証情報は参照が複雑になるので、セッション情報からユーザIDをセットしました。

app/plugins/media/models/attachment_ex.php

+       var $belongsTo = array(
+               'User' => array(
+                       'className' => 'User',
+                       'foreignKey' => 'user_id',
+                       'conditions' => '',
+                       'fields' => '',
+                       'order' => ''
+               )
+       );
(中略)
+       function beforeSave() {
+               if (!isset($this->data['Attachment']['delete'])) {
+                       // 登録者ID
+			if (isset($_SESSION['Auth']['User']['id']) &&
+				(!isset($this->data[$this->alias]['delete']) || $this->data[$this->alias]['delete'] === '0')
+                               $this->data[$this->alias]['user_id'] = $_SESSION['Auth']['User']['id'];
+                       }
+               }
+
+               return parent::beforeSave();
+       }

これで、attachmentsに実行者のユーザIDが登録されるようになります。

mysql> SELECT id, user_id, model, foreign_key, basename FROM attachments\G
*************************** 1. row ***************************
         id: 26
    user_id: 1
      model: User
foreign_key: 1
   basename: 4b876687-fefc-4fc4-9c1e-0a72c0a80b08.jpg
*************************** 2. row ***************************
         id: 27
    user_id: 25
      model: User
foreign_key: 25
   basename: 4b876695-6720-4aa3-ad7f-0a77c0a80b08.jpg
2 rows in set (0.00 sec)

削除権限チェック

削除前のユーザIDチェックは、MediaプラグインのモデルにbeforeDeleteを追加して、その中で行ないます。

+       function beforeDelete($cascade = true) {
+               if (!isset($this->data['Attachment']['id']) || empty($this->data['Attachment']['id'])) {
+                       return false;
+               }
+               $result = $this->find('first', array(
+                       'conditions' => array('id' => $this->data['Attachment']['id']),
+                       'fields'     => array('user_id'),
+                       'recursive'  => -1,
+               ));
+
+               if ($result["Attachment"]["user_id"] != $_SESSION['Auth']['User']['id']) {
+                       return false;
+               }
+
+               return parent::beforeDelete($cascade);
+       }

beforeDelete()でfalseを返すと、削除は行なわれませんがsaveAll()はエラーにならないので、これ単独ではコントローラ側でエラーメッセージ表示などは行なわれません。
今回は、エラーメッセージ表示は特に要らないと判断しましたが、メッセージが欲しい場合は、コントローラやバリデーション段階でのチェックの方が要ります。