Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
Item.php
1<?php
3
5use Bitrix\UI\Avatar;
6use SebastianBergmann\CodeCoverage\Report\PHP;
7
8class Item
9{
10 public const MAX_SIDE_SIZE = 1024; // in Pixels
11 public const MAX_FILE_SIZE = 600000; // in Bites
12 public const FILE_TYPES = ['png', 'gif'];
13 public const ORM_OPERATION_LIMIT_COUNT = 2;
14 private static array $buffer = [];
15 protected int $id;
16 protected array $data;
18
19 public function __construct(int $id)
20 {
21 if ($id > 0 && ($this->data = Avatar\Model\ItemTable::getById($id)->fetch()))
22 {
23 $this->id = $id;
24 if (is_subclass_of($this->data['OWNER_TYPE'], Owner\DefaultOwner::class))
25 {
26 $this->owner = new $this->data['OWNER_TYPE']($this->data['OWNER_ID']);
27 }
28 else
29 {
30 throw new Main\ArgumentTypeException("Mask owner ({$this->data['OWNER_TYPE']}) is unreachable.");
31 }
32 }
33 else
34 {
35 throw new Main\ObjectNotFoundException("Mask id ($id) is not found.");
36 }
37 }
38
39 public function getId(): int
40 {
41 return $this->id;
42 }
43
44 public function isEditableBy(Avatar\Mask\Consumer $consumer): bool
45 {
46 if ($consumer->isAdmin())
47 {
48 return true;
49 }
50 if ($this->getOwner() instanceof Owner\User && $this->getOwner()->getId() === $this->getId())
51 {
52 return true;
53 }
54 return false;
55 }
56
57 public function isReadableBy(Avatar\Mask\Consumer $consumer): bool
58 {
59 if ($consumer->isAdmin())
60 {
61 return true;
62 }
63 if ($this->getOwner() instanceof Owner\User && $this->getOwner()->getId() === $consumer->getId())
64 {
65 return true;
66 }
67 if (Avatar\Model\AccessTable::getList([
68 'select' => ['ID'],
69 'filter' => [
70 '=ITEM_ID' => $this->getId(),
71 '=ACCESS_CODE' => $consumer->getAccessCodes()
72 ]
73 ])->fetch())
74 {
75 return true;
76 }
77 return false;
78 }
79
80 public function update(array $data): Main\Result
81 {
82 $dataToSave = array_intersect_key($data, ['TITLE' => null, 'DESCRIPTION' => null]);
83 if (array_key_exists('FILE', $data)
84 && !empty($data['FILE'])
85 )
86 {
87 $file = $data['FILE'] + [
88 'MODULE_ID' => 'ui',
89 'del' => 'Y',
90 'old_file' => $this->data['FILE_ID'],
91 ];
92
93 if ($fileId = \CFile::SaveFile($file, 'ui/mask'))
94 {
95 $dataToSave['FILE_ID'] = $fileId;
96 }
97 }
98 if (!empty($dataToSave))
99 {
100 Avatar\Model\ItemTable::update($this->getId(), $dataToSave);
101 }
102
103 if (array_key_exists('ACCESS_CODE', $data))
104 {
105 $this->setAccessCode($data['ACCESS_CODE']);
106 }
107
108 return new Main\Result();
109 }
110
111 public function delete(): Main\Result
112 {
113 return static::deleteByFilter(['ID' => $this->getId()]);
114 }
115
116 public function getOwner(): Owner\DefaultOwner
117 {
118 return $this->owner;
119 }
120
121 public function getAccessCode(): array
122 {
123 return array_column(Avatar\Model\AccessTable::getList([
124 'select' => ['ACCESS_CODE'],
125 'filter' => ['ITEM_ID' => $this->getId()]
126 ])->fetchAll(), 'ACCESS_CODE');
127 }
128
129 protected function setAccessCode(array $accessCodes)
130 {
131 Avatar\Model\AccessTable::deleteByFilter(['ITEM_ID' => $this->getId()]);
132 if (!empty($accessCodes))
133 {
134 $itemId = $this->getId();
135 Avatar\Model\AccessTable::addMulti(array_map(function($accessCode) use ($itemId) {
136 return ['ITEM_ID' => $itemId, 'ACCESS_CODE' => $accessCode];
137 }, $accessCodes), true);
138 }
139 }
140
141 public function applyToFileBy(int $originalFileId, int $fileId, Avatar\Mask\Consumer $consumer)
142 {
143
144 return Avatar\Model\ItemToFileTable::add([
145 'ITEM_ID' => $this->id,
146 'ORIGINAL_FILE_ID' => $originalFileId,
147 'FILE_ID' => $fileId,
148 'USER_ID' => $consumer->getUserId()
149 ]);
150 }
151
152 public static function create(Owner\DefaultOwner $owner, array $file, ?array $descriptionParams = []): Main\Entity\AddResult
153 {
154 $result = new Main\Entity\AddResult();
155 $fileCheckResult = \CFile::CheckImageFile($file,
156 static::MAX_FILE_SIZE,
157 static::MAX_SIDE_SIZE,
158 static::MAX_SIDE_SIZE,
159 ['png', 'svg']
160 );
161 if ($fileCheckResult === null)
162 {
163 $file['MODULE_ID'] = 'ui';
164
165 if ($fileId = \CFile::SaveFile($file, 'ui/mask'))
166 {
167 $result = Avatar\Model\ItemTable::add([
168 'OWNER_TYPE' => get_class($owner),
169 'OWNER_ID' => $owner->getId(),
170 'GROUP_ID' => $descriptionParams['GROUP_ID'] ?? null,
171 'TITLE' => $descriptionParams['TITLE'] ?? null,
172 'DESCRIPTION' => $descriptionParams['DESCRIPTION'] ?? null,
173 'SORT' => $descriptionParams['SORT'] ?? 0,
174 'FILE_ID' => $fileId
175 ]);
176
177 if ($result->isSuccess() && ($item = static::getInstance($result->getId())))
178 {
179 $item->setAccessCode($descriptionParams['ACCESS_CODE'] ?? $owner->getDefaultAccess());
180 $result->setData([$item]);
181 return $result;
182 }
183 \CFile::Delete($fileId);
184 }
185 }
186 $result->addError(new Main\Error($fileCheckResult, 'image check'));
187 return $result;
188 }
189
190 public static function getInstance($id): ?Item
191 {
192 try
193 {
194 return new static($id);
195 }
196 catch (Main\ObjectNotFoundException $exception)
197 {
198 return null;
199 }
200 }
201
202 public static function deleteByFilter(array $filter): Main\Entity\DeleteResult
203 {
204 $result = new Main\Entity\DeleteResult();
205
206 $connection = Main\Application::getConnection();
207 $sqlHelper = $connection->getSqlHelper();
208 $sqlItemTableName = Avatar\Model\ItemTable::getTableName();
209 $where = Main\ORM\Query\Query::buildFilterSql(Avatar\Model\ItemTable::getEntity(), $filter);
210 if (empty($where))
211 {
212 return $result;
213 }
214
215 $connection = \Bitrix\Main\Application::getConnection();
216 $sql = [
217 $connection->getSqlHelper()->getInsertIgnore(
218 'b_ui_avatar_mask_file_deleted',
219 ' (ENTITY, ORIGINAL_FILE_ID, FILE_ID, ITEM_ID) ',
220 <<<SQL
221SELECT 'ITEM_TEMP', FILE_ID, FILE_ID, ID
222 FROM {$sqlHelper->quote($sqlItemTableName)}
223 WHERE {$where}
224SQL
225 ),
226 $connection->getType() === 'mysql' ?
227 <<<MYSQL
228DELETE ACCESS1
229 FROM {$sqlHelper->quote(Avatar\Model\AccessTable::getTableName())} AS ACCESS1,
230 b_ui_avatar_mask_file_deleted AS FDTABLE
231 WHERE ACCESS1.ITEM_ID = FDTABLE.ITEM_ID AND FDTABLE.ENTITY = 'ITEM_TEMP'
232MYSQL : ($connection->getType() ==='pgsql' ?
233 <<<PGSQL
234DELETE FROM {$sqlHelper->quote(Avatar\Model\AccessTable::getTableName())} AS ACCESS1
235USING b_ui_avatar_mask_file_deleted AS FDTABLE
236WHERE ACCESS1.ITEM_ID = FDTABLE.ITEM_ID AND FDTABLE.ENTITY = 'ITEM_TEMP'
237PGSQL : ''),
238 $connection->getType() === 'mysql' ?
239 <<<MYSQL
240DELETE RECENTLYUSED1
241 FROM {$sqlHelper->quote(Avatar\Model\RecentlyUsedTable::getTableName())} AS RECENTLYUSED1,
242 b_ui_avatar_mask_file_deleted AS FDTABLE
243 WHERE RECENTLYUSED1.ITEM_ID = FDTABLE.ITEM_ID AND FDTABLE.ENTITY = 'ITEM_TEMP'
244MYSQL : ($connection->getType() ==='pgsql' ?
245 <<<PGSQL
246DELETE FROM {$sqlHelper->quote(Avatar\Model\RecentlyUsedTable::getTableName())} AS RECENTLYUSED1
247USING b_ui_avatar_mask_file_deleted AS FDTABLE WHERE RECENTLYUSED1.ITEM_ID = FDTABLE.ITEM_ID AND FDTABLE.ENTITY = 'ITEM_TEMP'
248PGSQL : ''),
249 $connection->getSqlHelper()->getInsertIgnore(
250 'b_ui_avatar_mask_file_deleted',
251 ' (ENTITY, ORIGINAL_FILE_ID, FILE_ID, ITEM_ID) ',
252 <<<SQL
253SELECT 'LINK', LINK1.ORIGINAL_FILE_ID, LINK1.FILE_ID, LINK1.ITEM_ID
254 FROM {$sqlHelper->quote(Avatar\Model\ItemToFileTable::getTableName())} AS LINK1,
255 b_ui_avatar_mask_file_deleted AS FDTABLE
256 WHERE LINK1.ITEM_ID = FDTABLE.ITEM_ID AND FDTABLE.ENTITY = 'ITEM_TEMP'
257SQL
258 ),
259 $connection->getType() === 'mysql' ?
260 <<<MYSQL
261DELETE LINK1
262 FROM {$sqlHelper->quote(Avatar\Model\ItemToFileTable::getTableName())} AS LINK1,
263 b_ui_avatar_mask_file_deleted AS FDTABLE
264 WHERE LINK1.ITEM_ID = FDTABLE.ITEM_ID AND FDTABLE.ENTITY = 'ITEM_TEMP'
265MYSQL : ($connection->getType() ==='pgsql' ?
266 <<<PGSQL
267DELETE FROM {$sqlHelper->quote(Avatar\Model\ItemToFileTable::getTableName())} AS LINK1
268USING b_ui_avatar_mask_file_deleted AS FDTABLE WHERE LINK1.ITEM_ID = FDTABLE.ITEM_ID AND FDTABLE.ENTITY = 'ITEM_TEMP'
269PGSQL : ''),
270 <<<SQL
271UPDATE b_ui_avatar_mask_file_deleted SET ENTITY = 'ITEM' WHERE ENTITY = 'ITEM_TEMP';
272SQL,
273 ];
274 $sql = implode(';' . PHP_EOL, $sql);
275
276 $connection->executeSqlBatch($sql);
277
278 $cleaningString = static::clearFileTable();
279 if ($cleaningString !== '')
280 {
281 \CAgent::AddAgent($cleaningString, 'ui','Y',0, '',
282 'Y', '', 100, false, false
283 );
284 }
285 return $result;
286 }
287
288 public static function clearFileTable(): string
289 {
290 $connection = Main\Application::getConnection();
291 $limit = self::ORM_OPERATION_LIMIT_COUNT;
292 $dbRes = $connection->query("
293 SELECT *
294 FROM b_ui_avatar_mask_file_deleted
295 WHERE ENTITY IN ('ITEM', 'LINK')
296 ORDER BY ID ASC
297 LIMIT {$limit}
298 "
299 );
300 $items = [];
301 if ($res = $dbRes->fetch())
302 {
303 do
304 {
305 self::$buffer[] = $res['FILE_ID'];
306 $items[] = $res['ITEM_ID'];
307 \CFile::Delete($res['FILE_ID']);
308 } while ($res = $dbRes->fetch());
309 $itemsSql = implode(',', $items);
310 $connection->queryExecute("DELETE FROM b_ui_avatar_mask_file_deleted WHERE ITEM_ID IN ({$itemsSql})");
311 }
312
313 if (count($items) < self::ORM_OPERATION_LIMIT_COUNT)
314 {
315 return '';
316 }
317 return '\\' . __CLASS__ . "::" . __FUNCTION__ . "();";
318 }
319
320 public static function onFileDelete($file)
321 {
322 if (in_array($file['ID'], self::$buffer))
323 {
324 return;
325 }
326 if ($file['MODULE_ID'] === 'ui')
327 {
328 Avatar\Model\ItemToFileTable::deleteByFilter(['=FILE_ID' => $file['ID']]);
329 }
330 else if ($file['MODULE_ID'] === 'main' && Avatar\Model\ItemToFileTable::getList([
331 'select' => ['FILE_ID'],
332 'filter' => ['=ORIGINAL_FILE_ID' => $file['ID']],
333 ])->fetch())
334 {
335 $connection = Main\Application::getConnection();
336 $sqlHelper = $connection->getSqlHelper();
337 $fileId = (int) $file['ID'];
338
339 $sql = [
340 $sqlHelper->getInsertIgnore(
341 'b_ui_avatar_mask_file_deleted',
342 '(ENTITY, ORIGINAL_FILE_ID, FILE_ID, ITEM_ID)',
343 <<<SQL
344SELECT 'LINK', ORIGINAL_FILE_ID, FILE_ID, ITEM_ID
345 FROM {$sqlHelper->quote(Avatar\Model\ItemToFileTable::getTableName())}
346 WHERE ORIGINAL_FILE_ID = {$fileId}
347SQL
348 ),
349 <<<SQL
350DELETE FROM {$sqlHelper->quote(Avatar\Model\ItemToFileTable::getTableName())} WHERE ORIGINAL_FILE_ID = {$fileId}
351SQL,
352 ];
353 $sql = implode(';' . PHP_EOL, $sql);
354
355 $connection->executeSqlBatch($sql);
356
357 $cleaningString = static::clearFileTable();
358 if ($cleaningString !== '')
359 {
360 \CAgent::AddAgent($cleaningString, 'ui','Y',0, '',
361 'Y', '', 100, false, false
362 );
363 }
364 }
365 }
366}
static getList(array $parameters=array())
isEditableBy(Avatar\Mask\Consumer $consumer)
Definition Item.php:44
const ORM_OPERATION_LIMIT_COUNT
Definition Item.php:13
Owner DefaultOwner $owner
Definition Item.php:17
static deleteByFilter(array $filter)
Definition Item.php:202
setAccessCode(array $accessCodes)
Definition Item.php:129
static getInstance($id)
Definition Item.php:190
update(array $data)
Definition Item.php:80
applyToFileBy(int $originalFileId, int $fileId, Avatar\Mask\Consumer $consumer)
Definition Item.php:141
static onFileDelete($file)
Definition Item.php:320
static create(Owner\DefaultOwner $owner, array $file, ?array $descriptionParams=[])
Definition Item.php:152
isReadableBy(Avatar\Mask\Consumer $consumer)
Definition Item.php:57