3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2014
37 * BAO object for crm_log table
39 class CRM_Core_BAO_File
extends CRM_Core_DAO_File
{
41 static $_signableFields = array('entityTable', 'entityID', 'fileID');
45 * @param int $entityID
46 * @param null $entityTable
50 public static function path($fileID, $entityID, $entityTable = NULL) {
51 $entityFileDAO = new CRM_Core_DAO_EntityFile();
53 $entityFileDAO->entity_table
= $entityTable;
55 $entityFileDAO->entity_id
= $entityID;
56 $entityFileDAO->file_id
= $fileID;
58 if ($entityFileDAO->find(TRUE)) {
59 $fileDAO = new CRM_Core_DAO_File();
60 $fileDAO->id
= $fileID;
61 if ($fileDAO->find(TRUE)) {
62 $config = CRM_Core_Config
::singleton();
63 $path = $config->customFileUploadDir
. $fileDAO->uri
;
65 if (file_exists($path) && is_readable($path)) {
66 return array($path, $fileDAO->mime_type
);
71 return array(NULL, NULL);
77 * @param int $fileTypeID
79 * @param int $entityID
80 * @param $entitySubtype
81 * @param bool $overwrite
82 * @param null|array $fileParams
83 * @param string $uploadName
84 * @param null $mimeType
88 static function filePostProcess(
96 $uploadName = 'uploadFile',
100 CRM_Core_Error
::fatal(ts('Mime Type is now a required parameter'));
103 $config = CRM_Core_Config
::singleton();
105 $path = explode('/', $data);
106 $filename = $path[count($path) - 1];
108 // rename this file to go into the secure directory
109 if ($entitySubtype) {
110 $directoryName = $config->customFileUploadDir
. $entitySubtype . DIRECTORY_SEPARATOR
. $entityID;
113 $directoryName = $config->customFileUploadDir
;
116 CRM_Utils_File
::createDir($directoryName);
118 if (!rename($data, $directoryName . DIRECTORY_SEPARATOR
. $filename)) {
119 CRM_Core_Error
::fatal(ts('Could not move custom file to custom upload directory'));
123 if ($overwrite && $fileTypeID) {
124 list($sql, $params) = self
::sql($entityTable, $entityID, $fileTypeID);
127 list($sql, $params) = self
::sql($entityTable, $entityID, 0);
130 $dao = CRM_Core_DAO
::executeQuery($sql, $params);
133 $fileDAO = new CRM_Core_DAO_File();
135 if (isset($dao->cfID
) && $dao->cfID
) {
137 $fileDAO->id
= $dao->cfID
;
138 unlink($directoryName . DIRECTORY_SEPARATOR
. $dao->uri
);
141 if (!empty($fileParams)) {
142 $fileDAO->copyValues($fileParams);
145 $fileDAO->uri
= $filename;
146 $fileDAO->mime_type
= $mimeType;
147 $fileDAO->file_type_id
= $fileTypeID;
148 $fileDAO->upload_date
= date('Ymdhis');
151 // need to add/update civicrm_entity_file
152 $entityFileDAO = new CRM_Core_DAO_EntityFile();
153 if (isset($dao->cefID
) && $dao->cefID
) {
154 $entityFileDAO->id
= $dao->cefID
;
156 $entityFileDAO->entity_table
= $entityTable;
157 $entityFileDAO->entity_id
= $entityID;
158 $entityFileDAO->file_id
= $fileDAO->id
;
159 $entityFileDAO->save();
162 if (!empty($fileParams['tag'])) {
163 CRM_Core_BAO_EntityTag
::create($fileParams['tag'], 'civicrm_file', $entityFileDAO->id
);
167 if (isset($fileParams['attachment_taglist']) && !empty($fileParams['attachment_taglist'])) {
168 CRM_Core_Form_Tag
::postProcess($fileParams['attachment_taglist'], $entityFileDAO->id
, 'civicrm_file', CRM_Core_DAO
::$_nullObject);
171 // lets call the post hook here so attachments code can do the right stuff
172 CRM_Utils_Hook
::post($op, 'File', $fileDAO->id
, $fileDAO);
176 * A static function wrapper that deletes the various objects that are
177 * connected to a file object (i.e. file, entityFile and customValue
179 public static function deleteFileReferences($fileID, $entityID, $fieldID) {
180 $fileDAO = new CRM_Core_DAO_File();
181 $fileDAO->id
= $fileID;
182 if (!$fileDAO->find(TRUE)) {
183 CRM_Core_Error
::fatal();
186 // lets call a pre hook before the delete, so attachments hooks can get the info before things
188 CRM_Utils_Hook
::pre('delete', 'File', $fileID, $fileDAO);
190 // get the table and column name
191 list($tableName, $columnName, $groupID) = CRM_Core_BAO_CustomField
::getTableColumnGroup($fieldID);
193 $entityFileDAO = new CRM_Core_DAO_EntityFile();
194 $entityFileDAO->file_id
= $fileID;
195 $entityFileDAO->entity_id
= $entityID;
196 $entityFileDAO->entity_table
= $tableName;
198 if (!$entityFileDAO->find(TRUE)) {
199 CRM_Core_Error
::fatal();
202 $entityFileDAO->delete();
205 // also set the value to null of the table and column
206 $query = "UPDATE $tableName SET $columnName = null WHERE $columnName = %1";
207 $params = array(1 => array($fileID, 'Integer'));
208 CRM_Core_DAO
::executeQuery($query, $params);
212 * The $useWhere is used so that the signature matches the parent class
214 * public function delete($useWhere = FALSE) {
215 * list($fileID, $entityID, $fieldID) = func_get_args();
217 * self::deleteFileReferences($fileID, $entityID, $fieldID);
221 * Delete all the files and associated object associated with this
224 public static function deleteEntityFile($entityTable, $entityID, $fileTypeID = NULL, $fileID = NULL) {
225 if (empty($entityTable) ||
empty($entityID)) {
229 $config = CRM_Core_Config
::singleton();
231 list($sql, $params) = self
::sql($entityTable, $entityID, $fileTypeID, $fileID);
232 $dao = CRM_Core_DAO
::executeQuery($sql, $params);
236 while ($dao->fetch()) {
237 $cfIDs[$dao->cfID
] = $dao->uri
;
238 $cefIDs[] = $dao->cefID
;
241 if (!empty($cefIDs)) {
242 $cefIDs = implode(',', $cefIDs);
243 $sql = "DELETE FROM civicrm_entity_file where id IN ( $cefIDs )";
244 CRM_Core_DAO
::executeQuery($sql);
247 if (!empty($cfIDs)) {
248 // Delete file only if there no any entity using this file.
249 $deleteFiles = array();
250 foreach ($cfIDs as $fId => $fUri) {
251 //delete tags from entity tag table
253 'entity_table' => 'civicrm_file',
257 CRM_Core_BAO_EntityTag
::del($tagParams);
259 if (!CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_EntityFile', $fId, 'id', 'file_id')) {
260 unlink($config->customFileUploadDir
. DIRECTORY_SEPARATOR
. $fUri);
261 $deleteFiles[$fId] = $fId;
265 if (!empty($deleteFiles)) {
266 $deleteFiles = implode(',', $deleteFiles);
267 $sql = "DELETE FROM civicrm_file where id IN ( $deleteFiles )";
268 CRM_Core_DAO
::executeQuery($sql);
274 * Get all the files and associated object associated with this
277 public static function getEntityFile($entityTable, $entityID, $addDeleteArgs = FALSE) {
278 if (empty($entityTable) ||
!$entityID) {
283 $config = CRM_Core_Config
::singleton();
285 list($sql, $params) = self
::sql($entityTable, $entityID, NULL);
286 $dao = CRM_Core_DAO
::executeQuery($sql, $params);
288 while ($dao->fetch()) {
289 $result['fileID'] = $dao->cfID
;
290 $result['entityID'] = $dao->cefID
;
291 $result['mime_type'] = $dao->mime_type
;
292 $result['fileName'] = $dao->uri
;
293 $result['description'] = $dao->description
;
294 $result['cleanName'] = CRM_Utils_File
::cleanFileName($dao->uri
);
295 $result['fullPath'] = $config->customFileUploadDir
. DIRECTORY_SEPARATOR
. $dao->uri
;
296 $result['url'] = CRM_Utils_System
::url('civicrm/file', "reset=1&id={$dao->cfID}&eid={$dao->entity_id}");
297 $result['href'] = "<a href=\"{$result['url']}\">{$result['cleanName']}</a>";
298 $result['tag'] = CRM_Core_BAO_EntityTag
::getTag($dao->cfID
, 'civicrm_file');
299 if ($addDeleteArgs) {
300 $result['deleteURLArgs'] = self
::deleteURLArgs($dao->entity_table
, $dao->entity_id
, $dao->cfID
);
302 $results[$dao->cfID
] = $result;
306 $tags = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE));
308 foreach ($results as &$values) {
309 if (!empty($values['tag'])) {
311 foreach ($values['tag'] as $tid) {
312 $tagNames[] = $tags[$tid];
314 $values['tag'] = implode(', ', $tagNames);
326 * @param string $entityTable
327 * Table-name or "*" (to reference files directly by file-id).
328 * @param int $entityID
329 * @param int $fileTypeID
334 public static function sql($entityTable, $entityID, $fileTypeID = NULL, $fileID = NULL) {
335 if ($entityTable == '*') {
336 // $entityID is the ID of a specific file
338 SELECT CF.id as cfID,
340 CF.mime_type as mime_type,
341 CF.description as description,
343 CEF.entity_table as entity_table,
344 CEF.entity_id as entity_id
345 FROM civicrm_file AS CF
346 LEFT JOIN civicrm_entity_file AS CEF ON ( CEF.file_id = CF.id )
352 SELECT CF.id as cfID,
354 CF.mime_type as mime_type,
355 CF.description as description,
357 CEF.entity_table as entity_table,
358 CEF.entity_id as entity_id
359 FROM civicrm_file AS CF
360 LEFT JOIN civicrm_entity_file AS CEF ON ( CEF.file_id = CF.id )
361 WHERE CEF.entity_table = %1
362 AND CEF.entity_id = %2";
366 1 => array($entityTable, 'String'),
367 2 => array($entityID, 'Integer'),
370 if ($fileTypeID !== NULL) {
371 $sql .= " AND CF.file_type_id = %3";
372 $params[3] = array($fileTypeID, 'Integer');
375 if ($fileID !== NULL) {
376 $sql .= " AND CF.id = %4";
377 $params[4] = array($fileID, 'Integer');
380 return array($sql, $params);
384 * @param CRM_Core_Form $form
385 * @param string $entityTable
386 * @param int $entityID
387 * @param null $numAttachments
388 * @param bool $ajaxDelete
390 public static function buildAttachment(&$form, $entityTable, $entityID = NULL, $numAttachments = NULL, $ajaxDelete = FALSE) {
392 if (!$numAttachments) {
393 $numAttachments = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
, 'max_attachments');
395 // Assign maxAttachments count to template for help message
396 $form->assign('maxAttachments', $numAttachments);
398 $config = CRM_Core_Config
::singleton();
399 // set default max file size as 2MB
400 $maxFileSize = $config->maxFileSize ?
$config->maxFileSize
: 2;
402 $currentAttachmentInfo = self
::getEntityFile($entityTable, $entityID, TRUE);
403 $totalAttachments = 0;
404 if ($currentAttachmentInfo) {
405 $totalAttachments = count($currentAttachmentInfo);
406 $form->add('checkbox', 'is_delete_attachment', ts('Delete All Attachment(s)'));
407 $form->assign('currentAttachmentInfo', $currentAttachmentInfo);
410 $form->assign('currentAttachmentInfo', NULL);
413 if ($totalAttachments) {
414 if ($totalAttachments >= $numAttachments) {
418 $numAttachments -= $totalAttachments;
422 $form->assign('numAttachments', $numAttachments);
424 CRM_Core_BAO_Tag
::getTags('civicrm_file', $tags, NULL,
425 ' ', TRUE);
428 $parentNames = CRM_Core_BAO_Tag
::getTagSet('civicrm_file');
431 for ($i = 1; $i <= $numAttachments; $i++
) {
432 $form->addElement('file', "attachFile_$i", ts('Attach File'), 'size=30 maxlength=60');
433 $form->addUploadElement("attachFile_$i");
434 $form->setMaxFileSize($maxFileSize * 1024 * 1024);
435 $form->addRule("attachFile_$i",
436 ts('File size should be less than %1 MByte(s)',
437 array(1 => $maxFileSize)
440 $maxFileSize * 1024 * 1024
442 $form->addElement('text', "attachDesc_$i", NULL, array(
445 'placeholder' => ts('Description'),
449 $form->add('select', "tag_$i", ts('Tags'), $tags, FALSE,
452 'multiple' => 'multiple',
453 'class' => 'huge crm-select2',
454 'placeholder' => ts('- none -'),
458 CRM_Core_Form_Tag
::buildQuickForm($form, $parentNames, 'civicrm_file', NULL, FALSE, TRUE, "file_taglist_$i");
463 * Return a clean url string and the number of attachment for a
464 * given entityTable, entityID
466 * @param string $entityTable
467 * The entityTable to which the file is attached.
468 * @param int $entityID
469 * The id of the object in the above entityTable.
470 * @param string $separator
471 * The string separator where to implode the urls.
474 * An array with 2 elements. The string and the number of attachments
477 public static function attachmentInfo($entityTable, $entityID, $separator = '<br />') {
482 $currentAttachments = self
::getEntityFile($entityTable, $entityID);
483 if (!empty($currentAttachments)) {
484 $currentAttachmentURL = array();
485 foreach ($currentAttachments as $fileID => $attach) {
486 $currentAttachmentURL[] = $attach['href'];
488 return implode($separator, $currentAttachmentURL);
495 * @param array $params
496 * @param $entityTable
497 * @param int $entityID
499 static function formatAttachment(
506 // delete current attachments if applicable
507 if ($entityID && !empty($formValues['is_delete_attachment'])) {
508 CRM_Core_BAO_File
::deleteEntityFile($entityTable, $entityID);
511 $numAttachments = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
, 'max_attachments');
513 $now = date('Ymdhis');
515 // setup all attachments
516 for ($i = 1; $i <= $numAttachments; $i++
) {
517 $attachName = "attachFile_$i";
518 $attachDesc = "attachDesc_$i";
519 $attachTags = "tag_$i";
520 $attachFreeTags = "file_taglist_$i";
521 if (isset($formValues[$attachName]) && !empty($formValues[$attachName])) {
522 // add static tags if selects
523 $tagParams = array();
524 if (!empty($formValues[$attachTags])) {
525 foreach ($formValues[$attachTags] as $tag) {
526 $tagParams[$tag] = 1;
530 // we dont care if the file is empty or not
533 'uri' => $formValues[$attachName]['name'],
534 'type' => $formValues[$attachName]['type'],
535 'location' => $formValues[$attachName]['name'],
536 'description' => $formValues[$attachDesc],
537 'upload_date' => $now,
539 'attachment_taglist' => CRM_Utils_Array
::value($attachFreeTags, $formValues, array()),
542 $params[$attachName] = $fileParams;
548 * @param array $params
549 * @param $entityTable
550 * @param int $entityID
552 public static function processAttachment(&$params, $entityTable, $entityID) {
553 $numAttachments = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
, 'max_attachments');
555 for ($i = 1; $i <= $numAttachments; $i++
) {
557 isset($params["attachFile_$i"]) &&
558 is_array($params["attachFile_$i"])
560 self
::filePostProcess(
561 $params["attachFile_$i"]['location'],
567 $params["attachFile_$i"],
569 $params["attachFile_$i"]['type']
578 public static function uploadNames() {
579 $numAttachments = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
, 'max_attachments');
582 for ($i = 1; $i <= $numAttachments; $i++
) {
583 $names[] = "attachFile_{$i}";
585 $names[] = 'uploadFile';
590 * copy/attach an existing file to a different entity
594 * @param $oldEntityTable
595 * @param int $oldEntityId
596 * @param $newEntityTable
597 * @param int $newEntityId
599 public static function copyEntityFile($oldEntityTable, $oldEntityId, $newEntityTable, $newEntityId) {
600 $oldEntityFile = new CRM_Core_DAO_EntityFile();
601 $oldEntityFile->entity_id
= $oldEntityId;
602 $oldEntityFile->entity_table
= $oldEntityTable;
603 $oldEntityFile->find();
605 while ($oldEntityFile->fetch()) {
606 $newEntityFile = new CRM_Core_DAO_EntityFile();
607 $newEntityFile->entity_id
= $newEntityId;
608 $newEntityFile->entity_table
= $newEntityTable;
609 $newEntityFile->file_id
= $oldEntityFile->file_id
;
610 $newEntityFile->save();
615 * @param $entityTable
616 * @param int $entityID
621 public static function deleteURLArgs($entityTable, $entityID, $fileID) {
622 $params['entityTable'] = $entityTable;
623 $params['entityID'] = $entityID;
624 $params['fileID'] = $fileID;
626 $signer = new CRM_Utils_Signer(CRM_Core_Key
::privateKey(), self
::$_signableFields);
627 $params['_sgn'] = $signer->sign($params);
628 return CRM_Utils_System
::makeQueryString($params);
632 * Delete a file attachment from an entity table / entity ID
636 public static function deleteAttachment() {
638 $params['entityTable'] = CRM_Utils_Request
::retrieve('entityTable', 'String', CRM_Core_DAO
::$_nullObject, TRUE);
639 $params['entityID'] = CRM_Utils_Request
::retrieve('entityID', 'Positive', CRM_Core_DAO
::$_nullObject, TRUE);
640 $params['fileID'] = CRM_Utils_Request
::retrieve('fileID', 'Positive', CRM_Core_DAO
::$_nullObject, TRUE);
642 $signature = CRM_Utils_Request
::retrieve('_sgn', 'String', CRM_Core_DAO
::$_nullObject, TRUE);
644 $signer = new CRM_Utils_Signer(CRM_Core_Key
::privateKey(), self
::$_signableFields);
645 if (!$signer->validate($signature, $params)) {
646 CRM_Core_Error
::fatal('Request signature is invalid');
649 CRM_Core_BAO_File
::deleteEntityFile($params['entityTable'], $params['entityID'], NULL, $params['fileID']);
654 * Display paper icon for a file attachment -- CRM-13624
656 * @param string $entityTable
657 * The entityTable to which the file is attached. eg "civicrm_contact", "civicrm_note", "civicrm_activity".
658 * If you have the ID of a specific row in civicrm_file, use $entityTable='*'
659 * @param int $entityID
660 * The id of the object in the above entityTable.
662 * @return array|NULL list of HTML snippets; one HTML snippet for each attachment. If none found, then NULL
666 public static function paperIconAttachment($entityTable, $entityID) {
667 if (empty($entityTable) ||
!$entityID) {
671 $currentAttachmentInfo = self
::getEntityFile($entityTable, $entityID);
672 foreach ($currentAttachmentInfo as $fileKey => $fileValue) {
673 $fileID = $fileValue['fileID'];
674 $fileType = $fileValue['mime_type'];
677 if ($fileType == 'image/jpeg' ||
678 $fileType == 'image/pjpeg' ||
679 $fileType == 'image/gif' ||
680 $fileType == 'image/x-png' ||
681 $fileType == 'image/png'
683 $url = $fileValue['url'];
684 $alt = $fileValue['cleanName'];
685 $file_url[$fileID] = "
686 <a href=\"$url\" class='crm-image-popup'>
687 <div class='icon paper-icon' title=\"$alt\" alt=\"$alt\"></div>
689 // for non image files
692 $url = $fileValue['url'];
693 $alt = $fileValue['cleanName'];
694 $file_url[$fileID] = "<a href=\"$url\"><div class='icon paper-icon' title=\"$alt\" alt=\"$alt\"></div></a>";
698 if (empty($file_url)) {
702 $results = $file_url;
708 * Get a reference to the file-search service (if one is available).
710 * @return CRM_Core_FileSearchInterface|NULL
712 public static function getSearchService() {
713 $fileSearches = array();
714 CRM_Utils_Hook
::fileSearches($fileSearches);
716 // use the first available search
717 foreach ($fileSearches as $fileSearch) {
718 /** @var $fileSearch CRM_Core_FileSearchInterface */