misc.inc

  1. 5.x includes/misc.inc
  2. 4.x includes/misc.inc

This file contains misc functions for FlightPath

Functions & methods

NameDescription
argReturns the component of the page's path.
base_pathShortcut for getting the base_path variable from the global system settings.
csv_to_arraySimple function to split a basic CSV string, trim all elements, then return the resulting array.
depricated_messageDisplays a depricated message on screen. Useful for tracking down when depricated functions are being used.
filter_markupFilter HTML, allowing only certain tags, and removing dangerous attributes.
filter_xssThis function is taken almost directly from Drupal 7's core code. It is used to help us filter out dangerous HTML which the user might type. From the D7 documentation:
filter_xss_attributes
filter_xss_bad_protocol
filter_xss_splitLike the filter_xss function, this is taken from D7's _filter_xss_split function
fpmUses fp_add_message, but in this case, it also adds in the filename and line number which the message came from!
fpmctConvenience function, will use fp_debug_ct() to display a message, and the number of miliseconds since its last call.
fp_add_body_classAdd a CSS class to the body tag of the page. Useful for themeing later on.
fp_add_cssAdd an extra CSS file to the page with this function. Ex: fp_add_css(fp_get_module_path("admin") . '/css/admin.css');
fp_add_jsAdd extra javascript to the page.
fp_add_messageAdd a "message" to the top of the screen. Useful for short messages like "You have been logged out" or "Form submitted successfully."
fp_clear_cacheCall all modules which implement hook_clear_cache
fp_debug_ctShortcut to fp_debug_current_time_millis()
fp_debug_current_time_millisWhen called repeatedly, this function will display a message along with a milisecond count out to the side. Very useful for developers to time function calls or queries, to see how long they are taking.
fp_explode_assocTakes a string (created by fp_join_assoc()) and re-creates the 1 dimensional assoc array.
fp_get_degree_classificationsReturn back an assoc array of our set degree classifications, separated by "level"
fp_get_degree_classification_detailsReturns back an assoc array for the supplied code. Looks like: $arr["level_num"] = number $arr["title"] = the title
fp_get_files_pathConvenience function to return the /files system path. Does NOT end with a trailing slash.
fp_get_js_alert_linkCreates a javascript "alert" link, which tells the user some message with javascript alert().
fp_get_js_confirm_linkCreates a javascript "confirm" link, so when clicked it asks the user a question, then proceeds if they select OK. The main reason I want to do this is so I can pass the $question through my t() function. (do it when you call this function)
fp_get_js_prompt_linkCreates a javascript "prompt" link, which will ask the user a question.
fp_get_machine_readableSimple function to convert a string into a machine-readable string.
fp_get_module_detailsSimply returns the module's row from the modules table, if it exists.
fp_get_module_pathReturn the filepath to the module
fp_get_random_stringReturns a random string of length len.
fp_get_requirement_typesReturns back an array of all the available requirement types (by code) that have been defined.
fp_gotoRedirect the user's browser to the specified internal path + query.
fp_html_print_rSimilar to print_r, this will return an HTML-friendly click-to-open system similar in design to Krumo.
fp_http_requestSend a request through the Internet and return the result as an object.
fp_join_assocThis function will create a string from a 1 dimensional assoc array. Ex: arr = array("pet" => "dog", "name" => "Rex") will return: pet-dog,name-Rex under the default settings.
fp_load_degreeThis function provides a pass-thru to $d = new DegreePlan(args). However, it allows for quick caching look-up, so it should be used when possible instead of $x = new DegreePlan.
fp_mailSend an email. Drop-in replacement for PHP's mail() command, but can use SMTP protocol if enabled.
fp_no_html_xssRemove any possiblilty of a malicious attacker trying to inject nonsense. From: https://paragonie.com/blog/2015/06/preventing-xss-vulnerabilities-in-php...
fp_number_padSimple function to left padd numbers with 0's. 1 becomes 001 20 becomes 020 and so on.
fp_reduce_whitespaceSimple helper function to reduce whitespace (like double-spaces)
fp_screen_is_mobileThis function will attempt to determine automatically if we are on a mobile device, and should therefor use the mobile theme and layout settings.
fp_set_page_sub_tabsAllows the programmer to define subtabs at the top of the page.
fp_set_page_tabsIf this function is called, it will override any other page tabs which might be getting constructed. This lets the programmer, at run-time, completely control what tabs are at the top of the page.
fp_set_titleAllows the programmer to set the title of the page, overwriting any default title.
fp_strip_dangerous_protocols
fp_tokenReturns back the site's "token", which is a simply md5 of some randomness. It is used primarily with forms, to ensure against cross-site forgeries. The site's token gets saved to the variables table, for later use. The idea is…
fp_truncate_decimalsThis simple function will take a number and truncate the number of decimals to the requested places. This can be used in place of number_format(), which *rounds* numbers.
fp_urlThis function will take a path, ex: "admin/config/module" and a query, ex: "nid=5&whatever=yes" And join them together, respecting whether or not clean URL's are enabled.
fp_validate_utf8
get_global_database_handlerThis method will return a globally-set DatabaseHandler object, creating it if it does not already exist. This is for efficiency reasons, so every module or method does not need to keep creating databasehandler objects (and re-connecting to the database).
get_modules_menusSimilar to get_modules_permissions, this will scan through all installed modules' hook_menu() functions, and assemble an array which is sorted by "location" and then by "weight".
get_modules_permissionsThis function will read through all the modules' permissions and return back an array. Specifically, it retrieves arrays from each modules' hook_perm() function.
get_shorter_catalog_year_rangeThis is used usually when being viewed by a mobile device. It will shorten a catalog year range of 2008-2009 to just "08-09" or "2008-09" or even "09-2009".
get_term_descriptionConvert a term ID into a description. Ex: 20095 = Spring of 2009.
get_term_id_suffixesThis looks at the global termIDStructure setting and returns back an array of only term suffixes (like 40, 60, mm, etc).
get_term_structuresReturn an array version of the term_id_structure field from the admin settings
include_moduleThis will find and include the module in question, calling it's hook_init() function if it has one.
include_module_installFind and include the module's .install file, if it exists. Returns TRUE or FALSE if it was able to find & include the file.
invoke_hookInvoke all module hooks for the supplied hook.
lThis works like Drupal's l() function for creating links. Ex: l("Click here for course search!", "tools/course-search", "abc=xyz&hello=goodbye", array("class" => "my-class")); Do not…
modules_implement_hookReturn an array of enabled modules which implement the provided hook. Do not include the preceeding "_" on the hook name!
stProvides translation functionality when database is not available.
tThis function will facilitate translations by using hook_translate()
timer_readWorks with the timer_start() function to return how long it has been since the start.
timer_startBegin a microtime timer for later use.
user_has_permissionReturns TRUE or FALSE if the logged in user has access based on the permission supplied.
user_has_roleDoes the user have the specified role?
_fp_error_handlerThis is our custom error handler, which will intercept PHP warnings, notices, etc, and let us display them, log them, etc.
_fp_map_php_error_codeMap an error code into an Error word *

File

includes/misc.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * This file contains misc functions for FlightPath
  5. */
  6. /**
  7. * This is our custom error handler, which will intercept PHP warnings, notices, etc, and let us
  8. * display them, log them, etc.
  9. *
  10. * See https://www.php.net/manual/en/function.set-error-handler.php
  11. */
  12. function _fp_error_handler($error_level, $message, $filename, $line, $context = array()) {
  13. global $user;
  14. if (0 === error_reporting()) { return false;} // suppressed with @-operator
  15. $err_name = _fp_map_php_error_code($error_level);
  16. if (stristr($err_name, 'notice')) return FALSE; // don't care about Notices.
  17. // fpm() only displays for privileged users
  18. fpm($err_name . ": $message<br>... ($line) $filename");
  19. if (@$user->id !== '1' && @$user->id !== 1) {
  20. // We are NOT the admin user.
  21. // Should we email someone, as with mysql errors?
  22. $tomail = trim(variable_get("notify_php_error_email_address", ""));
  23. if ($tomail != "") {
  24. $hostname = php_uname('n');
  25. $msg = "";
  26. $msg .= "SERVER: $hostname \n";
  27. $msg .= "DATE: " . format_date() . "\n";
  28. $msg .= "SEVERITY: $err_name \n";
  29. $msg .= "--------------------------\n\n";
  30. $msg .= "$err_name: $message \n\n";
  31. $msg .= "... ($line) $filename";
  32. fp_mail($tomail, "PHP Error in FlightPath", $msg);
  33. }
  34. }
  35. } // _fp_error_handler
  36. /**
  37. * Map an error code into an Error word
  38. *
  39. * @param int $code Error code to map
  40. * @return array Array of error word, and log location.
  41. */
  42. function _fp_map_php_error_code($code) {
  43. $error = '';
  44. switch ($code) {
  45. case E_PARSE:
  46. case E_ERROR:
  47. case E_CORE_ERROR:
  48. case E_COMPILE_ERROR:
  49. case E_USER_ERROR:
  50. $error = 'Fatal Error';
  51. break;
  52. case E_WARNING:
  53. case E_USER_WARNING:
  54. case E_COMPILE_WARNING:
  55. case E_RECOVERABLE_ERROR:
  56. $error = 'Warning';
  57. break;
  58. case E_NOTICE:
  59. case E_USER_NOTICE:
  60. $error = 'Notice';
  61. break;
  62. case E_STRICT:
  63. $error = 'Strict';
  64. break;
  65. case E_DEPRECATED:
  66. case E_USER_DEPRECATED:
  67. $error = 'Deprecated';
  68. break;
  69. default :
  70. break;
  71. }
  72. return $error;
  73. }
  74. /**
  75. * Send an email. Drop-in replacement for PHP's mail() command,
  76. * but can use SMTP protocol if enabled.
  77. */
  78. function fp_mail($to,$subject,$msg) {
  79. // TODO: In the future, check to see if there are any other modules which invoke a hook to intercept mail.
  80. if (function_exists('smtp_mail')) {
  81. smtp_mail($to, $subject, $msg);
  82. return;
  83. }
  84. else {
  85. mail($to, $subject, $msg);
  86. }
  87. } // fp_mail
  88. /**
  89. * Returns the component of the page's path.
  90. *
  91. * When viewing a page at the path "admin/structure/types", for example, arg(0) returns "admin", arg(1) returns "structure", and arg(2) returns "types".
  92. *
  93. */
  94. function arg($index) {
  95. $q = $_REQUEST["q"];
  96. $temp = explode("/", $q);
  97. $rtn = @trim($temp[$index]);
  98. return $rtn;
  99. }
  100. /**
  101. * Send a request through the Internet and return the result as an object.
  102. *
  103. * This is a modified copy of the Drupal 6 function drupal_http_request(),
  104. * taken from here: http://api.drupal.org/api/drupal/includes!common.inc/function/drupal_http_request/6
  105. */
  106. function fp_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3, $timeout = 30.0) {
  107. global $db_prefix;
  108. $result = new stdClass();
  109. // Parse the URL and make sure we can handle the schema.
  110. $uri = parse_url($url);
  111. if ($uri == FALSE) {
  112. $result->error = 'unable to parse URL';
  113. $result->code = -1001;
  114. return $result;
  115. }
  116. if (!isset($uri['scheme'])) {
  117. $result->error = 'missing schema';
  118. $result->code = -1002;
  119. return $result;
  120. }
  121. timer_start(__FUNCTION__);
  122. switch ($uri['scheme']) {
  123. case 'http':
  124. case 'feed':
  125. $port = isset($uri['port']) ? $uri['port'] : 80;
  126. $host = $uri['host'] . ($port != 80 ? ':' . $port : '');
  127. $fp = @fsockopen($uri['host'], $port, $errno, $errstr, $timeout);
  128. break;
  129. case 'https':
  130. // Note: Only works for PHP 4.3 compiled with OpenSSL.
  131. $port = isset($uri['port']) ? $uri['port'] : 443;
  132. $host = $uri['host'] . ($port != 443 ? ':' . $port : '');
  133. $fp = @fsockopen('ssl://' . $uri['host'], $port, $errno, $errstr, $timeout);
  134. break;
  135. default:
  136. $result->error = 'invalid schema ' . $uri['scheme'];
  137. $result->code = -1003;
  138. return $result;
  139. }
  140. // Make sure the socket opened properly.
  141. if (!$fp) {
  142. // When a network error occurs, we use a negative number so it does not
  143. // clash with the HTTP status codes.
  144. $result->code = -$errno;
  145. $result->error = trim($errstr);
  146. // Log that this failed.
  147. watchdog("http_request", "fp_http_request failed! Perhaps the server cannot make requests?", array(), WATCHDOG_ERROR);
  148. return $result;
  149. }
  150. // Construct the path to act on.
  151. $path = isset($uri['path']) ? $uri['path'] : '/';
  152. if (isset($uri['query'])) {
  153. $path .= '?' . $uri['query'];
  154. }
  155. // Create HTTP request.
  156. $defaults = array(
  157. // RFC 2616: "non-standard ports MUST, default ports MAY be included".
  158. // We don't add the port to prevent from breaking rewrite rules checking the
  159. // host that do not take into account the port number.
  160. 'Host' => "Host: $host",
  161. 'User-Agent' => 'User-Agent: FlightPath (+http://getflightpath.com/)',
  162. );
  163. // Only add Content-Length if we actually have any content or if it is a POST
  164. // or PUT request. Some non-standard servers get confused by Content-Length in
  165. // at least HEAD/GET requests, and Squid always requires Content-Length in
  166. // POST/PUT requests.
  167. $content_length = strlen($data);
  168. if ($content_length > 0 || $method == 'POST' || $method == 'PUT') {
  169. $defaults['Content-Length'] = 'Content-Length: ' . $content_length;
  170. }
  171. // If the server url has a user then attempt to use basic authentication
  172. if (isset($uri['user'])) {
  173. $defaults['Authorization'] = 'Authorization: Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : ''));
  174. }
  175. foreach ($headers as $header => $value) {
  176. $defaults[$header] = $header . ': ' . $value;
  177. }
  178. $request = $method . ' ' . $path . " HTTP/1.0\r\n";
  179. $request .= implode("\r\n", $defaults);
  180. $request .= "\r\n\r\n";
  181. $request .= $data;
  182. $result->request = $request;
  183. // Calculate how much time is left of the original timeout value.
  184. $time_left = $timeout - timer_read(__FUNCTION__) / 1000;
  185. if ($time_left > 0) {
  186. stream_set_timeout($fp, floor($time_left), floor(1000000 * fmod($time_left, 1)));
  187. fwrite($fp, $request);
  188. }
  189. // Fetch response.
  190. $response = '';
  191. while (!feof($fp)) {
  192. // Calculate how much time is left of the original timeout value.
  193. $time_left = $timeout - timer_read(__FUNCTION__) / 1000;
  194. if ($time_left <= 0) {
  195. $result->code = HTTP_REQUEST_TIMEOUT;
  196. $result->error = 'request timed out';
  197. return $result;
  198. }
  199. stream_set_timeout($fp, floor($time_left), floor(1000000 * fmod($time_left, 1)));
  200. $chunk = fread($fp, 1024);
  201. $response .= $chunk;
  202. }
  203. fclose($fp);
  204. // Parse response headers from the response body.
  205. // Be tolerant of malformed HTTP responses that separate header and body with
  206. // \n\n or \r\r instead of \r\n\r\n. See http://drupal.org/node/183435
  207. list($split, $result->data) = preg_split("/\r\n\r\n|\n\n|\r\r/", $response, 2);
  208. $split = preg_split("/\r\n|\n|\r/", $split);
  209. list($protocol, $code, $status_message) = explode(' ', trim(array_shift($split)), 3);
  210. $result->protocol = $protocol;
  211. $result->status_message = $status_message;
  212. $result->headers = array();
  213. // Parse headers.
  214. while ($line = trim(array_shift($split))) {
  215. list($header, $value) = explode(':', $line, 2);
  216. if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
  217. // RFC 2109: the Set-Cookie response header comprises the token Set-
  218. // Cookie:, followed by a comma-separated list of one or more cookies.
  219. $result->headers[$header] .= ',' . trim($value);
  220. }
  221. else {
  222. $result->headers[$header] = trim($value);
  223. }
  224. }
  225. $responses = array(
  226. 100 => 'Continue',
  227. 101 => 'Switching Protocols',
  228. 200 => 'OK',
  229. 201 => 'Created',
  230. 202 => 'Accepted',
  231. 203 => 'Non-Authoritative Information',
  232. 204 => 'No Content',
  233. 205 => 'Reset Content',
  234. 206 => 'Partial Content',
  235. 300 => 'Multiple Choices',
  236. 301 => 'Moved Permanently',
  237. 302 => 'Found',
  238. 303 => 'See Other',
  239. 304 => 'Not Modified',
  240. 305 => 'Use Proxy',
  241. 307 => 'Temporary Redirect',
  242. 400 => 'Bad Request',
  243. 401 => 'Unauthorized',
  244. 402 => 'Payment Required',
  245. 403 => 'Forbidden',
  246. 404 => 'Not Found',
  247. 405 => 'Method Not Allowed',
  248. 406 => 'Not Acceptable',
  249. 407 => 'Proxy Authentication Required',
  250. 408 => 'Request Time-out',
  251. 409 => 'Conflict',
  252. 410 => 'Gone',
  253. 411 => 'Length Required',
  254. 412 => 'Precondition Failed',
  255. 413 => 'Request Entity Too Large',
  256. 414 => 'Request-URI Too Large',
  257. 415 => 'Unsupported Media Type',
  258. 416 => 'Requested range not satisfiable',
  259. 417 => 'Expectation Failed',
  260. 500 => 'Internal Server Error',
  261. 501 => 'Not Implemented',
  262. 502 => 'Bad Gateway',
  263. 503 => 'Service Unavailable',
  264. 504 => 'Gateway Time-out',
  265. 505 => 'HTTP Version not supported',
  266. );
  267. // RFC 2616 states that all unknown HTTP codes must be treated the same as the
  268. // base code in their class.
  269. if (!isset($responses[$code])) {
  270. $code = floor($code / 100) * 100;
  271. }
  272. switch ($code) {
  273. case 200: // OK
  274. case 304: // Not modified
  275. break;
  276. case 301: // Moved permanently
  277. case 302: // Moved temporarily
  278. case 307: // Moved temporarily
  279. $location = $result->headers['Location'];
  280. $timeout -= timer_read(__FUNCTION__) / 1000;
  281. if ($timeout <= 0) {
  282. $result->code = HTTP_REQUEST_TIMEOUT;
  283. $result->error = 'request timed out';
  284. }
  285. elseif ($retry) {
  286. $result = fp_http_request($result->headers['Location'], $headers, $method, $data, --$retry, $timeout);
  287. $result->redirect_code = $result->code;
  288. }
  289. $result->redirect_url = $location;
  290. break;
  291. default:
  292. $result->error = $status_message;
  293. }
  294. $result->code = $code;
  295. return $result;
  296. }
  297. /**
  298. * Begin a microtime timer for later use.
  299. */
  300. function timer_start($name) {
  301. global $timers;
  302. list($usec, $sec) = explode(' ', microtime());
  303. $timers[$name]['start'] = (float) $usec + (float) $sec;
  304. $timers[$name]['count'] = isset($timers[$name]['count']) ? ++$timers[$name]['count'] : 1;
  305. }
  306. /**
  307. * Works with the timer_start() function to return how long
  308. * it has been since the start.
  309. */
  310. function timer_read($name) {
  311. global $timers;
  312. if (isset($timers[$name]['start'])) {
  313. list($usec, $sec) = explode(' ', microtime());
  314. $stop = (float) $usec + (float) $sec;
  315. $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
  316. if (isset($timers[$name]['time'])) {
  317. $diff += $timers[$name]['time'];
  318. }
  319. return $diff;
  320. }
  321. }
  322. /**
  323. * Returns a random string of length len.
  324. */
  325. function fp_get_random_string($len = 7, $alpha = TRUE, $numeric = TRUE, $symbols = FALSE) {
  326. $base = "";
  327. if ($alpha) {
  328. $base .= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  329. }
  330. if ($numeric) {
  331. $base .= "12345678901234567890";
  332. }
  333. if ($symbols) {
  334. $base .= "!@#$%^&*()_+!@#$%^&*()-=";
  335. }
  336. $str = "";
  337. for ($t = 0; $t < $len; $t++) {
  338. $base = str_shuffle($base);
  339. $str .= $base[0];
  340. }
  341. return $str;
  342. }
  343. /**
  344. * Call all modules which implement hook_clear_cache
  345. */
  346. function fp_clear_cache() {
  347. // Find modules which implement hook_clear_cache
  348. $modules = modules_implement_hook("clear_cache");
  349. foreach ($modules as $module) {
  350. call_user_func($module . '_clear_cache');
  351. }
  352. }
  353. /**
  354. Remove any possiblilty of a malicious attacker trying to inject
  355. nonsense.
  356. From: https://paragonie.com/blog/2015/06/preventing-xss-vulnerabilities-in-php-everything-you-need-know
  357. */
  358. function fp_no_html_xss($string) {
  359. return htmlentities($string, ENT_QUOTES, 'UTF-8');
  360. //return htmlentities($string, ENT_QUOTES | ENT_HTML5, 'UTF-8'); // ENT_HTML5 requires PGP 5.4+
  361. }
  362. /**
  363. * Filter HTML, allowing only certain tags, and removing dangerous attributes.
  364. *
  365. * $type can be:
  366. * - "basic" - Only certain tags allowed, no attributes. Safest. New lines = <br>
  367. * - "full" - All HTML is allowed through.
  368. *
  369. */
  370. function filter_markup($str, $type = "basic") {
  371. // Use the DOM functions to repair any mismatched HTML.
  372. $doc = new DOMDocument();
  373. @$doc->loadHTML($str);
  374. $str = $doc->saveHTML();
  375. if ($type == "basic") {
  376. // To reduce extra newlines, remove any newline which is at the END of an existing <br> tag.
  377. $str = str_ireplace("<br>\n", "<br />", $str);
  378. $str = str_ireplace("<br />\n", "<br />", $str);
  379. $allowed_tags = array('a', 'em', 'strong', 'cite',
  380. 'blockquote', 'code', 'ul', 'ol', 'li',
  381. 'dl', 'dt', 'dd', 'span', 'div',
  382. 'b', 'i', 'u', 'br', 'p', 'table', 'tr',
  383. 'td', 'th', 'tbody', );
  384. $str = filter_xss($str, $allowed_tags);
  385. $str = trim($str);
  386. $str = nl2br($str);
  387. }
  388. return $str;
  389. }
  390. /**
  391. * This function is taken almost directly from Drupal 7's core code. It is used to help us filter out
  392. * dangerous HTML which the user might type.
  393. * From the D7 documentation:
  394. *
  395. * Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities.
  396. * Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses. For examples of various XSS attacks, see: http://ha.ckers.org/xss.html.
  397. * This code does four things:
  398. * Removes characters and constructs that can trick browsers.
  399. * Makes sure all HTML entities are well-formed.
  400. * Makes sure all HTML tags and attributes are well-formed.
  401. * Makes sure no HTML tags contain URLs with a disallowed protocol (e.g. javascript:).
  402. *
  403. */
  404. function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'span', 'div')) {
  405. // Only operate on valid UTF-8 strings. This is necessary to prevent cross
  406. // site scripting issues on Internet Explorer 6.
  407. if (!fp_validate_utf8($string)) {
  408. return '';
  409. }
  410. // Store the text format.
  411. filter_xss_split($allowed_tags, TRUE);
  412. // Remove NULL characters (ignored by some browsers).
  413. $string = str_replace(chr(0), '', $string);
  414. // Remove Netscape 4 JS entities.
  415. $string = preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
  416. // Defuse all HTML entities.
  417. $string = str_replace('&', '&amp;', $string);
  418. // Change back only well-formed entities in our whitelist:
  419. // Decimal numeric entities.
  420. $string = preg_replace('/&amp;#([0-9]+;)/', '&#\1', $string);
  421. // Hexadecimal numeric entities.
  422. $string = preg_replace('/&amp;#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/', '&#x\1', $string);
  423. // Named entities.
  424. $string = preg_replace('/&amp;([A-Za-z][A-Za-z0-9]*;)/', '&\1', $string);
  425. return preg_replace_callback('%
  426. (
  427. <(?=[^a-zA-Z!/]) # a lone <
  428. | # or
  429. <!--.*?--> # a comment
  430. | # or
  431. <[^>]*(>|$) # a string that starts with a <, up until the > or the end of the string
  432. | # or
  433. > # just a >
  434. )%x', 'filter_xss_split', $string);
  435. }
  436. /**
  437. * Like the filter_xss function, this is taken from D7's
  438. * _filter_xss_split function
  439. */
  440. function filter_xss_split($m, $store = FALSE) {
  441. static $allowed_html;
  442. if ($store) {
  443. $allowed_html = array_flip($m);
  444. return;
  445. }
  446. $string = $m[1];
  447. if (substr($string, 0, 1) != '<') {
  448. // We matched a lone ">" character.
  449. return '&gt;';
  450. }
  451. elseif (strlen($string) == 1) {
  452. // We matched a lone "<" character.
  453. return '&lt;';
  454. }
  455. if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
  456. // Seriously malformed.
  457. return '';
  458. }
  459. $slash = trim($matches[1]);
  460. $elem = &$matches[2];
  461. $attrlist = &$matches[3];
  462. $comment = &$matches[4];
  463. if ($comment) {
  464. $elem = '!--';
  465. }
  466. if (!isset($allowed_html[strtolower($elem)])) {
  467. // Disallowed HTML element.
  468. return '';
  469. }
  470. if ($comment) {
  471. return $comment;
  472. }
  473. if ($slash != '') {
  474. return "</$elem>";
  475. }
  476. // Is there a closing XHTML slash at the end of the attributes?
  477. $attrlist = preg_replace('%(\s?)/\s*$%', '\1', $attrlist, -1, $count);
  478. $xhtml_slash = $count ? ' /' : '';
  479. // Clean up attributes.
  480. $attr2 = implode(' ', filter_xss_attributes($attrlist));
  481. $attr2 = preg_replace('/[<>]/', '', $attr2);
  482. $attr2 = strlen($attr2) ? ' ' . $attr2 : '';
  483. return "<$elem$attr2$xhtml_slash>";
  484. }
  485. function filter_xss_attributes($attr) {
  486. $attrarr = array();
  487. $mode = 0;
  488. $attrname = '';
  489. while (strlen($attr) != 0) {
  490. // Was the last operation successful?
  491. $working = 0;
  492. switch ($mode) {
  493. case 0:
  494. // Attribute name, href for instance.
  495. if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
  496. $attrname = strtolower($match[1]);
  497. $skip = ($attrname == 'style' || substr($attrname, 0, 2) == 'on');
  498. $working = $mode = 1;
  499. $attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
  500. }
  501. break;
  502. case 1:
  503. // Equals sign or valueless ("selected").
  504. if (preg_match('/^\s*=\s*/', $attr)) {
  505. $working = 1;
  506. $mode = 2;
  507. $attr = preg_replace('/^\s*=\s*/', '', $attr);
  508. break;
  509. }
  510. if (preg_match('/^\s+/', $attr)) {
  511. $working = 1;
  512. $mode = 0;
  513. if (!$skip) {
  514. $attrarr[] = $attrname;
  515. }
  516. $attr = preg_replace('/^\s+/', '', $attr);
  517. }
  518. break;
  519. case 2:
  520. // Attribute value, a URL after href= for instance.
  521. if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match)) {
  522. $thisval = filter_xss_bad_protocol($match[1]);
  523. if (!$skip) {
  524. $attrarr[] = "$attrname=\"$thisval\"";
  525. }
  526. $working = 1;
  527. $mode = 0;
  528. $attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
  529. break;
  530. }
  531. if (preg_match("/^'([^']*)'(\s+|$)/", $attr, $match)) {
  532. $thisval = filter_xss_bad_protocol($match[1]);
  533. if (!$skip) {
  534. $attrarr[] = "$attrname='$thisval'";
  535. }
  536. $working = 1;
  537. $mode = 0;
  538. $attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
  539. break;
  540. }
  541. if (preg_match("%^([^\s\"']+)(\s+|$)%", $attr, $match)) {
  542. $thisval = filter_xss_bad_protocol($match[1]);
  543. if (!$skip) {
  544. $attrarr[] = "$attrname=\"$thisval\"";
  545. }
  546. $working = 1;
  547. $mode = 0;
  548. $attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
  549. }
  550. break;
  551. }
  552. if ($working == 0) {
  553. // Not well formed; remove and try again.
  554. $attr = preg_replace('/
  555. ^
  556. (
  557. "[^"]*("|$) # - a string that starts with a double quote, up until the next double quote or the end of the string
  558. | # or
  559. \'[^\']*(\'|$)| # - a string that starts with a quote, up until the next quote or the end of the string
  560. | # or
  561. \S # - a non-whitespace character
  562. )* # any number of the above three
  563. \s* # any number of whitespaces
  564. /x', '', $attr);
  565. $mode = 0;
  566. }
  567. }
  568. // The attribute list ends with a valueless attribute like "selected".
  569. if ($mode == 1 && !$skip) {
  570. $attrarr[] = $attrname;
  571. }
  572. return $attrarr;
  573. }
  574. function filter_xss_bad_protocol($string) {
  575. // Get the plain text representation of the attribute value (i.e. its meaning).
  576. $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8');
  577. return htmlspecialchars(fp_strip_dangerous_protocols($string), ENT_QUOTES, 'UTF-8');
  578. }
  579. function fp_strip_dangerous_protocols($uri) {
  580. static $allowed_protocols;
  581. if (!isset($allowed_protocols)) {
  582. $allowed_protocols = array_flip(array('ftp', 'http', 'https', 'irc', 'mailto', 'news', 'nntp', 'rtsp', 'sftp', 'ssh', 'tel', 'telnet', 'webcal'));
  583. }
  584. // Iteratively remove any invalid protocol found.
  585. do {
  586. $before = $uri;
  587. $colonpos = strpos($uri, ':');
  588. if ($colonpos > 0) {
  589. // We found a colon, possibly a protocol. Verify.
  590. $protocol = substr($uri, 0, $colonpos);
  591. // If a colon is preceded by a slash, question mark or hash, it cannot
  592. // possibly be part of the URL scheme. This must be a relative URL, which
  593. // inherits the (safe) protocol of the base document.
  594. if (preg_match('![/?#]!', $protocol)) {
  595. break;
  596. }
  597. // Check if this is a disallowed protocol. Per RFC2616, section 3.2.3
  598. // (URI Comparison) scheme comparison must be case-insensitive.
  599. if (!isset($allowed_protocols[strtolower($protocol)])) {
  600. $uri = substr($uri, $colonpos + 1);
  601. }
  602. }
  603. } while ($before != $uri);
  604. return $uri;
  605. }
  606. function fp_validate_utf8($text) {
  607. if (strlen($text) == 0) {
  608. return TRUE;
  609. }
  610. // With the PCRE_UTF8 modifier 'u', preg_match() fails silently on strings
  611. // containing invalid UTF-8 byte sequences. It does not reject character
  612. // codes above U+10FFFF (represented by 4 or more octets), though.
  613. return (preg_match('/^./us', $text) == 1);
  614. }
  615. /**
  616. * Simple function to convert a string into a machine-readable string.
  617. *
  618. * Useful for making possibly unsafe text work as an array index, a CSS class, etc. Replaces
  619. * "bad" characters, or characters which might not be allowed for variables, for example,
  620. * into underscores (_).
  621. *
  622. * @param unknown_type $str
  623. * @return unknown
  624. */
  625. function fp_get_machine_readable($str) {
  626. return preg_replace('@[^a-zA-Z0-9_]+@','_',$str);
  627. }
  628. /////////////////////////////////////////////////////////////////////
  629. /**
  630. * Return back an assoc array of our set degree classifications, separated by "level"
  631. */
  632. function fp_get_degree_classifications() {
  633. $rtn = array();
  634. // Level 1
  635. $temp = explode("\n", variable_get("degree_classifications_level_1", "MAJOR ~ Major"));
  636. foreach ($temp as $line) {
  637. $temp2 = explode("~", $line);
  638. $machine_name = trim($temp2[0]);
  639. $title = trim($temp2[1]);
  640. if ($machine_name != "") {
  641. $rtn["levels"][1][$machine_name] = $title;
  642. $rtn["machine_names"][$machine_name] = $title;
  643. }
  644. }
  645. // Level 2
  646. $temp = explode("\n", variable_get("degree_classifications_level_2", "MINOR ~ Minor"));
  647. foreach ($temp as $line) {
  648. $temp2 = explode("~", $line);
  649. $machine_name = trim($temp2[0]);
  650. $title = trim($temp2[1]);
  651. if ($machine_name != "") {
  652. $rtn["levels"][2][$machine_name] = $title;
  653. $rtn["machine_names"][$machine_name] = $title;
  654. }
  655. }
  656. // Level 3
  657. $temp = explode("\n", variable_get("degree_classifications_level_3", "CONC ~ Concentration"));
  658. foreach ($temp as $line) {
  659. $temp2 = explode("~", $line);
  660. $machine_name = trim($temp2[0]);
  661. $title = trim($temp2[1]);
  662. if ($machine_name != "") {
  663. $rtn["levels"][3][$machine_name] = $title;
  664. $rtn["machine_names"][$machine_name] = $title;
  665. }
  666. }
  667. return $rtn;
  668. }
  669. /**
  670. * Returns back an assoc array for the supplied code. Looks like:
  671. * $arr["level_num"] = number
  672. * $arr["title"] = the title
  673. *
  674. *
  675. */
  676. function fp_get_degree_classification_details($degree_class = "MAJOR", $bool_return_class_code_as_title_if_not_found = TRUE) {
  677. $rtn = array();
  678. if ($bool_return_class_code_as_title_if_not_found) {
  679. // Use the degree_class as title for default, if we can't find it otherwise.
  680. $rtn["level_num"] = 0;
  681. $rtn["title"] = $degree_class;
  682. $rtn["degree_class"] = $degree_class;
  683. }
  684. $degree_classifications = fp_get_degree_classifications();
  685. foreach ($degree_classifications["levels"] as $num => $details) {
  686. if (isset($details[$degree_class])) {
  687. $rtn["level_num"] = $num;
  688. $rtn["title"] = $details[$degree_class];
  689. $rtn["degree_class"] = $degree_class;
  690. break;
  691. }
  692. }
  693. return $rtn;
  694. }
  695. /**
  696. * Return an array version of the term_id_structure field from the admin settings
  697. *
  698. */
  699. function get_term_structures() {
  700. $rtn = array();
  701. $temp = $GLOBALS["fp_system_settings"]["term_id_structure"];
  702. $structures = explode("\n", $temp);
  703. foreach ($structures as $structure) {
  704. $tokens = explode(",", $structure);
  705. $term_def = trim($tokens[0]);
  706. // Get rid of the replacement pattern.
  707. // Looks like: [Y4]40. We want the 40.
  708. // Simply explode on "]"
  709. $temp = explode("]", $term_def);
  710. $rtn[trim($temp[1])] = array(
  711. "term_def" => $term_def,
  712. "short" => trim(@$tokens[1]),
  713. "full" => trim(@$tokens[2]),
  714. "abbr" => trim(@$tokens[3]),
  715. "disp_adjust" => trim(@$tokens[4]),
  716. );
  717. }
  718. return $rtn;
  719. }
  720. /**
  721. * Returns back an array of all the available requirement types (by code) that
  722. * have been defined.
  723. *
  724. */
  725. function fp_get_requirement_types() {
  726. $rtn = array();
  727. if (isset($GLOBALS['fp_temp_cache']['fp_get_requirement_types'])) {
  728. return $GLOBALS['fp_temp_cache']['fp_get_requirement_types'];
  729. }
  730. $temp = explode("\n", variable_get("requirement_types", "g ~ General Requirements\nc ~ Core Requirements\ne ~ Electives\nm ~ Major Requirements\ns ~ Supporting Requirements\nx ~ Additional Requirements"));
  731. foreach ($temp as $line) {
  732. $line = trim($line);
  733. if ($line == "") continue;
  734. $temp = explode("~", $line);
  735. $code = trim(strtolower($temp[0]));
  736. $desc = trim($temp[1]);
  737. $rtn[$code] = $desc;
  738. }
  739. // Make sure that code 'x' is set.
  740. if (!isset($rtn["x"])) {
  741. $rtn["x"] = t("Additional Requirements");
  742. }
  743. // Make sure code 'e' for Electives is set.
  744. if (!isset($rtn["e"])) {
  745. $rtn["e"] = t("Electives");
  746. }
  747. // Make sure code 'm' is set, for Major Requirements, our default type.
  748. if (!isset($rtn["m"])) {
  749. $rtn["m"] = t("Major Requirements");
  750. }
  751. $GLOBALS['fp_temp_cache']['fp_get_requirement_types'] = $rtn;
  752. return $rtn;
  753. }
  754. /**
  755. * This function provides a pass-thru to $d = new DegreePlan(args).
  756. * However, it allows for quick caching look-up, so it should be used when possible instead of $x = new DegreePlan.
  757. */
  758. function fp_load_degree($degree_id = "", DatabaseHandler $db = NULL, $bool_load_minimal = false, $array_significant_courses = false, $bool_use_draft = false) {
  759. // Create a "cache key" based on the arguments, so we can look this degree up faster later.
  760. $cache_key = md5(serialize(func_get_args()));
  761. //fpm("degree_id: $degree_id . cache key:" . $cache_key . "+++++++++++++");
  762. if (isset($GLOBALS['fp_temp_cache']['fp_load_degree'][$cache_key])) {
  763. $degree = $GLOBALS['fp_temp_cache']['fp_load_degree'][$cache_key];
  764. //fpm(" ... returning cache");
  765. return $degree;
  766. }
  767. $degree = new DegreePlan($degree_id, $db, $bool_load_minimal, $array_significant_courses, $bool_use_draft);
  768. // Save to our cache
  769. $GLOBALS['fp_temp_cache']['fp_load_degree'][$cache_key] = $degree;
  770. return $degree;
  771. }
  772. /**
  773. * If this function is called, it will override any other page tabs
  774. * which might be getting constructed. This lets the programmer,
  775. * at run-time, completely control what tabs are at the top of the page.
  776. */
  777. function fp_set_page_tabs($tab_array) {
  778. $GLOBALS["fp_set_page_tabs"] = $tab_array;
  779. }
  780. /**
  781. * Allows the programmer to define subtabs at the top of the page.
  782. *
  783. * @param unknown_type $tab_array
  784. */
  785. function fp_set_page_sub_tabs($tab_array) {
  786. $GLOBALS["fp_set_page_sub_tabs"] = $tab_array;
  787. }
  788. /**
  789. * Allows the programmer to set the title of the page, overwriting any default title.
  790. *
  791. * @param unknown_type $title
  792. */
  793. function fp_set_title($title) {
  794. $GLOBALS["fp_set_title"] = $title;
  795. if ($title == "") {
  796. fp_show_title(FALSE); // No title to show!
  797. }
  798. else {
  799. fp_show_title(TRUE); // If we are calling this function, we clearly want to display the title.
  800. }
  801. }
  802. /**
  803. * Add a CSS class to the body tag of the page. Useful for themeing later on.
  804. *
  805. * @param unknown_type $class
  806. */
  807. function fp_add_body_class($class) {
  808. @$GLOBALS["fp_add_body_classes"] .= " " . $class;
  809. }
  810. /**
  811. * Returns back the site's "token", which is a simply md5 of some randomness.
  812. * It is used primarily with forms, to ensure against cross-site forgeries.
  813. * The site's token gets saved to the variables table, for later use. The idea
  814. * is that every installation of FlightPath has a semi-unique token.
  815. */
  816. function fp_token() {
  817. $site_token = variable_get("site_token", "");
  818. if ($site_token == "") {
  819. $site_token = md5("" . time() . rand(1,9999));
  820. variable_set("site_token", $site_token);
  821. }
  822. return $site_token;
  823. }
  824. /**
  825. * Simple function to split a basic CSV string, trim all elements, then return
  826. * the resulting array.
  827. */
  828. function csv_to_array($csv_string) {
  829. $temp = explode(",", $csv_string);
  830. $temp = array_map("trim", $temp);
  831. return $temp;
  832. }
  833. /**
  834. * Add a "message" to the top of the screen. Useful for short messages like "You have been logged out"
  835. * or "Form submitted successfully."
  836. *
  837. * @param String $msg
  838. * This is the string message itself.
  839. * @param String $type
  840. * The "type" of message. This string is added as a CSS class to the message, for theming later.
  841. * @param boolean $bool_no_repeat
  842. * Boolean. Should the message show more than once per page view? Set to TRUE if it should NOT.
  843. */
  844. function fp_add_message($msg, $type = "status", $bool_no_repeat = FALSE) {
  845. $md5 = md5($type . $msg);
  846. if ($bool_no_repeat && isset($_SESSION["fp_messages"]) && is_array($_SESSION["fp_messages"])) {
  847. // Make sure this message isn't already in the session.
  848. foreach($_SESSION["fp_messages"] as $s) {
  849. if ($s["md5"] == $md5) return;
  850. }
  851. }
  852. $_SESSION["fp_messages"][] = array("type" => $type, "msg" => $msg, "md5" => $md5);
  853. }
  854. /**
  855. * Add an extra CSS file to the page with this function.
  856. * Ex: fp_add_css(fp_get_module_path("admin") . '/css/admin.css');
  857. *
  858. * @param String $path_to_css
  859. */
  860. function fp_add_css($path_to_css) {
  861. // Init if needed
  862. if (!isset($GLOBALS['fp_extra_css'])) $GLOBALS['fp_extra_css'] = array();
  863. if (!in_array($path_to_css, $GLOBALS['fp_extra_css'])) {
  864. $GLOBALS["fp_extra_css"][] = $path_to_css;
  865. }
  866. }
  867. /**
  868. * Add extra javascript to the page.
  869. *
  870. * - type = file... $js is expected to be the path to a javascript file.
  871. * - type = setting... $js is expected to be an associative array of settings.
  872. * For example: array("my_path" => "blah", "my_color" => "red").
  873. * They will be available in javascript in the object FlightPath like so:
  874. * FlightPath.settings.my_color;
  875. *
  876. * Ex: fp_add_js(fp_get_module_path("admin") . '/js/admin.js');
  877. *
  878. * @see fp_add_css()
  879. *
  880. */
  881. function fp_add_js($js, $type = "file") {
  882. // Init if needed
  883. if (!isset($GLOBALS['fp_extra_js'])) $GLOBALS['fp_extra_js'] = array();
  884. if ($type == "file") {
  885. if (!in_array($js, $GLOBALS['fp_extra_js'])) {
  886. $GLOBALS["fp_extra_js"][] = $js;
  887. }
  888. }
  889. if ($type == "setting") {
  890. if (!isset($GLOBALS["fp_extra_js_settings"])) $GLOBALS["fp_extra_js_settings"] = array();
  891. $GLOBALS["fp_extra_js_settings"] = array_merge($GLOBALS["fp_extra_js_settings"], $js);
  892. }
  893. }
  894. /**
  895. * This function will create a string from a 1 dimensional assoc array.
  896. * Ex: arr = array("pet" => "dog", "name" => "Rex")
  897. * will return: pet-dog,name-Rex under the default settings.
  898. *
  899. * Use the fp_explode_assoc function to piece it back together.
  900. * @see fp_explode_assoc
  901. */
  902. function fp_join_assoc($arr, $glue = ",", $assign_sep = "-") {
  903. $rtn = "";
  904. foreach ($arr as $key => $val) {
  905. $rtn .= $key . $assign_sep . $val . $glue;
  906. }
  907. // Should be an extra glue character at the end we need to trim off.
  908. $rtn = rtrim($rtn, $glue);
  909. return $rtn;
  910. }
  911. /**
  912. * Takes a string (created by fp_join_assoc()) and re-creates the 1 dimensional assoc array.
  913. *
  914. * @see fp_join_assoc()
  915. */
  916. function fp_explode_assoc($string, $delim = ",", $assign_sep = "-") {
  917. $rtn = array();
  918. $temp = explode($delim, $string);
  919. foreach($temp as $line) {
  920. $line = trim($line);
  921. if ($line == "") continue;
  922. $temp2 = explode($assign_sep, $line);
  923. if (is_numeric($temp2[1])) {
  924. $temp2[1] = $temp2[1] * 1; // if its numeric anyway, make it have a numeric type.
  925. }
  926. $rtn[$temp2[0]] = $temp2[1];
  927. }
  928. return $rtn;
  929. }
  930. /**
  931. * Return the filepath to the module
  932. *
  933. * @param unknown_type $module
  934. * @param unknown_type $bool_include_file_system_path
  935. * @param unknown_type $bool_include_base_path
  936. * @return unknown
  937. */
  938. function fp_get_module_path($module, $bool_include_file_system_path = FALSE, $bool_include_base_path = TRUE) {
  939. $p = menu_get_module_path($module, $bool_include_file_system_path);
  940. if ($bool_include_file_system_path == FALSE && $bool_include_base_path == TRUE) {
  941. $p = base_path() . "/" . $p;
  942. }
  943. return $p;
  944. }
  945. /**
  946. * Convenience function to return the /files system path. Does NOT end with a trailing slash.
  947. *
  948. */
  949. function fp_get_files_path() {
  950. return $GLOBALS["fp_system_settings"]["file_system_path"] . "/custom/files";
  951. }
  952. /**
  953. * Simply returns the module's row from the modules table, if it exists.
  954. *
  955. * @param unknown_type $module
  956. */
  957. function fp_get_module_details($module) {
  958. // Special case if we are looking up flightpath itself
  959. if ($module == "flightpath") {
  960. $rtn = array(
  961. "info" => array("name" => t("FlightPath (Core)")),
  962. "version" => FLIGHTPATH_VERSION,
  963. );
  964. return $rtn;
  965. }
  966. $res = db_query("SELECT * FROM modules WHERE name = '?' ", $module);
  967. $cur = db_fetch_array($res);
  968. if ($test = unserialize($cur["info"])) {
  969. $cur["info"] = $test;
  970. }
  971. return $cur;
  972. }
  973. /**
  974. * This function will facilitate translations by using hook_translate()
  975. *
  976. * Allows variable replacements. Use like this:
  977. * t("@name's blob", array("@name" => "Richard"));
  978. * or simply
  979. * t("My blob"); if you don't need replacements.
  980. *
  981. * Not implimented yet.
  982. */
  983. function t($str, $vars = array()) {
  984. // Note: at the moment, this isn't being used, but should one day be set.
  985. @$langcode = isset($langcode) ? $langcode : $GLOBALS["fp_system_settings"]["language"];
  986. // First, change $str if any other modules implement hook_translate().
  987. invoke_hook("translate", array(&$str, $langcode)); // str is passed by ref, so no return needed.
  988. if (is_array($vars) && count($vars) > 0) {
  989. foreach ($vars as $var => $val) {
  990. // If var begins with %, it means we want to italicize the val.
  991. if (strstr($var, "%")) {
  992. $val = "<em>$val</em>";
  993. }
  994. $str = str_replace($var, $val, $str);
  995. }
  996. }
  997. return $str;
  998. }
  999. /**
  1000. * Provides translation functionality when database is not available.
  1001. *
  1002. * Not implemented yet
  1003. */
  1004. function st($str, $vars = array()) {
  1005. // Not implemented yet. For now, just replicate t().
  1006. if (is_array($vars) && count($vars) > 0) {
  1007. foreach ($vars as $var => $val) {
  1008. // If var begins with %, it means we want to italicize the val.
  1009. if (strstr($var, "%")) {
  1010. $val = "<em>$val</em>";
  1011. }
  1012. $str = str_replace($var, $val, $str);
  1013. }
  1014. }
  1015. return $str;
  1016. }
  1017. /**
  1018. * Shortcut for getting the base_path variable from the global system settings.
  1019. */
  1020. function base_path() {
  1021. $p = $GLOBALS["fp_system_settings"]["base_path"];
  1022. // the base_path setting isn't set, so just use '.', meaning, start
  1023. // at the currect directory by default.
  1024. if ($p == "") {
  1025. $p = ".";
  1026. }
  1027. // if our base_path is simply "/" (meaning, we are hosted on a bare domain), then we should
  1028. // actually return nothing, so as not to cause errors with other systems.
  1029. if ($p == "/") {
  1030. $p = "";
  1031. }
  1032. return $p;
  1033. }
  1034. /**
  1035. * Convert a term ID into a description. Ex: 20095 = Spring of 2009.
  1036. */
  1037. function get_term_description($term_id, $bool_abbreviate = false) {
  1038. // Describe the term in plain english, for displays.
  1039. // Ex: "Fall of 2002."
  1040. $rtn = "";
  1041. // See if any modules would like to act on the term_id before we proceed.
  1042. invoke_hook("alter_term_id_prior_to_description", array(&$term_id, &$bool_abbreviate));
  1043. if (strstr($term_id, "1111"))
  1044. {
  1045. return "(data unavailable at this time)";
  1046. }
  1047. $year4 = intval(trim(substr($term_id, 0, 4)));
  1048. $year2 = intval(trim(substr($term_id, 2, 2)));
  1049. $ss = trim(substr($term_id, 4, strlen($term_id) - 4));
  1050. $year4p1 = $year4 + 1;
  1051. $year4m1 = $year4 - 1;
  1052. // left-pad these with 0's if needed.
  1053. $year2p1 = @fp_number_pad($year2 + 1, 2);
  1054. $year2m1 = @fp_number_pad($year2 - 1, 2);
  1055. // Let's look at the term_idStructure setting and attempt to match
  1056. // what we have been supplied.
  1057. // We expect this structure to look something like:
  1058. // [Y4]60, Spring, Spring of [Y4], Spr '[Y2]
  1059. // [Y4]40, Fall, Fall of [Y4-1], Fall '[Y2-1]
  1060. $temp = @$GLOBALS["fp_system_settings"]["term_id_structure"];
  1061. $structures = explode("\n", $temp);
  1062. foreach ($structures as $structure) {
  1063. // Perform the necessary replacement patterns on the structure.
  1064. $structure = str_replace("[Y4]", $year4, $structure);
  1065. $structure = str_replace("[Y2]", $year2, $structure);
  1066. $structure = str_replace("[Y4-1]", $year4m1, $structure);
  1067. $structure = str_replace("[Y2-1]", $year2m1, $structure);
  1068. $structure = str_replace("[Y4+1]", $year4p1, $structure);
  1069. $structure = str_replace("[Y2+1]", $year2p1, $structure);
  1070. // Now, break up the structure to make it easier to work with.
  1071. $tokens = explode(",", $structure);
  1072. $term_def = @trim($tokens[0]);
  1073. $full_description = @trim($tokens[2]);
  1074. $abbr_description = @trim($tokens[3]);
  1075. // Does our term_id match the termDef?
  1076. if ($term_def == $term_id) {
  1077. if ($bool_abbreviate) {
  1078. return $abbr_description;
  1079. }
  1080. else {
  1081. return $full_description;
  1082. }
  1083. }
  1084. }
  1085. // No descr could be found, so just display the term_id itself.
  1086. if (trim($rtn) == "") {
  1087. $rtn = $term_id;
  1088. }
  1089. return $rtn;
  1090. }
  1091. /**
  1092. * Redirect the user's browser to the specified internal path + query.
  1093. *
  1094. * We will automatically add the current_student_id variable, if it is not present
  1095. * in the query.
  1096. *
  1097. * Example uses:
  1098. * - fp_goto("admin");
  1099. * - fp_goto("test/1234");
  1100. * - fp_goto("test/123", "selection=yes&fruit=apple");
  1101. */
  1102. function fp_goto($path, $query = "") {
  1103. global $current_student_id;
  1104. if ($current_student_id != "" && !strstr($query, "current_student_id=")) {
  1105. // If the query doesn't contain the current_student_id, then add it in.
  1106. $query .= "&current_student_id=$current_student_id";
  1107. }
  1108. // Close the seesion before we try to redirect.
  1109. session_write_close();
  1110. if ($path == "<front>") {
  1111. $path = variable_get("front_page", "main");
  1112. }
  1113. header('Location: ' . fp_url($path, $query));
  1114. exit();
  1115. }
  1116. /**
  1117. * This works like Drupal's l() function for creating links.
  1118. * Ex: l("Click here for course search!", "tools/course-search", "abc=xyz&hello=goodbye", array("class" => "my-class"));
  1119. * Do not include preceeding or trailing slashes.
  1120. */
  1121. function l($text, $path, $query = "", $attributes = array()) {
  1122. $rtn = "";
  1123. if ($path == "<front>") {
  1124. $path = variable_get("front_page", "main");
  1125. }
  1126. // Does the path contain possible replacement patterns? (look for %)
  1127. if (strpos($path, "%") !== 0) {
  1128. $path = menu_convert_replacement_pattern($path);
  1129. }
  1130. // Does the query contain possible replacement patterns? (look for %)
  1131. if (strpos($query, "%") !== 0) {
  1132. $query = menu_convert_replacement_pattern($query);
  1133. }
  1134. $rtn .= '<a href="' . fp_url($path, $query) . '" ';
  1135. foreach ($attributes as $key => $value) {
  1136. $rtn .= $key . '="' . $value . '" ';
  1137. }
  1138. $rtn .= ">$text</a>";
  1139. return $rtn;
  1140. }
  1141. /**
  1142. * This function will take a path, ex: "admin/config/module"
  1143. * and a query, ex: "nid=5&whatever=yes"
  1144. * And join them together, respecting whether or not clean URL's are enabled.
  1145. */
  1146. function fp_url($path, $query = "", $include_base_path = TRUE) {
  1147. // If clean URLs are enabled, we should begin with a ?, if not, use an &
  1148. $rtn = "";
  1149. if ($include_base_path) {
  1150. $rtn .= base_path() . "/";
  1151. }
  1152. // Make sure that $rtn isn't now "//". This can happen if our
  1153. // site is hosted on a bare domain. Ex: http://fp.example.com
  1154. // And we have set the base_path to simply "/"
  1155. if ($rtn == "//") $rtn = "/";
  1156. $bool_clean_urls = variable_get("clean_urls", FALSE);
  1157. if (!$bool_clean_urls) {
  1158. // Clean URLs are NOT enabled! Let's make sure the URL contains "index.php?q="
  1159. $rtn .= "index.php?q=";
  1160. }
  1161. $rtn .= $path;
  1162. if ($query != "") {
  1163. // Is there a ? already in the $rtn? If not, add a ?. If so, use a &.
  1164. if (!strstr($rtn, "?")) {
  1165. $rtn .= "?";
  1166. }
  1167. else {
  1168. $rtn .= "&";
  1169. }
  1170. $rtn .= $query;
  1171. }
  1172. return $rtn;
  1173. }
  1174. /**
  1175. * This function will attempt to determine automatically
  1176. * if we are on a mobile device, and should therefor use the mobile
  1177. * theme and layout settings.
  1178. *
  1179. */
  1180. function fp_screen_is_mobile(){
  1181. if (isset($GLOBALS["fp_page_is_mobile"])) {
  1182. return $GLOBALS["fp_page_is_mobile"];
  1183. }
  1184. $user_agent = $_SERVER['HTTP_USER_AGENT'];
  1185. $look_for = array(
  1186. "ipod",
  1187. "iphone",
  1188. "android",
  1189. "opera mini",
  1190. "blackberry",
  1191. "(pre\/|palm os|palm|hiptop|avantgo|plucker|xiino|blazer|elaine)",
  1192. "(iris|3g_t|windows ce|opera mobi|windows ce; smartphone;|windows ce; iemobile)",
  1193. "(smartphone|iemobile)",
  1194. );
  1195. $is_mobile = FALSE;
  1196. foreach ($look_for as $test_agent) {
  1197. if (preg_match('/' . $test_agent . '/i',$user_agent)) {
  1198. $is_mobile = TRUE;
  1199. break;
  1200. }
  1201. }
  1202. $GLOBALS["fp_page_is_mobile"] = $is_mobile;
  1203. return $is_mobile;
  1204. } // ends function mobile_device_detect
  1205. ////////////////////////////////////////////////////////////////////
  1206. /////////////////////////////////////////////////////////////////////////////////////////////
  1207. /////////////////////////////////////////////////////////////////////////////////////////////
  1208. /////////////////////////////////////////////////////////////////////////////////////////////
  1209. /**
  1210. * Return an array of enabled modules which implement the provided hook.
  1211. * Do not include the preceeding "_" on the hook name!
  1212. */
  1213. function modules_implement_hook($hook = "example_hook_here") {
  1214. // Going to use a global array to keep track of what hooks exist.
  1215. if (!isset($GLOBALS['hook_cache'])) $GLOBALS['hook_cache'] = array();
  1216. // Have we already cached this list previously?
  1217. if (isset($GLOBALS['hook_cache'][$hook])) return $GLOBALS['hook_cache'][$hook];
  1218. // We have not already cached this, so let's look for it fresh...
  1219. $rtn = array();
  1220. // If we are in the install script, the GLOBALS array won't be set up, since there is no
  1221. // settings file yet. If that's the case, create a blank array so we don't have an issue.
  1222. if (!isset($GLOBALS['fp_system_settings'])) $GLOBALS['fp_system_settings'] = array();
  1223. if (!isset($GLOBALS['fp_system_settings']['modules'])) $GLOBALS['fp_system_settings']['modules'] = array();
  1224. foreach ($GLOBALS["fp_system_settings"]["modules"] as $module => $value) {
  1225. if (isset($value["enabled"]) && $value["enabled"] != "1") {
  1226. // Module is not enabled. Skip it.
  1227. continue;
  1228. }
  1229. if (function_exists($module . '_' . $hook)) {
  1230. $rtn[] = $module;
  1231. }
  1232. }
  1233. $GLOBALS['hook_cache'][$hook] = $rtn;
  1234. return $rtn;
  1235. }
  1236. /**
  1237. * Invoke all module hooks for the supplied hook.
  1238. */
  1239. function invoke_hook($hook = "example_hook_here", $params = array()) {
  1240. $rtn = array();
  1241. $modules = modules_implement_hook($hook);
  1242. foreach($modules as $module) {
  1243. $rtn[$module] = call_user_func_array($module . "_" . $hook, $params);
  1244. }
  1245. return $rtn;
  1246. }
  1247. /**
  1248. * This method will return a globally-set DatabaseHandler object,
  1249. * creating it if it does not already exist. This is for efficiency
  1250. * reasons, so every module or method does not need to keep creating
  1251. * databasehandler objects (and re-connecting to the database).
  1252. *
  1253. */
  1254. function get_global_database_handler() {
  1255. $db = @$GLOBALS["fp_global_database_handler"];
  1256. if (!is_object($db) || !is_object($db->pdo)) {
  1257. // Something isn't right, or it wasn't set correctly. Create a new connection.
  1258. $GLOBALS["fp_global_database_handler"] = new DatabaseHandler();
  1259. }
  1260. return $GLOBALS["fp_global_database_handler"];
  1261. }
  1262. /**
  1263. * Uses fp_add_message, but in this case, it also adds in the filename and line number
  1264. * which the message came from!
  1265. *
  1266. * Most useful for developers, tracking down issues. It's also only visible to administrators
  1267. * with the "view_fpm_debug" permission. So if you need to display a message only to admins,
  1268. * you can use fpm() as a shortcut.
  1269. *
  1270. * Note: If you attempt to fpm() an array
  1271. * or object with too many levels of nesting, it may run out of memory and your script will die.
  1272. */
  1273. function fpm($var, $max_levels = 20) {
  1274. if (!user_has_permission("view_fpm_debug")) {
  1275. return;
  1276. }
  1277. // Complex variable? Change it to print_r.
  1278. $str = $var;
  1279. if (is_array($str) || is_object($str)) {
  1280. $str = "<div class='fp-html-print-r-wrapper'>" . fp_html_print_r($str, "", 0, $max_levels) . "</div>";
  1281. }
  1282. $arr = debug_backtrace();
  1283. //pretty_print($arr);
  1284. $t = 0;
  1285. if (@$arr[1]['function'] == 'fpmct') {
  1286. $t = 1;
  1287. }
  1288. $file = $arr[$t]["file"];
  1289. if (strlen($file) > 70) {
  1290. $file = "..." . substr($file, strlen($file) - 70);
  1291. }
  1292. $str .= "<div class='fp-message-backtrace'>line {$arr[$t]["line"]}: $file</div>";
  1293. fp_add_message("&bull; " . $str);
  1294. }
  1295. /**
  1296. * Displays a depricated message on screen. Useful for tracking down
  1297. * when depricated functions are being used.
  1298. */
  1299. function depricated_message($str = "A depricated function has been called.") {
  1300. fpm($str);
  1301. fpm(debug_backtrace());
  1302. }
  1303. /**
  1304. * Convenience function, will use fp_debug_ct() to display
  1305. * a message, and the number of miliseconds since its last call.
  1306. */
  1307. function fpmct($val, $var = "") {
  1308. fpm(fp_debug_ct($val, $var));
  1309. }
  1310. /**
  1311. * Similar to print_r, this will return an HTML-friendly
  1312. * click-to-open system similar in design to Krumo.
  1313. */
  1314. function fp_html_print_r($var, $name = "", $cnt = 0, $max_levels = 20) {
  1315. $rtn = "";
  1316. if ($cnt > $max_levels) {
  1317. // Max levels deep. Deeper, and PHP might run
  1318. // out of memory or complain.
  1319. $rtn .= "<div class='fp-html-print-r-too-deep'>
  1320. " . t("Depth too great. To view deeper,
  1321. rephrase your fpm() call, starting at this depth.") . "
  1322. </div>";
  1323. return $rtn;
  1324. }
  1325. $type = gettype($var);
  1326. $rnd = md5(mt_rand(0, 999999) . microtime() . $type . $name);
  1327. if ($type == "boolean") {
  1328. $var = ($var == TRUE) ? "TRUE" : "FALSE";
  1329. }
  1330. $count = "";
  1331. if ($type == "string") {
  1332. $count = " - " . strlen($var) . " " . t("chars");
  1333. }
  1334. if ($type == "array" || $type == "object") {
  1335. if ($type == "array") {
  1336. $count = " - " . count($var) . " " . t("elements");
  1337. }
  1338. if ($type == "object") {
  1339. $count = " - " . get_class($var);
  1340. }
  1341. $rtn .= "<div class='fp-html-print-r-multi-row'>
  1342. <div class='fp-html-print-r-selector'
  1343. onClick='\$(\"#fp-html-print-r-var-value-$rnd\").toggle(\"medium\");'
  1344. >
  1345. <span class='fp-html-print-r-var-name'>$name</span>
  1346. <span class='fp-html-print-r-var-type'>($type$count)</span>
  1347. </div>
  1348. <div class='fp-html-print-r-var-value' id='fp-html-print-r-var-value-$rnd' style='display: none;'>";
  1349. foreach ($var as $key => $value) {
  1350. $rtn .= fp_html_print_r($value, $key, ($cnt + 1), $max_levels);
  1351. }
  1352. $rtn .= "</div>
  1353. </div>";
  1354. }
  1355. else if ($type == "string" && strlen($var) > 50) {
  1356. // If the variable is fairly long, we want to also make it a hide-to-show type field.
  1357. $rtn .= "<div class='fp-html-print-r-multi-row'>
  1358. <div
  1359. onClick='\$(\"#fp-html-print-r-var-value-$rnd\").toggle(\"medium\");'
  1360. >
  1361. <span class='fp-html-print-r-var-name'>$name</span>
  1362. <span class='fp-html-print-r-var-type'>($type$count)</span>
  1363. <span class='fp-html-print-r-var-value-abbr'>" . htmlentities(substr($var, 0, 50)) . "...</span>
  1364. </div>
  1365. <div class='fp-html-print-r-var-value' id='fp-html-print-r-var-value-$rnd' style='display: none;'>
  1366. ";
  1367. $rtn .= htmlentities($var);
  1368. $rtn .= "</div></div>";
  1369. }
  1370. else {
  1371. $html_val = $var;
  1372. if ($type != "resource") {
  1373. $html_val = htmlentities("" . $var);
  1374. }
  1375. $rtn .= "<div class='fp-html-print-r-single-row'>
  1376. <span class='fp-html-print-r-var-name'>$name</span>
  1377. <span class='fp-html-print-r-var-type'>($type$count)</span>
  1378. <span class='fp-html-print-r-var-value'>$html_val</span>
  1379. </div>";
  1380. }
  1381. return $rtn;
  1382. }
  1383. /**
  1384. * This is used usually when being viewed by a mobile device.
  1385. * It will shorten a catalog year range of 2008-2009 to just
  1386. * "08-09" or "2008-09" or even "09-2009".
  1387. *
  1388. * @param unknown_type $cat_range
  1389. */
  1390. function get_shorter_catalog_year_range($cat_range, $abbr_first = true, $abbr_second = true) {
  1391. $temp = explode("-", $cat_range);
  1392. $first = $temp[0];
  1393. $second = $temp[1];
  1394. if ($abbr_first) {
  1395. $first = substr($first, 2, 2);
  1396. }
  1397. if ($abbr_second) {
  1398. $second = substr($second, 2, 2);
  1399. }
  1400. return "$first-$second";
  1401. }
  1402. /**
  1403. * This will find and include the module in question, calling
  1404. * it's hook_init() function if it has one.
  1405. *
  1406. * Will return TRUE or FALSE for success or failure to include
  1407. * the module.
  1408. *
  1409. * If the use_module_path is set to some value, we will not attempt to use
  1410. * the setting for this module's path. Useful if we do not have the module in our
  1411. * modules table yet.
  1412. *
  1413. * Example use: include_module("course_search");
  1414. *
  1415. * @param string $module
  1416. */
  1417. function include_module($module, $bool_call_init = TRUE, $use_module_path = "") {
  1418. $system_path = trim($GLOBALS["fp_system_settings"]["file_system_path"]);
  1419. $module_path = $GLOBALS["fp_system_settings"]["modules"][$module]["path"];
  1420. if ($use_module_path != "") {
  1421. $module_path = $use_module_path;
  1422. }
  1423. if ($module_path != "") {
  1424. $path = $module_path . "/$module.module";
  1425. if (file_exists($system_path . "/" . $path)) {
  1426. require_once($system_path . "/" . $path);
  1427. }
  1428. else {
  1429. print "<br><b>Could not find module '$module' at '$system_path/$path'</b><br>";
  1430. }
  1431. // Now that we have included it, call the module's hook_init() method.
  1432. if ($bool_call_init) {
  1433. if (function_exists($module . "_init")) {
  1434. call_user_func($module . "_init");
  1435. }
  1436. }
  1437. return TRUE;
  1438. }
  1439. return FALSE;
  1440. }
  1441. /**
  1442. * Find and include the module's .install file, if it exists.
  1443. * Returns TRUE or FALSE if it was able to find & include the file.
  1444. */
  1445. function include_module_install($module, $path) {
  1446. $system_path = trim($GLOBALS["fp_system_settings"]["file_system_path"]);
  1447. $install_path = $path . "/$module.install";
  1448. if (file_exists($system_path . "/" . $install_path)) {
  1449. require_once($system_path . "/" . $install_path);
  1450. return TRUE;
  1451. }
  1452. return FALSE;
  1453. }
  1454. /**
  1455. * Creates a javascript "confirm" link, so when clicked it asks the user a question, then proceeds
  1456. * if they select OK. The main reason I want to do this is so I can pass the $question through
  1457. * my t() function. (do it when you call this function)
  1458. */
  1459. function fp_get_js_confirm_link($question, $action_if_yes, $link_text) {
  1460. $rtn = "";
  1461. $question = fp_reduce_whitespace($question);
  1462. $question = htmlentities($question, ENT_QUOTES);
  1463. $question = str_replace("\n", "\\n", $question);
  1464. $rtn .= "<a href='javascript: if(confirm(\"$question\")) { $action_if_yes; }'>$link_text</a>";
  1465. return $rtn;
  1466. }
  1467. /**
  1468. * Creates a javascript "prompt" link, which will ask the user a question.
  1469. *
  1470. * Similar to the fp_get_js_confirm_link function, but this is a prompt box
  1471. * which lets the user type in a response.
  1472. *
  1473. * @see fp_get_js_confirm_link
  1474. */
  1475. function fp_get_js_prompt_link($question, $default, $action_if_yes, $link_text) {
  1476. $rtn = "";
  1477. $question = fp_reduce_whitespace($question);
  1478. $question = htmlentities($question, ENT_QUOTES);
  1479. $question = str_replace("\n", "\\n", $question);
  1480. $rtn .= "<a href='javascript: var response = prompt(\"$question\", \"$default\");
  1481. if (response != null)
  1482. {
  1483. $action_if_yes ;
  1484. }
  1485. '>$link_text</a>";
  1486. return $rtn;
  1487. }
  1488. /**
  1489. * Creates a javascript "alert" link, which tells the user some message with javascript alert().
  1490. *
  1491. * Similar to the fp_get_js_confirm_link function, but this is a simple alert message,
  1492. * with no user input.
  1493. *
  1494. * @see fp_get_js_confirm_link
  1495. */
  1496. function fp_get_js_alert_link($message, $link_text = "", $extra_css_class = "") {
  1497. $rtn = "";
  1498. if ($link_text == "") {
  1499. $link_text = "[?]";
  1500. }
  1501. $message = fp_reduce_whitespace($message);
  1502. $message = str_replace("\n", "[NL]", $message);
  1503. $message = htmlentities($message, ENT_QUOTES);
  1504. $message = str_replace("&quot;", "\&quot;", $message);
  1505. $message = str_replace("[NL]", "\\n", $message);
  1506. $rtn .= "<a href='javascript: alert(\"" . $message . "\");' class='fp-alert-link $extra_css_class'>$link_text</a>";
  1507. return $rtn;
  1508. }
  1509. /**
  1510. * Simple helper function to reduce whitespace (like double-spaces)
  1511. *
  1512. * @param unknown_type $str
  1513. */
  1514. function fp_reduce_whitespace($str) {
  1515. // Cheap hack to get rid of whitespace
  1516. for ($t = 0; $t < 5; $t++) {
  1517. $str = str_replace(" ", " ", $str);
  1518. $str = str_replace("\n ", "\n", $str);
  1519. }
  1520. return $str;
  1521. }
  1522. /**
  1523. * Does the user have the specified role?
  1524. */
  1525. function user_has_role($role) {
  1526. global $user;
  1527. // Admin always = TRUE
  1528. if ($user->id == 1) return TRUE;
  1529. // Check for other users...
  1530. if (in_array($role, $user->roles)) return TRUE;
  1531. return FALSE;
  1532. }
  1533. /**
  1534. * Returns TRUE or FALSE if the logged in user has access based on the
  1535. * permission supplied.
  1536. *
  1537. * @param String $permission
  1538. */
  1539. function user_has_permission($permission) {
  1540. global $user;
  1541. //fpm("checking permission $permission");
  1542. // If the user is admin (id == 1) then they always have access.
  1543. if ($user->id == 1) return TRUE;
  1544. // Otherwise, simply check their permissions array.
  1545. if (in_array($permission, $user->permissions)) {
  1546. return TRUE;
  1547. }
  1548. return FALSE;
  1549. }
  1550. /**
  1551. * This looks at the global termIDStructure setting and returns back
  1552. * an array of only term suffixes (like 40, 60, mm, etc).
  1553. *
  1554. */
  1555. function get_term_id_suffixes() {
  1556. $rtn = array();
  1557. $temp = $GLOBALS["fp_system_settings"]["term_id_structure"];
  1558. $structures = explode("\n", $temp);
  1559. foreach ($structures as $structure) {
  1560. $tokens = explode(",", $structure);
  1561. $term_def = trim($tokens[0]);
  1562. // Get rid of the replacement pattern.
  1563. // Looks like: [Y4]40. We want the 40.
  1564. // Simply explode on "]"
  1565. $temp = explode("]", $term_def);
  1566. $rtn[] = trim($temp[1]);
  1567. }
  1568. return $rtn;
  1569. }
  1570. /**
  1571. * This function will read through all the modules' permissions and
  1572. * return back an array. Specifically, it retrieves arrays from each
  1573. * modules' hook_perm() function.
  1574. *
  1575. */
  1576. function get_modules_permissions() {
  1577. $rtn = array();
  1578. foreach ($GLOBALS["fp_system_settings"]["modules"] as $module => $value) {
  1579. if (isset($value["disabled"]) && $value["disabled"] == "yes") {
  1580. // Module is not enabled. Skip it.
  1581. continue;
  1582. }
  1583. if (function_exists($module . "_perm")) {
  1584. $rtn[$module][] = call_user_func($module . "_perm");
  1585. }
  1586. }
  1587. return $rtn;
  1588. }
  1589. /**
  1590. * Similar to get_modules_permissions, this will scan through all installed
  1591. * modules' hook_menu() functions, and assemble an array which is sorted
  1592. * by "location" and then by "weight".
  1593. *
  1594. */
  1595. function get_modules_menus() {
  1596. $menus = array();
  1597. foreach ($GLOBALS["fp_system_settings"]["modules"] as $module => $value) {
  1598. if (isset($value["disabled"]) && $value["disabled"] == "yes") {
  1599. // Module is not enabled. Skip it.
  1600. continue;
  1601. }
  1602. if (function_exists($module . "_menu")) {
  1603. $menus[] = call_user_func($module . "_menu");
  1604. }
  1605. }
  1606. // Let's re-order based on weight...
  1607. // Convert to a single dimensional array for easier sorting.
  1608. $temp = array();
  1609. foreach ($menus as $c => $value) {
  1610. foreach ($menus[$c] as $d => $menu_data) {
  1611. $w = $menu_data["weight"];
  1612. if ($w == "") $w = "0";
  1613. // We need to front-pad $w with zeros, so it is the same length
  1614. // for every entry. Otherwise it will not sort correctly.
  1615. $w = fp_number_pad($w, 10);
  1616. $temp[] = "$w~~$c~~$d";
  1617. }
  1618. }
  1619. //var_dump($temp);
  1620. // Now, sort $temp...
  1621. sort($temp);
  1622. //var_dump($temp);
  1623. // Now, go back through $temp and get our new array...
  1624. $new_array = array();
  1625. foreach ($temp as $t) {
  1626. $vals = explode("~~", $t);
  1627. $c = $vals[1];
  1628. $d = $vals[2];
  1629. // Place them into subarrays indexed by location
  1630. $new_array[$menus[$c][$d]["location"]][] = $menus[$c][$d];
  1631. }
  1632. return $new_array;
  1633. }
  1634. /**
  1635. * Simple function to left padd numbers with 0's.
  1636. * 1 becomes 001
  1637. * 20 becomes 020
  1638. * and so on.
  1639. *
  1640. * @param int $number
  1641. * @param int $n
  1642. * @return String
  1643. */
  1644. function fp_number_pad($number, $len) {
  1645. return str_pad((int) $number, $len, "0", STR_PAD_LEFT);
  1646. }
  1647. /**
  1648. * This simple function will take a number and truncate the number of decimals
  1649. * to the requested places. This can be used in place of number_format(), which *rounds*
  1650. * numbers.
  1651. *
  1652. * For example, number_format(1.99999, 2) gives you 2.00.
  1653. * But THIS function gives:
  1654. * fp_truncate_decimals(1.999999, 2) = 1.99
  1655. *
  1656. *
  1657. * @param unknown_type $places
  1658. */
  1659. function fp_truncate_decimals($num, $places = 2) {
  1660. // does $num contain a .? If not, add it on.
  1661. if (!strstr("" . $num, ".")) {
  1662. $num .= ".0";
  1663. }
  1664. // Break it by .
  1665. $temp = explode (".", "" . $num);
  1666. // Get just the decimals and trim 'em
  1667. $decimals = trim(substr($temp[1], 0, $places));
  1668. if (strlen($decimals) < $places) {
  1669. // Padd with zeros on the right!
  1670. $decimals = str_pad($decimals, $places, "0", STR_PAD_RIGHT);
  1671. }
  1672. $new_num = $temp[0] . "." . $decimals;
  1673. return $new_num;
  1674. }
  1675. /**
  1676. * Shortcut to fp_debug_current_time_millis()
  1677. *
  1678. * @see fp_debug_current_time_millis()
  1679. *
  1680. * @param unknown_type $debug_val
  1681. * @param unknown_type $var
  1682. * @return unknown
  1683. */
  1684. function fp_debug_ct($debug_val = "", $var = "")
  1685. { // Shortcut to the other function.
  1686. return fp_debug_current_time_millis($debug_val, false, $var);
  1687. }
  1688. /**
  1689. * When called repeatedly, this function will display a message along with a milisecond count
  1690. * out to the side. Very useful for developers to time function calls or queries, to see how long they
  1691. * are taking.
  1692. *
  1693. * For example:
  1694. * fp_debug_ct("starting query");
  1695. * db_query(".........") // whatever
  1696. * fp_debug_ct("finished query");
  1697. *
  1698. * On screen, that would display our messages, with time values, so we can see how many milliseconds
  1699. * it took to execute between calls of fp_debug_ct().
  1700. *
  1701. * @param String $debug_val
  1702. * The message to display on screen.
  1703. * @param boolean $show_current_time
  1704. * Should we display the current time as well?
  1705. * @param String $var
  1706. * Optional. Include a variable name so you can have more than one timer running
  1707. * at the same time.
  1708. * @return unknown
  1709. */
  1710. function fp_debug_current_time_millis($debug_val = "", $show_current_time = true, $var = "")
  1711. {
  1712. // Display the current time in milliseconds, and, if available,
  1713. // show how many milliseconds its been since the last time
  1714. // this function was called. This helps programmers tell how
  1715. // long a particular function takes to run. Just place a call
  1716. // to this function before and after the function call.
  1717. $rtn = "";
  1718. $debug_string = $debug_val;
  1719. if (is_array($debug_val) || is_object($debug_val)) {
  1720. $debug_string = "<pre>" . print_r($debug_val, true) . "</pre>";
  1721. }
  1722. $last_time = @($GLOBALS["current_time_millis" . $var]) * 1; //*1 forces numeric
  1723. $cur_time = microtime(true) * 1000;
  1724. $debug_string = "<span style='color:red;'>DEBUG:</span>
  1725. <span style='color:green;'>$debug_string</span>";
  1726. $rtn .= "<div style='background-color: white;'>$debug_string";
  1727. if ($last_time > 1)
  1728. {
  1729. $diff = round($cur_time - $last_time,2);
  1730. $rtn .= "<span style='color: blue;'> ($diff" . t("ms since last call") . "</span>";
  1731. } else {
  1732. // Start of clock...
  1733. $rtn .= "<span style='color: blue;'> --- </span>";
  1734. }
  1735. $rtn .= "</div>";
  1736. $GLOBALS["current_time_millis" . $var] = $cur_time;
  1737. $GLOBALS["current_time_millis"] = $cur_time;
  1738. return $rtn;
  1739. }