3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * class to provide simple static functions for file objects
21 class CRM_Utils_File
{
24 * Given a file name, determine if the file contents make it an ascii file
30 * true if file is ascii
32 public static function isAscii($name) {
33 $fd = fopen($name, "r");
40 $line = fgets($fd, 8192);
41 if (!CRM_Utils_String
::isAscii($line)) {
52 * Given a file name, determine if the file contents make it an html file
58 * true if file is html
60 public static function isHtml($name) {
61 $fd = fopen($name, "r");
68 while (!feof($fd) & $lineCount <= 5) {
70 $line = fgets($fd, 8192);
71 if (!CRM_Utils_String
::isHtml($line)) {
82 * Create a directory given a path name, creates parent directories
88 * Should we abort or just return an invalid code.
90 * NULL: Folder already exists or was not specified.
91 * TRUE: Creation succeeded.
92 * FALSE: Creation failed.
94 public static function createDir($path, $abort = TRUE) {
95 if (is_dir($path) ||
empty($path)) {
99 CRM_Utils_File
::createDir(dirname($path), $abort);
100 if (@mkdir
($path, 0777) == FALSE) {
102 $docLink = CRM_Utils_System
::docURL2('Moving an Existing Installation to a New Server or Location', NULL, NULL, NULL, NULL, "wiki");
103 echo "Error: Could not create directory: $path.<p>If you have moved an existing CiviCRM installation from one location or server to another there are several steps you will need to follow. They are detailed on this CiviCRM wiki page - {$docLink}. A fix for the specific problem that caused this error message to be displayed is to set the value of the config_backend column in the civicrm_domain table to NULL. However we strongly recommend that you review and follow all the steps in that document.</p>";
105 CRM_Utils_System
::civiExit();
115 * Delete a directory given a path name, delete children directories
116 * and files if needed
118 * @param string $target
121 * @param bool $verbose
125 public static function cleanDir($target, $rmdir = TRUE, $verbose = TRUE) {
126 static $exceptions = ['.', '..'];
127 if ($target == '' ||
$target == '/' ||
!$target) {
128 throw new Exception("Overly broad deletion");
131 if ($dh = @opendir
($target)) {
132 while (FALSE !== ($sibling = readdir($dh))) {
133 if (!in_array($sibling, $exceptions)) {
134 $object = $target . DIRECTORY_SEPARATOR
. $sibling;
136 if (is_dir($object)) {
137 CRM_Utils_File
::cleanDir($object, $rmdir, $verbose);
139 elseif (is_file($object)) {
140 if (!unlink($object)) {
141 CRM_Core_Session
::setStatus(ts('Unable to remove file %1', [1 => $object]), ts('Warning'), 'error');
149 if (rmdir($target)) {
151 CRM_Core_Session
::setStatus(ts('Removed directory %1', [1 => $target]), '', 'success');
156 CRM_Core_Session
::setStatus(ts('Unable to remove directory %1', [1 => $target]), ts('Warning'), 'error');
163 * Concatenate several files.
165 * @param array $files
166 * List of file names.
167 * @param string $delim
168 * An optional delimiter to put between files.
171 public static function concat($files, $delim = '') {
174 foreach ($files as $file) {
178 $buf .= file_get_contents($file);
185 * @param string $source
186 * @param string $destination
188 public static function copyDir($source, $destination) {
189 if ($dh = opendir($source)) {
190 @mkdir
($destination);
191 while (FALSE !== ($file = readdir($dh))) {
192 if (($file != '.') && ($file != '..')) {
193 if (is_dir($source . DIRECTORY_SEPARATOR
. $file)) {
194 CRM_Utils_File
::copyDir($source . DIRECTORY_SEPARATOR
. $file, $destination . DIRECTORY_SEPARATOR
. $file);
197 copy($source . DIRECTORY_SEPARATOR
. $file, $destination . DIRECTORY_SEPARATOR
. $file);
206 * Given a file name, recode it (in place!) to UTF-8
208 * @param string $name
212 * whether the file was recoded properly
214 public static function toUtf8($name) {
215 static $config = NULL;
216 static $legacyEncoding = NULL;
217 if ($config == NULL) {
218 $config = CRM_Core_Config
::singleton();
219 $legacyEncoding = $config->legacyEncoding
;
222 if (!function_exists('iconv')) {
228 $contents = file_get_contents($name);
229 if ($contents === FALSE) {
233 $contents = iconv($legacyEncoding, 'UTF-8', $contents);
234 if ($contents === FALSE) {
238 $file = fopen($name, 'w');
239 if ($file === FALSE) {
243 $written = fwrite($file, $contents);
244 $closed = fclose($file);
245 if ($written === FALSE or !$closed) {
253 * Appends a slash to the end of a string if it doesn't already end with one
255 * @param string $path
256 * @param string $slash
260 public static function addTrailingSlash($path, $slash = NULL) {
262 // FIXME: Defaulting to backslash on windows systems can produce
263 // unexpected results, esp for URL strings which should always use forward-slashes.
264 // I think this fn should default to forward-slash instead.
265 $slash = DIRECTORY_SEPARATOR
;
267 if (!in_array(substr($path, -1, 1), ['/', '\\'])) {
274 * Save a fake file somewhere
277 * The directory where the file should be saved.
278 * @param string $contents
279 * Optional: the contents of the file.
280 * @param string $fileName
283 * The filename saved, or FALSE on failure.
285 public static function createFakeFile($dir, $contents = 'delete me', $fileName = NULL) {
286 $dir = self
::addTrailingSlash($dir);
288 $fileName = 'delete-this-' . CRM_Utils_String
::createRandom(10, CRM_Utils_String
::ALPHANUMERIC
);
290 $success = @file_put_contents
($dir . $fileName, $contents);
292 return ($success === FALSE) ?
FALSE : $fileName;
296 * @param string|NULL $dsn
297 * Use NULL to load the default/active connection from CRM_Core_DAO.
298 * Otherwise, give a full DSN string.
299 * @param string $fileName
300 * @param string $prefix
301 * @param bool $dieOnErrors
303 public static function sourceSQLFile($dsn, $fileName, $prefix = NULL, $dieOnErrors = TRUE) {
304 if (FALSE === file_get_contents($fileName)) {
305 // Our file cannot be found.
306 // Using 'die' here breaks this on extension upgrade.
307 throw new CRM_Core_Exception('Could not find the SQL file.');
310 self
::runSqlQuery($dsn, file_get_contents($fileName), $prefix, $dieOnErrors);
315 * @param string|NULL $dsn
316 * @param string $queryString
317 * @param string $prefix
318 * @param bool $dieOnErrors
320 public static function runSqlQuery($dsn, $queryString, $prefix = NULL, $dieOnErrors = TRUE) {
321 $string = $prefix . $queryString;
324 $db = CRM_Core_DAO
::getConnection();
327 require_once 'DB.php';
328 $dsn = CRM_Utils_SQL
::autoSwitchDSN($dsn);
329 $db = DB
::connect($dsn);
332 if (PEAR
::isError($db)) {
333 die("Cannot open $dsn: " . $db->getMessage());
335 $db->query('SET NAMES utf8mb4');
336 $transactionId = CRM_Utils_Type
::escape(CRM_Utils_Request
::id(), 'String');
337 $db->query('SET @uniqueID = ' . "'$transactionId'");
339 // get rid of comments starting with # and --
341 $string = self
::stripComments($string);
343 $queries = preg_split('/;\s*$/m', $string);
344 foreach ($queries as $query) {
345 $query = trim($query);
346 if (!empty($query)) {
347 CRM_Core_Error
::debug_query($query);
348 $res = &$db->query($query);
349 if (PEAR
::isError($res)) {
351 die("Cannot execute $query: " . $res->getMessage());
354 echo "Cannot execute $query: " . $res->getMessage() . "<p>";
363 * Strips comment from a possibly multiline SQL string
365 * @param string $string
370 public static function stripComments($string) {
371 return preg_replace("/^(#|--).*\R*/m", "", $string);
379 public static function isExtensionSafe($ext) {
380 static $extensions = NULL;
382 $extensions = CRM_Core_OptionGroup
::values('safe_file_extension', TRUE);
384 // make extensions to lowercase
385 $extensions = array_change_key_case($extensions, CASE_LOWER
);
386 // allow html/htm extension ONLY if the user is admin
387 // and/or has access CiviMail
388 if (!(CRM_Core_Permission
::check('access CiviMail') ||
389 CRM_Core_Permission
::check('administer CiviCRM') ||
390 (CRM_Mailing_Info
::workflowEnabled() &&
391 CRM_Core_Permission
::check('create mailings')
395 unset($extensions['html']);
396 unset($extensions['htm']);
399 // support lower and uppercase file extensions
400 return (bool) isset($extensions[strtolower($ext)]);
404 * Determine whether a given file is listed in the PHP include path.
406 * @param string $name
410 * whether the file can be include()d or require()d
412 public static function isIncludable($name) {
413 $x = @fopen
($name, 'r', TRUE);
424 * Remove the 32 bit md5 we add to the fileName also remove the unknown tag if we added it.
430 public static function cleanFileName($name) {
431 // replace the last 33 character before the '.' with null
432 $name = preg_replace('/(_[\w]{32})\./', '.', $name);
437 * Make a valid file name.
439 * @param string $name
443 public static function makeFileName($name) {
444 $uniqID = md5(uniqid(rand(), TRUE));
445 $info = pathinfo($name);
446 $basename = substr($info['basename'],
447 0, -(strlen(CRM_Utils_Array
::value('extension', $info)) +
(CRM_Utils_Array
::value('extension', $info) == '' ?
0 : 1))
449 if (!self
::isExtensionSafe(CRM_Utils_Array
::value('extension', $info))) {
450 // munge extension so it cannot have an embbeded dot in it
451 // The maximum length of a filename for most filesystems is 255 chars.
452 // We'll truncate at 240 to give some room for the extension.
453 return CRM_Utils_String
::munge("{$basename}_" . CRM_Utils_Array
::value('extension', $info) . "_{$uniqID}", '_', 240) . ".unknown";
456 return CRM_Utils_String
::munge("{$basename}_{$uniqID}", '_', 240) . "." . CRM_Utils_Array
::value('extension', $info);
466 public static function duplicate($filePath) {
467 $oldName = pathinfo($filePath, PATHINFO_FILENAME
);
468 $uniqID = md5(uniqid(rand(), TRUE));
469 $newName = preg_replace('/(_[\w]{32})$/', '', $oldName) . '_' . $uniqID;
470 $newPath = str_replace($oldName, $newName, $filePath);
471 copy($filePath, $newPath);
476 * Get files for the extension.
478 * @param string $path
483 public static function getFilesByExtension($path, $ext) {
484 $path = self
::addTrailingSlash($path);
486 if ($dh = opendir($path)) {
487 while (FALSE !== ($elem = readdir($dh))) {
488 if (substr($elem, -(strlen($ext) +
1)) == '.' . $ext) {
489 $files[] .= $path . $elem;
498 * Restrict access to a given directory (by planting there a restrictive .htaccess file)
501 * The directory to be secured.
502 * @param bool $overwrite
504 public static function restrictAccess($dir, $overwrite = FALSE) {
505 // note: empty value for $dir can play havoc, since that might result in putting '.htaccess' to root dir
506 // of site, causing site to stop functioning.
507 // FIXME: we should do more checks here -
508 if (!empty($dir) && is_dir($dir)) {
509 $htaccess = <<<HTACCESS
512 <IfModule !authz_core_module>
518 <IfModule authz_core_module>
524 $file = $dir . '.htaccess';
525 if ($overwrite ||
!file_exists($file)) {
526 if (file_put_contents($file, $htaccess) === FALSE) {
527 CRM_Core_Error
::movedSiteError($file);
534 * Restrict remote users from browsing the given directory.
538 public static function restrictBrowsing($publicDir) {
539 if (!is_dir($publicDir) ||
!is_writable($publicDir)) {
544 $nobrowse = realpath($publicDir) . '/index.html';
545 if (!file_exists($nobrowse)) {
546 @file_put_contents
($nobrowse, '');
550 $dir = new RecursiveDirectoryIterator($publicDir);
551 foreach ($dir as $name => $object) {
552 if (is_dir($name) && $name != '..') {
553 $nobrowse = realpath($name) . '/index.html';
554 if (!file_exists($nobrowse)) {
555 @file_put_contents
($nobrowse, '');
562 * (Deprecated) Create the file-path from which all other internal paths are
563 * computed. This implementation determines it as `dirname(CIVICRM_TEMPLATE_COMPILEDIR)`.
565 * This approach is problematic - e.g. it prevents one from authentically
566 * splitting the CIVICRM_TEMPLATE_COMPILEDIR away from other dirs. The implementation
567 * is preserved for backwards compatibility (and should only be called by
568 * CMS-adapters and by Civi\Core\Paths).
570 * Do not use it for new path construction logic. Instead, use Civi::paths().
573 * @see \Civi::paths()
574 * @see \Civi\Core\Paths
576 public static function baseFilePath() {
577 static $_path = NULL;
579 // Note: Don't rely on $config; that creates a dependency loop.
580 if (!defined('CIVICRM_TEMPLATE_COMPILEDIR')) {
581 throw new RuntimeException("Undefined constant: CIVICRM_TEMPLATE_COMPILEDIR");
583 $templateCompileDir = CIVICRM_TEMPLATE_COMPILEDIR
;
585 $path = dirname($templateCompileDir);
587 //this fix is to avoid creation of upload dirs inside templates_c directory
588 $checkPath = explode(DIRECTORY_SEPARATOR
, $path);
590 $cnt = count($checkPath) - 1;
591 if ($checkPath[$cnt] == 'templates_c') {
592 unset($checkPath[$cnt]);
593 $path = implode(DIRECTORY_SEPARATOR
, $checkPath);
596 $_path = CRM_Utils_File
::addTrailingSlash($path);
602 * Determine if a path is absolute.
604 * @param string $path
607 * TRUE if absolute. FALSE if relative.
609 public static function isAbsolute($path) {
610 if (substr($path, 0, 1) === DIRECTORY_SEPARATOR
) {
613 if (strtoupper(substr(PHP_OS
, 0, 3)) === 'WIN') {
614 if (preg_match('!^[a-zA-Z]:[/\\\\]!', $path)) {
626 * Computation of a relative path requires some base.
627 * This implementation is problematic because it relies on an
628 * implicit base which was constructed problematically.
630 public static function relativeDirectory($directory) {
631 // Do nothing on windows
632 if (strtoupper(substr(PHP_OS
, 0, 3)) === 'WIN') {
636 // check if directory is relative, if so return immediately
637 if (!self
::isAbsolute($directory)) {
641 // make everything relative from the baseFilePath
642 $basePath = self
::baseFilePath();
643 // check if basePath is a substr of $directory, if so
644 // return rest of string
645 if (substr($directory, 0, strlen($basePath)) == $basePath) {
646 return substr($directory, strlen($basePath));
649 // return the original value
655 * @param string $basePath
656 * The base path when evaluating relative paths. Should include trailing slash.
660 public static function absoluteDirectory($directory, $basePath) {
661 // check if directory is already absolute, if so return immediately
662 // Note: Windows PHP accepts any mix of "/" or "\", so "C:\htdocs" or "C:/htdocs" would be a valid absolute path
663 if (strtoupper(substr(PHP_OS
, 0, 3)) === 'WIN' && preg_match(';^[a-zA-Z]:[/\\\\];', $directory)) {
667 // check if directory is already absolute, if so return immediately
668 if (substr($directory, 0, 1) == DIRECTORY_SEPARATOR
) {
672 if ($basePath === NULL) {
673 // Previous versions interpreted `NULL` to mean "default to `self::baseFilePath()`".
674 // However, no code in the known `universe` relies on this interpretation, and
675 // the `baseFilePath()` function is problematic/deprecated.
676 throw new \
RuntimeException("absoluteDirectory() requires specifying a basePath");
679 // ensure that $basePath has a trailing slash
680 $basePath = self
::addTrailingSlash($basePath);
681 return $basePath . $directory;
685 * Make a file path relative to some base dir.
692 public static function relativize($directory, $basePath) {
693 if (strtoupper(substr(PHP_OS
, 0, 3)) === 'WIN') {
694 $directory = strtr($directory, '\\', '/');
695 $basePath = strtr($basePath, '\\', '/');
697 if (substr($directory, 0, strlen($basePath)) == $basePath) {
698 return substr($directory, strlen($basePath));
706 * Create a path to a temporary file which can endure for multiple requests.
708 * @todo Automatic file cleanup using, eg, TTL policy
710 * @param string $prefix
712 * @return string, path to an openable/writable file
715 public static function tempnam($prefix = 'tmp-') {
716 // $config = CRM_Core_Config::singleton();
717 // $nonce = md5(uniqid() . $config->dsn . $config->userFrameworkResourceURL);
718 // $fileName = "{$config->configAndLogDir}" . $prefix . $nonce . $suffix;
719 $fileName = tempnam(sys_get_temp_dir(), $prefix);
724 * Create a path to a temporary directory which can endure for multiple requests.
726 * @todo Automatic file cleanup using, eg, TTL policy
728 * @param string $prefix
730 * @return string, path to an openable/writable directory; ends with '/'
733 public static function tempdir($prefix = 'tmp-') {
734 $fileName = self
::tempnam($prefix);
736 mkdir($fileName, 0700);
737 return $fileName . '/';
741 * Search directory tree for files which match a glob pattern.
743 * Note: Dot-directories (like "..", ".git", or ".svn") will be ignored.
747 * @param string $pattern
748 * glob pattern, eg "*.txt".
749 * @param bool $relative
750 * TRUE if paths should be made relative to $dir
751 * @return array(string)
753 public static function findFiles($dir, $pattern, $relative = FALSE) {
754 if (!is_dir($dir) ||
!is_readable($dir)) {
757 // Which dirs should we exclude from our searches?
758 // If not defined, we default to excluding any dirname that begins
759 // with a . which is the old behaviour and therefore excludes .git/
760 $excludeDirsPattern = defined('CIVICRM_EXCLUDE_DIRS_PATTERN')
761 ?
constant('CIVICRM_EXCLUDE_DIRS_PATTERN')
762 : '@' . preg_quote(DIRECTORY_SEPARATOR
) . '\.@';
764 $dir = rtrim($dir, '/');
767 while (!empty($todos)) {
768 $subdir = array_shift($todos);
769 $matches = glob("$subdir/$pattern");
770 if (is_array($matches)) {
771 foreach ($matches as $match) {
772 if (!is_dir($match)) {
773 $result[] = $relative ? CRM_Utils_File
::relativize($match, "$dir/") : $match;
777 // Find subdirs to recurse into.
778 if ($dh = opendir($subdir)) {
779 while (FALSE !== ($entry = readdir($dh))) {
780 $path = $subdir . DIRECTORY_SEPARATOR
. $entry;
781 // Exclude . (self) and .. (parent) to avoid infinite loop.
782 // Exclude configured exclude dirs.
783 // Exclude dirs we can't read.
784 // Exclude anything that's not a dir.
788 && (empty($excludeDirsPattern) ||
!preg_match($excludeDirsPattern, $path))
790 && is_readable($path)
802 * Determine if $child is a sub-directory of $parent
804 * @param string $parent
805 * @param string $child
806 * @param bool $checkRealPath
810 public static function isChildPath($parent, $child, $checkRealPath = TRUE) {
811 if ($checkRealPath) {
812 $parent = realpath($parent);
813 $child = realpath($child);
815 $parentParts = explode('/', rtrim($parent, '/'));
816 $childParts = explode('/', rtrim($child, '/'));
817 while (($parentPart = array_shift($parentParts)) !== NULL) {
818 $childPart = array_shift($childParts);
819 if ($parentPart != $childPart) {
823 if (empty($childParts)) {
833 * Move $fromDir to $toDir, replacing/deleting any
834 * pre-existing content.
836 * @param string $fromDir
837 * The directory which should be moved.
838 * @param string $toDir
839 * The new location of the directory.
840 * @param bool $verbose
845 public static function replaceDir($fromDir, $toDir, $verbose = FALSE) {
846 if (is_dir($toDir)) {
847 if (!self
::cleanDir($toDir, TRUE, $verbose)) {
852 // return rename($fromDir, $toDir); CRM-11987, https://bugs.php.net/bug.php?id=54097
854 CRM_Utils_File
::copyDir($fromDir, $toDir);
855 if (!CRM_Utils_File
::cleanDir($fromDir, TRUE, FALSE)) {
856 CRM_Core_Session
::setStatus(ts('Failed to clean temp dir: %1', [1 => $fromDir]), '', 'alert');
865 * @param array $param
866 * @param string $fileName
867 * @param array $extraParams
869 public static function formatFile(&$param, $fileName, $extraParams = []) {
870 if (empty($param[$fileName])) {
875 'uri' => $param[$fileName]['name'],
876 'type' => $param[$fileName]['type'],
877 'location' => $param[$fileName]['name'],
878 'upload_date' => date('YmdHis'),
881 $param[$fileName] = $fileParams;
885 * Return formatted file URL, like for image file return image url with image icon
887 * @param string $path
889 * @param string $fileType
891 * File preview link e.g. https://example.com/civicrm/file?reset=1&filename=image.png&mime-type=image/png
893 * @return string $url
895 public static function getFileURL($path, $fileType, $url = NULL) {
896 if (empty($path) ||
empty($fileType)) {
899 elseif (empty($url)) {
900 $fileName = basename($path);
901 $url = CRM_Utils_System
::url('civicrm/file', "reset=1&filename={$fileName}&mime-type={$fileType}");
910 list($imageWidth, $imageHeight) = getimagesize($path);
911 list($imageThumbWidth, $imageThumbHeight) = CRM_Contact_BAO_Contact
::getThumbSize($imageWidth, $imageHeight);
912 $url = "<a href=\"$url\" class='crm-image-popup'>
913 <img src=\"$url\" width=$imageThumbWidth height=$imageThumbHeight/>
918 $url = sprintf('<a href="%s">%s</a>', $url, self
::cleanFileName(basename($path)));
926 * Return formatted image icon
928 * @param string $imageURL
929 * Contact's image url
931 * @return string $url
933 public static function getImageURL($imageURL) {
934 // retrieve image name from $imageURL
935 $imageURL = CRM_Utils_String
::unstupifyUrl($imageURL);
936 parse_str(parse_url($imageURL, PHP_URL_QUERY
), $query);
939 if (!empty($query['photo'])) {
940 $path = CRM_Core_Config
::singleton()->customFileUploadDir
. $query['photo'];
943 $path = $url = $imageURL;
945 $fileExtension = strtolower(pathinfo($path, PATHINFO_EXTENSION
));
946 //According to (https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types),
947 // there are some extensions that would need translating.:
948 $translateMimeTypes = [
953 $mimeType = 'image/' . CRM_Utils_Array
::value(
959 return self
::getFileURL($path, $mimeType, $url);
965 * @param string $sourceFile
966 * Filesystem path to existing image on server
967 * @param int $targetWidth
968 * New width desired, in pixels
969 * @param int $targetHeight
970 * New height desired, in pixels
971 * @param string $suffix = ""
972 * If supplied, the image will be renamed to include this suffix. For
973 * example if the original file name is "foo.png" and $suffix = "_bar",
974 * then the final file name will be "foo_bar.png".
975 * @param bool $preserveAspect = TRUE
976 * When TRUE $width and $height will be used as a bounding box, outside of
977 * which the resized image will not extend.
978 * When FALSE, the image will be resized exactly to $width and $height, even
979 * if it means stretching it.
983 * @throws \CRM_Core_Exception
984 * Under the following conditions
985 * - When GD is not available.
986 * - When the source file is not an image.
988 public static function resizeImage($sourceFile, $targetWidth, $targetHeight, $suffix = "", $preserveAspect = TRUE) {
990 // Check if GD is installed
991 $gdSupport = CRM_Utils_System
::getModuleSetting('gd', 'GD Support');
993 throw new CRM_Core_Exception(ts('Unable to resize image because the GD image library is not currently compiled in your PHP installation.'));
996 $sourceMime = mime_content_type($sourceFile);
997 if ($sourceMime == 'image/gif') {
998 $sourceData = imagecreatefromgif($sourceFile);
1000 elseif ($sourceMime == 'image/png') {
1001 $sourceData = imagecreatefrompng($sourceFile);
1003 elseif ($sourceMime == 'image/jpeg') {
1004 $sourceData = imagecreatefromjpeg($sourceFile);
1007 throw new CRM_Core_Exception(ts('Unable to resize image because the file supplied was not an image.'));
1010 // get image about original image
1011 $sourceInfo = getimagesize($sourceFile);
1012 $sourceWidth = $sourceInfo[0];
1013 $sourceHeight = $sourceInfo[1];
1015 // Adjust target width/height if preserving aspect ratio
1016 if ($preserveAspect) {
1017 $sourceAspect = $sourceWidth / $sourceHeight;
1018 $targetAspect = $targetWidth / $targetHeight;
1019 if ($sourceAspect > $targetAspect) {
1020 $targetHeight = $targetWidth / $sourceAspect;
1022 if ($sourceAspect < $targetAspect) {
1023 $targetWidth = $targetHeight * $sourceAspect;
1027 // figure out the new filename
1028 $pathParts = pathinfo($sourceFile);
1029 $targetFile = $pathParts['dirname'] . DIRECTORY_SEPARATOR
1030 . $pathParts['filename'] . $suffix . "." . $pathParts['extension'];
1032 $targetData = imagecreatetruecolor($targetWidth, $targetHeight);
1035 imagecopyresized($targetData, $sourceData,
1037 $targetWidth, $targetHeight, $sourceWidth, $sourceHeight);
1039 // save the resized image
1040 $fp = fopen($targetFile, 'w+');
1042 imagejpeg($targetData);
1043 $image_buffer = ob_get_contents();
1045 imagedestroy($targetData);
1046 fwrite($fp, $image_buffer);
1050 // return the URL to link to
1051 $config = CRM_Core_Config
::singleton();
1052 return $config->imageUploadURL
. basename($targetFile);
1056 * Get file icon class for specific MIME Type
1058 * @param string $mimeType
1061 public static function getIconFromMimeType($mimeType) {
1062 if (!isset(Civi
::$statics[__CLASS__
]['mimeIcons'])) {
1063 Civi
::$statics[__CLASS__
]['mimeIcons'] = json_decode(file_get_contents(__DIR__
. '/File/mimeIcons.json'), TRUE);
1065 $iconClasses = Civi
::$statics[__CLASS__
]['mimeIcons'];
1066 foreach ($iconClasses as $text => $icon) {
1067 if (strpos($mimeType, $text) === 0) {
1071 return $iconClasses['*'];
1075 * Is the filename a safe and valid filename passed in from URL
1077 * @param string $fileName
1080 public static function isValidFileName($fileName = NULL) {
1082 $check = ($fileName === basename($fileName));
1084 if (substr($fileName, 0, 1) == '/' ||
substr($fileName, 0, 1) == '.' ||
substr($fileName, 0, 1) == DIRECTORY_SEPARATOR
) {
1094 * Get the extensions that this MimeTpe is for
1095 * @param string $mimeType the mime-type we want extensions for
1098 public static function getAcceptableExtensionsForMimeType($mimeType = []) {
1099 $mimeRepostory = new \MimeTyper\Repository\
ExtendedRepository();
1100 return $mimeRepostory->findExtensions($mimeType);
1104 * Get the extension of a file based on its path
1105 * @param string $path path of the file to query
1108 public static function getExtensionFromPath($path) {
1109 return pathinfo($path, PATHINFO_EXTENSION
);