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