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