[ Index ]
 

Code source de b2evolution 2.1.0-beta

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/blogs/inc/_core/ -> _misc.funcs.php (source)

   1  <?php
   2  /**

   3   * This file implements general purpose functions.

   4   *

   5   * This file is part of the evoCore framework - {@link http://evocore.net/}

   6   * See also {@link http://sourceforge.net/projects/evocms/}.

   7   *

   8   * @copyright (c)2003-2007 by Francois PLANQUE - {@link http://fplanque.net/}

   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.

  10   * Parts of this file are copyright (c)2005-2006 by PROGIDISTRI - {@link http://progidistri.com/}.

  11   *

  12   * {@internal License choice

  13   * - If you have received this file as part of a package, please find the license.txt file in

  14   *   the same folder or the closest folder above for complete license terms.

  15   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)

  16   *   then you must choose one of the following licenses before using the file:

  17   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php

  18   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php

  19   * }}

  20   *

  21   * {@internal Open Source relicensing agreement:

  22   * Daniel HAHLER grants Francois PLANQUE the right to license

  23   * Daniel HAHLER's contributions to this file and the b2evolution project

  24   * under any OSI approved OSS license (http://www.opensource.org/licenses/).

  25   *

  26   * PROGIDISTRI S.A.S. grants Francois PLANQUE the right to license

  27   * PROGIDISTRI S.A.S.'s contributions to this file and the b2evolution project

  28   * under any OSI approved OSS license (http://www.opensource.org/licenses/).

  29   * }}

  30   *

  31   * @package evocore

  32   *

  33   * @todo dh> Refactor into smaller chunks/files. We should avoid using a "huge" misc early!

  34   *       - _debug.funcs.php

  35   *       - _formatting.funcs.php

  36   *       - _date.funcs.php

  37   *       - ?

  38   *       NOTE: Encapsulation functions into classes would allow using autoloading (http://php.net/autoload) in PHP5..!

  39   *

  40   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}

  41   * @author cafelog (team)

  42   * @author blueyed: Daniel HAHLER.

  43   * @author fplanque: Francois PLANQUE.

  44   * @author jeffbearer: Jeff BEARER.

  45   * @author sakichan: Nobuo SAKIYAMA.

  46   * @author vegarg: Vegar BERG GULDAL.

  47   * @author mbruneau: Marc BRUNEAU / PROGIDISTRI

  48   *

  49   * @version $Id: _misc.funcs.php,v 1.7 2007/11/03 21:04:25 fplanque Exp $

  50   */
  51  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  52  
  53  
  54  /**

  55   * Dependencies

  56   */
  57  load_funcs('antispam/model/_antispam.funcs.php');
  58  load_funcs('files/model/_file.funcs.php');
  59  
  60  
  61  /***** Formatting functions *****/

  62  
  63  /**

  64   * Format a string/content for being output

  65   *

  66   * @author fplanque

  67   * @param string raw text

  68   * @param string format, can be one of the following

  69   * - raw: do nothing

  70   * - htmlbody: display in HTML page body: allow full HTML

  71   * - entityencoded: Special mode for RSS 0.92: allow full HTML but escape it

  72   * - htmlhead: strips out HTML (mainly for use in Title)

  73   * - htmlattr: use as an attribute: escapes quotes, strip tags

  74   * - formvalue: use as a form value: escapes quotes and < > but leaves code alone

  75   * - text: use as plain-text, e.g. for ascii-mails

  76   * - xml: use in an XML file: strip HTML tags

  77   * - xmlattr: use as an attribute: strips tags and escapes quotes

  78   * @return string formatted text

  79   */
  80  function format_to_output( $content, $format = 'htmlbody' )
  81  {
  82      global $Plugins;
  83  
  84      switch( $format )
  85      {
  86          case 'raw':
  87              // do nothing!

  88              break;
  89  
  90          case 'htmlbody':
  91              // display in HTML page body: allow full HTML

  92              $content = convert_chars($content, 'html');
  93              break;
  94  
  95          case 'urlencoded':
  96              // Encode string to be passed as part of an URL

  97              $content = rawurlencode( $content );
  98              break;
  99  
 100          case 'entityencoded':
 101              // Special mode for RSS 0.92: apply renders and allow full HTML but escape it

 102              $content = convert_chars($content, 'html');
 103              $content = htmlspecialchars( $content );
 104              break;
 105  
 106          case 'htmlhead':
 107              // strips out HTML (mainly for use in Title)

 108              $content = strip_tags($content);
 109              $content = convert_chars($content, 'html');
 110              break;
 111  
 112          case 'htmlattr':
 113              // use as an attribute: strips tags and escapes quotes

 114              $content = strip_tags($content);
 115              $content = convert_chars($content, 'html');
 116              $content = str_replace('"', '&quot;', $content );
 117              $content = str_replace("'", '&#039;', $content );
 118              break;
 119  
 120          case 'formvalue':
 121              // use as a form value: escapes &, quotes and < > but leaves code alone

 122              $content = htmlspecialchars( $content );           // Handles &, ", < and >

 123              $content = str_replace("'", '&#039;', $content );  // Handles '

 124              break;
 125  
 126          case 'xml':
 127              // use in an XML file: strip HTML tags

 128              $content = strip_tags($content);
 129              $content = convert_chars($content, 'xml');
 130              break;
 131  
 132          case 'xmlattr':
 133              // use as an attribute: strips tags and escapes quotes

 134              $content = strip_tags($content);
 135              $content = convert_chars($content, 'xml');
 136              $content = str_replace('"', '&quot;', $content );
 137              $content = str_replace("'", '&#039;', $content );
 138              break;
 139  
 140          case 'text':
 141              // use as plain-text, e.g. for ascii-mails

 142              $content = strip_tags( $content );
 143              $trans_tbl = get_html_translation_table( HTML_ENTITIES );
 144              $trans_tbl = array_flip( $trans_tbl );
 145              $content = strtr( $content, $trans_tbl );
 146              $content = preg_replace( '/[ \t]+/', ' ', $content);
 147              $content = trim($content);
 148              break;
 149  
 150          default:
 151              debug_die( 'Output format ['.$format.'] not supported.' );
 152      }
 153  
 154      return $content;
 155  }
 156  
 157  
 158  /**

 159   * Format raw HTML input to cleaned up and validated HTML.

 160   *

 161   * @param string The content to format

 162   * @param integer Create automated <br /> tags? (Deprecated??!)

 163   * @param integer Is this a comment? (Used for balanceTags(), SafeHtmlChecker()'s URI scheme, styling restrictions)

 164   * @param string Encoding (used for SafeHtmlChecker() only!); defaults to $io_charset

 165   * @return string

 166   */
 167  function format_to_post( $content, $autobr = 0, $is_comment = 0, $encoding = NULL )
 168  {
 169      global $use_balanceTags, $use_html_checker, $use_security_checker;
 170      global $allowed_tags, $allowed_attributes, $uri_attrs, $allowed_uri_scheme;
 171      global $comments_allowed_tags, $comments_allowed_attributes, $comments_allowed_uri_scheme;
 172      global $io_charset;
 173      global $Messages;
 174  
 175      // Replace any & that is not a character or entity reference with &amp;

 176      $content = preg_replace( '/&(?!#[0-9]+;|#x[0-9a-fA-F]+;|[a-zA-Z_:][a-zA-Z0-9._:-]*;)/', '&amp;', $content );
 177  
 178      if( $autobr )
 179      { // Auto <br />:
 180          // may put brs in the middle of multiline tags...

 181          // TODO: this may create "<br />" tags in "<UL>" (outside of <LI>) and make the HTML invalid!

 182          $content = autobrize($content);
 183      }
 184  
 185      if( $use_balanceTags )
 186      { // Auto close open tags:
 187          $content = balanceTags($content, $is_comment);
 188      }
 189  
 190      if( $use_html_checker )
 191      { // Check the code:
 192          load_class( 'xhtml_validator/_xhtml_validator.class.php' );
 193  
 194          if( empty($encoding) )
 195          {
 196              $encoding = $io_charset;
 197          }
 198          if( ! $is_comment )
 199          {
 200              $checker = & new SafeHtmlChecker( $allowed_tags, $allowed_attributes,
 201                      $uri_attrs, $allowed_uri_scheme, $encoding );
 202          }
 203          else
 204          {
 205              $checker = & new SafeHtmlChecker( $comments_allowed_tags, $comments_allowed_attributes,
 206                      $uri_attrs, $comments_allowed_uri_scheme, $encoding );
 207          }
 208  
 209          $checker->check( $content ); // TODO: see if we need to use convert_chars( $content, 'html' )

 210      }
 211  
 212      if( !isset( $use_security_checker ) ) $use_security_checker = 1;
 213      if( $use_security_checker )
 214      {
 215          // Security checking:

 216          $check = $content;
 217          // Open comments or '<![CDATA[' are dangerous

 218          $check = str_replace('<!', '<', $check);
 219          // # # are delimiters

 220          // i modifier at the end means caseless

 221          $matches = array();
 222          // onclick= etc...

 223          if( preg_match ('#\s(on[a-z]+)\s*=#i', $check, $matches)
 224              // action=, background=, cite=, classid=, codebase=, data=, href=, longdesc=, profile=, src=

 225              // usemap=

 226              || preg_match ('#=["\'\s]*(javascript|vbscript|about):#i', $check, $matches)
 227              || preg_match ('#\<\/?\s*(frame|iframe|applet|object)#i', $check, $matches) )
 228          {
 229              $Messages->add( T_('Illegal markup found: ').htmlspecialchars($matches[1]), 'error' );
 230          }
 231          // Styling restictions:

 232          $matches = array();
 233          if( $is_comment && preg_match ('#\s(style|class|id)\s*=#i', $check, $matches) )
 234          {
 235              $Messages->add( T_('Unallowed CSS markup found: ').htmlspecialchars($matches[1]), 'error' );
 236          }
 237      }
 238      return($content);
 239  }
 240  
 241  
 242  /*

 243   * autobrize(-)

 244   */
 245  function autobrize($content) {
 246      $content = preg_replace("/<br>\n/", "\n", $content);
 247      $content = preg_replace("/<br \/>\n/", "\n", $content);
 248      $content = preg_replace("/(\015\012)|(\015)|(\012)/", "<br />\n", $content);
 249      return($content);
 250      }
 251  
 252  /*

 253   * unautobrize(-)

 254   */
 255  function unautobrize($content)
 256  {
 257      $content = preg_replace("/<br>\n/", "\n", $content);   //for PHP versions before 4.0.5

 258      $content = preg_replace("/<br \/>\n/", "\n", $content);
 259      return($content);
 260  }
 261  
 262  /*

 263   * zeroise(-)

 264   */
 265  function zeroise($number, $threshold)
 266  { // function to add leading zeros when necessary
 267      $l = strlen($number);
 268      if ($l < $threshold)
 269          for ($i = 0; $i < ($threshold - $l); $i = $i + 1) { $number='0'.$number;    }
 270      return($number);
 271  }
 272  
 273  
 274  /**

 275   * Convert all non ASCII chars (except if UTF-8, GB2312 or CP1251) to &#nnnn; unicode references.

 276   * Also convert entities to &#nnnn; unicode references if output is not HTML (eg XML)

 277   *

 278   * Preserves < > and quotes.

 279   *

 280   * fplanque: simplified

 281   * sakichan: pregs instead of loop

 282   */
 283  function convert_chars( $content, $flag='html' )
 284  {
 285      global $b2_htmltrans, $b2_htmltranswinuni, $evo_charset;
 286  
 287      // Convert highbyte non ASCII/UTF-8 chars to urefs:

 288      if( ! in_array($evo_charset, array('utf-8', 'gb2312', 'windows-1251') ) )
 289      { // This is a single byte charset
 290          $content = preg_replace_callback(
 291              '/[\x80-\xff]/',
 292              create_function( '$j', 'return "&#".ord($j[0]).";";' ),
 293              $content);
 294      }
 295  
 296      // Convert Windows CP1252 => Unicode (valid HTML)

 297      // TODO: should this go to input conversions instead (?)

 298      $content = strtr( $content, $b2_htmltranswinuni );
 299  
 300      if( $flag == 'html' )
 301      { // we can use entities
 302          // Convert & chars that are not used in an entity

 303          $content = preg_replace('/&(?![#A-Za-z0-9]{2,20};)/', '&amp;', $content);
 304      }
 305      else
 306      { // unicode, xml...
 307          // Convert & chars that are not used in an entity

 308          $content = preg_replace('/&(?![#A-Za-z0-9]{2,20};)/', '&#38;', $content);
 309  
 310          // Convert HTML entities to urefs:

 311          $content = strtr($content, $b2_htmltrans);
 312      }
 313  
 314      return( $content );
 315  }
 316  
 317  
 318  /**

 319   * Split $text into blocks by using $pattern and call $callback on the non-matching blocks.

 320   *

 321   * The non-matching block's text is the first param to $callback and additionally $params gets passed.

 322   *

 323   * This gets used to make links clickable or replace smilies.

 324   *

 325   * E.g., to replace only in non-HTML tags, call it like:

 326   * <code>callback_on_non_matching_blocks( $text, '~<[^>]*>~s', 'your_callback' );</code>

 327   *

 328   * {@internal This function gets tested in misc.funcs.simpletest.php.}}

 329   *

 330   * @param string Text to handle

 331   * @param string Regular expression pattern that defines blocks to exclude.

 332   * @param callback Function name or object/method array to use as callback.

 333   *               Each non-matching block gets passed as first param, additional params may be

 334   *               passed with $params.

 335   * @param array Of additional ("static") params to $callback.

 336   * @return string

 337   */
 338  function callback_on_non_matching_blocks( $text, $pattern, $callback, $params = array() )
 339  {
 340      if( preg_match_all( $pattern, $text, $matches, PREG_OFFSET_CAPTURE | PREG_PATTERN_ORDER ) )
 341      { // $pattern matches, call the callback method on each non-matching block
 342          $pos = 0;
 343          $new_r = '';
 344  
 345          foreach( $matches[0] as $l_matching )
 346          {
 347              $pos_match = $l_matching[1];
 348              $non_match = substr( $text, $pos, ($pos_match - $pos) );
 349  
 350              // Callback:

 351              $callback_params = $params;
 352              array_unshift( $callback_params, $non_match );
 353              $new_r .= call_user_func_array( $callback, $callback_params );
 354  
 355              $new_r .= $l_matching[0];
 356              $pos += strlen($non_match)+strlen($l_matching[0]);
 357          }
 358  
 359          // Callback:

 360          $callback_params = $params;
 361          array_unshift( $callback_params, substr( $text, $pos ) );
 362          #pre_dump( $matches, $callback_params );

 363          $new_r .= call_user_func_array( $callback, $callback_params );
 364  
 365          return $new_r;
 366      }
 367  
 368      $callback_params = $params;
 369      array_unshift( $callback_params, $text );
 370      return call_user_func_array( $callback, $callback_params );
 371  }
 372  
 373  
 374  /**

 375   * Make links clickable in a given text.

 376   *

 377   * It replaces only text which is not between <a> tags already.

 378   *

 379   * @uses callback_on_non_matching_blocks()

 380   *

 381   * {@internal This function gets tested in misc.funcs.simpletest.php.}}

 382   *

 383   * @return string

 384   */
 385  function make_clickable( $text, $moredelim = '&amp;' )
 386  {
 387      $text = callback_on_non_matching_blocks( $text, '~<a[^>]*("[^"]"|\'[^\']\')?[^>]*>.*?</a>~is', 'make_clickable_callback', array( $moredelim ) );
 388  
 389      return $text;
 390  }
 391  
 392  
 393  /**

 394   * Callback function for {@link make_clickable()}.

 395   *

 396   * @todo IMHO it would be better to use "\b" (word boundary) to match the beginning of links..

 397   *

 398   * original function: phpBB, extended here for AIM & ICQ

 399   * fplanque restricted :// to http:// and mailto://

 400   * Fixed to not include trailing dot and comma.

 401   *

 402   * @return string The clickable text.

 403   */
 404  function make_clickable_callback( & $text, $moredelim = '&amp;' )
 405  {
 406      $pattern_domain = '([a-z0-9\-]+\.[a-z0-9\-.\~]+)'; // a domain name (not very strict)

 407      $text = preg_replace(
 408          array( '#(^|[\s>])(https?|mailto)://([^<>{}\s]+[^.,<>{}\s])#i',
 409              '#(^|[\s>])aim:([^,<\s]+)#i',
 410              '#(^|[\s>])icq:(\d+)#i',
 411              '#(^|[\s>])www\.'.$pattern_domain.'((?:/[^<\s]*)?[^.,\s])#i',
 412              '#(^|[\s>])([a-z0-9\-_.]+?)@'.$pattern_domain.'([^.,<\s]+)#i', ),
 413          array( '$1<a href="$2://$3">$2://$3</a>',
 414              '$1<a href="aim:goim?screenname=$2$3'.$moredelim.'message='.rawurlencode(T_('Hello')).'">$2$3</a>',
 415              '$1<a href="http://wwp.icq.com/scripts/search.dll?to=$2">$2</a>',
 416              '$1<a href="http://www.$2$3$4">www.$2$3$4</a>',
 417              '$1<a href="mailto:$2@$3$4">$2@$3$4</a>', ),
 418          $text );
 419  
 420      return $text;
 421  }
 422  
 423  
 424  /***** // Formatting functions *****/

 425  
 426  
 427  function date2mysql( $ts )
 428  {
 429      return date( 'Y-m-d H:i:s', $ts );
 430  }
 431  
 432  /**

 433   * Convert a MYSQL date to a UNIX timestamp

 434   */
 435  function mysql2timestamp( $m )
 436  {
 437      return mktime(substr($m,11,2),substr($m,14,2),substr($m,17,2),substr($m,5,2),substr($m,8,2),substr($m,0,4));
 438  }
 439  
 440  /**

 441   * Convert a MYSQL date -- WITHOUT the time -- to a UNIX timestamp

 442   */
 443  function mysql2datestamp( $m )
 444  {
 445      return mktime( 0, 0, 0, substr($m,5,2), substr($m,8,2), substr($m,0,4) );
 446  }
 447  
 448  /**

 449   * Format a MYSQL date to current locale date format.

 450   *

 451   * @param string MYSQL date YYYY-MM-DD HH:MM:SS

 452   */
 453  function mysql2localedate( $mysqlstring )
 454  {
 455      return mysql2date( locale_datefmt(), $mysqlstring );
 456  }
 457  
 458  function mysql2localetime( $mysqlstring )
 459  {
 460      return mysql2date( locale_timefmt(), $mysqlstring );
 461  }
 462  
 463  function mysql2localedatetime( $mysqlstring )
 464  {
 465      return mysql2date( locale_datefmt().' '.locale_timefmt(), $mysqlstring );
 466  }
 467  
 468  function mysql2localedatetime_spans( $mysqlstring )
 469  {
 470      return '<span class="date">'
 471                      .mysql2date( locale_datefmt(), $mysqlstring )
 472                      .'</span> <span class="time">'
 473                      .mysql2date( locale_timefmt(), $mysqlstring )
 474                      .'</span>';
 475  }
 476  
 477  
 478  /**

 479   * Format a MYSQL date.

 480   *

 481   * @param string enhanced format string

 482   * @param string MYSQL date YYYY-MM-DD HH:MM:SS

 483   * @param boolean true to use GM time

 484   */
 485  function mysql2date( $dateformatstring, $mysqlstring, $useGM = false )
 486  {
 487      $m = $mysqlstring;
 488      if( empty($m) || ($m == '0000-00-00 00:00:00' ))
 489          return false;
 490  
 491      // Get a timestamp:

 492      $unixtimestamp = mysql2timestamp( $m );
 493  
 494      return date_i18n( $dateformatstring, $unixtimestamp, $useGM );
 495  }
 496  
 497  
 498  /**

 499   * Date internationalization: same as date() formatting but with i18n support

 500   *

 501   * @param string enhanced format string

 502   * @param integer UNIX timestamp

 503   * @param boolean true to use GM time

 504   */
 505  function date_i18n( $dateformatstring, $unixtimestamp, $useGM = false )
 506  {
 507      global $month, $month_abbrev, $weekday, $weekday_abbrev, $weekday_letter;
 508      global $localtimenow, $time_difference;
 509  
 510      if( $dateformatstring == 'isoZ' )
 511      { // full ISO 8601 format
 512          $dateformatstring = 'Y-m-d\TH:i:s\Z';
 513      }
 514  
 515      if( $useGM )
 516      { // We want a Greenwich Meridian time:
 517          $r = gmdate($dateformatstring, ($unixtimestamp - $time_difference));
 518      }
 519      else
 520      { // We want default timezone time:
 521  
 522          /*

 523          Special symbols:

 524              'b': wether it's today (1) or not (0)

 525              'l': weekday

 526              'D': weekday abbrev

 527              'e': weekday letter

 528              'F': month

 529              'M': month abbrev

 530          */
 531  
 532          #echo $dateformatstring, '<br />';

 533  
 534          // protect special symbols, that date() would need proper locale set for

 535          $protected_dateformatstring = preg_replace( '/(?<!\\\)([blDeFM])/', '@@@\\\$1@@@', $dateformatstring );
 536  
 537          #echo $protected_dateformatstring, '<br />';

 538  
 539          $r = date( $protected_dateformatstring, $unixtimestamp );
 540  
 541          if( $protected_dateformatstring != $dateformatstring )
 542          { // we had special symbols, replace them
 543  
 544              $istoday = ( date('Ymd',$unixtimestamp) == date('Ymd',$localtimenow) ) ? '1' : '0';
 545              $datemonth = date('m', $unixtimestamp);
 546              $dateweekday = date('w', $unixtimestamp);
 547  
 548              // replace special symbols

 549              $r = str_replace( array(
 550                          '@@@b@@@',
 551                          '@@@l@@@',
 552                          '@@@D@@@',
 553                          '@@@e@@@',
 554                          '@@@F@@@',
 555                          '@@@M@@@',
 556                          ),
 557                      array( $istoday,
 558                          trim(T_($weekday[$dateweekday])),
 559                          trim(T_($weekday_abbrev[$dateweekday])),
 560                          trim(T_($weekday_letter[$dateweekday])),
 561                          trim(T_($month[$datemonth])),
 562                          trim(T_($month_abbrev[$datemonth])) ),
 563                      $r );
 564          }
 565      }
 566  
 567      return $r;
 568  }
 569  
 570  
 571  /**

 572   * Format dates into a string in a way similar to sprintf()

 573   */
 574  function date_sprintf( $string, $timestamp )
 575  {
 576      global $date_sprintf_timestamp;
 577      $date_sprintf_timestamp = $timestamp;
 578  
 579      return preg_replace_callback( '/%\{(.*?)\}/', 'date_sprintf_callback', $string );
 580  }
 581  
 582  function date_sprintf_callback( $matches )
 583  {
 584      global $date_sprintf_timestamp;
 585  
 586      return date_i18n( $matches[1], $date_sprintf_timestamp );
 587  }
 588  
 589  
 590  
 591  /**

 592   *

 593   * @param integer year

 594   * @param integer month (0-53)

 595   * @param integer 0 for sunday, 1 for monday

 596   */
 597  function get_start_date_for_week( $year, $week, $startofweek )
 598  {
 599      $new_years_date = mktime( 0, 0, 0, 1, 1, $year );
 600      $weekday = date('w', $new_years_date);
 601      // echo '<br> 1st day is a: '.$weekday;

 602  
 603      // How many days until start of week:

 604      $days_to_new_week = (7 - $weekday + $startofweek) % 7;
 605      // echo '<br> days to new week: '.$days_to_new_week;

 606  
 607      // We now add the required number of days to find the 1st sunday/monday in the year:

 608      //$first_week_start_date = $new_years_date + $days_to_new_week * 86400;

 609      //echo '<br> 1st week starts on '.date( 'Y-m-d H:i:s', $first_week_start_date );

 610  
 611      // We add the number of requested weeks:

 612      // This will fail when passing to Daylight Savings Time: $date = $first_week_start_date + (($week-1) * 604800);

 613      $date = mktime( 0, 0, 0, 1, $days_to_new_week + 1 + ($week-1) * 7, $year );
 614      // echo '<br> week '.$week.' starts on '.date( 'Y-m-d H:i:s', $date );

 615  
 616      return $date;
 617  }
 618  
 619  
 620  
 621  /**

 622   * Get start and end day of a week, based on week number and start-of-week

 623   *

 624   * Used by Calendar

 625   *

 626   * fp>> I'd really like someone to comment the magic of that thing...

 627   *

 628   * @param date

 629   * @param integer 0 for Sunday, 1 for Monday

 630   */
 631  function get_weekstartend( $date, $startOfWeek )
 632  {
 633      $weekday = date('w', $date);
 634      $i = 86400;
 635      while( $weekday <> $startOfWeek )
 636      {
 637          $weekday = date('w', $date);
 638          $date = $date - 86400;
 639          $i = 0;
 640      }
 641      $week['start'] = $date + 86400 - $i;
 642      $week['end']   = $date + 604800; // 691199;

 643  
 644      // pre_dump( 'weekstartend: ', date( 'Y-m-d', $week['start'] ), date( 'Y-m-d', $week['end'] ) );

 645  
 646      return( $week );
 647  }
 648  
 649  
 650  /**

 651   * Check that email address looks valid.

 652   *

 653   * @param string email address to check

 654   * @param string Format to use ('simple', 'rfc')

 655   *    'simple':

 656   *      Single email address.

 657   *    'rfc':

 658   *      Full email address, may include name (RFC2822)

 659   *      - example@example.org

 660   *      - Me <example@example.org>

 661   *      - "Me" <example@example.org>

 662   * @param boolean Return the match or boolean

 663   *

 664   * @return bool|array Either true/false or the match (see {@link $return_match})

 665   */
 666  function is_email( $email, $format = 'simple', $return_match = false )
 667  {
 668      #$chars = "/^([a-z0-9_]|\\-|\\.)+@(([a-z0-9_]|\\-)+\\.)+[a-z]{2,4}\$/i";

 669  
 670      switch( $format )
 671      {
 672          case 'rfc':
 673          case 'rfc2822':
 674              /**

 675               * Regexp pattern converted from: http://www.regexlib.com/REDetails.aspx?regexp_id=711

 676               * Extended to allow escaped quotes.

 677               */
 678              $pattern_email = '/^
 679                  (
 680                      (?>[a-zA-Z\d!\#$%&\'*+\-\/=?^_`{|}~]+\x20*
 681                          |"( \\\" | (?=[\x01-\x7f])[^"\\\] | \\[\x01-\x7f] )*"\x20*)* # Name
 682                      (<)
 683                  )?
 684                  (
 685                      (?!\.)(?>\.?[a-zA-Z\d!\#$%&\'*+\-\/=?^_`{|}~]+)+
 686                      |"( \\\" | (?=[\x01-\x7f])[^"\\\] | \\[\x01-\x7f] )* " # quoted mailbox name
 687                  )
 688                  @
 689                  (
 690                      ((?!-)[a-zA-Z\d\-]+(?<!-)\.)+[a-zA-Z]{2,}
 691                      |
 692                      \[(
 693                          ( (?(?<!\[)\.)(25[0-5] | 2[0-4]\d | [01]?\d?\d) ){4}
 694                          |
 695                          [a-zA-Z\d\-]*[a-zA-Z\d]:( (?=[\x01-\x7f])[^\\\[\]] | \\[\x01-\x7f] )+
 696                      )\]
 697                  )
 698                  (?(3)>) # match ">" if it was there
 699                  $/x';
 700              break;
 701  
 702          case 'simple':
 703          default:
 704              $pattern_email = '/^\S+@[^\.\s]\S*\.[a-z]{2,}$/i';
 705              break;
 706      }
 707  
 708      if( strpos( $email, '@' ) !== false && strpos( $email, '.' ) !== false )
 709      {
 710          if( $return_match )
 711          {
 712              preg_match( $pattern_email, $email, $match );
 713              return $match;
 714          }
 715          else
 716          {
 717              return (bool)preg_match( $pattern_email, $email );
 718          }
 719      }
 720      else
 721      {
 722          return $return_match ? array() : false;
 723      }
 724  }
 725  
 726  
 727  /**

 728   * Are we running on a Windows server?

 729   */
 730  function is_windows()
 731  {
 732      return ( strtoupper(substr(PHP_OS,0,3)) == 'WIN' );
 733  }
 734  
 735  
 736  function xmlrpc_getposttitle($content)
 737  {
 738      global $post_default_title;
 739      if (preg_match('/<title>(.+?)<\/title>/is', $content, $matchtitle))
 740      {
 741          $post_title = $matchtitle[1];
 742      }
 743      else
 744      {
 745          $post_title = $post_default_title;
 746      }
 747      return($post_title);
 748  }
 749  
 750  
 751  /**

 752   * Also used by post by mail

 753   * @deprecated by xmlrpc_getpostcategories()

 754   */
 755  function xmlrpc_getpostcategory($content)
 756  {
 757      if (preg_match('/<category>([0-9]+?)<\/category>/is', $content, $matchcat))
 758      {
 759          return $matchcat[1];
 760      }
 761  
 762      return false;
 763  }
 764  
 765  
 766  /**

 767   * Extract categories out of "<category>" tag from $content.

 768   *

 769   * NOTE: w.bloggar sends something like "<category>00000013,00000001,00000004,</category>" to

 770   *       blogger.newPost.

 771   *

 772   * @return false|array

 773   */
 774  function xmlrpc_getpostcategories($content)
 775  {
 776      if( preg_match('~<category>(\d+\s*(,\s*\d*)*)</category>~i', $content, $match) )
 777      {
 778          $cats = preg_split('~\s*,\s*~', $match[1], -1, PREG_SPLIT_NO_EMPTY);
 779          foreach( $cats as $k => $v )
 780          {
 781              $cats[$k] = (int)$v;
 782          }
 783          return $cats;
 784      }
 785  
 786      return false;
 787  }
 788  
 789  
 790  /*

 791   * xmlrpc_removepostdata(-)

 792   */
 793  function xmlrpc_removepostdata($content)
 794  {
 795      $content = preg_replace('/<title>(.*?)<\/title>/si', '', $content);
 796      $content = preg_replace('/<category>(.*?)<\/category>/si', '', $content);
 797      $content = trim($content);
 798      return($content);
 799  }
 800  
 801  
 802  /**

 803   * Echo the XML-RPC call Result and optionally log into file

 804   *

 805   * @param object XMLRPC response object

 806   * @param boolean true to echo

 807   * @param mixed File resource or == '' for no file logging.

 808   */
 809  function xmlrpc_displayresult( $result, $display = true, $log = '' )
 810  {
 811      if( ! $result )
 812      { // We got no response:
 813          if( $display ) echo T_('No response!')."<br />\n";
 814          return false;
 815      }
 816  
 817      if( $result->faultCode() )
 818      { // We got a remote error:
 819          if( $display ) echo T_('Remote error'), ': ', $result->faultString(), ' (', $result->faultCode(), ")<br />\n";
 820          debug_fwrite($log, $result->faultCode().' -- '.$result->faultString());
 821          return false;
 822      }
 823  
 824      // We'll display the response:

 825      $val = $result->value();
 826      $value = xmlrpc_decode_recurse($result->value());
 827  
 828      if( is_array($value) )
 829      {
 830          $out = '';
 831          foreach($value as $l_value)
 832          {
 833              $out .= ' ['.$l_value.'] ';
 834          }
 835      }
 836      else
 837      {
 838          $out = $value;
 839      }
 840  
 841      debug_fwrite($log, $out);
 842  
 843      if( $display ) echo T_('Response').': '.$out."<br />\n";
 844  
 845      return true;
 846  }
 847  
 848  
 849  /**

 850   * Log the XML-RPC call Result into LOG object

 851   *

 852   * @param object XMLRPC response object

 853   * @param Log object to add messages to

 854   * @return boolean true = success, false = error

 855   */
 856  function xmlrpc_logresult( $result, & $message_Log )
 857  {
 858      if( ! $result )
 859      { // We got no response:
 860          $message_Log->add( T_('No response!'), 'error' );
 861          return false;
 862      }
 863  
 864      if( $result->faultCode() )
 865      { // We got a remote error:
 866          $message_Log->add( T_('Remote error').': '.$result->faultString().' ('.$result->faultCode().')', 'error' );
 867          return false;
 868      }
 869  
 870      // We got a response:

 871      $val = $result->value();
 872      $value = xmlrpc_decode_recurse($result->value());
 873  
 874      if( is_array($value) )
 875      {
 876          $out = '';
 877          foreach($value as $l_value)
 878          {
 879              $out .= ' ['.$l_value.'] ';
 880          }
 881      }
 882      else
 883      {
 884          $out = $value;
 885      }
 886  
 887      $message_Log->add( T_('Response').': '.$out, 'success' );
 888  
 889      return true;
 890  }
 891  
 892  
 893  
 894  function debug_fopen($filename, $mode) {
 895      global $debug;
 896      if ($debug == 1 && ( !empty($filename) ) )
 897      {
 898          $fp = fopen($filename, $mode);
 899          return $fp;
 900      } else {
 901          return false;
 902      }
 903  }
 904  
 905  function debug_fwrite($fp, $string)
 906  {
 907      global $debug;
 908      if( $debug && $fp )
 909      {
 910          fwrite($fp, $string);
 911      }
 912  }
 913  
 914  function debug_fclose($fp)
 915  {
 916      global $debug;
 917      if( $debug && $fp )
 918      {
 919          fclose($fp);
 920      }
 921  }
 922  
 923  
 924  
 925  /**

 926   balanceTags

 927  

 928   Balances Tags of string using a modified stack.

 929  

 930   @param string    Text to be balanced

 931   @return string   Returns balanced text

 932   @author          Leonard Lin (leonard@acm.org)

 933   @version         v1.1

 934   @date            November 4, 2001

 935   @license         GPL v2.0

 936   @notes

 937   @changelog

 938               1.2  ***TODO*** Make better - change loop condition to $text

 939               1.1  Fixed handling of append/stack pop order of end text

 940                    Added Cleaning Hooks

 941               1.0  First Version

 942  */
 943  function balanceTags($text)
 944  {
 945      $tagstack = array();
 946      $stacksize = 0;
 947      $tagqueue = '';
 948      $newtext = '';
 949  
 950      # b2 bug fix for comments - in case you REALLY meant to type '< !--'

 951      $text = str_replace('< !--', '<    !--', $text);
 952  
 953      # b2 bug fix for LOVE <3 (and other situations with '<' before a number)

 954      $text = preg_replace('#<([0-9]{1})#', '&lt;$1', $text);
 955  
 956      while( preg_match('~<(\s*/?\w+)\s*([^>]*)>~', $text, $regex) )
 957      {
 958          $newtext = $newtext . $tagqueue;
 959  
 960          $i = strpos($text,$regex[0]);
 961          $l = strlen($tagqueue) + strlen($regex[0]);
 962  
 963          // clear the shifter

 964          $tagqueue = '';
 965  
 966          // Pop or Push

 967          if( substr($regex[1],0,1) == '/' )
 968          { // End Tag
 969              $tag = strtolower(substr($regex[1],1));
 970  
 971              // if too many closing tags

 972              if($stacksize <= 0) {
 973                  $tag = '';
 974                  //or close to be safe $tag = '/' . $tag;

 975              }
 976              // if stacktop value = tag close value then pop

 977              else if ($tagstack[$stacksize - 1] == $tag) { // found closing tag
 978                  $tag = '</' . $tag . '>'; // Close Tag

 979                  // Pop

 980                  array_pop ($tagstack);
 981                  $stacksize--;
 982              } else { // closing tag not at top, search for it
 983                  for ($j=$stacksize-1;$j>=0;$j--) {
 984                      if ($tagstack[$j] == $tag) {
 985                      // add tag to tagqueue

 986                          for ($k=$stacksize-1;$k>=$j;$k--){
 987                              $tagqueue .= '</' . array_pop ($tagstack) . '>';
 988                              $stacksize--;
 989                          }
 990                          break;
 991                      }
 992                  }
 993                  $tag = '';
 994              }
 995          }
 996          else
 997          { // Begin Tag
 998              $tag = strtolower($regex[1]);
 999  
1000              // Tag Cleaning

1001  
1002              // Push if not img or br or hr

1003              if($tag != 'br' && $tag != 'img' && $tag != 'hr' && $tag != 'param')
1004              {
1005                  $stacksize = array_push ($tagstack, $tag);
1006              }
1007  
1008              // Attributes

1009              // $attributes = $regex[2];

1010              $attributes = $regex[2];
1011              if($attributes) {
1012                  $attributes = ' '.$attributes;
1013              }
1014  
1015              $tag = '<'.$tag.$attributes.'>';
1016          }
1017  
1018          $newtext .= substr($text,0,$i) . $tag;
1019          $text = substr($text,$i+$l);
1020      }
1021  
1022      // Clear Tag Queue

1023      $newtext = $newtext . $tagqueue;
1024  
1025      // Add Remaining text

1026      $newtext .= $text;
1027  
1028      // Empty Stack

1029      while($x = array_pop($tagstack)) {
1030          $newtext = $newtext . '</' . $x . '>'; // Add remaining tags to close

1031      }
1032  
1033      # b2 fix for the bug with HTML comments

1034      $newtext = str_replace( '< !--', '<'.'!--', $newtext ); // the concatenation is needed to work around some strange parse error in PHP 4.3.1

1035      $newtext = str_replace( '<    !--', '< !--', $newtext );
1036  
1037      return $newtext;
1038  }
1039  
1040  
1041  /**

1042   * Wrap pre tag around {@link var_dump()} for better debugging.

1043   *

1044   * @param $var__var__var__var__,... mixed variable(s) to dump

1045   */
1046  function pre_dump( $var__var__var__var__ )
1047  {
1048      global $is_cli;
1049  
1050      #echo 'pre_dump(): '.debug_get_backtrace(); // see where a pre_dump() comes from

1051  
1052      $func_num_args = func_num_args();
1053      $count = 0;
1054  
1055      if( ! empty($is_cli) )
1056      { // CLI, no encoding of special chars:
1057          $count = 0;
1058          foreach( func_get_args() as $lvar )
1059          {
1060              var_dump($lvar);
1061  
1062              $count++;
1063              if( $count < $func_num_args )
1064              { // Put newline between arguments
1065                  echo "\n";
1066              }
1067          }
1068      }
1069      elseif( function_exists('xdebug_var_dump') )
1070      { // xdebug already does fancy displaying:
1071  
1072          // no limits:

1073          $old_var_display_max_children = ini_set('xdebug.var_display_max_children', -1); // default: 128

1074          $old_var_display_max_data = ini_set('xdebug.var_display_max_data', -1); // max string length; default: 512

1075          $old_var_display_max_depth = ini_set('xdebug.var_display_max_depth', -1); // default: 3

1076  
1077          echo "\n<div style=\"padding:1ex;border:1px solid #00f;\">\n";
1078          foreach( func_get_args() as $lvar )
1079          {
1080              xdebug_var_dump($lvar);
1081  
1082              $count++;
1083              if( $count < $func_num_args )
1084              { // Put HR between arguments
1085                  echo "<hr />\n";
1086              }
1087          }
1088          echo '</div>';
1089  
1090          // restore xdebug settings:

1091          ini_set('xdebug.var_display_max_children', $old_var_display_max_children);
1092          ini_set('xdebug.var_display_max_data', $old_var_display_max_data);
1093          ini_set('xdebug.var_display_max_depth', $old_var_display_max_depth);
1094      }
1095      else
1096      {
1097          $orig_html_errors = ini_set('html_errors', 0); // e.g. xdebug would use fancy html, if this is on; we catch (and use) xdebug explicitly above, but just in case

1098  
1099          echo "\n<pre style=\"padding:1ex;border:1px solid #00f;\">\n";
1100          foreach( func_get_args() as $lvar )
1101          {
1102              ob_start();
1103              var_dump($lvar); // includes "\n"; do not use var_export() because it does not detect recursion by design

1104              $buffer = ob_get_contents();
1105              ob_end_clean();
1106              echo htmlspecialchars($buffer);
1107  
1108              $count++;
1109              if( $count < $func_num_args )
1110              { // Put HR between arguments
1111                  echo "<hr />\n";
1112              }
1113          }
1114          echo "</pre>\n";
1115          ini_set('html_errors', $orig_html_errors);
1116      }
1117  }
1118  
1119  
1120  /**

1121   * Get a function trace from {@link debug_backtrace()} as html table.

1122   *

1123   * Adopted from {@link http://us2.php.net/manual/de/function.debug-backtrace.php#47644}.

1124   *

1125   * @param integer|NULL Get the last x entries from the stack (after $ignore_from is applied). Anything non-numeric means "all".

1126   * @param array After a key/value pair matches a stack entry, this and the rest is ignored.

1127   *              For example, array('class' => 'DB') would exclude everything after the stack

1128   *              "enters" class DB and everything that got called afterwards.

1129   *              You can also give an array of arrays which means that every condition in one of the given array must match.

1130   * @param integer Number of stack entries to include, after $ignore_from matches.

1131   * @return string HTML table

1132   */
1133  function debug_get_backtrace( $limit_to_last = NULL, $ignore_from = array( 'function' => 'debug_get_backtrace' ), $offset_ignore_from = 0 )
1134  {
1135      if( ! function_exists( 'debug_backtrace' ) ) // PHP 4.3.0
1136      {
1137          return 'Function debug_backtrace() is not available!';
1138      }
1139  
1140      $r = '';
1141  
1142      $backtrace = debug_backtrace();
1143      $count_ignored = 0; // remember how many have been ignored

1144      $limited = false;   // remember if we have limited to $limit_to_last

1145  
1146      if( $ignore_from )
1147      {    // we want to ignore from a certain point
1148          $trace_length = 0;
1149          $break_because_of_offset = false;
1150  
1151          for( $i = count($backtrace); $i > 0; $i-- )
1152          {    // Search the backtrace from behind (first call).
1153              $l_stack = & $backtrace[$i-1];
1154  
1155              if( $break_because_of_offset && $offset_ignore_from < 1 )
1156              { // we've respected the offset, but need to break now
1157                  break; // ignore from here

1158              }
1159  
1160              foreach( $ignore_from as $l_ignore_key => $l_ignore_value )
1161              {    // Check if we want to ignore from here
1162                  if( is_array($l_ignore_value) )
1163                  {    // It's an array - all must match
1164                      foreach( $l_ignore_value as $l_ignore_mult_key => $l_ignore_mult_val )
1165                      {
1166                          if( !isset($l_stack[$l_ignore_mult_key]) /* not set with this stack entry */
1167                              || strcasecmp($l_stack[$l_ignore_mult_key], $l_ignore_mult_val) /* not this value (case-insensitive) */ )
1168                          {
1169                              continue 2; // next ignore setting, because not all match.

1170                          }
1171                      }
1172                      if( $offset_ignore_from-- > 0 )
1173                      {
1174                          $break_because_of_offset = true;
1175                          break;
1176                      }
1177                      break 2; // ignore from here

1178                  }
1179                  elseif( isset($l_stack[$l_ignore_key])
1180                      && !strcasecmp($l_stack[$l_ignore_key], $l_ignore_value) /* is equal case-insensitive */ )
1181                  {
1182                      if( $offset_ignore_from-- > 0 )
1183                      {
1184                          $break_because_of_offset = true;
1185                          break;
1186                      }
1187                      break 2; // ignore from here

1188                  }
1189              }
1190              $trace_length++;
1191          }
1192  
1193          $count_ignored = count($backtrace) - $trace_length;
1194  
1195          $backtrace = array_slice( $backtrace, 0-$trace_length ); // cut off ignored ones

1196      }
1197  
1198      $count_backtrace = count($backtrace);
1199      if( is_numeric($limit_to_last) && $limit_to_last < $count_backtrace )
1200      {    // we want to limit to a maximum number
1201          $limited = true;
1202          $backtrace = array_slice( $backtrace, 0, $limit_to_last );
1203          $count_backtrace = $limit_to_last;
1204      }
1205  
1206      $r .= '<div style="padding:1ex; margin-bottom:1ex; text-align:left; color:#000; background-color:#ddf;">
1207                      <h3>Backtrace:</h3>'."\n";
1208      if( $count_backtrace )
1209      {
1210          $r .= '<ol style="font-family:monospace;">';
1211  
1212          $i = 0;
1213          foreach( $backtrace as $l_trace )
1214          {
1215              if( ++$i == $count_backtrace )
1216              {
1217                  $r .= '<li style="padding:0.5ex 0;">';
1218              }
1219              else
1220              {
1221                  $r .= '<li style="padding:0.5ex 0; border-bottom:1px solid #77d;">';
1222              }
1223              $args = array();
1224              if( isset($l_trace['args']) && is_array( $l_trace['args'] ) )
1225              {    // Prepare args:
1226                  foreach( $l_trace['args'] as $l_arg )
1227                  {
1228                      $l_arg_type = gettype($l_arg);
1229                      switch( $l_arg_type )
1230                      {
1231                          case 'integer':
1232                          case 'double':
1233                              $args[] = $l_arg;
1234                              break;
1235                          case 'string':
1236                              $args[] = '"'.htmlspecialchars(str_replace("\n", '', substr($l_arg, 0, 64))).((strlen($l_arg) > 64) ? '...' : '').'"';
1237                              break;
1238                          case 'array':
1239                              $args[] = 'Array('.count($l_arg).')';
1240                              break;
1241                          case 'object':
1242                              $args[] = 'Object('.get_class($l_arg).')';
1243                              break;
1244                          case 'resource':
1245                              $args[] = 'Resource('.strstr($l_arg, '#').')';
1246                              break;
1247                          case 'boolean':
1248                              $args[] = $l_arg ? 'true' : 'false';
1249                              break;
1250                          default:
1251                              $args[] = $l_arg_type;
1252                      }
1253                  }
1254              }
1255  
1256              $call = "<strong>\n";
1257              if( isset($l_trace['class']) )
1258              {
1259                  $call .= $l_trace['class'];
1260              }
1261              if( isset($l_trace['type']) )
1262              {
1263                  $call .= $l_trace['type'];
1264              }
1265              $call .= $l_trace['function']."( </strong>\n";
1266              if( $args )
1267              {
1268                  $call .= ' '.implode( ', ', $args ).' ';
1269              }
1270              $call .='<strong>)</strong>';
1271  
1272              $r .= $call."<br />\n";
1273  
1274              $r .= '<strong>';
1275              if( isset($l_trace['file']) )
1276              {
1277                  $r .= "File: </strong> ".$l_trace['file'];
1278              }
1279              else
1280              {
1281                  $r .= '[runtime created function]</strong>';
1282              }
1283              if( isset($l_trace['line']) )
1284              {
1285                  $r .= ' on line '.$l_trace['line'];
1286              }
1287  
1288              $r .= "</li>\n";
1289          }
1290          $r .= '</ol>';
1291      }
1292      else
1293      {
1294          $r .= '<p>No backtrace available.</p>';
1295      }
1296  
1297      // Extra notes, might be to much, but explains why we stopped at some point. Feel free to comment it out or remove it.

1298      $notes = array();
1299      if( $count_ignored )
1300      {
1301          $notes[] = 'Ignored last: '.$count_ignored;
1302      }
1303      if( $limited )
1304      {
1305          $notes[] = 'Limited to'.( $count_ignored ? ' remaining' : '' ).': '.$limit_to_last;
1306      }
1307      if( $notes )
1308      {
1309          $r .= '<p class="small">'.implode( ' - ', $notes ).'</p>';
1310      }
1311  
1312      $r .= "</div>\n";
1313  
1314      return $r;
1315  }
1316  
1317  
1318  /**

1319   * Outputs Unexpected Error message. When in debug mode it also prints a backtrace.

1320   *

1321   * This should be used instead of die() everywhere.

1322   * This should NOT be used instead of exit() anywhere.

1323   * Dying means the application has encontered and unexpected situation,

1324   * i-e: something that should never occur during normal operation.

1325   * Examples: database broken, user changed URL by hand...

1326   *

1327   * @param string Message to output

1328   */
1329  function debug_die( $additional_info = '' )
1330  {
1331      global $debug, $baseurl;
1332      global $log_app_errors, $app_name, $is_cli;
1333  
1334      // Attempt to output an error header (will not work if the output buffer has already flushed once):

1335      // This should help preventing indexing robots from indexing the error :P

1336      if( ! headers_sent() )
1337      {
1338          global $io_charset;
1339          header('Content-type: text/html; charset='.$io_charset); // it's ok, if a previous header would be replaced;

1340          header('HTTP/1.0 500 Internal Server Error');
1341      }
1342  
1343      if( $is_cli )
1344      { // Command line interface, e.g. in cron_exec.php:
1345          echo '== '.T_('An unexpected error has occured!')." ==\n";
1346          echo T_('If this error persits, please report it to the administrator.')."\n";
1347          echo T_('Additional information about this error:')."\n";
1348          echo strip_tags( $additional_info );
1349      }
1350      else
1351      {
1352          echo '<div style="background-color: #fdd; padding: 1ex; margin-bottom: 1ex;">';
1353          echo '<h3 style="color:#f00;">'.T_('An unexpected error has occured!').'</h3>';
1354          echo '<p>'.T_('If this error persits, please report it to the administrator.').'</p>';
1355          echo '<p><a href="'.$baseurl.'">'.T_('Go back to home page').'</a></p>';
1356          echo '</div>';
1357  
1358          if( ! empty( $additional_info ) )
1359          {
1360              echo '<div style="background-color: #ddd; padding: 1ex; margin-bottom: 1ex;">';
1361              echo '<h3>'.T_('Additional information about this error:').'</h3>';
1362              echo $additional_info;
1363              echo '</div>';
1364          }
1365      }
1366  
1367      if( $log_app_errors > 1 || $debug )
1368      { // Prepare backtrace
1369          $backtrace = debug_get_backtrace();
1370  
1371          if( $log_app_errors > 1 || $is_cli )
1372          {
1373              $backtrace_cli = trim(strip_tags($backtrace));
1374          }
1375      }
1376  
1377      if( $log_app_errors )
1378      { // Log error through PHP's logging facilities:
1379          $log_message = $app_name.' error: ';
1380          if( ! empty($additional_info) )
1381          {
1382              $log_message .= trim( strip_tags($additional_info) );
1383          }
1384          else
1385          {
1386              $log_message .= 'No info specified in debug_die()';
1387          }
1388  
1389          // Get file and line info:

1390          $file = 'Unknown';
1391          $line = 'Unknown';
1392          if( function_exists('debug_backtrace') /* PHP 4.3 */ )
1393          { // get the file and line
1394              foreach( debug_backtrace() as $v )
1395              {
1396                  if( isset($v['function']) && $v['function'] == 'debug_die' )
1397                  {
1398                      $file = isset($v['file']) ? $v['file'] : 'Unknown';
1399                      $line = isset($v['line']) ? $v['line'] : 'Unknown';
1400                      break;
1401                  }
1402              }
1403          }
1404          $log_message .= ' in '.$file.' at line '.$line;
1405  
1406          if( $log_app_errors > 1 )
1407          { // Append backtrace:
1408              // indent after newlines:

1409              $backtrace_cli = preg_replace( '~(\S)(\n)(\S)~', '$1  $2$3', $backtrace_cli );
1410              $log_message .= "\nBacktrace:\n".$backtrace_cli;
1411          }
1412          $log_message .= "\nREQUEST_URI:  ".( isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '-' );
1413          $log_message .= "\nHTTP_REFERER: ".( isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '-' );
1414  
1415          error_log( str_replace("\n", ' / ', $log_message), 0 /* PHP's system logger */ );
1416      }
1417  
1418  
1419      // DEBUG OUTPUT:

1420      if( $debug )
1421      {
1422          if( $is_cli )
1423              echo $backtrace_cli;
1424          else
1425              echo $backtrace;
1426  
1427          debug_info();
1428      }
1429  
1430      // EXIT:

1431      if( ! $is_cli )
1432      { // Attempt to keep the html valid (but it doesn't really matter anyway)
1433          echo '</body></html>';
1434      }
1435      die();
1436  }
1437  
1438  
1439  /**

1440   * Outputs Bad request Error message. When in debug mode it also prints a backtrace.

1441   *

1442   * This should be used when a bad user input is detected?

1443   *

1444   * @param string Message to output

1445   */
1446  function bad_request_die( $additional_info = '' )
1447  {
1448      global $debug, $baseurl;
1449  
1450      // Attempt to output an error header (will not work if the output buffer has already flushed once):

1451      // This should help preventing indexing robots from indexing the error :P

1452      if( ! headers_sent() )
1453      {
1454          global $io_charset;
1455          header('Content-type: text/html; charset='.$io_charset); // it's ok, if a previous header would be replaced;

1456          header('HTTP/1.0 400 Bad Request');
1457      }
1458  
1459      echo '<div style="background-color: #fdd; padding: 1ex; margin-bottom: 1ex;">';
1460      echo '<h3 style="color:#f00;">'.T_('Bad Request!').'</h3>';
1461      echo '<p>'.T_('The parameters of your request are invalid.').'</p>';
1462      echo '<p>'.T_('If you have obtained this error by clicking on a link INSIDE of this site, please report the bad link to the administrator.').'</p>';
1463      echo '<p><a href="'.$baseurl.'">'.T_('Go back to home page').'</a></p>';
1464      echo '</div>';
1465  
1466      if( !empty( $additional_info ) )
1467      {
1468          echo '<div style="background-color: #ddd; padding: 1ex; margin-bottom: 1ex;">';
1469          echo '<h3>'.T_('Additional information about this error:').'</h3>';
1470          echo $additional_info;
1471          echo '</div>';
1472      }
1473  
1474      if( $debug )
1475      {
1476          echo debug_get_backtrace();
1477          debug_info();
1478      }
1479  
1480      // Attempt to keep the html valid (but it doesn't really matter anyway)

1481      die( '</body></html>' );
1482  }
1483  
1484  
1485  /**

1486   * Outputs debug info, according to {@link $debug} or $force param. This gets called typically at the end of the page.

1487   *

1488   * @todo dh> add get_debug_info() which returns and supports non-html format (for debug_die())

1489   * @param boolean true to force output regardless of {@link $debug}

1490   */
1491  function debug_info( $force = false )
1492  {
1493      global $debug, $Debuglog, $DB, $obhandler_debug, $Timer, $ReqHost, $ReqPath;
1494      global $cache_imgsize, $cache_File;
1495      global $Session;
1496      global $db_config, $tableprefix;
1497  
1498      if( ! $debug && ! $force )
1499      { // No debug output:
1500          return;
1501      }
1502  
1503      $ReqHostPathQuery = $ReqHost.$ReqPath.( empty( $_SERVER['QUERY_STRING'] ) ? '' : '?'.$_SERVER['QUERY_STRING'] );
1504      echo '<div class="debug"><h2>Debug info</h2>';
1505  
1506      $Debuglog->add( 'Len of serialized $cache_imgsize: '.strlen(serialize($cache_imgsize)), 'memory' );
1507      $Debuglog->add( 'Len of serialized $cache_File: '.strlen(serialize($cache_File)), 'memory' );
1508  
1509      if( !$obhandler_debug )
1510      { // don't display changing items when we want to test obhandler
1511  
1512          // Timer table:

1513          $time_page = $Timer->get_duration( 'total' );
1514          $timer_rows = array();
1515          foreach( $Timer->get_categories() as $l_cat )
1516          {
1517              if( $l_cat == 'sql_query' )
1518              {
1519                  continue;
1520              }
1521              $timer_rows[ $l_cat ] = $Timer->get_duration( $l_cat );
1522          }
1523          // arsort( $timer_rows );

1524          ksort( $timer_rows );
1525          echo '<table><thead>'
1526              .'<tr><th colspan="4" class="center">Timers</th></tr>'
1527              .'<tr><th>Category</th><th>Time</th><th>%</th><th>Count</th></tr>'
1528              .'</thead><tbody>';
1529  
1530          $table_rows_ignore_perhaps = array();
1531          foreach( $timer_rows as $l_cat => $l_time )
1532          {
1533              $percent_l_cat = $time_page > 0 ? number_format( 100/$time_page * $l_time, 2 ) : '0';
1534  
1535              $row = "\n<tr>"
1536                  .'<td>'.$l_cat.'</td>'
1537                  .'<td class="right">'.$l_time.'</td>'
1538                  .'<td class="right">'.$percent_l_cat.'%</td>'
1539                  .'<td class="right">'.$Timer->get_count( $l_cat ).'</td></tr>';
1540  
1541              if( $l_time < 0.005 )
1542              {
1543                  $table_rows_ignore_perhaps[] = $row;
1544              }
1545              else
1546              {
1547                  echo $row;
1548              }
1549          }
1550          $count_ignored = count($table_rows_ignore_perhaps);
1551          if( $count_ignored > 5 )
1552          {
1553              echo '<tr><td colspan="4" class="center"> + '.$count_ignored.' &lt; 0.005s </td></tr>';
1554          }
1555          else
1556          {
1557              echo implode( "\n", $table_rows_ignore_perhaps );
1558          }
1559          echo '</tbody>';
1560          echo '</table>';
1561  
1562          if( isset($DB) )
1563          {
1564              echo '<a href="'.$ReqHostPathQuery.'#evo_debug_queries">Database queries: '.$DB->num_queries.'.</a><br />';
1565          }
1566  
1567          foreach( array( // note: 8MB is default for memory_limit and is reported as 8388608 bytes
1568              'memory_get_usage' => array( 'display' => 'Memory usage', 'high' => 8000000 ),
1569              'memory_get_peak_usage' /* PHP 5.2 */ => array( 'display' => 'Memory peak usage', 'high' => 8000000 ) ) as $l_func => $l_var )
1570          {
1571              if( function_exists( $l_func ) )
1572              {
1573                  $_usage = $l_func();
1574                  if( $_usage > $l_var['high'] ) echo '<span style="color:red; font-weight:bold">';
1575                  echo $l_var['display'].': '.bytesreadable( $_usage );
1576                  if( $_usage > $l_var['high'] ) echo '</span>';
1577                  echo '<br />';
1578              }
1579          }
1580      }
1581  
1582  
1583      // DEBUGLOG(s) FROM PREVIOUS SESSIONS, after REDIRECT(s) (with list of categories at top):

1584      if( isset($Session) && ($sess_Debuglogs = $Session->get('Debuglogs')) && ! empty($sess_Debuglogs) )
1585      {
1586          $count_sess_Debuglogs = count($sess_Debuglogs);
1587          if( $count_sess_Debuglogs > 1 )
1588          { // Links to those Debuglogs:
1589              echo '<p>There are '.$count_sess_Debuglogs.' Debuglogs from redirected pages: ';
1590              for( $i = 1; $i <= $count_sess_Debuglogs; $i++ )
1591              {
1592                  echo '<a href="'.$ReqHostPathQuery.'#debug_sess_debuglog_'.$i.'">#'.$i.'</a> ';
1593              }
1594              echo '</p>';
1595          }
1596  
1597          foreach( $sess_Debuglogs as $k => $sess_Debuglog )
1598          {
1599              $log_categories = array( 'error', 'note', 'all' ); // Categories to output (in that order)

1600              $log_cats = array_keys($sess_Debuglog->get_messages( $log_categories )); // the real list (with all replaced and only existing ones)

1601              $log_container_head = '<h3 id="debug_sess_debuglog_'.($k+1).'" style="color:#f00;">Debug messages from redirected page (#'.($k+1).')</h3>'
1602                  // link to real Debuglog:

1603                  .'<p><a href="'.$ReqHostPathQuery.'#debug_debuglog">See below for the Debuglog from the current request.</a></p>';
1604              $log_head_links = array();
1605              foreach( $log_cats as $l_cat )
1606              {
1607                  $log_head_links[] .= '<a href="'.$ReqHostPathQuery.'#debug_redir_'.($k+1).'_info_cat_'.str_replace( ' ', '_', $l_cat ).'">'.$l_cat.'</a>';
1608              }
1609              $log_container_head .= implode( ' | ', $log_head_links );
1610              echo format_to_output(
1611                  $sess_Debuglog->display( array(
1612                          'container' => array( 'string' => $log_container_head, 'template' => false ),
1613                          'all' => array( 'string' => '<h4 id="debug_redir_'.($k+1).'_info_cat_%s">%s:</h4>', 'template' => false ) ),
1614                      '', false, $log_categories ),
1615                  'htmlbody' );
1616          }
1617  
1618          $Session->delete( 'Debuglogs' );
1619      }
1620  
1621  
1622      // CURRENT DEBUGLOG (with list of categories at top):

1623      $log_categories = array( 'error', 'note', 'all' ); // Categories to output (in that order)

1624      $log_cats = array_keys($Debuglog->get_messages( $log_categories )); // the real list (with all replaced and only existing ones)

1625      $log_container_head = '<h3 id="debug_debuglog">Debug messages</h3>';
1626      if( ! empty($sess_Debuglogs) )
1627      { // link to first sess_Debuglog:
1628          $log_container_head .= '<p><a href="'.$ReqHostPathQuery.'#debug_sess_debuglog_1">See above for the Debuglog(s) from before the redirect.</a></p>';
1629      }
1630      $log_head_links = array();
1631      foreach( $log_cats as $l_cat )
1632      {
1633          $log_head_links[] .= '<a href="'.$ReqHostPathQuery.'#debug_info_cat_'.str_replace( ' ', '_', $l_cat ).'">'.$l_cat.'</a>';
1634      }
1635      $log_container_head .= implode( ' | ', $log_head_links );
1636      echo format_to_output(
1637          $Debuglog->display( array(
1638                  'container' => array( 'string' => $log_container_head, 'template' => false ),
1639                  'all' => array( 'string' => '<h4 id="debug_info_cat_%s">%s:</h4>', 'template' => false ) ),
1640              '', false, $log_categories ),
1641          'htmlbody' );
1642  
1643  
1644      echo '<h3 id="evo_debug_queries">DB</h3>';
1645  
1646      if($db_config)
1647      {
1648          echo '<pre>',
1649          T_('DB Username').': '.$db_config['user']."\n".
1650          T_('DB Database').': '.$db_config['name']."\n".
1651          T_('DB Host').': '.$db_config['host']."\n".
1652          T_('DB tables prefix').': '.$tableprefix."\n".
1653          T_('DB connection charset').': '.$db_config['connection_charset']."\n".
1654          '</pre>';
1655      }
1656  
1657      if( !isset($DB) )
1658      {
1659          echo 'No DB object.';
1660      }
1661      else
1662      {
1663          $DB->dump_queries();
1664      }
1665      echo '</div>';
1666  }
1667  
1668  
1669  /**

1670   * Sends a mail, wrapping PHP's mail() function.

1671   *

1672   * {@link $current_locale} will be used to set the charset.

1673   *

1674   * Note: we use a single \n as line ending, though it does not comply to

1675   * {@link http://www.faqs.org/rfcs/rfc2822 RFC2822}, but seems to be safer,

1676   * because some mail transfer agents replace \n by \r\n automatically.

1677   *

1678   * @todo Unit testing with "nice addresses" This gets broken over and over again.

1679   *

1680   * @param string Recipient, either email only or in "Name <example@example.com>" format (RFC2822).

1681   *               Can be multiple comma-separated addresses.

1682   * @param string Subject of the mail

1683   * @param string The message text

1684   * @param string From address, being added to headers (we'll prevent injections);

1685   *               see {@link http://securephp.damonkohler.com/index.php/Email_Injection}.

1686   *               Might be just an email address or of the same form as {@link $to}.

1687   *               {@link $notify_from} gets used as default (if NULL).

1688   * @param array Additional headers ( headername => value ). Take care of injection!

1689   * @return boolean True if mail could be sent (not necessarily delivered!), false if not - (return value of {@link mail()})

1690   */
1691  function send_mail( $to, $subject, $message, $from = NULL, $headers = array() )
1692  {
1693      global $debug, $app_name, $app_version, $current_locale, $current_charset, $evo_charset, $locales, $Debuglog, $notify_from;
1694  
1695      $NL = "\n";
1696  
1697      if( is_windows() )
1698      {    // fplanque: Windows XP, Apache 1.3, PHP 4.4, MS SMTP : will not accept "nice" addresses.
1699          $to = preg_replace( '/^.*?<(.+?)>$/', '$1', $to );
1700          $from = preg_replace( '/^.*?<(.+?)>$/', '$1', $from );
1701      }
1702  
1703      // echo 'sending email to: ['.htmlspecialchars($to).'] from ['.htmlspecialchars($from).']';

1704  
1705      if( !is_array( $headers ) )
1706      { // Make sure $headers is an array
1707          $headers = array( $headers );
1708      }
1709  
1710      // Specify charset and content-type of email

1711      $headers['Content-Type'] = 'text/plain; charset='.$current_charset;
1712      $headers['X-Mailer'] = $app_name.' '.$app_version.' - PHP/'.phpversion();
1713      $headers['X-Remote-Addr'] = implode( ',', get_ip_list() );
1714  
1715      // -- Build headers ----

1716      if( empty($from) )
1717      {
1718          $from = $notify_from;
1719      }
1720      else
1721      {
1722          $from = trim($from);
1723      }
1724  
1725      if( ! empty($from) )
1726      { // From has to go into headers
1727          $from_save = preg_replace( '~(\r|\n).*$~s', '', $from ); // Prevent injection! (remove everything after (and including) \n or \r)

1728  
1729          if( $from != $from_save )
1730          {
1731              if( strpos( $from_save, '<' ) !== false && !strpos( $from_save, '>' ) )
1732              { // We have probably stripped the '>' at the end!
1733                  $from_save .= '>';
1734              }
1735  
1736              // Add X-b2evo notification mail header about this

1737              $headers['X-b2evo'] = 'Fixed email header injection (From)';
1738              $Debuglog->add( 'Detected email injection! Fixed &laquo;'.htmlspecialchars($from).'&raquo; to &laquo;'.htmlspecialchars($from_save).'&raquo;.', 'security' );
1739  
1740              $from = $from_save;
1741          }
1742  
1743          $headers['From'] = $from;
1744      }
1745  
1746      $headerstring = '';
1747      reset( $headers );
1748      while( list( $lKey, $lValue ) = each( $headers ) )
1749      { // Add additional headers
1750          $headerstring .= $lKey.': '.$lValue.$NL;
1751      }
1752  
1753      if( function_exists('mb_encode_mimeheader') )
1754      { // encode subject
1755          $subject = mb_encode_mimeheader( $subject, mb_internal_encoding(), 'B', $NL );
1756      }
1757  
1758      $message = str_replace( array( "\r\n", "\r" ), $NL, $message );
1759  
1760      // Convert encoding of message (from internal encoding to the one of the message):

1761      $message = convert_charset( $message, $current_charset, $evo_charset );
1762  
1763      if( $debug > 1 )
1764      {    // We agree to die for debugging...
1765          if( ! mail( $to, $subject, $message, $headerstring ) )
1766          {
1767              debug_die( 'Sending mail from &laquo;'.htmlspecialchars($from).'&raquo; to &laquo;'.htmlspecialchars($to).'&raquo;, Subject &laquo;'.htmlspecialchars($subject).'&raquo; FAILED.' );
1768          }
1769      }
1770      else
1771      {    // Soft debugging only....
1772          if( ! @mail( $to, $subject, $message, $headerstring ) )
1773          {
1774              $Debuglog->add( 'Sending mail from &laquo;'.htmlspecialchars($from).'&raquo; to &laquo;'.htmlspecialchars($to).'&raquo;, Subject &laquo;'.htmlspecialchars($subject).'&raquo; FAILED.', 'error' );
1775              return false;
1776          }
1777      }
1778  
1779      $Debuglog->add( 'Sent mail from &laquo;'.htmlspecialchars($from).'&raquo; to &laquo;'.htmlspecialchars($to).'&raquo;, Subject &laquo;'.htmlspecialchars($subject).'&raquo;.' );
1780      return true;
1781  }
1782  
1783  
1784  
1785  /**

1786   * If first parameter evaluates to true printf() gets called using the first parameter

1787   * as args and the second parameter as print-pattern

1788   *

1789   * @param mixed variable to test and output if it's true or $disp_none is given

1790   * @param string printf-pattern to use (%s gets replaced by $var)

1791   * @param string printf-pattern to use, if $var is numeric and > 1 (%s gets replaced by $var)

1792   * @param string printf-pattern to use if $var evaluates to false (%s gets replaced by $var)

1793   */
1794  function disp_cond( $var, $disp_one, $disp_more = NULL, $disp_none = NULL )
1795  {
1796      if( is_numeric($var) && $var > 1 )
1797      {
1798          printf( ( $disp_more === NULL ? $disp_one : $disp_more ), $var );
1799          return true;
1800      }
1801      elseif( $var )
1802      {
1803          printf( $disp_one, $var );
1804          return true;
1805      }
1806      else
1807      {
1808          if( $disp_none !== NULL )
1809          {
1810              printf( $disp_none, $var );
1811              return false;
1812          }
1813      }
1814  }
1815  
1816  
1817  /**

1818   * Create IMG tag for an action icon.

1819   *

1820   * @param string TITLE text (IMG and A link)

1821   * @param string icon code for {@link get_icon()}

1822   * @param string URL where the icon gets linked to (empty to not wrap the icon in a link)

1823   * @param string word to be displayed after icon

1824   * @param integer 1-5: weight of the icon. the icon will be displayed only if its weight is >= than the user setting threshold

1825   *                     Use 5, if it's a required icon - all others could get disabled by the user.

1826   * @param integer 1-5: weight of the word. the word will be displayed only if its weight is >= than the user setting threshold

1827   * @param array Additional attributes to the A tag. The values must be properly encoded for html output (e.g. quotes).

1828   *        It may also contain these params:

1829   *         - 'use_js_popup': if true, the link gets opened as JS popup. You must also pass an "id" attribute for this!

1830   *         - 'use_js_size': use this to override the default popup size ("500, 400")

1831   *         - 'class': defaults to 'action_icon', if not set; use "" to not use it

1832   * @return string The generated action icon link.

1833   */
1834  function action_icon( $title, $icon, $url, $word = NULL, $icon_weight = 4, $word_weight = 1, $link_attribs = array() )
1835  {
1836      global $UserSettings;
1837  
1838      $link_attribs['href'] = $url;
1839      $link_attribs['title'] = $title;
1840  
1841      if( ! isset($link_attribs['class']) )
1842      {
1843          $link_attribs['class'] = 'action_icon';
1844      }
1845  
1846      if( get_icon( $icon, 'rollover' ) )
1847      {
1848          if( empty($link_attribs['class']) )
1849          {
1850              $link_attribs['class'] = 'rollover';
1851          }
1852          else
1853          {
1854              $link_attribs['class'] .= ' rollover';
1855          }
1856      }
1857  
1858      // "use_js_popup": open link in a JS popup

1859      if( ! empty($link_attribs['use_js_popup']) )
1860      {
1861          $popup_js = 'var win = new PopupWindow(); win.setUrl( \''.$link_attribs['href'].'\' ); win.setSize(  ); ';
1862  
1863          if( isset($link_attribs['use_js_size']) )
1864          {
1865              if( ! empty($link_attribs['use_js_size']) )
1866              {
1867                  $popup_size = $link_attribs['use_js_size'];
1868              }
1869          }
1870          else
1871          {
1872              $popup_size = '500, 400';
1873          }
1874          if( isset($popup_size) )
1875          {
1876              $popup_js .= 'win.setSize( '.$popup_size.' ); ';
1877          }
1878          $popup_js .= 'win.showPopup(\''.$link_attribs['id'].'\'); return false;';
1879  
1880          if( empty( $link_attribs['onclick'] ) )
1881          {
1882              $link_attribs['onclick'] = $popup_js;
1883          }
1884          else
1885          {
1886              $link_attribs['onclick'] .= $popup_js;
1887          }
1888          unset($link_attribs['use_js_popup']);
1889          unset($link_attribs['use_js_size']);
1890      }
1891  
1892      // NOTE: We do not use format_to_output with get_field_attribs_as_string() here, because it interferes with the Results class (eval() fails on entitied quotes..) (blueyed)

1893      $r = '<a '.get_field_attribs_as_string( $link_attribs, false ).'>';
1894  
1895      $display_icon = ($icon_weight >= $UserSettings->get('action_icon_threshold'));
1896      $display_word = ($word_weight >= $UserSettings->get('action_word_threshold'));
1897  
1898      if( $display_icon || ! $display_word )
1899      {    // We MUST display an action icon in order to make the user happy:
1900          // OR we default to icon because the user doesn't want the word either!!

1901  
1902          $r .= get_icon( $icon, 'imgtag', array( 'title'=>$title ), true );
1903      }
1904  
1905      if( $display_word )
1906      {    // We MUST display an action word in order to make the user happy:
1907  
1908          if( $display_icon )
1909          { // We already have an icon, display a SHORT word:
1910              if( !empty($word) )
1911              {    // We have provided a short word:
1912                  $r .= $word;
1913              }
1914              else
1915              {    // We fall back to alt:
1916                  $r .= get_icon( $icon, 'legend' );
1917              }
1918          }
1919          else
1920          {    // No icon display, let's display a LONG word/text:
1921              $r .= trim( $title, ' .!' );
1922          }
1923      }
1924  
1925      $r .= '</a>';
1926  
1927      return $r;
1928  }
1929  
1930  
1931  /**

1932   * Get properties of an icon.

1933   *

1934   * Note: to get a file type icon, use {@link File::get_icon()} instead.

1935   *

1936   * @uses get_icon_info()

1937   * @param string icon for what? (key)

1938   * @param string what to return for that icon ('imgtag', 'alt', 'legend', 'file', 'url', 'size' {@link imgsize()})

1939   * @param array additional params (

1940   *              'class' => class name when getting 'imgtag',

1941   *              'size' => param for 'size',

1942   *              'title' => title attribute for 'imgtag')

1943   * @param boolean true to include this icon into the legend at the bottom of the page (works for 'imgtag' only)

1944   */
1945  function get_icon( $iconKey, $what = 'imgtag', $params = NULL, $include_in_legend = false )
1946  {
1947      global $basepath, $admin_subdir, $baseurl, $Debuglog,    $IconLegend, $use_strict;
1948      global $conf_path;
1949  
1950      if( ! function_exists('get_icon_info') )
1951      {
1952          require_once $conf_path.'_icons.php';
1953      }
1954  
1955      $icon = get_icon_info($iconKey);
1956      if( ! $icon || ! isset( $icon['file'] ) )
1957      {
1958          return '[no image defined for '.var_export( $iconKey, true ).'!]';
1959      }
1960  
1961      switch( $what )
1962      {
1963          case 'rollover':
1964              if( isset( $icon['rollover'] ) )
1965              {    // Image has rollover available
1966                  return $icon['rollover'];
1967              }
1968              return false;
1969              /* BREAK */

1970  
1971  
1972          case 'file':
1973              return $basepath.$icon['file'];
1974              /* BREAK */

1975  
1976  
1977          case 'alt':
1978              if( isset( $icon['alt'] ) )
1979              { // alt tag from $map_iconfiles
1980                  return $icon['alt'];
1981              }
1982              else
1983              { // fallback to $iconKey as alt-tag
1984                  return $iconKey;
1985              }
1986              /* BREAK */

1987  
1988  
1989          case 'legend':
1990              if( isset( $icon['legend'] ) )
1991              { // legend tag from $map_iconfiles
1992                  return $icon['legend'];
1993              }
1994              else
1995              if( isset( $icon['alt'] ) )
1996              { // alt tag from $map_iconfiles
1997                  return $icon['alt'];
1998              }
1999              else
2000              { // fallback to $iconKey as alt-tag
2001                  return $iconKey;
2002              }
2003              /* BREAK */

2004  
2005  
2006          case 'class':
2007              if( isset($icon['class']) )
2008              {
2009                  return $icon['class'];
2010              }
2011              else
2012              {
2013                  return '';
2014              }
2015              /* BREAK */

2016  
2017          case 'url':
2018              return $baseurl.$icon['file'];
2019              /* BREAK */

2020  
2021          case 'size':
2022              if( !isset( $icon['size'] ) )
2023              {
2024                  $Debuglog->add( 'No iconsize for ['.$iconKey.']', 'icons' );
2025  
2026                  $icon['size'] = imgsize( $icon['file'] );
2027              }
2028  
2029              switch( $params['size'] )
2030              {
2031                  case 'width':
2032                      return $icon['size'][0];
2033  
2034                  case 'height':
2035                      return $icon['size'][1];
2036  
2037                  case 'widthxheight':
2038                      return $icon['size'][0].'x'.$icon['size'][1];
2039  
2040                  case 'width':
2041                      return $icon['size'][0];
2042  
2043                  case 'string':
2044                      return 'width="'.$icon['size'][0].'" height="'.$icon['size'][1].'"';
2045  
2046                  default:
2047                      return $icon['size'];
2048              }
2049              /* BREAK */

2050  
2051  
2052          case 'imgtag':
2053              $r = '<img src="'.$baseurl.$icon['file'].'" ';
2054  
2055              if( !$use_strict )
2056              {    // Include non CSS fallbacks - transitional only:
2057                  $r .= 'border="0" align="top" ';
2058              }
2059  
2060              // Include class (will default to "icon"):

2061              if( ! isset( $params['class'] ) )
2062              {
2063                  if( isset($icon['class']) )
2064                  {    // This icon has a class
2065                      $params['class'] = $icon['class'];
2066                  }
2067                  else
2068                  {
2069                      $params['class'] = '';
2070                  }
2071              }
2072  
2073              // Include size (optional):

2074              if( isset( $icon['size'] ) )
2075              {
2076                  $r .= 'width="'.$icon['size'][0].'" height="'.$icon['size'][1].'" ';
2077              }
2078  
2079              // Include alt (XHTML mandatory):

2080              if( ! isset( $params['alt'] ) )
2081              {
2082                  if( isset( $icon['alt'] ) )
2083                  { // alt-tag from $map_iconfiles
2084                      $params['alt'] = $icon['alt'];
2085                  }
2086                  else
2087                  { // $iconKey as alt-tag
2088                      $params['alt'] = $iconKey;
2089                  }
2090              }
2091  
2092              // Add all the attributes:

2093              $r .= get_field_attribs_as_string( $params, false );
2094  
2095              // Close tag:

2096              $r .= '/>';
2097  
2098  
2099              if( $include_in_legend && isset( $IconLegend ) )
2100              { // This icon should be included into the legend:
2101                  $IconLegend->add_icon( $iconKey );
2102              }
2103  
2104              return $r;
2105              /* BREAK */

2106  
2107          case 'noimg':
2108              $blank_icon = get_icon_info('pixel');
2109  
2110              $r = '<img src="'.$baseurl.$blank_icon['file'].'" ';
2111  
2112              // Include non CSS fallbacks:

2113              $r .= 'border="0" align="top" ';
2114  
2115              // Include class (will default to "noicon"):

2116              if( ! isset( $params['class'] ) )
2117              {
2118                  if( isset($icon['class']) )
2119                  {    // This icon has a class
2120                      $params['class'] = $icon['class'];
2121                  }
2122                  else
2123                  {
2124                      $params['class'] = 'no_icon';
2125                  }
2126              }
2127  
2128              // Include size (optional):

2129              if( isset( $icon['size'] ) )
2130              {
2131                  $r .= 'width="'.$icon['size'][0].'" height="'.$icon['size'][1].'" ';
2132              }
2133  
2134              // Include alt (XHTML mandatory):

2135              if( ! isset( $params['alt'] ) )
2136              {
2137                  $params['alt'] = '';
2138              }
2139  
2140              // Add all the attributes:

2141              $r .= get_field_attribs_as_string( $params, false );
2142  
2143              // Close tag:

2144              $r .= '/>';
2145  
2146              return $r;
2147              /* BREAK */

2148  
2149      }
2150  }
2151  
2152  
2153  /**

2154   * @param string date (YYYY-MM-DD)

2155   * @param string time

2156   */
2157  function form_date( $date, $time = '' )
2158  {
2159      return substr( $date.'          ', 0, 10 ).' '.$time;
2160  }
2161  
2162  
2163  /**

2164   * Displays an empty or a full bullet based on boolean

2165   *

2166   * @param boolean true for full bullet, false for empty bullet

2167   */
2168  function bullet( $bool )
2169  {
2170      if( $bool )
2171          return get_icon( 'bullet_full', 'imgtag' );
2172  
2173      return get_icon( 'bullet_empty', 'imgtag' );
2174  }
2175  
2176  
2177  /**

2178   * Get list of client IP addresses from REMOTE_ADDR and HTTP_X_FORWARDED_FOR,

2179   * in this order. '' is used when no IP could be found.

2180   *

2181   * @param boolean True, to get only the first IP (probably REMOTE_ADDR)

2182   * @return array|string Depends on first param.

2183   */
2184  function get_ip_list( $firstOnly = false )
2185  {
2186      $r = array();
2187  
2188      if( !empty( $_SERVER['REMOTE_ADDR'] ) )
2189      {
2190          foreach( explode( ',', $_SERVER['REMOTE_ADDR'] ) as $l_ip )
2191          {
2192              $l_ip = trim($l_ip);
2193              if( !empty($l_ip) )
2194              {
2195                  $r[] = $l_ip;
2196              }
2197          }
2198      }
2199  
2200      if( !empty($_SERVER['HTTP_X_FORWARDED_FOR']) )
2201      { // IP(s) behind Proxy - this can be easily forged!
2202          foreach( explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] ) as $l_ip )
2203          {
2204              $l_ip = trim($l_ip);
2205              if( !empty($l_ip) && $l_ip != 'unknown' )
2206              {
2207                  $r[] = $l_ip;
2208              }
2209          }
2210      }
2211  
2212      if( !isset( $r[0] ) )
2213      { // No IP found.
2214          $r[] = '';
2215      }
2216  
2217      return $firstOnly ? $r[0] : $r;
2218  }
2219  
2220  
2221  /**

2222   * Get the base domain (without protocol and any subdomain) of an URL.

2223   *

2224   * Gets a max of 3 domain parts (x.y.tld)

2225   *

2226   * @param string URL

2227   * @return string the base domain (may become empty, if found invalid)

2228   */
2229  function get_base_domain( $url )
2230  {
2231      //echo '<p>'.$url;

2232      // Chop away the http part and the path:

2233      $domain = preg_replace( '~^([a-z]+://)?([^:/#]+)(.*)$~i', '\\2', $url );
2234  
2235      if( empty($domain) || preg_match( '~^([0-9]+\.)+$~', $domain ) )
2236      {    // Empty or All numeric = IP address, don't try to cut it any further
2237          return $domain;
2238      }
2239  
2240      //echo '<br>'.$domain;

2241  
2242      // Get the base domain up to 3 levels (x.y.tld):

2243      // NOTE: "_" is not really valid, but for Windows it is..

2244      // TODO: dh> this should also handle IDN "raw" domains with umlauts..

2245      // NOTE: \w does not always match umlauts.. it's based on setlocale().. (fp> hum, it was too good to be real :P)

2246      // NOTE: \w includes "_"

2247      if( ! preg_match( '~  ( \w (\w|-|_)* \. ){0,2}   \w (\w|-|_)*  $~ix', $domain, $matches ) )
2248      {
2249          return '';
2250      }
2251      $base_domain = $matches[0];
2252  
2253      // Remove any www*. prefix:

2254      $base_domain = preg_replace( '~^(www (\w|-|_)* \. )~xi', '', $base_domain );
2255  
2256      //echo '<br>'.$base_domain.'</p>';

2257  
2258      return $base_domain;
2259  }
2260  
2261  
2262  /**

2263   * Generate a valid key of size $length.

2264   *

2265   * @param integer length of key

2266   * @param string chars to use in generated key

2267   * @return string key

2268   */
2269  function generate_random_key( $length = 32, $keychars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' )
2270  {
2271      $key = '';
2272      $rnd_max = strlen($keychars) - 1;
2273  
2274      for( $i = 0; $i < $length; $i++ )
2275      {
2276          $key .= $keychars{mt_rand(0, $rnd_max)}; // get a random character out of $keychars

2277      }
2278  
2279      return $key;
2280  }
2281  
2282  
2283  /**

2284   * Generate a random password with no ambiguous chars

2285   *

2286   * @param integer length of password

2287   * @return string password

2288   */
2289  function generate_random_passwd( $length = 8 )
2290  {
2291      // fp> NOTE: do not include any characters that would make autogenerated passwords ambiguous

2292      // 1 (one) vs l (L) vs I (i)

2293      // O (letter) vs 0 (digit)

2294  
2295      return generate_random_key( $length, 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789' );
2296  }
2297  
2298  
2299  /**

2300   * Sends HTTP headers to avoid caching of the page.

2301   */
2302  function header_nocache()
2303  {
2304      header('Expires: Tue, 25 Mar 2003 05:00:00 GMT');
2305      header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
2306      header('Cache-Control: no-cache, must-revalidate');
2307      header('Pragma: no-cache');
2308  }
2309  
2310  
2311  /**

2312   * Sends HTTP header to redirect to the previous location (which

2313   * can be given as function parameter, GET parameter (redirect_to),

2314   * is taken from {@link Hit::$referer} or {@link $baseurl}).

2315   *

2316   * {@link $Debuglog} and {@link $Messages} get stored in {@link $Session}, so they

2317   * are available after the redirect.

2318   *

2319   * NOTE: This function {@link exit() exits} the php script execution.

2320   *

2321   * @todo fp> do NOT allow $redirect_to = NULL. This leads to spaghetti code and unpredictable behavior.

2322   *

2323   * @param string URL to redirect to (overrides detection)

2324   * @param boolean is this a permanent redirect? if true, send a 301; otherwise a 303

2325   */
2326  function header_redirect( $redirect_to = NULL, $permanent = false )
2327  {
2328      global $Hit, $baseurl, $Blog, $htsrv_url_sensitive;
2329      global $Session, $Debuglog, $Messages;
2330  
2331      // fp> get this out

2332      if( empty($redirect_to) )
2333      { // see if there's a redirect_to request param given (where & is encoded as &amp;):
2334          $redirect_to = param( 'redirect_to', 'string', '' );
2335  
2336          if( empty($redirect_to) )
2337          {
2338              if( ! empty($Hit->referer) )
2339              {
2340                  $redirect_to = $Hit->referer;
2341              }
2342              elseif( isset($Blog) && is_object($Blog) )
2343              {
2344                  $redirect_to = $Blog->get('url');
2345              }
2346              else
2347              {
2348                  $redirect_to = $baseurl;
2349              }
2350          }
2351      }
2352      // <fp

2353  
2354      /* fp>why do we need this?

2355      if( substr($redirect_to, 0, 1) == '/' )

2356      { // relative URL, prepend current host:

2357          global $ReqHost;

2358  

2359          $redirect_to = $ReqHost.$redirect_to;

2360      }

2361      */
2362  
2363      if( strpos($redirect_to, $htsrv_url_sensitive) === 0 /* we're going somewhere on $htsrv_url_sensitive */
2364       || strpos($redirect_to, $baseurl) === 0   /* we're going somewhere on $baseurl */ )
2365      {
2366          // Remove login and pwd parameters from URL, so that they do not trigger the login screen again:

2367          // Also remove "action" get param to avoid unwanted actions

2368          // blueyed> Removed the removing of "action" here, as it is used to trigger certain views. Instead, "confirm(ed)?" gets removed now

2369          // fp> which views please (important to list in order to remove asap)

2370          // dh> sorry, don't remember

2371          // TODO: fp> action should actually not be used to trigger views. This should be changed at some point.

2372          $redirect_to = preg_replace( '~(?<=\?|&) (login|pwd|confirm(ed)?) = [^&]+ ~x', '', $redirect_to );
2373      }
2374  
2375  
2376      $status = $permanent ? 301 : 303;
2377       $Debuglog->add('Redirecting to '.$redirect_to.' (status '.$status.')');
2378  
2379      // Transfer of Debuglog to next page:

2380      if( $Debuglog->count('all') )
2381      { // Save Debuglog into Session, so that it's available after redirect (gets loaded by Session constructor):
2382          $sess_Debuglogs = $Session->get('Debuglogs');
2383          if( empty($sess_Debuglogs) )
2384              $sess_Debuglogs = array();
2385  
2386          $sess_Debuglogs[] = $Debuglog;
2387          $Session->set( 'Debuglogs', $sess_Debuglogs, 60 /* expire in 60 seconds */ );
2388      }
2389  
2390      // Transfer of Messages to next page:

2391      if( $Messages->count('all') )
2392      { // Set Messages into user's session, so they get restored on the next page (after redirect):
2393          $Session->set( 'Messages', $Messages );
2394      }
2395  
2396      $Session->dbsave(); // If we don't save now, we run the risk that the redirect goes faster than the PHP script shutdown.

2397  
2398       // see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

2399      if( $permanent )
2400      {    // This should be a permanent move redirect!
2401          header( 'HTTP/1.1 301 Moved Permanently' );
2402      }
2403      else
2404      {    // This should be a "follow up" redirect
2405          // Note: Also see http://de3.php.net/manual/en/function.header.php#50588 and the other comments around

2406          header( 'HTTP/1.1 303 See Other' );
2407      }
2408  
2409      header( 'Location: '.$redirect_to, true, $status ); // explictly setting the status is required for (fast)cgi

2410      exit();
2411  }
2412  
2413  
2414  function is_create_action( $action )
2415  {
2416      $action_parts = explode( '_', $action );
2417  
2418      switch( $action_parts[0] )
2419      {
2420          case 'new':
2421          case 'new_switchtab':
2422          case 'copy':
2423          case 'create':    // we return in this state after a validation error
2424              return true;
2425  
2426          case 'edit':
2427          case 'edit_switchtab':
2428          case 'update':    // we return in this state after a validation error
2429          case 'delete':
2430          // The following one's a bit far fetched, but can happen if we have no sheet display:

2431          case 'unlink':
2432          case 'view':
2433              return false;
2434  
2435          default:
2436              debug_die( 'Unhandled action in form: '.strip_tags($action_parts[0]) );
2437      }
2438  }
2439  
2440  
2441  /**

2442   * Generate a link that toggles display of an element on clicking.

2443   *

2444   * @todo Provide functionality to make those links accessible without JS (using GET parameter)

2445   * @uses toggle_display_by_id() (JS)

2446   * @param string ID (html) of the link

2447   * @param string ID (html) of the target to toggle displaying

2448   * @return string

2449   */
2450  function get_link_showhide( $link_id, $target_id, $text_when_displayed, $text_when_hidden, $display_hidden = true )
2451  {
2452      $html = "<a id='$link_id' href='#' onclick='return toggle_display_by_id(\"$link_id\", \"$target_id\", \""
2453          .jsspecialchars( $text_when_displayed ).'", "'.jsspecialchars( $text_when_hidden ).'")\'>'
2454          .( $display_hidden ? $text_when_hidden : $text_when_displayed )
2455          .'</a>';
2456  
2457      return $html;
2458  }
2459  
2460  
2461  /**

2462   * Escape a string to be used in Javascript.

2463   *

2464   * @param string

2465   * @return string

2466   */
2467  function jsspecialchars($s)
2468  {
2469      $r = str_replace(
2470          array(  '\\', '"', "'" ),
2471          array( '\\\\', '\"', "\'" ),
2472          $s );
2473      return htmlspecialchars($r, ENT_QUOTES);
2474  }
2475  
2476  
2477  /**

2478   * Compact a date in a number keeping only integer value of the string

2479   *

2480   * @param string date

2481   */
2482  function compact_date( $date )
2483  {
2484       return preg_replace( '#[^0-9]#', '', $date );
2485  }
2486  
2487  
2488  /**

2489   * Decompact a date in a date format ( Y-m-d h:m:s )

2490   *

2491   * @param string date

2492   */
2493  function decompact_date( $date )
2494  {
2495      $date0 = $date;
2496  
2497      return  substr($date0,0,4).'-'.substr($date0,4,2).'-'.substr($date0,6,2).' '
2498                                  .substr($date0,8,2).':'.substr($date0,10,2).':'.substr($date0,12,2);
2499  }
2500  
2501  /**

2502   * Check the format of the phone number param and

2503   * format it in a french number if it is.

2504   *

2505   * @param string phone number

2506   */
2507  function format_phone( $phone, $hide_country_dialing_code_if_same_as_locale = true )
2508  {
2509      global $CountryCache;
2510  
2511      $dialing_code = NULL;
2512  
2513      if( substr( $phone, 0, 1 ) == '+' )
2514      {    // We have a dialing code in the phone, so we extract it:
2515          $dialing_code = $CountryCache->extract_country_dialing_code( substr( $phone, 1 ) );
2516      }
2517  
2518      if( !is_null( $dialing_code ) && ( locale_dialing_code() == $dialing_code )
2519              && $hide_country_dialing_code_if_same_as_locale )
2520      {    // The phone dialing code is same as locale and we want to hide it in this case
2521          if( ( strlen( $phone ) - strlen( $dialing_code ) ) == 10 )
2522          {    // We can format it like a french phone number ( 0x.xx.xx.xx.xx )
2523              $phone_formated = format_french_phone( '0'.substr( $phone, strlen( $dialing_code )+1 ) );
2524          }
2525          else
2526          { // ( 0xxxxxxxxxxxxxx )
2527              $phone_formated = '0'.substr( $phone, strlen( $dialing_code )+1 );
2528          }
2529  
2530      }
2531      elseif( !is_null( $dialing_code ) )
2532      {    // Phone has a dialing code
2533          if( ( strlen( $phone ) - strlen( $dialing_code ) ) == 10 )
2534          { // We can format it like a french phone number with the dialing code ( +dialing x.xx.xx.xx.xx )
2535              $phone_formated = '+'.$dialing_code.format_french_phone( ' '.substr( $phone, strlen( $dialing_code )+1 ) );
2536          }
2537          else
2538          { // ( +dialing  xxxxxxxxxxx )
2539              $phone_formated = '+'.$dialing_code.' '.substr( $phone, strlen( $dialing_code )+1 );
2540          }
2541      }
2542      else
2543      {
2544          if( strlen( $phone ) == 10 )
2545          { //  We can format it like a french phone number ( xx.xx.xx.xx.xx )
2546              $phone_formated = format_french_phone( $phone );
2547          }
2548          else
2549          {    // We don't format phone: TODO generic format phone ( xxxxxxxxxxxxxxxx )
2550              $phone_formated = $phone;
2551          }
2552      }
2553  
2554      return $phone_formated;
2555  }
2556  
2557  
2558  /**

2559   * Format a string in a french phone number

2560   *

2561   * @param string phone number

2562   */
2563  function format_french_phone( $phone )
2564  {
2565      return substr($phone, 0 , 2).'.'.substr($phone, 2, 2).'.'.substr($phone, 4, 2)
2566                      .'.'.substr($phone, 6, 2).'.'.substr($phone, 8, 2);
2567  }
2568  
2569  
2570  /**

2571   * Generate a link to a online help resource.

2572   * testing the concept of online help (aka webhelp).

2573   * this function should be relocated somewhere better if it is taken onboard by the project

2574   *

2575   * @todo replace [?] with icon,

2576   * @todo write url suffix dynamically based on topic and language

2577   *

2578   * QUESTION: launch new window with javascript maybe?

2579   * @param string Topic

2580   *        The topic should be in a format like [\w]+(/[\w]+)*, e.g features/online_help.

2581   * @return string

2582   */
2583  function get_manual_link( $topic )
2584  {
2585      global $Settings, $current_locale, $app_shortname, $app_version;
2586  
2587      if( $Settings->get('webhelp_enabled') )
2588      {
2589          $manual_url = 'http://manual.b2evolution.net/redirect/'.str_replace(" ","_",strtolower($topic)).'?lang='.$current_locale.'&amp;app='.$app_shortname.'&amp;version='.$app_version;
2590  
2591          $webhelp_link = action_icon( T_('Open relevant page in online manual'), 'manual', $manual_url, T_('Manual'), 5, 1, array( 'target' => '_blank' ) );
2592  
2593          return ' '.$webhelp_link;
2594      }
2595      else
2596      {
2597          return '';
2598      }
2599  }
2600  
2601  
2602  /**

2603   * Build a string out of $field_attribs, with each attribute

2604   * prefixed by a space character.

2605   *

2606   * @param array Array of field attributes.

2607   * @param boolean Use format_to_output() for the attributes?

2608   * @return string

2609   */
2610  function get_field_attribs_as_string( $field_attribs, $format_to_output = true )
2611  {
2612      $r = '';
2613  
2614      if( empty( $field_attribs ) )
2615      { // TODO: This extra check should not be needed, if $field_attribs is an array! (blueyed)
2616          return $r;
2617      }
2618  
2619      foreach( $field_attribs as $l_attr => $l_value )
2620      {
2621          if( $l_value === '' || $l_value === NULL )
2622          { // don't generate empty attributes (it may be NULL if we pass 'value' => NULL as field_param for example, because isset() does not match it!)
2623              continue;
2624          }
2625  
2626          if( $format_to_output )
2627          {
2628              if( $l_attr == 'value' )
2629              {
2630                  $r .= ' '.$l_attr.'="'.format_to_output( $l_value, 'formvalue' ).'"';
2631              }
2632              else
2633              {
2634                  // TODO: this uses strip_tags et al! Shouldn't we just use "formvalue" always? (E.g. it kills "for( var i=0; i<a; i++ )..." (in an onclick attr) from "<a" on. The workaround is to use spaces ("i < a"), but I was confused first)

2635                  $r .= ' '.$l_attr.'="'.format_to_output( $l_value, 'htmlattr' ).'"';
2636              }
2637          }
2638          else
2639          {
2640              $r .= ' '.$l_attr.'="'.$l_value.'"';
2641          }
2642      }
2643  
2644      return $r;
2645  }
2646  
2647  
2648  /**

2649   * Is the current page an admin/backoffice page?

2650   *

2651   * @return boolean

2652   */
2653  function is_admin_page()
2654  {
2655      global $is_admin_page;
2656  
2657      return isset($is_admin_page) && $is_admin_page === true; // check for type also, because of register_globals!

2658  }
2659  
2660  
2661  /**

2662   * Implode array( 'x', 'y', 'z' ) to something like 'x, y and z'. Useful for displaying list to the end user.

2663   *

2664   * If there's one element in the table, it is returned.

2665   * If there are at least two elements, the last one is concatenated using $implode_last, while the ones before are imploded using $implode_by.

2666   *

2667   * @todo dh> I don't think using entities/HTML as default for $implode_last is sane!

2668   *           Use "&" instead and make sure that the output for HTML is HTML compliant..

2669   * @todo Support for locales that have a different kind of enumeration?!

2670   * @return string

2671   */
2672  function implode_with_and( $arr, $implode_by = ', ', $implode_last = ' &amp; ' )
2673  {
2674      switch( count($arr) )
2675      {
2676          case 0:
2677              return '';
2678  
2679          case 1:
2680              $r = array_shift($arr);
2681              return $r;
2682  
2683          default:
2684              $r = implode( $implode_by, array_slice( $arr, 0, -1 ) )
2685                  .$implode_last.array_pop( $arr );
2686              return $r;
2687      }
2688  }
2689  
2690  
2691  /**

2692   * Returns a "<base />" tag and remembers that we've used it ({@link regenerate_url()} needs this).

2693   *

2694   * @param string URL to use (this gets used as base URL for all relative links on the HTML page)

2695   * @return string

2696   */
2697  function base_tag( $url, $target = NULL )
2698  {
2699      global $base_tag_set;
2700  
2701      $base_tag_set = true;
2702      echo '<base href="'.$url.'"';
2703  
2704      if( !empty($target) )
2705      {
2706          echo ' target="'.$target.'"';
2707      }
2708      echo ' />';
2709  }
2710  
2711  
2712  /**

2713   * Display an array as a list:

2714   *

2715   * @param array

2716   * @param string

2717   * @param string

2718   * @param string

2719   * @param string

2720   * @param string

2721   */
2722  function display_list( $items, $list_start = '<ul>', $list_end = '</ul>', $item_separator = '',
2723                                                  $item_start = '<li>', $item_end = '</li>', $force_hash = NULL )
2724  {
2725      if( !empty( $items ) )
2726      {
2727          echo $list_start;
2728          $first = true;
2729          foreach( $items as $item )
2730          {    // For each list item:
2731              $link = resolve_link_params( $item, $force_hash );
2732              if( empty( $link ) )
2733              {
2734                  continue;
2735              }
2736  
2737              if( $first )
2738              {
2739                  $first = false;
2740              }
2741              else
2742              {
2743                  echo $item_separator;
2744              }
2745              echo $item_start.$link.$item_end;
2746          }
2747          echo $list_end;
2748      }
2749  }
2750  
2751  function display_param_link( $params )
2752  {
2753      echo resolve_link_params( $params );
2754  }
2755  
2756  /**

2757   * Resolve a link based on params

2758   *

2759   * @param array

2760   * @return string

2761   */
2762  function resolve_link_params( $item, $force_hash = NULL )
2763  {
2764      global $current_locale;
2765  
2766      // echo 'resolve link ';

2767  
2768      if( is_array( $item ) )
2769      {
2770          if( isset( $item[0] ) )
2771          {    // Older format, which displays the same thing for all locales:
2772              return generate_link_from_params( $item );
2773          }
2774          else
2775          {    // First get the right locale:
2776              // echo $current_locale;

2777              foreach( $item as $l_locale => $loc_item )
2778              {
2779                  if( $l_locale == substr( $current_locale, 0, strlen( $l_locale) ) )
2780                  {    // We found a matching locale:
2781  
2782                      if( is_array( $loc_item[0] ) )
2783                      {    // Randomize:
2784                          $loc_item = hash_link_params( $loc_item, $force_hash );
2785                      }
2786  
2787                      return generate_link_from_params( $loc_item );
2788                  }
2789              }
2790              // No match found!

2791              return '';
2792          }
2793      }
2794  
2795      // Super old format:

2796      return $item;
2797  }
2798  
2799  
2800  /**

2801   * Get a link line, based url hash combined with probability percentage in first column

2802   *

2803   * @param array of arrays

2804   */
2805  function hash_link_params( $link_array, $force_hash = NULL )
2806  {
2807      global $ReqHost, $ReqPath;
2808  
2809      static $hash;
2810  
2811      if( !is_null($force_hash) )
2812      {
2813          $hash = $force_hash;
2814      }
2815      elseif( !isset($hash) )
2816      {
2817          $key = $ReqHost.$ReqPath;
2818          $hash = 0;
2819          for( $i=0; $i<strlen($key); $i++ )
2820          {
2821              $hash += ord($key[$i]);
2822          }
2823          $hash = $hash % 100 + 1;
2824  
2825          // $hash = rand( 1, 100 );

2826          global $debug, $Debuglog;
2827          if( $debug )
2828          {
2829              $Debuglog->add( 'Hash key: '.$hash, 'vars' );
2830          }
2831      }
2832      //    echo "[$hash] ";

2833  
2834      foreach( $link_array as $link_params )
2835      {
2836          // echo '<br>'.$hash.'-'.$link_params[ 0 ];

2837          if( $hash <= $link_params[ 0 ] )
2838          {    // select this link!
2839              // pre_dump( $link_params );

2840              array_shift( $link_params );
2841              return $link_params;
2842          }
2843      }
2844      // somehow no match, return 1st element:

2845      $link_params = $link_array[0];
2846      array_shift( $link_params );
2847      return $link_params;
2848  }
2849  
2850  
2851  /**

2852   * Generate a link from params

2853   */
2854  function generate_link_from_params( $link_params )
2855  {
2856      $url = $link_params[0];
2857      $text = $link_params[1];
2858  
2859      if( is_array($text) )
2860      {
2861          $text = hash_link_params( $text );
2862          $text = $text[0];
2863      }
2864  
2865      return '<a href="'.$url.'">'.$text.'</a>';
2866  }
2867  
2868  
2869  /**

2870   * Try to make $url relative to $target_url, if scheme, host, user and pass matches.

2871   *

2872   * This is useful for redirect_to params, to keep them short and avoid mod_security

2873   * rejecting the request as "Not Acceptable" (whole URL as param).

2874   *

2875   * @param string URL to handle

2876   * @param string URL where we want to make $url relative to

2877   * @return string

2878   */
2879  function url_rel_to_same_host( $url, $target_url )
2880  {
2881      $parsed_url = @parse_url( $url );
2882      if( ! $parsed_url )
2883      { // invalid url
2884          return $url;
2885      }
2886      if( empty($parsed_url['scheme']) || empty($parsed_url['host']) )
2887      { // no protocol or host information
2888          return $url;
2889      }
2890  
2891      $target_url = @parse_url( $target_url );
2892      if( ! $target_url )
2893      { // invalid url
2894          return $url;
2895      }
2896      if( ! empty($target_url['scheme']) && $target_url['scheme'] != $parsed_url['scheme'] )
2897      { // scheme/protocol is different
2898          return $url;
2899      }
2900      if( ! empty($target_url['host']) )
2901      {
2902          if( empty($target_url['scheme']) || $target_url['host'] != $parsed_url['host'] )
2903          { // target has no scheme (but a host) or hosts differ
2904              return $url;
2905          }
2906  
2907          if( @$target_url['port'] != @$parsed_url['port'] )
2908              return $url;
2909          if( @$target_url['user'] != @$parsed_url['user'] )
2910              return $url;
2911          if( @$target_url['pass'] != @$parsed_url['pass'] )
2912              return $url;
2913      }
2914  
2915      // We can make the URL relative:

2916      $r = '';
2917      if( ! empty($parsed_url['path']) )
2918          $r .= $parsed_url['path'];
2919      if( ! empty($parsed_url['query']) )
2920          $r .= '?'.$parsed_url['query'];
2921      if( ! empty($parsed_url['fragment']) )
2922          $r .= '?'.$parsed_url['fragment'];
2923  
2924      return $r;
2925  }
2926  
2927  
2928  /**

2929   * Make an $url absolute according to $host, if it is not absolute yet.

2930   *

2931   * @param string URL

2932   * @param string Host (including protocol, e.g. 'http://example.com'); defaults to {@link $ReqHost}

2933   * @return string

2934   */
2935  function url_absolute( $url, $host = NULL )
2936  {
2937      if( preg_match( '~^(\w+:)?//~', $url ) )
2938      { // URL is relative already ("//foo/bar" is absolute - leaving the protocol out):
2939          return $url;
2940      }
2941  
2942      if( empty($host) )
2943      {
2944          global $ReqHost;
2945          $host = $ReqHost;
2946      }
2947      return $host.$url;
2948  }
2949  
2950  
2951  /**

2952   * Make links in $s absolute.

2953   *

2954   * It searches for "src" and "href" HTML tag attributes and makes the absolute.

2955   *

2956   * @uses url_absolute()

2957   * @param string content

2958   * @param string Hostname including scheme, e.g. http://example.com; defaults to $ReqHost

2959   * @return string

2960   */
2961  function make_rel_links_abs( $s, $host = NULL )
2962  {
2963      if( empty($host) )
2964      {
2965          global $ReqHost;
2966          $host = $ReqHost;
2967      }
2968  
2969      $s = preg_replace_callback( '~(<[^>]+?)\b((?:src|href)\s*=\s*)(["\'])?([^\\3]+?)(\\3)~i', create_function( '$m', '
2970          return $m[1].$m[2].$m[3].url_absolute($m[4], "'.$host.'").$m[5];' ), $s );
2971      return $s;
2972  }
2973  
2974  
2975  /*

2976   * $Log: _misc.funcs.php,v $

2977   * Revision 1.7  2007/11/03 21:04:25  fplanque

2978   * skin cleanup

2979   *

2980   * Revision 1.6  2007/10/25 18:29:41  blueyed

2981   * PasteFromBranch: Fixed url_absolute for '//foo/bar'

2982   *

2983   * Revision 1.5  2007/09/22 19:23:56  fplanque

2984   * various fixes & enhancements

2985   *

2986   * Revision 1.4  2007/09/12 21:00:30  fplanque

2987   * UI improvements

2988   *

2989   * Revision 1.3  2007/09/08 18:38:08  fplanque

2990   * MFB

2991   *

2992   * Revision 1.2  2007/09/04 14:56:20  fplanque

2993   * antispam cleanup

2994   *

2995   * Revision 1.1  2007/06/25 10:58:52  fplanque

2996   * MODULES (refactored MVC)

2997   *

2998   * Revision 1.180  2007/06/19 23:15:08  blueyed

2999   * doc fixes

3000   *

3001   * Revision 1.179  2007/06/16 19:54:39  blueyed

3002   * doc/(-fixes)

3003   *

3004   * Revision 1.178  2007/06/05 17:00:02  blueyed

3005   * MFB v-1-10: Consistent logging of HTTP_REFERER/REQUEST_URI; fixed possible E_NOTICE

3006   *

3007   * Revision 1.177  2007/05/23 23:07:53  blueyed

3008   * Display DB info (user, database, host, tableprefix, charset) in debug_info()

3009   *

3010   * Revision 1.176  2007/05/13 20:49:34  fplanque

3011   * removed hack that did inconsistent action upon the HTML and the Security checkers.

3012   *

3013   * Revision 1.175  2007/05/13 18:31:23  blueyed

3014   * Trim special date param replacements in date_i18n(). Fixes http://forums.b2evolution.net/viewtopic.php?p=55213#55213.

3015   *

3016   * Revision 1.174  2007/05/12 10:13:25  yabs

3017   * secuirty checker uses setting for allowing id/style in comments

3018   * amended get_icon to respect $use_strict

3019   *

3020   * Revision 1.173  2007/04/26 00:11:08  fplanque

3021   * (c) 2007

3022   *

3023   * Revision 1.172  2007/04/25 18:47:42  fplanque

3024   * MFB 1.10: groovy links

3025   *

3026   * Revision 1.171  2007/04/19 20:36:42  blueyed

3027   * Fixed possible E_NOTICE with REQUEST_URI/cron

3028   *

3029   * Revision 1.170  2007/02/16 11:23:02  blueyed

3030   * No limits for xdebug_var_dump() in pre_dump()

3031   *

3032   * Revision 1.169  2007/02/11 02:10:35  blueyed

3033   * Minor improvements to debug_get_backtrace()

3034   *

3035   * Revision 1.168  2007/02/06 13:47:25  blueyed

3036   * Fixed escaping in get_link_showhide(); added jsspecialchars(); see http://forums.b2evolution.net/viewtopic.php?p=50564

3037   *

3038   * Revision 1.167  2007/01/29 09:58:55  fplanque

3039   * enhanced toolbar - experimental

3040   *

3041   * Revision 1.166  2007/01/29 09:24:41  fplanque

3042   * icon stuff

3043   *

3044   * Revision 1.165  2007/01/26 20:43:03  blueyed

3045   * Fixed exp. app error logging

3046   *

3047   * Revision 1.164  2007/01/26 04:52:53  fplanque

3048   * clean comment popups (skins 2.0)

3049   *

3050   * Revision 1.163  2007/01/24 01:32:30  fplanque

3051   * 'zip & css' is more readable than 'zip and css'

3052   *

3053   * Revision 1.162  2007/01/23 22:23:04  fplanque

3054   * FIXED (!!!) disappearing help window!

3055   *

3056   * Revision 1.161  2007/01/23 21:44:43  fplanque

3057   * handle generic "empty"/noimg icons

3058   *

3059   * Revision 1.160  2007/01/23 05:30:21  fplanque

3060   * "Contact the owner"

3061   *

3062   * Revision 1.159  2007/01/20 00:38:19  blueyed

3063   * Added Debulog entry in header_redirect()

3064   *

3065   * Revision 1.158  2007/01/19 03:06:57  fplanque

3066   * Changed many little thinsg in the login procedure.

3067   * There may be new bugs, sorry. I tested this for several hours though.

3068   * More refactoring to be done.

3069   *

3070   * Revision 1.157  2007/01/14 05:41:10  blueyed

3071   * Send correct charset with bad_request_die()

3072   *

3073   * Revision 1.156  2006/12/19 17:21:54  blueyed

3074   * Fixed domain extraction if anchor (#) follows domain name directly. See http://forums.b2evolution.net/viewtopic.php?p=48672#48672

3075   *

3076   * Revision 1.155  2006/12/14 00:42:04  fplanque

3077   * A little bit of windows detection / normalization

3078   *

3079   * Revision 1.154  2006/12/12 23:23:30  fplanque

3080   * finished post editing v2.0

3081   *

3082   * Revision 1.153  2006/12/05 06:22:24  blueyed

3083   * Fixed blogger.newPost to accept a list of categories, as given by w.bloggar

3084   *

3085   * Revision 1.152  2006/12/02 17:48:31  blueyed

3086   * Fixed regexp for balanceTags(), so it does not handle HTML comments as tags

3087   *

3088   * Revision 1.151  2006/12/02 17:32:26  blueyed

3089   * Missing "global $Messages" in format_to_post()

3090   *

3091   * Revision 1.150  2006/11/29 20:48:46  blueyed

3092   * Moved url_rel_to_same_host() from _misc.funcs.php to _url.funcs.php

3093   *

3094   * Revision 1.149  2006/11/29 20:17:23  blueyed

3095   * Refactored generate_random_passwd()

3096   *

3097   * Revision 1.148  2006/11/28 02:52:26  fplanque

3098   * doc

3099   *

3100   * Revision 1.147  2006/11/26 02:30:39  fplanque

3101   * doc / todo

3102   *

3103   * Revision 1.146  2006/11/24 18:27:27  blueyed

3104   * Fixed link to b2evo CVS browsing interface in file docblocks

3105   *

3106   * Revision 1.145  2006/11/24 18:06:02  blueyed

3107   * Handle saving of $Messages centrally in header_redirect()

3108   *

3109   * Revision 1.144  2006/11/23 23:20:11  blueyed

3110   * get_base_domain(): added test, whcih currently fails + notes

3111   *

3112   * Revision 1.143  2006/11/23 02:29:00  fplanque

3113   * fixed IDN and more IDN

3114   *

3115   * Revision 1.142  2006/11/22 18:47:59  blueyed

3116   * Fixed get_base_domain() again some more; added tests (2 failing ones)

3117   *

3118   * Revision 1.141  2006/11/22 01:19:34  fplanque

3119   * contact the admin feature

3120   *

3121   * Revision 1.140  2006/11/22 00:24:38  blueyed

3122   * Fixed get_base_domain() for empty URL/domain

3123   *

3124   * Revision 1.139  2006/11/21 19:18:40  fplanque

3125   * get_base_domain()  / get_ban_domain() may need more unit tests, especially about what to do when invalid URLs are passed.

3126   *

3127   * Revision 1.138  2006/11/19 23:43:05  blueyed

3128   * Optimized icon and $IconLegend handling

3129   *

3130   * Revision 1.137  2006/11/14 22:10:41  blueyed

3131   * Removed own bloat in debug_die(), it is an error after all. doc

3132   *

3133   * Revision 1.136  2006/11/14 21:57:19  blueyed

3134   * Store an array of Debuglog entries in the session, not just the last one - because we may redirect more than once, e.g. when redirecting to canonical url, after having posted a comment.

3135   *

3136   * Revision 1.135  2006/11/06 19:43:27  blueyed

3137   * *** empty log message ***

3138   *

3139   * Revision 1.134  2006/11/05 20:13:57  fplanque

3140   * minor

3141   *

3142   * Revision 1.133  2006/11/02 15:57:47  blueyed

3143   * Fixes for pre_dump() and add_icon()

3144   *

3145   * Revision 1.132  2006/11/02 01:34:43  blueyed

3146   * doc/QUESTION

3147   *

3148   * Revision 1.131  2006/11/01 19:04:35  blueyed

3149   * re-added htmlspecialchars() to pre_dump() and made it CLI aware

3150   *

3151   * Revision 1.130  2006/10/31 01:17:56  blueyed

3152   * Send contenttype/CHARSET in debug_die() if not done so before

3153   *

3154   * Revision 1.129  2006/10/31 00:32:57  blueyed

3155   * doc

3156   *

3157   * Revision 1.128  2006/10/26 20:23:00  blueyed

3158   * Handle invalid URLs gracefully in url_rel_to_same_host()

3159   *

3160   * Revision 1.127  2006/10/23 22:19:03  blueyed

3161   * Fixed/unified encoding of redirect_to param. Use just rawurlencode() and no funky &amp; replacements

3162   *

3163   * Revision 1.126  2006/10/16 08:39:10  blueyed

3164   * Merged fixes from v-1-9 branch

3165   *

3166   * Revision 1.125  2006/10/14 16:06:38  blueyed

3167   * Fixed anchors for "redirection-Debuglog"

3168   *

3169   * Revision 1.124  2006/10/14 02:12:01  blueyed

3170   * Added url_absolute(), make_rel_links_abs() + Tests; Fixed validate_url() and allow relative URLs, which get converted to absolute ones in feeds.

3171   *

3172   * Revision 1.123  2006/10/13 11:47:55  blueyed

3173   * Use var_dump() only in pre_dump()

3174   *

3175   * Revision 1.122  2006/10/13 11:44:45  blueyed

3176   * MFB: url_rel_to_same_host()

3177   *

3178   * Revision 1.121  2006/10/09 10:42:44  blueyed

3179   * better layout for pre_dump()

3180   *

3181   * Revision 1.120  2006/10/09 10:29:16  blueyed

3182   * Handling of "resource" arguments in pre_dump()

3183   */
3184  ?>


Généré le : Thu Nov 29 23:58:50 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics