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