]> git.mxchange.org Git - friendica.git/blob - src/Model/FileTag.php
Merge pull request #8148 from annando/remote-comment
[friendica.git] / src / Model / FileTag.php
1 <?php
2 /**
3  * @file src/Model/FileTag.php
4  */
5
6 namespace Friendica\Model;
7
8 use Friendica\Database\DBA;
9 use Friendica\DI;
10
11 /**
12  * This class handles FileTag related functions
13  *
14  * post categories and "save to file" use the same item.file table for storage.
15  * We will differentiate the different uses by wrapping categories in angle brackets
16  * and save to file categories in square brackets.
17  * To do this we need to escape these characters if they appear in our tag.
18  */
19 class FileTag
20 {
21         /**
22          * URL encode <, >, left and right brackets
23          *
24          * @param string $s String to be URL encoded.
25          *
26          * @return string   The URL encoded string.
27          */
28         public static function encode($s)
29         {
30                 return str_replace(['<', '>', '[', ']'], ['%3c', '%3e', '%5b', '%5d'], $s);
31         }
32
33         /**
34          * URL decode <, >, left and right brackets
35          *
36          * @param string $s The URL encoded string to be decoded
37          *
38          * @return string   The decoded string.
39          */
40         public static function decode($s)
41         {
42                 return str_replace(['%3c', '%3e', '%5b', '%5d'], ['<', '>', '[', ']'], $s);
43         }
44
45         /**
46          * Query files for tag
47          *
48          * @param string $table The table to be queired.
49          * @param string $s     The search term
50          * @param string $type  Optional file type.
51          *
52          * @return string       Query string.
53          */
54         public static function fileQuery($table, $s, $type = 'file')
55         {
56                 if ($type == 'file') {
57                         $str = preg_quote('[' . str_replace('%', '%%', self::encode($s)) . ']');
58                 } else {
59                         $str = preg_quote('<' . str_replace('%', '%%', self::encode($s)) . '>');
60                 }
61
62                 return " AND " . (($table) ? DBA::escape($table) . '.' : '') . "file regexp '" . DBA::escape($str) . "' ";
63         }
64
65         /**
66          * Get file tags from array
67          *
68          * ex. given [music,video] return <music><video> or [music][video]
69          *
70          * @param array  $array A list of tags.
71          * @param string $type  Optional file type.
72          *
73          * @return string       A list of file tags.
74          */
75         public static function arrayToFile(array $array, string $type = 'file')
76         {
77                 $tag_list = '';
78                 if ($type == 'file') {
79                         $lbracket = '[';
80                         $rbracket = ']';
81                 } else {
82                         $lbracket = '<';
83                         $rbracket = '>';
84                 }
85
86                 foreach ($array as $item) {
87                         if (strlen($item)) {
88                                 $tag_list .= $lbracket . self::encode(trim($item)) . $rbracket;
89                         }
90                 }
91
92                 return $tag_list;
93         }
94
95         /**
96          * Get tag list from file tags
97          *
98          * ex. given <music><video>[friends], return [music,video] or [friends]
99          *
100          * @param string $file File tags
101          * @param string $type Optional file type.
102          *
103          * @return array        List of tag names.
104          */
105         public static function fileToArray(string $file, string $type = 'file')
106         {
107                 $matches = [];
108                 $return = [];
109
110                 if ($type == 'file') {
111                         $cnt = preg_match_all('/\[(.*?)\]/', $file, $matches, PREG_SET_ORDER);
112                 } else {
113                         $cnt = preg_match_all('/<(.*?)>/', $file, $matches, PREG_SET_ORDER);
114                 }
115
116                 if ($cnt) {
117                         foreach ($matches as $match) {
118                                 $return[] = self::decode($match[1]);
119                         }
120                 }
121
122                 return $return;
123         }
124
125         /**
126          * Get file tags from list
127          *
128          * ex. given music,video return <music><video> or [music][video]
129          * @param string $list A comma delimited list of tags.
130          * @param string $type Optional file type.
131          *
132          * @return string       A list of file tags.
133          * @deprecated since 2019.06 use arrayToFile() instead
134          */
135         public static function listToFile(string $list, string $type = 'file')
136         {
137                 $list_array = explode(',', $list);
138
139                 return self::arrayToFile($list_array, $type);
140         }
141
142         /**
143          * Get list from file tags
144          *
145          * ex. given <music><video>[friends], return music,video or friends
146          * @param string $file File tags
147          * @param string $type Optional file type.
148          *
149          * @return string       Comma delimited list of tag names.
150          * @deprecated since 2019.06 use fileToArray() instead
151          */
152         public static function fileToList(string $file, $type = 'file')
153         {
154                 return implode(',', self::fileToArray($file, $type));
155         }
156
157         /**
158          * Update file tags in PConfig
159          *
160          * @param int    $uid      Unique Identity.
161          * @param string $file_old Categories previously associated with an item
162          * @param string $file_new New list of categories for an item
163          * @param string $type     Optional file type.
164          *
165          * @return boolean          A value indicating success or failure.
166          * @throws \Exception
167          */
168         public static function updatePconfig(int $uid, string $file_old, string $file_new, string $type = 'file')
169         {
170                 if (!intval($uid)) {
171                         return false;
172                 } elseif ($file_old == $file_new) {
173                         return true;
174                 }
175
176                 $saved = DI::pConfig()->get($uid, 'system', 'filetags');
177
178                 if (strlen($saved)) {
179                         if ($type == 'file') {
180                                 $lbracket = '[';
181                                 $rbracket = ']';
182                                 $termtype = TERM_FILE;
183                         } else {
184                                 $lbracket = '<';
185                                 $rbracket = '>';
186                                 $termtype = TERM_CATEGORY;
187                         }
188
189                         $filetags_updated = $saved;
190
191                         // check for new tags to be added as filetags in pconfig
192                         $new_tags = [];
193                         foreach (self::fileToArray($file_new, $type) as $tag) {
194                                 if (!stristr($saved, $lbracket . self::encode($tag) . $rbracket)) {
195                                         $new_tags[] = $tag;
196                                 }
197                         }
198
199                         $filetags_updated .= self::arrayToFile($new_tags, $type);
200
201                         // check for deleted tags to be removed from filetags in pconfig
202                         $deleted_tags = [];
203                         foreach (self::fileToArray($file_old, $type) as $tag) {
204                                 if (!stristr($file_new, $lbracket . self::encode($tag) . $rbracket)) {
205                                         $deleted_tags[] = $tag;
206                                 }
207                         }
208
209                         foreach ($deleted_tags as $key => $tag) {
210                                 $r = q("SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d",
211                                         DBA::escape($tag),
212                                         intval(Term::OBJECT_TYPE_POST),
213                                         intval($termtype),
214                                         intval($uid));
215
216                                 if (DBA::isResult($r)) {
217                                         unset($deleted_tags[$key]);
218                                 } else {
219                                         $filetags_updated = str_replace($lbracket . self::encode($tag) . $rbracket, '', $filetags_updated);
220                                 }
221                         }
222
223                         if ($saved != $filetags_updated) {
224                                 DI::pConfig()->set($uid, 'system', 'filetags', $filetags_updated);
225                         }
226
227                         return true;
228                 } elseif (strlen($file_new)) {
229                         DI::pConfig()->set($uid, 'system', 'filetags', $file_new);
230                 }
231
232                 return true;
233         }
234
235         /**
236          * Add tag to file
237          *
238          * @param int    $uid     Unique identity.
239          * @param int    $item_id Item identity.
240          * @param string $file    File tag.
241          *
242          * @return boolean      A value indicating success or failure.
243          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
244          */
245         public static function saveFile($uid, $item_id, $file)
246         {
247                 if (!intval($uid)) {
248                         return false;
249                 }
250
251                 $item = Item::selectFirst(['file'], ['id' => $item_id, 'uid' => $uid]);
252                 if (DBA::isResult($item)) {
253                         if (!stristr($item['file'], '[' . self::encode($file) . ']')) {
254                                 $fields = ['file' => $item['file'] . '[' . self::encode($file) . ']'];
255                                 Item::update($fields, ['id' => $item_id]);
256                         }
257
258                         $saved = DI::pConfig()->get($uid, 'system', 'filetags');
259
260                         if (!strlen($saved) || !stristr($saved, '[' . self::encode($file) . ']')) {
261                                 DI::pConfig()->set($uid, 'system', 'filetags', $saved . '[' . self::encode($file) . ']');
262                         }
263
264                         info(DI::l10n()->t('Item filed'));
265                 }
266
267                 return true;
268         }
269
270         /**
271          * Remove tag from file
272          *
273          * @param int     $uid     Unique identity.
274          * @param int     $item_id Item identity.
275          * @param string  $file    File tag.
276          * @param boolean $cat     Optional value indicating the term type (i.e. Category or File)
277          *
278          * @return boolean      A value indicating success or failure.
279          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
280          */
281         public static function unsaveFile($uid, $item_id, $file, $cat = false)
282         {
283                 if (!intval($uid)) {
284                         return false;
285                 }
286
287                 if ($cat == true) {
288                         $pattern = '<' . self::encode($file) . '>';
289                         $termtype = Term::CATEGORY;
290                 } else {
291                         $pattern = '[' . self::encode($file) . ']';
292                         $termtype = Term::FILE;
293                 }
294
295                 $item = Item::selectFirst(['file'], ['id' => $item_id, 'uid' => $uid]);
296
297                 if (!DBA::isResult($item)) {
298                         return false;
299                 }
300
301                 $fields = ['file' => str_replace($pattern, '', $item['file'])];
302
303                 Item::update($fields, ['id' => $item_id]);
304
305                 $r = q("SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d",
306                         DBA::escape($file),
307                         intval(Term::OBJECT_TYPE_POST),
308                         intval($termtype),
309                         intval($uid)
310                 );
311
312                 if (!DBA::isResult($r)) {
313                         $saved = DI::pConfig()->get($uid, 'system', 'filetags');
314                         DI::pConfig()->set($uid, 'system', 'filetags', str_replace($pattern, '', $saved));
315                 }
316
317                 return true;
318         }
319 }