Merge pull request #14250 from totten/master-select-null
[civicrm-core.git] / CRM / Core / BAO / File.php
CommitLineData
6a488035 1<?php
0c56e4c8
TO
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
0c56e4c8 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
0c56e4c8
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
0c56e4c8
TO
27
28/**
29 *
30 * @package CRM
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
0c56e4c8
TO
32 * $Id$
33 *
6a488035
TO
34 */
35
0c56e4c8
TO
36/**
37 * BAO object for crm_log table
38 */
39class CRM_Core_BAO_File extends CRM_Core_DAO_File {
40
518fa0ee 41 public static $_signableFields = ['entityTable', 'entityID', 'fileID'];
0c56e4c8 42
ae2c7e00 43 /**
44 * Takes an associative array and creates a File object.
45 *
46 * @param array $params
47 * (reference ) an assoc array of name/value pairs.
48 *
49 * @return CRM_Core_BAO_File
50 */
51 public static function create($params) {
52 $fileDAO = new CRM_Core_DAO_File();
53
54 $op = empty($params['id']) ? 'create' : 'edit';
55
56 CRM_Utils_Hook::pre($op, 'File', CRM_Utils_Array::value('id', $params), $params);
57
58 $fileDAO->copyValues($params);
59
60 if (empty($params['id']) && empty($params['created_id'])) {
61 $fileDAO->created_id = CRM_Core_Session::getLoggedInContactID();
62 }
63
64 $fileDAO->save();
65
66 CRM_Utils_Hook::post($op, 'File', $fileDAO->id, $fileDAO);
67
68 return $fileDAO;
69 }
70
0c56e4c8 71 /**
100fef9d
CW
72 * @param int $fileID
73 * @param int $entityID
0c56e4c8
TO
74 *
75 * @return array
76 */
9bbc34f1 77 public static function path($fileID, $entityID) {
0c56e4c8 78 $entityFileDAO = new CRM_Core_DAO_EntityFile();
0c56e4c8
TO
79 $entityFileDAO->entity_id = $entityID;
80 $entityFileDAO->file_id = $fileID;
81
82 if ($entityFileDAO->find(TRUE)) {
83 $fileDAO = new CRM_Core_DAO_File();
84 $fileDAO->id = $fileID;
85 if ($fileDAO->find(TRUE)) {
86 $config = CRM_Core_Config::singleton();
87 $path = $config->customFileUploadDir . $fileDAO->uri;
88
89 if (file_exists($path) && is_readable($path)) {
be2fb01f 90 return [$path, $fileDAO->mime_type];
0c56e4c8
TO
91 }
92 }
93 }
94
be2fb01f 95 return [NULL, NULL];
0c56e4c8
TO
96 }
97
0c56e4c8
TO
98 /**
99 * @param $data
100fef9d 100 * @param int $fileTypeID
0c56e4c8 101 * @param $entityTable
100fef9d 102 * @param int $entityID
0c56e4c8
TO
103 * @param $entitySubtype
104 * @param bool $overwrite
75fd1335 105 * @param null|array $fileParams
0c56e4c8
TO
106 * @param string $uploadName
107 * @param null $mimeType
108 *
109 * @throws Exception
110 */
ae5ffbb7 111 public static function filePostProcess(
0c56e4c8
TO
112 $data,
113 $fileTypeID,
114 $entityTable,
115 $entityID,
116 $entitySubtype,
117 $overwrite = TRUE,
118 $fileParams = NULL,
119 $uploadName = 'uploadFile',
120 $mimeType = NULL
121 ) {
122 if (!$mimeType) {
b93376a8 123 CRM_Core_Error::statusBounce(ts('Mime Type is now a required parameter for file upload'));
0c56e4c8
TO
124 }
125
126 $config = CRM_Core_Config::singleton();
127
128 $path = explode('/', $data);
129 $filename = $path[count($path) - 1];
130
131 // rename this file to go into the secure directory
132 if ($entitySubtype) {
133 $directoryName = $config->customFileUploadDir . $entitySubtype . DIRECTORY_SEPARATOR . $entityID;
134 }
135 else {
136 $directoryName = $config->customFileUploadDir;
137 }
138
139 CRM_Utils_File::createDir($directoryName);
140
141 if (!rename($data, $directoryName . DIRECTORY_SEPARATOR . $filename)) {
b93376a8 142 CRM_Core_Error::statusBounce(ts('Could not move custom file to custom upload directory'));
0c56e4c8
TO
143 }
144
145 // to get id's
146 if ($overwrite && $fileTypeID) {
147 list($sql, $params) = self::sql($entityTable, $entityID, $fileTypeID);
148 }
149 else {
150 list($sql, $params) = self::sql($entityTable, $entityID, 0);
151 }
152
153 $dao = CRM_Core_DAO::executeQuery($sql, $params);
154 $dao->fetch();
155
156 $fileDAO = new CRM_Core_DAO_File();
157 $op = 'create';
158 if (isset($dao->cfID) && $dao->cfID) {
159 $op = 'edit';
160 $fileDAO->id = $dao->cfID;
161 unlink($directoryName . DIRECTORY_SEPARATOR . $dao->uri);
162 }
163
164 if (!empty($fileParams)) {
165 $fileDAO->copyValues($fileParams);
166 }
167
168 $fileDAO->uri = $filename;
169 $fileDAO->mime_type = $mimeType;
170 $fileDAO->file_type_id = $fileTypeID;
77ca21d6 171 $fileDAO->upload_date = date('YmdHis');
0c56e4c8
TO
172 $fileDAO->save();
173
174 // need to add/update civicrm_entity_file
175 $entityFileDAO = new CRM_Core_DAO_EntityFile();
176 if (isset($dao->cefID) && $dao->cefID) {
177 $entityFileDAO->id = $dao->cefID;
178 }
179 $entityFileDAO->entity_table = $entityTable;
180 $entityFileDAO->entity_id = $entityID;
181 $entityFileDAO->file_id = $fileDAO->id;
182 $entityFileDAO->save();
183
184 //save static tags
185 if (!empty($fileParams['tag'])) {
186 CRM_Core_BAO_EntityTag::create($fileParams['tag'], 'civicrm_file', $entityFileDAO->id);
187 }
188
189 //save free tags
190 if (isset($fileParams['attachment_taglist']) && !empty($fileParams['attachment_taglist'])) {
1273d77c 191 CRM_Core_Form_Tag::postProcess($fileParams['attachment_taglist'], $entityFileDAO->id, 'civicrm_file');
0c56e4c8
TO
192 }
193
194 // lets call the post hook here so attachments code can do the right stuff
195 CRM_Utils_Hook::post($op, 'File', $fileDAO->id, $fileDAO);
196 }
197
198 /**
ad37ac8e 199 * A static function wrapper that deletes the various objects.
200 *
201 * Objects are those hat are connected to a file object (i.e. file, entityFile and customValue.
202 *
203 * @param int $fileID
204 * @param int $entityID
205 * @param int $fieldID
206 *
207 * @throws \Exception
0c56e4c8
TO
208 */
209 public static function deleteFileReferences($fileID, $entityID, $fieldID) {
210 $fileDAO = new CRM_Core_DAO_File();
211 $fileDAO->id = $fileID;
212 if (!$fileDAO->find(TRUE)) {
eea8ec44 213 throw new CRM_Core_Exception(ts('File not found'));
0c56e4c8
TO
214 }
215
216 // lets call a pre hook before the delete, so attachments hooks can get the info before things
217 // disappear
218 CRM_Utils_Hook::pre('delete', 'File', $fileID, $fileDAO);
219
220 // get the table and column name
221 list($tableName, $columnName, $groupID) = CRM_Core_BAO_CustomField::getTableColumnGroup($fieldID);
222
223 $entityFileDAO = new CRM_Core_DAO_EntityFile();
224 $entityFileDAO->file_id = $fileID;
225 $entityFileDAO->entity_id = $entityID;
226 $entityFileDAO->entity_table = $tableName;
227
228 if (!$entityFileDAO->find(TRUE)) {
eea8ec44 229 throw new CRM_Core_Exception(sprintf('No record found for given file ID - %d and entity ID - %d', $fileID, $entityID));
0c56e4c8
TO
230 }
231
232 $entityFileDAO->delete();
233 $fileDAO->delete();
234
235 // also set the value to null of the table and column
236 $query = "UPDATE $tableName SET $columnName = null WHERE $columnName = %1";
be2fb01f 237 $params = [1 => [$fileID, 'Integer']];
0c56e4c8
TO
238 CRM_Core_DAO::executeQuery($query, $params);
239 }
240
241 /**
242 * The $useWhere is used so that the signature matches the parent class
0527cd81 243 *
353ffa53
TO
244 * public function delete($useWhere = FALSE) {
245 * list($fileID, $entityID, $fieldID) = func_get_args();
246 *
247 * self::deleteFileReferences($fileID, $entityID, $fieldID);
248 * } */
0c56e4c8
TO
249
250 /**
ad37ac8e 251 * Delete all the files and associated object associated with this combination.
252 *
253 * @param string $entityTable
254 * @param int $entityID
255 * @param int $fileTypeID
256 * @param int $fileID
257 *
7b8bc7a1
SB
258 * @return bool
259 * Was file deleted?
0c56e4c8 260 */
00be9182 261 public static function deleteEntityFile($entityTable, $entityID, $fileTypeID = NULL, $fileID = NULL) {
7b8bc7a1 262 $isDeleted = FALSE;
0c56e4c8 263 if (empty($entityTable) || empty($entityID)) {
7b8bc7a1 264 return $isDeleted;
0c56e4c8
TO
265 }
266
267 $config = CRM_Core_Config::singleton();
268
269 list($sql, $params) = self::sql($entityTable, $entityID, $fileTypeID, $fileID);
270 $dao = CRM_Core_DAO::executeQuery($sql, $params);
271
be2fb01f
CW
272 $cfIDs = [];
273 $cefIDs = [];
0c56e4c8
TO
274 while ($dao->fetch()) {
275 $cfIDs[$dao->cfID] = $dao->uri;
276 $cefIDs[] = $dao->cefID;
277 }
278
279 if (!empty($cefIDs)) {
280 $cefIDs = implode(',', $cefIDs);
281 $sql = "DELETE FROM civicrm_entity_file where id IN ( $cefIDs )";
282 CRM_Core_DAO::executeQuery($sql);
7b8bc7a1 283 $isDeleted = TRUE;
0c56e4c8
TO
284 }
285
286 if (!empty($cfIDs)) {
287 // Delete file only if there no any entity using this file.
be2fb01f 288 $deleteFiles = [];
0c56e4c8
TO
289 foreach ($cfIDs as $fId => $fUri) {
290 //delete tags from entity tag table
be2fb01f 291 $tagParams = [
0c56e4c8 292 'entity_table' => 'civicrm_file',
21dfd5f5 293 'entity_id' => $fId,
be2fb01f 294 ];
0c56e4c8
TO
295
296 CRM_Core_BAO_EntityTag::del($tagParams);
297
298 if (!CRM_Core_DAO::getFieldValue('CRM_Core_DAO_EntityFile', $fId, 'id', 'file_id')) {
299 unlink($config->customFileUploadDir . DIRECTORY_SEPARATOR . $fUri);
300 $deleteFiles[$fId] = $fId;
301 }
302 }
303
304 if (!empty($deleteFiles)) {
305 $deleteFiles = implode(',', $deleteFiles);
306 $sql = "DELETE FROM civicrm_file where id IN ( $deleteFiles )";
307 CRM_Core_DAO::executeQuery($sql);
308 }
7b8bc7a1 309 $isDeleted = TRUE;
0c56e4c8 310 }
7b8bc7a1 311 return $isDeleted;
0c56e4c8
TO
312 }
313
314 /**
ad37ac8e 315 * Get all the files and associated object associated with this combination.
316 *
317 * @param string $entityTable
318 * @param int $entityID
319 * @param bool $addDeleteArgs
320 *
321 * @return array|null
0c56e4c8 322 */
00be9182 323 public static function getEntityFile($entityTable, $entityID, $addDeleteArgs = FALSE) {
0c56e4c8
TO
324 if (empty($entityTable) || !$entityID) {
325 $results = NULL;
326 return $results;
327 }
328
329 $config = CRM_Core_Config::singleton();
330
331 list($sql, $params) = self::sql($entityTable, $entityID, NULL);
332 $dao = CRM_Core_DAO::executeQuery($sql, $params);
be2fb01f 333 $results = [];
0c56e4c8 334 while ($dao->fetch()) {
ff9aeadb 335 $fileHash = self::generateFileHash($dao->entity_id, $dao->cfID);
0c56e4c8
TO
336 $result['fileID'] = $dao->cfID;
337 $result['entityID'] = $dao->cefID;
338 $result['mime_type'] = $dao->mime_type;
339 $result['fileName'] = $dao->uri;
340 $result['description'] = $dao->description;
341 $result['cleanName'] = CRM_Utils_File::cleanFileName($dao->uri);
342 $result['fullPath'] = $config->customFileUploadDir . DIRECTORY_SEPARATOR . $dao->uri;
ff9aeadb 343 $result['url'] = CRM_Utils_System::url('civicrm/file', "reset=1&id={$dao->cfID}&eid={$dao->entity_id}&fcs={$fileHash}");
0c56e4c8
TO
344 $result['href'] = "<a href=\"{$result['url']}\">{$result['cleanName']}</a>";
345 $result['tag'] = CRM_Core_BAO_EntityTag::getTag($dao->cfID, 'civicrm_file');
30a0455f 346 $result['icon'] = CRM_Utils_File::getIconFromMimeType($dao->mime_type);
0c56e4c8 347 if ($addDeleteArgs) {
06faabfa 348 $result['deleteURLArgs'] = self::deleteURLArgs($dao->entity_table, $dao->entity_id, $dao->cfID);
0c56e4c8
TO
349 }
350 $results[$dao->cfID] = $result;
351 }
352
353 //fix tag names
be2fb01f 354 $tags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]);
0c56e4c8
TO
355
356 foreach ($results as &$values) {
357 if (!empty($values['tag'])) {
be2fb01f 358 $tagNames = [];
0c56e4c8
TO
359 foreach ($values['tag'] as $tid) {
360 $tagNames[] = $tags[$tid];
361 }
362 $values['tag'] = implode(', ', $tagNames);
363 }
364 else {
365 $values['tag'] = '';
366 }
367 }
368
0c56e4c8
TO
369 return $results;
370 }
371
372 /**
6a0b768e
TO
373 * @param string $entityTable
374 * Table-name or "*" (to reference files directly by file-id).
06faabfa 375 * @param int $entityID
100fef9d
CW
376 * @param int $fileTypeID
377 * @param int $fileID
0c56e4c8
TO
378 *
379 * @return array
380 */
00be9182 381 public static function sql($entityTable, $entityID, $fileTypeID = NULL, $fileID = NULL) {
06faabfa
TO
382 if ($entityTable == '*') {
383 // $entityID is the ID of a specific file
384 $sql = "
385SELECT CF.id as cfID,
386 CF.uri as uri,
387 CF.mime_type as mime_type,
388 CF.description as description,
389 CEF.id as cefID,
390 CEF.entity_table as entity_table,
391 CEF.entity_id as entity_id
392FROM civicrm_file AS CF
393LEFT JOIN civicrm_entity_file AS CEF ON ( CEF.file_id = CF.id )
394WHERE CF.id = %2";
395
0db6c3e1
TO
396 }
397 else {
06faabfa 398 $sql = "
0c56e4c8 399SELECT CF.id as cfID,
6a488035
TO
400 CF.uri as uri,
401 CF.mime_type as mime_type,
402 CF.description as description,
06faabfa
TO
403 CEF.id as cefID,
404 CEF.entity_table as entity_table,
405 CEF.entity_id as entity_id
0c56e4c8
TO
406FROM civicrm_file AS CF
407LEFT JOIN civicrm_entity_file AS CEF ON ( CEF.file_id = CF.id )
408WHERE CEF.entity_table = %1
409AND CEF.entity_id = %2";
06faabfa 410 }
0c56e4c8 411
be2fb01f
CW
412 $params = [
413 1 => [$entityTable, 'String'],
414 2 => [$entityID, 'Integer'],
415 ];
0c56e4c8
TO
416
417 if ($fileTypeID !== NULL) {
418 $sql .= " AND CF.file_type_id = %3";
be2fb01f 419 $params[3] = [$fileTypeID, 'Integer'];
0c56e4c8
TO
420 }
421
422 if ($fileID !== NULL) {
423 $sql .= " AND CF.id = %4";
be2fb01f 424 $params[4] = [$fileID, 'Integer'];
0c56e4c8
TO
425 }
426
be2fb01f 427 return [$sql, $params];
0c56e4c8
TO
428 }
429
430 /**
75fd1335
TO
431 * @param CRM_Core_Form $form
432 * @param string $entityTable
100fef9d 433 * @param int $entityID
0c56e4c8
TO
434 * @param null $numAttachments
435 * @param bool $ajaxDelete
436 */
00be9182 437 public static function buildAttachment(&$form, $entityTable, $entityID = NULL, $numAttachments = NULL, $ajaxDelete = FALSE) {
0c56e4c8
TO
438
439 if (!$numAttachments) {
aaffa79f 440 $numAttachments = Civi::settings()->get('max_attachments');
0c56e4c8
TO
441 }
442 // Assign maxAttachments count to template for help message
443 $form->assign('maxAttachments', $numAttachments);
444
445 $config = CRM_Core_Config::singleton();
446 // set default max file size as 2MB
447 $maxFileSize = $config->maxFileSize ? $config->maxFileSize : 2;
448
449 $currentAttachmentInfo = self::getEntityFile($entityTable, $entityID, TRUE);
450 $totalAttachments = 0;
451 if ($currentAttachmentInfo) {
452 $totalAttachments = count($currentAttachmentInfo);
453 $form->add('checkbox', 'is_delete_attachment', ts('Delete All Attachment(s)'));
454 $form->assign('currentAttachmentInfo', $currentAttachmentInfo);
455 }
456 else {
457 $form->assign('currentAttachmentInfo', NULL);
458 }
459
460 if ($totalAttachments) {
461 if ($totalAttachments >= $numAttachments) {
462 $numAttachments = 0;
463 }
464 else {
465 $numAttachments -= $totalAttachments;
466 }
467 }
468
469 $form->assign('numAttachments', $numAttachments);
470
e0f9d6a2 471 CRM_Core_BAO_Tag::getTags('civicrm_file', $tags, NULL,
472 '&nbsp;&nbsp;', TRUE);
0c56e4c8
TO
473
474 // get tagset info
475 $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_file');
476
477 // add attachments
478 for ($i = 1; $i <= $numAttachments; $i++) {
d00e9ef0 479 $form->addElement('file', "attachFile_$i", ts('Attach File'), 'size=30 maxlength=221');
0c56e4c8
TO
480 $form->addUploadElement("attachFile_$i");
481 $form->setMaxFileSize($maxFileSize * 1024 * 1024);
482 $form->addRule("attachFile_$i",
483 ts('File size should be less than %1 MByte(s)',
be2fb01f 484 [1 => $maxFileSize]
0c56e4c8
TO
485 ),
486 'maxfilesize',
487 $maxFileSize * 1024 * 1024
488 );
be2fb01f 489 $form->addElement('text', "attachDesc_$i", NULL, [
0c56e4c8
TO
490 'size' => 40,
491 'maxlength' => 255,
21dfd5f5 492 'placeholder' => ts('Description'),
be2fb01f 493 ]);
0c56e4c8
TO
494
495 if (!empty($tags)) {
496 $form->add('select', "tag_$i", ts('Tags'), $tags, FALSE,
be2fb01f 497 [
0c56e4c8
TO
498 'id' => "tags_$i",
499 'multiple' => 'multiple',
500 'class' => 'huge crm-select2',
21dfd5f5 501 'placeholder' => ts('- none -'),
be2fb01f 502 ]
0c56e4c8
TO
503 );
504 }
505 CRM_Core_Form_Tag::buildQuickForm($form, $parentNames, 'civicrm_file', NULL, FALSE, TRUE, "file_taglist_$i");
506 }
507 }
508
509 /**
100fef9d 510 * Return a clean url string and the number of attachment for a
0c56e4c8
TO
511 * given entityTable, entityID
512 *
5a4f6742
CW
513 * @param string $entityTable
514 * The entityTable to which the file is attached.
515 * @param int $entityID
516 * The id of the object in the above entityTable.
517 * @param string $separator
518 * The string separator where to implode the urls.
0c56e4c8 519 *
a6c01b45
CW
520 * @return array
521 * An array with 2 elements. The string and the number of attachments
0c56e4c8 522 */
00be9182 523 public static function attachmentInfo($entityTable, $entityID, $separator = '<br />') {
0c56e4c8
TO
524 if (!$entityID) {
525 return NULL;
526 }
527
528 $currentAttachments = self::getEntityFile($entityTable, $entityID);
529 if (!empty($currentAttachments)) {
be2fb01f 530 $currentAttachmentURL = [];
0c56e4c8
TO
531 foreach ($currentAttachments as $fileID => $attach) {
532 $currentAttachmentURL[] = $attach['href'];
533 }
534 return implode($separator, $currentAttachmentURL);
535 }
536 return NULL;
537 }
538
539 /**
540 * @param $formValues
c490a46a 541 * @param array $params
0c56e4c8 542 * @param $entityTable
100fef9d 543 * @param int $entityID
0c56e4c8 544 */
ae5ffbb7 545 public static function formatAttachment(
0c56e4c8
TO
546 &$formValues,
547 &$params,
548 $entityTable,
549 $entityID = NULL
550 ) {
551
552 // delete current attachments if applicable
553 if ($entityID && !empty($formValues['is_delete_attachment'])) {
554 CRM_Core_BAO_File::deleteEntityFile($entityTable, $entityID);
555 }
556
aaffa79f 557 $numAttachments = Civi::settings()->get('max_attachments');
0c56e4c8 558
0c56e4c8
TO
559 // setup all attachments
560 for ($i = 1; $i <= $numAttachments; $i++) {
561 $attachName = "attachFile_$i";
562 $attachDesc = "attachDesc_$i";
563 $attachTags = "tag_$i";
564 $attachFreeTags = "file_taglist_$i";
565 if (isset($formValues[$attachName]) && !empty($formValues[$attachName])) {
566 // add static tags if selects
be2fb01f 567 $tagParams = [];
0c56e4c8
TO
568 if (!empty($formValues[$attachTags])) {
569 foreach ($formValues[$attachTags] as $tag) {
570 $tagParams[$tag] = 1;
571 }
572 }
573
574 // we dont care if the file is empty or not
575 // CRM-7448
be2fb01f 576 $extraParams = [
0c56e4c8 577 'description' => $formValues[$attachDesc],
0c56e4c8 578 'tag' => $tagParams,
be2fb01f
CW
579 'attachment_taglist' => CRM_Utils_Array::value($attachFreeTags, $formValues, []),
580 ];
0c56e4c8 581
90a73810 582 CRM_Utils_File::formatFile($formValues, $attachName, $extraParams);
cde03305 583
584 // set the formatted attachment attributes to $params, later used by
585 // CRM_Activity_BAO_Activity::sendEmail(...) to send mail with desired attachments
586 if (!empty($formValues[$attachName])) {
587 $params[$attachName] = $formValues[$attachName];
588 }
0c56e4c8
TO
589 }
590 }
591 }
592
593 /**
c490a46a 594 * @param array $params
0c56e4c8 595 * @param $entityTable
100fef9d 596 * @param int $entityID
0c56e4c8 597 */
00be9182 598 public static function processAttachment(&$params, $entityTable, $entityID) {
aaffa79f 599 $numAttachments = Civi::settings()->get('max_attachments');
0c56e4c8
TO
600
601 for ($i = 1; $i <= $numAttachments; $i++) {
602 if (
603 isset($params["attachFile_$i"]) &&
604 is_array($params["attachFile_$i"])
605 ) {
606 self::filePostProcess(
607 $params["attachFile_$i"]['location'],
608 NULL,
609 $entityTable,
610 $entityID,
611 NULL,
612 TRUE,
613 $params["attachFile_$i"],
614 "attachFile_$i",
615 $params["attachFile_$i"]['type']
616 );
617 }
618 }
619 }
620
621 /**
622 * @return array
623 */
00be9182 624 public static function uploadNames() {
aaffa79f 625 $numAttachments = Civi::settings()->get('max_attachments');
0c56e4c8 626
be2fb01f 627 $names = [];
0c56e4c8
TO
628 for ($i = 1; $i <= $numAttachments; $i++) {
629 $names[] = "attachFile_{$i}";
630 }
631 $names[] = 'uploadFile';
632 return $names;
633 }
634
d424ffde 635 /**
c490a46a 636 * copy/attach an existing file to a different entity
0c56e4c8 637 * table and id.
d424ffde 638 *
0c56e4c8 639 * @param $oldEntityTable
100fef9d 640 * @param int $oldEntityId
0c56e4c8 641 * @param $newEntityTable
100fef9d 642 * @param int $newEntityId
0c56e4c8 643 */
00be9182 644 public static function copyEntityFile($oldEntityTable, $oldEntityId, $newEntityTable, $newEntityId) {
6a488035
TO
645 $oldEntityFile = new CRM_Core_DAO_EntityFile();
646 $oldEntityFile->entity_id = $oldEntityId;
647 $oldEntityFile->entity_table = $oldEntityTable;
648 $oldEntityFile->find();
649
650 while ($oldEntityFile->fetch()) {
651 $newEntityFile = new CRM_Core_DAO_EntityFile();
652 $newEntityFile->entity_id = $newEntityId;
653 $newEntityFile->entity_table = $newEntityTable;
654 $newEntityFile->file_id = $oldEntityFile->file_id;
655 $newEntityFile->save();
656 }
657 }
658
0c56e4c8
TO
659 /**
660 * @param $entityTable
100fef9d
CW
661 * @param int $entityID
662 * @param int $fileID
0c56e4c8
TO
663 *
664 * @return string
665 */
00be9182 666 public static function deleteURLArgs($entityTable, $entityID, $fileID) {
6a488035 667 $params['entityTable'] = $entityTable;
0c56e4c8
TO
668 $params['entityID'] = $entityID;
669 $params['fileID'] = $fileID;
6a488035
TO
670
671 $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), self::$_signableFields);
672 $params['_sgn'] = $signer->sign($params);
673 return CRM_Utils_System::makeQueryString($params);
674 }
675
676 /**
100fef9d 677 * Delete a file attachment from an entity table / entity ID
6a488035 678 *
6a488035 679 */
00be9182 680 public static function deleteAttachment() {
be2fb01f 681 $params = [];
0c56e4c8
TO
682 $params['entityTable'] = CRM_Utils_Request::retrieve('entityTable', 'String', CRM_Core_DAO::$_nullObject, TRUE);
683 $params['entityID'] = CRM_Utils_Request::retrieve('entityID', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
684 $params['fileID'] = CRM_Utils_Request::retrieve('fileID', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
6a488035 685
0c56e4c8 686 $signature = CRM_Utils_Request::retrieve('_sgn', 'String', CRM_Core_DAO::$_nullObject, TRUE);
6a488035
TO
687
688 $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), self::$_signableFields);
0c56e4c8 689 if (!$signer->validate($signature, $params)) {
6a488035
TO
690 CRM_Core_Error::fatal('Request signature is invalid');
691 }
692
33d245c8 693 self::deleteEntityFile($params['entityTable'], $params['entityID'], NULL, $params['fileID']);
6a488035
TO
694 }
695
34f51a07 696 /**
100fef9d 697 * Display paper icon for a file attachment -- CRM-13624
34f51a07 698 *
5a4f6742
CW
699 * @param string $entityTable
700 * The entityTable to which the file is attached. eg "civicrm_contact", "civicrm_note", "civicrm_activity".
06faabfa 701 * If you have the ID of a specific row in civicrm_file, use $entityTable='*'
5a4f6742
CW
702 * @param int $entityID
703 * The id of the object in the above entityTable.
1a7ab71e 704 *
72b3a70c
CW
705 * @return array|NULL
706 * list of HTML snippets; one HTML snippet for each attachment. If none found, then NULL
1a7ab71e 707 *
34f51a07 708 */
00be9182 709 public static function paperIconAttachment($entityTable, $entityID) {
0c56e4c8
TO
710 if (empty($entityTable) || !$entityID) {
711 $results = NULL;
712 return $results;
713 }
714 $currentAttachmentInfo = self::getEntityFile($entityTable, $entityID);
715 foreach ($currentAttachmentInfo as $fileKey => $fileValue) {
34f51a07 716 $fileID = $fileValue['fileID'];
0c56e4c8 717 if ($fileID) {
b9773de0
CW
718 $fileType = $fileValue['mime_type'];
719 $url = $fileValue['url'];
720 $title = $fileValue['cleanName'];
34f51a07 721 if ($fileType == 'image/jpeg' ||
0c56e4c8
TO
722 $fileType == 'image/pjpeg' ||
723 $fileType == 'image/gif' ||
724 $fileType == 'image/x-png' ||
725 $fileType == 'image/png'
726 ) {
34f51a07 727 $file_url[$fileID] = "
b9773de0
CW
728 <a href='$url' class='crm-image-popup' title='$title'>
729 <i class='crm-i fa-file-image-o'></i>
34f51a07 730 </a>";
34f51a07 731 }
b9773de0 732 // for non image files
34f51a07 733 else {
b9773de0
CW
734 $file_url[$fileID] = "
735 <a href='$url' title='$title'>
736 <i class='crm-i fa-paperclip'></i>
737 </a>";
34f51a07
N
738 }
739 }
740 }
0c56e4c8
TO
741 if (empty($file_url)) {
742 $results = NULL;
34f51a07
N
743 }
744 else {
0c56e4c8 745 $results = $file_url;
34f51a07
N
746 }
747 return $results;
748 }
6cccc6d4
TO
749
750 /**
751 * Get a reference to the file-search service (if one is available).
752 *
753 * @return CRM_Core_FileSearchInterface|NULL
754 */
00be9182 755 public static function getSearchService() {
be2fb01f 756 $fileSearches = [];
6cccc6d4
TO
757 CRM_Utils_Hook::fileSearches($fileSearches);
758
759 // use the first available search
760 foreach ($fileSearches as $fileSearch) {
761 /** @var $fileSearch CRM_Core_FileSearchInterface */
762 return $fileSearch;
763 }
764 return NULL;
765 }
96025800 766
ff9aeadb 767 /**
7618eb9d
TO
768 * Generates an access-token for downloading a specific file.
769 *
efddd94f
TO
770 * @param int $entityId entity id the file is attached to
771 * @param int $fileId file ID
518fa0ee
SL
772 * @param int $genTs
773 * @param int $life
ff9aeadb
SL
774 * @return string
775 */
efddd94f 776 public static function generateFileHash($entityId = NULL, $fileId = NULL, $genTs = NULL, $life = NULL) {
ff9aeadb 777 // Use multiple (but stable) inputs for hash information.
65d060f5
TO
778 $siteKey = CRM_Utils_Constant::value('CIVICRM_SITE_KEY');
779 if (!$siteKey) {
780 throw new \CRM_Core_Exception("Cannot generate file access token. Please set CIVICRM_SITE_KEY.");
781 }
b68895ad
SL
782
783 if (!$genTs) {
784 $genTs = time();
785 }
786 if (!$life) {
54a043e4 787 $days = Civi::settings()->get('checksum_timeout');
efddd94f 788 $life = 24 * $days;
b68895ad 789 }
ff9aeadb
SL
790 // Trim 8 chars off the string, make it slightly easier to find
791 // but reveals less information from the hash.
efddd94f 792 $cs = hash_hmac('sha256', "entity={$entityId}&file={$fileId}&life={$life}", $siteKey);
b68895ad 793 return "{$cs}_{$genTs}_{$life}";
ff9aeadb
SL
794 }
795
c5c40df7 796 /**
7618eb9d
TO
797 * Validate a file access token.
798 *
c5c40df7 799 * @param string $hash
7618eb9d
TO
800 * @param int $entityId Entity Id the file is attached to
801 * @param int $fileId File Id
c5c40df7
TO
802 * @return bool
803 */
7618eb9d 804 public static function validateFileHash($hash, $entityId, $fileId) {
c5c40df7
TO
805 $input = CRM_Utils_System::explode('_', $hash, 3);
806 $inputTs = CRM_Utils_Array::value(1, $input);
807 $inputLF = CRM_Utils_Array::value(2, $input);
7618eb9d 808 $testHash = CRM_Core_BAO_File::generateFileHash($entityId, $fileId, $inputTs, $inputLF);
c5c40df7
TO
809 if (hash_equals($testHash, $hash)) {
810 $now = time();
811 if ($inputTs + ($inputLF * 60 * 60) >= $now) {
812 return TRUE;
813 }
814 else {
815 return FALSE;
816 }
817 }
818 return FALSE;
819 }
820
b2aaa85e 821}