Файловый менеджер - Редактировать - /home/lmsyaran/public_html/joomla5/libraries/fabrik/fabrik/fabrik/Helpers/Worker.php
Назад
<?php /** * Generic tools that all models use * * @package Joomla * @subpackage Fabrik * @copyright Copyright (C) 2005-2020 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Table\Table; use Joomla\CMS\Access\Access; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Multilanguage; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Version; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Log\Log; use Joomla\CMS\Uri\Uri; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Session\Session; use Joomla\CMS\Form\Form; use Joomla\CMS\Factory; use Joomla\CMS\Mail\Mail; use Joomla\CMS\Mail\MailHelper; use Joomla\CMS\Crypt\Crypt; use Joomla\CMS\Crypt\Key; use Joomla\CMS\Crypt\Cipher\SimpleCipher; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Path; use Joomla\Database\DatabaseDriver; use Joomla\CMS\Date\Date; use Joomla\CMS\Language\Text; use Fabrik\Helpers\FCipher; use FabTable; use Joomla\CMS\Application\CMSApplication; use Fabrik\Helpers\Php; /** * Generic tools that all models use * This code used to be in models/parent.php * * @package Joomla * @subpackage Fabrik.helpers * @since 3.0 */ class Worker { /** * Fabrik database objects * * @var JDatabaseDriver[] */ public static $database = null; /** * Fabrik db connections * * @var array */ public static $connection = null; /** * Plugin manager * * @var object */ public static $pluginManager = null; /** * Strtotime final date format * * @var string */ static protected $finalFormat = null; /** * Add slashes in parse message * * @var bool */ protected $parseAddSlashes = false; /** * Search data to replace placeholders * * @var array */ protected $_searchData = array(); /** * Get array of valid view types * * @return array */ public static function getViewTypes() { return array( 'article', 'cron', 'csv', 'details', 'element', 'form', 'list', 'package', 'visualization' ); } /** * Returns true if $view is a valid view type * * @param string $view View type * * @return bool */ public static function isViewType($view) { $view = strtolower(trim($view)); $viewTypes = self::getViewTypes(); return in_array($view, $viewTypes); } /** * Returns true if $file has an image extension type * * @param string $file Filename * * @return bool */ public static function isImageExtension($file) { $path_parts = pathinfo($file); if (array_key_exists('extension', $path_parts)) { $image_extensions_eregi = 'bmp|gif|jpg|jpeg|png'; return preg_match('/' . $image_extensions_eregi . '/i', $path_parts['extension']) > 0; } return false; } /** * Get audio mime type array, keyed by file extension * * @return array */ public static function getAudioMimeTypes() { return array( 'mp3' => 'audio/x-mpeg', 'm4a' => 'audio/x-m4a' ); } /** * Get audio mime type array, keyed by file extension * * @return array */ public static function getImageMimeTypes() { return array( 'png' => 'image/png', 'gif' => 'image/gif', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'bmp' => 'image/bmp', 'webp' => 'image/webp' ); } /** * Get document mime type array, keyed by file extension * * @return array */ public static function getDocMimeTypes() { return array( 'pdf' => 'application/pdf', 'epub' => 'document/x-epub' ); } /** * Get video mime type array, keyed by file extension * * @return array */ public static function getVideoMimeTypes() { return array( 'mp4' => 'video/mp4', 'm4v' => 'video/x-m4v', 'mov' => 'video/quicktime' ); } /** * Get Audio Mime type * * @param string $file Filename * * @return bool|string */ public static function getAudioMimeType($file) { $path_parts = pathinfo($file); $types = self::getAudioMimeTypes(); return ArrayHelper::getValue( $types, ArrayHelper::getValue($path_parts, 'extension', ''), false ); } /** * Get Audio Mime type * * @param string $file Filename * * @return bool|string */ public static function getImageMimeType($file) { $path_parts = pathinfo($file); $types = self::getImageMimeTypes(); return ArrayHelper::getValue( $types, ArrayHelper::getValue($path_parts, 'extension', ''), false ); } /** * Get Video Mime type * * @param string $file Filename * * @return bool|string */ public static function getVideoMimeType($file) { $path_parts = pathinfo($file); $types = self::getVideoMimeTypes(); return ArrayHelper::getValue( $types, ArrayHelper::getValue($path_parts, 'extension', ''), false ); } /** * Get Doc Mime type * * @param string $file Filename * * @return bool|string */ public static function getDocMimeType($file) { $path_parts = pathinfo($file); $types = self::getDocMimeTypes(); return ArrayHelper::getValue( $types, ArrayHelper::getValue($path_parts, 'extension', ''), false ); } /** * Get Podcast Mime type * * @param string $file Filename * * @return bool|string */ public static function getPodcastMimeType($file) { $mime_type = false; if ($mime_type = self::getVideoMimeType($file)) { return $mime_type; } elseif ($mime_type = self::getAudioMimeType($file)) { return $mime_type; } elseif ($mime_type = self::getDocMimeType($file)) { return $mime_type; } elseif ($mime_type = self::getImageMimeType($file)) { return $mime_type; } return $mime_type; } /** * Format a string to datetime * * http://fr.php.net/strftime * (use as strftime) * * @param string $date String date to format * @param string $format Date format strftime format * * @return array|void date info */ public static function strToDateTime($date, $format) { $weekdays = array('Sun' => '0', 'Mon' => '1', 'Tue' => '2', 'Wed' => '3', 'Thu' => '4', 'Fri' => '5', 'Sat' => '6'); if (!($date = self::str2Time($date, $format))) { return; } $shortMonths = array(Text::_('Jan'), Text::_('Feb'), Text::_('Mar'), Text::_('Apr'), Text::_('May'), Text::_('Jun'), Text::_('Jul'), Text::_('Aug'), Text::_('Sept'), Text::_('Oct'), Text::_('Nov'), Text::_('Dec')); /*$$ rob set day default to 1, so that if you have a date format string of %m-%Y the day is set to the first day of the month * and not the last day of the previous month (which is what a 0 here would represent) */ $dateTime = array('sec' => 0, 'min' => 0, 'hour' => 0, 'day' => 1, 'mon' => 0, 'year' => 0, 'timestamp' => 0); foreach ($date as $key => $val) { switch ($key) { case 'd': case 'e': case 'j': $dateTime['day'] = intval($val); break; case 'D': $dateTime['day'] = intval($weekdays[$val]); break; case 'm': case 'n': $dateTime['mon'] = intval($val); break; case 'b': $dateTime['mon'] = $shortMonths[$val] + 1; break; case 'Y': $dateTime['year'] = intval($val); break; case 'y': $dateTime['year'] = intval($val) + 2000; break; case 'G': case 'g': case 'H': case 'h': $dateTime['hour'] = intval($val); break; case 'M': $dateTime['min'] = intval($val); break; case 'i': $dateTime['min'] = intval($val); break; case 's': case 'S': $dateTime['sec'] = intval($val); break; } } $dateTime['timestamp'] = mktime($dateTime['hour'], $dateTime['min'], $dateTime['sec'], $dateTime['mon'], $dateTime['day'], $dateTime['year']); return $dateTime; } /** * Check for, and convert, any 'special' formats for strtotime, like 'yesterday', etc. * * @param string $date Date to check * @param bool $gmt Set date to universal time? * * @return string date */ public static function specialStrToMySQL($date, $gmt = true) { /** * $$$ hugh - if date is empty, just return today's date */ if (empty($date)) { $d = Factory::getDate(); $date = $d->toSql(!$gmt); return $date; } /** * lets check if we have some special text as per : * http://php.net/strtotime - this means we can use "+2 week" as a url filter * do this before we urldecode the date otherwise the + is replaced with ' '; */ $matches = array(); $matches2 = array(); $matches3 = array(); // E.g. now preg_match("/(now|ago|midnight|yesterday|today)/i", $date, $matches); // E.g. +2 Week preg_match("/[+|-][0-9]* (week\b|year\b|day\b|month\b)/i", $date, $matches2); // E.g. next Wednesday preg_match("/[next|last]* (monday\b|tuesday\b|wednesday\b|thursday\b|friday\b|saturday\b|sunday\b)/i", $date, $matches3); $matches = array_merge($matches, $matches2, $matches3); if (!empty($matches)) { $d = Factory::getDate($date); $date = $d->toSql(!$gmt); } return $date; } /** * String to time * * @param string $date Date representation * @param string $format Date format * * @return array date bits keyed on date representations e.g. m/d/Y */ public static function str2Time($date, $format) { /** * lets check if we have some special text as per : * http://php.net/strtotime - this means we can use "+2 week" as a url filter * do this before we urldecode the date otherwise the + is replaced with ' '; */ $matches = array(); $matches2 = array(); $matches3 = array(); // E.g. now preg_match("/[now|ago|midnight|yesterday|today]/i", $date, $matches); // E.g. +2 Week preg_match("/[+|-][0-9]* (week\b|year\b|day\b|month\b)/i", $date, $matches2); // E.g. next Wednesday preg_match("/[next|last]* (monday\b|tuesday\b|wednesday\b|thursday\b|friday\b|saturday\b|sunday\b)/i", $date, $matches3); $matches = array_merge($matches, $matches2, $matches3); if (!empty($matches)) { $d = Factory::getDate($date); $date = $d->format($format); } /* $$$ - hugh : urldecode (useful when ajax calls, may need better fix) * as per http://fabrikar.com/forums/showthread.php?p=43314#post43314 */ $date = urldecode($date); // Strip any textual date representations from the string $days = array('%A', '%a'); foreach ($days as $day) { if (strstr($format, $day)) { $format = str_replace($day, '', $format); $date = self::stripDay($date, $day == '%a' ? true : false); } } $months = array('%B', '%b', '%h'); foreach ($months as $month) { if (strstr($format, $month)) { $format = str_replace($month, '%m', $format); $date = self::monthToInt($date, $month == '%B' ? false : true); } } // @TODO: some of these aren't right for strftime self::$finalFormat = $format; $search = array('%d', '%e', '%D', '%j', '%m', '%b', '%Y', '%y', '%g', '%H', '%h', '%i', '%s', '%S', '%M'); $replace = array('(\d{2})', '(\d{1,2})', '(\w{3})', '(\d{1,2})', '(\d{2})', '(\w{3})', '(\d{4})', '(\d{2})', '(\d{1,2})', '(\d{2})', '(\d{2})', '(\d{2})', '(\d{2})', '(\d{2})', '(\d{2})'); $pattern = str_replace($search, $replace, $format); if (!preg_match("#$pattern#", $date, $matches)) { // Lets allow for partial date formats - e.g. just the date and ignore the time $format = explode('%', $format); if (empty($format)) { // No format left to test so return false return false; } array_pop($format); $format = trim(implode('%', $format)); self::$finalFormat = $format; return self::str2Time($date, $format); } $dp = $matches; if (!preg_match_all('#%(\w)#', $format, $matches)) { return false; } $id = $matches['1']; if (count($dp) != count($id) + 1) { return false; } $ret = array(); for ($i = 0, $j = count($id); $i < $j; $i++) { $ret[$id[$i]] = $dp[$i + 1]; } return $ret; } /** * Removed day of week name from string * * @param string $date The string date * @param bool $abrv Abbreviated day? * * @return string date */ public static function stripDay($date, $abrv = false) { if ($abrv) { $date = str_replace(Text::_('SUN'), '', $date); $date = str_replace(Text::_('MON'), '', $date); $date = str_replace(Text::_('TUE'), '', $date); $date = str_replace(Text::_('WED'), '', $date); $date = str_replace(Text::_('THU'), '', $date); $date = str_replace(Text::_('FRI'), '', $date); $date = str_replace(Text::_('SAT'), '', $date); } else { $date = str_replace(Text::_('SUNDAY'), '', $date); $date = str_replace(Text::_('MONDAY'), '', $date); $date = str_replace(Text::_('TUESDAY'), '', $date); $date = str_replace(Text::_('WEDNESDAY'), '', $date); $date = str_replace(Text::_('THURSDAY'), '', $date); $date = str_replace(Text::_('FRIDAY'), '', $date); $date = str_replace(Text::_('SATURDAY'), '', $date); } return $date; } /** * Convert a month (could be in any language) into the month number (1 = jan) * * @param string $date Data to convert * @param bool $abrv Is the month is a short or full name version * * @return string */ public static function monthToInt($date, $abrv = false) { if ($abrv) { $date = str_replace(Text::_('JANUARY_SHORT'), '01', $date); $date = str_replace(Text::_('FEBRUARY_SHORT'), '02', $date); $date = str_replace(Text::_('MARCH_SHORT'), '03', $date); $date = str_replace(Text::_('APRIL_SHORT'), '04', $date); $date = str_replace(Text::_('MAY_SHORT'), '05', $date); $date = str_replace(Text::_('JUNE_SHORT'), '06', $date); $date = str_replace(Text::_('JULY_SHORT'), '07', $date); $date = str_replace(Text::_('AUGUST_SHORT'), '08', $date); $date = str_replace(Text::_('SEPTEMBER_SHORT'), '09', $date); $date = str_replace(Text::_('OCTOBER_SHORT'), 10, $date); $date = str_replace(Text::_('NOVEMBER_SHORT'), 11, $date); $date = str_replace(Text::_('DECEMBER_SHORT'), 12, $date); } else { $date = str_replace(Text::_('JANUARY'), '01', $date); $date = str_replace(Text::_('FEBRUARY'), '02', $date); $date = str_replace(Text::_('MARCH'), '03', $date); $date = str_replace(Text::_('APRIL'), '04', $date); $date = str_replace(Text::_('MAY'), '05', $date); $date = str_replace(Text::_('JUNE'), '06', $date); $date = str_replace(Text::_('JULY'), '07', $date); $date = str_replace(Text::_('AUGUST'), '08', $date); $date = str_replace(Text::_('SEPTEMBER'), '09', $date); $date = str_replace(Text::_('OCTOBER'), 10, $date); $date = str_replace(Text::_('NOVEMBER'), 11, $date); $date = str_replace(Text::_('DECEMBER'), 12, $date); } return $date; } /** * Check a string is not reserved by Fabrik * * @param string $str To check * @param bool $strict Include things like rowid, listid in the reserved words, defaults to true * * @return bool */ public static function isReserved($str, $strict = true) { $reservedWords = array("task", "view", "layout", "option", "formid", "submit", "ul_max_file_size" , "ul_file_types", "ul_directory", 'adddropdownvalue', 'adddropdownlabel', 'ul_end_dir'); /* * $$$ hugh - a little arbitrary, but need to be able to exclude these so people can create lists from things like * log files, which include field names like rowid and itemid. So when saving an element, we now set strict mode * to false if it's not a new element. */ $strictWords = array("listid", 'rowid', 'itemid'); if ($strict) { $reservedWords = array_merge($reservedWords, $strictWords); } if (in_array(StringHelper::strtolower($str), $reservedWords)) { return true; } return false; } /** * Check a string is valid to use as an element name * * @param string $str To check * @param bool $strict Include things like rowid, listid in the reserved words, defaults to true * * @return bool */ public static function validElementName($str, $strict = true) { // check if it's a Fabrik reserved word if (self::isReserved($str, $strict)) { return false; } // check valid MySQL - start with letter or _, then only alphanumeric or underscore if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $str)) { return false; } // check for various other gotchas, like ending in _raw, starting with more than one _, etc. if (preg_match('/^submit|^__|_raw$/', $str)) { return false; } return true; } /** * Get the crypt object * * @param string type type of encryption (aes, crypt or simple) * * @since 3.1 * * @return Fabrik\Helpers\FCipher */ public static function getCrypt($type = 'simple') { return new FCipher($type); } /** * Special case placeholder handling for repeat data. When something (usually an element plugin) is doing * replacements for elements which are in the "same" repeat group, almost always they will want * the value for the same repeat instance, not a comma seperated list of all the values. So (say) * the upload element is creating a file path, for an upload element in a repeat group, of ... * '/uploads/{repeat_table___userid}/', and there are 4 repeat instance, it doesn't want a path of ... * '/uploads/34,45,94,103/', it just wants the one value from the same repeat count as the upload * element. Or a calc element doing "return '{repeat_table___first_name} {repeat_table___last_name}';". Etc. * * Rather than make this a part of parseMessageForPlaceHolder, for now I'm making it a sperate function, * which just handles this one very specific data replacement. Will look at merging it in with the main * parsing once we have a better understanding of where / when / how to do it. * * @param string $msg Text to parse * @param array $searchData Data to search for placeholders * @param object $el Element model of the element which is doing the replacing * @param int $repeatCounter Repeat instance * * @return string parsed message */ public function parseMessageForRepeats($msg, $searchData, $el, $repeatCounter) { if (strstr($msg??'', '{') && !empty($searchData)) { $groupModel = $el->getGroupModel(); if ($groupModel->canRepeat()) { $elementModels = $groupModel->getPublishedElements(); $formModel = $el->getFormModel(); foreach ($elementModels as $elementModel) { $repeatElName = $elementModel->getFullName(true, false); foreach (array($repeatElName, $repeatElName . '_raw') as $tmpElName) { if (strstr($msg, '{' . $tmpElName . '}')) { if (array_key_exists($tmpElName, $searchData) && is_array($searchData[$tmpElName]) && array_key_exists($repeatCounter, $searchData[$tmpElName])) { $tmpVal = $searchData[$tmpElName][$repeatCounter]; if (is_array($tmpVal)) { $tmpVal = implode(',', $tmpVal); } $msg = str_replace('{' . $tmpElName . '}', $tmpVal??'', $msg); } } } } } } return $msg; } /** * Iterates through string to replace every * {placeholder} with posted data * * @param mixed $msg Text|Array to parse * @param array $searchData Data to search for placeholders (default $_REQUEST) * @param bool $keepPlaceholders If no data found for the place holder do we keep the {...} string in the * message * @param bool $addSlashes Add slashed to the text? * @param object $theirUser User to use in replaceWithUserData (defaults to logged in user) * @param bool $unsafe If true (default) will not replace certain placeholders like $jConfig_secret * must not be shown to users * * @return string parsed message */ public function parseMessageForPlaceHolder($msg, $searchData = null, $keepPlaceholders = true, $addSlashes = false, $theirUser = null, $unsafe = true) { $returnType = is_array($msg) ? 'array' : 'string'; $messages = (array) $msg; foreach ($messages as &$msg) { $this->parseAddSlashes = $addSlashes; if (!($msg == '' || is_array($msg) || StringHelper::strpos($msg, '{') === false)) { $msg = str_replace(array('%7B', '%7D'), array('{', '}'), $msg); if (is_object($searchData)) { $searchData = ArrayHelper::fromObject($searchData); } // Merge in request and specified search data $f = InputFilter::getInstance(); $post = $f->clean($_REQUEST, 'array'); //J!4 & SEF: $_REQUEST is empty, take also inputVars $app = Factory::getApplication(); $inputVars = $app->getInput()->getArray(); $inputVars = $f->clean($inputVars,'string'); $searchData = is_null($searchData) ? $inputVars : array_merge($inputVars, $searchData); $this->_searchData = is_null($searchData) ? $post : array_merge($post, $searchData); // Enable users to use placeholder to insert session token $this->_searchData['Session::getFormToken'] = Session::getFormToken(); // Replace with the user's data $msg = self::replaceWithUserData($msg); if (!is_null($theirUser)) { // Replace with a specified user's data $msg = self::replaceWithUserData($msg, $theirUser, 'your'); } $msg = self::replaceWithGlobals($msg); if (!$unsafe) { $msg = self::replaceWithUnsafe($msg); $msg = self::replaceWithSession($msg); } $msg = preg_replace("/{}/", "", $msg); // Replace {element name} with form data $msg = preg_replace_callback("/{([^}\s]+(\|\|[\w|\s]+|<\?php.*\?>)*)}/i", array($this, 'replaceWithFormData'), $msg); if (!$keepPlaceholders) { $msg = preg_replace("/{[^}\s]+}/i", '', $msg); } } } return $returnType === 'array' ? $messages : ArrayHelper::getValue($messages, 0, ''); } /** * Replace {varname} with request data (called from J content plugin) * * @param string &$msg String to parse * * @return void */ public function replaceRequest(&$msg) { static $request; if (!is_array($request)) { $request = array(); $f = InputFilter::getInstance(); foreach ($_REQUEST as $k => $v) { if (is_string($v)) { $request[$k] = $f->clean($v, 'CMD'); } } } foreach ($request as $key => $val) { if (is_string($val)) { // $$$ hugh - escape the key so preg_replace won't puke if key contains / $key = str_replace('/', '\/', $key); $msg = preg_replace("/\{$key\}/", $val, $msg); } } } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with user ($my) data * AND * {$their->var->email} placeholders * * @param string $msg Message to parse * @param object $user Joomla user object * @param string $prefix Search string to look for e.g. 'my' to look for {$my->id} * * @return string parsed message */ public static function replaceWithUserData($msg, $user = null, $prefix = 'my') { $app = Factory::getApplication(); if (is_null($user)) { $user = Factory::getUser(); } if ($user->id == 0) return $msg; $user->levels = $user->getAuthorisedViewLevels(); if (is_object($user)) { foreach ($user as $key => $val) { if (substr($key, 0, 1) != '_' && !empty($val)) { if (!is_object($val) && !is_array($val)) { $msg = str_replace('{$' . $prefix . '->' . $key . '}', $val, $msg); $msg = str_replace('{$' . $prefix . '->' . $key . '}', $val, $msg); } elseif (is_array($val)) { $msg = str_replace('{$' . $prefix . '->' . $key . '}', implode(',', $val), $msg); $msg = str_replace('{$' . $prefix . '->' . $key . '}', implode(',', $val), $msg); } } } } /* * $$$rob parse another users data into the string: * format: is {$their->var->email} where var is the $app->input var to search for * e.g url - index.php?owner=62 with placeholder {$their->owner->id} * var should be an integer corresponding to the user id to load */ $matches = array(); preg_match('/{\$their-\>(.*?)}/', $msg, $matches); foreach ($matches as $match) { $bits = explode('->', str_replace(array('{', '}'), '', $match)); if (count($bits) !== 3) { continue; } $userId = $app->input->getInt(ArrayHelper::getValue($bits, 1)); // things like user elements might be single entry arrays if (is_array($userId)) { $userId = array_pop($userId); } if (!empty($userId)) { $user = Factory::getUser($userId); $val = $user->get(ArrayHelper::getValue($bits, 2)); $msg = str_replace($match, $val, $msg); } } return $msg; } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with global data * * @param string $msg Message to parse * * @return string parsed message */ public static function replaceWithGlobals($msg) { $replacements = self::globalReplacements(); foreach ($replacements as $key => $value) { if (!empty($value)) $msg = str_replace($key, $value, $msg); } return $msg; } /** * Utility function for replacing language tags. * {lang} - Joomla code for user's selected language, like en-GB * {langtag} - as {lang} with with _ instead of - * {shortlang} - first two letters of {lang}, like en * {multilang} - multilang URL code * * @param string $msg Message to parse * * @return string parsed message */ public static function replaceWithLanguageTags($msg) { $replacements = self::langReplacements(); foreach ($replacements as $key => $value) { $msg = str_replace($key, $value, $msg); } return $msg; } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with unsafe data * * @param string $msg Message to parse * * @return string parsed message */ public static function replaceWithUnsafe($msg) { $replacements = self::unsafeReplacements(); foreach ($replacements as $key => $value) { $msg = str_replace($key, $value, $msg); } return $msg; } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with session data * * @param string $msg Message to parse * * @return string parsed message */ public static function replaceWithSession($msg) { if (strstr($msg, '{$session->')) { $session = Factory::getSession(); $sessionData = array( 'id' => $session->getId(), 'token' => $session->get('session.token'), 'formtoken' => Session::getFormToken() ); foreach ($sessionData as $key => $value) { $msg = str_replace('{$session->' . $key . '}', $value, $msg); } $msg = preg_replace_callback( '/{\$session-\>(.*?)}/', function($matches) use ($session) { $bits = explode(':', $matches[1]); if (count($bits) > 1) { $sessionKey = $bits[1]; $nameSpace = $bits[0]; } else { $sessionKey = $bits[0]; $nameSpace = 'default'; } $val = $session->get($sessionKey, '', $nameSpace); if (is_string($val)) { return $val; } else if (is_numeric($val)) { return (string) $val; } return ''; }, $msg ); } return $msg; } /** * Get an associative array of replacements for 'unsafe' value, like $jConfig_secret, which we * only want to use for stricty internal use that won't ever get shown to the user * * @return array * @throws \Exception */ public static function unsafeReplacements() { $config = Factory::getApplication()->getConfig(); $replacements = array( '{$jConfig_absolute_path}' => JPATH_SITE, '{$jConfig_secret}' => $config->get('secret') ); return $replacements; } /** * Get an associative array of replacements strings and values * * @return array * @throws \Exception */ public static function globalReplacements() { $app = Factory::getApplication(); $itemId = self::itemId(); $config = Factory::getApplication()->getConfig(); $session = Factory::getSession(); $token = $session->get('session.token'); $replacements = array( '{$jConfig_live_site}' => COM_FABRIK_LIVESITE, '{$jConfig_offset}' => $config->get('offset'), '{$Itemid}' => $itemId, '{$jConfig_sitename}' => $config->get('sitename'), '{$jConfig_mailfrom}' => $config->get('mailfrom'), '{where_i_came_from}' => $app->input->server->get('HTTP_REFERER', '', 'string'), '{date}' => date('Ymd'), '{year}' => date('Y'), '{mysql_date}' => date('Y-m-d H:i:s'), '{session.token}' => $token ); foreach ($_SERVER as $key => $val) { if (!is_object($val) && !is_array($val)) { $replacements['{$_SERVER->' . $key . '}'] = $val; $replacements['{$_SERVER->' . $key . '}'] = $val; } } if ($app->isClient('administrator')) { $replacements['{formview}'] = 'task=form.view'; $replacements['{listview}'] = 'task=list.view'; $replacements['{detailsview}'] = 'task=details.view'; } else { $replacements['{formview}'] = 'view=form'; $replacements['{listview}'] = 'view=list'; $replacements['{detailsview}'] = 'view=details'; } return array_merge($replacements, self::langReplacements()); } /** * Returns array of language tag replacements * * @return array */ public static function langReplacements() { $langtag = Factory::getApplication()->getLanguage()->getTag(); $lang = str_replace('-', '_', $langtag); $shortlang = explode('_', $lang); $shortlang = $shortlang[0]; $multilang = Worker::getMultiLangURLCode(); $replacements = array( '{lang}' => $lang, '{langtag}' => $langtag, '{multilang}' => $multilang, '{shortlang}' => $shortlang, ); return $replacements; } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with posted data * * @param string $matches Placeholder e.g. {placeholder} * * @return string posted data that corresponds with placeholder */ protected function replaceWithFormData($matches) { // Merge any join data key val pairs down into the main data array $joins = ArrayHelper::getValue($this->_searchData, 'join', array()); foreach ($joins as $k => $data) { foreach ($data as $k => $v) { /* * Only replace if we haven't explicitly set the key in _searchData. * Otherwise, calc element in repeat group uses all repeating groups values rather than the * current one that the plugin sets when it fire its Ajax request. */ if (!array_key_exists($k, $this->_searchData)) { $this->_searchData[$k] = $v; } } } $match = $matches[0]; $orig = $match; // Strip the {} $match = StringHelper::substr($match, 1, StringHelper::strlen($match) - 2); /* $$$ hugh - added dbprefix substitution * Not 100% if we should do this on $match before copying to $orig, but for now doing it * after, so we don't potentially disclose dbprefix if no substitution found. */ $config = Factory::getApplication()->getConfig(); $prefix = $config->get('dbprefix'); $match = str_replace('#__', $prefix, $match); // $$$ rob test this format searchvalue||defaultsearchvalue $bits = explode('||', $match); if (count($bits) == 2) { $match = self::parseMessageForPlaceHolder('{' . $bits[0] . '}', $this->_searchData, false); if (in_array($match, array('', '<ul></ul>', '<ul><li></li></ul>'))) { // experiment with eval'ed code in defaults if (strstr($bits[1], '<?php')) { $code = preg_replace('/^<\?php(.*)(\?>)$/s', '$1', $bits[1]); FabrikWorker::clearEval(); $bits[1] = Php::Eval(['code' => $code]); FabrikWorker::logEval($default, 'Caught exception on eval of ' . $formModel->label . ': %s'); } return $bits[1] !== '' ? $bits[1] : $orig; } else { return $match !== '' ? $match : $orig; } } $match = preg_replace("/ /", "_", $match); if (!strstr($match, '.')) { // For some reason array_key_exists wasn't working for nested arrays?? $aKeys = array_keys($this->_searchData); // Remove the table prefix from the post key $aPrefixFields = array(); for ($i = 0; $i < count($aKeys); $i++) { $aKeyParts = explode('___', $aKeys[$i]); if (count($aKeyParts) == 2) { $tablePrefix = array_shift($aKeyParts); $field = array_pop($aKeyParts); $aPrefixFields[$field] = $tablePrefix; } } if (array_key_exists($match, $aPrefixFields)) { $match = $aPrefixFields[$match] . '___' . $match; } // Test to see if the made match is in the post key arrays $found = in_array($match, $aKeys, true); if ($found) { // Get the post data $match = $this->_searchData[$match]; if (is_array($match)) { $newMatch = ''; // Deal with radio boxes etc. inside repeat groups foreach ($match as $m) { if (is_array($m)) { $newMatch .= ',' . implode(',', $m); } else { $newMatch .= ',' . $m; } } $match = StringHelper::ltrim($newMatch, ','); } } else { $match = ''; } } else { // Could be looking for URL field type e.g. for $_POST[url][link] the match text will be url.link $aMatch = explode('.', $match); $aPost = $this->_searchData; foreach ($aMatch as $sPossibleArrayKey) { if (is_array($aPost)) { if (!isset($aPost[$sPossibleArrayKey])) { return $orig; } else { $aPost = $aPost[$sPossibleArrayKey]; } } } $match = $aPost; $found = true; } if (!empty($match) && $this->parseAddSlashes) { $match = htmlspecialchars($match, ENT_QUOTES, 'UTF-8'); } return $found ? $match : $orig; } /** * Internal function to recursive scan directories * * @param string $imagePath Image path * @param string $folderPath Path to scan * @param string &$folders Root path of this folder * @param array &$images Value array of all existing folders * @param array $aFolderFilter Value array of all existing images * @param bool $makeOptions Make options out for the results * * @return void */ public static function readImages($imagePath, $folderPath, &$folders, &$images, $aFolderFilter, $makeOptions = true) { $imgFiles = self::fabrikReadDirectory($imagePath, '.', false, false, $aFolderFilter); foreach ($imgFiles as $file) { $ff_ = $folderPath . $file . '/'; $ff = $folderPath . $file; $i_f = $imagePath . '/' . $file; if (is_dir($i_f) && $file != 'CVS' && $file != '.svn') { if (!in_array($file, $aFolderFilter)) { $folders[] = HTMLHelper::_('select.option', $ff_); self::readImages($i_f, $ff_, $folders, $images, $aFolderFilter); } } elseif (preg_match('/bmp|gif|jpg|png/i', $file) && is_file($i_f)) { // Leading / we don't need $imageFile = StringHelper::substr($ff, 1); $images[$folderPath][] = $makeOptions ? HTMLHelper::_('select.option', $imageFile, $file) : $file; } } } /** * Utility function to read the files in a directory * * @param string $path The file system path * @param string $filter A filter for the names * @param bool $recurse Recurse search into sub-directories * @param bool $fullPath True if to prepend the full path to the file name * @param array $aFolderFilter Folder names not to recurse into * @param bool $foldersOnly Return a list of folders only (true) * * @return array of file/folder names */ public static function fabrikReadDirectory($path, $filter = '.', $recurse = false, $fullPath = false, $aFolderFilter = array(), $foldersOnly = false) { $arr = array(); if (!@is_dir($path)) { return $arr; } $handle = opendir($path); while ($file = readdir($handle)) { $dir = Path::clean($path . '/' . $file); $isDir = is_dir($dir); if ($file != "." && $file != "..") { if (preg_match("/$filter/", $file)) { if (($isDir && $foldersOnly) || !$foldersOnly) { if ($fullPath) { $arr[] = trim(Path::clean($path . '/' . $file)); } else { $arr[] = trim($file); } } } $goDown = true; if ($recurse && $isDir) { foreach ($aFolderFilter as $sFolderFilter) { if (strstr($dir, $sFolderFilter)) { $goDown = false; } } if ($goDown) { $arr2 = self::fabrikReadDirectory($dir, $filter, $recurse, $fullPath, $aFolderFilter, $foldersOnly); $arrDiff = array_diff($arr, $arr2); $arr = array_merge($arrDiff); } } } } closedir($handle); asort($arr); return $arr; } /** * Joomfish translations don't seem to work when you do an ajax call * it seems to load the geographical location language rather than the selected lang * so for ajax calls that need to use jf translated text we need to get the current lang and * send it to the js code which will then append the lang=XX to the ajax querystring * * Renamed to getShortLang as we don't support Joomfish any more * * @since 2.0.5 * * @return string first two letters of lang code - e.g. nl from 'nl-NL' */ public static function getShortLang() { $lang = Factory::getApplication()->getLanguage(); $lang = explode('-', $lang->getTag()); return array_shift($lang); } /** * If J! multiple languages is enabled, return the URL language code for the currently selected language, which is * set by the admin in the 'content languages'. If not multi lang, return false; * * @return boolean || string */ public static function getMultiLangURLCode() { $multiLang = false; if (Multilanguage::isEnabled()) { $lang = Factory::getApplication()->getLanguage()->getTag(); $languages = LanguageHelper::getLanguages(); foreach ($languages as $language) { if ($language->lang_code === $lang) { $multiLang = $language->sef; break; } } } return $multiLang; } /** * Get the content filter used both in form and admin pages for content filter * takes values from J content filtering options * * @return array (bool should the filter be used, object the filter to use) */ public static function getContentFilter() { $filter = false; // Filter settings jimport('joomla.application.component.helper'); // Get Config and Filters in Joomla 2.5 $config = ComponentHelper::getParams('com_config'); $filters = $config->get('filters'); // If no filter data found, get from com_content (Joomla 1.6/1.7 sites) if (empty($filters)) { $contentParams = ComponentHelper::getParams('com_content'); $filters = $contentParams->get('filters'); } $user = Factory::getUser(); $userGroups = Access::getGroupsByUser($user->get('id')); $blackListTags = array(); $blackListAttributes = array(); $whiteListTags = array(); $whiteListAttributes = array(); $whiteList = false; $blackList = false; $unfiltered = false; // Cycle through each of the user groups the user is in. // Remember they are include in the Public group as well. foreach ($userGroups AS $groupId) { // May have added a group by not saved the filters. if (!isset($filters->$groupId)) { continue; } // Each group the user is in could have different filtering properties. $filterData = $filters->$groupId; $filterType = StringHelper::strtoupper($filterData->filter_type); if ($filterType == 'NH') { // Maximum HTML filtering. } elseif ($filterType == 'NONE') { // No HTML filtering. $unfiltered = true; } else { // Black or white list. // Pre-process the tags and attributes. $tags = explode(',', $filterData->filter_tags); $attributes = explode(',', $filterData->filter_attributes); $tempTags = array(); $tempAttributes = array(); foreach ($tags as $tag) { $tag = trim($tag); if ($tag) { $tempTags[] = $tag; } } foreach ($attributes as $attribute) { $attribute = trim($attribute); if ($attribute) { $tempAttributes[] = $attribute; } } // Collect the black or white list tags and attributes. // Each list is cumulative. if ($filterType == 'BL' || $filterType == 'CBL') { $blackList = true; $blackListTags = array_merge($blackListTags, $tempTags); $blackListAttributes = array_merge($blackListAttributes, $tempAttributes); } elseif ($filterType == 'WL') { $whiteList = true; $whiteListTags = array_merge($whiteListTags, $tempTags); $whiteListAttributes = array_merge($whiteListAttributes, $tempAttributes); } } } // Remove duplicates before processing (because the black list uses both sets of arrays). $blackListTags = array_unique($blackListTags); $blackListAttributes = array_unique($blackListAttributes); $whiteListTags = array_unique($whiteListTags); $whiteListAttributes = array_unique($whiteListAttributes); // Unfiltered assumes first priority. if ($unfiltered) { $doFilter = false; // Don't apply filtering. } else { $doFilter = true; // Black lists take second precedence. if ($blackList) { // Remove the white-listed attributes from the black-list. $tags = array_diff($blackListTags, $whiteListTags); $filter = InputFilter::getInstance($tags, array_diff($blackListAttributes, $whiteListAttributes), 1, 1); } // White lists take third precedence. elseif ($whiteList) { // Turn off xss auto clean $filter = InputFilter::getInstance($whiteListTags, $whiteListAttributes, 0, 0, 0); } // No HTML takes last place. else { $filter = InputFilter::getInstance(); } } return array($doFilter, $filter); } /** * Clear PHP errors prior to running eval'd code * * @return void */ public static function clearEval() { /** * "Clear" PHP's errors. NOTE that error_get_last() will still return non-null after this * if there were any errors, but $error['message'] will be empty. See comment in logEval() * below for details. */ if (version_compare(PHP_VERSION, '7.0.0') >= 0) { error_clear_last(); } else { @trigger_error(""); } } /** * Raise a J Error notice if the eval'd result is false and there is a error * * @param mixed $val Evaluated result * @param string $msg Error message, should contain %s as we sprintf in the error_get_last()'s message property * * @return void */ public static function logEval($val, $msg) { /* if ($val !== false) { return; } */ $error = error_get_last(); /** * $$$ hugh - added check for 'message' being empty, so we can do .. * @trigger_error(''); * ... prior to eval'ing code if we want to "clear" anything pitched prior * to the eval. For instance, in the PHP validation plugin. If we don't "clear" * the errors before running the eval'd validation code, we end up reporting any * warnings or notices pitched in our code prior to the validation running, which * can be REALLY confusing. After a trigger_error(), error_get_last() won't return null, * but 'message' will be empty. */ if (is_null($error) || empty($error['message'])) { // No error set (eval could have actually returned false as a correct value) return; } $enqMsgType = 'error'; $indentHTML = '<br/> Debug: '; $errString = Text::_('COM_FABRIK_EVAL_ERROR_USER_WARNING'); // Give a technical error message to the developer if (version_compare(phpversion(), '5.2.0', '>=') && $error && is_array($error)) { $errString .= $indentHTML . sprintf($msg, $error['message']); } else { $errString .= $indentHTML . sprintf($msg, "unknown error - php version < 5.2.0"); } self::logError($errString, $enqMsgType); } /** * Raise a J Error notice if in dev mode or log a J error otherwise * * @param string $errString Message to display / log * @param string $msgType Joomla enqueueMessage message type e.g. 'error', 'warning' etc. * * @return void */ public static function logError($errString, $msgType) { if (Html::isDebug()) { $app = Factory::getApplication(); $app->enqueueMessage($errString, $msgType); } else { switch ($msgType) { case 'message': $priority = Log::INFO; break; case 'warning': $priority = Log::WARNING; break; case 'error': default: $priority = Log::ERROR; break; } Log::add($errString, $priority, 'com_fabrik'); } } /** * Log to table jos_fabrik_logs * * @param string $type E.g. 'fabrik.fileupload.download' * @param mixed $msg Array/object/string * @param bool $jsonEncode Should we json encode the message? * * @return void */ public static function log($type, $msg, $jsonEncode = true) { if ($jsonEncode) { $msg = json_encode($msg); } $log = FabTable::getInstance('log', 'FabrikTable'); $log->message_type = $type; $log->message = $msg; $log->store(); } /** * Get a database object * * Returns the global {@link JDatabase} object, only creating it * if it doesn't already exist. * * @param bool $loadJoomlaDb Force (if true) the loading of the main J database, * needed in admin to connect to J db whilst still using fab db drivers "fabrik" * replacement text * * @param mixed $cnnId If null then loads the fabrik default connection, if an int then loads the * specified connection by its id * * @return JDatabaseDriver object */ public static function getDbo($loadJoomlaDb = false, $cnnId = null) { $sig = (int) $loadJoomlaDb . '.' . $cnnId; if (!self::$database) { self::$database = array(); self::$database = array(); } if (!array_key_exists($sig, self::$database)) { Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_fabrik/tables'); $conf = Factory::getApplication()->getConfig(); if (!$loadJoomlaDb) { $cnModel = Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Connection', 'FabrikFEModel'); $cn = $cnModel->getConnection($cnnId); $host = $cn->host; $user = $cn->user; $password = $cn->password; $database = $cn->database; } else { $host = $conf->get('host'); $user = $conf->get('user'); $password = $conf->get('password'); $database = $conf->get('db'); } $dbPrefix = $conf->get('dbprefix'); $driver = $conf->get('dbtype'); // Test for swapping db table names $options = array('driver' => $driver, 'host' => $host, 'user' => $user, 'password' => $password, 'database' => $database, 'prefix' => $dbPrefix); self::$database[$sig] = DatabaseDriver::getInstance($options); Worker::bigSelects(self::$database[$sig]); } return self::$database[$sig]; } /** * $$$ hugh - testing doing bigSelects stuff here * Reason being, some folk on shared hosting plans with very restrictive MySQL * setups are hitting the 'big selects' problem on Fabrik internal queries, not * just on their List specific queries. So we need to apply 'big selects' to our * default connection as well, essentially enabling it for ALL queries we do. * * @param JDatabaseDriver $fabrikDb * * @return void */ public static function bigSelects($fabrikDb) { $fbConfig = ComponentHelper::getParams('com_fabrik'); if ($fbConfig->get('enable_big_selects', 0) == '1') { /** * Use of OPTION in SET deprecated from MySQL 5.1. onward * http://www.fabrikar.com/forums/index.php?threads/enable-big-selects-error.39463/#post-198293 * NOTE - technically, using verison_compare on MySQL version could fail, if it's a "gamma" * release, which PHP desn't grok! */ if (version_compare($fabrikDb->getVersion(), '5.1.0', '>=')) { $fabrikDb->setQuery("SET SQL_BIG_SELECTS=1, GROUP_CONCAT_MAX_LEN=10240"); } else { $fabrikDb->setQuery("SET OPTION SQL_BIG_SELECTS=1, GROUP_CONCAT_MAX_LEN=10240"); } try { $fabrikDb->execute(); } catch (\Exception $e) { // Fail silently } } } /** * Helper function get get a connection * * @param mixed $item A list table or connection id * * @since 3.0b * * @return FabrikFEModelConnection connection */ public static function getConnection($item = null) { $app = Factory::getApplication(); $input = $app->input; $jForm = $input->get('jform', array(), 'array'); if (is_object($item)) { $item = is_null($item->connection_id) ? ArrayHelper::getValue($jForm, 'connection_id', -1) : $item->connection_id; } $connId = (int) $item; if (!self::$connection) { self::$connection = array(); } if (!array_key_exists($connId, self::$connection)) { $connectionModel = Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Connection', 'FabrikFEModel'); $connectionModel->setId($connId); if ($connId === -1) { // -1 for creating new table $connectionModel->loadDefaultConnection(); $connectionModel->setId($connectionModel->getConnection()->id); } $connectionModel->getConnection(); self::$connection[$connId] = $connectionModel; } return self::$connection[$connId]; } /** * Get the plugin manager * * @since 3.0b * * @return FabrikFEModelPluginmanager Plugin manager */ public static function getPluginManager() { if (!self::$pluginManager) { self::$pluginManager = Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Pluginmanager', 'FabrikFEModel'); } return self::$pluginManager; } /** * Takes a string which may or may not be json and returns either string/array/object * will also turn valGROUPSPLITTERval2 to array * * @param string $data Json encoded string * @param bool $toArray Force data to be an array * @param bool $emptyish Set to false to return an empty array if $data is an empty string, instead of an * emptyish (one empty string entry) array * * @return mixed data */ public static function JSONtoData($data, $toArray = false, $emptyish = true) { if (is_string($data)) { if (!strstr($data, '{')) { // Was messing up date rendering @ http://www.podion.eu/dev2/index.php/2011-12-19-10-33-59/actueel // return $toArray ? (array) $data : $data; } // Repeat elements are concatenated with the GROUPSPLITTER - convert to json string before continuing. if (strstr($data, GROUPSPLITTER)) { $data = json_encode(explode(GROUPSPLITTER, $data)); } /* half hearted attempt to see if string is actually json or not. * issue was that if you try to decode '000123' its turned into '123' */ if (strstr($data, '{') || strstr($data, '[')) { $json = json_decode($data); // Only works in PHP5.3 // $data = (json_last_error() == JSON_ERROR_NONE) ? $json : $data; if (is_null($json)) { /* * if coming back from a failed validation - the json string may have been htmlspecialchars_encoded in * the form model getGroupView method */ $json = json_decode(stripslashes(htmlspecialchars_decode($data, ENT_QUOTES))); } $data = is_null($json) ? $data : $json; } // If $data was an empty string and "emptyish" is not set, we want an empty array, not an array with one empty string if ($toArray && !$emptyish && $data === '') { $data = array(); } } $data = $toArray ? (array) $data : $data; return $data; } /** * Test if a string is a compatible date * * @param string $d Date to test * @param bool $notNull don't allow null / empty dates * * @return bool */ public static function isNullDate($d) { if (is_string($d)) $d = trim($d); $db = self::getDbo(true); $aNullDates = array('0000-00-000000-00-00', '0000-00-00 00:00:00', '0000-00-00', '', 0, $db->getNullDate(), null); if ($d < '1000-01-01') return true; return in_array($d, $aNullDates); } /** * Test if a string is a compatible date * * @param string $d Date to test * @param bool $notNull don't allow null / empty dates * * @return bool */ public static function isDate($d, $notNull = true) { // Catch for ',' if (empty($d) || strlen($d) < 2) { return false; } if ($notNull && self::isNullDate($d)) { return false; } $cerl = error_reporting (); error_reporting (0); try { $dt = new Date($d); } catch (\Exception $e) { error_reporting ($cerl); self::clearEval(); return false; } error_reporting ($cerl); return true; } public static function addMonthsInterval($months, Date $date) { $next = new Date($date->format('d-m-Y H:i:s')); $next->modify('last day of +' . $months . ' month'); if ($date->format('d') > $next->format('d')) { return $date->diff($next); } else { return new DateInterval('P' . $months . 'M'); } } public static function addMonths($months, Date $date) { return $date->add(self::addMonthsInterval($months, $date)); } /** * Get a user's TZ offset in MySql format, suitable for CONVERT_TZ * * @param int userId userid or null (use logged on user if null) * * @return string symbolic timezone name (America/Chicago) */ public static function getUserTzOffsetMySql($userId = null) { $tz = self::getUserTzName($userId); $tz = new \DateTimeZone($tz); $date = new Date("now", $tz); $offset = $tz->getOffset($date) . ' seconds'; $dateOffset = clone $date; $dateOffset->sub(\DateInterval::createFromDateString($offset)); $interval = $dateOffset->diff($date); return $interval->format('%R%H:%I'); } /** * Get a user's TZ offset in seconds * * @param int userId userid or null (use logged on user if null) * * @return int seconds offset */ public static function getUserTzOffset($userId = null) { $tz = self::getUserTzName($userId); $tz = new \DateTimeZone($tz); $date = new Date("now", $tz); return $tz->getOffset($date); } /** * Get a user's TZ name * * @param int userId userid or null (use logged on user if null) * * @return string symbolic timezone name (America/Chicago) */ public static function getUserTzName($userId = null) { if (empty($userId)) { $user = Factory::getUser(); } else { $user = Factory::getUser($userId); } $config = Factory::getApplication()->getConfig(); $tz = $user->getParam('timezone', $config->get('offset')); return $tz; } /** * See if data is JSON or not. * * @param mixed $data Date to test * @params bool $quotedString should we treat a single quoted string as JSON * @since 3.0.6 * * @return bool */ public static function isJSON($data, $quotedString = true) { if (!is_string($data)) { return false; } if (is_numeric($data)) { return false; } if (!$quotedString) { $data = trim($data, '"'); } return json_decode($data) !== null; } /** * Is the email really an email (more strict than MailHelper::isEmailAddress()) * * @param string $email Email address * @param bool $sms test for SMS phone number instead of email, default false * * @since 3.0.4 * * @return bool */ public static function isEmail($email, $sms = false) { if ($sms) { return self::isSMS($email); } $conf = Factory::getApplication()->getConfig(); $mailer = $conf->get('mailer'); if ($mailer === 'mail') { // Sendmail and Joomla isEmailAddress don't use the same conditions return (MailHelper::isEmailAddress($email) && Mail::ValidateAddress($email)); } return MailHelper::isEmailAddress($email); } /** * Is valid SMS number format * This is just a stub which return true for now! * * @param string $sms SMS number * * @since 3.4.0 * * @return bool */ public static function isSMS($sms) { return true; } /** * Function to send an email * * @param string $from From email address * @param string $fromName From name * @param mixed $recipient Recipient email address(es) * @param string $subject email subject * @param string $body Message body * @param boolean $mode false = plain text, true = HTML * @param mixed $cc CC email address(es) * @param mixed $bcc BCC email address(es) * @param mixed $attachment Attachment file name(s) * @param mixed $replyTo Reply to email address(es) * @param mixed $replyToName Reply to name(s) * @param array $headers Optional custom headers, assoc array keyed by header name * * @return boolean True on success * * @since 11.1 */ public static function sendMail($from, $fromName, $recipient, $subject, $body, $mode = false, $cc = null, $bcc = null, $attachment = null, $replyTo = null, $replyToName = null, $headers = array()) { // do a couple of tweaks to improve spam scores // Get a Mail instance $mailer = Factory::getMailer(); // If html, make sure there's an <html> tag if ($mode) { if (!stristr($body, '<html>')) { $body = '<html>' . $body . '</html>'; } } /** * if simple single email recipient with no name part, fake out name part to avoid TO_NO_BKRT hit in spam filters * (don't do it for sendmail, as sendmail only groks simple emails in To header!) */ $recipientName = ''; if ($mailer->Mailer !== 'sendmail' && is_string($recipient) && !strstr($recipient, '<')) { $recipientName = $recipient; } $mailer->setSubject($subject); $mailer->setBody($body); $mailer->Encoding = 'base64'; // Are we sending the email as HTML? $mailer->isHtml($mode); try { $mailer->addRecipient($recipient, $recipientName); } catch (\Exception $e) { self::log('fabrik.helper.sendmail.error', 'Exception in addRecipient: ' . $e->getMessage(), false); return false; } try { $mailer->addCc($cc); } catch (\Exception $e) { self::log('fabrik.helper.sendmail.warning', 'Exception in addCc: ' . $e->getMessage()); // not sure if we should bail if Cc is bad, for now just soldier on } try { $mailer->addBcc($bcc); } catch (\Exception $e) { self::log('fabrik.helper.sendmail.warning', 'Exception in addBcc: ' . $e->getMessage(), false); // not sure if we should bail if Bcc is bad, for now just soldier on } if (!empty($attachment)) { try { $mailer->addAttachment($attachment); } catch (\Exception $e) { self::log('fabrik.helper.sendmail.warning', 'Exception in addAttachment: ' . $e->getMessage(), false); // most likely file didn't exist, ignore } } $autoReplyTo = false; // Take care of reply email addresses if (is_array($replyTo)) { $numReplyTo = count($replyTo); for ($i = 0; $i < $numReplyTo; $i++) { try { $mailer->addReplyTo($replyTo[$i], $replyToName[$i]); } catch (\Exception $e) { self::log('fabrik.helper.sendmail.warning', 'Exception in addReplyTo: ' . $e->getMessage(), false); // carry on } } } elseif (isset($replyTo)) { try { $mailer->addReplyTo($replyTo, $replyToName); } catch (\Exception $e) { self::log('fabrik.helper.sendmail.warning', 'Exception in addReplyTo: ' . $e->getMessage(), false); // carry on } } else { $autoReplyTo = true; } try { $mailer->setSender(array($from, $fromName, $autoReplyTo)); } catch (\Exception $e) { self::log('fabrik.helper.sendmail.error', 'Exception in setSender: ' . $e->getMessage(), false); return false; } /** * Set the plain text AltBody, which forces the PHP mailer class to make this * a multipart MIME type, with an alt body for plain text. If we don't do this, * the default behavior is to send it as just text/html, which causes spam filters * to downgrade it. * @@@trob: insert \n before <br to keep newlines(strip_tag may then strip <br> or <br /> etc, decode html */ if ($mode) { $body = str_ireplace(array("<br />","<br>","<br/>"), "\n<br />", $body); $body = html_entity_decode($body); $mailer->AltBody = MailHelper::cleanText(strip_tags($body)); } foreach ($headers as $headerName => $headerValue) { $mailer->addCustomHeader($headerName, $headerValue); } $config = ComponentHelper::getParams('com_fabrik'); if ($config->get('verify_peer', '1') === '0') { $mailer->SMTPOptions = array( 'ssl' => array( 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true ) ); } try { $ret = $mailer->Send(); } catch (\Exception $e) { self::log('fabrik.helper.sendmail.error', 'Exception in Send: ' . $e->getMessage(), false); return false; } // if ACY mailing is installed, it returns an exception (!) rather than false if (is_bool($ret) === false && get_parent_class($ret) === 'Exception') { self::log('fabrik.helper.sendmail.error', 'Exception in Send: ' . $ret->getMessage(), false); $ret = false; } return $ret; } /** * Get a JS go back action e.g 'onclick="history.back()" * * @return string */ public static function goBackAction() { jimport('joomla.environment.browser'); $uri = Uri::getInstance(); $url = filter_var(ArrayHelper::getValue($_SERVER, 'HTTP_REFERER'), FILTER_SANITIZE_URL); if ($uri->getScheme() === 'https') { $goBackAction = 'onclick="parent.location=\'' . $url . '\'"'; } else { $goBackAction = 'onclick="parent.location=\'' . $url . '\'"'; } return $goBackAction; } /** * Attempt to find the active menu item id - Only for front end * * - First checked $listId for menu items * - Then checks if itemId in $input * - Finally checked active menu item * * @param int $listId List id to attempt to get the menu item id for the list. * * @return mixed NULL if nothing found, int if menu item found */ public static function itemId($listId = null) { static $listIds = array(); $app = Factory::getApplication(); if (!$app->isClient('administrator')) { // Attempt to get Itemid from possible list menu item. if (!is_null($listId)) { if (!array_key_exists($listId, $listIds)) { $db = Factory::getContainer()->get('DatabaseDriver'); $myLanguage = Factory::getApplication()->getLanguage(); $myTag = $myLanguage->getTag(); $qLanguage = !empty($myTag) ? ' AND ' . $db->q($myTag) . ' = ' . $db->qn('m.language') : ''; $query = $db->getQuery(true); $query->select('m.id AS itemId')->from('#__extensions AS e') ->leftJoin('#__menu AS m ON m.component_id = e.extension_id') ->where('e.name = "com_fabrik" and e.type = "component" and m.link LIKE "%listid=' . $listId . '"' . $qLanguage); $db->setQuery($query); if ($itemId = $db->loadResult()) { $listIds[$listId] = $itemId; } else{ $listIds[$listId] = false; } } else{ if ($listIds[$listId] !== false) { return $listIds[$listId]; } } } $itemId = (int) $app->input->getInt('itemId'); if ($itemId !== 0) { return $itemId; } $menus = $app->getMenu(); $menu = $menus->getActive(); if (is_object($menu)) { return $menu->id; } } return null; } /** * Attempt to get a variable first from the menu params (if they exists) if not from request * * @param string $name Param name * @param mixed $val Default * @param bool $mambot If set to true menu params ignored * @param string $priority Defaults that menu priorities override request - set to 'request' * to inverse this priority * @param array $opts Options 'listid' -> if priority = menu then the menu list id must * match this value to use the menu param. * * @return string */ public static function getMenuOrRequestVar($name, $val = '', $mambot = false, $priority = 'menu', $opts = array()) { $app = Factory::getApplication(); $input = $app->input; if ($priority === 'menu') { $val = $input->get($name, $val, 'string'); if (!$app->isClient('administrator')) { if (!$mambot) { $menus = $app->getMenu(); $menu = $menus->getActive(); if (is_object($menu)) { $match = true; if (array_key_exists('listid', $opts)) { $menuListId = ArrayHelper::getValue($menu->query, 'listid', ''); $checkListId = ArrayHelper::getValue($opts, 'listid', $menuListId); $match = (int) $menuListId === (int) $checkListId; } else if (array_key_exists('formid', $opts)) { $menuFormId = ArrayHelper::getValue($menu->query, 'formid', ''); $checkFormId = ArrayHelper::getValue($opts, 'formid', $menuFormId); $match = (int) $menuFormId === (int) $checkFormId; } if ($match) { $val = $menu->getParams()->get($name, $val); } } } } } else { if (!$app->isClient('administrator')) { $menus = $app->getMenu(); $menu = $menus->getActive(); // If there is a menu item available AND the view is not rendered in a content plugin if (is_object($menu) && !$mambot) { $match = true; if (array_key_exists('listid', $opts)) { $menuListId = ArrayHelper::getValue($menu->query, 'listid', ''); $checkListId = ArrayHelper::getValue($opts, 'listid', $menuListId); $match = (int) $menuListId === (int) $checkListId; } else if (array_key_exists('formid', $opts)) { $menuFormId = ArrayHelper::getValue($menu->query, 'formid', ''); $checkFormId = ArrayHelper::getValue($opts, 'formid', $menuFormId); $match = (int) $menuFormId === (int) $checkFormId; } if ($match) { $val = $menu->getParams()->get($name, $val); } } } $val = $input->get($name, $val, 'string'); } return $val; } /** * Access control function for determining if the user can perform * a designated function on a specific row * * @param object $params Item params to test * @param object $row Data * @param string $col Access control setting to compare against * * @return mixed - if ACL setting defined here return bool, otherwise return -1 to continue with default acl * setting */ public static function canUserDo($params, $row, $col) { if (!is_null($row)) { $app = Factory::getApplication(); $input = $app->input; $user = Factory::getUser(); $userCol = $params->get($col, ''); if ($userCol != '') { $userCol = StringHelper::safeColNameToArrayKey($userCol); if ((is_array($row) && !array_key_exists($userCol, $row)) || (is_object($row) && !isset($row->{$userCol}))) { return false; } else { $userColRaw = $userCol . '_raw'; if ((is_array($row) && array_key_exists($userColRaw, $row)) || (is_object($row) && property_exists($row,$userColRaw))) { $userCol .= '_raw'; } $myId = $user->get('id'); // -1 for menu items that link to their own records $userColVal = is_array($row) ? $row[$userCol] : $row->$userCol; // User element stores as object if (is_object($userColVal)) { $userColVal = ArrayHelper::fromObject($userColVal); } // Could be coming back from a failed validation in which case val might be an array if (is_array($userColVal)) { $userColVal = array_shift($userColVal); } if (empty($userColVal) && empty($myId)) { return false; } if (intVal($userColVal) === intVal($myId) || $input->get('rowid') == -1) { return true; } } } } return -1; } /** * Can Fabrik render PDF - required the DOMPDF library to be installed in Joomla libraries folder * * @param bool $puke throw an exception if can't * * @throws RuntimeException * * @return bool */ public static function canPdf($puke = true) { $config = ComponentHelper::getParams('com_fabrik'); $pdfLibrary = $config->get('fabrik_pdf_lib', 'dompdf'); if ($pdfLibrary === 'dompdf') { $file = COM_FABRIK_LIBRARY . '/vendor/vendor/dompdf/dompdf/composer.json'; } else { $file = COM_FABRIK_LIBRARY . '/vendor/vendor/mpdf/mpdf/composer.json'; } if (!File::exists($file)) { if ($puke) { throw new \RuntimeException(Text::_($pdfLibrary === 'dompdf' ? 'COM_FABRIK_NOTICE_DOMPDF_NOT_FOUND' : 'COM_FABRIK_NOTICE_MPDF_NOT_FOUND')); } else { return false; } } return true; } /** * Get a cache handler * $$$ hugh - added $listModel arg, needed so we can see if they have set "Disable Caching" on the List * * @param object $listModel List Model * @param string $group group name (will default to package) * @param int $ttl time to live in minutes, defaults to J! config * * @since 3.0.7 * * @return Cache */ public static function getCache($listModel = null, $group = null, $ttl = null) { $config = Factory::getApplication()->getConfig(); $app = Factory::getApplication(); $package = isset($group) ? $group : $app->getUserState('com_fabrik.package', 'fabrik'); $time = isset($ttl) ? (int) $ttl : (int) $config->get('cachetime'); $base = JPATH_BASE . '/cache/'; $opts = array('defaultgroup' => 'com_' . $package, 'cachebase' => $base, 'lifetime' => $time, 'language' => 'en-GB', 'storage' => 'file'); $cache = Cache::getInstance('callback', $opts); $doCache = $config->get('caching', 0) > 0 ? true : false; if ($doCache && $listModel !== null) { $doCache = $listModel->getParams()->get('list_disable_caching', '0') == '0'; } $cache->setCaching($doCache); return $cache; } /** * Is caching enabled * * @param object $listModel List Model * @param bool $noGuest disable caching for guests * * @since 3.8 * * @return Cache */ public static function useCache($listModel = null, $noGuest = true, $excludedFormats = null) { $config = Factory::getApplication()->getConfig(); $app = Factory::getApplication(); if (!isset($excludedFormats)) { $excludedFormats = array('raw', 'csv', 'pdf', 'json', 'fabrikfeed', 'feed'); } // check global J! system cache setting $doCache = $config->get('caching', 0) > 0 ? true : false; // if enabled, see if any other settingg disables it if ($doCache) { // Check the Fabrik global option $fabrikConfig = ComponentHelper::getParams('com_fabrik'); if ($fabrikConfig->get('disable_caching', '0') === '1') { return false; } // If a list model has been specified, see if caching is disabled for this list if ($listModel !== null) { if ($listModel->getParams()->get('list_disable_caching', '0') === '1') { return false; } } // Check if caching is disabled for guests if ($noGuest) { if (Factory::getUser()->get('id') === '0') { return false; } } if (in_array($app->input->get('format'), $excludedFormats)) { return false; } } return $doCache; } /** * Get the default values for a given Form * * @param string $form Form name e.g. list, form etc. * * @since 3.0.7 * * @return array key field name, value default value */ public static function formDefaults($form) { Form::addFormPath(JPATH_COMPONENT . '/models/forms'); Form::addFieldPath(JPATH_COMPONENT . '/models/fields'); $form = Form::getInstance('com_fabrik.' . $form, $form, array('control' => '', 'load_data' => true)); $fs = $form->getFieldset(); $json = array('params' => array()); foreach ($fs as $name => $field) { if (substr($name, 0, 7) === 'params_') { $name = str_replace('params_', '', $name); $json['params'][$name] = $field->value; } else { $json[$name] = $field->value; } } return $json; } /** * Are we in J3 or using a bootstrap tmpl * * @since 3.1 * * @return bool */ /* public static function j3() { // do we need this function any more? return true; } */ /** * Are we in a form process task * * @since 3.2 * * @return bool */ public static function inFormProcess() { $app = Factory::getApplication(); return $app->input->get('task') == 'form.process' || ($app->isClient('administrator') && $app->input->get('task') == 'process'); } /** * Are we in an AJAX validation process task * * @since 3.9.2 * * @return bool */ public static function inAJAXValidation() { $app = Factory::getApplication(); return $app->input->get('task', '') === 'form.ajax_validate'; } /** * Remove messages from JApplicationCMS * * @param CMSApplication $app Application to kill messages from * @param string $type Message type e.g. 'warning', 'error' * * @return array Remaining messages. */ public static function killMessage(CMSApplication $app, $type) { $appReflection = new \ReflectionClass(get_class($app)); $messageQueue = $appReflection->getProperty('messageQueue'); $messageQueue->setAccessible(true); $messages = $messageQueue->getValue($app); foreach ($messages as $key => $message) { if ($message['type'] == $type) { unset($messages[$key]); } } $messageQueue->setValue($app, $messages); return $messages; } /** * Loose casing to boolean * * @param mixed $var Var to test * @param boolean $default if neither a truish or falsy match are found * * @return bool - Set to false if false is found. */ public static function toBoolean($var, $default) { if ($var === 'false' || $var === 0 || $var === false) { return false; } if ($var === 'true' || $var === 1 || $var === true) { return true; } return $default; } /** * Get a getID3 instance - check if library installed, if not, toss an exception * * @return object|bool - getid3 object or false if lib not installed */ public static function getID3Instance() { $getID3 = false; if (File::exists(COM_FABRIK_LIBRARY . '/libs/libs/getid3/getid3/getid3.php')) { ini_set('display_errors', true); require_once COM_FABRIK_LIBRARY . '/libs/libs/getid3/getid3/getid3.php'; require_once COM_FABRIK_LIBRARY . '/libs/libs/getid3/getid3/getid3.lib.php'; \getid3_lib::IncludeDependency(COM_FABRIK_LIBRARY . '/libs/libs/getid3/getid3/extension.cache.mysqli.php', __FILE__, true); $config = Factory::getApplication()->getConfig(); $host = $config->get('host'); $database = $config->get('db'); $username = $config->get('user'); $password = $config->get('password'); $getID3 = new \getID3_cached_mysqli($host, $database, $username, $password); } return $getID3; } public static function getMemoryLimit($symbolic = false) { $memory = trim(ini_get('memory_limit')); $memory = trim($memory); if ($symbolic) { return $memory; } if ($memory === '-1') { return PHP_INT_MAX; } $last = strtolower($memory[strlen($memory)-1]); $val = substr($memory, 0, -1); switch($last) { case 'g': $val *= 1024; case 'm': $val *= 1024; case 'k': $val *= 1024; } return $val; } }
| ver. 1.4 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0.09 |
proxy
|
phpinfo
|
Настройка