PageRenderTime 58ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/MantisBT/core/string_api.php

https://bitbucket.org/crypticrod/sr_wp_code
PHP | 899 lines | 449 code | 106 blank | 344 comment | 94 complexity | 5072f0c28b1de4ed06c2e7b06865fc07 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, LGPL-2.1, GPL-3.0, LGPL-2.0, AGPL-3.0
  1. <?php
  2. # MantisBT - a php based bugtracking system
  3. # MantisBT is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 2 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # MantisBT is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with MantisBT. If not, see <http://www.gnu.org/licenses/>.
  15. /**
  16. * @package CoreAPI
  17. * @subpackage StringProcessingAPI
  18. * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
  19. * @copyright Copyright (C) 2002 - 2011 MantisBT Team - mantisbt-dev@lists.sourceforge.net
  20. * @link http://www.mantisbt.org
  21. */
  22. /**
  23. * requires bug_api
  24. */
  25. require_once( 'bug_api.php' );
  26. /**
  27. * requires user_pref_api
  28. */
  29. require_once( 'user_pref_api.php' );
  30. $g_cache_html_valid_tags = '';
  31. $g_cache_html_valid_tags_single_line = '';
  32. /**
  33. * Preserve spaces at beginning of lines.
  34. * Lines must be separated by \n rather than <br />
  35. * @param string $p_string
  36. * @return string
  37. */
  38. function string_preserve_spaces_at_bol( $p_string ) {
  39. $lines = explode( "\n", $p_string );
  40. $line_count = count( $lines );
  41. for( $i = 0;$i < $line_count;$i++ ) {
  42. $count = 0;
  43. $prefix = '';
  44. $t_char = utf8_substr( $lines[$i], $count, 1 );
  45. $spaces = 0;
  46. while(( $t_char == ' ' ) || ( $t_char == "\t" ) ) {
  47. if( $t_char == ' ' ) {
  48. $spaces++;
  49. } else {
  50. $spaces += 4;
  51. }
  52. // 1 tab = 4 spaces, can be configurable.
  53. $count++;
  54. $t_char = utf8_substr( $lines[$i], $count, 1 );
  55. }
  56. for( $j = 0;$j < $spaces;$j++ ) {
  57. $prefix .= '&#160;';
  58. }
  59. $lines[$i] = $prefix . utf8_substr( $lines[$i], $count );
  60. }
  61. return implode( "\n", $lines );
  62. }
  63. /**
  64. * Prepare a string to be printed without being broken into multiple lines
  65. * @param string $p_string
  66. * @return string
  67. */
  68. function string_no_break( $p_string ) {
  69. if( strpos( $p_string, ' ' ) !== false ) {
  70. return '<span class="nowrap">' . $p_string . "</span>";
  71. } else {
  72. return $p_string;
  73. }
  74. }
  75. /**
  76. * Similar to nl2br, but fixes up a problem where new lines are doubled between
  77. * html pre tags.
  78. * additionally, wrap the text an $p_wrap character intervals if the config is set
  79. * @param string $p_string
  80. * @param int $p_wrap
  81. * @return string
  82. */
  83. function string_nl2br( $p_string, $p_wrap = 100 ) {
  84. $output = '';
  85. $pieces = preg_split( '/(<pre[^>]*>.*?<\/pre>)/is', $p_string, -1, PREG_SPLIT_DELIM_CAPTURE );
  86. if( isset( $pieces[1] ) ) {
  87. foreach( $pieces as $piece ) {
  88. if( preg_match( '/(<pre[^>]*>.*?<\/pre>)/is', $piece ) ) {
  89. $piece = preg_replace( "/<br[^>]*?>/", '', $piece );
  90. # @@@ thraxisp - this may want to be replaced by html_entity_decode (or equivalent)
  91. # if other encoded characters are a problem
  92. $piece = preg_replace( '/&#160;/', ' ', $piece );
  93. if( ON == config_get( 'wrap_in_preformatted_text' ) ) {
  94. $output .= preg_replace( '/([^\n]{' . $p_wrap . ',}?[\s]+)(?!<\/pre>)/', "$1\n", $piece );
  95. } else {
  96. $output .= $piece;
  97. }
  98. } else {
  99. $output .= nl2br( $piece );
  100. }
  101. }
  102. return $output;
  103. } else {
  104. return nl2br( $p_string );
  105. }
  106. }
  107. /**
  108. * Prepare a multiple line string for display to HTML
  109. * @param string $p_string
  110. * @return string
  111. */
  112. function string_display( $p_string ) {
  113. $t_data = event_signal( 'EVENT_DISPLAY_TEXT', $p_string, true );
  114. return $t_data;
  115. }
  116. /**
  117. * Prepare a single line string for display to HTML
  118. * @param string $p_string
  119. * @return string
  120. */
  121. function string_display_line( $p_string ) {
  122. $t_data = event_signal( 'EVENT_DISPLAY_TEXT', $p_string, false );
  123. return $t_data;
  124. }
  125. /**
  126. * Prepare a string for display to HTML and add href anchors for URLs, emails,
  127. * bug references, and cvs references
  128. * @param string $p_string
  129. * @return string
  130. */
  131. function string_display_links( $p_string ) {
  132. $t_data = event_signal( 'EVENT_DISPLAY_FORMATTED', $p_string, true );
  133. return $t_data;
  134. }
  135. /**
  136. * Prepare a single line string for display to HTML and add href anchors for
  137. * URLs, emails, bug references, and cvs references
  138. * @param string $p_string
  139. * @return string
  140. */
  141. function string_display_line_links( $p_string ) {
  142. $t_data = event_signal( 'EVENT_DISPLAY_FORMATTED', $p_string, false );
  143. return $t_data;
  144. }
  145. /**
  146. * Prepare a string for display in rss
  147. * @param string
  148. * @return string
  149. */
  150. function string_rss_links( $p_string ) {
  151. # rss can not start with &#160; which spaces will be replaced into by string_display().
  152. $t_string = trim( $p_string );
  153. $t_string = event_signal( 'EVENT_DISPLAY_RSS', $t_string );
  154. # another escaping to escape the special characters created by the generated links
  155. return string_html_specialchars( $t_string );
  156. }
  157. /**
  158. * Prepare a string for plain text display in email
  159. * @param string $p_string
  160. * @return string
  161. */
  162. function string_email( $p_string ) {
  163. return string_strip_hrefs( $p_string );
  164. }
  165. /**
  166. * Prepare a string for plain text display in email and add URLs for bug
  167. * links and cvs links
  168. * @param string
  169. * @return string
  170. */
  171. function string_email_links( $p_string ) {
  172. return event_signal( 'EVENT_DISPLAY_EMAIL', $p_string );
  173. }
  174. # --------------------
  175. # Process a string for display in a textarea box
  176. /**
  177. * @todo function documentation
  178. * @param string
  179. * @return string
  180. */
  181. function string_textarea( $p_string ) {
  182. return string_html_specialchars( $p_string );
  183. }
  184. /**
  185. * Process a string for display in a text box
  186. * @param string
  187. * @return string
  188. */
  189. function string_attribute( $p_string ) {
  190. return string_html_specialchars( $p_string );
  191. }
  192. /**
  193. * Process a string for inclusion in a URL as a GET parameter
  194. * @param string $p_string
  195. * @return string
  196. */
  197. function string_url( $p_string ) {
  198. return rawurlencode( $p_string );
  199. }
  200. /**
  201. * validate the url as part of this site before continuing
  202. * @param string $p_url
  203. * @param bool $p_return_absolute
  204. * @return string
  205. */
  206. function string_sanitize_url( $p_url, $p_return_absolute = false ) {
  207. $t_url = strip_tags( urldecode( $p_url ) );
  208. $t_path = rtrim( config_get( 'path' ), '/' );
  209. $t_short_path = rtrim( config_get( 'short_path' ), '/' );
  210. $t_pattern = '(?:/*(?P<script>[^\?#]*))(?:\?(?P<query>[^#]*))?(?:#(?P<anchor>[^#]*))?';
  211. # Break the given URL into pieces for path, script, query, and anchor
  212. $t_type = 0;
  213. if ( preg_match( '@^(?P<path>' . preg_quote( $t_path, '@' ) . ')' . $t_pattern . '$@', $t_url, $t_matches ) ) {
  214. $t_type = 1;
  215. } else if ( preg_match( '@^(?P<path>' . preg_quote( $t_short_path, '@' ) . ')' . $t_pattern . '$@', $t_url, $t_matches ) ) {
  216. $t_type = 2;
  217. } else if ( preg_match( '@^(?P<path>)' . $t_pattern . '$@', $t_url, $t_matches ) ) {
  218. $t_type = 3;
  219. }
  220. # Check for URL's pointing to other domains
  221. if ( 0 == $t_type || empty( $t_matches['script'] ) ||
  222. 3 == $t_type && preg_match( '@(?:[^:]*)?://@', $t_url ) > 0 ) {
  223. return ( $p_return_absolute ? $t_path . '/' : '' ) . 'index.php';
  224. }
  225. # Start extracting regex matches
  226. $t_script = $t_matches['script'];
  227. $t_script_path = $t_matches['path'];
  228. # Clean/encode query params
  229. $t_query = '';
  230. if ( isset( $t_matches['query'] ) ) {
  231. $t_pairs = array();
  232. parse_str( html_entity_decode( $t_matches['query'] ), $t_pairs );
  233. $t_clean_pairs = array();
  234. foreach( $t_pairs as $t_key => $t_value ) {
  235. if ( is_array( $t_value ) ) {
  236. foreach( $t_value as $t_value_each ) {
  237. $t_clean_pairs[] .= rawurlencode( $t_key ) . '[]=' . rawurlencode( $t_value_each );
  238. }
  239. } else {
  240. $t_clean_pairs[] = rawurlencode( $t_key ) . '=' . rawurlencode( $t_value );
  241. }
  242. }
  243. if ( !empty( $t_clean_pairs ) ) {
  244. $t_query = '?' . join( '&', $t_clean_pairs );
  245. }
  246. }
  247. # encode link anchor
  248. $t_anchor = '';
  249. if ( isset( $t_matches['anchor'] ) ) {
  250. $t_anchor = '#' . rawurlencode( $t_matches['anchor'] );
  251. }
  252. # Return an appropriate re-combined URL string
  253. if ( $p_return_absolute ) {
  254. return $t_path . '/' . $t_script . $t_query . $t_anchor;
  255. } else {
  256. return ( !empty( $t_script_path ) ? $t_script_path . '/' : '' ) . $t_script . $t_query . $t_anchor;
  257. }
  258. }
  259. /**
  260. * process the $p_string and convert filenames in the format
  261. * cvs:filename.ext or cvs:filename.ext:n.nn to a html link
  262. * if $p_include_anchor is true, include an <a href="..."> tag,
  263. * otherwise, just insert the URL as text
  264. * @param string $p_string
  265. * @param bool $p_include_anchor
  266. * @return string
  267. */
  268. function string_process_cvs_link( $p_string, $p_include_anchor = true ) {
  269. $t_cvs_web = config_get( 'cvs_web' );
  270. if( $p_include_anchor ) {
  271. $t_replace_with = '[CVS] <a href="' . $t_cvs_web . '\\1?rev=\\4" target="_new">\\1</a>\\5';
  272. } else {
  273. $t_replace_with = '[CVS] ' . $t_cvs_web . '\\1?rev=\\4\\5';
  274. }
  275. return preg_replace( '/cvs:([^\.\s:,\?!<]+(\.[^\.\s:,\?!<]+)*)(:)?(\d\.[\d\.]+)?([\W\s])?/i', $t_replace_with, $p_string );
  276. }
  277. $string_process_bug_link_callback = array();
  278. /**
  279. * Process $p_string, looking for bug ID references and creating bug view
  280. * links for them.
  281. *
  282. * Returns the processed string.
  283. *
  284. * If $p_include_anchor is true, include the href tag, otherwise just insert
  285. * the URL
  286. *
  287. * The bug tag ('#' by default) must be at the beginning of the string or
  288. * preceeded by a character that is not a letter, a number or an underscore
  289. *
  290. * if $p_include_anchor = false, $p_fqdn is ignored and assumed to true.
  291. * @param string $p_string
  292. * @param bool $p_include_anchor
  293. * @param bool $p_detail_info
  294. * @param bool $p_fqdn
  295. * @return string
  296. */
  297. function string_process_bug_link( $p_string, $p_include_anchor = true, $p_detail_info = true, $p_fqdn = false ) {
  298. global $string_process_bug_link_callback;
  299. $t_tag = config_get( 'bug_link_tag' );
  300. # bail if the link tag is blank
  301. if( '' == $t_tag || $p_string == '' ) {
  302. return $p_string;
  303. }
  304. if( !isset( $string_process_bug_link_callback[$p_include_anchor][$p_detail_info][$p_fqdn] ) ) {
  305. if( $p_include_anchor ) {
  306. $string_process_bug_link_callback[$p_include_anchor][$p_detail_info][$p_fqdn] = create_function( '$p_array', '
  307. if ( bug_exists( (int)$p_array[2] ) && access_has_bug_level( VIEWER, (int)$p_array[2] ) ) {
  308. return $p_array[1] . string_get_bug_view_link( (int)$p_array[2], null, ' . ( $p_detail_info ? 'true' : 'false' ) . ', ' . ( $p_fqdn ? 'true' : 'false' ) . ');
  309. } else {
  310. return $p_array[0];
  311. }
  312. ' );
  313. } else {
  314. $string_process_bug_link_callback[$p_include_anchor][$p_detail_info][$p_fqdn] = create_function( '$p_array', '
  315. # We might as well create the link here even if the bug
  316. # doesnt exist. In the case above we dont want to do
  317. # the summary lookup on a non-existant bug. But here, we
  318. # can create the link and by the time it is clicked on, the
  319. # bug may exist.
  320. return $p_array[1] . string_get_bug_view_url_with_fqdn( (int)$p_array[2], null );
  321. ' );
  322. }
  323. }
  324. $p_string = preg_replace_callback( '/(^|[^\w&])' . preg_quote( $t_tag, '/' ) . '(\d+)\b/', $string_process_bug_link_callback[$p_include_anchor][$p_detail_info][$p_fqdn], $p_string );
  325. return $p_string;
  326. }
  327. $string_process_bugnote_link_callback = array();
  328. /**
  329. * Process $p_string, looking for bugnote ID references and creating bug view
  330. * links for them.
  331. *
  332. * Returns the processed string.
  333. *
  334. * If $p_include_anchor is true, include the href tag, otherwise just insert
  335. * the URL
  336. *
  337. * The bugnote tag ('~' by default) must be at the beginning of the string or
  338. * preceeded by a character that is not a letter, a number or an underscore
  339. *
  340. * if $p_include_anchor = false, $p_fqdn is ignored and assumed to true.
  341. * @param string $p_string
  342. * @param bool $p_include_anchor
  343. * @param bool $p_detail_info
  344. * @param bool $p_fqdn
  345. * @return string
  346. */
  347. function string_process_bugnote_link( $p_string, $p_include_anchor = true, $p_detail_info = true, $p_fqdn = false ) {
  348. global $string_process_bugnote_link_callback;
  349. $t_tag = config_get( 'bugnote_link_tag' );
  350. # bail if the link tag is blank
  351. if( '' == $t_tag || $p_string == '' ) {
  352. return $p_string;
  353. }
  354. if( !isset( $string_process_bugnote_link_callback[$p_include_anchor][$p_detail_info][$p_fqdn] ) ) {
  355. if( $p_include_anchor ) {
  356. $string_process_bugnote_link_callback[$p_include_anchor][$p_detail_info][$p_fqdn] = create_function( '$p_array', '
  357. if ( bugnote_exists( (int)$p_array[2] ) ) {
  358. $t_bug_id = bugnote_get_field( (int)$p_array[2], \'bug_id\' );
  359. $g_project_override = bug_get_field( $t_bug_id, \'project_id\' );
  360. if ( bug_exists( $t_bug_id ) && ( access_compare_level( user_get_access_level( auth_get_current_user_id(), bug_get_field( $t_bug_id, \'project_id\' ) ), config_get( \'private_bugnote_threshold\' ) ) || ( bugnote_get_field( (int)$p_array[2], \'reporter_id\' ) == auth_get_current_user_id() ) || bugnote_get_field( (int)$p_array[2], \'view_state\' ) == VS_PUBLIC ) ) {
  361. $g_project_override = null;
  362. return $p_array[1] . string_get_bugnote_view_link( $t_bug_id, (int)$p_array[2], null, ' . ( $p_detail_info ? 'true' : 'false' ) . ', ' . ( $p_fqdn ? 'true' : 'false' ) . ' );
  363. } else {
  364. $g_project_override = null;
  365. return $p_array[0];
  366. }
  367. } else {
  368. return $p_array[0];
  369. }
  370. ' );
  371. } else {
  372. $string_process_bugnote_link_callback[$p_include_anchor][$p_detail_info][$p_fqdn] = create_function( '$p_array', '
  373. # We might as well create the link here even if the bug
  374. # doesnt exist. In the case above we dont want to do
  375. # the summary lookup on a non-existant bug. But here, we
  376. # can create the link and by the time it is clicked on, the
  377. # bug may exist.
  378. $t_bug_id = bugnote_get_field( (int)$p_array[2], \'bug_id\' );
  379. if ( bug_exists( $t_bug_id ) ) {
  380. return $p_array[1] . string_get_bugnote_view_url_with_fqdn( $t_bug_id, (int)$p_array[2], null );
  381. } else {
  382. return $p_array[0];
  383. }
  384. ' );
  385. }
  386. }
  387. $p_string = preg_replace_callback( '/(^|[^\w])' . preg_quote( $t_tag, '/' ) . '(\d+)\b/', $string_process_bugnote_link_callback[$p_include_anchor][$p_detail_info][$p_fqdn], $p_string );
  388. return $p_string;
  389. }
  390. /**
  391. * Detect URLs and email addresses in the string and replace them with href anchors
  392. * @param string $p_string
  393. * @return string
  394. */
  395. function string_insert_hrefs( $p_string ) {
  396. static $s_url_regex = null;
  397. if( !config_get( 'html_make_links' ) ) {
  398. return $p_string;
  399. }
  400. $t_change_quotes = false;
  401. if( ini_get_bool( 'magic_quotes_sybase' ) && function_exists( 'ini_set' ) ) {
  402. $t_change_quotes = true;
  403. ini_set( 'magic_quotes_sybase', false );
  404. }
  405. # Find any URL in a string and replace it by a clickable link
  406. if ( is_null( $s_url_regex ) ) {
  407. # %2A notation in url's
  408. $t_url_hex = '%[[:digit:]A-Fa-f]{2}';
  409. # valid set of characters that may occur in url scheme. Note: - should be first (A-F != -AF).
  410. $t_url_valid_chars = '-_.,!~*\';\/?%^\\\\:@&={\|}+$#[:alnum:]\pL';
  411. $t_url_chars = "(?:${t_url_hex}|[${t_url_valid_chars}\(\)\[\]])";
  412. $t_url_chars2 = "(?:${t_url_hex}|[${t_url_valid_chars}])";
  413. $t_url_chars_in_brackets = "(?:${t_url_hex}|[${t_url_valid_chars}\(\)])";
  414. $t_url_chars_in_parens = "(?:${t_url_hex}|[${t_url_valid_chars}\[\]])";
  415. $t_url_part1 = "${t_url_chars}";
  416. $t_url_part2 = "(?:\(${t_url_chars_in_parens}*\)|\[${t_url_chars_in_brackets}*\]|${t_url_chars2})";
  417. $s_url_regex = "/(([[:alpha:]][-+.[:alnum:]]*):\/\/(${t_url_part1}*?${t_url_part2}+))/sue";
  418. }
  419. $p_string = preg_replace( $s_url_regex, "'<a href=\"'.rtrim('\\1','.').'\">\\1</a> [<a href=\"'.rtrim('\\1','.').'\" target=\"_blank\">^</a>]'", $p_string );
  420. if( $t_change_quotes ) {
  421. ini_set( 'magic_quotes_sybase', true );
  422. }
  423. $p_string = preg_replace( email_regex_simple(), '<a href="mailto:\0">\0</a>', $p_string );
  424. return $p_string;
  425. }
  426. /**
  427. * Detect href anchors in the string and replace them with URLs and email addresses
  428. * @param string $p_string
  429. * @return string
  430. */
  431. function string_strip_hrefs( $p_string ) {
  432. # First grab mailto: hrefs. We don't care whether the URL is actually
  433. # correct - just that it's inside an href attribute.
  434. $p_string = preg_replace( '/<a\s[^\>]*href="mailto:([^\"]+)"[^\>]*>[^\<]*<\/a>/si', '\1', $p_string );
  435. # Then grab any other href
  436. $p_string = preg_replace( '/<a\s[^\>]*href="([^\"]+)"[^\>]*>[^\<]*<\/a>/si', '\1', $p_string );
  437. return $p_string;
  438. }
  439. /**
  440. * This function looks for text with htmlentities
  441. * like &lt;b&gt; and converts is into corresponding
  442. * html < b > tag based on the configuration presets
  443. * @param string $p_string
  444. * @param bool $p_multiline
  445. * @return string
  446. */
  447. function string_restore_valid_html_tags( $p_string, $p_multiline = true ) {
  448. global $g_cache_html_valid_tags_single_line, $g_cache_html_valid_tags;
  449. $tags = '';
  450. if( is_blank(( $p_multiline ? $g_cache_html_valid_tags : $g_cache_html_valid_tags_single_line ) ) ) {
  451. $t_html_valid_tags = config_get( $p_multiline ? 'html_valid_tags' : 'html_valid_tags_single_line' );
  452. if( OFF === $t_html_valid_tags || is_blank( $t_html_valid_tags ) ) {
  453. return $p_string;
  454. }
  455. $tags = explode( ',', $t_html_valid_tags );
  456. foreach( $tags as $key => $value ) {
  457. if( !is_blank( $value ) ) {
  458. $tags[$key] = trim( $value );
  459. }
  460. }
  461. $tags = implode( '|', $tags );
  462. if( $p_multiline ) {
  463. $g_cache_html_valid_tags = $tags;
  464. } else {
  465. $g_cache_html_valid_tags_single_line = $tags;
  466. }
  467. } else {
  468. $tags = ( $p_multiline ? $g_cache_html_valid_tags : $g_cache_html_valid_tags_single_line );
  469. }
  470. $p_string = preg_replace( '/&lt;(' . $tags . ')\s*&gt;/ui', '<\\1>', $p_string );
  471. $p_string = preg_replace( '/&lt;\/(' . $tags . ')\s*&gt;/ui', '</\\1>', $p_string );
  472. $p_string = preg_replace( '/&lt;(' . $tags . ')\s*\/&gt;/ui', '<\\1 />', $p_string );
  473. return $p_string;
  474. }
  475. /**
  476. * return the name of a bug page for the user
  477. * account for the user preference and site override
  478. * $p_action should be something like 'view', 'update', or 'report'
  479. * If $p_user_id is null or not specified, use the current user * @param string $p_action
  480. * @param string $p_action
  481. * @param int $p_user_id
  482. * @return string
  483. */
  484. function string_get_bug_page( $p_action, $p_user_id = null ) {
  485. if ( $p_action == 'view' ) {
  486. return 'bug_view_page.php';
  487. }
  488. if ( $p_action == 'update' ) {
  489. return 'bug_update_page.php';
  490. }
  491. if ( $p_action == 'report' ) {
  492. return 'bug_report_page.php';
  493. }
  494. trigger_error( ERROR_GENERIC, ERROR );
  495. }
  496. /**
  497. * return an href anchor that links to a bug VIEW page for the given bug
  498. * account for the user preference and site override
  499. * @param int $p_bug_id
  500. * @param int $p_user_id
  501. * @param bool $p_detail_info
  502. * @param bool $p_fqdn
  503. * @return string
  504. */
  505. function string_get_bug_view_link( $p_bug_id, $p_user_id = null, $p_detail_info = true, $p_fqdn = false ) {
  506. if( bug_exists( $p_bug_id ) ) {
  507. $t_link = '<a href="';
  508. if( $p_fqdn ) {
  509. $t_link .= config_get_global( 'path' );
  510. } else {
  511. $t_link .= config_get_global( 'short_path' );
  512. }
  513. $t_link .= string_get_bug_view_url( $p_bug_id, $p_user_id ) . '"';
  514. if( $p_detail_info ) {
  515. $t_summary = string_attribute( bug_get_field( $p_bug_id, 'summary' ) );
  516. $t_status = string_attribute( get_enum_element( 'status', bug_get_field( $p_bug_id, 'status' ) ) );
  517. $t_link .= ' title="[' . $t_status . '] ' . $t_summary . '"';
  518. $t_resolved = bug_get_field( $p_bug_id, 'status' ) >= config_get( 'bug_resolved_status_threshold' );
  519. if( $t_resolved ) {
  520. $t_link .= ' class="resolved"';
  521. }
  522. }
  523. $t_link .= '>' . bug_format_id( $p_bug_id ) . '</a>';
  524. } else {
  525. $t_link = bug_format_id( $p_bug_id );
  526. }
  527. return $t_link;
  528. }
  529. /**
  530. * return an href anchor that links to a bug VIEW page for the given bug
  531. * account for the user preference and site override
  532. * @param int $p_bug_id
  533. * @param int $p_bugnote_id
  534. * @param int $p_user_id
  535. * @param bool $p_detail_info
  536. * @param bool $p_fqdn
  537. * @return string
  538. */
  539. function string_get_bugnote_view_link( $p_bug_id, $p_bugnote_id, $p_user_id = null, $p_detail_info = true, $p_fqdn = false ) {
  540. $t_bug_id = (int)$p_bug_id;
  541. if( bug_exists( $t_bug_id ) && bugnote_exists( $p_bugnote_id ) ) {
  542. $t_link = '<a href="';
  543. if( $p_fqdn ) {
  544. $t_link .= config_get_global( 'path' );
  545. } else {
  546. $t_link .= config_get_global( 'short_path' );
  547. }
  548. $t_link .= string_get_bugnote_view_url( $p_bug_id, $p_bugnote_id, $p_user_id ) . '"';
  549. if( $p_detail_info ) {
  550. $t_reporter = string_attribute( user_get_name( bugnote_get_field( $p_bugnote_id, 'reporter_id' ) ) );
  551. $t_update_date = string_attribute( date( config_get( 'normal_date_format' ), ( bugnote_get_field( $p_bugnote_id, 'last_modified' ) ) ) );
  552. $t_link .= ' title="' . bug_format_id( $t_bug_id ) . ': [' . $t_update_date . '] ' . $t_reporter . '"';
  553. }
  554. $t_link .= '>' . bug_format_id( $t_bug_id ) . ':' . bugnote_format_id( $p_bugnote_id ) . '</a>';
  555. } else {
  556. $t_link = bugnote_format_id( $t_bug_id ) . ':' . bugnote_format_id( $p_bugnote_id );
  557. }
  558. return $t_link;
  559. }
  560. /**
  561. * return the name and GET parameters of a bug VIEW page for the given bug
  562. * @param int $p_bug_id
  563. * @return string
  564. */
  565. function string_get_bug_view_url( $p_bug_id ) {
  566. return 'view.php?id=' . $p_bug_id;
  567. }
  568. /**
  569. * return the name and GET parameters of a bug VIEW page for the given bug
  570. * @param int $p_bug_id
  571. * @param int $p_bugnote_id
  572. * @return string
  573. */
  574. function string_get_bugnote_view_url( $p_bug_id, $p_bugnote_id ) {
  575. return 'view.php?id=' . $p_bug_id . '#c' . $p_bugnote_id;
  576. }
  577. /**
  578. * return the name and GET parameters of a bug VIEW page for the given bug
  579. * account for the user preference and site override
  580. * The returned url includes the fully qualified domain, hence it is suitable to be included
  581. * in emails.
  582. * @param int $p_bug_id
  583. * @param int $p_bugnote_id
  584. * @param int $p_user_id
  585. * @return string
  586. */
  587. function string_get_bugnote_view_url_with_fqdn( $p_bug_id, $p_bugnote_id, $p_user_id = null ) {
  588. return config_get( 'path' ) . string_get_bug_view_url( $p_bug_id, $p_user_id ) . '#c' . $p_bugnote_id;
  589. }
  590. /**
  591. * return the name and GET parameters of a bug VIEW page for the given bug
  592. * account for the user preference and site override
  593. * The returned url includes the fully qualified domain, hence it is suitable to be included in emails.
  594. * @param int $p_bug_id
  595. * @param int $p_user_id
  596. * @return string
  597. */
  598. function string_get_bug_view_url_with_fqdn( $p_bug_id, $p_user_id = null ) {
  599. return config_get( 'path' ) . string_get_bug_view_url( $p_bug_id, $p_user_id );
  600. }
  601. /**
  602. * return the name of a bug VIEW page for the user
  603. * account for the user preference and site override
  604. * @param int $p_user_id
  605. * @return string
  606. */
  607. function string_get_bug_view_page( $p_user_id = null ) {
  608. return string_get_bug_page( 'view', $p_user_id );
  609. }
  610. /**
  611. * return an href anchor that links to a bug UPDATE page for the given bug
  612. * account for the user preference and site override
  613. * @param int $p_bug_id
  614. * @param int $p_user_id
  615. * @return string
  616. */
  617. function string_get_bug_update_link( $p_bug_id, $p_user_id = null ) {
  618. $t_summary = string_attribute( bug_get_field( $p_bug_id, 'summary' ) );
  619. return '<a href="' . helper_mantis_url( string_get_bug_update_url( $p_bug_id, $p_user_id ) ) . '" title="' . $t_summary . '">' . bug_format_id( $p_bug_id ) . '</a>';
  620. }
  621. /**
  622. * return the name and GET parameters of a bug UPDATE page for the given bug
  623. * account for the user preference and site override
  624. * @param int $p_bug_id
  625. * @param int $p_user_id
  626. * @return string
  627. */
  628. function string_get_bug_update_url( $p_bug_id, $p_user_id = null ) {
  629. return string_get_bug_update_page( $p_user_id ) . '?bug_id=' . $p_bug_id;
  630. }
  631. /**
  632. * return the name of a bug UPDATE page for the user
  633. * account for the user preference and site override
  634. * @param int $p_user_id
  635. * @return string
  636. */
  637. function string_get_bug_update_page( $p_user_id = null ) {
  638. return string_get_bug_page( 'update', $p_user_id );
  639. }
  640. /**
  641. * return an href anchor that links to a bug REPORT page for the given bug
  642. * account for the user preference and site override
  643. * @param int $p_user_id
  644. * @return string
  645. */
  646. function string_get_bug_report_link( $p_user_id = null ) {
  647. return '<a href="' . helper_mantis_url( string_get_bug_report_url( $p_user_id ) ) . '">' . lang_get( 'report_bug_link' ) . '</a>';
  648. }
  649. /**
  650. * return the name and GET parameters of a bug REPORT page for the given bug
  651. * account for the user preference and site override
  652. * @param int $p_user_id
  653. * @return string
  654. */
  655. function string_get_bug_report_url( $p_user_id = null ) {
  656. return string_get_bug_report_page( $p_user_id );
  657. }
  658. /**
  659. * return the name of a bug REPORT page for the user
  660. * account for the user preference and site override
  661. * @param int $p_user_id
  662. * @return string
  663. */
  664. function string_get_bug_report_page( $p_user_id = null ) {
  665. return string_get_bug_page( 'report', $p_user_id );
  666. }
  667. /**
  668. * return the complete url link to checkin using the confirm_hash
  669. * @param int $p_user_id
  670. * @param string $p_confirm_hash
  671. * @return string
  672. */
  673. function string_get_confirm_hash_url( $p_user_id, $p_confirm_hash ) {
  674. $t_path = config_get( 'path' );
  675. return $t_path . "verify.php?id=" . string_url( $p_user_id ) . "&confirm_hash=" . string_url( $p_confirm_hash );
  676. }
  677. /**
  678. * Format date for display
  679. * @param int $p_date
  680. * @return string
  681. */
  682. function string_format_complete_date( $p_date ) {
  683. return date( config_get( 'complete_date_format' ), $p_date );
  684. }
  685. /**
  686. * Shorten a string for display on a dropdown to prevent the page rendering too wide
  687. * ref issues #4630, #5072, #5131
  688. * @param string $p_string
  689. * @param int $p_max
  690. * @return string
  691. */
  692. function string_shorten( $p_string, $p_max = null ) {
  693. if( $p_max === null ) {
  694. $t_max = config_get( 'max_dropdown_length' );
  695. } else {
  696. $t_max = (int) $p_max;
  697. }
  698. if( ( $t_max > 0 ) && ( utf8_strlen( $p_string ) > $t_max ) ) {
  699. $t_pattern = '/([\s|.|,|\-|_|\/|\?]+)/';
  700. $t_bits = preg_split( $t_pattern, $p_string, -1, PREG_SPLIT_DELIM_CAPTURE );
  701. $t_string = '';
  702. $t_last = $t_bits[count( $t_bits ) - 1];
  703. $t_last_len = strlen( $t_last );
  704. if( count( $t_bits ) == 1 ) {
  705. $t_string .= utf8_substr( $t_last, 0, $t_max - 3 );
  706. $t_string .= '...';
  707. } else {
  708. foreach( $t_bits as $t_bit ) {
  709. if(( utf8_strlen( $t_string ) + utf8_strlen( $t_bit ) + $t_last_len + 3 <= $t_max ) || ( strpos( $t_bit, '.,-/?' ) > 0 ) ) {
  710. $t_string .= $t_bit;
  711. } else {
  712. break;
  713. }
  714. }
  715. $t_string .= '...' . $t_last;
  716. }
  717. return $t_string;
  718. } else {
  719. return $p_string;
  720. }
  721. }
  722. /**
  723. * Normalize a string by removing leading, trailing and excessive internal spaces
  724. * note a space is used as the pattern instead of '\s' to make it work with UTF-8 strings
  725. * @param string $p_string
  726. * @return string
  727. */
  728. function string_normalize( $p_string ) {
  729. return preg_replace( '/ +/', ' ', trim( $p_string ) );
  730. }
  731. /**
  732. * remap a field name to a string name (for sort filter)
  733. * @param string $p_string
  734. * @return string
  735. */
  736. function string_get_field_name( $p_string ) {
  737. $t_map = array(
  738. 'attachment_count' => 'attachments',
  739. 'category_id' => 'category',
  740. 'handler_id' => 'assigned_to',
  741. 'id' => 'email_bug',
  742. 'last_updated' => 'updated',
  743. 'project_id' => 'email_project',
  744. 'reporter_id' => 'reporter',
  745. 'view_state' => 'view_status',
  746. );
  747. $t_string = $p_string;
  748. if( isset( $t_map[$p_string] ) ) {
  749. $t_string = $t_map[$p_string];
  750. }
  751. return lang_get_defaulted( $t_string );
  752. }
  753. /**
  754. * Calls htmlentities on the specified string, passing along
  755. * the current charset.
  756. * @param string $p_string
  757. * @return string
  758. */
  759. function string_html_entities( $p_string ) {
  760. return htmlentities( $p_string, ENT_COMPAT, 'utf-8' );
  761. }
  762. /**
  763. * Calls htmlspecialchars on the specified string, handling utf8
  764. * @param string $p_string
  765. * @return string
  766. */
  767. function string_html_specialchars( $p_string ) {
  768. # achumakov: @ added to avoid warning output in unsupported codepages
  769. # e.g. 8859-2, windows-1257, Korean, which are treated as 8859-1.
  770. # This is VERY important for Eastern European, Baltic and Korean languages
  771. return preg_replace( "/&amp;(#[0-9]+|[a-z]+);/i", "&$1;", @htmlspecialchars( $p_string, ENT_COMPAT, 'utf-8' ) );
  772. }
  773. /**
  774. * Prepares a string to be used as part of header().
  775. * @param string $p_string
  776. * @return string
  777. */
  778. function string_prepare_header( $p_string ) {
  779. $t_string= explode( "\n", $p_string, 2 );
  780. $t_string= explode( "\r", $t_string[0], 2 );
  781. return $t_string[0];
  782. }
  783. /**
  784. * Checks the supplied string for scripting characters, if it contains any, then return true, otherwise return false.
  785. * @param string $p_string
  786. * @return bool
  787. */
  788. function string_contains_scripting_chars( $p_string ) {
  789. if(( strstr( $p_string, '<' ) !== false ) || ( strstr( $p_string, '>' ) !== false ) ) {
  790. return true;
  791. }
  792. return false;
  793. }