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