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