3 /* Reminder: always indent with 4 spaces (no tabs). */
4 // +---------------------------------------------------------------------------+
6 // +---------------------------------------------------------------------------+
9 // | Geeklog common library. |
10 // +---------------------------------------------------------------------------+
11 // | Copyright (C) 2000-2009 by the following authors: |
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 // +---------------------------------------------------------------------------+
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. |
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. |
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. |
34 // +---------------------------------------------------------------------------+
36 // Prevent PHP from reporting uninitialized variables
37 error_reporting( E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR );
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.
44 * --- You don't need to modify anything in this file! ---
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.
53 * Turn this on to get various debug messages from the code in this library
54 * @global Boolean $_COM_VERBOSE
56 $_COM_VERBOSE = false;
59 * Prevent getting any surprise values. But we should really stop
60 * using $_REQUEST altogether.
62 $_REQUEST = array_merge($_GET, $_POST);
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...
71 * Must make sure that the function hasn't been disabled before calling it.
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.
80 $defaultErrorHandler = set_error_handler('COM_handleError',
83 $defaultErrorHandler = set_error_handler('COM_handleError');
88 * Configuration Include:
89 * You do NOT need to modify anything here any more!
91 require_once 'siteconfig.php';
96 require_once $_CONF['path_system'] . 'classes/config.class.php';
98 $config =& config::get_instance();
99 $config->set_configfile($_CONF['path'] . 'db-config.php');
100 $config->load_baseconfig();
101 $config->initConfig();
103 $_CONF = $config->get_config('Core');
105 // Before we do anything else, check to ensure site is enabled
107 if (isset($_CONF['site_enabled']) && !$_CONF['site_enabled']) {
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.';
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']);
118 header("HTTP/1.1 503 Service Unavailable");
119 header("Status: 503 Service Unavailable");
120 echo $_CONF['site_disabled_msg'];
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');
133 // timezone hack - set the webserver's timezone
134 if( !empty( $_CONF['timezone'] ) && !ini_get( 'safe_mode' ) &&
135 function_exists( 'putenv' )) {
136 putenv( 'TZ=' . $_CONF['timezone'] );
140 // +---------------------------------------------------------------------------+
141 // | Library Includes: You shouldn't have to touch anything below here |
142 // +---------------------------------------------------------------------------+
145 * If needed, add our PEAR path to the list of include paths
148 if (! $_CONF['have_pear']) {
149 $curPHPIncludePath = get_include_path();
150 if (empty($curPHPIncludePath)) {
151 $curPHPIncludePath = $_CONF['path_pear'];
153 $curPHPIncludePath = $_CONF['path_pear'] . PATH_SEPARATOR
154 . $curPHPIncludePath;
157 if (set_include_path($curPHPIncludePath) === false) {
158 COM_errorLog('set_include_path failed - there may be problems using the PEAR classes.', 1);
163 * Include plugin class.
164 * This is a poorly implemented class that was not very well thought out.
165 * Still very necessary
169 require_once( $_CONF['path_system'] . 'lib-plugins.php' );
172 * Include page time -- used to time how fast each page was created
176 require_once( $_CONF['path_system'] . 'classes/timer.class.php' );
177 $_PAGE_TIMER = new timerobject();
178 $_PAGE_TIMER->startTimer();
183 * This provides optional URL rewriting functionality.
186 require_once( $_CONF['path_system'] . 'classes/url.class.php' );
187 $_URL = new url( $_CONF['url_rewrite'] );
190 * This is our HTML template class. It is the same one found in PHPLib and is
191 * licensed under the LGPL. See that file for details.
195 require_once( $_CONF['path_system'] . 'classes/template.class.php' );
198 * This is the security library used for application security
202 require_once( $_CONF['path_system'] . 'lib-security.php' );
205 * This is the syndication library used to offer (RSS) feeds.
209 require_once( $_CONF['path_system'] . 'lib-syndication.php' );
212 *These variables were taken out of the configuration and placed here since they
213 *are necessary to change with the themes, not whole sites. They should now be
214 *overridden by setting them to a different value than here in the theme's
215 *function.php or in lib-custom.php. Therefore they are NOT TO BE CHANGED HERE.
217 $_CONF['left_blocks_in_footer'] = 0; // use left blocks in header
218 $_CONF['right_blocks_in_footer'] = 1; // use right blocks in footer
221 * This is the custom library.
223 * It is the sandbox for every Geeklog Admin to play in.
224 * The lib-custom.php as shipped will never contain required code,
225 * so it's safe to always use your own copy.
226 * This should hold all custom hacks to make upgrading easier.
230 require_once( $_CONF['path_system'] . 'lib-custom.php' );
233 * Session management library
237 require_once( $_CONF['path_system'] . 'lib-sessions.php' );
240 * Ulf Harnhammar's kses class
244 require_once( $_CONF['path_system'] . 'classes/kses.class.php' );
247 * Multibyte functions
250 require_once( $_CONF['path_system'] . 'lib-mbyte.php' );
255 if( isset( $_POST['usetheme'] ))
257 $usetheme = COM_sanitizeFilename($_POST['usetheme'], true);
259 if( !empty( $usetheme ) && is_dir( $_CONF['path_themes'] . $usetheme ))
261 $_CONF['theme'] = $usetheme;
262 $_CONF['path_layout'] = $_CONF['path_themes'] . $_CONF['theme'] . '/';
263 $_CONF['layout_url'] = $_CONF['site_url'] . '/layout/' . $_CONF['theme'];
265 else if( $_CONF['allow_user_themes'] == 1 )
267 if( isset( $_COOKIE[$_CONF['cookie_theme']] ) && empty( $_USER['theme'] ))
269 $theme = COM_sanitizeFilename($_COOKIE[$_CONF['cookie_theme']], true);
270 if( is_dir( $_CONF['path_themes'] . $theme ))
272 $_USER['theme'] = $theme;
276 if( !empty( $_USER['theme'] ))
278 if( is_dir( $_CONF['path_themes'] . $_USER['theme'] ))
280 $_CONF['theme'] = $_USER['theme'];
281 $_CONF['path_layout'] = $_CONF['path_themes'] . $_CONF['theme'] . '/';
282 $_CONF['layout_url'] = $_CONF['site_url'] . '/layout/' . $_CONF['theme'];
286 $_USER['theme'] = $_CONF['theme'];
292 * Include theme functions file
295 // Include theme functions file which may/may not do anything
297 if (file_exists($_CONF['path_layout'] . 'functions.php')) {
298 require_once $_CONF['path_layout'] . 'functions.php';
302 * ensure XHTML constant is defined to avoid problems elsewhere
304 if (!defined('XHTML')) {
305 switch ($_CONF['doctype']) {
306 case 'xhtml10transitional':
307 case 'xhtml10strict':
308 define('XHTML', ' /');
320 // themes can now specify the default image type
321 // fall back to 'gif' if they don't
323 if (empty($_IMAGE_TYPE)) {
324 $_IMAGE_TYPE = 'gif';
327 // Similarly set language
329 if( isset( $_COOKIE[$_CONF['cookie_language']] ) && empty( $_USER['language'] ))
331 $language = COM_sanitizeFilename($_COOKIE[$_CONF['cookie_language']]);
332 if( is_file( $_CONF['path_language'] . $language . '.php' ) &&
333 ( $_CONF['allow_user_language'] == 1 ))
335 $_USER['language'] = $language;
336 $_CONF['language'] = $language;
339 else if( !empty( $_USER['language'] ))
341 if( is_file( $_CONF['path_language'] . $_USER['language'] . '.php' ) &&
342 ( $_CONF['allow_user_language'] == 1 ))
344 $_CONF['language'] = $_USER['language'];
347 else if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
349 $_CONF['language'] = COM_getLanguage();
352 // Handle Who's Online block
353 if (COM_isAnonUser() && isset($_SERVER['REMOTE_ADDR'])) {
354 // The following code handles anonymous users so they show up properly
355 DB_delete($_TABLES['sessions'], array('remote_ip', 'uid'),
356 array($_SERVER['REMOTE_ADDR'], 1));
361 // Build a useless sess_id (needed for insert to work properly)
362 $sess_id = mt_rand();
365 // Insert anonymous user session
366 $result = DB_query( "INSERT INTO {$_TABLES['sessions']} (sess_id, start_time, remote_ip, uid) VALUES ($sess_id, $curtime, '{$_SERVER['REMOTE_ADDR']}', 1)", 1 );
369 while(( $result === false) && ( $tries < 5 ));
372 // Clear out any expired sessions
373 DB_query( "DELETE FROM {$_TABLES['sessions']} WHERE start_time < " . ( time() - $_CONF['whosonline_threshold'] ));
381 require_once $_CONF['path_language'] . $_CONF['language'] . '.php';
383 if (empty($LANG_DIRECTION)) {
384 // default to left-to-right
385 $LANG_DIRECTION = 'ltr';
388 COM_switchLocaleSettings();
390 if( setlocale( LC_ALL, $_CONF['locale'] ) === false )
392 setlocale( LC_TIME, $_CONF['locale'] );
396 * Global array of groups current user belongs to
398 * @global array $_GROUPS
402 if( !COM_isAnonUser() )
404 $_GROUPS = SEC_getUserGroups( $_USER['uid'] );
408 $_GROUPS = SEC_getUserGroups( 1 );
412 * Global array of current user permissions [read,edit]
414 * @global array $_RIGHTS
418 $_RIGHTS = explode( ',', SEC_getUserPermissions() );
420 if( isset( $_GET['topic'] ))
422 $topic = COM_applyFilter( $_GET['topic'] );
424 else if( isset( $_POST['topic'] ))
426 $topic = COM_applyFilter( $_POST['topic'] );
434 // +---------------------------------------------------------------------------+
436 // +---------------------------------------------------------------------------+
439 * Return the file to use for a block template.
441 * This returns the template needed to build the HTML for a block. This function
442 * allows designers to give a block it's own custom look and feel. If no
443 * templates for the block are specified, the default blockheader.html and
444 * blockfooter.html will be used.
446 * @param string $blockname corresponds to name field in block table
447 * @param string $which can be either 'header' or 'footer' for corresponding template
448 * @param string $position can be 'left', 'right' or blank. If set, will be used to find a side specific override template.
449 * @see function COM_startBlock
450 * @see function COM_endBlock
451 * @see function COM_showBlocks
452 * @see function COM_showBlock
453 * @return string template name
455 function COM_getBlockTemplate( $blockname, $which, $position='' )
457 global $_BLOCK_TEMPLATE, $_COM_VERBOSE, $_CONF;
461 COM_errorLog( "_BLOCK_TEMPLATE[$blockname] = " . $_BLOCK_TEMPLATE[$blockname], 1 );
464 if( !empty( $_BLOCK_TEMPLATE[$blockname] ))
466 $templates = explode( ',', $_BLOCK_TEMPLATE[$blockname] );
467 if( $which == 'header' )
469 if( !empty( $templates[0] ))
471 $template = $templates[0];
475 $template = 'blockheader.thtml';
480 if( !empty( $templates[1] ))
482 $template = $templates[1];
486 $template = 'blockfooter.thtml';
492 if( $which == 'header' )
494 $template = 'blockheader.thtml';
498 $template = 'blockfooter.thtml';
502 // If we have a position specific request, and the template is not already
503 // position specific then look to see if there is a position specific
505 $templateLC = strtolower($template);
506 if( !empty($position) && ( strpos($templateLC, $position) === false ) )
508 // Trim .thtml from the end.
509 $positionSpecific = substr($template, 0, strlen($template) - 6);
510 $positionSpecific .= '-' . $position . '.thtml';
511 if( file_exists( $_CONF['path_layout'] . $positionSpecific ) )
513 $template = $positionSpecific;
519 COM_errorLog( "Block template for the $which of $blockname is: $template", 1 );
526 * Gets all installed themes
528 * Returns a list of all the directory names in $_CONF['path_themes'], i.e.
529 * a list of all the theme names.
531 * @param boolean $all if true, return all themes even if users aren't allowed to change their default themes
532 * @return array All installed themes
535 function COM_getThemes( $all = false )
543 // If users aren't allowed to change their theme then only return the default theme
545 if(( $_CONF['allow_user_themes'] == 0 ) && !$all )
547 $themes[$index] = $_CONF['theme'];
551 $fd = opendir( $_CONF['path_themes'] );
553 while(( $dir = @readdir( $fd )) == TRUE )
555 if( is_dir( $_CONF['path_themes'] . $dir) && $dir <> '.' && $dir <> '..' && $dir <> 'CVS' && substr( $dir, 0 , 1 ) <> '.' )
558 $themes[$index] = $dir;
568 * Create the menu, i.e. replace {menu_elements} in the site header with the
569 * actual menu entries.
571 * @param Template &$header reference to the header template
572 * @param array $plugin_menu array of plugin menu entries, if any
575 function COM_renderMenu( &$header, $plugin_menu )
577 global $_CONF, $_USER, $LANG01, $topic;
579 if( empty( $_CONF['menu_elements'] ))
581 $_CONF['menu_elements'] = array( // default set of links
582 'contribute', 'search', 'stats', 'directory', 'plugins' );
585 $anon = COM_isAnonUser();
590 $num_plugins = count( $plugin_menu );
591 if( ( $num_plugins == 0 ) && in_array( 'plugins', $_CONF['menu_elements'] ))
593 $key = array_search( 'plugins', $_CONF['menu_elements'] );
594 unset( $_CONF['menu_elements'][$key] );
597 if( in_array( 'custom', $_CONF['menu_elements'] ))
599 $custom_entries = array();
600 if( function_exists( 'CUSTOM_menuEntries' ))
602 $custom_entries = CUSTOM_menuEntries();
604 if( count( $custom_entries ) == 0 )
606 $key = array_search( 'custom', $_CONF['menu_elements'] );
607 unset( $_CONF['menu_elements'][$key] );
611 $num_elements = count( $_CONF['menu_elements'] );
613 foreach( $_CONF['menu_elements'] as $item )
617 $last_entry = ( $counter == $num_elements ) ? true : false;
624 $url = $_CONF['site_url'] . '/submit.php?type=story';
625 $header->set_var( 'current_topic', '' );
629 $url = $_CONF['site_url']
630 . '/submit.php?type=story&topic=' . $topic;
631 $header->set_var( 'current_topic', '&topic=' . $topic );
633 $label = $LANG01[71];
634 if( $anon && ( $_CONF['loginrequired'] ||
635 $_CONF['submitloginrequired'] ))
642 if (function_exists('CUSTOM_renderMenu')) {
643 CUSTOM_renderMenu($header, $custom_entries, $menuCounter);
646 $custom_size = count($custom_entries);
647 foreach ($custom_entries as $entry) {
650 if (empty($entry['url']) || empty($entry['label'])) {
654 $header->set_var('menuitem_url', $entry['url']);
655 $header->set_var('menuitem_text', $entry['label']);
657 if ($last_entry && ($custom_count == $custom_size)) {
658 $header->parse('menu_elements', 'menuitem_last',
661 $header->parse('menu_elements', 'menuitem', true);
671 $url = $_CONF['site_url'] . '/directory.php';
672 if( !empty( $topic ))
674 $url = COM_buildUrl( $url . '?topic='
675 . urlencode( $topic ));
677 $label = $LANG01[117];
678 if( $anon && ( $_CONF['loginrequired'] ||
679 $_CONF['directoryloginrequired'] ))
686 $url = $_CONF['site_url'] . '/';
687 $label = $LANG01[90];
691 for( $i = 1; $i <= $num_plugins; $i++ )
693 $header->set_var( 'menuitem_url', current( $plugin_menu ));
694 $header->set_var( 'menuitem_text', key( $plugin_menu ));
696 if( $last_entry && ( $i == $num_plugins ))
698 $header->parse( 'menu_elements', 'menuitem_last',
703 $header->parse( 'menu_elements', 'menuitem', true );
707 next( $plugin_menu );
714 $url = $_CONF['site_url'] . '/usersettings.php';
715 $label = $LANG01[48];
719 $url = $_CONF['site_url'] . '/search.php';
720 $label = $LANG01[75];
721 if( $anon && ( $_CONF['loginrequired'] ||
722 $_CONF['searchloginrequired'] ))
729 $url = $_CONF['site_url'] . '/stats.php';
730 $label = $LANG01[76];
732 ( $_CONF['loginrequired'] || $_CONF['statsloginrequired'] ))
738 default: // unknown entry
744 if( !empty( $url ) && !empty( $label ))
746 $header->set_var( 'menuitem_url', $url );
747 $header->set_var( 'menuitem_text', $label );
750 $header->parse( 'menu_elements', 'menuitem_last', true );
754 $header->parse( 'menu_elements', 'menuitem', true );
762 $header->parse( 'allowed_menu_elements', 'menuitem_last',
767 $header->parse( 'allowed_menu_elements', 'menuitem', true );
774 if( $menuCounter == 0 )
776 $header->parse( 'menu_elements', 'menuitem_none', true );
778 if( $allowedCounter == 0 )
780 $header->parse( 'allowed_menu_elements', 'menuitem_none', true );
785 * Returns the site header
787 * This loads the proper templates, does variable substitution and returns the
788 * HTML for the site header with or without blocks depending on the value of $what
792 * The two functions COM_siteHeader and COM_siteFooter provide the framework for
793 * page display in Geeklog. COM_siteHeader controls the display of the Header
794 * and left blocks and COM_siteFooter controls the dsiplay of the right blocks
795 * and the footer. You use them like a sandwich. Thus the following code will
796 * display a Geeklog page with both right and left blocks displayed.
800 * require_once 'lib-common.php';
801 * // Change to COM_siteHeader('none') to not display left blocks
802 * $display .= COM_siteHeader();
803 * $display .= "Here is your html for display";
804 * // Change to COM_siteFooter() to not display right blocks
805 * $display .= COM_siteFooter(true);
810 * Note that the default for the header is to display the left blocks and the
811 * default of the footer is to not display the right blocks.
813 * This sandwich produces code like this (greatly simplified)
816 * <table><tr><td colspan="3">Header</td></tr>
817 * <tr><td>Left Blocks</td><td>
819 * // Your HTML goes here
820 * Here is your html for display
823 * </td><td>Right Blocks</td></tr>
824 * <tr><td colspan="3">Footer</td></table>
827 * @param string $what If 'none' then no left blocks are returned, if 'menu' (default) then right blocks are returned
828 * @param string $pagetitle optional content for the page's <title>
829 * @param string $headercode optional code to go into the page's <head>
830 * @return string Formatted HTML containing the site header
831 * @see function COM_siteFooter
834 function COM_siteHeader( $what = 'menu', $pagetitle = '', $headercode = '' )
836 global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_BUTTONS, $LANG_DIRECTION,
837 $_IMAGE_TYPE, $topic, $_COM_VERBOSE;
839 // If the theme implemented this for us then call their version instead.
841 $function = $_CONF['theme'] . '_siteHeader';
843 if( function_exists( $function ))
845 return $function( $what, $pagetitle, $headercode );
848 // If we reach here then either we have the default theme OR
849 // the current theme only needs the default variable substitutions
851 switch ($_CONF['doctype']) {
852 case 'html401transitional':
853 $doctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
856 case 'html401strict':
857 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
860 case 'xhtml10transitional':
861 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
864 case 'xhtml10strict':
865 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
868 default: // fallback: HTML 4.01 Transitional w/o system identifier
869 $doctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">';
873 // send out the charset header
874 header('Content-Type: text/html; charset=' . COM_getCharset());
876 if (!empty($_CONF['frame_options'])) {
877 header('X-FRAME-OPTIONS: ' . $_CONF['frame_options']);
880 $header = new Template( $_CONF['path_layout'] );
881 $header->set_file( array(
882 'header' => 'header.thtml',
883 'menuitem' => 'menuitem.thtml',
884 'menuitem_last' => 'menuitem_last.thtml',
885 'menuitem_none' => 'menuitem_none.thtml',
886 'leftblocks' => 'leftblocks.thtml',
887 'rightblocks' => 'rightblocks.thtml'
889 $header->set_var('doctype', $doctype);
890 $header->set_var('xhtml', XHTML);
892 $header->set_var('xmlns', '');
894 $header->set_var('xmlns', ' xmlns="http://www.w3.org/1999/xhtml"');
897 // get topic if not on home page
898 if( !isset( $_GET['topic'] ))
900 if( isset( $_GET['story'] ))
902 $sid = COM_applyFilter( $_GET['story'] );
904 elseif( isset( $_GET['sid'] ))
906 $sid = COM_applyFilter( $_GET['sid'] );
908 elseif( isset( $_POST['story'] ))
910 $sid = COM_applyFilter( $_POST['story'] );
912 if( empty( $sid ) && $_CONF['url_rewrite'] &&
913 ( strpos( $_SERVER['PHP_SELF'], 'article.php' ) !== false ))
915 COM_setArgNames( array( 'story', 'mode' ));
916 $sid = COM_applyFilter( COM_getArgument( 'story' ));
920 $topic = DB_getItem( $_TABLES['stories'], 'tid', "sid='$sid'" );
925 $topic = COM_applyFilter( $_GET['topic'] );
929 if( $_CONF['backend'] == 1 ) // add feed-link to header if applicable
931 $baseurl = SYND_getFeedUrl();
933 $sql = 'SELECT format, filename, title, language FROM '
934 . $_TABLES['syndication'] . " WHERE (header_tid = 'all')";
935 if( !empty( $topic ))
937 $sql .= " OR (header_tid = '" . addslashes( $topic ) . "')";
939 $result = DB_query( $sql );
940 $numRows = DB_numRows( $result );
941 for( $i = 0; $i < $numRows; $i++ )
943 $A = DB_fetchArray( $result );
944 if ( !empty( $A['filename'] ))
946 $format = explode( '-', $A['format'] );
947 $format_type = strtolower( $format[0] );
948 $format_name = ucwords( $format[0] );
950 $feed_url[] = '<link rel="alternate" type="application/'
951 . $format_type . '+xml" hreflang="' . $A['language']
952 . '" href="' . $baseurl . $A['filename'] . '" title="'
953 . $format_name . ' Feed: ' . $A['title'] . '"' . XHTML . '>';
957 $header->set_var( 'feed_url', implode( LB, $feed_url ));
960 if( !COM_onFrontpage() )
962 $relLinks['home'] = '<link rel="home" href="' . $_CONF['site_url']
963 . '/" title="' . $LANG01[90] . '"' . XHTML . '>';
965 $loggedInUser = !COM_isAnonUser();
966 if( $loggedInUser || (( $_CONF['loginrequired'] == 0 ) &&
967 ( $_CONF['searchloginrequired'] == 0 )))
969 if(( substr( $_SERVER['PHP_SELF'], -strlen( '/search.php' ))
970 != '/search.php' ) || isset( $_GET['mode'] ))
972 $relLinks['search'] = '<link rel="search" href="'
973 . $_CONF['site_url'] . '/search.php" title="'
974 . $LANG01[75] . '"' . XHTML . '>';
977 if( $loggedInUser || (( $_CONF['loginrequired'] == 0 ) &&
978 ( $_CONF['directoryloginrequired'] == 0 )))
980 if( strpos( $_SERVER['PHP_SELF'], '/article.php' ) !== false ) {
981 $relLinks['contents'] = '<link rel="contents" href="'
982 . $_CONF['site_url'] . '/directory.php" title="'
983 . $LANG01[117] . '"' . XHTML . '>';
986 if (!$_CONF['disable_webservices']) {
987 $relLinks['service'] = '<link rel="service" '
988 . 'type="application/atomsvc+xml" ' . 'href="'
989 . $_CONF['site_url'] . '/webservices/atom/?introspection" '
990 . 'title="' . $LANG01[130] . '"' . XHTML . '>';
992 // TBD: add a plugin API and a lib-custom.php function
993 $header->set_var( 'rel_links', implode( LB, $relLinks ));
995 $pagetitle_siteslogan = false;
996 if( empty( $pagetitle ))
1000 $pagetitle = $_CONF['site_slogan'];
1001 $pagetitle_siteslogan = true;
1005 $pagetitle = stripslashes( DB_getItem( $_TABLES['topics'], 'topic',
1006 "tid = '$topic'" ));
1009 if( !empty( $pagetitle ))
1011 $header->set_var( 'page_site_splitter', ' - ');
1015 $header->set_var( 'page_site_splitter', '');
1017 $header->set_var( 'page_title', $pagetitle );
1018 $header->set_var( 'site_name', $_CONF['site_name']);
1020 if (COM_onFrontpage() OR $pagetitle_siteslogan) {
1021 $title_and_name = $_CONF['site_name'];
1022 if (!empty($pagetitle)) {
1023 $title_and_name .= ' - ' . $pagetitle;
1026 $title_and_name = '';
1027 if (!empty($pagetitle)) {
1028 $title_and_name = $pagetitle . ' - ';
1030 $title_and_name .= $_CONF['site_name'];
1032 $header->set_var('page_title_and_site_name', $title_and_name);
1034 COM_setLangIdAndAttribute($header);
1036 $header->set_var( 'background_image', $_CONF['layout_url']
1037 . '/images/bg.' . $_IMAGE_TYPE );
1038 $header->set_var( 'site_url', $_CONF['site_url'] );
1039 $header->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1040 $header->set_var( 'layout_url', $_CONF['layout_url'] );
1041 $header->set_var( 'site_mail', "mailto:{$_CONF['site_mail']}" );
1042 $header->set_var( 'site_name', $_CONF['site_name'] );
1043 $header->set_var( 'site_slogan', $_CONF['site_slogan'] );
1044 $rdf = substr_replace( $_CONF['rdf_file'], $_CONF['site_url'], 0,
1045 strlen( $_CONF['path_html'] ) - 1 );
1046 $header->set_var( 'rdf_file', $rdf );
1047 $header->set_var( 'rss_url', $rdf );
1049 $msg = rtrim($LANG01[67]) . ' ' . $_CONF['site_name'];
1051 if( !empty( $_USER['username'] ))
1053 $msg .= ', ' . COM_getDisplayName( $_USER['uid'], $_USER['username'],
1054 $_USER['fullname'] );
1057 $curtime = COM_getUserDateTimeFormat();
1059 $header->set_var( 'welcome_msg', $msg );
1060 $header->set_var( 'datetime', $curtime[0] );
1061 $header->set_var( 'site_logo', $_CONF['layout_url']
1062 . '/images/logo.' . $_IMAGE_TYPE );
1063 $header->set_var( 'css_url', $_CONF['layout_url'] . '/style.css' );
1064 $header->set_var( 'theme', $_CONF['theme'] );
1066 $header->set_var('charset', COM_getCharset());
1067 $header->set_var('direction', $LANG_DIRECTION);
1069 // Now add variables for buttons like e.g. those used by the Yahoo theme
1070 $header->set_var( 'button_home', $LANG_BUTTONS[1] );
1071 $header->set_var( 'button_contact', $LANG_BUTTONS[2] );
1072 $header->set_var( 'button_contribute', $LANG_BUTTONS[3] );
1073 $header->set_var( 'button_sitestats', $LANG_BUTTONS[7] );
1074 $header->set_var( 'button_personalize', $LANG_BUTTONS[8] );
1075 $header->set_var( 'button_search', $LANG_BUTTONS[9] );
1076 $header->set_var( 'button_advsearch', $LANG_BUTTONS[10] );
1077 $header->set_var( 'button_directory', $LANG_BUTTONS[11] );
1079 // Get plugin menu options
1080 $plugin_menu = PLG_getMenuItems();
1084 COM_errorLog( 'num plugin menu items in header = ' . count( $plugin_menu ), 1 );
1087 // Now add nested template for menu items
1088 COM_renderMenu( $header, $plugin_menu );
1090 if( count( $plugin_menu ) == 0 )
1092 $header->parse( 'plg_menu_elements', 'menuitem_none', true );
1096 $count_plugin_menu = count( $plugin_menu );
1097 for( $i = 1; $i <= $count_plugin_menu; $i++ )
1099 $header->set_var( 'menuitem_url', current( $plugin_menu ));
1100 $header->set_var( 'menuitem_text', key( $plugin_menu ));
1102 if( $i == $count_plugin_menu )
1104 $header->parse( 'plg_menu_elements', 'menuitem_last', true );
1108 $header->parse( 'plg_menu_elements', 'menuitem', true );
1111 next( $plugin_menu );
1115 // Call to plugins to set template variables in the header
1116 PLG_templateSetVars( 'header', $header );
1118 if( $_CONF['left_blocks_in_footer'] == 1 )
1120 $header->set_var( 'left_blocks', '' );
1121 $header->set_var( 'geeklog_blocks', '' );
1127 /* Check if an array has been passed that includes the name of a plugin
1128 * function or custom function
1129 * This can be used to take control over what blocks are then displayed
1131 if( is_array( $what ))
1133 $function = $what[0];
1134 if( function_exists( $function ))
1136 $lblocks = $function( $what[1], 'left' );
1140 $lblocks = COM_showBlocks( 'left', $topic );
1143 else if( $what <> 'none' )
1145 // Now show any blocks -- need to get the topic if not on home page
1146 $lblocks = COM_showBlocks( 'left', $topic );
1149 if( empty( $lblocks ))
1151 $header->set_var( 'left_blocks', '' );
1152 $header->set_var( 'geeklog_blocks', '' );
1156 $header->set_var( 'geeklog_blocks', $lblocks );
1157 $header->parse( 'left_blocks', 'leftblocks', true );
1158 $header->set_var( 'geeklog_blocks', '');
1162 if( $_CONF['right_blocks_in_footer'] == 1 )
1164 $header->set_var( 'right_blocks', '' );
1165 $header->set_var( 'geeklog_blocks', '' );
1171 /* Check if an array has been passed that includes the name of a plugin
1172 * function or custom function
1173 * This can be used to take control over what blocks are then displayed
1175 if( is_array( $what ))
1177 $function = $what[0];
1178 if( function_exists( $function ))
1180 $rblocks = $function( $what[1], 'right' );
1184 $rblocks = COM_showBlocks( 'right', $topic );
1187 else if( $what <> 'none' )
1189 // Now show any blocks -- need to get the topic if not on home page
1190 $rblocks = COM_showBlocks( 'right', $topic );
1193 if( empty( $rblocks ))
1195 $header->set_var( 'right_blocks', '' );
1196 $header->set_var( 'geeklog_blocks', '' );
1200 $header->set_var( 'geeklog_blocks', $rblocks, true );
1201 $header->parse( 'right_blocks', 'rightblocks', true );
1205 if( isset( $_CONF['advanced_editor'] ) && ( $_CONF['advanced_editor'] == 1 )
1206 && file_exists( $_CONF['path_layout']
1207 . 'advanced_editor_header.thtml' ))
1209 $header->set_file( 'editor' , 'advanced_editor_header.thtml');
1210 $header->parse( 'advanced_editor', 'editor' );
1215 $header->set_var( 'advanced_editor', '' );
1218 // Call any plugin that may want to include extra Meta tags
1219 // or Javascript functions
1220 $headercode .= PLG_getHeaderCode();
1223 // 0 = Disabled, 1 = Enabled, 2 = Enabled but default just for homepage
1224 if ($_CONF['meta_tags'] > 0) {
1225 $meta_description = '';
1226 $meta_keywords = '';
1227 $no_meta_description = 1;
1228 $no_meta_keywords = 1;
1230 //Find out if the meta tag description or keywords already exist in the headercode
1231 if ($headercode != '') {
1232 $pattern = '/<meta ([^>]*)name="([^"\'>]*)"([^>]*)/im';
1233 if (preg_match_all($pattern, $headercode, $matches, PREG_SET_ORDER)) {
1234 // Loop through all meta tags looking for description and keywords
1235 for ($i = 0; $i<count($matches) && (($no_meta_description == 1) || ($no_meta_keywords == 1)); $i++) {
1236 $str_matches = strtolower($matches[$i][0]);
1237 $pos = strpos($str_matches,'name=');
1238 if (!(is_bool($pos) && !$pos)) {
1239 $name = trim(substr($str_matches,$pos+5),'"');
1240 $pos = strpos($name,'"');
1241 $name = substr($name,0,$pos);
1243 if (strcasecmp("description",$name) == 0) {
1244 $pos = strpos($str_matches,'content=');
1245 if (!(is_bool($pos) && !$pos)) {
1246 $no_meta_description = 0;
1249 if (strcasecmp("keywords",$name) == 0) {
1250 $pos = strpos($str_matches,'content=');
1251 if (!(is_bool($pos) && !$pos)) {
1252 $no_meta_keywords = 0;
1261 If (COM_onFrontpage() && $_CONF['meta_tags'] == 2) { // Display default meta tags only on home page
1262 If ($no_meta_description) {
1263 $meta_description = $_CONF['meta_description'];
1265 If ($no_meta_keywords) {
1266 $meta_keywords = $_CONF['meta_keywords'];
1268 } else if ( $_CONF['meta_tags'] == 1 ) { // Display default meta tags anywhere there are no tags
1269 If ($no_meta_description) {
1270 $meta_description = $_CONF['meta_description'];
1272 If ($no_meta_keywords) {
1273 $meta_keywords = $_CONF['meta_keywords'];
1277 If ($no_meta_description OR $no_meta_keywords) {
1278 $headercode .= COM_createMetaTags($meta_description, $meta_keywords);
1282 $header->set_var( 'plg_headercode', $headercode );
1284 // The following lines allow users to embed PHP in their templates. This
1285 // is almost a contradition to the reasons for using templates but this may
1286 // prove useful at times ...
1287 // Don't use PHP in templates if you can live without it!
1289 $tmp = $header->finish($header->parse('index_header', 'header'));
1291 $xml_declaration = '';
1292 if ( get_cfg_var('short_open_tag') == '1' )
1294 if ( preg_match( '/(<\?xml[^>]*>)(.*)/s', $tmp, $match ) )
1296 $xml_declaration = $match[1] . LB;
1302 eval( '?>' . $tmp );
1303 $retval = $xml_declaration . ob_get_contents();
1311 * Returns the site footer
1313 * This loads the proper templates, does variable substitution and returns the
1314 * HTML for the site footer.
1316 * @param boolean $rightblock Whether or not to show blocks on right hand side default is no
1317 * @param array $custom An array defining custom function to be used to format Rightblocks
1318 * @see function COM_siteHeader
1319 * @return string Formated HTML containing site footer and optionally right blocks
1322 function COM_siteFooter( $rightblock = -1, $custom = '' )
1324 global $_CONF, $_TABLES, $LANG01, $_PAGE_TIMER, $topic, $LANG_BUTTONS;
1326 // If the theme implemented this for us then call their version instead.
1328 $function = $_CONF['theme'] . '_siteFooter';
1330 if( function_exists( $function ))
1332 return $function( $rightblock, $custom );
1337 // Set template directory
1338 $footer = new Template( $_CONF['path_layout'] );
1340 // Set template file
1341 $footer->set_file( array(
1342 'footer' => 'footer.thtml',
1343 'rightblocks' => 'rightblocks.thtml',
1344 'leftblocks' => 'leftblocks.thtml'
1347 // Do variable assignments
1348 $footer->set_var( 'xhtml', XHTML );
1349 $footer->set_var( 'site_url', $_CONF['site_url']);
1350 $footer->set_var( 'site_admin_url', $_CONF['site_admin_url']);
1351 $footer->set_var( 'layout_url',$_CONF['layout_url']);
1352 $footer->set_var( 'site_mail', "mailto:{$_CONF['site_mail']}" );
1353 $footer->set_var( 'site_name', $_CONF['site_name'] );
1354 $footer->set_var( 'site_slogan', $_CONF['site_slogan'] );
1355 $rdf = substr_replace( $_CONF['rdf_file'], $_CONF['site_url'], 0,
1356 strlen( $_CONF['path_html'] ) - 1 );
1357 $footer->set_var( 'rdf_file', $rdf );
1358 $footer->set_var( 'rss_url', $rdf );
1360 $year = date( 'Y' );
1361 $copyrightyear = $year;
1362 if( !empty( $_CONF['copyrightyear'] ))
1364 $copyrightyear = $_CONF['copyrightyear'];
1366 $footer->set_var( 'copyright_notice', ' ' . $LANG01[93] . ' © '
1367 . $copyrightyear . ' ' . $_CONF['site_name'] . '<br' . XHTML . '> '
1369 $footer->set_var( 'copyright_msg', $LANG01[93] . ' © '
1370 . $copyrightyear . ' ' . $_CONF['site_name'] );
1371 $footer->set_var( 'current_year', $year );
1372 $footer->set_var( 'lang_copyright', $LANG01[93] );
1373 $footer->set_var( 'trademark_msg', $LANG01[94] );
1374 $footer->set_var( 'powered_by', $LANG01[95] );
1375 $footer->set_var( 'geeklog_url', 'http://www.geeklog.net/' );
1376 $footer->set_var( 'geeklog_version', VERSION );
1377 // Now add variables for buttons like e.g. those used by the Yahoo theme
1378 $footer->set_var( 'button_home', $LANG_BUTTONS[1] );
1379 $footer->set_var( 'button_contact', $LANG_BUTTONS[2] );
1380 $footer->set_var( 'button_contribute', $LANG_BUTTONS[3] );
1381 $footer->set_var( 'button_sitestats', $LANG_BUTTONS[7] );
1382 $footer->set_var( 'button_personalize', $LANG_BUTTONS[8] );
1383 $footer->set_var( 'button_search', $LANG_BUTTONS[9] );
1384 $footer->set_var( 'button_advsearch', $LANG_BUTTONS[10] );
1385 $footer->set_var( 'button_directory', $LANG_BUTTONS[11] );
1387 /* Right blocks. Argh. Don't talk to me about right blocks...
1388 * Right blocks will be displayed if Right_blocks_in_footer is set [1],
1389 * AND (this function has been asked to show them (first param) OR the
1390 * show_right_blocks conf variable has been set to override what the code
1393 * If $custom sets an array (containing functionname and first argument)
1394 * then this is used instead of the default (COM_showBlocks) to render
1395 * the right blocks (and left).
1397 * [1] - if it isn't, they'll be in the header already.
1400 $displayRightBlocks = true;
1401 if ($_CONF['right_blocks_in_footer'] == 1)
1403 if( ($rightblock < 0) || !$rightblock )
1405 if( isset( $_CONF['show_right_blocks'] ) )
1407 $displayRightBlocks = $_CONF['show_right_blocks'];
1411 $displayRightBlocks = false;
1414 $displayRightBlocks = true;
1417 $displayRightBlocks = false;
1420 if ($displayRightBlocks)
1422 /* Check if an array has been passed that includes the name of a plugin
1423 * function or custom function.
1424 * This can be used to take control over what blocks are then displayed
1426 if( is_array( $custom ))
1428 $function = $custom['0'];
1429 if( function_exists( $function ))
1431 $rblocks = $function( $custom['1'], 'right' );
1433 $rblocks = COM_showBlocks( 'right', $topic );
1436 $rblocks = COM_showBlocks( 'right', $topic );
1439 if( empty( $rblocks ))
1441 $footer->set_var( 'geeklog_blocks', '');
1442 $footer->set_var( 'right_blocks', '' );
1444 $footer->set_var( 'geeklog_blocks', $rblocks);
1445 $footer->parse( 'right_blocks', 'rightblocks', true );
1446 $footer->set_var( 'geeklog_blocks', '');
1449 $footer->set_var( 'geeklog_blocks', '');
1450 $footer->set_var( 'right_blocks', '' );
1453 if( $_CONF['left_blocks_in_footer'] == 1 )
1457 /* Check if an array has been passed that includes the name of a plugin
1458 * function or custom function
1459 * This can be used to take control over what blocks are then displayed
1461 if( is_array( $custom ))
1463 $function = $custom[0];
1464 if( function_exists( $function ))
1466 $lblocks = $function( $custom[1], 'left' );
1471 $lblocks = COM_showBlocks( 'left', $topic );
1474 if( empty( $lblocks ))
1476 $footer->set_var( 'left_blocks', '' );
1477 $footer->set_var( 'geeklog_blocks', '');
1481 $footer->set_var( 'geeklog_blocks', $lblocks);
1482 $footer->parse( 'left_blocks', 'leftblocks', true );
1483 $footer->set_var( 'geeklog_blocks', '');
1487 // Global centerspan variable set in index.php
1488 if( isset( $GLOBALS['centerspan'] ))
1490 $footer->set_var( 'centerblockfooter-span', '</td></tr></table>' );
1493 $exectime = $_PAGE_TIMER->stopTimer();
1494 $exectext = $LANG01[91] . ' ' . $exectime . ' ' . $LANG01[92];
1496 $footer->set_var( 'execution_time', $exectime );
1497 $footer->set_var( 'execution_textandtime', $exectext );
1499 // Call to plugins to set template variables in the footer
1500 PLG_templateSetVars( 'footer', $footer );
1502 // Actually parse the template and make variable substitutions
1503 $footer->parse( 'index_footer', 'footer' );
1505 // Return resulting HTML
1506 return $footer->finish( $footer->get_var( 'index_footer' ));
1510 * Prints out standard block header
1512 * Prints out standard block header but pulling header HTML formatting from
1515 * Programming Note: The two functions COM_startBlock and COM_endBlock are used
1516 * to sandwich your block content. These functions are not used only for blocks
1517 * but anything that uses that format, e.g. Stats page. They are used like
1518 * COM_siteHeader and COM_siteFooter but for internal page elements.
1520 * @param string $title Value to set block title to
1521 * @param string $helpfile Help file, if one exists
1522 * @param string $template HTML template file to use to format the block
1523 * @return string Formatted HTML containing block header
1525 * @see COM_siteHeader
1529 function COM_startBlock( $title='', $helpfile='', $template='blockheader.thtml' )
1531 global $_CONF, $LANG01, $_IMAGE_TYPE;
1533 $block = new Template( $_CONF['path_layout'] );
1534 $block->set_file( 'block', $template );
1536 $block->set_var( 'xhtml', XHTML );
1537 $block->set_var( 'site_url', $_CONF['site_url'] );
1538 $block->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1539 $block->set_var( 'layout_url', $_CONF['layout_url'] );
1540 $block->set_var( 'block_title', stripslashes( $title ));
1542 if( !empty( $helpfile ))
1544 $helpimg = $_CONF['layout_url'] . '/images/button_help.' . $_IMAGE_TYPE;
1545 $help_content = '<img src="' . $helpimg. '" alt="?"' . XHTML . '>';
1546 $help_attr = array('class'=>'blocktitle');
1547 if( !stristr( $helpfile, 'http://' ))
1549 $help_url = $_CONF['site_url'] . "/help/$helpfile";
1553 $help_url = $helpfile;
1555 $help = COM_createLink($help_content, $help_url, $help_attr);
1556 $block->set_var( 'block_help', $help );
1559 $block->parse( 'startHTML', 'block' );
1561 return $block->finish( $block->get_var( 'startHTML' ));
1565 * Closes out COM_startBlock
1567 * @param string $template HTML template file used to format block footer
1568 * @return string Formatted HTML to close block
1569 * @see function COM_startBlock
1572 function COM_endBlock( $template='blockfooter.thtml' )
1576 $block = new Template( $_CONF['path_layout'] );
1577 $block->set_file( 'block', $template );
1579 $block->set_var( 'xhtml', XHTML );
1580 $block->set_var( 'site_url', $_CONF['site_url'] );
1581 $block->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1582 $block->set_var( 'layout_url', $_CONF['layout_url'] );
1583 $block->parse( 'endHTML', 'block' );
1585 return $block->finish( $block->get_var( 'endHTML' ));
1590 * Creates a <option> list from a database list for use in forms
1592 * Creates option list form field using given arguments
1594 * @param string $table Database Table to get data from
1595 * @param string $selection Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed. This is used in a SQL statement and can include DISTINCT to start.
1596 * @param string/array $selected Value (from $selection) to set to SELECTED or default
1597 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1598 * @param string $where Optional WHERE clause to use in the SQL Selection
1599 * @see function COM_checkList
1600 * @return string Formated HTML of option values
1603 function COM_optionList( $table, $selection, $selected='', $sortcol=1, $where='' )
1605 global $_DB_table_prefix;
1609 $LangTableName = '';
1610 if( substr( $table, 0, strlen( $_DB_table_prefix )) == $_DB_table_prefix )
1612 $LangTableName = 'LANG_' . substr( $table, strlen( $_DB_table_prefix ));
1616 $LangTableName = 'LANG_' . $table;
1619 global $$LangTableName;
1621 if( isset( $$LangTableName ))
1623 $LangTable = $$LangTableName;
1627 $LangTable = array();
1630 $tmp = str_replace( 'DISTINCT ', '', $selection );
1631 $select_set = explode( ',', $tmp );
1633 $sql = "SELECT $selection FROM $table";
1636 $sql .= " WHERE $where";
1638 $sql .= " ORDER BY {$select_set[$sortcol]}";
1639 $result = DB_query( $sql );
1640 $nrows = DB_numRows( $result );
1642 for( $i = 0; $i < $nrows; $i++ )
1644 $A = DB_fetchArray( $result, true );
1645 $retval .= '<option value="' . $A[0] . '"';
1647 if( is_array( $selected ) AND count( $selected ) > 0 )
1649 foreach( $selected as $selected_item )
1651 if( $A[0] == $selected_item )
1653 $retval .= ' selected="selected"';
1657 elseif( !is_array( $selected ) AND $A[0] == $selected )
1659 $retval .= ' selected="selected"';
1663 if( empty( $LangTable[$A[0]] ))
1669 $retval .= $LangTable[$A[0]];
1671 $retval .= '</option>' . LB;
1678 * Create and return a dropdown-list of available topics
1680 * This is a variation of COM_optionList() from lib-common.php. It will add
1681 * only those topics to the option list which are accessible by the current
1684 * @param string $selection Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed. This is used in a SQL statement and can include DISTINCT to start.
1685 * @param string $selected Value (from $selection) to set to SELECTED or default
1686 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1687 * @param boolean $ignorelang Whether to return all topics (true) or only the ones for the current language (false)
1688 * @see function COM_optionList
1689 * @return string Formated HTML of option values
1692 function COM_topicList( $selection, $selected = '', $sortcol = 1, $ignorelang = false )
1698 $topics = COM_topicArray($selection, $sortcol, $ignorelang);
1699 foreach ($topics as $tid => $topic) {
1700 $retval .= '<option value="' . $tid . '"';
1701 if ($tid == $selected) {
1702 $retval .= ' selected="selected"';
1704 $retval .= '>' . $topic . '</option>' . LB;
1711 * Return a list of topics in an array
1712 * (derived from COM_topicList - API may change)
1714 * @param string $selection Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed. This is used in a SQL statement and can include DISTINCT to start.
1715 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1716 * @param boolean $ignorelang Whether to return all topics (true) or only the ones for the current language (false)
1717 * @return array Array of topics
1718 * @see function COM_topicList
1721 function COM_topicArray($selection, $sortcol = 0, $ignorelang = false)
1727 $tmp = str_replace('DISTINCT ', '', $selection);
1728 $select_set = explode(',', $tmp);
1730 $sql = "SELECT $selection FROM {$_TABLES['topics']}";
1732 $sql .= COM_getPermSQL();
1734 $permsql = COM_getPermSQL();
1735 if (empty($permsql)) {
1736 $sql .= COM_getLangSQL('tid');
1738 $sql .= $permsql . COM_getLangSQL('tid', 'AND');
1741 $sql .= " ORDER BY $select_set[$sortcol]";
1743 $result = DB_query($sql);
1744 $nrows = DB_numRows($result);
1746 if (count($select_set) > 1) {
1747 for ($i = 0; $i < $nrows; $i++) {
1748 $A = DB_fetchArray($result, true);
1749 $retval[$A[0]] = stripslashes($A[1]);
1752 for ($i = 0; $i < $nrows; $i++) {
1753 $A = DB_fetchArray($result, true);
1762 * Creates a <input> checklist from a database list for use in forms
1764 * Creates a group of checkbox form fields with given arguments
1766 * @param string $table DB Table to pull data from
1767 * @param string $selection Comma delimited list of fields to pull from table
1768 * @param string $where Where clause of SQL statement
1769 * @param string $selected Value to set to CHECKED
1770 * @param string $fieldname Name to use for the checkbox array
1771 * @return string HTML with Checkbox code
1772 * @see COM_optionList
1775 function COM_checkList($table, $selection, $where = '', $selected = '', $fieldname = '')
1777 global $_TABLES, $_COM_VERBOSE;
1779 $sql = "SELECT $selection FROM $table";
1781 if( !empty( $where ))
1783 $sql .= " WHERE $where";
1786 $result = DB_query( $sql );
1787 $nrows = DB_numRows( $result );
1789 if( !empty( $selected ))
1793 COM_errorLog( "exploding selected array: $selected in COM_checkList", 1 );
1796 $S = explode( ' ', $selected );
1802 COM_errorLog( 'selected string was empty COM_checkList', 1 );
1807 $retval = '<ul class="checkboxes-list">' . LB;
1808 for( $i = 0; $i < $nrows; $i++ )
1811 $A = DB_fetchArray( $result, true );
1813 if( $table == $_TABLES['topics'] AND SEC_hasTopicAccess( $A['tid'] ) == 0 )
1818 if (empty($fieldname)) {
1819 // Not a good idea, as that will expose our table name and prefix!
1820 // Make sure you pass a distinct field name!
1821 $fieldname = $table;
1826 $retval .= '<li><input type="checkbox" name="' . $fieldname . '[]" value="' . $A[0] . '"';
1828 $sizeS = count( $S );
1829 for( $x = 0; $x < $sizeS; $x++ )
1831 if( $A[0] == $S[$x] )
1833 $retval .= ' checked="checked"';
1838 if(( $table == $_TABLES['blocks'] ) && isset( $A[2] ) && ( $A[2] == 'gldefault' ))
1840 $retval .= XHTML . '><span class="gldefault">' . stripslashes( $A[1] ) . '</span></li>' . LB;
1844 $retval .= XHTML . '><span>' . stripslashes( $A[1] ) . '</span></li>' . LB;
1848 $retval .= '</ul>' . LB;
1854 * Prints out an associative array for debugging
1856 * The core of this code has been lifted from phpweblog which is licenced
1857 * under the GPL. This is not used very much in the code but you can use it
1860 * @param array $array Array to loop through and print values for
1861 * @return string $retval Formatted HTML List
1865 function COM_debug($array)
1868 if(!empty($array)) {
1869 $retval = '<ul><pre><p>---- DEBUG ----</p>';
1870 foreach($array as $k => $v) {
1871 $retval .= sprintf("<li>%13s [%s]</li>\n", $k, $v);
1873 $retval .= '<p>---------------</p></pre></ul>';
1880 * Checks to see if RDF file needs updating and updates it if so.
1881 * Checks to see if we need to update the RDF as a result
1882 * of an article with a future publish date reaching it's
1883 * publish time and if so updates the RDF file.
1885 * NOTE: When called without parameters, this will only check for new entries to
1886 * include in the feeds. Pass the $updated_XXX parameters when the content
1887 * of an existing entry has changed.
1889 * @param string $updated_type (optional) feed type to update
1890 * @param string $updated_topic (optional) feed topic to update
1891 * @param string $updated_id (optional) feed id to update
1893 * @see file lib-syndication.php
1896 function COM_rdfUpToDateCheck( $updated_type = '', $updated_topic = '', $updated_id = '' )
1898 global $_CONF, $_TABLES;
1900 if( $_CONF['backend'] > 0 )
1902 if( !empty( $updated_type ) && ( $updated_type != 'article' ))
1904 // when a plugin's feed is to be updated, skip Geeklog's own feeds
1905 $sql = "SELECT fid,type,topic,limits,update_info FROM {$_TABLES['syndication']} WHERE (is_enabled = 1) AND (type <> 'article')";
1909 $sql = "SELECT fid,type,topic,limits,update_info FROM {$_TABLES['syndication']} WHERE is_enabled = 1";
1911 $result = DB_query( $sql );
1912 $num = DB_numRows( $result );
1913 for( $i = 0; $i < $num; $i++)
1915 $A = DB_fetchArray( $result );
1918 if( $A['type'] == 'article' )
1920 $is_current = SYND_feedUpdateCheck( $A['topic'],
1921 $A['update_info'], $A['limits'],
1922 $updated_topic, $updated_id );
1926 $is_current = PLG_feedUpdateCheck( $A['type'], $A['fid'],
1927 $A['topic'], $A['update_info'], $A['limits'],
1928 $updated_type, $updated_topic, $updated_id );
1932 SYND_updateFeed( $A['fid'] );
1939 * Checks and Updates the featured status of all articles.
1941 * Checks to see if any articles that were published for the future have been
1942 * published and, if so, will see if they are featured. If they are featured,
1943 * this will set old featured article (if there is one) to normal
1947 function COM_featuredCheck()
1951 $curdate = date( "Y-m-d H:i:s", time() );
1953 // Loop through each topic
1954 $sql = "SELECT tid FROM {$_TABLES['topics']}";
1955 $result = DB_query( $sql );
1956 $num = DB_numRows( $result );
1957 for( $i = 0; $i < $num; $i++)
1959 $A = DB_fetchArray( $result );
1961 if( DB_getItem( $_TABLES['stories'], 'COUNT(*)', "featured = 1 AND draft_flag = 0 AND tid = '{$A['tid']}' AND date <= '$curdate'" ) > 1 )
1963 // OK, we have two featured stories in a topic, fix that
1964 $sid = DB_getItem( $_TABLES['stories'], 'sid', "featured = 1 AND draft_flag = 0 ORDER BY date LIMIT 1" );
1965 DB_query( "UPDATE {$_TABLES['stories']} SET featured = 0 WHERE sid = '$sid'" );
1972 * Logs messages to error.log or the web page or both
1974 * Prints a well formatted message to either the web page, error log
1977 * @param string $logentry Text to log to error log
1978 * @param int $actionid where 1 = write to log file, 2 = write to screen (default) both
1979 * @see function COM_accessLog
1980 * @return string If $actionid = 2 or '' then HTML formatted string (wrapped in block) else nothing
1984 function COM_errorLog( $logentry, $actionid = '' )
1986 global $_CONF, $LANG01;
1990 if( !empty( $logentry ))
1992 $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
1995 $timestamp = @strftime( '%c' );
1997 if (!isset($_CONF['path_layout']) &&
1998 (($actionid == 2) || empty($actionid))) {
2001 if (!isset($_CONF['path_log']) && ($actionid != 2)) {
2008 $logfile = $_CONF['path_log'] . 'error.log';
2010 if( !$file = fopen( $logfile, 'a' ))
2012 $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2016 fputs( $file, "$timestamp - $logentry \n" );
2021 $retval .= COM_startBlock( $LANG01[55] . ' ' . $timestamp, '',
2022 COM_getBlockTemplate( '_msg_block', 'header' ))
2023 . nl2br( $logentry )
2024 . COM_endBlock( COM_getBlockTemplate( '_msg_block',
2029 $retval = nl2br($logentry);
2033 $logfile = $_CONF['path_log'] . 'error.log';
2035 if( !$file = fopen( $logfile, 'a' ))
2037 $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2041 fputs( $file, "$timestamp - $logentry \n" );
2042 $retval .= COM_startBlock( $LANG01[34] . ' - ' . $timestamp,
2043 '', COM_getBlockTemplate( '_msg_block',
2045 . nl2br( $logentry )
2046 . COM_endBlock( COM_getBlockTemplate( '_msg_block',
2057 * Logs message to access.log
2059 * This will print a message to the Geeklog access log
2061 * @param string $logentry Message to write to access log
2066 function COM_accessLog( $logentry )
2068 global $_CONF, $_USER, $LANG01;
2072 if( !empty( $logentry ))
2074 $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
2077 $timestamp = @strftime( '%c' );
2078 $logfile = $_CONF['path_log'] . 'access.log';
2080 if( !$file = fopen( $logfile, 'a' ))
2082 return $LANG01[33] . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2085 if( isset( $_USER['uid'] ))
2087 $byuser = $_USER['uid'] . '@' . $_SERVER['REMOTE_ADDR'];
2091 $byuser = 'anon@' . $_SERVER['REMOTE_ADDR'];
2094 fputs( $file, "$timestamp ($byuser) - $logentry\n" );
2101 * Shows all available topics
2103 * Show the topics in the system the user has access to and prints them in HTML.
2104 * This function is used to show the topics in the topics block.
2106 * @param string $topic ID of currently selected topic
2107 * @return string HTML formatted topic list
2111 function COM_showTopics( $topic='' )
2113 global $_CONF, $_TABLES, $_USER, $LANG01, $_BLOCK_TEMPLATE, $page;
2115 $langsql = COM_getLangSQL( 'tid' );
2116 if( empty( $langsql ))
2125 $sql = "SELECT tid,topic,imageurl,meta_description FROM {$_TABLES['topics']}" . $langsql;
2126 if( !COM_isAnonUser() )
2128 $tids = DB_getItem( $_TABLES['userindex'], 'tids',
2129 "uid = '{$_USER['uid']}'" );
2130 if( !empty( $tids ))
2132 $sql .= " $op (tid NOT IN ('" . str_replace( ' ', "','", $tids )
2133 . "'))" . COM_getPermSQL( 'AND' );
2137 $sql .= COM_getPermSQL( $op );
2142 $sql .= COM_getPermSQL( $op );
2144 if( $_CONF['sortmethod'] == 'alpha' )
2146 $sql .= ' ORDER BY topic ASC';
2150 $sql .= ' ORDER BY sortnum';
2152 $result = DB_query( $sql );
2155 $sections = new Template( $_CONF['path_layout'] );
2156 if( isset( $_BLOCK_TEMPLATE['topicoption'] ))
2158 $templates = explode( ',', $_BLOCK_TEMPLATE['topicoption'] );
2159 $sections->set_file( array( 'option' => $templates[0],
2160 'current' => $templates[1] ));
2164 $sections->set_file( array( 'option' => 'topicoption.thtml',
2165 'inactive' => 'topicoption_off.thtml' ));
2168 $sections->set_var( 'xhtml', XHTML );
2169 $sections->set_var( 'site_url', $_CONF['site_url'] );
2170 $sections->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2171 $sections->set_var( 'layout_url', $_CONF['layout_url'] );
2172 $sections->set_var( 'block_name', str_replace( '_', '-', 'section_block' ));
2174 if( $_CONF['hide_home_link'] == 0 )
2176 // Give a link to the homepage here since a lot of people use this for
2177 // navigating the site
2179 if( COM_onFrontpage() )
2181 $sections->set_var( 'option_url', '' );
2182 $sections->set_var( 'option_label', $LANG01[90] );
2183 $sections->set_var( 'option_count', '' );
2184 $sections->set_var( 'topic_image', '' );
2185 $retval .= $sections->parse( 'item', 'inactive' );
2189 $sections->set_var( 'option_url',
2190 $_CONF['site_url'] . '/index.php' );
2191 $sections->set_var( 'option_label', $LANG01[90] );
2192 $sections->set_var( 'option_count', '' );
2193 $sections->set_var( 'topic_image', '' );
2194 $retval .= $sections->parse( 'item', 'option' );
2198 if( $_CONF['showstorycount'] )
2200 $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['stories']} "
2201 . 'WHERE (draft_flag = 0) AND (date <= NOW()) '
2202 . COM_getPermSQL( 'AND' )
2204 $rcount = DB_query( $sql );
2205 while( $C = DB_fetchArray( $rcount ))
2207 $storycount[$C['tid']] = $C['count'];
2211 if( $_CONF['showsubmissioncount'] )
2213 $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['storysubmission']} "
2215 $rcount = DB_query( $sql );
2216 while( $C = DB_fetchArray( $rcount ))
2218 $submissioncount[$C['tid']] = $C['count'];
2222 while( $A = DB_fetchArray( $result ) )
2224 $topicname = stripslashes( $A['topic'] );
2225 $sections->set_var( 'option_url', $_CONF['site_url']
2226 . '/index.php?topic=' . $A['tid'] );
2227 $sections->set_var( 'option_label', $topicname );
2230 if( $_CONF['showstorycount'] || $_CONF['showsubmissioncount'] )
2232 $countstring .= '(';
2234 if( $_CONF['showstorycount'] )
2236 if( empty( $storycount[$A['tid']] ))
2242 $countstring .= COM_numberFormat( $storycount[$A['tid']] );
2246 if( $_CONF['showsubmissioncount'] )
2248 if( $_CONF['showstorycount'] )
2250 $countstring .= '/';
2252 if( empty( $submissioncount[$A['tid']] ))
2258 $countstring .= COM_numberFormat( $submissioncount[$A['tid']] );
2262 $countstring .= ')';
2264 $sections->set_var( 'option_count', $countstring );
2267 if( !empty( $A['imageurl'] ))
2269 $imageurl = COM_getTopicImageUrl( $A['imageurl'] );
2270 $topicimage = '<img src="' . $imageurl . '" alt="' . $topicname
2271 . '" title="' . $topicname . '"' . XHTML . '>';
2273 $sections->set_var( 'topic_image', $topicimage );
2275 $desc = trim($A['meta_description']);
2276 $sections->set_var('topic_description', $desc);
2277 $desc_escaped = htmlspecialchars($desc);
2278 $sections->set_var('topic_description_escaped', $desc_escaped);
2279 if (! empty($desc)) {
2280 $sections->set_var('topic_title_attribute',
2281 'title="' . $desc_escaped . '"');
2283 $sections->set_var('topic_title_attribute', '');
2286 if(( $A['tid'] == $topic ) && ( $page == 1 ))
2288 $retval .= $sections->parse( 'item', 'inactive' );
2292 $retval .= $sections->parse( 'item', 'option' );
2300 * Shows the user their menu options
2302 * This shows the average Joe User their menu options. This is the user block on the left side
2304 * @param string $help Help file to show
2305 * @param string $title Title of Menu
2306 * @param string $position Side being shown on 'left', 'right'. Though blank works not likely.
2307 * @see function COM_adminMenu
2310 function COM_userMenu( $help='', $title='', $position='' )
2312 global $_TABLES, $_USER, $_CONF, $LANG01, $LANG04, $_BLOCK_TEMPLATE;
2316 if( !COM_isAnonUser() )
2318 $usermenu = new Template( $_CONF['path_layout'] );
2319 if( isset( $_BLOCK_TEMPLATE['useroption'] ))
2321 $templates = explode( ',', $_BLOCK_TEMPLATE['useroption'] );
2322 $usermenu->set_file( array( 'option' => $templates[0],
2323 'current' => $templates[1] ));
2327 $usermenu->set_file( array( 'option' => 'useroption.thtml',
2328 'current' => 'useroption_off.thtml' ));
2330 $usermenu->set_var( 'xhtml', XHTML );
2331 $usermenu->set_var( 'site_url', $_CONF['site_url'] );
2332 $usermenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2333 $usermenu->set_var( 'layout_url', $_CONF['layout_url'] );
2334 $usermenu->set_var( 'block_name', str_replace( '_', '-', 'user_block' ));
2336 if( empty( $title ))
2338 $title = DB_getItem( $_TABLES['blocks'], 'title',
2339 "name='user_block'" );
2342 // what's our current URL?
2343 $thisUrl = COM_getCurrentURL();
2345 $retval .= COM_startBlock( $title, $help,
2346 COM_getBlockTemplate( 'user_block', 'header', $position ));
2348 // This function will show the user options for all installed plugins
2351 $plugin_options = PLG_getUserOptions();
2352 $nrows = count( $plugin_options );
2354 for( $i = 0; $i < $nrows; $i++ )
2356 $plg = current( $plugin_options );
2357 $usermenu->set_var( 'option_label', $plg->adminlabel );
2359 if( !empty( $plg->numsubmissions ))
2361 $usermenu->set_var( 'option_count', '(' . $plg->numsubmissions . ')' );
2365 $usermenu->set_var( 'option_count', '' );
2368 $usermenu->set_var( 'option_url', $plg->adminurl );
2369 if( $thisUrl == $plg->adminurl )
2371 $retval .= $usermenu->parse( 'item', 'current' );
2375 $retval .= $usermenu->parse( 'item', 'option' );
2377 next( $plugin_options );
2380 $url = $_CONF['site_url'] . '/usersettings.php';
2381 $usermenu->set_var( 'option_label', $LANG01[48] );
2382 $usermenu->set_var( 'option_count', '' );
2383 $usermenu->set_var( 'option_url', $url );
2384 if( $thisUrl == $url )
2386 $retval .= $usermenu->parse( 'item', 'current' );
2390 $retval .= $usermenu->parse( 'item', 'option' );
2393 $url = $_CONF['site_url'] . '/users.php?mode=logout';
2394 $usermenu->set_var( 'option_label', $LANG01[19] );
2395 $usermenu->set_var( 'option_count', '' );
2396 $usermenu->set_var( 'option_url', $url );
2397 $retval .= $usermenu->finish($usermenu->parse('item', 'option'));
2398 $retval .= COM_endBlock(COM_getBlockTemplate('user_block', 'footer', $position));
2402 $retval .= COM_startBlock( $LANG01[47], $help,
2403 COM_getBlockTemplate( 'user_block', 'header', $position ));
2404 $login = new Template( $_CONF['path_layout'] );
2405 $login->set_file( 'form', 'loginform.thtml' );
2406 $login->set_var( 'xhtml', XHTML );
2407 $login->set_var( 'site_url', $_CONF['site_url'] );
2408 $login->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2409 $login->set_var( 'layout_url', $_CONF['layout_url'] );
2410 $login->set_var( 'lang_username', $LANG01[21] );
2411 $login->set_var( 'lang_password', $LANG01[57] );
2412 $login->set_var( 'lang_forgetpassword', $LANG01[119] );
2413 $login->set_var( 'lang_login', $LANG01[58] );
2414 if( $_CONF['disable_new_user_registration'] == 1 )
2416 $login->set_var( 'lang_signup', '' );
2420 $login->set_var( 'lang_signup', $LANG01[59] );
2423 // 3rd party remote authentification.
2424 if ($_CONF['user_login_method']['3rdparty'] && !$_CONF['usersubmission']) {
2425 $modules = SEC_collectRemoteAuthenticationModules();
2426 if (count($modules) == 0) {
2427 $user_templates->set_var('services', '');
2429 if (!$_CONF['user_login_method']['standard'] &&
2430 (count($modules) == 1)) {
2431 $select = '<input type="hidden" name="service" value="'
2432 . $modules[0] . '"' . XHTML . '>' . $modules[0];
2435 $select = '<select name="service" id="service">';
2436 if ($_CONF['user_login_method']['standard']) {
2437 $select .= '<option value="">' . $_CONF['site_name']
2440 foreach ($modules as $service) {
2441 $select .= '<option value="' . $service . '">'
2442 . $service . '</option>';
2444 $select .= '</select>';
2447 $login->set_file('services', 'blockservices.thtml');
2448 $login->set_var('lang_service', $LANG04[121]);
2449 $login->set_var('select_service', $select);
2450 $login->parse('output', 'services');
2451 $login->set_var('services',
2452 $login->finish($login->get_var('output')));
2455 $login->set_var('services', '');
2458 // OpenID remote authentification.
2459 if ($_CONF['user_login_method']['openid'] && ($_CONF['usersubmission'] == 0) && !$_CONF['disable_new_user_registration']) {
2460 $login->set_file('openid_login', 'loginform_openid.thtml');
2461 $login->set_var('lang_openid_login', $LANG01[128]);
2462 $login->set_var('input_field_size', 18);
2463 $login->set_var('app_url', $_CONF['site_url'] . '/users.php');
2464 $login->parse('output', 'openid_login');
2465 $login->set_var('openid_login',
2466 $login->finish($login->get_var('output')));
2468 $login->set_var('openid_login', '');
2471 $retval .= $login->finish($login->parse('output', 'form'));
2472 $retval .= COM_endBlock( COM_getBlockTemplate('user_block', 'footer', $position));
2479 * Prints administration menu
2481 * This will return the administration menu items that the user has
2482 * sufficient rights to -- Admin Block on the left side.
2484 * @param string $help Help file to show
2485 * @param string $title Menu Title
2486 * @param string $position Side being shown on 'left', 'right' or blank.
2487 * @see function COM_userMenu
2490 function COM_adminMenu( $help = '', $title = '', $position = '' )
2492 global $_TABLES, $_USER, $_CONF, $LANG01, $LANG_ADMIN, $_BLOCK_TEMPLATE,
2497 if( empty( $_USER['username'] ))
2502 $plugin_options = PLG_getAdminOptions();
2503 $num_plugins = count( $plugin_options );
2505 if( SEC_isModerator() OR SEC_hasRights( 'story.edit,block.edit,topic.edit,user.edit,plugin.edit,user.mail,syndication.edit', 'OR' ) OR ( $num_plugins > 0 ))
2507 // what's our current URL?
2508 $thisUrl = COM_getCurrentURL();
2510 $adminmenu = new Template( $_CONF['path_layout'] );
2511 if( isset( $_BLOCK_TEMPLATE['adminoption'] ))
2513 $templates = explode( ',', $_BLOCK_TEMPLATE['adminoption'] );
2514 $adminmenu->set_file( array( 'option' => $templates[0],
2515 'current' => $templates[1] ));
2519 $adminmenu->set_file( array( 'option' => 'adminoption.thtml',
2520 'current' => 'adminoption_off.thtml' ));
2522 $adminmenu->set_var( 'xhtml', XHTML );
2523 $adminmenu->set_var( 'site_url', $_CONF['site_url'] );
2524 $adminmenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2525 $adminmenu->set_var( 'layout_url', $_CONF['layout_url'] );
2526 $adminmenu->set_var( 'block_name', str_replace( '_', '-', 'admin_block' ));
2528 if( empty( $title ))
2530 $title = DB_getItem( $_TABLES['blocks'], 'title',
2531 "name = 'admin_block'" );
2534 $retval .= COM_startBlock( $title, $help,
2535 COM_getBlockTemplate( 'admin_block', 'header', $position ));
2538 if( SEC_isModerator() || SEC_hasRights( 'story.edit' ))
2540 $tresult = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
2541 . COM_getPermSQL() );
2542 $trows = DB_numRows( $tresult );
2546 for( $i = 0; $i < $trows; $i++ )
2548 $T = DB_fetchArray( $tresult );
2549 $tids[] = $T['tid'];
2551 if( count( $tids ) > 0 )
2553 $topicsql = " (tid IN ('" . implode( "','", $tids ) . "'))";
2559 if (SEC_hasRights('story.edit,story.moderate', 'OR') ||
2560 (($_CONF['commentsubmission'] == 1) &&
2561 SEC_hasRights('comment.moderate')) ||
2562 (($_CONF['usersubmission'] == 1) &&
2563 SEC_hasRights('user.edit,user.delete'))) {
2565 if (SEC_hasRights('story.moderate')) {
2566 if (empty($topicsql)) {
2567 $modnum += DB_count($_TABLES['storysubmission']);
2569 $sresult = DB_query("SELECT COUNT(*) AS count FROM {$_TABLES['storysubmission']} WHERE" . $topicsql);
2570 $S = DB_fetchArray($sresult);
2571 $modnum += $S['count'];
2575 if (($_CONF['listdraftstories'] == 1) && SEC_hasRights('story.edit')) {
2576 $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (draft_flag = 1)";
2577 if (!empty($topicsql)) {
2578 $sql .= ' AND' . $topicsql;
2580 $result = DB_query($sql . COM_getPermSQL('AND', 0, 3));
2581 $A = DB_fetchArray($result);
2582 $modnum += $A['count'];
2585 if (($_CONF['commentsubmission'] == 1) && SEC_hasRights('comment.moderate')) {
2586 $modnum += DB_count($_TABLES['commentsubmissions']);
2589 if ($_CONF['usersubmission'] == 1) {
2590 if (SEC_hasRights('user.edit') && SEC_hasRights('user.delete')) {
2591 $modnum += DB_count($_TABLES['users'], 'status', '2');
2596 if (SEC_inGroup('Root')) {
2597 $url = $_CONF['site_admin_url'] . '/configuration.php';
2598 $adminmenu->set_var('option_url', $url);
2599 $adminmenu->set_var('option_label', $LANG01[129]);
2600 $adminmenu->set_var('option_count', count($config->_get_groups()));
2601 $menu_item = $adminmenu->parse('item',
2602 ($thisUrl == $url) ? 'current' :
2604 $link_array[$LANG01[129]] = $menu_item;
2608 // now handle submissions for plugins
2609 $modnum += PLG_getSubmissionCount();
2611 if( SEC_hasRights( 'story.edit' ))
2613 $url = $_CONF['site_admin_url'] . '/story.php';
2614 $adminmenu->set_var( 'option_url', $url );
2615 $adminmenu->set_var( 'option_label', $LANG01[11] );
2616 if( empty( $topicsql ))
2618 $numstories = DB_count( $_TABLES['stories'] );
2622 $nresult = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE" . $topicsql . COM_getPermSql( 'AND' ));
2623 $N = DB_fetchArray( $nresult );
2624 $numstories = $N['count'];
2626 $adminmenu->set_var( 'option_count',
2627 COM_numberFormat( $numstories ));
2628 $menu_item = $adminmenu->parse( 'item',
2629 ( $thisUrl == $url ) ? 'current' : 'option' );
2630 $link_array[$LANG01[11]] = $menu_item;
2633 if( SEC_hasRights( 'block.edit' ))
2635 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['blocks']}" . COM_getPermSql());
2636 list( $count ) = DB_fetchArray( $result );
2638 $url = $_CONF['site_admin_url'] . '/block.php';
2639 $adminmenu->set_var( 'option_url', $url );
2640 $adminmenu->set_var( 'option_label', $LANG01[12] );
2641 $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
2643 $menu_item = $adminmenu->parse( 'item',
2644 ( $thisUrl == $url ) ? 'current' : 'option' );
2645 $link_array[$LANG01[12]] = $menu_item;
2648 if( SEC_hasRights( 'topic.edit' ))
2650 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['topics']}" . COM_getPermSql());
2651 list( $count ) = DB_fetchArray( $result );
2653 $url = $_CONF['site_admin_url'] . '/topic.php';
2654 $adminmenu->set_var( 'option_url', $url );
2655 $adminmenu->set_var( 'option_label', $LANG01[13] );
2656 $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
2658 $menu_item = $adminmenu->parse( 'item',
2659 ( $thisUrl == $url ) ? 'current' : 'option' );
2660 $link_array[$LANG01[13]] = $menu_item;
2663 if( SEC_hasRights( 'user.edit' ))
2665 $url = $_CONF['site_admin_url'] . '/user.php';
2666 $adminmenu->set_var( 'option_url', $url );
2667 $adminmenu->set_var( 'option_label', $LANG01[17] );
2668 $adminmenu->set_var( 'option_count',
2669 COM_numberFormat( DB_count( $_TABLES['users'] ) -1 ));
2671 $menu_item = $adminmenu->parse( 'item',
2672 ( $thisUrl == $url ) ? 'current' : 'option' );
2673 $link_array[$LANG01[17]] = $menu_item;
2676 if( SEC_hasRights( 'group.edit' ))
2678 if (SEC_inGroup('Root')) {
2681 $thisUsersGroups = SEC_getUserGroups ();
2682 $grpFilter = 'WHERE (grp_id IN (' . implode (',', $thisUsersGroups) . '))';
2684 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['groups']} $grpFilter;" );
2685 $A = DB_fetchArray( $result );
2687 $url = $_CONF['site_admin_url'] . '/group.php';
2688 $adminmenu->set_var( 'option_url', $url );
2689 $adminmenu->set_var( 'option_label', $LANG01[96] );
2690 $adminmenu->set_var( 'option_count',
2691 COM_numberFormat( $A['count'] ));
2693 $menu_item = $adminmenu->parse( 'item',
2694 ( $thisUrl == $url ) ? 'current' : 'option' );
2695 $link_array[$LANG01[96]] = $menu_item;
2698 if( SEC_hasRights( 'user.mail' ))
2700 $url = $_CONF['site_admin_url'] . '/mail.php';
2701 $adminmenu->set_var( 'option_url', $url );
2702 $adminmenu->set_var( 'option_label', $LANG01[105] );
2703 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2705 $menu_item = $adminmenu->parse( 'item',
2706 ( $thisUrl == $url ) ? 'current' : 'option' );
2707 $link_array[$LANG01[105]] = $menu_item;
2710 if(( $_CONF['backend'] == 1 ) && SEC_hasRights( 'syndication.edit' ))
2712 $url = $_CONF['site_admin_url'] . '/syndication.php';
2713 $adminmenu->set_var( 'option_url', $url );
2714 $adminmenu->set_var( 'option_label', $LANG01[38] );
2715 $count = COM_numberFormat( DB_count( $_TABLES['syndication'] ));
2716 $adminmenu->set_var( 'option_count', $count );
2718 $menu_item = $adminmenu->parse( 'item',
2719 ( $thisUrl == $url ) ? 'current' : 'option' );
2720 $link_array[$LANG01[38]] = $menu_item;
2723 if(( $_CONF['trackback_enabled'] || $_CONF['pingback_enabled'] ||
2724 $_CONF['ping_enabled'] ) && SEC_hasRights( 'story.ping' ))
2726 $url = $_CONF['site_admin_url'] . '/trackback.php';
2727 $adminmenu->set_var( 'option_url', $url );
2728 $adminmenu->set_var( 'option_label', $LANG01[116] );
2729 if( $_CONF['ping_enabled'] )
2731 $count = COM_numberFormat( DB_count( $_TABLES['pingservice'] ));
2732 $adminmenu->set_var( 'option_count', $count );
2736 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2739 $menu_item = $adminmenu->parse( 'item',
2740 ( $thisUrl == $url ) ? 'current' : 'option' );
2741 $link_array[$LANG01[116]] = $menu_item;
2744 if (SEC_hasRights('plugin.edit')) {
2745 $url = $_CONF['site_admin_url'] . '/plugins.php';
2746 $adminmenu->set_var('option_url', $url);
2747 $adminmenu->set_var('option_label', $LANG01[77]);
2748 $adminmenu->set_var('option_count',
2749 COM_numberFormat(DB_count($_TABLES['plugins'],
2752 $menu_item = $adminmenu->parse('item',
2753 ($thisUrl == $url) ? 'current' : 'option');
2754 $link_array[$LANG01[77]] = $menu_item;
2757 // This will show the admin options for all installed plugins (if any)
2759 for( $i = 0; $i < $num_plugins; $i++ )
2761 $plg = current( $plugin_options );
2763 $adminmenu->set_var( 'option_url', $plg->adminurl );
2764 $adminmenu->set_var( 'option_label', $plg->adminlabel );
2766 if( empty( $plg->numsubmissions ))
2768 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2772 $adminmenu->set_var( 'option_count',
2773 COM_numberFormat( $plg->numsubmissions ));
2776 $menu_item = $adminmenu->parse( 'item',
2777 ( $thisUrl == $plg->adminurl ) ? 'current' : 'option', true );
2778 $link_array[$plg->adminlabel] = $menu_item;
2780 next( $plugin_options );
2783 if(( $_CONF['allow_mysqldump'] == 1 ) AND ( $_DB_dbms == 'mysql' ) AND
2784 SEC_inGroup( 'Root' ))
2786 $url = $_CONF['site_admin_url'] . '/database.php';
2787 $adminmenu->set_var( 'option_url', $url );
2788 $adminmenu->set_var( 'option_label', $LANG01[103] );
2789 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2791 $menu_item = $adminmenu->parse( 'item',
2792 ( $thisUrl == $url ) ? 'current' : 'option' );
2793 $link_array[$LANG01[103]] = $menu_item;
2796 if ($_CONF['link_documentation'] == 1) {
2797 $doclang = COM_getLanguageName();
2798 $docs = 'docs/' . $doclang . '/index.html';
2799 if (file_exists($_CONF['path_html'] . $docs)) {
2800 $adminmenu->set_var('option_url', $_CONF['site_url']
2803 $adminmenu->set_var('option_url', $_CONF['site_url']
2804 . '/docs/english/index.html');
2806 $adminmenu->set_var('option_label', $LANG01[113]);
2807 $adminmenu->set_var('option_count', $LANG_ADMIN['na']);
2808 $menu_item = $adminmenu->parse('item', 'option');
2809 $link_array[$LANG01[113]] = $menu_item;
2812 if( $_CONF['link_versionchecker'] == 1 AND SEC_inGroup( 'Root' ))
2814 $adminmenu->set_var( 'option_url',
2815 'http://www.geeklog.net/versionchecker.php?version=' . VERSION );
2816 $adminmenu->set_var( 'option_label', $LANG01[107] );
2817 $adminmenu->set_var( 'option_count', VERSION );
2819 $menu_item = $adminmenu->parse( 'item', 'option' );
2820 $link_array[$LANG01[107]] = $menu_item;
2823 if( $_CONF['sort_admin'] )
2825 uksort( $link_array, 'strcasecmp' );
2828 $url = $_CONF['site_admin_url'] . '/moderation.php';
2829 $adminmenu->set_var('option_url', $url);
2830 $adminmenu->set_var('option_label', $LANG01[10]);
2831 $adminmenu->set_var('option_count', COM_numberFormat($modnum));
2832 $menu_item = $adminmenu->finish($adminmenu->parse('item',
2833 ($thisUrl == $url) ? 'current' : 'option'));
2834 $link_array = array($menu_item) + $link_array;
2836 foreach( $link_array as $link )
2841 $retval .= COM_endBlock( COM_getBlockTemplate( 'admin_block', 'footer', $position ));
2848 * Redirects user to a given URL
2850 * This function does a redirect using a meta refresh. This is (or at least
2851 * used to be) more compatible than using a HTTP Location: header.
2853 * NOTE: This does not need to be XHTML compliant. It may also be used
2854 * in situations where the XHTML constant is not defined yet ...
2856 * @param string $url URL to send user to
2857 * @return string HTML meta redirect
2860 function COM_refresh($url)
2862 return "<html><head><meta http-equiv=\"refresh\" content=\"0; URL=$url\"></head></html>\n";
2866 * DEPRECIATED -- see CMT_userComments in lib-comment.php
2867 * @deprecated since Geeklog 1.4.0
2868 * @see CMT_userComments
2870 function COM_userComments( $sid, $title, $type='article', $order='', $mode='', $pid = 0, $page = 1, $cid = false, $delete_option = false )
2874 require_once $_CONF['path_system'] . 'lib-comment.php';
2876 return CMT_userComments( $sid, $title, $type, $order, $mode, $pid, $page, $cid, $delete_option );
2880 * This censors inappropriate content
2882 * This will replace 'bad words' with something more appropriate
2884 * @param string $Message String to check
2885 * @see function COM_checkHTML
2886 * @return string Edited $Message
2890 function COM_checkWords( $Message )
2894 $EditedMessage = $Message;
2896 if( $_CONF['censormode'] != 0 )
2898 if( is_array( $_CONF['censorlist'] ))
2900 $Replacement = $_CONF['censorreplace'];
2902 switch( $_CONF['censormode'])
2904 case 1: # Exact match
2905 $RegExPrefix = '(\s*)';
2906 $RegExSuffix = '(\W*)';
2909 case 2: # Word beginning
2910 $RegExPrefix = '(\s*)';
2911 $RegExSuffix = '(\w*)';
2914 case 3: # Word fragment
2915 $RegExPrefix = '(\w*)';
2916 $RegExSuffix = '(\w*)';
2920 foreach ($_CONF['censorlist'] as $c) {
2922 $EditedMessage = MBYTE_eregi_replace($RegExPrefix . $c
2923 . $RegExSuffix, "\\1$Replacement\\2", $EditedMessage);
2929 return $EditedMessage;
2933 * Takes some amount of text and replaces all javascript events on*= with in
2935 * This script takes some amount of text and matches all javascript events, on*= (onBlur= onMouseClick=)
2936 * and replaces them with in*=
2937 * Essentially this will cause onBlur to become inBlur, onFocus to be inFocus
2938 * These are not valid javascript events and the browser will ignore them.
2939 * @param string $Message Text to filter
2940 * @return string $Message with javascript filtered
2941 * @see COM_checkWords
2942 * @see COM_checkHTML
2946 function COM_killJS( $Message )
2948 return( preg_replace( '/(\s)+[oO][nN](\w*) ?=/', '\1in\2=', $Message ));
2952 * Handles the part within a [code] ... [/code] section, i.e. escapes all
2953 * special characters.
2955 * @param string $str the code section to encode
2956 * @return string $str with the special characters encoded
2957 * @see COM_checkHTML
2960 function COM_handleCode( $str )
2962 $search = array( '&', '\\', '<', '>', '[', ']' );
2963 $replace = array( '&', '\', '<', '>', '[', ']' );
2965 $str = str_replace( $search, $replace, $str );
2971 * This function checks html tags.
2973 * Checks to see that the HTML tags are on the approved list and
2974 * removes them if not.
2976 * @param string $str HTML to check
2977 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
2978 * @return string Filtered HTML
2981 function COM_checkHTML( $str, $permissions = 'story.edit' )
2985 // replace any \ with \ (HTML equiv)
2986 $str = str_replace('\\', '\', COM_stripslashes($str) );
2988 // Get rid of any newline characters
2989 $str = preg_replace( "/\n/", '', $str );
2991 // Replace any $ with $ (HTML equiv)
2992 $str = str_replace( '$', '$', $str );
2993 // handle [code] ... [/code]
2996 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[code]' );
2997 if( $start_pos !== false )
2999 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/code]' );
3000 if( $end_pos !== false )
3002 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6,
3003 $end_pos - ( $start_pos + 6 )));
3004 $encoded = '<pre><code>' . $encoded . '</code></pre>';
3005 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
3006 . MBYTE_substr( $str, $end_pos + 7 );
3008 else // missing [/code]
3010 // Treat the rest of the text as code (so as not to lose any
3011 // special characters). However, the calling entity should
3012 // better be checking for missing [/code] before calling this
3014 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6 ));
3015 $encoded = '<pre><code>' . $encoded . '</code></pre>';
3016 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3020 while( $start_pos !== false );
3022 // handle [raw] ... [/raw]
3025 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[raw]' );
3026 if( $start_pos !== false )
3028 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/raw]' );
3029 if( $end_pos !== false )
3031 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5,
3032 $end_pos - ( $start_pos + 5 )));
3033 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3035 $encoded = '[raw2]' . $encoded . '[/raw2]';
3036 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
3037 . MBYTE_substr( $str, $end_pos + 6 );
3039 else // missing [/raw]
3041 // Treat the rest of the text as raw (so as not to lose any
3042 // special characters). However, the calling entity should
3043 // better be checking for missing [/raw] before calling this
3045 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5 ));
3046 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3048 $encoded = '[raw2]' . $encoded . '[/raw2]';
3049 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3053 while( $start_pos !== false );
3055 if( isset( $_CONF['skip_html_filter_for_root'] ) &&
3056 ( $_CONF['skip_html_filter_for_root'] == 1 ) &&
3057 SEC_inGroup( 'Root' ))
3062 // strip_tags() gets confused by HTML comments ...
3063 $str = preg_replace( '/<!--.+?-->/', '', $str );
3065 $filter = new kses4;
3066 if( isset( $_CONF['allowed_protocols'] ) && is_array( $_CONF['allowed_protocols'] ) && ( count( $_CONF['allowed_protocols'] ) > 0 ))
3068 $filter->SetProtocols( $_CONF['allowed_protocols'] );
3072 $filter->SetProtocols( array( 'http:', 'https:', 'ftp:' ));
3075 if( empty( $permissions) || !SEC_hasRights( $permissions ) ||
3076 empty( $_CONF['admin_html'] ))
3078 $html = $_CONF['user_html'];
3082 if ($_CONF['advanced_editor'] && is_array($_CONF['advanced_html'])) {
3083 $html = array_merge_recursive( $_CONF['user_html'],
3084 $_CONF['admin_html'],
3085 $_CONF['advanced_html'] );
3087 $html = array_merge_recursive( $_CONF['user_html'],
3088 $_CONF['admin_html'] );
3092 foreach( $html as $tag => $attr )
3094 $filter->AddHTML( $tag, $attr );
3096 /* Replace [raw][/raw] with <!--raw--><!--/raw-->, note done "late" because
3097 * of the above noted // strip_tags() gets confused by HTML comments ...
3099 $str = $filter->Parse( $str );
3100 $str = str_replace('[raw2]','<!--raw--><span class="raw">', $str);
3101 $str = str_replace('[/raw2]','</span><!--/raw-->', $str);
3107 * undo function for htmlspecialchars()
3109 * This function translates HTML entities created by htmlspecialchars() back
3110 * into their ASCII equivalents. Also handles the entities for $, {, and }.
3112 * @param string $string The string to convert.
3113 * @return string The converted string.
3116 function COM_undoSpecialChars( $string )
3118 $string = str_replace( '$', '$', $string );
3119 $string = str_replace( '{', '{', $string );
3120 $string = str_replace( '}', '}', $string );
3121 $string = str_replace( '>', '>', $string );
3122 $string = str_replace( '<', '<', $string );
3123 $string = str_replace( '"', '"', $string );
3124 $string = str_replace( ' ', ' ', $string );
3125 $string = str_replace( '&', '&', $string );
3131 * Makes an ID based on current date/time
3133 * This function creates a 17 digit sid for stories based on the 14 digit date
3134 * and a 3 digit random number that was seeded with the number of microseconds
3135 * (.000001th of a second) since the last full second.
3136 * NOTE: this is now used for more than just stories!
3138 * @return string $sid Story ID
3141 function COM_makesid()
3143 $sid = date( 'YmdHis' );
3144 $sid .= rand( 0, 999 );
3150 * Checks to see if email address is valid.
3152 * This function checks to see if an email address is in the correct from.
3154 * @param string $email Email address to verify
3155 * @return boolean True if valid otherwise false
3158 function COM_isEmail( $email )
3160 require_once( 'Mail/RFC822.php' );
3162 $rfc822 = new Mail_RFC822;
3164 return( $rfc822->isValidInetAddress( $email ) ? true : false );
3169 * Encode a string such that it can be used in an email header
3171 * @param string $string the text to be encoded
3172 * @return string encoded text
3175 function COM_emailEscape( $string )
3179 if (function_exists('CUSTOM_emailEscape')) {
3180 return CUSTOM_emailEscape($string);
3183 $charset = COM_getCharset();
3184 if(( $charset == 'utf-8' ) && ( $string != utf8_decode( $string )))
3186 if( function_exists( 'iconv_mime_encode' ))
3188 $mime_parameters = array( 'input-charset' => 'utf-8',
3189 'output-charset' => 'utf-8',
3190 // 'Q' encoding is more readable than 'B'
3193 $string = substr( iconv_mime_encode( '', $string,
3194 $mime_parameters ), 2 );
3198 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3201 else if( preg_match( '/[^0-9a-z\-\.,:;\?! ]/i', $string ))
3203 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3210 * Takes a name and an email address and returns a string that vaguely
3211 * resembles an email address specification conforming to RFC(2)822 ...
3213 * @param string $name name, e.g. John Doe
3214 * @param string $address email address only, e.g. john.doe@example.com
3215 * @return string formatted email address
3218 function COM_formatEmailAddress($name, $address)
3220 $name = trim($name);
3221 $address = trim($address);
3223 if (function_exists('CUSTOM_formatEmailAddress')) {
3224 return CUSTOM_formatEmailAddress($name, $address);
3227 $formatted_name = COM_emailEscape($name);
3229 // if the name comes back unchanged, it's not UTF-8, so preg_match is fine
3230 if (($formatted_name == $name) && preg_match('/[^0-9a-z ]/i', $name)) {
3231 $formatted_name = str_replace('"', '\\"', $formatted_name);
3232 $formatted_name = '"' . $formatted_name . '"';
3235 return $formatted_name . ' <' . $address . '>';
3241 * All emails sent by Geeklog are sent through this function.
3243 * NOTE: Please note that using CC: will expose the email addresses of
3244 * all recipients. Use with care.
3246 * @param string $to recipients name and email address
3247 * @param string $subject subject of the email
3248 * @param string $message the text of the email
3249 * @param string $from (optional) sender of the the email
3250 * @param boolean $html (optional) true if to be sent as HTML email
3251 * @param int $priority (optional) add X-Priority header, if > 0
3252 * @param mixed $optional (optional) other headers or CC:
3253 * @return boolean true if successful, otherwise false
3256 function COM_mail($to, $subject, $message, $from = '', $html = false, $priority = 0, $optional = null)
3263 $from = COM_formatEmailAddress($_CONF['site_name'], $_CONF['site_mail']);
3266 $to = substr($to, 0, strcspn($to, "\r\n"));
3267 if (($optional != null) && !is_array($optional)) {
3268 $optional = substr($optional, 0, strcspn($optional, "\r\n"));
3270 $from = substr($from, 0, strcspn($from, "\r\n"));
3271 $subject = substr($subject, 0, strcspn($subject, "\r\n"));
3272 $subject = COM_emailEscape($subject);
3274 if (function_exists('CUSTOM_mail')) {
3275 return CUSTOM_mail($to, $subject, $message, $from, $html, $priority,
3279 include_once 'Mail.php';
3280 include_once 'Mail/RFC822.php';
3282 $method = $_CONF['mail_settings']['backend'];
3284 if (! isset($mailobj)) {
3285 if (($method == 'sendmail') || ($method == 'smtp')) {
3286 $mailobj =& Mail::factory($method, $_CONF['mail_settings']);
3289 $mailobj =& Mail::factory($method);
3293 $charset = COM_getCharset();
3296 $headers['From'] = $from;
3297 if ($method != 'mail') {
3298 $headers['To'] = $to;
3300 if (($optional != null) && !is_array($optional) && !empty($optional)) {
3301 // assume old (optional) CC: header
3302 $headers['Cc'] = $optional;
3304 $headers['Date'] = date('r'); // RFC822 formatted date
3305 if($method == 'smtp') {
3306 list($usec, $sec) = explode(' ', microtime());
3307 $m = substr($usec, 2, 5);
3308 $headers['Message-Id'] = '<' . date('YmdHis') . '.' . $m
3309 . '@' . $_CONF['mail_settings']['host'] . '>';
3312 $headers['Content-Type'] = 'text/html; charset=' . $charset;
3313 $headers['Content-Transfer-Encoding'] = '8bit';
3315 $headers['Content-Type'] = 'text/plain; charset=' . $charset;
3317 $headers['Subject'] = $subject;
3318 if ($priority > 0) {
3319 $headers['X-Priority'] = $priority;
3321 $headers['X-Mailer'] = 'Geeklog ' . VERSION;
3323 if (!empty($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['SERVER_ADDR']) &&
3324 ($_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR'])) {
3325 $url = COM_getCurrentURL();
3326 if (substr($url, 0, strlen($_CONF['site_admin_url']))
3327 != $_CONF['site_admin_url']) {
3328 $headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
3332 // add optional headers last
3333 if (($optional != null) && is_array($optional)) {
3334 foreach ($optional as $h => $v) {
3339 $retval = $mailobj->send($to, $headers, $message);
3340 if ($retval !== true) {
3341 COM_errorLog($retval->toString(), 1);
3344 return($retval === true ? true : false);
3349 * Creates older stuff block
3351 * Creates the olderstuff block for display.
3352 * Actually updates the olderstuff record in the gl_blocks database.
3356 function COM_olderStuff()
3358 global $_TABLES, $_CONF;
3360 $sql = "SELECT sid,tid,title,comments,UNIX_TIMESTAMP(date) AS day FROM {$_TABLES['stories']} WHERE (perm_anon = 2) AND (frontpage = 1) AND (date <= NOW()) AND (draft_flag = 0)" . COM_getTopicSQL( 'AND', 1 ) . " ORDER BY featured DESC, date DESC LIMIT {$_CONF['limitnews']}, {$_CONF['limitnews']}";
3361 $result = DB_query( $sql );
3362 $nrows = DB_numRows( $result );
3366 $dateonly = $_CONF['dateonly'];
3367 if( empty( $dateonly ))
3369 $dateonly = '%d-%b'; // fallback: day - abbrev. month name
3375 for( $i = 0; $i < $nrows; $i++ )
3377 $A = DB_fetchArray( $result );
3379 $daycheck = strftime( '%A', $A['day'] );
3380 if( $day != $daycheck )
3382 if( $day != 'noday' )
3384 $daylist = COM_makeList($oldnews, 'list-older-stories');
3385 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3386 $string .= $daylist . '<br' . XHTML . '>';
3389 $day2 = strftime( $dateonly, $A['day'] );
3390 $string .= '<h3>' . $daycheck . ' <small>' . $day2
3391 . '</small></h3>' . LB;
3396 $oldnews_url = COM_buildUrl( $_CONF['site_url'] . '/article.php?story='
3398 $oldnews[] = COM_createLink($A['title'], $oldnews_url)
3399 .' (' . COM_numberFormat( $A['comments'] ) . ')';
3402 if( !empty( $oldnews ))
3404 $daylist = COM_makeList($oldnews, 'list-older-stories');
3405 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3406 $string .= $daylist;
3407 $string = addslashes( $string );
3409 DB_query( "UPDATE {$_TABLES['blocks']} SET content = '$string' WHERE name = 'older_stories'" );
3415 * Shows a single Geeklog block
3417 * This shows a single block and is typically called from
3418 * COM_showBlocks OR from plugin code
3420 * @param string $name Logical name of block (not same as title) -- 'user_block', 'admin_block', 'section_block', 'whats_new_block'.
3421 * @param string $help Help file location
3422 * @param string $title Title shown in block header
3423 * @param string $position Side, 'left', 'right' or empty.
3424 * @see function COM_showBlocks
3425 * @return string HTML Formated block
3429 function COM_showBlock( $name, $help='', $title='', $position='' )
3431 global $_CONF, $topic, $_TABLES, $_USER;
3435 if( !isset( $_USER['noboxes'] ))
3437 if( !COM_isAnonUser() )
3439 $_USER['noboxes'] = DB_getItem( $_TABLES['userindex'], 'noboxes',
3440 "uid = {$_USER['uid']}" );
3444 $_USER['noboxes'] = 0;
3451 $retval .= COM_userMenu( $help,$title, $position );
3455 $retval .= COM_adminMenu( $help,$title, $position );
3458 case 'section_block':
3459 $retval .= COM_startBlock( $title, $help,
3460 COM_getBlockTemplate( $name, 'header', $position ))
3461 . COM_showTopics( $topic )
3462 . COM_endBlock( COM_getBlockTemplate( $name, 'footer', $position ));
3465 case 'whats_new_block':
3466 if( !$_USER['noboxes'] )
3468 $retval .= COM_whatsNewBlock( $help, $title, $position );
3478 * Shows Geeklog blocks
3480 * Returns HTML for blocks on a given side and, potentially, for
3481 * a given topic. Currently only used by static pages.
3483 * @param string $side Side to get blocks for (right or left for now)
3484 * @param string $topic Only get blocks for this topic
3485 * @param string $name Block name (not used)
3486 * @see function COM_showBlock
3487 * @return string HTML Formated blocks
3491 function COM_showBlocks( $side, $topic='', $name='all' )
3493 global $_CONF, $_TABLES, $_USER, $LANG21, $topic, $page;
3497 // Get user preferences on blocks
3498 if( !isset( $_USER['noboxes'] ) || !isset( $_USER['boxes'] ))
3500 if( !COM_isAnonUser() )
3502 $result = DB_query( "SELECT boxes,noboxes FROM {$_TABLES['userindex']} "
3503 ."WHERE uid = '{$_USER['uid']}'" );
3504 list($_USER['boxes'], $_USER['noboxes']) = DB_fetchArray( $result );
3508 $_USER['boxes'] = '';
3509 $_USER['noboxes'] = 0;
3513 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3514 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3515 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3517 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3519 $commonsql = "FROM {$_TABLES['blocks']} WHERE is_enabled = 1";
3521 if( $side == 'left' )
3523 $commonsql .= " AND onleft = 1";
3527 $commonsql .= " AND onleft = 0";
3530 if( !empty( $topic ))
3532 $tp = addslashes($topic);
3533 $commonsql .= " AND (tid = '$tp' OR tid = 'all')";
3537 if( COM_onFrontpage() )
3539 $commonsql .= " AND (tid = 'homeonly' OR tid = 'all')";
3543 $commonsql .= " AND (tid = 'all')";
3547 if( !empty( $_USER['boxes'] ))
3549 $BOXES = str_replace( ' ', ',', $_USER['boxes'] );
3551 $commonsql .= " AND (bid NOT IN ($BOXES) OR bid = '-1')";
3554 $commonsql .= ' ORDER BY blockorder,title ASC';
3556 $blocksql['mysql'] .= $commonsql;
3557 $blocksql['mssql'] .= $commonsql;
3558 $result = DB_query( $blocksql );
3559 $nrows = DB_numRows( $result );
3561 // convert result set to an array of associated arrays
3563 for( $i = 0; $i < $nrows; $i++ )
3565 $blocks[] = DB_fetchArray( $result );
3568 // Check and see if any plugins have blocks to show
3569 $pluginBlocks = PLG_getBlocks( $side, $topic, $name );
3570 $blocks = array_merge( $blocks, $pluginBlocks );
3572 // sort the resulting array by block order
3573 $column = 'blockorder';
3574 $sortedBlocks = $blocks;
3575 $num_sortedBlocks = count( $sortedBlocks );
3576 for( $i = 0; $i < $num_sortedBlocks - 1; $i++ )
3578 for( $j = 0; $j < $num_sortedBlocks - 1 - $i; $j++ )
3580 if( $sortedBlocks[$j][$column] > $sortedBlocks[$j+1][$column] )
3582 $tmp = $sortedBlocks[$j];
3583 $sortedBlocks[$j] = $sortedBlocks[$j + 1];
3584 $sortedBlocks[$j + 1] = $tmp;
3588 $blocks = $sortedBlocks;
3590 // Loop though resulting sorted array and pass associative arrays
3591 // to COM_formatBlock
3592 foreach( $blocks as $A )
3594 if( $A['type'] == 'dynamic' or SEC_hasAccess( $A['owner_id'], $A['group_id'], $A['perm_owner'], $A['perm_group'], $A['perm_members'], $A['perm_anon'] ) > 0 )
3596 $retval .= COM_formatBlock( $A, $_USER['noboxes'] );
3604 * Formats a Geeklog block
3606 * This shows a single block and is typically called from
3607 * COM_showBlocks OR from plugin code
3609 * @param array $A Block Record
3610 * @param boolean $noboxes Set to true if userpref is no blocks
3611 * @return string HTML Formated block
3614 function COM_formatBlock( $A, $noboxes = false )
3616 global $_CONF, $_TABLES, $_USER, $LANG21;
3620 $lang = COM_getLanguageId();
3621 if (!empty($lang)) {
3623 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3624 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3625 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3627 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3629 $commonsql = "FROM {$_TABLES['blocks']} WHERE name = '"
3630 . $A['name'] . '_' . $lang . "'";
3632 $blocksql['mysql'] .= $commonsql;
3633 $blocksql['mssql'] .= $commonsql;
3634 $result = DB_query( $blocksql );
3636 if (DB_numRows($result) == 1) {
3637 // overwrite with data for language-specific block
3638 $A = DB_fetchArray($result);
3642 if( array_key_exists( 'onleft', $A ) )
3644 if( $A['onleft'] == 1 )
3648 $position = 'right';
3654 if( $A['type'] == 'portal' )
3656 if( COM_rdfCheck( $A['bid'], $A['rdfurl'], $A['date'], $A['rdflimit'] ))
3658 $A['content'] = DB_getItem( $_TABLES['blocks'], 'content',
3659 "bid = '{$A['bid']}'");
3663 if( $A['type'] == 'gldefault' )
3665 $retval .= COM_showBlock( $A['name'], $A['help'], $A['title'], $position );
3668 if( $A['type'] == 'phpblock' && !$noboxes )
3670 if( !( $A['name'] == 'whosonline_block' AND DB_getItem( $_TABLES['blocks'], 'is_enabled', "name='whosonline_block'" ) == 0 ))
3672 $function = $A['phpblockfn'];
3674 if (preg_match('/^(phpblock_\w*)\\((.*)\\)$/', $function, $matches) == 1)
3676 $function = $matches[1];
3677 $args = $matches[2];
3679 $blkheader = COM_startBlock( $A['title'], $A['help'],
3680 COM_getBlockTemplate( $A['name'], 'header', $position ));
3681 $blkfooter = COM_endBlock( COM_getBlockTemplate( $A['name'],
3682 'footer', $position ));
3684 if( function_exists( $function ))
3688 $fretval = $function($A, $args);
3690 $fretval = $function();
3692 if( !empty( $fretval ))
3694 $retval .= $blkheader;
3695 $retval .= $fretval;
3696 $retval .= $blkfooter;
3701 // show error message
3702 $retval .= $blkheader;
3703 $retval .= sprintf( $LANG21[31], $function );
3704 $retval .= $blkfooter;
3709 if( !empty( $A['content'] ) && ( trim( $A['content'] ) != '' ) && !$noboxes )
3711 $blockcontent = stripslashes( $A['content'] );
3713 // Hack: If the block content starts with a '<' assume it
3714 // contains HTML and do not call nl2br() which would only add
3715 // unwanted <br> tags.
3717 if( substr( $blockcontent, 0, 1 ) != '<' )
3719 $blockcontent = nl2br( $blockcontent );
3722 // autotags are only(!) allowed in normal blocks
3723 if(( $A['allow_autotags'] == 1 ) && ( $A['type'] == 'normal' ))
3725 $blockcontent = PLG_replaceTags( $blockcontent );
3727 $blockcontent = str_replace( array( '<?', '?>' ), '', $blockcontent );
3729 $retval .= COM_startBlock( $A['title'], $A['help'],
3730 COM_getBlockTemplate( $A['name'], 'header', $position ))
3731 . $blockcontent . LB
3732 . COM_endBlock( COM_getBlockTemplate( $A['name'], 'footer', $position ));
3740 * Checks to see if it's time to import and RDF/RSS block again
3742 * Updates RDF/RSS block if needed
3744 * @param string $bid Block ID
3745 * @param string $rdfurl URL to get headlines from
3746 * @param string $date Last time the headlines were imported
3747 * @param string $maxheadlines max. number of headlines to import
3749 * @see function COM_rdfImport
3752 function COM_rdfCheck( $bid, $rdfurl, $date, $maxheadlines = 0 )
3755 $nextupdate = $date + 3600;
3757 if( $nextupdate < time() )
3759 COM_rdfImport( $bid, $rdfurl, $maxheadlines );
3767 * Syndication import function. Imports headline data to a portal block.
3769 * Rewritten December 19th 2004 by Michael Jervis (mike AT fuckingbrit DOT com).
3770 * Now utilises a Factory Pattern to open a URL and automaticaly retreive a feed
3771 * object populated with feed data. Then import it into the portal block.
3773 * @param string $bid Block ID
3774 * @param string $rdfurl URL to get content from
3775 * @param int $maxheadlines Maximum number of headlines to display
3777 * @see function COM_rdfCheck
3780 function COM_rdfImport($bid, $rdfurl, $maxheadlines = 0)
3782 global $_CONF, $_TABLES, $LANG21;
3784 // Import the feed handling classes:
3785 require_once $_CONF['path_system']
3786 . '/classes/syndication/parserfactory.class.php';
3787 require_once $_CONF['path_system']
3788 . '/classes/syndication/feedparserbase.class.php';
3790 $result = DB_query("SELECT rdf_last_modified, rdf_etag FROM {$_TABLES['blocks']} WHERE bid = $bid");
3791 list($last_modified, $etag) = DB_fetchArray($result);
3793 // Load the actual feed handlers:
3794 $factory = new FeedParserFactory($_CONF['path_system']
3795 . '/classes/syndication/');
3796 $factory->userAgent = 'Geeklog/' . VERSION;
3797 if (!empty($last_modified) && !empty($etag)) {
3798 $factory->lastModified = $last_modified;
3799 $factory->eTag = $etag;
3803 $feed = $factory->reader($rdfurl, $_CONF['default_charset']);
3806 /* We have located a reader, and populated it with the information from
3807 * the syndication file. Now we will sort out our display, and update
3810 if ($maxheadlines == 0) {
3811 if (!empty($_CONF['syndication_max_headlines'])) {
3812 $maxheadlines = $_CONF['syndication_max_headlines'];
3814 $maxheadlines = count($feed->articles);
3818 $update = date('Y-m-d H:i:s');
3819 $last_modified = '';
3820 if (!empty($factory->lastModified)) {
3821 $last_modified = addslashes($factory->lastModified);
3824 if (!empty($factory->eTag)) {
3825 $etag = addslashes($factory->eTag);
3828 if (empty($last_modified) || empty($etag)) {
3829 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = '$bid'");
3831 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = '$last_modified', rdf_etag = '$etag' WHERE bid = '$bid'");
3834 $charset = COM_getCharset();
3836 // format articles for display
3837 $readmax = min($maxheadlines, count($feed->articles));
3838 for ($i = 0; $i < $readmax; $i++) {
3839 if (empty($feed->articles[$i]['title'])) {
3840 $feed->articles[$i]['title'] = $LANG21[61];
3843 if ($charset == 'utf-8') {
3844 $title = $feed->articles[$i]['title'];
3846 $title = utf8_decode($feed->articles[$i]['title']);
3848 if ($feed->articles[$i]['link'] != '') {
3849 $content = COM_createLink($title, $feed->articles[$i]['link']);
3850 } elseif ($feed->articles[$i]['enclosureurl'] != '') {
3851 $content = COM_createLink($title, $feed->articles[$i]['enclosureurl']);
3855 $articles[] = $content;
3859 $content = COM_makeList($articles, 'list-feed');
3860 $content = str_replace(array("\015", "\012"), '', $content);
3862 if (strlen($content) > 65000) {
3863 $content = $LANG21[68];
3866 // Standard theme based function to put it in the block
3867 $result = DB_change($_TABLES['blocks'], 'content',
3868 addslashes($content), 'bid', $bid);
3869 } else if ($factory->errorStatus !== false) {
3870 // failed to aquire info, 0 out the block and log an error
3871 COM_errorLog("Unable to aquire feed reader for $rdfurl", 1);
3872 COM_errorLog($factory->errorStatus[0] . ' ' .
3873 $factory->errorStatus[1] . ' ' .
3874 $factory->errorStatus[2]);
3875 $content = addslashes($LANG21[4]);
3876 DB_query("UPDATE {$_TABLES['blocks']} SET content = '$content', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = $bid");
3882 * Returns what HTML is allowed in content
3884 * Returns what HTML tags the system allows to be used inside content.
3885 * You can modify this by changing $_CONF['user_html'] in the configuration
3886 * (for admins, see also $_CONF['admin_html']).
3888 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
3889 * @param boolean $list_only true = return only the list of HTML tags
3890 * @return string HTML <div>/<span> enclosed string
3891 * @see function COM_checkHTML
3892 * @todo Bugs: The list always includes the [code], [raw], and [page_break]
3893 * tags when story.* permissions are required, even when those tags
3894 * are not actually available (e.g. in comments on stories).
3897 function COM_allowedHTML($permissions = 'story.edit', $list_only = false)
3899 global $_CONF, $LANG01;
3903 if (isset($_CONF['skip_html_filter_for_root']) &&
3904 ($_CONF['skip_html_filter_for_root'] == 1) &&
3905 SEC_inGroup('Root')) {
3908 $retval .= '<span class="warningsmall">' . $LANG01[123]
3911 $retval .= '<div dir="ltr" class="warningsmall">';
3916 $retval .= '<span class="warningsmall">' . $LANG01[31] . '</span> ';
3919 if (empty($permissions) || !SEC_hasRights($permissions) ||
3920 empty($_CONF['admin_html'])) {
3921 $html = $_CONF['user_html'];
3923 $html = array_merge_recursive($_CONF['user_html'],
3924 $_CONF['admin_html']);
3927 $retval .= '<div dir="ltr" class="warningsmall">';
3928 foreach ($html as $tag => $attr) {
3929 $retval .= '<' . $tag . '>, ';
3933 $with_story_perms = false;
3934 $perms = explode(',', $permissions);
3935 foreach ($perms as $p) {
3936 if (substr($p, 0, 6) == 'story.') {
3937 $with_story_perms = true;
3942 if ($with_story_perms) {
3943 $retval .= '[code], [raw], ';
3945 if ($_CONF['allow_page_breaks'] == 1) {
3946 $retval .= '[page_break], ';
3951 $autotags = array_keys(PLG_collectTags());
3952 $retval .= '[' . implode(':], [', $autotags) . ':]';
3953 $retval .= '</div>';
3959 * Return the password for the given username
3961 * Fetches a password for the given user
3963 * @param string $loginname username to get password for
3964 * @return string Password or ''
3968 function COM_getPassword( $loginname )
3970 global $_TABLES, $LANG01;
3972 $result = DB_query( "SELECT passwd FROM {$_TABLES['users']} WHERE username='$loginname'" );
3974 $nrows = DB_numRows( $result );
3976 if(( $tmp == 0 ) && ( $nrows == 1 ))
3978 $U = DB_fetchArray( $result );
3979 return $U['passwd'];
3983 $tmp = $LANG01[32] . ": '" . $loginname . "'";
3984 COM_errorLog( $tmp, 1 );
3992 * Return the username or fullname for the passed member id (uid)
3994 * Allows the siteAdmin to determine if loginname (username) or fullname
3995 * should be displayed.
3997 * @param int $uid site member id
3998 * @param string $username Username, if this is set no lookup is done.
3999 * @param string $fullname Users full name.
4000 * @param string $remoteusername Username on remote service
4001 * @param string $remoteservice Remote login service.
4002 * @return string Username, fullname or username@Service
4005 function COM_getDisplayName( $uid = '', $username='', $fullname='', $remoteusername='', $remoteservice='' )
4007 global $_CONF, $_TABLES, $_USER;
4011 if( COM_isAnonUser() )
4017 $uid = $_USER['uid'];
4021 if( empty( $username ))
4023 $query = DB_query( "SELECT username, fullname, remoteusername, remoteservice FROM {$_TABLES['users']} WHERE uid='$uid'" );
4024 list( $username, $fullname, $remoteusername, $remoteservice ) = DB_fetchArray( $query );
4027 if( !empty( $fullname ) && ($_CONF['show_fullname'] == 1 ))
4031 else if(( $_CONF['user_login_method']['3rdparty'] || $_CONF['user_login_method']['openid'] ) && !empty( $remoteusername ))
4033 if( !empty( $username ))
4035 $remoteusername = $username;
4038 if( $_CONF['show_servicename'] )
4040 return "$remoteusername@$remoteservice";
4044 return $remoteusername;
4053 * Adds a hit to the system
4055 * This function is called in the footer of every page and is used to
4056 * track the number of hits to the Geeklog system. This information is
4057 * shown on stats.php
4065 DB_change($_TABLES['vars'], 'value', 'value + 1', 'name', 'totalhits', '', true);
4069 * This will email new stories in the topics that the user is interested in
4071 * In account information the user can specify which topics for which they
4072 * will receive any new article for in a daily digest.
4077 function COM_emailUserTopics()
4079 global $_CONF, $_TABLES, $LANG04, $LANG08, $LANG24;
4081 if ($_CONF['emailstories'] == 0) {
4085 $subject = strip_tags( $_CONF['site_name'] . $LANG08[30] . strftime( '%Y-%m-%d', time() ));
4089 // Get users who want stories emailed to them
4090 $usersql = "SELECT username,email,etids,{$_TABLES['users']}.uid AS uuid "
4091 . "FROM {$_TABLES['users']}, {$_TABLES['userindex']} "
4092 . "WHERE {$_TABLES['users']}.uid > 1 AND {$_TABLES['userindex']}.uid = {$_TABLES['users']}.uid AND (etids <> '-' OR etids IS NULL) ORDER BY {$_TABLES['users']}.uid";
4094 $users = DB_query( $usersql );
4095 $nrows = DB_numRows( $users );
4097 $lastrun = DB_getItem( $_TABLES['vars'], 'value', "name = 'lastemailedstories'" );
4099 // For each user, pull the stories they want and email it to them
4100 for( $x = 0; $x < $nrows; $x++ )
4102 $U = DB_fetchArray( $users );
4104 $storysql = array();
4105 $storysql['mysql'] = "SELECT sid,uid,date AS day,title,introtext,postmode";
4107 $storysql['mssql'] = "SELECT sid,uid,date AS day,title,CAST(introtext AS text) AS introtext,postmode";
4109 $commonsql = " FROM {$_TABLES['stories']} WHERE draft_flag = 0 AND date <= NOW() AND date >= '{$lastrun}'";
4111 $topicsql = "SELECT tid FROM {$_TABLES['topics']}"
4112 . COM_getPermSQL( 'WHERE', $U['uuid'] );
4113 $tresult = DB_query( $topicsql );
4114 $trows = DB_numRows( $tresult );
4118 // this user doesn't seem to have access to any topics ...
4123 for( $i = 0; $i < $trows; $i++ )
4125 $T = DB_fetchArray( $tresult );
4126 $TIDS[] = $T['tid'];
4129 if( !empty( $U['etids'] ))
4131 $ETIDS = explode( ' ', $U['etids'] );
4132 $TIDS = array_intersect( $TIDS, $ETIDS );
4135 if( count( $TIDS ) > 0)
4137 $commonsql .= " AND (tid IN ('" . implode( "','", $TIDS ) . "'))";
4140 $commonsql .= COM_getPermSQL( 'AND', $U['uuid'] );
4141 $commonsql .= ' ORDER BY featured DESC, date DESC';
4143 $storysql['mysql'] .= $commonsql;
4144 $storysql['mssql'] .= $commonsql;
4146 $stories = DB_query( $storysql );
4147 $nsrows = DB_numRows( $stories );
4151 // If no new stories where pulled for this user, continue with next
4155 $mailtext = $LANG08[29] . strftime( $_CONF['shortdate'], time() ) . "\n";
4157 for( $y = 0; $y < $nsrows; $y++ )
4159 // Loop through stories building the requested email message
4160 $S = DB_fetchArray( $stories );
4162 $mailtext .= "\n------------------------------\n\n";
4163 $mailtext .= "$LANG08[31]: "
4164 . COM_undoSpecialChars( stripslashes( $S['title'] )) . "\n";
4165 if( $_CONF['contributedbyline'] == 1 )
4167 if( empty( $authors[$S['uid']] ))
4169 $storyauthor = COM_getDisplayName ($S['uid']);
4170 $authors[$S['uid']] = $storyauthor;
4174 $storyauthor = $authors[$S['uid']];
4176 $mailtext .= "$LANG24[7]: " . $storyauthor . "\n";
4179 $mailtext .= "$LANG08[32]: " . strftime( $_CONF['date'], strtotime( $S['day' ])) . "\n\n";
4181 if( $_CONF['emailstorieslength'] > 0 )
4183 if($S['postmode']==='wikitext'){
4184 $storytext = COM_undoSpecialChars( strip_tags( COM_renderWikiText ( stripslashes( $S['introtext'] ))));
4186 $storytext = COM_undoSpecialChars( strip_tags( PLG_replaceTags( stripslashes( $S['introtext'] ))));
4189 if( $_CONF['emailstorieslength'] > 1 )
4191 $storytext = COM_truncate( $storytext,
4192 $_CONF['emailstorieslength'], '...' );
4195 $mailtext .= $storytext . "\n\n";
4198 $mailtext .= $LANG08[33] . ' ' . COM_buildUrl( $_CONF['site_url']
4199 . '/article.php?story=' . $S['sid'] ) . "\n";
4202 $mailtext .= "\n------------------------------\n";
4203 $mailtext .= "\n$LANG08[34]\n";
4204 $mailtext .= "\n------------------------------\n";
4206 $mailto = $U['username'] . ' <' . $U['email'] . '>';
4208 if ($_CONF['site_mail'] !== $_CONF['noreply_mail']) {
4209 $mailfrom = $_CONF['noreply_mail'];
4210 $mailtext .= LB . LB . $LANG04[159];
4212 $mailfrom = $_CONF['site_mail'];
4214 COM_mail( $mailto, $subject, $mailtext , $mailfrom);
4217 DB_query( "UPDATE {$_TABLES['vars']} SET value = NOW() WHERE name = 'lastemailedstories'" );
4221 * Shows any new information in a block
4223 * Return the HTML that shows any new stories, comments, etc
4225 * @param string $help Help file for block
4226 * @param string $title Title used in block header
4227 * @param string $position Position in which block is being rendered 'left', 'right' or blank (for centre)
4228 * @return string Return the HTML that shows any new stories, comments, etc
4232 function COM_whatsNewBlock( $help = '', $title = '', $position = '' )
4234 global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_WHATSNEW, $page, $newstories;
4236 $retval = COM_startBlock( $title, $help,
4237 COM_getBlockTemplate( 'whats_new_block', 'header', $position ));
4240 if(( $_CONF['hidenewstories'] == 0 ) || ( $_CONF['hidenewcomments'] == 0 )
4241 || ( $_CONF['trackback_enabled']
4242 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4244 $topicsql = COM_getTopicSql ('AND', 0, $_TABLES['stories']);
4247 if( $_CONF['hidenewstories'] == 0 )
4250 $archivetid = DB_getItem( $_TABLES['topics'], 'tid', "archive_flag=1" );
4251 if( !empty( $archivetid ))
4253 $archsql = " AND (tid <> '" . addslashes( $archivetid ) . "')";
4256 // Find the newest stories
4257 $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (date >= (date_sub(NOW(), INTERVAL {$_CONF['newstoriesinterval']} SECOND))) AND (date <= NOW()) AND (draft_flag = 0)" . $archsql . COM_getPermSQL( 'AND' ) . $topicsql . COM_getLangSQL( 'sid', 'AND' );
4258 $result = DB_query( $sql );
4259 $A = DB_fetchArray( $result );
4260 $nrows = $A['count'];
4262 if( empty( $title ))
4264 $title = DB_getItem( $_TABLES['blocks'], 'title', "name='whats_new_block'" );
4267 // Any late breaking news stories?
4268 $retval .= '<h3>' . $LANG01[99] . '</h3>';
4272 $newmsg = COM_formatTimeString( $LANG_WHATSNEW['new_string'],
4273 $_CONF['newstoriesinterval'], $LANG01[11], $nrows);
4275 if( $newstories && ( $page < 2 ))
4277 $retval .= $newmsg . '<br' . XHTML . '>';
4281 $retval .= COM_createLink($newmsg, $_CONF['site_url']
4282 . '/index.php?display=new') . '<br' . XHTML . '>';
4287 $retval .= $LANG01[100] . '<br' . XHTML . '>';
4290 if(( $_CONF['hidenewcomments'] == 0 ) || ( $_CONF['trackback_enabled']
4291 && ( $_CONF['hidenewtrackbacks'] == 0 ))
4292 || ( $_CONF['hidenewplugins'] == 0 ))
4294 $retval .= '<br' . XHTML . '>';
4298 if( $_CONF['hidenewcomments'] == 0 )
4300 // Go get the newest comments
4301 $retval .= '<h3>' . $LANG01[83] . ' <small>'
4302 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4303 $_CONF['newcommentsinterval'] )
4308 if( !COM_isAnonUser() )
4310 $stwhere .= "({$_TABLES['stories']}.owner_id IS NOT NULL AND {$_TABLES['stories']}.perm_owner IS NOT NULL) OR ";
4311 $stwhere .= "({$_TABLES['stories']}.group_id IS NOT NULL AND {$_TABLES['stories']}.perm_group IS NOT NULL) OR ";
4312 $stwhere .= "({$_TABLES['stories']}.perm_members IS NOT NULL)";
4316 $stwhere .= "({$_TABLES['stories']}.perm_anon IS NOT NULL)";
4318 $sql = "SELECT DISTINCT COUNT(*) AS dups, type, {$_TABLES['stories']}.title, {$_TABLES['stories']}.sid, max({$_TABLES['comments']}.date) AS lastdate FROM {$_TABLES['comments']} LEFT JOIN {$_TABLES['stories']} ON (({$_TABLES['stories']}.sid = {$_TABLES['comments']}.sid)" . COM_getPermSQL( 'AND', 0, 2, $_TABLES['stories'] ) . " AND ({$_TABLES['stories']}.draft_flag = 0) AND ({$_TABLES['stories']}.commentcode >= 0)" . $topicsql . COM_getLangSQL( 'sid', 'AND', $_TABLES['stories'] ) . ") WHERE ({$_TABLES['comments']}.date >= (DATE_SUB(NOW(), INTERVAL {$_CONF['newcommentsinterval']} SECOND))) AND ((({$stwhere}))) GROUP BY {$_TABLES['comments']}.sid,type, {$_TABLES['stories']}.title, {$_TABLES['stories']}.title, {$_TABLES['stories']}.sid ORDER BY 5 DESC LIMIT 15";
4320 $result = DB_query( $sql );
4322 $nrows = DB_numRows( $result );
4326 $newcomments = array();
4328 for( $x = 0; $x < $nrows; $x++ )
4330 $A = DB_fetchArray( $result );
4332 if(( $A['type'] == 'article' ) || empty( $A['type'] ))
4334 $url = COM_buildUrl( $_CONF['site_url']
4335 . '/article.php?story=' . $A['sid'] ) . '#comments';
4338 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4339 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4341 if( $title != $titletouse )
4343 $attr = array('title' => htmlspecialchars($title));
4349 $acomment = str_replace( '$', '$', $titletouse );
4350 $acomment = str_replace( ' ', ' ', $acomment );
4352 if( $A['dups'] > 1 )
4354 $acomment .= ' [+' . $A['dups'] . ']';
4357 $newcomments[] = COM_createLink($acomment, $url, $attr);
4360 $retval .= COM_makeList( $newcomments, 'list-new-comments' );
4364 $retval .= $LANG01[86] . '<br' . XHTML . '>' . LB;
4366 if(( $_CONF['hidenewplugins'] == 0 )
4367 || ( $_CONF['trackback_enabled']
4368 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4370 $retval .= '<br' . XHTML . '>';
4374 if( $_CONF['trackback_enabled'] && ( $_CONF['hidenewtrackbacks'] == 0 ))
4376 $retval .= '<h3>' . $LANG01[114] . ' <small>'
4377 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4378 $_CONF['newtrackbackinterval'] )
4381 $sql = "SELECT DISTINCT COUNT(*) AS count,{$_TABLES['stories']}.title,t.sid,max(t.date) AS lastdate FROM {$_TABLES['trackback']} AS t,{$_TABLES['stories']} WHERE (t.type = 'article') AND (t.sid = {$_TABLES['stories']}.sid) AND (t.date >= (DATE_SUB(NOW(), INTERVAL {$_CONF['newtrackbackinterval']} SECOND)))" . COM_getPermSQL( 'AND', 0, 2, $_TABLES['stories'] ) . " AND ({$_TABLES['stories']}.draft_flag = 0) AND ({$_TABLES['stories']}.trackbackcode = 0)" . $topicsql . COM_getLangSQL( 'sid', 'AND', $_TABLES['stories'] ) . " GROUP BY t.sid, {$_TABLES['stories']}.title ORDER BY lastdate DESC LIMIT 15";
4382 $result = DB_query( $sql );
4384 $nrows = DB_numRows( $result );
4387 $newcomments = array();
4389 for( $i = 0; $i < $nrows; $i++ )
4391 $A = DB_fetchArray( $result );
4393 $url = COM_buildUrl( $_CONF['site_url']
4394 . '/article.php?story=' . $A['sid'] ) . '#trackback';
4396 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4397 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4400 if( $title != $titletouse )
4402 $attr = array('title' => htmlspecialchars($title));
4408 $acomment = str_replace( '$', '$', $titletouse );
4409 $acomment = str_replace( ' ', ' ', $acomment );
4411 if( $A['count'] > 1 )
4413 $acomment .= ' [+' . $A['count'] . ']';
4416 $newcomments[] = COM_createLink($acomment, $url, $attr);
4419 $retval .= COM_makeList( $newcomments, 'list-new-trackbacks' );
4423 $retval .= $LANG01[115] . '<br' . XHTML . '>' . LB;
4425 if( $_CONF['hidenewplugins'] == 0 )
4427 $retval .= '<br' . XHTML . '>';
4431 if( $_CONF['hidenewplugins'] == 0 )
4433 list( $headlines, $smallheadlines, $content ) = PLG_getWhatsNew();
4434 $plugins = count( $headlines );
4437 for( $i = 0; $i < $plugins; $i++ )
4439 $retval .= '<h3>' . $headlines[$i] . ' <small>'
4440 . $smallheadlines[$i] . '</small></h3>';
4441 if( is_array( $content[$i] ))
4443 $retval .= COM_makeList( $content[$i], 'list-new-plugins' );
4447 $retval .= $content[$i];
4450 if( $i + 1 < $plugins )
4452 $retval .= '<br' . XHTML . '>';
4458 $retval .= COM_endBlock( COM_getBlockTemplate( 'whats_new_block', 'footer', $position ));
4464 * Creates the string that indicates the timespan in which new items were found
4466 * @param string $time_string template string
4467 * @param int $time number of seconds in which results are found
4468 * @param string $type type (translated string) of new item
4469 * @param int $amount amount of things that have been found.
4471 function COM_formatTimeString( $time_string, $time, $type = '', $amount = 0 )
4473 global $LANG_WHATSNEW;
4475 $retval = $time_string;
4477 // This is the amount you have to divide the previous by to get the
4478 // different time intervals: hour, day, week, months
4479 $time_divider = array( 60, 60, 24, 7, 30 );
4481 // These are the respective strings to the numbers above. They have to match
4482 // the strings in $LANG_WHATSNEW (i.e. these are the keys for the array -
4483 // the actual text strings are taken from the language file).
4484 $time_description = array( 'minute', 'hour', 'day', 'week', 'month' );
4485 $times_description = array( 'minutes', 'hours', 'days', 'weeks', 'months' );
4487 $time_dividers = count( $time_divider );
4488 for( $s = 0; $s < $time_dividers; $s++ )
4490 $time = $time / $time_divider[$s];
4491 if( $time < $time_divider[$s + 1] )
4497 $time_str = $time_description[$s];
4499 else // go back to the previous unit, e.g. 1 day -> 24 hours
4501 $time_str = $times_description[$s - 1];
4502 $time *= $time_divider[$s];
4507 $time_str = $times_description[$s];
4509 $fields = array( '%n', '%i', '%t', '%s' );
4510 $values = array( $amount, $type, $time, $LANG_WHATSNEW[$time_str] );
4511 $retval = str_replace( $fields, $values, $retval );
4520 * Displays a message text in a "System Message" block
4522 * @param string $message Message text; may contain HTML
4523 * @param string $title (optional) alternative block title
4524 * @return string HTML block with message
4525 * @see COM_showMessage
4526 * @see COM_showMessageFromParameter
4529 function COM_showMessageText($message, $title = '')
4531 global $_CONF, $MESSAGE, $_IMAGE_TYPE;
4535 if (!empty($message)) {
4536 if (empty($title)) {
4537 $title = $MESSAGE[40];
4539 $timestamp = strftime($_CONF['daytime']);
4540 $retval .= COM_startBlock($title . ' - ' . $timestamp, '',
4541 COM_getBlockTemplate('_msg_block', 'header'))
4542 . '<p class="sysmessage"><img src="' . $_CONF['layout_url']
4543 . '/images/sysmessage.' . $_IMAGE_TYPE . '" alt="" ' . XHTML
4544 . '>' . $message . '</p>'
4545 . COM_endBlock(COM_getBlockTemplate('_msg_block', 'footer'));
4552 * Displays a message on the webpage
4554 * Display one of the predefined messages from the $MESSAGE array. If a plugin
4555 * name is provided, display that plugin's message instead.
4557 * @param int $msg ID of message to show
4558 * @param string $plugin Optional name of plugin to lookup plugin defined message
4559 * @return string HTML block with message
4560 * @see COM_showMessageFromParameter
4561 * @see COM_showMessageText
4564 function COM_showMessage($msg, $plugin = '')
4571 if (!empty($plugin)) {
4572 $var = 'PLG_' . $plugin . '_MESSAGE' . $msg;
4577 $message = sprintf($MESSAGE[61], $plugin);
4578 COM_errorLog($message . ": " . $var, 1);
4581 $message = $MESSAGE[$msg];
4584 if (!empty($message)) {
4585 $retval .= COM_showMessageText($message);
4593 * Displays a message, as defined by URL parameters
4595 * Helper function to display a message, if URL parameters 'msg' and 'plugin'
4596 * (optional) are defined. Only for GET requests, but that's what Geeklog uses
4597 * everywhere anyway.
4599 * @return string HTML block with message
4600 * @see COM_showMessage
4601 * @see COM_showMessageText
4604 function COM_showMessageFromParameter()
4608 if (isset($_GET['msg'])) {
4609 $msg = COM_applyFilter($_GET['msg'], true);
4612 if (isset($_GET['plugin'])) {
4613 $plugin = COM_applyFilter($_GET['plugin']);
4615 $retval .= COM_showMessage($msg, $plugin);
4623 * Prints Google(tm)-like paging navigation
4625 * @param string $base_url base url to use for all generated links
4626 * @param int $curpage current page we are on
4627 * @param int $num_pages Total number of pages
4628 * @param string $page_str page-variable name AND '='
4629 * @param boolean $do_rewrite if true, url-rewriting is respected
4630 * @param string $msg to be displayed with the navigation
4631 * @param string $open_ended replace next/last links with this
4632 * @return string HTML formatted widget
4634 function COM_printPageNavigation( $base_url, $curpage, $num_pages,
4635 $page_str='page=', $do_rewrite=false, $msg='',
4642 if( $num_pages < 2 )
4649 $hasargs = strstr( $base_url, '?' );
4667 $retval .= COM_createLink($LANG05[7], $base_url) . ' | ';
4669 if( ( $curpage - 1 ) > 1 )
4671 $pg = $sep . $page_str . ( $curpage - 1 );
4673 $retval .= COM_createLink($LANG05[6], $base_url . $pg ) . ' | ';
4677 $retval .= $LANG05[7] . ' | ' ;
4678 $retval .= $LANG05[6] . ' | ' ;
4681 for( $pgcount = ( $curpage - 10 ); ( $pgcount <= ( $curpage + 9 )) AND ( $pgcount <= $num_pages ); $pgcount++ )
4688 if( $pgcount == $curpage )
4690 $retval .= '<b>' . $pgcount . '</b> ';
4697 $pg = $sep . $page_str . $pgcount;
4699 $retval .= COM_createLink($pgcount, $base_url . $pg) . ' ';
4703 if( !empty( $open_ended ))
4705 $retval .= '| ' . $open_ended;
4707 else if( $curpage == $num_pages )
4709 $retval .= '| ' . $LANG05[5] . ' ';
4710 $retval .= '| ' . $LANG05[8];
4714 $retval .= '| ' . COM_createLink($LANG05[5], $base_url . $sep
4715 . $page_str . ($curpage + 1));
4716 $retval .= ' | ' . COM_createLink($LANG05[8], $base_url . $sep
4717 . $page_str . $num_pages);
4720 if( !empty( $retval ))
4726 $retval = '<div class="pagenav">' . $msg . $retval . '</div>';
4733 * Returns formatted date/time for user
4735 * This function COM_takes a date in either unixtimestamp or in english and
4736 * formats it to the users preference. If the user didn't specify a format
4737 * the format in the config file is used. This returns an array where array[0]
4738 * is the formatted date and array[1] is the unixtimestamp
4740 * @param string $date date to format, otherwise we format current date/time
4741 * @return array array[0] is the formatted date and array[1] is the unixtimestamp.
4744 function COM_getUserDateTimeFormat( $date='' )
4746 global $_TABLES, $_USER, $_CONF;
4748 // Get display format for time
4750 if( !COM_isAnonUser() )
4752 if( empty( $_USER['format'] ))
4754 $dateformat = $_CONF['date'];
4758 $dateformat = $_USER['format'];
4763 $dateformat = $_CONF['date'];
4768 // Date is empty, get current date/time
4771 else if( is_numeric( $date ))
4773 // This is a timestamp
4778 // This is a string representation of a date/time
4779 $stamp = strtotime( $date );
4784 $date = strftime( $dateformat, $stamp );
4786 return array( $date, $stamp );
4790 * Returns user-defined cookie timeout
4792 * In account preferences users can specify when their long-term cookie expires.
4793 * This function returns that value.
4795 * @return int Cookie time out value in seconds
4798 function COM_getUserCookieTimeout()
4800 global $_TABLES, $_USER, $_CONF;
4802 if( empty( $_USER ))
4807 $timeoutvalue = DB_getItem( $_TABLES['users'], 'cookietimeout', "uid = {$_USER['uid']}" );
4809 if( empty( $timeoutvalue ))
4814 return $timeoutvalue;
4818 * Shows who is online in slick little block
4819 * @return string HTML string of online users seperated by line breaks.
4822 function phpblock_whosonline()
4824 global $_CONF, $_TABLES, $_USER, $LANG01, $_IMAGE_TYPE;
4828 $expire_time = time() - $_CONF['whosonline_threshold'];
4830 $byname = 'username';
4831 if( $_CONF['show_fullname'] == 1 )
4833 $byname .= ',fullname';
4835 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4837 $byname .= ',remoteusername,remoteservice';
4840 $result = DB_query( "SELECT DISTINCT {$_TABLES['sessions']}.uid,{$byname},photo,showonline FROM {$_TABLES['sessions']},{$_TABLES['users']},{$_TABLES['userprefs']} WHERE {$_TABLES['users']}.uid = {$_TABLES['sessions']}.uid AND {$_TABLES['users']}.uid = {$_TABLES['userprefs']}.uid AND start_time >= $expire_time AND {$_TABLES['sessions']}.uid <> 1 ORDER BY {$byname}" );
4841 $nrows = DB_numRows( $result );
4846 for( $i = 0; $i < $nrows; $i++ )
4848 $A = DB_fetchArray( $result );
4850 if( $A['showonline'] == 1 )
4853 if( $_CONF['show_fullname'] == 1 )
4855 $fullname = $A['fullname'];
4857 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4859 $username = COM_getDisplayName( $A['uid'], $A['username'],
4860 $fullname, $A['remoteusername'], $A['remoteservice'] );
4864 $username = COM_getDisplayName( $A['uid'], $A['username'],
4867 $url = $_CONF['site_url'] . '/users.php?mode=profile&uid=' . $A['uid'];
4868 $retval .= COM_createLink($username, $url);
4870 if( !empty( $A['photo'] ) AND $_CONF['allow_user_photo'] == 1)
4872 $usrimg = '<img src="' . $_CONF['layout_url']
4873 . '/images/smallcamera.' . $_IMAGE_TYPE
4874 . '" alt=""' . XHTML . '>';
4875 $retval .= ' ' . COM_createLink($usrimg, $url);
4877 $retval .= '<br' . XHTML . '>';
4882 // this user does not want to show up in Who's Online
4883 $num_anon++; // count as anonymous
4887 $num_anon += DB_count( $_TABLES['sessions'], 'uid', 1 );
4889 if(( $_CONF['whosonline_anonymous'] == 1 ) &&
4892 // note that we're overwriting the contents of $retval here
4895 $retval = $LANG01[112] . ': ' . COM_numberFormat($num_reg)
4896 . '<br' . XHTML . '>';
4906 $retval .= $LANG01[41] . ': ' . COM_numberFormat($num_anon)
4907 . '<br' . XHTML . '>';
4914 * Gets the <option> values for calendar months
4916 * @param string $selected Selected month
4917 * @see function COM_getDayFormOptions
4918 * @see function COM_getYearFormOptions
4919 * @see function COM_getHourFormOptions
4920 * @see function COM_getMinuteFormOptions
4921 * @return string HTML Months as option values
4924 function COM_getMonthFormOptions( $selected = '' )
4928 $month_options = '';
4930 for( $i = 1; $i <= 12; $i++ )
4933 $month_options .= '<option value="' . $mval . '"';
4935 if( $i == $selected )
4937 $month_options .= ' selected="selected"';
4940 $month_options .= '>' . $LANG_MONTH[$mval] . '</option>';
4943 return $month_options;
4947 * Gets the <option> values for calendar days
4949 * @param string $selected Selected day
4950 * @see function COM_getMonthFormOptions
4951 * @see function COM_getYearFormOptions
4952 * @see function COM_getHourFormOptions
4953 * @see function COM_getMinuteFormOptions
4954 * @return string HTML days as option values
4957 function COM_getDayFormOptions( $selected = '' )
4961 for( $i = 1; $i <= 31; $i++ )
4972 $day_options .= '<option value="' . $dval . '"';
4974 if( $i == $selected )
4976 $day_options .= ' selected="selected"';
4979 $day_options .= '>' . $dval . '</option>';
4982 return $day_options;
4986 * Gets the <option> values for calendar years
4988 * Returns Option list Containing 5 years starting with current
4989 * unless @selected is < current year then starts with @selected
4991 * @param string $selected Selected year
4992 * @param int $startoffset Optional (can be +/-) Used to determine start year for range of years
4993 * @param int $endoffset Optional (can be +/-) Used to determine end year for range of years
4994 * @see function COM_getMonthFormOptions
4995 * @see function COM_getDayFormOptions
4996 * @see function COM_getHourFormOptions
4997 * @see function COM_getMinuteFormOptions
4998 * @return string HTML years as option values
5001 function COM_getYearFormOptions($selected = '', $startoffset = -1, $endoffset = 5)
5004 $start_year = date('Y') + $startoffset;
5005 $cur_year = date('Y', time());
5006 $finish_year = $cur_year + $endoffset;
5008 if (!empty($selected)) {
5009 if ($selected < $cur_year) {
5010 $start_year = $selected;
5014 for ($i = $start_year; $i <= $finish_year; $i++) {
5015 $year_options .= '<option value="' . $i . '"';
5017 if ($i == $selected) {
5018 $year_options .= ' selected="selected"';
5021 $year_options .= '>' . $i . '</option>';
5024 return $year_options;
5028 * Gets the <option> values for clock hours
5030 * @param string $selected Selected hour
5031 * @param int $mode 12 or 24 hour mode
5032 * @return string HTML string of options
5033 * @see function COM_getMonthFormOptions
5034 * @see function COM_getDayFormOptions
5035 * @see function COM_getYearFormOptions
5036 * @see function COM_getMinuteFormOptions
5039 function COM_getHourFormOptions( $selected = '', $mode = 12 )
5045 for( $i = 1; $i <= 11; $i++ )
5058 $hour_options .= '<option value="12"';
5060 if( $selected == 12 )
5062 $hour_options .= ' selected="selected"';
5065 $hour_options .= '>12</option>';
5068 $hour_options .= '<option value="' . $hval . '"';
5070 if( $selected == $i )
5072 $hour_options .= ' selected="selected"';
5075 $hour_options .= '>' . $i . '</option>';
5078 else // if( $mode == 24 )
5080 for( $i = 0; $i < 24; $i++ )
5091 $hour_options .= '<option value="' . $hval . '"';
5093 if( $selected == $i )
5095 $hour_options .= ' selected="selected"';
5098 $hour_options .= '>' . $i . '</option>';
5102 return $hour_options;
5106 * Gets the <option> values for clock minutes
5108 * @param string $selected Selected minutes
5109 * @param int $step number of minutes between options, e.g. 15
5110 * @see function COM_getMonthFormOptions
5111 * @see function COM_getDayFormOptions
5112 * @see function COM_getHourFormOptions
5113 * @see function COM_getYearFormOptions
5114 * @return string HTML of option minutes
5117 function COM_getMinuteFormOptions( $selected = '', $step = 1 )
5119 $minute_options = '';
5121 if(( $step < 1 ) || ( $step > 30 ))
5126 for( $i = 0; $i <= 59; $i += $step )
5137 $minute_options .= '<option value="' . $mval . '"';
5139 if( $selected == $i )
5141 $minute_options .= ' selected="selected"';
5144 $minute_options .= '>' . $mval . '</option>';
5147 return $minute_options;
5151 * For backward compatibility only.
5152 * This function should always have been called COM_getMinuteFormOptions
5153 * @see COM_getMinuteFormOptions
5155 function COM_getMinuteOptions( $selected = '', $step = 1 )
5157 return COM_getMinuteFormOptions( $selected, $step );
5161 * Create an am/pm selector dropdown menu
5163 * @param string $name name of the <select>
5164 * @param string $selected preselection: 'am' or 'pm'
5165 * @return string HTML for the dropdown; empty string in 24 hour mode
5168 function COM_getAmPmFormSelection( $name, $selected = '' )
5174 if( isset( $_CONF['hour_mode'] ) && ( $_CONF['hour_mode'] == 24 ))
5180 if( empty( $selected ))
5182 $selected = date( 'a' );
5185 $retval .= '<select name="' . $name . '">' . LB;
5186 $retval .= '<option value="am"';
5187 if( $selected == 'am' )
5189 $retval .= ' selected="selected"';
5191 $retval .= '>am</option>' . LB . '<option value="pm"';
5192 if( $selected == 'pm' )
5194 $retval .= ' selected="selected"';
5196 $retval .= '>pm</option>' . LB . '</select>' . LB;
5203 * Creates an HTML unordered list from the given array.
5204 * It formats one list item per array element, using the list.thtml
5205 * and listitem.thtml templates.
5207 * @param array $listofitems Items to list out
5208 * @param string $classname optional CSS class name for the list
5209 * @return string HTML unordered list of array items
5211 function COM_makeList($listofitems, $classname = '')
5215 $list = new Template($_CONF['path_layout']);
5216 $list->set_file(array('list' => 'list.thtml',
5217 'listitem' => 'listitem.thtml'));
5218 $list->set_var( 'xhtml', XHTML );
5219 $list->set_var('site_url', $_CONF['site_url']);
5220 $list->set_var('site_admin_url', $_CONF['site_admin_url']);
5221 $list->set_var('layout_url', $_CONF['layout_url']);
5223 if (empty($classname)) {
5224 $list->set_var('list_class', '');
5225 $list->set_var('list_class_name', '');
5227 $list->set_var('list_class', 'class="' . $classname . '"');
5228 $list->set_var('list_class_name', $classname);
5231 if (is_array($listofitems)) {
5232 foreach ($listofitems as $oneitem) {
5233 $list->set_var('list_item', $oneitem);
5234 $list->parse('list_items', 'listitem', true);
5238 $list->parse('newlist', 'list', true);
5240 return $list->finish($list->get_var('newlist'));
5244 * Check if speed limit applies
5246 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5247 * @param int $max max number of allowed tries within speed limit
5248 * @param string $property IP address or other identifiable property
5249 * @return int 0: does not apply, else: seconds since last post
5251 function COM_checkSpeedlimit($type = 'submit', $max = 1, $property = '')
5257 if (empty($property)) {
5258 $property = $_SERVER['REMOTE_ADDR'];
5260 $property = addslashes($property);
5262 $res = DB_query("SELECT date FROM {$_TABLES['speedlimit']} WHERE (type = '$type') AND (ipaddress = '$property') ORDER BY date ASC");
5264 // If the number of allowed tries has not been reached,
5265 // return 0 (didn't hit limit)
5266 if (DB_numRows($res) < $max) {
5270 list($date) = DB_fetchArray($res);
5272 if (!empty($date)) {
5273 $last = time() - $date;
5275 // just in case someone manages to submit something in < 1 sec.
5284 * Store post info for speed limit
5286 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5287 * @param string $property IP address or other identifiable property
5290 function COM_updateSpeedlimit($type = 'submit', $property = '')
5294 if (empty($property)) {
5295 $property = $_SERVER['REMOTE_ADDR'];
5297 $property = addslashes($property);
5299 DB_save($_TABLES['speedlimit'], 'ipaddress,date,type',
5300 "'$property',UNIX_TIMESTAMP(),'$type'");
5304 * Clear out expired speed limits, i.e. entries older than 'x' seconds
5306 * @param speedlimit int number of seconds
5307 * @param type string type of speed limit, e.g. 'submit', 'comment'
5310 function COM_clearSpeedlimit($speedlimit = 60, $type = '')
5314 $sql = "DELETE FROM {$_TABLES['speedlimit']} WHERE ";
5315 if (!empty($type)) {
5316 $sql .= "(type = '$type') AND ";
5318 $sql .= "(date < UNIX_TIMESTAMP() - $speedlimit)";
5323 * Reset the speedlimit
5325 * @param string $type type of speed limit to reset, e.g. 'submit'
5326 * @param string $property IP address or other identifiable property
5329 function COM_resetSpeedlimit($type = 'submit', $property = '')
5333 if (empty($property)) {
5334 $property = $_SERVER['REMOTE_ADDR'];
5336 $property = addslashes($property);
5338 DB_delete($_TABLES['speedlimit'], array('type', 'ipaddress'),
5339 array($type, $property));
5343 * Wrapper function for URL class so as to not confuse people as this will
5344 * eventually get used all over the place
5346 * This function returns a crawler friendly URL (if possible)
5348 * @param string $url URL to try to build crawler friendly URL for
5349 * @return string Rewritten URL
5352 function COM_buildURL( $url )
5356 return $_URL->buildURL( $url );
5360 * Wrapper function for URL class so as to not confuse people
5362 * This function sets the name of the arguments found in url
5364 * @param array $names Names of arguments in query string to assign to values
5365 * @return boolean True if successful
5368 function COM_setArgNames( $names )
5372 return $_URL->setArgNames( $names );
5376 * Wrapper function for URL class
5378 * returns value for specified argument
5380 * @param string $name argument to get value for
5381 * @return string Argument value
5384 function COM_getArgument( $name )
5388 return $_URL->getArgument( $name );
5394 * This will take a number of occurrences, and number of seconds for the time span and return
5395 * the smallest #/time interval
5397 * @param int $occurrences how many occurrences during time interval
5398 * @param int $timespan time interval in seconds
5399 * @return int Seconds per interval
5402 function COM_getRate( $occurrences, $timespan )
5404 // want to define some common time words (yes, dirk, i need to put this in LANG)
5405 // time words and their value in seconds
5406 // week is 7 * day, month is 30 * day, year is 365.25 * day
5408 $common_time = array(
5418 if( $occurrences != 0 )
5420 $rate = ( int )( $timespan / $occurrences );
5421 $adjustedRate = $occurrences + 1;
5422 $time_unit = 'second';
5426 foreach( $common_time as $unit=>$seconds )
5428 if( $rate > $seconds )
5430 $foo = ( int )(( $rate / $seconds ) + .5 );
5432 if(( $foo < $occurrences ) && ( $foo > 0 ))
5434 $adjustedRate = $foo;
5440 $singular = '1 shout every ' . $adjustedRate . ' ' . $time_unit;
5442 if( $adjustedRate > 1 )
5449 $singular = 'No events';
5456 * Return SQL expression to check for permissions.
5458 * Creates part of an SQL expression that can be used to request items with the
5459 * standard set of Geeklog permissions.
5461 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5462 * @param int $u_id user id or 0 = current user
5463 * @param int $access access to check for (2=read, 3=r&write)
5464 * @param string $table table name if ambiguous (e.g. in JOINs)
5465 * @return string SQL expression string (may be empty)
5468 function COM_getPermSQL( $type = 'WHERE', $u_id = 0, $access = 2, $table = '' )
5470 global $_USER, $_GROUPS;
5472 if( !empty( $table ))
5478 if( COM_isAnonUser() )
5484 $uid = $_USER['uid'];
5492 $UserGroups = array();
5493 if(( empty( $_USER['uid'] ) && ( $uid == 1 )) || ( $uid == $_USER['uid'] ))
5495 if( empty( $_GROUPS ))
5497 $_GROUPS = SEC_getUserGroups( $uid );
5499 $UserGroups = $_GROUPS;
5503 $UserGroups = SEC_getUserGroups( $uid );
5506 if( empty( $UserGroups ))
5508 // this shouldn't really happen, but if it does, handle user
5509 // like an anonymous user
5513 if( SEC_inGroup( 'Root', $uid ))
5518 $sql = ' ' . $type . ' (';
5522 $sql .= "(({$table}owner_id = '{$uid}') AND ({$table}perm_owner >= $access)) OR ";
5524 $sql .= "(({$table}group_id IN (" . implode( ',', $UserGroups )
5525 . ")) AND ({$table}perm_group >= $access)) OR ";
5526 $sql .= "({$table}perm_members >= $access)";
5530 $sql .= "{$table}perm_anon >= $access";
5539 * Return SQL expression to check for allowed topics.
5541 * Creates part of an SQL expression that can be used to only request stories
5542 * from topics to which the user has access to.
5544 * Note that this function does an SQL request, so you should cache
5545 * the resulting SQL expression if you need it more than once.
5547 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5548 * @param int $u_id user id or 0 = current user
5549 * @param string $table table name if ambiguous (e.g. in JOINs)
5550 * @return string SQL expression string (may be empty)
5553 function COM_getTopicSQL( $type = 'WHERE', $u_id = 0, $table = '' )
5555 global $_TABLES, $_USER, $_GROUPS;
5557 $topicsql = ' ' . $type . ' ';
5559 if( !empty( $table ))
5564 $UserGroups = array();
5565 if(( $u_id <= 0 ) || ( isset( $_USER['uid'] ) && $u_id == $_USER['uid'] ))
5567 if( !COM_isAnonUser() )
5569 $uid = $_USER['uid'];
5575 $UserGroups = $_GROUPS;
5580 $UserGroups = SEC_getUserGroups( $uid );
5583 if( empty( $UserGroups ))
5585 // this shouldn't really happen, but if it does, handle user
5586 // like an anonymous user
5590 if( SEC_inGroup( 'Root', $uid ))
5595 $result = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
5596 . COM_getPermSQL( 'WHERE', $uid ));
5598 while( $T = DB_fetchArray( $result ))
5600 $tids[] = $T['tid'];
5603 if( count( $tids ) > 0 )
5605 $topicsql .= "({$table}tid IN ('" . implode( "','", $tids ) . "'))";
5616 * Strip slashes from a string only when magic_quotes_gpc = on.
5618 * @param string $text The text
5619 * @return string The text, possibly without slashes.
5621 function COM_stripslashes( $text )
5623 if( get_magic_quotes_gpc() == 1 )
5625 return( stripslashes( $text ));
5632 * Filter parameters passed per GET (URL) or POST.
5634 * @param string $parameter the parameter to test
5635 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5636 * @return string the filtered parameter (may now be empty or 0)
5637 * @see COM_applyBasicFilter
5640 function COM_applyFilter( $parameter, $isnumeric = false )
5642 $p = COM_stripslashes($parameter);
5644 return COM_applyBasicFilter($p, $isnumeric);
5650 * NOTE: Use this function instead of COM_applyFilter for parameters
5651 * _not_ coming in through a GET or POST request.
5653 * @param string $parameter the parameter to test
5654 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5655 * @return string the filtered parameter (may now be empty or 0)
5656 * @see COM_applyFilter
5659 function COM_applyBasicFilter( $parameter, $isnumeric = false )
5661 $log_manipulation = false; // set to true to log when the filter applied
5663 $p = strip_tags( $parameter );
5664 $p = COM_killJS( $p ); // doesn't help a lot right now, but still ...
5668 // Note: PHP's is_numeric() accepts values like 4e4 as numeric
5669 if( !is_numeric( $p ) || ( preg_match( '/^-?\d+$/', $p ) == 0 ))
5676 $p = preg_replace( '/\/\*.*/', '', $p );
5677 $pa = explode( "'", $p );
5678 $pa = explode( '"', $pa[0] );
5679 $pa = explode( '`', $pa[0] );
5680 $pa = explode( ';', $pa[0] );
5681 $pa = explode( ',', $pa[0] );
5682 $pa = explode( '\\', $pa[0] );
5686 if( $log_manipulation )
5688 if( strcmp( $p, $parameter ) != 0 )
5690 COM_errorLog( "Filter applied: >> $parameter << filtered to $p [IP {$_SERVER['REMOTE_ADDR']}]", 1);
5700 * @param string $url URL to sanitized
5701 * @param array $allowed_protocols array of allowed protocols
5702 * @param string $default_protocol replacement protocol (default: http)
5703 * @return string sanitized URL
5706 function COM_sanitizeUrl( $url, $allowed_protocols = '', $default_protocol = '' )
5710 if( empty( $allowed_protocols ))
5712 $allowed_protocols = $_CONF['allowed_protocols'];
5714 else if( !is_array( $allowed_protocols ))
5716 $allowed_protocols = array( $allowed_protocols );
5719 if( empty( $default_protocol ))
5721 $default_protocol = 'http:';
5723 else if( substr( $default_protocol, -1 ) != ':' )
5725 $default_protocol .= ':';
5728 $url = strip_tags( $url );
5731 $pos = MBYTE_strpos( $url, ':' );
5732 if( $pos === false )
5734 $url = $default_protocol . '//' . $url;
5738 $protocol = MBYTE_substr( $url, 0, $pos + 1 );
5740 foreach( $allowed_protocols as $allowed )
5742 if( substr( $allowed, -1 ) != ':' )
5746 if( $protocol == $allowed )
5754 $url = $default_protocol . MBYTE_substr( $url, $pos + 1 );
5763 * Ensure an ID contains only alphanumeric characters, dots, dashes, or underscores
5765 * @param string $id the ID to sanitize
5766 * @param boolean $new_id true = create a new ID in case we end up with an empty string
5767 * @return string the sanitized ID
5769 function COM_sanitizeID( $id, $new_id = true )
5771 $id = str_replace( ' ', '', $id );
5772 $id = str_replace( array( '/', '\\', ':', '+' ), '-', $id );
5773 $id = preg_replace( '/[^a-zA-Z0-9\-_\.]/', '', $id );
5774 if( empty( $id ) && $new_id )
5776 $id = COM_makesid();
5783 * Sanitize a filename.
5785 * NOTE: This function is pretty strict in what it allows. Meant to be used
5786 * for files to be included where part of the filename is dynamic.
5788 * @param string $filename the filename to clean up
5789 * @param boolean $allow_dots whether to allow dots in the filename or not
5790 * @return string sanitized filename
5793 function COM_sanitizeFilename($filename, $allow_dots = false)
5796 $filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $filename);
5797 $filename = str_replace('..', '', $filename);
5799 $filename = preg_replace('/[^a-zA-Z0-9\-_]/', '', $filename);
5806 * Detect links in a plain-ascii text and turn them into clickable links.
5807 * Will detect links starting with "http:", "https:", "ftp:", and "www.".
5809 * @param string $text the (plain-ascii) text string
5810 * @return string the same string, with links enclosed in <a>...</a> tags
5813 function COM_makeClickableLinks( $text )
5817 if (! $_CONF['clickable_links']) {
5821 // These regular expressions will work for this purpuse, but
5822 // they should NOT be used for validating links.
5824 // matches anything starting with http:// or https:// or ftp:// or ftps://
5825 $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:(?:ht|f)tps?:\/{2})(?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&)?)+)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
5826 $replace[] = "COM_makeClickableLinksCallback('', '\\1')";
5828 // matches anything containing a top level domain: xxx.com or xxx.yyy.net/stuff.php or xxx.yyy.zz
5829 // list taken from: http://en.wikipedia.org/wiki/List_of_Internet_TLDs
5830 $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:[a-z0-9]+\.)*[a-z0-9]+\.(?:aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z]{2})(?:[\/?#](?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&)?)*)?)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
5831 $replace[] = "COM_makeClickableLinksCallback('http://', '\\1')";
5833 $text = preg_replace( $regex, $replace, $text );
5839 * Callback function to help format links in COM_makeClickableLinks
5841 * @param string $http set to 'http://' when not already in the url
5842 * @param string $link the url
5843 * @return string link enclosed in <a>...</a> tags
5846 function COM_makeClickableLinksCallback( $http, $link )
5848 $text = COM_truncate( $link, 50, '...', '10' );
5850 return "<a href=\"$http$link\">$text</a>";
5854 * Undo the conversion of URLs to clickable links (in plain text posts),
5855 * e.g. so that we can present the user with the post as they entered them.
5857 * @param string $text story text
5858 * @return string story text without links
5861 function COM_undoClickableLinks( $text )
5863 $text = preg_replace( '/<a href="([^"]*)">([^<]*)<\/a>/', '\1', $text );
5869 * Highlight the words from a search query in a given text string.
5871 * @param string $text the text
5872 * @param string $query the search query
5873 * @param string $class html class to use to highlight
5874 * @return string the text with highlighted search words
5877 function COM_highlightQuery( $text, $query, $class = 'highlight' )
5879 // escape PCRE special characters
5880 $query = preg_quote($query, '/');
5882 $mywords = explode(' ', $query);
5883 foreach ($mywords as $searchword)
5885 if (!empty($searchword))
5887 $before = "/(?!(?:[^<]+>|[^>]+<\/a>))\b";
5889 if ($searchword <> utf8_encode($searchword)) {
5890 if (@preg_match('/^\pL$/u', urldecode('%C3%B1'))) { // Unicode property support
5891 $before = "/(?<!\p{L})";
5892 $after = "(?!\p{L})/u";
5898 $text = preg_replace($before . $searchword . $after, "<span class=\"$class\">\\0</span>", '<!-- x -->' . $text . '<!-- x -->' );
5905 * Determines the difference between two dates.
5907 * This will takes either unixtimestamps or English dates as input and will
5908 * automatically do the date diff on the more recent of the two dates (e.g. the
5909 * order of the two dates given doesn't matter).
5911 * @author Tony Bibbs, tony DOT bibbs AT iowa DOT gov
5913 * @param string $interval Can be:
5920 * @param string|int $date1 English date (e.g. 10 Dec 2004) or unixtimestamp
5921 * @param string|int $date2 English date (e.g. 10 Dec 2004) or unixtimestamp
5922 * @return int Difference of the two dates in the unit of time indicated by the interval
5925 function COM_dateDiff( $interval, $date1, $date2 )
5927 // Convert dates to timestamps, if needed.
5928 if( !is_numeric( $date1 ))
5930 $date1 = strtotime( $date1 );
5933 if( !is_numeric( $date2 ))
5935 $date2 = strtotime( $date2 );
5938 // Function roughly equivalent to the ASP "DateDiff" function
5939 if( $date2 > $date1 )
5941 $seconds = $date2 - $date1;
5945 $seconds = $date1 - $date2;
5951 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5952 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5953 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5954 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5955 $diff = $year2 - $year1;
5956 if($month1 > $month2) {
5958 } elseif($month1 == $month2) {
5961 } elseif($day1 == $day2) {
5962 if($time1 > $time2) {
5969 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5970 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5971 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5972 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5973 $diff = ($year2 * 12 + $month2) - ($year1 * 12 + $month1);
5976 } elseif($day1 == $day2) {
5977 if($time1 > $time2) {
5983 // Only simple seconds calculation needed from here on
5984 $diff = floor($seconds / 604800);
5987 $diff = floor($seconds / 86400);
5990 $diff = floor($seconds / 3600);
5993 $diff = floor($seconds / 60);
6004 * Try to figure out our current URL, including all parameters.
6006 * This is an ugly hack since there's no single variable that returns what
6007 * we want and the variables used here may not be available on all servers
6010 * Seems to work on Apache (1.3.x and 2.x), IIS, and Zeus ...
6012 * @return string complete URL, e.g. 'http://www.example.com/blah.php?foo=bar'
6015 function COM_getCurrentURL()
6021 if( empty( $_SERVER['SCRIPT_URI'] ))
6023 if( !empty( $_SERVER['DOCUMENT_URI'] ))
6025 $thisUrl = $_SERVER['DOCUMENT_URI'];
6030 $thisUrl = $_SERVER['SCRIPT_URI'];
6032 if( !empty( $thisUrl ) && !empty( $_SERVER['QUERY_STRING'] ))
6034 $thisUrl .= '?' . $_SERVER['QUERY_STRING'];
6036 if( empty( $thisUrl ))
6038 $requestUri = $_SERVER['REQUEST_URI'];
6039 if( empty( $_SERVER['REQUEST_URI'] ))
6041 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6042 if( empty( $_SERVER['PATH_INFO'] ))
6044 $requestUri = $_SERVER['SCRIPT_NAME'];
6048 $requestUri = $_SERVER['PATH_INFO'];
6050 if( !empty( $_SERVER['QUERY_STRING'] ))
6052 $requestUri .= '?' . $_SERVER['QUERY_STRING'];
6056 $firstslash = strpos( $_CONF['site_url'], '/' );
6057 if( $firstslash === false )
6059 // special case - assume it's okay
6060 $thisUrl = $_CONF['site_url'] . $requestUri;
6062 else if( $firstslash + 1 == strrpos( $_CONF['site_url'], '/' ))
6064 // site is in the document root
6065 $thisUrl = $_CONF['site_url'] . $requestUri;
6069 // extract server name first
6070 $pos = strpos( $_CONF['site_url'], '/', $firstslash + 2 );
6071 $thisUrl = substr( $_CONF['site_url'], 0, $pos ) . $requestUri;
6079 * Check if we're on Geeklog's index page.
6081 * See if we're on the main index page (first page, no topics selected).
6083 * @return boolean true = we're on the frontpage, false = we're not
6086 function COM_onFrontpage()
6088 global $_CONF, $topic, $page, $newstories;
6090 // Note: We can't use $PHP_SELF here since the site may not be in the
6092 $onFrontpage = false;
6094 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6095 if( empty( $_SERVER['PATH_INFO'] ))
6097 $scriptName = $_SERVER['SCRIPT_NAME'];
6101 $scriptName = $_SERVER['PATH_INFO'];
6104 preg_match( '/\/\/[^\/]*(.*)/', $_CONF['site_url'], $pathonly );
6105 if(( $scriptName == $pathonly[1] . '/index.php' ) &&
6106 empty( $topic ) && ( $page == 1 ) && !$newstories )
6108 $onFrontpage = true;
6111 return $onFrontpage;
6115 * Check if we're on Geeklog's index page [deprecated]
6117 * Note that this function returns FALSE when we're on the index page. Due to
6118 * the inverted return values, it has been deprecated and is only provided for
6119 * backward compatibility - use COM_onFrontpage() instead.
6121 * @deprecated since Geeklog 1.4.1
6122 * @see COM_onFrontpage
6125 function COM_isFrontpage()
6127 return !COM_onFrontpage();
6131 * Converts a number for output into a formatted number with thousands-
6132 * separator, comma-separator and fixed decimals if necessary
6134 * @param float $number Number that will be formatted
6135 * @return string formatted number
6138 function COM_numberFormat( $number )
6142 if( $number - floor( $number ) > 0 ) // number has decimals
6144 $dc = $_CONF['decimal_count'];
6150 $ts = $_CONF['thousand_separator'];
6151 $ds = $_CONF['decimal_separator'];
6153 return number_format( $number, $dc, $ds, $ts );
6157 * Convert a text based date YYYY-MM-DD to a unix timestamp integer value
6159 * @param string $date Date in the format YYYY-MM-DD
6160 * @param string $time Option time in the format HH:MM::SS
6161 * @return int UNIX Timestamp
6163 function COM_convertDate2Timestamp( $date, $time = '' )
6168 // Breakup the string using either a space, fwd slash, dash, bkwd slash or
6169 // colon as a delimiter
6170 $atok = strtok( $date, ' /-\\:' );
6171 while( $atok !== FALSE )
6174 $atok = strtok( ' /-\\:' ); // get the next token
6177 for( $i = 0; $i < 3; $i++ )
6179 if( !isset( $atoks[$i] ) || !is_numeric( $atoks[$i] ))
6187 $timestamp = mktime( 0, 0, 0, $atoks[1], $atoks[2], $atoks[0] );
6191 $btok = strtok( $time, ' /-\\:' );
6192 while( $btok !== FALSE )
6195 $btok = strtok( ' /-\\:' );
6198 for( $i = 0; $i < 3; $i++ )
6200 if( !isset( $btoks[$i] ) || !is_numeric( $btoks[$i] ))
6206 $timestamp = mktime( $btoks[0], $btoks[1], $btoks[2],
6207 $atoks[1], $atoks[2], $atoks[0] );
6214 * Get the HTML for an image with height & width
6216 * @param string $file full path to the file
6217 * @return string html that will be included in the img-tag
6219 function COM_getImgSizeAttributes( $file )
6221 $sizeattributes = '';
6223 if( file_exists( $file ))
6225 $dimensions = getimagesize( $file );
6226 if( !empty( $dimensions[0] ) AND !empty( $dimensions[1] ))
6228 $sizeattributes = 'width="' . $dimensions[0]
6229 . '" height="' . $dimensions[1] . '" ';
6233 return $sizeattributes;
6237 * Display a message and abort
6239 * NOTE: Displays the message and aborts the script.
6241 * @param int $msg message number
6242 * @param string $plugin plugin name, if applicable
6243 * @param int $http_status HTTP status code to send with the message
6244 * @param string $http_text Textual version of the HTTP status code
6247 function COM_displayMessageAndAbort( $msg, $plugin = '', $http_status = 200, $http_text = 'OK')
6249 $display = COM_siteHeader( 'menu' )
6250 . COM_showMessage( $msg, $plugin )
6251 . COM_siteFooter( true );
6253 if( $http_status != 200 )
6255 header( "HTTP/1.1 $http_status $http_text" );
6256 header( "Status: $http_status $http_text" );
6263 * Return full URL of a topic icon
6265 * @param string $imageurl (relative) topic icon URL
6266 * @return string Full URL
6269 function COM_getTopicImageUrl( $imageurl )
6271 global $_CONF, $_THEME_URL;
6275 if( !empty( $imageurl ))
6277 if( isset( $_THEME_URL ))
6279 $iconurl = $_THEME_URL . $imageurl;
6283 $stdImageLoc = true;
6284 if( !strstr( $_CONF['path_images'], $_CONF['path_html'] ))
6286 $stdImageLoc = false;
6291 $iconurl = $_CONF['site_url'] . $imageurl;
6295 $t = explode( '/', $imageurl );
6296 $topicicon = $t[count( $t ) - 1];
6297 $iconurl = $_CONF['site_url']
6298 . '/getimage.php?mode=topics&image=' . $topicicon;
6307 * Create an HTML link
6309 * @param string $content the object to be linked (text, image etc)
6310 * @param string $url the URL the link will point to
6311 * @param array $attr an array of optional attributes for the link
6312 * for example array('title' => 'whatever');
6313 * @return string the HTML link
6315 function COM_createLink($content, $url, $attr = array())
6319 $attr_str = 'href="' . $url . '"';
6320 foreach ($attr as $key => $value) {
6321 $attr_str .= " $key=\"$value\"";
6323 $retval .= "<a $attr_str>$content</a>";
6329 * Create an HTML img
6331 * @param string $url the URL of the image, either starting with
6332 * http://... or $_CONF['layout_url'] is prepended
6333 * @param string $alt the 'alt'-tag of the image
6334 * @param array $attr an array of optional attributes for the link
6335 * for example array('title' => 'whatever');
6336 * @return string the HTML img
6338 function COM_createImage($url, $alt = "", $attr = array())
6344 if (preg_match("/^(https?):/", $url) !== 1) {
6345 $url = $_CONF['layout_url'] . $url;
6347 $attr_str = 'src="' . $url . '"';
6349 foreach ($attr as $key => $value) {
6350 $attr_str .= " $key=\"$value\"";
6353 $retval = "<img $attr_str alt=\"$alt\"" . XHTML . ">";
6359 * Try to determine the user's preferred language by looking at the
6360 * "Accept-Language" header sent by their browser (assuming they bothered
6361 * to select a preferred language there).
6363 * Sample header: Accept-Language: en-us,en;q=0.7,de-de;q=0.3
6365 * @return string name of the language file to use or an empty string
6366 * @todo Bugs: Does not take the quantity ('q') parameter into account,
6367 * but only looks at the order of language codes.
6370 function COM_getLanguageFromBrowser()
6376 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
6377 $accept = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
6378 foreach ($accept as $l) {
6379 $l = explode(';', trim($l));
6381 if (array_key_exists($l, $_CONF['language_files'])) {
6382 $retval = $_CONF['language_files'][$l];
6385 $l = explode('-', $l);
6387 if (array_key_exists($l, $_CONF['language_files'])) {
6388 $retval = $_CONF['language_files'][$l];
6399 * Determine current language
6401 * @return string name of the language file (minus the '.php' extension)
6404 function COM_getLanguage()
6406 global $_CONF, $_USER;
6410 if (!empty($_USER['language'])) {
6411 $langfile = $_USER['language'];
6412 } elseif (!empty($_COOKIE[$_CONF['cookie_language']])) {
6413 $langfile = $_COOKIE[$_CONF['cookie_language']];
6414 } elseif (isset($_CONF['languages'])) {
6415 $langfile = COM_getLanguageFromBrowser();
6418 $langfile = COM_sanitizeFilename($langfile);
6419 if (!empty($langfile)) {
6420 if (is_file($_CONF['path_language'] . $langfile . '.php')) {
6425 // if all else fails, return the default language
6426 return $_CONF['language'];
6430 * Determine the ID to use for the current language
6432 * The $_CONF['language_files'] array maps language IDs to language file names.
6433 * This function returns the language ID for a certain language file, to be
6434 * used in language-dependent URLs.
6436 * @param string $language current language file name (optional)
6437 * @return string language ID, e.g 'en'; empty string on error
6440 function COM_getLanguageId($language = '')
6444 if (empty($language)) {
6445 $language = COM_getLanguage();
6449 if (isset($_CONF['language_files'])) {
6450 $lang_id = array_search($language, $_CONF['language_files']);
6452 if ($lang_id === false) {
6453 // that looks like a misconfigured $_CONF['language_files'] array
6454 COM_errorLog('Language "' . $language . '" not found in $_CONF[\'language_files\'] array!');
6456 $lang_id = ''; // not much we can do here ...
6464 * Return SQL expression to request language-specific content
6466 * Creates part of an SQL expression that can be used to request items in the
6467 * current language only.
6469 * @param string $field name of the "id" field, e.g. 'sid' for stories
6470 * @param string $type part of the SQL expression, e.g. 'WHERE', 'AND'
6471 * @param string $table table name if ambiguous, e.g. in JOINs
6472 * @return string SQL expression string (may be empty)
6475 function COM_getLangSQL( $field, $type = 'WHERE', $table = '' )
6481 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6483 if( !empty( $table ))
6488 $lang_id = COM_getLanguageId();
6490 if( !empty( $lang_id ))
6492 $sql = ' ' . $type . " ({$table}$field LIKE '%\\_$lang_id')";
6500 * Provide a block to switch languages
6502 * Provides a drop-down menu (or simple link, if you only have two languages)
6503 * to switch languages. This can be used as a PHP block or called from within
6504 * your theme's header.thtml:
6506 * <?php print phpblock_switch_language(); ?>
6509 * @return string HTML for drop-down or link to switch languages
6512 function phpblock_switch_language()
6518 if( empty( $_CONF['languages'] ) || empty( $_CONF['language_files'] ) ||
6519 ( count( $_CONF['languages'] ) != count( $_CONF['language_files'] )))
6524 $lang = COM_getLanguage();
6525 $langId = COM_getLanguageId( $lang );
6527 if( count( $_CONF['languages'] ) == 2 )
6529 foreach( $_CONF['languages'] as $key => $value )
6531 if( $key != $langId )
6539 $switchUrl = COM_buildUrl( $_CONF['site_url'] . '/switchlang.php?lang='
6541 $retval .= COM_createLink($newLang, $switchUrl);
6545 $retval .= '<form name="change" action="'. $_CONF['site_url']
6546 . '/switchlang.php" method="get">' . LB;
6547 $retval .= '<input type="hidden" name="oldlang" value="' . $langId
6548 . '"' . XHTML . '>' . LB;
6550 $retval .= '<select onchange="change.submit()" name="lang">';
6551 foreach( $_CONF['languages'] as $key => $value )
6553 if( $lang == $_CONF['language_files'][$key] )
6555 $selected = ' selected="selected"';
6561 $retval .= '<option value="' . $key . '"' . $selected . '>'
6562 . $value . '</option>' . LB;
6564 $retval .= '</select>' . LB;
6565 $retval .= '</form>' . LB;
6572 * Switch locale settings
6574 * When multi-language support is enabled, allow overwriting the default locale
6575 * settings with language-specific settings (date format, etc.). So in addition
6576 * to $_CONF['date'] you can have a $_CONF['date_en'], $_CONF['date_de'], etc.
6579 function COM_switchLocaleSettings()
6583 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6585 $overridables = array
6588 'date', 'daytime', 'shortdate', 'dateonly', 'timeonly',
6589 'week_start', 'hour_mode',
6590 'thousand_separator', 'decimal_separator'
6593 $langId = COM_getLanguageId();
6594 foreach( $overridables as $option )
6596 if( isset( $_CONF[$option . '_' . $langId] ))
6598 $_CONF[$option] = $_CONF[$option . '_' . $langId];
6605 * Get the name of the current language, minus the character set
6607 * Strips the character set from $_CONF['language'].
6609 * @return string language name
6612 function COM_getLanguageName()
6618 $charset = '_' . strtolower(COM_getCharset());
6619 if (substr($_CONF['language'], -strlen($charset)) == $charset) {
6620 $retval = substr($_CONF['language'], 0, -strlen($charset));
6622 $retval = $_CONF['language'];
6631 * Truncates a string to a max. length and optionally adds a filler string,
6632 * e.g. '...', to indicate the truncation.
6633 * This function is multi-byte string aware, based on a patch by Yusuke Sakata.
6635 * NOTE: The truncated string may be shorter but will never be longer than
6636 * $maxlen characters, i.e. the $filler string is taken into account.
6638 * @param string $text the text string to truncate
6639 * @param int $maxlen max. number of characters in the truncated string
6640 * @param string $filler optional filler string, e.g. '...'
6641 * @param int $endchars number of characters to show after the filler
6642 * @return string truncated string
6645 function COM_truncate( $text, $maxlen, $filler = '', $endchars = 0 )
6647 $newlen = $maxlen - MBYTE_strlen( $filler );
6648 $len = MBYTE_strlen( $text );
6649 if( $len > $maxlen )
6651 $text = MBYTE_substr( $text, 0, $newlen - $endchars ) . $filler . MBYTE_substr( $text, $len - $endchars, $endchars );
6658 * Get the current character set
6660 * Uses (if available, and in this order)
6661 * - $LANG_CHARSET (from the current language file)
6662 * - $_CONF['default_charset'] (from siteconfig.php)
6663 * - 'iso-8859-1' (hard-coded fallback)
6665 * @return string character set, e.g. 'utf-8'
6668 function COM_getCharset()
6670 global $_CONF, $LANG_CHARSET;
6672 if( empty( $LANG_CHARSET )) {
6673 $charset = $_CONF['default_charset'];
6674 if( empty( $charset )) {
6675 $charset = 'iso-8859-1';
6678 $charset = $LANG_CHARSET;
6687 * This function will handle all PHP errors thrown at it, without exposing
6688 * paths, and hopefully, providing much more information to Root Users than
6689 * the default white error page.
6691 * This function will call out to CUSTOM_handleError if it exists, but, be
6692 * advised, only override this function with a very, very stable function. I'd
6693 * suggest one that outputs some static, basic HTML.
6695 * The PHP feature that allows us to do so is documented here:
6696 * http://uk2.php.net/manual/en/function.set-error-handler.php
6698 * @param int $errno Error Number.
6699 * @param string $errstr Error Message.
6700 * @param string $errfile The file the error was raised in.
6701 * @param int $errline The line of the file that the error was raised at.
6702 * @param array $errcontext An array that points to the active symbol table at the point the error occurred.
6704 function COM_handleError($errno, $errstr, $errfile='', $errline=0, $errcontext='')
6706 global $_CONF, $_USER;
6708 // Handle @ operator
6709 if (error_reporting() == 0) {
6713 // If in PHP4, then respect error_reporting
6714 if ((PHP_VERSION < 5) && (($errno & error_reporting()) == 0)) {
6719 * If we have a root user, then output detailed error message:
6721 if ((is_array($_USER) && function_exists('SEC_inGroup'))
6722 || (isset($_CONF['rootdebug']) && $_CONF['rootdebug'])) {
6723 if ($_CONF['rootdebug'] || SEC_inGroup('Root')) {
6725 header('HTTP/1.1 500 Internal Server Error');
6726 header('Status: 500 Internal Server Error');
6728 $title = 'An Error Occurred';
6729 if (!empty($_CONF['site_name'])) {
6730 $title = $_CONF['site_name'] . ' - ' . $title;
6732 echo("<html><head><title>$title</title></head>\n<body>\n");
6734 echo('<h1>An error has occurred:</h1>');
6735 if ($_CONF['rootdebug']) {
6736 echo('<h2 style="color: red">This is being displayed as "Root Debugging" is enabled
6737 in your Geeklog configuration.</h2><p>If this is a production
6738 website you <strong><em>must disable</em></strong> this
6739 option once you have resolved any issues you are
6740 investigating.</p>');
6742 echo('<p>(This text is only displayed to users in the group \'Root\')</p>');
6744 echo("<p>$errno - $errstr @ $errfile line $errline</p>");
6746 if (!function_exists('SEC_inGroup') || !SEC_inGroup('Root')) {
6747 if ('force' != ''.$_CONF['rootdebug']) {
6748 $errcontext = COM_rootDebugClean($errcontext);
6750 echo('<h2 style="color: red">Root Debug is set to "force", this
6751 means that passwords and session cookies are exposed in this
6757 var_dump($errcontext);
6758 $errcontext = htmlspecialchars(ob_get_contents());
6760 echo("$errcontext</pre></body></html>");
6765 /* If there is a custom error handler, fail over to that, but only
6766 * if the error wasn't in lib-custom.php
6768 if (is_array($_CONF) && !(strstr($errfile, 'lib-custom.php'))) {
6769 if (array_key_exists('path_system', $_CONF)) {
6770 if (file_exists($_CONF['path_system'] . 'lib-custom.php')) {
6771 require_once $_CONF['path_system'] . 'lib-custom.php';
6773 if (function_exists('CUSTOM_handleError')) {
6774 CUSTOM_handleError($errno, $errstr, $errfile, $errline, $errcontext);
6780 // if we do not throw the error back to an admin, still log it in the error.log
6781 COM_errorLog("$errno - $errstr @ $errfile line $errline", 1);
6783 header('HTTP/1.1 500 Internal Server Error');
6784 header('Status: 500 Internal Server Error');
6786 // Does the theme implement an error message html file?
6787 if (!empty($_CONF['path_layout']) &&
6788 file_exists($_CONF['path_layout'] . 'errormessage.html')) {
6789 // NOTE: NOT A TEMPLATE! JUST HTML!
6790 include $_CONF['path_layout'] . 'errormessage.html';
6792 // Otherwise, display simple error message
6793 $title = 'An Error Occurred';
6794 if (!empty($_CONF['site_name'])) {
6795 $title = $_CONF['site_name'] . ' - ' . $title;
6800 <title>{$title}</title>
6803 <div style=\"width: 100%; text-align: center;\">
6804 Unfortunately, an error has occurred rendering this page. Please try
6816 * Recurse through the error context array removing/blanking password/cookie
6817 * values in case the "for development" only switch is left on in a production
6820 * [Not fit for public consumption comments about what users who enable root
6821 * debug in production should have done to them, and why making this change
6822 * defeats the point of the entire root debug feature go here.]
6824 * @param array $array Array of state info (Recursive array).
6825 * @param boolean $blank override (wouldn't that blank out everything?)
6826 * @return array Cleaned array
6828 function COM_rootDebugClean($array, $blank=false)
6830 $blankField = false;
6831 while(list($key, $value) = each($array)) {
6832 $lkey = strtolower($key);
6833 if((strpos($lkey, 'pass') !== false) || (strpos($lkey, 'cookie') !== false)) {
6836 $blankField = $blank;
6838 if(is_array($value)) {
6839 $array[$key] = COM_rootDebugClean($value, $blankField);
6840 } elseif($blankField) {
6841 $array[$key] = '[VALUE REMOVED]';
6848 * Checks to see if a specified user, or the current user if non-specified
6849 * is the anonymous user.
6851 * @param int $uid ID of the user to check, or none for the current user.
6852 * @return boolean true if the user is the anonymous user.
6854 function COM_isAnonUser($uid = '')
6858 /* If no user was specified, fail over to the current user if there is one */
6861 if( isset( $_USER['uid'] ) )
6863 $uid = $_USER['uid'];
6867 if( !empty( $uid ) )
6876 * Create Meta Tags to be used by COM_siteHeader in the headercode variable
6878 * @param string $meta_description the text for the meta description of the page being displayed
6879 * @param string $meta_keywords the text for the meta keywords of the page being displayed
6880 * @return string XHTML formatted text
6883 function COM_createMetaTags($meta_description, $meta_keywords)
6889 If ($_CONF['meta_tags'] > 0) {
6890 if ($meta_description != '') {
6891 $headercode .= LB . '<meta name="description" content="' . $meta_description . '"' . XHTML . '>';
6893 if ($meta_keywords != '') {
6894 $headercode .= LB . '<meta name="keywords" content="' . $meta_keywords . '"' . XHTML . '>';
6904 * Convert wiki-formatted text to (X)HTML
6906 * @param string $wikitext wiki-formatted text
6907 * @return string XHTML formatted text
6910 function COM_renderWikiText($wikitext)
6914 if (!$_CONF['wikitext_editor']) {
6918 require_once 'Text/Wiki.php';
6920 $wiki = new Text_Wiki();
6921 $wiki->setFormatConf('Xhtml', 'translate', HTML_SPECIALCHARS);
6922 $wiki->setRenderConf('Xhtml', 'charset', COM_getCharset());
6923 $wiki->disableRule('wikilink');
6924 $wiki->disableRule('freelink');
6925 $wiki->disableRule('interwiki');
6927 return $wiki->transform($wikitext, 'Xhtml');
6931 * Set the {lang_id} and {lang_attribute} variables for a template
6933 * NOTE: {lang_attribute} is only set in multi-language environments.
6935 * @param ref &$template template to use
6939 function COM_setLangIdAndAttribute(&$template)
6946 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6947 $langId = COM_getLanguageId();
6949 // try to derive the language id from the locale
6950 $l = explode('.', $_CONF['locale']); // get rid of character set
6952 $l = explode('@', $langId); // get rid of '@euro', etc.
6956 if (!empty($langId)) {
6957 $l = explode('-', str_replace('_', '-', $langId));
6958 if ((count($l) == 1) && (strlen($langId) == 2)) {
6959 $langAttr = 'lang="' . $langId . '"';
6960 } else if (count($l) == 2) {
6961 if (($l[0] == 'i') || ($l[0] == 'x')) {
6962 $langId = implode('-', $l);
6963 $langAttr = 'lang="' . $langId . '"';
6964 } else if (strlen($l[0]) == 2) {
6965 $langId = implode('-', $l);
6966 $langAttr = 'lang="' . $langId . '"';
6969 // this isn't a valid lang attribute, so don't set $langAttr
6973 $template->set_var('lang_id', $langId);
6975 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6976 $template->set_var('lang_attribute', ' ' . $langAttr);
6978 $template->set_var('lang_attribute', '');
6983 * Sends compressed output to browser.
6985 * Assumes that $display contains the _entire_ output for a request - no
6986 * echoes are allowed before or after this function.
6987 * Currently only supports gzip compression. Checks if zlib compression is
6988 * enabled in PHP and does uncompressed output if it is.
6990 * @param string $display Content to send to browser
6994 function COM_output($display)
6998 if (empty($display)) {
7002 if ($_CONF['compressed_output']) {
7003 $gzip_accepted = false;
7004 if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
7005 $enc = str_replace(' ', '', $_SERVER['HTTP_ACCEPT_ENCODING']);
7006 $accept = explode(',', strtolower($enc));
7007 $gzip_accepted = in_array('gzip', $accept);
7010 if ($gzip_accepted && function_exists('gzencode')) {
7012 $zlib_comp = ini_get('zlib.output_compression');
7013 if (empty($zlib_comp) || (strcasecmp($zlib_comp, 'off') == 0)) {
7015 header('Content-encoding: gzip');
7016 echo gzencode($display);
7027 * Turn a piece of HTML into continuous(!) plain text
7029 * This function removes HTML tags, line breaks, etc. and returns one long
7030 * line of text. This is useful for word counts (do an explode() on the result)
7031 * and for text excerpts.
7033 * @param string $text original text, including HTML and line breaks
7034 * @return string continuous plain text
7037 function COM_getTextContent($text)
7039 // replace <br> with spaces so that Text<br>Text becomes two words
7040 $text = preg_replace('/\<br(\s*)?\/?\>/i', ' ', $text);
7042 // add extra space between tags, e.g. <p>Text</p><p>Text</p>
7043 $text = str_replace('><', '> <', $text);
7045 // only now remove all HTML tags
7046 $text = strip_tags($text);
7048 // replace all tabs, newlines, and carrriage returns with spaces
7049 $text = str_replace(array("\011", "\012", "\015"), ' ', $text);
7051 // replace entities with plain spaces
7052 $text = str_replace(array('', ' ', ' '), ' ', $text);
7054 // collapse whitespace
7055 $text = preg_replace('/\s\s+/', ' ', $text);
7061 * Now include all plugin functions
7063 foreach ($_PLUGINS as $pi_name) {
7064 require_once $_CONF['path'] . 'plugins/' . $pi_name . '/functions.inc';
7067 // Check and see if any plugins (or custom functions)
7068 // have scheduled tasks to perform
7069 if ($_CONF['cron_schedule_interval'] > 0) {
7070 if ((DB_getItem($_TABLES['vars'], 'value', "name='last_scheduled_run'")
7071 + $_CONF['cron_schedule_interval']) <= time()) {
7072 DB_query("UPDATE {$_TABLES['vars']} SET value=UNIX_TIMESTAMP() WHERE name='last_scheduled_run'");
7073 PLG_runScheduledTask();