public_html/lib-common.php
author Dirk Haun <dirk@haun-online.de>
Sun, 01 Nov 2009 09:43:28 +0100
branchHEAD
changeset 7434 aa322b3c4d3d
parent 7433 a8de0db60197
parent 7409 c776e22f2de6
child 7445 b0971977fd28
permissions -rw-r--r--
Merged with "timezone" feature branch
     1 <?php
     2 
     3 /* Reminder: always indent with 4 spaces (no tabs). */
     4 // +---------------------------------------------------------------------------+
     5 // | Geeklog 1.6                                                               |
     6 // +---------------------------------------------------------------------------+
     7 // | lib-common.php                                                            |
     8 // |                                                                           |
     9 // | Geeklog common library.                                                   |
    10 // +---------------------------------------------------------------------------+
    11 // | Copyright (C) 2000-2009 by the following authors:                         |
    12 // |                                                                           |
    13 // | Authors: Tony Bibbs        - tony AT tonybibbs DOT com                    |
    14 // |          Mark Limburg      - mlimburg AT users DOT sourceforge DOT net    |
    15 // |          Jason Whittenburg - jwhitten AT securitygeeks DOT com            |
    16 // |          Dirk Haun         - dirk AT haun-online DOT de                   |
    17 // |          Vincent Furia     - vinny01 AT users DOT sourceforge DOT net     |
    18 // +---------------------------------------------------------------------------+
    19 // |                                                                           |
    20 // | This program is free software; you can redistribute it and/or             |
    21 // | modify it under the terms of the GNU General Public License               |
    22 // | as published by the Free Software Foundation; either version 2            |
    23 // | of the License, or (at your option) any later version.                    |
    24 // |                                                                           |
    25 // | This program is distributed in the hope that it will be useful,           |
    26 // | but WITHOUT ANY WARRANTY; without even the implied warranty of            |
    27 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             |
    28 // | GNU General Public License for more details.                              |
    29 // |                                                                           |
    30 // | You should have received a copy of the GNU General Public License         |
    31 // | along with this program; if not, write to the Free Software Foundation,   |
    32 // | Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           |
    33 // |                                                                           |
    34 // +---------------------------------------------------------------------------+
    35 
    36 // Prevent PHP from reporting uninitialized variables
    37 error_reporting( E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR );
    38 
    39 /**
    40 * This is the common library for Geeklog.  Through our code, you will see
    41 * functions with the COM_ prefix (e.g. COM_siteHeader()).  Any such functions
    42 * can be found in this file.
    43 *
    44 * --- You don't need to modify anything in this file! ---
    45 *
    46 * WARNING: put any custom hacks in lib-custom.php and not in here.  This file is
    47 * modified frequently by the Geeklog development team.  If you put your hacks in
    48 * lib-custom.php you will find upgrading much easier.
    49 *
    50 */
    51 
    52 /**
    53 * Turn this on to get various debug messages from the code in this library
    54 * @global Boolean $_COM_VERBOSE
    55 */
    56 $_COM_VERBOSE = false;
    57 
    58 /**
    59 * Prevent getting any surprise values. But we should really stop
    60 * using $_REQUEST altogether.
    61 */
    62 $_REQUEST = array_merge($_GET, $_POST);
    63 
    64 /**
    65 * Here, we shall establish an error handler. This will mean that whenever a
    66 * php level error is encountered, our own code handles it. This will hopefuly
    67 * go someway towards preventing nasties like path exposures from ever being
    68 * possible. That is, unless someone has overridden our error handler with one
    69 * with a path exposure issue...
    70 *
    71 * Must make sure that the function hasn't been disabled before calling it.
    72 *
    73 */ 
    74 if (function_exists('set_error_handler')) {
    75     if (PHP_VERSION >= 5) {
    76         /* Tell the error handler to use the default error reporting options.
    77          * You may like to change this to use it in more/less cases, if so,
    78          * just use the syntax used in the call to error_reporting() above.
    79          */
    80         $defaultErrorHandler = set_error_handler('COM_handleError',
    81                                                  error_reporting());
    82     } else {
    83         $defaultErrorHandler = set_error_handler('COM_handleError');
    84     }
    85 }
    86 
    87 /**
    88 * Configuration Include:
    89 * You do NOT need to modify anything here any more!
    90 */
    91 require_once 'siteconfig.php';
    92 
    93 /**
    94 * Configuration class
    95 */
    96 require_once $_CONF['path_system'] . 'classes/config.class.php';
    97 
    98 $config =& config::get_instance();
    99 $config->set_configfile($_CONF['path'] . 'db-config.php');
   100 $config->load_baseconfig();
   101 $config->initConfig();
   102 
   103 $_CONF = $config->get_config('Core');
   104 
   105 // Before we do anything else, check to ensure site is enabled
   106 
   107 if (isset($_CONF['site_enabled']) && !$_CONF['site_enabled']) {
   108 
   109     if (empty($_CONF['site_disabled_msg'])) {
   110         header("HTTP/1.1 503 Service Unavailable");
   111         header("Status: 503 Service Unavailable");
   112         echo $_CONF['site_name'] . ' is temporarily down.  Please check back soon.';
   113     } else {
   114         // if the msg starts with http: assume it's a URL we should redirect to
   115         if (preg_match("/^(https?):/", $_CONF['site_disabled_msg']) === 1) {
   116             echo COM_refresh($_CONF['site_disabled_msg']);
   117         } else {
   118             header("HTTP/1.1 503 Service Unavailable");
   119             header("Status: 503 Service Unavailable");
   120             echo $_CONF['site_disabled_msg'];
   121         }
   122     }
   123 
   124     exit;
   125 }
   126 
   127 // this file can't be used on its own - redirect to index.php
   128 if (strpos(strtolower($_SERVER['PHP_SELF']), 'lib-common.php') !== false) {
   129     echo COM_refresh($_CONF['site_url'] . '/index.php');
   130     exit;
   131 }
   132 
   133 
   134 // +---------------------------------------------------------------------------+
   135 // | Library Includes: You shouldn't have to touch anything below here         |
   136 // +---------------------------------------------------------------------------+
   137 
   138 /**
   139 * If needed, add our PEAR path to the list of include paths
   140 *
   141 */
   142 if (! $_CONF['have_pear']) {
   143     $curPHPIncludePath = get_include_path();
   144     if (empty($curPHPIncludePath)) {
   145         $curPHPIncludePath = $_CONF['path_pear'];
   146     } else {
   147         $curPHPIncludePath = $_CONF['path_pear'] . PATH_SEPARATOR
   148                            . $curPHPIncludePath;
   149     }
   150 
   151     if (set_include_path($curPHPIncludePath) === false) {
   152         COM_errorLog('set_include_path failed - there may be problems using the PEAR classes.', 1);
   153     }
   154 }
   155 
   156 /**
   157 * Set the webserver's timezone
   158 */
   159 
   160 require_once $_CONF['path_system'] . 'classes/timezoneconfig.class.php';
   161 TimeZoneConfig::setSystemTimeZone();
   162 
   163 /**
   164 * Include plugin class.
   165 * This is a poorly implemented class that was not very well thought out.
   166 * Still very necessary
   167 *
   168 */
   169 
   170 require_once( $_CONF['path_system'] . 'lib-plugins.php' );
   171 
   172 /**
   173 * Include page time -- used to time how fast each page was created
   174 *
   175 */
   176 
   177 require_once( $_CONF['path_system'] . 'classes/timer.class.php' );
   178 $_PAGE_TIMER = new timerobject();
   179 $_PAGE_TIMER->startTimer();
   180 
   181 /**
   182 * Include URL class
   183 *
   184 * This provides optional URL rewriting functionality.
   185 */
   186 
   187 require_once( $_CONF['path_system'] . 'classes/url.class.php' );
   188 $_URL = new url( $_CONF['url_rewrite'] );
   189 
   190 /**
   191 * This is our HTML template class.  It is the same one found in PHPLib and is
   192 * licensed under the LGPL.  See that file for details.
   193 *
   194 */
   195 
   196 require_once( $_CONF['path_system'] . 'classes/template.class.php' );
   197 
   198 /**
   199 * This is the security library used for application security
   200 *
   201 */
   202 
   203 require_once( $_CONF['path_system'] . 'lib-security.php' );
   204 
   205 /**
   206 * This is the syndication library used to offer (RSS) feeds.
   207 *
   208 */
   209 
   210 require_once( $_CONF['path_system'] . 'lib-syndication.php' );
   211 
   212 /**
   213  *These variables were taken out of the configuration and placed here since they
   214  *are necessary to change with the themes, not whole sites. They should now be
   215  *overridden by setting them to a different value than here in the theme's
   216  *function.php or in lib-custom.php. Therefore they are NOT TO BE CHANGED HERE.
   217  */
   218 $_CONF['left_blocks_in_footer'] = 0;  // use left blocks in header
   219 $_CONF['right_blocks_in_footer'] = 1;  // use right blocks in footer
   220 
   221 /**
   222 * This is the custom library.
   223 *
   224 * It is the sandbox for every Geeklog Admin to play in.
   225 * The lib-custom.php as shipped will never contain required code,
   226 * so it's safe to always use your own copy.
   227 * This should hold all custom hacks to make upgrading easier.
   228 *
   229 */
   230 
   231 require_once( $_CONF['path_system'] . 'lib-custom.php' );
   232 
   233 /**
   234 * Session management library
   235 *
   236 */
   237 
   238 require_once( $_CONF['path_system'] . 'lib-sessions.php' );
   239 TimeZoneConfig::setUserTimeZone();
   240 
   241 /**
   242 * Ulf Harnhammar's kses class
   243 *
   244 */
   245 
   246 require_once( $_CONF['path_system'] . 'classes/kses.class.php' );
   247 
   248 /**
   249 * Multibyte functions
   250 *
   251 */
   252 require_once( $_CONF['path_system'] . 'lib-mbyte.php' );
   253 
   254 // Set theme
   255 
   256 $usetheme = '';
   257 if( isset( $_POST['usetheme'] ))
   258 {
   259     $usetheme = COM_sanitizeFilename($_POST['usetheme'], true);
   260 }
   261 if( !empty( $usetheme ) && is_dir( $_CONF['path_themes'] . $usetheme ))
   262 {
   263     $_CONF['theme'] = $usetheme;
   264     $_CONF['path_layout'] = $_CONF['path_themes'] . $_CONF['theme'] . '/';
   265     $_CONF['layout_url'] = $_CONF['site_url'] . '/layout/' . $_CONF['theme'];
   266 }
   267 else if( $_CONF['allow_user_themes'] == 1 )
   268 {
   269     if( isset( $_COOKIE[$_CONF['cookie_theme']] ) && empty( $_USER['theme'] ))
   270     {
   271         $theme = COM_sanitizeFilename($_COOKIE[$_CONF['cookie_theme']], true);
   272         if( is_dir( $_CONF['path_themes'] . $theme ))
   273         {
   274             $_USER['theme'] = $theme;
   275         }
   276     }
   277 
   278     if( !empty( $_USER['theme'] ))
   279     {
   280         if( is_dir( $_CONF['path_themes'] . $_USER['theme'] ))
   281         {
   282             $_CONF['theme'] = $_USER['theme'];
   283             $_CONF['path_layout'] = $_CONF['path_themes'] . $_CONF['theme'] . '/';
   284             $_CONF['layout_url'] = $_CONF['site_url'] . '/layout/' . $_CONF['theme'];
   285         }
   286         else
   287         {
   288             $_USER['theme'] = $_CONF['theme'];
   289         }
   290     }
   291 }
   292 
   293 /**
   294 * Include theme functions file
   295 */
   296 
   297 // Include theme functions file which may/may not do anything
   298 
   299 if (file_exists($_CONF['path_layout'] . 'functions.php')) {
   300     require_once $_CONF['path_layout'] . 'functions.php';
   301 }
   302 
   303 /**
   304 * ensure XHTML constant is defined to avoid problems elsewhere
   305 */
   306 if (!defined('XHTML')) {
   307     switch ($_CONF['doctype']) {
   308     case 'xhtml10transitional':
   309     case 'xhtml10strict':
   310         define('XHTML', ' /');
   311         break;
   312 
   313     default:
   314         /**
   315         * @ignore
   316         */
   317         define('XHTML', '');
   318         break;
   319     }
   320 }
   321 
   322 // themes can now specify the default image type
   323 // fall back to 'gif' if they don't
   324 
   325 if (empty($_IMAGE_TYPE)) {
   326     $_IMAGE_TYPE = 'gif';
   327 }
   328 
   329 // Similarly set language
   330 
   331 if( isset( $_COOKIE[$_CONF['cookie_language']] ) && empty( $_USER['language'] ))
   332 {
   333     $language = COM_sanitizeFilename($_COOKIE[$_CONF['cookie_language']]);
   334     if( is_file( $_CONF['path_language'] . $language . '.php' ) &&
   335             ( $_CONF['allow_user_language'] == 1 ))
   336     {
   337         $_USER['language'] = $language;
   338         $_CONF['language'] = $language;
   339     }
   340 }
   341 else if( !empty( $_USER['language'] ))
   342 {
   343     if( is_file( $_CONF['path_language'] . $_USER['language'] . '.php' ) &&
   344             ( $_CONF['allow_user_language'] == 1 ))
   345     {
   346         $_CONF['language'] = $_USER['language'];
   347     }
   348 }
   349 else if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
   350 {
   351     $_CONF['language'] = COM_getLanguage();
   352 }
   353 
   354 // Handle Who's Online block
   355 if (COM_isAnonUser() && isset($_SERVER['REMOTE_ADDR'])) {
   356     // The following code handles anonymous users so they show up properly
   357     DB_delete($_TABLES['sessions'], array('remote_ip', 'uid'),
   358                                     array($_SERVER['REMOTE_ADDR'], 1));
   359 
   360     $tries = 0;
   361     do
   362     {
   363         // Build a useless sess_id (needed for insert to work properly)
   364         $sess_id = mt_rand();
   365         $curtime = time();
   366 
   367         // Insert anonymous user session
   368         $result = DB_query( "INSERT INTO {$_TABLES['sessions']} (sess_id, start_time, remote_ip, uid) VALUES ($sess_id, $curtime, '{$_SERVER['REMOTE_ADDR']}', 1)", 1 );
   369         $tries++;
   370     }
   371     while(( $result === false) && ( $tries < 5 ));
   372 }
   373 
   374 // Clear out any expired sessions
   375 DB_query( "DELETE FROM {$_TABLES['sessions']} WHERE start_time < " . ( time() - $_CONF['whosonline_threshold'] ));
   376 
   377 /**
   378 *
   379 * Language include
   380 *
   381 */
   382 
   383 require_once $_CONF['path_language'] . $_CONF['language'] . '.php';
   384 
   385 if (empty($LANG_DIRECTION)) {
   386     // default to left-to-right
   387     $LANG_DIRECTION = 'ltr';
   388 }
   389 
   390 COM_switchLocaleSettings();
   391 
   392 if( setlocale( LC_ALL, $_CONF['locale'] ) === false )
   393 {
   394     setlocale( LC_TIME, $_CONF['locale'] );
   395 }
   396 
   397 /**
   398 * Global array of groups current user belongs to
   399 *
   400 * @global array $_GROUPS
   401 *
   402 */
   403 
   404 if( !COM_isAnonUser() )
   405 {
   406     $_GROUPS = SEC_getUserGroups( $_USER['uid'] );
   407 }
   408 else
   409 {
   410     $_GROUPS = SEC_getUserGroups( 1 );
   411 }
   412 
   413 /**
   414 * Global array of current user permissions [read,edit]
   415 *
   416 * @global array $_RIGHTS
   417 *
   418 */
   419 
   420 $_RIGHTS = explode( ',', SEC_getUserPermissions() );
   421 
   422 if( isset( $_GET['topic'] ))
   423 {
   424     $topic = COM_applyFilter( $_GET['topic'] );
   425 }
   426 else if( isset( $_POST['topic'] ))
   427 {
   428     $topic = COM_applyFilter( $_POST['topic'] );
   429 }
   430 else
   431 {
   432     $topic = '';
   433 }
   434 
   435 
   436 // +---------------------------------------------------------------------------+
   437 // | HTML WIDGETS                                                              |
   438 // +---------------------------------------------------------------------------+
   439 
   440 /**
   441 * Return the file to use for a block template.
   442 *
   443 * This returns the template needed to build the HTML for a block.  This function
   444 * allows designers to give a block it's own custom look and feel.  If no
   445 * templates for the block are specified, the default blockheader.html and
   446 * blockfooter.html will be used.
   447 *
   448 * @param        string      $blockname      corresponds to name field in block table
   449 * @param        string      $which          can be either 'header' or 'footer' for corresponding template
   450 * @param        string      $position       can be 'left', 'right' or blank. If set, will be used to find a side specific override template.
   451 * @see function COM_startBlock
   452 * @see function COM_endBlock
   453 * @see function COM_showBlocks
   454 * @see function COM_showBlock
   455 * @return   string  template name
   456 */
   457 function COM_getBlockTemplate( $blockname, $which, $position='' )
   458 {
   459     global $_BLOCK_TEMPLATE, $_COM_VERBOSE, $_CONF;
   460 
   461     if( $_COM_VERBOSE )
   462     {
   463         COM_errorLog( "_BLOCK_TEMPLATE[$blockname] = " . $_BLOCK_TEMPLATE[$blockname], 1 );
   464     }
   465 
   466     if( !empty( $_BLOCK_TEMPLATE[$blockname] ))
   467     {
   468         $templates = explode( ',', $_BLOCK_TEMPLATE[$blockname] );
   469         if( $which == 'header' )
   470         {
   471             if( !empty( $templates[0] ))
   472             {
   473                 $template = $templates[0];
   474             }
   475             else
   476             {
   477                 $template = 'blockheader.thtml';
   478             }
   479         }
   480         else
   481         {
   482             if( !empty( $templates[1] ))
   483             {
   484                 $template = $templates[1];
   485             }
   486             else
   487             {
   488                 $template = 'blockfooter.thtml';
   489             }
   490         }
   491     }
   492     else
   493     {
   494         if( $which == 'header' )
   495         {
   496             $template = 'blockheader.thtml';
   497         }
   498         else
   499         {
   500             $template = 'blockfooter.thtml';
   501         }
   502     }
   503 
   504     // If we have a position specific request, and the template is not already
   505     // position specific then look to see if there is a position specific
   506     // override.
   507     $templateLC = strtolower($template);
   508     if( !empty($position) && ( strpos($templateLC, $position) === false ) )
   509     {
   510         // Trim .thtml from the end.
   511         $positionSpecific = substr($template, 0, strlen($template) - 6);
   512         $positionSpecific .= '-' . $position . '.thtml';
   513         if( file_exists( $_CONF['path_layout'] . $positionSpecific ) )
   514         {
   515             $template = $positionSpecific;
   516         }
   517     }
   518 
   519     if( $_COM_VERBOSE )
   520     {
   521         COM_errorLog( "Block template for the $which of $blockname is: $template", 1 );
   522     }
   523 
   524     return $template;
   525 }
   526 
   527 /**
   528 * Gets all installed themes
   529 *
   530 * Returns a list of all the directory names in $_CONF['path_themes'], i.e.
   531 * a list of all the theme names.
   532 *
   533 * @param    boolean $all    if true, return all themes even if users aren't allowed to change their default themes
   534 * @return   array           All installed themes
   535 *
   536 */
   537 function COM_getThemes( $all = false )
   538 {
   539     global $_CONF;
   540 
   541     $index = 1;
   542 
   543     $themes = array();
   544 
   545     // If users aren't allowed to change their theme then only return the default theme
   546 
   547     if(( $_CONF['allow_user_themes'] == 0 ) && !$all )
   548     {
   549         $themes[$index] = $_CONF['theme'];
   550     }
   551     else
   552     {
   553         $fd = opendir( $_CONF['path_themes'] );
   554 
   555         while(( $dir = @readdir( $fd )) == TRUE )
   556         {
   557             if( is_dir( $_CONF['path_themes'] . $dir) && $dir <> '.' && $dir <> '..' && $dir <> 'CVS' && substr( $dir, 0 , 1 ) <> '.' )
   558             {
   559                 clearstatcache();
   560                 $themes[$index] = $dir;
   561                 $index++;
   562             }
   563         }
   564     }
   565 
   566     return $themes;
   567 }
   568 
   569 /**
   570 * Create the menu, i.e. replace {menu_elements} in the site header with the
   571 * actual menu entries.
   572 *
   573 * @param    Template    &$header        reference to the header template
   574 * @param    array       $plugin_menu    array of plugin menu entries, if any
   575 *
   576 */
   577 function COM_renderMenu( &$header, $plugin_menu )
   578 {
   579     global $_CONF, $_USER, $LANG01, $topic;
   580 
   581     if( empty( $_CONF['menu_elements'] ))
   582     {
   583         $_CONF['menu_elements'] = array( // default set of links
   584                 'contribute', 'search', 'stats', 'directory', 'plugins' );
   585     }
   586 
   587     $anon = COM_isAnonUser();
   588     $menuCounter = 0;
   589     $allowedCounter = 0;
   590     $counter = 0;
   591 
   592     $num_plugins = count( $plugin_menu );
   593     if( ( $num_plugins == 0 ) && in_array( 'plugins', $_CONF['menu_elements'] ))
   594     {
   595         $key = array_search( 'plugins', $_CONF['menu_elements'] );
   596         unset( $_CONF['menu_elements'][$key] );
   597     }
   598 
   599     if( in_array( 'custom', $_CONF['menu_elements'] ))
   600     {
   601         $custom_entries = array();
   602         if( function_exists( 'CUSTOM_menuEntries' ))
   603         {
   604             $custom_entries = CUSTOM_menuEntries();
   605         }
   606         if( count( $custom_entries ) == 0 )
   607         {
   608             $key = array_search( 'custom', $_CONF['menu_elements'] );
   609             unset( $_CONF['menu_elements'][$key] );
   610         }
   611     }
   612 
   613     $num_elements = count( $_CONF['menu_elements'] );
   614 
   615     foreach( $_CONF['menu_elements'] as $item )
   616     {
   617         $counter++;
   618         $allowed = true;
   619         $last_entry = ( $counter == $num_elements ) ? true : false;
   620 
   621         switch( $item )
   622         {
   623             case 'contribute':
   624                 if( empty( $topic ))
   625                 {
   626                     $url = $_CONF['site_url'] . '/submit.php?type=story';
   627                     $header->set_var( 'current_topic', '' );
   628                 }
   629                 else
   630                 {
   631                     $url = $_CONF['site_url']
   632                          . '/submit.php?type=story&amp;topic=' . $topic;
   633                     $header->set_var( 'current_topic', '&amp;topic=' . $topic );
   634                 }
   635                 $label = $LANG01[71];
   636                 if( $anon && ( $_CONF['loginrequired'] ||
   637                         $_CONF['submitloginrequired'] ))
   638                 {
   639                     $allowed = false;
   640                 }
   641                 break;
   642 
   643             case 'custom':
   644                 if (function_exists('CUSTOM_renderMenu')) {
   645                     CUSTOM_renderMenu($header, $custom_entries, $menuCounter);
   646                 } else {
   647                     $custom_count = 0;
   648                     $custom_size = count($custom_entries);
   649                     foreach ($custom_entries as $entry) {
   650                         $custom_count++;
   651 
   652                         if (empty($entry['url']) || empty($entry['label'])) {
   653                             continue;
   654                         }
   655 
   656                         $header->set_var('menuitem_url',  $entry['url']);
   657                         $header->set_var('menuitem_text', $entry['label']);
   658 
   659                         if ($last_entry && ($custom_count == $custom_size)) {
   660                             $header->parse('menu_elements', 'menuitem_last',
   661                                            true);
   662                         } else {
   663                             $header->parse('menu_elements', 'menuitem', true);
   664                         }
   665                         $menuCounter++;
   666                     }
   667                 }
   668                 $url = '';
   669                 $label = '';
   670                 break;
   671 
   672             case 'directory':
   673                 $url = $_CONF['site_url'] . '/directory.php';
   674                 if( !empty( $topic ))
   675                 {
   676                     $url = COM_buildUrl( $url . '?topic='
   677                                          . urlencode( $topic ));
   678                 }
   679                 $label = $LANG01[117];
   680                 if( $anon && ( $_CONF['loginrequired'] ||
   681                         $_CONF['directoryloginrequired'] ))
   682                 {
   683                     $allowed = false;
   684                 }
   685                 break;
   686 
   687             case 'home':
   688                 $url = $_CONF['site_url'] . '/';
   689                 $label = $LANG01[90];
   690                 break;
   691 
   692             case 'plugins':
   693                 for( $i = 1; $i <= $num_plugins; $i++ )
   694                 {
   695                     $header->set_var( 'menuitem_url', current( $plugin_menu ));
   696                     $header->set_var( 'menuitem_text', key( $plugin_menu ));
   697 
   698                     if( $last_entry && ( $i == $num_plugins ))
   699                     {
   700                         $header->parse( 'menu_elements', 'menuitem_last',
   701                                         true );
   702                     }
   703                     else
   704                     {
   705                         $header->parse( 'menu_elements', 'menuitem', true );
   706                     }
   707                     $menuCounter++;
   708 
   709                     next( $plugin_menu );
   710                 }
   711                 $url = '';
   712                 $label = '';
   713                 break;
   714 
   715             case 'prefs':
   716                 $url = $_CONF['site_url'] . '/usersettings.php';
   717                 $label = $LANG01[48];
   718                 break;
   719 
   720             case 'search':
   721                 $url = $_CONF['site_url'] . '/search.php';
   722                 $label = $LANG01[75];
   723                 if( $anon && ( $_CONF['loginrequired'] ||
   724                         $_CONF['searchloginrequired'] ))
   725                 {
   726                     $allowed = false;
   727                 }
   728                 break;
   729 
   730             case 'stats':
   731                 $url = $_CONF['site_url'] . '/stats.php';
   732                 $label = $LANG01[76];
   733                 if( $anon &&
   734                     ( $_CONF['loginrequired'] || $_CONF['statsloginrequired'] ))
   735                 {
   736                     $allowed = false;
   737                 }
   738                 break;
   739 
   740             default: // unknown entry
   741                 $url = '';
   742                 $label = '';
   743                 break;
   744         }
   745 
   746         if( !empty( $url ) && !empty( $label ))
   747         {
   748             $header->set_var( 'menuitem_url',  $url );
   749             $header->set_var( 'menuitem_text', $label );
   750             if( $last_entry )
   751             {
   752                 $header->parse( 'menu_elements', 'menuitem_last', true );
   753             }
   754             else
   755             {
   756                 $header->parse( 'menu_elements', 'menuitem', true );
   757             }
   758             $menuCounter++;
   759 
   760             if( $allowed )
   761             {
   762                 if( $last_entry )
   763                 {
   764                     $header->parse( 'allowed_menu_elements', 'menuitem_last',
   765                                     true );
   766                 }
   767                 else
   768                 {
   769                     $header->parse( 'allowed_menu_elements', 'menuitem', true );
   770                 }
   771                 $allowedCounter++;
   772             }
   773         }
   774     }
   775 
   776     if( $menuCounter == 0 )
   777     {
   778         $header->parse( 'menu_elements', 'menuitem_none', true );
   779     }
   780     if( $allowedCounter == 0 )
   781     {
   782         $header->parse( 'allowed_menu_elements', 'menuitem_none', true );
   783     }
   784 }
   785 
   786 /**
   787 * Returns the site header
   788 *
   789 * This loads the proper templates, does variable substitution and returns the
   790 * HTML for the site header with or without blocks depending on the value of $what
   791 *
   792 * Programming Note:
   793 *
   794 * The two functions COM_siteHeader and COM_siteFooter provide the framework for
   795 * page display in Geeklog.  COM_siteHeader controls the display of the Header
   796 * and left blocks and COM_siteFooter controls the dsiplay of the right blocks
   797 * and the footer.  You use them like a sandwich.  Thus the following code will
   798 * display a Geeklog page with both right and left blocks displayed.
   799 *
   800 * <code>
   801 * <?php
   802 * require_once 'lib-common.php';
   803 * // Change to COM_siteHeader('none') to not display left blocks
   804 * $display .= COM_siteHeader();
   805 * $display .= "Here is your html for display";
   806 * // Change to COM_siteFooter() to not display right blocks
   807 * $display .= COM_siteFooter(true);
   808 * echo $display;
   809 * ? >
   810 * </code>
   811 *
   812 * Note that the default for the header is to display the left blocks and the
   813 * default of the footer is to not display the right blocks.
   814 *
   815 * This sandwich produces code like this (greatly simplified)
   816 * <code>
   817 * // COM_siteHeader
   818 * <table><tr><td colspan="3">Header</td></tr>
   819 * <tr><td>Left Blocks</td><td>
   820 *
   821 * // Your HTML goes here
   822 * Here is your html for display
   823 *
   824 * // COM_siteFooter
   825 * </td><td>Right Blocks</td></tr>
   826 * <tr><td colspan="3">Footer</td></table>
   827 * </code>
   828 *
   829 * @param    string  $what       If 'none' then no left blocks are returned, if 'menu' (default) then right blocks are returned
   830 * @param    string  $pagetitle  optional content for the page's <title>
   831 * @param    string  $headercode optional code to go into the page's <head>
   832 * @return   string              Formatted HTML containing the site header
   833 * @see function COM_siteFooter
   834 *
   835 */
   836 function COM_siteHeader( $what = 'menu', $pagetitle = '', $headercode = '' )
   837 {
   838     global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_BUTTONS, $LANG_DIRECTION,
   839            $_IMAGE_TYPE, $topic, $_COM_VERBOSE;
   840 
   841     // If the theme implemented this for us then call their version instead.
   842 
   843     $function = $_CONF['theme'] . '_siteHeader';
   844 
   845     if( function_exists( $function ))
   846     {
   847         return $function( $what, $pagetitle, $headercode );
   848     }
   849 
   850     // If we reach here then either we have the default theme OR
   851     // the current theme only needs the default variable substitutions
   852 
   853     switch ($_CONF['doctype']) {
   854     case 'html401transitional':
   855         $doctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
   856         break;
   857 
   858     case 'html401strict':
   859         $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
   860         break;
   861 
   862     case 'xhtml10transitional':
   863         $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
   864         break;
   865 
   866     case 'xhtml10strict':
   867         $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
   868         break;
   869 
   870     default: // fallback: HTML 4.01 Transitional w/o system identifier
   871         $doctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">';
   872         break;
   873     }
   874 
   875     // send out the charset header
   876     header('Content-Type: text/html; charset=' . COM_getCharset());
   877 
   878     if (!empty($_CONF['frame_options'])) {
   879         header('X-FRAME-OPTIONS: ' . $_CONF['frame_options']);
   880     }
   881 
   882     $header = new Template( $_CONF['path_layout'] );
   883     $header->set_file( array(
   884         'header'        => 'header.thtml',
   885         'menuitem'      => 'menuitem.thtml',
   886         'menuitem_last' => 'menuitem_last.thtml',
   887         'menuitem_none' => 'menuitem_none.thtml',
   888         'leftblocks'    => 'leftblocks.thtml',
   889         'rightblocks'   => 'rightblocks.thtml'
   890         ));
   891     $header->set_var('doctype', $doctype);
   892     $header->set_var('xhtml', XHTML);
   893     if (XHTML == '') {
   894         $header->set_var('xmlns', '');
   895     } else {
   896         $header->set_var('xmlns', ' xmlns="http://www.w3.org/1999/xhtml"');
   897     }
   898 
   899     // get topic if not on home page
   900     if( !isset( $_GET['topic'] ))
   901     {
   902         if( isset( $_GET['story'] ))
   903         {
   904             $sid = COM_applyFilter( $_GET['story'] );
   905         }
   906         elseif( isset( $_GET['sid'] ))
   907         {
   908             $sid = COM_applyFilter( $_GET['sid'] );
   909         }
   910         elseif( isset( $_POST['story'] ))
   911         {
   912             $sid = COM_applyFilter( $_POST['story'] );
   913         }
   914         if( empty( $sid ) && $_CONF['url_rewrite'] &&
   915                 ( strpos( $_SERVER['PHP_SELF'], 'article.php' ) !== false ))
   916         {
   917             COM_setArgNames( array( 'story', 'mode' ));
   918             $sid = COM_applyFilter( COM_getArgument( 'story' ));
   919         }
   920         if( !empty( $sid ))
   921         {
   922             $topic = DB_getItem( $_TABLES['stories'], 'tid', "sid='$sid'" );
   923         }
   924     }
   925     else
   926     {
   927         $topic = COM_applyFilter( $_GET['topic'] );
   928     }
   929 
   930     $feed_url = array();
   931     if( $_CONF['backend'] == 1 ) // add feed-link to header if applicable
   932     {
   933         $baseurl = SYND_getFeedUrl();
   934 
   935         $sql = 'SELECT format, filename, title, language FROM '
   936              . $_TABLES['syndication'] . " WHERE (header_tid = 'all')";
   937         if( !empty( $topic ))
   938         {
   939             $sql .= " OR (header_tid = '" . addslashes( $topic ) . "')";
   940         }
   941         $result = DB_query( $sql );
   942         $numRows = DB_numRows( $result );
   943         for( $i = 0; $i < $numRows; $i++ )
   944         {
   945             $A = DB_fetchArray( $result );
   946             if ( !empty( $A['filename'] ))
   947             {
   948                 $format = explode( '-', $A['format'] );
   949                 $format_type = strtolower( $format[0] );
   950                 $format_name = ucwords( $format[0] );
   951 
   952                 $feed_url[] = '<link rel="alternate" type="application/'
   953                           . $format_type . '+xml" hreflang="' . $A['language']
   954                           . '" href="' . $baseurl . $A['filename'] . '" title="'
   955                           . $format_name . ' Feed: ' . $A['title'] . '"' . XHTML . '>';
   956             }
   957         }
   958     }
   959     $header->set_var( 'feed_url', implode( LB, $feed_url ));
   960 
   961     $relLinks = array();
   962     if( !COM_onFrontpage() )
   963     {
   964         $relLinks['home'] = '<link rel="home" href="' . $_CONF['site_url']
   965                           . '/" title="' . $LANG01[90] . '"' . XHTML . '>';
   966     }
   967     $loggedInUser = !COM_isAnonUser();
   968     if( $loggedInUser || (( $_CONF['loginrequired'] == 0 ) &&
   969                 ( $_CONF['searchloginrequired'] == 0 )))
   970     {
   971         if(( substr( $_SERVER['PHP_SELF'], -strlen( '/search.php' ))
   972                 != '/search.php' ) || isset( $_GET['mode'] ))
   973         {
   974             $relLinks['search'] = '<link rel="search" href="'
   975                                 . $_CONF['site_url'] . '/search.php" title="'
   976                                 . $LANG01[75] . '"' . XHTML . '>';
   977         }
   978     }
   979     if( $loggedInUser || (( $_CONF['loginrequired'] == 0 ) &&
   980                 ( $_CONF['directoryloginrequired'] == 0 )))
   981     {
   982         if( strpos( $_SERVER['PHP_SELF'], '/article.php' ) !== false ) {
   983             $relLinks['contents'] = '<link rel="contents" href="'
   984                         . $_CONF['site_url'] . '/directory.php" title="'
   985                         . $LANG01[117] . '"' . XHTML . '>';
   986         }
   987     }
   988     if (!$_CONF['disable_webservices']) {
   989         $relLinks['service'] = '<link rel="service" '
   990                     . 'type="application/atomsvc+xml" ' . 'href="'
   991                     . $_CONF['site_url'] . '/webservices/atom/?introspection" '
   992                     . 'title="' . $LANG01[130] . '"' . XHTML . '>';
   993     }
   994     // TBD: add a plugin API and a lib-custom.php function
   995     $header->set_var( 'rel_links', implode( LB, $relLinks ));
   996 
   997     $pagetitle_siteslogan = false;
   998     if( empty( $pagetitle ))
   999     {
  1000         if( empty( $topic ))
  1001         {
  1002             $pagetitle = $_CONF['site_slogan'];
  1003             $pagetitle_siteslogan = true;
  1004         }
  1005         else
  1006         {
  1007             $pagetitle = stripslashes( DB_getItem( $_TABLES['topics'], 'topic',
  1008                                                    "tid = '$topic'" ));
  1009         }
  1010     }
  1011     if( !empty( $pagetitle ))
  1012     {
  1013         $header->set_var( 'page_site_splitter', ' - ');
  1014     }
  1015     else
  1016     {
  1017         $header->set_var( 'page_site_splitter', '');
  1018     }
  1019     $header->set_var( 'page_title', $pagetitle );
  1020     $header->set_var( 'site_name', $_CONF['site_name']);
  1021 
  1022     if (COM_onFrontpage() OR $pagetitle_siteslogan) {
  1023         $title_and_name = $_CONF['site_name'];
  1024         if (!empty($pagetitle)) {
  1025             $title_and_name .= ' - ' . $pagetitle;
  1026         }
  1027     } else {
  1028         $title_and_name = '';
  1029         if (!empty($pagetitle)) {
  1030             $title_and_name = $pagetitle . ' - ';
  1031         }
  1032         $title_and_name .= $_CONF['site_name'];
  1033     }
  1034     $header->set_var('page_title_and_site_name', $title_and_name);
  1035 
  1036     COM_setLangIdAndAttribute($header);
  1037 
  1038     $header->set_var( 'background_image', $_CONF['layout_url']
  1039                                           . '/images/bg.' . $_IMAGE_TYPE );
  1040     $header->set_var( 'site_url', $_CONF['site_url'] );
  1041     $header->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  1042     $header->set_var( 'layout_url', $_CONF['layout_url'] );
  1043     $header->set_var( 'site_mail', "mailto:{$_CONF['site_mail']}" );
  1044     $header->set_var( 'site_name', $_CONF['site_name'] );
  1045     $header->set_var( 'site_slogan', $_CONF['site_slogan'] );
  1046     $rdf = substr_replace( $_CONF['rdf_file'], $_CONF['site_url'], 0,
  1047                            strlen( $_CONF['path_html'] ) - 1 );
  1048     $header->set_var( 'rdf_file', $rdf );
  1049     $header->set_var( 'rss_url', $rdf );
  1050 
  1051     $msg = rtrim($LANG01[67]) . ' ' . $_CONF['site_name'];
  1052 
  1053     if( !empty( $_USER['username'] ))
  1054     {
  1055         $msg .= ', ' . COM_getDisplayName( $_USER['uid'], $_USER['username'],
  1056                                            $_USER['fullname'] );
  1057     }
  1058 
  1059     $curtime =  COM_getUserDateTimeFormat();
  1060 
  1061     $header->set_var( 'welcome_msg', $msg );
  1062     $header->set_var( 'datetime', $curtime[0] );
  1063     $header->set_var( 'site_logo', $_CONF['layout_url']
  1064                                    . '/images/logo.' . $_IMAGE_TYPE );
  1065     $header->set_var( 'css_url', $_CONF['layout_url'] . '/style.css' );
  1066     $header->set_var( 'theme', $_CONF['theme'] );
  1067 
  1068     $header->set_var('charset', COM_getCharset());
  1069     $header->set_var('direction', $LANG_DIRECTION);
  1070 
  1071     // Now add variables for buttons like e.g. those used by the Yahoo theme
  1072     $header->set_var( 'button_home', $LANG_BUTTONS[1] );
  1073     $header->set_var( 'button_contact', $LANG_BUTTONS[2] );
  1074     $header->set_var( 'button_contribute', $LANG_BUTTONS[3] );
  1075     $header->set_var( 'button_sitestats', $LANG_BUTTONS[7] );
  1076     $header->set_var( 'button_personalize', $LANG_BUTTONS[8] );
  1077     $header->set_var( 'button_search', $LANG_BUTTONS[9] );
  1078     $header->set_var( 'button_advsearch', $LANG_BUTTONS[10] );
  1079     $header->set_var( 'button_directory', $LANG_BUTTONS[11] );
  1080 
  1081     // Get plugin menu options
  1082     $plugin_menu = PLG_getMenuItems();
  1083 
  1084     if( $_COM_VERBOSE )
  1085     {
  1086         COM_errorLog( 'num plugin menu items in header = ' . count( $plugin_menu ), 1 );
  1087     }
  1088 
  1089     // Now add nested template for menu items
  1090     COM_renderMenu( $header, $plugin_menu );
  1091 
  1092     if( count( $plugin_menu ) == 0 )
  1093     {
  1094         $header->parse( 'plg_menu_elements', 'menuitem_none', true );
  1095     }
  1096     else
  1097     {
  1098         $count_plugin_menu = count( $plugin_menu );
  1099         for( $i = 1; $i <= $count_plugin_menu; $i++ )
  1100         {
  1101             $header->set_var( 'menuitem_url', current( $plugin_menu ));
  1102             $header->set_var( 'menuitem_text', key( $plugin_menu ));
  1103 
  1104             if( $i == $count_plugin_menu )
  1105             {
  1106                 $header->parse( 'plg_menu_elements', 'menuitem_last', true );
  1107             }
  1108             else
  1109             {
  1110                 $header->parse( 'plg_menu_elements', 'menuitem', true );
  1111             }
  1112 
  1113             next( $plugin_menu );
  1114         }
  1115     }
  1116 
  1117     // Call to plugins to set template variables in the header
  1118     PLG_templateSetVars( 'header', $header );
  1119 
  1120     if( $_CONF['left_blocks_in_footer'] == 1 )
  1121     {
  1122         $header->set_var( 'left_blocks', '' );
  1123         $header->set_var( 'geeklog_blocks', '' );
  1124     }
  1125     else
  1126     {
  1127         $lblocks = '';
  1128 
  1129         /* Check if an array has been passed that includes the name of a plugin
  1130          * function or custom function
  1131          * This can be used to take control over what blocks are then displayed
  1132          */
  1133         if( is_array( $what ))
  1134         {
  1135             $function = $what[0];
  1136             if( function_exists( $function ))
  1137             {
  1138                 $lblocks = $function( $what[1], 'left' );
  1139             }
  1140             else
  1141             {
  1142                 $lblocks = COM_showBlocks( 'left', $topic );
  1143             }
  1144         }
  1145         else if( $what <> 'none' )
  1146         {
  1147             // Now show any blocks -- need to get the topic if not on home page
  1148             $lblocks = COM_showBlocks( 'left', $topic );
  1149         }
  1150 
  1151         if( empty( $lblocks ))
  1152         {
  1153             $header->set_var( 'left_blocks', '' );
  1154             $header->set_var( 'geeklog_blocks', '' );
  1155         }
  1156         else
  1157         {
  1158             $header->set_var( 'geeklog_blocks', $lblocks );
  1159             $header->parse( 'left_blocks', 'leftblocks', true );
  1160             $header->set_var( 'geeklog_blocks', '');
  1161         }
  1162     }
  1163 
  1164     if( $_CONF['right_blocks_in_footer'] == 1 )
  1165     {
  1166         $header->set_var( 'right_blocks', '' );
  1167         $header->set_var( 'geeklog_blocks', '' );
  1168     }
  1169     else
  1170     {
  1171         $rblocks = '';
  1172 
  1173         /* Check if an array has been passed that includes the name of a plugin
  1174          * function or custom function
  1175          * This can be used to take control over what blocks are then displayed
  1176          */
  1177         if( is_array( $what ))
  1178         {
  1179             $function = $what[0];
  1180             if( function_exists( $function ))
  1181             {
  1182                 $rblocks = $function( $what[1], 'right' );
  1183             }
  1184             else
  1185             {
  1186                 $rblocks = COM_showBlocks( 'right', $topic );
  1187             }
  1188         }
  1189         else if( $what <> 'none' )
  1190         {
  1191             // Now show any blocks -- need to get the topic if not on home page
  1192             $rblocks = COM_showBlocks( 'right', $topic );
  1193         }
  1194 
  1195         if( empty( $rblocks ))
  1196         {
  1197             $header->set_var( 'right_blocks', '' );
  1198             $header->set_var( 'geeklog_blocks', '' );
  1199         }
  1200         else
  1201         {
  1202             $header->set_var( 'geeklog_blocks', $rblocks, true );
  1203             $header->parse( 'right_blocks', 'rightblocks', true );
  1204         }
  1205     }
  1206 
  1207     if( isset( $_CONF['advanced_editor'] ) && ( $_CONF['advanced_editor'] == 1 )
  1208             && file_exists( $_CONF['path_layout']
  1209                             . 'advanced_editor_header.thtml' ))
  1210     {
  1211         $header->set_file( 'editor'  , 'advanced_editor_header.thtml');
  1212         $header->parse( 'advanced_editor', 'editor' );
  1213 
  1214     }
  1215     else
  1216     {
  1217          $header->set_var( 'advanced_editor', '' );
  1218     }
  1219 
  1220     // Call any plugin that may want to include extra Meta tags
  1221     // or Javascript functions
  1222     $headercode .= PLG_getHeaderCode();
  1223     
  1224     // Meta Tags
  1225     // 0 = Disabled, 1 = Enabled, 2 = Enabled but default just for homepage
  1226     if ($_CONF['meta_tags'] > 0) {
  1227         $meta_description = '';
  1228         $meta_keywords = '';
  1229         $no_meta_description = 1;
  1230         $no_meta_keywords = 1;
  1231         
  1232         //Find out if the meta tag description or keywords already exist in the headercode
  1233         if ($headercode != '') { 
  1234             $pattern = '/<meta ([^>]*)name="([^"\'>]*)"([^>]*)/im'; 
  1235             if (preg_match_all($pattern, $headercode, $matches, PREG_SET_ORDER)) {
  1236                 // Loop through all meta tags looking for description and keywords
  1237                 for ($i = 0; $i<count($matches) && (($no_meta_description == 1) || ($no_meta_keywords == 1)); $i++) { 
  1238                     $str_matches = strtolower($matches[$i][0]); 
  1239                     $pos = strpos($str_matches,'name='); 
  1240                     if (!(is_bool($pos) && !$pos)) { 
  1241                         $name = trim(substr($str_matches,$pos+5),'"'); 
  1242                         $pos = strpos($name,'"'); 
  1243                         $name = substr($name,0,$pos); 
  1244 
  1245                         if (strcasecmp("description",$name) == 0) { 
  1246                             $pos = strpos($str_matches,'content='); 
  1247                             if (!(is_bool($pos) && !$pos)) {
  1248                                 $no_meta_description = 0;
  1249                             }
  1250                         }
  1251                         if (strcasecmp("keywords",$name) == 0) { 
  1252                             $pos = strpos($str_matches,'content='); 
  1253                             if (!(is_bool($pos) && !$pos)) {
  1254                                 $no_meta_keywords = 0;
  1255                             }
  1256                         }
  1257                         
  1258                     }
  1259                 }
  1260             } 
  1261         }
  1262         
  1263         If (COM_onFrontpage() && $_CONF['meta_tags'] == 2) { // Display default meta tags only on home page
  1264             If ($no_meta_description) {
  1265                 $meta_description = $_CONF['meta_description'];
  1266             }
  1267             If ($no_meta_keywords) {
  1268                 $meta_keywords = $_CONF['meta_keywords'];
  1269             }
  1270         } else if ( $_CONF['meta_tags'] == 1 ) { // Display default meta tags anywhere there are no tags
  1271             If ($no_meta_description) {
  1272                 $meta_description = $_CONF['meta_description'];
  1273             }
  1274             If ($no_meta_keywords) {
  1275                 $meta_keywords = $_CONF['meta_keywords'];
  1276             }            
  1277         }
  1278         
  1279         If ($no_meta_description OR $no_meta_keywords) {
  1280             $headercode .= COM_createMetaTags($meta_description, $meta_keywords);
  1281         }
  1282     }
  1283     
  1284     $header->set_var( 'plg_headercode', $headercode );
  1285 
  1286     // The following lines allow users to embed PHP in their templates.  This
  1287     // is almost a contradition to the reasons for using templates but this may
  1288     // prove useful at times ...
  1289     // Don't use PHP in templates if you can live without it!
  1290 
  1291     $tmp = $header->finish($header->parse('index_header', 'header'));
  1292 
  1293     $xml_declaration = '';
  1294     if ( get_cfg_var('short_open_tag') == '1' )
  1295     {
  1296         if ( preg_match( '/(<\?xml[^>]*>)(.*)/s', $tmp, $match ) )
  1297         {
  1298             $xml_declaration = $match[1] . LB;
  1299             $tmp = $match[2];
  1300         }
  1301     }
  1302 
  1303     ob_start();
  1304     eval( '?>' . $tmp );
  1305     $retval = $xml_declaration . ob_get_contents();
  1306     ob_end_clean();
  1307 
  1308     return $retval;
  1309 }
  1310 
  1311 
  1312 /**
  1313 * Returns the site footer
  1314 *
  1315 * This loads the proper templates, does variable substitution and returns the
  1316 * HTML for the site footer.
  1317 *
  1318 * @param   boolean     $rightblock     Whether or not to show blocks on right hand side default is no
  1319 * @param   array       $custom         An array defining custom function to be used to format Rightblocks
  1320 * @see function COM_siteHeader
  1321 * @return   string  Formated HTML containing site footer and optionally right blocks
  1322 *
  1323 */
  1324 function COM_siteFooter( $rightblock = -1, $custom = '' )
  1325 {
  1326     global $_CONF, $_TABLES, $LANG01, $_PAGE_TIMER, $topic, $LANG_BUTTONS;
  1327 
  1328     // If the theme implemented this for us then call their version instead.
  1329 
  1330     $function = $_CONF['theme'] . '_siteFooter';
  1331 
  1332     if( function_exists( $function ))
  1333     {
  1334         return $function( $rightblock, $custom );
  1335     }
  1336 
  1337     COM_hit();
  1338 
  1339     // Set template directory
  1340     $footer = new Template( $_CONF['path_layout'] );
  1341 
  1342     // Set template file
  1343     $footer->set_file( array(
  1344             'footer'      => 'footer.thtml',
  1345             'rightblocks' => 'rightblocks.thtml',
  1346             'leftblocks'  => 'leftblocks.thtml'
  1347             ));
  1348 
  1349     // Do variable assignments
  1350     $footer->set_var( 'xhtml', XHTML );
  1351     $footer->set_var( 'site_url', $_CONF['site_url']);
  1352     $footer->set_var( 'site_admin_url', $_CONF['site_admin_url']);
  1353     $footer->set_var( 'layout_url',$_CONF['layout_url']);
  1354     $footer->set_var( 'site_mail', "mailto:{$_CONF['site_mail']}" );
  1355     $footer->set_var( 'site_name', $_CONF['site_name'] );
  1356     $footer->set_var( 'site_slogan', $_CONF['site_slogan'] );
  1357     $rdf = substr_replace( $_CONF['rdf_file'], $_CONF['site_url'], 0,
  1358                            strlen( $_CONF['path_html'] ) - 1 );
  1359     $footer->set_var( 'rdf_file', $rdf );
  1360     $footer->set_var( 'rss_url', $rdf );
  1361 
  1362     $year = date( 'Y' );
  1363     $copyrightyear = $year;
  1364     if( !empty( $_CONF['copyrightyear'] ))
  1365     {
  1366         $copyrightyear = $_CONF['copyrightyear'];
  1367     }
  1368     $footer->set_var( 'copyright_notice', '&nbsp;' . $LANG01[93] . ' &copy; '
  1369             . $copyrightyear . ' ' . $_CONF['site_name'] . '<br' . XHTML . '>&nbsp;'
  1370             . $LANG01[94] );
  1371     $footer->set_var( 'copyright_msg', $LANG01[93] . ' &copy; '
  1372             . $copyrightyear . ' ' . $_CONF['site_name'] );
  1373     $footer->set_var( 'current_year', $year );
  1374     $footer->set_var( 'lang_copyright', $LANG01[93] );
  1375     $footer->set_var( 'trademark_msg', $LANG01[94] );
  1376     $footer->set_var( 'powered_by', $LANG01[95] );
  1377     $footer->set_var( 'geeklog_url', 'http://www.geeklog.net/' );
  1378     $footer->set_var( 'geeklog_version', VERSION );
  1379     // Now add variables for buttons like e.g. those used by the Yahoo theme
  1380     $footer->set_var( 'button_home', $LANG_BUTTONS[1] );
  1381     $footer->set_var( 'button_contact', $LANG_BUTTONS[2] );
  1382     $footer->set_var( 'button_contribute', $LANG_BUTTONS[3] );
  1383     $footer->set_var( 'button_sitestats', $LANG_BUTTONS[7] );
  1384     $footer->set_var( 'button_personalize', $LANG_BUTTONS[8] );
  1385     $footer->set_var( 'button_search', $LANG_BUTTONS[9] );
  1386     $footer->set_var( 'button_advsearch', $LANG_BUTTONS[10] );
  1387     $footer->set_var( 'button_directory', $LANG_BUTTONS[11] );
  1388 
  1389     /* Right blocks. Argh. Don't talk to me about right blocks...
  1390      * Right blocks will be displayed if Right_blocks_in_footer is set [1],
  1391      * AND (this function has been asked to show them (first param) OR the
  1392      * show_right_blocks conf variable has been set to override what the code
  1393      * wants to do.
  1394      *
  1395      * If $custom sets an array (containing functionname and first argument)
  1396      * then this is used instead of the default (COM_showBlocks) to render
  1397      * the right blocks (and left).
  1398      *
  1399      * [1] - if it isn't, they'll be in the header already.
  1400      *
  1401      */
  1402     $displayRightBlocks = true;
  1403     if ($_CONF['right_blocks_in_footer'] == 1)
  1404     {
  1405         if( ($rightblock < 0) || !$rightblock )
  1406         {
  1407             if( isset( $_CONF['show_right_blocks'] ) )
  1408             {
  1409                 $displayRightBlocks = $_CONF['show_right_blocks'];
  1410             }
  1411             else
  1412             {
  1413                 $displayRightBlocks = false;
  1414             }
  1415         } else {
  1416             $displayRightBlocks = true;
  1417         }
  1418     } else {
  1419         $displayRightBlocks = false;
  1420     }
  1421     
  1422     if ($displayRightBlocks)
  1423     {
  1424         /* Check if an array has been passed that includes the name of a plugin
  1425          * function or custom function.
  1426          * This can be used to take control over what blocks are then displayed
  1427          */
  1428         if( is_array( $custom ))
  1429         {
  1430             $function = $custom['0'];
  1431             if( function_exists( $function ))
  1432             {
  1433                 $rblocks = $function( $custom['1'], 'right' );
  1434             } else {
  1435                 $rblocks = COM_showBlocks( 'right', $topic );
  1436             }
  1437         } else {
  1438             $rblocks = COM_showBlocks( 'right', $topic );
  1439         }
  1440         
  1441         if( empty( $rblocks ))
  1442         {
  1443             $footer->set_var( 'geeklog_blocks', '');
  1444             $footer->set_var( 'right_blocks', '' );
  1445         } else {
  1446             $footer->set_var( 'geeklog_blocks', $rblocks);
  1447             $footer->parse( 'right_blocks', 'rightblocks', true );
  1448             $footer->set_var( 'geeklog_blocks', '');
  1449         }
  1450     } else {
  1451         $footer->set_var( 'geeklog_blocks', '');
  1452         $footer->set_var( 'right_blocks', '' );
  1453     }
  1454 
  1455     if( $_CONF['left_blocks_in_footer'] == 1 )
  1456     {
  1457         $lblocks = '';
  1458 
  1459         /* Check if an array has been passed that includes the name of a plugin
  1460          * function or custom function
  1461          * This can be used to take control over what blocks are then displayed
  1462          */
  1463         if( is_array( $custom ))
  1464         {
  1465             $function = $custom[0];
  1466             if( function_exists( $function ))
  1467             {
  1468                 $lblocks = $function( $custom[1], 'left' );
  1469             }
  1470         }
  1471         else
  1472         {
  1473             $lblocks = COM_showBlocks( 'left', $topic );
  1474         }
  1475 
  1476         if( empty( $lblocks ))
  1477         {
  1478             $footer->set_var( 'left_blocks', '' );
  1479             $footer->set_var( 'geeklog_blocks', '');
  1480         }
  1481         else
  1482         {
  1483             $footer->set_var( 'geeklog_blocks', $lblocks);
  1484             $footer->parse( 'left_blocks', 'leftblocks', true );
  1485             $footer->set_var( 'geeklog_blocks', '');
  1486         }
  1487     }
  1488 
  1489     // Global centerspan variable set in index.php
  1490     if( isset( $GLOBALS['centerspan'] ))
  1491     {
  1492         $footer->set_var( 'centerblockfooter-span', '</td></tr></table>' );
  1493     }
  1494 
  1495     $exectime = $_PAGE_TIMER->stopTimer();
  1496     $exectext = $LANG01[91] . ' ' . $exectime . ' ' . $LANG01[92];
  1497 
  1498     $footer->set_var( 'execution_time', $exectime );
  1499     $footer->set_var( 'execution_textandtime', $exectext );
  1500 
  1501     // Call to plugins to set template variables in the footer
  1502     PLG_templateSetVars( 'footer', $footer );
  1503 
  1504     // Actually parse the template and make variable substitutions
  1505     $footer->parse( 'index_footer', 'footer' );
  1506 
  1507     // Return resulting HTML
  1508     return $footer->finish( $footer->get_var( 'index_footer' ));
  1509 }
  1510 
  1511 /**
  1512 * Prints out standard block header
  1513 *
  1514 * Prints out standard block header but pulling header HTML formatting from
  1515 * the database.
  1516 *
  1517 * Programming Note:  The two functions COM_startBlock and COM_endBlock are used
  1518 * to sandwich your block content.  These functions are not used only for blocks
  1519 * but anything that uses that format, e.g. Stats page.  They are used like
  1520 * COM_siteHeader and COM_siteFooter but for internal page elements.
  1521 *
  1522 * @param    string  $title      Value to set block title to
  1523 * @param    string  $helpfile   Help file, if one exists
  1524 * @param    string  $template   HTML template file to use to format the block
  1525 * @return   string              Formatted HTML containing block header
  1526 * @see COM_endBlock
  1527 * @see COM_siteHeader
  1528 *
  1529 */
  1530 
  1531 function COM_startBlock( $title='', $helpfile='', $template='blockheader.thtml' )
  1532 {
  1533     global $_CONF, $LANG01, $_IMAGE_TYPE;
  1534 
  1535     $block = new Template( $_CONF['path_layout'] );
  1536     $block->set_file( 'block', $template );
  1537 
  1538     $block->set_var( 'xhtml', XHTML );
  1539     $block->set_var( 'site_url', $_CONF['site_url'] );
  1540     $block->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  1541     $block->set_var( 'layout_url', $_CONF['layout_url'] );
  1542     $block->set_var( 'block_title', stripslashes( $title ));
  1543 
  1544     if( !empty( $helpfile ))
  1545     {
  1546         $helpimg = $_CONF['layout_url'] . '/images/button_help.' . $_IMAGE_TYPE;
  1547         $help_content = '<img src="' . $helpimg. '" alt="?"' . XHTML . '>';
  1548         $help_attr = array('class'=>'blocktitle');
  1549         if( !stristr( $helpfile, 'http://' ))
  1550         {
  1551             $help_url = $_CONF['site_url'] . "/help/$helpfile";
  1552         }
  1553         else
  1554         {
  1555             $help_url = $helpfile;
  1556         }
  1557         $help = COM_createLink($help_content, $help_url, $help_attr);
  1558         $block->set_var( 'block_help', $help );
  1559     }
  1560 
  1561     $block->parse( 'startHTML', 'block' );
  1562 
  1563     return $block->finish( $block->get_var( 'startHTML' ));
  1564 }
  1565 
  1566 /**
  1567 * Closes out COM_startBlock
  1568 *
  1569 * @param        string      $template       HTML template file used to format block footer
  1570 * @return   string  Formatted HTML to close block
  1571 * @see function COM_startBlock
  1572 *
  1573 */
  1574 function COM_endBlock( $template='blockfooter.thtml' )
  1575 {
  1576     global $_CONF;
  1577 
  1578     $block = new Template( $_CONF['path_layout'] );
  1579     $block->set_file( 'block', $template );
  1580 
  1581     $block->set_var( 'xhtml', XHTML );
  1582     $block->set_var( 'site_url', $_CONF['site_url'] );
  1583     $block->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  1584     $block->set_var( 'layout_url', $_CONF['layout_url'] );
  1585     $block->parse( 'endHTML', 'block' );
  1586 
  1587     return $block->finish( $block->get_var( 'endHTML' ));
  1588 }
  1589 
  1590 
  1591 /**
  1592 * Creates a <option> list from a database list for use in forms
  1593 *
  1594 * Creates option list form field using given arguments
  1595 *
  1596 * @param        string      $table      Database Table to get data from
  1597 * @param        string      $selection  Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed.  This is used in a SQL statement and can include DISTINCT to start.
  1598 * @param        string/array      $selected   Value (from $selection) to set to SELECTED or default
  1599 * @param        int         $sortcol    Which field to sort option list by 0 (value) or 1 (label)
  1600 * @param        string      $where      Optional WHERE clause to use in the SQL Selection
  1601 * @see function COM_checkList
  1602 * @return   string  Formated HTML of option values
  1603 *
  1604 */
  1605 function COM_optionList( $table, $selection, $selected='', $sortcol=1, $where='' )
  1606 {
  1607     global $_DB_table_prefix;
  1608 
  1609     $retval = '';
  1610 
  1611     $LangTableName = '';
  1612     if( substr( $table, 0, strlen( $_DB_table_prefix )) == $_DB_table_prefix )
  1613     {
  1614         $LangTableName = 'LANG_' . substr( $table, strlen( $_DB_table_prefix ));
  1615     }
  1616     else
  1617     {
  1618         $LangTableName = 'LANG_' . $table;
  1619     }
  1620 
  1621     global $$LangTableName;
  1622 
  1623     if( isset( $$LangTableName ))
  1624     {
  1625         $LangTable = $$LangTableName;
  1626     }
  1627     else
  1628     {
  1629         $LangTable = array();
  1630     }
  1631 
  1632     $tmp = str_replace( 'DISTINCT ', '', $selection );
  1633     $select_set = explode( ',', $tmp );
  1634 
  1635     $sql = "SELECT $selection FROM $table";
  1636     if( $where != '' )
  1637     {
  1638         $sql .= " WHERE $where";
  1639     }
  1640     $sql .= " ORDER BY {$select_set[$sortcol]}";
  1641     $result = DB_query( $sql );
  1642     $nrows = DB_numRows( $result );
  1643 
  1644     for( $i = 0; $i < $nrows; $i++ )
  1645     {
  1646         $A = DB_fetchArray( $result, true );
  1647         $retval .= '<option value="' . $A[0] . '"';
  1648 
  1649         if( is_array( $selected ) AND count( $selected ) > 0 )
  1650         {
  1651             foreach( $selected as $selected_item )
  1652             {
  1653                 if( $A[0] == $selected_item )
  1654                 {
  1655                     $retval .= ' selected="selected"';
  1656                 }
  1657             }
  1658         }
  1659         elseif( !is_array( $selected ) AND $A[0] == $selected )
  1660         {
  1661             $retval .= ' selected="selected"';
  1662         }
  1663 
  1664         $retval .= '>';
  1665         if( empty( $LangTable[$A[0]] ))
  1666         {
  1667             $retval .= $A[1];
  1668         }
  1669         else
  1670         {
  1671             $retval .= $LangTable[$A[0]];
  1672         }
  1673         $retval .= '</option>' . LB;
  1674     }
  1675 
  1676     return $retval;
  1677 }
  1678 
  1679 /**
  1680 * Create and return a dropdown-list of available topics
  1681 *
  1682 * This is a variation of COM_optionList() from lib-common.php. It will add
  1683 * only those topics to the option list which are accessible by the current
  1684 * user.
  1685 *
  1686 * @param        string      $selection  Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed.  This is used in a SQL statement and can include DISTINCT to start.
  1687 * @param        string      $selected   Value (from $selection) to set to SELECTED or default
  1688 * @param        int         $sortcol    Which field to sort option list by 0 (value) or 1 (label)
  1689 * @param        boolean     $ignorelang Whether to return all topics (true) or only the ones for the current language (false)
  1690 * @see function COM_optionList
  1691 * @return   string  Formated HTML of option values
  1692 *
  1693 */
  1694 function COM_topicList( $selection, $selected = '', $sortcol = 1, $ignorelang = false )
  1695 {
  1696     global $_TABLES;
  1697 
  1698     $retval = '';
  1699 
  1700     $topics = COM_topicArray($selection, $sortcol, $ignorelang);
  1701     foreach ($topics as $tid => $topic) {
  1702         $retval .= '<option value="' . $tid . '"';
  1703         if ($tid == $selected) {
  1704             $retval .= ' selected="selected"';
  1705         }
  1706         $retval .= '>' . $topic . '</option>' . LB;
  1707     }
  1708 
  1709     return $retval;
  1710 }
  1711 
  1712 /**
  1713 * Return a list of topics in an array
  1714 * (derived from COM_topicList - API may change)
  1715 *
  1716 * @param    string  $selection  Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed.  This is used in a SQL statement and can include DISTINCT to start.
  1717 * @param    int     $sortcol    Which field to sort option list by 0 (value) or 1 (label)
  1718 * @param    boolean $ignorelang Whether to return all topics (true) or only the ones for the current language (false)
  1719 * @return   array               Array of topics
  1720 * @see function COM_topicList
  1721 *
  1722 */
  1723 function COM_topicArray($selection, $sortcol = 0, $ignorelang = false)
  1724 {
  1725     global $_TABLES;
  1726 
  1727     $retval = array();
  1728 
  1729     $tmp = str_replace('DISTINCT ', '', $selection);
  1730     $select_set = explode(',', $tmp);
  1731 
  1732     $sql = "SELECT $selection FROM {$_TABLES['topics']}";
  1733     if ($ignorelang) {
  1734         $sql .= COM_getPermSQL();
  1735     } else {
  1736         $permsql = COM_getPermSQL();
  1737         if (empty($permsql)) {
  1738             $sql .= COM_getLangSQL('tid');
  1739         } else {
  1740             $sql .= $permsql . COM_getLangSQL('tid', 'AND');
  1741         }
  1742     }
  1743     $sql .=  " ORDER BY $select_set[$sortcol]";
  1744 
  1745     $result = DB_query($sql);
  1746     $nrows = DB_numRows($result);
  1747 
  1748     if (count($select_set) > 1) {
  1749         for ($i = 0; $i < $nrows; $i++) {
  1750             $A = DB_fetchArray($result, true);
  1751             $retval[$A[0]] = stripslashes($A[1]);
  1752         }
  1753     } else {
  1754         for ($i = 0; $i < $nrows; $i++) {
  1755             $A = DB_fetchArray($result, true);
  1756             $retval[] = $A[0];
  1757         }
  1758     }
  1759 
  1760     return $retval;
  1761 }
  1762 
  1763 /**
  1764 * Creates a <input> checklist from a database list for use in forms
  1765 *
  1766 * Creates a group of checkbox form fields with given arguments
  1767 *
  1768 * @param    string  $table      DB Table to pull data from
  1769 * @param    string  $selection  Comma delimited list of fields to pull from table
  1770 * @param    string  $where      Where clause of SQL statement
  1771 * @param    string  $selected   Value to set to CHECKED
  1772 * @param    string  $fieldname  Name to use for the checkbox array
  1773 * @return   string              HTML with Checkbox code
  1774 * @see      COM_optionList
  1775 *
  1776 */
  1777 function COM_checkList($table, $selection, $where = '', $selected = '', $fieldname = '')
  1778 {
  1779     global $_TABLES, $_COM_VERBOSE;
  1780 
  1781     $sql = "SELECT $selection FROM $table";
  1782 
  1783     if( !empty( $where ))
  1784     {
  1785         $sql .= " WHERE $where";
  1786     }
  1787 
  1788     $result = DB_query( $sql );
  1789     $nrows = DB_numRows( $result );
  1790 
  1791     if( !empty( $selected ))
  1792     {
  1793         if( $_COM_VERBOSE )
  1794         {
  1795             COM_errorLog( "exploding selected array: $selected in COM_checkList", 1 );
  1796         }
  1797 
  1798         $S = explode( ' ', $selected );
  1799     }
  1800     else
  1801     {
  1802         if( $_COM_VERBOSE)
  1803         {
  1804             COM_errorLog( 'selected string was empty COM_checkList', 1 );
  1805         }
  1806 
  1807         $S = array();
  1808     }
  1809     $retval = '<ul class="checkboxes-list">' . LB;
  1810     for( $i = 0; $i < $nrows; $i++ )
  1811     {
  1812         $access = true;
  1813         $A = DB_fetchArray( $result, true );
  1814 
  1815         if( $table == $_TABLES['topics'] AND SEC_hasTopicAccess( $A['tid'] ) == 0 )
  1816         {
  1817             $access = false;
  1818         }
  1819 
  1820         if (empty($fieldname)) {
  1821             // Not a good idea, as that will expose our table name and prefix!
  1822             // Make sure you pass a distinct field name!
  1823             $fieldname = $table;
  1824         }
  1825 
  1826         if( $access )
  1827         {
  1828             $retval .= '<li><input type="checkbox" name="' . $fieldname . '[]" value="' . $A[0] . '"';
  1829 
  1830             $sizeS = count( $S );
  1831             for( $x = 0; $x < $sizeS; $x++ )
  1832             {
  1833                 if( $A[0] == $S[$x] )
  1834                 {
  1835                     $retval .= ' checked="checked"';
  1836                     break;
  1837                 }
  1838             }
  1839 
  1840             if(( $table == $_TABLES['blocks'] ) && isset( $A[2] ) && ( $A[2] == 'gldefault' ))
  1841             {
  1842                 $retval .= XHTML . '><span class="gldefault">' . stripslashes( $A[1] ) . '</span></li>' . LB;
  1843             }
  1844             else
  1845             {
  1846                 $retval .= XHTML . '><span>' . stripslashes( $A[1] ) . '</span></li>' . LB;
  1847             }
  1848         }
  1849     }
  1850     $retval .= '</ul>' . LB;
  1851 
  1852     return $retval;
  1853 }
  1854 
  1855 /**
  1856 * Prints out an associative array for debugging
  1857 *
  1858 * The core of this code has been lifted from phpweblog which is licenced
  1859 * under the GPL.  This is not used very much in the code but you can use it
  1860 * if you see fit
  1861 *
  1862 * @param    array   $array    Array to loop through and print values for
  1863 * @return   string  $retval    Formatted HTML List
  1864 *
  1865 */
  1866 
  1867 function COM_debug($array)
  1868 {
  1869     $retval = '';    
  1870     if(!empty($array)) {
  1871         $retval = '<ul><pre><p>---- DEBUG ----</p>';
  1872         foreach($array as $k => $v) { 
  1873             $retval .= sprintf("<li>%13s [%s]</li>\n", $k, $v);
  1874         }
  1875         $retval .= '<p>---------------</p></pre></ul>';
  1876     }    
  1877     return $retval;
  1878 }
  1879 
  1880 /**
  1881 *
  1882 * Checks to see if RDF file needs updating and updates it if so.
  1883 * Checks to see if we need to update the RDF as a result
  1884 * of an article with a future publish date reaching it's
  1885 * publish time and if so updates the RDF file.
  1886 *
  1887 * NOTE: When called without parameters, this will only check for new entries to
  1888 *       include in the feeds. Pass the $updated_XXX parameters when the content
  1889 *       of an existing entry has changed.
  1890 *
  1891 * @param    string  $updated_type   (optional) feed type to update
  1892 * @param    string  $updated_topic  (optional) feed topic to update
  1893 * @param    string  $updated_id     (optional) feed id to update
  1894 *
  1895 * @see file lib-syndication.php
  1896 *
  1897 */
  1898 function COM_rdfUpToDateCheck( $updated_type = '', $updated_topic = '', $updated_id = '' )
  1899 {
  1900     global $_CONF, $_TABLES;
  1901 
  1902     if( $_CONF['backend'] > 0 )
  1903     {
  1904         if( !empty( $updated_type ) && ( $updated_type != 'article' ))
  1905         {
  1906             // when a plugin's feed is to be updated, skip Geeklog's own feeds
  1907             $sql = "SELECT fid,type,topic,limits,update_info FROM {$_TABLES['syndication']} WHERE (is_enabled = 1) AND (type <> 'article')";
  1908         }
  1909         else
  1910         {
  1911             $sql = "SELECT fid,type,topic,limits,update_info FROM {$_TABLES['syndication']} WHERE is_enabled = 1";
  1912         }
  1913         $result = DB_query( $sql );
  1914         $num = DB_numRows( $result );
  1915         for( $i = 0; $i < $num; $i++)
  1916         {
  1917             $A = DB_fetchArray( $result );
  1918 
  1919             $is_current = true;
  1920             if( $A['type'] == 'article' )
  1921             {
  1922                 $is_current = SYND_feedUpdateCheck( $A['topic'],
  1923                                 $A['update_info'], $A['limits'],
  1924                                 $updated_topic, $updated_id );
  1925             }
  1926             else
  1927             {
  1928                 $is_current = PLG_feedUpdateCheck( $A['type'], $A['fid'],
  1929                                 $A['topic'], $A['update_info'], $A['limits'],
  1930                                 $updated_type, $updated_topic, $updated_id );
  1931             }
  1932             if( !$is_current )
  1933             {
  1934                 SYND_updateFeed( $A['fid'] );
  1935             }
  1936         }
  1937     }
  1938 }
  1939 
  1940 /**
  1941 * Checks and Updates the featured status of all articles.
  1942 *
  1943 * Checks to see if any articles that were published for the future have been
  1944 * published and, if so, will see if they are featured.  If they are featured,
  1945 * this will set old featured article (if there is one) to normal
  1946 *
  1947 */
  1948 
  1949 function COM_featuredCheck()
  1950 {
  1951     global $_TABLES;
  1952 
  1953     $curdate = date( "Y-m-d H:i:s", time() );
  1954 
  1955     // Loop through each topic
  1956     $sql = "SELECT tid FROM {$_TABLES['topics']}";
  1957     $result = DB_query( $sql );
  1958     $num = DB_numRows( $result );
  1959     for( $i = 0; $i < $num; $i++)
  1960     {
  1961         $A = DB_fetchArray( $result );
  1962 
  1963         if( DB_getItem( $_TABLES['stories'], 'COUNT(*)', "featured = 1 AND draft_flag = 0 AND tid = '{$A['tid']}' AND date <= '$curdate'" ) > 1 )
  1964         {
  1965             // OK, we have two featured stories in a topic, fix that
  1966             $sid = DB_getItem( $_TABLES['stories'], 'sid', "featured = 1 AND draft_flag = 0 ORDER BY date LIMIT 1" );
  1967             DB_query( "UPDATE {$_TABLES['stories']} SET featured = 0 WHERE sid = '$sid'" );
  1968         }
  1969     }
  1970 }
  1971 
  1972 /**
  1973 *
  1974 * Logs messages to error.log or the web page or both
  1975 *
  1976 * Prints a well formatted message to either the web page, error log
  1977 * or both.
  1978 *
  1979 * @param        string      $logentry       Text to log to error log
  1980 * @param        int         $actionid       where 1 = write to log file, 2 = write to screen (default) both
  1981 * @see function COM_accessLog
  1982 * @return   string  If $actionid = 2 or '' then HTML formatted string (wrapped in block) else nothing
  1983 *
  1984 */
  1985 
  1986 function COM_errorLog( $logentry, $actionid = '' )
  1987 {
  1988     global $_CONF, $LANG01;
  1989 
  1990     $retval = '';
  1991 
  1992     if( !empty( $logentry ))
  1993     {
  1994         $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
  1995                                  $logentry );
  1996 
  1997         $timestamp = @strftime( '%c' );
  1998 
  1999         if (!isset($_CONF['path_layout']) &&
  2000                 (($actionid == 2) || empty($actionid))) {
  2001             $actionid = 1;
  2002         }
  2003         if (!isset($_CONF['path_log']) && ($actionid != 2)) {
  2004             $actionid = 3;
  2005         }
  2006 
  2007         switch( $actionid )
  2008         {
  2009             case 1:
  2010                 $logfile = $_CONF['path_log'] . 'error.log';
  2011 
  2012                 if( !$file = fopen( $logfile, 'a' ))
  2013                 {
  2014                     $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
  2015                 }
  2016                 else
  2017                 {
  2018                     fputs( $file, "$timestamp - $logentry \n" );
  2019                 }
  2020                 break;
  2021 
  2022             case 2:
  2023                 $retval .= COM_startBlock( $LANG01[55] . ' ' . $timestamp, '',
  2024                                COM_getBlockTemplate( '_msg_block', 'header' ))
  2025                         . nl2br( $logentry )
  2026                         . COM_endBlock( COM_getBlockTemplate( '_msg_block',
  2027                                                               'footer' ));
  2028                 break;
  2029 
  2030             case 3:
  2031                 $retval = nl2br($logentry);
  2032                 break;
  2033 
  2034             default:
  2035                 $logfile = $_CONF['path_log'] . 'error.log';
  2036 
  2037                 if( !$file = fopen( $logfile, 'a' ))
  2038                 {
  2039                     $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
  2040                 }
  2041                 else
  2042                 {
  2043                     fputs( $file, "$timestamp - $logentry \n" );
  2044                     $retval .= COM_startBlock( $LANG01[34] . ' - ' . $timestamp,
  2045                                    '', COM_getBlockTemplate( '_msg_block',
  2046                                    'header' ))
  2047                             . nl2br( $logentry )
  2048                             . COM_endBlock( COM_getBlockTemplate( '_msg_block',
  2049                                                                   'footer' ));
  2050                 }
  2051                 break;
  2052         }
  2053     }
  2054 
  2055     return $retval;
  2056 }
  2057 
  2058 /**
  2059 * Logs message to access.log
  2060 *
  2061 * This will print a message to the Geeklog access log
  2062 *
  2063 * @param        string      $logentry       Message to write to access log
  2064 * @see COM_errorLog
  2065 *
  2066 */
  2067 
  2068 function COM_accessLog( $logentry )
  2069 {
  2070     global $_CONF, $_USER, $LANG01;
  2071 
  2072     $retval = '';
  2073 
  2074     if( !empty( $logentry ))
  2075     {
  2076         $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
  2077                                  $logentry );
  2078 
  2079         $timestamp = @strftime( '%c' );
  2080         $logfile = $_CONF['path_log'] . 'access.log';
  2081 
  2082         if( !$file = fopen( $logfile, 'a' ))
  2083         {
  2084             return $LANG01[33] . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
  2085         }
  2086 
  2087         if( isset( $_USER['uid'] ))
  2088         {
  2089             $byuser = $_USER['uid'] . '@' . $_SERVER['REMOTE_ADDR'];
  2090         }
  2091         else
  2092         {
  2093             $byuser = 'anon@' . $_SERVER['REMOTE_ADDR'];
  2094         }
  2095 
  2096         fputs( $file, "$timestamp ($byuser) - $logentry\n" );
  2097     }
  2098 
  2099     return $retval;
  2100 }
  2101 
  2102 /**
  2103 * Shows all available topics
  2104 *
  2105 * Show the topics in the system the user has access to and prints them in HTML.
  2106 * This function is used to show the topics in the topics block.
  2107 *
  2108 * @param    string    $topic      ID of currently selected topic
  2109 * @return   string                HTML formatted topic list
  2110 *
  2111 */
  2112 
  2113 function COM_showTopics( $topic='' )
  2114 {
  2115     global $_CONF, $_TABLES, $_USER, $LANG01, $_BLOCK_TEMPLATE, $page;
  2116 
  2117     $langsql = COM_getLangSQL( 'tid' );
  2118     if( empty( $langsql ))
  2119     {
  2120         $op = 'WHERE';
  2121     }
  2122     else
  2123     {
  2124         $op = 'AND';
  2125     }
  2126 
  2127     $sql = "SELECT tid,topic,imageurl,meta_description FROM {$_TABLES['topics']}" . $langsql;
  2128     if( !COM_isAnonUser() )
  2129     {
  2130         $tids = DB_getItem( $_TABLES['userindex'], 'tids',
  2131                             "uid = '{$_USER['uid']}'" );
  2132         if( !empty( $tids ))
  2133         {
  2134             $sql .= " $op (tid NOT IN ('" . str_replace( ' ', "','", $tids )
  2135                  . "'))" . COM_getPermSQL( 'AND' );
  2136         }
  2137         else
  2138         {
  2139             $sql .= COM_getPermSQL( $op );
  2140         }
  2141     }
  2142     else
  2143     {
  2144         $sql .= COM_getPermSQL( $op );
  2145     }
  2146     if( $_CONF['sortmethod'] == 'alpha' )
  2147     {
  2148         $sql .= ' ORDER BY topic ASC';
  2149     }
  2150     else
  2151     {
  2152         $sql .= ' ORDER BY sortnum';
  2153     }
  2154     $result = DB_query( $sql );
  2155 
  2156     $retval = '';
  2157     $sections = new Template( $_CONF['path_layout'] );
  2158     if( isset( $_BLOCK_TEMPLATE['topicoption'] ))
  2159     {
  2160         $templates = explode( ',', $_BLOCK_TEMPLATE['topicoption'] );
  2161         $sections->set_file( array( 'option'  => $templates[0],
  2162                                     'current' => $templates[1] ));
  2163     }
  2164     else
  2165     {
  2166         $sections->set_file( array( 'option'   => 'topicoption.thtml',
  2167                                     'inactive' => 'topicoption_off.thtml' ));
  2168     }
  2169 
  2170     $sections->set_var( 'xhtml', XHTML );
  2171     $sections->set_var( 'site_url', $_CONF['site_url'] );
  2172     $sections->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  2173     $sections->set_var( 'layout_url', $_CONF['layout_url'] );
  2174     $sections->set_var( 'block_name', str_replace( '_', '-', 'section_block' ));
  2175 
  2176     if( $_CONF['hide_home_link'] == 0 )
  2177     {
  2178         // Give a link to the homepage here since a lot of people use this for
  2179         // navigating the site
  2180 
  2181         if( COM_onFrontpage() )
  2182         {
  2183             $sections->set_var( 'option_url', '' );
  2184             $sections->set_var( 'option_label', $LANG01[90] );
  2185             $sections->set_var( 'option_count', '' );
  2186             $sections->set_var( 'topic_image', '' );
  2187             $retval .= $sections->parse( 'item', 'inactive' );
  2188         }
  2189         else
  2190         {
  2191             $sections->set_var( 'option_url',
  2192                                 $_CONF['site_url'] . '/index.php' );
  2193             $sections->set_var( 'option_label', $LANG01[90] );
  2194             $sections->set_var( 'option_count', '' );
  2195             $sections->set_var( 'topic_image', '' );
  2196             $retval .= $sections->parse( 'item', 'option' );
  2197         }
  2198     }
  2199 
  2200     if( $_CONF['showstorycount'] )
  2201     {
  2202         $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['stories']} "
  2203              . 'WHERE (draft_flag = 0) AND (date <= NOW()) '
  2204              . COM_getPermSQL( 'AND' )
  2205              . ' GROUP BY tid';
  2206         $rcount = DB_query( $sql );
  2207         while( $C = DB_fetchArray( $rcount ))
  2208         {
  2209             $storycount[$C['tid']] = $C['count'];
  2210         }
  2211     }
  2212 
  2213     if( $_CONF['showsubmissioncount'] )
  2214     {
  2215         $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['storysubmission']} "
  2216              . ' GROUP BY tid';
  2217         $rcount = DB_query( $sql );
  2218         while( $C = DB_fetchArray( $rcount ))
  2219         {
  2220             $submissioncount[$C['tid']] = $C['count'];
  2221         }
  2222     }
  2223 
  2224     while( $A = DB_fetchArray( $result ) )
  2225     {
  2226         $topicname = stripslashes( $A['topic'] );
  2227         $sections->set_var( 'option_url', $_CONF['site_url']
  2228                             . '/index.php?topic=' . $A['tid'] );
  2229         $sections->set_var( 'option_label', $topicname );
  2230 
  2231         $countstring = '';
  2232         if( $_CONF['showstorycount'] || $_CONF['showsubmissioncount'] )
  2233         {
  2234             $countstring .= '(';
  2235 
  2236             if( $_CONF['showstorycount'] )
  2237             {
  2238                 if( empty( $storycount[$A['tid']] ))
  2239                 {
  2240                     $countstring .= 0;
  2241                 }
  2242                 else
  2243                 {
  2244                     $countstring .= COM_numberFormat( $storycount[$A['tid']] );
  2245                 }
  2246             }
  2247 
  2248             if( $_CONF['showsubmissioncount'] )
  2249             {
  2250                 if( $_CONF['showstorycount'] )
  2251                 {
  2252                     $countstring .= '/';
  2253                 }
  2254                 if( empty( $submissioncount[$A['tid']] ))
  2255                 {
  2256                     $countstring .= 0;
  2257                 }
  2258                 else
  2259                 {
  2260                     $countstring .= COM_numberFormat( $submissioncount[$A['tid']] );
  2261                 }
  2262             }
  2263 
  2264             $countstring .= ')';
  2265         }
  2266         $sections->set_var( 'option_count', $countstring );
  2267 
  2268         $topicimage = '';
  2269         if( !empty( $A['imageurl'] ))
  2270         {
  2271             $imageurl = COM_getTopicImageUrl( $A['imageurl'] );
  2272             $topicimage = '<img src="' . $imageurl . '" alt="' . $topicname
  2273                         . '" title="' . $topicname . '"' . XHTML . '>';
  2274         }
  2275         $sections->set_var( 'topic_image', $topicimage );
  2276 
  2277         $desc = trim($A['meta_description']);
  2278         $sections->set_var('topic_description', $desc);
  2279         $desc_escaped = htmlspecialchars($desc);
  2280         $sections->set_var('topic_description_escaped', $desc_escaped);
  2281         if (! empty($desc)) {
  2282             $sections->set_var('topic_title_attribute',
  2283                                'title="' . $desc_escaped . '"');
  2284         } else {
  2285             $sections->set_var('topic_title_attribute', '');
  2286         }
  2287 
  2288         if(( $A['tid'] == $topic ) && ( $page == 1 ))
  2289         {
  2290             $retval .= $sections->parse( 'item', 'inactive' );
  2291         }
  2292         else
  2293         {
  2294             $retval .= $sections->parse( 'item', 'option' );
  2295         }
  2296     }
  2297 
  2298     return $retval;
  2299 }
  2300 
  2301 /**
  2302 * Shows the user their menu options
  2303 *
  2304 * This shows the average Joe User their menu options. This is the user block on the left side
  2305 *
  2306 * @param        string      $help       Help file to show
  2307 * @param        string      $title      Title of Menu
  2308 * @param        string      $position   Side being shown on 'left', 'right'. Though blank works not likely.
  2309 * @see function COM_adminMenu
  2310 *
  2311 */
  2312 function COM_userMenu( $help='', $title='', $position='' )
  2313 {
  2314     global $_TABLES, $_USER, $_CONF, $LANG01, $LANG04, $_BLOCK_TEMPLATE;
  2315 
  2316     $retval = '';
  2317 
  2318     if( !COM_isAnonUser() )
  2319     {
  2320         $usermenu = new Template( $_CONF['path_layout'] );
  2321         if( isset( $_BLOCK_TEMPLATE['useroption'] ))
  2322         {
  2323             $templates = explode( ',', $_BLOCK_TEMPLATE['useroption'] );
  2324             $usermenu->set_file( array( 'option' => $templates[0],
  2325                                         'current' => $templates[1] ));
  2326         }
  2327         else
  2328         {
  2329            $usermenu->set_file( array( 'option' => 'useroption.thtml',
  2330                                        'current' => 'useroption_off.thtml' ));
  2331         }
  2332         $usermenu->set_var( 'xhtml', XHTML );
  2333         $usermenu->set_var( 'site_url', $_CONF['site_url'] );
  2334         $usermenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  2335         $usermenu->set_var( 'layout_url', $_CONF['layout_url'] );
  2336         $usermenu->set_var( 'block_name', str_replace( '_', '-', 'user_block' ));
  2337 
  2338         if( empty( $title ))
  2339         {
  2340             $title = DB_getItem( $_TABLES['blocks'], 'title',
  2341                                  "name='user_block'" );
  2342         }
  2343 
  2344         // what's our current URL?
  2345         $thisUrl = COM_getCurrentURL();
  2346 
  2347         $retval .= COM_startBlock( $title, $help,
  2348                            COM_getBlockTemplate( 'user_block', 'header', $position ));
  2349 
  2350         // This function will show the user options for all installed plugins
  2351         // (if any)
  2352 
  2353         $plugin_options = PLG_getUserOptions();
  2354         $nrows = count( $plugin_options );
  2355 
  2356         for( $i = 0; $i < $nrows; $i++ )
  2357         {
  2358             $plg = current( $plugin_options );
  2359             $usermenu->set_var( 'option_label', $plg->adminlabel );
  2360 
  2361             if( !empty( $plg->numsubmissions ))
  2362             {
  2363                 $usermenu->set_var( 'option_count', '(' . $plg->numsubmissions . ')' );
  2364             }
  2365             else
  2366             {
  2367                 $usermenu->set_var( 'option_count', '' );
  2368             }
  2369 
  2370             $usermenu->set_var( 'option_url', $plg->adminurl );
  2371             if( $thisUrl == $plg->adminurl )
  2372             {
  2373                 $retval .= $usermenu->parse( 'item', 'current' );
  2374             }
  2375             else
  2376             {
  2377                 $retval .= $usermenu->parse( 'item', 'option' );
  2378             }
  2379             next( $plugin_options );
  2380         }
  2381 
  2382         $url = $_CONF['site_url'] . '/usersettings.php';
  2383         $usermenu->set_var( 'option_label', $LANG01[48] );
  2384         $usermenu->set_var( 'option_count', '' );
  2385         $usermenu->set_var( 'option_url', $url );
  2386         if( $thisUrl == $url )
  2387         {
  2388             $retval .= $usermenu->parse( 'item', 'current' );
  2389         }
  2390         else
  2391         {
  2392             $retval .= $usermenu->parse( 'item', 'option' );
  2393         }
  2394 
  2395         $url = $_CONF['site_url'] . '/users.php?mode=logout';
  2396         $usermenu->set_var( 'option_label', $LANG01[19] );
  2397         $usermenu->set_var( 'option_count', '' );
  2398         $usermenu->set_var( 'option_url', $url );
  2399         $retval .= $usermenu->finish($usermenu->parse('item', 'option'));
  2400         $retval .=  COM_endBlock(COM_getBlockTemplate('user_block', 'footer', $position));
  2401     }
  2402     else
  2403     {
  2404         $retval .= COM_startBlock( $LANG01[47], $help,
  2405                            COM_getBlockTemplate( 'user_block', 'header', $position ));
  2406         $login = new Template( $_CONF['path_layout'] );
  2407         $login->set_file( 'form', 'loginform.thtml' );
  2408         $login->set_var( 'xhtml', XHTML );
  2409         $login->set_var( 'site_url', $_CONF['site_url'] );
  2410         $login->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  2411         $login->set_var( 'layout_url', $_CONF['layout_url'] );
  2412         $login->set_var( 'lang_username', $LANG01[21] );
  2413         $login->set_var( 'lang_password', $LANG01[57] );
  2414         $login->set_var( 'lang_forgetpassword', $LANG01[119] );
  2415         $login->set_var( 'lang_login', $LANG01[58] );
  2416         if( $_CONF['disable_new_user_registration'] == 1 )
  2417         {
  2418             $login->set_var( 'lang_signup', '' );
  2419         }
  2420         else
  2421         {
  2422             $login->set_var( 'lang_signup', $LANG01[59] );
  2423         }
  2424 
  2425         // 3rd party remote authentification.
  2426         if ($_CONF['user_login_method']['3rdparty'] && !$_CONF['usersubmission']) {
  2427             $modules = SEC_collectRemoteAuthenticationModules();
  2428             if (count($modules) == 0) {
  2429                 $user_templates->set_var('services', '');
  2430             } else {
  2431                 if (!$_CONF['user_login_method']['standard'] &&
  2432                         (count($modules) == 1)) {
  2433                     $select = '<input type="hidden" name="service" value="'
  2434                             . $modules[0] . '"' . XHTML . '>' . $modules[0];
  2435                 } else {
  2436                     // Build select
  2437                     $select = '<select name="service" id="service">';
  2438                     if ($_CONF['user_login_method']['standard']) {
  2439                         $select .= '<option value="">' . $_CONF['site_name']
  2440                                 . '</option>';
  2441                     }
  2442                     foreach ($modules as $service) {
  2443                         $select .= '<option value="' . $service . '">'
  2444                                 . $service . '</option>';
  2445                     }
  2446                     $select .= '</select>';
  2447                 }
  2448 
  2449                 $login->set_file('services', 'blockservices.thtml');
  2450                 $login->set_var('lang_service', $LANG04[121]);
  2451                 $login->set_var('select_service', $select);
  2452                 $login->parse('output', 'services');
  2453                 $login->set_var('services',
  2454                                 $login->finish($login->get_var('output')));
  2455             }
  2456         } else {
  2457            $login->set_var('services', '');
  2458         }
  2459 
  2460         // OpenID remote authentification.
  2461         if ($_CONF['user_login_method']['openid'] && ($_CONF['usersubmission'] == 0) && !$_CONF['disable_new_user_registration']) {
  2462             $login->set_file('openid_login', 'loginform_openid.thtml');
  2463             $login->set_var('lang_openid_login', $LANG01[128]);
  2464             $login->set_var('input_field_size', 18);
  2465             $login->set_var('app_url', $_CONF['site_url'] . '/users.php');
  2466             $login->parse('output', 'openid_login');
  2467             $login->set_var('openid_login',
  2468                 $login->finish($login->get_var('output')));
  2469         } else {
  2470             $login->set_var('openid_login', '');
  2471         }
  2472 
  2473         $retval .= $login->finish($login->parse('output', 'form'));
  2474         $retval .= COM_endBlock( COM_getBlockTemplate('user_block', 'footer', $position));
  2475     }
  2476 
  2477     return $retval;
  2478 }
  2479 
  2480 /**
  2481 * Prints administration menu
  2482 *
  2483 * This will return the administration menu items that the user has
  2484 * sufficient rights to -- Admin Block on the left side.
  2485 *
  2486 * @param        string      $help       Help file to show
  2487 * @param        string      $title      Menu Title
  2488 * @param        string      $position   Side being shown on 'left', 'right' or blank.
  2489 * @see function COM_userMenu
  2490 *
  2491 */
  2492 function COM_adminMenu( $help = '', $title = '', $position = '' )
  2493 {
  2494     global $_TABLES, $_USER, $_CONF, $LANG01, $LANG_ADMIN, $_BLOCK_TEMPLATE,
  2495            $_DB_dbms, $config;
  2496 
  2497     $retval = '';
  2498 
  2499     if( empty( $_USER['username'] ))
  2500     {
  2501         return $retval;
  2502     }
  2503 
  2504     $plugin_options = PLG_getAdminOptions();
  2505     $num_plugins = count( $plugin_options );
  2506 
  2507     if( SEC_isModerator() OR SEC_hasRights( 'story.edit,block.edit,topic.edit,user.edit,plugin.edit,user.mail,syndication.edit', 'OR' ) OR ( $num_plugins > 0 ))
  2508     {
  2509         // what's our current URL?
  2510         $thisUrl = COM_getCurrentURL();
  2511 
  2512         $adminmenu = new Template( $_CONF['path_layout'] );
  2513         if( isset( $_BLOCK_TEMPLATE['adminoption'] ))
  2514         {
  2515             $templates = explode( ',', $_BLOCK_TEMPLATE['adminoption'] );
  2516             $adminmenu->set_file( array( 'option' => $templates[0],
  2517                                          'current' => $templates[1] ));
  2518         }
  2519         else
  2520         {
  2521             $adminmenu->set_file( array( 'option' => 'adminoption.thtml',
  2522                                          'current' => 'adminoption_off.thtml' ));
  2523         }
  2524         $adminmenu->set_var( 'xhtml', XHTML );
  2525         $adminmenu->set_var( 'site_url', $_CONF['site_url'] );
  2526         $adminmenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  2527         $adminmenu->set_var( 'layout_url', $_CONF['layout_url'] );
  2528         $adminmenu->set_var( 'block_name', str_replace( '_', '-', 'admin_block' ));
  2529 
  2530         if( empty( $title ))
  2531         {
  2532             $title = DB_getItem( $_TABLES['blocks'], 'title',
  2533                                  "name = 'admin_block'" );
  2534         }
  2535 
  2536         $retval .= COM_startBlock( $title, $help,
  2537                            COM_getBlockTemplate( 'admin_block', 'header', $position ));
  2538 
  2539         $topicsql = '';
  2540         if( SEC_isModerator() || SEC_hasRights( 'story.edit' ))
  2541         {
  2542             $tresult = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
  2543                                  . COM_getPermSQL() );
  2544             $trows = DB_numRows( $tresult );
  2545             if( $trows > 0 )
  2546             {
  2547                 $tids = array();
  2548                 for( $i = 0; $i < $trows; $i++ )
  2549                 {
  2550                     $T = DB_fetchArray( $tresult );
  2551                     $tids[] = $T['tid'];
  2552                 }
  2553                 if( count( $tids ) > 0 )
  2554                 {
  2555                     $topicsql = " (tid IN ('" . implode( "','", $tids ) . "'))";
  2556                 }
  2557             }
  2558         }
  2559 
  2560         $modnum = 0;
  2561         if (SEC_hasRights('story.edit,story.moderate', 'OR') ||
  2562                 (($_CONF['commentsubmission'] == 1) &&
  2563                     SEC_hasRights('comment.moderate')) ||
  2564                 (($_CONF['usersubmission'] == 1) &&
  2565                     SEC_hasRights('user.edit,user.delete'))) {
  2566 
  2567             if (SEC_hasRights('story.moderate')) {
  2568                 if (empty($topicsql)) {
  2569                     $modnum += DB_count($_TABLES['storysubmission']);
  2570                 } else {
  2571                     $sresult = DB_query("SELECT COUNT(*) AS count FROM {$_TABLES['storysubmission']} WHERE" . $topicsql);
  2572                     $S = DB_fetchArray($sresult);
  2573                     $modnum += $S['count'];
  2574                 }
  2575             }
  2576 
  2577             if (($_CONF['listdraftstories'] == 1) && SEC_hasRights('story.edit')) {
  2578                 $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (draft_flag = 1)";
  2579                 if (!empty($topicsql)) {
  2580                     $sql .= ' AND' . $topicsql;
  2581                 }
  2582                 $result = DB_query($sql . COM_getPermSQL('AND', 0, 3));
  2583                 $A = DB_fetchArray($result);
  2584                 $modnum += $A['count'];
  2585             }
  2586 
  2587             if (($_CONF['commentsubmission'] == 1) && SEC_hasRights('comment.moderate')) {
  2588                 $modnum += DB_count($_TABLES['commentsubmissions']);
  2589             }
  2590 
  2591             if ($_CONF['usersubmission'] == 1) {
  2592                 if (SEC_hasRights('user.edit') && SEC_hasRights('user.delete')) {
  2593                     $modnum += DB_count($_TABLES['users'], 'status', '2');
  2594                 }
  2595             }
  2596         }
  2597 
  2598         if (SEC_inGroup('Root')) {
  2599             $url = $_CONF['site_admin_url'] . '/configuration.php';
  2600             $adminmenu->set_var('option_url', $url);
  2601             $adminmenu->set_var('option_label', $LANG01[129]);
  2602             $adminmenu->set_var('option_count', count($config->_get_groups()));
  2603             $menu_item = $adminmenu->parse('item',
  2604                                            ($thisUrl == $url) ? 'current' :
  2605                                                                 'option');
  2606             $link_array[$LANG01[129]] = $menu_item;
  2607         }
  2608 
  2609 
  2610         // now handle submissions for plugins
  2611         $modnum += PLG_getSubmissionCount();
  2612 
  2613         if( SEC_hasRights( 'story.edit' ))
  2614         {
  2615             $url = $_CONF['site_admin_url'] . '/story.php';
  2616             $adminmenu->set_var( 'option_url', $url );
  2617             $adminmenu->set_var( 'option_label', $LANG01[11] );
  2618             if( empty( $topicsql ))
  2619             {
  2620                 $numstories = DB_count( $_TABLES['stories'] );
  2621             }
  2622             else
  2623             {
  2624                 $nresult = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE" . $topicsql . COM_getPermSql( 'AND' ));
  2625                 $N = DB_fetchArray( $nresult );
  2626                 $numstories = $N['count'];
  2627             }
  2628             $adminmenu->set_var( 'option_count',
  2629                                  COM_numberFormat( $numstories ));
  2630             $menu_item = $adminmenu->parse( 'item',
  2631                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2632             $link_array[$LANG01[11]] = $menu_item;
  2633         }
  2634 
  2635         if( SEC_hasRights( 'block.edit' ))
  2636         {
  2637             $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['blocks']}" . COM_getPermSql());
  2638             list( $count ) = DB_fetchArray( $result );
  2639 
  2640             $url = $_CONF['site_admin_url'] . '/block.php';
  2641             $adminmenu->set_var( 'option_url', $url );
  2642             $adminmenu->set_var( 'option_label', $LANG01[12] );
  2643             $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
  2644 
  2645             $menu_item = $adminmenu->parse( 'item',
  2646                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2647             $link_array[$LANG01[12]] = $menu_item;
  2648         }
  2649 
  2650         if( SEC_hasRights( 'topic.edit' ))
  2651         {
  2652             $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['topics']}" . COM_getPermSql());
  2653             list( $count ) = DB_fetchArray( $result );
  2654 
  2655             $url = $_CONF['site_admin_url'] . '/topic.php';
  2656             $adminmenu->set_var( 'option_url', $url );
  2657             $adminmenu->set_var( 'option_label', $LANG01[13] );
  2658             $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
  2659 
  2660             $menu_item = $adminmenu->parse( 'item',
  2661                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2662             $link_array[$LANG01[13]] = $menu_item;
  2663         }
  2664 
  2665         if( SEC_hasRights( 'user.edit' ))
  2666         {
  2667             $url = $_CONF['site_admin_url'] . '/user.php';
  2668             $adminmenu->set_var( 'option_url', $url );
  2669             $adminmenu->set_var( 'option_label', $LANG01[17] );
  2670             $adminmenu->set_var( 'option_count',
  2671                     COM_numberFormat( DB_count( $_TABLES['users'] ) -1 ));
  2672 
  2673             $menu_item = $adminmenu->parse( 'item',
  2674                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2675             $link_array[$LANG01[17]] = $menu_item;
  2676         }
  2677 
  2678         if( SEC_hasRights( 'group.edit' ))
  2679         {
  2680             if (SEC_inGroup('Root')) {
  2681                 $grpFilter = '';
  2682             } else {
  2683                 $thisUsersGroups = SEC_getUserGroups ();
  2684                 $grpFilter = 'WHERE (grp_id IN (' . implode (',', $thisUsersGroups) . '))';
  2685             }
  2686             $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['groups']} $grpFilter;" );
  2687             $A = DB_fetchArray( $result );
  2688 
  2689             $url = $_CONF['site_admin_url'] . '/group.php';
  2690             $adminmenu->set_var( 'option_url', $url );
  2691             $adminmenu->set_var( 'option_label', $LANG01[96] );
  2692             $adminmenu->set_var( 'option_count',
  2693                                  COM_numberFormat( $A['count'] ));
  2694 
  2695             $menu_item = $adminmenu->parse( 'item',
  2696                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2697             $link_array[$LANG01[96]] = $menu_item;
  2698         }
  2699 
  2700         if( SEC_hasRights( 'user.mail' ))
  2701         {
  2702             $url = $_CONF['site_admin_url'] . '/mail.php';
  2703             $adminmenu->set_var( 'option_url', $url );
  2704             $adminmenu->set_var( 'option_label', $LANG01[105] );
  2705             $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
  2706 
  2707             $menu_item = $adminmenu->parse( 'item',
  2708                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2709             $link_array[$LANG01[105]] = $menu_item;
  2710         }
  2711 
  2712         if(( $_CONF['backend'] == 1 ) && SEC_hasRights( 'syndication.edit' ))
  2713         {
  2714             $url = $_CONF['site_admin_url'] . '/syndication.php';
  2715             $adminmenu->set_var( 'option_url', $url );
  2716             $adminmenu->set_var( 'option_label', $LANG01[38] );
  2717             $count = COM_numberFormat( DB_count( $_TABLES['syndication'] ));
  2718             $adminmenu->set_var( 'option_count', $count );
  2719 
  2720             $menu_item = $adminmenu->parse( 'item',
  2721                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2722             $link_array[$LANG01[38]] = $menu_item;
  2723         }
  2724 
  2725         if(( $_CONF['trackback_enabled'] || $_CONF['pingback_enabled'] ||
  2726                 $_CONF['ping_enabled'] ) && SEC_hasRights( 'story.ping' ))
  2727         {
  2728             $url = $_CONF['site_admin_url'] . '/trackback.php';
  2729             $adminmenu->set_var( 'option_url', $url );
  2730             $adminmenu->set_var( 'option_label', $LANG01[116] );
  2731             if( $_CONF['ping_enabled'] )
  2732             {
  2733                 $count = COM_numberFormat( DB_count( $_TABLES['pingservice'] ));
  2734                 $adminmenu->set_var( 'option_count', $count );
  2735             }
  2736             else
  2737             {
  2738                 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
  2739             }
  2740 
  2741             $menu_item = $adminmenu->parse( 'item',
  2742                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2743             $link_array[$LANG01[116]] = $menu_item;
  2744         }
  2745 
  2746         if (SEC_hasRights('plugin.edit')) {
  2747             $url = $_CONF['site_admin_url'] . '/plugins.php';
  2748             $adminmenu->set_var('option_url', $url);
  2749             $adminmenu->set_var('option_label', $LANG01[77]);
  2750             $adminmenu->set_var('option_count',
  2751                     COM_numberFormat(DB_count($_TABLES['plugins'],
  2752                                               'pi_enabled', 1)));
  2753 
  2754             $menu_item = $adminmenu->parse('item',
  2755                     ($thisUrl == $url) ? 'current' : 'option');
  2756             $link_array[$LANG01[77]] = $menu_item;
  2757         }
  2758 
  2759         // This will show the admin options for all installed plugins (if any)
  2760 
  2761         for( $i = 0; $i < $num_plugins; $i++ )
  2762         {
  2763             $plg = current( $plugin_options );
  2764 
  2765             $adminmenu->set_var( 'option_url', $plg->adminurl );
  2766             $adminmenu->set_var( 'option_label', $plg->adminlabel );
  2767 
  2768             if( empty( $plg->numsubmissions ))
  2769             {
  2770                 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
  2771             }
  2772             else
  2773             {
  2774                 $adminmenu->set_var( 'option_count',
  2775                                      COM_numberFormat( $plg->numsubmissions ));
  2776             }
  2777 
  2778             $menu_item = $adminmenu->parse( 'item',
  2779                     ( $thisUrl == $plg->adminurl ) ? 'current' : 'option', true );
  2780             $link_array[$plg->adminlabel] = $menu_item;
  2781 
  2782             next( $plugin_options );
  2783         }
  2784 
  2785         if(( $_CONF['allow_mysqldump'] == 1 ) AND ( $_DB_dbms == 'mysql' ) AND
  2786                 SEC_inGroup( 'Root' ))
  2787         {
  2788             $url = $_CONF['site_admin_url'] . '/database.php';
  2789             $adminmenu->set_var( 'option_url', $url );
  2790             $adminmenu->set_var( 'option_label', $LANG01[103] );
  2791             $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
  2792 
  2793             $menu_item = $adminmenu->parse( 'item',
  2794                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2795             $link_array[$LANG01[103]] = $menu_item;
  2796         }
  2797 
  2798         if ($_CONF['link_documentation'] == 1) {
  2799             $doclang = COM_getLanguageName();
  2800             $docs = 'docs/' . $doclang . '/index.html';
  2801             if (file_exists($_CONF['path_html'] . $docs)) {
  2802                 $adminmenu->set_var('option_url', $_CONF['site_url']
  2803                                     . '/' . $docs);
  2804             } else {
  2805                 $adminmenu->set_var('option_url', $_CONF['site_url']
  2806                                     . '/docs/english/index.html');
  2807             }
  2808             $adminmenu->set_var('option_label', $LANG01[113]);
  2809             $adminmenu->set_var('option_count', $LANG_ADMIN['na']);
  2810             $menu_item = $adminmenu->parse('item', 'option');
  2811             $link_array[$LANG01[113]] = $menu_item;
  2812         }
  2813 
  2814         if( $_CONF['link_versionchecker'] == 1 AND SEC_inGroup( 'Root' ))
  2815         {
  2816             $adminmenu->set_var( 'option_url',
  2817                'http://www.geeklog.net/versionchecker.php?version=' . VERSION );
  2818             $adminmenu->set_var( 'option_label', $LANG01[107] );
  2819             $adminmenu->set_var( 'option_count', VERSION );
  2820 
  2821             $menu_item = $adminmenu->parse( 'item', 'option' );
  2822             $link_array[$LANG01[107]] = $menu_item;
  2823         }
  2824 
  2825         if( $_CONF['sort_admin'] )
  2826         {
  2827             uksort( $link_array, 'strcasecmp' );
  2828         }
  2829 
  2830         $url = $_CONF['site_admin_url'] . '/moderation.php';
  2831         $adminmenu->set_var('option_url', $url);
  2832         $adminmenu->set_var('option_label', $LANG01[10]);
  2833         $adminmenu->set_var('option_count', COM_numberFormat($modnum));
  2834         $menu_item = $adminmenu->finish($adminmenu->parse('item',
  2835                         ($thisUrl == $url) ? 'current' : 'option'));
  2836         $link_array = array($menu_item) + $link_array;
  2837 
  2838         foreach( $link_array as $link )
  2839         {
  2840             $retval .= $link;
  2841         }
  2842 
  2843         $retval .= COM_endBlock( COM_getBlockTemplate( 'admin_block', 'footer', $position ));
  2844     }
  2845 
  2846     return $retval;
  2847 }
  2848 
  2849 /**
  2850 * Redirects user to a given URL
  2851 *
  2852 * This function does a redirect using a meta refresh. This is (or at least
  2853 * used to be) more compatible than using a HTTP Location: header.
  2854 *
  2855 * NOTE:     This does not need to be XHTML compliant. It may also be used
  2856 *           in situations where the XHTML constant is not defined yet ...
  2857 *
  2858 * @param    string  $url    URL to send user to
  2859 * @return   string          HTML meta redirect
  2860 *
  2861 */
  2862 function COM_refresh($url)
  2863 {
  2864     return "<html><head><meta http-equiv=\"refresh\" content=\"0; URL=$url\"></head></html>\n";
  2865 }
  2866 
  2867 /**
  2868  * DEPRECIATED -- see CMT_userComments in lib-comment.php
  2869  * @deprecated since Geeklog 1.4.0
  2870  * @see CMT_userComments
  2871  */
  2872 function COM_userComments( $sid, $title, $type='article', $order='', $mode='', $pid = 0, $page = 1, $cid = false, $delete_option = false )
  2873 {
  2874     global $_CONF;
  2875 
  2876     require_once $_CONF['path_system'] . 'lib-comment.php';
  2877 
  2878     return CMT_userComments( $sid, $title, $type, $order, $mode, $pid, $page, $cid, $delete_option );
  2879 }
  2880 
  2881 /**
  2882 * This censors inappropriate content
  2883 *
  2884 * This will replace 'bad words' with something more appropriate
  2885 *
  2886 * @param        string      $Message        String to check
  2887 * @see function COM_checkHTML
  2888 * @return   string  Edited $Message
  2889 *
  2890 */
  2891 
  2892 function COM_checkWords( $Message )
  2893 {
  2894     global $_CONF;
  2895 
  2896     $EditedMessage = $Message;
  2897 
  2898     if( $_CONF['censormode'] != 0 )
  2899     {
  2900         if( is_array( $_CONF['censorlist'] ))
  2901         {
  2902             $Replacement = $_CONF['censorreplace'];
  2903 
  2904             switch( $_CONF['censormode'])
  2905             {
  2906                 case 1: # Exact match
  2907                     $RegExPrefix = '(\s*)';
  2908                     $RegExSuffix = '(\W*)';
  2909                     break;
  2910 
  2911                 case 2: # Word beginning
  2912                     $RegExPrefix = '(\s*)';
  2913                     $RegExSuffix = '(\w*)';
  2914                     break;
  2915 
  2916                 case 3: # Word fragment
  2917                     $RegExPrefix   = '(\w*)';
  2918                     $RegExSuffix   = '(\w*)';
  2919                     break;
  2920             }
  2921 
  2922             foreach ($_CONF['censorlist'] as $c) {
  2923                 if (!empty($c)) {
  2924                     $EditedMessage = MBYTE_eregi_replace($RegExPrefix . $c
  2925                         . $RegExSuffix, "\\1$Replacement\\2", $EditedMessage);
  2926                 }
  2927             }
  2928         }
  2929     }
  2930 
  2931     return $EditedMessage;
  2932 }
  2933 
  2934 /**
  2935 *  Takes some amount of text and replaces all javascript events on*= with in
  2936 *
  2937 *  This script takes some amount of text and matches all javascript events, on*= (onBlur= onMouseClick=)
  2938 *  and replaces them with in*=
  2939 *  Essentially this will cause onBlur to become inBlur, onFocus to be inFocus
  2940 *  These are not valid javascript events and the browser will ignore them.
  2941 * @param    string  $Message    Text to filter
  2942 * @return   string  $Message with javascript filtered
  2943 * @see  COM_checkWords
  2944 * @see  COM_checkHTML
  2945 *
  2946 */
  2947 
  2948 function COM_killJS( $Message )
  2949 {
  2950     return( preg_replace( '/(\s)+[oO][nN](\w*) ?=/', '\1in\2=', $Message ));
  2951 }
  2952 
  2953 /**
  2954 * Handles the part within a [code] ... [/code] section, i.e. escapes all
  2955 * special characters.
  2956 *
  2957 * @param   string  $str  the code section to encode
  2958 * @return  string  $str with the special characters encoded
  2959 * @see     COM_checkHTML
  2960 *
  2961 */
  2962 function COM_handleCode( $str )
  2963 {
  2964     $search  = array( '&',     '\\',    '<',    '>',    '[',     ']'     );
  2965     $replace = array( '&amp;', '&#92;', '&lt;', '&gt;', '&#91;', '&#93;' );
  2966 
  2967     $str = str_replace( $search, $replace, $str );
  2968 
  2969     return( $str );
  2970 }
  2971 
  2972 /**
  2973 * This function checks html tags.
  2974 *
  2975 * Checks to see that the HTML tags are on the approved list and
  2976 * removes them if not.
  2977 *
  2978 * @param    string  $str            HTML to check
  2979 * @param    string  $permissions    comma-separated list of rights which identify the current user as an "Admin"
  2980 * @return   string                  Filtered HTML
  2981 *
  2982 */
  2983 function COM_checkHTML( $str, $permissions = 'story.edit' )
  2984 {
  2985     global $_CONF;
  2986 
  2987     // replace any \ with &#092; (HTML equiv)
  2988     $str = str_replace('\\', '&#092;', COM_stripslashes($str) );
  2989 
  2990     // Get rid of any newline characters
  2991     $str = preg_replace( "/\n/", '', $str );
  2992 
  2993     // Replace any $ with &#36; (HTML equiv)
  2994     $str = str_replace( '$', '&#36;', $str );
  2995     // handle [code] ... [/code]
  2996     do
  2997     {
  2998         $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[code]' );
  2999         if( $start_pos !== false )
  3000         {
  3001             $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/code]' );
  3002             if( $end_pos !== false )
  3003             {
  3004                 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6,
  3005                         $end_pos - ( $start_pos + 6 )));
  3006                 $encoded = '<pre><code>' . $encoded . '</code></pre>';
  3007                 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
  3008                      . MBYTE_substr( $str, $end_pos + 7 );
  3009             }
  3010             else // missing [/code]
  3011             {
  3012                 // Treat the rest of the text as code (so as not to lose any
  3013                 // special characters). However, the calling entity should
  3014                 // better be checking for missing [/code] before calling this
  3015                 // function ...
  3016                 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6 ));
  3017                 $encoded = '<pre><code>' . $encoded . '</code></pre>';
  3018                 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
  3019             }
  3020         }
  3021     }
  3022     while( $start_pos !== false );
  3023 
  3024     // handle [raw] ... [/raw]
  3025     do
  3026     {
  3027         $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[raw]' );
  3028         if( $start_pos !== false )
  3029         {
  3030             $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/raw]' );
  3031             if( $end_pos !== false )
  3032             {
  3033                 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5,
  3034                         $end_pos - ( $start_pos + 5 )));
  3035                 // [raw2] to avoid infinite loop. Not HTML comment as we strip
  3036                 // them later.
  3037                 $encoded = '[raw2]' . $encoded . '[/raw2]';
  3038                 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
  3039                      . MBYTE_substr( $str, $end_pos + 6 );
  3040             }
  3041             else // missing [/raw]
  3042             {
  3043                 // Treat the rest of the text as raw (so as not to lose any
  3044                 // special characters). However, the calling entity should
  3045                 // better be checking for missing [/raw] before calling this
  3046                 // function ...
  3047                 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5 ));
  3048                 // [raw2] to avoid infinite loop. Not HTML comment as we strip
  3049                 // them later.
  3050                 $encoded = '[raw2]' . $encoded . '[/raw2]';
  3051                 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
  3052             }
  3053         }
  3054     }
  3055     while( $start_pos !== false );
  3056 
  3057     if( isset( $_CONF['skip_html_filter_for_root'] ) &&
  3058              ( $_CONF['skip_html_filter_for_root'] == 1 ) &&
  3059             SEC_inGroup( 'Root' ))
  3060     {
  3061         return $str;
  3062     }
  3063 
  3064     // strip_tags() gets confused by HTML comments ...
  3065     $str = preg_replace( '/<!--.+?-->/', '', $str );
  3066 
  3067     $filter = new kses4;
  3068     if( isset( $_CONF['allowed_protocols'] ) && is_array( $_CONF['allowed_protocols'] ) && ( count( $_CONF['allowed_protocols'] ) > 0 ))
  3069     {
  3070         $filter->SetProtocols( $_CONF['allowed_protocols'] );
  3071     }
  3072     else
  3073     {
  3074         $filter->SetProtocols( array( 'http:', 'https:', 'ftp:' ));
  3075     }
  3076 
  3077     if( empty( $permissions) || !SEC_hasRights( $permissions ) ||
  3078             empty( $_CONF['admin_html'] ))
  3079     {
  3080         $html = $_CONF['user_html'];
  3081     }
  3082     else
  3083     {
  3084         if ($_CONF['advanced_editor'] && is_array($_CONF['advanced_html'])) {
  3085             $html = array_merge_recursive( $_CONF['user_html'],
  3086                                            $_CONF['admin_html'],
  3087                                            $_CONF['advanced_html'] );
  3088         } else {
  3089             $html = array_merge_recursive( $_CONF['user_html'],
  3090                                            $_CONF['admin_html'] );
  3091         }
  3092     }
  3093 
  3094     foreach( $html as $tag => $attr )
  3095     {
  3096         $filter->AddHTML( $tag, $attr );
  3097     }
  3098     /* Replace [raw][/raw] with <!--raw--><!--/raw-->, note done "late" because
  3099      * of the above noted // strip_tags() gets confused by HTML comments ...
  3100      */
  3101     $str = $filter->Parse( $str );
  3102     $str = str_replace('[raw2]','<!--raw--><span class="raw">', $str);
  3103     $str = str_replace('[/raw2]','</span><!--/raw-->', $str);
  3104 
  3105     return $str;
  3106 }
  3107 
  3108 /**
  3109 * undo function for htmlspecialchars()
  3110 *
  3111 * This function translates HTML entities created by htmlspecialchars() back
  3112 * into their ASCII equivalents. Also handles the entities for $, {, and }.
  3113 *
  3114 * @param    string   $string   The string to convert.
  3115 * @return   string   The converted string.
  3116 *
  3117 */
  3118 function COM_undoSpecialChars( $string )
  3119 {
  3120     $string = str_replace( '&#36;',  '$', $string );
  3121     $string = str_replace( '&#123;', '{', $string );
  3122     $string = str_replace( '&#125;', '}', $string );
  3123     $string = str_replace( '&gt;',   '>', $string );
  3124     $string = str_replace( '&lt;',   '<', $string );
  3125     $string = str_replace( '&quot;', '"', $string );
  3126     $string = str_replace( '&nbsp;', ' ', $string );
  3127     $string = str_replace( '&amp;',  '&', $string );
  3128 
  3129     return( $string );
  3130 }
  3131 
  3132 /**
  3133 * Makes an ID based on current date/time
  3134 *
  3135 * This function creates a 17 digit sid for stories based on the 14 digit date
  3136 * and a 3 digit random number that was seeded with the number of microseconds
  3137 * (.000001th of a second) since the last full second.
  3138 * NOTE: this is now used for more than just stories!
  3139 *
  3140 * @return   string  $sid  Story ID
  3141 *
  3142 */
  3143 function COM_makesid()
  3144 {
  3145     $sid = date( 'YmdHis' );
  3146     $sid .= rand( 0, 999 );
  3147 
  3148     return $sid;
  3149 }
  3150 
  3151 /**
  3152 * Checks to see if email address is valid.
  3153 *
  3154 * This function checks to see if an email address is in the correct from.
  3155 *
  3156 * @param    string    $email   Email address to verify
  3157 * @return   boolean            True if valid otherwise false
  3158 *
  3159 */
  3160 function COM_isEmail( $email )
  3161 {
  3162     require_once( 'Mail/RFC822.php' );
  3163 
  3164     $rfc822 = new Mail_RFC822;
  3165 
  3166     return( $rfc822->isValidInetAddress( $email ) ? true : false );
  3167 }
  3168 
  3169 
  3170 /**
  3171 * Encode a string such that it can be used in an email header
  3172 *
  3173 * @param    string  $string     the text to be encoded
  3174 * @return   string              encoded text
  3175 *
  3176 */
  3177 function COM_emailEscape( $string )
  3178 {
  3179     global $_CONF;
  3180 
  3181     if (function_exists('CUSTOM_emailEscape')) {
  3182         return CUSTOM_emailEscape($string);
  3183     }
  3184 
  3185     $charset = COM_getCharset();
  3186     if(( $charset == 'utf-8' ) && ( $string != utf8_decode( $string )))
  3187     {
  3188         if( function_exists( 'iconv_mime_encode' ))
  3189         {
  3190             $mime_parameters = array( 'input-charset'  => 'utf-8',
  3191                                       'output-charset' => 'utf-8',
  3192                                       // 'Q' encoding is more readable than 'B'
  3193                                       'scheme'         => 'Q'
  3194                                     );
  3195             $string = substr( iconv_mime_encode( '', $string,
  3196                                                  $mime_parameters ), 2 );
  3197         }
  3198         else
  3199         {
  3200             $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
  3201         }
  3202     }
  3203     else if( preg_match( '/[^0-9a-z\-\.,:;\?! ]/i', $string ))
  3204     {
  3205         $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
  3206     }
  3207 
  3208     return $string;
  3209 }
  3210 
  3211 /**
  3212 * Takes a name and an email address and returns a string that vaguely
  3213 * resembles an email address specification conforming to RFC(2)822 ...
  3214 *
  3215 * @param    string  $name       name, e.g. John Doe
  3216 * @param    string  $address    email address only, e.g. john.doe@example.com
  3217 * @return   string              formatted email address
  3218 *
  3219 */
  3220 function COM_formatEmailAddress($name, $address)
  3221 {
  3222     $name = trim($name);
  3223     $address = trim($address);
  3224 
  3225     if (function_exists('CUSTOM_formatEmailAddress')) {
  3226         return CUSTOM_formatEmailAddress($name, $address);
  3227     }
  3228 
  3229     $formatted_name = COM_emailEscape($name);
  3230 
  3231     // if the name comes back unchanged, it's not UTF-8, so preg_match is fine
  3232     if (($formatted_name == $name) && preg_match('/[^0-9a-z ]/i', $name)) {
  3233         $formatted_name = str_replace('"', '\\"', $formatted_name);
  3234         $formatted_name = '"' . $formatted_name . '"';
  3235     }
  3236 
  3237     return $formatted_name . ' <' . $address . '>';
  3238 }
  3239 
  3240 /**
  3241 * Send an email.
  3242 *
  3243 * All emails sent by Geeklog are sent through this function.
  3244 *
  3245 * NOTE: Please note that using CC: will expose the email addresses of
  3246 *       all recipients. Use with care.
  3247 *
  3248 * @param    string      $to         recipients name and email address
  3249 * @param    string      $subject    subject of the email
  3250 * @param    string      $message    the text of the email
  3251 * @param    string      $from       (optional) sender of the the email
  3252 * @param    boolean     $html       (optional) true if to be sent as HTML email
  3253 * @param    int         $priority   (optional) add X-Priority header, if > 0
  3254 * @param    mixed       $optional   (optional) other headers or CC:
  3255 * @return   boolean                 true if successful,  otherwise false
  3256 *
  3257 */
  3258 function COM_mail($to, $subject, $message, $from = '', $html = false, $priority = 0, $optional = null)
  3259 {
  3260     global $_CONF;
  3261 
  3262     static $mailobj;
  3263 
  3264     if (empty($from)) {
  3265         $from = COM_formatEmailAddress($_CONF['site_name'], $_CONF['site_mail']);
  3266     }
  3267 
  3268     $to = substr($to, 0, strcspn($to, "\r\n"));
  3269     if (($optional != null) && !is_array($optional)) {
  3270         $optional = substr($optional, 0, strcspn($optional, "\r\n"));
  3271     }
  3272     $from = substr($from, 0, strcspn($from, "\r\n"));
  3273     $subject = substr($subject, 0, strcspn($subject, "\r\n"));
  3274     $subject = COM_emailEscape($subject);
  3275 
  3276     if (function_exists('CUSTOM_mail')) {
  3277         return CUSTOM_mail($to, $subject, $message, $from, $html, $priority,
  3278                            $optional);
  3279     }
  3280 
  3281     include_once 'Mail.php';
  3282     include_once 'Mail/RFC822.php';
  3283 
  3284     $method = $_CONF['mail_settings']['backend'];
  3285 
  3286     if (! isset($mailobj)) {
  3287         if (($method == 'sendmail') || ($method == 'smtp')) {
  3288             $mailobj =& Mail::factory($method, $_CONF['mail_settings']);
  3289         } else {
  3290             $method = 'mail';
  3291             $mailobj =& Mail::factory($method);
  3292         }
  3293     }
  3294 
  3295     $charset = COM_getCharset();
  3296     $headers = array();
  3297 
  3298     $headers['From'] = $from;
  3299     if ($method != 'mail') {
  3300         $headers['To'] = $to;
  3301     }
  3302     if (($optional != null) && !is_array($optional) && !empty($optional)) {
  3303         // assume old (optional) CC: header
  3304         $headers['Cc'] = $optional;
  3305     }
  3306     $headers['Date'] = date('r'); // RFC822 formatted date
  3307     if($method == 'smtp') {
  3308         list($usec, $sec) = explode(' ', microtime());
  3309         $m = substr($usec, 2, 5);
  3310         $headers['Message-Id'] = '<' .  date('YmdHis') . '.' . $m
  3311                                . '@' . $_CONF['mail_settings']['host'] . '>';
  3312     }
  3313     if ($html) {
  3314         $headers['Content-Type'] = 'text/html; charset=' . $charset;
  3315         $headers['Content-Transfer-Encoding'] = '8bit';
  3316     } else {
  3317         $headers['Content-Type'] = 'text/plain; charset=' . $charset;
  3318     }
  3319     $headers['Subject'] = $subject;
  3320     if ($priority > 0) {
  3321         $headers['X-Priority'] = $priority;
  3322     }
  3323     $headers['X-Mailer'] = 'Geeklog ' . VERSION;
  3324 
  3325     if (!empty($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['SERVER_ADDR']) &&
  3326             ($_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR'])) {
  3327         $url = COM_getCurrentURL();
  3328         if (substr($url, 0, strlen($_CONF['site_admin_url']))
  3329                 != $_CONF['site_admin_url']) {
  3330             $headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
  3331         }
  3332     }
  3333 
  3334     // add optional headers last
  3335     if (($optional != null) && is_array($optional)) {
  3336         foreach ($optional as $h => $v) {
  3337             $headers[$h] = $v;
  3338         }
  3339     }
  3340 
  3341     $retval = $mailobj->send($to, $headers, $message);
  3342     if ($retval !== true) {
  3343         COM_errorLog($retval->toString(), 1);
  3344     }
  3345 
  3346     return($retval === true ? true : false);
  3347 }
  3348 
  3349 
  3350 /**
  3351 * Creates older stuff block
  3352 *
  3353 * Creates the olderstuff block for display.
  3354 * Actually updates the olderstuff record in the gl_blocks database.
  3355 * @return   void
  3356 */
  3357 
  3358 function COM_olderStuff()
  3359 {
  3360     global $_TABLES, $_CONF;
  3361 
  3362     $sql = "SELECT sid,tid,title,comments,UNIX_TIMESTAMP(date) AS day FROM {$_TABLES['stories']} WHERE (perm_anon = 2) AND (frontpage = 1) AND (date <= NOW()) AND (draft_flag = 0)" . COM_getTopicSQL( 'AND', 1 ) . " ORDER BY featured DESC, date DESC LIMIT {$_CONF['limitnews']}, {$_CONF['limitnews']}";
  3363     $result = DB_query( $sql );
  3364     $nrows = DB_numRows( $result );
  3365 
  3366     if( $nrows > 0 )
  3367     {
  3368         $dateonly = $_CONF['dateonly'];
  3369         if( empty( $dateonly ))
  3370         {
  3371             $dateonly = '%d-%b'; // fallback: day - abbrev. month name
  3372         }
  3373 
  3374         $day = 'noday';
  3375         $string = '';
  3376 
  3377         for( $i = 0; $i < $nrows; $i++ )
  3378         {
  3379             $A = DB_fetchArray( $result );
  3380 
  3381             $daycheck = strftime( '%A', $A['day'] );
  3382             if( $day != $daycheck )
  3383             {
  3384                 if( $day != 'noday' )
  3385                 {
  3386                     $daylist = COM_makeList($oldnews, 'list-older-stories');
  3387                     $daylist = str_replace(array("\015", "\012"), '', $daylist);
  3388                     $string .= $daylist . '<br' . XHTML . '>';
  3389                 }
  3390 
  3391                 $day2 = strftime( $dateonly, $A['day'] );
  3392                 $string .= '<h3>' . $daycheck . ' <small>' . $day2
  3393                         . '</small></h3>' . LB;
  3394                 $oldnews = array();
  3395                 $day = $daycheck;
  3396             }
  3397 
  3398             $oldnews_url = COM_buildUrl( $_CONF['site_url'] . '/article.php?story='
  3399                 . $A['sid'] );
  3400             $oldnews[] = COM_createLink($A['title'], $oldnews_url)
  3401                 .' (' . COM_numberFormat( $A['comments'] ) . ')';
  3402         }
  3403 
  3404         if( !empty( $oldnews ))
  3405         {
  3406             $daylist = COM_makeList($oldnews, 'list-older-stories');
  3407             $daylist = str_replace(array("\015", "\012"), '', $daylist);
  3408             $string .= $daylist;
  3409             $string = addslashes( $string );
  3410 
  3411             DB_query( "UPDATE {$_TABLES['blocks']} SET content = '$string' WHERE name = 'older_stories'" );
  3412         }
  3413     }
  3414 }
  3415 
  3416 /**
  3417 * Shows a single Geeklog block
  3418 *
  3419 * This shows a single block and is typically called from
  3420 * COM_showBlocks OR from plugin code
  3421 *
  3422 * @param        string      $name       Logical name of block (not same as title) -- 'user_block', 'admin_block', 'section_block', 'whats_new_block'.
  3423 * @param        string      $help       Help file location
  3424 * @param        string      $title      Title shown in block header
  3425 * @param        string      $position   Side, 'left', 'right' or empty.
  3426 * @see function COM_showBlocks
  3427 * @return   string  HTML Formated block
  3428 *
  3429 */
  3430 
  3431 function COM_showBlock( $name, $help='', $title='', $position='' )
  3432 {
  3433     global $_CONF, $topic, $_TABLES, $_USER;
  3434 
  3435     $retval = '';
  3436 
  3437     if( !isset( $_USER['noboxes'] ))
  3438     {
  3439         if( !COM_isAnonUser() )
  3440         {
  3441             $_USER['noboxes'] = DB_getItem( $_TABLES['userindex'], 'noboxes',
  3442                                             "uid = {$_USER['uid']}" );
  3443         }
  3444         else
  3445         {
  3446             $_USER['noboxes'] = 0;
  3447         }
  3448     }
  3449 
  3450     switch( $name )
  3451     {
  3452         case 'user_block':
  3453             $retval .= COM_userMenu( $help,$title, $position );
  3454             break;
  3455 
  3456         case 'admin_block':
  3457             $retval .= COM_adminMenu( $help,$title, $position );
  3458             break;
  3459 
  3460         case 'section_block':
  3461             $retval .= COM_startBlock( $title, $help,
  3462                                COM_getBlockTemplate( $name, 'header', $position ))
  3463                 . COM_showTopics( $topic )
  3464                 . COM_endBlock( COM_getBlockTemplate( $name, 'footer', $position ));
  3465             break;
  3466 
  3467         case 'whats_new_block':
  3468             if( !$_USER['noboxes'] )
  3469             {
  3470                 $retval .= COM_whatsNewBlock( $help, $title, $position );
  3471             }
  3472             break;
  3473     }
  3474 
  3475     return $retval;
  3476 }
  3477 
  3478 
  3479 /**
  3480 * Shows Geeklog blocks
  3481 *
  3482 * Returns HTML for blocks on a given side and, potentially, for
  3483 * a given topic. Currently only used by static pages.
  3484 *
  3485 * @param        string      $side       Side to get blocks for (right or left for now)
  3486 * @param        string      $topic      Only get blocks for this topic
  3487 * @param        string      $name       Block name (not used)
  3488 * @see function COM_showBlock
  3489 * @return   string  HTML Formated blocks
  3490 *
  3491 */
  3492 
  3493 function COM_showBlocks( $side, $topic='', $name='all' )
  3494 {
  3495     global $_CONF, $_TABLES, $_USER, $LANG21, $topic, $page;
  3496 
  3497     $retval = '';
  3498 
  3499     // Get user preferences on blocks
  3500     if( !isset( $_USER['noboxes'] ) || !isset( $_USER['boxes'] ))
  3501     {
  3502         if( !COM_isAnonUser() )
  3503         {
  3504             $result = DB_query( "SELECT boxes,noboxes FROM {$_TABLES['userindex']} "
  3505                                ."WHERE uid = '{$_USER['uid']}'" );
  3506             list($_USER['boxes'], $_USER['noboxes']) = DB_fetchArray( $result );
  3507         }
  3508         else
  3509         {
  3510             $_USER['boxes'] = '';
  3511             $_USER['noboxes'] = 0;
  3512         }
  3513     }
  3514 
  3515     $blocksql['mssql']  = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
  3516     $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
  3517     $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
  3518 
  3519     $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
  3520 
  3521     $commonsql = "FROM {$_TABLES['blocks']} WHERE is_enabled = 1";
  3522 
  3523     if( $side == 'left' )
  3524     {
  3525         $commonsql .= " AND onleft = 1";
  3526     }
  3527     else
  3528     {
  3529         $commonsql .= " AND onleft = 0";
  3530     }
  3531 
  3532     if( !empty( $topic ))
  3533     {
  3534         $tp = addslashes($topic);
  3535         $commonsql .= " AND (tid = '$tp' OR tid = 'all')";
  3536     }
  3537     else
  3538     {
  3539         if( COM_onFrontpage() )
  3540         {
  3541             $commonsql .= " AND (tid = 'homeonly' OR tid = 'all')";
  3542         }
  3543         else
  3544         {
  3545             $commonsql .= " AND (tid = 'all')";
  3546         }
  3547     }
  3548 
  3549     if( !empty( $_USER['boxes'] ))
  3550     {
  3551         $BOXES = str_replace( ' ', ',', $_USER['boxes'] );
  3552 
  3553         $commonsql .= " AND (bid NOT IN ($BOXES) OR bid = '-1')";
  3554     }
  3555 
  3556     $commonsql .= ' ORDER BY blockorder,title ASC';
  3557 
  3558     $blocksql['mysql'] .= $commonsql;
  3559     $blocksql['mssql'] .= $commonsql;
  3560     $result = DB_query( $blocksql );
  3561     $nrows = DB_numRows( $result );
  3562 
  3563     // convert result set to an array of associated arrays
  3564     $blocks = array();
  3565     for( $i = 0; $i < $nrows; $i++ )
  3566     {
  3567         $blocks[] = DB_fetchArray( $result );
  3568     }
  3569 
  3570     // Check and see if any plugins have blocks to show
  3571     $pluginBlocks = PLG_getBlocks( $side, $topic, $name );
  3572     $blocks = array_merge( $blocks, $pluginBlocks );
  3573 
  3574     // sort the resulting array by block order
  3575     $column = 'blockorder';
  3576     $sortedBlocks = $blocks;
  3577     $num_sortedBlocks = count( $sortedBlocks );
  3578     for( $i = 0; $i < $num_sortedBlocks - 1; $i++ )
  3579     {
  3580         for( $j = 0; $j < $num_sortedBlocks - 1 - $i; $j++ )
  3581         {
  3582             if( $sortedBlocks[$j][$column] > $sortedBlocks[$j+1][$column] )
  3583             {
  3584                 $tmp = $sortedBlocks[$j];
  3585                 $sortedBlocks[$j] = $sortedBlocks[$j + 1];
  3586                 $sortedBlocks[$j + 1] = $tmp;
  3587             }
  3588         }
  3589     }
  3590     $blocks = $sortedBlocks;
  3591 
  3592     // Loop though resulting sorted array and pass associative arrays
  3593     // to COM_formatBlock
  3594     foreach( $blocks as $A )
  3595     {
  3596         if( $A['type'] == 'dynamic' or SEC_hasAccess( $A['owner_id'], $A['group_id'], $A['perm_owner'], $A['perm_group'], $A['perm_members'], $A['perm_anon'] ) > 0 )
  3597         {
  3598            $retval .= COM_formatBlock( $A, $_USER['noboxes'] );
  3599         }
  3600     }
  3601 
  3602     return $retval;
  3603 }
  3604 
  3605 /**
  3606 * Formats a Geeklog block
  3607 *
  3608 * This shows a single block and is typically called from
  3609 * COM_showBlocks OR from plugin code
  3610 *
  3611 * @param        array     $A          Block Record
  3612 * @param        boolean   $noboxes    Set to true if userpref is no blocks
  3613 * @return       string    HTML Formated block
  3614 *
  3615 */
  3616 function COM_formatBlock( $A, $noboxes = false )
  3617 {
  3618     global $_CONF, $_TABLES, $_USER, $LANG21;
  3619 
  3620     $retval = '';
  3621 
  3622     $lang = COM_getLanguageId();
  3623     if (!empty($lang)) {
  3624 
  3625         $blocksql['mssql']  = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
  3626         $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
  3627         $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
  3628 
  3629         $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
  3630 
  3631         $commonsql = "FROM {$_TABLES['blocks']} WHERE name = '"
  3632                    . $A['name'] . '_' . $lang . "'";
  3633 
  3634         $blocksql['mysql'] .= $commonsql;
  3635         $blocksql['mssql'] .= $commonsql;
  3636         $result = DB_query( $blocksql );
  3637 
  3638         if (DB_numRows($result) == 1) {
  3639             // overwrite with data for language-specific block
  3640             $A = DB_fetchArray($result);
  3641         }
  3642     }
  3643     
  3644     if( array_key_exists( 'onleft', $A ) )
  3645     {
  3646         if( $A['onleft'] == 1 )
  3647         {
  3648             $position = 'left';
  3649         } else {
  3650             $position = 'right';
  3651         }
  3652     } else {
  3653         $position = '';
  3654     }
  3655 
  3656     if( $A['type'] == 'portal' )
  3657     {
  3658         if( COM_rdfCheck( $A['bid'], $A['rdfurl'], $A['date'], $A['rdflimit'] ))
  3659         {
  3660             $A['content'] = DB_getItem( $_TABLES['blocks'], 'content',
  3661                                         "bid = '{$A['bid']}'");
  3662         }
  3663     }
  3664 
  3665     if( $A['type'] == 'gldefault' )
  3666     {
  3667         $retval .= COM_showBlock( $A['name'], $A['help'], $A['title'], $position );
  3668     }
  3669 
  3670     if( $A['type'] == 'phpblock' && !$noboxes )
  3671     {
  3672         if( !( $A['name'] == 'whosonline_block' AND DB_getItem( $_TABLES['blocks'], 'is_enabled', "name='whosonline_block'" ) == 0 ))
  3673         {
  3674             $function = $A['phpblockfn'];
  3675             $matches = array();
  3676             if (preg_match('/^(phpblock_\w*)\\((.*)\\)$/', $function, $matches) == 1)
  3677             {
  3678                 $function = $matches[1];
  3679                 $args = $matches[2];
  3680             }
  3681             $blkheader = COM_startBlock( $A['title'], $A['help'],
  3682                     COM_getBlockTemplate( $A['name'], 'header', $position ));
  3683             $blkfooter = COM_endBlock( COM_getBlockTemplate( $A['name'],
  3684                     'footer', $position ));
  3685 
  3686             if( function_exists( $function ))
  3687             {
  3688                if (isset($args))
  3689                {
  3690                     $fretval = $function($A, $args);
  3691                } else {
  3692                     $fretval = $function();
  3693                }
  3694                if( !empty( $fretval ))
  3695                {
  3696                     $retval .= $blkheader;
  3697                     $retval .= $fretval;
  3698                     $retval .= $blkfooter;
  3699                }
  3700             }
  3701             else
  3702             {
  3703                 // show error message
  3704                 $retval .= $blkheader;
  3705                 $retval .= sprintf( $LANG21[31], $function );
  3706                 $retval .= $blkfooter;
  3707             }
  3708         }
  3709     }
  3710 
  3711     if( !empty( $A['content'] ) && ( trim( $A['content'] ) != '' ) && !$noboxes )
  3712     {
  3713         $blockcontent = stripslashes( $A['content'] );
  3714 
  3715         // Hack: If the block content starts with a '<' assume it
  3716         // contains HTML and do not call nl2br() which would only add
  3717         // unwanted <br> tags.
  3718 
  3719         if( substr( $blockcontent, 0, 1 ) != '<' )
  3720         {
  3721             $blockcontent = nl2br( $blockcontent );
  3722         }
  3723 
  3724         // autotags are only(!) allowed in normal blocks
  3725         if(( $A['allow_autotags'] == 1 ) && ( $A['type'] == 'normal' ))
  3726         {
  3727             $blockcontent = PLG_replaceTags( $blockcontent );
  3728         }
  3729         $blockcontent = str_replace( array( '<?', '?>' ), '', $blockcontent );
  3730 
  3731         $retval .= COM_startBlock( $A['title'], $A['help'],
  3732                        COM_getBlockTemplate( $A['name'], 'header', $position ))
  3733                 . $blockcontent . LB
  3734                 . COM_endBlock( COM_getBlockTemplate( $A['name'], 'footer', $position ));
  3735     }
  3736 
  3737     return $retval;
  3738 }
  3739 
  3740 
  3741 /**
  3742 * Checks to see if it's time to import and RDF/RSS block again
  3743 *
  3744 * Updates RDF/RSS block if needed
  3745 *
  3746 * @param    string  $bid            Block ID
  3747 * @param    string  $rdfurl         URL to get headlines from
  3748 * @param    string  $date           Last time the headlines were imported
  3749 * @param    string  $maxheadlines   max. number of headlines to import
  3750 * @return   void
  3751 * @see function COM_rdfImport
  3752 *
  3753 */
  3754 function COM_rdfCheck( $bid, $rdfurl, $date, $maxheadlines = 0 )
  3755 {
  3756     $retval = false;
  3757     $nextupdate = $date + 3600;
  3758 
  3759     if( $nextupdate < time() )
  3760     {
  3761         COM_rdfImport( $bid, $rdfurl, $maxheadlines );
  3762         $retval = true;
  3763     }
  3764 
  3765     return $retval;
  3766 }
  3767 
  3768 /**
  3769 * Syndication import function. Imports headline data to a portal block.
  3770 *
  3771 * Rewritten December 19th 2004 by Michael Jervis (mike AT fuckingbrit DOT com).
  3772 * Now utilises a Factory Pattern to open a URL and automaticaly retreive a feed
  3773 * object populated with feed data. Then import it into the portal block.
  3774 *
  3775 * @param    string  $bid            Block ID
  3776 * @param    string  $rdfurl         URL to get content from
  3777 * @param    int     $maxheadlines   Maximum number of headlines to display
  3778 * @return   void
  3779 * @see function COM_rdfCheck
  3780 *
  3781 */
  3782 function COM_rdfImport($bid, $rdfurl, $maxheadlines = 0)
  3783 {
  3784     global $_CONF, $_TABLES, $LANG21;
  3785 
  3786     // Import the feed handling classes:
  3787     require_once $_CONF['path_system']
  3788                  . '/classes/syndication/parserfactory.class.php';
  3789     require_once $_CONF['path_system']
  3790                  . '/classes/syndication/feedparserbase.class.php';
  3791 
  3792     $result = DB_query("SELECT rdf_last_modified, rdf_etag FROM {$_TABLES['blocks']} WHERE bid = $bid");
  3793     list($last_modified, $etag) = DB_fetchArray($result);
  3794 
  3795     // Load the actual feed handlers:
  3796     $factory = new FeedParserFactory($_CONF['path_system']
  3797                                      . '/classes/syndication/');
  3798     $factory->userAgent = 'Geeklog/' . VERSION;
  3799     if (!empty($last_modified) && !empty($etag)) {
  3800         $factory->lastModified = $last_modified;
  3801         $factory->eTag = $etag;
  3802     }
  3803 
  3804     // Aquire a reader:
  3805     $feed = $factory->reader($rdfurl, $_CONF['default_charset']);
  3806 
  3807     if ($feed) {
  3808         /* We have located a reader, and populated it with the information from
  3809          * the syndication file. Now we will sort out our display, and update
  3810          * the block.
  3811          */
  3812         if ($maxheadlines == 0) {
  3813             if (!empty($_CONF['syndication_max_headlines'])) {
  3814                 $maxheadlines = $_CONF['syndication_max_headlines'];
  3815             } else {
  3816                 $maxheadlines = count($feed->articles);
  3817             }
  3818         }
  3819 
  3820         $update = date('Y-m-d H:i:s');
  3821         $last_modified = '';
  3822         if (!empty($factory->lastModified)) {
  3823             $last_modified = addslashes($factory->lastModified);
  3824         }
  3825         $etag = '';
  3826         if (!empty($factory->eTag)) {
  3827             $etag = addslashes($factory->eTag);
  3828         }
  3829 
  3830         if (empty($last_modified) || empty($etag)) {
  3831             DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = '$bid'");
  3832         } else {
  3833             DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = '$last_modified', rdf_etag = '$etag' WHERE bid = '$bid'");
  3834         }
  3835 
  3836         $charset = COM_getCharset();
  3837 
  3838         // format articles for display
  3839         $readmax = min($maxheadlines, count($feed->articles));
  3840         for ($i = 0; $i < $readmax; $i++) {
  3841             if (empty($feed->articles[$i]['title'])) {
  3842                 $feed->articles[$i]['title'] = $LANG21[61];
  3843             }
  3844 
  3845             if ($charset == 'utf-8') {
  3846                 $title = $feed->articles[$i]['title'];
  3847             } else {
  3848                 $title = utf8_decode($feed->articles[$i]['title']);
  3849             }
  3850             if ($feed->articles[$i]['link'] != '') {
  3851                 $content = COM_createLink($title, $feed->articles[$i]['link']);
  3852             } elseif ($feed->articles[$i]['enclosureurl'] != '') {
  3853                 $content = COM_createLink($title, $feed->articles[$i]['enclosureurl']);
  3854             } else {
  3855                 $content = $title;
  3856             }
  3857             $articles[] = $content;
  3858         }
  3859 
  3860         // build a list
  3861         $content = COM_makeList($articles, 'list-feed');
  3862         $content = str_replace(array("\015", "\012"), '', $content);
  3863 
  3864         if (strlen($content) > 65000) {
  3865             $content = $LANG21[68];
  3866         }
  3867 
  3868         // Standard theme based function to put it in the block
  3869         $result = DB_change($_TABLES['blocks'], 'content',
  3870                             addslashes($content), 'bid', $bid);
  3871     } else if ($factory->errorStatus !== false) {
  3872         // failed to aquire info, 0 out the block and log an error
  3873         COM_errorLog("Unable to aquire feed reader for $rdfurl", 1);
  3874         COM_errorLog($factory->errorStatus[0] . ' ' .
  3875                      $factory->errorStatus[1] . ' ' .
  3876                      $factory->errorStatus[2]);
  3877         $content = addslashes($LANG21[4]);
  3878         DB_query("UPDATE {$_TABLES['blocks']} SET content = '$content', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = $bid");
  3879     }
  3880 }
  3881 
  3882 
  3883 /**
  3884 * Returns what HTML is allowed in content
  3885 *
  3886 * Returns what HTML tags the system allows to be used inside content.
  3887 * You can modify this by changing $_CONF['user_html'] in the configuration
  3888 * (for admins, see also $_CONF['admin_html']).
  3889 *
  3890 * @param    string  $permissions    comma-separated list of rights which identify the current user as an "Admin"
  3891 * @param    boolean $list_only      true = return only the list of HTML tags
  3892 * @return   string                  HTML <div>/<span> enclosed string
  3893 * @see      function COM_checkHTML
  3894 * @todo     Bugs: The list always includes the [code], [raw], and [page_break]
  3895 *           tags when story.* permissions are required, even when those tags
  3896 *           are not actually available (e.g. in comments on stories).
  3897 *
  3898 */
  3899 function COM_allowedHTML($permissions = 'story.edit', $list_only = false)
  3900 {
  3901     global $_CONF, $LANG01;
  3902 
  3903     $retval = '';
  3904 
  3905     if (isset($_CONF['skip_html_filter_for_root']) &&
  3906              ($_CONF['skip_html_filter_for_root'] == 1) &&
  3907             SEC_inGroup('Root')) {
  3908 
  3909         if (!$list_only) {
  3910             $retval .= '<span class="warningsmall">' . $LANG01[123]
  3911                     . ',</span> ';
  3912         }
  3913         $retval .= '<div dir="ltr" class="warningsmall">';
  3914 
  3915     } else {
  3916 
  3917         if (! $list_only) {
  3918             $retval .= '<span class="warningsmall">' . $LANG01[31] . '</span> ';
  3919         }
  3920 
  3921         if (empty($permissions) || !SEC_hasRights($permissions) ||
  3922                 empty($_CONF['admin_html'])) {
  3923             $html = $_CONF['user_html'];
  3924         } else {
  3925             $html = array_merge_recursive($_CONF['user_html'],
  3926                                           $_CONF['admin_html']);
  3927         }
  3928 
  3929         $retval .= '<div dir="ltr" class="warningsmall">';
  3930         foreach ($html as $tag => $attr) {
  3931             $retval .= '&lt;' . $tag . '&gt;, ';
  3932         }
  3933     }
  3934 
  3935     $with_story_perms = false;
  3936     $perms = explode(',', $permissions);
  3937     foreach ($perms as $p) {
  3938         if (substr($p, 0, 6) == 'story.') {
  3939             $with_story_perms = true;
  3940             break;
  3941         }
  3942     }
  3943 
  3944     if ($with_story_perms) {
  3945         $retval .= '[code], [raw], ';
  3946 
  3947         if ($_CONF['allow_page_breaks'] == 1) {
  3948             $retval .= '[page_break], ';
  3949         }
  3950     }
  3951 
  3952     // list autotags
  3953     $autotags = array_keys(PLG_collectTags());
  3954     $retval .= '[' . implode(':], [', $autotags) . ':]';
  3955     $retval .= '</div>';
  3956 
  3957     return $retval;
  3958 }
  3959 
  3960 /**
  3961 * Return the password for the given username
  3962 *
  3963 * Fetches a password for the given user
  3964 *
  3965 * @param    string  $loginname  username to get password for
  3966 * @return   string              Password or ''
  3967 *
  3968 */
  3969 
  3970 function COM_getPassword( $loginname )
  3971 {
  3972     global $_TABLES, $LANG01;
  3973 
  3974     $result = DB_query( "SELECT passwd FROM {$_TABLES['users']} WHERE username='$loginname'" );
  3975     $tmp = DB_error();
  3976     $nrows = DB_numRows( $result );
  3977 
  3978     if(( $tmp == 0 ) && ( $nrows == 1 ))
  3979     {
  3980         $U = DB_fetchArray( $result );
  3981         return $U['passwd'];
  3982     }
  3983     else
  3984     {
  3985         $tmp = $LANG01[32] . ": '" . $loginname . "'";
  3986         COM_errorLog( $tmp, 1 );
  3987     }
  3988 
  3989     return '';
  3990 }
  3991 
  3992 
  3993 /**
  3994 * Return the username or fullname for the passed member id (uid)
  3995 *
  3996 * Allows the siteAdmin to determine if loginname (username) or fullname
  3997 * should be displayed.
  3998 *
  3999 * @param    int     $uid        site member id
  4000 * @param    string  $username   Username, if this is set no lookup is done.
  4001 * @param    string  $fullname   Users full name.
  4002 * @param    string  $remoteusername  Username on remote service
  4003 * @param    string  $remoteservice   Remote login service.
  4004 * @return   string  Username, fullname or username@Service
  4005 *
  4006 */
  4007 function COM_getDisplayName( $uid = '', $username='', $fullname='', $remoteusername='', $remoteservice='' )
  4008 {
  4009     global $_CONF, $_TABLES, $_USER;
  4010 
  4011     if ($uid == '')
  4012     {
  4013         if( COM_isAnonUser() )
  4014         {
  4015             $uid = 1;
  4016         }
  4017         else
  4018         {
  4019             $uid = $_USER['uid'];
  4020         }
  4021     }
  4022 
  4023     if( empty( $username ))
  4024     {
  4025         $query = DB_query( "SELECT username, fullname, remoteusername, remoteservice FROM {$_TABLES['users']} WHERE uid='$uid'" );
  4026         list( $username, $fullname, $remoteusername, $remoteservice ) = DB_fetchArray( $query );
  4027     }
  4028 
  4029     if( !empty( $fullname ) && ($_CONF['show_fullname'] == 1 ))
  4030     {
  4031         return $fullname;
  4032     }
  4033     else if(( $_CONF['user_login_method']['3rdparty'] || $_CONF['user_login_method']['openid'] ) && !empty( $remoteusername ))
  4034     {
  4035         if( !empty( $username ))
  4036         {
  4037             $remoteusername = $username;
  4038         }
  4039 
  4040         if( $_CONF['show_servicename'] )
  4041         {
  4042             return "$remoteusername@$remoteservice";
  4043         }
  4044         else
  4045         {
  4046             return $remoteusername;
  4047         }
  4048     }
  4049 
  4050     return $username;
  4051 }
  4052 
  4053 
  4054 /**
  4055 * Adds a hit to the system
  4056 *
  4057 * This function is called in the footer of every page and is used to
  4058 * track the number of hits to the Geeklog system.  This information is
  4059 * shown on stats.php
  4060 *
  4061 */
  4062 
  4063 function COM_hit()
  4064 {
  4065     global $_TABLES;
  4066 
  4067     DB_change($_TABLES['vars'], 'value', 'value + 1', 'name', 'totalhits', '', true);
  4068 }
  4069 
  4070 /**
  4071 * This will email new stories in the topics that the user is interested in
  4072 *
  4073 * In account information the user can specify which topics for which they
  4074 * will receive any new article for in a daily digest.
  4075 *
  4076 * @return   void
  4077 */
  4078 
  4079 function COM_emailUserTopics()
  4080 {
  4081     global $_CONF, $_TABLES, $LANG04, $LANG08, $LANG24;
  4082 
  4083     if ($_CONF['emailstories'] == 0) {
  4084         return;
  4085     }
  4086 
  4087     $subject = strip_tags( $_CONF['site_name'] . $LANG08[30] . strftime( '%Y-%m-%d', time() ));
  4088 
  4089     $authors = array();
  4090 
  4091     // Get users who want stories emailed to them
  4092     $usersql = "SELECT username,email,etids,{$_TABLES['users']}.uid AS uuid "
  4093         . "FROM {$_TABLES['users']}, {$_TABLES['userindex']} "
  4094         . "WHERE {$_TABLES['users']}.uid > 1 AND {$_TABLES['userindex']}.uid = {$_TABLES['users']}.uid AND (etids <> '-' OR etids IS NULL) ORDER BY {$_TABLES['users']}.uid";
  4095 
  4096     $users = DB_query( $usersql );
  4097     $nrows = DB_numRows( $users );
  4098 
  4099     $lastrun = DB_getItem( $_TABLES['vars'], 'value', "name = 'lastemailedstories'" );
  4100 
  4101     // For each user, pull the stories they want and email it to them
  4102     for( $x = 0; $x < $nrows; $x++ )
  4103     {
  4104         $U = DB_fetchArray( $users );
  4105 
  4106         $storysql = array();
  4107         $storysql['mysql'] = "SELECT sid,uid,date AS day,title,introtext,postmode";
  4108 
  4109         $storysql['mssql'] = "SELECT sid,uid,date AS day,title,CAST(introtext AS text) AS introtext,postmode";
  4110 
  4111         $commonsql = " FROM {$_TABLES['stories']} WHERE draft_flag = 0 AND date <= NOW() AND date >= '{$lastrun}'";
  4112 
  4113         $topicsql = "SELECT tid FROM {$_TABLES['topics']}"
  4114                   . COM_getPermSQL( 'WHERE', $U['uuid'] );
  4115         $tresult = DB_query( $topicsql );
  4116         $trows = DB_numRows( $tresult );
  4117 
  4118         if( $trows == 0 )
  4119         {
  4120             // this user doesn't seem to have access to any topics ...
  4121             continue;
  4122         }
  4123 
  4124         $TIDS = array();
  4125         for( $i = 0; $i < $trows; $i++ )
  4126         {
  4127             $T = DB_fetchArray( $tresult );
  4128             $TIDS[] = $T['tid'];
  4129         }
  4130 
  4131         if( !empty( $U['etids'] ))
  4132         {
  4133             $ETIDS = explode( ' ', $U['etids'] );
  4134             $TIDS = array_intersect( $TIDS, $ETIDS );
  4135         }
  4136 
  4137         if( count( $TIDS ) > 0)
  4138         {
  4139             $commonsql .= " AND (tid IN ('" . implode( "','", $TIDS ) . "'))";
  4140         }
  4141 
  4142         $commonsql .= COM_getPermSQL( 'AND', $U['uuid'] );
  4143         $commonsql .= ' ORDER BY featured DESC, date DESC';
  4144 
  4145         $storysql['mysql'] .= $commonsql;
  4146         $storysql['mssql'] .= $commonsql;
  4147 
  4148         $stories = DB_query( $storysql );
  4149         $nsrows = DB_numRows( $stories );
  4150 
  4151         if( $nsrows == 0 )
  4152         {
  4153             // If no new stories where pulled for this user, continue with next
  4154             continue;
  4155         }
  4156 
  4157         $mailtext = $LANG08[29] . strftime( $_CONF['shortdate'], time() ) . "\n";
  4158 
  4159         for( $y = 0; $y < $nsrows; $y++ )
  4160         {
  4161             // Loop through stories building the requested email message
  4162             $S = DB_fetchArray( $stories );
  4163 
  4164             $mailtext .= "\n------------------------------\n\n";
  4165             $mailtext .= "$LANG08[31]: "
  4166                 . COM_undoSpecialChars( stripslashes( $S['title'] )) . "\n";
  4167             if( $_CONF['contributedbyline'] == 1 )
  4168             {
  4169                 if( empty( $authors[$S['uid']] ))
  4170                 {
  4171                     $storyauthor = COM_getDisplayName ($S['uid']);
  4172                     $authors[$S['uid']] = $storyauthor;
  4173                 }
  4174                 else
  4175                 {
  4176                     $storyauthor = $authors[$S['uid']];
  4177                 }
  4178                 $mailtext .= "$LANG24[7]: " . $storyauthor . "\n";
  4179             }
  4180 
  4181             $mailtext .= "$LANG08[32]: " . strftime( $_CONF['date'], strtotime( $S['day' ])) . "\n\n";
  4182 
  4183             if( $_CONF['emailstorieslength'] > 0 )
  4184             {
  4185                 if($S['postmode']==='wikitext'){
  4186                     $storytext = COM_undoSpecialChars( strip_tags( COM_renderWikiText ( stripslashes( $S['introtext'] ))));
  4187                 } else {
  4188                     $storytext = COM_undoSpecialChars( strip_tags( PLG_replaceTags( stripslashes( $S['introtext'] ))));
  4189                 }
  4190 
  4191                 if( $_CONF['emailstorieslength'] > 1 )
  4192                 {
  4193                     $storytext = COM_truncate( $storytext,
  4194                                     $_CONF['emailstorieslength'], '...' );
  4195                 }
  4196 
  4197                 $mailtext .= $storytext . "\n\n";
  4198             }
  4199 
  4200             $mailtext .= $LANG08[33] . ' ' . COM_buildUrl( $_CONF['site_url']
  4201                       . '/article.php?story=' . $S['sid'] ) . "\n";
  4202         }
  4203 
  4204         $mailtext .= "\n------------------------------\n";
  4205         $mailtext .= "\n$LANG08[34]\n";
  4206         $mailtext .= "\n------------------------------\n";
  4207 
  4208         $mailto = $U['username'] . ' <' . $U['email'] . '>';
  4209 
  4210         if ($_CONF['site_mail'] !== $_CONF['noreply_mail']) {
  4211             $mailfrom = $_CONF['noreply_mail'];
  4212             $mailtext .= LB . LB . $LANG04[159];
  4213         } else {
  4214             $mailfrom = $_CONF['site_mail'];
  4215         }
  4216         COM_mail( $mailto, $subject, $mailtext , $mailfrom);
  4217     }
  4218 
  4219     DB_query( "UPDATE {$_TABLES['vars']} SET value = NOW() WHERE name = 'lastemailedstories'" );
  4220 }
  4221 
  4222 /**
  4223 * Shows any new information in a block
  4224 *
  4225 * Return the HTML that shows any new stories, comments, etc
  4226 *
  4227 * @param    string  $help     Help file for block
  4228 * @param    string  $title    Title used in block header
  4229 * @param    string  $position Position in which block is being rendered 'left', 'right' or blank (for centre)
  4230 * @return   string  Return the HTML that shows any new stories, comments, etc
  4231 *
  4232 */
  4233 
  4234 function COM_whatsNewBlock( $help = '', $title = '', $position = '' )
  4235 {
  4236     global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_WHATSNEW, $page, $newstories;
  4237 
  4238     $retval = COM_startBlock( $title, $help,
  4239                        COM_getBlockTemplate( 'whats_new_block', 'header', $position ));
  4240 
  4241     $topicsql = '';
  4242     if(( $_CONF['hidenewstories'] == 0 ) || ( $_CONF['hidenewcomments'] == 0 )
  4243             || ( $_CONF['trackback_enabled']
  4244             && ( $_CONF['hidenewtrackbacks'] == 0 )))
  4245     {
  4246         $topicsql = COM_getTopicSql ('AND', 0, $_TABLES['stories']);
  4247     }
  4248 
  4249     if( $_CONF['hidenewstories'] == 0 )
  4250     {
  4251         $archsql = '';
  4252         $archivetid = DB_getItem( $_TABLES['topics'], 'tid', "archive_flag=1" );
  4253         if( !empty( $archivetid ))
  4254         {
  4255             $archsql = " AND (tid <> '" . addslashes( $archivetid ) . "')";
  4256         }
  4257 
  4258         // Find the newest stories
  4259         $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (date >= (date_sub(NOW(), INTERVAL {$_CONF['newstoriesinterval']} SECOND))) AND (date <= NOW()) AND (draft_flag = 0)" . $archsql . COM_getPermSQL( 'AND' ) . $topicsql . COM_getLangSQL( 'sid', 'AND' );
  4260         $result = DB_query( $sql );
  4261         $A = DB_fetchArray( $result );
  4262         $nrows = $A['count'];
  4263 
  4264         if( empty( $title ))
  4265         {
  4266             $title = DB_getItem( $_TABLES['blocks'], 'title', "name='whats_new_block'" );
  4267         }
  4268 
  4269         // Any late breaking news stories?
  4270         $retval .= '<h3>' . $LANG01[99] . '</h3>';
  4271 
  4272         if( $nrows > 0 )
  4273         {
  4274             $newmsg = COM_formatTimeString( $LANG_WHATSNEW['new_string'],
  4275                         $_CONF['newstoriesinterval'], $LANG01[11], $nrows);
  4276 
  4277             if( $newstories && ( $page < 2 ))
  4278             {
  4279                 $retval .= $newmsg . '<br' . XHTML . '>';
  4280             }
  4281             else
  4282             {
  4283                 $retval .= COM_createLink($newmsg, $_CONF['site_url']
  4284                     . '/index.php?display=new') . '<br' . XHTML . '>';
  4285             }
  4286         }
  4287         else
  4288         {
  4289             $retval .= $LANG01[100] . '<br' . XHTML . '>';
  4290         }
  4291 
  4292         if(( $_CONF['hidenewcomments'] == 0 ) || ( $_CONF['trackback_enabled']
  4293                 && ( $_CONF['hidenewtrackbacks'] == 0 ))
  4294                 || ( $_CONF['hidenewplugins'] == 0 ))
  4295         {
  4296             $retval .= '<br' . XHTML . '>';
  4297         }
  4298     }
  4299 
  4300     if( $_CONF['hidenewcomments'] == 0 )
  4301     {
  4302         // Go get the newest comments
  4303         $retval .= '<h3>' . $LANG01[83] . ' <small>'
  4304                 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
  4305                                         $_CONF['newcommentsinterval'] )
  4306                 . '</small></h3>';
  4307 
  4308         $stwhere = '';
  4309 
  4310         if( !COM_isAnonUser() )
  4311         {
  4312             $stwhere .= "({$_TABLES['stories']}.owner_id IS NOT NULL AND {$_TABLES['stories']}.perm_owner IS NOT NULL) OR ";
  4313             $stwhere .= "({$_TABLES['stories']}.group_id IS NOT NULL AND {$_TABLES['stories']}.perm_group IS NOT NULL) OR ";
  4314             $stwhere .= "({$_TABLES['stories']}.perm_members IS NOT NULL)";
  4315         }
  4316         else
  4317         {
  4318             $stwhere .= "({$_TABLES['stories']}.perm_anon IS NOT NULL)";
  4319         }
  4320         $sql = "SELECT DISTINCT COUNT(*) AS dups, type, {$_TABLES['stories']}.title, {$_TABLES['stories']}.sid, max({$_TABLES['comments']}.date) AS lastdate FROM {$_TABLES['comments']} LEFT JOIN {$_TABLES['stories']} ON (({$_TABLES['stories']}.sid = {$_TABLES['comments']}.sid)" . COM_getPermSQL( 'AND', 0, 2, $_TABLES['stories'] ) . " AND ({$_TABLES['stories']}.draft_flag = 0) AND ({$_TABLES['stories']}.commentcode >= 0)" . $topicsql . COM_getLangSQL( 'sid', 'AND', $_TABLES['stories'] ) . ") WHERE ({$_TABLES['comments']}.date >= (DATE_SUB(NOW(), INTERVAL {$_CONF['newcommentsinterval']} SECOND))) AND ((({$stwhere}))) GROUP BY {$_TABLES['comments']}.sid,type, {$_TABLES['stories']}.title, {$_TABLES['stories']}.title, {$_TABLES['stories']}.sid ORDER BY 5 DESC LIMIT 15";
  4321 
  4322         $result = DB_query( $sql );
  4323 
  4324         $nrows = DB_numRows( $result );
  4325 
  4326         if( $nrows > 0 )
  4327         {
  4328             $newcomments = array();
  4329 
  4330             for( $x = 0; $x < $nrows; $x++ )
  4331             {
  4332                 $A = DB_fetchArray( $result );
  4333 
  4334                 if(( $A['type'] == 'article' ) || empty( $A['type'] ))
  4335                 {
  4336                     $url = COM_buildUrl( $_CONF['site_url']
  4337                         . '/article.php?story=' . $A['sid'] ) . '#comments';
  4338                 }
  4339 
  4340                 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
  4341                 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
  4342                                             '...' );
  4343                 if( $title != $titletouse )
  4344                 {
  4345                     $attr = array('title' => htmlspecialchars($title));
  4346                 }
  4347                 else
  4348                 {
  4349                     $attr = array();
  4350                 }
  4351                 $acomment = str_replace( '$', '&#36;', $titletouse );
  4352                 $acomment = str_replace( ' ', '&nbsp;', $acomment );
  4353 
  4354                 if( $A['dups'] > 1 )
  4355                 {
  4356                     $acomment .= ' [+' . $A['dups'] . ']';
  4357                 }
  4358 
  4359                 $newcomments[] = COM_createLink($acomment, $url, $attr);
  4360             }
  4361 
  4362             $retval .= COM_makeList( $newcomments, 'list-new-comments' );
  4363         }
  4364         else
  4365         {
  4366             $retval .= $LANG01[86] . '<br' . XHTML . '>' . LB;
  4367         }
  4368         if(( $_CONF['hidenewplugins'] == 0 )
  4369                 || ( $_CONF['trackback_enabled']
  4370                 && ( $_CONF['hidenewtrackbacks'] == 0 )))
  4371         {
  4372             $retval .= '<br' . XHTML . '>';
  4373         }
  4374     }
  4375 
  4376     if( $_CONF['trackback_enabled'] && ( $_CONF['hidenewtrackbacks'] == 0 ))
  4377     {
  4378         $retval .= '<h3>' . $LANG01[114] . ' <small>'
  4379                 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
  4380                                         $_CONF['newtrackbackinterval'] )
  4381                 . '</small></h3>';
  4382 
  4383         $sql = "SELECT DISTINCT COUNT(*) AS count,{$_TABLES['stories']}.title,t.sid,max(t.date) AS lastdate FROM {$_TABLES['trackback']} AS t,{$_TABLES['stories']} WHERE (t.type = 'article') AND (t.sid = {$_TABLES['stories']}.sid) AND (t.date >= (DATE_SUB(NOW(), INTERVAL {$_CONF['newtrackbackinterval']} SECOND)))" . COM_getPermSQL( 'AND', 0, 2, $_TABLES['stories'] ) . " AND ({$_TABLES['stories']}.draft_flag = 0) AND ({$_TABLES['stories']}.trackbackcode = 0)" . $topicsql . COM_getLangSQL( 'sid', 'AND', $_TABLES['stories'] ) . " GROUP BY t.sid, {$_TABLES['stories']}.title ORDER BY lastdate DESC LIMIT 15";
  4384         $result = DB_query( $sql );
  4385 
  4386         $nrows = DB_numRows( $result );
  4387         if( $nrows > 0 )
  4388         {
  4389             $newcomments = array();
  4390 
  4391             for( $i = 0; $i < $nrows; $i++ )
  4392             {
  4393                 $A = DB_fetchArray( $result );
  4394 
  4395                 $url = COM_buildUrl( $_CONF['site_url']
  4396                     . '/article.php?story=' . $A['sid'] ) . '#trackback';
  4397 
  4398                 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
  4399                 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
  4400                                             '...' );
  4401 
  4402                 if( $title != $titletouse )
  4403                 {
  4404                     $attr = array('title' => htmlspecialchars($title));
  4405                 }
  4406                 else
  4407                 {
  4408                     $attr = array();
  4409                 }
  4410                 $acomment = str_replace( '$', '&#36;', $titletouse );
  4411                 $acomment = str_replace( ' ', '&nbsp;', $acomment );
  4412 
  4413                 if( $A['count'] > 1 )
  4414                 {
  4415                     $acomment .= ' [+' . $A['count'] . ']';
  4416                 }
  4417 
  4418                 $newcomments[] = COM_createLink($acomment, $url, $attr);
  4419             }
  4420 
  4421             $retval .= COM_makeList( $newcomments, 'list-new-trackbacks' );
  4422         }
  4423         else
  4424         {
  4425             $retval .= $LANG01[115] . '<br' . XHTML . '>' . LB;
  4426         }
  4427         if( $_CONF['hidenewplugins'] == 0 )
  4428         {
  4429             $retval .= '<br' . XHTML . '>';
  4430         }
  4431     }
  4432 
  4433     if( $_CONF['hidenewplugins'] == 0 )
  4434     {
  4435         list( $headlines, $smallheadlines, $content ) = PLG_getWhatsNew();
  4436         $plugins = count( $headlines );
  4437         if( $plugins > 0 )
  4438         {
  4439             for( $i = 0; $i < $plugins; $i++ )
  4440             {
  4441                 $retval .= '<h3>' . $headlines[$i] . ' <small>'
  4442                         . $smallheadlines[$i] . '</small></h3>';
  4443                 if( is_array( $content[$i] ))
  4444                 {
  4445                     $retval .= COM_makeList( $content[$i], 'list-new-plugins' );
  4446                 }
  4447                 else
  4448                 {
  4449                     $retval .= $content[$i];
  4450                 }
  4451 
  4452                 if( $i + 1 < $plugins )
  4453                 {
  4454                     $retval .= '<br' . XHTML . '>';
  4455                 }
  4456             }
  4457         }
  4458     }
  4459 
  4460     $retval .= COM_endBlock( COM_getBlockTemplate( 'whats_new_block', 'footer', $position ));
  4461 
  4462     return $retval;
  4463 }
  4464 
  4465 /**
  4466 * Creates the string that indicates the timespan in which new items were found
  4467 *
  4468 * @param    string  $time_string    template string
  4469 * @param    int     $time           number of seconds in which results are found
  4470 * @param    string  $type           type (translated string) of new item
  4471 * @param    int     $amount         amount of things that have been found.
  4472 */
  4473 function COM_formatTimeString( $time_string, $time, $type = '', $amount = 0 )
  4474 {
  4475     global $LANG_WHATSNEW;
  4476 
  4477     $retval = $time_string;
  4478 
  4479     // This is the amount you have to divide the previous by to get the
  4480     // different time intervals: hour, day, week, months
  4481     $time_divider = array( 60, 60, 24, 7, 30 );
  4482 
  4483     // These are the respective strings to the numbers above. They have to match
  4484     // the strings in $LANG_WHATSNEW (i.e. these are the keys for the array -
  4485     // the actual text strings are taken from the language file).
  4486     $time_description  = array( 'minute',  'hour',  'day',  'week',  'month'  );
  4487     $times_description = array( 'minutes', 'hours', 'days', 'weeks', 'months' );
  4488 
  4489     $time_dividers = count( $time_divider );
  4490     for( $s = 0; $s < $time_dividers; $s++ )
  4491     {
  4492         $time = $time / $time_divider[$s];
  4493         if( $time < $time_divider[$s + 1] )
  4494         {
  4495             if( $time == 1 )
  4496             {
  4497                 if( $s == 0 )
  4498                 {
  4499                     $time_str = $time_description[$s];
  4500                 }
  4501                 else // go back to the previous unit, e.g. 1 day -> 24 hours
  4502                 {
  4503                     $time_str = $times_description[$s - 1];
  4504                     $time *= $time_divider[$s];
  4505                 }
  4506             }
  4507             else
  4508             {
  4509                 $time_str = $times_description[$s];
  4510             }
  4511             $fields = array( '%n', '%i', '%t', '%s' );
  4512             $values = array( $amount, $type, $time, $LANG_WHATSNEW[$time_str] );
  4513             $retval = str_replace( $fields, $values, $retval );
  4514             break;
  4515         }
  4516     }
  4517 
  4518     return $retval;
  4519 }
  4520 
  4521 /**
  4522 * Displays a message text in a "System Message" block
  4523 *
  4524 * @param    string  $message    Message text; may contain HTML
  4525 * @param    string  $title      (optional) alternative block title
  4526 * @return   string              HTML block with message
  4527 * @see      COM_showMessage
  4528 * @see      COM_showMessageFromParameter
  4529 *
  4530 */
  4531 function COM_showMessageText($message, $title = '')
  4532 {
  4533     global $_CONF, $MESSAGE, $_IMAGE_TYPE;
  4534 
  4535     $retval = '';
  4536 
  4537     if (!empty($message)) {
  4538         if (empty($title)) {
  4539             $title = $MESSAGE[40];
  4540         }
  4541         $timestamp = strftime($_CONF['daytime']);
  4542         $retval .= COM_startBlock($title . ' - ' . $timestamp, '',
  4543                                   COM_getBlockTemplate('_msg_block', 'header'))
  4544                 . '<p class="sysmessage"><img src="' . $_CONF['layout_url']
  4545                 . '/images/sysmessage.' . $_IMAGE_TYPE . '" alt="" ' . XHTML
  4546                 . '>' . $message . '</p>'
  4547                 . COM_endBlock(COM_getBlockTemplate('_msg_block', 'footer'));
  4548     }
  4549 
  4550     return $retval;
  4551 }
  4552 
  4553 /**
  4554 * Displays a message on the webpage
  4555 *
  4556 * Display one of the predefined messages from the $MESSAGE array. If a plugin
  4557 * name is provided, display that plugin's message instead.
  4558 * 
  4559 * @param    int     $msg        ID of message to show
  4560 * @param    string  $plugin     Optional name of plugin to lookup plugin defined message
  4561 * @return   string              HTML block with message
  4562 * @see      COM_showMessageFromParameter
  4563 * @see      COM_showMessageText
  4564 *
  4565 */
  4566 function COM_showMessage($msg, $plugin = '')
  4567 {
  4568     global $MESSAGE;
  4569 
  4570     $retval = '';
  4571 
  4572     if ($msg > 0) {
  4573         if (!empty($plugin)) {
  4574             $var = 'PLG_' . $plugin . '_MESSAGE' . $msg;
  4575             global $$var;
  4576             if (isset($$var)) {
  4577                 $message = $$var;
  4578             } else {
  4579                 $message = sprintf($MESSAGE[61], $plugin);
  4580                 COM_errorLog($message . ": " . $var, 1);
  4581             }
  4582         } else {
  4583             $message = $MESSAGE[$msg];
  4584         }
  4585 
  4586         if (!empty($message)) {
  4587             $retval .= COM_showMessageText($message);
  4588         }
  4589     }
  4590 
  4591     return $retval;
  4592 }
  4593 
  4594 /**
  4595 * Displays a message, as defined by URL parameters
  4596 *
  4597 * Helper function to display a message, if URL parameters 'msg' and 'plugin'
  4598 * (optional) are defined. Only for GET requests, but that's what Geeklog uses
  4599 * everywhere anyway.
  4600 *
  4601 * @return   string  HTML block with message
  4602 * @see      COM_showMessage
  4603 * @see      COM_showMessageText
  4604 *
  4605 */
  4606 function COM_showMessageFromParameter()
  4607 {
  4608     $retval = '';
  4609 
  4610     if (isset($_GET['msg'])) {
  4611         $msg = COM_applyFilter($_GET['msg'], true);
  4612         if ($msg > 0) {
  4613             $plugin = '';
  4614             if (isset($_GET['plugin'])) {
  4615                 $plugin = COM_applyFilter($_GET['plugin']);
  4616             }
  4617             $retval .= COM_showMessage($msg, $plugin);
  4618         }
  4619     }
  4620 
  4621     return $retval;
  4622 }
  4623 
  4624 /**
  4625 * Prints Google(tm)-like paging navigation
  4626 *
  4627 * @param        string      $base_url       base url to use for all generated links
  4628 * @param        int         $curpage        current page we are on
  4629 * @param        int         $num_pages      Total number of pages
  4630 * @param        string      $page_str       page-variable name AND '='
  4631 * @param        boolean     $do_rewrite     if true, url-rewriting is respected
  4632 * @param        string      $msg            to be displayed with the navigation
  4633 * @param        string      $open_ended     replace next/last links with this
  4634 * @return   string   HTML formatted widget
  4635 */
  4636 function COM_printPageNavigation( $base_url, $curpage, $num_pages,
  4637                                   $page_str='page=', $do_rewrite=false, $msg='',
  4638                                   $open_ended = '')
  4639 {
  4640     global $LANG05;
  4641 
  4642     $retval = '';
  4643 
  4644     if( $num_pages < 2 )
  4645     {
  4646         return;
  4647     }
  4648 
  4649     if( !$do_rewrite )
  4650     {
  4651         $hasargs = strstr( $base_url, '?' );
  4652         if( $hasargs )
  4653         {
  4654             $sep = '&amp;';
  4655         }
  4656         else
  4657         {
  4658             $sep = '?';
  4659         }
  4660     }
  4661     else
  4662     {
  4663         $sep = '/';
  4664         $page_str = '';
  4665     }
  4666 
  4667     if( $curpage > 1 )
  4668     {
  4669         $retval .= COM_createLink($LANG05[7], $base_url) . ' | ';
  4670         $pg = '';
  4671         if( ( $curpage - 1 ) > 1 )
  4672         {
  4673             $pg = $sep . $page_str . ( $curpage - 1 );
  4674         }
  4675         $retval .= COM_createLink($LANG05[6], $base_url . $pg ) . ' | ';
  4676     }
  4677     else
  4678     {
  4679         $retval .= $LANG05[7] . ' | ' ;
  4680         $retval .= $LANG05[6] . ' | ' ;
  4681     }
  4682 
  4683     for( $pgcount = ( $curpage - 10 ); ( $pgcount <= ( $curpage + 9 )) AND ( $pgcount <= $num_pages ); $pgcount++ )
  4684     {
  4685         if( $pgcount <= 0 )
  4686         {
  4687             $pgcount = 1;
  4688         }
  4689 
  4690         if( $pgcount == $curpage )
  4691         {
  4692             $retval .= '<b>' . $pgcount . '</b> ';
  4693         }
  4694         else
  4695         {
  4696             $pg = '';
  4697             if( $pgcount > 1 )
  4698             {
  4699                 $pg = $sep . $page_str . $pgcount;
  4700             }
  4701             $retval .= COM_createLink($pgcount, $base_url . $pg) . ' ';
  4702         }
  4703     }
  4704 
  4705     if( !empty( $open_ended ))
  4706     {
  4707         $retval .= '| ' . $open_ended;
  4708     }
  4709     else if( $curpage == $num_pages )
  4710     {
  4711         $retval .= '| ' . $LANG05[5] . ' ';
  4712         $retval .= '| ' . $LANG05[8];
  4713     }
  4714     else
  4715     {
  4716         $retval .= '| ' . COM_createLink($LANG05[5], $base_url . $sep
  4717                                          . $page_str . ($curpage + 1));
  4718         $retval .= ' | ' . COM_createLink($LANG05[8], $base_url . $sep
  4719                                           . $page_str . $num_pages);
  4720     }
  4721 
  4722     if( !empty( $retval ))
  4723     {
  4724         if( !empty( $msg ))
  4725         {
  4726             $msg .=  ' ';
  4727         }
  4728         $retval = '<div class="pagenav">' . $msg . $retval . '</div>';
  4729     }
  4730 
  4731     return $retval;
  4732 }
  4733 
  4734 /**
  4735 * Returns formatted date/time for user
  4736 *
  4737 * This function COM_takes a date in either unixtimestamp or in english and
  4738 * formats it to the users preference.  If the user didn't specify a format
  4739 * the format in the config file is used.  This returns an array where array[0]
  4740 * is the formatted date and array[1] is the unixtimestamp
  4741 *
  4742 * @param        string      $date       date to format, otherwise we format current date/time
  4743 * @return   array   array[0] is the formatted date and array[1] is the unixtimestamp.
  4744 */
  4745 
  4746 function COM_getUserDateTimeFormat( $date='' )
  4747 {
  4748     global $_TABLES, $_USER, $_CONF;
  4749 
  4750     // Get display format for time
  4751 
  4752     if( !COM_isAnonUser() )
  4753     {
  4754         if( empty( $_USER['format'] ))
  4755         {
  4756             $dateformat = $_CONF['date'];
  4757         }
  4758         else
  4759         {
  4760             $dateformat = $_USER['format'];
  4761         }
  4762     }
  4763     else
  4764     {
  4765         $dateformat = $_CONF['date'];
  4766     }
  4767 
  4768     if( empty( $date ))
  4769     {
  4770         // Date is empty, get current date/time
  4771         $stamp = time();
  4772     }
  4773     else if( is_numeric( $date ))
  4774     {
  4775         // This is a timestamp
  4776         $stamp = $date;
  4777     }
  4778     else
  4779     {
  4780         // This is a string representation of a date/time
  4781         $stamp = strtotime( $date );
  4782     }
  4783 
  4784     // Format the date
  4785 
  4786     $date = strftime( $dateformat, $stamp );
  4787 
  4788     return array( $date, $stamp );
  4789 }
  4790 
  4791 /**
  4792 * Returns user-defined cookie timeout
  4793 *
  4794 * In account preferences users can specify when their long-term cookie expires.
  4795 * This function returns that value.
  4796 *
  4797 * @return   int Cookie time out value in seconds
  4798 */
  4799 
  4800 function COM_getUserCookieTimeout()
  4801 {
  4802     global $_TABLES, $_USER, $_CONF;
  4803 
  4804     if( empty( $_USER ))
  4805     {
  4806         return;
  4807     }
  4808 
  4809     $timeoutvalue = DB_getItem( $_TABLES['users'], 'cookietimeout', "uid = {$_USER['uid']}" );
  4810 
  4811     if( empty( $timeoutvalue ))
  4812     {
  4813         $timeoutvalue = 0;
  4814     }
  4815 
  4816     return $timeoutvalue;
  4817 }
  4818 
  4819 /**
  4820 * Shows who is online in slick little block
  4821 * @return   string  HTML string of online users seperated by line breaks.
  4822 */
  4823 
  4824 function phpblock_whosonline()
  4825 {
  4826     global $_CONF, $_TABLES, $_USER, $LANG01, $_IMAGE_TYPE;
  4827 
  4828     $retval = '';
  4829 
  4830     $expire_time = time() - $_CONF['whosonline_threshold'];
  4831 
  4832     $byname = 'username';
  4833     if( $_CONF['show_fullname'] == 1 )
  4834     {
  4835         $byname .= ',fullname';
  4836     }
  4837     if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
  4838     {
  4839         $byname .= ',remoteusername,remoteservice';
  4840     }
  4841 
  4842     $result = DB_query( "SELECT DISTINCT {$_TABLES['sessions']}.uid,{$byname},photo,showonline FROM {$_TABLES['sessions']},{$_TABLES['users']},{$_TABLES['userprefs']} WHERE {$_TABLES['users']}.uid = {$_TABLES['sessions']}.uid AND {$_TABLES['users']}.uid = {$_TABLES['userprefs']}.uid AND start_time >= $expire_time AND {$_TABLES['sessions']}.uid <> 1 ORDER BY {$byname}" );
  4843     $nrows = DB_numRows( $result );
  4844 
  4845     $num_anon = 0;
  4846     $num_reg  = 0;
  4847 
  4848     for( $i = 0; $i < $nrows; $i++ )
  4849     {
  4850         $A = DB_fetchArray( $result );
  4851 
  4852         if( $A['showonline'] == 1 )
  4853         {
  4854             $fullname = '';
  4855             if( $_CONF['show_fullname'] == 1 )
  4856             {
  4857                 $fullname = $A['fullname'];
  4858             }
  4859             if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
  4860             {
  4861                 $username = COM_getDisplayName( $A['uid'], $A['username'],
  4862                         $fullname, $A['remoteusername'], $A['remoteservice'] );
  4863             }
  4864             else
  4865             {
  4866                 $username = COM_getDisplayName( $A['uid'], $A['username'],
  4867                                                 $fullname );
  4868             }
  4869             $url = $_CONF['site_url'] . '/users.php?mode=profile&amp;uid=' . $A['uid'];
  4870             $retval .= COM_createLink($username, $url);
  4871 
  4872             if( !empty( $A['photo'] ) AND $_CONF['allow_user_photo'] == 1)
  4873             {
  4874                 $usrimg = '<img src="' . $_CONF['layout_url']
  4875                         . '/images/smallcamera.' . $_IMAGE_TYPE
  4876                         . '" alt=""' . XHTML . '>';
  4877                 $retval .= '&nbsp;' . COM_createLink($usrimg, $url);
  4878             }
  4879             $retval .= '<br' . XHTML . '>';
  4880             $num_reg++;
  4881         }
  4882         else
  4883         {
  4884             // this user does not want to show up in Who's Online
  4885             $num_anon++; // count as anonymous
  4886         }
  4887     }
  4888 
  4889     $num_anon += DB_count( $_TABLES['sessions'], 'uid', 1 );
  4890 
  4891     if(( $_CONF['whosonline_anonymous'] == 1 ) &&
  4892             COM_isAnonUser() )
  4893     {
  4894         // note that we're overwriting the contents of $retval here
  4895         if( $num_reg > 0 )
  4896         {
  4897             $retval = $LANG01[112] . ': ' . COM_numberFormat($num_reg)
  4898                     . '<br' . XHTML . '>';
  4899         }
  4900         else
  4901         {
  4902             $retval = '';
  4903         }
  4904     }
  4905 
  4906     if( $num_anon > 0 )
  4907     {
  4908         $retval .= $LANG01[41] . ': ' . COM_numberFormat($num_anon)
  4909                 . '<br' . XHTML . '>';
  4910     }
  4911 
  4912     return $retval;
  4913 }
  4914 
  4915 /**
  4916 * Gets the <option> values for calendar months
  4917 *
  4918 * @param        string      $selected       Selected month
  4919 * @see function COM_getDayFormOptions
  4920 * @see function COM_getYearFormOptions
  4921 * @see function COM_getHourFormOptions
  4922 * @see function COM_getMinuteFormOptions
  4923 * @return   string  HTML Months as option values
  4924 */
  4925 
  4926 function COM_getMonthFormOptions( $selected = '' )
  4927 {
  4928     global $LANG_MONTH;
  4929 
  4930     $month_options = '';
  4931 
  4932     for( $i = 1; $i <= 12; $i++ )
  4933     {
  4934         $mval = $i;
  4935         $month_options .= '<option value="' . $mval . '"';
  4936 
  4937         if( $i == $selected )
  4938         {
  4939             $month_options .= ' selected="selected"';
  4940         }
  4941 
  4942         $month_options .= '>' . $LANG_MONTH[$mval] . '</option>';
  4943     }
  4944 
  4945     return $month_options;
  4946 }
  4947 
  4948 /**
  4949 * Gets the <option> values for calendar days
  4950 *
  4951 * @param        string      $selected       Selected day
  4952 * @see function COM_getMonthFormOptions
  4953 * @see function COM_getYearFormOptions
  4954 * @see function COM_getHourFormOptions
  4955 * @see function COM_getMinuteFormOptions
  4956 * @return string HTML days as option values
  4957 */
  4958 
  4959 function COM_getDayFormOptions( $selected = '' )
  4960 {
  4961     $day_options = '';
  4962 
  4963     for( $i = 1; $i <= 31; $i++ )
  4964     {
  4965         if( $i < 10 )
  4966         {
  4967             $dval = '0' . $i;
  4968         }
  4969         else
  4970         {
  4971             $dval = $i;
  4972         }
  4973 
  4974         $day_options .= '<option value="' . $dval . '"';
  4975 
  4976         if( $i == $selected )
  4977         {
  4978             $day_options .= ' selected="selected"';
  4979         }
  4980 
  4981         $day_options .= '>' . $dval . '</option>';
  4982     }
  4983 
  4984     return $day_options;
  4985 }
  4986 
  4987 /**
  4988 * Gets the <option> values for calendar years
  4989 *
  4990 * Returns Option list Containing 5 years starting with current
  4991 * unless @selected is < current year then starts with @selected
  4992 *
  4993 * @param        string      $selected     Selected year
  4994 * @param        int         $startoffset  Optional (can be +/-) Used to determine start year for range of years
  4995 * @param        int         $endoffset    Optional (can be +/-) Used to determine end year for range of years
  4996 * @see function COM_getMonthFormOptions
  4997 * @see function COM_getDayFormOptions
  4998 * @see function COM_getHourFormOptions
  4999 * @see function COM_getMinuteFormOptions
  5000 * @return string  HTML years as option values
  5001 */
  5002 
  5003 function COM_getYearFormOptions($selected = '', $startoffset = -1, $endoffset = 5)
  5004 {
  5005     $year_options = '';
  5006     $start_year  = date('Y') + $startoffset;
  5007     $cur_year    = date('Y', time());
  5008     $finish_year = $cur_year + $endoffset;
  5009 
  5010     if (!empty($selected)) {
  5011         if ($selected < $cur_year) {
  5012             $start_year = $selected;
  5013         }
  5014     }
  5015 
  5016     for ($i = $start_year; $i <= $finish_year; $i++) {
  5017         $year_options .= '<option value="' . $i . '"';
  5018 
  5019         if ($i == $selected) {
  5020             $year_options .= ' selected="selected"';
  5021         }
  5022 
  5023         $year_options .= '>' . $i . '</option>';
  5024     }
  5025 
  5026     return $year_options;
  5027 }
  5028 
  5029 /**
  5030 * Gets the <option> values for clock hours
  5031 *
  5032 * @param    string  $selected   Selected hour
  5033 * @param    int     $mode       12 or 24 hour mode
  5034 * @return   string              HTML string of options
  5035 * @see function COM_getMonthFormOptions
  5036 * @see function COM_getDayFormOptions
  5037 * @see function COM_getYearFormOptions
  5038 * @see function COM_getMinuteFormOptions
  5039 */
  5040 
  5041 function COM_getHourFormOptions( $selected = '', $mode = 12 )
  5042 {
  5043     $hour_options = '';
  5044 
  5045     if( $mode == 12 )
  5046     {
  5047         for( $i = 1; $i <= 11; $i++ )
  5048         {
  5049             if( $i < 10 )
  5050             {
  5051                 $hval = '0' . $i;
  5052             }
  5053             else
  5054             {
  5055                 $hval = $i;
  5056             }
  5057 
  5058             if( $i == 1 )
  5059             {
  5060                 $hour_options .= '<option value="12"';
  5061 
  5062                 if( $selected == 12 )
  5063                 {
  5064                     $hour_options .= ' selected="selected"';
  5065                 }
  5066 
  5067                 $hour_options .= '>12</option>';
  5068             }
  5069 
  5070             $hour_options .= '<option value="' . $hval . '"';
  5071 
  5072             if( $selected == $i )
  5073             {
  5074                 $hour_options .= ' selected="selected"';
  5075             }
  5076 
  5077             $hour_options .= '>' . $i . '</option>';
  5078         }
  5079     }
  5080     else // if( $mode == 24 )
  5081     {
  5082         for( $i = 0; $i < 24; $i++ )
  5083         {
  5084             if( $i < 10 )
  5085             {
  5086                 $hval = '0' . $i;
  5087             }
  5088             else
  5089             {
  5090                 $hval = $i;
  5091             }
  5092 
  5093             $hour_options .= '<option value="' . $hval . '"';
  5094 
  5095             if( $selected == $i )
  5096             {
  5097                 $hour_options .= ' selected="selected"';
  5098             }
  5099 
  5100             $hour_options .= '>' . $i . '</option>';
  5101         }
  5102     }
  5103 
  5104     return $hour_options;
  5105 }
  5106 
  5107 /**
  5108 * Gets the <option> values for clock minutes
  5109 *
  5110 * @param    string      $selected   Selected minutes
  5111 * @param    int         $step       number of minutes between options, e.g. 15
  5112 * @see function COM_getMonthFormOptions
  5113 * @see function COM_getDayFormOptions
  5114 * @see function COM_getHourFormOptions
  5115 * @see function COM_getYearFormOptions
  5116 * @return string  HTML of option minutes
  5117 */
  5118 
  5119 function COM_getMinuteFormOptions( $selected = '', $step = 1 )
  5120 {
  5121     $minute_options = '';
  5122 
  5123     if(( $step < 1 ) || ( $step > 30 ))
  5124     {
  5125         $step = 1;
  5126     }
  5127 
  5128     for( $i = 0; $i <= 59; $i += $step )
  5129     {
  5130         if( $i < 10 )
  5131         {
  5132             $mval = '0' . $i;
  5133         }
  5134         else
  5135         {
  5136             $mval = $i;
  5137         }
  5138 
  5139         $minute_options .= '<option value="' . $mval . '"';
  5140 
  5141         if( $selected == $i )
  5142         {
  5143             $minute_options .= ' selected="selected"';
  5144         }
  5145 
  5146         $minute_options .= '>' . $mval . '</option>';
  5147     }
  5148 
  5149     return $minute_options;
  5150 }
  5151 
  5152 /**
  5153 * For backward compatibility only.
  5154 * This function should always have been called COM_getMinuteFormOptions
  5155 * @see COM_getMinuteFormOptions
  5156 */
  5157 function COM_getMinuteOptions( $selected = '', $step = 1 )
  5158 {
  5159     return COM_getMinuteFormOptions( $selected, $step );
  5160 }
  5161 
  5162 /**
  5163 * Create an am/pm selector dropdown menu
  5164 *
  5165 * @param    string  $name       name of the <select>
  5166 * @param    string  $selected   preselection: 'am' or 'pm'
  5167 * @return   string  HTML for the dropdown; empty string in 24 hour mode
  5168 *
  5169 */
  5170 function COM_getAmPmFormSelection( $name, $selected = '' )
  5171 {
  5172     global $_CONF;
  5173 
  5174     $retval = '';
  5175 
  5176     if( isset( $_CONF['hour_mode'] ) && ( $_CONF['hour_mode'] == 24 ))
  5177     {
  5178         $retval = '';
  5179     }
  5180     else
  5181     {
  5182         if( empty( $selected ))
  5183         {
  5184             $selected = date( 'a' );
  5185         }
  5186 
  5187         $retval .= '<select name="' . $name . '">' . LB;
  5188         $retval .= '<option value="am"';
  5189         if( $selected == 'am' )
  5190         {
  5191             $retval .= ' selected="selected"';
  5192         }
  5193         $retval .= '>am</option>' . LB . '<option value="pm"';
  5194         if( $selected == 'pm' )
  5195         {
  5196             $retval .= ' selected="selected"';
  5197         }
  5198         $retval .= '>pm</option>' . LB . '</select>' . LB;
  5199     }
  5200 
  5201     return $retval;
  5202 }
  5203 
  5204 /**
  5205 * Creates an HTML unordered list from the given array.
  5206 * It formats one list item per array element, using the list.thtml
  5207 * and listitem.thtml templates.
  5208 *
  5209 * @param    array   $listofitems    Items to list out
  5210 * @param    string  $classname      optional CSS class name for the list
  5211 * @return   string                  HTML unordered list of array items
  5212 */
  5213 function COM_makeList($listofitems, $classname = '')
  5214 {
  5215     global $_CONF;
  5216 
  5217     $list = new Template($_CONF['path_layout']);
  5218     $list->set_file(array('list'     => 'list.thtml',
  5219                           'listitem' => 'listitem.thtml'));
  5220     $list->set_var( 'xhtml', XHTML );
  5221     $list->set_var('site_url', $_CONF['site_url']);
  5222     $list->set_var('site_admin_url', $_CONF['site_admin_url']);
  5223     $list->set_var('layout_url', $_CONF['layout_url']);
  5224 
  5225     if (empty($classname)) {
  5226         $list->set_var('list_class',      '');
  5227         $list->set_var('list_class_name', '');
  5228     } else {
  5229         $list->set_var('list_class',      'class="' . $classname . '"');
  5230         $list->set_var('list_class_name', $classname);
  5231     }
  5232 
  5233     if (is_array($listofitems)) {
  5234         foreach ($listofitems as $oneitem) {
  5235             $list->set_var('list_item', $oneitem);
  5236             $list->parse('list_items', 'listitem', true);
  5237         }
  5238     }
  5239 
  5240     $list->parse('newlist', 'list', true);
  5241 
  5242     return $list->finish($list->get_var('newlist'));
  5243 }
  5244 
  5245 /**
  5246 * Check if speed limit applies
  5247 *
  5248 * @param    string  $type       type of speed limit, e.g. 'submit', 'comment'
  5249 * @param    int     $max        max number of allowed tries within speed limit
  5250 * @param    string  $property   IP address or other identifiable property
  5251 * @return   int                 0: does not apply, else: seconds since last post
  5252 */
  5253 function COM_checkSpeedlimit($type = 'submit', $max = 1, $property = '')
  5254 {
  5255     global $_TABLES;
  5256 
  5257     $last = 0;
  5258 
  5259     if (empty($property)) {
  5260         $property = $_SERVER['REMOTE_ADDR'];
  5261     }
  5262     $property = addslashes($property);
  5263 
  5264     $res  = DB_query("SELECT date FROM {$_TABLES['speedlimit']} WHERE (type = '$type') AND (ipaddress = '$property') ORDER BY date ASC");
  5265 
  5266     // If the number of allowed tries has not been reached,
  5267     // return 0 (didn't hit limit)
  5268     if (DB_numRows($res) < $max) {
  5269         return $last;
  5270     }
  5271 
  5272     list($date) = DB_fetchArray($res);
  5273 
  5274     if (!empty($date)) {
  5275         $last = time() - $date;
  5276         if ($last == 0) {
  5277             // just in case someone manages to submit something in < 1 sec.
  5278             $last = 1;
  5279         }
  5280     }
  5281 
  5282     return $last;
  5283 }
  5284 
  5285 /**
  5286 * Store post info for speed limit
  5287 *
  5288 * @param    string  $type       type of speed limit, e.g. 'submit', 'comment'
  5289 * @param    string  $property   IP address or other identifiable property
  5290 *
  5291 */
  5292 function COM_updateSpeedlimit($type = 'submit', $property = '')
  5293 {
  5294     global $_TABLES;
  5295 
  5296     if (empty($property)) {
  5297         $property = $_SERVER['REMOTE_ADDR'];
  5298     }
  5299     $property = addslashes($property);
  5300 
  5301     DB_save($_TABLES['speedlimit'], 'ipaddress,date,type',
  5302             "'$property',UNIX_TIMESTAMP(),'$type'");
  5303 }
  5304 
  5305 /**
  5306 * Clear out expired speed limits, i.e. entries older than 'x' seconds
  5307 *
  5308 * @param speedlimit   int      number of seconds
  5309 * @param type         string   type of speed limit, e.g. 'submit', 'comment'
  5310 *
  5311 */
  5312 function COM_clearSpeedlimit($speedlimit = 60, $type = '')
  5313 {
  5314     global $_TABLES;
  5315 
  5316     $sql = "DELETE FROM {$_TABLES['speedlimit']} WHERE ";
  5317     if (!empty($type)) {
  5318         $sql .= "(type = '$type') AND ";
  5319     }
  5320     $sql .= "(date < UNIX_TIMESTAMP() - $speedlimit)";
  5321     DB_query($sql);
  5322 }
  5323 
  5324 /**
  5325 * Reset the speedlimit
  5326 *
  5327 * @param    string  $type       type of speed limit to reset, e.g. 'submit'
  5328 * @param    string  $property   IP address or other identifiable property
  5329 *
  5330 */
  5331 function COM_resetSpeedlimit($type = 'submit', $property = '')
  5332 {
  5333     global $_TABLES;
  5334 
  5335     if (empty($property)) {
  5336         $property = $_SERVER['REMOTE_ADDR'];
  5337     }
  5338     $property = addslashes($property);
  5339 
  5340     DB_delete($_TABLES['speedlimit'], array('type', 'ipaddress'),
  5341                                       array($type, $property));
  5342 }
  5343 
  5344 /**
  5345 * Wrapper function for URL class so as to not confuse people as this will
  5346 * eventually get used all over the place
  5347 *
  5348 * This function returns a crawler friendly URL (if possible)
  5349 *
  5350 * @param    string      $url    URL to try to build crawler friendly URL for
  5351 * @return   string              Rewritten URL
  5352 */
  5353 
  5354 function COM_buildURL( $url )
  5355 {
  5356     global $_URL;
  5357 
  5358     return $_URL->buildURL( $url );
  5359 }
  5360 
  5361 /**
  5362 * Wrapper function for URL class so as to not confuse people
  5363 *
  5364 * This function sets the name of the arguments found in url
  5365 *
  5366 * @param    array   $names  Names of arguments in query string to assign to values
  5367 * @return   boolean         True if successful
  5368 */
  5369 
  5370 function COM_setArgNames( $names )
  5371 {
  5372     global $_URL;
  5373 
  5374     return $_URL->setArgNames( $names );
  5375 }
  5376 
  5377 /**
  5378 * Wrapper function for URL class
  5379 *
  5380 * returns value for specified argument
  5381 *
  5382 * @param        string      $name       argument to get value for
  5383 * @return   string     Argument value
  5384 */
  5385 
  5386 function COM_getArgument( $name )
  5387 {
  5388     global $_URL;
  5389 
  5390     return $_URL->getArgument( $name );
  5391 }
  5392 
  5393 /**
  5394 * Occurences / time
  5395 *
  5396 * This will take a number of occurrences, and number of seconds for the time span and return
  5397 * the smallest #/time interval
  5398 *
  5399 * @param    int     $occurrences        how many occurrences during time interval
  5400 * @param    int     $timespan           time interval in seconds
  5401 * @return   int Seconds per interval
  5402 */
  5403 
  5404 function COM_getRate( $occurrences, $timespan )
  5405 {
  5406     // want to define some common time words (yes, dirk, i need to put this in LANG)
  5407     // time words and their value in seconds
  5408     // week is 7 * day, month is 30 * day, year is 365.25 * day
  5409 
  5410     $common_time = array(
  5411         "second" => 1,
  5412         "minute" => 60,
  5413         "hour"   => 3600,
  5414         "day"    => 86400,
  5415         "week"   => 604800,
  5416         "month"  => 2592000,
  5417         "year"   => 31557600
  5418         );
  5419 
  5420     if( $occurrences != 0 )
  5421     {
  5422         $rate = ( int )( $timespan / $occurrences );
  5423         $adjustedRate = $occurrences + 1;
  5424         $time_unit = 'second';
  5425 
  5426         $found_one = false;
  5427 
  5428         foreach( $common_time as $unit=>$seconds )
  5429         {
  5430             if( $rate > $seconds )
  5431             {
  5432                 $foo = ( int )(( $rate / $seconds ) + .5 );
  5433 
  5434                 if(( $foo < $occurrences ) && ( $foo > 0 ))
  5435                 {
  5436                     $adjustedRate = $foo;
  5437                     $time_unit = $unit;
  5438                 }
  5439             }
  5440         }
  5441 
  5442         $singular = '1 shout every ' . $adjustedRate . ' ' . $time_unit;
  5443 
  5444         if( $adjustedRate > 1 )
  5445         {
  5446             $singular .= 's';
  5447         }
  5448     }
  5449     else
  5450     {
  5451         $singular = 'No events';
  5452     }
  5453 
  5454     return $singular;
  5455 }
  5456 
  5457 /**
  5458 * Return SQL expression to check for permissions.
  5459 *
  5460 * Creates part of an SQL expression that can be used to request items with the
  5461 * standard set of Geeklog permissions.
  5462 *
  5463 * @param        string      $type     part of the SQL expr. e.g. 'WHERE', 'AND'
  5464 * @param        int         $u_id     user id or 0 = current user
  5465 * @param        int         $access   access to check for (2=read, 3=r&write)
  5466 * @param        string      $table    table name if ambiguous (e.g. in JOINs)
  5467 * @return       string      SQL expression string (may be empty)
  5468 *
  5469 */
  5470 function COM_getPermSQL( $type = 'WHERE', $u_id = 0, $access = 2, $table = '' )
  5471 {
  5472     global $_USER, $_GROUPS;
  5473 
  5474     if( !empty( $table ))
  5475     {
  5476         $table .= '.';
  5477     }
  5478     if( $u_id <= 0)
  5479     {
  5480         if( COM_isAnonUser() )
  5481         {
  5482             $uid = 1;
  5483         }
  5484         else
  5485         {
  5486             $uid = $_USER['uid'];
  5487         }
  5488     }
  5489     else
  5490     {
  5491         $uid = $u_id;
  5492     }
  5493 
  5494     $UserGroups = array();
  5495     if(( empty( $_USER['uid'] ) && ( $uid == 1 )) || ( $uid == $_USER['uid'] ))
  5496     {
  5497         if( empty( $_GROUPS ))
  5498         {
  5499             $_GROUPS = SEC_getUserGroups( $uid );
  5500         }
  5501         $UserGroups = $_GROUPS;
  5502     }
  5503     else
  5504     {
  5505         $UserGroups = SEC_getUserGroups( $uid );
  5506     }
  5507 
  5508     if( empty( $UserGroups ))
  5509     {
  5510         // this shouldn't really happen, but if it does, handle user
  5511         // like an anonymous user
  5512         $uid = 1;
  5513     }
  5514 
  5515     if( SEC_inGroup( 'Root', $uid ))
  5516     {
  5517         return '';
  5518     }
  5519 
  5520     $sql = ' ' . $type . ' (';
  5521 
  5522     if( $uid > 1 )
  5523     {
  5524         $sql .= "(({$table}owner_id = '{$uid}') AND ({$table}perm_owner >= $access)) OR ";
  5525 
  5526         $sql .= "(({$table}group_id IN (" . implode( ',', $UserGroups )
  5527              . ")) AND ({$table}perm_group >= $access)) OR ";
  5528         $sql .= "({$table}perm_members >= $access)";
  5529     }
  5530     else
  5531     {
  5532         $sql .= "{$table}perm_anon >= $access";
  5533     }
  5534 
  5535     $sql .= ')';
  5536 
  5537     return $sql;
  5538 }
  5539 
  5540 /**
  5541 * Return SQL expression to check for allowed topics.
  5542 *
  5543 * Creates part of an SQL expression that can be used to only request stories
  5544 * from topics to which the user has access to.
  5545 *
  5546 * Note that this function does an SQL request, so you should cache
  5547 * the resulting SQL expression if you need it more than once.
  5548 *
  5549 * @param    string  $type   part of the SQL expr. e.g. 'WHERE', 'AND'
  5550 * @param    int     $u_id   user id or 0 = current user
  5551 * @param    string  $table  table name if ambiguous (e.g. in JOINs)
  5552 * @return   string          SQL expression string (may be empty)
  5553 *
  5554 */
  5555 function COM_getTopicSQL( $type = 'WHERE', $u_id = 0, $table = '' )
  5556 {
  5557     global $_TABLES, $_USER, $_GROUPS;
  5558 
  5559     $topicsql = ' ' . $type . ' ';
  5560 
  5561     if( !empty( $table ))
  5562     {
  5563         $table .= '.';
  5564     }
  5565 
  5566     $UserGroups = array();
  5567     if(( $u_id <= 0 ) || ( isset( $_USER['uid'] ) && $u_id == $_USER['uid'] ))
  5568     {
  5569         if( !COM_isAnonUser() )
  5570         {
  5571             $uid = $_USER['uid'];
  5572         }
  5573         else
  5574         {
  5575             $uid = 1;
  5576         }
  5577         $UserGroups = $_GROUPS;
  5578     }
  5579     else
  5580     {
  5581         $uid = $u_id;
  5582         $UserGroups = SEC_getUserGroups( $uid );
  5583     }
  5584 
  5585     if( empty( $UserGroups ))
  5586     {
  5587         // this shouldn't really happen, but if it does, handle user
  5588         // like an anonymous user
  5589         $uid = 1;
  5590     }
  5591 
  5592     if( SEC_inGroup( 'Root', $uid ))
  5593     {
  5594         return '';
  5595     }
  5596 
  5597     $result = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
  5598                         . COM_getPermSQL( 'WHERE', $uid ));
  5599     $tids = array();
  5600     while( $T = DB_fetchArray( $result ))
  5601     {
  5602         $tids[] = $T['tid'];
  5603     }
  5604 
  5605     if( count( $tids ) > 0 )
  5606     {
  5607         $topicsql .= "({$table}tid IN ('" . implode( "','", $tids ) . "'))";
  5608     }
  5609     else
  5610     {
  5611         $topicsql .= '0';
  5612     }
  5613 
  5614     return $topicsql;
  5615 }
  5616 
  5617 /**
  5618 * Strip slashes from a string only when magic_quotes_gpc = on.
  5619 *
  5620 * @param   string  $text  The text
  5621 * @return  string  The text, possibly without slashes.
  5622 */
  5623 function COM_stripslashes( $text )
  5624 {
  5625     if( get_magic_quotes_gpc() == 1 )
  5626     {
  5627         return( stripslashes( $text ));
  5628     }
  5629 
  5630     return( $text );
  5631 }
  5632 
  5633 /**
  5634 * Filter parameters passed per GET (URL) or POST.
  5635 *
  5636 * @param    string    $parameter   the parameter to test
  5637 * @param    boolean   $isnumeric   true if $parameter is supposed to be numeric
  5638 * @return   string    the filtered parameter (may now be empty or 0)
  5639 * @see COM_applyBasicFilter
  5640 *
  5641 */
  5642 function COM_applyFilter( $parameter, $isnumeric = false )
  5643 {
  5644     $p = COM_stripslashes($parameter);
  5645 
  5646     return COM_applyBasicFilter($p, $isnumeric);
  5647 }
  5648 
  5649 /**
  5650 * Filter parameters
  5651 *
  5652 * NOTE:     Use this function instead of COM_applyFilter for parameters
  5653 *           _not_ coming in through a GET or POST request.
  5654 *
  5655 * @param    string    $parameter   the parameter to test
  5656 * @param    boolean   $isnumeric   true if $parameter is supposed to be numeric
  5657 * @return   string    the filtered parameter (may now be empty or 0)
  5658 * @see COM_applyFilter
  5659 *
  5660 */
  5661 function COM_applyBasicFilter( $parameter, $isnumeric = false )
  5662 {
  5663     $log_manipulation = false; // set to true to log when the filter applied
  5664 
  5665     $p = strip_tags( $parameter );
  5666     $p = COM_killJS( $p ); // doesn't help a lot right now, but still ...
  5667 
  5668     if( $isnumeric )
  5669     {
  5670         // Note: PHP's is_numeric() accepts values like 4e4 as numeric
  5671         if( !is_numeric( $p ) || ( preg_match( '/^-?\d+$/', $p ) == 0 ))
  5672         {
  5673             $p = 0;
  5674         }
  5675     }
  5676     else
  5677     {
  5678         $p = preg_replace( '/\/\*.*/', '', $p );
  5679         $pa = explode( "'", $p );
  5680         $pa = explode( '"', $pa[0] );
  5681         $pa = explode( '`', $pa[0] );
  5682         $pa = explode( ';', $pa[0] );
  5683         $pa = explode( ',', $pa[0] );
  5684         $pa = explode( '\\', $pa[0] );
  5685         $p = $pa[0];
  5686     }
  5687 
  5688     if( $log_manipulation )
  5689     {
  5690         if( strcmp( $p, $parameter ) != 0 )
  5691         {
  5692             COM_errorLog( "Filter applied: >> $parameter << filtered to $p [IP {$_SERVER['REMOTE_ADDR']}]", 1);
  5693         }
  5694     }
  5695 
  5696     return $p;
  5697 }
  5698 
  5699 /**
  5700 * Sanitize a URL
  5701 *
  5702 * @param    string  $url                URL to sanitized
  5703 * @param    array   $allowed_protocols  array of allowed protocols
  5704 * @param    string  $default_protocol   replacement protocol (default: http)
  5705 * @return   string                      sanitized URL
  5706 *
  5707 */
  5708 function COM_sanitizeUrl( $url, $allowed_protocols = '', $default_protocol = '' )
  5709 {
  5710     global $_CONF;
  5711 
  5712     if( empty( $allowed_protocols ))
  5713     {
  5714         $allowed_protocols = $_CONF['allowed_protocols'];
  5715     }
  5716     else if( !is_array( $allowed_protocols ))
  5717     {
  5718         $allowed_protocols = array( $allowed_protocols );
  5719     }
  5720 
  5721     if( empty( $default_protocol ))
  5722     {
  5723         $default_protocol = 'http:';
  5724     }
  5725     else if( substr( $default_protocol, -1 ) != ':' )
  5726     {
  5727         $default_protocol .= ':';
  5728     }
  5729 
  5730     $url = strip_tags( $url );
  5731     if( !empty( $url ))
  5732     {
  5733         $pos = MBYTE_strpos( $url, ':' );
  5734         if( $pos === false )
  5735         {
  5736             $url = $default_protocol . '//' . $url;
  5737         }
  5738         else
  5739         {
  5740             $protocol = MBYTE_substr( $url, 0, $pos + 1 );
  5741             $found_it = false;
  5742             foreach( $allowed_protocols as $allowed )
  5743             {
  5744                 if( substr( $allowed, -1 ) != ':' )
  5745                 {
  5746                     $allowed .= ':';
  5747                 }
  5748                 if( $protocol == $allowed )
  5749                 {
  5750                     $found_it = true;
  5751                     break;
  5752                 }
  5753             }
  5754             if( !$found_it )
  5755             {
  5756                 $url = $default_protocol . MBYTE_substr( $url, $pos + 1 );
  5757             }
  5758         }
  5759     }
  5760 
  5761     return $url;
  5762 }
  5763 
  5764 /**
  5765 * Ensure an ID contains only alphanumeric characters, dots, dashes, or underscores
  5766 *
  5767 * @param    string  $id     the ID to sanitize
  5768 * @param    boolean $new_id true = create a new ID in case we end up with an empty string
  5769 * @return   string          the sanitized ID
  5770 */
  5771 function COM_sanitizeID( $id, $new_id = true )
  5772 {
  5773     $id = str_replace( ' ', '', $id );
  5774     $id = str_replace( array( '/', '\\', ':', '+' ), '-', $id );
  5775     $id = preg_replace( '/[^a-zA-Z0-9\-_\.]/', '', $id );
  5776     if( empty( $id ) && $new_id )
  5777     {
  5778         $id = COM_makesid();
  5779     }
  5780 
  5781     return $id;
  5782 }
  5783 
  5784 /**
  5785 * Sanitize a filename.
  5786 *
  5787 * NOTE:     This function is pretty strict in what it allows. Meant to be used
  5788 *           for files to be included where part of the filename is dynamic.
  5789 *
  5790 * @param    string  $filename   the filename to clean up
  5791 * @param    boolean $allow_dots whether to allow dots in the filename or not
  5792 * @return   string              sanitized filename
  5793 *
  5794 */
  5795 function COM_sanitizeFilename($filename, $allow_dots = false)
  5796 {
  5797     if ($allow_dots) {
  5798         $filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $filename);
  5799         $filename = str_replace('..', '', $filename);
  5800     } else {
  5801         $filename = preg_replace('/[^a-zA-Z0-9\-_]/', '', $filename);
  5802     }
  5803 
  5804     return $filename;
  5805 }
  5806 
  5807 /**
  5808 * Detect links in a plain-ascii text and turn them into clickable links.
  5809 * Will detect links starting with "http:", "https:", "ftp:", and "www.".
  5810 *
  5811 * @param    string    $text     the (plain-ascii) text string
  5812 * @return   string    the same string, with links enclosed in <a>...</a> tags
  5813 *
  5814 */
  5815 function COM_makeClickableLinks( $text )
  5816 {
  5817     global $_CONF;
  5818 
  5819     if (! $_CONF['clickable_links']) {
  5820         return $text;
  5821     }
  5822 
  5823     // These regular expressions will work for this purpuse, but
  5824     // they should NOT be used for validating links.
  5825 
  5826     // matches anything starting with http:// or https:// or ftp:// or ftps://
  5827     $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:(?:ht|f)tps?:\/{2})(?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&amp;)?)+)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
  5828     $replace[] = "COM_makeClickableLinksCallback('', '\\1')";
  5829 
  5830     // matches anything containing a top level domain: xxx.com or xxx.yyy.net/stuff.php or xxx.yyy.zz
  5831     // list taken from: http://en.wikipedia.org/wiki/List_of_Internet_TLDs
  5832     $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:[a-z0-9]+\.)*[a-z0-9]+\.(?:aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z]{2})(?:[\/?#](?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&amp;)?)*)?)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
  5833     $replace[] = "COM_makeClickableLinksCallback('http://', '\\1')";
  5834 
  5835     $text = preg_replace( $regex, $replace, $text );
  5836 
  5837     return $text;
  5838 }
  5839 
  5840 /**
  5841 * Callback function to help format links in COM_makeClickableLinks
  5842 *
  5843 * @param    string  $http   set to 'http://' when not already in the url
  5844 * @param    string  $link   the url
  5845 * @return   string          link enclosed in <a>...</a> tags
  5846 *
  5847 */
  5848 function COM_makeClickableLinksCallback( $http, $link )
  5849 {
  5850     $text = COM_truncate( $link, 50, '...', '10' );
  5851 
  5852     return "<a href=\"$http$link\">$text</a>";
  5853 }
  5854 
  5855 /**
  5856 * Undo the conversion of URLs to clickable links (in plain text posts),
  5857 * e.g. so that we can present the user with the post as they entered them.
  5858 *
  5859 * @param    string  $text   story text
  5860 * @return   string          story text without links
  5861 *
  5862 */
  5863 function COM_undoClickableLinks( $text )
  5864 {
  5865     $text = preg_replace( '/<a href="([^"]*)">([^<]*)<\/a>/', '\1', $text );
  5866 
  5867     return $text;
  5868 }
  5869 
  5870 /**
  5871 * Highlight the words from a search query in a given text string.
  5872 *
  5873 * @param    string  $text   the text
  5874 * @param    string  $query  the search query
  5875 * @param    string  $class  html class to use to highlight
  5876 * @return   string          the text with highlighted search words
  5877 *
  5878 */
  5879 function COM_highlightQuery( $text, $query, $class = 'highlight' )
  5880 {
  5881     // escape PCRE special characters
  5882     $query = preg_quote($query, '/');
  5883 
  5884     $mywords = explode(' ', $query);
  5885     foreach ($mywords as $searchword)
  5886     {
  5887         if (!empty($searchword))
  5888         {
  5889             $before = "/(?!(?:[^<]+>|[^>]+<\/a>))\b";
  5890             $after = "\b/i";
  5891             if ($searchword <> utf8_encode($searchword)) {
  5892                  if (@preg_match('/^\pL$/u', urldecode('%C3%B1'))) { // Unicode property support
  5893                       $before = "/(?<!\p{L})";
  5894                       $after = "(?!\p{L})/u";
  5895                  } else {
  5896                       $before = "/";
  5897                       $after = "/u";
  5898                  }
  5899             }
  5900             $text = preg_replace($before . $searchword . $after, "<span class=\"$class\">\\0</span>", '<!-- x -->' . $text . '<!-- x -->' );
  5901         }
  5902     }
  5903     return $text;
  5904 }
  5905 
  5906 /**
  5907 * Determines the difference between two dates.
  5908 *
  5909 * This will takes either unixtimestamps or English dates as input and will
  5910 * automatically do the date diff on the more recent of the two dates (e.g. the
  5911 * order of the two dates given doesn't matter).
  5912 *
  5913 * @author Tony Bibbs, tony DOT bibbs AT iowa DOT gov
  5914 * @access public
  5915 * @param string $interval Can be:
  5916 * y = year
  5917 * m = month
  5918 * w = week
  5919 * h = hours
  5920 * i = minutes
  5921 * s = seconds
  5922 * @param string|int $date1 English date (e.g. 10 Dec 2004) or unixtimestamp
  5923 * @param string|int $date2 English date (e.g. 10 Dec 2004) or unixtimestamp
  5924 * @return int Difference of the two dates in the unit of time indicated by the interval
  5925 *
  5926 */
  5927 function COM_dateDiff( $interval, $date1, $date2 )
  5928 {
  5929     // Convert dates to timestamps, if needed.
  5930     if( !is_numeric( $date1 ))
  5931     {
  5932         $date1 = strtotime( $date1 );
  5933     }
  5934 
  5935     if( !is_numeric( $date2 ))
  5936     {
  5937         $date2 = strtotime( $date2 );
  5938     }
  5939 
  5940     // Function roughly equivalent to the ASP "DateDiff" function
  5941     if( $date2 > $date1 )
  5942     {
  5943         $seconds = $date2 - $date1;
  5944     }
  5945     else
  5946     {
  5947         $seconds = $date1 - $date2;
  5948     }
  5949 
  5950     switch( $interval )
  5951     {
  5952         case "y":
  5953             list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
  5954             list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
  5955             $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
  5956             $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
  5957             $diff = $year2 - $year1;
  5958             if($month1 > $month2) {
  5959                 $diff -= 1;
  5960             } elseif($month1 == $month2) {
  5961                 if($day1 > $day2) {
  5962                     $diff -= 1;
  5963                 } elseif($day1 == $day2) {
  5964                     if($time1 > $time2) {
  5965                         $diff -= 1;
  5966                     }
  5967                 }
  5968             }
  5969             break;
  5970         case "m":
  5971             list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
  5972             list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
  5973             $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
  5974             $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
  5975             $diff = ($year2 * 12 + $month2) - ($year1 * 12 + $month1);
  5976             if($day1 > $day2) {
  5977                 $diff -= 1;
  5978             } elseif($day1 == $day2) {
  5979                 if($time1 > $time2) {
  5980                     $diff -= 1;
  5981                 }
  5982             }
  5983             break;
  5984         case "w":
  5985             // Only simple seconds calculation needed from here on
  5986             $diff = floor($seconds / 604800);
  5987             break;
  5988          case "d":
  5989             $diff = floor($seconds / 86400);
  5990             break;
  5991         case "h":
  5992             $diff = floor($seconds / 3600);
  5993             break;
  5994         case "i":
  5995             $diff = floor($seconds / 60);
  5996             break;
  5997         case "s":
  5998             $diff = $seconds;
  5999             break;
  6000     }
  6001 
  6002     return $diff;
  6003 }
  6004 
  6005 /**
  6006 * Try to figure out our current URL, including all parameters.
  6007 *
  6008 * This is an ugly hack since there's no single variable that returns what
  6009 * we want and the variables used here may not be available on all servers
  6010 * and / or setups.
  6011 *
  6012 * Seems to work on Apache (1.3.x and 2.x), IIS, and Zeus ...
  6013 *
  6014 * @return   string  complete URL, e.g. 'http://www.example.com/blah.php?foo=bar'
  6015 *
  6016 */
  6017 function COM_getCurrentURL()
  6018 {
  6019     global $_CONF;
  6020 
  6021     $thisUrl = '';
  6022 
  6023     if( empty( $_SERVER['SCRIPT_URI'] ))
  6024     {
  6025         if( !empty( $_SERVER['DOCUMENT_URI'] ))
  6026         {
  6027             $thisUrl = $_SERVER['DOCUMENT_URI'];
  6028         }
  6029     }
  6030     else
  6031     {
  6032         $thisUrl = $_SERVER['SCRIPT_URI'];
  6033     }
  6034     if( !empty( $thisUrl ) && !empty( $_SERVER['QUERY_STRING'] ))
  6035     {
  6036         $thisUrl .= '?' . $_SERVER['QUERY_STRING'];
  6037     }
  6038     if( empty( $thisUrl ))
  6039     {
  6040         $requestUri = $_SERVER['REQUEST_URI'];
  6041         if( empty( $_SERVER['REQUEST_URI'] ))
  6042         {
  6043             // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
  6044             if( empty( $_SERVER['PATH_INFO'] ))
  6045             {
  6046                 $requestUri = $_SERVER['SCRIPT_NAME'];
  6047             }
  6048             else
  6049             {
  6050                 $requestUri = $_SERVER['PATH_INFO'];
  6051             }
  6052             if( !empty( $_SERVER['QUERY_STRING'] ))
  6053             {
  6054                 $requestUri .= '?' . $_SERVER['QUERY_STRING'];
  6055             }
  6056         }
  6057 
  6058         $firstslash = strpos( $_CONF['site_url'], '/' );
  6059         if( $firstslash === false )
  6060         {
  6061             // special case - assume it's okay
  6062             $thisUrl = $_CONF['site_url'] . $requestUri;
  6063         }
  6064         else if( $firstslash + 1 == strrpos( $_CONF['site_url'], '/' ))
  6065         {
  6066             // site is in the document root
  6067             $thisUrl = $_CONF['site_url'] . $requestUri;
  6068         }
  6069         else
  6070         {
  6071             // extract server name first
  6072             $pos = strpos( $_CONF['site_url'], '/', $firstslash + 2 );
  6073             $thisUrl = substr( $_CONF['site_url'], 0, $pos ) . $requestUri;
  6074         }
  6075     }
  6076 
  6077     return $thisUrl;
  6078 }
  6079 
  6080 /**
  6081 * Check if we're on Geeklog's index page.
  6082 *
  6083 * See if we're on the main index page (first page, no topics selected).
  6084 *
  6085 * @return   boolean     true = we're on the frontpage, false = we're not
  6086 *
  6087 */
  6088 function COM_onFrontpage()
  6089 {
  6090     global $_CONF, $topic, $page, $newstories;
  6091 
  6092     // Note: We can't use $PHP_SELF here since the site may not be in the
  6093     // DocumentRoot
  6094     $onFrontpage = false;
  6095 
  6096     // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
  6097     if( empty( $_SERVER['PATH_INFO'] ))
  6098     {
  6099         $scriptName = $_SERVER['SCRIPT_NAME'];
  6100     }
  6101     else
  6102     {
  6103         $scriptName = $_SERVER['PATH_INFO'];
  6104     }
  6105 
  6106     preg_match( '/\/\/[^\/]*(.*)/', $_CONF['site_url'], $pathonly );
  6107     if(( $scriptName == $pathonly[1] . '/index.php' ) &&
  6108             empty( $topic ) && ( $page == 1 ) && !$newstories )
  6109     {
  6110         $onFrontpage = true;
  6111     }
  6112 
  6113     return $onFrontpage;
  6114 }
  6115 
  6116 /**
  6117 * Check if we're on Geeklog's index page [deprecated]
  6118 *
  6119 * Note that this function returns FALSE when we're on the index page. Due to
  6120 * the inverted return values, it has been deprecated and is only provided for
  6121 * backward compatibility - use COM_onFrontpage() instead.
  6122 *
  6123 * @deprecated since Geeklog 1.4.1
  6124 * @see COM_onFrontpage
  6125 *
  6126 */
  6127 function COM_isFrontpage()
  6128 {
  6129     return !COM_onFrontpage();
  6130 }
  6131 
  6132 /**
  6133 * Converts a number for output into a formatted number with thousands-
  6134 * separator, comma-separator and fixed decimals if necessary
  6135 *
  6136 * @param        float        $number        Number that will be formatted
  6137 * @return        string                        formatted number
  6138 *
  6139 */
  6140 function COM_numberFormat( $number )
  6141 {
  6142     global $_CONF;
  6143 
  6144     if( $number - floor( $number ) > 0 ) // number has decimals
  6145     {
  6146         $dc = $_CONF['decimal_count'];
  6147     }
  6148     else
  6149     {
  6150         $dc = 0;
  6151     }
  6152     $ts = $_CONF['thousand_separator'];
  6153     $ds = $_CONF['decimal_separator'];
  6154 
  6155     return number_format( $number, $dc, $ds, $ts );
  6156 }
  6157 
  6158 /**
  6159 * Convert a text based date YYYY-MM-DD to a unix timestamp integer value
  6160 *
  6161 * @param    string  $date   Date in the format YYYY-MM-DD
  6162 * @param    string  $time   Option time in the format HH:MM::SS
  6163 * @return   int             UNIX Timestamp
  6164 */
  6165 function COM_convertDate2Timestamp( $date, $time = '' )
  6166 {
  6167     $atoks = array();
  6168     $btoks = array();
  6169 
  6170     // Breakup the string using either a space, fwd slash, dash, bkwd slash or
  6171     // colon as a delimiter
  6172     $atok = strtok( $date, ' /-\\:' );
  6173     while( $atok !== FALSE )
  6174     {
  6175         $atoks[] = $atok;
  6176         $atok = strtok( ' /-\\:' );  // get the next token
  6177     }
  6178 
  6179     for( $i = 0; $i < 3; $i++ )
  6180     {
  6181         if( !isset( $atoks[$i] ) || !is_numeric( $atoks[$i] ))
  6182         {
  6183             $atoks[$i] = 0;
  6184         }
  6185     }
  6186 
  6187     if( $time == '' )
  6188     {
  6189         $timestamp = mktime( 0, 0, 0, $atoks[1], $atoks[2], $atoks[0] );
  6190     }
  6191     else
  6192     {
  6193         $btok = strtok( $time, ' /-\\:' );
  6194         while( $btok !== FALSE )
  6195         {
  6196             $btoks[] = $btok;
  6197             $btok = strtok( ' /-\\:' );
  6198         }
  6199 
  6200         for( $i = 0; $i < 3; $i++ )
  6201         {
  6202             if( !isset( $btoks[$i] ) || !is_numeric( $btoks[$i] ))
  6203             {
  6204                 $btoks[$i] = 0;
  6205             }
  6206         }
  6207 
  6208         $timestamp = mktime( $btoks[0], $btoks[1], $btoks[2],
  6209                              $atoks[1], $atoks[2], $atoks[0] );
  6210     }
  6211 
  6212     return $timestamp;
  6213 }
  6214 
  6215 /**
  6216 * Get the HTML for an image with height & width
  6217 *
  6218 * @param    string  $file   full path to the file
  6219 * @return   string          html that will be included in the img-tag
  6220 */
  6221 function COM_getImgSizeAttributes( $file )
  6222 {
  6223     $sizeattributes = '';
  6224 
  6225     if( file_exists( $file ))
  6226     {
  6227         $dimensions = getimagesize( $file );
  6228         if( !empty( $dimensions[0] ) AND !empty( $dimensions[1] ))
  6229         {
  6230             $sizeattributes = 'width="' . $dimensions[0]
  6231                             . '" height="' . $dimensions[1] . '" ';
  6232         }
  6233     }
  6234 
  6235     return $sizeattributes;
  6236 }
  6237 
  6238 /**
  6239 * Display a message and abort
  6240 *
  6241 * NOTE: Displays the message and aborts the script.
  6242 *
  6243 * @param    int     $msg            message number
  6244 * @param    string  $plugin         plugin name, if applicable
  6245 * @param    int     $http_status    HTTP status code to send with the message
  6246 * @param    string  $http_text      Textual version of the HTTP status code
  6247 *
  6248 */
  6249 function COM_displayMessageAndAbort( $msg, $plugin = '', $http_status = 200, $http_text = 'OK')
  6250 {
  6251     $display = COM_siteHeader( 'menu' )
  6252              . COM_showMessage( $msg, $plugin )
  6253              . COM_siteFooter( true );
  6254 
  6255     if( $http_status != 200 )
  6256     {
  6257         header( "HTTP/1.1 $http_status $http_text" );
  6258         header( "Status: $http_status $http_text" );
  6259     }
  6260     echo $display;
  6261     exit;
  6262 }
  6263 
  6264 /**
  6265 * Return full URL of a topic icon
  6266 *
  6267 * @param    string  $imageurl   (relative) topic icon URL
  6268 * @return   string              Full URL
  6269 *
  6270 */
  6271 function COM_getTopicImageUrl( $imageurl )
  6272 {
  6273     global $_CONF, $_THEME_URL;
  6274 
  6275     $iconurl = '';
  6276 
  6277     if( !empty( $imageurl ))
  6278     {
  6279         if( isset( $_THEME_URL ))
  6280         {
  6281             $iconurl = $_THEME_URL . $imageurl;
  6282         }
  6283         else
  6284         {
  6285             $stdImageLoc = true;
  6286             if( !strstr( $_CONF['path_images'], $_CONF['path_html'] ))
  6287             {
  6288                 $stdImageLoc = false;
  6289             }
  6290 
  6291             if( $stdImageLoc )
  6292             {
  6293                 $iconurl = $_CONF['site_url'] . $imageurl;
  6294             }
  6295             else
  6296             {
  6297                 $t = explode( '/', $imageurl );
  6298                 $topicicon = $t[count( $t ) - 1];
  6299                 $iconurl = $_CONF['site_url']
  6300                          . '/getimage.php?mode=topics&amp;image=' . $topicicon;
  6301             }
  6302         }
  6303     }
  6304 
  6305     return $iconurl;
  6306 }
  6307 
  6308 /**
  6309  * Create an HTML link
  6310  *
  6311  * @param   string  $content    the object to be linked (text, image etc)
  6312  * @param   string  $url        the URL the link will point to
  6313  * @param   array   $attr       an array of optional attributes for the link
  6314  *                              for example array('title' => 'whatever');
  6315  * @return  string              the HTML link
  6316  */
  6317 function COM_createLink($content, $url, $attr = array())
  6318 {
  6319     $retval = '';
  6320 
  6321     $attr_str = 'href="' . $url . '"';
  6322     foreach ($attr as $key => $value) {
  6323         $attr_str .= " $key=\"$value\"";
  6324     }
  6325     $retval .= "<a $attr_str>$content</a>";
  6326 
  6327     return $retval;
  6328 }
  6329 
  6330 /**
  6331  * Create an HTML img
  6332  *
  6333  * @param   string  $url        the URL of the image, either starting with
  6334  *                              http://... or $_CONF['layout_url'] is prepended
  6335  * @param   string  $alt        the 'alt'-tag of the image
  6336  * @param   array   $attr       an array of optional attributes for the link
  6337  *                              for example array('title' => 'whatever');
  6338  * @return  string              the HTML img
  6339  */
  6340 function COM_createImage($url, $alt = "", $attr = array())
  6341 {
  6342     global $_CONF;
  6343 
  6344     $retval = '';
  6345 
  6346     if (preg_match("/^(https?):/", $url) !== 1) {
  6347         $url = $_CONF['layout_url'] . $url;
  6348     }
  6349     $attr_str = 'src="' . $url . '"';
  6350 
  6351     foreach ($attr as $key => $value) {
  6352         $attr_str .= " $key=\"$value\"";
  6353     }
  6354 
  6355     $retval = "<img $attr_str alt=\"$alt\"" . XHTML . ">";
  6356 
  6357     return $retval;
  6358 }
  6359 
  6360 /**
  6361 * Try to determine the user's preferred language by looking at the
  6362 * "Accept-Language" header sent by their browser (assuming they bothered
  6363 * to select a preferred language there).
  6364 *
  6365 * Sample header: Accept-Language: en-us,en;q=0.7,de-de;q=0.3
  6366 *
  6367 * @return   string  name of the language file to use or an empty string
  6368 * @todo     Bugs: Does not take the quantity ('q') parameter into account,
  6369 *           but only looks at the order of language codes.
  6370 *
  6371 */
  6372 function COM_getLanguageFromBrowser()
  6373 {
  6374     global $_CONF;
  6375 
  6376     $retval = '';
  6377 
  6378     if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
  6379         $accept = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
  6380         foreach ($accept as $l) {
  6381             $l = explode(';', trim($l));
  6382             $l = $l[0];
  6383             if (array_key_exists($l, $_CONF['language_files'])) {
  6384                 $retval = $_CONF['language_files'][$l];
  6385                 break;
  6386             } else {
  6387                 $l = explode('-', $l);
  6388                 $l = $l[0];
  6389                 if (array_key_exists($l, $_CONF['language_files'])) {
  6390                     $retval = $_CONF['language_files'][$l];
  6391                     break;
  6392                 }
  6393             }
  6394         }
  6395     }
  6396 
  6397     return $retval;
  6398 }
  6399 
  6400 /**
  6401 * Determine current language
  6402 *
  6403 * @return   string  name of the language file (minus the '.php' extension)
  6404 *
  6405 */
  6406 function COM_getLanguage()
  6407 {
  6408     global $_CONF, $_USER;
  6409 
  6410     $langfile = '';
  6411 
  6412     if (!empty($_USER['language'])) {
  6413         $langfile = $_USER['language'];
  6414     } elseif (!empty($_COOKIE[$_CONF['cookie_language']])) {
  6415         $langfile = $_COOKIE[$_CONF['cookie_language']];
  6416     } elseif (isset($_CONF['languages'])) {
  6417         $langfile = COM_getLanguageFromBrowser();
  6418     }
  6419 
  6420     $langfile = COM_sanitizeFilename($langfile);
  6421     if (!empty($langfile)) {
  6422         if (is_file($_CONF['path_language'] . $langfile . '.php')) {
  6423             return $langfile;
  6424         }
  6425     }
  6426 
  6427     // if all else fails, return the default language
  6428     return $_CONF['language'];
  6429 }
  6430 
  6431 /**
  6432 * Determine the ID to use for the current language
  6433 *
  6434 * The $_CONF['language_files'] array maps language IDs to language file names.
  6435 * This function returns the language ID for a certain language file, to be
  6436 * used in language-dependent URLs.
  6437 *
  6438 * @param    string  $language   current language file name (optional)
  6439 * @return   string              language ID, e.g 'en'; empty string on error
  6440 *
  6441 */
  6442 function COM_getLanguageId($language = '')
  6443 {
  6444     global $_CONF;
  6445 
  6446     if (empty($language)) {
  6447         $language = COM_getLanguage();
  6448     }
  6449 
  6450     $lang_id = '';
  6451     if (isset($_CONF['language_files'])) {
  6452         $lang_id = array_search($language, $_CONF['language_files']);
  6453 
  6454         if ($lang_id === false) {
  6455             // that looks like a misconfigured $_CONF['language_files'] array
  6456             COM_errorLog('Language "' . $language . '" not found in $_CONF[\'language_files\'] array!');
  6457 
  6458             $lang_id = ''; // not much we can do here ...
  6459         }
  6460     }
  6461 
  6462     return $lang_id;
  6463 }
  6464 
  6465 /**
  6466 * Return SQL expression to request language-specific content
  6467 *
  6468 * Creates part of an SQL expression that can be used to request items in the
  6469 * current language only.
  6470 *
  6471 * @param    string  $field  name of the "id" field, e.g. 'sid' for stories
  6472 * @param    string  $type   part of the SQL expression, e.g. 'WHERE', 'AND'
  6473 * @param    string  $table  table name if ambiguous, e.g. in JOINs
  6474 * @return   string          SQL expression string (may be empty)
  6475 *
  6476 */
  6477 function COM_getLangSQL( $field, $type = 'WHERE', $table = '' )
  6478 {
  6479     global $_CONF;
  6480 
  6481     $sql = '';
  6482 
  6483     if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
  6484     {
  6485         if( !empty( $table ))
  6486         {
  6487             $table .= '.';
  6488         }
  6489 
  6490         $lang_id = COM_getLanguageId();
  6491 
  6492         if( !empty( $lang_id ))
  6493         {
  6494             $sql = ' ' . $type . " ({$table}$field LIKE '%\\_$lang_id')";
  6495         }
  6496     }
  6497 
  6498     return $sql;
  6499 }
  6500 
  6501 /**
  6502 * Provide a block to switch languages
  6503 *
  6504 * Provides a drop-down menu (or simple link, if you only have two languages)
  6505 * to switch languages. This can be used as a PHP block or called from within
  6506 * your theme's header.thtml:
  6507 * <code>
  6508 * <?php print phpblock_switch_language(); ?>
  6509 * </code>
  6510 *
  6511 * @return   string  HTML for drop-down or link to switch languages
  6512 *
  6513 */
  6514 function phpblock_switch_language()
  6515 {
  6516     global $_CONF;
  6517 
  6518     $retval = '';
  6519 
  6520     if( empty( $_CONF['languages'] ) || empty( $_CONF['language_files'] ) ||
  6521           ( count( $_CONF['languages'] ) != count( $_CONF['language_files'] )))
  6522     {
  6523         return $retval;
  6524     }
  6525 
  6526     $lang = COM_getLanguage();
  6527     $langId = COM_getLanguageId( $lang );
  6528 
  6529     if( count( $_CONF['languages'] ) == 2 )
  6530     {
  6531         foreach( $_CONF['languages'] as $key => $value )
  6532         {
  6533             if( $key != $langId )
  6534             {
  6535                 $newLang = $value;
  6536                 $newLangId = $key;
  6537                 break;
  6538             }
  6539         }
  6540 
  6541         $switchUrl = COM_buildUrl( $_CONF['site_url'] . '/switchlang.php?lang='
  6542                                    . $newLangId );
  6543         $retval .= COM_createLink($newLang, $switchUrl);
  6544     }
  6545     else
  6546     {
  6547         $retval .= '<form name="change" action="'. $_CONF['site_url']
  6548                 . '/switchlang.php" method="get">' . LB;
  6549         $retval .= '<input type="hidden" name="oldlang" value="' . $langId
  6550                 . '"' . XHTML . '>' . LB;
  6551 
  6552         $retval .= '<select onchange="change.submit()" name="lang">';
  6553         foreach( $_CONF['languages'] as $key => $value )
  6554         {
  6555             if( $lang == $_CONF['language_files'][$key] )
  6556             {
  6557                 $selected = ' selected="selected"';
  6558             }
  6559             else
  6560             {
  6561                 $selected = '';
  6562             }
  6563             $retval .= '<option value="' . $key . '"' . $selected . '>'
  6564                     . $value . '</option>' . LB;
  6565         }
  6566         $retval .= '</select>' . LB;
  6567         $retval .= '</form>' . LB;
  6568     }
  6569 
  6570     return $retval;
  6571 }
  6572 
  6573 /**
  6574 * Switch locale settings
  6575 *
  6576 * When multi-language support is enabled, allow overwriting the default locale
  6577 * settings with language-specific settings (date format, etc.). So in addition
  6578 * to $_CONF['date'] you can have a $_CONF['date_en'], $_CONF['date_de'], etc.
  6579 *
  6580 */
  6581 function COM_switchLocaleSettings()
  6582 {
  6583     global $_CONF;
  6584 
  6585     if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
  6586     {
  6587         $overridables = array
  6588         (
  6589           'locale',
  6590           'date', 'daytime', 'shortdate', 'dateonly', 'timeonly',
  6591           'week_start', 'hour_mode',
  6592           'thousand_separator', 'decimal_separator'
  6593         );
  6594 
  6595         $langId = COM_getLanguageId();
  6596         foreach( $overridables as $option )
  6597         {
  6598             if( isset( $_CONF[$option . '_' . $langId] ))
  6599             {
  6600                 $_CONF[$option] = $_CONF[$option . '_' . $langId];
  6601             }
  6602         }
  6603     }
  6604 }
  6605 
  6606 /**
  6607 * Get the name of the current language, minus the character set
  6608 *
  6609 * Strips the character set from $_CONF['language'].
  6610 *
  6611 * @return   string  language name
  6612 *
  6613 */
  6614 function COM_getLanguageName()
  6615 {
  6616     global $_CONF;
  6617 
  6618     $retval = '';
  6619 
  6620     $charset = '_' . strtolower(COM_getCharset());
  6621     if (substr($_CONF['language'], -strlen($charset)) == $charset) {
  6622         $retval = substr($_CONF['language'], 0, -strlen($charset));
  6623     } else {
  6624         $retval = $_CONF['language'];
  6625     }
  6626 
  6627     return $retval;
  6628 }
  6629 
  6630 /**
  6631 * Truncate a string
  6632 *
  6633 * Truncates a string to a max. length and optionally adds a filler string,
  6634 * e.g. '...', to indicate the truncation.
  6635 * This function is multi-byte string aware, based on a patch by Yusuke Sakata.
  6636 *
  6637 * NOTE: The truncated string may be shorter but will never be longer than
  6638 *       $maxlen characters, i.e. the $filler string is taken into account.
  6639 *
  6640 * @param    string  $text       the text string to truncate
  6641 * @param    int     $maxlen     max. number of characters in the truncated string
  6642 * @param    string  $filler     optional filler string, e.g. '...'
  6643 * @param    int     $endchars   number of characters to show after the filler
  6644 * @return   string              truncated string
  6645 *
  6646 */
  6647 function COM_truncate( $text, $maxlen, $filler = '', $endchars = 0 )
  6648 {
  6649     $newlen = $maxlen - MBYTE_strlen( $filler );
  6650     $len = MBYTE_strlen( $text );
  6651     if( $len > $maxlen )
  6652     {
  6653         $text = MBYTE_substr( $text, 0, $newlen - $endchars ) . $filler . MBYTE_substr( $text, $len - $endchars, $endchars );
  6654     }
  6655 
  6656     return $text;
  6657 }
  6658 
  6659 /**
  6660 * Get the current character set
  6661 *
  6662 * Uses (if available, and in this order)
  6663 * - $LANG_CHARSET (from the current language file)
  6664 * - $_CONF['default_charset'] (from siteconfig.php)
  6665 * - 'iso-8859-1' (hard-coded fallback)
  6666 *
  6667 * @return   string      character set, e.g. 'utf-8'
  6668 *
  6669 */
  6670 function COM_getCharset()
  6671 {
  6672     global $_CONF, $LANG_CHARSET;
  6673 
  6674     if( empty( $LANG_CHARSET )) {
  6675         $charset = $_CONF['default_charset'];
  6676         if( empty( $charset )) {
  6677             $charset = 'iso-8859-1';
  6678         }
  6679     } else {
  6680         $charset = $LANG_CHARSET;
  6681     }
  6682 
  6683     return $charset;
  6684 }
  6685 
  6686 /**
  6687   * Handle errors.
  6688   *
  6689   * This function will handle all PHP errors thrown at it, without exposing
  6690   * paths, and hopefully, providing much more information to Root Users than
  6691   * the default white error page.
  6692   *
  6693   * This function will call out to CUSTOM_handleError if it exists, but, be
  6694   * advised, only override this function with a very, very stable function. I'd
  6695   * suggest one that outputs some static, basic HTML.
  6696   *
  6697   * The PHP feature that allows us to do so is documented here:
  6698   * http://uk2.php.net/manual/en/function.set-error-handler.php
  6699   *
  6700   * @param  int     $errno      Error Number.
  6701   * @param  string  $errstr     Error Message.
  6702   * @param  string  $errfile    The file the error was raised in.
  6703   * @param  int     $errline    The line of the file that the error was raised at.
  6704   * @param  array   $errcontext An array that points to the active symbol table at the point the error occurred.
  6705   */
  6706 function COM_handleError($errno, $errstr, $errfile='', $errline=0, $errcontext='')
  6707 {
  6708     global $_CONF, $_USER;
  6709 
  6710     // Handle @ operator
  6711     if (error_reporting() == 0) {
  6712         return;
  6713     }
  6714 
  6715     // If in PHP4, then respect error_reporting
  6716     if ((PHP_VERSION < 5) && (($errno & error_reporting()) == 0)) {
  6717         return;
  6718     }
  6719 
  6720     /*
  6721      * If we have a root user, then output detailed error message:
  6722      */
  6723     if ((is_array($_USER) && function_exists('SEC_inGroup'))
  6724             || (isset($_CONF['rootdebug']) && $_CONF['rootdebug'])) {
  6725         if ($_CONF['rootdebug'] || SEC_inGroup('Root')) {
  6726 
  6727             header('HTTP/1.1 500 Internal Server Error');
  6728             header('Status: 500 Internal Server Error');
  6729 
  6730             $title = 'An Error Occurred';
  6731             if (!empty($_CONF['site_name'])) {
  6732                 $title = $_CONF['site_name'] . ' - ' . $title;
  6733             }
  6734             echo("<html><head><title>$title</title></head>\n<body>\n");
  6735 
  6736             echo('<h1>An error has occurred:</h1>');
  6737             if ($_CONF['rootdebug']) {
  6738                 echo('<h2 style="color: red">This is being displayed as "Root Debugging" is enabled
  6739                         in your Geeklog configuration.</h2><p>If this is a production
  6740                         website you <strong><em>must disable</em></strong> this
  6741                         option once you have resolved any issues you are
  6742                         investigating.</p>');
  6743             } else {
  6744                 echo('<p>(This text is only displayed to users in the group \'Root\')</p>');
  6745             }
  6746             echo("<p>$errno - $errstr @ $errfile line $errline</p>");
  6747 
  6748             if (!function_exists('SEC_inGroup') || !SEC_inGroup('Root')) {
  6749                 if ('force' != ''.$_CONF['rootdebug']) {
  6750                     $errcontext = COM_rootDebugClean($errcontext);
  6751                 } else {
  6752                     echo('<h2 style="color: red">Root Debug is set to "force", this
  6753                     means that passwords and session cookies are exposed in this
  6754                     message!!!</h2>');
  6755                 }
  6756             }
  6757             echo('<pre>');
  6758             ob_start();
  6759             var_dump($errcontext);
  6760             $errcontext = htmlspecialchars(ob_get_contents());
  6761             ob_end_clean();
  6762             echo("$errcontext</pre></body></html>");
  6763             exit;
  6764         }
  6765     }
  6766 
  6767     /* If there is a custom error handler, fail over to that, but only
  6768      * if the error wasn't in lib-custom.php
  6769      */
  6770     if (is_array($_CONF) && !(strstr($errfile, 'lib-custom.php'))) {
  6771         if (array_key_exists('path_system', $_CONF)) {
  6772             if (file_exists($_CONF['path_system'] . 'lib-custom.php')) {
  6773                 require_once $_CONF['path_system'] . 'lib-custom.php';
  6774             }
  6775             if (function_exists('CUSTOM_handleError')) {
  6776                 CUSTOM_handleError($errno, $errstr, $errfile, $errline, $errcontext);
  6777                 exit;
  6778             }
  6779         }
  6780     }
  6781 
  6782     // if we do not throw the error back to an admin, still log it in the error.log
  6783     COM_errorLog("$errno - $errstr @ $errfile line $errline", 1);
  6784 
  6785     header('HTTP/1.1 500 Internal Server Error');
  6786     header('Status: 500 Internal Server Error');
  6787 
  6788     // Does the theme implement an error message html file?
  6789     if (!empty($_CONF['path_layout']) &&
  6790             file_exists($_CONF['path_layout'] . 'errormessage.html')) {
  6791         // NOTE: NOT A TEMPLATE! JUST HTML!
  6792         include $_CONF['path_layout'] . 'errormessage.html';
  6793     } else {
  6794         // Otherwise, display simple error message
  6795         $title = 'An Error Occurred';
  6796         if (!empty($_CONF['site_name'])) {
  6797             $title = $_CONF['site_name'] . ' - ' . $title;
  6798         }
  6799         echo("
  6800         <html>
  6801             <head>
  6802                 <title>{$title}</title>
  6803             </head>
  6804             <body>
  6805             <div style=\"width: 100%; text-align: center;\">
  6806             Unfortunately, an error has occurred rendering this page. Please try
  6807             again later.
  6808             </div>
  6809             </body>
  6810         </html>
  6811         ");
  6812     }
  6813 
  6814     exit;
  6815 }
  6816 
  6817 /**
  6818   * Recurse through the error context array removing/blanking password/cookie
  6819   * values in case the "for development" only switch is left on in a production
  6820   * environment.
  6821   *
  6822   * [Not fit for public consumption comments about what users who enable root
  6823   * debug in production should have done to them, and why making this change
  6824   * defeats the point of the entire root debug feature go here.]
  6825   *
  6826   * @param  array    $array  Array of state info (Recursive array).
  6827   * @param  boolean  $blank  override (wouldn't that blank out everything?)
  6828   * @return array            Cleaned array
  6829   */
  6830 function COM_rootDebugClean($array, $blank=false)
  6831 {
  6832     $blankField = false;
  6833     while(list($key, $value) = each($array)) {
  6834         $lkey = strtolower($key);
  6835         if((strpos($lkey, 'pass') !== false) || (strpos($lkey, 'cookie') !== false)) {
  6836             $blankField = true;
  6837         } else {
  6838             $blankField = $blank;
  6839         }
  6840         if(is_array($value)) {
  6841             $array[$key] = COM_rootDebugClean($value, $blankField);
  6842         } elseif($blankField) {
  6843             $array[$key] = '[VALUE REMOVED]';
  6844         }
  6845     }
  6846     return $array;
  6847 }
  6848 
  6849 /**
  6850   * Checks to see if a specified user, or the current user if non-specified
  6851   * is the anonymous user.
  6852   *
  6853   * @param  int $uid    ID of the user to check, or none for the current user.
  6854   * @return boolean     true if the user is the anonymous user.
  6855   */
  6856 function COM_isAnonUser($uid = '')
  6857 {
  6858     global $_USER;
  6859 
  6860     /* If no user was specified, fail over to the current user if there is one */
  6861     if( empty( $uid ) )
  6862     {
  6863         if( isset( $_USER['uid'] ) )
  6864         {
  6865             $uid = $_USER['uid'];
  6866         }
  6867     }
  6868 
  6869     if( !empty( $uid ) )
  6870     {
  6871         return ($uid == 1);
  6872     } else {
  6873         return true;
  6874     }
  6875 }
  6876 
  6877 /**
  6878 * Create Meta Tags to be used by COM_siteHeader in the headercode variable
  6879 *
  6880 * @param    string  $meta_description   the text for the meta description of the page being displayed
  6881 * @param    string  $meta_keywords        the text for the meta keywords of the page being displayed
  6882 * @return   string                         XHTML formatted text
  6883 *
  6884 */
  6885 function COM_createMetaTags($meta_description, $meta_keywords)
  6886 {
  6887     global $_CONF;
  6888 
  6889     $headercode ='';
  6890     
  6891     If ($_CONF['meta_tags'] > 0) {
  6892         if ($meta_description != '') {
  6893             $headercode .= LB . '<meta name="description" content="' . $meta_description . '"' . XHTML . '>';
  6894         }
  6895         if ($meta_keywords != '') {
  6896             $headercode .= LB . '<meta name="keywords" content="' . $meta_keywords . '"' . XHTML . '>';
  6897         }
  6898     }    
  6899 
  6900     return $headercode;
  6901 }
  6902 
  6903 
  6904 
  6905 /**
  6906 * Convert wiki-formatted text to (X)HTML
  6907 *
  6908 * @param    string  $wikitext   wiki-formatted text
  6909 * @return   string              XHTML formatted text
  6910 *
  6911 */
  6912 function COM_renderWikiText($wikitext)
  6913 {
  6914     global $_CONF;
  6915 
  6916     if (!$_CONF['wikitext_editor']) {
  6917         return $wikitext;
  6918     }
  6919 
  6920     require_once 'Text/Wiki.php';
  6921 
  6922     $wiki = new Text_Wiki();
  6923     $wiki->setFormatConf('Xhtml', 'translate', HTML_SPECIALCHARS);
  6924     $wiki->setRenderConf('Xhtml', 'charset', COM_getCharset());
  6925     $wiki->disableRule('wikilink');
  6926     $wiki->disableRule('freelink');
  6927     $wiki->disableRule('interwiki');
  6928 
  6929     return $wiki->transform($wikitext, 'Xhtml');
  6930 }
  6931 
  6932 /**
  6933 * Set the {lang_id} and {lang_attribute} variables for a template
  6934 *
  6935 * NOTE:     {lang_attribute} is only set in multi-language environments.
  6936 *
  6937 * @param    ref     &$template  template to use
  6938 * @return   void
  6939 *
  6940 */
  6941 function COM_setLangIdAndAttribute(&$template)
  6942 {
  6943     global $_CONF;
  6944 
  6945     $langAttr = '';
  6946     $langId   = '';
  6947 
  6948     if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
  6949         $langId = COM_getLanguageId();
  6950     } else {
  6951         // try to derive the language id from the locale
  6952         $l = explode('.', $_CONF['locale']); // get rid of character set
  6953         $langId = $l[0];
  6954         $l = explode('@', $langId); // get rid of '@euro', etc.
  6955         $langId = $l[0];
  6956     }
  6957 
  6958     if (!empty($langId)) {
  6959         $l = explode('-', str_replace('_', '-', $langId));
  6960         if ((count($l) == 1) && (strlen($langId) == 2)) {
  6961             $langAttr = 'lang="' . $langId . '"';
  6962         } else if (count($l) == 2) {
  6963             if (($l[0] == 'i') || ($l[0] == 'x')) {
  6964                 $langId = implode('-', $l);
  6965                 $langAttr = 'lang="' . $langId . '"';
  6966             } else if (strlen($l[0]) == 2) {
  6967                 $langId = implode('-', $l);
  6968                 $langAttr = 'lang="' . $langId . '"';
  6969             } else {
  6970                 $langId = $l[0];
  6971                 // this isn't a valid lang attribute, so don't set $langAttr
  6972             }
  6973         }
  6974     }
  6975     $template->set_var('lang_id', $langId);
  6976 
  6977     if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
  6978         $template->set_var('lang_attribute', ' ' . $langAttr);
  6979     } else {
  6980         $template->set_var('lang_attribute', '');
  6981     }
  6982 }
  6983 
  6984 /**
  6985 * Sends compressed output to browser.
  6986 *
  6987 * Assumes that $display contains the _entire_ output for a request - no
  6988 * echoes are allowed before or after this function.
  6989 * Currently only supports gzip compression. Checks if zlib compression is
  6990 * enabled in PHP and does uncompressed output if it is.
  6991 *
  6992 * @param    string  $display    Content to send to browser
  6993 * @return   void
  6994 *
  6995 */
  6996 function COM_output($display)
  6997 {
  6998     global $_CONF;
  6999 
  7000     if (empty($display)) {
  7001         return;
  7002     }
  7003 
  7004     if ($_CONF['compressed_output']) {
  7005         $gzip_accepted = false;
  7006         if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  7007             $enc = str_replace(' ', '', $_SERVER['HTTP_ACCEPT_ENCODING']);
  7008             $accept = explode(',', strtolower($enc));
  7009             $gzip_accepted = in_array('gzip', $accept);
  7010         }
  7011 
  7012         if ($gzip_accepted && function_exists('gzencode')) {
  7013 
  7014             $zlib_comp = ini_get('zlib.output_compression');
  7015             if (empty($zlib_comp) || (strcasecmp($zlib_comp, 'off') == 0)) {
  7016 
  7017                 header('Content-encoding: gzip');
  7018                 echo gzencode($display);
  7019                 return;
  7020 
  7021             }
  7022         }
  7023     }
  7024 
  7025     echo $display;
  7026 }
  7027 
  7028 /**
  7029 * Turn a piece of HTML into continuous(!) plain text
  7030 *
  7031 * This function removes HTML tags, line breaks, etc. and returns one long
  7032 * line of text. This is useful for word counts (do an explode() on the result)
  7033 * and for text excerpts.
  7034 *
  7035 * @param    string  $text   original text, including HTML and line breaks
  7036 * @return   string          continuous plain text
  7037 * 
  7038 */
  7039 function COM_getTextContent($text)
  7040 {
  7041     // replace <br> with spaces so that Text<br>Text becomes two words
  7042     $text = preg_replace('/\<br(\s*)?\/?\>/i', ' ', $text);
  7043 
  7044     // add extra space between tags, e.g. <p>Text</p><p>Text</p>
  7045     $text = str_replace('><', '> <', $text);
  7046 
  7047     // only now remove all HTML tags
  7048     $text = strip_tags($text);
  7049 
  7050     // replace all tabs, newlines, and carrriage returns with spaces
  7051     $text = str_replace(array("\011", "\012", "\015"), ' ', $text);
  7052 
  7053     // replace entities with plain spaces
  7054     $text = str_replace(array('&#20;', '&#160;', '&nbsp;'), ' ', $text);
  7055 
  7056     // collapse whitespace
  7057     $text = preg_replace('/\s\s+/', ' ', $text);
  7058 
  7059     return trim($text);
  7060 }
  7061 
  7062 /**
  7063 * Now include all plugin functions
  7064 */
  7065 foreach ($_PLUGINS as $pi_name) {
  7066     require_once $_CONF['path'] . 'plugins/' . $pi_name . '/functions.inc';
  7067 }
  7068 
  7069 // Check and see if any plugins (or custom functions)
  7070 // have scheduled tasks to perform
  7071 if ($_CONF['cron_schedule_interval'] > 0) {
  7072     if ((DB_getItem($_TABLES['vars'], 'value', "name='last_scheduled_run'")
  7073             + $_CONF['cron_schedule_interval']) <= time()) {
  7074         DB_query("UPDATE {$_TABLES['vars']} SET value=UNIX_TIMESTAMP() WHERE name='last_scheduled_run'");
  7075         PLG_runScheduledTask();
  7076     }
  7077 }
  7078 
  7079 ?>