freescout/freescout-dist/app/Misc/Helper.php

2123 lines
69 KiB
PHP

<?php
/**
* Class for defining common app functions.
*/
namespace App\Misc;
use Carbon\Carbon;
use App\Option;
use App\User;
use App\CustomerChannel;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Console\Output\BufferedOutput;
class Helper
{
/**
* Default query cache time in seconds for remember() function.
*/
const QUERY_CACHE_TIME = 1000;
/**
* Text preview max length.
*/
const PREVIEW_MAXLENGTH = 255;
/**
* Deafult background job queue.
*/
const QUEUE_DEFAULT = 'default';
/**
* Limit for IN queries.
*/
const IN_LIMIT = 1000;
/**
* Permissions for directories.
*/
const DIR_PERMISSIONS = 0755;
public static $csp_nonce = null;
/**
* Stores list of global entities (for caching).
*/
public static $global_entities = [];
/**
* Flat allowing not to include datepicker JS and CSS twice.
*/
public static $datepicker_included = false;
/**
* Files with such extensions are being renamed on upload.
*/
public static $restricted_extensions = [
'php.*',
'sh',
'pl',
];
/**
* Menu structure used to display active menu item.
* Array are mnemonic names, strings - route names.
* Menu has 2 levels.
*/
public static $menu = [
'dashboard' => 'dashboard',
'mailbox' => [
'mailboxes.view',
'mailboxes.view.folder',
'conversations.view',
'conversations.create',
'conversations.draft',
//'conversations.search',
],
'manage' => [
'settings' => 'settings',
'mailboxes' => [
'mailboxes',
'mailboxes.update',
'mailboxes.create',
'mailboxes.connection',
'mailboxes.connection.incoming',
'mailboxes.permissions',
'mailboxes.auto_reply',
],
'users' => [
'users',
'users.create',
'users.profile',
'users.permissions',
'users.notifications',
'users.password',
],
'logs' => [
'logs',
'logs.app',
],
'system' => [
'system',
'system.tools',
],
],
// No menu item selected
'customers' => [],
];
/**
* Locales data.
*
* @var [type]
*/
public static $locales = [
'af' => ['name' => 'Afrikaans',
'name_en' => 'Afrikaans',
],
'sq' => ['name' => 'Shqip',
'name_en' => 'Albanian',
],
'ar' => ['name' => 'العربية',
'name_en' => 'Arabic',
],
'ar-IQ' => ['name' => 'العربية',
'name_en' => 'Arabic (Iraq)',
],
'ar-LY' => ['name' => 'العربية',
'name_en' => 'Arabic (Libya)',
],
'ar-MA' => ['name' => 'العربية',
'name_en' => 'Arabic (Morocco)',
],
'ar-OM' => ['name' => 'العربية',
'name_en' => 'Arabic (Oman)',
],
'ar-SY' => ['name' => 'العربية',
'name_en' => 'Arabic (Syria)',
],
'ar-LB' => ['name' => 'العربية',
'name_en' => 'Arabic (Lebanon)',
],
'ar-AE' => ['name' => 'العربية',
'name_en' => 'Arabic (U.A.E.)',
],
'ar-QA' => ['name' => 'العربية',
'name_en' => 'Arabic (Qatar)',
],
'ar-SA' => ['name' => 'العربية',
'name_en' => 'Arabic (Saudi Arabia)',
],
'ar-EG' => ['name' => 'العربية',
'name_en' => 'Arabic (Egypt)',
],
'ar-DZ' => ['name' => 'العربية',
'name_en' => 'Arabic (Algeria)',
],
'ar-TN' => ['name' => 'العربية',
'name_en' => 'Arabic (Tunisia)',
],
'ar-YE' => ['name' => 'العربية',
'name_en' => 'Arabic (Yemen)',
],
'ar-JO' => ['name' => 'العربية',
'name_en' => 'Arabic (Jordan)',
],
'ar-KW' => ['name' => 'العربية',
'name_en' => 'Arabic (Kuwait)',
],
'ar-BH' => ['name' => 'العربية',
'name_en' => 'Arabic (Bahrain)',
],
'az' => ['name' => 'Azerbaijani',
'name_en' => 'Azerbaijani',
],
'eu' => ['name' => 'Euskara',
'name_en' => 'Basque',
],
'be' => ['name' => 'Беларуская',
'name_en' => 'Belarusian',
],
'bn' => ['name' => 'বাংলা',
'name_en' => 'Bengali',
],
'bg' => ['name' => 'Български език',
'name_en' => 'Bulgarian',
],
'ca' => ['name' => 'Català',
'name_en' => 'Catalan',
],
'zh-CN' => ['name' => '简体中文',
'name_en' => 'Chinese (Simplified)',
],
'zh-SG' => ['name' => '简体中文',
'name_en' => 'Chinese (Singapore)',
],
'zh-TW' => ['name' => '简体中文',
'name_en' => 'Chinese (Traditional)',
],
'zh-HK' => ['name' => '简体中文',
'name_en' => 'Chinese (Hong Kong SAR)',
],
'hr' => ['name' => 'Hrvatski',
'name_en' => 'Croatian',
],
'cs' => ['name' => 'Čeština',
'name_en' => 'Czech',
],
'da' => ['name' => 'Dansk',
'name_en' => 'Danish',
],
'nl' => ['name' => 'Nederlands',
'name_en' => 'Dutch',
],
// 'nl_BE' => ['name' => 'Nederlands',
// 'name_en' => 'Dutch (Belgium)',
// ],
// 'en_US' => ['name' => 'English',
// 'name_en' => 'English (United States)',
// ],
// 'en_AU' => ['name' => '',
// 'name_en' => 'English (Australia)',
// ],
// 'en_NZ' => ['name' => '',
// 'name_en' => 'English (New Zealand)',
// ],
// 'en_ZA' => ['name' => '',
// 'name_en' => 'English (South Africa)',
// ],
'en' => ['name' => 'English',
'name_en' => 'English',
],
// 'en_TT' => ['name' => '',
// 'name_en' => 'English (Trinidad)',
// ],
// 'en_GB' => ['name' => '',
// 'name_en' => 'English (United Kingdom)',
// ],
// 'en_CA' => ['name' => '',
// 'name_en' => 'English (Canada)',
// ],
// 'en_IE' => ['name' => '',
// 'name_en' => 'English (Ireland)',
// ],
// 'en_JM' => ['name' => '',
// 'name_en' => 'English (Jamaica)',
// ],
// 'en_BZ' => ['name' => '',
// 'name_en' => 'English (Belize)',
// ],
'et' => ['name' => 'Eesti',
'name_en' => 'Estonian',
],
'fo' => ['name' => 'Føroyskt',
'name_en' => 'Faeroese',
],
'fa' => ['name' => 'فارسی',
'name_en' => 'Farsi',
],
'fi' => ['name' => 'Suomi',
'name_en' => 'Finnish',
],
'fr' => ['name' => 'Français',
'name_en' => 'French',
],
// 'fr_CA' => ['name' => '',
// 'name_en' => 'French (Canada)',
// ],
// 'fr_LU' => ['name' => '',
// 'name_en' => 'French (Luxembourg)',
// ],
// 'fr_BE' => ['name' => '',
// 'name_en' => 'French (Belgium)',
// ],
// 'fr_CH' => ['name' => '',
// 'name_en' => 'French (Switzerland)',
// ],
'gd' => ['name' => 'Gàidhlig',
'name_en' => 'Gaelic (Scotland)',
],
'de' => ['name' => 'Deutsch',
'name_en' => 'German',
],
// 'de_CH' => ['name' => '',
// 'name_en' => 'German (Switzerland)',
// ],
// 'de_LU' => ['name' => '',
// 'name_en' => 'German (Luxembourg)',
// ],
// 'de_AT' => ['name' => '',
// 'name_en' => 'German (Austria)',
// ],
// 'de_LI' => ['name' => '',
// 'name_en' => 'German (Liechtenstein)',
// ],
'el' => ['name' => 'Ελληνικά',
'name_en' => 'Greek',
],
'he' => ['name' => 'עברית',
'name_en' => 'Hebrew',
],
'hi' => ['name' => 'हिन्दी',
'name_en' => 'Hindi',
],
'hu' => ['name' => 'Magyar',
'name_en' => 'Hungarian',
],
'is' => ['name' => 'Íslenska',
'name_en' => 'Icelandic',
],
'id' => ['name' => 'Bahasa Indonesia',
'name_en' => 'Indonesian',
],
'ga' => ['name' => 'Gaeilge',
'name_en' => 'Irish',
],
'it' => ['name' => 'Italiano',
'name_en' => 'Italian',
],
// 'it_CH' => ['name' => 'Italiano',
// 'name_en' => 'Italian (Switzerland)',
// ],
'ja' => ['name' => '日本語',
'name_en' => 'Japanese',
],
'ko' => ['name' => '한국어 (韓國語)',
'name_en' => 'Korean (Johab)',
],
'lv' => ['name' => 'Latviešu valoda',
'name_en' => 'Latvian',
],
'lt' => ['name' => 'Lietuvių kalba',
'name_en' => 'Lithuanian',
],
'mk' => ['name' => 'Македонски јазик',
'name_en' => 'Macedonian (FYROM)',
],
'ms' => ['name' => 'Bahasa Melayu, بهاس ملايو',
'name_en' => 'Malay',
],
'mt' => ['name' => 'Malti',
'name_en' => 'Maltese',
],
'ne' => ['name' => 'नेपाली',
'name_en' => 'Nepali',
],
'no' => ['name' => 'Norsk bokmål',
'name_en' => 'Norwegian (Bokmal)',
],
'pl' => ['name' => 'Polski',
'name_en' => 'Polish',
],
'pt-PT' => ['name' => 'Português',
'name_en' => 'Portuguese (Portugal)',
],
'pt-BR' => ['name' => 'Português do Brasil',
'name_en' => 'Portuguese (Brazil)',
],
'ro' => ['name' => 'Română',
'name_en' => 'Romanian',
],
// 'ro_MO' => ['name' => 'Română',
// 'name_en' => 'Romanian (Republic of Moldova)',
// ],
'rm' => ['name' => 'Rumantsch grischun',
'name_en' => 'Romansh',
],
'ru' => ['name' => 'Русский',
'name_en' => 'Russian',
],
// 'ru_MO' => ['name' => '',
// 'name_en' => 'Russian (Republic of Moldova)',
// ],
'sz' => ['name' => 'Davvisámegiella',
'name_en' => 'Sami (Lappish)',
],
'sr' => ['name' => 'Српски језик',
'name_en' => 'Serbian (Latin)',
],
'sk' => ['name' => 'Slovenčina',
'name_en' => 'Slovak',
],
'sl' => ['name' => 'Slovenščina',
'name_en' => 'Slovenian',
],
/*'sb' => ['name' => 'Serbsce',
'name_en' => 'Sorbian',
],*/
'es' => ['name' => 'Español',
'name_en' => 'Spanish',
],
// 'es_GT' => ['name' => '',
// 'name_en' => 'Spanish (Guatemala)',
// ],
// 'es_PA' => ['name' => '',
// 'name_en' => 'Spanish (Panama)',
// ],
// 'es_VE' => ['name' => '',
// 'name_en' => 'Spanish (Venezuela)',
// ],
// 'es_PE' => ['name' => '',
// 'name_en' => 'Spanish (Peru)',
// ],
// 'es_EC' => ['name' => '',
// 'name_en' => 'Spanish (Ecuador)',
// ],
// 'es_UY' => ['name' => '',
// 'name_en' => 'Spanish (Uruguay)',
// ],
// 'es_BO' => ['name' => '',
// 'name_en' => 'Spanish (Bolivia)',
// ],
// 'es_HN' => ['name' => '',
// 'name_en' => 'Spanish (Honduras)',
// ],
// 'es_PR' => ['name' => '',
// 'name_en' => 'Spanish (Puerto Rico)',
// ],
// 'es_MX' => ['name' => '',
// 'name_en' => 'Spanish (Mexico)',
// ],
// 'es_CR' => ['name' => '',
// 'name_en' => 'Spanish (Costa Rica)',
// ],
// 'es_DO' => ['name' => '',
// 'name_en' => 'Spanish (Dominican Republic)',
// ],
// 'es_CO' => ['name' => '',
// 'name_en' => 'Spanish (Colombia)',
// ],
// 'es_AR' => ['name' => '',
// 'name_en' => 'Spanish (Argentina)',
// ],
// 'es_CL' => ['name' => '',
// 'name_en' => 'Spanish (Chile)',
// ],
// 'es_PY' => ['name' => '',
// 'name_en' => 'Spanish (Paraguay)',
// ],
// 'es_SV' => ['name' => '',
// 'name_en' => 'Spanish (El Salvador)',
// ],
// 'es_NI' => ['name' => '',
// 'name_en' => 'Spanish (Nicaragua)',
// ],
'sv' => ['name' => 'Svenska',
'name_en' => 'Swedish',
],
// unknown
// 'sx' => ['name' => '',
// 'name_en' => 'Sutu',
// ],
// 'sv_FI' => ['name' => '',
// 'name_en' => 'Swedish (Finland)',
// ],
'th' => ['name' => 'ไทย',
'name_en' => 'Thai',
],
'ts' => ['name' => 'Xitsonga',
'name_en' => 'Tsonga',
],
'tn' => ['name' => 'Setswana',
'name_en' => 'Tswana',
],
'tr' => ['name' => 'Türkçe',
'name_en' => 'Turkish',
],
'uk' => ['name' => 'українська',
'name_en' => 'Ukrainian',
],
'ur' => ['name' => 'اردو',
'name_en' => 'Urdu',
],
've' => ['name' => 'Tshivenḓa',
'name_en' => 'Venda',
],
'vi' => ['name' => 'Tiếng Việt',
'name_en' => 'Vietnamese',
],
'xh' => ['name' => 'isiXhosa',
'name_en' => 'Xhosa',
],
'ji' => ['name' => 'ייִדיש',
'name_en' => 'Yiddish',
],
'zu' => ['name' => 'isiZulu',
'name_en' => 'Zulu',
],
];
/**
* Cache time of the DB query.
*/
public static function cacheTime($enabled = true)
{
if ($enabled) {
return self::QUERY_CACHE_TIME;
} else {
return 0;
}
}
public static function setGlobalEntity($name, $entity)
{
self::$global_entities[$name] = $entity;
}
public static function getGlobalEntity($name)
{
return self::$global_entities[$name] ?? null;
}
/**
* Remove from text all tags, double spaces, etc.
*/
public static function stripTags($text)
{
// Remove all kinds of spaces after tags.
// https://stackoverflow.com/questions/3230623/filter-all-types-of-whitespace-in-php
$text = preg_replace("/^(.*)>[\r\n]*\s+/mu", '$1>', $text ?? '');
// Remove <script> and <style> blocks.
$text = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $text);
$text = preg_replace('#<style(.*?)>(.*?)</style>#is', '', $text);
// Remove tags.
$text = strip_tags($text);
$text = preg_replace('/\s+/mu', ' ', $text);
// Trim
$text = trim($text);
$text = preg_replace('/^\s+/mu', '', $text);
// Causes "General error: 1366 Incorrect string value"
// Remove "undetectable" whitespaces
// $whitespaces = ['%81', '%7F', '%C5%8D', '%8D', '%8F', '%C2%90', '%C2', '%90', '%9D', '%C2%A0', '%A0', '%C2%AD', '%AD', '%08', '%09', '%0A', '%0D'];
// $text = urlencode($text);
// foreach ($whitespaces as $char) {
// $text = str_replace($char, ' ', $text);
// }
// $text = urldecode($text);
$text = trim(preg_replace('/[ ]+/', ' ', $text));
return $text;
}
/**
* Get preview of the text in a plain form.
*/
public static function textPreview($text, $length = self::PREVIEW_MAXLENGTH)
{
$text = strtr($text ?? '', [
'</div>' => ' </div>',
'</p>' => ' </p>'
]);
$text = self::stripTags($text);
$text = mb_substr($text, 0, $length);
return $text;
}
/**
* Check if passed route name equals to the current one.
*/
public static function isCurrentRoute($route_name)
{
if (\Request::route()->getName() == $route_name) {
return true;
} else {
return false;
}
}
/**
* Check if menu item is selected.
* Each menu item has a mnemonic name.
*/
public static function isMenuSelected($menu_item_name)
{
$current_route = \Request::route()->getName();
$menu = \Eventy::filter('menu.selected', self::$menu);
foreach ($menu as $primary_name => $primary_items) {
if (!is_array($primary_items)) {
if ($current_route == $primary_items) {
return $primary_name == $menu_item_name;
}
if ($primary_name == $menu_item_name) {
return false;
}
continue;
}
foreach ($primary_items as $secondary_name => $secondary_routes) {
if (is_array($secondary_routes)) {
if (in_array($current_route, $secondary_routes)) {
return $secondary_name == $menu_item_name || $primary_name == $menu_item_name;
}
} elseif (is_string($secondary_name)) {
if ($current_route == $secondary_routes) {
return $secondary_name == $menu_item_name || $primary_name == $menu_item_name;
}
} else {
if ($current_route == $secondary_routes) {
return $primary_name == $menu_item_name || $menu_item_name == $secondary_routes;
}
}
}
}
return false;
}
public static function menuSelectedHtml($menu_item_name)
{
if (self::isMenuSelected($menu_item_name)) {
return 'active';
} else {
return '';
}
}
/**
* Resize image without using Intervention package.
*/
public static function resizeImage($file, $mime_type, $thumb_width, $thumb_height)
{
list($width, $height) = getimagesize($file);
if (!$width) {
return false;
}
if (preg_match('/png/i', $mime_type)) {
$src = imagecreatefrompng($file);
$kek = imagecolorallocate($src, 255, 255, 255);
imagefill($src, 0, 0, $kek);
} elseif (preg_match('/gif/i', $mime_type)) {
$src = imagecreatefromgif($file);
$kek = imagecolorallocate($src, 255, 255, 255);
imagefill($src, 0, 0, $kek);
} elseif (preg_match('/bmp/i', $mime_type)) {
$src = imagecreatefrombmp($file);
} else {
$src = imagecreatefromjpeg($file);
}
$original_aspect = $width / $height;
$thumb_aspect = $thumb_width / $thumb_height;
if ($original_aspect == $thumb_aspect) {
$new_height = $thumb_height;
$new_width = $thumb_width;
} elseif ($original_aspect > $thumb_aspect) {
// If image is wider than thumbnail (in aspect ratio sense)
$new_height = $thumb_height;
$new_width = $width / ($height / $thumb_height);
} else {
// If the thumbnail is wider than the image
$new_width = $thumb_width;
$new_height = $height / ($width / $thumb_width);
}
$thumb = imagecreatetruecolor($thumb_width, $thumb_height);
// Resize and crop
imagecopyresampled($thumb,
$src,
ceil(0 - ($new_width - $thumb_width) / 2), // Center the image horizontally
ceil(0 - ($new_height - $thumb_height) / 2), // Center the image vertically
0, 0,
ceil($new_width),
ceil($new_height),
$width, $height);
imagedestroy($src);
return $thumb;
}
public static function jsonToArray($json, $exclude_array = [])
{
if ($json) {
$array = json_decode($json, true);
if (json_last_error()) {
$array = [$json];
}
if ($array && $exclude_array) {
$array = array_diff($array, $exclude_array);
}
return $array;
} else {
return [];
}
}
public static function getDomain()
{
return parse_url(\Config::get('app.url'), PHP_URL_HOST);
}
/**
* Create zip archive.
* Source example: public/files/*
* File name example: test.zip.
* storage_path without app/
*/
public static function createZipArchive($source, $file_name, $folder = '', $storage_file_path = '')
{
if (!$source || !$file_name) {
return false;
}
$files = glob($source);
if (!$storage_file_path) {
$storage_file_path = 'zipper'.DIRECTORY_SEPARATOR.$file_name;
} else {
// if (!self::getPrivateStorage()->exists($storage_path)) {
// self::getPrivateStorage()->makeDirectory($storage_path);
// }
}
$dest_path = storage_path().DIRECTORY_SEPARATOR.'app'.DIRECTORY_SEPARATOR.$storage_file_path;
// If file exists it has to be deleted, otherwise Zipper will add file to the existing archive
if (self::getPrivateStorage()->exists($storage_file_path)) {
self::getPrivateStorage()->delete($storage_file_path);
}
\Chumper\Zipper\Facades\Zipper::make($dest_path)->folder($folder)->add($files)->close();
return $dest_path;
}
public static function getPrivateStorage()
{
return \Storage::disk('private');
}
public static function getPublicStorage()
{
return \Storage::disk('public');
}
public static function formatException($e)
{
return 'Error: '.$e->getMessage().'; File: '.$e->getFile().' ('.$e->getLine().')';
}
public static function denyAccess($msg = '')
{
abort(403, $msg ?: 'This action is unauthorized.');
}
/**
* Check if application version.
*
* @param [type] $ver [description]
*
* @return [type] [description]
*/
public static function checkAppVersion($version2, $operator = '>=')
{
return version_compare(\Config::get('app.version'), $version2, $operator);
}
/**
* Download remote file and save as file.
*/
public static function downloadRemoteFile($url, $destinationFilePath)
{
$client = new \GuzzleHttp\Client();
try {
$client->request('GET', $url, \Helper::setGuzzleDefaultOptions([
'sink' => $destinationFilePath,
'timeout' => 300, // seconds
'connect_timeout' => 7,
]));
} catch (\Exception $e) {
self::logException($e);
}
}
/**
* Extract ZIP archive.
* to: must be apsolute path, otherwise extracted into /public/$to.
*/
public static function unzip($archive, $to)
{
\Chumper\Zipper\Facades\Zipper::make($archive)->extractTo($to);
}
public static function logException($e, $prefix = '')
{
if ($prefix) {
$prefix .= ' ';
}
\Log::error($prefix.self::formatException($e));
}
/**
* Safely decrypt.
*
* @param [type] $e [description]
*
* @return [type] [description]
*/
public static function decrypt($value)
{
try {
$value = decrypt($value);
} catch (\Exception $e) {
// Do nothing.
}
return $value;
}
/**
* Log custom data to activity log.
*
* @param [type] $log_name [description]
* @param [type] $data [description]
* @param [type] $code [description]
*
* @return [type] [description]
*/
public static function log($log_name, $description, $properties = [])
{
activity()
->withProperties($properties)
->useLog($log_name)
->log($description);
}
/**
* Log exception to activity log.
*/
public static function logExceptionToActivityLog($e, $log_name, $description, $properties = [])
{
$properties['error'] = self::formatException($e);
activity()
->withProperties($properties)
->useLog($log_name)
->log($description);
}
/**
* Check if folder is writable.
*
* @param [type] $path [description]
*
* @return bool [description]
*/
public static function isFolderWritable($path)
{
if (!file_exists($path)) {
return false;
}
$path = rtrim($path, DIRECTORY_SEPARATOR);
try {
$file = $path.DIRECTORY_SEPARATOR.'.writable_test';
if ($file && file_put_contents($file, 'test')) {
unlink($file);
return true;
} else {
return false;
}
} catch (\Exception $e) {
return false;
}
}
public static function setLocale($locale)
{
if (in_array($locale, config('app.locales'))) {
app()->setLocale($locale);
}
}
/**
* Get locale's data.
*
* @param [type] $locale [description]
* @param string $param [description]
*
* @return [type] [description]
*/
public static function getLocaleData($locale, $param = '')
{
if (is_string($locale) && isset(self::$locales[$locale])) {
$data = self::$locales[$locale];
} else {
return;
}
if ($param) {
if (isset(self::$locales[$locale])) {
return self::$locales[$locale][$param];
} else {
return;
}
} else {
return $data;
}
}
/**
* Clear application cache.
*
* @return [type] [description]
*/
public static function clearCache($options = [])
{
\Artisan::call('freescout:clear-cache', $options);
}
/**
* Set variable in .evn file.
*/
public static function setEnvFileVar($key, $value)
{
$env_path = app()->environmentFilePath();
$contents = file_get_contents($env_path);
if (strstr($value, '"')) {
// Escape quotes.
$value = '"'.str_replace('"', '\"', $value).'"';
} elseif (!preg_match('/^[a-zA-Z0-9_]+$/', $value) && $value !== '') {
// Add quotes.
$value = '"'.$value.'"';
}
$old_value = '';
// Match the given key at the beginning of a line
preg_match("/^{$key}=[^\r\n]*/m", $contents, $matches);
if (count($matches)) {
$old_value = substr($matches[0], strlen($key) + 1);
}
if ($old_value) {
// Replace.
$contents = str_replace("{$key}={$old_value}", "{$key}={$value}", $contents);
} else {
// Add or empty value
preg_match("/^{$key}=[\r\n]/m", $contents, $matches);
if (count($matches)) {
// Replace empty value
$contents = str_replace("{$key}=", "{$key}={$value}", $contents);
} else {
// Add.
$contents = $contents."\n{$key}={$value}\n";
}
}
\File::put($env_path, $contents);
}
/**
* User may add an extra translation to the app on Translate page.
*
* @return [type] [description]
*/
public static function getCustomLocales()
{
return \Barryvdh\TranslationManager\Models\Translation::distinct()->pluck('locale')->toArray();
}
/**
* Get built in and custom locales.
*
* @return [type] [description]
*/
public static function getAllLocales()
{
$app_locales = config('app.locales');
// User may add an extra translation to the app on Translate page,
// we should allow user to see his custom translations.
$custom_locales = [];
try {
$custom_locales = \Helper::getCustomLocales();
} catch (\Exception $e) {
// During installation it throws an error as there is no tables yet.
}
if (count($custom_locales)) {
$app_locales = array_unique(array_merge($app_locales, $custom_locales));
}
return $app_locales;
}
/**
* app()->setLocale() in Localize middleware also changes config('app.locale'),
* so we are keeping real app locale in real_locale parameter.
*/
public static function getRealAppLocale()
{
return config('app.real_locale');
}
/**
* Create a backgound job executing specified action.
*
* @return [type] [description]
*/
public static function backgroundAction($action, $params, $delay = 0)
{
$delay = \Eventy::filter('backgound_action.dispatch_delay', $delay, $action, $params);
$job = \App\Jobs\TriggerAction::dispatch($action, $params);
if ($delay) {
$job->delay($delay);
}
$job->onQueue('default');
}
/**
* Convert HTML into the text with \n.
*
* @param [type] $text [description]
*/
public static function htmlToText($text, $embed_images = false, $options = ['width' => 0])
{
// Process blockquotes.
$text = str_ireplace('<blockquote>', '<div>', $text);
$text = str_ireplace('</blockquote>', '</div>', $text);
if ($embed_images) {
// Replace embedded images with their urls.
$text = preg_replace( '/<img\b[^>]*src=\"([^>"]+)\"[^>]*>/i', "<div>$1</div>", $text);
}
return (new \Html2Text\Html2Text($text, $options))->getText();
}
/**
* Trim text removing non-breaking spaces also.
*
* @param [type] $text [description]
* @return [type] [description]
*/
public static function trim($text)
{
$text = preg_replace("/^\s+/u", '', $text);
$text = preg_replace("/\s+$/u", '', $text);
return $text;
}
/**
* Unicode escape sequences like “\u00ed” to proper UTF-8 encoded characters.
*
* @param [type] $text [description]
* @return [type] [description]
*/
public static function entities2utf8($text)
{
try {
return json_decode('"'.str_replace('"', '\\"', $text).'"');
} catch(\Exception $e) {
return $text;
}
}
/**
* Get app subdirectory in /subdirectory/1/2/ format.
*/
public static function getSubdirectory($keep_trailing_slash = false, $keep_front_slash = false)
{
$subdirectory = '';
$app_url = config('app.url');
// Check host to ignore default values.
$app_host = parse_url($app_url, PHP_URL_HOST);
if ($app_url && !in_array($app_host, ['localhost', 'example.com'])) {
$subdirectory = parse_url($app_url, PHP_URL_PATH);
} else {
// Before app is installed
$subdirectory = $_SERVER['PHP_SELF'];
$filename = basename($_SERVER['SCRIPT_FILENAME']);
if (basename($_SERVER['SCRIPT_NAME']) === $filename) {
$subdirectory = $_SERVER['SCRIPT_NAME'];
} elseif (basename($_SERVER['PHP_SELF']) === $filename) {
$subdirectory = $_SERVER['PHP_SELF'];
} elseif (array_key_exists('ORIG_SCRIPT_NAME', $_SERVER) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
$subdirectory = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
} else {
// Backtrack up the script_filename to find the portion matching
// php_self
$path = $_SERVER['PHP_SELF'];
$file = $_SERVER['SCRIPT_FILENAME'];
$segs = explode('/', trim($file, '/'));
$segs = array_reverse($segs);
$index = 0;
$last = \count($segs);
$subdirectory = '';
do {
$seg = $segs[$index];
$subdirectory = '/'.$seg.$subdirectory;
++$index;
} while ($last > $index && (false !== $pos = strpos($path, $subdirectory)) && 0 != $pos);
}
}
if ($subdirectory === null) {
$subdirectory = '';
}
$subdirectory = str_replace('public/index.php', '', $subdirectory);
$subdirectory = str_replace('index.php', '', $subdirectory);
$subdirectory = trim($subdirectory, '/');
if ($keep_trailing_slash) {
$subdirectory .= '/';
}
if ($keep_front_slash && $subdirectory != '/') {
$subdirectory = '/'.$subdirectory;
}
return $subdirectory;
}
/**
* Check current route.
*/
public static function isRoute($route_name)
{
$route = \Route::current();
if (!$route) {
return false;
}
$current = $route->getName();
if (is_array($route_name)) {
return in_array($current, $route_name);
} else {
return ($current == $route_name);
}
}
/**
* Check if passed app URL has default Laravel value.
*/
public static function isDefaultAppUrl($app_url)
{
$app_host = parse_url($app_url, PHP_URL_HOST);
if ($app_url && !in_array($app_host, ['localhost', 'example.com'])) {
return false;
} else {
return true;
}
}
/**
* Stop all queue:work processes.
*/
public static function queueWorkerRestart()
{
\Cache::forever('illuminate:queue:restart', Carbon::now()->getTimestamp());
// In some systems queue:work runs on a separate file system,
// so those queue:work processes may not get illuminate:queue:restart.
$job_exists = \App\Job::where('queue', 'default')
->where('payload', 'like', '{"displayName":"App\\\\\\\\Jobs\\\\\\\\RestartQueueWorker"%')
->exists();
if (!$job_exists) {
\App\Jobs\RestartQueueWorker::dispatch()->onQueue('default');
}
}
/**
* UTF-8 split text into parts with max. length.
*/
public static function strSplitKeepWords($str, $max_length = 75)
{
$array_words = explode(' ', $str);
$currentLength = 0;
$index = 0;
$array_output = [''];
foreach ($array_words as $word) {
// +1 because the word will receive back the space in the end that it loses in explode()
$wordLength = strlen($word) + 1;
if (($currentLength + $wordLength) <= $max_length) {
$array_output[$index] .= $word . ' ';
$currentLength += $wordLength;
} else {
$index += 1;
$currentLength = $wordLength;
$array_output[$index] = $word;
}
}
return $array_output;
}
/**
* Replace new line with doble <br />.
*/
public static function nl2brDouble($text)
{
return str_replace('<br />', '<br /><br />', nl2br($text));
}
/**
* Decode \u00ed.
*/
public static function decodeUnicode($str)
{
$str = preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function ($match) {
return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
}, $str);
return $str;
}
/**
* Convert text into json without converting chars into \u0411.
*/
public static function jsonEncodeUtf8($text)
{
return json_encode($text, JSON_UNESCAPED_UNICODE);
}
/**
* It looks like this is not used anywhere.
* Json encode to avoid "Unable to JSON encode payload. Error code: 5"
*/
// public static function jsonEncodeSafe($value, $options = 0, $depth = 512, $utfErrorFlag = false)
// {
// $encoded = json_encode($value, $options, $depth);
// switch (json_last_error()) {
// case JSON_ERROR_NONE:
// return $encoded;
// // case JSON_ERROR_DEPTH:
// // return 'Maximum stack depth exceeded'; // or trigger_error() or throw new Exception()
// // case JSON_ERROR_STATE_MISMATCH:
// // return 'Underflow or the modes mismatch'; // or trigger_error() or throw new Exception()
// // case JSON_ERROR_CTRL_CHAR:
// // return 'Unexpected control character found';
// // case JSON_ERROR_SYNTAX:
// // return 'Syntax error, malformed JSON'; // or trigger_error() or throw new Exception()
// case JSON_ERROR_UTF8:
// $clean = self::utf8ize($value);
// if ($utfErrorFlag) {
// //return 'UTF8 encoding error'; // or trigger_error() or throw new Exception()
// }
// return self::jsonEncodeSafe($clean, $options, $depth, true);
// // default:
// // return 'Unknown error'; // or trigger_error() or throw new Exception()
// }
// }
// public static function utf8ize($mixed)
// {
// if (is_array($mixed)) {
// foreach ($mixed as $key => $value) {
// $mixed[$key] = self::utf8ize($value);
// }
// } else if (is_string ($mixed)) {
// return utf8_encode($mixed);
// }
// return $mixed;
// }
/**
* Check if host is available on the port specified.
*/
public static function checkPort($host, $port, $timeout = 10)
{
$connection = @fsockopen($host, $port);
if (is_resource($connection)) {
fclose($connection);
return true;
} else {
return false;
}
}
public static function purifyHtml($html)
{
if (!$html) {
return $html;
}
$html = \Purifier::clean($html);
// It's not clear why it was needed to remove spaces after tags.
//
// Remove all kinds of spaces after tags
// https://stackoverflow.com/questions/3230623/filter-all-types-of-whitespace-in-php
//$html = preg_replace("/^(.*)>[\r\n]*\s+/mu", '$1>', $html);
return $html;
}
/**
* Replace password with asterisks.
*/
public static function safePassword($password)
{
return str_repeat("*", mb_strlen($password ?? ''));
}
/**
* Turn all URLs in clickable links.
* Released under public domain
* https://gist.github.com/jasny/2000705
*
* @param string $value
* @param array $protocols http/https, ftp, mail
* @param array $attributes
* @return string
*/
public static function linkify($value, $protocols = ['http', 'mail'], array $attributes = [])
{
// Link attributes
$attr = '';
foreach ($attributes as $key => $val) {
$attr .= ' ' . $key . '="' . htmlentities($val) . '"';
}
$links = array();
// Extract existing links and tags
$value = preg_replace_callback('~(<a .*?>.*?</a>|<.*?>)~i', function ($match) use (&$links) { return '<' . array_push($links, $match[1]) . '>'; }, $value ?? '') ?: $value;
$value = $value ?? '';
// Extract text links for each protocol
foreach ((array)$protocols as $protocol) {
switch ($protocol) {
case 'http':
case 'https':
//$value = preg_replace_callback('~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?<![\.,:])~i', function ($match) use ($protocol, &$links, $attr) {
//$value = preg_replace_callback('%(\b(([\w-]+)://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))%s', function ($match) use ($protocol, &$links, $attr) {
// https://github.com/freescout-helpdesk/freescout/issues/3402
$value = preg_replace_callback('%([>\r\n\s:;\( ]|^)((([\w-]+)://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))%s', function ($match) use ($protocol, &$links, $attr) {
if ($match[4]) {
$protocol = $match[4];
}
$link = $match[2];
$link = substr($link, strlen($match[3]));
//return '<' . array_push($links, "<a $attr href=\"$protocol://$link\">$protocol://$link</a>") . '>';
return $match[1].'<' . array_push($links, "<a $attr href=\"$protocol://$link\">".$match[2]."</a>") . '>';
}, $value) ?: $value;
break;
case 'mail': $value = preg_replace_callback('~([^\s<>]+?@[^\s<]+?\.[^\s<]+)(?<![\.,:\)])~', function ($match) use (&$links, $attr) { return '<' . array_push($links, "<a $attr href=\"mailto:{$match[1]}\">{$match[1]}</a>") . '>'; }, $value) ?: $value;
break;
default: $value = preg_replace_callback('~' . preg_quote($protocol, '~') . '://([^\s<]+?)(?<![\.,:])~i', function ($match) use ($protocol, &$links, $attr) { return '<' . array_push($links, "<a $attr href=\"$protocol://{$match[1]}\">$protocol://{$match[1]}</a>") . '>'; }, $value) ?: $value;
break;
}
}
// Insert all links
return preg_replace_callback('/<(\d+)>/', function ($match) use (&$links) { return $links[$match[1] - 1]; }, $value ?? '') ?: $value;
}
/**
* Generates unique ID of the application.
*/
public static function getAppIdentifier()
{
$identifier = md5(config('app.key').parse_url(config('app.url'), PHP_URL_HOST));
return $identifier;
}
/**
* Are we in the mobile app.
*/
public static function isInApp()
{
return (int)app('request')->cookie('in_app');
}
/**
* Get identifier for queue:work
*/
public static function getWorkerIdentifier($salt = '')
{
return md5((config('app.key') ?? '').$salt);
}
/**
* Get pids of the specified processes.
*/
public static function getRunningProcesses($search = '')
{
if (empty($search)) {
$search = \Helper::getWorkerIdentifier();
}
$pids = [];
try {
$processes = preg_split("/[\r\n]/", shell_exec("ps aux | grep '".$search."'"));
foreach ($processes as $process) {
$process = trim($process);
preg_match("/^[\S]+\s+([\d]+)\s+/", $process, $m);
if (empty($m)) {
// Another format (used in Docker image).
// 1713 nginx 0:00 /usr/bin/php82...
preg_match("/^([\d]+)\s+[\S]+\s+/", $process, $m);
}
if (!preg_match("/(sh \-c|grep )/", $process) && !empty($m[1])) {
$pids[] = $m[1];
}
}
} catch (\Exception $e) {
// Do nothing
}
return $pids;
}
public static function uploadFile($file, $allowed_exts = [], $allowed_mimes = [])
{
$ext = strtolower($file->getClientOriginalExtension());
if ($allowed_exts) {
if (!in_array($ext, $allowed_exts)) {
throw new \Exception(__('Unsupported file type'), 1);
}
}
if ($allowed_mimes) {
$mime_type = $file->getMimeType();
if (!in_array($mime_type, $allowed_mimes)) {
throw new \Exception(__('Unsupported file type'), 1);
}
}
$file_name = \Str::random(25).'.'.$ext;
$file_name = \Helper::sanitizeUploadedFileName($file_name, $file);
$file->storeAs('uploads', $file_name);
self::sanitizeUploadedFileData('uploads'.DIRECTORY_SEPARATOR.$file_name, self::getPublicStorage());
return self::uploadedFilePath($file_name);
}
public static function sanitizeUploadedFileData($file_path, $storage, $content = null)
{
// Remove <script>, href="", iframe, etc from SVG files.
// Any image can be interpreted as SVG by browser,
// so checking extension is not enough.
if ($storage->exists($file_path)
&& ($storage->mimeType($file_path) == 'image/svg+xml'
|| strtolower(pathinfo($file_path, PATHINFO_EXTENSION)) == 'svg')
) {
if (!$content) {
$content = $storage->get($file_path);
}
if ($content) {
$svg_sanitizer = new \enshrined\svgSanitize\Sanitizer();
$clean_content = $svg_sanitizer->sanitize($content);
if (!$clean_content) {
$clean_content = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $content);
}
$storage->put($file_path, $clean_content);
}
}
}
public static function uploadedFileRemove($name)
{
\Storage::delete('uploads/'.$name);
}
public static function uploadedFilePath($name)
{
return storage_path('uploads/'.$name);
}
public static function uploadedFileUrl($name)
{
return \Storage::url('uploads/'.$name);
}
public static function addSessionError($text, $key = 'default')
{
$errors = \Session::get('errors', new \Illuminate\Support\ViewErrorBag);
if (! $errors instanceof \Illuminate\Support\ViewErrorBag) {
$errors = new \Illuminate\Support\ViewErrorBag;
}
$message_bag = new \Illuminate\Support\MessageBag;
$message_bag->add($key, $text);
\Session::flash(
'errors', $errors->put('default', $message_bag)
);
}
public static function addFloatingFlash($text, $type = 'danger', $role = '')
{
$flashes = \Session::get('flashes_floating', []);
$flashes[] = [
'text' => $text,
'type' => $type,
'role' => $role,
];
\Session::flash('flashes_floating', $flashes);
}
public static function isMySql()
{
return \DB::connection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME) == 'mysql';
}
public static function isPgSql()
{
return \DB::connection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME) == 'pgsql';
}
public static function sqlLikeOperator()
{
return self::isPgSql() ? 'ilike' : 'like';
}
// PostgreSQL truncates string if it contains \u0000 symbol starting from this symbol.
// https://stackoverflow.com/questions/31671634/handling-unicode-sequences-in-postgresql
// https://github.com/freescout-helpdesk/freescout/issues/3485
public static function sqlSanitizeString($string)
{
return str_replace(json_decode('"\u0000"'), "", $string);
}
public static function humanFileSize($size, $unit="")
{
if ((!$unit && $size >= 1<<30) || $unit == "GB") {
return number_format($size/(1<<30),2)."GB";
}
if ((!$unit && $size >= 1<<20) || $unit == "MB") {
return number_format($size/(1<<20),2)."MB";
}
//if ((!$unit && $size >= 1<<10) || $unit == "KB") {
return number_format($size/(1<<10),2)."KB";
// }
// return number_format($size)." bytes";
}
public static function isPrint()
{
return (bool)app('request')->input('print');
}
public static function isDev()
{
return config('app.env') != 'production';
}
public static function substrUnicode($str, $s, $l = null)
{
return join("", array_slice(preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY), $s, $l));
}
/**
* Disable sql_require_primary_key option to avoid errors when migrating.
* Only for MySQL.
*/
public static function disableSqlRequirePrimaryKey()
{
if (!self::isMySql()) {
return;
}
try {
\DB::statement('SET SESSION sql_require_primary_key=0');
} catch (\Exception $e) {
// General error: 1193 Unknown system variable 'sql_require_primary_key'.
// Do nothing.
}
}
public static function downloadRemoteFileAsTmp($uri)
{
try {
$contents = self::getRemoteFileContents($uri);
if (!$contents) {
return false;
}
$temp_file = self::getTempFileName();
\File::put($temp_file, $contents);
return $temp_file;
} catch (\Exception $e) {
\Helper::logException($e, 'Error downloading a remote file ('.$uri.'): ');
return false;
}
}
// Replacement for file_get_contents() as some hostings
// do not allow reading remote files via allow_url_fopen option.
public static function getRemoteFileContents($url)
{
try {
$headers = get_headers($url);
// 307 - Temporary Redirect.
if (!preg_match("/(200|301|302|307)/", $headers[0])) {
return false;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
\Helper::setCurlDefaultOptions($ch);
curl_setopt($ch, CURLOPT_TIMEOUT, 180);
$contents = curl_exec($ch);
if (curl_errno($ch)) {
throw new \Exception(curl_errno($ch).' '.curl_error($ch), 1);
}
curl_close($ch);
if (!$contents) {
return false;
}
return $contents;
} catch (\Exception $e) {
\Helper::logException($e, 'Error downloading a remote file ('.$url.'): ');
return false;
}
}
public static function getTempDir()
{
return sys_get_temp_dir() ?: '/tmp';
}
public static function getTempFileName()
{
return tempnam(self::getTempDir(), self::getTempFilePrefix());
}
public static function getTempFilePrefix()
{
return 'fs-'.substr(md5(config('app.key').'temp_prefix'), 0, 8).'_';
}
// Keep in mind that $uploaded_file->getClientMimeType() returns
// incorrect mime type for images: application/octet-stream
public static function downloadRemoteFileAsTmpFile($uri)
{
$file_path = self::downloadRemoteFileAsTmp($uri);
if ($file_path) {
return new \Illuminate\Http\UploadedFile(
$file_path, basename($file_path),
null, null, true
);
} else {
return null;
}
}
public static function sanitizeUploadedFileName($file_name, $uploaded_file = null, $contents = null)
{
// Check extension.
$ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
if (preg_match('/('.implode('|', self::$restricted_extensions).')/', $ext)) {
// Add underscore to the extension if file has restricted extension.
$file_name = $file_name.'_';
} elseif ($ext == 'pdf') {
// Rename PDF to avoid running embedded JavaScript.
if ($uploaded_file && !$contents) {
$contents = file_get_contents($uploaded_file->getRealPath());
}
if ($contents && strstr($contents, '/JavaScript')) {
$file_name = $file_name.'_';
}
}
// Remove illegal chars.
$illegal_chars = [
// Unix.
'/',
chr(0),
// Windows.
'<',
'>',
':',
'"',
'/',
'\\',
'|',
'?',
'*',
// Macos.
':',
];
// 0-31 (ASCII control characters) for Windows.
for ($i = 0; $i < 32; $i++) {
$illegal_chars[] = chr($i);
}
$escaped_regex = preg_quote(implode('', $illegal_chars), '/');
// https://github.com/freescout-helpdesk/freescout/issues/3377
$file_name = mb_convert_encoding($file_name, 'UTF-8', 'UTF-8');
$file_name = preg_replace('/[' . $escaped_regex . ']/', '_', $file_name);
$file_name = preg_replace("/[\t\r\n]/", '', $file_name);
return $file_name;
}
public static function remoteFileName($file_url)
{
return preg_replace("/\?.*/", '', basename($file_url));
}
public static function binaryDataMimeType($data)
{
$finfo = new \finfo(FILEINFO_MIME_TYPE);
return $finfo->buffer($data);
}
/**
* https://php.watch/versions/8.0/deprecated-reflectionparameter-methods
*/
public static function getClass($param)
{
return $param->getType() && !$param->getType()->isBuiltin() ? new \ReflectionClass(method_exists($param->getType(), 'getName') ? $param->getType()->getName() : $param->getClass()->name) : null;
}
/**
* https://php.watch/versions/8.0/deprecated-reflectionparameter-methods
*/
public static function getClassName($param)
{
return $param->getType() && !$param->getType()->isBuiltin() ? method_exists($param->getType(), 'getName') ? $param->getType()->getName() : $param->getClass()->name : null;
}
public static function getWebCronHash()
{
return md5(config('app.key').'web_cron_hash');
}
public static function getProtocol($url = '')
{
return mb_strtolower(parse_url($url ?: config('app.url'), PHP_URL_SCHEME) ?: 'http');
}
public static function isHttps($url = '')
{
if (\Helper::isInstaller()) {
// In the Installer we determine HTTPS from URL.
return self::isCurrentUrlHttps();
} else {
return self::getProtocol($url) == 'https';
}
}
public static function isInstaller()
{
$request_uri = $_SERVER['REQUEST_URI'] ?? '';
$request_uri = preg_replace("#\?.*#", '', $request_uri);
return strstr($request_uri, '/install/') || preg_match("#/install$#", $request_uri);
}
public static function isCurrentUrlHttps()
{
if (in_array(strtolower($_SERVER['X_FORWARDED_PROTO'] ?? ''), array('https', 'on', 'ssl', '1'), true)
|| strtolower($_SERVER['HTTPS'] ?? '') == 'on'
|| ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') == 'https'
|| ($_SERVER['HTTP_CF_VISITOR'] ?? '') == '{"scheme":"https"}'
) {
return true;
} else {
return false;
}
}
public static function fixProtocol($url)
{
if (self::getProtocol() == 'http' && parse_url($url, PHP_URL_SCHEME) != 'http') {
return str_replace('https://', 'http://', $url);
}
if (self::getProtocol() == 'https' && parse_url($url, PHP_URL_SCHEME) != 'https') {
return str_replace('http://', 'https://', $url);
}
return $url;
}
/**
* Fix and parse date to Carbon.
*/
public static function parseDateToCarbon($date, $current_if_invalid = true)
{
if (preg_match('/\+0580/', $date)) {
$date = str_replace('+0580', '+0530', $date);
}
$date = trim(rtrim($date));
$date = preg_replace('/[<>]/', '', $date);
$date = str_replace('_', ' ', $date);
try {
return Carbon::parse($date);
} catch (\Exception $e) {
switch (true) {
case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
$date .= 'C';
break;
case preg_match('/([A-Z]{2,3}[\,|\ \,]\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}.*)+$/i', $date) > 0:
case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
case preg_match('/([A-Z]{2,3}\, \ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{2,4}\ [0-9]{2}\:[0-9]{2}\:[0-9]{2}\ [A-Z]{2}\ \-[0-9]{2}\:[0-9]{2}\ \([A-Z]{2,3}\ \-[0-9]{2}:[0-9]{2}\))+$/i', $date) > 0:
$array = explode('(', $date);
$array = array_reverse($array);
$date = trim(array_pop($array));
break;
}
try {
return Carbon::parse($date);
} catch (\Exception $_e) {
if ($current_if_invalid) {
return Carbon::now();
} else {
return null;
}
}
}
return null;
}
public static function urlHome()
{
return \Config::get('app.url');
// $url = rtrim($url, "/");
// return $url.'/home';
}
/**
* Request::url() may return URL with incorrect protocol.
* Use \Request::getRequestUri() instead.
*/
/*public static function currentUrl()
{
$url = \Request::urlFull();
if (\Str::startsWith(config('app.url'), 'http://') && !\Str::startsWith($url, 'http://')) {
$url = str_replace('https://', 'http://', $url);
}
if (\Str::startsWith(config('app.url'), 'https://') && !\Str::startsWith($url, 'https://')) {
$url = str_replace('http://', 'https://', $url);
}
return $url;
}*/
public static function isLocaleRtl(): bool
{
return in_array(app()->getLocale(), config("app.locales_rtl") ?? []);
}
public static function phoneToNumeric($phone)
{
$phone = preg_replace("/[^0-9]/", '', $phone);
return (string)$phone;
}
public static function checkRequiredExtensions()
{
$php_extensions = [];
$required_extensions = \Config::get('installer.requirements.php');
// Optional.
$required_extensions[] = 'intl';
foreach ($required_extensions as $extension_name) {
$alternatives = explode('/', $extension_name);
if ($alternatives) {
foreach ($alternatives as $alternative) {
$php_extensions[$extension_name] = extension_loaded(trim($alternative));
if ($php_extensions[$extension_name]) {
break;
}
}
} else {
$php_extensions[$extension_name] = extension_loaded($extension_name);
}
}
// Required in console.
if (self::isConsole() || !function_exists('shell_exec')) {
$pcntl_enabled = extension_loaded('pcntl');
} else {
$pcntl_enabled = preg_match("/enable/m", shell_exec("php -i | grep pcntl") ?? '');
}
$php_extensions['pcntl (console PHP)'] = $pcntl_enabled;
return $php_extensions;
}
public static function checkRequiredFunctions()
{
return [
'shell_exec (PHP)' => function_exists('shell_exec'),
'proc_open (PHP)' => function_exists('proc_open'),
'fpassthru (PHP)' => function_exists('fpassthru'),
'symlink (PHP)' => function_exists('symlink'),
'pcntl_signal (console PHP)' => function_exists('shell_exec') ? (int)shell_exec('php -r "echo (int)function_exists(\'pcntl_signal\');"') : false,
'ps (shell)' => function_exists('shell_exec') ? shell_exec('ps') : false,
];
}
public static function isInstalled()
{
return file_exists(storage_path().DIRECTORY_SEPARATOR.'.installed');
}
public static function isConsole()
{
return app()->runningInConsole();
}
/**
* Show a warning when background jobs sending emails
* are not processed for some time.
* https://github.com/freescout-helpdesk/freescout/issues/2808
*/
public static function maybeShowSendingProblemsAlert()
{
$flashes = [];
if (\Option::get('send_emails_problem')) {
$flashes[] = [
'type' => 'warning',
'text' => __('There is a problem processing outgoing mail queue — an admin should check :%a_begin%System Status:%a_end% and :%a_begin_recommendations%Recommendations:%a_end%', ['%a_begin%' => '<a href="'.route('system').'#cron" target="_blank">', '%a_end%' => '</a>', /*'%a_begin_logs%' => '<a href="'.route('logs', ['name' => 'send_errors']).'#cron" target="_blank">',*/ '%a_begin_recommendations%' => '<a href="https://github.com/freescout-helpdesk/freescout/wiki/Background-Jobs" target="_blank">']),
'unescaped' => true,
];
}
return $flashes;
}
public static function mbUcfirst($string, $encoding = 'UTF-8')
{
$first_char = mb_substr($string, 0, 1, $encoding);
$then = mb_substr($string, 1, null, $encoding);
return mb_strtoupper($first_char, $encoding) . $then;
}
/**
* This is needed to allow using regexes for large texts.
*/
public static function setPcreBacktrackLimit()
{
if ((int)ini_get('pcre.backtrack_limit') <= 1000000) {
ini_set('pcre.backtrack_limit', 1000000000);
}
}
/**
* Get client IP address.
*/
public static function getClientIp()
{
// Fix for CloudFlare: https://laracasts.com/discuss/channels/laravel/cloudflare-and-user-ip
// But if CloudFlare is not used any value can be set to "Cf-Connecting-Ip" header.
// if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
// $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
// }
return request()->ip();
}
public static function getTimeFormat()
{
$user = auth()->user();
if ($user) {
return $user->time_format;
} else {
return Option::get('time_format', User::TIME_FORMAT_24);
}
}
public static function isTimeFormat24()
{
return self::getTimeFormat() == User::TIME_FORMAT_24;
}
/**
* Runs artisan command and returns it's output.
*/
public static function runCommand($command, $options = [])
{
$output_buffer = new BufferedOutput();
\Artisan::call($command, $options, $output_buffer);
return $output_buffer->fetch();
}
public static function setCurlDefaultOptions($ch)
{
// Curl has default CURLOPT_CONNECTTIMEOUT=30 seconds.
curl_setopt($ch, CURLOPT_TIMEOUT, config('app.curl_timeout'));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, config('app.curl_connect_timeout'));
curl_setopt($ch, CURLOPT_PROXY, config('app.proxy'));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, config('app.curl_ssl_verifypeer'));
}
public static function setGuzzleDefaultOptions($params)
{
$default_params = [
'timeout' => config('app.curl_timeout'),
'connect_timeout' => config('app.curl_connect_timeout'),
'proxy' => config('app.proxy'),
// https://docs.guzzlephp.org/en/6.5/request-options.html#verify
'verify' => config('app.curl_ssl_verifypeer'),
];
return array_merge($default_params, $params);
}
public static function cspNonce()
{
if (self::$csp_nonce === null) {
self::$csp_nonce = \Str::random(25);
}
return self::$csp_nonce;
}
public static function cspMetaTag()
{
if (!config('app.csp_enabled')) {
return '';
}
$nonce = \Helper::cspNonce();
return "<meta http-equiv=\"Content-Security-Policy\" content=\"script-src 'self' 'nonce-".$nonce."' "
.config('app.csp_script_src').' '.\Eventy::filter('csp.script_src', '')."\">";
//<meta property=\"csp-nonce\" id=\"csp-nonce\" content=\"".$nonce."\">";
}
public static function cspNonceAttr()
{
if (!config('app.csp_enabled')) {
return '';
}
return ' nonce="'.\Helper::cspNonce().'"';
}
public static function isChatModeAvailable()
{
return count(CustomerChannel::getChannels());
}
public static function isChatMode()
{
return (int)\Session::get('chat_mode', 0);
}
public static function setChatMode($is_on)
{
if ((int)$is_on) {
\Session::put('chat_mode', 1);
} else {
\Session::forget('chat_mode');
}
}
public static function detectCloudFlare()
{
if (!empty($_SERVER['HTTP_CF_IPCOUNTRY'])
|| !empty($_SERVER['HTTP_CF_CONNECTING_IP'])
|| !empty($_SERVER['HTTP_CF_VISITOR'])
|| !empty($_SERVER['HTTP_CF_RAY'])
|| ($_SERVER['HTTP_CDN_LOOP'] ?? '') == 'cloudflare'
) {
return true;
} else {
return false;
}
}
// Correct format: 2023-12-14 19:21
// Datepicker with enableTime option enabled
// may return value in different format on iOS Safari: 2023-12-14T11:25
public static function sanitizeDatepickerDatetime($datetime)
{
return str_replace('T', ' ', $datetime);
}
}