misc.inc

This file contains misc functions for FlightPath

Functions & methods

NameDescription
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!
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_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_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_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.
tEventually, this function will be used to translate strings. For now, just pass through. Allows variable replacements. Use like this: t("@name's blob", array("@name" => "Richard")); or simply t("My…
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?

File

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