(NFC) Update CRM/Core CRM/Custom CRM/Dedupe to match the new coder style
[civicrm-core.git] / CRM / Core / BAO / File.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
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 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2019
32 * $Id$
33 *
34 */
35
36 /**
37 * BAO object for crm_log table
38 */
39 class CRM_Core_BAO_File extends CRM_Core_DAO_File {
40
41 public static $_signableFields = ['entityTable', 'entityID', 'fileID'];
42
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
71 /**
72 * @param int $fileID
73 * @param int $entityID
74 *
75 * @return array
76 */
77 public static function path($fileID, $entityID) {
78 $entityFileDAO = new CRM_Core_DAO_EntityFile();
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)) {
90 return [$path, $fileDAO->mime_type];
91 }
92 }
93 }
94
95 return [NULL, NULL];
96 }
97
98 /**
99 * @param $data
100 * @param int $fileTypeID
101 * @param $entityTable
102 * @param int $entityID
103 * @param $entitySubtype
104 * @param bool $overwrite
105 * @param null|array $fileParams
106 * @param string $uploadName
107 * @param null $mimeType
108 *
109 * @throws Exception
110 */
111 public static function filePostProcess(
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) {
123 CRM_Core_Error::statusBounce(ts('Mime Type is now a required parameter for file upload'));
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)) {
142 CRM_Core_Error::statusBounce(ts('Could not move custom file to custom upload directory'));
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;
171 $fileDAO->upload_date = date('YmdHis');
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'])) {
191 CRM_Core_Form_Tag::postProcess($fileParams['attachment_taglist'], $entityFileDAO->id, 'civicrm_file');
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 /**
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
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)) {
213 CRM_Core_Error::fatal();
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)) {
229 CRM_Core_Error::fatal(sprintf('No record found for given file ID - %d and entity ID - %d', $fileID, $entityID));
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";
237 $params = [1 => [$fileID, 'Integer']];
238 CRM_Core_DAO::executeQuery($query, $params);
239 }
240
241 /**
242 * The $useWhere is used so that the signature matches the parent class
243 *
244 * public function delete($useWhere = FALSE) {
245 * list($fileID, $entityID, $fieldID) = func_get_args();
246 *
247 * self::deleteFileReferences($fileID, $entityID, $fieldID);
248 * } */
249
250 /**
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 *
258 * @return bool
259 * Was file deleted?
260 */
261 public static function deleteEntityFile($entityTable, $entityID, $fileTypeID = NULL, $fileID = NULL) {
262 $isDeleted = FALSE;
263 if (empty($entityTable) || empty($entityID)) {
264 return $isDeleted;
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
272 $cfIDs = [];
273 $cefIDs = [];
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);
283 $isDeleted = TRUE;
284 }
285
286 if (!empty($cfIDs)) {
287 // Delete file only if there no any entity using this file.
288 $deleteFiles = [];
289 foreach ($cfIDs as $fId => $fUri) {
290 //delete tags from entity tag table
291 $tagParams = [
292 'entity_table' => 'civicrm_file',
293 'entity_id' => $fId,
294 ];
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 }
309 $isDeleted = TRUE;
310 }
311 return $isDeleted;
312 }
313
314 /**
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
322 */
323 public static function getEntityFile($entityTable, $entityID, $addDeleteArgs = FALSE) {
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);
333 $results = [];
334 while ($dao->fetch()) {
335 $fileHash = self::generateFileHash($dao->entity_id, $dao->cfID);
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;
343 $result['url'] = CRM_Utils_System::url('civicrm/file', "reset=1&id={$dao->cfID}&eid={$dao->entity_id}&fcs={$fileHash}");
344 $result['href'] = "<a href=\"{$result['url']}\">{$result['cleanName']}</a>";
345 $result['tag'] = CRM_Core_BAO_EntityTag::getTag($dao->cfID, 'civicrm_file');
346 $result['icon'] = CRM_Utils_File::getIconFromMimeType($dao->mime_type);
347 if ($addDeleteArgs) {
348 $result['deleteURLArgs'] = self::deleteURLArgs($dao->entity_table, $dao->entity_id, $dao->cfID);
349 }
350 $results[$dao->cfID] = $result;
351 }
352
353 //fix tag names
354 $tags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]);
355
356 foreach ($results as &$values) {
357 if (!empty($values['tag'])) {
358 $tagNames = [];
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
369 $dao->free();
370 return $results;
371 }
372
373 /**
374 * @param string $entityTable
375 * Table-name or "*" (to reference files directly by file-id).
376 * @param int $entityID
377 * @param int $fileTypeID
378 * @param int $fileID
379 *
380 * @return array
381 */
382 public static function sql($entityTable, $entityID, $fileTypeID = NULL, $fileID = NULL) {
383 if ($entityTable == '*') {
384 // $entityID is the ID of a specific file
385 $sql = "
386 SELECT CF.id as cfID,
387 CF.uri as uri,
388 CF.mime_type as mime_type,
389 CF.description as description,
390 CEF.id as cefID,
391 CEF.entity_table as entity_table,
392 CEF.entity_id as entity_id
393 FROM civicrm_file AS CF
394 LEFT JOIN civicrm_entity_file AS CEF ON ( CEF.file_id = CF.id )
395 WHERE CF.id = %2";
396
397 }
398 else {
399 $sql = "
400 SELECT CF.id as cfID,
401 CF.uri as uri,
402 CF.mime_type as mime_type,
403 CF.description as description,
404 CEF.id as cefID,
405 CEF.entity_table as entity_table,
406 CEF.entity_id as entity_id
407 FROM civicrm_file AS CF
408 LEFT JOIN civicrm_entity_file AS CEF ON ( CEF.file_id = CF.id )
409 WHERE CEF.entity_table = %1
410 AND CEF.entity_id = %2";
411 }
412
413 $params = [
414 1 => [$entityTable, 'String'],
415 2 => [$entityID, 'Integer'],
416 ];
417
418 if ($fileTypeID !== NULL) {
419 $sql .= " AND CF.file_type_id = %3";
420 $params[3] = [$fileTypeID, 'Integer'];
421 }
422
423 if ($fileID !== NULL) {
424 $sql .= " AND CF.id = %4";
425 $params[4] = [$fileID, 'Integer'];
426 }
427
428 return [$sql, $params];
429 }
430
431 /**
432 * @param CRM_Core_Form $form
433 * @param string $entityTable
434 * @param int $entityID
435 * @param null $numAttachments
436 * @param bool $ajaxDelete
437 */
438 public static function buildAttachment(&$form, $entityTable, $entityID = NULL, $numAttachments = NULL, $ajaxDelete = FALSE) {
439
440 if (!$numAttachments) {
441 $numAttachments = Civi::settings()->get('max_attachments');
442 }
443 // Assign maxAttachments count to template for help message
444 $form->assign('maxAttachments', $numAttachments);
445
446 $config = CRM_Core_Config::singleton();
447 // set default max file size as 2MB
448 $maxFileSize = $config->maxFileSize ? $config->maxFileSize : 2;
449
450 $currentAttachmentInfo = self::getEntityFile($entityTable, $entityID, TRUE);
451 $totalAttachments = 0;
452 if ($currentAttachmentInfo) {
453 $totalAttachments = count($currentAttachmentInfo);
454 $form->add('checkbox', 'is_delete_attachment', ts('Delete All Attachment(s)'));
455 $form->assign('currentAttachmentInfo', $currentAttachmentInfo);
456 }
457 else {
458 $form->assign('currentAttachmentInfo', NULL);
459 }
460
461 if ($totalAttachments) {
462 if ($totalAttachments >= $numAttachments) {
463 $numAttachments = 0;
464 }
465 else {
466 $numAttachments -= $totalAttachments;
467 }
468 }
469
470 $form->assign('numAttachments', $numAttachments);
471
472 CRM_Core_BAO_Tag::getTags('civicrm_file', $tags, NULL,
473 '&nbsp;&nbsp;', TRUE);
474
475 // get tagset info
476 $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_file');
477
478 // add attachments
479 for ($i = 1; $i <= $numAttachments; $i++) {
480 $form->addElement('file', "attachFile_$i", ts('Attach File'), 'size=30 maxlength=221');
481 $form->addUploadElement("attachFile_$i");
482 $form->setMaxFileSize($maxFileSize * 1024 * 1024);
483 $form->addRule("attachFile_$i",
484 ts('File size should be less than %1 MByte(s)',
485 [1 => $maxFileSize]
486 ),
487 'maxfilesize',
488 $maxFileSize * 1024 * 1024
489 );
490 $form->addElement('text', "attachDesc_$i", NULL, [
491 'size' => 40,
492 'maxlength' => 255,
493 'placeholder' => ts('Description'),
494 ]);
495
496 if (!empty($tags)) {
497 $form->add('select', "tag_$i", ts('Tags'), $tags, FALSE,
498 [
499 'id' => "tags_$i",
500 'multiple' => 'multiple',
501 'class' => 'huge crm-select2',
502 'placeholder' => ts('- none -'),
503 ]
504 );
505 }
506 CRM_Core_Form_Tag::buildQuickForm($form, $parentNames, 'civicrm_file', NULL, FALSE, TRUE, "file_taglist_$i");
507 }
508 }
509
510 /**
511 * Return a clean url string and the number of attachment for a
512 * given entityTable, entityID
513 *
514 * @param string $entityTable
515 * The entityTable to which the file is attached.
516 * @param int $entityID
517 * The id of the object in the above entityTable.
518 * @param string $separator
519 * The string separator where to implode the urls.
520 *
521 * @return array
522 * An array with 2 elements. The string and the number of attachments
523 */
524 public static function attachmentInfo($entityTable, $entityID, $separator = '<br />') {
525 if (!$entityID) {
526 return NULL;
527 }
528
529 $currentAttachments = self::getEntityFile($entityTable, $entityID);
530 if (!empty($currentAttachments)) {
531 $currentAttachmentURL = [];
532 foreach ($currentAttachments as $fileID => $attach) {
533 $currentAttachmentURL[] = $attach['href'];
534 }
535 return implode($separator, $currentAttachmentURL);
536 }
537 return NULL;
538 }
539
540 /**
541 * @param $formValues
542 * @param array $params
543 * @param $entityTable
544 * @param int $entityID
545 */
546 public static function formatAttachment(
547 &$formValues,
548 &$params,
549 $entityTable,
550 $entityID = NULL
551 ) {
552
553 // delete current attachments if applicable
554 if ($entityID && !empty($formValues['is_delete_attachment'])) {
555 CRM_Core_BAO_File::deleteEntityFile($entityTable, $entityID);
556 }
557
558 $numAttachments = Civi::settings()->get('max_attachments');
559
560 // setup all attachments
561 for ($i = 1; $i <= $numAttachments; $i++) {
562 $attachName = "attachFile_$i";
563 $attachDesc = "attachDesc_$i";
564 $attachTags = "tag_$i";
565 $attachFreeTags = "file_taglist_$i";
566 if (isset($formValues[$attachName]) && !empty($formValues[$attachName])) {
567 // add static tags if selects
568 $tagParams = [];
569 if (!empty($formValues[$attachTags])) {
570 foreach ($formValues[$attachTags] as $tag) {
571 $tagParams[$tag] = 1;
572 }
573 }
574
575 // we dont care if the file is empty or not
576 // CRM-7448
577 $extraParams = [
578 'description' => $formValues[$attachDesc],
579 'tag' => $tagParams,
580 'attachment_taglist' => CRM_Utils_Array::value($attachFreeTags, $formValues, []),
581 ];
582
583 CRM_Utils_File::formatFile($formValues, $attachName, $extraParams);
584
585 // set the formatted attachment attributes to $params, later used by
586 // CRM_Activity_BAO_Activity::sendEmail(...) to send mail with desired attachments
587 if (!empty($formValues[$attachName])) {
588 $params[$attachName] = $formValues[$attachName];
589 }
590 }
591 }
592 }
593
594 /**
595 * @param array $params
596 * @param $entityTable
597 * @param int $entityID
598 */
599 public static function processAttachment(&$params, $entityTable, $entityID) {
600 $numAttachments = Civi::settings()->get('max_attachments');
601
602 for ($i = 1; $i <= $numAttachments; $i++) {
603 if (
604 isset($params["attachFile_$i"]) &&
605 is_array($params["attachFile_$i"])
606 ) {
607 self::filePostProcess(
608 $params["attachFile_$i"]['location'],
609 NULL,
610 $entityTable,
611 $entityID,
612 NULL,
613 TRUE,
614 $params["attachFile_$i"],
615 "attachFile_$i",
616 $params["attachFile_$i"]['type']
617 );
618 }
619 }
620 }
621
622 /**
623 * @return array
624 */
625 public static function uploadNames() {
626 $numAttachments = Civi::settings()->get('max_attachments');
627
628 $names = [];
629 for ($i = 1; $i <= $numAttachments; $i++) {
630 $names[] = "attachFile_{$i}";
631 }
632 $names[] = 'uploadFile';
633 return $names;
634 }
635
636 /**
637 * copy/attach an existing file to a different entity
638 * table and id.
639 *
640 * @param $oldEntityTable
641 * @param int $oldEntityId
642 * @param $newEntityTable
643 * @param int $newEntityId
644 */
645 public static function copyEntityFile($oldEntityTable, $oldEntityId, $newEntityTable, $newEntityId) {
646 $oldEntityFile = new CRM_Core_DAO_EntityFile();
647 $oldEntityFile->entity_id = $oldEntityId;
648 $oldEntityFile->entity_table = $oldEntityTable;
649 $oldEntityFile->find();
650
651 while ($oldEntityFile->fetch()) {
652 $newEntityFile = new CRM_Core_DAO_EntityFile();
653 $newEntityFile->entity_id = $newEntityId;
654 $newEntityFile->entity_table = $newEntityTable;
655 $newEntityFile->file_id = $oldEntityFile->file_id;
656 $newEntityFile->save();
657 }
658 }
659
660 /**
661 * @param $entityTable
662 * @param int $entityID
663 * @param int $fileID
664 *
665 * @return string
666 */
667 public static function deleteURLArgs($entityTable, $entityID, $fileID) {
668 $params['entityTable'] = $entityTable;
669 $params['entityID'] = $entityID;
670 $params['fileID'] = $fileID;
671
672 $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), self::$_signableFields);
673 $params['_sgn'] = $signer->sign($params);
674 return CRM_Utils_System::makeQueryString($params);
675 }
676
677 /**
678 * Delete a file attachment from an entity table / entity ID
679 *
680 */
681 public static function deleteAttachment() {
682 $params = [];
683 $params['entityTable'] = CRM_Utils_Request::retrieve('entityTable', 'String', CRM_Core_DAO::$_nullObject, TRUE);
684 $params['entityID'] = CRM_Utils_Request::retrieve('entityID', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
685 $params['fileID'] = CRM_Utils_Request::retrieve('fileID', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
686
687 $signature = CRM_Utils_Request::retrieve('_sgn', 'String', CRM_Core_DAO::$_nullObject, TRUE);
688
689 $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), self::$_signableFields);
690 if (!$signer->validate($signature, $params)) {
691 CRM_Core_Error::fatal('Request signature is invalid');
692 }
693
694 self::deleteEntityFile($params['entityTable'], $params['entityID'], NULL, $params['fileID']);
695 }
696
697 /**
698 * Display paper icon for a file attachment -- CRM-13624
699 *
700 * @param string $entityTable
701 * The entityTable to which the file is attached. eg "civicrm_contact", "civicrm_note", "civicrm_activity".
702 * If you have the ID of a specific row in civicrm_file, use $entityTable='*'
703 * @param int $entityID
704 * The id of the object in the above entityTable.
705 *
706 * @return array|NULL
707 * list of HTML snippets; one HTML snippet for each attachment. If none found, then NULL
708 *
709 */
710 public static function paperIconAttachment($entityTable, $entityID) {
711 if (empty($entityTable) || !$entityID) {
712 $results = NULL;
713 return $results;
714 }
715 $currentAttachmentInfo = self::getEntityFile($entityTable, $entityID);
716 foreach ($currentAttachmentInfo as $fileKey => $fileValue) {
717 $fileID = $fileValue['fileID'];
718 if ($fileID) {
719 $fileType = $fileValue['mime_type'];
720 $url = $fileValue['url'];
721 $title = $fileValue['cleanName'];
722 if ($fileType == 'image/jpeg' ||
723 $fileType == 'image/pjpeg' ||
724 $fileType == 'image/gif' ||
725 $fileType == 'image/x-png' ||
726 $fileType == 'image/png'
727 ) {
728 $file_url[$fileID] = "
729 <a href='$url' class='crm-image-popup' title='$title'>
730 <i class='crm-i fa-file-image-o'></i>
731 </a>";
732 }
733 // for non image files
734 else {
735 $file_url[$fileID] = "
736 <a href='$url' title='$title'>
737 <i class='crm-i fa-paperclip'></i>
738 </a>";
739 }
740 }
741 }
742 if (empty($file_url)) {
743 $results = NULL;
744 }
745 else {
746 $results = $file_url;
747 }
748 return $results;
749 }
750
751 /**
752 * Get a reference to the file-search service (if one is available).
753 *
754 * @return CRM_Core_FileSearchInterface|NULL
755 */
756 public static function getSearchService() {
757 $fileSearches = [];
758 CRM_Utils_Hook::fileSearches($fileSearches);
759
760 // use the first available search
761 foreach ($fileSearches as $fileSearch) {
762 /** @var $fileSearch CRM_Core_FileSearchInterface */
763 return $fileSearch;
764 }
765 return NULL;
766 }
767
768 /**
769 * Generates an access-token for downloading a specific file.
770 *
771 * @param int $entityId entity id the file is attached to
772 * @param int $fileId file ID
773 * @param int $genTs
774 * @param int $life
775 * @return string
776 */
777 public static function generateFileHash($entityId = NULL, $fileId = NULL, $genTs = NULL, $life = NULL) {
778 // Use multiple (but stable) inputs for hash information.
779 $siteKey = CRM_Utils_Constant::value('CIVICRM_SITE_KEY');
780 if (!$siteKey) {
781 throw new \CRM_Core_Exception("Cannot generate file access token. Please set CIVICRM_SITE_KEY.");
782 }
783
784 if (!$genTs) {
785 $genTs = time();
786 }
787 if (!$life) {
788 $days = Civi::settings()->get('checksum_timeout');
789 $life = 24 * $days;
790 }
791 // Trim 8 chars off the string, make it slightly easier to find
792 // but reveals less information from the hash.
793 $cs = hash_hmac('sha256', "entity={$entityId}&file={$fileId}&life={$life}", $siteKey);
794 return "{$cs}_{$genTs}_{$life}";
795 }
796
797 /**
798 * Validate a file access token.
799 *
800 * @param string $hash
801 * @param int $entityId Entity Id the file is attached to
802 * @param int $fileId File Id
803 * @return bool
804 */
805 public static function validateFileHash($hash, $entityId, $fileId) {
806 $input = CRM_Utils_System::explode('_', $hash, 3);
807 $inputTs = CRM_Utils_Array::value(1, $input);
808 $inputLF = CRM_Utils_Array::value(2, $input);
809 $testHash = CRM_Core_BAO_File::generateFileHash($entityId, $fileId, $inputTs, $inputLF);
810 if (hash_equals($testHash, $hash)) {
811 $now = time();
812 if ($inputTs + ($inputLF * 60 * 60) >= $now) {
813 return TRUE;
814 }
815 else {
816 return FALSE;
817 }
818 }
819 return FALSE;
820 }
821
822 }