Issue 10105: Use legacy photo data field if used
[friendica.git/.git] / src / Model / Attach.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Model;
23
24 use Friendica\Core\System;
25 use Friendica\Database\DBA;
26 use Friendica\Database\DBStructure;
27 use Friendica\DI;
28 use Friendica\Object\Image;
29 use Friendica\Util\DateTimeFormat;
30 use Friendica\Util\Mimetype;
31 use Friendica\Security\Security;
32
33 /**
34  * Class to handle attach dabatase table
35  */
36 class Attach
37 {
38
39         /**
40          * Return a list of fields that are associated with the attach table
41          *
42          * @return array field list
43          * @throws \Exception
44          */
45         private static function getFields()
46         {
47                 $allfields = DBStructure::definition(DI::app()->getBasePath(), false);
48                 $fields = array_keys($allfields['attach']['fields']);
49                 array_splice($fields, array_search('data', $fields), 1);
50                 return $fields;
51         }
52
53         /**
54          * Select rows from the attach table and return them as array
55          *
56          * @param array $fields     Array of selected fields, empty for all
57          * @param array $conditions Array of fields for conditions
58          * @param array $params     Array of several parameters
59          *
60          * @return array
61          *
62          * @throws \Exception
63          * @see   \Friendica\Database\DBA::selectToArray
64          */
65         public static function selectToArray(array $fields = [], array $conditions = [], array $params = [])
66         {
67                 if (empty($fields)) {
68                         $fields = self::getFields();
69                 }
70
71                 return DBA::selectToArray('attach', $fields, $conditions, $params);
72         }
73
74         /**
75          * Retrieve a single record from the attach table
76          *
77          * @param array $fields     Array of selected fields, empty for all
78          * @param array $conditions Array of fields for conditions
79          * @param array $params     Array of several parameters
80          *
81          * @return bool|array
82          *
83          * @throws \Exception
84          * @see   \Friendica\Database\DBA::select
85          */
86         public static function selectFirst(array $fields = [], array $conditions = [], array $params = [])
87         {
88                 if (empty($fields)) {
89                         $fields = self::getFields();
90                 }
91
92                 return DBA::selectFirst('attach', $fields, $conditions, $params);
93         }
94
95         /**
96          * Check if attachment with given conditions exists
97          *
98          * @param array $conditions Array of extra conditions
99          *
100          * @return boolean
101          * @throws \Exception
102          */
103         public static function exists(array $conditions)
104         {
105                 return DBA::exists('attach', $conditions);
106         }
107
108         /**
109          * Retrive a single record given the ID
110          *
111          * @param int $id Row id of the record
112          *
113          * @return bool|array
114          *
115          * @throws \Exception
116          * @see   \Friendica\Database\DBA::select
117          */
118         public static function getById($id)
119         {
120                 return self::selectFirst([], ['id' => $id]);
121         }
122
123         /**
124          * Retrive a single record given the ID
125          *
126          * @param int $id Row id of the record
127          *
128          * @return bool|array
129          *
130          * @throws \Exception
131          * @see   \Friendica\Database\DBA::select
132          */
133         public static function getByIdWithPermission($id)
134         {
135                 $r = self::selectFirst(['uid'], ['id' => $id]);
136                 if ($r === false) {
137                         return false;
138                 }
139
140                 $sql_acl = Security::getPermissionsSQLByUserId($r['uid']);
141
142                 $conditions = [
143                         '`id` = ?' . $sql_acl,
144                         $id
145                 ];
146
147                 $item = self::selectFirst([], $conditions);
148
149                 return $item;
150         }
151
152         /**
153          * Get file data for given row id. null if row id does not exist
154          *
155          * @param array $item Attachment data. Needs at least 'id', 'backend-class', 'backend-ref'
156          *
157          * @return string  file data
158          * @throws \Exception
159          */
160         public static function getData($item)
161         {
162                 if (!empty($item['data'])) {
163                         return $item['data'];
164                 }
165
166                 $backendClass = DI::storageManager()->getByName($item['backend-class'] ?? '');
167                 if (empty($backendClass)) {
168                         // legacy data storage in 'data' column
169                         $i = self::selectFirst(['data'], ['id' => $item['id']]);
170                         if ($i === false) {
171                                 return null;
172                         }
173                         return $i['data'];
174                 } else {
175                         $backendRef = $item['backend-ref'];
176                         return $backendClass->get($backendRef);
177                 }
178         }
179
180         /**
181          * Store new file metadata in db and binary in default backend
182          *
183          * @param string  $data      Binary data
184          * @param integer $uid       User ID
185          * @param string  $filename  Filename
186          * @param string  $filetype  Mimetype. optional, default = ''
187          * @param integer $filesize  File size in bytes. optional, default = null
188          * @param string  $allow_cid Permissions, allowed contacts. optional, default = ''
189          * @param string  $allow_gid Permissions, allowed groups. optional, default = ''
190          * @param string  $deny_cid  Permissions, denied contacts.optional, default = ''
191          * @param string  $deny_gid  Permissions, denied greoup.optional, default = ''
192          *
193          * @return boolean/integer Row id on success, False on errors
194          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
195          */
196         public static function store($data, $uid, $filename, $filetype = '' , $filesize = null, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '')
197         {
198                 if ($filetype === '') {
199                         $filetype = Mimetype::getContentType($filename);
200                 }
201
202                 if (is_null($filesize)) {
203                         $filesize = strlen($data);
204                 }
205
206                 $backend_ref = DI::storage()->put($data);
207                 $data = '';
208
209                 $hash = System::createGUID(64);
210                 $created = DateTimeFormat::utcNow();
211
212                 $fields = [
213                         'uid' => $uid,
214                         'hash' => $hash,
215                         'filename' => $filename,
216                         'filetype' => $filetype,
217                         'filesize' => $filesize,
218                         'data' => $data,
219                         'created' => $created,
220                         'edited' => $created,
221                         'allow_cid' => $allow_cid,
222                         'allow_gid' => $allow_gid,
223                         'deny_cid' => $deny_cid,
224                         'deny_gid' => $deny_gid,
225                         'backend-class' => (string)DI::storage(),
226                         'backend-ref' => $backend_ref
227                 ];
228
229                 $r = DBA::insert('attach', $fields);
230                 if ($r === true) {
231                         return DBA::lastInsertId();
232                 }
233                 return $r;
234         }
235
236         /**
237          * Store new file metadata in db and binary in default backend from existing file
238          *
239          * @param        $src
240          * @param        $uid
241          * @param string $filename
242          * @param string $allow_cid
243          * @param string $allow_gid
244          * @param string $deny_cid
245          * @param string $deny_gid
246          * @return boolean True on success
247          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
248          */
249         public static function storeFile($src, $uid, $filename = '', $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '')
250         {
251                 if ($filename === '') {
252                         $filename = basename($src);
253                 }
254
255                 $data = @file_get_contents($src);
256
257                 return self::store($data, $uid, $filename, '', null, $allow_cid, $allow_gid,  $deny_cid, $deny_gid);
258         }
259
260
261         /**
262          * Update an attached file
263          *
264          * @param array         $fields     Contains the fields that are updated
265          * @param array         $conditions Condition array with the key values
266          * @param Image         $img        Image data to update. Optional, default null.
267          * @param array|boolean $old_fields Array with the old field values that are about to be replaced (true = update on duplicate)
268          *
269          * @return boolean  Was the update successful?
270          *
271          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
272          * @see   \Friendica\Database\DBA::update
273          */
274         public static function update($fields, $conditions, Image $img = null, array $old_fields = [])
275         {
276                 if (!is_null($img)) {
277                         // get items to update
278                         $items = self::selectToArray(['backend-class','backend-ref'], $conditions);
279
280                         foreach($items as $item) {
281                                 $backend_class = DI::storageManager()->getByName($item['backend-class'] ?? '');
282                                 if (!empty($backend_class)) {
283                                         $fields['backend-ref'] = $backend_class->put($img->asString(), $item['backend-ref'] ?? '');
284                                 } else {
285                                         $fields['data'] = $img->asString();
286                                 }
287                         }
288                 }
289
290                 $fields['edited'] = DateTimeFormat::utcNow();
291
292                 return DBA::update('attach', $fields, $conditions, $old_fields);
293         }
294
295
296         /**
297          * Delete info from table and data from storage
298          *
299          * @param array $conditions Field condition(s)
300          * @param array $options    Options array, Optional
301          *
302          * @return boolean
303          *
304          * @throws \Exception
305          * @see   \Friendica\Database\DBA::delete
306          */
307         public static function delete(array $conditions, array $options = [])
308         {
309                 // get items to delete data info
310                 $items = self::selectToArray(['backend-class','backend-ref'], $conditions);
311
312                 foreach($items as $item) {
313                         $backend_class = DI::storageManager()->getByName($item['backend-class'] ?? '');
314                         if (!empty($backend_class)) {
315                                 $backend_class->delete($item['backend-ref'] ?? '');
316                         }
317                 }
318
319                 return DBA::delete('attach', $conditions, $options);
320         }
321 }