] * @since [v2.0] * @return String */ public static function parseEscapedMarkedown($str) { $Parsedown = new \Parsedown(); if ($str) { return $Parsedown->text(e($str)); } } /** * The importer has formatted number strings since v3, * so the value might be a string, or an integer. * If it's a number, format it as a string. * * @author [A. Gianotto] [] * @since [v2.0] * @return String */ public static function formatCurrencyOutput($cost) { if (is_numeric($cost)) { if (Setting::getSettings()->digit_separator=='1.234,56') { return number_format($cost, 2, ',', '.'); } return number_format($cost, 2, '.', ','); } // It's already been parsed. return $cost; } /** * Static colors for pie charts. * * @author [A. Gianotto] [] * @since [v3.3] * @return Array */ public static function defaultChartColors($index = 0) { $colors = [ "#008941", "#FF851B", "#006FA6", "#A30059", "#1CE6FF", "#FFDBE5", "#7A4900", "#0000A6", "#63FFAC", "#B79762", "#004D43", "#8FB0FF", "#997D87", "#5A0007", "#809693", "#FEFFE6", "#1B4400", "#4FC601", "#3B5DFF", "#4A3B53", "#FF2F80", "#61615A", "#BA0900", "#6B7900", "#00C2A0", "#FFAA92", "#FF90C9", "#B903AA", "#D16100", "#DDEFFF", "#000035", "#7B4F4B", "#A1C299", "#300018", "#0AA6D8", "#013349", "#00846F", "#372101", "#FFB500", "#C2FFED", "#A079BF", "#CC0744", "#C0B9B2", "#C2FF99", "#001E09", "#00489C", "#6F0062", "#0CBD66", "#EEC3FF", "#456D75", "#B77B68", "#7A87A1", "#788D66", "#885578", "#FAD09F", "#FF8A9A", "#D157A0", "#BEC459", "#456648", "#0086ED", "#886F4C", "#34362D", "#B4A8BD", "#00A6AA", "#452C2C", "#636375", "#A3C8C9", "#FF913F", "#938A81", "#575329", "#00FECF", "#B05B6F", "#8CD0FF", "#3B9700", "#04F757", "#C8A1A1", "#1E6E00", "#7900D7", "#A77500", "#6367A9", "#A05837", "#6B002C", "#772600", "#D790FF", "#9B9700", "#549E79", "#FFF69F", "#201625", "#72418F", "#BC23FF", "#99ADC0", "#3A2465", "#922329", "#5B4534", "#FDE8DC", "#404E55", "#0089A3", "#CB7E98", "#A4E804", "#324E72", "#6A3A4C", "#83AB58", "#001C1E", "#D1F7CE", "#004B28", "#C8D0F6", "#A3A489", "#806C66", "#222800", "#BF5650", "#E83000", "#66796D", "#DA007C", "#FF1A59", "#8ADBB4", "#1E0200", "#5B4E51", "#C895C5", "#320033", "#FF6832", "#66E1D3", "#CFCDAC", "#D0AC94", "#7ED379", "#012C58", "#7A7BFF", "#D68E01", "#353339", "#78AFA1", "#FEB2C6", "#75797C", "#837393", "#943A4D", "#B5F4FF", "#D2DCD5", "#9556BD", "#6A714A", "#001325", "#02525F", "#0AA3F7", "#E98176", "#DBD5DD", "#5EBCD1", "#3D4F44", "#7E6405", "#02684E", "#962B75", "#8D8546", "#9695C5", "#E773CE", "#D86A78", "#3E89BE", "#CA834E", "#518A87", "#5B113C", "#55813B", "#E704C4", "#00005F", "#A97399", "#4B8160", "#59738A", "#FF5DA7", "#F7C9BF", "#643127", "#513A01", "#6B94AA", "#51A058", "#A45B02", "#1D1702", "#E20027", "#E7AB63", "#4C6001", "#9C6966", "#64547B", "#97979E", "#006A66", "#391406", "#F4D749", "#0045D2", "#006C31", "#DDB6D0", "#7C6571", "#9FB2A4", "#00D891", "#15A08A", "#BC65E9", "#FFFFFE", "#C6DC99", "#203B3C", "#671190", "#6B3A64", "#F5E1FF", "#FFA0F2", "#CCAA35", "#374527", "#8BB400", "#797868", "#C6005A", "#3B000A", "#C86240", "#29607C", "#402334", "#7D5A44", "#CCB87C", "#B88183", "#AA5199", "#B5D6C3", "#A38469", "#9F94F0", "#A74571", "#B894A6", "#71BB8C", "#00B433", "#789EC9", "#6D80BA", "#953F00", "#5EFF03", "#E4FFFC", "#1BE177", "#BCB1E5", "#76912F", "#003109", "#0060CD", "#D20096", "#895563", "#29201D", "#5B3213", "#A76F42", "#89412E", "#1A3A2A", "#494B5A", "#A88C85", "#F4ABAA", "#A3F3AB", "#00C6C8", "#EA8B66", "#958A9F", "#BDC9D2", "#9FA064", "#BE4700", "#658188", "#83A485", "#453C23", "#47675D", "#3A3F00", "#061203", "#DFFB71", "#868E7E", "#98D058", "#6C8F7D", "#D7BFC2", "#3C3E6E", "#D83D66", "#2F5D9B", "#6C5E46", "#D25B88", "#5B656C", "#00B57F", "#545C46", "#866097", "#365D25", "#252F99", "#00CCFF", "#674E60", "#FC009C", "#92896B", ]; return $colors[$index]; } /** * Increases or decreases the brightness of a color by a percentage of the current brightness. * * @param string $hexCode Supported formats: `#FFF`, `#FFFFFF`, `FFF`, `FFFFFF` * @param float $adjustPercent A number between -1 and 1. E.g. 0.3 = 30% lighter; -0.4 = 40% darker. * * @return string */ public static function adjustBrightness($hexCode, $adjustPercent) { $hexCode = ltrim($hexCode, '#'); if (strlen($hexCode) == 3) { $hexCode = $hexCode[0] . $hexCode[0] . $hexCode[1] . $hexCode[1] . $hexCode[2] . $hexCode[2]; } $hexCode = array_map('hexdec', str_split($hexCode, 2)); foreach ($hexCode as & $color) { $adjustableLimit = $adjustPercent < 0 ? $color : 255 - $color; $adjustAmount = ceil($adjustableLimit * $adjustPercent); $color = str_pad(dechex($color + $adjustAmount), 2, '0', STR_PAD_LEFT); } return '#' . implode($hexCode); } /** * Static background (highlight) colors for pie charts * This is inelegant, and could be refactored later. * * @author [A. Gianotto] [] * @since [v3.2] * @return Array */ public static function chartBackgroundColors() { $colors = [ '#f56954', '#00a65a', '#f39c12', '#00c0ef', '#3c8dbc', '#d2d6de', '#3c8dbc', '#3c8dbc', '#3c8dbc', ]; return $colors; } /** * Format currency using comma for thousands until local info is property used. * * @author [A. Gianotto] [] * @since [v2.7] * @return String */ public static function ParseFloat($floatString) { $LocaleInfo = localeconv(); $floatString = str_replace(",", "", $floatString); $floatString = str_replace($LocaleInfo["decimal_point"], ".", $floatString); // Strip Currency symbol // If no currency symbol is set, default to $ because Murica $currencySymbol = $LocaleInfo['currency_symbol']; if (empty($currencySymbol)) { $currencySymbol = '$'; } $floatString = str_replace($currencySymbol, '', $floatString); return floatval($floatString); } /** * Get the list of status labels in an array to make a dropdown menu * * @author [A. Gianotto] [] * @since [v2.5] * @return Array */ public static function statusLabelList() { $statuslabel_list = array('' => trans('general.select_statuslabel')) + Statuslabel::orderBy('default_label', 'desc')->orderBy('name','asc')->orderBy('deployable','desc') ->pluck('name', 'id')->toArray(); return $statuslabel_list; } /** * Get the list of deployable status labels in an array to make a dropdown menu * * @todo This should probably be a selectlist, same as the other endpoints * and we should probably add to the API controllers to make sure that * the status_id submitted is actually really deployable. * * @author [A. Gianotto] [] * @since [v5.1.0] * @return Array */ public static function deployableStatusLabelList() { $statuslabel_list = Statuslabel::where('deployable', '=', '1')->orderBy('default_label', 'desc') ->orderBy('name','asc') ->orderBy('deployable','desc') ->pluck('name', 'id')->toArray(); return $statuslabel_list; } /** * Get the list of status label types in an array to make a dropdown menu * * @author [A. Gianotto] [] * @since [v2.5] * @return Array */ public static function statusTypeList() { $statuslabel_types = array('' => trans('admin/hardware/form.select_statustype')) + array('deployable' => trans('admin/hardware/general.deployable')) + array('pending' => trans('admin/hardware/general.pending')) + array('undeployable' => trans('admin/hardware/general.undeployable')) + array('archived' => trans('admin/hardware/general.archived')); return $statuslabel_types; } /** * Get the list of depreciations in an array to make a dropdown menu * * @author [A. Gianotto] [] * @since [v2.5] * @return Array */ public static function depreciationList() { $depreciation_list = ['' => 'Do Not Depreciate'] + Depreciation::orderBy('name', 'asc') ->pluck('name', 'id')->toArray(); return $depreciation_list; } /** * Get the list of category types in an array to make a dropdown menu * * @author [A. Gianotto] [] * @since [v2.5] * @return Array */ public static function categoryTypeList() { $category_types = array( '' => '', 'accessory' => 'Accessory', 'asset' => 'Asset', 'consumable' => 'Consumable', 'component' => 'Component', 'license' => 'License' ); return $category_types; } /** * Get the list of custom fields in an array to make a dropdown menu * * @author [A. Gianotto] [] * @since [v2.5] * @return Array */ public static function customFieldsetList() { $customfields = array('' => trans('admin/models/general.no_custom_field')) + CustomFieldset::pluck('name', 'id')->toArray(); return $customfields; } /** * Get the list of custom field formats in an array to make a dropdown menu * * @author [A. Gianotto] [] * @since [v3.4] * @return Array */ public static function predefined_formats() { $keys = array_keys(CustomField::PREDEFINED_FORMATS); $stuff = array_combine($keys, $keys); return $stuff; } /** * Get the list of barcode dimensions * * @author [A. Gianotto] [] * @since [v3.3] * @return Array */ public static function barcodeDimensions($barcode_type = 'QRCODE') { if ($barcode_type == 'C128') { $size['height'] = '-1'; $size['width'] = '-10'; } elseif ($barcode_type == 'PDF417') { $size['height'] = '-3'; $size['width'] = '-10'; } else { $size['height'] = '-3'; $size['width'] = '-3'; } return $size; } /** * Generates a random string * * @author [A. Gianotto] [] * @since [v3.0] * @return Array */ public static function generateRandomString($length = 10) { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $charactersLength = strlen($characters); $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, $charactersLength - 1)]; } return $randomString; } /** * This nasty little method gets the low inventory info for the * alert dropdown * * @author [A. Gianotto] [] * @since [v3.0] * @return Array */ public static function checkLowInventory() { $consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get(); $accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get(); $components = Component::withCount('assets as assets_count')->whereNotNull('min_amt')->get(); $avail_consumables = 0; $items_array = array(); $all_count = 0; foreach ($consumables as $consumable) { $avail = $consumable->numRemaining(); if ($avail < ($consumable->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) { if ($consumable->qty > 0) { $percent = number_format((($avail / $consumable->qty) * 100), 0); } else { $percent = 100; } $items_array[$all_count]['id'] = $consumable->id; $items_array[$all_count]['name'] = $consumable->name; $items_array[$all_count]['type'] = 'consumables'; $items_array[$all_count]['percent'] = $percent; $items_array[$all_count]['remaining'] = $avail; $items_array[$all_count]['min_amt']=$consumable->min_amt; $all_count++; } } foreach ($accessories as $accessory) { $avail = $accessory->qty - $accessory->users_count; if ($avail < ($accessory->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) { if ($accessory->qty > 0) { $percent = number_format((($avail / $accessory->qty) * 100), 0); } else { $percent = 100; } $items_array[$all_count]['id'] = $accessory->id; $items_array[$all_count]['name'] = $accessory->name; $items_array[$all_count]['type'] = 'accessories'; $items_array[$all_count]['percent'] = $percent; $items_array[$all_count]['remaining'] = $avail; $items_array[$all_count]['min_amt']=$accessory->min_amt; $all_count++; } } foreach ($components as $component) { $avail = $component->qty - $component->assets_count; if ($avail < ($component->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) { if ($component->qty > 0) { $percent = number_format((($avail / $component->qty) * 100), 0); } else { $percent = 100; } $items_array[$all_count]['id'] = $component->id; $items_array[$all_count]['name'] = $component->name; $items_array[$all_count]['type'] = 'components'; $items_array[$all_count]['percent'] = $percent; $items_array[$all_count]['remaining'] = $avail; $items_array[$all_count]['min_amt']=$component->min_amt; $all_count++; } } return $items_array; } /** * Check if the file is an image, so we can show a preview * * @author [A. Gianotto] [] * @since [v3.0] * @param File $file * @return String | Boolean */ public static function checkUploadIsImage($file) { $finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension $filetype = @finfo_file($finfo, $file); finfo_close($finfo); if (($filetype=="image/jpeg") || ($filetype=="image/jpg") || ($filetype=="image/png") || ($filetype=="image/bmp") || ($filetype=="image/gif")) { return $filetype; } return false; } /** * Walks through the permissions in the permissions config file and determines if * permissions are granted based on a $selected_arr array. * * The $permissions array is a multidimensional array broke down by section. * (Licenses, Assets, etc) * * The $selected_arr should be a flattened array that contains just the * corresponding permission name and a true or false boolean to determine * if that group/user has been granted that permission. * * @author [A. Gianotto] [] * @since [v3.0] * @return boolean */ public static function checkIfRequired($class, $field) { $rules = $class::rules(); foreach ($rules as $rule_name => $rule) { if ($rule_name == $field) { if (strpos($rule, 'required') === false) { return false; } else { return true; } } } } /** * Check to see if the given key exists in the array, and trim excess white space before returning it * * @author Daniel Melzter * @since 3.0 * @param $array array * @param $key string * @param $default string * @return string */ public static function array_smart_fetch(array $array, $key, $default = '') { array_change_key_case($array, CASE_LOWER); return array_key_exists(strtolower($key), array_change_key_case($array)) ? e(trim($array[ $key ])) : $default; } /** * Gracefully handle decrypting encrypted fields (custom fields, etc). * * @todo allow this to handle more than just strings (arrays, etc) * * @author A. Gianotto * @since 3.6 * @param CustomField $field * @param String $string * @return string */ public static function gracefulDecrypt(CustomField $field, $string) { if ($field->isFieldDecryptable($string)) { try { Crypt::decrypt($string); return Crypt::decrypt($string); } catch (DecryptException $e) { return 'Error Decrypting: '.$e->getMessage(); } } return $string; } public static function formatStandardApiResponse($status, $payload = null, $messages = null) { $array['status'] = $status; $array['messages'] = $messages; if (($messages) && (is_array($messages)) && (count($messages) > 0)) { $array['messages'] = $messages; } ($payload) ? $array['payload'] = $payload : $array['payload'] = null; return $array; } /* Possible solution for unicode fieldnames */ public static function make_slug($string) { return preg_replace('/\s+/u', '_', trim($string)); } public static function getFormattedDateObject($date, $type = 'datetime', $array = true) { if ($date=='') { return null; } $settings = Setting::getSettings(); $tmp_date = new \Carbon($date); if ($type == 'datetime') { $dt['datetime'] = $tmp_date->format('Y-m-d H:i:s'); $dt['formatted'] = $tmp_date->format($settings->date_display_format .' '. $settings->time_display_format); } else { $dt['date'] = $tmp_date->format('Y-m-d'); $dt['formatted'] = $tmp_date->format($settings->date_display_format); } if ($array == 'true') { return $dt; } return $dt['formatted']; } // Nicked from Drupal :) // Returns a file size limit in bytes based on the PHP upload_max_filesize // and post_max_size public static function file_upload_max_size() { static $max_size = -1; if ($max_size < 0) { // Start with post_max_size. $post_max_size = Helper::parse_size(ini_get('post_max_size')); if ($post_max_size > 0) { $max_size = $post_max_size; } // If upload_max_size is less, then reduce. Except if upload_max_size is // zero, which indicates no limit. $upload_max = Helper::parse_size(ini_get('upload_max_filesize')); if ($upload_max > 0 && $upload_max < $max_size) { $max_size = $upload_max; } } return $max_size; } public static function file_upload_max_size_readable() { static $max_size = -1; if ($max_size < 0) { // Start with post_max_size. $post_max_size = Helper::parse_size(ini_get('post_max_size')); if ($post_max_size > 0) { $max_size = ini_get('post_max_size'); } // If upload_max_size is less, then reduce. Except if upload_max_size is // zero, which indicates no limit. $upload_max = Helper::parse_size(ini_get('upload_max_filesize')); if ($upload_max > 0 && $upload_max < $post_max_size) { $max_size = ini_get('upload_max_filesize'); } } return $max_size; } public static function parse_size($size) { $unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size. $size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size. if ($unit) { // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by. return round($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); } else { return round($size); } } public static function filetype_icon($filename) { $extension = substr(strrchr($filename,'.'),1); $allowedExtensionMap = [ // Images 'jpg' => 'fa fa-file-image-o', 'jpeg' => 'fa fa-file-image-o', 'gif' => 'fa fa-file-image-o', 'png' => 'fa fa-file-image-o', // word 'doc' => 'fa fa-file-word-o', 'docx' => 'fa fa-file-word-o', // Excel 'xls' => 'fa fa-file-excel-o', 'xlsx' => 'fa fa-file-excel-o', // archive 'zip' => 'fa fa-file-archive-o', 'rar' => 'fa fa-file-archive-o', //Text 'txt' => 'fa fa-file-text-o', 'rtf' => 'fa fa-file-text-o', 'xml' => 'fa fa-file-text-o', // Misc 'pdf' => 'fa fa-file-pdf-o', 'lic' => 'fa fa-file-floppy-o', ]; if ($extension && array_key_exists($extension, $allowedExtensionMap)) { return $allowedExtensionMap[$extension]; } return "fa fa-file-o"; } public static function show_file_inline($filename) { $extension = substr(strrchr($filename,'.'),1); if ($extension) { switch ($extension) { case 'jpg': case 'jpeg': case 'gif': case 'png': return true; break; default: return false; } } return false; } /** * Generate a random encrypted password. * * @author Wes Hulette * * @since 5.0.0 * * @return string */ public static function generateEncyrptedPassword(): string { return bcrypt(Helper::generateUnencryptedPassword()); } /** * Get a random unencrypted password. * * @author Steffen Buehl * * @since 5.0.0 * * @return string */ public static function generateUnencryptedPassword(): string { $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $password = ''; for ( $i = 0; $i < 20; $i++ ) { $password .= substr( $chars, random_int( 0, strlen( $chars ) - 1 ), 1 ); } return $password; } /** * Process base64 encoded image data and save it on supplied path * * @param string $image_data base64 encoded image data with mime type * @param string $save_path path to a folder where the image should be saved * @return string path to uploaded image or false if something went wrong */ public static function processUploadedImage(String $image_data, String $save_path) { if ($image_data == null || $save_path == null) { return false; } // After modification, the image is prefixed by mime info like the following: // data:image/jpeg;base64,; This causes the image library to be unhappy, so we need to remove it. $header = explode(';', $image_data, 2)[0]; // Grab the image type from the header while we're at it. $extension = substr($header, strpos($header, '/')+1); // Start reading the image after the first comma, postceding the base64. $image = substr($image_data, strpos($image_data, ',')+1); $file_name = str_random(25).".".$extension; $directory= public_path($save_path); // Check if the uploads directory exists. If not, try to create it. if (!file_exists($directory)) { mkdir($directory, 0755, true); } $path = public_path($save_path.$file_name); try { Image::make($image)->resize(500, 500, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); })->save($path); } catch (\Exception $e) { return false; } return $file_name; } }