public_html/lib-common.php
author Dirk Haun <dirk@haun-online.de>
Sun, 20 Sep 2009 19:11:47 +0200
branchHEAD
changeset 7433 a8de0db60197
parent 7428 df55886043f2
child 7434 aa322b3c4d3d
permissions -rw-r--r--
The user's time zone selection (from My Account) is actually used now
     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     if( DB_getItem( $_TABLES['stories'], 'COUNT(*)', "featured = 1 AND draft_flag = 0 AND date <= '$curdate'" ) > 1 )
  1956     {
  1957         // OK, we have two featured stories, fix that
  1958 
  1959         $sid = DB_getItem( $_TABLES['stories'], 'sid', "featured = 1 AND draft_flag = 0 ORDER BY date LIMIT 1" );
  1960         DB_query( "UPDATE {$_TABLES['stories']} SET featured = 0 WHERE sid = '$sid'" );
  1961     }
  1962 }
  1963 
  1964 /**
  1965 *
  1966 * Logs messages to error.log or the web page or both
  1967 *
  1968 * Prints a well formatted message to either the web page, error log
  1969 * or both.
  1970 *
  1971 * @param        string      $logentry       Text to log to error log
  1972 * @param        int         $actionid       where 1 = write to log file, 2 = write to screen (default) both
  1973 * @see function COM_accessLog
  1974 * @return   string  If $actionid = 2 or '' then HTML formatted string (wrapped in block) else nothing
  1975 *
  1976 */
  1977 
  1978 function COM_errorLog( $logentry, $actionid = '' )
  1979 {
  1980     global $_CONF, $LANG01;
  1981 
  1982     $retval = '';
  1983 
  1984     if( !empty( $logentry ))
  1985     {
  1986         $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
  1987                                  $logentry );
  1988 
  1989         $timestamp = @strftime( '%c' );
  1990 
  1991         if (!isset($_CONF['path_layout']) &&
  1992                 (($actionid == 2) || empty($actionid))) {
  1993             $actionid = 1;
  1994         }
  1995         if (!isset($_CONF['path_log']) && ($actionid != 2)) {
  1996             $actionid = 3;
  1997         }
  1998 
  1999         switch( $actionid )
  2000         {
  2001             case 1:
  2002                 $logfile = $_CONF['path_log'] . 'error.log';
  2003 
  2004                 if( !$file = fopen( $logfile, 'a' ))
  2005                 {
  2006                     $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
  2007                 }
  2008                 else
  2009                 {
  2010                     fputs( $file, "$timestamp - $logentry \n" );
  2011                 }
  2012                 break;
  2013 
  2014             case 2:
  2015                 $retval .= COM_startBlock( $LANG01[55] . ' ' . $timestamp, '',
  2016                                COM_getBlockTemplate( '_msg_block', 'header' ))
  2017                         . nl2br( $logentry )
  2018                         . COM_endBlock( COM_getBlockTemplate( '_msg_block',
  2019                                                               'footer' ));
  2020                 break;
  2021 
  2022             case 3:
  2023                 $retval = nl2br($logentry);
  2024                 break;
  2025 
  2026             default:
  2027                 $logfile = $_CONF['path_log'] . 'error.log';
  2028 
  2029                 if( !$file = fopen( $logfile, 'a' ))
  2030                 {
  2031                     $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
  2032                 }
  2033                 else
  2034                 {
  2035                     fputs( $file, "$timestamp - $logentry \n" );
  2036                     $retval .= COM_startBlock( $LANG01[34] . ' - ' . $timestamp,
  2037                                    '', COM_getBlockTemplate( '_msg_block',
  2038                                    'header' ))
  2039                             . nl2br( $logentry )
  2040                             . COM_endBlock( COM_getBlockTemplate( '_msg_block',
  2041                                                                   'footer' ));
  2042                 }
  2043                 break;
  2044         }
  2045     }
  2046 
  2047     return $retval;
  2048 }
  2049 
  2050 /**
  2051 * Logs message to access.log
  2052 *
  2053 * This will print a message to the Geeklog access log
  2054 *
  2055 * @param        string      $logentry       Message to write to access log
  2056 * @see COM_errorLog
  2057 *
  2058 */
  2059 
  2060 function COM_accessLog( $logentry )
  2061 {
  2062     global $_CONF, $_USER, $LANG01;
  2063 
  2064     $retval = '';
  2065 
  2066     if( !empty( $logentry ))
  2067     {
  2068         $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
  2069                                  $logentry );
  2070 
  2071         $timestamp = @strftime( '%c' );
  2072         $logfile = $_CONF['path_log'] . 'access.log';
  2073 
  2074         if( !$file = fopen( $logfile, 'a' ))
  2075         {
  2076             return $LANG01[33] . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
  2077         }
  2078 
  2079         if( isset( $_USER['uid'] ))
  2080         {
  2081             $byuser = $_USER['uid'] . '@' . $_SERVER['REMOTE_ADDR'];
  2082         }
  2083         else
  2084         {
  2085             $byuser = 'anon@' . $_SERVER['REMOTE_ADDR'];
  2086         }
  2087 
  2088         fputs( $file, "$timestamp ($byuser) - $logentry\n" );
  2089     }
  2090 
  2091     return $retval;
  2092 }
  2093 
  2094 /**
  2095 * Shows all available topics
  2096 *
  2097 * Show the topics in the system the user has access to and prints them in HTML.
  2098 * This function is used to show the topics in the topics block.
  2099 *
  2100 * @param    string    $topic      ID of currently selected topic
  2101 * @return   string                HTML formatted topic list
  2102 *
  2103 */
  2104 
  2105 function COM_showTopics( $topic='' )
  2106 {
  2107     global $_CONF, $_TABLES, $_USER, $LANG01, $_BLOCK_TEMPLATE, $page;
  2108 
  2109     $langsql = COM_getLangSQL( 'tid' );
  2110     if( empty( $langsql ))
  2111     {
  2112         $op = 'WHERE';
  2113     }
  2114     else
  2115     {
  2116         $op = 'AND';
  2117     }
  2118 
  2119     $sql = "SELECT tid,topic,imageurl FROM {$_TABLES['topics']}" . $langsql;
  2120     if( !COM_isAnonUser() )
  2121     {
  2122         $tids = DB_getItem( $_TABLES['userindex'], 'tids',
  2123                             "uid = '{$_USER['uid']}'" );
  2124         if( !empty( $tids ))
  2125         {
  2126             $sql .= " $op (tid NOT IN ('" . str_replace( ' ', "','", $tids )
  2127                  . "'))" . COM_getPermSQL( 'AND' );
  2128         }
  2129         else
  2130         {
  2131             $sql .= COM_getPermSQL( $op );
  2132         }
  2133     }
  2134     else
  2135     {
  2136         $sql .= COM_getPermSQL( $op );
  2137     }
  2138     if( $_CONF['sortmethod'] == 'alpha' )
  2139     {
  2140         $sql .= ' ORDER BY topic ASC';
  2141     }
  2142     else
  2143     {
  2144         $sql .= ' ORDER BY sortnum';
  2145     }
  2146     $result = DB_query( $sql );
  2147 
  2148     $retval = '';
  2149     $sections = new Template( $_CONF['path_layout'] );
  2150     if( isset( $_BLOCK_TEMPLATE['topicoption'] ))
  2151     {
  2152         $templates = explode( ',', $_BLOCK_TEMPLATE['topicoption'] );
  2153         $sections->set_file( array( 'option'  => $templates[0],
  2154                                     'current' => $templates[1] ));
  2155     }
  2156     else
  2157     {
  2158         $sections->set_file( array( 'option'   => 'topicoption.thtml',
  2159                                     'inactive' => 'topicoption_off.thtml' ));
  2160     }
  2161 
  2162     $sections->set_var( 'xhtml', XHTML );
  2163     $sections->set_var( 'site_url', $_CONF['site_url'] );
  2164     $sections->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  2165     $sections->set_var( 'layout_url', $_CONF['layout_url'] );
  2166     $sections->set_var( 'block_name', str_replace( '_', '-', 'section_block' ));
  2167 
  2168     if( $_CONF['hide_home_link'] == 0 )
  2169     {
  2170         // Give a link to the homepage here since a lot of people use this for
  2171         // navigating the site
  2172 
  2173         if( COM_onFrontpage() )
  2174         {
  2175             $sections->set_var( 'option_url', '' );
  2176             $sections->set_var( 'option_label', $LANG01[90] );
  2177             $sections->set_var( 'option_count', '' );
  2178             $sections->set_var( 'topic_image', '' );
  2179             $retval .= $sections->parse( 'item', 'inactive' );
  2180         }
  2181         else
  2182         {
  2183             $sections->set_var( 'option_url',
  2184                                 $_CONF['site_url'] . '/index.php' );
  2185             $sections->set_var( 'option_label', $LANG01[90] );
  2186             $sections->set_var( 'option_count', '' );
  2187             $sections->set_var( 'topic_image', '' );
  2188             $retval .= $sections->parse( 'item', 'option' );
  2189         }
  2190     }
  2191 
  2192     if( $_CONF['showstorycount'] )
  2193     {
  2194         $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['stories']} "
  2195              . 'WHERE (draft_flag = 0) AND (date <= NOW()) '
  2196              . COM_getPermSQL( 'AND' )
  2197              . ' GROUP BY tid';
  2198         $rcount = DB_query( $sql );
  2199         while( $C = DB_fetchArray( $rcount ))
  2200         {
  2201             $storycount[$C['tid']] = $C['count'];
  2202         }
  2203     }
  2204 
  2205     if( $_CONF['showsubmissioncount'] )
  2206     {
  2207         $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['storysubmission']} "
  2208              . ' GROUP BY tid';
  2209         $rcount = DB_query( $sql );
  2210         while( $C = DB_fetchArray( $rcount ))
  2211         {
  2212             $submissioncount[$C['tid']] = $C['count'];
  2213         }
  2214     }
  2215 
  2216     while( $A = DB_fetchArray( $result ) )
  2217     {
  2218         $topicname = stripslashes( $A['topic'] );
  2219         $sections->set_var( 'option_url', $_CONF['site_url']
  2220                             . '/index.php?topic=' . $A['tid'] );
  2221         $sections->set_var( 'option_label', $topicname );
  2222 
  2223         $countstring = '';
  2224         if( $_CONF['showstorycount'] || $_CONF['showsubmissioncount'] )
  2225         {
  2226             $countstring .= '(';
  2227 
  2228             if( $_CONF['showstorycount'] )
  2229             {
  2230                 if( empty( $storycount[$A['tid']] ))
  2231                 {
  2232                     $countstring .= 0;
  2233                 }
  2234                 else
  2235                 {
  2236                     $countstring .= COM_numberFormat( $storycount[$A['tid']] );
  2237                 }
  2238             }
  2239 
  2240             if( $_CONF['showsubmissioncount'] )
  2241             {
  2242                 if( $_CONF['showstorycount'] )
  2243                 {
  2244                     $countstring .= '/';
  2245                 }
  2246                 if( empty( $submissioncount[$A['tid']] ))
  2247                 {
  2248                     $countstring .= 0;
  2249                 }
  2250                 else
  2251                 {
  2252                     $countstring .= COM_numberFormat( $submissioncount[$A['tid']] );
  2253                 }
  2254             }
  2255 
  2256             $countstring .= ')';
  2257         }
  2258         $sections->set_var( 'option_count', $countstring );
  2259 
  2260         $topicimage = '';
  2261         if( !empty( $A['imageurl'] ))
  2262         {
  2263             $imageurl = COM_getTopicImageUrl( $A['imageurl'] );
  2264             $topicimage = '<img src="' . $imageurl . '" alt="' . $topicname
  2265                         . '" title="' . $topicname . '"' . XHTML . '>';
  2266         }
  2267         $sections->set_var( 'topic_image', $topicimage );
  2268 
  2269         if(( $A['tid'] == $topic ) && ( $page == 1 ))
  2270         {
  2271             $retval .= $sections->parse( 'item', 'inactive' );
  2272         }
  2273         else
  2274         {
  2275             $retval .= $sections->parse( 'item', 'option' );
  2276         }
  2277     }
  2278 
  2279     return $retval;
  2280 }
  2281 
  2282 /**
  2283 * Shows the user their menu options
  2284 *
  2285 * This shows the average Joe User their menu options. This is the user block on the left side
  2286 *
  2287 * @param        string      $help       Help file to show
  2288 * @param        string      $title      Title of Menu
  2289 * @param        string      $position   Side being shown on 'left', 'right'. Though blank works not likely.
  2290 * @see function COM_adminMenu
  2291 *
  2292 */
  2293 function COM_userMenu( $help='', $title='', $position='' )
  2294 {
  2295     global $_TABLES, $_USER, $_CONF, $LANG01, $LANG04, $_BLOCK_TEMPLATE;
  2296 
  2297     $retval = '';
  2298 
  2299     if( !COM_isAnonUser() )
  2300     {
  2301         $usermenu = new Template( $_CONF['path_layout'] );
  2302         if( isset( $_BLOCK_TEMPLATE['useroption'] ))
  2303         {
  2304             $templates = explode( ',', $_BLOCK_TEMPLATE['useroption'] );
  2305             $usermenu->set_file( array( 'option' => $templates[0],
  2306                                         'current' => $templates[1] ));
  2307         }
  2308         else
  2309         {
  2310            $usermenu->set_file( array( 'option' => 'useroption.thtml',
  2311                                        'current' => 'useroption_off.thtml' ));
  2312         }
  2313         $usermenu->set_var( 'xhtml', XHTML );
  2314         $usermenu->set_var( 'site_url', $_CONF['site_url'] );
  2315         $usermenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  2316         $usermenu->set_var( 'layout_url', $_CONF['layout_url'] );
  2317         $usermenu->set_var( 'block_name', str_replace( '_', '-', 'user_block' ));
  2318 
  2319         if( empty( $title ))
  2320         {
  2321             $title = DB_getItem( $_TABLES['blocks'], 'title',
  2322                                  "name='user_block'" );
  2323         }
  2324 
  2325         // what's our current URL?
  2326         $thisUrl = COM_getCurrentURL();
  2327 
  2328         $retval .= COM_startBlock( $title, $help,
  2329                            COM_getBlockTemplate( 'user_block', 'header', $position ));
  2330 
  2331         // This function will show the user options for all installed plugins
  2332         // (if any)
  2333 
  2334         $plugin_options = PLG_getUserOptions();
  2335         $nrows = count( $plugin_options );
  2336 
  2337         for( $i = 0; $i < $nrows; $i++ )
  2338         {
  2339             $plg = current( $plugin_options );
  2340             $usermenu->set_var( 'option_label', $plg->adminlabel );
  2341 
  2342             if( !empty( $plg->numsubmissions ))
  2343             {
  2344                 $usermenu->set_var( 'option_count', '(' . $plg->numsubmissions . ')' );
  2345             }
  2346             else
  2347             {
  2348                 $usermenu->set_var( 'option_count', '' );
  2349             }
  2350 
  2351             $usermenu->set_var( 'option_url', $plg->adminurl );
  2352             if( $thisUrl == $plg->adminurl )
  2353             {
  2354                 $retval .= $usermenu->parse( 'item', 'current' );
  2355             }
  2356             else
  2357             {
  2358                 $retval .= $usermenu->parse( 'item', 'option' );
  2359             }
  2360             next( $plugin_options );
  2361         }
  2362 
  2363         $url = $_CONF['site_url'] . '/usersettings.php';
  2364         $usermenu->set_var( 'option_label', $LANG01[48] );
  2365         $usermenu->set_var( 'option_count', '' );
  2366         $usermenu->set_var( 'option_url', $url );
  2367         if( $thisUrl == $url )
  2368         {
  2369             $retval .= $usermenu->parse( 'item', 'current' );
  2370         }
  2371         else
  2372         {
  2373             $retval .= $usermenu->parse( 'item', 'option' );
  2374         }
  2375 
  2376         $url = $_CONF['site_url'] . '/users.php?mode=logout';
  2377         $usermenu->set_var( 'option_label', $LANG01[19] );
  2378         $usermenu->set_var( 'option_count', '' );
  2379         $usermenu->set_var( 'option_url', $url );
  2380         $retval .= $usermenu->finish($usermenu->parse('item', 'option'));
  2381         $retval .=  COM_endBlock(COM_getBlockTemplate('user_block', 'footer', $position));
  2382     }
  2383     else
  2384     {
  2385         $retval .= COM_startBlock( $LANG01[47], $help,
  2386                            COM_getBlockTemplate( 'user_block', 'header', $position ));
  2387         $login = new Template( $_CONF['path_layout'] );
  2388         $login->set_file( 'form', 'loginform.thtml' );
  2389         $login->set_var( 'xhtml', XHTML );
  2390         $login->set_var( 'site_url', $_CONF['site_url'] );
  2391         $login->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  2392         $login->set_var( 'layout_url', $_CONF['layout_url'] );
  2393         $login->set_var( 'lang_username', $LANG01[21] );
  2394         $login->set_var( 'lang_password', $LANG01[57] );
  2395         $login->set_var( 'lang_forgetpassword', $LANG01[119] );
  2396         $login->set_var( 'lang_login', $LANG01[58] );
  2397         if( $_CONF['disable_new_user_registration'] == 1 )
  2398         {
  2399             $login->set_var( 'lang_signup', '' );
  2400         }
  2401         else
  2402         {
  2403             $login->set_var( 'lang_signup', $LANG01[59] );
  2404         }
  2405 
  2406         // 3rd party remote authentification.
  2407         if ($_CONF['user_login_method']['3rdparty'] && !$_CONF['usersubmission']) {
  2408             $modules = SEC_collectRemoteAuthenticationModules();
  2409             if (count($modules) == 0) {
  2410                 $user_templates->set_var('services', '');
  2411             } else {
  2412                 if (!$_CONF['user_login_method']['standard'] &&
  2413                         (count($modules) == 1)) {
  2414                     $select = '<input type="hidden" name="service" value="'
  2415                             . $modules[0] . '"' . XHTML . '>' . $modules[0];
  2416                 } else {
  2417                     // Build select
  2418                     $select = '<select name="service" id="service">';
  2419                     if ($_CONF['user_login_method']['standard']) {
  2420                         $select .= '<option value="">' . $_CONF['site_name']
  2421                                 . '</option>';
  2422                     }
  2423                     foreach ($modules as $service) {
  2424                         $select .= '<option value="' . $service . '">'
  2425                                 . $service . '</option>';
  2426                     }
  2427                     $select .= '</select>';
  2428                 }
  2429 
  2430                 $login->set_file('services', 'blockservices.thtml');
  2431                 $login->set_var('lang_service', $LANG04[121]);
  2432                 $login->set_var('select_service', $select);
  2433                 $login->parse('output', 'services');
  2434                 $login->set_var('services',
  2435                                 $login->finish($login->get_var('output')));
  2436             }
  2437         } else {
  2438            $login->set_var('services', '');
  2439         }
  2440 
  2441         // OpenID remote authentification.
  2442         if ($_CONF['user_login_method']['openid'] && ($_CONF['usersubmission'] == 0) && !$_CONF['disable_new_user_registration']) {
  2443             $login->set_file('openid_login', 'loginform_openid.thtml');
  2444             $login->set_var('lang_openid_login', $LANG01[128]);
  2445             $login->set_var('input_field_size', 18);
  2446             $login->set_var('app_url', $_CONF['site_url'] . '/users.php');
  2447             $login->parse('output', 'openid_login');
  2448             $login->set_var('openid_login',
  2449                 $login->finish($login->get_var('output')));
  2450         } else {
  2451             $login->set_var('openid_login', '');
  2452         }
  2453 
  2454         $retval .= $login->finish($login->parse('output', 'form'));
  2455         $retval .= COM_endBlock( COM_getBlockTemplate('user_block', 'footer', $position));
  2456     }
  2457 
  2458     return $retval;
  2459 }
  2460 
  2461 /**
  2462 * Prints administration menu
  2463 *
  2464 * This will return the administration menu items that the user has
  2465 * sufficient rights to -- Admin Block on the left side.
  2466 *
  2467 * @param        string      $help       Help file to show
  2468 * @param        string      $title      Menu Title
  2469 * @param        string      $position   Side being shown on 'left', 'right' or blank.
  2470 * @see function COM_userMenu
  2471 *
  2472 */
  2473 function COM_adminMenu( $help = '', $title = '', $position = '' )
  2474 {
  2475     global $_TABLES, $_USER, $_CONF, $LANG01, $LANG_ADMIN, $_BLOCK_TEMPLATE,
  2476            $_DB_dbms, $config;
  2477 
  2478     $retval = '';
  2479 
  2480     if( empty( $_USER['username'] ))
  2481     {
  2482         return $retval;
  2483     }
  2484 
  2485     $plugin_options = PLG_getAdminOptions();
  2486     $num_plugins = count( $plugin_options );
  2487 
  2488     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 ))
  2489     {
  2490         // what's our current URL?
  2491         $thisUrl = COM_getCurrentURL();
  2492 
  2493         $adminmenu = new Template( $_CONF['path_layout'] );
  2494         if( isset( $_BLOCK_TEMPLATE['adminoption'] ))
  2495         {
  2496             $templates = explode( ',', $_BLOCK_TEMPLATE['adminoption'] );
  2497             $adminmenu->set_file( array( 'option' => $templates[0],
  2498                                          'current' => $templates[1] ));
  2499         }
  2500         else
  2501         {
  2502             $adminmenu->set_file( array( 'option' => 'adminoption.thtml',
  2503                                          'current' => 'adminoption_off.thtml' ));
  2504         }
  2505         $adminmenu->set_var( 'xhtml', XHTML );
  2506         $adminmenu->set_var( 'site_url', $_CONF['site_url'] );
  2507         $adminmenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
  2508         $adminmenu->set_var( 'layout_url', $_CONF['layout_url'] );
  2509         $adminmenu->set_var( 'block_name', str_replace( '_', '-', 'admin_block' ));
  2510 
  2511         if( empty( $title ))
  2512         {
  2513             $title = DB_getItem( $_TABLES['blocks'], 'title',
  2514                                  "name = 'admin_block'" );
  2515         }
  2516 
  2517         $retval .= COM_startBlock( $title, $help,
  2518                            COM_getBlockTemplate( 'admin_block', 'header', $position ));
  2519 
  2520         $topicsql = '';
  2521         if( SEC_isModerator() || SEC_hasRights( 'story.edit' ))
  2522         {
  2523             $tresult = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
  2524                                  . COM_getPermSQL() );
  2525             $trows = DB_numRows( $tresult );
  2526             if( $trows > 0 )
  2527             {
  2528                 $tids = array();
  2529                 for( $i = 0; $i < $trows; $i++ )
  2530                 {
  2531                     $T = DB_fetchArray( $tresult );
  2532                     $tids[] = $T['tid'];
  2533                 }
  2534                 if( count( $tids ) > 0 )
  2535                 {
  2536                     $topicsql = " (tid IN ('" . implode( "','", $tids ) . "'))";
  2537                 }
  2538             }
  2539         }
  2540 
  2541         $modnum = 0;
  2542         if (SEC_hasRights('story.edit,story.moderate', 'OR') ||
  2543                 (($_CONF['commentsubmission'] == 1) &&
  2544                     SEC_hasRights('comment.moderate')) ||
  2545                 (($_CONF['usersubmission'] == 1) &&
  2546                     SEC_hasRights('user.edit,user.delete'))) {
  2547 
  2548             if (SEC_hasRights('story.moderate')) {
  2549                 if (empty($topicsql)) {
  2550                     $modnum += DB_count($_TABLES['storysubmission']);
  2551                 } else {
  2552                     $sresult = DB_query("SELECT COUNT(*) AS count FROM {$_TABLES['storysubmission']} WHERE" . $topicsql);
  2553                     $S = DB_fetchArray($sresult);
  2554                     $modnum += $S['count'];
  2555                 }
  2556             }
  2557 
  2558             if (($_CONF['listdraftstories'] == 1) && SEC_hasRights('story.edit')) {
  2559                 $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (draft_flag = 1)";
  2560                 if (!empty($topicsql)) {
  2561                     $sql .= ' AND' . $topicsql;
  2562                 }
  2563                 $result = DB_query($sql . COM_getPermSQL('AND', 0, 3));
  2564                 $A = DB_fetchArray($result);
  2565                 $modnum += $A['count'];
  2566             }
  2567 
  2568             if (($_CONF['commentsubmission'] == 1) && SEC_hasRights('comment.moderate')) {
  2569                 $modnum += DB_count($_TABLES['commentsubmissions']);
  2570             }
  2571 
  2572             if ($_CONF['usersubmission'] == 1) {
  2573                 if (SEC_hasRights('user.edit') && SEC_hasRights('user.delete')) {
  2574                     $modnum += DB_count($_TABLES['users'], 'status', '2');
  2575                 }
  2576             }
  2577         }
  2578 
  2579         if (SEC_inGroup('Root')) {
  2580             $url = $_CONF['site_admin_url'] . '/configuration.php';
  2581             $adminmenu->set_var('option_url', $url);
  2582             $adminmenu->set_var('option_label', $LANG01[129]);
  2583             $adminmenu->set_var('option_count', count($config->_get_groups()));
  2584             $menu_item = $adminmenu->parse('item',
  2585                                            ($thisUrl == $url) ? 'current' :
  2586                                                                 'option');
  2587             $link_array[$LANG01[129]] = $menu_item;
  2588         }
  2589 
  2590 
  2591         // now handle submissions for plugins
  2592         $modnum += PLG_getSubmissionCount();
  2593 
  2594         if( SEC_hasRights( 'story.edit' ))
  2595         {
  2596             $url = $_CONF['site_admin_url'] . '/story.php';
  2597             $adminmenu->set_var( 'option_url', $url );
  2598             $adminmenu->set_var( 'option_label', $LANG01[11] );
  2599             if( empty( $topicsql ))
  2600             {
  2601                 $numstories = DB_count( $_TABLES['stories'] );
  2602             }
  2603             else
  2604             {
  2605                 $nresult = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE" . $topicsql . COM_getPermSql( 'AND' ));
  2606                 $N = DB_fetchArray( $nresult );
  2607                 $numstories = $N['count'];
  2608             }
  2609             $adminmenu->set_var( 'option_count',
  2610                                  COM_numberFormat( $numstories ));
  2611             $menu_item = $adminmenu->parse( 'item',
  2612                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2613             $link_array[$LANG01[11]] = $menu_item;
  2614         }
  2615 
  2616         if( SEC_hasRights( 'block.edit' ))
  2617         {
  2618             $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['blocks']}" . COM_getPermSql());
  2619             list( $count ) = DB_fetchArray( $result );
  2620 
  2621             $url = $_CONF['site_admin_url'] . '/block.php';
  2622             $adminmenu->set_var( 'option_url', $url );
  2623             $adminmenu->set_var( 'option_label', $LANG01[12] );
  2624             $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
  2625 
  2626             $menu_item = $adminmenu->parse( 'item',
  2627                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2628             $link_array[$LANG01[12]] = $menu_item;
  2629         }
  2630 
  2631         if( SEC_hasRights( 'topic.edit' ))
  2632         {
  2633             $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['topics']}" . COM_getPermSql());
  2634             list( $count ) = DB_fetchArray( $result );
  2635 
  2636             $url = $_CONF['site_admin_url'] . '/topic.php';
  2637             $adminmenu->set_var( 'option_url', $url );
  2638             $adminmenu->set_var( 'option_label', $LANG01[13] );
  2639             $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
  2640 
  2641             $menu_item = $adminmenu->parse( 'item',
  2642                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2643             $link_array[$LANG01[13]] = $menu_item;
  2644         }
  2645 
  2646         if( SEC_hasRights( 'user.edit' ))
  2647         {
  2648             $url = $_CONF['site_admin_url'] . '/user.php';
  2649             $adminmenu->set_var( 'option_url', $url );
  2650             $adminmenu->set_var( 'option_label', $LANG01[17] );
  2651             $adminmenu->set_var( 'option_count',
  2652                     COM_numberFormat( DB_count( $_TABLES['users'] ) -1 ));
  2653 
  2654             $menu_item = $adminmenu->parse( 'item',
  2655                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2656             $link_array[$LANG01[17]] = $menu_item;
  2657         }
  2658 
  2659         if( SEC_hasRights( 'group.edit' ))
  2660         {
  2661             if (SEC_inGroup('Root')) {
  2662                 $grpFilter = '';
  2663             } else {
  2664                 $thisUsersGroups = SEC_getUserGroups ();
  2665                 $grpFilter = 'WHERE (grp_id IN (' . implode (',', $thisUsersGroups) . '))';
  2666             }
  2667             $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['groups']} $grpFilter;" );
  2668             $A = DB_fetchArray( $result );
  2669 
  2670             $url = $_CONF['site_admin_url'] . '/group.php';
  2671             $adminmenu->set_var( 'option_url', $url );
  2672             $adminmenu->set_var( 'option_label', $LANG01[96] );
  2673             $adminmenu->set_var( 'option_count',
  2674                                  COM_numberFormat( $A['count'] ));
  2675 
  2676             $menu_item = $adminmenu->parse( 'item',
  2677                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2678             $link_array[$LANG01[96]] = $menu_item;
  2679         }
  2680 
  2681         if( SEC_hasRights( 'user.mail' ))
  2682         {
  2683             $url = $_CONF['site_admin_url'] . '/mail.php';
  2684             $adminmenu->set_var( 'option_url', $url );
  2685             $adminmenu->set_var( 'option_label', $LANG01[105] );
  2686             $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
  2687 
  2688             $menu_item = $adminmenu->parse( 'item',
  2689                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2690             $link_array[$LANG01[105]] = $menu_item;
  2691         }
  2692 
  2693         if(( $_CONF['backend'] == 1 ) && SEC_hasRights( 'syndication.edit' ))
  2694         {
  2695             $url = $_CONF['site_admin_url'] . '/syndication.php';
  2696             $adminmenu->set_var( 'option_url', $url );
  2697             $adminmenu->set_var( 'option_label', $LANG01[38] );
  2698             $count = COM_numberFormat( DB_count( $_TABLES['syndication'] ));
  2699             $adminmenu->set_var( 'option_count', $count );
  2700 
  2701             $menu_item = $adminmenu->parse( 'item',
  2702                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2703             $link_array[$LANG01[38]] = $menu_item;
  2704         }
  2705 
  2706         if(( $_CONF['trackback_enabled'] || $_CONF['pingback_enabled'] ||
  2707                 $_CONF['ping_enabled'] ) && SEC_hasRights( 'story.ping' ))
  2708         {
  2709             $url = $_CONF['site_admin_url'] . '/trackback.php';
  2710             $adminmenu->set_var( 'option_url', $url );
  2711             $adminmenu->set_var( 'option_label', $LANG01[116] );
  2712             if( $_CONF['ping_enabled'] )
  2713             {
  2714                 $count = COM_numberFormat( DB_count( $_TABLES['pingservice'] ));
  2715                 $adminmenu->set_var( 'option_count', $count );
  2716             }
  2717             else
  2718             {
  2719                 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
  2720             }
  2721 
  2722             $menu_item = $adminmenu->parse( 'item',
  2723                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2724             $link_array[$LANG01[116]] = $menu_item;
  2725         }
  2726 
  2727         if (SEC_hasRights('plugin.edit')) {
  2728             $url = $_CONF['site_admin_url'] . '/plugins.php';
  2729             $adminmenu->set_var('option_url', $url);
  2730             $adminmenu->set_var('option_label', $LANG01[77]);
  2731             $adminmenu->set_var('option_count',
  2732                     COM_numberFormat(DB_count($_TABLES['plugins'],
  2733                                               'pi_enabled', 1)));
  2734 
  2735             $menu_item = $adminmenu->parse('item',
  2736                     ($thisUrl == $url) ? 'current' : 'option');
  2737             $link_array[$LANG01[77]] = $menu_item;
  2738         }
  2739 
  2740         // This will show the admin options for all installed plugins (if any)
  2741 
  2742         for( $i = 0; $i < $num_plugins; $i++ )
  2743         {
  2744             $plg = current( $plugin_options );
  2745 
  2746             $adminmenu->set_var( 'option_url', $plg->adminurl );
  2747             $adminmenu->set_var( 'option_label', $plg->adminlabel );
  2748 
  2749             if( empty( $plg->numsubmissions ))
  2750             {
  2751                 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
  2752             }
  2753             else
  2754             {
  2755                 $adminmenu->set_var( 'option_count',
  2756                                      COM_numberFormat( $plg->numsubmissions ));
  2757             }
  2758 
  2759             $menu_item = $adminmenu->parse( 'item',
  2760                     ( $thisUrl == $plg->adminurl ) ? 'current' : 'option', true );
  2761             $link_array[$plg->adminlabel] = $menu_item;
  2762 
  2763             next( $plugin_options );
  2764         }
  2765 
  2766         if(( $_CONF['allow_mysqldump'] == 1 ) AND ( $_DB_dbms == 'mysql' ) AND
  2767                 SEC_inGroup( 'Root' ))
  2768         {
  2769             $url = $_CONF['site_admin_url'] . '/database.php';
  2770             $adminmenu->set_var( 'option_url', $url );
  2771             $adminmenu->set_var( 'option_label', $LANG01[103] );
  2772             $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
  2773 
  2774             $menu_item = $adminmenu->parse( 'item',
  2775                     ( $thisUrl == $url ) ? 'current' : 'option' );
  2776             $link_array[$LANG01[103]] = $menu_item;
  2777         }
  2778 
  2779         if ($_CONF['link_documentation'] == 1) {
  2780             $doclang = COM_getLanguageName();
  2781             $docs = 'docs/' . $doclang . '/index.html';
  2782             if (file_exists($_CONF['path_html'] . $docs)) {
  2783                 $adminmenu->set_var('option_url', $_CONF['site_url']
  2784                                     . '/' . $docs);
  2785             } else {
  2786                 $adminmenu->set_var('option_url', $_CONF['site_url']
  2787                                     . '/docs/english/index.html');
  2788             }
  2789             $adminmenu->set_var('option_label', $LANG01[113]);
  2790             $adminmenu->set_var('option_count', $LANG_ADMIN['na']);
  2791             $menu_item = $adminmenu->parse('item', 'option');
  2792             $link_array[$LANG01[113]] = $menu_item;
  2793         }
  2794 
  2795         if( $_CONF['link_versionchecker'] == 1 AND SEC_inGroup( 'Root' ))
  2796         {
  2797             $adminmenu->set_var( 'option_url',
  2798                'http://www.geeklog.net/versionchecker.php?version=' . VERSION );
  2799             $adminmenu->set_var( 'option_label', $LANG01[107] );
  2800             $adminmenu->set_var( 'option_count', VERSION );
  2801 
  2802             $menu_item = $adminmenu->parse( 'item', 'option' );
  2803             $link_array[$LANG01[107]] = $menu_item;
  2804         }
  2805 
  2806         if( $_CONF['sort_admin'] )
  2807         {
  2808             uksort( $link_array, 'strcasecmp' );
  2809         }
  2810 
  2811         $url = $_CONF['site_admin_url'] . '/moderation.php';
  2812         $adminmenu->set_var('option_url', $url);
  2813         $adminmenu->set_var('option_label', $LANG01[10]);
  2814         $adminmenu->set_var('option_count', COM_numberFormat($modnum));
  2815         $menu_item = $adminmenu->finish($adminmenu->parse('item',
  2816                         ($thisUrl == $url) ? 'current' : 'option'));
  2817         $link_array = array($menu_item) + $link_array;
  2818 
  2819         foreach( $link_array as $link )
  2820         {
  2821             $retval .= $link;
  2822         }
  2823 
  2824         $retval .= COM_endBlock( COM_getBlockTemplate( 'admin_block', 'footer', $position ));
  2825     }
  2826 
  2827     return $retval;
  2828 }
  2829 
  2830 /**
  2831 * Redirects user to a given URL
  2832 *
  2833 * This function does a redirect using a meta refresh. This is (or at least
  2834 * used to be) more compatible than using a HTTP Location: header.
  2835 *
  2836 * NOTE:     This does not need to be XHTML compliant. It may also be used
  2837 *           in situations where the XHTML constant is not defined yet ...
  2838 *
  2839 * @param    string  $url    URL to send user to
  2840 * @return   string          HTML meta redirect
  2841 *
  2842 */
  2843 function COM_refresh($url)
  2844 {
  2845     return "<html><head><meta http-equiv=\"refresh\" content=\"0; URL=$url\"></head></html>\n";
  2846 }
  2847 
  2848 /**
  2849  * DEPRECIATED -- see CMT_userComments in lib-comment.php
  2850  * @deprecated since Geeklog 1.4.0
  2851  * @see CMT_userComments
  2852  */
  2853 function COM_userComments( $sid, $title, $type='article', $order='', $mode='', $pid = 0, $page = 1, $cid = false, $delete_option = false )
  2854 {
  2855     global $_CONF;
  2856 
  2857     require_once $_CONF['path_system'] . 'lib-comment.php';
  2858 
  2859     return CMT_userComments( $sid, $title, $type, $order, $mode, $pid, $page, $cid, $delete_option );
  2860 }
  2861 
  2862 /**
  2863 * This censors inappropriate content
  2864 *
  2865 * This will replace 'bad words' with something more appropriate
  2866 *
  2867 * @param        string      $Message        String to check
  2868 * @see function COM_checkHTML
  2869 * @return   string  Edited $Message
  2870 *
  2871 */
  2872 
  2873 function COM_checkWords( $Message )
  2874 {
  2875     global $_CONF;
  2876 
  2877     $EditedMessage = $Message;
  2878 
  2879     if( $_CONF['censormode'] != 0 )
  2880     {
  2881         if( is_array( $_CONF['censorlist'] ))
  2882         {
  2883             $Replacement = $_CONF['censorreplace'];
  2884 
  2885             switch( $_CONF['censormode'])
  2886             {
  2887                 case 1: # Exact match
  2888                     $RegExPrefix = '(\s*)';
  2889                     $RegExSuffix = '(\W*)';
  2890                     break;
  2891 
  2892                 case 2: # Word beginning
  2893                     $RegExPrefix = '(\s*)';
  2894                     $RegExSuffix = '(\w*)';
  2895                     break;
  2896 
  2897                 case 3: # Word fragment
  2898                     $RegExPrefix   = '(\w*)';
  2899                     $RegExSuffix   = '(\w*)';
  2900                     break;
  2901             }
  2902 
  2903             foreach ($_CONF['censorlist'] as $c) {
  2904                 if (!empty($c)) {
  2905                     $EditedMessage = MBYTE_eregi_replace($RegExPrefix . $c
  2906                         . $RegExSuffix, "\\1$Replacement\\2", $EditedMessage);
  2907                 }
  2908             }
  2909         }
  2910     }
  2911 
  2912     return $EditedMessage;
  2913 }
  2914 
  2915 /**
  2916 *  Takes some amount of text and replaces all javascript events on*= with in
  2917 *
  2918 *  This script takes some amount of text and matches all javascript events, on*= (onBlur= onMouseClick=)
  2919 *  and replaces them with in*=
  2920 *  Essentially this will cause onBlur to become inBlur, onFocus to be inFocus
  2921 *  These are not valid javascript events and the browser will ignore them.
  2922 * @param    string  $Message    Text to filter
  2923 * @return   string  $Message with javascript filtered
  2924 * @see  COM_checkWords
  2925 * @see  COM_checkHTML
  2926 *
  2927 */
  2928 
  2929 function COM_killJS( $Message )
  2930 {
  2931     return( preg_replace( '/(\s)+[oO][nN](\w*) ?=/', '\1in\2=', $Message ));
  2932 }
  2933 
  2934 /**
  2935 * Handles the part within a [code] ... [/code] section, i.e. escapes all
  2936 * special characters.
  2937 *
  2938 * @param   string  $str  the code section to encode
  2939 * @return  string  $str with the special characters encoded
  2940 * @see     COM_checkHTML
  2941 *
  2942 */
  2943 function COM_handleCode( $str )
  2944 {
  2945     $search  = array( '&',     '\\',    '<',    '>',    '[',     ']'     );
  2946     $replace = array( '&amp;', '&#92;', '&lt;', '&gt;', '&#91;', '&#93;' );
  2947 
  2948     $str = str_replace( $search, $replace, $str );
  2949 
  2950     return( $str );
  2951 }
  2952 
  2953 /**
  2954 * This function checks html tags.
  2955 *
  2956 * Checks to see that the HTML tags are on the approved list and
  2957 * removes them if not.
  2958 *
  2959 * @param    string  $str            HTML to check
  2960 * @param    string  $permissions    comma-separated list of rights which identify the current user as an "Admin"
  2961 * @return   string                  Filtered HTML
  2962 *
  2963 */
  2964 function COM_checkHTML( $str, $permissions = 'story.edit' )
  2965 {
  2966     global $_CONF;
  2967 
  2968     // replace any \ with &#092; (HTML equiv)
  2969     $str = str_replace('\\', '&#092;', COM_stripslashes($str) );
  2970 
  2971     // Get rid of any newline characters
  2972     $str = preg_replace( "/\n/", '', $str );
  2973 
  2974     // Replace any $ with &#36; (HTML equiv)
  2975     $str = str_replace( '$', '&#36;', $str );
  2976     // handle [code] ... [/code]
  2977     do
  2978     {
  2979         $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[code]' );
  2980         if( $start_pos !== false )
  2981         {
  2982             $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/code]' );
  2983             if( $end_pos !== false )
  2984             {
  2985                 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6,
  2986                         $end_pos - ( $start_pos + 6 )));
  2987                 $encoded = '<pre><code>' . $encoded . '</code></pre>';
  2988                 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
  2989                      . MBYTE_substr( $str, $end_pos + 7 );
  2990             }
  2991             else // missing [/code]
  2992             {
  2993                 // Treat the rest of the text as code (so as not to lose any
  2994                 // special characters). However, the calling entity should
  2995                 // better be checking for missing [/code] before calling this
  2996                 // function ...
  2997                 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6 ));
  2998                 $encoded = '<pre><code>' . $encoded . '</code></pre>';
  2999                 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
  3000             }
  3001         }
  3002     }
  3003     while( $start_pos !== false );
  3004 
  3005     // handle [raw] ... [/raw]
  3006     do
  3007     {
  3008         $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[raw]' );
  3009         if( $start_pos !== false )
  3010         {
  3011             $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/raw]' );
  3012             if( $end_pos !== false )
  3013             {
  3014                 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5,
  3015                         $end_pos - ( $start_pos + 5 )));
  3016                 // [raw2] to avoid infinite loop. Not HTML comment as we strip
  3017                 // them later.
  3018                 $encoded = '[raw2]' . $encoded . '[/raw2]';
  3019                 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
  3020                      . MBYTE_substr( $str, $end_pos + 6 );
  3021             }
  3022             else // missing [/raw]
  3023             {
  3024                 // Treat the rest of the text as raw (so as not to lose any
  3025                 // special characters). However, the calling entity should
  3026                 // better be checking for missing [/raw] before calling this
  3027                 // function ...
  3028                 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5 ));
  3029                 // [raw2] to avoid infinite loop. Not HTML comment as we strip
  3030                 // them later.
  3031                 $encoded = '[raw2]' . $encoded . '[/raw2]';
  3032                 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
  3033             }
  3034         }
  3035     }
  3036     while( $start_pos !== false );
  3037 
  3038     if( isset( $_CONF['skip_html_filter_for_root'] ) &&
  3039              ( $_CONF['skip_html_filter_for_root'] == 1 ) &&
  3040             SEC_inGroup( 'Root' ))
  3041     {
  3042         return $str;
  3043     }
  3044 
  3045     // strip_tags() gets confused by HTML comments ...
  3046     $str = preg_replace( '/<!--.+?-->/', '', $str );
  3047 
  3048     $filter = new kses4;
  3049     if( isset( $_CONF['allowed_protocols'] ) && is_array( $_CONF['allowed_protocols'] ) && ( count( $_CONF['allowed_protocols'] ) > 0 ))
  3050     {
  3051         $filter->SetProtocols( $_CONF['allowed_protocols'] );
  3052     }
  3053     else
  3054     {
  3055         $filter->SetProtocols( array( 'http:', 'https:', 'ftp:' ));
  3056     }
  3057 
  3058     if( empty( $permissions) || !SEC_hasRights( $permissions ) ||
  3059             empty( $_CONF['admin_html'] ))
  3060     {
  3061         $html = $_CONF['user_html'];
  3062     }
  3063     else
  3064     {
  3065         if ($_CONF['advanced_editor'] && is_array($_CONF['advanced_html'])) {
  3066             $html = array_merge_recursive( $_CONF['user_html'],
  3067                                            $_CONF['admin_html'],
  3068                                            $_CONF['advanced_html'] );
  3069         } else {
  3070             $html = array_merge_recursive( $_CONF['user_html'],
  3071                                            $_CONF['admin_html'] );
  3072         }
  3073     }
  3074 
  3075     foreach( $html as $tag => $attr )
  3076     {
  3077         $filter->AddHTML( $tag, $attr );
  3078     }
  3079     /* Replace [raw][/raw] with <!--raw--><!--/raw-->, note done "late" because
  3080      * of the above noted // strip_tags() gets confused by HTML comments ...
  3081      */
  3082     $str = $filter->Parse( $str );
  3083     $str = str_replace('[raw2]','<!--raw--><span class="raw">', $str);
  3084     $str = str_replace('[/raw2]','</span><!--/raw-->', $str);
  3085 
  3086     return $str;
  3087 }
  3088 
  3089 /**
  3090 * undo function for htmlspecialchars()
  3091 *
  3092 * This function translates HTML entities created by htmlspecialchars() back
  3093 * into their ASCII equivalents. Also handles the entities for $, {, and }.
  3094 *
  3095 * @param    string   $string   The string to convert.
  3096 * @return   string   The converted string.
  3097 *
  3098 */
  3099 function COM_undoSpecialChars( $string )
  3100 {
  3101     $string = str_replace( '&#36;',  '$', $string );
  3102     $string = str_replace( '&#123;', '{', $string );
  3103     $string = str_replace( '&#125;', '}', $string );
  3104     $string = str_replace( '&gt;',   '>', $string );
  3105     $string = str_replace( '&lt;',   '<', $string );
  3106     $string = str_replace( '&quot;', '"', $string );
  3107     $string = str_replace( '&nbsp;', ' ', $string );
  3108     $string = str_replace( '&amp;',  '&', $string );
  3109 
  3110     return( $string );
  3111 }
  3112 
  3113 /**
  3114 * Makes an ID based on current date/time
  3115 *
  3116 * This function creates a 17 digit sid for stories based on the 14 digit date
  3117 * and a 3 digit random number that was seeded with the number of microseconds
  3118 * (.000001th of a second) since the last full second.
  3119 * NOTE: this is now used for more than just stories!
  3120 *
  3121 * @return   string  $sid  Story ID
  3122 *
  3123 */
  3124 function COM_makesid()
  3125 {
  3126     $sid = date( 'YmdHis' );
  3127     $sid .= rand( 0, 999 );
  3128 
  3129     return $sid;
  3130 }
  3131 
  3132 /**
  3133 * Checks to see if email address is valid.
  3134 *
  3135 * This function checks to see if an email address is in the correct from.
  3136 *
  3137 * @param    string    $email   Email address to verify
  3138 * @return   boolean            True if valid otherwise false
  3139 *
  3140 */
  3141 function COM_isEmail( $email )
  3142 {
  3143     require_once( 'Mail/RFC822.php' );
  3144 
  3145     $rfc822 = new Mail_RFC822;
  3146 
  3147     return( $rfc822->isValidInetAddress( $email ) ? true : false );
  3148 }
  3149 
  3150 
  3151 /**
  3152 * Encode a string such that it can be used in an email header
  3153 *
  3154 * @param    string  $string     the text to be encoded
  3155 * @return   string              encoded text
  3156 *
  3157 */
  3158 function COM_emailEscape( $string )
  3159 {
  3160     global $_CONF;
  3161 
  3162     if (function_exists('CUSTOM_emailEscape')) {
  3163         return CUSTOM_emailEscape($string);
  3164     }
  3165 
  3166     $charset = COM_getCharset();
  3167     if(( $charset == 'utf-8' ) && ( $string != utf8_decode( $string )))
  3168     {
  3169         if( function_exists( 'iconv_mime_encode' ))
  3170         {
  3171             $mime_parameters = array( 'input-charset'  => 'utf-8',
  3172                                       'output-charset' => 'utf-8',
  3173                                       // 'Q' encoding is more readable than 'B'
  3174                                       'scheme'         => 'Q'
  3175                                     );
  3176             $string = substr( iconv_mime_encode( '', $string,
  3177                                                  $mime_parameters ), 2 );
  3178         }
  3179         else
  3180         {
  3181             $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
  3182         }
  3183     }
  3184     else if( preg_match( '/[^0-9a-z\-\.,:;\?! ]/i', $string ))
  3185     {
  3186         $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
  3187     }
  3188 
  3189     return $string;
  3190 }
  3191 
  3192 /**
  3193 * Takes a name and an email address and returns a string that vaguely
  3194 * resembles an email address specification conforming to RFC(2)822 ...
  3195 *
  3196 * @param    string  $name       name, e.g. John Doe
  3197 * @param    string  $address    email address only, e.g. john.doe@example.com
  3198 * @return   string              formatted email address
  3199 *
  3200 */
  3201 function COM_formatEmailAddress($name, $address)
  3202 {
  3203     $name = trim($name);
  3204     $address = trim($address);
  3205 
  3206     if (function_exists('CUSTOM_formatEmailAddress')) {
  3207         return CUSTOM_formatEmailAddress($name, $address);
  3208     }
  3209 
  3210     $formatted_name = COM_emailEscape($name);
  3211 
  3212     // if the name comes back unchanged, it's not UTF-8, so preg_match is fine
  3213     if (($formatted_name == $name) && preg_match('/[^0-9a-z ]/i', $name)) {
  3214         $formatted_name = str_replace('"', '\\"', $formatted_name);
  3215         $formatted_name = '"' . $formatted_name . '"';
  3216     }
  3217 
  3218     return $formatted_name . ' <' . $address . '>';
  3219 }
  3220 
  3221 /**
  3222 * Send an email.
  3223 *
  3224 * All emails sent by Geeklog are sent through this function.
  3225 *
  3226 * NOTE: Please note that using CC: will expose the email addresses of
  3227 *       all recipients. Use with care.
  3228 *
  3229 * @param    string      $to         recipients name and email address
  3230 * @param    string      $subject    subject of the email
  3231 * @param    string      $message    the text of the email
  3232 * @param    string      $from       (optional) sender of the the email
  3233 * @param    boolean     $html       (optional) true if to be sent as HTML email
  3234 * @param    int         $priority   (optional) add X-Priority header, if > 0
  3235 * @param    mixed       $optional   (optional) other headers or CC:
  3236 * @return   boolean                 true if successful,  otherwise false
  3237 *
  3238 */
  3239 function COM_mail($to, $subject, $message, $from = '', $html = false, $priority = 0, $optional = null)
  3240 {
  3241     global $_CONF;
  3242 
  3243     static $mailobj;
  3244 
  3245     if (empty($from)) {
  3246         $from = COM_formatEmailAddress($_CONF['site_name'], $_CONF['site_mail']);
  3247     }
  3248 
  3249     $to = substr($to, 0, strcspn($to, "\r\n"));
  3250     if (($optional != null) && !is_array($optional)) {
  3251         $optional = substr($optional, 0, strcspn($optional, "\r\n"));
  3252     }
  3253     $from = substr($from, 0, strcspn($from, "\r\n"));
  3254     $subject = substr($subject, 0, strcspn($subject, "\r\n"));
  3255     $subject = COM_emailEscape($subject);
  3256 
  3257     if (function_exists('CUSTOM_mail')) {
  3258         return CUSTOM_mail($to, $subject, $message, $from, $html, $priority,
  3259                            $optional);
  3260     }
  3261 
  3262     include_once 'Mail.php';
  3263     include_once 'Mail/RFC822.php';
  3264 
  3265     $method = $_CONF['mail_settings']['backend'];
  3266 
  3267     if (! isset($mailobj)) {
  3268         if (($method == 'sendmail') || ($method == 'smtp')) {
  3269             $mailobj =& Mail::factory($method, $_CONF['mail_settings']);
  3270         } else {
  3271             $method = 'mail';
  3272             $mailobj =& Mail::factory($method);
  3273         }
  3274     }
  3275 
  3276     $charset = COM_getCharset();
  3277     $headers = array();
  3278 
  3279     $headers['From'] = $from;
  3280     if ($method != 'mail') {
  3281         $headers['To'] = $to;
  3282     }
  3283     if (($optional != null) && !is_array($optional) && !empty($optional)) {
  3284         // assume old (optional) CC: header
  3285         $headers['Cc'] = $optional;
  3286     }
  3287     $headers['Date'] = date('r'); // RFC822 formatted date
  3288     if($method == 'smtp') {
  3289         list($usec, $sec) = explode(' ', microtime());
  3290         $m = substr($usec, 2, 5);
  3291         $headers['Message-Id'] = '<' .  date('YmdHis') . '.' . $m
  3292                                . '@' . $_CONF['mail_settings']['host'] . '>';
  3293     }
  3294     if ($html) {
  3295         $headers['Content-Type'] = 'text/html; charset=' . $charset;
  3296         $headers['Content-Transfer-Encoding'] = '8bit';
  3297     } else {
  3298         $headers['Content-Type'] = 'text/plain; charset=' . $charset;
  3299     }
  3300     $headers['Subject'] = $subject;
  3301     if ($priority > 0) {
  3302         $headers['X-Priority'] = $priority;
  3303     }
  3304     $headers['X-Mailer'] = 'Geeklog ' . VERSION;
  3305 
  3306     if (!empty($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['SERVER_ADDR']) &&
  3307             ($_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR'])) {
  3308         $url = COM_getCurrentURL();
  3309         if (substr($url, 0, strlen($_CONF['site_admin_url']))
  3310                 != $_CONF['site_admin_url']) {
  3311             $headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
  3312         }
  3313     }
  3314 
  3315     // add optional headers last
  3316     if (($optional != null) && is_array($optional)) {
  3317         foreach ($optional as $h => $v) {
  3318             $headers[$h] = $v;
  3319         }
  3320     }
  3321 
  3322     $retval = $mailobj->send($to, $headers, $message);
  3323     if ($retval !== true) {
  3324         COM_errorLog($retval->toString(), 1);
  3325     }
  3326 
  3327     return($retval === true ? true : false);
  3328 }
  3329 
  3330 
  3331 /**
  3332 * Creates older stuff block
  3333 *
  3334 * Creates the olderstuff block for display.
  3335 * Actually updates the olderstuff record in the gl_blocks database.
  3336 * @return   void
  3337 */
  3338 
  3339 function COM_olderStuff()
  3340 {
  3341     global $_TABLES, $_CONF;
  3342 
  3343     $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']}";
  3344     $result = DB_query( $sql );
  3345     $nrows = DB_numRows( $result );
  3346 
  3347     if( $nrows > 0 )
  3348     {
  3349         $dateonly = $_CONF['dateonly'];
  3350         if( empty( $dateonly ))
  3351         {
  3352             $dateonly = '%d-%b'; // fallback: day - abbrev. month name
  3353         }
  3354 
  3355         $day = 'noday';
  3356         $string = '';
  3357 
  3358         for( $i = 0; $i < $nrows; $i++ )
  3359         {
  3360             $A = DB_fetchArray( $result );
  3361 
  3362             $daycheck = strftime( '%A', $A['day'] );
  3363             if( $day != $daycheck )
  3364             {
  3365                 if( $day != 'noday' )
  3366                 {
  3367                     $daylist = COM_makeList($oldnews, 'list-older-stories');
  3368                     $daylist = str_replace(array("\015", "\012"), '', $daylist);
  3369                     $string .= $daylist . '<br' . XHTML . '>';
  3370                 }
  3371 
  3372                 $day2 = strftime( $dateonly, $A['day'] );
  3373                 $string .= '<h3>' . $daycheck . ' <small>' . $day2
  3374                         . '</small></h3>' . LB;
  3375                 $oldnews = array();
  3376                 $day = $daycheck;
  3377             }
  3378 
  3379             $oldnews_url = COM_buildUrl( $_CONF['site_url'] . '/article.php?story='
  3380                 . $A['sid'] );
  3381             $oldnews[] = COM_createLink($A['title'], $oldnews_url)
  3382                 .' (' . COM_numberFormat( $A['comments'] ) . ')';
  3383         }
  3384 
  3385         if( !empty( $oldnews ))
  3386         {
  3387             $daylist = COM_makeList($oldnews, 'list-older-stories');
  3388             $daylist = str_replace(array("\015", "\012"), '', $daylist);
  3389             $string .= $daylist;
  3390             $string = addslashes( $string );
  3391 
  3392             DB_query( "UPDATE {$_TABLES['blocks']} SET content = '$string' WHERE name = 'older_stories'" );
  3393         }
  3394     }
  3395 }
  3396 
  3397 /**
  3398 * Shows a single Geeklog block
  3399 *
  3400 * This shows a single block and is typically called from
  3401 * COM_showBlocks OR from plugin code
  3402 *
  3403 * @param        string      $name       Logical name of block (not same as title) -- 'user_block', 'admin_block', 'section_block', 'whats_new_block'.
  3404 * @param        string      $help       Help file location
  3405 * @param        string      $title      Title shown in block header
  3406 * @param        string      $position   Side, 'left', 'right' or empty.
  3407 * @see function COM_showBlocks
  3408 * @return   string  HTML Formated block
  3409 *
  3410 */
  3411 
  3412 function COM_showBlock( $name, $help='', $title='', $position='' )
  3413 {
  3414     global $_CONF, $topic, $_TABLES, $_USER;
  3415 
  3416     $retval = '';
  3417 
  3418     if( !isset( $_USER['noboxes'] ))
  3419     {
  3420         if( !COM_isAnonUser() )
  3421         {
  3422             $_USER['noboxes'] = DB_getItem( $_TABLES['userindex'], 'noboxes',
  3423                                             "uid = {$_USER['uid']}" );
  3424         }
  3425         else
  3426         {
  3427             $_USER['noboxes'] = 0;
  3428         }
  3429     }
  3430 
  3431     switch( $name )
  3432     {
  3433         case 'user_block':
  3434             $retval .= COM_userMenu( $help,$title, $position );
  3435             break;
  3436 
  3437         case 'admin_block':
  3438             $retval .= COM_adminMenu( $help,$title, $position );
  3439             break;
  3440 
  3441         case 'section_block':
  3442             $retval .= COM_startBlock( $title, $help,
  3443                                COM_getBlockTemplate( $name, 'header', $position ))
  3444                 . COM_showTopics( $topic )
  3445                 . COM_endBlock( COM_getBlockTemplate( $name, 'footer', $position ));
  3446             break;
  3447 
  3448         case 'whats_new_block':
  3449             if( !$_USER['noboxes'] )
  3450             {
  3451                 $retval .= COM_whatsNewBlock( $help, $title, $position );
  3452             }
  3453             break;
  3454     }
  3455 
  3456     return $retval;
  3457 }
  3458 
  3459 
  3460 /**
  3461 * Shows Geeklog blocks
  3462 *
  3463 * Returns HTML for blocks on a given side and, potentially, for
  3464 * a given topic. Currently only used by static pages.
  3465 *
  3466 * @param        string      $side       Side to get blocks for (right or left for now)
  3467 * @param        string      $topic      Only get blocks for this topic
  3468 * @param        string      $name       Block name (not used)
  3469 * @see function COM_showBlock
  3470 * @return   string  HTML Formated blocks
  3471 *
  3472 */
  3473 
  3474 function COM_showBlocks( $side, $topic='', $name='all' )
  3475 {
  3476     global $_CONF, $_TABLES, $_USER, $LANG21, $topic, $page;
  3477 
  3478     $retval = '';
  3479 
  3480     // Get user preferences on blocks
  3481     if( !isset( $_USER['noboxes'] ) || !isset( $_USER['boxes'] ))
  3482     {
  3483         if( !COM_isAnonUser() )
  3484         {
  3485             $result = DB_query( "SELECT boxes,noboxes FROM {$_TABLES['userindex']} "
  3486                                ."WHERE uid = '{$_USER['uid']}'" );
  3487             list($_USER['boxes'], $_USER['noboxes']) = DB_fetchArray( $result );
  3488         }
  3489         else
  3490         {
  3491             $_USER['boxes'] = '';
  3492             $_USER['noboxes'] = 0;
  3493         }
  3494     }
  3495 
  3496     $blocksql['mssql']  = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
  3497     $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
  3498     $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
  3499 
  3500     $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
  3501 
  3502     $commonsql = "FROM {$_TABLES['blocks']} WHERE is_enabled = 1";
  3503 
  3504     if( $side == 'left' )
  3505     {
  3506         $commonsql .= " AND onleft = 1";
  3507     }
  3508     else
  3509     {
  3510         $commonsql .= " AND onleft = 0";
  3511     }
  3512 
  3513     if( !empty( $topic ))
  3514     {
  3515         $tp = addslashes($topic);
  3516         $commonsql .= " AND (tid = '$tp' OR tid = 'all')";
  3517     }
  3518     else
  3519     {
  3520         if( COM_onFrontpage() )
  3521         {
  3522             $commonsql .= " AND (tid = 'homeonly' OR tid = 'all')";
  3523         }
  3524         else
  3525         {
  3526             $commonsql .= " AND (tid = 'all')";
  3527         }
  3528     }
  3529 
  3530     if( !empty( $_USER['boxes'] ))
  3531     {
  3532         $BOXES = str_replace( ' ', ',', $_USER['boxes'] );
  3533 
  3534         $commonsql .= " AND (bid NOT IN ($BOXES) OR bid = '-1')";
  3535     }
  3536 
  3537     $commonsql .= ' ORDER BY blockorder,title ASC';
  3538 
  3539     $blocksql['mysql'] .= $commonsql;
  3540     $blocksql['mssql'] .= $commonsql;
  3541     $result = DB_query( $blocksql );
  3542     $nrows = DB_numRows( $result );
  3543 
  3544     // convert result set to an array of associated arrays
  3545     $blocks = array();
  3546     for( $i = 0; $i < $nrows; $i++ )
  3547     {
  3548         $blocks[] = DB_fetchArray( $result );
  3549     }
  3550 
  3551     // Check and see if any plugins have blocks to show
  3552     $pluginBlocks = PLG_getBlocks( $side, $topic, $name );
  3553     $blocks = array_merge( $blocks, $pluginBlocks );
  3554 
  3555     // sort the resulting array by block order
  3556     $column = 'blockorder';
  3557     $sortedBlocks = $blocks;
  3558     $num_sortedBlocks = count( $sortedBlocks );
  3559     for( $i = 0; $i < $num_sortedBlocks - 1; $i++ )
  3560     {
  3561         for( $j = 0; $j < $num_sortedBlocks - 1 - $i; $j++ )
  3562         {
  3563             if( $sortedBlocks[$j][$column] > $sortedBlocks[$j+1][$column] )
  3564             {
  3565                 $tmp = $sortedBlocks[$j];
  3566                 $sortedBlocks[$j] = $sortedBlocks[$j + 1];
  3567                 $sortedBlocks[$j + 1] = $tmp;
  3568             }
  3569         }
  3570     }
  3571     $blocks = $sortedBlocks;
  3572 
  3573     // Loop though resulting sorted array and pass associative arrays
  3574     // to COM_formatBlock
  3575     foreach( $blocks as $A )
  3576     {
  3577         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 )
  3578         {
  3579            $retval .= COM_formatBlock( $A, $_USER['noboxes'] );
  3580         }
  3581     }
  3582 
  3583     return $retval;
  3584 }
  3585 
  3586 /**
  3587 * Formats a Geeklog block
  3588 *
  3589 * This shows a single block and is typically called from
  3590 * COM_showBlocks OR from plugin code
  3591 *
  3592 * @param        array     $A          Block Record
  3593 * @param        boolean   $noboxes    Set to true if userpref is no blocks
  3594 * @return       string    HTML Formated block
  3595 *
  3596 */
  3597 function COM_formatBlock( $A, $noboxes = false )
  3598 {
  3599     global $_CONF, $_TABLES, $_USER, $LANG21;
  3600 
  3601     $retval = '';
  3602 
  3603     $lang = COM_getLanguageId();
  3604     if (!empty($lang)) {
  3605 
  3606         $blocksql['mssql']  = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
  3607         $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
  3608         $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
  3609 
  3610         $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
  3611 
  3612         $commonsql = "FROM {$_TABLES['blocks']} WHERE name = '"
  3613                    . $A['name'] . '_' . $lang . "'";
  3614 
  3615         $blocksql['mysql'] .= $commonsql;
  3616         $blocksql['mssql'] .= $commonsql;
  3617         $result = DB_query( $blocksql );
  3618 
  3619         if (DB_numRows($result) == 1) {
  3620             // overwrite with data for language-specific block
  3621             $A = DB_fetchArray($result);
  3622         }
  3623     }
  3624     
  3625     if( array_key_exists( 'onleft', $A ) )
  3626     {
  3627         if( $A['onleft'] == 1 )
  3628         {
  3629             $position = 'left';
  3630         } else {
  3631             $position = 'right';
  3632         }
  3633     } else {
  3634         $position = '';
  3635     }
  3636 
  3637     if( $A['type'] == 'portal' )
  3638     {
  3639         if( COM_rdfCheck( $A['bid'], $A['rdfurl'], $A['date'], $A['rdflimit'] ))
  3640         {
  3641             $A['content'] = DB_getItem( $_TABLES['blocks'], 'content',
  3642                                         "bid = '{$A['bid']}'");
  3643         }
  3644     }
  3645 
  3646     if( $A['type'] == 'gldefault' )
  3647     {
  3648         $retval .= COM_showBlock( $A['name'], $A['help'], $A['title'], $position );
  3649     }
  3650 
  3651     if( $A['type'] == 'phpblock' && !$noboxes )
  3652     {
  3653         if( !( $A['name'] == 'whosonline_block' AND DB_getItem( $_TABLES['blocks'], 'is_enabled', "name='whosonline_block'" ) == 0 ))
  3654         {
  3655             $function = $A['phpblockfn'];
  3656             $matches = array();
  3657             if (preg_match('/^(phpblock_\w*)\\((.*)\\)$/', $function, $matches) == 1)
  3658             {
  3659                 $function = $matches[1];
  3660                 $args = $matches[2];
  3661             }
  3662             $blkheader = COM_startBlock( $A['title'], $A['help'],
  3663                     COM_getBlockTemplate( $A['name'], 'header', $position ));
  3664             $blkfooter = COM_endBlock( COM_getBlockTemplate( $A['name'],
  3665                     'footer', $position ));
  3666 
  3667             if( function_exists( $function ))
  3668             {
  3669                if (isset($args))
  3670                {
  3671                     $fretval = $function($A, $args);
  3672                } else {
  3673                     $fretval = $function();
  3674                }
  3675                if( !empty( $fretval ))
  3676                {
  3677                     $retval .= $blkheader;
  3678                     $retval .= $fretval;
  3679                     $retval .= $blkfooter;
  3680                }
  3681             }
  3682             else
  3683             {
  3684                 // show error message
  3685                 $retval .= $blkheader;
  3686                 $retval .= sprintf( $LANG21[31], $function );
  3687                 $retval .= $blkfooter;
  3688             }
  3689         }
  3690     }
  3691 
  3692     if( !empty( $A['content'] ) && ( trim( $A['content'] ) != '' ) && !$noboxes )
  3693     {
  3694         $blockcontent = stripslashes( $A['content'] );
  3695 
  3696         // Hack: If the block content starts with a '<' assume it
  3697         // contains HTML and do not call nl2br() which would only add
  3698         // unwanted <br> tags.
  3699 
  3700         if( substr( $blockcontent, 0, 1 ) != '<' )
  3701         {
  3702             $blockcontent = nl2br( $blockcontent );
  3703         }
  3704 
  3705         // autotags are only(!) allowed in normal blocks
  3706         if(( $A['allow_autotags'] == 1 ) && ( $A['type'] == 'normal' ))
  3707         {
  3708             $blockcontent = PLG_replaceTags( $blockcontent );
  3709         }
  3710         $blockcontent = str_replace( array( '<?', '?>' ), '', $blockcontent );
  3711 
  3712         $retval .= COM_startBlock( $A['title'], $A['help'],
  3713                        COM_getBlockTemplate( $A['name'], 'header', $position ))
  3714                 . $blockcontent . LB
  3715                 . COM_endBlock( COM_getBlockTemplate( $A['name'], 'footer', $position ));
  3716     }
  3717 
  3718     return $retval;
  3719 }
  3720 
  3721 
  3722 /**
  3723 * Checks to see if it's time to import and RDF/RSS block again
  3724 *
  3725 * Updates RDF/RSS block if needed
  3726 *
  3727 * @param    string  $bid            Block ID
  3728 * @param    string  $rdfurl         URL to get headlines from
  3729 * @param    string  $date           Last time the headlines were imported
  3730 * @param    string  $maxheadlines   max. number of headlines to import
  3731 * @return   void
  3732 * @see function COM_rdfImport
  3733 *
  3734 */
  3735 function COM_rdfCheck( $bid, $rdfurl, $date, $maxheadlines = 0 )
  3736 {
  3737     $retval = false;
  3738     $nextupdate = $date + 3600;
  3739 
  3740     if( $nextupdate < time() )
  3741     {
  3742         COM_rdfImport( $bid, $rdfurl, $maxheadlines );
  3743         $retval = true;
  3744     }
  3745 
  3746     return $retval;
  3747 }
  3748 
  3749 /**
  3750 * Syndication import function. Imports headline data to a portal block.
  3751 *
  3752 * Rewritten December 19th 2004 by Michael Jervis (mike AT fuckingbrit DOT com).
  3753 * Now utilises a Factory Pattern to open a URL and automaticaly retreive a feed
  3754 * object populated with feed data. Then import it into the portal block.
  3755 *
  3756 * @param    string  $bid            Block ID
  3757 * @param    string  $rdfurl         URL to get content from
  3758 * @param    int     $maxheadlines   Maximum number of headlines to display
  3759 * @return   void
  3760 * @see function COM_rdfCheck
  3761 *
  3762 */
  3763 function COM_rdfImport($bid, $rdfurl, $maxheadlines = 0)
  3764 {
  3765     global $_CONF, $_TABLES, $LANG21;
  3766 
  3767     // Import the feed handling classes:
  3768     require_once $_CONF['path_system']
  3769                  . '/classes/syndication/parserfactory.class.php';
  3770     require_once $_CONF['path_system']
  3771                  . '/classes/syndication/feedparserbase.class.php';
  3772 
  3773     $result = DB_query("SELECT rdf_last_modified, rdf_etag FROM {$_TABLES['blocks']} WHERE bid = $bid");
  3774     list($last_modified, $etag) = DB_fetchArray($result);
  3775 
  3776     // Load the actual feed handlers:
  3777     $factory = new FeedParserFactory($_CONF['path_system']
  3778                                      . '/classes/syndication/');
  3779     $factory->userAgent = 'Geeklog/' . VERSION;
  3780     if (!empty($last_modified) && !empty($etag)) {
  3781         $factory->lastModified = $last_modified;
  3782         $factory->eTag = $etag;
  3783     }
  3784 
  3785     // Aquire a reader:
  3786     $feed = $factory->reader($rdfurl, $_CONF['default_charset']);
  3787 
  3788     if ($feed) {
  3789         /* We have located a reader, and populated it with the information from
  3790          * the syndication file. Now we will sort out our display, and update
  3791          * the block.
  3792          */
  3793         if ($maxheadlines == 0) {
  3794             if (!empty($_CONF['syndication_max_headlines'])) {
  3795                 $maxheadlines = $_CONF['syndication_max_headlines'];
  3796             } else {
  3797                 $maxheadlines = count($feed->articles);
  3798             }
  3799         }
  3800 
  3801         $update = date('Y-m-d H:i:s');
  3802         $last_modified = '';
  3803         if (!empty($factory->lastModified)) {
  3804             $last_modified = addslashes($factory->lastModified);
  3805         }
  3806         $etag = '';
  3807         if (!empty($factory->eTag)) {
  3808             $etag = addslashes($factory->eTag);
  3809         }
  3810 
  3811         if (empty($last_modified) || empty($etag)) {
  3812             DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = '$bid'");
  3813         } else {
  3814             DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = '$last_modified', rdf_etag = '$etag' WHERE bid = '$bid'");
  3815         }
  3816 
  3817         $charset = COM_getCharset();
  3818 
  3819         // format articles for display
  3820         $readmax = min($maxheadlines, count($feed->articles));
  3821         for ($i = 0; $i < $readmax; $i++) {
  3822             if (empty($feed->articles[$i]['title'])) {
  3823                 $feed->articles[$i]['title'] = $LANG21[61];
  3824             }
  3825 
  3826             if ($charset == 'utf-8') {
  3827                 $title = $feed->articles[$i]['title'];
  3828             } else {
  3829                 $title = utf8_decode($feed->articles[$i]['title']);
  3830             }
  3831             if ($feed->articles[$i]['link'] != '') {
  3832                 $content = COM_createLink($title, $feed->articles[$i]['link']);
  3833             } elseif ($feed->articles[$i]['enclosureurl'] != '') {
  3834                 $content = COM_createLink($title, $feed->articles[$i]['enclosureurl']);
  3835             } else {
  3836                 $content = $title;
  3837             }
  3838             $articles[] = $content;
  3839         }
  3840 
  3841         // build a list
  3842         $content = COM_makeList($articles, 'list-feed');
  3843         $content = str_replace(array("\015", "\012"), '', $content);
  3844 
  3845         if (strlen($content) > 65000) {
  3846             $content = $LANG21[68];
  3847         }
  3848 
  3849         // Standard theme based function to put it in the block
  3850         $result = DB_change($_TABLES['blocks'], 'content',
  3851                             addslashes($content), 'bid', $bid);
  3852     } else if ($factory->errorStatus !== false) {
  3853         // failed to aquire info, 0 out the block and log an error
  3854         COM_errorLog("Unable to aquire feed reader for $rdfurl", 1);
  3855         COM_errorLog($factory->errorStatus[0] . ' ' .
  3856                      $factory->errorStatus[1] . ' ' .
  3857                      $factory->errorStatus[2]);
  3858         $content = addslashes($LANG21[4]);
  3859         DB_query("UPDATE {$_TABLES['blocks']} SET content = '$content', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = $bid");
  3860     }
  3861 }
  3862 
  3863 
  3864 /**
  3865 * Returns what HTML is allowed in content
  3866 *
  3867 * Returns what HTML tags the system allows to be used inside content.
  3868 * You can modify this by changing $_CONF['user_html'] in the configuration
  3869 * (for admins, see also $_CONF['admin_html']).
  3870 *
  3871 * @param    string  $permissions    comma-separated list of rights which identify the current user as an "Admin"
  3872 * @param    boolean $list_only      true = return only the list of HTML tags
  3873 * @return   string                  HTML <div>/<span> enclosed string
  3874 * @see      function COM_checkHTML
  3875 * @todo     Bugs: The list always includes the [code], [raw], and [page_break]
  3876 *           tags when story.* permissions are required, even when those tags
  3877 *           are not actually available (e.g. in comments on stories).
  3878 *
  3879 */
  3880 function COM_allowedHTML($permissions = 'story.edit', $list_only = false)
  3881 {
  3882     global $_CONF, $LANG01;
  3883 
  3884     $retval = '';
  3885 
  3886     if (isset($_CONF['skip_html_filter_for_root']) &&
  3887              ($_CONF['skip_html_filter_for_root'] == 1) &&
  3888             SEC_inGroup('Root')) {
  3889 
  3890         if (!$list_only) {
  3891             $retval .= '<span class="warningsmall">' . $LANG01[123]
  3892                     . ',</span> ';
  3893         }
  3894         $retval .= '<div dir="ltr" class="warningsmall">';
  3895 
  3896     } else {
  3897 
  3898         if (! $list_only) {
  3899             $retval .= '<span class="warningsmall">' . $LANG01[31] . '</span> ';
  3900         }
  3901 
  3902         if (empty($permissions) || !SEC_hasRights($permissions) ||
  3903                 empty($_CONF['admin_html'])) {
  3904             $html = $_CONF['user_html'];
  3905         } else {
  3906             $html = array_merge_recursive($_CONF['user_html'],
  3907                                           $_CONF['admin_html']);
  3908         }
  3909 
  3910         $retval .= '<div dir="ltr" class="warningsmall">';
  3911         foreach ($html as $tag => $attr) {
  3912             $retval .= '&lt;' . $tag . '&gt;, ';
  3913         }
  3914     }
  3915 
  3916     $with_story_perms = false;
  3917     $perms = explode(',', $permissions);
  3918     foreach ($perms as $p) {
  3919         if (substr($p, 0, 6) == 'story.') {
  3920             $with_story_perms = true;
  3921             break;
  3922         }
  3923     }
  3924 
  3925     if ($with_story_perms) {
  3926         $retval .= '[code], [raw], ';
  3927 
  3928         if ($_CONF['allow_page_breaks'] == 1) {
  3929             $retval .= '[page_break], ';
  3930         }
  3931     }
  3932 
  3933     // list autotags
  3934     $autotags = array_keys(PLG_collectTags());
  3935     $retval .= '[' . implode(':], [', $autotags) . ':]';
  3936     $retval .= '</div>';
  3937 
  3938     return $retval;
  3939 }
  3940 
  3941 /**
  3942 * Return the password for the given username
  3943 *
  3944 * Fetches a password for the given user
  3945 *
  3946 * @param    string  $loginname  username to get password for
  3947 * @return   string              Password or ''
  3948 *
  3949 */
  3950 
  3951 function COM_getPassword( $loginname )
  3952 {
  3953     global $_TABLES, $LANG01;
  3954 
  3955     $result = DB_query( "SELECT passwd FROM {$_TABLES['users']} WHERE username='$loginname'" );
  3956     $tmp = DB_error();
  3957     $nrows = DB_numRows( $result );
  3958 
  3959     if(( $tmp == 0 ) && ( $nrows == 1 ))
  3960     {
  3961         $U = DB_fetchArray( $result );
  3962         return $U['passwd'];
  3963     }
  3964     else
  3965     {
  3966         $tmp = $LANG01[32] . ": '" . $loginname . "'";
  3967         COM_errorLog( $tmp, 1 );
  3968     }
  3969 
  3970     return '';
  3971 }
  3972 
  3973 
  3974 /**
  3975 * Return the username or fullname for the passed member id (uid)
  3976 *
  3977 * Allows the siteAdmin to determine if loginname (username) or fullname
  3978 * should be displayed.
  3979 *
  3980 * @param    int     $uid        site member id
  3981 * @param    string  $username   Username, if this is set no lookup is done.
  3982 * @param    string  $fullname   Users full name.
  3983 * @param    string  $remoteusername  Username on remote service
  3984 * @param    string  $remoteservice   Remote login service.
  3985 * @return   string  Username, fullname or username@Service
  3986 *
  3987 */
  3988 function COM_getDisplayName( $uid = '', $username='', $fullname='', $remoteusername='', $remoteservice='' )
  3989 {
  3990     global $_CONF, $_TABLES, $_USER;
  3991 
  3992     if ($uid == '')
  3993     {
  3994         if( COM_isAnonUser() )
  3995         {
  3996             $uid = 1;
  3997         }
  3998         else
  3999         {
  4000             $uid = $_USER['uid'];
  4001         }
  4002     }
  4003 
  4004     if( empty( $username ))
  4005     {
  4006         $query = DB_query( "SELECT username, fullname, remoteusername, remoteservice FROM {$_TABLES['users']} WHERE uid='$uid'" );
  4007         list( $username, $fullname, $remoteusername, $remoteservice ) = DB_fetchArray( $query );
  4008     }
  4009 
  4010     if( !empty( $fullname ) && ($_CONF['show_fullname'] == 1 ))
  4011     {
  4012         return $fullname;
  4013     }
  4014     else if(( $_CONF['user_login_method']['3rdparty'] || $_CONF['user_login_method']['openid'] ) && !empty( $remoteusername ))
  4015     {
  4016         if( !empty( $username ))
  4017         {
  4018             $remoteusername = $username;
  4019         }
  4020 
  4021         if( $_CONF['show_servicename'] )
  4022         {
  4023             return "$remoteusername@$remoteservice";
  4024         }
  4025         else
  4026         {
  4027             return $remoteusername;
  4028         }
  4029     }
  4030 
  4031     return $username;
  4032 }
  4033 
  4034 
  4035 /**
  4036 * Adds a hit to the system
  4037 *
  4038 * This function is called in the footer of every page and is used to
  4039 * track the number of hits to the Geeklog system.  This information is
  4040 * shown on stats.php
  4041 *
  4042 */
  4043 
  4044 function COM_hit()
  4045 {
  4046     global $_TABLES;
  4047 
  4048     DB_change($_TABLES['vars'], 'value', 'value + 1', 'name', 'totalhits', '', true);
  4049 }
  4050 
  4051 /**
  4052 * This will email new stories in the topics that the user is interested in
  4053 *
  4054 * In account information the user can specify which topics for which they
  4055 * will receive any new article for in a daily digest.
  4056 *
  4057 * @return   void
  4058 */
  4059 
  4060 function COM_emailUserTopics()
  4061 {
  4062     global $_CONF, $_TABLES, $LANG04, $LANG08, $LANG24;
  4063 
  4064     if ($_CONF['emailstories'] == 0) {
  4065         return;
  4066     }
  4067 
  4068     $subject = strip_tags( $_CONF['site_name'] . $LANG08[30] . strftime( '%Y-%m-%d', time() ));
  4069 
  4070     $authors = array();
  4071 
  4072     // Get users who want stories emailed to them
  4073     $usersql = "SELECT username,email,etids,{$_TABLES['users']}.uid AS uuid "
  4074         . "FROM {$_TABLES['users']}, {$_TABLES['userindex']} "
  4075         . "WHERE {$_TABLES['users']}.uid > 1 AND {$_TABLES['userindex']}.uid = {$_TABLES['users']}.uid AND (etids <> '-' OR etids IS NULL) ORDER BY {$_TABLES['users']}.uid";
  4076 
  4077     $users = DB_query( $usersql );
  4078     $nrows = DB_numRows( $users );
  4079 
  4080     $lastrun = DB_getItem( $_TABLES['vars'], 'value', "name = 'lastemailedstories'" );
  4081 
  4082     // For each user, pull the stories they want and email it to them
  4083     for( $x = 0; $x < $nrows; $x++ )
  4084     {
  4085         $U = DB_fetchArray( $users );
  4086 
  4087         $storysql = array();
  4088         $storysql['mysql'] = "SELECT sid,uid,date AS day,title,introtext,postmode";
  4089 
  4090         $storysql['mssql'] = "SELECT sid,uid,date AS day,title,CAST(introtext AS text) AS introtext,postmode";
  4091 
  4092         $commonsql = " FROM {$_TABLES['stories']} WHERE draft_flag = 0 AND date <= NOW() AND date >= '{$lastrun}'";
  4093 
  4094         $topicsql = "SELECT tid FROM {$_TABLES['topics']}"
  4095                   . COM_getPermSQL( 'WHERE', $U['uuid'] );
  4096         $tresult = DB_query( $topicsql );
  4097         $trows = DB_numRows( $tresult );
  4098 
  4099         if( $trows == 0 )
  4100         {
  4101             // this user doesn't seem to have access to any topics ...
  4102             continue;
  4103         }
  4104 
  4105         $TIDS = array();
  4106         for( $i = 0; $i < $trows; $i++ )
  4107         {
  4108             $T = DB_fetchArray( $tresult );
  4109             $TIDS[] = $T['tid'];
  4110         }
  4111 
  4112         if( !empty( $U['etids'] ))
  4113         {
  4114             $ETIDS = explode( ' ', $U['etids'] );
  4115             $TIDS = array_intersect( $TIDS, $ETIDS );
  4116         }
  4117 
  4118         if( count( $TIDS ) > 0)
  4119         {
  4120             $commonsql .= " AND (tid IN ('" . implode( "','", $TIDS ) . "'))";
  4121         }
  4122 
  4123         $commonsql .= COM_getPermSQL( 'AND', $U['uuid'] );
  4124         $commonsql .= ' ORDER BY featured DESC, date DESC';
  4125 
  4126         $storysql['mysql'] .= $commonsql;
  4127         $storysql['mssql'] .= $commonsql;
  4128 
  4129         $stories = DB_query( $storysql );
  4130         $nsrows = DB_numRows( $stories );
  4131 
  4132         if( $nsrows == 0 )
  4133         {
  4134             // If no new stories where pulled for this user, continue with next
  4135             continue;
  4136         }
  4137 
  4138         $mailtext = $LANG08[29] . strftime( $_CONF['shortdate'], time() ) . "\n";
  4139 
  4140         for( $y = 0; $y < $nsrows; $y++ )
  4141         {
  4142             // Loop through stories building the requested email message
  4143             $S = DB_fetchArray( $stories );
  4144 
  4145             $mailtext .= "\n------------------------------\n\n";
  4146             $mailtext .= "$LANG08[31]: "
  4147                 . COM_undoSpecialChars( stripslashes( $S['title'] )) . "\n";
  4148             if( $_CONF['contributedbyline'] == 1 )
  4149             {
  4150                 if( empty( $authors[$S['uid']] ))
  4151                 {
  4152                     $storyauthor = COM_getDisplayName ($S['uid']);
  4153                     $authors[$S['uid']] = $storyauthor;
  4154                 }
  4155                 else
  4156                 {
  4157                     $storyauthor = $authors[$S['uid']];
  4158                 }
  4159                 $mailtext .= "$LANG24[7]: " . $storyauthor . "\n";
  4160             }
  4161 
  4162             $mailtext .= "$LANG08[32]: " . strftime( $_CONF['date'], strtotime( $S['day' ])) . "\n\n";
  4163 
  4164             if( $_CONF['emailstorieslength'] > 0 )
  4165             {
  4166                 if($S['postmode']==='wikitext'){
  4167                     $storytext = COM_undoSpecialChars( strip_tags( COM_renderWikiText ( stripslashes( $S['introtext'] ))));
  4168                 } else {
  4169                     $storytext = COM_undoSpecialChars( strip_tags( PLG_replaceTags( stripslashes( $S['introtext'] ))));
  4170                 }
  4171 
  4172                 if( $_CONF['emailstorieslength'] > 1 )
  4173                 {
  4174                     $storytext = COM_truncate( $storytext,
  4175                                     $_CONF['emailstorieslength'], '...' );
  4176                 }
  4177 
  4178                 $mailtext .= $storytext . "\n\n";
  4179             }
  4180 
  4181             $mailtext .= $LANG08[33] . ' ' . COM_buildUrl( $_CONF['site_url']
  4182                       . '/article.php?story=' . $S['sid'] ) . "\n";
  4183         }
  4184 
  4185         $mailtext .= "\n------------------------------\n";
  4186         $mailtext .= "\n$LANG08[34]\n";
  4187         $mailtext .= "\n------------------------------\n";
  4188 
  4189         $mailto = $U['username'] . ' <' . $U['email'] . '>';
  4190 
  4191         if ($_CONF['site_mail'] !== $_CONF['noreply_mail']) {
  4192             $mailfrom = $_CONF['noreply_mail'];
  4193             $mailtext .= LB . LB . $LANG04[159];
  4194         } else {
  4195             $mailfrom = $_CONF['site_mail'];
  4196         }
  4197         COM_mail( $mailto, $subject, $mailtext , $mailfrom);
  4198     }
  4199 
  4200     DB_query( "UPDATE {$_TABLES['vars']} SET value = NOW() WHERE name = 'lastemailedstories'" );
  4201 }
  4202 
  4203 /**
  4204 * Shows any new information in a block
  4205 *
  4206 * Return the HTML that shows any new stories, comments, etc
  4207 *
  4208 * @param    string  $help     Help file for block
  4209 * @param    string  $title    Title used in block header
  4210 * @param    string  $position Position in which block is being rendered 'left', 'right' or blank (for centre)
  4211 * @return   string  Return the HTML that shows any new stories, comments, etc
  4212 *
  4213 */
  4214 
  4215 function COM_whatsNewBlock( $help = '', $title = '', $position = '' )
  4216 {
  4217     global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_WHATSNEW, $page, $newstories;
  4218 
  4219     $retval = COM_startBlock( $title, $help,
  4220                        COM_getBlockTemplate( 'whats_new_block', 'header', $position ));
  4221 
  4222     $topicsql = '';
  4223     if(( $_CONF['hidenewstories'] == 0 ) || ( $_CONF['hidenewcomments'] == 0 )
  4224             || ( $_CONF['trackback_enabled']
  4225             && ( $_CONF['hidenewtrackbacks'] == 0 )))
  4226     {
  4227         $topicsql = COM_getTopicSql ('AND', 0, $_TABLES['stories']);
  4228     }
  4229 
  4230     if( $_CONF['hidenewstories'] == 0 )
  4231     {
  4232         $archsql = '';
  4233         $archivetid = DB_getItem( $_TABLES['topics'], 'tid', "archive_flag=1" );
  4234         if( !empty( $archivetid ))
  4235         {
  4236             $archsql = " AND (tid <> '" . addslashes( $archivetid ) . "')";
  4237         }
  4238 
  4239         // Find the newest stories
  4240         $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' );
  4241         $result = DB_query( $sql );
  4242         $A = DB_fetchArray( $result );
  4243         $nrows = $A['count'];
  4244 
  4245         if( empty( $title ))
  4246         {
  4247             $title = DB_getItem( $_TABLES['blocks'], 'title', "name='whats_new_block'" );
  4248         }
  4249 
  4250         // Any late breaking news stories?
  4251         $retval .= '<h3>' . $LANG01[99] . '</h3>';
  4252 
  4253         if( $nrows > 0 )
  4254         {
  4255             $newmsg = COM_formatTimeString( $LANG_WHATSNEW['new_string'],
  4256                         $_CONF['newstoriesinterval'], $LANG01[11], $nrows);
  4257 
  4258             if( $newstories && ( $page < 2 ))
  4259             {
  4260                 $retval .= $newmsg . '<br' . XHTML . '>';
  4261             }
  4262             else
  4263             {
  4264                 $retval .= COM_createLink($newmsg, $_CONF['site_url']
  4265                     . '/index.php?display=new') . '<br' . XHTML . '>';
  4266             }
  4267         }
  4268         else
  4269         {
  4270             $retval .= $LANG01[100] . '<br' . XHTML . '>';
  4271         }
  4272 
  4273         if(( $_CONF['hidenewcomments'] == 0 ) || ( $_CONF['trackback_enabled']
  4274                 && ( $_CONF['hidenewtrackbacks'] == 0 ))
  4275                 || ( $_CONF['hidenewplugins'] == 0 ))
  4276         {
  4277             $retval .= '<br' . XHTML . '>';
  4278         }
  4279     }
  4280 
  4281     if( $_CONF['hidenewcomments'] == 0 )
  4282     {
  4283         // Go get the newest comments
  4284         $retval .= '<h3>' . $LANG01[83] . ' <small>'
  4285                 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
  4286                                         $_CONF['newcommentsinterval'] )
  4287                 . '</small></h3>';
  4288 
  4289         $stwhere = '';
  4290 
  4291         if( !COM_isAnonUser() )
  4292         {
  4293             $stwhere .= "({$_TABLES['stories']}.owner_id IS NOT NULL AND {$_TABLES['stories']}.perm_owner IS NOT NULL) OR ";
  4294             $stwhere .= "({$_TABLES['stories']}.group_id IS NOT NULL AND {$_TABLES['stories']}.perm_group IS NOT NULL) OR ";
  4295             $stwhere .= "({$_TABLES['stories']}.perm_members IS NOT NULL)";
  4296         }
  4297         else
  4298         {
  4299             $stwhere .= "({$_TABLES['stories']}.perm_anon IS NOT NULL)";
  4300         }
  4301         $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";
  4302 
  4303         $result = DB_query( $sql );
  4304 
  4305         $nrows = DB_numRows( $result );
  4306 
  4307         if( $nrows > 0 )
  4308         {
  4309             $newcomments = array();
  4310 
  4311             for( $x = 0; $x < $nrows; $x++ )
  4312             {
  4313                 $A = DB_fetchArray( $result );
  4314 
  4315                 if(( $A['type'] == 'article' ) || empty( $A['type'] ))
  4316                 {
  4317                     $url = COM_buildUrl( $_CONF['site_url']
  4318                         . '/article.php?story=' . $A['sid'] ) . '#comments';
  4319                 }
  4320 
  4321                 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
  4322                 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
  4323                                             '...' );
  4324                 if( $title != $titletouse )
  4325                 {
  4326                     $attr = array('title' => htmlspecialchars($title));
  4327                 }
  4328                 else
  4329                 {
  4330                     $attr = array();
  4331                 }
  4332                 $acomment = str_replace( '$', '&#36;', $titletouse );
  4333                 $acomment = str_replace( ' ', '&nbsp;', $acomment );
  4334 
  4335                 if( $A['dups'] > 1 )
  4336                 {
  4337                     $acomment .= ' [+' . $A['dups'] . ']';
  4338                 }
  4339 
  4340                 $newcomments[] = COM_createLink($acomment, $url, $attr);
  4341             }
  4342 
  4343             $retval .= COM_makeList( $newcomments, 'list-new-comments' );
  4344         }
  4345         else
  4346         {
  4347             $retval .= $LANG01[86] . '<br' . XHTML . '>' . LB;
  4348         }
  4349         if(( $_CONF['hidenewplugins'] == 0 )
  4350                 || ( $_CONF['trackback_enabled']
  4351                 && ( $_CONF['hidenewtrackbacks'] == 0 )))
  4352         {
  4353             $retval .= '<br' . XHTML . '>';
  4354         }
  4355     }
  4356 
  4357     if( $_CONF['trackback_enabled'] && ( $_CONF['hidenewtrackbacks'] == 0 ))
  4358     {
  4359         $retval .= '<h3>' . $LANG01[114] . ' <small>'
  4360                 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
  4361                                         $_CONF['newtrackbackinterval'] )
  4362                 . '</small></h3>';
  4363 
  4364         $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";
  4365         $result = DB_query( $sql );
  4366 
  4367         $nrows = DB_numRows( $result );
  4368         if( $nrows > 0 )
  4369         {
  4370             $newcomments = array();
  4371 
  4372             for( $i = 0; $i < $nrows; $i++ )
  4373             {
  4374                 $A = DB_fetchArray( $result );
  4375 
  4376                 $url = COM_buildUrl( $_CONF['site_url']
  4377                     . '/article.php?story=' . $A['sid'] ) . '#trackback';
  4378 
  4379                 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
  4380                 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
  4381                                             '...' );
  4382 
  4383                 if( $title != $titletouse )
  4384                 {
  4385                     $attr = array('title' => htmlspecialchars($title));
  4386                 }
  4387                 else
  4388                 {
  4389                     $attr = array();
  4390                 }
  4391                 $acomment = str_replace( '$', '&#36;', $titletouse );
  4392                 $acomment = str_replace( ' ', '&nbsp;', $acomment );
  4393 
  4394                 if( $A['count'] > 1 )
  4395                 {
  4396                     $acomment .= ' [+' . $A['count'] . ']';
  4397                 }
  4398 
  4399                 $newcomments[] = COM_createLink($acomment, $url, $attr);
  4400             }
  4401 
  4402             $retval .= COM_makeList( $newcomments, 'list-new-trackbacks' );
  4403         }
  4404         else
  4405         {
  4406             $retval .= $LANG01[115] . '<br' . XHTML . '>' . LB;
  4407         }
  4408         if( $_CONF['hidenewplugins'] == 0 )
  4409         {
  4410             $retval .= '<br' . XHTML . '>';
  4411         }
  4412     }
  4413 
  4414     if( $_CONF['hidenewplugins'] == 0 )
  4415     {
  4416         list( $headlines, $smallheadlines, $content ) = PLG_getWhatsNew();
  4417         $plugins = count( $headlines );
  4418         if( $plugins > 0 )
  4419         {
  4420             for( $i = 0; $i < $plugins; $i++ )
  4421             {
  4422                 $retval .= '<h3>' . $headlines[$i] . ' <small>'
  4423                         . $smallheadlines[$i] . '</small></h3>';
  4424                 if( is_array( $content[$i] ))
  4425                 {
  4426                     $retval .= COM_makeList( $content[$i], 'list-new-plugins' );
  4427                 }
  4428                 else
  4429                 {
  4430                     $retval .= $content[$i];
  4431                 }
  4432 
  4433                 if( $i + 1 < $plugins )
  4434                 {
  4435                     $retval .= '<br' . XHTML . '>';
  4436                 }
  4437             }
  4438         }
  4439     }
  4440 
  4441     $retval .= COM_endBlock( COM_getBlockTemplate( 'whats_new_block', 'footer', $position ));
  4442 
  4443     return $retval;
  4444 }
  4445 
  4446 /**
  4447 * Creates the string that indicates the timespan in which new items were found
  4448 *
  4449 * @param    string  $time_string    template string
  4450 * @param    int     $time           number of seconds in which results are found
  4451 * @param    string  $type           type (translated string) of new item
  4452 * @param    int     $amount         amount of things that have been found.
  4453 */
  4454 function COM_formatTimeString( $time_string, $time, $type = '', $amount = 0 )
  4455 {
  4456     global $LANG_WHATSNEW;
  4457 
  4458     $retval = $time_string;
  4459 
  4460     // This is the amount you have to divide the previous by to get the
  4461     // different time intervals: hour, day, week, months
  4462     $time_divider = array( 60, 60, 24, 7, 30 );
  4463 
  4464     // These are the respective strings to the numbers above. They have to match
  4465     // the strings in $LANG_WHATSNEW (i.e. these are the keys for the array -
  4466     // the actual text strings are taken from the language file).
  4467     $time_description  = array( 'minute',  'hour',  'day',  'week',  'month'  );
  4468     $times_description = array( 'minutes', 'hours', 'days', 'weeks', 'months' );
  4469 
  4470     $time_dividers = count( $time_divider );
  4471     for( $s = 0; $s < $time_dividers; $s++ )
  4472     {
  4473         $time = $time / $time_divider[$s];
  4474         if( $time < $time_divider[$s + 1] )
  4475         {
  4476             if( $time == 1 )
  4477             {
  4478                 if( $s == 0 )
  4479                 {
  4480                     $time_str = $time_description[$s];
  4481                 }
  4482                 else // go back to the previous unit, e.g. 1 day -> 24 hours
  4483                 {
  4484                     $time_str = $times_description[$s - 1];
  4485                     $time *= $time_divider[$s];
  4486                 }
  4487             }
  4488             else
  4489             {
  4490                 $time_str = $times_description[$s];
  4491             }
  4492             $fields = array( '%n', '%i', '%t', '%s' );
  4493             $values = array( $amount, $type, $time, $LANG_WHATSNEW[$time_str] );
  4494             $retval = str_replace( $fields, $values, $retval );
  4495             break;
  4496         }
  4497     }
  4498 
  4499     return $retval;
  4500 }
  4501 
  4502 /**
  4503 * Displays a message text in a "System Message" block
  4504 *
  4505 * @param    string  $message    Message text; may contain HTML
  4506 * @param    string  $title      (optional) alternative block title
  4507 * @return   string              HTML block with message
  4508 * @see      COM_showMessage
  4509 * @see      COM_showMessageFromParameter
  4510 *
  4511 */
  4512 function COM_showMessageText($message, $title = '')
  4513 {
  4514     global $_CONF, $MESSAGE, $_IMAGE_TYPE;
  4515 
  4516     $retval = '';
  4517 
  4518     if (!empty($message)) {
  4519         if (empty($title)) {
  4520             $title = $MESSAGE[40];
  4521         }
  4522         $timestamp = strftime($_CONF['daytime']);
  4523         $retval .= COM_startBlock($title . ' - ' . $timestamp, '',
  4524                                   COM_getBlockTemplate('_msg_block', 'header'))
  4525                 . '<p class="sysmessage"><img src="' . $_CONF['layout_url']
  4526                 . '/images/sysmessage.' . $_IMAGE_TYPE . '" alt="" ' . XHTML
  4527                 . '>' . $message . '</p>'
  4528                 . COM_endBlock(COM_getBlockTemplate('_msg_block', 'footer'));
  4529     }
  4530 
  4531     return $retval;
  4532 }
  4533 
  4534 /**
  4535 * Displays a message on the webpage
  4536 *
  4537 * Display one of the predefined messages from the $MESSAGE array. If a plugin
  4538 * name is provided, display that plugin's message instead.
  4539 * 
  4540 * @param    int     $msg        ID of message to show
  4541 * @param    string  $plugin     Optional name of plugin to lookup plugin defined message
  4542 * @return   string              HTML block with message
  4543 * @see      COM_showMessageFromParameter
  4544 * @see      COM_showMessageText
  4545 *
  4546 */
  4547 function COM_showMessage($msg, $plugin = '')
  4548 {
  4549     global $MESSAGE;
  4550 
  4551     $retval = '';
  4552 
  4553     if ($msg > 0) {
  4554         if (!empty($plugin)) {
  4555             $var = 'PLG_' . $plugin . '_MESSAGE' . $msg;
  4556             global $$var;
  4557             if (isset($$var)) {
  4558                 $message = $$var;
  4559             } else {
  4560                 $message = sprintf($MESSAGE[61], $plugin);
  4561                 COM_errorLog($message . ": " . $var, 1);
  4562             }
  4563         } else {
  4564             $message = $MESSAGE[$msg];
  4565         }
  4566 
  4567         if (!empty($message)) {
  4568             $retval .= COM_showMessageText($message);
  4569         }
  4570     }
  4571 
  4572     return $retval;
  4573 }
  4574 
  4575 /**
  4576 * Displays a message, as defined by URL parameters
  4577 *
  4578 * Helper function to display a message, if URL parameters 'msg' and 'plugin'
  4579 * (optional) are defined. Only for GET requests, but that's what Geeklog uses
  4580 * everywhere anyway.
  4581 *
  4582 * @return   string  HTML block with message
  4583 * @see      COM_showMessage
  4584 * @see      COM_showMessageText
  4585 *
  4586 */
  4587 function COM_showMessageFromParameter()
  4588 {
  4589     $retval = '';
  4590 
  4591     if (isset($_GET['msg'])) {
  4592         $msg = COM_applyFilter($_GET['msg'], true);
  4593         if ($msg > 0) {
  4594             $plugin = '';
  4595             if (isset($_GET['plugin'])) {
  4596                 $plugin = COM_applyFilter($_GET['plugin']);
  4597             }
  4598             $retval .= COM_showMessage($msg, $plugin);
  4599         }
  4600     }
  4601 
  4602     return $retval;
  4603 }
  4604 
  4605 /**
  4606 * Prints Google(tm)-like paging navigation
  4607 *
  4608 * @param        string      $base_url       base url to use for all generated links
  4609 * @param        int         $curpage        current page we are on
  4610 * @param        int         $num_pages      Total number of pages
  4611 * @param        string      $page_str       page-variable name AND '='
  4612 * @param        boolean     $do_rewrite     if true, url-rewriting is respected
  4613 * @param        string      $msg            to be displayed with the navigation
  4614 * @param        string      $open_ended     replace next/last links with this
  4615 * @return   string   HTML formatted widget
  4616 */
  4617 function COM_printPageNavigation( $base_url, $curpage, $num_pages,
  4618                                   $page_str='page=', $do_rewrite=false, $msg='',
  4619                                   $open_ended = '')
  4620 {
  4621     global $LANG05;
  4622 
  4623     $retval = '';
  4624 
  4625     if( $num_pages < 2 )
  4626     {
  4627         return;
  4628     }
  4629 
  4630     if( !$do_rewrite )
  4631     {
  4632         $hasargs = strstr( $base_url, '?' );
  4633         if( $hasargs )
  4634         {
  4635             $sep = '&amp;';
  4636         }
  4637         else
  4638         {
  4639             $sep = '?';
  4640         }
  4641     }
  4642     else
  4643     {
  4644         $sep = '/';
  4645         $page_str = '';
  4646     }
  4647 
  4648     if( $curpage > 1 )
  4649     {
  4650         $retval .= COM_createLink($LANG05[7], $base_url) . ' | ';
  4651         $pg = '';
  4652         if( ( $curpage - 1 ) > 1 )
  4653         {
  4654             $pg = $sep . $page_str . ( $curpage - 1 );
  4655         }
  4656         $retval .= COM_createLink($LANG05[6], $base_url . $pg ) . ' | ';
  4657     }
  4658     else
  4659     {
  4660         $retval .= $LANG05[7] . ' | ' ;
  4661         $retval .= $LANG05[6] . ' | ' ;
  4662     }
  4663 
  4664     for( $pgcount = ( $curpage - 10 ); ( $pgcount <= ( $curpage + 9 )) AND ( $pgcount <= $num_pages ); $pgcount++ )
  4665     {
  4666         if( $pgcount <= 0 )
  4667         {
  4668             $pgcount = 1;
  4669         }
  4670 
  4671         if( $pgcount == $curpage )
  4672         {
  4673             $retval .= '<b>' . $pgcount . '</b> ';
  4674         }
  4675         else
  4676         {
  4677             $pg = '';
  4678             if( $pgcount > 1 )
  4679             {
  4680                 $pg = $sep . $page_str . $pgcount;
  4681             }
  4682             $retval .= COM_createLink($pgcount, $base_url . $pg) . ' ';
  4683         }
  4684     }
  4685 
  4686     if( !empty( $open_ended ))
  4687     {
  4688         $retval .= '| ' . $open_ended;
  4689     }
  4690     else if( $curpage == $num_pages )
  4691     {
  4692         $retval .= '| ' . $LANG05[5] . ' ';
  4693         $retval .= '| ' . $LANG05[8];
  4694     }
  4695     else
  4696     {
  4697         $retval .= '| ' . COM_createLink($LANG05[5], $base_url . $sep
  4698                                          . $page_str . ($curpage + 1));
  4699         $retval .= ' | ' . COM_createLink($LANG05[8], $base_url . $sep
  4700                                           . $page_str . $num_pages);
  4701     }
  4702 
  4703     if( !empty( $retval ))
  4704     {
  4705         if( !empty( $msg ))
  4706         {
  4707             $msg .=  ' ';
  4708         }
  4709         $retval = '<div class="pagenav">' . $msg . $retval . '</div>';
  4710     }
  4711 
  4712     return $retval;
  4713 }
  4714 
  4715 /**
  4716 * Returns formatted date/time for user
  4717 *
  4718 * This function COM_takes a date in either unixtimestamp or in english and
  4719 * formats it to the users preference.  If the user didn't specify a format
  4720 * the format in the config file is used.  This returns an array where array[0]
  4721 * is the formatted date and array[1] is the unixtimestamp
  4722 *
  4723 * @param        string      $date       date to format, otherwise we format current date/time
  4724 * @return   array   array[0] is the formatted date and array[1] is the unixtimestamp.
  4725 */
  4726 
  4727 function COM_getUserDateTimeFormat( $date='' )
  4728 {
  4729     global $_TABLES, $_USER, $_CONF;
  4730 
  4731     // Get display format for time
  4732 
  4733     if( !COM_isAnonUser() )
  4734     {
  4735         if( empty( $_USER['format'] ))
  4736         {
  4737             $dateformat = $_CONF['date'];
  4738         }
  4739         else
  4740         {
  4741             $dateformat = $_USER['format'];
  4742         }
  4743     }
  4744     else
  4745     {
  4746         $dateformat = $_CONF['date'];
  4747     }
  4748 
  4749     if( empty( $date ))
  4750     {
  4751         // Date is empty, get current date/time
  4752         $stamp = time();
  4753     }
  4754     else if( is_numeric( $date ))
  4755     {
  4756         // This is a timestamp
  4757         $stamp = $date;
  4758     }
  4759     else
  4760     {
  4761         // This is a string representation of a date/time
  4762         $stamp = strtotime( $date );
  4763     }
  4764 
  4765     // Format the date
  4766 
  4767     $date = strftime( $dateformat, $stamp );
  4768 
  4769     return array( $date, $stamp );
  4770 }
  4771 
  4772 /**
  4773 * Returns user-defined cookie timeout
  4774 *
  4775 * In account preferences users can specify when their long-term cookie expires.
  4776 * This function returns that value.
  4777 *
  4778 * @return   int Cookie time out value in seconds
  4779 */
  4780 
  4781 function COM_getUserCookieTimeout()
  4782 {
  4783     global $_TABLES, $_USER, $_CONF;
  4784 
  4785     if( empty( $_USER ))
  4786     {
  4787         return;
  4788     }
  4789 
  4790     $timeoutvalue = DB_getItem( $_TABLES['users'], 'cookietimeout', "uid = {$_USER['uid']}" );
  4791 
  4792     if( empty( $timeoutvalue ))
  4793     {
  4794         $timeoutvalue = 0;
  4795     }
  4796 
  4797     return $timeoutvalue;
  4798 }
  4799 
  4800 /**
  4801 * Shows who is online in slick little block
  4802 * @return   string  HTML string of online users seperated by line breaks.
  4803 */
  4804 
  4805 function phpblock_whosonline()
  4806 {
  4807     global $_CONF, $_TABLES, $_USER, $LANG01, $_IMAGE_TYPE;
  4808 
  4809     $retval = '';
  4810 
  4811     $expire_time = time() - $_CONF['whosonline_threshold'];
  4812 
  4813     $byname = 'username';
  4814     if( $_CONF['show_fullname'] == 1 )
  4815     {
  4816         $byname .= ',fullname';
  4817     }
  4818     if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
  4819     {
  4820         $byname .= ',remoteusername,remoteservice';
  4821     }
  4822 
  4823     $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}" );
  4824     $nrows = DB_numRows( $result );
  4825 
  4826     $num_anon = 0;
  4827     $num_reg  = 0;
  4828 
  4829     for( $i = 0; $i < $nrows; $i++ )
  4830     {
  4831         $A = DB_fetchArray( $result );
  4832 
  4833         if( $A['showonline'] == 1 )
  4834         {
  4835             $fullname = '';
  4836             if( $_CONF['show_fullname'] == 1 )
  4837             {
  4838                 $fullname = $A['fullname'];
  4839             }
  4840             if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
  4841             {
  4842                 $username = COM_getDisplayName( $A['uid'], $A['username'],
  4843                         $fullname, $A['remoteusername'], $A['remoteservice'] );
  4844             }
  4845             else
  4846             {
  4847                 $username = COM_getDisplayName( $A['uid'], $A['username'],
  4848                                                 $fullname );
  4849             }
  4850             $url = $_CONF['site_url'] . '/users.php?mode=profile&amp;uid=' . $A['uid'];
  4851             $retval .= COM_createLink($username, $url);
  4852 
  4853             if( !empty( $A['photo'] ) AND $_CONF['allow_user_photo'] == 1)
  4854             {
  4855                 $usrimg = '<img src="' . $_CONF['layout_url']
  4856                         . '/images/smallcamera.' . $_IMAGE_TYPE
  4857                         . '" alt=""' . XHTML . '>';
  4858                 $retval .= '&nbsp;' . COM_createLink($usrimg, $url);
  4859             }
  4860             $retval .= '<br' . XHTML . '>';
  4861             $num_reg++;
  4862         }
  4863         else
  4864         {
  4865             // this user does not want to show up in Who's Online
  4866             $num_anon++; // count as anonymous
  4867         }
  4868     }
  4869 
  4870     $num_anon += DB_count( $_TABLES['sessions'], 'uid', 1 );
  4871 
  4872     if(( $_CONF['whosonline_anonymous'] == 1 ) &&
  4873             COM_isAnonUser() )
  4874     {
  4875         // note that we're overwriting the contents of $retval here
  4876         if( $num_reg > 0 )
  4877         {
  4878             $retval = $LANG01[112] . ': ' . COM_numberFormat($num_reg)
  4879                     . '<br' . XHTML . '>';
  4880         }
  4881         else
  4882         {
  4883             $retval = '';
  4884         }
  4885     }
  4886 
  4887     if( $num_anon > 0 )
  4888     {
  4889         $retval .= $LANG01[41] . ': ' . COM_numberFormat($num_anon)
  4890                 . '<br' . XHTML . '>';
  4891     }
  4892 
  4893     return $retval;
  4894 }
  4895 
  4896 /**
  4897 * Gets the <option> values for calendar months
  4898 *
  4899 * @param        string      $selected       Selected month
  4900 * @see function COM_getDayFormOptions
  4901 * @see function COM_getYearFormOptions
  4902 * @see function COM_getHourFormOptions
  4903 * @see function COM_getMinuteFormOptions
  4904 * @return   string  HTML Months as option values
  4905 */
  4906 
  4907 function COM_getMonthFormOptions( $selected = '' )
  4908 {
  4909     global $LANG_MONTH;
  4910 
  4911     $month_options = '';
  4912 
  4913     for( $i = 1; $i <= 12; $i++ )
  4914     {
  4915         $mval = $i;
  4916         $month_options .= '<option value="' . $mval . '"';
  4917 
  4918         if( $i == $selected )
  4919         {
  4920             $month_options .= ' selected="selected"';
  4921         }
  4922 
  4923         $month_options .= '>' . $LANG_MONTH[$mval] . '</option>';
  4924     }
  4925 
  4926     return $month_options;
  4927 }
  4928 
  4929 /**
  4930 * Gets the <option> values for calendar days
  4931 *
  4932 * @param        string      $selected       Selected day
  4933 * @see function COM_getMonthFormOptions
  4934 * @see function COM_getYearFormOptions
  4935 * @see function COM_getHourFormOptions
  4936 * @see function COM_getMinuteFormOptions
  4937 * @return string HTML days as option values
  4938 */
  4939 
  4940 function COM_getDayFormOptions( $selected = '' )
  4941 {
  4942     $day_options = '';
  4943 
  4944     for( $i = 1; $i <= 31; $i++ )
  4945     {
  4946         if( $i < 10 )
  4947         {
  4948             $dval = '0' . $i;
  4949         }
  4950         else
  4951         {
  4952             $dval = $i;
  4953         }
  4954 
  4955         $day_options .= '<option value="' . $dval . '"';
  4956 
  4957         if( $i == $selected )
  4958         {
  4959             $day_options .= ' selected="selected"';
  4960         }
  4961 
  4962         $day_options .= '>' . $dval . '</option>';
  4963     }
  4964 
  4965     return $day_options;
  4966 }
  4967 
  4968 /**
  4969 * Gets the <option> values for calendar years
  4970 *
  4971 * Returns Option list Containing 5 years starting with current
  4972 * unless @selected is < current year then starts with @selected
  4973 *
  4974 * @param        string      $selected     Selected year
  4975 * @param        int         $startoffset  Optional (can be +/-) Used to determine start year for range of years
  4976 * @param        int         $endoffset    Optional (can be +/-) Used to determine end year for range of years
  4977 * @see function COM_getMonthFormOptions
  4978 * @see function COM_getDayFormOptions
  4979 * @see function COM_getHourFormOptions
  4980 * @see function COM_getMinuteFormOptions
  4981 * @return string  HTML years as option values
  4982 */
  4983 
  4984 function COM_getYearFormOptions($selected = '', $startoffset = -1, $endoffset = 5)
  4985 {
  4986     $year_options = '';
  4987     $start_year  = date('Y') + $startoffset;
  4988     $cur_year    = date('Y', time());
  4989     $finish_year = $cur_year + $endoffset;
  4990 
  4991     if (!empty($selected)) {
  4992         if ($selected < $cur_year) {
  4993             $start_year = $selected;
  4994         }
  4995     }
  4996 
  4997     for ($i = $start_year; $i <= $finish_year; $i++) {
  4998         $year_options .= '<option value="' . $i . '"';
  4999 
  5000         if ($i == $selected) {
  5001             $year_options .= ' selected="selected"';
  5002         }
  5003 
  5004         $year_options .= '>' . $i . '</option>';
  5005     }
  5006 
  5007     return $year_options;
  5008 }
  5009 
  5010 /**
  5011 * Gets the <option> values for clock hours
  5012 *
  5013 * @param    string  $selected   Selected hour
  5014 * @param    int     $mode       12 or 24 hour mode
  5015 * @return   string              HTML string of options
  5016 * @see function COM_getMonthFormOptions
  5017 * @see function COM_getDayFormOptions
  5018 * @see function COM_getYearFormOptions
  5019 * @see function COM_getMinuteFormOptions
  5020 */
  5021 
  5022 function COM_getHourFormOptions( $selected = '', $mode = 12 )
  5023 {
  5024     $hour_options = '';
  5025 
  5026     if( $mode == 12 )
  5027     {
  5028         for( $i = 1; $i <= 11; $i++ )
  5029         {
  5030             if( $i < 10 )
  5031             {
  5032                 $hval = '0' . $i;
  5033             }
  5034             else
  5035             {
  5036                 $hval = $i;
  5037             }
  5038 
  5039             if( $i == 1 )
  5040             {
  5041                 $hour_options .= '<option value="12"';
  5042 
  5043                 if( $selected == 12 )
  5044                 {
  5045                     $hour_options .= ' selected="selected"';
  5046                 }
  5047 
  5048                 $hour_options .= '>12</option>';
  5049             }
  5050 
  5051             $hour_options .= '<option value="' . $hval . '"';
  5052 
  5053             if( $selected == $i )
  5054             {
  5055                 $hour_options .= ' selected="selected"';
  5056             }
  5057 
  5058             $hour_options .= '>' . $i . '</option>';
  5059         }
  5060     }
  5061     else // if( $mode == 24 )
  5062     {
  5063         for( $i = 0; $i < 24; $i++ )
  5064         {
  5065             if( $i < 10 )
  5066             {
  5067                 $hval = '0' . $i;
  5068             }
  5069             else
  5070             {
  5071                 $hval = $i;
  5072             }
  5073 
  5074             $hour_options .= '<option value="' . $hval . '"';
  5075 
  5076             if( $selected == $i )
  5077             {
  5078                 $hour_options .= ' selected="selected"';
  5079             }
  5080 
  5081             $hour_options .= '>' . $i . '</option>';
  5082         }
  5083     }
  5084 
  5085     return $hour_options;
  5086 }
  5087 
  5088 /**
  5089 * Gets the <option> values for clock minutes
  5090 *
  5091 * @param    string      $selected   Selected minutes
  5092 * @param    int         $step       number of minutes between options, e.g. 15
  5093 * @see function COM_getMonthFormOptions
  5094 * @see function COM_getDayFormOptions
  5095 * @see function COM_getHourFormOptions
  5096 * @see function COM_getYearFormOptions
  5097 * @return string  HTML of option minutes
  5098 */
  5099 
  5100 function COM_getMinuteFormOptions( $selected = '', $step = 1 )
  5101 {
  5102     $minute_options = '';
  5103 
  5104     if(( $step < 1 ) || ( $step > 30 ))
  5105     {
  5106         $step = 1;
  5107     }
  5108 
  5109     for( $i = 0; $i <= 59; $i += $step )
  5110     {
  5111         if( $i < 10 )
  5112         {
  5113             $mval = '0' . $i;
  5114         }
  5115         else
  5116         {
  5117             $mval = $i;
  5118         }
  5119 
  5120         $minute_options .= '<option value="' . $mval . '"';
  5121 
  5122         if( $selected == $i )
  5123         {
  5124             $minute_options .= ' selected="selected"';
  5125         }
  5126 
  5127         $minute_options .= '>' . $mval . '</option>';
  5128     }
  5129 
  5130     return $minute_options;
  5131 }
  5132 
  5133 /**
  5134 * For backward compatibility only.
  5135 * This function should always have been called COM_getMinuteFormOptions
  5136 * @see COM_getMinuteFormOptions
  5137 */
  5138 function COM_getMinuteOptions( $selected = '', $step = 1 )
  5139 {
  5140     return COM_getMinuteFormOptions( $selected, $step );
  5141 }
  5142 
  5143 /**
  5144 * Create an am/pm selector dropdown menu
  5145 *
  5146 * @param    string  $name       name of the <select>
  5147 * @param    string  $selected   preselection: 'am' or 'pm'
  5148 * @return   string  HTML for the dropdown; empty string in 24 hour mode
  5149 *
  5150 */
  5151 function COM_getAmPmFormSelection( $name, $selected = '' )
  5152 {
  5153     global $_CONF;
  5154 
  5155     $retval = '';
  5156 
  5157     if( isset( $_CONF['hour_mode'] ) && ( $_CONF['hour_mode'] == 24 ))
  5158     {
  5159         $retval = '';
  5160     }
  5161     else
  5162     {
  5163         if( empty( $selected ))
  5164         {
  5165             $selected = date( 'a' );
  5166         }
  5167 
  5168         $retval .= '<select name="' . $name . '">' . LB;
  5169         $retval .= '<option value="am"';
  5170         if( $selected == 'am' )
  5171         {
  5172             $retval .= ' selected="selected"';
  5173         }
  5174         $retval .= '>am</option>' . LB . '<option value="pm"';
  5175         if( $selected == 'pm' )
  5176         {
  5177             $retval .= ' selected="selected"';
  5178         }
  5179         $retval .= '>pm</option>' . LB . '</select>' . LB;
  5180     }
  5181 
  5182     return $retval;
  5183 }
  5184 
  5185 /**
  5186 * Creates an HTML unordered list from the given array.
  5187 * It formats one list item per array element, using the list.thtml
  5188 * and listitem.thtml templates.
  5189 *
  5190 * @param    array   $listofitems    Items to list out
  5191 * @param    string  $classname      optional CSS class name for the list
  5192 * @return   string                  HTML unordered list of array items
  5193 */
  5194 function COM_makeList($listofitems, $classname = '')
  5195 {
  5196     global $_CONF;
  5197 
  5198     $list = new Template($_CONF['path_layout']);
  5199     $list->set_file(array('list'     => 'list.thtml',
  5200                           'listitem' => 'listitem.thtml'));
  5201     $list->set_var( 'xhtml', XHTML );
  5202     $list->set_var('site_url', $_CONF['site_url']);
  5203     $list->set_var('site_admin_url', $_CONF['site_admin_url']);
  5204     $list->set_var('layout_url', $_CONF['layout_url']);
  5205 
  5206     if (empty($classname)) {
  5207         $list->set_var('list_class',      '');
  5208         $list->set_var('list_class_name', '');
  5209     } else {
  5210         $list->set_var('list_class',      'class="' . $classname . '"');
  5211         $list->set_var('list_class_name', $classname);
  5212     }
  5213 
  5214     if (is_array($listofitems)) {
  5215         foreach ($listofitems as $oneitem) {
  5216             $list->set_var('list_item', $oneitem);
  5217             $list->parse('list_items', 'listitem', true);
  5218         }
  5219     }
  5220 
  5221     $list->parse('newlist', 'list', true);
  5222 
  5223     return $list->finish($list->get_var('newlist'));
  5224 }
  5225 
  5226 /**
  5227 * Check if speed limit applies
  5228 *
  5229 * @param    string  $type       type of speed limit, e.g. 'submit', 'comment'
  5230 * @param    int     $max        max number of allowed tries within speed limit
  5231 * @param    string  $property   IP address or other identifiable property
  5232 * @return   int                 0: does not apply, else: seconds since last post
  5233 */
  5234 function COM_checkSpeedlimit($type = 'submit', $max = 1, $property = '')
  5235 {
  5236     global $_TABLES;
  5237 
  5238     $last = 0;
  5239 
  5240     if (empty($property)) {
  5241         $property = $_SERVER['REMOTE_ADDR'];
  5242     }
  5243     $property = addslashes($property);
  5244 
  5245     $res  = DB_query("SELECT date FROM {$_TABLES['speedlimit']} WHERE (type = '$type') AND (ipaddress = '$property') ORDER BY date ASC");
  5246 
  5247     // If the number of allowed tries has not been reached,
  5248     // return 0 (didn't hit limit)
  5249     if (DB_numRows($res) < $max) {
  5250         return $last;
  5251     }
  5252 
  5253     list($date) = DB_fetchArray($res);
  5254 
  5255     if (!empty($date)) {
  5256         $last = time() - $date;
  5257         if ($last == 0) {
  5258             // just in case someone manages to submit something in < 1 sec.
  5259             $last = 1;
  5260         }
  5261     }
  5262 
  5263     return $last;
  5264 }
  5265 
  5266 /**
  5267 * Store post info for speed limit
  5268 *
  5269 * @param    string  $type       type of speed limit, e.g. 'submit', 'comment'
  5270 * @param    string  $property   IP address or other identifiable property
  5271 *
  5272 */
  5273 function COM_updateSpeedlimit($type = 'submit', $property = '')
  5274 {
  5275     global $_TABLES;
  5276 
  5277     if (empty($property)) {
  5278         $property = $_SERVER['REMOTE_ADDR'];
  5279     }
  5280     $property = addslashes($property);
  5281 
  5282     DB_save($_TABLES['speedlimit'], 'ipaddress,date,type',
  5283             "'$property',UNIX_TIMESTAMP(),'$type'");
  5284 }
  5285 
  5286 /**
  5287 * Clear out expired speed limits, i.e. entries older than 'x' seconds
  5288 *
  5289 * @param speedlimit   int      number of seconds
  5290 * @param type         string   type of speed limit, e.g. 'submit', 'comment'
  5291 *
  5292 */
  5293 function COM_clearSpeedlimit($speedlimit = 60, $type = '')
  5294 {
  5295     global $_TABLES;
  5296 
  5297     $sql = "DELETE FROM {$_TABLES['speedlimit']} WHERE ";
  5298     if (!empty($type)) {
  5299         $sql .= "(type = '$type') AND ";
  5300     }
  5301     $sql .= "(date < UNIX_TIMESTAMP() - $speedlimit)";
  5302     DB_query($sql);
  5303 }
  5304 
  5305 /**
  5306 * Reset the speedlimit
  5307 *
  5308 * @param    string  $type       type of speed limit to reset, e.g. 'submit'
  5309 * @param    string  $property   IP address or other identifiable property
  5310 *
  5311 */
  5312 function COM_resetSpeedlimit($type = 'submit', $property = '')
  5313 {
  5314     global $_TABLES;
  5315 
  5316     if (empty($property)) {
  5317         $property = $_SERVER['REMOTE_ADDR'];
  5318     }
  5319     $property = addslashes($property);
  5320 
  5321     DB_delete($_TABLES['speedlimit'], array('type', 'ipaddress'),
  5322                                       array($type, $property));
  5323 }
  5324 
  5325 /**
  5326 * Wrapper function for URL class so as to not confuse people as this will
  5327 * eventually get used all over the place
  5328 *
  5329 * This function returns a crawler friendly URL (if possible)
  5330 *
  5331 * @param    string      $url    URL to try to build crawler friendly URL for
  5332 * @return   string              Rewritten URL
  5333 */
  5334 
  5335 function COM_buildURL( $url )
  5336 {
  5337     global $_URL;
  5338 
  5339     return $_URL->buildURL( $url );
  5340 }
  5341 
  5342 /**
  5343 * Wrapper function for URL class so as to not confuse people
  5344 *
  5345 * This function sets the name of the arguments found in url
  5346 *
  5347 * @param    array   $names  Names of arguments in query string to assign to values
  5348 * @return   boolean         True if successful
  5349 */
  5350 
  5351 function COM_setArgNames( $names )
  5352 {
  5353     global $_URL;
  5354 
  5355     return $_URL->setArgNames( $names );
  5356 }
  5357 
  5358 /**
  5359 * Wrapper function for URL class
  5360 *
  5361 * returns value for specified argument
  5362 *
  5363 * @param        string      $name       argument to get value for
  5364 * @return   string     Argument value
  5365 */
  5366 
  5367 function COM_getArgument( $name )
  5368 {
  5369     global $_URL;
  5370 
  5371     return $_URL->getArgument( $name );
  5372 }
  5373 
  5374 /**
  5375 * Occurences / time
  5376 *
  5377 * This will take a number of occurrences, and number of seconds for the time span and return
  5378 * the smallest #/time interval
  5379 *
  5380 * @param    int     $occurrences        how many occurrences during time interval
  5381 * @param    int     $timespan           time interval in seconds
  5382 * @return   int Seconds per interval
  5383 */
  5384 
  5385 function COM_getRate( $occurrences, $timespan )
  5386 {
  5387     // want to define some common time words (yes, dirk, i need to put this in LANG)
  5388     // time words and their value in seconds
  5389     // week is 7 * day, month is 30 * day, year is 365.25 * day
  5390 
  5391     $common_time = array(
  5392         "second" => 1,
  5393         "minute" => 60,
  5394         "hour"   => 3600,
  5395         "day"    => 86400,
  5396         "week"   => 604800,
  5397         "month"  => 2592000,
  5398         "year"   => 31557600
  5399         );
  5400 
  5401     if( $occurrences != 0 )
  5402     {
  5403         $rate = ( int )( $timespan / $occurrences );
  5404         $adjustedRate = $occurrences + 1;
  5405         $time_unit = 'second';
  5406 
  5407         $found_one = false;
  5408 
  5409         foreach( $common_time as $unit=>$seconds )
  5410         {
  5411             if( $rate > $seconds )
  5412             {
  5413                 $foo = ( int )(( $rate / $seconds ) + .5 );
  5414 
  5415                 if(( $foo < $occurrences ) && ( $foo > 0 ))
  5416                 {
  5417                     $adjustedRate = $foo;
  5418                     $time_unit = $unit;
  5419                 }
  5420             }
  5421         }
  5422 
  5423         $singular = '1 shout every ' . $adjustedRate . ' ' . $time_unit;
  5424 
  5425         if( $adjustedRate > 1 )
  5426         {
  5427             $singular .= 's';
  5428         }
  5429     }
  5430     else
  5431     {
  5432         $singular = 'No events';
  5433     }
  5434 
  5435     return $singular;
  5436 }
  5437 
  5438 /**
  5439 * Return SQL expression to check for permissions.
  5440 *
  5441 * Creates part of an SQL expression that can be used to request items with the
  5442 * standard set of Geeklog permissions.
  5443 *
  5444 * @param        string      $type     part of the SQL expr. e.g. 'WHERE', 'AND'
  5445 * @param        int         $u_id     user id or 0 = current user
  5446 * @param        int         $access   access to check for (2=read, 3=r&write)
  5447 * @param        string      $table    table name if ambiguous (e.g. in JOINs)
  5448 * @return       string      SQL expression string (may be empty)
  5449 *
  5450 */
  5451 function COM_getPermSQL( $type = 'WHERE', $u_id = 0, $access = 2, $table = '' )
  5452 {
  5453     global $_USER, $_GROUPS;
  5454 
  5455     if( !empty( $table ))
  5456     {
  5457         $table .= '.';
  5458     }
  5459     if( $u_id <= 0)
  5460     {
  5461         if( COM_isAnonUser() )
  5462         {
  5463             $uid = 1;
  5464         }
  5465         else
  5466         {
  5467             $uid = $_USER['uid'];
  5468         }
  5469     }
  5470     else
  5471     {
  5472         $uid = $u_id;
  5473     }
  5474 
  5475     $UserGroups = array();
  5476     if(( empty( $_USER['uid'] ) && ( $uid == 1 )) || ( $uid == $_USER['uid'] ))
  5477     {
  5478         if( empty( $_GROUPS ))
  5479         {
  5480             $_GROUPS = SEC_getUserGroups( $uid );
  5481         }
  5482         $UserGroups = $_GROUPS;
  5483     }
  5484     else
  5485     {
  5486         $UserGroups = SEC_getUserGroups( $uid );
  5487     }
  5488 
  5489     if( empty( $UserGroups ))
  5490     {
  5491         // this shouldn't really happen, but if it does, handle user
  5492         // like an anonymous user
  5493         $uid = 1;
  5494     }
  5495 
  5496     if( SEC_inGroup( 'Root', $uid ))
  5497     {
  5498         return '';
  5499     }
  5500 
  5501     $sql = ' ' . $type . ' (';
  5502 
  5503     if( $uid > 1 )
  5504     {
  5505         $sql .= "(({$table}owner_id = '{$uid}') AND ({$table}perm_owner >= $access)) OR ";
  5506 
  5507         $sql .= "(({$table}group_id IN (" . implode( ',', $UserGroups )
  5508              . ")) AND ({$table}perm_group >= $access)) OR ";
  5509         $sql .= "({$table}perm_members >= $access)";
  5510     }
  5511     else
  5512     {
  5513         $sql .= "{$table}perm_anon >= $access";
  5514     }
  5515 
  5516     $sql .= ')';
  5517 
  5518     return $sql;
  5519 }
  5520 
  5521 /**
  5522 * Return SQL expression to check for allowed topics.
  5523 *
  5524 * Creates part of an SQL expression that can be used to only request stories
  5525 * from topics to which the user has access to.
  5526 *
  5527 * Note that this function does an SQL request, so you should cache
  5528 * the resulting SQL expression if you need it more than once.
  5529 *
  5530 * @param    string  $type   part of the SQL expr. e.g. 'WHERE', 'AND'
  5531 * @param    int     $u_id   user id or 0 = current user
  5532 * @param    string  $table  table name if ambiguous (e.g. in JOINs)
  5533 * @return   string          SQL expression string (may be empty)
  5534 *
  5535 */
  5536 function COM_getTopicSQL( $type = 'WHERE', $u_id = 0, $table = '' )
  5537 {
  5538     global $_TABLES, $_USER, $_GROUPS;
  5539 
  5540     $topicsql = ' ' . $type . ' ';
  5541 
  5542     if( !empty( $table ))
  5543     {
  5544         $table .= '.';
  5545     }
  5546 
  5547     $UserGroups = array();
  5548     if(( $u_id <= 0 ) || ( isset( $_USER['uid'] ) && $u_id == $_USER['uid'] ))
  5549     {
  5550         if( !COM_isAnonUser() )
  5551         {
  5552             $uid = $_USER['uid'];
  5553         }
  5554         else
  5555         {
  5556             $uid = 1;
  5557         }
  5558         $UserGroups = $_GROUPS;
  5559     }
  5560     else
  5561     {
  5562         $uid = $u_id;
  5563         $UserGroups = SEC_getUserGroups( $uid );
  5564     }
  5565 
  5566     if( empty( $UserGroups ))
  5567     {
  5568         // this shouldn't really happen, but if it does, handle user
  5569         // like an anonymous user
  5570         $uid = 1;
  5571     }
  5572 
  5573     if( SEC_inGroup( 'Root', $uid ))
  5574     {
  5575         return '';
  5576     }
  5577 
  5578     $result = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
  5579                         . COM_getPermSQL( 'WHERE', $uid ));
  5580     $tids = array();
  5581     while( $T = DB_fetchArray( $result ))
  5582     {
  5583         $tids[] = $T['tid'];
  5584     }
  5585 
  5586     if( count( $tids ) > 0 )
  5587     {
  5588         $topicsql .= "({$table}tid IN ('" . implode( "','", $tids ) . "'))";
  5589     }
  5590     else
  5591     {
  5592         $topicsql .= '0';
  5593     }
  5594 
  5595     return $topicsql;
  5596 }
  5597 
  5598 /**
  5599 * Strip slashes from a string only when magic_quotes_gpc = on.
  5600 *
  5601 * @param   string  $text  The text
  5602 * @return  string  The text, possibly without slashes.
  5603 */
  5604 function COM_stripslashes( $text )
  5605 {
  5606     if( get_magic_quotes_gpc() == 1 )
  5607     {
  5608         return( stripslashes( $text ));
  5609     }
  5610 
  5611     return( $text );
  5612 }
  5613 
  5614 /**
  5615 * Filter parameters passed per GET (URL) or POST.
  5616 *
  5617 * @param    string    $parameter   the parameter to test
  5618 * @param    boolean   $isnumeric   true if $parameter is supposed to be numeric
  5619 * @return   string    the filtered parameter (may now be empty or 0)
  5620 * @see COM_applyBasicFilter
  5621 *
  5622 */
  5623 function COM_applyFilter( $parameter, $isnumeric = false )
  5624 {
  5625     $p = COM_stripslashes($parameter);
  5626 
  5627     return COM_applyBasicFilter($p, $isnumeric);
  5628 }
  5629 
  5630 /**
  5631 * Filter parameters
  5632 *
  5633 * NOTE:     Use this function instead of COM_applyFilter for parameters
  5634 *           _not_ coming in through a GET or POST request.
  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_applyFilter
  5640 *
  5641 */
  5642 function COM_applyBasicFilter( $parameter, $isnumeric = false )
  5643 {
  5644     $log_manipulation = false; // set to true to log when the filter applied
  5645 
  5646     $p = strip_tags( $parameter );
  5647     $p = COM_killJS( $p ); // doesn't help a lot right now, but still ...
  5648 
  5649     if( $isnumeric )
  5650     {
  5651         // Note: PHP's is_numeric() accepts values like 4e4 as numeric
  5652         if( !is_numeric( $p ) || ( preg_match( '/^-?\d+$/', $p ) == 0 ))
  5653         {
  5654             $p = 0;
  5655         }
  5656     }
  5657     else
  5658     {
  5659         $p = preg_replace( '/\/\*.*/', '', $p );
  5660         $pa = explode( "'", $p );
  5661         $pa = explode( '"', $pa[0] );
  5662         $pa = explode( '`', $pa[0] );
  5663         $pa = explode( ';', $pa[0] );
  5664         $pa = explode( ',', $pa[0] );
  5665         $pa = explode( '\\', $pa[0] );
  5666         $p = $pa[0];
  5667     }
  5668 
  5669     if( $log_manipulation )
  5670     {
  5671         if( strcmp( $p, $parameter ) != 0 )
  5672         {
  5673             COM_errorLog( "Filter applied: >> $parameter << filtered to $p [IP {$_SERVER['REMOTE_ADDR']}]", 1);
  5674         }
  5675     }
  5676 
  5677     return $p;
  5678 }
  5679 
  5680 /**
  5681 * Sanitize a URL
  5682 *
  5683 * @param    string  $url                URL to sanitized
  5684 * @param    array   $allowed_protocols  array of allowed protocols
  5685 * @param    string  $default_protocol   replacement protocol (default: http)
  5686 * @return   string                      sanitized URL
  5687 *
  5688 */
  5689 function COM_sanitizeUrl( $url, $allowed_protocols = '', $default_protocol = '' )
  5690 {
  5691     global $_CONF;
  5692 
  5693     if( empty( $allowed_protocols ))
  5694     {
  5695         $allowed_protocols = $_CONF['allowed_protocols'];
  5696     }
  5697     else if( !is_array( $allowed_protocols ))
  5698     {
  5699         $allowed_protocols = array( $allowed_protocols );
  5700     }
  5701 
  5702     if( empty( $default_protocol ))
  5703     {
  5704         $default_protocol = 'http:';
  5705     }
  5706     else if( substr( $default_protocol, -1 ) != ':' )
  5707     {
  5708         $default_protocol .= ':';
  5709     }
  5710 
  5711     $url = strip_tags( $url );
  5712     if( !empty( $url ))
  5713     {
  5714         $pos = MBYTE_strpos( $url, ':' );
  5715         if( $pos === false )
  5716         {
  5717             $url = $default_protocol . '//' . $url;
  5718         }
  5719         else
  5720         {
  5721             $protocol = MBYTE_substr( $url, 0, $pos + 1 );
  5722             $found_it = false;
  5723             foreach( $allowed_protocols as $allowed )
  5724             {
  5725                 if( substr( $allowed, -1 ) != ':' )
  5726                 {
  5727                     $allowed .= ':';
  5728                 }
  5729                 if( $protocol == $allowed )
  5730                 {
  5731                     $found_it = true;
  5732                     break;
  5733                 }
  5734             }
  5735             if( !$found_it )
  5736             {
  5737                 $url = $default_protocol . MBYTE_substr( $url, $pos + 1 );
  5738             }
  5739         }
  5740     }
  5741 
  5742     return $url;
  5743 }
  5744 
  5745 /**
  5746 * Ensure an ID contains only alphanumeric characters, dots, dashes, or underscores
  5747 *
  5748 * @param    string  $id     the ID to sanitize
  5749 * @param    boolean $new_id true = create a new ID in case we end up with an empty string
  5750 * @return   string          the sanitized ID
  5751 */
  5752 function COM_sanitizeID( $id, $new_id = true )
  5753 {
  5754     $id = str_replace( ' ', '', $id );
  5755     $id = str_replace( array( '/', '\\', ':', '+' ), '-', $id );
  5756     $id = preg_replace( '/[^a-zA-Z0-9\-_\.]/', '', $id );
  5757     if( empty( $id ) && $new_id )
  5758     {
  5759         $id = COM_makesid();
  5760     }
  5761 
  5762     return $id;
  5763 }
  5764 
  5765 /**
  5766 * Sanitize a filename.
  5767 *
  5768 * NOTE:     This function is pretty strict in what it allows. Meant to be used
  5769 *           for files to be included where part of the filename is dynamic.
  5770 *
  5771 * @param    string  $filename   the filename to clean up
  5772 * @param    boolean $allow_dots whether to allow dots in the filename or not
  5773 * @return   string              sanitized filename
  5774 *
  5775 */
  5776 function COM_sanitizeFilename($filename, $allow_dots = false)
  5777 {
  5778     if ($allow_dots) {
  5779         $filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $filename);
  5780         $filename = str_replace('..', '', $filename);
  5781     } else {
  5782         $filename = preg_replace('/[^a-zA-Z0-9\-_]/', '', $filename);
  5783     }
  5784 
  5785     return $filename;
  5786 }
  5787 
  5788 /**
  5789 * Detect links in a plain-ascii text and turn them into clickable links.
  5790 * Will detect links starting with "http:", "https:", "ftp:", and "www.".
  5791 *
  5792 * @param    string    $text     the (plain-ascii) text string
  5793 * @return   string    the same string, with links enclosed in <a>...</a> tags
  5794 *
  5795 */
  5796 function COM_makeClickableLinks( $text )
  5797 {
  5798     global $_CONF;
  5799 
  5800     if (! $_CONF['clickable_links']) {
  5801         return $text;
  5802     }
  5803 
  5804     // These regular expressions will work for this purpuse, but
  5805     // they should NOT be used for validating links.
  5806 
  5807     // matches anything starting with http:// or https:// or ftp:// or ftps://
  5808     $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:(?:ht|f)tps?:\/{2})(?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&amp;)?)+)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
  5809     $replace[] = "COM_makeClickableLinksCallback('', '\\1')";
  5810 
  5811     // matches anything containing a top level domain: xxx.com or xxx.yyy.net/stuff.php or xxx.yyy.zz
  5812     // list taken from: http://en.wikipedia.org/wiki/List_of_Internet_TLDs
  5813     $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';
  5814     $replace[] = "COM_makeClickableLinksCallback('http://', '\\1')";
  5815 
  5816     $text = preg_replace( $regex, $replace, $text );
  5817 
  5818     return $text;
  5819 }
  5820 
  5821 /**
  5822 * Callback function to help format links in COM_makeClickableLinks
  5823 *
  5824 * @param    string  $http   set to 'http://' when not already in the url
  5825 * @param    string  $link   the url
  5826 * @return   string          link enclosed in <a>...</a> tags
  5827 *
  5828 */
  5829 function COM_makeClickableLinksCallback( $http, $link )
  5830 {
  5831     $text = COM_truncate( $link, 50, '...', '10' );
  5832 
  5833     return "<a href=\"$http$link\">$text</a>";
  5834 }
  5835 
  5836 /**
  5837 * Undo the conversion of URLs to clickable links (in plain text posts),
  5838 * e.g. so that we can present the user with the post as they entered them.
  5839 *
  5840 * @param    string  $text   story text
  5841 * @return   string          story text without links
  5842 *
  5843 */
  5844 function COM_undoClickableLinks( $text )
  5845 {
  5846     $text = preg_replace( '/<a href="([^"]*)">([^<]*)<\/a>/', '\1', $text );
  5847 
  5848     return $text;
  5849 }
  5850 
  5851 /**
  5852 * Highlight the words from a search query in a given text string.
  5853 *
  5854 * @param    string  $text   the text
  5855 * @param    string  $query  the search query
  5856 * @param    string  $class  html class to use to highlight
  5857 * @return   string          the text with highlighted search words
  5858 *
  5859 */
  5860 function COM_highlightQuery( $text, $query, $class = 'highlight' )
  5861 {
  5862     // escape PCRE special characters
  5863     $query = preg_quote($query, '/');
  5864 
  5865     $mywords = explode(' ', $query);
  5866     foreach ($mywords as $searchword)
  5867     {
  5868         if (!empty($searchword))
  5869         {
  5870             $before = "/(?!(?:[^<]+>|[^>]+<\/a>))\b";
  5871             $after = "\b/i";
  5872             if ($searchword <> utf8_encode($searchword)) {
  5873                  if (@preg_match('/^\pL$/u', urldecode('%C3%B1'))) { // Unicode property support
  5874                       $before = "/(?<!\p{L})";
  5875                       $after = "(?!\p{L})/u";
  5876                  } else {
  5877                       $before = "/";
  5878                       $after = "/u";
  5879                  }
  5880             }
  5881             $text = preg_replace($before . $searchword . $after, "<span class=\"$class\">\\0</span>", '<!-- x -->' . $text . '<!-- x -->' );
  5882         }
  5883     }
  5884     return $text;
  5885 }
  5886 
  5887 /**
  5888 * Determines the difference between two dates.
  5889 *
  5890 * This will takes either unixtimestamps or English dates as input and will
  5891 * automatically do the date diff on the more recent of the two dates (e.g. the
  5892 * order of the two dates given doesn't matter).
  5893 *
  5894 * @author Tony Bibbs, tony DOT bibbs AT iowa DOT gov
  5895 * @access public
  5896 * @param string $interval Can be:
  5897 * y = year
  5898 * m = month
  5899 * w = week
  5900 * h = hours
  5901 * i = minutes
  5902 * s = seconds
  5903 * @param string|int $date1 English date (e.g. 10 Dec 2004) or unixtimestamp
  5904 * @param string|int $date2 English date (e.g. 10 Dec 2004) or unixtimestamp
  5905 * @return int Difference of the two dates in the unit of time indicated by the interval
  5906 *
  5907 */
  5908 function COM_dateDiff( $interval, $date1, $date2 )
  5909 {
  5910     // Convert dates to timestamps, if needed.
  5911     if( !is_numeric( $date1 ))
  5912     {
  5913         $date1 = strtotime( $date1 );
  5914     }
  5915 
  5916     if( !is_numeric( $date2 ))
  5917     {
  5918         $date2 = strtotime( $date2 );
  5919     }
  5920 
  5921     // Function roughly equivalent to the ASP "DateDiff" function
  5922     if( $date2 > $date1 )
  5923     {
  5924         $seconds = $date2 - $date1;
  5925     }
  5926     else
  5927     {
  5928         $seconds = $date1 - $date2;
  5929     }
  5930 
  5931     switch( $interval )
  5932     {
  5933         case "y":
  5934             list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
  5935             list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
  5936             $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
  5937             $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
  5938             $diff = $year2 - $year1;
  5939             if($month1 > $month2) {
  5940                 $diff -= 1;
  5941             } elseif($month1 == $month2) {
  5942                 if($day1 > $day2) {
  5943                     $diff -= 1;
  5944                 } elseif($day1 == $day2) {
  5945                     if($time1 > $time2) {
  5946                         $diff -= 1;
  5947                     }
  5948                 }
  5949             }
  5950             break;
  5951         case "m":
  5952             list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
  5953             list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
  5954             $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
  5955             $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
  5956             $diff = ($year2 * 12 + $month2) - ($year1 * 12 + $month1);
  5957             if($day1 > $day2) {
  5958                 $diff -= 1;
  5959             } elseif($day1 == $day2) {
  5960                 if($time1 > $time2) {
  5961                     $diff -= 1;
  5962                 }
  5963             }
  5964             break;
  5965         case "w":
  5966             // Only simple seconds calculation needed from here on
  5967             $diff = floor($seconds / 604800);
  5968             break;
  5969          case "d":
  5970             $diff = floor($seconds / 86400);
  5971             break;
  5972         case "h":
  5973             $diff = floor($seconds / 3600);
  5974             break;
  5975         case "i":
  5976             $diff = floor($seconds / 60);
  5977             break;
  5978         case "s":
  5979             $diff = $seconds;
  5980             break;
  5981     }
  5982 
  5983     return $diff;
  5984 }
  5985 
  5986 /**
  5987 * Try to figure out our current URL, including all parameters.
  5988 *
  5989 * This is an ugly hack since there's no single variable that returns what
  5990 * we want and the variables used here may not be available on all servers
  5991 * and / or setups.
  5992 *
  5993 * Seems to work on Apache (1.3.x and 2.x), IIS, and Zeus ...
  5994 *
  5995 * @return   string  complete URL, e.g. 'http://www.example.com/blah.php?foo=bar'
  5996 *
  5997 */
  5998 function COM_getCurrentURL()
  5999 {
  6000     global $_CONF;
  6001 
  6002     $thisUrl = '';
  6003 
  6004     if( empty( $_SERVER['SCRIPT_URI'] ))
  6005     {
  6006         if( !empty( $_SERVER['DOCUMENT_URI'] ))
  6007         {
  6008             $thisUrl = $_SERVER['DOCUMENT_URI'];
  6009         }
  6010     }
  6011     else
  6012     {
  6013         $thisUrl = $_SERVER['SCRIPT_URI'];
  6014     }
  6015     if( !empty( $thisUrl ) && !empty( $_SERVER['QUERY_STRING'] ))
  6016     {
  6017         $thisUrl .= '?' . $_SERVER['QUERY_STRING'];
  6018     }
  6019     if( empty( $thisUrl ))
  6020     {
  6021         $requestUri = $_SERVER['REQUEST_URI'];
  6022         if( empty( $_SERVER['REQUEST_URI'] ))
  6023         {
  6024             // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
  6025             if( empty( $_SERVER['PATH_INFO'] ))
  6026             {
  6027                 $requestUri = $_SERVER['SCRIPT_NAME'];
  6028             }
  6029             else
  6030             {
  6031                 $requestUri = $_SERVER['PATH_INFO'];
  6032             }
  6033             if( !empty( $_SERVER['QUERY_STRING'] ))
  6034             {
  6035                 $requestUri .= '?' . $_SERVER['QUERY_STRING'];
  6036             }
  6037         }
  6038 
  6039         $firstslash = strpos( $_CONF['site_url'], '/' );
  6040         if( $firstslash === false )
  6041         {
  6042             // special case - assume it's okay
  6043             $thisUrl = $_CONF['site_url'] . $requestUri;
  6044         }
  6045         else if( $firstslash + 1 == strrpos( $_CONF['site_url'], '/' ))
  6046         {
  6047             // site is in the document root
  6048             $thisUrl = $_CONF['site_url'] . $requestUri;
  6049         }
  6050         else
  6051         {
  6052             // extract server name first
  6053             $pos = strpos( $_CONF['site_url'], '/', $firstslash + 2 );
  6054             $thisUrl = substr( $_CONF['site_url'], 0, $pos ) . $requestUri;
  6055         }
  6056     }
  6057 
  6058     return $thisUrl;
  6059 }
  6060 
  6061 /**
  6062 * Check if we're on Geeklog's index page.
  6063 *
  6064 * See if we're on the main index page (first page, no topics selected).
  6065 *
  6066 * @return   boolean     true = we're on the frontpage, false = we're not
  6067 *
  6068 */
  6069 function COM_onFrontpage()
  6070 {
  6071     global $_CONF, $topic, $page, $newstories;
  6072 
  6073     // Note: We can't use $PHP_SELF here since the site may not be in the
  6074     // DocumentRoot
  6075     $onFrontpage = false;
  6076 
  6077     // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
  6078     if( empty( $_SERVER['PATH_INFO'] ))
  6079     {
  6080         $scriptName = $_SERVER['SCRIPT_NAME'];
  6081     }
  6082     else
  6083     {
  6084         $scriptName = $_SERVER['PATH_INFO'];
  6085     }
  6086 
  6087     preg_match( '/\/\/[^\/]*(.*)/', $_CONF['site_url'], $pathonly );
  6088     if(( $scriptName == $pathonly[1] . '/index.php' ) &&
  6089             empty( $topic ) && ( $page == 1 ) && !$newstories )
  6090     {
  6091         $onFrontpage = true;
  6092     }
  6093 
  6094     return $onFrontpage;
  6095 }
  6096 
  6097 /**
  6098 * Check if we're on Geeklog's index page [deprecated]
  6099 *
  6100 * Note that this function returns FALSE when we're on the index page. Due to
  6101 * the inverted return values, it has been deprecated and is only provided for
  6102 * backward compatibility - use COM_onFrontpage() instead.
  6103 *
  6104 * @deprecated since Geeklog 1.4.1
  6105 * @see COM_onFrontpage
  6106 *
  6107 */
  6108 function COM_isFrontpage()
  6109 {
  6110     return !COM_onFrontpage();
  6111 }
  6112 
  6113 /**
  6114 * Converts a number for output into a formatted number with thousands-
  6115 * separator, comma-separator and fixed decimals if necessary
  6116 *
  6117 * @param        float        $number        Number that will be formatted
  6118 * @return        string                        formatted number
  6119 *
  6120 */
  6121 function COM_numberFormat( $number )
  6122 {
  6123     global $_CONF;
  6124 
  6125     if( $number - floor( $number ) > 0 ) // number has decimals
  6126     {
  6127         $dc = $_CONF['decimal_count'];
  6128     }
  6129     else
  6130     {
  6131         $dc = 0;
  6132     }
  6133     $ts = $_CONF['thousand_separator'];
  6134     $ds = $_CONF['decimal_separator'];
  6135 
  6136     return number_format( $number, $dc, $ds, $ts );
  6137 }
  6138 
  6139 /**
  6140 * Convert a text based date YYYY-MM-DD to a unix timestamp integer value
  6141 *
  6142 * @param    string  $date   Date in the format YYYY-MM-DD
  6143 * @param    string  $time   Option time in the format HH:MM::SS
  6144 * @return   int             UNIX Timestamp
  6145 */
  6146 function COM_convertDate2Timestamp( $date, $time = '' )
  6147 {
  6148     $atoks = array();
  6149     $btoks = array();
  6150 
  6151     // Breakup the string using either a space, fwd slash, dash, bkwd slash or
  6152     // colon as a delimiter
  6153     $atok = strtok( $date, ' /-\\:' );
  6154     while( $atok !== FALSE )
  6155     {
  6156         $atoks[] = $atok;
  6157         $atok = strtok( ' /-\\:' );  // get the next token
  6158     }
  6159 
  6160     for( $i = 0; $i < 3; $i++ )
  6161     {
  6162         if( !isset( $atoks[$i] ) || !is_numeric( $atoks[$i] ))
  6163         {
  6164             $atoks[$i] = 0;
  6165         }
  6166     }
  6167 
  6168     if( $time == '' )
  6169     {
  6170         $timestamp = mktime( 0, 0, 0, $atoks[1], $atoks[2], $atoks[0] );
  6171     }
  6172     else
  6173     {
  6174         $btok = strtok( $time, ' /-\\:' );
  6175         while( $btok !== FALSE )
  6176         {
  6177             $btoks[] = $btok;
  6178             $btok = strtok( ' /-\\:' );
  6179         }
  6180 
  6181         for( $i = 0; $i < 3; $i++ )
  6182         {
  6183             if( !isset( $btoks[$i] ) || !is_numeric( $btoks[$i] ))
  6184             {
  6185                 $btoks[$i] = 0;
  6186             }
  6187         }
  6188 
  6189         $timestamp = mktime( $btoks[0], $btoks[1], $btoks[2],
  6190                              $atoks[1], $atoks[2], $atoks[0] );
  6191     }
  6192 
  6193     return $timestamp;
  6194 }
  6195 
  6196 /**
  6197 * Get the HTML for an image with height & width
  6198 *
  6199 * @param    string  $file   full path to the file
  6200 * @return   string          html that will be included in the img-tag
  6201 */
  6202 function COM_getImgSizeAttributes( $file )
  6203 {
  6204     $sizeattributes = '';
  6205 
  6206     if( file_exists( $file ))
  6207     {
  6208         $dimensions = getimagesize( $file );
  6209         if( !empty( $dimensions[0] ) AND !empty( $dimensions[1] ))
  6210         {
  6211             $sizeattributes = 'width="' . $dimensions[0]
  6212                             . '" height="' . $dimensions[1] . '" ';
  6213         }
  6214     }
  6215 
  6216     return $sizeattributes;
  6217 }
  6218 
  6219 /**
  6220 * Display a message and abort
  6221 *
  6222 * NOTE: Displays the message and aborts the script.
  6223 *
  6224 * @param    int     $msg            message number
  6225 * @param    string  $plugin         plugin name, if applicable
  6226 * @param    int     $http_status    HTTP status code to send with the message
  6227 * @param    string  $http_text      Textual version of the HTTP status code
  6228 *
  6229 */
  6230 function COM_displayMessageAndAbort( $msg, $plugin = '', $http_status = 200, $http_text = 'OK')
  6231 {
  6232     $display = COM_siteHeader( 'menu' )
  6233              . COM_showMessage( $msg, $plugin )
  6234              . COM_siteFooter( true );
  6235 
  6236     if( $http_status != 200 )
  6237     {
  6238         header( "HTTP/1.1 $http_status $http_text" );
  6239         header( "Status: $http_status $http_text" );
  6240     }
  6241     echo $display;
  6242     exit;
  6243 }
  6244 
  6245 /**
  6246 * Return full URL of a topic icon
  6247 *
  6248 * @param    string  $imageurl   (relative) topic icon URL
  6249 * @return   string              Full URL
  6250 *
  6251 */
  6252 function COM_getTopicImageUrl( $imageurl )
  6253 {
  6254     global $_CONF, $_THEME_URL;
  6255 
  6256     $iconurl = '';
  6257 
  6258     if( !empty( $imageurl ))
  6259     {
  6260         if( isset( $_THEME_URL ))
  6261         {
  6262             $iconurl = $_THEME_URL . $imageurl;
  6263         }
  6264         else
  6265         {
  6266             $stdImageLoc = true;
  6267             if( !strstr( $_CONF['path_images'], $_CONF['path_html'] ))
  6268             {
  6269                 $stdImageLoc = false;
  6270             }
  6271 
  6272             if( $stdImageLoc )
  6273             {
  6274                 $iconurl = $_CONF['site_url'] . $imageurl;
  6275             }
  6276             else
  6277             {
  6278                 $t = explode( '/', $imageurl );
  6279                 $topicicon = $t[count( $t ) - 1];
  6280                 $iconurl = $_CONF['site_url']
  6281                          . '/getimage.php?mode=topics&amp;image=' . $topicicon;
  6282             }
  6283         }
  6284     }
  6285 
  6286     return $iconurl;
  6287 }
  6288 
  6289 /**
  6290  * Create an HTML link
  6291  *
  6292  * @param   string  $content    the object to be linked (text, image etc)
  6293  * @param   string  $url        the URL the link will point to
  6294  * @param   array   $attr       an array of optional attributes for the link
  6295  *                              for example array('title' => 'whatever');
  6296  * @return  string              the HTML link
  6297  */
  6298 function COM_createLink($content, $url, $attr = array())
  6299 {
  6300     $retval = '';
  6301 
  6302     $attr_str = 'href="' . $url . '"';
  6303     foreach ($attr as $key => $value) {
  6304         $attr_str .= " $key=\"$value\"";
  6305     }
  6306     $retval .= "<a $attr_str>$content</a>";
  6307 
  6308     return $retval;
  6309 }
  6310 
  6311 /**
  6312  * Create an HTML img
  6313  *
  6314  * @param   string  $url        the URL of the image, either starting with
  6315  *                              http://... or $_CONF['layout_url'] is prepended
  6316  * @param   string  $alt        the 'alt'-tag of the image
  6317  * @param   array   $attr       an array of optional attributes for the link
  6318  *                              for example array('title' => 'whatever');
  6319  * @return  string              the HTML img
  6320  */
  6321 function COM_createImage($url, $alt = "", $attr = array())
  6322 {
  6323     global $_CONF;
  6324 
  6325     $retval = '';
  6326 
  6327     if (preg_match("/^(https?):/", $url) !== 1) {
  6328         $url = $_CONF['layout_url'] . $url;
  6329     }
  6330     $attr_str = 'src="' . $url . '"';
  6331 
  6332     foreach ($attr as $key => $value) {
  6333         $attr_str .= " $key=\"$value\"";
  6334     }
  6335 
  6336     $retval = "<img $attr_str alt=\"$alt\"" . XHTML . ">";
  6337 
  6338     return $retval;
  6339 }
  6340 
  6341 /**
  6342 * Try to determine the user's preferred language by looking at the
  6343 * "Accept-Language" header sent by their browser (assuming they bothered
  6344 * to select a preferred language there).
  6345 *
  6346 * Sample header: Accept-Language: en-us,en;q=0.7,de-de;q=0.3
  6347 *
  6348 * @return   string  name of the language file to use or an empty string
  6349 * @todo     Bugs: Does not take the quantity ('q') parameter into account,
  6350 *           but only looks at the order of language codes.
  6351 *
  6352 */
  6353 function COM_getLanguageFromBrowser()
  6354 {
  6355     global $_CONF;
  6356 
  6357     $retval = '';
  6358 
  6359     if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
  6360         $accept = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
  6361         foreach ($accept as $l) {
  6362             $l = explode(';', trim($l));
  6363             $l = $l[0];
  6364             if (array_key_exists($l, $_CONF['language_files'])) {
  6365                 $retval = $_CONF['language_files'][$l];
  6366                 break;
  6367             } else {
  6368                 $l = explode('-', $l);
  6369                 $l = $l[0];
  6370                 if (array_key_exists($l, $_CONF['language_files'])) {
  6371                     $retval = $_CONF['language_files'][$l];
  6372                     break;
  6373                 }
  6374             }
  6375         }
  6376     }
  6377 
  6378     return $retval;
  6379 }
  6380 
  6381 /**
  6382 * Determine current language
  6383 *
  6384 * @return   string  name of the language file (minus the '.php' extension)
  6385 *
  6386 */
  6387 function COM_getLanguage()
  6388 {
  6389     global $_CONF, $_USER;
  6390 
  6391     $langfile = '';
  6392 
  6393     if (!empty($_USER['language'])) {
  6394         $langfile = $_USER['language'];
  6395     } elseif (!empty($_COOKIE[$_CONF['cookie_language']])) {
  6396         $langfile = $_COOKIE[$_CONF['cookie_language']];
  6397     } elseif (isset($_CONF['languages'])) {
  6398         $langfile = COM_getLanguageFromBrowser();
  6399     }
  6400 
  6401     $langfile = COM_sanitizeFilename($langfile);
  6402     if (!empty($langfile)) {
  6403         if (is_file($_CONF['path_language'] . $langfile . '.php')) {
  6404             return $langfile;
  6405         }
  6406     }
  6407 
  6408     // if all else fails, return the default language
  6409     return $_CONF['language'];
  6410 }
  6411 
  6412 /**
  6413 * Determine the ID to use for the current language
  6414 *
  6415 * The $_CONF['language_files'] array maps language IDs to language file names.
  6416 * This function returns the language ID for a certain language file, to be
  6417 * used in language-dependent URLs.
  6418 *
  6419 * @param    string  $language   current language file name (optional)
  6420 * @return   string              language ID, e.g 'en'; empty string on error
  6421 *
  6422 */
  6423 function COM_getLanguageId($language = '')
  6424 {
  6425     global $_CONF;
  6426 
  6427     if (empty($language)) {
  6428         $language = COM_getLanguage();
  6429     }
  6430 
  6431     $lang_id = '';
  6432     if (isset($_CONF['language_files'])) {
  6433         $lang_id = array_search($language, $_CONF['language_files']);
  6434 
  6435         if ($lang_id === false) {
  6436             // that looks like a misconfigured $_CONF['language_files'] array
  6437             COM_errorLog('Language "' . $language . '" not found in $_CONF[\'language_files\'] array!');
  6438 
  6439             $lang_id = ''; // not much we can do here ...
  6440         }
  6441     }
  6442 
  6443     return $lang_id;
  6444 }
  6445 
  6446 /**
  6447 * Return SQL expression to request language-specific content
  6448 *
  6449 * Creates part of an SQL expression that can be used to request items in the
  6450 * current language only.
  6451 *
  6452 * @param    string  $field  name of the "id" field, e.g. 'sid' for stories
  6453 * @param    string  $type   part of the SQL expression, e.g. 'WHERE', 'AND'
  6454 * @param    string  $table  table name if ambiguous, e.g. in JOINs
  6455 * @return   string          SQL expression string (may be empty)
  6456 *
  6457 */
  6458 function COM_getLangSQL( $field, $type = 'WHERE', $table = '' )
  6459 {
  6460     global $_CONF;
  6461 
  6462     $sql = '';
  6463 
  6464     if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
  6465     {
  6466         if( !empty( $table ))
  6467         {
  6468             $table .= '.';
  6469         }
  6470 
  6471         $lang_id = COM_getLanguageId();
  6472 
  6473         if( !empty( $lang_id ))
  6474         {
  6475             $sql = ' ' . $type . " ({$table}$field LIKE '%\\_$lang_id')";
  6476         }
  6477     }
  6478 
  6479     return $sql;
  6480 }
  6481 
  6482 /**
  6483 * Provide a block to switch languages
  6484 *
  6485 * Provides a drop-down menu (or simple link, if you only have two languages)
  6486 * to switch languages. This can be used as a PHP block or called from within
  6487 * your theme's header.thtml:
  6488 * <code>
  6489 * <?php print phpblock_switch_language(); ?>
  6490 * </code>
  6491 *
  6492 * @return   string  HTML for drop-down or link to switch languages
  6493 *
  6494 */
  6495 function phpblock_switch_language()
  6496 {
  6497     global $_CONF;
  6498 
  6499     $retval = '';
  6500 
  6501     if( empty( $_CONF['languages'] ) || empty( $_CONF['language_files'] ) ||
  6502           ( count( $_CONF['languages'] ) != count( $_CONF['language_files'] )))
  6503     {
  6504         return $retval;
  6505     }
  6506 
  6507     $lang = COM_getLanguage();
  6508     $langId = COM_getLanguageId( $lang );
  6509 
  6510     if( count( $_CONF['languages'] ) == 2 )
  6511     {
  6512         foreach( $_CONF['languages'] as $key => $value )
  6513         {
  6514             if( $key != $langId )
  6515             {
  6516                 $newLang = $value;
  6517                 $newLangId = $key;
  6518                 break;
  6519             }
  6520         }
  6521 
  6522         $switchUrl = COM_buildUrl( $_CONF['site_url'] . '/switchlang.php?lang='
  6523                                    . $newLangId );
  6524         $retval .= COM_createLink($newLang, $switchUrl);
  6525     }
  6526     else
  6527     {
  6528         $retval .= '<form name="change" action="'. $_CONF['site_url']
  6529                 . '/switchlang.php" method="get">' . LB;
  6530         $retval .= '<input type="hidden" name="oldlang" value="' . $langId
  6531                 . '"' . XHTML . '>' . LB;
  6532 
  6533         $retval .= '<select onchange="change.submit()" name="lang">';
  6534         foreach( $_CONF['languages'] as $key => $value )
  6535         {
  6536             if( $lang == $_CONF['language_files'][$key] )
  6537             {
  6538                 $selected = ' selected="selected"';
  6539             }
  6540             else
  6541             {
  6542                 $selected = '';
  6543             }
  6544             $retval .= '<option value="' . $key . '"' . $selected . '>'
  6545                     . $value . '</option>' . LB;
  6546         }
  6547         $retval .= '</select>' . LB;
  6548         $retval .= '</form>' . LB;
  6549     }
  6550 
  6551     return $retval;
  6552 }
  6553 
  6554 /**
  6555 * Switch locale settings
  6556 *
  6557 * When multi-language support is enabled, allow overwriting the default locale
  6558 * settings with language-specific settings (date format, etc.). So in addition
  6559 * to $_CONF['date'] you can have a $_CONF['date_en'], $_CONF['date_de'], etc.
  6560 *
  6561 */
  6562 function COM_switchLocaleSettings()
  6563 {
  6564     global $_CONF;
  6565 
  6566     if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
  6567     {
  6568         $overridables = array
  6569         (
  6570           'locale',
  6571           'date', 'daytime', 'shortdate', 'dateonly', 'timeonly',
  6572           'week_start', 'hour_mode',
  6573           'thousand_separator', 'decimal_separator'
  6574         );
  6575 
  6576         $langId = COM_getLanguageId();
  6577         foreach( $overridables as $option )
  6578         {
  6579             if( isset( $_CONF[$option . '_' . $langId] ))
  6580             {
  6581                 $_CONF[$option] = $_CONF[$option . '_' . $langId];
  6582             }
  6583         }
  6584     }
  6585 }
  6586 
  6587 /**
  6588 * Get the name of the current language, minus the character set
  6589 *
  6590 * Strips the character set from $_CONF['language'].
  6591 *
  6592 * @return   string  language name
  6593 *
  6594 */
  6595 function COM_getLanguageName()
  6596 {
  6597     global $_CONF;
  6598 
  6599     $retval = '';
  6600 
  6601     $charset = '_' . strtolower(COM_getCharset());
  6602     if (substr($_CONF['language'], -strlen($charset)) == $charset) {
  6603         $retval = substr($_CONF['language'], 0, -strlen($charset));
  6604     } else {
  6605         $retval = $_CONF['language'];
  6606     }
  6607 
  6608     return $retval;
  6609 }
  6610 
  6611 /**
  6612 * Truncate a string
  6613 *
  6614 * Truncates a string to a max. length and optionally adds a filler string,
  6615 * e.g. '...', to indicate the truncation.
  6616 * This function is multi-byte string aware, based on a patch by Yusuke Sakata.
  6617 *
  6618 * NOTE: The truncated string may be shorter but will never be longer than
  6619 *       $maxlen characters, i.e. the $filler string is taken into account.
  6620 *
  6621 * @param    string  $text       the text string to truncate
  6622 * @param    int     $maxlen     max. number of characters in the truncated string
  6623 * @param    string  $filler     optional filler string, e.g. '...'
  6624 * @param    int     $endchars   number of characters to show after the filler
  6625 * @return   string              truncated string
  6626 *
  6627 */
  6628 function COM_truncate( $text, $maxlen, $filler = '', $endchars = 0 )
  6629 {
  6630     $newlen = $maxlen - MBYTE_strlen( $filler );
  6631     $len = MBYTE_strlen( $text );
  6632     if( $len > $maxlen )
  6633     {
  6634         $text = MBYTE_substr( $text, 0, $newlen - $endchars ) . $filler . MBYTE_substr( $text, $len - $endchars, $endchars );
  6635     }
  6636 
  6637     return $text;
  6638 }
  6639 
  6640 /**
  6641 * Get the current character set
  6642 *
  6643 * Uses (if available, and in this order)
  6644 * - $LANG_CHARSET (from the current language file)
  6645 * - $_CONF['default_charset'] (from siteconfig.php)
  6646 * - 'iso-8859-1' (hard-coded fallback)
  6647 *
  6648 * @return   string      character set, e.g. 'utf-8'
  6649 *
  6650 */
  6651 function COM_getCharset()
  6652 {
  6653     global $_CONF, $LANG_CHARSET;
  6654 
  6655     if( empty( $LANG_CHARSET )) {
  6656         $charset = $_CONF['default_charset'];
  6657         if( empty( $charset )) {
  6658             $charset = 'iso-8859-1';
  6659         }
  6660     } else {
  6661         $charset = $LANG_CHARSET;
  6662     }
  6663 
  6664     return $charset;
  6665 }
  6666 
  6667 /**
  6668   * Handle errors.
  6669   *
  6670   * This function will handle all PHP errors thrown at it, without exposing
  6671   * paths, and hopefully, providing much more information to Root Users than
  6672   * the default white error page.
  6673   *
  6674   * This function will call out to CUSTOM_handleError if it exists, but, be
  6675   * advised, only override this function with a very, very stable function. I'd
  6676   * suggest one that outputs some static, basic HTML.
  6677   *
  6678   * The PHP feature that allows us to do so is documented here:
  6679   * http://uk2.php.net/manual/en/function.set-error-handler.php
  6680   *
  6681   * @param  int     $errno      Error Number.
  6682   * @param  string  $errstr     Error Message.
  6683   * @param  string  $errfile    The file the error was raised in.
  6684   * @param  int     $errline    The line of the file that the error was raised at.
  6685   * @param  array   $errcontext An array that points to the active symbol table at the point the error occurred.
  6686   */
  6687 function COM_handleError($errno, $errstr, $errfile='', $errline=0, $errcontext='')
  6688 {
  6689     global $_CONF, $_USER;
  6690 
  6691     // Handle @ operator
  6692     if (error_reporting() == 0) {
  6693         return;
  6694     }
  6695 
  6696     // If in PHP4, then respect error_reporting
  6697     if ((PHP_VERSION < 5) && (($errno & error_reporting()) == 0)) {
  6698         return;
  6699     }
  6700 
  6701     /*
  6702      * If we have a root user, then output detailed error message:
  6703      */
  6704     if ((is_array($_USER) && function_exists('SEC_inGroup'))
  6705             || (isset($_CONF['rootdebug']) && $_CONF['rootdebug'])) {
  6706         if ($_CONF['rootdebug'] || SEC_inGroup('Root')) {
  6707 
  6708             header('HTTP/1.1 500 Internal Server Error');
  6709             header('Status: 500 Internal Server Error');
  6710 
  6711             $title = 'An Error Occurred';
  6712             if (!empty($_CONF['site_name'])) {
  6713                 $title = $_CONF['site_name'] . ' - ' . $title;
  6714             }
  6715             echo("<html><head><title>$title</title></head>\n<body>\n");
  6716 
  6717             echo('<h1>An error has occurred:</h1>');
  6718             if ($_CONF['rootdebug']) {
  6719                 echo('<h2 style="color: red">This is being displayed as "Root Debugging" is enabled
  6720                         in your Geeklog configuration.</h2><p>If this is a production
  6721                         website you <strong><em>must disable</em></strong> this
  6722                         option once you have resolved any issues you are
  6723                         investigating.</p>');
  6724             } else {
  6725                 echo('<p>(This text is only displayed to users in the group \'Root\')</p>');
  6726             }
  6727             echo("<p>$errno - $errstr @ $errfile line $errline</p>");
  6728 
  6729             if (!function_exists('SEC_inGroup') || !SEC_inGroup('Root')) {
  6730                 if ('force' != ''.$_CONF['rootdebug']) {
  6731                     $errcontext = COM_rootDebugClean($errcontext);
  6732                 } else {
  6733                     echo('<h2 style="color: red">Root Debug is set to "force", this
  6734                     means that passwords and session cookies are exposed in this
  6735                     message!!!</h2>');
  6736                 }
  6737             }
  6738             echo('<pre>');
  6739             ob_start();
  6740             var_dump($errcontext);
  6741             $errcontext = htmlspecialchars(ob_get_contents());
  6742             ob_end_clean();
  6743             echo("$errcontext</pre></body></html>");
  6744             exit;
  6745         }
  6746     }
  6747 
  6748     /* If there is a custom error handler, fail over to that, but only
  6749      * if the error wasn't in lib-custom.php
  6750      */
  6751     if (is_array($_CONF) && !(strstr($errfile, 'lib-custom.php'))) {
  6752         if (array_key_exists('path_system', $_CONF)) {
  6753             if (file_exists($_CONF['path_system'] . 'lib-custom.php')) {
  6754                 require_once $_CONF['path_system'] . 'lib-custom.php';
  6755             }
  6756             if (function_exists('CUSTOM_handleError')) {
  6757                 CUSTOM_handleError($errno, $errstr, $errfile, $errline, $errcontext);
  6758                 exit;
  6759             }
  6760         }
  6761     }
  6762 
  6763     // if we do not throw the error back to an admin, still log it in the error.log
  6764     COM_errorLog("$errno - $errstr @ $errfile line $errline", 1);
  6765 
  6766     header('HTTP/1.1 500 Internal Server Error');
  6767     header('Status: 500 Internal Server Error');
  6768 
  6769     // Does the theme implement an error message html file?
  6770     if (!empty($_CONF['path_layout']) &&
  6771             file_exists($_CONF['path_layout'] . 'errormessage.html')) {
  6772         // NOTE: NOT A TEMPLATE! JUST HTML!
  6773         include $_CONF['path_layout'] . 'errormessage.html';
  6774     } else {
  6775         // Otherwise, display simple error message
  6776         $title = 'An Error Occurred';
  6777         if (!empty($_CONF['site_name'])) {
  6778             $title = $_CONF['site_name'] . ' - ' . $title;
  6779         }
  6780         echo("
  6781         <html>
  6782             <head>
  6783                 <title>{$title}</title>
  6784             </head>
  6785             <body>
  6786             <div style=\"width: 100%; text-align: center;\">
  6787             Unfortunately, an error has occurred rendering this page. Please try
  6788             again later.
  6789             </div>
  6790             </body>
  6791         </html>
  6792         ");
  6793     }
  6794 
  6795     exit;
  6796 }
  6797 
  6798 /**
  6799   * Recurse through the error context array removing/blanking password/cookie
  6800   * values in case the "for development" only switch is left on in a production
  6801   * environment.
  6802   *
  6803   * [Not fit for public consumption comments about what users who enable root
  6804   * debug in production should have done to them, and why making this change
  6805   * defeats the point of the entire root debug feature go here.]
  6806   *
  6807   * @param  array    $array  Array of state info (Recursive array).
  6808   * @param  boolean  $blank  override (wouldn't that blank out everything?)
  6809   * @return array            Cleaned array
  6810   */
  6811 function COM_rootDebugClean($array, $blank=false)
  6812 {
  6813     $blankField = false;
  6814     while(list($key, $value) = each($array)) {
  6815         $lkey = strtolower($key);
  6816         if((strpos($lkey, 'pass') !== false) || (strpos($lkey, 'cookie') !== false)) {
  6817             $blankField = true;
  6818         } else {
  6819             $blankField = $blank;
  6820         }
  6821         if(is_array($value)) {
  6822             $array[$key] = COM_rootDebugClean($value, $blankField);
  6823         } elseif($blankField) {
  6824             $array[$key] = '[VALUE REMOVED]';
  6825         }
  6826     }
  6827     return $array;
  6828 }
  6829 
  6830 /**
  6831   * Checks to see if a specified user, or the current user if non-specified
  6832   * is the anonymous user.
  6833   *
  6834   * @param  int $uid    ID of the user to check, or none for the current user.
  6835   * @return boolean     true if the user is the anonymous user.
  6836   */
  6837 function COM_isAnonUser($uid = '')
  6838 {
  6839     global $_USER;
  6840 
  6841     /* If no user was specified, fail over to the current user if there is one */
  6842     if( empty( $uid ) )
  6843     {
  6844         if( isset( $_USER['uid'] ) )
  6845         {
  6846             $uid = $_USER['uid'];
  6847         }
  6848     }
  6849 
  6850     if( !empty( $uid ) )
  6851     {
  6852         return ($uid == 1);
  6853     } else {
  6854         return true;
  6855     }
  6856 }
  6857 
  6858 /**
  6859 * Create Meta Tags to be used by COM_siteHeader in the headercode variable
  6860 *
  6861 * @param    string  $meta_description   the text for the meta description of the page being displayed
  6862 * @param    string  $meta_keywords        the text for the meta keywords of the page being displayed
  6863 * @return   string                         XHTML formatted text
  6864 *
  6865 */
  6866 function COM_createMetaTags($meta_description, $meta_keywords)
  6867 {
  6868     global $_CONF;
  6869 
  6870     $headercode ='';
  6871     
  6872     If ($_CONF['meta_tags'] > 0) {
  6873         if ($meta_description != '') {
  6874             $headercode .= LB . '<meta name="description" content="' . $meta_description . '"' . XHTML . '>';
  6875         }
  6876         if ($meta_keywords != '') {
  6877             $headercode .= LB . '<meta name="keywords" content="' . $meta_keywords . '"' . XHTML . '>';
  6878         }
  6879     }    
  6880 
  6881     return $headercode;
  6882 }
  6883 
  6884 
  6885 
  6886 /**
  6887 * Convert wiki-formatted text to (X)HTML
  6888 *
  6889 * @param    string  $wikitext   wiki-formatted text
  6890 * @return   string              XHTML formatted text
  6891 *
  6892 */
  6893 function COM_renderWikiText($wikitext)
  6894 {
  6895     global $_CONF;
  6896 
  6897     if (!$_CONF['wikitext_editor']) {
  6898         return $wikitext;
  6899     }
  6900 
  6901     require_once 'Text/Wiki.php';
  6902 
  6903     $wiki = new Text_Wiki();
  6904     $wiki->setFormatConf('Xhtml', 'translate', HTML_SPECIALCHARS);
  6905     $wiki->setRenderConf('Xhtml', 'charset', COM_getCharset());
  6906     $wiki->disableRule('wikilink');
  6907     $wiki->disableRule('freelink');
  6908     $wiki->disableRule('interwiki');
  6909 
  6910     return $wiki->transform($wikitext, 'Xhtml');
  6911 }
  6912 
  6913 /**
  6914 * Set the {lang_id} and {lang_attribute} variables for a template
  6915 *
  6916 * NOTE:     {lang_attribute} is only set in multi-language environments.
  6917 *
  6918 * @param    ref     &$template  template to use
  6919 * @return   void
  6920 *
  6921 */
  6922 function COM_setLangIdAndAttribute(&$template)
  6923 {
  6924     global $_CONF;
  6925 
  6926     $langAttr = '';
  6927     $langId   = '';
  6928 
  6929     if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
  6930         $langId = COM_getLanguageId();
  6931     } else {
  6932         // try to derive the language id from the locale
  6933         $l = explode('.', $_CONF['locale']); // get rid of character set
  6934         $langId = $l[0];
  6935         $l = explode('@', $langId); // get rid of '@euro', etc.
  6936         $langId = $l[0];
  6937     }
  6938 
  6939     if (!empty($langId)) {
  6940         $l = explode('-', str_replace('_', '-', $langId));
  6941         if ((count($l) == 1) && (strlen($langId) == 2)) {
  6942             $langAttr = 'lang="' . $langId . '"';
  6943         } else if (count($l) == 2) {
  6944             if (($l[0] == 'i') || ($l[0] == 'x')) {
  6945                 $langId = implode('-', $l);
  6946                 $langAttr = 'lang="' . $langId . '"';
  6947             } else if (strlen($l[0]) == 2) {
  6948                 $langId = implode('-', $l);
  6949                 $langAttr = 'lang="' . $langId . '"';
  6950             } else {
  6951                 $langId = $l[0];
  6952                 // this isn't a valid lang attribute, so don't set $langAttr
  6953             }
  6954         }
  6955     }
  6956     $template->set_var('lang_id', $langId);
  6957 
  6958     if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
  6959         $template->set_var('lang_attribute', ' ' . $langAttr);
  6960     } else {
  6961         $template->set_var('lang_attribute', '');
  6962     }
  6963 }
  6964 
  6965 /**
  6966 * Sends compressed output to browser.
  6967 *
  6968 * Assumes that $display contains the _entire_ output for a request - no
  6969 * echoes are allowed before or after this function.
  6970 * Currently only supports gzip compression. Checks if zlib compression is
  6971 * enabled in PHP and does uncompressed output if it is.
  6972 *
  6973 * @param    string  $display    Content to send to browser
  6974 * @return   void
  6975 *
  6976 */
  6977 function COM_output($display)
  6978 {
  6979     global $_CONF;
  6980 
  6981     if (empty($display)) {
  6982         return;
  6983     }
  6984 
  6985     if ($_CONF['compressed_output']) {
  6986         $gzip_accepted = false;
  6987         if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  6988             $enc = str_replace(' ', '', $_SERVER['HTTP_ACCEPT_ENCODING']);
  6989             $accept = explode(',', strtolower($enc));
  6990             $gzip_accepted = in_array('gzip', $accept);
  6991         }
  6992 
  6993         if ($gzip_accepted && function_exists('gzencode')) {
  6994 
  6995             $zlib_comp = ini_get('zlib.output_compression');
  6996             if (empty($zlib_comp) || (strcasecmp($zlib_comp, 'off') == 0)) {
  6997 
  6998                 header('Content-encoding: gzip');
  6999                 echo gzencode($display);
  7000                 return;
  7001 
  7002             }
  7003         }
  7004     }
  7005 
  7006     echo $display;
  7007 }
  7008 
  7009 /**
  7010 * Turn a piece of HTML into continuous(!) plain text
  7011 *
  7012 * This function removes HTML tags, line breaks, etc. and returns one long
  7013 * line of text. This is useful for word counts (do an explode() on the result)
  7014 * and for text excerpts.
  7015 *
  7016 * @param    string  $text   original text, including HTML and line breaks
  7017 * @return   string          continuous plain text
  7018 * 
  7019 */
  7020 function COM_getTextContent($text)
  7021 {
  7022     // replace <br> with spaces so that Text<br>Text becomes two words
  7023     $text = preg_replace('/\<br(\s*)?\/?\>/i', ' ', $text);
  7024 
  7025     // add extra space between tags, e.g. <p>Text</p><p>Text</p>
  7026     $text = str_replace('><', '> <', $text);
  7027 
  7028     // only now remove all HTML tags
  7029     $text = strip_tags($text);
  7030 
  7031     // replace all tabs, newlines, and carrriage returns with spaces
  7032     $text = str_replace(array("\011", "\012", "\015"), ' ', $text);
  7033 
  7034     // replace entities with plain spaces
  7035     $text = str_replace(array('&#20;', '&#160;', '&nbsp;'), ' ', $text);
  7036 
  7037     // collapse whitespace
  7038     $text = preg_replace('/\s\s+/', ' ', $text);
  7039 
  7040     return trim($text);
  7041 }
  7042 
  7043 /**
  7044 * Now include all plugin functions
  7045 */
  7046 foreach ($_PLUGINS as $pi_name) {
  7047     require_once $_CONF['path'] . 'plugins/' . $pi_name . '/functions.inc';
  7048 }
  7049 
  7050 // Check and see if any plugins (or custom functions)
  7051 // have scheduled tasks to perform
  7052 if ($_CONF['cron_schedule_interval'] > 0) {
  7053     if ((DB_getItem($_TABLES['vars'], 'value', "name='last_scheduled_run'")
  7054             + $_CONF['cron_schedule_interval']) <= time()) {
  7055         DB_query("UPDATE {$_TABLES['vars']} SET value=UNIX_TIMESTAMP() WHERE name='last_scheduled_run'");
  7056         PLG_runScheduledTask();
  7057     }
  7058 }
  7059 
  7060 ?>