00001 <?php
00002 # This file is part of the Savane project
00003 # <http://gna.org/projects/savane/>
00004 #
00005 # $Id: utils.php 5500 2006-02-26 12:11:12Z toddy $
00006 #
00007 # Copyright 1999-2000 (c) The SourceForge Crew
00008 # Copyright 2000-2003 (c) Free Software Foundation
00009 #
00010 # Copyright 2002-2006 (c) Mathieu Roy <yeupou--gnu.org>,
00011 # Tobias Toedter <t.toedter--gmx.net>
00012 #
00013 # The Savane project is free software; you can redistribute it and/or
00014 # modify it under the terms of the GNU General Public License
00015 # as published by the Free Software Foundation; either version 2
00016 # of the License, or (at your option) any later version.
00017 #
00018 # The Savane project is distributed in the hope that it will be useful,
00019 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00020 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00021 # GNU General Public License for more details.
00022 #
00023 # You should have received a copy of the GNU General Public License
00024 # along with the Savane project; if not, write to the Free Software
00025 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
00026
00027 function utils_safeinput ($string)
00028 {
00029 return safeinput($string);
00030 }
00031
00032
00033 # This function permit including site specific content with ease
00034 function utils_get_content ($file)
00035 {
00036 if (is_file($GLOBALS['sys_incdir'].'/'.$file.'.'.$GLOBALS['locale']))
00037 {
00038 # there is localized version of the file :
00039 include($GLOBALS['sys_incdir'].'/'.$file.'.'.$GLOBALS['locale']);
00040 }
00041 elseif (is_file($GLOBALS['sys_incdir'].'/'.$file.'.txt'))
00042 {
00043 include($GLOBALS['sys_incdir'].'/'.$file.'.txt');
00044 }
00045 else
00046 {
00047 fb(sprintf(_("Warning: Savane was not able to read \"%s\" site-specific information, please contact administrators"), $file), 1);
00048 }
00049 }
00050
00051 # Make sure that to avoid malicious file paths
00052 function utils_check_path ($path)
00053 {
00054 if (eregi(".*\.\.\/.*", $path))
00055 {
00056 exit_error('Error','Malformed url');
00057 }
00058 }
00059
00060 # In a string, replace %PROJECT by the group_name
00061 # (useful for group type configuration)
00062 function utils_makereal ($data, $string="%PROJECT", $replacement=0)
00063 {
00064 if (!$replacement)
00065 { $replacement = $GLOBALS[group_name]; }
00066 return ereg_replace($string, $replacement, $data);
00067 }
00068
00069 # Add unavailable css class to a link if required
00070 function utils_link ($url, $title, $defaultclass=0, $available=1, $help=0)
00071 {
00072 if (!$available)
00073 { $defaultclass = 'unavailable'; }
00074
00075 $return = '<a href="'.$url.'"';
00076
00077 if ($defaultclass)
00078 { $return .= ' class="'.$defaultclass.'"'; }
00079 if ($help)
00080 { $return .= ' title="'.$help.'"'; }
00081 $return .= '>'.$title.'</a>';
00082 return $return;
00083 }
00084
00085 # make an clean email link depending on the authentification level of the user
00086 function utils_email ($address, $nohtml=0)
00087 {
00088 if (user_isloggedin())
00089 {
00090 if ($nohtml)
00091 { return $address; }
00092
00093 return '<a href="mailto:'.$address.'">'.$address.'</a>';
00094
00095 }
00096 else
00097 {
00098 if ($nohtml)
00099 { return _("-unavailable-"); }
00100
00101 return utils_help(_("-unavailable-"),
00102 _("This information is not provided to anonymous users"),
00103 1);
00104 }
00105
00106 }
00107
00108 # Found out if a string is pure ASCII or not
00109 function utils_is_ascii ($string)
00110 {
00111 return preg_match('%^(?: [\x09\x0A\x0D\x20-\x7E] )*$%xs', $string);
00112 }
00113
00114 # Alias function
00115 function utils_altrow ($i)
00116 {
00117 return html_get_alt_row_color ($i);
00118 }
00119
00120 function utils_cutstring ($string, $lenght=35)
00121 {
00122 $string = rtrim($string);
00123 if (strlen($string) > $lenght)
00124 {
00125 $string = substr($string, 0, $lenght);
00126 $string = substr($string, 0, strrpos($string, ' '));
00127 $string .= "...";
00128 }
00129 return $string;
00130 }
00131
00138 function format_date($format="default", $timestamp, $default_value='-')
00139 {
00140 return utils_format_date($timestamp, $format);
00141 }
00142
00158 function utils_format_date($timestamp, $format="default")
00159 {
00160 global $sys_datefmt;
00161 if ($timestamp == 0)
00162 {
00163 return '-';
00164 }
00165
00166 # The installation configured a specific date format. This is not nice
00167 # this will prevent locales from being used
00168 if ($sys_datefmt)
00169 {
00170 return strftime($sys_datefmt, $timestamp);
00171 }
00172
00173 ## Go at task #2614 to discuss about this
00174 # Used by default
00175 switch ($format)
00176 {
00177 case 'short':
00178 {
00179 # To be used in tables, nowhere else
00180 return strftime('%a %x, %R', $timestamp);
00181 }
00182 case 'minimal':
00183 {
00184 # To be used where place is really lacking, like in feature boxes.
00185 # (Nowhere else, it is too uninformative)
00186 return strftime('%x', $timestamp);
00187 }
00188 default:
00189 {
00190 # Used by default
00191 # Mention timezone to non-logged in users or in printer mode.
00192 # Logged-in users have this as account setting, so we can assume they
00193 # now and dont want time wasted by that
00194 if (user_isloggedin() && !defined(PRINTER))
00195 {
00196 return strftime('%A %x '._("at").' %R', $timestamp);
00197 }
00198 else
00199 {
00200 return strftime('%A %x '._("at").' %R %Z', $timestamp);
00201 }
00202 }
00203 }
00204
00205 return false;
00206 }
00207
00208
00209 # Convert a date as used in the bug tracking system and other services (YYYY-MM-DD)
00210 # into a Unix time
00211 # Returns a list with two values: the unix time and a boolean saying whether the conversion
00212 # went well (true) or bad (false)
00213 function utils_date_to_unixtime ($date)
00214 {
00215 $res = preg_match("/\s*(\d+)-(\d+)-(\d+)/",$date,$match);
00216 if ($res == 0)
00217 { return array(0,false); }
00218 list(,$year,$month,$day) = $match;
00219 $time = mktime(0, 0, 0, $month, $day, $year);
00220 dbg("DBG Matching date $date -> year $year, month $month,day $day -> time = $time<br />");
00221 return array($time,true);
00222 }
00223
00224 function utils_read_file($filename)
00225 {
00226 @$fp = fopen($filename, "r");
00227 if ($fp)
00228 {
00229 $val = fread($fp, filesize($filename));
00230 fclose ($fp);
00231 return $val;
00232 }
00233 return false;
00234 }
00235
00236 function utils_filesize($filename, $file_size=0)
00237 {
00238 # If file size is defined, assume that we just want an unit conversion.
00239 if (!$file_size)
00240 { $file_size = filesize($filename); }
00241
00242 if ($file_size >= 1048576)
00243 {
00244 $file_size = round($file_size / 1048576 * 100) / 100 . _("MB");
00245 }
00246 elseif ($file_size >= 1024)
00247 {
00248 $file_size = round($file_size / 1024 * 100) / 100 . _("KB");
00249 }
00250 else
00251 {
00252 $file_size = $file_size . _("B");
00253 }
00254
00255 return $file_size;
00256 }
00257
00258 function utils_fileextension($filename)
00259 {
00260
00261 $ext = substr(basename($filename), strrpos(basename($filename),".") + 1);
00262 if ($ext==gz || $ext==bz2)
00263 {
00264 $ext = substr(basename($filename), strrpos(basename($filename),".") - 3);
00265 }
00266 if ($ext==rpm)
00267 {
00268 $long_ext = _("rpm package");
00269 }
00270 if ($ext==deb)
00271 {
00272 $long_ext = _("debian package");
00273 }
00274 if ($ext==deb || $ext==rpm)
00275 {
00276 $arch_type = substr(basename($filename), strrpos(basename($filename),".") - 3);
00277 if ($arch_type == "src.".$ext)
00278 {
00279 $long_ext = sprintf(_("source %s"), $long_ext);
00280 }
00281 if ($arch_type == "rch.".$ext)
00282 {
00283 $long_ext = sprintf(_("arch independant %s"), $long_ext);
00284 }
00285 if ($arch_type == "386.".$ext)
00286 {
00287 $long_ext = sprintf(_("%s for i386 (ix86)"), $long_ext);
00288 }
00289 if ($arch_type == "586.".$ext)
00290 {
00291 $long_ext = sprintf(_("%s for i586"), $long_ext);
00292 }
00293 if ($arch_type == "686.".$ext)
00294 {
00295 $long_ext = sprintf(_("%s for i686"), $long_ext);
00296 }
00297 if ($arch_type == "a64.".$ext)
00298 {
00299 $long_ext = sprintf(_("%s for Itanium 64"), $long_ext);
00300 }
00301 if ($arch_type == "arc.".$ext)
00302 {
00303 $long_ext = sprintf(_("%s for Sparc"), $long_ext);
00304 }
00305 if ($arch_type == "pha.".$ext)
00306 {
00307 $long_ext = sprintf(_("%s for Alpha"), $long_ext);
00308 }
00309 if ($arch_type == "ppc.".$ext)
00310 {
00311 $long_ext = sprintf(_("%s for PowerPC"), $long_ext);
00312 }
00313 if ($arch_type == "390.".$ext)
00314 {
00315 $long_ext = sprintf(_("%s for s390"), $long_ext);
00316 }
00317 $ext = $long_ext;
00318 }
00319 return $ext;
00320 }
00321
00322 function utils_prep_string_for_sendmail($body)
00323 {
00324 $body=str_replace("\\","\\\\",$body);
00325 $body=str_replace("\"","\\\"",$body);
00326 $body=str_replace("\$","\\\$",$body);
00327 $body=str_replace("`","\\`",$body);
00328 return $body;
00329 }
00330
00331 function utils_unconvert_htmlspecialchars($string)
00332 {
00333 if (strlen($string) < 1)
00334 {
00335 return '';
00336 }
00337 else
00338 {
00339 $string=str_replace(' ',' ',$string);
00340 $string=str_replace('"','"',$string);
00341 $string=str_replace('>','>',$string);
00342 $string=str_replace('<','<',$string);
00343 $string=str_replace('&','&',$string);
00344 return $string;
00345 }
00346 }
00347
00348 function utils_remove_htmlheader($string)
00349 {
00350 $string = eregi_replace('(^.*<html[^>]*>.*<body[^>]*>)|(</body[^>]*>.*</html[^>]*>.*$)', '', $string);
00351 return $string;
00352 }
00353
00354 function utils_result_column_to_array($result, $col=0)
00355 {
00356 /*
00357 Takes a result set and turns the optional column into
00358 an array
00359 */
00360 $rows=db_numrows($result);
00361
00362 if ($rows > 0)
00363 {
00364 $arr=array();
00365 for ($i=0; $i<$rows; $i++)
00366 {
00367 $arr[$i]=db_result($result,$i,$col);
00368 }
00369 }
00370 else
00371 {
00372 $arr=array();
00373 }
00374 return $arr;
00375 }
00376
00377 function result_column_to_array($result, $col=0)
00378 {
00379 /*
00380 backwards compatibility
00381 */
00382 return utils_result_column_to_array($result, $col);
00383 }
00384
00385 function utils_wrap_find_space($string,$wrap)
00386 {
00387 $start=$wrap-5;
00388 $try=1;
00389 $found=false;
00390
00391 while (!$found)
00392 {
00393 #find the first space starting at $start
00394 $pos=@strpos($string,' ',$start);
00395
00396 #if that space is too far over, go back and start more to the left
00397 if (($pos > ($wrap+5)) || !$pos)
00398 {
00399 $try++;
00400 $start=($wrap-($try*5));
00401 #if we've gotten so far left , just truncate the line
00402 if ($start<=10)
00403 {
00404 return $wrap;
00405 }
00406 $found=false;
00407 }
00408 else
00409 {
00410 $found=true;
00411 }
00412 }
00413
00414 return $pos;
00415 }
00416
00417 function utils_line_wrap ($text, $wrap = 78, $break = "\n")
00418 {
00419 $paras = explode("\n", $text);
00420
00421 $result = array();
00422 $i = 0;
00423 while ($i < count($paras))
00424 {
00425 if (strlen($paras[$i]) <= $wrap)
00426 {
00427 $result[] = $paras[$i];
00428 $i++;
00429 }
00430 else
00431 {
00432 $pos=utils_wrap_find_space($paras[$i],$wrap);
00433
00434 $result[] = substr($paras[$i], 0, $pos);
00435
00436 $new = trim(substr($paras[$i], $pos, strlen($paras[$i]) - $pos));
00437 if ($new != '')
00438 {
00439 $paras[$i] = $new;
00440 $pos=utils_wrap_find_space($paras[$i],$wrap);
00441 }
00442 else
00443 {
00444 $i++;
00445 }
00446 }
00447 }
00448 return implode($break, $result);
00449 }
00450
00451
00452
00464 function utils_basic_markup($text)
00465 {
00466 $lines = explode("\n", $text);
00467 $result = array();
00468
00469 foreach ($lines as $line)
00470 {
00471 $result[] = _markup_inline($line);
00472 }
00473
00474 return join("\n", $result);
00475 }
00476
00477
00478
00490 function utils_rich_markup($text)
00491 {
00492 return utils_full_markup($text, false);
00493 }
00494
00495
00496
00504 function utils_full_markup($text, $allow_headings=true)
00505 {
00506 $lines = explode("\n", $text);
00507 $result = array();
00508
00509 # we use a stack (last in, first out) to track the current
00510 # context (paragraph, lists) so we can correctly close tags
00511 $context_stack = array();
00512
00513 $quoted_text = false;
00514 $verbatim = false;
00515 foreach ($lines as $index => $line)
00516 {
00517 # the verbatim tags are not allowed to be nested, because
00518 # they are translated to HTML <pre>, which in turn is also
00519 # not allowed to be nested.
00520 # therefore, we don't need a counter of the level, but only
00521 # a simple bool flag
00522 if ($line == '+verbatim+' and !$verbatim)
00523 {
00524 $verbatim = true;
00525 # empty the context stack
00526 $line = join("\n", $context_stack).'<pre>';
00527 $context_stack = array('</pre>');
00528 }
00529
00530 # if we're in the verbatim markup, don't apply the markup
00531 if ($verbatim)
00532 {
00533 # disable the +nomarkup+ tags by inserting a unique string.
00534 # this has to be done in the original string, because that
00535 # is the one which will be split upon the +nomarkup+ tags,
00536 # see below
00537 $escaped_line = str_replace('nomarkup',
00538 'no-1a4f67a7-4eae-4aa1-a2ef-eecd8af6a997-markup', $line);
00539 $lines[$index] = $escaped_line;
00540 $result[] = $escaped_line;
00541 }
00542 else
00543 {
00544 $result[] = _full_markup($line, $allow_headings, &$context_stack, &$quoted_text);
00545 }
00546
00547 if ($line == '-verbatim-' and $verbatim)
00548 {
00549 $verbatim = false;
00550 # empty the context stack
00551 $line = join("\n", $context_stack);
00552 $context_stack = array();
00553 array_pop($result);
00554 $result[] = '</pre>';
00555 }
00556 }
00557
00558 # make sure that all previously used contexts get their
00559 # proper closing tag by merging in the last closing tags
00560 $markup_text = join("\n", array_merge($result, $context_stack));
00561
00562 # it's easiest to markup everything, without supporting the nomarkup
00563 # tag. afterwards, we replace every nomarkup tag pair with the content
00564 # between those tags in the original string
00565 $original = preg_split('/([+-]nomarkup[+-])/', join("\n", $lines), -1,
00566 PREG_SPLIT_DELIM_CAPTURE);
00567 $markup = preg_split('/([+-]nomarkup[+-])/', $markup_text, -1,
00568 PREG_SPLIT_DELIM_CAPTURE);
00569 # save the HTML tags from the last element in the markup array, see below
00570 $last_tags = $markup[count($markup)-1];
00571 $nomarkup_level = 0;
00572
00573 foreach ($original as $index => $original_text)
00574 {
00575 # keep track of nomarkup tags
00576 if ($original_text == '+nomarkup+') $nomarkup_level++;
00577 if ($original_text == '-nomarkup-') $nomarkup_level--;
00578
00579 # if the current match is the nomarkup tag, we don't want it to
00580 # show up in the markup text -> set it to an empty string
00581 if (preg_match('/([+-]nomarkup[+-])/', $original_text))
00582 {
00583 $markup[$index] = '';
00584 $original_text = '';
00585 }
00586 # while we're in a nomarkup environment, the already marked up text
00587 # needs to be replaced with the original content. Also, we need
00588 # to add <br /> tags for newlines.
00589 if ($nomarkup_level > 0)
00590 {
00591 $markup[$index] = nl2br($original_text);
00592 }
00593 }
00594
00595 # normally, $nomarkup_level must be zero at this point. however, if
00596 # the user submits wrong markup and forgets to close the -nomarkup-
00597 # tag, we need to take care of that.
00598 # To do this, we need to look for closing tags which have been deleted.
00599 if ($nomarkup_level > 0)
00600 {
00601 $trailing_markup = array_reverse(split("\n", $last_tags));
00602 $restored_tags = '';
00603 foreach ($trailing_markup as $tag)
00604 {
00605 if (preg_match('/^\s*<\/[a-z]+>$/', $tag))
00606 {
00607 $restored_tags = "\n$tag$restored_tags";
00608 }
00609 else
00610 {
00611 $markup[] = $restored_tags;
00612 break;
00613 }
00614 }
00615 }
00616
00617 # lastly, revert the escaping of +nomarkup+ tags done above
00618 # for verbatim environments
00619 return str_replace('no-1a4f67a7-4eae-4aa1-a2ef-eecd8af6a997-markup',
00620 'nomarkup', join('', $markup));
00621 }
00622
00623
00624
00633 function _full_markup($line, $allow_headings, &$context_stack, &$quoted_text)
00634 {
00635 #############################################################
00636 # context formatting
00637 #
00638 # the code below marks up recognized special characters,
00639 # by starting a new context (e.g. headings and lists)
00640 #############################################################
00641
00642 # generally, we want to start a new paragraph. this will be set
00643 # to false, if a new paragraph is no longer appropriate, like
00644 # for headings or lists
00645 $start_paragraph = true;
00646
00647 # Match the headings, e.g. === heading ===
00648 if ($allow_headings)
00649 {
00650 $line = _markup_headings($line, &$context_stack, &$start_paragraph);
00651 }
00652
00653 # Match list items
00654 $line = _markup_lists($line, &$context_stack, &$start_paragraph);
00655
00656 # replace at least four '-' sign with a horizontal ruler
00657 if (preg_match('/^----+\s*$/', $line))
00658 {
00659 $line = join("\n", $context_stack).'<hr />';
00660 $context_stack = array();
00661 $start_paragraph = false;
00662 }
00663
00664 #############################################################
00665 # inline formatting
00666 #
00667 # the code below marks up recognized special characters,
00668 # without starting a new context (e.g. <strong> and <em>)
00669 #############################################################
00670
00671 $line = _markup_inline($line);
00672
00673 #############################################################
00674 # paragraph formatting
00675 #
00676 # the code below is responsible for doing the Right Thing(tm)
00677 # by either starting a new paragraph and closing any previous
00678 # context or continuing an existing paragraph
00679 #############################################################
00680
00681 # change the quoteing mode when the line start with '>'
00682 if (substr($line, 0, 4) == '>')
00683 {
00684 # if the previous line was not quoted, start a new quote paragraph
00685 if (!$quoted_text)
00686 {
00687 $line = join("\n", $context_stack)."<p class=\"quote\">$line";
00688 # empty the stack
00689 $context_stack = array('</p>');
00690 $start_paragraph = false;
00691 }
00692 $quoted_text = true;
00693 }
00694 else
00695 {
00696 # if the previous line was quoted, end the quote paragraph
00697 if ($quoted_text and $start_paragraph and $line != '')
00698 {
00699 $line = join("\n", $context_stack)."\n<p>$line";
00700 # empty the stack
00701 $context_stack = array('</p>');
00702 }
00703 $quoted_text = false;
00704 }
00705
00706 # don't start a new paragraph again, if we already did that
00707 if ($context_stack[0] == '</p>')
00708 {
00709 $start_paragraph = false;
00710 }
00711
00712 # add proper closing tags when we encounter an empty line.
00713 # note that there might be no closing tags, in this case
00714 # the line will remain emtpy.
00715 if ($line == '')
00716 {
00717 $line = join("\n", $context_stack)."$line";
00718 # empty the stack
00719 $context_stack = array();
00720 $start_paragraph = false;
00721 }
00722
00723 # Finally start a new paragraph if appropriate
00724 if ($start_paragraph)
00725 {
00726 # make sure that all previously used contexts get their
00727 # proper closing tag
00728 $line = join("\n", $context_stack)."<p>$line";
00729 # empty the stack
00730 $context_stack = array('</p>');
00731 }
00732
00733 # append a linebreak while in paragraph mode
00734 if ($context_stack[0] == '</p>')
00735 {
00736 $line .= '<br />';
00737 }
00738
00739 return $line;
00740 }
00741
00742
00743
00751 function _markup_headings($line, &$context_stack, &$start_paragraph)
00752 {
00753 if (preg_match(
00754 # find one to four '=' signs at the start of a line
00755 '/^(={1,4})'
00756 # followed by exactly one space
00757 .' '
00758 # followed by any character
00759 .'(.+)'
00760 # followed by exactly one space
00761 .' '
00762 # followed by one to four '=' signs at the end of a line (whitespace allowed)
00763 .'(={1,4})\s*$/', $line, $matches))
00764 {
00765 $header_level_start = max(min(strlen($matches[1]), 4), 1);
00766 $header_level_end = strlen($matches[3]);
00767 if ($header_level_start == $header_level_end)
00768 {
00769 # if the user types '= heading =' (one '=' sign), it will
00770 # actually be rendered as a level 3 heading <h3>
00771 $header_level_start += 2;
00772 $header_level_end += 2;
00773
00774 $line = "<h$header_level_start>$matches[2]</h$header_level_end>";
00775 # make sure that all previously used contexts get their
00776 # proper closing tag
00777 $line = join("\n", $context_stack).$line;
00778 # empty the stack
00779 $context_stack = array();
00780 $start_paragraph = false;
00781 }
00782 }
00783 return $line;
00784 }
00785
00786
00787
00795 function _markup_lists($line, &$context_stack, &$start_paragraph)
00796 {
00797 if (preg_match('/^([*0]+) (.+)$/', $line, $matches))
00798 {
00799 # determine the list level currently in use
00800 $current_list_level = 0;
00801 foreach ($context_stack as $context)
00802 {
00803 if ($context == '</ul>' or $context == '</ol>')
00804 {
00805 $current_list_level++;
00806 }
00807 }
00808
00809 # determine whether the user list levels match the list
00810 # level we have in our context stack
00811 #
00812 # this will catch (potential) errors of the following form:
00813 # * list start
00814 # 0 maybe wrong list character
00815 # * list end
00816 $markup_position = 0;
00817 foreach (array_reverse($context_stack) as $context)
00818 {
00819 # we only care for the list types
00820 if ($context != '</ul>' and $context != '</ol>')
00821 {
00822 continue;
00823 }
00824
00825 $markup_character = substr($matches[1], $markup_position, 1);
00826
00827 if (($markup_character === '*' and $context != '</ul>')
00828 or ($markup_character === '0' and $context != '</ol>'))
00829 {
00830 # force a new and clean list start
00831 $current_list_level = 0;
00832 break;
00833 }
00834 else
00835 {
00836 $markup_position++;
00837 }
00838 }
00839
00840 # if we're not in a list, close the previous context
00841 $line = '';
00842 if ($current_list_level == 0)
00843 {
00844 $line = join("\n", $context_stack);
00845 $context_stack = array();
00846 }
00847
00848 # determine the list level the user wanted
00849 $wanted_list_level = strlen($matches[1]);
00850
00851 # here we start a new list and make sure that the markup
00852 # is valid, even if the user did skip one or more list levels
00853 $list_level_counter = $current_list_level;
00854 while ($list_level_counter < $wanted_list_level)
00855 {
00856 switch (substr($matches[1], $list_level_counter, 1))
00857 {
00858 case '*':
00859 $tag = 'ul';
00860 break;
00861 case '0':
00862 $tag = 'ol';
00863 break;
00864 }
00865 $line .= "<$tag>\n<li>";
00866 array_unshift($context_stack, "</$tag>");
00867 array_unshift($context_stack, "</li>");
00868 $list_level_counter++;
00869 }
00870
00871 # here we end a previous list and make sure that the markup
00872 # is valid, even if the user did skip one or more list levels
00873 $list_level_counter = $current_list_level;
00874 while ($list_level_counter > $wanted_list_level)
00875 {
00876 $line .= array_shift($context_stack)."\n"
00877 .array_shift($context_stack)."\n";
00878 $list_level_counter--;
00879 }
00880
00881 # prepare the next item of the same list level
00882 if ($current_list_level >= $wanted_list_level)
00883 {
00884 $line .= "</li>\n<li>";
00885 }
00886
00887 # finally, append the list item
00888 $line .= $matches[2];
00889 $start_paragraph = false;
00890 }
00891 return $line;
00892 }
00893
00894
00895
00903 function _markup_inline($line)
00904 {
00905 # Group_id may be necessary for recipe #nnn links
00906 global $group_id;
00907
00908 if ($group_id)
00909 {
00910 $comingfrom = "&comingfrom=$group_id";
00911 }
00912
00913 if (strlen($line) == 0)
00914 {
00915 return;
00916 }
00917
00918 # 1 and 2 could maybe be replaced by a better regexp, as they may in
00919 # some very rare case, break content. But we havent found such case.
00920 # Feel free to propose better regexp.
00921
00922 # 1. Don't mess with HTML links already there by escaping
00923 # the protocol separator with ":##"
00924 $line = eregi_replace("(<a href=\"[a-z]+)://([^<[:space:]]+)://([^<[:space:]]+)</a>",
00925 '\1:##\2:##\3</a>', $line);
00926 $line = eregi_replace("(<a href=\"[a-z]+)://([^<\"[:space:]]+)\"",
00927 '\1:##\2"', $line);
00928
00929 # 2. Dont mess with links surrounded by < >
00930 # (Which are in fact html special chars)
00931 $line = str_replace('>', ' ##>', $line);
00932 $line = str_replace('<', '<## ', $line);
00933
00934 # Prepare usual links: prefix every "www." with "http:
00935 $line = preg_replace('/(^|\s+)(www\.)/i', '$1http:
00936
00937 # replace the @ sign with an HTML entity, if it is used within
00938 # an url (e.g. for pointers to mailing lists). This way, the
00939 # @ sign doesn't get mangled in the e-mail markup code
00940 # below. See bug #2689 on http:
00941 $line = eregi_replace("([a-z]+://[^<>[:space:]]+)@", "\\1@", $line);
00942
00943 # Prepare the markup for normal links, e.g. http:
00944 # surrounding them with braces []
00945 $line = preg_replace('/(^|[^\[a-z])([a-z]+:\/\/[^<>\s]+[a-z0-9\/]+)/i',
00946 '$1[$2]', $line);
00947
00948 # do a markup for mail links, e.g. info@support.org
00949 $line = eregi_replace("([a-z0-9_+-.]+@([a-z0-9_+-]+\.)+[a-z]+)",
00950 utils_email('\1'), $line);
00951
00952 # Revert the escaping of already provided HTML links, done above
00953 $line = str_replace(":##", "://", $line);
00954 $line = str_replace(' ##>', '>', $line);
00955 $line = str_replace('<## ', '<', $line);
00956
00957 # Links between items
00958 # FIXME: it should be i18n, but in a clever way, meaning that everytime
00959 # a form is submitted with such string, the string get converted in
00960 # english so we always get the links found without having a regexp
00961 # including every possible language.
00962 $trackers = array (
00963 "bugs?" => "bugs/?",
00964 "support|sr" => "support/?",
00965 "tasks?" => "task/?",
00966 "recipes?|rcp" => "cookbook/?func=detailitem$comingfrom&item_id=",
00967 "patch" => "patch/?",
00968 # In this case, we make the link pointing to support, it wont matter,
00969 # the download page is in every tracker and does not check if the tracker
00970 # is actually used
00971 "files?" => "support/download.php?file_id=",
00972 );
00973 foreach ($trackers as $regexp => $link)
00974 {
00975 $line = preg_replace("/(^|\s+)($regexp)\s*#([0-9]+)/i",
00976 '$1<em><a href="'.$GLOBALS['sys_home']
00977 .$link.'$3">$2 #$3</a></em>', $line);
00978 }
00979
00980 # add an internal link for comments
00981 $line = preg_replace('/(comments?)\s*#([0-9]+)/i',
00982 '<em><a href="#comment$2">$1 #$2</a></em>', $line);
00983
00984 # Add support for named hyperlinks, e.g.
00985 # [http:
00986 $line = preg_replace(
00987 # find the opening brace '['
00988 '/\['
00989 # followed by the protocol, either http:
00990 .'(https?:\/\/'
00991 # match any character except whitespace or the closing
00992 # brace ']' for the actual link
00993 .'[^\s\]]+)'
00994 # followed by at least one whitespace
00995 .'\s+'
00996 # followed by any character (non-greedy) and the
00997 # next closing brace ']'
00998 .'(.+?)\]/', '<a href="$1">$2</a>', $line);
00999
01000 # Add support for unnamed hyperlinks, e.g.
01001 # [http:
01002 $line = preg_replace(
01003 # find the opening brace '['
01004 '/\['
01005 # followed by the protocol, either http:
01006 .'(https?:\/\/'
01007 # match any character except whitespace (non-greedy) for
01008 # the actual link, followed by the closing brace ']'
01009 .'[^\s]+?)\]/', '<a href="$1">$1</a>', $line);
01010
01011 # *word* -> <strong>word</strong>
01012 $line = preg_replace(
01013 # find an asterisk
01014 '/\*'
01015 # then one character (except a space or asterisk)
01016 .'([^* ]'
01017 # then (optionally) any character except asterisk
01018 .'[^*]*?)'
01019 # then an asterisk
01020 .'\*/', '<strong>$1</strong>', $line);
01021
01022 # _word_ -> <em>word</em>
01023 $line = preg_replace(
01024 # allow for the pattern to start at the beginning of a line.
01025 # if it doesn't start there, the character before the slash
01026 # must be either whitespace or the closing brace '>', to
01027 # allow for nested html tags (e.g. <p>_markup_</p>).
01028 '/(^|\s+|>)'
01029 # match the underscore
01030 .'_'
01031 # match any character (non-greedy)
01032 .'(.+?)'
01033 # match the ending underscore and either end of line or
01034 # a non-word character
01035 .'_(\W|$)/', '$1<em>$2</em>$3', $line);
01036
01037 return $line;
01038 }
01039
01040
01041
01042 function utils_user_link ($username, $realname=0)
01043 {
01044 if ( $username == 'None' || empty($username))
01045 {
01046 return $username;
01047 }
01048 else
01049 {
01050 $re = '<a href="'.$GLOBALS['sys_home'].'users/'.$username.'">';
01051 if ($realname)
01052 {
01053 $re .= $realname." <".$username.">";
01054 }
01055 else
01056 {
01057 $re .= $username;
01058 }
01059 $re .= '</a>';
01060
01061 return $re;
01062 }
01063 }
01064
01065 function utils_double_diff_array($arr1, $arr2)
01066 {
01067 # first transform both arrays in hashes
01068 reset($arr1); reset($arr2);
01069 while ( list(,$v) = each($arr1))
01070 { $h1[$v] = $v; }
01071 while ( list(,$v) = each($arr2))
01072 { $h2[$v] = $v; }
01073
01074 $deleted = array();
01075 while ( list($k,) = each($h1))
01076 {
01077 if (!isset($h2[$k]))
01078 { $deleted[] = $k; }
01079 }
01080
01081 $added = array();
01082 while ( list($k,) = each($h2))
01083 {
01084 if (!isset($h1[$k]))
01085 { $added[] = $k; }
01086 }
01087
01088 return array($deleted, $added);
01089 }
01090
01091 function utils_registration_history ($unix_group_name)
01092 {
01093 # Meaningless with chrooted system; all www system should be chrooted.
01094 }
01095
01096 function show_priority_colors_key()
01097 {
01098 print '<p class="smaller">';
01099 print _("Open Items Priority Colors:")."<br /> \n";
01100
01101 for ($i=1; $i<10; $i++)
01102 {
01103 print '<span class="'.utils_get_priority_color($i).'"> '.$i.' </span>'."\n";
01104 }
01105
01106 print '<br />';
01107 print _("Closed Items Priority Colors:")."<br /> \n";
01108
01109 for ($i=11; $i<20; $i++)
01110 {
01111 print '<span class="'.utils_get_priority_color($i).'"> '.($i-10).' </span>'."\n";
01112 }
01113
01114 print "</p>";
01115 }
01116
01117 function get_priority_color ($index, $closed="")
01118 { return utils_get_priority_color($index, $closed); }
01119
01120
01121 function utils_get_tracker_icon ($tracker)
01122 {
01123 if ($tracker == "bugs")
01124 { return "bug"; }
01125 if ($tracker == "support")
01126 { return "help"; }
01127 if ($tracker == "cookbook")
01128 { return "man"; }
01129 return $tracker;
01130 }
01131
01132 function utils_get_tracker_prefix ($tracker)
01133 {
01134 if ($tracker == "bugs")
01135 { return "bug"; }
01136 if ($tracker == "support")
01137 { return "sr"; }
01138 if ($tracker == "cookbook")
01139 { return "recipe"; }
01140 return $tracker;
01141 }
01142
01143
01144 function utils_get_priority_color ($index, $closed="")
01145 {
01146 global $bgpri;
01147 # If the item is closed, add ten to the index number to get closed colors
01148 if ($closed == 3)
01149 { $index = $index + 10; }
01150
01151 return $bgpri[$index];
01152 }
01153
01154 function build_priority_select_box ($name="priority", $checked_val="5")
01155 {
01156
01157
01158
01159
01160
01161 print "<select name=\"$name\">\n";
01162 print '<option value="1"'.($checked_val=="1" ?" selected":"").'>1 - '._("Lowest").'</option>';
01163 print '<option value="2"'.($checked_val=="2" ?" selected":"").'>2</option>';
01164 print '<option value="3"'.($checked_val=="3" ?" selected":"").'>3</option>';
01165 print '<option value="4"'.($checked_val=="4" ?" selected":"").'>4</option>';
01166 print '<option value="5"'.($checked_val=="5" ?" selected":"").'>5 - '._("Medium").'</option>';
01167 print '<option value="6"'.($checked_val=="6" ?" selected":"").'>6</option>';
01168 print '<option value="7"'.($checked_val=="7" ?" selected":"").'>7</option>';
01169 print '<option value="8"'.($checked_val=="8" ?" selected":"").'>8</option>';
01170 print '<option value="9"'.($checked_val=="9" ?" selected":"").'>9 - '._("Highest").'</option>';
01171 print '</select>';
01172
01173
01174 }
01175
01176 # ########################################### checkbox array
01177 # ################# mostly for group languages and environments
01178
01179 function utils_buildcheckboxarray($options,$name,$checked_array)
01180 {
01181 $option_count=count($options);
01182 $checked_count=count($checked_array);
01183
01184 for ($i=1; $i<=$option_count; $i++)
01185 {
01186 print '
01187 <BR><INPUT type="checkbox" name="'.$name.'" value="'.$i.'"';
01188 for ($j=0; $j<$checked_count; $j++)
01189 {
01190 if ($i == $checked_array[$j])
01191 {
01192 print ' CHECKED';
01193 }
01194 }
01195 print '> '.$options[$i];
01196 }
01197 }
01198
01199 # deprecated name
01200 function GraphResult($result,$title)
01201 {
01202 utils_graph_result($result, $title);
01203 }
01204
01205 # DEPRECATED:
01206 function utils_graph_result ($result,$title)
01207 {
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224 $rows=db_numrows($result);
01225
01226 if ((!$result) || ($rows < 1))
01227 {
01228 print 'None Found.';
01229 }
01230 else
01231 {
01232 $names=array();
01233 $values=array();
01234
01235 for ($j=0; $j<db_numrows($result); $j++)
01236 {
01237 if (db_result($result, $j, 0) != '' && db_result($result, $j, 1) != '' )
01238 {
01239 $names[$j]= db_result($result, $j, 0);
01240 $values[$j]= db_result($result, $j, 1);
01241 }
01242 }
01243
01244
01245
01246
01247 GraphIt($names,$values,$title);
01248 }
01249 }
01250
01251
01252 # DEPRECATED: deprecated name
01253 function GraphIt ($name_string,$value_string,$title)
01254 {
01255 utils_graph_it($name_string,$value_string,$title);
01256 }
01257
01258
01259 # DEPRECATED:
01260 function utils_graph_it ($name_string,$value_string,$title)
01261 {
01262
01263
01264
01265
01266 $counter=count($name_string);
01267
01268
01269
01270
01271 $bars=array();
01272
01273 for ($i = 0; $i < $counter; $i++)
01274 {
01275 $bars[$i]=$GLOBALS['COLOR_LTBACK1'];
01276 }
01277
01278 $counter=count($value_string);
01279
01280
01281
01282
01283
01284 $max_value=0;
01285
01286 for ($i = 0; $i < $counter; $i++)
01287 {
01288 if ($value_string[$i] > $max_value)
01289 {
01290 $max_value=$value_string[$i];
01291 }
01292 }
01293
01294 if ($max_value < 1)
01295 {
01296 $max_value=1;
01297 }
01298
01299
01300
01301
01302
01303 $scale=(400/$max_value);
01304
01305
01306
01307
01308
01309 $title_arr=array();
01310 $title_arr[]=$title;
01311
01312 print html_build_list_table_top ($title_arr);
01313 print '<TR><TD>';
01314
01315
01316
01317
01318 $vals = array(
01319 'vlabel'=>'',
01320 'hlabel'=>'',
01321 'type'=>'',
01322 'cellpadding'=>'',
01323 'cellspacing'=>'0',
01324 'border'=>'',
01325 'width'=>'',
01326 'background'=>'',
01327 'vfcolor'=>'',
01328 'hfcolor'=>'',
01329 'vbgcolor'=>'',
01330 'hbgcolor'=>'',
01331 'vfstyle'=>'',
01332 'hfstyle'=>'',
01333 'noshowvals'=>'',
01334 'scale'=>$scale,
01335 'namebgcolor'=>'',
01336 'valuebgcolor'=>'',
01337 'namefcolor'=>'',
01338 'valuefcolor'=>'',
01339 'namefstyle'=>'',
01340 'valuefstyle'=>'',
01341 'doublefcolor'=>'');
01342
01343
01344
01345
01346
01347 html_graph($name_string,$value_string,$bars,$vals);
01348
01349 print '
01350 </TD></TR></TABLE>
01351 <!-- end outer graph table -->';
01352 }
01353
01354
01355
01356 function utils_show_result_set ($result,$title="Untitled",$linkify=false)
01357 {
01358 global $group_id,$HTML;
01359
01360
01361
01362
01363
01364
01365 if ($result) {
01366 $rows = db_numrows($result);
01367 $cols = db_numfields($result);
01368
01369 # Show title
01370 print "<h4>$title</h4>\n";
01371 print '<table border="0" width="100%" summary="'.$title.'">'."\n";
01372
01373
01374 print "<tr>\n";
01375 for ($i=0; $i < $cols; $i++)
01376 {
01377 print '<th>'.db_fieldname($result, $i)."</th>\n";
01378 }
01379 print "</tr>\n";
01380
01381
01382 for ($j = 0; $j < $rows; $j++)
01383 {
01384 print '<tr class="'. html_get_alt_row_color($j) .'">';
01385 for ($i = 0; $i < $cols; $i++)
01386 {
01387 if ($linkify && $i == 0)
01388 {
01389 $link = '<a href="'.$PHP_SELF.'?';
01390 $linkend = '</a>';
01391 if ($linkify == "bug_cat")
01392 {
01393 $link .= 'group_id='.$group_id.'&bug_cat_mod=y&bug_cat_id='.db_result($result, $j, 'bug_category_id').'">';
01394 } else if($linkify == "bug_group")
01395 {
01396 $link .= 'group_id='.$group_id.'&bug_group_mod=y&bug_group_id='.db_result($result, $j, 'bug_group_id').'">';
01397 } else if($linkify == "patch_cat")
01398 {
01399 $link .= 'group_id='.$group_id.'&patch_cat_mod=y&patch_cat_id='.db_result($result, $j, 'patch_category_id').'">';
01400 } else if($linkify == "support_cat")
01401 {
01402 $link .= 'group_id='.$group_id.'&support_cat_mod=y&support_cat_id='.db_result($result, $j, 'support_category_id').'">';
01403 } else if($linkify == "pm_project")
01404 {
01405 $link .= 'group_id='.$group_id.'&project_cat_mod=y&project_cat_id='.db_result($result, $j, 'group_project_id').'">';
01406 }
01407 else
01408 {
01409 $link = $linkend = '';
01410 }
01411 }
01412 else
01413 {
01414 $link = $linkend = '';
01415 }
01416 print '<td>'.$link . db_result($result, $j, $i) . $linkend.'</td>';
01417
01418 }
01419 print '</tr>';
01420 }
01421 print "</table>\n";
01422 }
01423 else
01424 {
01425 print db_error();
01426 }
01427 }
01428
01429
01430 # Clean up email address (remove spaces...) and put to lower case
01431 function utils_cleanup_emails ($addresses)
01432 {
01433 return strtolower(preg_replace("/\s/","", $addresses));
01434 }
01435
01436 # Clean up email address (remove spaces...) and add @... if it is a simple
01437 # login name
01438 function utils_normalize_email ($address)
01439 {
01440 $address = utils_cleanup_emails($address);
01441 if (validate_email($address))
01442 return $address;
01443 else
01444 return $address."@".$GLOBALS['sys_default_domain'];
01445 }
01446
01447
01448 # Clean up email address (remove spaces...) and split comma separated emails
01449 function utils_split_emails($addresses)
01450 {
01451 $addresses = utils_cleanup_emails($addresses);
01452 $addresses = ereg_replace(";", ",", $addresses);
01453 return split(',',$addresses);
01454 }
01455
01456 # Email Verification
01457 function validate_email ($address)
01458 {
01459 return (ereg('^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+'. '@'. '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.' . '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$', $address));
01460 }
01461
01462 # Verification of comma separated list of email addresses
01463 function validate_emails ($addresses)
01464 {
01465 $arr = utils_split_emails($addresses);
01466 while (list(, $addr) = each ($arr))
01467 {
01468 if (!validate_email($addr))
01469 { return false;}
01470 }
01471 return true;
01472 }
01473
01474 function utils_is_valid_filename ($file)
01475 {
01476 if (ereg("[]~`! ~@#\"$%^,&*();=|[{}<>?/]",$file))
01477 {
01478 return false;
01479 }
01480 else
01481 {
01482 if (strstr($file,'..'))
01483 {
01484 return false;
01485 }
01486 else
01487 {
01488 return true;
01489 }
01490 }
01491 }
01492
01493 # alias
01494 function util_debug ($msg)
01495 {
01496 dbg($msg);
01497 }
01498
01499 # alias
01500 function debug ($msg)
01501 {
01502 dbg($msg);
01503 }
01504
01505 # add debugging information
01506 function dbg ($msg)
01507 {
01508 if ($GLOBALS['sys_debug_on'])
01509 {
01510 $GLOBALS['debug'] .= "<br /><br />latest func called: ".ereg_replace($_SERVER["DOCUMENT_ROOT"].$GLOBALS['sys_home'], "",$GLOBALS['sys_debug_where']);
01511 $GLOBALS['debug'] .= "<br />msg: $msg";
01512 }
01513 }
01514
01515
01516 # alias
01517 function util_feedback ($msg, $error=0)
01518 {
01519 fb($msg, $error);
01520 }
01521
01522 # alias
01523 function feedback ($msg, $error=0)
01524 {
01525 fb($msg, $error);
01526 }
01527
01528 # add feedback information
01529 function fb ($msg, $error=0, $enablehtml=0)
01530 {
01531 # If ware in enablehtml mode, it means that the feedback may contain real
01532 # html.
01533 # It means that we need to remove previous feedback to avoid malicious usage
01534 # of the feedback form.
01535 # This should be used on rare occasions.
01536 # We will also set feedback_html.
01537 if ($enablehtml)
01538 {
01539 unset($GLOBALS['feedback'], $GLOBALS['ffeedback']);
01540 $GLOBALS['feedback_html'] = 1;
01541 }
01542
01543 # Increment feedback count
01544 $GLOBALS['feedback_count']++;
01545
01546 # Remove the dot at the end, if existing
01547 if (substr($msg, -1) == ".")
01548 {
01549 $msg = substr($msg, 0, (strlen($msg)-1));
01550 }
01551
01552
01553 # First letter capitalized, that's all
01554 $msg = strtoupper(substr($msg, 0, 1)).strtolower(substr($msg, 1, strlen($msg)));
01555
01556 if ($GLOBALS['sys_debug_on'])
01557 {
01558 $msg .= ' [#'.$GLOBALS['feedback_count'].']';
01559 dbg("Add feedback #".$GLOBALS['feedback_count']);
01560 }
01561
01562 $msg .= _(";").' ';
01563
01564 # feed
01565 if (!$error)
01566 {
01567 $GLOBALS['feedback'] .= $msg;
01568 }
01569 else
01570 {
01571 $GLOBALS['ffeedback'] .= $msg;
01572 }
01573 }
01574
01575 # fb function to be used about database error when context of error is obvious
01576 function fb_dberror()
01577 {
01578 fb(_("Error updating database"),1);
01579 }
01580
01581 # fb function to be used about database error when context of error is obvious
01582 function fb_dbsuccess()
01583 {
01584 fb(_("Database successfully updated"));
01585 }
01586
01587
01588 # alias
01589 function utils_help ($text, $explanation_array, $noarray=0)
01590 {
01591 return help($text, $explanation_array, $noarray);
01592 }
01593
01594 # print help about a word
01595 # $text is the sentence where ballons are
01596 # $explanation_array is the table word->explanation, must be in the
01597 # array syntax.
01598 function help ($text, $explanation_array, $noarray=0)
01599 {
01600 if (!$noarray)
01601 {
01602 while (list($word,$explanation) = each($explanation_array))
01603 {
01604 $text = str_replace($word,
01605 '<span class="help" title="'.$explanation.'">'.$word.'</span>',
01606 $text);
01607 }
01608 return $text;
01609 }
01610
01611
01612 return '<span class="help"