When a plugin returns 0 items for the Admins Block, don't display that as 'N/A' (bug #0001025)
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');
134 // +---------------------------------------------------------------------------+
135 // | Library Includes: You shouldn't have to touch anything below here |
136 // +---------------------------------------------------------------------------+
139 * If needed, add our PEAR path to the list of include paths
142 if (! $_CONF['have_pear']) {
143 $curPHPIncludePath = get_include_path();
144 if (empty($curPHPIncludePath)) {
145 $curPHPIncludePath = $_CONF['path_pear'];
147 $curPHPIncludePath = $_CONF['path_pear'] . PATH_SEPARATOR
148 . $curPHPIncludePath;
151 if (set_include_path($curPHPIncludePath) === false) {
152 COM_errorLog('set_include_path failed - there may be problems using the PEAR classes.', 1);
157 * Set the webserver's timezone
160 require_once $_CONF['path_system'] . 'classes/timezoneconfig.class.php';
161 TimeZoneConfig::setSystemTimeZone();
164 * Include plugin class.
165 * This is a poorly implemented class that was not very well thought out.
166 * Still very necessary
170 require_once( $_CONF['path_system'] . 'lib-plugins.php' );
173 * Include page time -- used to time how fast each page was created
177 require_once( $_CONF['path_system'] . 'classes/timer.class.php' );
178 $_PAGE_TIMER = new timerobject();
179 $_PAGE_TIMER->startTimer();
184 * This provides optional URL rewriting functionality.
187 require_once( $_CONF['path_system'] . 'classes/url.class.php' );
188 $_URL = new url( $_CONF['url_rewrite'] );
191 * This is our HTML template class. It is the same one found in PHPLib and is
192 * licensed under the LGPL. See that file for details.
196 require_once( $_CONF['path_system'] . 'classes/template.class.php' );
199 * This is the security library used for application security
203 require_once( $_CONF['path_system'] . 'lib-security.php' );
206 * This is the syndication library used to offer (RSS) feeds.
210 require_once( $_CONF['path_system'] . 'lib-syndication.php' );
213 *These variables were taken out of the configuration and placed here since they
214 *are necessary to change with the themes, not whole sites. They should now be
215 *overridden by setting them to a different value than here in the theme's
216 *function.php or in lib-custom.php. Therefore they are NOT TO BE CHANGED HERE.
218 $_CONF['left_blocks_in_footer'] = 0; // use left blocks in header
219 $_CONF['right_blocks_in_footer'] = 1; // use right blocks in footer
222 * This is the custom library.
224 * It is the sandbox for every Geeklog Admin to play in.
225 * The lib-custom.php as shipped will never contain required code,
226 * so it's safe to always use your own copy.
227 * This should hold all custom hacks to make upgrading easier.
231 require_once( $_CONF['path_system'] . 'lib-custom.php' );
234 * Session management library
238 require_once( $_CONF['path_system'] . 'lib-sessions.php' );
239 TimeZoneConfig::setUserTimeZone();
242 * Ulf Harnhammar's kses class
246 require_once( $_CONF['path_system'] . 'classes/kses.class.php' );
249 * Multibyte functions
252 require_once( $_CONF['path_system'] . 'lib-mbyte.php' );
257 if( isset( $_POST['usetheme'] ))
259 $usetheme = COM_sanitizeFilename($_POST['usetheme'], true);
261 if( !empty( $usetheme ) && is_dir( $_CONF['path_themes'] . $usetheme ))
263 $_CONF['theme'] = $usetheme;
264 $_CONF['path_layout'] = $_CONF['path_themes'] . $_CONF['theme'] . '/';
265 $_CONF['layout_url'] = $_CONF['site_url'] . '/layout/' . $_CONF['theme'];
267 else if( $_CONF['allow_user_themes'] == 1 )
269 if( isset( $_COOKIE[$_CONF['cookie_theme']] ) && empty( $_USER['theme'] ))
271 $theme = COM_sanitizeFilename($_COOKIE[$_CONF['cookie_theme']], true);
272 if( is_dir( $_CONF['path_themes'] . $theme ))
274 $_USER['theme'] = $theme;
278 if( !empty( $_USER['theme'] ))
280 if( is_dir( $_CONF['path_themes'] . $_USER['theme'] ))
282 $_CONF['theme'] = $_USER['theme'];
283 $_CONF['path_layout'] = $_CONF['path_themes'] . $_CONF['theme'] . '/';
284 $_CONF['layout_url'] = $_CONF['site_url'] . '/layout/' . $_CONF['theme'];
288 $_USER['theme'] = $_CONF['theme'];
294 * Include theme functions file
297 // Include theme functions file which may/may not do anything
299 if (file_exists($_CONF['path_layout'] . 'functions.php')) {
300 require_once $_CONF['path_layout'] . 'functions.php';
304 * ensure XHTML constant is defined to avoid problems elsewhere
306 if (!defined('XHTML')) {
307 switch ($_CONF['doctype']) {
308 case 'xhtml10transitional':
309 case 'xhtml10strict':
310 define('XHTML', ' /');
322 // themes can now specify the default image type
323 // fall back to 'gif' if they don't
325 if (empty($_IMAGE_TYPE)) {
326 $_IMAGE_TYPE = 'gif';
329 // Similarly set language
331 if( isset( $_COOKIE[$_CONF['cookie_language']] ) && empty( $_USER['language'] ))
333 $language = COM_sanitizeFilename($_COOKIE[$_CONF['cookie_language']]);
334 if( is_file( $_CONF['path_language'] . $language . '.php' ) &&
335 ( $_CONF['allow_user_language'] == 1 ))
337 $_USER['language'] = $language;
338 $_CONF['language'] = $language;
341 else if( !empty( $_USER['language'] ))
343 if( is_file( $_CONF['path_language'] . $_USER['language'] . '.php' ) &&
344 ( $_CONF['allow_user_language'] == 1 ))
346 $_CONF['language'] = $_USER['language'];
349 else if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
351 $_CONF['language'] = COM_getLanguage();
354 // Handle Who's Online block
355 if (COM_isAnonUser() && isset($_SERVER['REMOTE_ADDR'])) {
356 // The following code handles anonymous users so they show up properly
357 DB_delete($_TABLES['sessions'], array('remote_ip', 'uid'),
358 array($_SERVER['REMOTE_ADDR'], 1));
363 // Build a useless sess_id (needed for insert to work properly)
364 $sess_id = mt_rand();
367 // Insert anonymous user session
368 $result = DB_query( "INSERT INTO {$_TABLES['sessions']} (sess_id, start_time, remote_ip, uid) VALUES ($sess_id, $curtime, '{$_SERVER['REMOTE_ADDR']}', 1)", 1 );
371 while(( $result === false) && ( $tries < 5 ));
374 // Clear out any expired sessions
375 DB_query( "DELETE FROM {$_TABLES['sessions']} WHERE start_time < " . ( time() - $_CONF['whosonline_threshold'] ));
383 require_once $_CONF['path_language'] . $_CONF['language'] . '.php';
385 if (empty($LANG_DIRECTION)) {
386 // default to left-to-right
387 $LANG_DIRECTION = 'ltr';
390 COM_switchLocaleSettings();
392 if( setlocale( LC_ALL, $_CONF['locale'] ) === false )
394 setlocale( LC_TIME, $_CONF['locale'] );
398 * Global array of groups current user belongs to
400 * @global array $_GROUPS
404 if( !COM_isAnonUser() )
406 $_GROUPS = SEC_getUserGroups( $_USER['uid'] );
410 $_GROUPS = SEC_getUserGroups( 1 );
414 * Global array of current user permissions [read,edit]
416 * @global array $_RIGHTS
420 $_RIGHTS = explode( ',', SEC_getUserPermissions() );
422 if( isset( $_GET['topic'] ))
424 $topic = COM_applyFilter( $_GET['topic'] );
426 else if( isset( $_POST['topic'] ))
428 $topic = COM_applyFilter( $_POST['topic'] );
436 // +---------------------------------------------------------------------------+
438 // +---------------------------------------------------------------------------+
441 * Return the file to use for a block template.
443 * This returns the template needed to build the HTML for a block. This function
444 * allows designers to give a block it's own custom look and feel. If no
445 * templates for the block are specified, the default blockheader.html and
446 * blockfooter.html will be used.
448 * @param string $blockname corresponds to name field in block table
449 * @param string $which can be either 'header' or 'footer' for corresponding template
450 * @param string $position can be 'left', 'right' or blank. If set, will be used to find a side specific override template.
451 * @see function COM_startBlock
452 * @see function COM_endBlock
453 * @see function COM_showBlocks
454 * @see function COM_showBlock
455 * @return string template name
457 function COM_getBlockTemplate( $blockname, $which, $position='' )
459 global $_BLOCK_TEMPLATE, $_COM_VERBOSE, $_CONF;
463 COM_errorLog( "_BLOCK_TEMPLATE[$blockname] = " . $_BLOCK_TEMPLATE[$blockname], 1 );
466 if( !empty( $_BLOCK_TEMPLATE[$blockname] ))
468 $templates = explode( ',', $_BLOCK_TEMPLATE[$blockname] );
469 if( $which == 'header' )
471 if( !empty( $templates[0] ))
473 $template = $templates[0];
477 $template = 'blockheader.thtml';
482 if( !empty( $templates[1] ))
484 $template = $templates[1];
488 $template = 'blockfooter.thtml';
494 if( $which == 'header' )
496 $template = 'blockheader.thtml';
500 $template = 'blockfooter.thtml';
504 // If we have a position specific request, and the template is not already
505 // position specific then look to see if there is a position specific
507 $templateLC = strtolower($template);
508 if( !empty($position) && ( strpos($templateLC, $position) === false ) )
510 // Trim .thtml from the end.
511 $positionSpecific = substr($template, 0, strlen($template) - 6);
512 $positionSpecific .= '-' . $position . '.thtml';
513 if( file_exists( $_CONF['path_layout'] . $positionSpecific ) )
515 $template = $positionSpecific;
521 COM_errorLog( "Block template for the $which of $blockname is: $template", 1 );
528 * Gets all installed themes
530 * Returns a list of all the directory names in $_CONF['path_themes'], i.e.
531 * a list of all the theme names.
533 * @param boolean $all if true, return all themes even if users aren't allowed to change their default themes
534 * @return array All installed themes
537 function COM_getThemes( $all = false )
545 // If users aren't allowed to change their theme then only return the default theme
547 if(( $_CONF['allow_user_themes'] == 0 ) && !$all )
549 $themes[$index] = $_CONF['theme'];
553 $fd = opendir( $_CONF['path_themes'] );
555 while(( $dir = @readdir( $fd )) == TRUE )
557 if( is_dir( $_CONF['path_themes'] . $dir) && $dir <> '.' && $dir <> '..' && $dir <> 'CVS' && substr( $dir, 0 , 1 ) <> '.' )
560 $themes[$index] = $dir;
570 * Create the menu, i.e. replace {menu_elements} in the site header with the
571 * actual menu entries.
573 * @param Template &$header reference to the header template
574 * @param array $plugin_menu array of plugin menu entries, if any
577 function COM_renderMenu( &$header, $plugin_menu )
579 global $_CONF, $_USER, $LANG01, $topic;
581 if( empty( $_CONF['menu_elements'] ))
583 $_CONF['menu_elements'] = array( // default set of links
584 'contribute', 'search', 'stats', 'directory', 'plugins' );
587 $anon = COM_isAnonUser();
592 $num_plugins = count( $plugin_menu );
593 if( ( $num_plugins == 0 ) && in_array( 'plugins', $_CONF['menu_elements'] ))
595 $key = array_search( 'plugins', $_CONF['menu_elements'] );
596 unset( $_CONF['menu_elements'][$key] );
599 if( in_array( 'custom', $_CONF['menu_elements'] ))
601 $custom_entries = array();
602 if( function_exists( 'CUSTOM_menuEntries' ))
604 $custom_entries = CUSTOM_menuEntries();
606 if( count( $custom_entries ) == 0 )
608 $key = array_search( 'custom', $_CONF['menu_elements'] );
609 unset( $_CONF['menu_elements'][$key] );
613 $num_elements = count( $_CONF['menu_elements'] );
615 foreach( $_CONF['menu_elements'] as $item )
619 $last_entry = ( $counter == $num_elements ) ? true : false;
626 $url = $_CONF['site_url'] . '/submit.php?type=story';
627 $header->set_var( 'current_topic', '' );
631 $url = $_CONF['site_url']
632 . '/submit.php?type=story&topic=' . $topic;
633 $header->set_var( 'current_topic', '&topic=' . $topic );
635 $label = $LANG01[71];
636 if( $anon && ( $_CONF['loginrequired'] ||
637 $_CONF['submitloginrequired'] ))
644 if (function_exists('CUSTOM_renderMenu')) {
645 CUSTOM_renderMenu($header, $custom_entries, $menuCounter);
648 $custom_size = count($custom_entries);
649 foreach ($custom_entries as $entry) {
652 if (empty($entry['url']) || empty($entry['label'])) {
656 $header->set_var('menuitem_url', $entry['url']);
657 $header->set_var('menuitem_text', $entry['label']);
659 if ($last_entry && ($custom_count == $custom_size)) {
660 $header->parse('menu_elements', 'menuitem_last',
663 $header->parse('menu_elements', 'menuitem', true);
673 $url = $_CONF['site_url'] . '/directory.php';
674 if( !empty( $topic ))
676 $url = COM_buildUrl( $url . '?topic='
677 . urlencode( $topic ));
679 $label = $LANG01[117];
680 if( $anon && ( $_CONF['loginrequired'] ||
681 $_CONF['directoryloginrequired'] ))
688 $url = $_CONF['site_url'] . '/';
689 $label = $LANG01[90];
693 for( $i = 1; $i <= $num_plugins; $i++ )
695 $header->set_var( 'menuitem_url', current( $plugin_menu ));
696 $header->set_var( 'menuitem_text', key( $plugin_menu ));
698 if( $last_entry && ( $i == $num_plugins ))
700 $header->parse( 'menu_elements', 'menuitem_last',
705 $header->parse( 'menu_elements', 'menuitem', true );
709 next( $plugin_menu );
716 $url = $_CONF['site_url'] . '/usersettings.php';
717 $label = $LANG01[48];
721 $url = $_CONF['site_url'] . '/search.php';
722 $label = $LANG01[75];
723 if( $anon && ( $_CONF['loginrequired'] ||
724 $_CONF['searchloginrequired'] ))
731 $url = $_CONF['site_url'] . '/stats.php';
732 $label = $LANG01[76];
734 ( $_CONF['loginrequired'] || $_CONF['statsloginrequired'] ))
740 default: // unknown entry
746 if( !empty( $url ) && !empty( $label ))
748 $header->set_var( 'menuitem_url', $url );
749 $header->set_var( 'menuitem_text', $label );
752 $header->parse( 'menu_elements', 'menuitem_last', true );
756 $header->parse( 'menu_elements', 'menuitem', true );
764 $header->parse( 'allowed_menu_elements', 'menuitem_last',
769 $header->parse( 'allowed_menu_elements', 'menuitem', true );
776 if( $menuCounter == 0 )
778 $header->parse( 'menu_elements', 'menuitem_none', true );
780 if( $allowedCounter == 0 )
782 $header->parse( 'allowed_menu_elements', 'menuitem_none', true );
787 * Returns the site header
789 * This loads the proper templates, does variable substitution and returns the
790 * HTML for the site header with or without blocks depending on the value of $what
794 * The two functions COM_siteHeader and COM_siteFooter provide the framework for
795 * page display in Geeklog. COM_siteHeader controls the display of the Header
796 * and left blocks and COM_siteFooter controls the dsiplay of the right blocks
797 * and the footer. You use them like a sandwich. Thus the following code will
798 * display a Geeklog page with both right and left blocks displayed.
802 * require_once 'lib-common.php';
803 * // Change to COM_siteHeader('none') to not display left blocks
804 * $display .= COM_siteHeader();
805 * $display .= "Here is your html for display";
806 * // Change to COM_siteFooter() to not display right blocks
807 * $display .= COM_siteFooter(true);
812 * Note that the default for the header is to display the left blocks and the
813 * default of the footer is to not display the right blocks.
815 * This sandwich produces code like this (greatly simplified)
818 * <table><tr><td colspan="3">Header</td></tr>
819 * <tr><td>Left Blocks</td><td>
821 * // Your HTML goes here
822 * Here is your html for display
825 * </td><td>Right Blocks</td></tr>
826 * <tr><td colspan="3">Footer</td></table>
829 * @param string $what If 'none' then no left blocks are returned, if 'menu' (default) then right blocks are returned
830 * @param string $pagetitle optional content for the page's <title>
831 * @param string $headercode optional code to go into the page's <head>
832 * @return string Formatted HTML containing the site header
833 * @see function COM_siteFooter
836 function COM_siteHeader( $what = 'menu', $pagetitle = '', $headercode = '' )
838 global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_BUTTONS, $LANG_DIRECTION,
839 $_IMAGE_TYPE, $topic, $_COM_VERBOSE;
841 // If the theme implemented this for us then call their version instead.
843 $function = $_CONF['theme'] . '_siteHeader';
845 if( function_exists( $function ))
847 return $function( $what, $pagetitle, $headercode );
850 // If we reach here then either we have the default theme OR
851 // the current theme only needs the default variable substitutions
853 switch ($_CONF['doctype']) {
854 case 'html401transitional':
855 $doctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
858 case 'html401strict':
859 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
862 case 'xhtml10transitional':
863 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
866 case 'xhtml10strict':
867 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
870 default: // fallback: HTML 4.01 Transitional w/o system identifier
871 $doctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">';
875 // send out the charset header
876 header('Content-Type: text/html; charset=' . COM_getCharset());
878 if (!empty($_CONF['frame_options'])) {
879 header('X-FRAME-OPTIONS: ' . $_CONF['frame_options']);
882 $header = new Template( $_CONF['path_layout'] );
883 $header->set_file( array(
884 'header' => 'header.thtml',
885 'menuitem' => 'menuitem.thtml',
886 'menuitem_last' => 'menuitem_last.thtml',
887 'menuitem_none' => 'menuitem_none.thtml',
888 'leftblocks' => 'leftblocks.thtml',
889 'rightblocks' => 'rightblocks.thtml'
891 $header->set_var('doctype', $doctype);
892 $header->set_var('xhtml', XHTML);
894 $header->set_var('xmlns', '');
896 $header->set_var('xmlns', ' xmlns="http://www.w3.org/1999/xhtml"');
899 // get topic if not on home page
900 if( !isset( $_GET['topic'] ))
902 if( isset( $_GET['story'] ))
904 $sid = COM_applyFilter( $_GET['story'] );
906 elseif( isset( $_GET['sid'] ))
908 $sid = COM_applyFilter( $_GET['sid'] );
910 elseif( isset( $_POST['story'] ))
912 $sid = COM_applyFilter( $_POST['story'] );
914 if( empty( $sid ) && $_CONF['url_rewrite'] &&
915 ( strpos( $_SERVER['PHP_SELF'], 'article.php' ) !== false ))
917 COM_setArgNames( array( 'story', 'mode' ));
918 $sid = COM_applyFilter( COM_getArgument( 'story' ));
922 $topic = DB_getItem( $_TABLES['stories'], 'tid', "sid='$sid'" );
927 $topic = COM_applyFilter( $_GET['topic'] );
931 if( $_CONF['backend'] == 1 ) // add feed-link to header if applicable
933 $baseurl = SYND_getFeedUrl();
935 $sql = 'SELECT format, filename, title, language FROM '
936 . $_TABLES['syndication'] . " WHERE (header_tid = 'all')";
937 if( !empty( $topic ))
939 $sql .= " OR (header_tid = '" . addslashes( $topic ) . "')";
941 $result = DB_query( $sql );
942 $numRows = DB_numRows( $result );
943 for( $i = 0; $i < $numRows; $i++ )
945 $A = DB_fetchArray( $result );
946 if ( !empty( $A['filename'] ))
948 $format = explode( '-', $A['format'] );
949 $format_type = strtolower( $format[0] );
950 $format_name = ucwords( $format[0] );
952 $feed_url[] = '<link rel="alternate" type="application/'
953 . $format_type . '+xml" hreflang="' . $A['language']
954 . '" href="' . $baseurl . $A['filename'] . '" title="'
955 . $format_name . ' Feed: ' . $A['title'] . '"' . XHTML . '>';
959 $header->set_var( 'feed_url', implode( LB, $feed_url ));
962 if( !COM_onFrontpage() )
964 $relLinks['home'] = '<link rel="home" href="' . $_CONF['site_url']
965 . '/" title="' . $LANG01[90] . '"' . XHTML . '>';
967 $loggedInUser = !COM_isAnonUser();
968 if( $loggedInUser || (( $_CONF['loginrequired'] == 0 ) &&
969 ( $_CONF['searchloginrequired'] == 0 )))
971 if(( substr( $_SERVER['PHP_SELF'], -strlen( '/search.php' ))
972 != '/search.php' ) || isset( $_GET['mode'] ))
974 $relLinks['search'] = '<link rel="search" href="'
975 . $_CONF['site_url'] . '/search.php" title="'
976 . $LANG01[75] . '"' . XHTML . '>';
979 if( $loggedInUser || (( $_CONF['loginrequired'] == 0 ) &&
980 ( $_CONF['directoryloginrequired'] == 0 )))
982 if( strpos( $_SERVER['PHP_SELF'], '/article.php' ) !== false ) {
983 $relLinks['contents'] = '<link rel="contents" href="'
984 . $_CONF['site_url'] . '/directory.php" title="'
985 . $LANG01[117] . '"' . XHTML . '>';
988 if (!$_CONF['disable_webservices']) {
989 $relLinks['service'] = '<link rel="service" '
990 . 'type="application/atomsvc+xml" ' . 'href="'
991 . $_CONF['site_url'] . '/webservices/atom/?introspection" '
992 . 'title="' . $LANG01[130] . '"' . XHTML . '>';
994 // TBD: add a plugin API and a lib-custom.php function
995 $header->set_var( 'rel_links', implode( LB, $relLinks ));
997 $pagetitle_siteslogan = false;
998 if( empty( $pagetitle ))
1000 if( empty( $topic ))
1002 $pagetitle = $_CONF['site_slogan'];
1003 $pagetitle_siteslogan = true;
1007 $pagetitle = stripslashes( DB_getItem( $_TABLES['topics'], 'topic',
1008 "tid = '$topic'" ));
1011 if( !empty( $pagetitle ))
1013 $header->set_var( 'page_site_splitter', ' - ');
1017 $header->set_var( 'page_site_splitter', '');
1019 $header->set_var( 'page_title', $pagetitle );
1020 $header->set_var( 'site_name', $_CONF['site_name']);
1022 if (COM_onFrontpage() OR $pagetitle_siteslogan) {
1023 $title_and_name = $_CONF['site_name'];
1024 if (!empty($pagetitle)) {
1025 $title_and_name .= ' - ' . $pagetitle;
1028 $title_and_name = '';
1029 if (!empty($pagetitle)) {
1030 $title_and_name = $pagetitle . ' - ';
1032 $title_and_name .= $_CONF['site_name'];
1034 $header->set_var('page_title_and_site_name', $title_and_name);
1036 COM_setLangIdAndAttribute($header);
1038 $header->set_var( 'background_image', $_CONF['layout_url']
1039 . '/images/bg.' . $_IMAGE_TYPE );
1040 $header->set_var( 'site_url', $_CONF['site_url'] );
1041 $header->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1042 $header->set_var( 'layout_url', $_CONF['layout_url'] );
1043 $header->set_var( 'site_mail', "mailto:{$_CONF['site_mail']}" );
1044 $header->set_var( 'site_name', $_CONF['site_name'] );
1045 $header->set_var( 'site_slogan', $_CONF['site_slogan'] );
1046 $rdf = substr_replace( $_CONF['rdf_file'], $_CONF['site_url'], 0,
1047 strlen( $_CONF['path_html'] ) - 1 );
1048 $header->set_var( 'rdf_file', $rdf );
1049 $header->set_var( 'rss_url', $rdf );
1051 $msg = rtrim($LANG01[67]) . ' ' . $_CONF['site_name'];
1053 if( !empty( $_USER['username'] ))
1055 $msg .= ', ' . COM_getDisplayName( $_USER['uid'], $_USER['username'],
1056 $_USER['fullname'] );
1059 $curtime = COM_getUserDateTimeFormat();
1061 $header->set_var( 'welcome_msg', $msg );
1062 $header->set_var( 'datetime', $curtime[0] );
1063 $header->set_var( 'site_logo', $_CONF['layout_url']
1064 . '/images/logo.' . $_IMAGE_TYPE );
1065 $header->set_var( 'css_url', $_CONF['layout_url'] . '/style.css' );
1066 $header->set_var( 'theme', $_CONF['theme'] );
1068 $header->set_var('charset', COM_getCharset());
1069 $header->set_var('direction', $LANG_DIRECTION);
1071 // Now add variables for buttons like e.g. those used by the Yahoo theme
1072 $header->set_var( 'button_home', $LANG_BUTTONS[1] );
1073 $header->set_var( 'button_contact', $LANG_BUTTONS[2] );
1074 $header->set_var( 'button_contribute', $LANG_BUTTONS[3] );
1075 $header->set_var( 'button_sitestats', $LANG_BUTTONS[7] );
1076 $header->set_var( 'button_personalize', $LANG_BUTTONS[8] );
1077 $header->set_var( 'button_search', $LANG_BUTTONS[9] );
1078 $header->set_var( 'button_advsearch', $LANG_BUTTONS[10] );
1079 $header->set_var( 'button_directory', $LANG_BUTTONS[11] );
1081 // Get plugin menu options
1082 $plugin_menu = PLG_getMenuItems();
1086 COM_errorLog( 'num plugin menu items in header = ' . count( $plugin_menu ), 1 );
1089 // Now add nested template for menu items
1090 COM_renderMenu( $header, $plugin_menu );
1092 if( count( $plugin_menu ) == 0 )
1094 $header->parse( 'plg_menu_elements', 'menuitem_none', true );
1098 $count_plugin_menu = count( $plugin_menu );
1099 for( $i = 1; $i <= $count_plugin_menu; $i++ )
1101 $header->set_var( 'menuitem_url', current( $plugin_menu ));
1102 $header->set_var( 'menuitem_text', key( $plugin_menu ));
1104 if( $i == $count_plugin_menu )
1106 $header->parse( 'plg_menu_elements', 'menuitem_last', true );
1110 $header->parse( 'plg_menu_elements', 'menuitem', true );
1113 next( $plugin_menu );
1117 // Call to plugins to set template variables in the header
1118 PLG_templateSetVars( 'header', $header );
1120 if( $_CONF['left_blocks_in_footer'] == 1 )
1122 $header->set_var( 'left_blocks', '' );
1123 $header->set_var( 'geeklog_blocks', '' );
1129 /* Check if an array has been passed that includes the name of a plugin
1130 * function or custom function
1131 * This can be used to take control over what blocks are then displayed
1133 if( is_array( $what ))
1135 $function = $what[0];
1136 if( function_exists( $function ))
1138 $lblocks = $function( $what[1], 'left' );
1142 $lblocks = COM_showBlocks( 'left', $topic );
1145 else if( $what <> 'none' )
1147 // Now show any blocks -- need to get the topic if not on home page
1148 $lblocks = COM_showBlocks( 'left', $topic );
1151 if( empty( $lblocks ))
1153 $header->set_var( 'left_blocks', '' );
1154 $header->set_var( 'geeklog_blocks', '' );
1158 $header->set_var( 'geeklog_blocks', $lblocks );
1159 $header->parse( 'left_blocks', 'leftblocks', true );
1160 $header->set_var( 'geeklog_blocks', '');
1164 if( $_CONF['right_blocks_in_footer'] == 1 )
1166 $header->set_var( 'right_blocks', '' );
1167 $header->set_var( 'geeklog_blocks', '' );
1173 /* Check if an array has been passed that includes the name of a plugin
1174 * function or custom function
1175 * This can be used to take control over what blocks are then displayed
1177 if( is_array( $what ))
1179 $function = $what[0];
1180 if( function_exists( $function ))
1182 $rblocks = $function( $what[1], 'right' );
1186 $rblocks = COM_showBlocks( 'right', $topic );
1189 else if( $what <> 'none' )
1191 // Now show any blocks -- need to get the topic if not on home page
1192 $rblocks = COM_showBlocks( 'right', $topic );
1195 if( empty( $rblocks ))
1197 $header->set_var( 'right_blocks', '' );
1198 $header->set_var( 'geeklog_blocks', '' );
1202 $header->set_var( 'geeklog_blocks', $rblocks, true );
1203 $header->parse( 'right_blocks', 'rightblocks', true );
1207 if( isset( $_CONF['advanced_editor'] ) && ( $_CONF['advanced_editor'] == 1 )
1208 && file_exists( $_CONF['path_layout']
1209 . 'advanced_editor_header.thtml' ))
1211 $header->set_file( 'editor' , 'advanced_editor_header.thtml');
1212 $header->parse( 'advanced_editor', 'editor' );
1217 $header->set_var( 'advanced_editor', '' );
1220 // Call any plugin that may want to include extra Meta tags
1221 // or Javascript functions
1222 $headercode .= PLG_getHeaderCode();
1225 // 0 = Disabled, 1 = Enabled, 2 = Enabled but default just for homepage
1226 if ($_CONF['meta_tags'] > 0) {
1227 $meta_description = '';
1228 $meta_keywords = '';
1229 $no_meta_description = 1;
1230 $no_meta_keywords = 1;
1232 //Find out if the meta tag description or keywords already exist in the headercode
1233 if ($headercode != '') {
1234 $pattern = '/<meta ([^>]*)name="([^"\'>]*)"([^>]*)/im';
1235 if (preg_match_all($pattern, $headercode, $matches, PREG_SET_ORDER)) {
1236 // Loop through all meta tags looking for description and keywords
1237 for ($i = 0; $i<count($matches) && (($no_meta_description == 1) || ($no_meta_keywords == 1)); $i++) {
1238 $str_matches = strtolower($matches[$i][0]);
1239 $pos = strpos($str_matches,'name=');
1240 if (!(is_bool($pos) && !$pos)) {
1241 $name = trim(substr($str_matches,$pos+5),'"');
1242 $pos = strpos($name,'"');
1243 $name = substr($name,0,$pos);
1245 if (strcasecmp("description",$name) == 0) {
1246 $pos = strpos($str_matches,'content=');
1247 if (!(is_bool($pos) && !$pos)) {
1248 $no_meta_description = 0;
1251 if (strcasecmp("keywords",$name) == 0) {
1252 $pos = strpos($str_matches,'content=');
1253 if (!(is_bool($pos) && !$pos)) {
1254 $no_meta_keywords = 0;
1263 If (COM_onFrontpage() && $_CONF['meta_tags'] == 2) { // Display default meta tags only on home page
1264 If ($no_meta_description) {
1265 $meta_description = $_CONF['meta_description'];
1267 If ($no_meta_keywords) {
1268 $meta_keywords = $_CONF['meta_keywords'];
1270 } else if ( $_CONF['meta_tags'] == 1 ) { // Display default meta tags anywhere there are no tags
1271 If ($no_meta_description) {
1272 $meta_description = $_CONF['meta_description'];
1274 If ($no_meta_keywords) {
1275 $meta_keywords = $_CONF['meta_keywords'];
1279 If ($no_meta_description OR $no_meta_keywords) {
1280 $headercode .= COM_createMetaTags($meta_description, $meta_keywords);
1284 $header->set_var( 'plg_headercode', $headercode );
1286 // The following lines allow users to embed PHP in their templates. This
1287 // is almost a contradition to the reasons for using templates but this may
1288 // prove useful at times ...
1289 // Don't use PHP in templates if you can live without it!
1291 $tmp = $header->finish($header->parse('index_header', 'header'));
1293 $xml_declaration = '';
1294 if ( get_cfg_var('short_open_tag') == '1' )
1296 if ( preg_match( '/(<\?xml[^>]*>)(.*)/s', $tmp, $match ) )
1298 $xml_declaration = $match[1] . LB;
1304 eval( '?>' . $tmp );
1305 $retval = $xml_declaration . ob_get_contents();
1313 * Returns the site footer
1315 * This loads the proper templates, does variable substitution and returns the
1316 * HTML for the site footer.
1318 * @param boolean $rightblock Whether or not to show blocks on right hand side default is no
1319 * @param array $custom An array defining custom function to be used to format Rightblocks
1320 * @see function COM_siteHeader
1321 * @return string Formated HTML containing site footer and optionally right blocks
1324 function COM_siteFooter( $rightblock = -1, $custom = '' )
1326 global $_CONF, $_TABLES, $LANG01, $_PAGE_TIMER, $topic, $LANG_BUTTONS;
1328 // If the theme implemented this for us then call their version instead.
1330 $function = $_CONF['theme'] . '_siteFooter';
1332 if( function_exists( $function ))
1334 return $function( $rightblock, $custom );
1339 // Set template directory
1340 $footer = new Template( $_CONF['path_layout'] );
1342 // Set template file
1343 $footer->set_file( array(
1344 'footer' => 'footer.thtml',
1345 'rightblocks' => 'rightblocks.thtml',
1346 'leftblocks' => 'leftblocks.thtml'
1349 // Do variable assignments
1350 $footer->set_var( 'xhtml', XHTML );
1351 $footer->set_var( 'site_url', $_CONF['site_url']);
1352 $footer->set_var( 'site_admin_url', $_CONF['site_admin_url']);
1353 $footer->set_var( 'layout_url',$_CONF['layout_url']);
1354 $footer->set_var( 'site_mail', "mailto:{$_CONF['site_mail']}" );
1355 $footer->set_var( 'site_name', $_CONF['site_name'] );
1356 $footer->set_var( 'site_slogan', $_CONF['site_slogan'] );
1357 $rdf = substr_replace( $_CONF['rdf_file'], $_CONF['site_url'], 0,
1358 strlen( $_CONF['path_html'] ) - 1 );
1359 $footer->set_var( 'rdf_file', $rdf );
1360 $footer->set_var( 'rss_url', $rdf );
1362 $year = date( 'Y' );
1363 $copyrightyear = $year;
1364 if( !empty( $_CONF['copyrightyear'] ))
1366 $copyrightyear = $_CONF['copyrightyear'];
1368 $footer->set_var( 'copyright_notice', ' ' . $LANG01[93] . ' © '
1369 . $copyrightyear . ' ' . $_CONF['site_name'] . '<br' . XHTML . '> '
1371 $footer->set_var( 'copyright_msg', $LANG01[93] . ' © '
1372 . $copyrightyear . ' ' . $_CONF['site_name'] );
1373 $footer->set_var( 'current_year', $year );
1374 $footer->set_var( 'lang_copyright', $LANG01[93] );
1375 $footer->set_var( 'trademark_msg', $LANG01[94] );
1376 $footer->set_var( 'powered_by', $LANG01[95] );
1377 $footer->set_var( 'geeklog_url', 'http://www.geeklog.net/' );
1378 $footer->set_var( 'geeklog_version', VERSION );
1379 // Now add variables for buttons like e.g. those used by the Yahoo theme
1380 $footer->set_var( 'button_home', $LANG_BUTTONS[1] );
1381 $footer->set_var( 'button_contact', $LANG_BUTTONS[2] );
1382 $footer->set_var( 'button_contribute', $LANG_BUTTONS[3] );
1383 $footer->set_var( 'button_sitestats', $LANG_BUTTONS[7] );
1384 $footer->set_var( 'button_personalize', $LANG_BUTTONS[8] );
1385 $footer->set_var( 'button_search', $LANG_BUTTONS[9] );
1386 $footer->set_var( 'button_advsearch', $LANG_BUTTONS[10] );
1387 $footer->set_var( 'button_directory', $LANG_BUTTONS[11] );
1389 /* Right blocks. Argh. Don't talk to me about right blocks...
1390 * Right blocks will be displayed if Right_blocks_in_footer is set [1],
1391 * AND (this function has been asked to show them (first param) OR the
1392 * show_right_blocks conf variable has been set to override what the code
1395 * If $custom sets an array (containing functionname and first argument)
1396 * then this is used instead of the default (COM_showBlocks) to render
1397 * the right blocks (and left).
1399 * [1] - if it isn't, they'll be in the header already.
1402 $displayRightBlocks = true;
1403 if ($_CONF['right_blocks_in_footer'] == 1)
1405 if( ($rightblock < 0) || !$rightblock )
1407 if( isset( $_CONF['show_right_blocks'] ) )
1409 $displayRightBlocks = $_CONF['show_right_blocks'];
1413 $displayRightBlocks = false;
1416 $displayRightBlocks = true;
1419 $displayRightBlocks = false;
1422 if ($displayRightBlocks)
1424 /* Check if an array has been passed that includes the name of a plugin
1425 * function or custom function.
1426 * This can be used to take control over what blocks are then displayed
1428 if( is_array( $custom ))
1430 $function = $custom['0'];
1431 if( function_exists( $function ))
1433 $rblocks = $function( $custom['1'], 'right' );
1435 $rblocks = COM_showBlocks( 'right', $topic );
1438 $rblocks = COM_showBlocks( 'right', $topic );
1441 if( empty( $rblocks ))
1443 $footer->set_var( 'geeklog_blocks', '');
1444 $footer->set_var( 'right_blocks', '' );
1446 $footer->set_var( 'geeklog_blocks', $rblocks);
1447 $footer->parse( 'right_blocks', 'rightblocks', true );
1448 $footer->set_var( 'geeklog_blocks', '');
1451 $footer->set_var( 'geeklog_blocks', '');
1452 $footer->set_var( 'right_blocks', '' );
1455 if( $_CONF['left_blocks_in_footer'] == 1 )
1459 /* Check if an array has been passed that includes the name of a plugin
1460 * function or custom function
1461 * This can be used to take control over what blocks are then displayed
1463 if( is_array( $custom ))
1465 $function = $custom[0];
1466 if( function_exists( $function ))
1468 $lblocks = $function( $custom[1], 'left' );
1473 $lblocks = COM_showBlocks( 'left', $topic );
1476 if( empty( $lblocks ))
1478 $footer->set_var( 'left_blocks', '' );
1479 $footer->set_var( 'geeklog_blocks', '');
1483 $footer->set_var( 'geeklog_blocks', $lblocks);
1484 $footer->parse( 'left_blocks', 'leftblocks', true );
1485 $footer->set_var( 'geeklog_blocks', '');
1489 // Global centerspan variable set in index.php
1490 if( isset( $GLOBALS['centerspan'] ))
1492 $footer->set_var( 'centerblockfooter-span', '</td></tr></table>' );
1495 $exectime = $_PAGE_TIMER->stopTimer();
1496 $exectext = $LANG01[91] . ' ' . $exectime . ' ' . $LANG01[92];
1498 $footer->set_var( 'execution_time', $exectime );
1499 $footer->set_var( 'execution_textandtime', $exectext );
1501 // Call to plugins to set template variables in the footer
1502 PLG_templateSetVars( 'footer', $footer );
1504 // Actually parse the template and make variable substitutions
1505 $footer->parse( 'index_footer', 'footer' );
1507 // Return resulting HTML
1508 return $footer->finish( $footer->get_var( 'index_footer' ));
1512 * Prints out standard block header
1514 * Prints out standard block header but pulling header HTML formatting from
1517 * Programming Note: The two functions COM_startBlock and COM_endBlock are used
1518 * to sandwich your block content. These functions are not used only for blocks
1519 * but anything that uses that format, e.g. Stats page. They are used like
1520 * COM_siteHeader and COM_siteFooter but for internal page elements.
1522 * @param string $title Value to set block title to
1523 * @param string $helpfile Help file, if one exists
1524 * @param string $template HTML template file to use to format the block
1525 * @return string Formatted HTML containing block header
1527 * @see COM_siteHeader
1531 function COM_startBlock( $title='', $helpfile='', $template='blockheader.thtml' )
1533 global $_CONF, $LANG01, $_IMAGE_TYPE;
1535 $block = new Template( $_CONF['path_layout'] );
1536 $block->set_file( 'block', $template );
1538 $block->set_var( 'xhtml', XHTML );
1539 $block->set_var( 'site_url', $_CONF['site_url'] );
1540 $block->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1541 $block->set_var( 'layout_url', $_CONF['layout_url'] );
1542 $block->set_var( 'block_title', stripslashes( $title ));
1544 if( !empty( $helpfile ))
1546 $helpimg = $_CONF['layout_url'] . '/images/button_help.' . $_IMAGE_TYPE;
1547 $help_content = '<img src="' . $helpimg. '" alt="?"' . XHTML . '>';
1548 $help_attr = array('class'=>'blocktitle');
1549 if( !stristr( $helpfile, 'http://' ))
1551 $help_url = $_CONF['site_url'] . "/help/$helpfile";
1555 $help_url = $helpfile;
1557 $help = COM_createLink($help_content, $help_url, $help_attr);
1558 $block->set_var( 'block_help', $help );
1561 $block->parse( 'startHTML', 'block' );
1563 return $block->finish( $block->get_var( 'startHTML' ));
1567 * Closes out COM_startBlock
1569 * @param string $template HTML template file used to format block footer
1570 * @return string Formatted HTML to close block
1571 * @see function COM_startBlock
1574 function COM_endBlock( $template='blockfooter.thtml' )
1578 $block = new Template( $_CONF['path_layout'] );
1579 $block->set_file( 'block', $template );
1581 $block->set_var( 'xhtml', XHTML );
1582 $block->set_var( 'site_url', $_CONF['site_url'] );
1583 $block->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1584 $block->set_var( 'layout_url', $_CONF['layout_url'] );
1585 $block->parse( 'endHTML', 'block' );
1587 return $block->finish( $block->get_var( 'endHTML' ));
1592 * Creates a <option> list from a database list for use in forms
1594 * Creates option list form field using given arguments
1596 * @param string $table Database Table to get data from
1597 * @param string $selection Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed. This is used in a SQL statement and can include DISTINCT to start.
1598 * @param string/array $selected Value (from $selection) to set to SELECTED or default
1599 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1600 * @param string $where Optional WHERE clause to use in the SQL Selection
1601 * @see function COM_checkList
1602 * @return string Formated HTML of option values
1605 function COM_optionList( $table, $selection, $selected='', $sortcol=1, $where='' )
1607 global $_DB_table_prefix;
1611 $LangTableName = '';
1612 if( substr( $table, 0, strlen( $_DB_table_prefix )) == $_DB_table_prefix )
1614 $LangTableName = 'LANG_' . substr( $table, strlen( $_DB_table_prefix ));
1618 $LangTableName = 'LANG_' . $table;
1621 global $$LangTableName;
1623 if( isset( $$LangTableName ))
1625 $LangTable = $$LangTableName;
1629 $LangTable = array();
1632 $tmp = str_replace( 'DISTINCT ', '', $selection );
1633 $select_set = explode( ',', $tmp );
1635 $sql = "SELECT $selection FROM $table";
1638 $sql .= " WHERE $where";
1640 $sql .= " ORDER BY {$select_set[$sortcol]}";
1641 $result = DB_query( $sql );
1642 $nrows = DB_numRows( $result );
1644 for( $i = 0; $i < $nrows; $i++ )
1646 $A = DB_fetchArray( $result, true );
1647 $retval .= '<option value="' . $A[0] . '"';
1649 if( is_array( $selected ) AND count( $selected ) > 0 )
1651 foreach( $selected as $selected_item )
1653 if( $A[0] == $selected_item )
1655 $retval .= ' selected="selected"';
1659 elseif( !is_array( $selected ) AND $A[0] == $selected )
1661 $retval .= ' selected="selected"';
1665 if( empty( $LangTable[$A[0]] ))
1671 $retval .= $LangTable[$A[0]];
1673 $retval .= '</option>' . LB;
1680 * Create and return a dropdown-list of available topics
1682 * This is a variation of COM_optionList() from lib-common.php. It will add
1683 * only those topics to the option list which are accessible by the current
1686 * @param string $selection Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed. This is used in a SQL statement and can include DISTINCT to start.
1687 * @param string $selected Value (from $selection) to set to SELECTED or default
1688 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1689 * @param boolean $ignorelang Whether to return all topics (true) or only the ones for the current language (false)
1690 * @see function COM_optionList
1691 * @return string Formated HTML of option values
1694 function COM_topicList( $selection, $selected = '', $sortcol = 1, $ignorelang = false )
1700 $topics = COM_topicArray($selection, $sortcol, $ignorelang);
1701 foreach ($topics as $tid => $topic) {
1702 $retval .= '<option value="' . $tid . '"';
1703 if ($tid == $selected) {
1704 $retval .= ' selected="selected"';
1706 $retval .= '>' . $topic . '</option>' . LB;
1713 * Return a list of topics in an array
1714 * (derived from COM_topicList - API may change)
1716 * @param string $selection Comma delimited string of fields to pull The first field is the value of the option and the second is the label to be displayed. This is used in a SQL statement and can include DISTINCT to start.
1717 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1718 * @param boolean $ignorelang Whether to return all topics (true) or only the ones for the current language (false)
1719 * @return array Array of topics
1720 * @see function COM_topicList
1723 function COM_topicArray($selection, $sortcol = 0, $ignorelang = false)
1729 $tmp = str_replace('DISTINCT ', '', $selection);
1730 $select_set = explode(',', $tmp);
1732 $sql = "SELECT $selection FROM {$_TABLES['topics']}";
1734 $sql .= COM_getPermSQL();
1736 $permsql = COM_getPermSQL();
1737 if (empty($permsql)) {
1738 $sql .= COM_getLangSQL('tid');
1740 $sql .= $permsql . COM_getLangSQL('tid', 'AND');
1743 $sql .= " ORDER BY $select_set[$sortcol]";
1745 $result = DB_query($sql);
1746 $nrows = DB_numRows($result);
1748 if (count($select_set) > 1) {
1749 for ($i = 0; $i < $nrows; $i++) {
1750 $A = DB_fetchArray($result, true);
1751 $retval[$A[0]] = stripslashes($A[1]);
1754 for ($i = 0; $i < $nrows; $i++) {
1755 $A = DB_fetchArray($result, true);
1764 * Creates a <input> checklist from a database list for use in forms
1766 * Creates a group of checkbox form fields with given arguments
1768 * @param string $table DB Table to pull data from
1769 * @param string $selection Comma delimited list of fields to pull from table
1770 * @param string $where Where clause of SQL statement
1771 * @param string $selected Value to set to CHECKED
1772 * @param string $fieldname Name to use for the checkbox array
1773 * @return string HTML with Checkbox code
1774 * @see COM_optionList
1777 function COM_checkList($table, $selection, $where = '', $selected = '', $fieldname = '')
1779 global $_TABLES, $_COM_VERBOSE;
1781 $sql = "SELECT $selection FROM $table";
1783 if( !empty( $where ))
1785 $sql .= " WHERE $where";
1788 $result = DB_query( $sql );
1789 $nrows = DB_numRows( $result );
1791 if( !empty( $selected ))
1795 COM_errorLog( "exploding selected array: $selected in COM_checkList", 1 );
1798 $S = explode( ' ', $selected );
1804 COM_errorLog( 'selected string was empty COM_checkList', 1 );
1809 $retval = '<ul class="checkboxes-list">' . LB;
1810 for( $i = 0; $i < $nrows; $i++ )
1813 $A = DB_fetchArray( $result, true );
1815 if( $table == $_TABLES['topics'] AND SEC_hasTopicAccess( $A['tid'] ) == 0 )
1820 if (empty($fieldname)) {
1821 // Not a good idea, as that will expose our table name and prefix!
1822 // Make sure you pass a distinct field name!
1823 $fieldname = $table;
1828 $retval .= '<li><input type="checkbox" name="' . $fieldname . '[]" value="' . $A[0] . '"';
1830 $sizeS = count( $S );
1831 for( $x = 0; $x < $sizeS; $x++ )
1833 if( $A[0] == $S[$x] )
1835 $retval .= ' checked="checked"';
1840 if(( $table == $_TABLES['blocks'] ) && isset( $A[2] ) && ( $A[2] == 'gldefault' ))
1842 $retval .= XHTML . '><span class="gldefault">' . stripslashes( $A[1] ) . '</span></li>' . LB;
1846 $retval .= XHTML . '><span>' . stripslashes( $A[1] ) . '</span></li>' . LB;
1850 $retval .= '</ul>' . LB;
1856 * Prints out an associative array for debugging
1858 * The core of this code has been lifted from phpweblog which is licenced
1859 * under the GPL. This is not used very much in the code but you can use it
1862 * @param array $array Array to loop through and print values for
1863 * @return string $retval Formatted HTML List
1867 function COM_debug($array)
1870 if(!empty($array)) {
1871 $retval = '<ul><pre><p>---- DEBUG ----</p>';
1872 foreach($array as $k => $v) {
1873 $retval .= sprintf("<li>%13s [%s]</li>\n", $k, $v);
1875 $retval .= '<p>---------------</p></pre></ul>';
1882 * Checks to see if RDF file needs updating and updates it if so.
1883 * Checks to see if we need to update the RDF as a result
1884 * of an article with a future publish date reaching it's
1885 * publish time and if so updates the RDF file.
1887 * NOTE: When called without parameters, this will only check for new entries to
1888 * include in the feeds. Pass the $updated_XXX parameters when the content
1889 * of an existing entry has changed.
1891 * @param string $updated_type (optional) feed type to update
1892 * @param string $updated_topic (optional) feed topic to update
1893 * @param string $updated_id (optional) feed id to update
1895 * @see file lib-syndication.php
1898 function COM_rdfUpToDateCheck( $updated_type = '', $updated_topic = '', $updated_id = '' )
1900 global $_CONF, $_TABLES;
1902 if( $_CONF['backend'] > 0 )
1904 if( !empty( $updated_type ) && ( $updated_type != 'article' ))
1906 // when a plugin's feed is to be updated, skip Geeklog's own feeds
1907 $sql = "SELECT fid,type,topic,limits,update_info FROM {$_TABLES['syndication']} WHERE (is_enabled = 1) AND (type <> 'article')";
1911 $sql = "SELECT fid,type,topic,limits,update_info FROM {$_TABLES['syndication']} WHERE is_enabled = 1";
1913 $result = DB_query( $sql );
1914 $num = DB_numRows( $result );
1915 for( $i = 0; $i < $num; $i++)
1917 $A = DB_fetchArray( $result );
1920 if( $A['type'] == 'article' )
1922 $is_current = SYND_feedUpdateCheck( $A['topic'],
1923 $A['update_info'], $A['limits'],
1924 $updated_topic, $updated_id );
1928 $is_current = PLG_feedUpdateCheck( $A['type'], $A['fid'],
1929 $A['topic'], $A['update_info'], $A['limits'],
1930 $updated_type, $updated_topic, $updated_id );
1934 SYND_updateFeed( $A['fid'] );
1941 * Checks and Updates the featured status of all articles.
1943 * Checks to see if any articles that were published for the future have been
1944 * published and, if so, will see if they are featured. If they are featured,
1945 * this will set old featured article (if there is one) to normal
1949 function COM_featuredCheck()
1953 $curdate = date( "Y-m-d H:i:s", time() );
1955 // Loop through each topic
1956 $sql = "SELECT tid FROM {$_TABLES['topics']}";
1957 $result = DB_query( $sql );
1958 $num = DB_numRows( $result );
1959 for( $i = 0; $i < $num; $i++)
1961 $A = DB_fetchArray( $result );
1963 if( DB_getItem( $_TABLES['stories'], 'COUNT(*)', "featured = 1 AND draft_flag = 0 AND tid = '{$A['tid']}' AND date <= '$curdate'" ) > 1 )
1965 // OK, we have two featured stories in a topic, fix that
1966 $sid = DB_getItem( $_TABLES['stories'], 'sid', "featured = 1 AND draft_flag = 0 ORDER BY date LIMIT 1" );
1967 DB_query( "UPDATE {$_TABLES['stories']} SET featured = 0 WHERE sid = '$sid'" );
1974 * Logs messages to error.log or the web page or both
1976 * Prints a well formatted message to either the web page, error log
1979 * @param string $logentry Text to log to error log
1980 * @param int $actionid where 1 = write to log file, 2 = write to screen (default) both
1981 * @see function COM_accessLog
1982 * @return string If $actionid = 2 or '' then HTML formatted string (wrapped in block) else nothing
1986 function COM_errorLog( $logentry, $actionid = '' )
1988 global $_CONF, $LANG01;
1992 if( !empty( $logentry ))
1994 $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
1997 $timestamp = @strftime( '%c' );
1999 if (!isset($_CONF['path_layout']) &&
2000 (($actionid == 2) || empty($actionid))) {
2003 if (!isset($_CONF['path_log']) && ($actionid != 2)) {
2010 $logfile = $_CONF['path_log'] . 'error.log';
2012 if( !$file = fopen( $logfile, 'a' ))
2014 $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2018 fputs( $file, "$timestamp - $logentry \n" );
2023 $retval .= COM_startBlock( $LANG01[55] . ' ' . $timestamp, '',
2024 COM_getBlockTemplate( '_msg_block', 'header' ))
2025 . nl2br( $logentry )
2026 . COM_endBlock( COM_getBlockTemplate( '_msg_block',
2031 $retval = nl2br($logentry);
2035 $logfile = $_CONF['path_log'] . 'error.log';
2037 if( !$file = fopen( $logfile, 'a' ))
2039 $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2043 fputs( $file, "$timestamp - $logentry \n" );
2044 $retval .= COM_startBlock( $LANG01[34] . ' - ' . $timestamp,
2045 '', COM_getBlockTemplate( '_msg_block',
2047 . nl2br( $logentry )
2048 . COM_endBlock( COM_getBlockTemplate( '_msg_block',
2059 * Logs message to access.log
2061 * This will print a message to the Geeklog access log
2063 * @param string $logentry Message to write to access log
2068 function COM_accessLog( $logentry )
2070 global $_CONF, $_USER, $LANG01;
2074 if( !empty( $logentry ))
2076 $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
2079 $timestamp = @strftime( '%c' );
2080 $logfile = $_CONF['path_log'] . 'access.log';
2082 if( !$file = fopen( $logfile, 'a' ))
2084 return $LANG01[33] . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2087 if( isset( $_USER['uid'] ))
2089 $byuser = $_USER['uid'] . '@' . $_SERVER['REMOTE_ADDR'];
2093 $byuser = 'anon@' . $_SERVER['REMOTE_ADDR'];
2096 fputs( $file, "$timestamp ($byuser) - $logentry\n" );
2103 * Shows all available topics
2105 * Show the topics in the system the user has access to and prints them in HTML.
2106 * This function is used to show the topics in the topics block.
2108 * @param string $topic ID of currently selected topic
2109 * @return string HTML formatted topic list
2113 function COM_showTopics( $topic='' )
2115 global $_CONF, $_TABLES, $_USER, $LANG01, $_BLOCK_TEMPLATE, $page;
2117 $langsql = COM_getLangSQL( 'tid' );
2118 if( empty( $langsql ))
2127 $sql = "SELECT tid,topic,imageurl,meta_description FROM {$_TABLES['topics']}" . $langsql;
2128 if( !COM_isAnonUser() )
2130 $tids = DB_getItem( $_TABLES['userindex'], 'tids',
2131 "uid = '{$_USER['uid']}'" );
2132 if( !empty( $tids ))
2134 $sql .= " $op (tid NOT IN ('" . str_replace( ' ', "','", $tids )
2135 . "'))" . COM_getPermSQL( 'AND' );
2139 $sql .= COM_getPermSQL( $op );
2144 $sql .= COM_getPermSQL( $op );
2146 if( $_CONF['sortmethod'] == 'alpha' )
2148 $sql .= ' ORDER BY topic ASC';
2152 $sql .= ' ORDER BY sortnum';
2154 $result = DB_query( $sql );
2157 $sections = new Template( $_CONF['path_layout'] );
2158 if( isset( $_BLOCK_TEMPLATE['topicoption'] ))
2160 $templates = explode( ',', $_BLOCK_TEMPLATE['topicoption'] );
2161 $sections->set_file( array( 'option' => $templates[0],
2162 'current' => $templates[1] ));
2166 $sections->set_file( array( 'option' => 'topicoption.thtml',
2167 'inactive' => 'topicoption_off.thtml' ));
2170 $sections->set_var( 'xhtml', XHTML );
2171 $sections->set_var( 'site_url', $_CONF['site_url'] );
2172 $sections->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2173 $sections->set_var( 'layout_url', $_CONF['layout_url'] );
2174 $sections->set_var( 'block_name', str_replace( '_', '-', 'section_block' ));
2176 if( $_CONF['hide_home_link'] == 0 )
2178 // Give a link to the homepage here since a lot of people use this for
2179 // navigating the site
2181 if( COM_onFrontpage() )
2183 $sections->set_var( 'option_url', '' );
2184 $sections->set_var( 'option_label', $LANG01[90] );
2185 $sections->set_var( 'option_count', '' );
2186 $sections->set_var( 'topic_image', '' );
2187 $retval .= $sections->parse( 'item', 'inactive' );
2191 $sections->set_var( 'option_url',
2192 $_CONF['site_url'] . '/index.php' );
2193 $sections->set_var( 'option_label', $LANG01[90] );
2194 $sections->set_var( 'option_count', '' );
2195 $sections->set_var( 'topic_image', '' );
2196 $retval .= $sections->parse( 'item', 'option' );
2200 if( $_CONF['showstorycount'] )
2202 $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['stories']} "
2203 . 'WHERE (draft_flag = 0) AND (date <= NOW()) '
2204 . COM_getPermSQL( 'AND' )
2206 $rcount = DB_query( $sql );
2207 while( $C = DB_fetchArray( $rcount ))
2209 $storycount[$C['tid']] = $C['count'];
2213 if( $_CONF['showsubmissioncount'] )
2215 $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['storysubmission']} "
2217 $rcount = DB_query( $sql );
2218 while( $C = DB_fetchArray( $rcount ))
2220 $submissioncount[$C['tid']] = $C['count'];
2224 while( $A = DB_fetchArray( $result ) )
2226 $topicname = stripslashes( $A['topic'] );
2227 $sections->set_var( 'option_url', $_CONF['site_url']
2228 . '/index.php?topic=' . $A['tid'] );
2229 $sections->set_var( 'option_label', $topicname );
2232 if( $_CONF['showstorycount'] || $_CONF['showsubmissioncount'] )
2234 $countstring .= '(';
2236 if( $_CONF['showstorycount'] )
2238 if( empty( $storycount[$A['tid']] ))
2244 $countstring .= COM_numberFormat( $storycount[$A['tid']] );
2248 if( $_CONF['showsubmissioncount'] )
2250 if( $_CONF['showstorycount'] )
2252 $countstring .= '/';
2254 if( empty( $submissioncount[$A['tid']] ))
2260 $countstring .= COM_numberFormat( $submissioncount[$A['tid']] );
2264 $countstring .= ')';
2266 $sections->set_var( 'option_count', $countstring );
2269 if( !empty( $A['imageurl'] ))
2271 $imageurl = COM_getTopicImageUrl( $A['imageurl'] );
2272 $topicimage = '<img src="' . $imageurl . '" alt="' . $topicname
2273 . '" title="' . $topicname . '"' . XHTML . '>';
2275 $sections->set_var( 'topic_image', $topicimage );
2277 $desc = trim($A['meta_description']);
2278 $sections->set_var('topic_description', $desc);
2279 $desc_escaped = htmlspecialchars($desc);
2280 $sections->set_var('topic_description_escaped', $desc_escaped);
2281 if (! empty($desc)) {
2282 $sections->set_var('topic_title_attribute',
2283 'title="' . $desc_escaped . '"');
2285 $sections->set_var('topic_title_attribute', '');
2288 if(( $A['tid'] == $topic ) && ( $page == 1 ))
2290 $retval .= $sections->parse( 'item', 'inactive' );
2294 $retval .= $sections->parse( 'item', 'option' );
2302 * Shows the user their menu options
2304 * This shows the average Joe User their menu options. This is the user block on the left side
2306 * @param string $help Help file to show
2307 * @param string $title Title of Menu
2308 * @param string $position Side being shown on 'left', 'right'. Though blank works not likely.
2309 * @see function COM_adminMenu
2312 function COM_userMenu( $help='', $title='', $position='' )
2314 global $_TABLES, $_USER, $_CONF, $LANG01, $LANG04, $_BLOCK_TEMPLATE;
2318 if( !COM_isAnonUser() )
2320 $usermenu = new Template( $_CONF['path_layout'] );
2321 if( isset( $_BLOCK_TEMPLATE['useroption'] ))
2323 $templates = explode( ',', $_BLOCK_TEMPLATE['useroption'] );
2324 $usermenu->set_file( array( 'option' => $templates[0],
2325 'current' => $templates[1] ));
2329 $usermenu->set_file( array( 'option' => 'useroption.thtml',
2330 'current' => 'useroption_off.thtml' ));
2332 $usermenu->set_var( 'xhtml', XHTML );
2333 $usermenu->set_var( 'site_url', $_CONF['site_url'] );
2334 $usermenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2335 $usermenu->set_var( 'layout_url', $_CONF['layout_url'] );
2336 $usermenu->set_var( 'block_name', str_replace( '_', '-', 'user_block' ));
2338 if( empty( $title ))
2340 $title = DB_getItem( $_TABLES['blocks'], 'title',
2341 "name='user_block'" );
2344 // what's our current URL?
2345 $thisUrl = COM_getCurrentURL();
2347 $retval .= COM_startBlock( $title, $help,
2348 COM_getBlockTemplate( 'user_block', 'header', $position ));
2350 // This function will show the user options for all installed plugins
2353 $plugin_options = PLG_getUserOptions();
2354 $nrows = count( $plugin_options );
2356 for( $i = 0; $i < $nrows; $i++ )
2358 $plg = current( $plugin_options );
2359 $usermenu->set_var( 'option_label', $plg->adminlabel );
2361 if( !empty( $plg->numsubmissions ))
2363 $usermenu->set_var( 'option_count', '(' . $plg->numsubmissions . ')' );
2367 $usermenu->set_var( 'option_count', '' );
2370 $usermenu->set_var( 'option_url', $plg->adminurl );
2371 if( $thisUrl == $plg->adminurl )
2373 $retval .= $usermenu->parse( 'item', 'current' );
2377 $retval .= $usermenu->parse( 'item', 'option' );
2379 next( $plugin_options );
2382 $url = $_CONF['site_url'] . '/usersettings.php';
2383 $usermenu->set_var( 'option_label', $LANG01[48] );
2384 $usermenu->set_var( 'option_count', '' );
2385 $usermenu->set_var( 'option_url', $url );
2386 if( $thisUrl == $url )
2388 $retval .= $usermenu->parse( 'item', 'current' );
2392 $retval .= $usermenu->parse( 'item', 'option' );
2395 $url = $_CONF['site_url'] . '/users.php?mode=logout';
2396 $usermenu->set_var( 'option_label', $LANG01[19] );
2397 $usermenu->set_var( 'option_count', '' );
2398 $usermenu->set_var( 'option_url', $url );
2399 $retval .= $usermenu->finish($usermenu->parse('item', 'option'));
2400 $retval .= COM_endBlock(COM_getBlockTemplate('user_block', 'footer', $position));
2404 $retval .= COM_startBlock( $LANG01[47], $help,
2405 COM_getBlockTemplate( 'user_block', 'header', $position ));
2406 $login = new Template( $_CONF['path_layout'] );
2407 $login->set_file( 'form', 'loginform.thtml' );
2408 $login->set_var( 'xhtml', XHTML );
2409 $login->set_var( 'site_url', $_CONF['site_url'] );
2410 $login->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2411 $login->set_var( 'layout_url', $_CONF['layout_url'] );
2412 $login->set_var( 'lang_username', $LANG01[21] );
2413 $login->set_var( 'lang_password', $LANG01[57] );
2414 $login->set_var( 'lang_forgetpassword', $LANG01[119] );
2415 $login->set_var( 'lang_login', $LANG01[58] );
2416 if( $_CONF['disable_new_user_registration'] == 1 )
2418 $login->set_var( 'lang_signup', '' );
2422 $login->set_var( 'lang_signup', $LANG01[59] );
2425 // 3rd party remote authentification.
2426 if ($_CONF['user_login_method']['3rdparty'] && !$_CONF['usersubmission']) {
2427 $modules = SEC_collectRemoteAuthenticationModules();
2428 if (count($modules) == 0) {
2429 $user_templates->set_var('services', '');
2431 if (!$_CONF['user_login_method']['standard'] &&
2432 (count($modules) == 1)) {
2433 $select = '<input type="hidden" name="service" value="'
2434 . $modules[0] . '"' . XHTML . '>' . $modules[0];
2437 $select = '<select name="service" id="service">';
2438 if ($_CONF['user_login_method']['standard']) {
2439 $select .= '<option value="">' . $_CONF['site_name']
2442 foreach ($modules as $service) {
2443 $select .= '<option value="' . $service . '">'
2444 . $service . '</option>';
2446 $select .= '</select>';
2449 $login->set_file('services', 'blockservices.thtml');
2450 $login->set_var('lang_service', $LANG04[121]);
2451 $login->set_var('select_service', $select);
2452 $login->parse('output', 'services');
2453 $login->set_var('services',
2454 $login->finish($login->get_var('output')));
2457 $login->set_var('services', '');
2460 // OpenID remote authentification.
2461 if ($_CONF['user_login_method']['openid'] && ($_CONF['usersubmission'] == 0) && !$_CONF['disable_new_user_registration']) {
2462 $login->set_file('openid_login', 'loginform_openid.thtml');
2463 $login->set_var('lang_openid_login', $LANG01[128]);
2464 $login->set_var('input_field_size', 18);
2465 $login->set_var('app_url', $_CONF['site_url'] . '/users.php');
2466 $login->parse('output', 'openid_login');
2467 $login->set_var('openid_login',
2468 $login->finish($login->get_var('output')));
2470 $login->set_var('openid_login', '');
2473 $retval .= $login->finish($login->parse('output', 'form'));
2474 $retval .= COM_endBlock( COM_getBlockTemplate('user_block', 'footer', $position));
2481 * Prints administration menu
2483 * This will return the administration menu items that the user has
2484 * sufficient rights to -- Admin Block on the left side.
2486 * @param string $help Help file to show
2487 * @param string $title Menu Title
2488 * @param string $position Side being shown on 'left', 'right' or blank.
2489 * @see function COM_userMenu
2492 function COM_adminMenu( $help = '', $title = '', $position = '' )
2494 global $_TABLES, $_USER, $_CONF, $LANG01, $LANG_ADMIN, $_BLOCK_TEMPLATE,
2499 if( empty( $_USER['username'] ))
2504 $plugin_options = PLG_getAdminOptions();
2505 $num_plugins = count( $plugin_options );
2507 if( SEC_isModerator() OR SEC_hasRights( 'story.edit,block.edit,topic.edit,user.edit,plugin.edit,user.mail,syndication.edit', 'OR' ) OR ( $num_plugins > 0 ))
2509 // what's our current URL?
2510 $thisUrl = COM_getCurrentURL();
2512 $adminmenu = new Template( $_CONF['path_layout'] );
2513 if( isset( $_BLOCK_TEMPLATE['adminoption'] ))
2515 $templates = explode( ',', $_BLOCK_TEMPLATE['adminoption'] );
2516 $adminmenu->set_file( array( 'option' => $templates[0],
2517 'current' => $templates[1] ));
2521 $adminmenu->set_file( array( 'option' => 'adminoption.thtml',
2522 'current' => 'adminoption_off.thtml' ));
2524 $adminmenu->set_var( 'xhtml', XHTML );
2525 $adminmenu->set_var( 'site_url', $_CONF['site_url'] );
2526 $adminmenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2527 $adminmenu->set_var( 'layout_url', $_CONF['layout_url'] );
2528 $adminmenu->set_var( 'block_name', str_replace( '_', '-', 'admin_block' ));
2530 if( empty( $title ))
2532 $title = DB_getItem( $_TABLES['blocks'], 'title',
2533 "name = 'admin_block'" );
2536 $retval .= COM_startBlock( $title, $help,
2537 COM_getBlockTemplate( 'admin_block', 'header', $position ));
2540 if( SEC_isModerator() || SEC_hasRights( 'story.edit' ))
2542 $tresult = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
2543 . COM_getPermSQL() );
2544 $trows = DB_numRows( $tresult );
2548 for( $i = 0; $i < $trows; $i++ )
2550 $T = DB_fetchArray( $tresult );
2551 $tids[] = $T['tid'];
2553 if( count( $tids ) > 0 )
2555 $topicsql = " (tid IN ('" . implode( "','", $tids ) . "'))";
2561 if (SEC_hasRights('story.edit,story.moderate', 'OR') ||
2562 (($_CONF['commentsubmission'] == 1) &&
2563 SEC_hasRights('comment.moderate')) ||
2564 (($_CONF['usersubmission'] == 1) &&
2565 SEC_hasRights('user.edit,user.delete'))) {
2567 if (SEC_hasRights('story.moderate')) {
2568 if (empty($topicsql)) {
2569 $modnum += DB_count($_TABLES['storysubmission']);
2571 $sresult = DB_query("SELECT COUNT(*) AS count FROM {$_TABLES['storysubmission']} WHERE" . $topicsql);
2572 $S = DB_fetchArray($sresult);
2573 $modnum += $S['count'];
2577 if (($_CONF['listdraftstories'] == 1) && SEC_hasRights('story.edit')) {
2578 $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (draft_flag = 1)";
2579 if (!empty($topicsql)) {
2580 $sql .= ' AND' . $topicsql;
2582 $result = DB_query($sql . COM_getPermSQL('AND', 0, 3));
2583 $A = DB_fetchArray($result);
2584 $modnum += $A['count'];
2587 if (($_CONF['commentsubmission'] == 1) && SEC_hasRights('comment.moderate')) {
2588 $modnum += DB_count($_TABLES['commentsubmissions']);
2591 if ($_CONF['usersubmission'] == 1) {
2592 if (SEC_hasRights('user.edit') && SEC_hasRights('user.delete')) {
2593 $modnum += DB_count($_TABLES['users'], 'status', '2');
2598 if (SEC_inGroup('Root')) {
2599 $url = $_CONF['site_admin_url'] . '/configuration.php';
2600 $adminmenu->set_var('option_url', $url);
2601 $adminmenu->set_var('option_label', $LANG01[129]);
2602 $adminmenu->set_var('option_count', count($config->_get_groups()));
2603 $menu_item = $adminmenu->parse('item',
2604 ($thisUrl == $url) ? 'current' :
2606 $link_array[$LANG01[129]] = $menu_item;
2610 // now handle submissions for plugins
2611 $modnum += PLG_getSubmissionCount();
2613 if( SEC_hasRights( 'story.edit' ))
2615 $url = $_CONF['site_admin_url'] . '/story.php';
2616 $adminmenu->set_var( 'option_url', $url );
2617 $adminmenu->set_var( 'option_label', $LANG01[11] );
2618 if( empty( $topicsql ))
2620 $numstories = DB_count( $_TABLES['stories'] );
2624 $nresult = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE" . $topicsql . COM_getPermSql( 'AND' ));
2625 $N = DB_fetchArray( $nresult );
2626 $numstories = $N['count'];
2628 $adminmenu->set_var( 'option_count',
2629 COM_numberFormat( $numstories ));
2630 $menu_item = $adminmenu->parse( 'item',
2631 ( $thisUrl == $url ) ? 'current' : 'option' );
2632 $link_array[$LANG01[11]] = $menu_item;
2635 if( SEC_hasRights( 'block.edit' ))
2637 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['blocks']}" . COM_getPermSql());
2638 list( $count ) = DB_fetchArray( $result );
2640 $url = $_CONF['site_admin_url'] . '/block.php';
2641 $adminmenu->set_var( 'option_url', $url );
2642 $adminmenu->set_var( 'option_label', $LANG01[12] );
2643 $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
2645 $menu_item = $adminmenu->parse( 'item',
2646 ( $thisUrl == $url ) ? 'current' : 'option' );
2647 $link_array[$LANG01[12]] = $menu_item;
2650 if( SEC_hasRights( 'topic.edit' ))
2652 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['topics']}" . COM_getPermSql());
2653 list( $count ) = DB_fetchArray( $result );
2655 $url = $_CONF['site_admin_url'] . '/topic.php';
2656 $adminmenu->set_var( 'option_url', $url );
2657 $adminmenu->set_var( 'option_label', $LANG01[13] );
2658 $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
2660 $menu_item = $adminmenu->parse( 'item',
2661 ( $thisUrl == $url ) ? 'current' : 'option' );
2662 $link_array[$LANG01[13]] = $menu_item;
2665 if( SEC_hasRights( 'user.edit' ))
2667 $url = $_CONF['site_admin_url'] . '/user.php';
2668 $adminmenu->set_var( 'option_url', $url );
2669 $adminmenu->set_var( 'option_label', $LANG01[17] );
2670 $adminmenu->set_var( 'option_count',
2671 COM_numberFormat( DB_count( $_TABLES['users'] ) -1 ));
2673 $menu_item = $adminmenu->parse( 'item',
2674 ( $thisUrl == $url ) ? 'current' : 'option' );
2675 $link_array[$LANG01[17]] = $menu_item;
2678 if( SEC_hasRights( 'group.edit' ))
2680 if (SEC_inGroup('Root')) {
2683 $thisUsersGroups = SEC_getUserGroups ();
2684 $grpFilter = 'WHERE (grp_id IN (' . implode (',', $thisUsersGroups) . '))';
2686 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['groups']} $grpFilter;" );
2687 $A = DB_fetchArray( $result );
2689 $url = $_CONF['site_admin_url'] . '/group.php';
2690 $adminmenu->set_var( 'option_url', $url );
2691 $adminmenu->set_var( 'option_label', $LANG01[96] );
2692 $adminmenu->set_var( 'option_count',
2693 COM_numberFormat( $A['count'] ));
2695 $menu_item = $adminmenu->parse( 'item',
2696 ( $thisUrl == $url ) ? 'current' : 'option' );
2697 $link_array[$LANG01[96]] = $menu_item;
2700 if( SEC_hasRights( 'user.mail' ))
2702 $url = $_CONF['site_admin_url'] . '/mail.php';
2703 $adminmenu->set_var( 'option_url', $url );
2704 $adminmenu->set_var( 'option_label', $LANG01[105] );
2705 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2707 $menu_item = $adminmenu->parse( 'item',
2708 ( $thisUrl == $url ) ? 'current' : 'option' );
2709 $link_array[$LANG01[105]] = $menu_item;
2712 if(( $_CONF['backend'] == 1 ) && SEC_hasRights( 'syndication.edit' ))
2714 $url = $_CONF['site_admin_url'] . '/syndication.php';
2715 $adminmenu->set_var( 'option_url', $url );
2716 $adminmenu->set_var( 'option_label', $LANG01[38] );
2717 $count = COM_numberFormat( DB_count( $_TABLES['syndication'] ));
2718 $adminmenu->set_var( 'option_count', $count );
2720 $menu_item = $adminmenu->parse( 'item',
2721 ( $thisUrl == $url ) ? 'current' : 'option' );
2722 $link_array[$LANG01[38]] = $menu_item;
2725 if(( $_CONF['trackback_enabled'] || $_CONF['pingback_enabled'] ||
2726 $_CONF['ping_enabled'] ) && SEC_hasRights( 'story.ping' ))
2728 $url = $_CONF['site_admin_url'] . '/trackback.php';
2729 $adminmenu->set_var( 'option_url', $url );
2730 $adminmenu->set_var( 'option_label', $LANG01[116] );
2731 if( $_CONF['ping_enabled'] )
2733 $count = COM_numberFormat( DB_count( $_TABLES['pingservice'] ));
2734 $adminmenu->set_var( 'option_count', $count );
2738 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2741 $menu_item = $adminmenu->parse( 'item',
2742 ( $thisUrl == $url ) ? 'current' : 'option' );
2743 $link_array[$LANG01[116]] = $menu_item;
2746 if (SEC_hasRights('plugin.edit')) {
2747 $url = $_CONF['site_admin_url'] . '/plugins.php';
2748 $adminmenu->set_var('option_url', $url);
2749 $adminmenu->set_var('option_label', $LANG01[77]);
2750 $adminmenu->set_var('option_count',
2751 COM_numberFormat(DB_count($_TABLES['plugins'],
2754 $menu_item = $adminmenu->parse('item',
2755 ($thisUrl == $url) ? 'current' : 'option');
2756 $link_array[$LANG01[77]] = $menu_item;
2759 // This will show the admin options for all installed plugins (if any)
2761 for ($i = 0; $i < $num_plugins; $i++) {
2762 $plg = current($plugin_options);
2764 $adminmenu->set_var('option_url', $plg->adminurl);
2765 $adminmenu->set_var('option_label', $plg->adminlabel);
2767 if (isset($plg->numsubmissions) &&
2768 is_numeric($plg->numsubmissions)) {
2769 $adminmenu->set_var('option_count',
2770 COM_numberFormat($plg->numsubmissions));
2771 } elseif (! empty($plg->numsubmissions)) {
2772 $adminmenu->set_var('option_count', $plg->numsubmissions);
2774 $adminmenu->set_var('option_count', $LANG_ADMIN['na']);
2777 $menu_item = $adminmenu->parse('item',
2778 ($thisUrl == $plg->adminurl) ? 'current' : 'option', true);
2779 $link_array[$plg->adminlabel] = $menu_item;
2781 next($plugin_options);
2784 if(( $_CONF['allow_mysqldump'] == 1 ) AND ( $_DB_dbms == 'mysql' ) AND
2785 SEC_inGroup( 'Root' ))
2787 $url = $_CONF['site_admin_url'] . '/database.php';
2788 $adminmenu->set_var( 'option_url', $url );
2789 $adminmenu->set_var( 'option_label', $LANG01[103] );
2790 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2792 $menu_item = $adminmenu->parse( 'item',
2793 ( $thisUrl == $url ) ? 'current' : 'option' );
2794 $link_array[$LANG01[103]] = $menu_item;
2797 if ($_CONF['link_documentation'] == 1) {
2798 $doclang = COM_getLanguageName();
2799 $docs = 'docs/' . $doclang . '/index.html';
2800 if (file_exists($_CONF['path_html'] . $docs)) {
2801 $adminmenu->set_var('option_url', $_CONF['site_url']
2804 $adminmenu->set_var('option_url', $_CONF['site_url']
2805 . '/docs/english/index.html');
2807 $adminmenu->set_var('option_label', $LANG01[113]);
2808 $adminmenu->set_var('option_count', $LANG_ADMIN['na']);
2809 $menu_item = $adminmenu->parse('item', 'option');
2810 $link_array[$LANG01[113]] = $menu_item;
2813 if( $_CONF['link_versionchecker'] == 1 AND SEC_inGroup( 'Root' ))
2815 $adminmenu->set_var( 'option_url',
2816 'http://www.geeklog.net/versionchecker.php?version=' . VERSION );
2817 $adminmenu->set_var( 'option_label', $LANG01[107] );
2818 $adminmenu->set_var( 'option_count', VERSION );
2820 $menu_item = $adminmenu->parse( 'item', 'option' );
2821 $link_array[$LANG01[107]] = $menu_item;
2824 if( $_CONF['sort_admin'] )
2826 uksort( $link_array, 'strcasecmp' );
2829 $url = $_CONF['site_admin_url'] . '/moderation.php';
2830 $adminmenu->set_var('option_url', $url);
2831 $adminmenu->set_var('option_label', $LANG01[10]);
2832 $adminmenu->set_var('option_count', COM_numberFormat($modnum));
2833 $menu_item = $adminmenu->finish($adminmenu->parse('item',
2834 ($thisUrl == $url) ? 'current' : 'option'));
2835 $link_array = array($menu_item) + $link_array;
2837 foreach( $link_array as $link )
2842 $retval .= COM_endBlock( COM_getBlockTemplate( 'admin_block', 'footer', $position ));
2849 * Redirects user to a given URL
2851 * This function does a redirect using a meta refresh. This is (or at least
2852 * used to be) more compatible than using a HTTP Location: header.
2854 * NOTE: This does not need to be XHTML compliant. It may also be used
2855 * in situations where the XHTML constant is not defined yet ...
2857 * @param string $url URL to send user to
2858 * @return string HTML meta redirect
2861 function COM_refresh($url)
2863 return "<html><head><meta http-equiv=\"refresh\" content=\"0; URL=$url\"></head></html>\n";
2867 * DEPRECIATED -- see CMT_userComments in lib-comment.php
2868 * @deprecated since Geeklog 1.4.0
2869 * @see CMT_userComments
2871 function COM_userComments( $sid, $title, $type='article', $order='', $mode='', $pid = 0, $page = 1, $cid = false, $delete_option = false )
2875 require_once $_CONF['path_system'] . 'lib-comment.php';
2877 return CMT_userComments( $sid, $title, $type, $order, $mode, $pid, $page, $cid, $delete_option );
2881 * This censors inappropriate content
2883 * This will replace 'bad words' with something more appropriate
2885 * @param string $Message String to check
2886 * @see function COM_checkHTML
2887 * @return string Edited $Message
2891 function COM_checkWords( $Message )
2895 $EditedMessage = $Message;
2897 if( $_CONF['censormode'] != 0 )
2899 if( is_array( $_CONF['censorlist'] ))
2901 $Replacement = $_CONF['censorreplace'];
2903 switch( $_CONF['censormode'])
2905 case 1: # Exact match
2906 $RegExPrefix = '(\s*)';
2907 $RegExSuffix = '(\W*)';
2910 case 2: # Word beginning
2911 $RegExPrefix = '(\s*)';
2912 $RegExSuffix = '(\w*)';
2915 case 3: # Word fragment
2916 $RegExPrefix = '(\w*)';
2917 $RegExSuffix = '(\w*)';
2921 foreach ($_CONF['censorlist'] as $c) {
2923 $EditedMessage = MBYTE_eregi_replace($RegExPrefix . $c
2924 . $RegExSuffix, "\\1$Replacement\\2", $EditedMessage);
2930 return $EditedMessage;
2934 * Takes some amount of text and replaces all javascript events on*= with in
2936 * This script takes some amount of text and matches all javascript events, on*= (onBlur= onMouseClick=)
2937 * and replaces them with in*=
2938 * Essentially this will cause onBlur to become inBlur, onFocus to be inFocus
2939 * These are not valid javascript events and the browser will ignore them.
2940 * @param string $Message Text to filter
2941 * @return string $Message with javascript filtered
2942 * @see COM_checkWords
2943 * @see COM_checkHTML
2947 function COM_killJS( $Message )
2949 return( preg_replace( '/(\s)+[oO][nN](\w*) ?=/', '\1in\2=', $Message ));
2953 * Handles the part within a [code] ... [/code] section, i.e. escapes all
2954 * special characters.
2956 * @param string $str the code section to encode
2957 * @return string $str with the special characters encoded
2958 * @see COM_checkHTML
2961 function COM_handleCode( $str )
2963 $search = array( '&', '\\', '<', '>', '[', ']' );
2964 $replace = array( '&', '\', '<', '>', '[', ']' );
2966 $str = str_replace( $search, $replace, $str );
2972 * This function checks html tags.
2974 * Checks to see that the HTML tags are on the approved list and
2975 * removes them if not.
2977 * @param string $str HTML to check
2978 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
2979 * @return string Filtered HTML
2982 function COM_checkHTML( $str, $permissions = 'story.edit' )
2986 // replace any \ with \ (HTML equiv)
2987 $str = str_replace('\\', '\', COM_stripslashes($str) );
2989 // Get rid of any newline characters
2990 $str = preg_replace( "/\n/", '', $str );
2992 // Replace any $ with $ (HTML equiv)
2993 $str = str_replace( '$', '$', $str );
2994 // handle [code] ... [/code]
2997 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[code]' );
2998 if( $start_pos !== false )
3000 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/code]' );
3001 if( $end_pos !== false )
3003 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6,
3004 $end_pos - ( $start_pos + 6 )));
3005 $encoded = '<pre><code>' . $encoded . '</code></pre>';
3006 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
3007 . MBYTE_substr( $str, $end_pos + 7 );
3009 else // missing [/code]
3011 // Treat the rest of the text as code (so as not to lose any
3012 // special characters). However, the calling entity should
3013 // better be checking for missing [/code] before calling this
3015 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6 ));
3016 $encoded = '<pre><code>' . $encoded . '</code></pre>';
3017 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3021 while( $start_pos !== false );
3023 // handle [raw] ... [/raw]
3026 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[raw]' );
3027 if( $start_pos !== false )
3029 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/raw]' );
3030 if( $end_pos !== false )
3032 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5,
3033 $end_pos - ( $start_pos + 5 )));
3034 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3036 $encoded = '[raw2]' . $encoded . '[/raw2]';
3037 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
3038 . MBYTE_substr( $str, $end_pos + 6 );
3040 else // missing [/raw]
3042 // Treat the rest of the text as raw (so as not to lose any
3043 // special characters). However, the calling entity should
3044 // better be checking for missing [/raw] before calling this
3046 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5 ));
3047 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3049 $encoded = '[raw2]' . $encoded . '[/raw2]';
3050 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3054 while( $start_pos !== false );
3056 if( isset( $_CONF['skip_html_filter_for_root'] ) &&
3057 ( $_CONF['skip_html_filter_for_root'] == 1 ) &&
3058 SEC_inGroup( 'Root' ))
3063 // strip_tags() gets confused by HTML comments ...
3064 $str = preg_replace( '/<!--.+?-->/', '', $str );
3066 $filter = new kses4;
3067 if( isset( $_CONF['allowed_protocols'] ) && is_array( $_CONF['allowed_protocols'] ) && ( count( $_CONF['allowed_protocols'] ) > 0 ))
3069 $filter->SetProtocols( $_CONF['allowed_protocols'] );
3073 $filter->SetProtocols( array( 'http:', 'https:', 'ftp:' ));
3076 if( empty( $permissions) || !SEC_hasRights( $permissions ) ||
3077 empty( $_CONF['admin_html'] ))
3079 $html = $_CONF['user_html'];
3083 if ($_CONF['advanced_editor'] && is_array($_CONF['advanced_html'])) {
3084 $html = array_merge_recursive( $_CONF['user_html'],
3085 $_CONF['admin_html'],
3086 $_CONF['advanced_html'] );
3088 $html = array_merge_recursive( $_CONF['user_html'],
3089 $_CONF['admin_html'] );
3093 foreach( $html as $tag => $attr )
3095 $filter->AddHTML( $tag, $attr );
3097 /* Replace [raw][/raw] with <!--raw--><!--/raw-->, note done "late" because
3098 * of the above noted // strip_tags() gets confused by HTML comments ...
3100 $str = $filter->Parse( $str );
3101 $str = str_replace('[raw2]','<!--raw--><span class="raw">', $str);
3102 $str = str_replace('[/raw2]','</span><!--/raw-->', $str);
3108 * undo function for htmlspecialchars()
3110 * This function translates HTML entities created by htmlspecialchars() back
3111 * into their ASCII equivalents. Also handles the entities for $, {, and }.
3113 * @param string $string The string to convert.
3114 * @return string The converted string.
3117 function COM_undoSpecialChars( $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 );
3126 $string = str_replace( '&', '&', $string );
3132 * Makes an ID based on current date/time
3134 * This function creates a 17 digit sid for stories based on the 14 digit date
3135 * and a 3 digit random number that was seeded with the number of microseconds
3136 * (.000001th of a second) since the last full second.
3137 * NOTE: this is now used for more than just stories!
3139 * @return string $sid Story ID
3142 function COM_makesid()
3144 $sid = date( 'YmdHis' );
3145 $sid .= rand( 0, 999 );
3151 * Checks to see if email address is valid.
3153 * This function checks to see if an email address is in the correct from.
3155 * @param string $email Email address to verify
3156 * @return boolean True if valid otherwise false
3159 function COM_isEmail( $email )
3161 require_once( 'Mail/RFC822.php' );
3163 $rfc822 = new Mail_RFC822;
3165 return( $rfc822->isValidInetAddress( $email ) ? true : false );
3170 * Encode a string such that it can be used in an email header
3172 * @param string $string the text to be encoded
3173 * @return string encoded text
3176 function COM_emailEscape( $string )
3180 if (function_exists('CUSTOM_emailEscape')) {
3181 return CUSTOM_emailEscape($string);
3184 $charset = COM_getCharset();
3185 if(( $charset == 'utf-8' ) && ( $string != utf8_decode( $string )))
3187 if( function_exists( 'iconv_mime_encode' ))
3189 $mime_parameters = array( 'input-charset' => 'utf-8',
3190 'output-charset' => 'utf-8',
3191 // 'Q' encoding is more readable than 'B'
3194 $string = substr( iconv_mime_encode( '', $string,
3195 $mime_parameters ), 2 );
3199 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3202 else if( preg_match( '/[^0-9a-z\-\.,:;\?! ]/i', $string ))
3204 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3211 * Takes a name and an email address and returns a string that vaguely
3212 * resembles an email address specification conforming to RFC(2)822 ...
3214 * @param string $name name, e.g. John Doe
3215 * @param string $address email address only, e.g. john.doe@example.com
3216 * @return string formatted email address
3219 function COM_formatEmailAddress($name, $address)
3221 $name = trim($name);
3222 $address = trim($address);
3224 if (function_exists('CUSTOM_formatEmailAddress')) {
3225 return CUSTOM_formatEmailAddress($name, $address);
3228 $formatted_name = COM_emailEscape($name);
3230 // if the name comes back unchanged, it's not UTF-8, so preg_match is fine
3231 if (($formatted_name == $name) && preg_match('/[^0-9a-z ]/i', $name)) {
3232 $formatted_name = str_replace('"', '\\"', $formatted_name);
3233 $formatted_name = '"' . $formatted_name . '"';
3236 return $formatted_name . ' <' . $address . '>';
3242 * All emails sent by Geeklog are sent through this function.
3244 * NOTE: Please note that using CC: will expose the email addresses of
3245 * all recipients. Use with care.
3247 * @param string $to recipients name and email address
3248 * @param string $subject subject of the email
3249 * @param string $message the text of the email
3250 * @param string $from (optional) sender of the the email
3251 * @param boolean $html (optional) true if to be sent as HTML email
3252 * @param int $priority (optional) add X-Priority header, if > 0
3253 * @param mixed $optional (optional) other headers or CC:
3254 * @return boolean true if successful, otherwise false
3257 function COM_mail($to, $subject, $message, $from = '', $html = false, $priority = 0, $optional = null)
3264 $from = COM_formatEmailAddress($_CONF['site_name'], $_CONF['site_mail']);
3267 $to = substr($to, 0, strcspn($to, "\r\n"));
3268 if (($optional != null) && !is_array($optional)) {
3269 $optional = substr($optional, 0, strcspn($optional, "\r\n"));
3271 $from = substr($from, 0, strcspn($from, "\r\n"));
3272 $subject = substr($subject, 0, strcspn($subject, "\r\n"));
3273 $subject = COM_emailEscape($subject);
3275 if (function_exists('CUSTOM_mail')) {
3276 return CUSTOM_mail($to, $subject, $message, $from, $html, $priority,
3280 include_once 'Mail.php';
3281 include_once 'Mail/RFC822.php';
3283 $method = $_CONF['mail_settings']['backend'];
3285 if (! isset($mailobj)) {
3286 if (($method == 'sendmail') || ($method == 'smtp')) {
3287 $mailobj =& Mail::factory($method, $_CONF['mail_settings']);
3290 $mailobj =& Mail::factory($method);
3294 $charset = COM_getCharset();
3297 $headers['From'] = $from;
3298 if ($method != 'mail') {
3299 $headers['To'] = $to;
3301 if (($optional != null) && !is_array($optional) && !empty($optional)) {
3302 // assume old (optional) CC: header
3303 $headers['Cc'] = $optional;
3305 $headers['Date'] = date('r'); // RFC822 formatted date
3306 if($method == 'smtp') {
3307 list($usec, $sec) = explode(' ', microtime());
3308 $m = substr($usec, 2, 5);
3309 $headers['Message-Id'] = '<' . date('YmdHis') . '.' . $m
3310 . '@' . $_CONF['mail_settings']['host'] . '>';
3313 $headers['Content-Type'] = 'text/html; charset=' . $charset;
3314 $headers['Content-Transfer-Encoding'] = '8bit';
3316 $headers['Content-Type'] = 'text/plain; charset=' . $charset;
3318 $headers['Subject'] = $subject;
3319 if ($priority > 0) {
3320 $headers['X-Priority'] = $priority;
3322 $headers['X-Mailer'] = 'Geeklog ' . VERSION;
3324 if (!empty($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['SERVER_ADDR']) &&
3325 ($_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR'])) {
3326 $url = COM_getCurrentURL();
3327 if (substr($url, 0, strlen($_CONF['site_admin_url']))
3328 != $_CONF['site_admin_url']) {
3329 $headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
3333 // add optional headers last
3334 if (($optional != null) && is_array($optional)) {
3335 foreach ($optional as $h => $v) {
3340 $retval = $mailobj->send($to, $headers, $message);
3341 if ($retval !== true) {
3342 COM_errorLog($retval->toString(), 1);
3345 return($retval === true ? true : false);
3350 * Creates older stuff block
3352 * Creates the olderstuff block for display.
3353 * Actually updates the olderstuff record in the gl_blocks database.
3357 function COM_olderStuff()
3359 global $_TABLES, $_CONF;
3361 $sql = "SELECT sid,tid,title,comments,UNIX_TIMESTAMP(date) AS day FROM {$_TABLES['stories']} WHERE (perm_anon = 2) AND (frontpage = 1) AND (date <= NOW()) AND (draft_flag = 0)" . COM_getTopicSQL( 'AND', 1 ) . " ORDER BY featured DESC, date DESC LIMIT {$_CONF['limitnews']}, {$_CONF['limitnews']}";
3362 $result = DB_query( $sql );
3363 $nrows = DB_numRows( $result );
3367 $dateonly = $_CONF['dateonly'];
3368 if( empty( $dateonly ))
3370 $dateonly = '%d-%b'; // fallback: day - abbrev. month name
3376 for( $i = 0; $i < $nrows; $i++ )
3378 $A = DB_fetchArray( $result );
3380 $daycheck = strftime( '%A', $A['day'] );
3381 if( $day != $daycheck )
3383 if( $day != 'noday' )
3385 $daylist = COM_makeList($oldnews, 'list-older-stories');
3386 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3387 $string .= $daylist . '<br' . XHTML . '>';
3390 $day2 = strftime( $dateonly, $A['day'] );
3391 $string .= '<h3>' . $daycheck . ' <small>' . $day2
3392 . '</small></h3>' . LB;
3397 $oldnews_url = COM_buildUrl( $_CONF['site_url'] . '/article.php?story='
3399 $oldnews[] = COM_createLink($A['title'], $oldnews_url)
3400 .' (' . COM_numberFormat( $A['comments'] ) . ')';
3403 if( !empty( $oldnews ))
3405 $daylist = COM_makeList($oldnews, 'list-older-stories');
3406 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3407 $string .= $daylist;
3408 $string = addslashes( $string );
3410 DB_query( "UPDATE {$_TABLES['blocks']} SET content = '$string' WHERE name = 'older_stories'" );
3416 * Shows a single Geeklog block
3418 * This shows a single block and is typically called from
3419 * COM_showBlocks OR from plugin code
3421 * @param string $name Logical name of block (not same as title) -- 'user_block', 'admin_block', 'section_block', 'whats_new_block'.
3422 * @param string $help Help file location
3423 * @param string $title Title shown in block header
3424 * @param string $position Side, 'left', 'right' or empty.
3425 * @see function COM_showBlocks
3426 * @return string HTML Formated block
3430 function COM_showBlock( $name, $help='', $title='', $position='' )
3432 global $_CONF, $topic, $_TABLES, $_USER;
3436 if( !isset( $_USER['noboxes'] ))
3438 if( !COM_isAnonUser() )
3440 $_USER['noboxes'] = DB_getItem( $_TABLES['userindex'], 'noboxes',
3441 "uid = {$_USER['uid']}" );
3445 $_USER['noboxes'] = 0;
3452 $retval .= COM_userMenu( $help,$title, $position );
3456 $retval .= COM_adminMenu( $help,$title, $position );
3459 case 'section_block':
3460 $retval .= COM_startBlock( $title, $help,
3461 COM_getBlockTemplate( $name, 'header', $position ))
3462 . COM_showTopics( $topic )
3463 . COM_endBlock( COM_getBlockTemplate( $name, 'footer', $position ));
3466 case 'whats_new_block':
3467 if( !$_USER['noboxes'] )
3469 $retval .= COM_whatsNewBlock( $help, $title, $position );
3479 * Shows Geeklog blocks
3481 * Returns HTML for blocks on a given side and, potentially, for
3482 * a given topic. Currently only used by static pages.
3484 * @param string $side Side to get blocks for (right or left for now)
3485 * @param string $topic Only get blocks for this topic
3486 * @param string $name Block name (not used)
3487 * @see function COM_showBlock
3488 * @return string HTML Formated blocks
3492 function COM_showBlocks( $side, $topic='', $name='all' )
3494 global $_CONF, $_TABLES, $_USER, $LANG21, $topic, $page;
3498 // Get user preferences on blocks
3499 if( !isset( $_USER['noboxes'] ) || !isset( $_USER['boxes'] ))
3501 if( !COM_isAnonUser() )
3503 $result = DB_query( "SELECT boxes,noboxes FROM {$_TABLES['userindex']} "
3504 ."WHERE uid = '{$_USER['uid']}'" );
3505 list($_USER['boxes'], $_USER['noboxes']) = DB_fetchArray( $result );
3509 $_USER['boxes'] = '';
3510 $_USER['noboxes'] = 0;
3514 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3515 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3516 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3518 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3520 $commonsql = "FROM {$_TABLES['blocks']} WHERE is_enabled = 1";
3522 if( $side == 'left' )
3524 $commonsql .= " AND onleft = 1";
3528 $commonsql .= " AND onleft = 0";
3531 if( !empty( $topic ))
3533 $tp = addslashes($topic);
3534 $commonsql .= " AND (tid = '$tp' OR tid = 'all')";
3538 if( COM_onFrontpage() )
3540 $commonsql .= " AND (tid = 'homeonly' OR tid = 'all')";
3544 $commonsql .= " AND (tid = 'all')";
3548 if( !empty( $_USER['boxes'] ))
3550 $BOXES = str_replace( ' ', ',', $_USER['boxes'] );
3552 $commonsql .= " AND (bid NOT IN ($BOXES) OR bid = '-1')";
3555 $commonsql .= ' ORDER BY blockorder,title ASC';
3557 $blocksql['mysql'] .= $commonsql;
3558 $blocksql['mssql'] .= $commonsql;
3559 $result = DB_query( $blocksql );
3560 $nrows = DB_numRows( $result );
3562 // convert result set to an array of associated arrays
3564 for( $i = 0; $i < $nrows; $i++ )
3566 $blocks[] = DB_fetchArray( $result );
3569 // Check and see if any plugins have blocks to show
3570 $pluginBlocks = PLG_getBlocks( $side, $topic, $name );
3571 $blocks = array_merge( $blocks, $pluginBlocks );
3573 // sort the resulting array by block order
3574 $column = 'blockorder';
3575 $sortedBlocks = $blocks;
3576 $num_sortedBlocks = count( $sortedBlocks );
3577 for( $i = 0; $i < $num_sortedBlocks - 1; $i++ )
3579 for( $j = 0; $j < $num_sortedBlocks - 1 - $i; $j++ )
3581 if( $sortedBlocks[$j][$column] > $sortedBlocks[$j+1][$column] )
3583 $tmp = $sortedBlocks[$j];
3584 $sortedBlocks[$j] = $sortedBlocks[$j + 1];
3585 $sortedBlocks[$j + 1] = $tmp;
3589 $blocks = $sortedBlocks;
3591 // Loop though resulting sorted array and pass associative arrays
3592 // to COM_formatBlock
3593 foreach( $blocks as $A )
3595 if( $A['type'] == 'dynamic' or SEC_hasAccess( $A['owner_id'], $A['group_id'], $A['perm_owner'], $A['perm_group'], $A['perm_members'], $A['perm_anon'] ) > 0 )
3597 $retval .= COM_formatBlock( $A, $_USER['noboxes'] );
3605 * Formats a Geeklog block
3607 * This shows a single block and is typically called from
3608 * COM_showBlocks OR from plugin code
3610 * @param array $A Block Record
3611 * @param boolean $noboxes Set to true if userpref is no blocks
3612 * @return string HTML Formated block
3615 function COM_formatBlock( $A, $noboxes = false )
3617 global $_CONF, $_TABLES, $_USER, $LANG21;
3621 $lang = COM_getLanguageId();
3622 if (!empty($lang)) {
3624 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3625 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3626 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3628 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3630 $commonsql = "FROM {$_TABLES['blocks']} WHERE name = '"
3631 . $A['name'] . '_' . $lang . "'";
3633 $blocksql['mysql'] .= $commonsql;
3634 $blocksql['mssql'] .= $commonsql;
3635 $result = DB_query( $blocksql );
3637 if (DB_numRows($result) == 1) {
3638 // overwrite with data for language-specific block
3639 $A = DB_fetchArray($result);
3643 if( array_key_exists( 'onleft', $A ) )
3645 if( $A['onleft'] == 1 )
3649 $position = 'right';
3655 if( $A['type'] == 'portal' )
3657 if( COM_rdfCheck( $A['bid'], $A['rdfurl'], $A['date'], $A['rdflimit'] ))
3659 $A['content'] = DB_getItem( $_TABLES['blocks'], 'content',
3660 "bid = '{$A['bid']}'");
3664 if( $A['type'] == 'gldefault' )
3666 $retval .= COM_showBlock( $A['name'], $A['help'], $A['title'], $position );
3669 if( $A['type'] == 'phpblock' && !$noboxes )
3671 if( !( $A['name'] == 'whosonline_block' AND DB_getItem( $_TABLES['blocks'], 'is_enabled', "name='whosonline_block'" ) == 0 ))
3673 $function = $A['phpblockfn'];
3675 if (preg_match('/^(phpblock_\w*)\\((.*)\\)$/', $function, $matches) == 1)
3677 $function = $matches[1];
3678 $args = $matches[2];
3680 $blkheader = COM_startBlock( $A['title'], $A['help'],
3681 COM_getBlockTemplate( $A['name'], 'header', $position ));
3682 $blkfooter = COM_endBlock( COM_getBlockTemplate( $A['name'],
3683 'footer', $position ));
3685 if( function_exists( $function ))
3689 $fretval = $function($A, $args);
3691 $fretval = $function();
3693 if( !empty( $fretval ))
3695 $retval .= $blkheader;
3696 $retval .= $fretval;
3697 $retval .= $blkfooter;
3702 // show error message
3703 $retval .= $blkheader;
3704 $retval .= sprintf( $LANG21[31], $function );
3705 $retval .= $blkfooter;
3710 if( !empty( $A['content'] ) && ( trim( $A['content'] ) != '' ) && !$noboxes )
3712 $blockcontent = stripslashes( $A['content'] );
3714 // Hack: If the block content starts with a '<' assume it
3715 // contains HTML and do not call nl2br() which would only add
3716 // unwanted <br> tags.
3718 if( substr( $blockcontent, 0, 1 ) != '<' )
3720 $blockcontent = nl2br( $blockcontent );
3723 // autotags are only(!) allowed in normal blocks
3724 if(( $A['allow_autotags'] == 1 ) && ( $A['type'] == 'normal' ))
3726 $blockcontent = PLG_replaceTags( $blockcontent );
3728 $blockcontent = str_replace( array( '<?', '?>' ), '', $blockcontent );
3730 $retval .= COM_startBlock( $A['title'], $A['help'],
3731 COM_getBlockTemplate( $A['name'], 'header', $position ))
3732 . $blockcontent . LB
3733 . COM_endBlock( COM_getBlockTemplate( $A['name'], 'footer', $position ));
3741 * Checks to see if it's time to import and RDF/RSS block again
3743 * Updates RDF/RSS block if needed
3745 * @param string $bid Block ID
3746 * @param string $rdfurl URL to get headlines from
3747 * @param string $date Last time the headlines were imported
3748 * @param string $maxheadlines max. number of headlines to import
3750 * @see function COM_rdfImport
3753 function COM_rdfCheck( $bid, $rdfurl, $date, $maxheadlines = 0 )
3756 $nextupdate = $date + 3600;
3758 if( $nextupdate < time() )
3760 COM_rdfImport( $bid, $rdfurl, $maxheadlines );
3768 * Syndication import function. Imports headline data to a portal block.
3770 * Rewritten December 19th 2004 by Michael Jervis (mike AT fuckingbrit DOT com).
3771 * Now utilises a Factory Pattern to open a URL and automaticaly retreive a feed
3772 * object populated with feed data. Then import it into the portal block.
3774 * @param string $bid Block ID
3775 * @param string $rdfurl URL to get content from
3776 * @param int $maxheadlines Maximum number of headlines to display
3778 * @see function COM_rdfCheck
3781 function COM_rdfImport($bid, $rdfurl, $maxheadlines = 0)
3783 global $_CONF, $_TABLES, $LANG21;
3785 // Import the feed handling classes:
3786 require_once $_CONF['path_system']
3787 . '/classes/syndication/parserfactory.class.php';
3788 require_once $_CONF['path_system']
3789 . '/classes/syndication/feedparserbase.class.php';
3791 $result = DB_query("SELECT rdf_last_modified, rdf_etag FROM {$_TABLES['blocks']} WHERE bid = $bid");
3792 list($last_modified, $etag) = DB_fetchArray($result);
3794 // Load the actual feed handlers:
3795 $factory = new FeedParserFactory($_CONF['path_system']
3796 . '/classes/syndication/');
3797 $factory->userAgent = 'Geeklog/' . VERSION;
3798 if (!empty($last_modified) && !empty($etag)) {
3799 $factory->lastModified = $last_modified;
3800 $factory->eTag = $etag;
3804 $feed = $factory->reader($rdfurl, $_CONF['default_charset']);
3807 /* We have located a reader, and populated it with the information from
3808 * the syndication file. Now we will sort out our display, and update
3811 if ($maxheadlines == 0) {
3812 if (!empty($_CONF['syndication_max_headlines'])) {
3813 $maxheadlines = $_CONF['syndication_max_headlines'];
3815 $maxheadlines = count($feed->articles);
3819 $update = date('Y-m-d H:i:s');
3820 $last_modified = '';
3821 if (!empty($factory->lastModified)) {
3822 $last_modified = addslashes($factory->lastModified);
3825 if (!empty($factory->eTag)) {
3826 $etag = addslashes($factory->eTag);
3829 if (empty($last_modified) || empty($etag)) {
3830 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = '$bid'");
3832 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = '$last_modified', rdf_etag = '$etag' WHERE bid = '$bid'");
3835 $charset = COM_getCharset();
3837 // format articles for display
3838 $readmax = min($maxheadlines, count($feed->articles));
3839 for ($i = 0; $i < $readmax; $i++) {
3840 if (empty($feed->articles[$i]['title'])) {
3841 $feed->articles[$i]['title'] = $LANG21[61];
3844 if ($charset == 'utf-8') {
3845 $title = $feed->articles[$i]['title'];
3847 $title = utf8_decode($feed->articles[$i]['title']);
3849 if ($feed->articles[$i]['link'] != '') {
3850 $content = COM_createLink($title, $feed->articles[$i]['link']);
3851 } elseif ($feed->articles[$i]['enclosureurl'] != '') {
3852 $content = COM_createLink($title, $feed->articles[$i]['enclosureurl']);
3856 $articles[] = $content;
3860 $content = COM_makeList($articles, 'list-feed');
3861 $content = str_replace(array("\015", "\012"), '', $content);
3863 if (strlen($content) > 65000) {
3864 $content = $LANG21[68];
3867 // Standard theme based function to put it in the block
3868 $result = DB_change($_TABLES['blocks'], 'content',
3869 addslashes($content), 'bid', $bid);
3870 } else if ($factory->errorStatus !== false) {
3871 // failed to aquire info, 0 out the block and log an error
3872 COM_errorLog("Unable to aquire feed reader for $rdfurl", 1);
3873 COM_errorLog($factory->errorStatus[0] . ' ' .
3874 $factory->errorStatus[1] . ' ' .
3875 $factory->errorStatus[2]);
3876 $content = addslashes($LANG21[4]);
3877 DB_query("UPDATE {$_TABLES['blocks']} SET content = '$content', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = $bid");
3883 * Returns what HTML is allowed in content
3885 * Returns what HTML tags the system allows to be used inside content.
3886 * You can modify this by changing $_CONF['user_html'] in the configuration
3887 * (for admins, see also $_CONF['admin_html']).
3889 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
3890 * @param boolean $list_only true = return only the list of HTML tags
3891 * @return string HTML <div>/<span> enclosed string
3892 * @see function COM_checkHTML
3893 * @todo Bugs: The list always includes the [code], [raw], and [page_break]
3894 * tags when story.* permissions are required, even when those tags
3895 * are not actually available (e.g. in comments on stories).
3898 function COM_allowedHTML($permissions = 'story.edit', $list_only = false)
3900 global $_CONF, $LANG01;
3904 if (isset($_CONF['skip_html_filter_for_root']) &&
3905 ($_CONF['skip_html_filter_for_root'] == 1) &&
3906 SEC_inGroup('Root')) {
3909 $retval .= '<span class="warningsmall">' . $LANG01[123]
3912 $retval .= '<div dir="ltr" class="warningsmall">';
3917 $retval .= '<span class="warningsmall">' . $LANG01[31] . '</span> ';
3920 if (empty($permissions) || !SEC_hasRights($permissions) ||
3921 empty($_CONF['admin_html'])) {
3922 $html = $_CONF['user_html'];
3924 $html = array_merge_recursive($_CONF['user_html'],
3925 $_CONF['admin_html']);
3928 $retval .= '<div dir="ltr" class="warningsmall">';
3929 foreach ($html as $tag => $attr) {
3930 $retval .= '<' . $tag . '>, ';
3934 $with_story_perms = false;
3935 $perms = explode(',', $permissions);
3936 foreach ($perms as $p) {
3937 if (substr($p, 0, 6) == 'story.') {
3938 $with_story_perms = true;
3943 if ($with_story_perms) {
3944 $retval .= '[code], [raw], ';
3946 if ($_CONF['allow_page_breaks'] == 1) {
3947 $retval .= '[page_break], ';
3952 $autotags = array_keys(PLG_collectTags());
3953 $retval .= '[' . implode(':], [', $autotags) . ':]';
3954 $retval .= '</div>';
3960 * Return the password for the given username
3962 * Fetches a password for the given user
3964 * @param string $loginname username to get password for
3965 * @return string Password or ''
3969 function COM_getPassword( $loginname )
3971 global $_TABLES, $LANG01;
3973 $result = DB_query( "SELECT passwd FROM {$_TABLES['users']} WHERE username='$loginname'" );
3975 $nrows = DB_numRows( $result );
3977 if(( $tmp == 0 ) && ( $nrows == 1 ))
3979 $U = DB_fetchArray( $result );
3980 return $U['passwd'];
3984 $tmp = $LANG01[32] . ": '" . $loginname . "'";
3985 COM_errorLog( $tmp, 1 );
3993 * Return the username or fullname for the passed member id (uid)
3995 * Allows the siteAdmin to determine if loginname (username) or fullname
3996 * should be displayed.
3998 * @param int $uid site member id
3999 * @param string $username Username, if this is set no lookup is done.
4000 * @param string $fullname Users full name.
4001 * @param string $remoteusername Username on remote service
4002 * @param string $remoteservice Remote login service.
4003 * @return string Username, fullname or username@Service
4006 function COM_getDisplayName( $uid = '', $username='', $fullname='', $remoteusername='', $remoteservice='' )
4008 global $_CONF, $_TABLES, $_USER;
4012 if( COM_isAnonUser() )
4018 $uid = $_USER['uid'];
4022 if( empty( $username ))
4024 $query = DB_query( "SELECT username, fullname, remoteusername, remoteservice FROM {$_TABLES['users']} WHERE uid='$uid'" );
4025 list( $username, $fullname, $remoteusername, $remoteservice ) = DB_fetchArray( $query );
4028 if( !empty( $fullname ) && ($_CONF['show_fullname'] == 1 ))
4032 else if(( $_CONF['user_login_method']['3rdparty'] || $_CONF['user_login_method']['openid'] ) && !empty( $remoteusername ))
4034 if( !empty( $username ))
4036 $remoteusername = $username;
4039 if( $_CONF['show_servicename'] )
4041 return "$remoteusername@$remoteservice";
4045 return $remoteusername;
4054 * Adds a hit to the system
4056 * This function is called in the footer of every page and is used to
4057 * track the number of hits to the Geeklog system. This information is
4058 * shown on stats.php
4066 DB_change($_TABLES['vars'], 'value', 'value + 1', 'name', 'totalhits', '', true);
4070 * This will email new stories in the topics that the user is interested in
4072 * In account information the user can specify which topics for which they
4073 * will receive any new article for in a daily digest.
4078 function COM_emailUserTopics()
4080 global $_CONF, $_TABLES, $LANG04, $LANG08, $LANG24;
4082 if ($_CONF['emailstories'] == 0) {
4086 $subject = strip_tags( $_CONF['site_name'] . $LANG08[30] . strftime( '%Y-%m-%d', time() ));
4090 // Get users who want stories emailed to them
4091 $usersql = "SELECT username,email,etids,{$_TABLES['users']}.uid AS uuid "
4092 . "FROM {$_TABLES['users']}, {$_TABLES['userindex']} "
4093 . "WHERE {$_TABLES['users']}.uid > 1 AND {$_TABLES['userindex']}.uid = {$_TABLES['users']}.uid AND (etids <> '-' OR etids IS NULL) ORDER BY {$_TABLES['users']}.uid";
4095 $users = DB_query( $usersql );
4096 $nrows = DB_numRows( $users );
4098 $lastrun = DB_getItem( $_TABLES['vars'], 'value', "name = 'lastemailedstories'" );
4100 // For each user, pull the stories they want and email it to them
4101 for( $x = 0; $x < $nrows; $x++ )
4103 $U = DB_fetchArray( $users );
4105 $storysql = array();
4106 $storysql['mysql'] = "SELECT sid,uid,date AS day,title,introtext,postmode";
4108 $storysql['mssql'] = "SELECT sid,uid,date AS day,title,CAST(introtext AS text) AS introtext,postmode";
4110 $commonsql = " FROM {$_TABLES['stories']} WHERE draft_flag = 0 AND date <= NOW() AND date >= '{$lastrun}'";
4112 $topicsql = "SELECT tid FROM {$_TABLES['topics']}"
4113 . COM_getPermSQL( 'WHERE', $U['uuid'] );
4114 $tresult = DB_query( $topicsql );
4115 $trows = DB_numRows( $tresult );
4119 // this user doesn't seem to have access to any topics ...
4124 for( $i = 0; $i < $trows; $i++ )
4126 $T = DB_fetchArray( $tresult );
4127 $TIDS[] = $T['tid'];
4130 if( !empty( $U['etids'] ))
4132 $ETIDS = explode( ' ', $U['etids'] );
4133 $TIDS = array_intersect( $TIDS, $ETIDS );
4136 if( count( $TIDS ) > 0)
4138 $commonsql .= " AND (tid IN ('" . implode( "','", $TIDS ) . "'))";
4141 $commonsql .= COM_getPermSQL( 'AND', $U['uuid'] );
4142 $commonsql .= ' ORDER BY featured DESC, date DESC';
4144 $storysql['mysql'] .= $commonsql;
4145 $storysql['mssql'] .= $commonsql;
4147 $stories = DB_query( $storysql );
4148 $nsrows = DB_numRows( $stories );
4152 // If no new stories where pulled for this user, continue with next
4156 $mailtext = $LANG08[29] . strftime( $_CONF['shortdate'], time() ) . "\n";
4158 for( $y = 0; $y < $nsrows; $y++ )
4160 // Loop through stories building the requested email message
4161 $S = DB_fetchArray( $stories );
4163 $mailtext .= "\n------------------------------\n\n";
4164 $mailtext .= "$LANG08[31]: "
4165 . COM_undoSpecialChars( stripslashes( $S['title'] )) . "\n";
4166 if( $_CONF['contributedbyline'] == 1 )
4168 if( empty( $authors[$S['uid']] ))
4170 $storyauthor = COM_getDisplayName ($S['uid']);
4171 $authors[$S['uid']] = $storyauthor;
4175 $storyauthor = $authors[$S['uid']];
4177 $mailtext .= "$LANG24[7]: " . $storyauthor . "\n";
4180 $mailtext .= "$LANG08[32]: " . strftime( $_CONF['date'], strtotime( $S['day' ])) . "\n\n";
4182 if( $_CONF['emailstorieslength'] > 0 )
4184 if($S['postmode']==='wikitext'){
4185 $storytext = COM_undoSpecialChars( strip_tags( COM_renderWikiText ( stripslashes( $S['introtext'] ))));
4187 $storytext = COM_undoSpecialChars( strip_tags( PLG_replaceTags( stripslashes( $S['introtext'] ))));
4190 if( $_CONF['emailstorieslength'] > 1 )
4192 $storytext = COM_truncate( $storytext,
4193 $_CONF['emailstorieslength'], '...' );
4196 $mailtext .= $storytext . "\n\n";
4199 $mailtext .= $LANG08[33] . ' ' . COM_buildUrl( $_CONF['site_url']
4200 . '/article.php?story=' . $S['sid'] ) . "\n";
4203 $mailtext .= "\n------------------------------\n";
4204 $mailtext .= "\n$LANG08[34]\n";
4205 $mailtext .= "\n------------------------------\n";
4207 $mailto = $U['username'] . ' <' . $U['email'] . '>';
4209 if ($_CONF['site_mail'] !== $_CONF['noreply_mail']) {
4210 $mailfrom = $_CONF['noreply_mail'];
4211 $mailtext .= LB . LB . $LANG04[159];
4213 $mailfrom = $_CONF['site_mail'];
4215 COM_mail( $mailto, $subject, $mailtext , $mailfrom);
4218 DB_query( "UPDATE {$_TABLES['vars']} SET value = NOW() WHERE name = 'lastemailedstories'" );
4222 * Shows any new information in a block
4224 * Return the HTML that shows any new stories, comments, etc
4226 * @param string $help Help file for block
4227 * @param string $title Title used in block header
4228 * @param string $position Position in which block is being rendered 'left', 'right' or blank (for centre)
4229 * @return string Return the HTML that shows any new stories, comments, etc
4233 function COM_whatsNewBlock( $help = '', $title = '', $position = '' )
4235 global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_WHATSNEW, $page, $newstories;
4237 $retval = COM_startBlock( $title, $help,
4238 COM_getBlockTemplate( 'whats_new_block', 'header', $position ));
4241 if(( $_CONF['hidenewstories'] == 0 ) || ( $_CONF['hidenewcomments'] == 0 )
4242 || ( $_CONF['trackback_enabled']
4243 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4245 $topicsql = COM_getTopicSql ('AND', 0, $_TABLES['stories']);
4248 if( $_CONF['hidenewstories'] == 0 )
4251 $archivetid = DB_getItem( $_TABLES['topics'], 'tid', "archive_flag=1" );
4252 if( !empty( $archivetid ))
4254 $archsql = " AND (tid <> '" . addslashes( $archivetid ) . "')";
4257 // Find the newest stories
4258 $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (date >= (date_sub(NOW(), INTERVAL {$_CONF['newstoriesinterval']} SECOND))) AND (date <= NOW()) AND (draft_flag = 0)" . $archsql . COM_getPermSQL( 'AND' ) . $topicsql . COM_getLangSQL( 'sid', 'AND' );
4259 $result = DB_query( $sql );
4260 $A = DB_fetchArray( $result );
4261 $nrows = $A['count'];
4263 if( empty( $title ))
4265 $title = DB_getItem( $_TABLES['blocks'], 'title', "name='whats_new_block'" );
4268 // Any late breaking news stories?
4269 $retval .= '<h3>' . $LANG01[99] . '</h3>';
4273 $newmsg = COM_formatTimeString( $LANG_WHATSNEW['new_string'],
4274 $_CONF['newstoriesinterval'], $LANG01[11], $nrows);
4276 if( $newstories && ( $page < 2 ))
4278 $retval .= $newmsg . '<br' . XHTML . '>';
4282 $retval .= COM_createLink($newmsg, $_CONF['site_url']
4283 . '/index.php?display=new') . '<br' . XHTML . '>';
4288 $retval .= $LANG01[100] . '<br' . XHTML . '>';
4291 if(( $_CONF['hidenewcomments'] == 0 ) || ( $_CONF['trackback_enabled']
4292 && ( $_CONF['hidenewtrackbacks'] == 0 ))
4293 || ( $_CONF['hidenewplugins'] == 0 ))
4295 $retval .= '<br' . XHTML . '>';
4299 if( $_CONF['hidenewcomments'] == 0 )
4301 // Go get the newest comments
4302 $retval .= '<h3>' . $LANG01[83] . ' <small>'
4303 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4304 $_CONF['newcommentsinterval'] )
4309 if( !COM_isAnonUser() )
4311 $stwhere .= "({$_TABLES['stories']}.owner_id IS NOT NULL AND {$_TABLES['stories']}.perm_owner IS NOT NULL) OR ";
4312 $stwhere .= "({$_TABLES['stories']}.group_id IS NOT NULL AND {$_TABLES['stories']}.perm_group IS NOT NULL) OR ";
4313 $stwhere .= "({$_TABLES['stories']}.perm_members IS NOT NULL)";
4317 $stwhere .= "({$_TABLES['stories']}.perm_anon IS NOT NULL)";
4319 $sql = "SELECT DISTINCT COUNT(*) AS dups, type, {$_TABLES['stories']}.title, {$_TABLES['stories']}.sid, max({$_TABLES['comments']}.date) AS lastdate FROM {$_TABLES['comments']} LEFT JOIN {$_TABLES['stories']} ON (({$_TABLES['stories']}.sid = {$_TABLES['comments']}.sid)" . COM_getPermSQL( 'AND', 0, 2, $_TABLES['stories'] ) . " AND ({$_TABLES['stories']}.draft_flag = 0) AND ({$_TABLES['stories']}.commentcode >= 0)" . $topicsql . COM_getLangSQL( 'sid', 'AND', $_TABLES['stories'] ) . ") WHERE ({$_TABLES['comments']}.date >= (DATE_SUB(NOW(), INTERVAL {$_CONF['newcommentsinterval']} SECOND))) AND ((({$stwhere}))) GROUP BY {$_TABLES['comments']}.sid,type, {$_TABLES['stories']}.title, {$_TABLES['stories']}.title, {$_TABLES['stories']}.sid ORDER BY 5 DESC LIMIT 15";
4321 $result = DB_query( $sql );
4323 $nrows = DB_numRows( $result );
4327 $newcomments = array();
4329 for( $x = 0; $x < $nrows; $x++ )
4331 $A = DB_fetchArray( $result );
4333 if(( $A['type'] == 'article' ) || empty( $A['type'] ))
4335 $url = COM_buildUrl( $_CONF['site_url']
4336 . '/article.php?story=' . $A['sid'] ) . '#comments';
4339 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4340 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4342 if( $title != $titletouse )
4344 $attr = array('title' => htmlspecialchars($title));
4350 $acomment = str_replace( '$', '$', $titletouse );
4351 $acomment = str_replace( ' ', ' ', $acomment );
4353 if( $A['dups'] > 1 )
4355 $acomment .= ' [+' . $A['dups'] . ']';
4358 $newcomments[] = COM_createLink($acomment, $url, $attr);
4361 $retval .= COM_makeList( $newcomments, 'list-new-comments' );
4365 $retval .= $LANG01[86] . '<br' . XHTML . '>' . LB;
4367 if(( $_CONF['hidenewplugins'] == 0 )
4368 || ( $_CONF['trackback_enabled']
4369 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4371 $retval .= '<br' . XHTML . '>';
4375 if( $_CONF['trackback_enabled'] && ( $_CONF['hidenewtrackbacks'] == 0 ))
4377 $retval .= '<h3>' . $LANG01[114] . ' <small>'
4378 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4379 $_CONF['newtrackbackinterval'] )
4382 $sql = "SELECT DISTINCT COUNT(*) AS count,{$_TABLES['stories']}.title,t.sid,max(t.date) AS lastdate FROM {$_TABLES['trackback']} AS t,{$_TABLES['stories']} WHERE (t.type = 'article') AND (t.sid = {$_TABLES['stories']}.sid) AND (t.date >= (DATE_SUB(NOW(), INTERVAL {$_CONF['newtrackbackinterval']} SECOND)))" . COM_getPermSQL( 'AND', 0, 2, $_TABLES['stories'] ) . " AND ({$_TABLES['stories']}.draft_flag = 0) AND ({$_TABLES['stories']}.trackbackcode = 0)" . $topicsql . COM_getLangSQL( 'sid', 'AND', $_TABLES['stories'] ) . " GROUP BY t.sid, {$_TABLES['stories']}.title ORDER BY lastdate DESC LIMIT 15";
4383 $result = DB_query( $sql );
4385 $nrows = DB_numRows( $result );
4388 $newcomments = array();
4390 for( $i = 0; $i < $nrows; $i++ )
4392 $A = DB_fetchArray( $result );
4394 $url = COM_buildUrl( $_CONF['site_url']
4395 . '/article.php?story=' . $A['sid'] ) . '#trackback';
4397 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4398 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4401 if( $title != $titletouse )
4403 $attr = array('title' => htmlspecialchars($title));
4409 $acomment = str_replace( '$', '$', $titletouse );
4410 $acomment = str_replace( ' ', ' ', $acomment );
4412 if( $A['count'] > 1 )
4414 $acomment .= ' [+' . $A['count'] . ']';
4417 $newcomments[] = COM_createLink($acomment, $url, $attr);
4420 $retval .= COM_makeList( $newcomments, 'list-new-trackbacks' );
4424 $retval .= $LANG01[115] . '<br' . XHTML . '>' . LB;
4426 if( $_CONF['hidenewplugins'] == 0 )
4428 $retval .= '<br' . XHTML . '>';
4432 if( $_CONF['hidenewplugins'] == 0 )
4434 list( $headlines, $smallheadlines, $content ) = PLG_getWhatsNew();
4435 $plugins = count( $headlines );
4438 for( $i = 0; $i < $plugins; $i++ )
4440 $retval .= '<h3>' . $headlines[$i] . ' <small>'
4441 . $smallheadlines[$i] . '</small></h3>';
4442 if( is_array( $content[$i] ))
4444 $retval .= COM_makeList( $content[$i], 'list-new-plugins' );
4448 $retval .= $content[$i];
4451 if( $i + 1 < $plugins )
4453 $retval .= '<br' . XHTML . '>';
4459 $retval .= COM_endBlock( COM_getBlockTemplate( 'whats_new_block', 'footer', $position ));
4465 * Creates the string that indicates the timespan in which new items were found
4467 * @param string $time_string template string
4468 * @param int $time number of seconds in which results are found
4469 * @param string $type type (translated string) of new item
4470 * @param int $amount amount of things that have been found.
4472 function COM_formatTimeString( $time_string, $time, $type = '', $amount = 0 )
4474 global $LANG_WHATSNEW;
4476 $retval = $time_string;
4478 // This is the amount you have to divide the previous by to get the
4479 // different time intervals: hour, day, week, months
4480 $time_divider = array( 60, 60, 24, 7, 30 );
4482 // These are the respective strings to the numbers above. They have to match
4483 // the strings in $LANG_WHATSNEW (i.e. these are the keys for the array -
4484 // the actual text strings are taken from the language file).
4485 $time_description = array( 'minute', 'hour', 'day', 'week', 'month' );
4486 $times_description = array( 'minutes', 'hours', 'days', 'weeks', 'months' );
4488 $time_dividers = count( $time_divider );
4489 for( $s = 0; $s < $time_dividers; $s++ )
4491 $time = $time / $time_divider[$s];
4492 if( $time < $time_divider[$s + 1] )
4498 $time_str = $time_description[$s];
4500 else // go back to the previous unit, e.g. 1 day -> 24 hours
4502 $time_str = $times_description[$s - 1];
4503 $time *= $time_divider[$s];
4508 $time_str = $times_description[$s];
4510 $fields = array( '%n', '%i', '%t', '%s' );
4511 $values = array( $amount, $type, $time, $LANG_WHATSNEW[$time_str] );
4512 $retval = str_replace( $fields, $values, $retval );
4521 * Displays a message text in a "System Message" block
4523 * @param string $message Message text; may contain HTML
4524 * @param string $title (optional) alternative block title
4525 * @return string HTML block with message
4526 * @see COM_showMessage
4527 * @see COM_showMessageFromParameter
4530 function COM_showMessageText($message, $title = '')
4532 global $_CONF, $MESSAGE, $_IMAGE_TYPE;
4536 if (!empty($message)) {
4537 if (empty($title)) {
4538 $title = $MESSAGE[40];
4540 $timestamp = strftime($_CONF['daytime']);
4541 $retval .= COM_startBlock($title . ' - ' . $timestamp, '',
4542 COM_getBlockTemplate('_msg_block', 'header'))
4543 . '<p class="sysmessage"><img src="' . $_CONF['layout_url']
4544 . '/images/sysmessage.' . $_IMAGE_TYPE . '" alt="" ' . XHTML
4545 . '>' . $message . '</p>'
4546 . COM_endBlock(COM_getBlockTemplate('_msg_block', 'footer'));
4553 * Displays a message on the webpage
4555 * Display one of the predefined messages from the $MESSAGE array. If a plugin
4556 * name is provided, display that plugin's message instead.
4558 * @param int $msg ID of message to show
4559 * @param string $plugin Optional name of plugin to lookup plugin defined message
4560 * @return string HTML block with message
4561 * @see COM_showMessageFromParameter
4562 * @see COM_showMessageText
4565 function COM_showMessage($msg, $plugin = '')
4572 if (!empty($plugin)) {
4573 $var = 'PLG_' . $plugin . '_MESSAGE' . $msg;
4578 $message = sprintf($MESSAGE[61], $plugin);
4579 COM_errorLog($message . ": " . $var, 1);
4582 $message = $MESSAGE[$msg];
4585 if (!empty($message)) {
4586 $retval .= COM_showMessageText($message);
4594 * Displays a message, as defined by URL parameters
4596 * Helper function to display a message, if URL parameters 'msg' and 'plugin'
4597 * (optional) are defined. Only for GET requests, but that's what Geeklog uses
4598 * everywhere anyway.
4600 * @return string HTML block with message
4601 * @see COM_showMessage
4602 * @see COM_showMessageText
4605 function COM_showMessageFromParameter()
4609 if (isset($_GET['msg'])) {
4610 $msg = COM_applyFilter($_GET['msg'], true);
4613 if (isset($_GET['plugin'])) {
4614 $plugin = COM_applyFilter($_GET['plugin']);
4616 $retval .= COM_showMessage($msg, $plugin);
4624 * Prints Google(tm)-like paging navigation
4626 * @param string $base_url base url to use for all generated links
4627 * @param int $curpage current page we are on
4628 * @param int $num_pages Total number of pages
4629 * @param string $page_str page-variable name AND '='
4630 * @param boolean $do_rewrite if true, url-rewriting is respected
4631 * @param string $msg to be displayed with the navigation
4632 * @param string $open_ended replace next/last links with this
4633 * @return string HTML formatted widget
4635 function COM_printPageNavigation( $base_url, $curpage, $num_pages,
4636 $page_str='page=', $do_rewrite=false, $msg='',
4643 if( $num_pages < 2 )
4650 $hasargs = strstr( $base_url, '?' );
4668 $retval .= COM_createLink($LANG05[7], $base_url) . ' | ';
4670 if( ( $curpage - 1 ) > 1 )
4672 $pg = $sep . $page_str . ( $curpage - 1 );
4674 $retval .= COM_createLink($LANG05[6], $base_url . $pg ) . ' | ';
4678 $retval .= $LANG05[7] . ' | ' ;
4679 $retval .= $LANG05[6] . ' | ' ;
4682 for( $pgcount = ( $curpage - 10 ); ( $pgcount <= ( $curpage + 9 )) AND ( $pgcount <= $num_pages ); $pgcount++ )
4689 if( $pgcount == $curpage )
4691 $retval .= '<b>' . $pgcount . '</b> ';
4698 $pg = $sep . $page_str . $pgcount;
4700 $retval .= COM_createLink($pgcount, $base_url . $pg) . ' ';
4704 if( !empty( $open_ended ))
4706 $retval .= '| ' . $open_ended;
4708 else if( $curpage == $num_pages )
4710 $retval .= '| ' . $LANG05[5] . ' ';
4711 $retval .= '| ' . $LANG05[8];
4715 $retval .= '| ' . COM_createLink($LANG05[5], $base_url . $sep
4716 . $page_str . ($curpage + 1));
4717 $retval .= ' | ' . COM_createLink($LANG05[8], $base_url . $sep
4718 . $page_str . $num_pages);
4721 if( !empty( $retval ))
4727 $retval = '<div class="pagenav">' . $msg . $retval . '</div>';
4734 * Returns formatted date/time for user
4736 * This function COM_takes a date in either unixtimestamp or in english and
4737 * formats it to the users preference. If the user didn't specify a format
4738 * the format in the config file is used. This returns an array where array[0]
4739 * is the formatted date and array[1] is the unixtimestamp
4741 * @param string $date date to format, otherwise we format current date/time
4742 * @return array array[0] is the formatted date and array[1] is the unixtimestamp.
4745 function COM_getUserDateTimeFormat( $date='' )
4747 global $_TABLES, $_USER, $_CONF;
4749 // Get display format for time
4751 if( !COM_isAnonUser() )
4753 if( empty( $_USER['format'] ))
4755 $dateformat = $_CONF['date'];
4759 $dateformat = $_USER['format'];
4764 $dateformat = $_CONF['date'];
4769 // Date is empty, get current date/time
4772 else if( is_numeric( $date ))
4774 // This is a timestamp
4779 // This is a string representation of a date/time
4780 $stamp = strtotime( $date );
4785 $date = strftime( $dateformat, $stamp );
4787 return array( $date, $stamp );
4791 * Returns user-defined cookie timeout
4793 * In account preferences users can specify when their long-term cookie expires.
4794 * This function returns that value.
4796 * @return int Cookie time out value in seconds
4799 function COM_getUserCookieTimeout()
4801 global $_TABLES, $_USER, $_CONF;
4803 if( empty( $_USER ))
4808 $timeoutvalue = DB_getItem( $_TABLES['users'], 'cookietimeout', "uid = {$_USER['uid']}" );
4810 if( empty( $timeoutvalue ))
4815 return $timeoutvalue;
4819 * Shows who is online in slick little block
4820 * @return string HTML string of online users seperated by line breaks.
4823 function phpblock_whosonline()
4825 global $_CONF, $_TABLES, $_USER, $LANG01, $_IMAGE_TYPE;
4829 $expire_time = time() - $_CONF['whosonline_threshold'];
4831 $byname = 'username';
4832 if( $_CONF['show_fullname'] == 1 )
4834 $byname .= ',fullname';
4836 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4838 $byname .= ',remoteusername,remoteservice';
4841 $result = DB_query( "SELECT DISTINCT {$_TABLES['sessions']}.uid,{$byname},photo,showonline FROM {$_TABLES['sessions']},{$_TABLES['users']},{$_TABLES['userprefs']} WHERE {$_TABLES['users']}.uid = {$_TABLES['sessions']}.uid AND {$_TABLES['users']}.uid = {$_TABLES['userprefs']}.uid AND start_time >= $expire_time AND {$_TABLES['sessions']}.uid <> 1 ORDER BY {$byname}" );
4842 $nrows = DB_numRows( $result );
4847 for( $i = 0; $i < $nrows; $i++ )
4849 $A = DB_fetchArray( $result );
4851 if( $A['showonline'] == 1 )
4854 if( $_CONF['show_fullname'] == 1 )
4856 $fullname = $A['fullname'];
4858 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4860 $username = COM_getDisplayName( $A['uid'], $A['username'],
4861 $fullname, $A['remoteusername'], $A['remoteservice'] );
4865 $username = COM_getDisplayName( $A['uid'], $A['username'],
4868 $url = $_CONF['site_url'] . '/users.php?mode=profile&uid=' . $A['uid'];
4869 $retval .= COM_createLink($username, $url);
4871 if( !empty( $A['photo'] ) AND $_CONF['allow_user_photo'] == 1)
4873 $usrimg = '<img src="' . $_CONF['layout_url']
4874 . '/images/smallcamera.' . $_IMAGE_TYPE
4875 . '" alt=""' . XHTML . '>';
4876 $retval .= ' ' . COM_createLink($usrimg, $url);
4878 $retval .= '<br' . XHTML . '>';
4883 // this user does not want to show up in Who's Online
4884 $num_anon++; // count as anonymous
4888 $num_anon += DB_count( $_TABLES['sessions'], 'uid', 1 );
4890 if(( $_CONF['whosonline_anonymous'] == 1 ) &&
4893 // note that we're overwriting the contents of $retval here
4896 $retval = $LANG01[112] . ': ' . COM_numberFormat($num_reg)
4897 . '<br' . XHTML . '>';
4907 $retval .= $LANG01[41] . ': ' . COM_numberFormat($num_anon)
4908 . '<br' . XHTML . '>';
4915 * Gets the <option> values for calendar months
4917 * @param string $selected Selected month
4918 * @see function COM_getDayFormOptions
4919 * @see function COM_getYearFormOptions
4920 * @see function COM_getHourFormOptions
4921 * @see function COM_getMinuteFormOptions
4922 * @return string HTML Months as option values
4925 function COM_getMonthFormOptions( $selected = '' )
4929 $month_options = '';
4931 for( $i = 1; $i <= 12; $i++ )
4934 $month_options .= '<option value="' . $mval . '"';
4936 if( $i == $selected )
4938 $month_options .= ' selected="selected"';
4941 $month_options .= '>' . $LANG_MONTH[$mval] . '</option>';
4944 return $month_options;
4948 * Gets the <option> values for calendar days
4950 * @param string $selected Selected day
4951 * @see function COM_getMonthFormOptions
4952 * @see function COM_getYearFormOptions
4953 * @see function COM_getHourFormOptions
4954 * @see function COM_getMinuteFormOptions
4955 * @return string HTML days as option values
4958 function COM_getDayFormOptions( $selected = '' )
4962 for( $i = 1; $i <= 31; $i++ )
4973 $day_options .= '<option value="' . $dval . '"';
4975 if( $i == $selected )
4977 $day_options .= ' selected="selected"';
4980 $day_options .= '>' . $dval . '</option>';
4983 return $day_options;
4987 * Gets the <option> values for calendar years
4989 * Returns Option list Containing 5 years starting with current
4990 * unless @selected is < current year then starts with @selected
4992 * @param string $selected Selected year
4993 * @param int $startoffset Optional (can be +/-) Used to determine start year for range of years
4994 * @param int $endoffset Optional (can be +/-) Used to determine end year for range of years
4995 * @see function COM_getMonthFormOptions
4996 * @see function COM_getDayFormOptions
4997 * @see function COM_getHourFormOptions
4998 * @see function COM_getMinuteFormOptions
4999 * @return string HTML years as option values
5002 function COM_getYearFormOptions($selected = '', $startoffset = -1, $endoffset = 5)
5005 $start_year = date('Y') + $startoffset;
5006 $cur_year = date('Y', time());
5007 $finish_year = $cur_year + $endoffset;
5009 if (!empty($selected)) {
5010 if ($selected < $cur_year) {
5011 $start_year = $selected;
5015 for ($i = $start_year; $i <= $finish_year; $i++) {
5016 $year_options .= '<option value="' . $i . '"';
5018 if ($i == $selected) {
5019 $year_options .= ' selected="selected"';
5022 $year_options .= '>' . $i . '</option>';
5025 return $year_options;
5029 * Gets the <option> values for clock hours
5031 * @param string $selected Selected hour
5032 * @param int $mode 12 or 24 hour mode
5033 * @return string HTML string of options
5034 * @see function COM_getMonthFormOptions
5035 * @see function COM_getDayFormOptions
5036 * @see function COM_getYearFormOptions
5037 * @see function COM_getMinuteFormOptions
5040 function COM_getHourFormOptions( $selected = '', $mode = 12 )
5046 for( $i = 1; $i <= 11; $i++ )
5059 $hour_options .= '<option value="12"';
5061 if( $selected == 12 )
5063 $hour_options .= ' selected="selected"';
5066 $hour_options .= '>12</option>';
5069 $hour_options .= '<option value="' . $hval . '"';
5071 if( $selected == $i )
5073 $hour_options .= ' selected="selected"';
5076 $hour_options .= '>' . $i . '</option>';
5079 else // if( $mode == 24 )
5081 for( $i = 0; $i < 24; $i++ )
5092 $hour_options .= '<option value="' . $hval . '"';
5094 if( $selected == $i )
5096 $hour_options .= ' selected="selected"';
5099 $hour_options .= '>' . $i . '</option>';
5103 return $hour_options;
5107 * Gets the <option> values for clock minutes
5109 * @param string $selected Selected minutes
5110 * @param int $step number of minutes between options, e.g. 15
5111 * @see function COM_getMonthFormOptions
5112 * @see function COM_getDayFormOptions
5113 * @see function COM_getHourFormOptions
5114 * @see function COM_getYearFormOptions
5115 * @return string HTML of option minutes
5118 function COM_getMinuteFormOptions( $selected = '', $step = 1 )
5120 $minute_options = '';
5122 if(( $step < 1 ) || ( $step > 30 ))
5127 for( $i = 0; $i <= 59; $i += $step )
5138 $minute_options .= '<option value="' . $mval . '"';
5140 if( $selected == $i )
5142 $minute_options .= ' selected="selected"';
5145 $minute_options .= '>' . $mval . '</option>';
5148 return $minute_options;
5152 * For backward compatibility only.
5153 * This function should always have been called COM_getMinuteFormOptions
5154 * @see COM_getMinuteFormOptions
5156 function COM_getMinuteOptions( $selected = '', $step = 1 )
5158 return COM_getMinuteFormOptions( $selected, $step );
5162 * Create an am/pm selector dropdown menu
5164 * @param string $name name of the <select>
5165 * @param string $selected preselection: 'am' or 'pm'
5166 * @return string HTML for the dropdown; empty string in 24 hour mode
5169 function COM_getAmPmFormSelection( $name, $selected = '' )
5175 if( isset( $_CONF['hour_mode'] ) && ( $_CONF['hour_mode'] == 24 ))
5181 if( empty( $selected ))
5183 $selected = date( 'a' );
5186 $retval .= '<select name="' . $name . '">' . LB;
5187 $retval .= '<option value="am"';
5188 if( $selected == 'am' )
5190 $retval .= ' selected="selected"';
5192 $retval .= '>am</option>' . LB . '<option value="pm"';
5193 if( $selected == 'pm' )
5195 $retval .= ' selected="selected"';
5197 $retval .= '>pm</option>' . LB . '</select>' . LB;
5204 * Creates an HTML unordered list from the given array.
5205 * It formats one list item per array element, using the list.thtml
5206 * and listitem.thtml templates.
5208 * @param array $listofitems Items to list out
5209 * @param string $classname optional CSS class name for the list
5210 * @return string HTML unordered list of array items
5212 function COM_makeList($listofitems, $classname = '')
5216 $list = new Template($_CONF['path_layout']);
5217 $list->set_file(array('list' => 'list.thtml',
5218 'listitem' => 'listitem.thtml'));
5219 $list->set_var( 'xhtml', XHTML );
5220 $list->set_var('site_url', $_CONF['site_url']);
5221 $list->set_var('site_admin_url', $_CONF['site_admin_url']);
5222 $list->set_var('layout_url', $_CONF['layout_url']);
5224 if (empty($classname)) {
5225 $list->set_var('list_class', '');
5226 $list->set_var('list_class_name', '');
5228 $list->set_var('list_class', 'class="' . $classname . '"');
5229 $list->set_var('list_class_name', $classname);
5232 if (is_array($listofitems)) {
5233 foreach ($listofitems as $oneitem) {
5234 $list->set_var('list_item', $oneitem);
5235 $list->parse('list_items', 'listitem', true);
5239 $list->parse('newlist', 'list', true);
5241 return $list->finish($list->get_var('newlist'));
5245 * Check if speed limit applies
5247 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5248 * @param int $max max number of allowed tries within speed limit
5249 * @param string $property IP address or other identifiable property
5250 * @return int 0: does not apply, else: seconds since last post
5252 function COM_checkSpeedlimit($type = 'submit', $max = 1, $property = '')
5258 if (empty($property)) {
5259 $property = $_SERVER['REMOTE_ADDR'];
5261 $property = addslashes($property);
5263 $res = DB_query("SELECT date FROM {$_TABLES['speedlimit']} WHERE (type = '$type') AND (ipaddress = '$property') ORDER BY date ASC");
5265 // If the number of allowed tries has not been reached,
5266 // return 0 (didn't hit limit)
5267 if (DB_numRows($res) < $max) {
5271 list($date) = DB_fetchArray($res);
5273 if (!empty($date)) {
5274 $last = time() - $date;
5276 // just in case someone manages to submit something in < 1 sec.
5285 * Store post info for speed limit
5287 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5288 * @param string $property IP address or other identifiable property
5291 function COM_updateSpeedlimit($type = 'submit', $property = '')
5295 if (empty($property)) {
5296 $property = $_SERVER['REMOTE_ADDR'];
5298 $property = addslashes($property);
5300 DB_save($_TABLES['speedlimit'], 'ipaddress,date,type',
5301 "'$property',UNIX_TIMESTAMP(),'$type'");
5305 * Clear out expired speed limits, i.e. entries older than 'x' seconds
5307 * @param speedlimit int number of seconds
5308 * @param type string type of speed limit, e.g. 'submit', 'comment'
5311 function COM_clearSpeedlimit($speedlimit = 60, $type = '')
5315 $sql = "DELETE FROM {$_TABLES['speedlimit']} WHERE ";
5316 if (!empty($type)) {
5317 $sql .= "(type = '$type') AND ";
5319 $sql .= "(date < UNIX_TIMESTAMP() - $speedlimit)";
5324 * Reset the speedlimit
5326 * @param string $type type of speed limit to reset, e.g. 'submit'
5327 * @param string $property IP address or other identifiable property
5330 function COM_resetSpeedlimit($type = 'submit', $property = '')
5334 if (empty($property)) {
5335 $property = $_SERVER['REMOTE_ADDR'];
5337 $property = addslashes($property);
5339 DB_delete($_TABLES['speedlimit'], array('type', 'ipaddress'),
5340 array($type, $property));
5344 * Wrapper function for URL class so as to not confuse people as this will
5345 * eventually get used all over the place
5347 * This function returns a crawler friendly URL (if possible)
5349 * @param string $url URL to try to build crawler friendly URL for
5350 * @return string Rewritten URL
5353 function COM_buildURL( $url )
5357 return $_URL->buildURL( $url );
5361 * Wrapper function for URL class so as to not confuse people
5363 * This function sets the name of the arguments found in url
5365 * @param array $names Names of arguments in query string to assign to values
5366 * @return boolean True if successful
5369 function COM_setArgNames( $names )
5373 return $_URL->setArgNames( $names );
5377 * Wrapper function for URL class
5379 * returns value for specified argument
5381 * @param string $name argument to get value for
5382 * @return string Argument value
5385 function COM_getArgument( $name )
5389 return $_URL->getArgument( $name );
5395 * This will take a number of occurrences, and number of seconds for the time span and return
5396 * the smallest #/time interval
5398 * @param int $occurrences how many occurrences during time interval
5399 * @param int $timespan time interval in seconds
5400 * @return int Seconds per interval
5403 function COM_getRate( $occurrences, $timespan )
5405 // want to define some common time words (yes, dirk, i need to put this in LANG)
5406 // time words and their value in seconds
5407 // week is 7 * day, month is 30 * day, year is 365.25 * day
5409 $common_time = array(
5419 if( $occurrences != 0 )
5421 $rate = ( int )( $timespan / $occurrences );
5422 $adjustedRate = $occurrences + 1;
5423 $time_unit = 'second';
5427 foreach( $common_time as $unit=>$seconds )
5429 if( $rate > $seconds )
5431 $foo = ( int )(( $rate / $seconds ) + .5 );
5433 if(( $foo < $occurrences ) && ( $foo > 0 ))
5435 $adjustedRate = $foo;
5441 $singular = '1 shout every ' . $adjustedRate . ' ' . $time_unit;
5443 if( $adjustedRate > 1 )
5450 $singular = 'No events';
5457 * Return SQL expression to check for permissions.
5459 * Creates part of an SQL expression that can be used to request items with the
5460 * standard set of Geeklog permissions.
5462 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5463 * @param int $u_id user id or 0 = current user
5464 * @param int $access access to check for (2=read, 3=r&write)
5465 * @param string $table table name if ambiguous (e.g. in JOINs)
5466 * @return string SQL expression string (may be empty)
5469 function COM_getPermSQL( $type = 'WHERE', $u_id = 0, $access = 2, $table = '' )
5471 global $_USER, $_GROUPS;
5473 if( !empty( $table ))
5479 if( COM_isAnonUser() )
5485 $uid = $_USER['uid'];
5493 $UserGroups = array();
5494 if(( empty( $_USER['uid'] ) && ( $uid == 1 )) || ( $uid == $_USER['uid'] ))
5496 if( empty( $_GROUPS ))
5498 $_GROUPS = SEC_getUserGroups( $uid );
5500 $UserGroups = $_GROUPS;
5504 $UserGroups = SEC_getUserGroups( $uid );
5507 if( empty( $UserGroups ))
5509 // this shouldn't really happen, but if it does, handle user
5510 // like an anonymous user
5514 if( SEC_inGroup( 'Root', $uid ))
5519 $sql = ' ' . $type . ' (';
5523 $sql .= "(({$table}owner_id = '{$uid}') AND ({$table}perm_owner >= $access)) OR ";
5525 $sql .= "(({$table}group_id IN (" . implode( ',', $UserGroups )
5526 . ")) AND ({$table}perm_group >= $access)) OR ";
5527 $sql .= "({$table}perm_members >= $access)";
5531 $sql .= "{$table}perm_anon >= $access";
5540 * Return SQL expression to check for allowed topics.
5542 * Creates part of an SQL expression that can be used to only request stories
5543 * from topics to which the user has access to.
5545 * Note that this function does an SQL request, so you should cache
5546 * the resulting SQL expression if you need it more than once.
5548 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5549 * @param int $u_id user id or 0 = current user
5550 * @param string $table table name if ambiguous (e.g. in JOINs)
5551 * @return string SQL expression string (may be empty)
5554 function COM_getTopicSQL( $type = 'WHERE', $u_id = 0, $table = '' )
5556 global $_TABLES, $_USER, $_GROUPS;
5558 $topicsql = ' ' . $type . ' ';
5560 if( !empty( $table ))
5565 $UserGroups = array();
5566 if(( $u_id <= 0 ) || ( isset( $_USER['uid'] ) && $u_id == $_USER['uid'] ))
5568 if( !COM_isAnonUser() )
5570 $uid = $_USER['uid'];
5576 $UserGroups = $_GROUPS;
5581 $UserGroups = SEC_getUserGroups( $uid );
5584 if( empty( $UserGroups ))
5586 // this shouldn't really happen, but if it does, handle user
5587 // like an anonymous user
5591 if( SEC_inGroup( 'Root', $uid ))
5596 $result = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
5597 . COM_getPermSQL( 'WHERE', $uid ));
5599 while( $T = DB_fetchArray( $result ))
5601 $tids[] = $T['tid'];
5604 if( count( $tids ) > 0 )
5606 $topicsql .= "({$table}tid IN ('" . implode( "','", $tids ) . "'))";
5617 * Strip slashes from a string only when magic_quotes_gpc = on.
5619 * @param string $text The text
5620 * @return string The text, possibly without slashes.
5622 function COM_stripslashes( $text )
5624 if( get_magic_quotes_gpc() == 1 )
5626 return( stripslashes( $text ));
5633 * Filter parameters passed per GET (URL) or POST.
5635 * @param string $parameter the parameter to test
5636 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5637 * @return string the filtered parameter (may now be empty or 0)
5638 * @see COM_applyBasicFilter
5641 function COM_applyFilter( $parameter, $isnumeric = false )
5643 $p = COM_stripslashes($parameter);
5645 return COM_applyBasicFilter($p, $isnumeric);
5651 * NOTE: Use this function instead of COM_applyFilter for parameters
5652 * _not_ coming in through a GET or POST request.
5654 * @param string $parameter the parameter to test
5655 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5656 * @return string the filtered parameter (may now be empty or 0)
5657 * @see COM_applyFilter
5660 function COM_applyBasicFilter( $parameter, $isnumeric = false )
5662 $log_manipulation = false; // set to true to log when the filter applied
5664 $p = strip_tags( $parameter );
5665 $p = COM_killJS( $p ); // doesn't help a lot right now, but still ...
5669 // Note: PHP's is_numeric() accepts values like 4e4 as numeric
5670 if( !is_numeric( $p ) || ( preg_match( '/^-?\d+$/', $p ) == 0 ))
5677 $p = preg_replace( '/\/\*.*/', '', $p );
5678 $pa = explode( "'", $p );
5679 $pa = explode( '"', $pa[0] );
5680 $pa = explode( '`', $pa[0] );
5681 $pa = explode( ';', $pa[0] );
5682 $pa = explode( ',', $pa[0] );
5683 $pa = explode( '\\', $pa[0] );
5687 if( $log_manipulation )
5689 if( strcmp( $p, $parameter ) != 0 )
5691 COM_errorLog( "Filter applied: >> $parameter << filtered to $p [IP {$_SERVER['REMOTE_ADDR']}]", 1);
5701 * @param string $url URL to sanitized
5702 * @param array $allowed_protocols array of allowed protocols
5703 * @param string $default_protocol replacement protocol (default: http)
5704 * @return string sanitized URL
5707 function COM_sanitizeUrl( $url, $allowed_protocols = '', $default_protocol = '' )
5711 if( empty( $allowed_protocols ))
5713 $allowed_protocols = $_CONF['allowed_protocols'];
5715 else if( !is_array( $allowed_protocols ))
5717 $allowed_protocols = array( $allowed_protocols );
5720 if( empty( $default_protocol ))
5722 $default_protocol = 'http:';
5724 else if( substr( $default_protocol, -1 ) != ':' )
5726 $default_protocol .= ':';
5729 $url = strip_tags( $url );
5732 $pos = MBYTE_strpos( $url, ':' );
5733 if( $pos === false )
5735 $url = $default_protocol . '//' . $url;
5739 $protocol = MBYTE_substr( $url, 0, $pos + 1 );
5741 foreach( $allowed_protocols as $allowed )
5743 if( substr( $allowed, -1 ) != ':' )
5747 if( $protocol == $allowed )
5755 $url = $default_protocol . MBYTE_substr( $url, $pos + 1 );
5764 * Ensure an ID contains only alphanumeric characters, dots, dashes, or underscores
5766 * @param string $id the ID to sanitize
5767 * @param boolean $new_id true = create a new ID in case we end up with an empty string
5768 * @return string the sanitized ID
5770 function COM_sanitizeID( $id, $new_id = true )
5772 $id = str_replace( ' ', '', $id );
5773 $id = str_replace( array( '/', '\\', ':', '+' ), '-', $id );
5774 $id = preg_replace( '/[^a-zA-Z0-9\-_\.]/', '', $id );
5775 if( empty( $id ) && $new_id )
5777 $id = COM_makesid();
5784 * Sanitize a filename.
5786 * NOTE: This function is pretty strict in what it allows. Meant to be used
5787 * for files to be included where part of the filename is dynamic.
5789 * @param string $filename the filename to clean up
5790 * @param boolean $allow_dots whether to allow dots in the filename or not
5791 * @return string sanitized filename
5794 function COM_sanitizeFilename($filename, $allow_dots = false)
5797 $filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $filename);
5798 $filename = str_replace('..', '', $filename);
5800 $filename = preg_replace('/[^a-zA-Z0-9\-_]/', '', $filename);
5807 * Detect links in a plain-ascii text and turn them into clickable links.
5808 * Will detect links starting with "http:", "https:", "ftp:", and "www.".
5810 * @param string $text the (plain-ascii) text string
5811 * @return string the same string, with links enclosed in <a>...</a> tags
5814 function COM_makeClickableLinks( $text )
5818 if (! $_CONF['clickable_links']) {
5822 // These regular expressions will work for this purpuse, but
5823 // they should NOT be used for validating links.
5825 // matches anything starting with http:// or https:// or ftp:// or ftps://
5826 $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:(?:ht|f)tps?:\/{2})(?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&)?)+)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
5827 $replace[] = "COM_makeClickableLinksCallback('', '\\1')";
5829 // matches anything containing a top level domain: xxx.com or xxx.yyy.net/stuff.php or xxx.yyy.zz
5830 // list taken from: http://en.wikipedia.org/wiki/List_of_Internet_TLDs
5831 $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:[a-z0-9]+\.)*[a-z0-9]+\.(?:aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z]{2})(?:[\/?#](?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&)?)*)?)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
5832 $replace[] = "COM_makeClickableLinksCallback('http://', '\\1')";
5834 $text = preg_replace( $regex, $replace, $text );
5840 * Callback function to help format links in COM_makeClickableLinks
5842 * @param string $http set to 'http://' when not already in the url
5843 * @param string $link the url
5844 * @return string link enclosed in <a>...</a> tags
5847 function COM_makeClickableLinksCallback( $http, $link )
5849 $text = COM_truncate( $link, 50, '...', '10' );
5851 return "<a href=\"$http$link\">$text</a>";
5855 * Undo the conversion of URLs to clickable links (in plain text posts),
5856 * e.g. so that we can present the user with the post as they entered them.
5858 * @param string $text story text
5859 * @return string story text without links
5862 function COM_undoClickableLinks( $text )
5864 $text = preg_replace( '/<a href="([^"]*)">([^<]*)<\/a>/', '\1', $text );
5870 * Highlight the words from a search query in a given text string.
5872 * @param string $text the text
5873 * @param string $query the search query
5874 * @param string $class html class to use to highlight
5875 * @return string the text with highlighted search words
5878 function COM_highlightQuery( $text, $query, $class = 'highlight' )
5880 // escape PCRE special characters
5881 $query = preg_quote($query, '/');
5883 $mywords = explode(' ', $query);
5884 foreach ($mywords as $searchword)
5886 if (!empty($searchword))
5888 $before = "/(?!(?:[^<]+>|[^>]+<\/a>))\b";
5890 if ($searchword <> utf8_encode($searchword)) {
5891 if (@preg_match('/^\pL$/u', urldecode('%C3%B1'))) { // Unicode property support
5892 $before = "/(?<!\p{L})";
5893 $after = "(?!\p{L})/u";
5899 $text = preg_replace($before . $searchword . $after, "<span class=\"$class\">\\0</span>", '<!-- x -->' . $text . '<!-- x -->' );
5906 * Determines the difference between two dates.
5908 * This will takes either unixtimestamps or English dates as input and will
5909 * automatically do the date diff on the more recent of the two dates (e.g. the
5910 * order of the two dates given doesn't matter).
5912 * @author Tony Bibbs, tony DOT bibbs AT iowa DOT gov
5914 * @param string $interval Can be:
5921 * @param string|int $date1 English date (e.g. 10 Dec 2004) or unixtimestamp
5922 * @param string|int $date2 English date (e.g. 10 Dec 2004) or unixtimestamp
5923 * @return int Difference of the two dates in the unit of time indicated by the interval
5926 function COM_dateDiff( $interval, $date1, $date2 )
5928 // Convert dates to timestamps, if needed.
5929 if( !is_numeric( $date1 ))
5931 $date1 = strtotime( $date1 );
5934 if( !is_numeric( $date2 ))
5936 $date2 = strtotime( $date2 );
5939 // Function roughly equivalent to the ASP "DateDiff" function
5940 if( $date2 > $date1 )
5942 $seconds = $date2 - $date1;
5946 $seconds = $date1 - $date2;
5952 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5953 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5954 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5955 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5956 $diff = $year2 - $year1;
5957 if($month1 > $month2) {
5959 } elseif($month1 == $month2) {
5962 } elseif($day1 == $day2) {
5963 if($time1 > $time2) {
5970 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5971 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5972 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5973 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5974 $diff = ($year2 * 12 + $month2) - ($year1 * 12 + $month1);
5977 } elseif($day1 == $day2) {
5978 if($time1 > $time2) {
5984 // Only simple seconds calculation needed from here on
5985 $diff = floor($seconds / 604800);
5988 $diff = floor($seconds / 86400);
5991 $diff = floor($seconds / 3600);
5994 $diff = floor($seconds / 60);
6005 * Try to figure out our current URL, including all parameters.
6007 * This is an ugly hack since there's no single variable that returns what
6008 * we want and the variables used here may not be available on all servers
6011 * Seems to work on Apache (1.3.x and 2.x), IIS, and Zeus ...
6013 * @return string complete URL, e.g. 'http://www.example.com/blah.php?foo=bar'
6016 function COM_getCurrentURL()
6022 if( empty( $_SERVER['SCRIPT_URI'] ))
6024 if( !empty( $_SERVER['DOCUMENT_URI'] ))
6026 $thisUrl = $_SERVER['DOCUMENT_URI'];
6031 $thisUrl = $_SERVER['SCRIPT_URI'];
6033 if( !empty( $thisUrl ) && !empty( $_SERVER['QUERY_STRING'] ))
6035 $thisUrl .= '?' . $_SERVER['QUERY_STRING'];
6037 if( empty( $thisUrl ))
6039 $requestUri = $_SERVER['REQUEST_URI'];
6040 if( empty( $_SERVER['REQUEST_URI'] ))
6042 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6043 if( empty( $_SERVER['PATH_INFO'] ))
6045 $requestUri = $_SERVER['SCRIPT_NAME'];
6049 $requestUri = $_SERVER['PATH_INFO'];
6051 if( !empty( $_SERVER['QUERY_STRING'] ))
6053 $requestUri .= '?' . $_SERVER['QUERY_STRING'];
6057 $firstslash = strpos( $_CONF['site_url'], '/' );
6058 if( $firstslash === false )
6060 // special case - assume it's okay
6061 $thisUrl = $_CONF['site_url'] . $requestUri;
6063 else if( $firstslash + 1 == strrpos( $_CONF['site_url'], '/' ))
6065 // site is in the document root
6066 $thisUrl = $_CONF['site_url'] . $requestUri;
6070 // extract server name first
6071 $pos = strpos( $_CONF['site_url'], '/', $firstslash + 2 );
6072 $thisUrl = substr( $_CONF['site_url'], 0, $pos ) . $requestUri;
6080 * Check if we're on Geeklog's index page.
6082 * See if we're on the main index page (first page, no topics selected).
6084 * @return boolean true = we're on the frontpage, false = we're not
6087 function COM_onFrontpage()
6089 global $_CONF, $topic, $page, $newstories;
6091 // Note: We can't use $PHP_SELF here since the site may not be in the
6093 $onFrontpage = false;
6095 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6096 if( empty( $_SERVER['PATH_INFO'] ))
6098 $scriptName = $_SERVER['SCRIPT_NAME'];
6102 $scriptName = $_SERVER['PATH_INFO'];
6105 preg_match( '/\/\/[^\/]*(.*)/', $_CONF['site_url'], $pathonly );
6106 if(( $scriptName == $pathonly[1] . '/index.php' ) &&
6107 empty( $topic ) && ( $page == 1 ) && !$newstories )
6109 $onFrontpage = true;
6112 return $onFrontpage;
6116 * Check if we're on Geeklog's index page [deprecated]
6118 * Note that this function returns FALSE when we're on the index page. Due to
6119 * the inverted return values, it has been deprecated and is only provided for
6120 * backward compatibility - use COM_onFrontpage() instead.
6122 * @deprecated since Geeklog 1.4.1
6123 * @see COM_onFrontpage
6126 function COM_isFrontpage()
6128 return !COM_onFrontpage();
6132 * Converts a number for output into a formatted number with thousands-
6133 * separator, comma-separator and fixed decimals if necessary
6135 * @param float $number Number that will be formatted
6136 * @return string formatted number
6139 function COM_numberFormat( $number )
6143 if( $number - floor( $number ) > 0 ) // number has decimals
6145 $dc = $_CONF['decimal_count'];
6151 $ts = $_CONF['thousand_separator'];
6152 $ds = $_CONF['decimal_separator'];
6154 return number_format( $number, $dc, $ds, $ts );
6158 * Convert a text based date YYYY-MM-DD to a unix timestamp integer value
6160 * @param string $date Date in the format YYYY-MM-DD
6161 * @param string $time Option time in the format HH:MM::SS
6162 * @return int UNIX Timestamp
6164 function COM_convertDate2Timestamp( $date, $time = '' )
6169 // Breakup the string using either a space, fwd slash, dash, bkwd slash or
6170 // colon as a delimiter
6171 $atok = strtok( $date, ' /-\\:' );
6172 while( $atok !== FALSE )
6175 $atok = strtok( ' /-\\:' ); // get the next token
6178 for( $i = 0; $i < 3; $i++ )
6180 if( !isset( $atoks[$i] ) || !is_numeric( $atoks[$i] ))
6188 $timestamp = mktime( 0, 0, 0, $atoks[1], $atoks[2], $atoks[0] );
6192 $btok = strtok( $time, ' /-\\:' );
6193 while( $btok !== FALSE )
6196 $btok = strtok( ' /-\\:' );
6199 for( $i = 0; $i < 3; $i++ )
6201 if( !isset( $btoks[$i] ) || !is_numeric( $btoks[$i] ))
6207 $timestamp = mktime( $btoks[0], $btoks[1], $btoks[2],
6208 $atoks[1], $atoks[2], $atoks[0] );
6215 * Get the HTML for an image with height & width
6217 * @param string $file full path to the file
6218 * @return string html that will be included in the img-tag
6220 function COM_getImgSizeAttributes( $file )
6222 $sizeattributes = '';
6224 if( file_exists( $file ))
6226 $dimensions = getimagesize( $file );
6227 if( !empty( $dimensions[0] ) AND !empty( $dimensions[1] ))
6229 $sizeattributes = 'width="' . $dimensions[0]
6230 . '" height="' . $dimensions[1] . '" ';
6234 return $sizeattributes;
6238 * Display a message and abort
6240 * NOTE: Displays the message and aborts the script.
6242 * @param int $msg message number
6243 * @param string $plugin plugin name, if applicable
6244 * @param int $http_status HTTP status code to send with the message
6245 * @param string $http_text Textual version of the HTTP status code
6248 function COM_displayMessageAndAbort( $msg, $plugin = '', $http_status = 200, $http_text = 'OK')
6250 $display = COM_siteHeader( 'menu' )
6251 . COM_showMessage( $msg, $plugin )
6252 . COM_siteFooter( true );
6254 if( $http_status != 200 )
6256 header( "HTTP/1.1 $http_status $http_text" );
6257 header( "Status: $http_status $http_text" );
6264 * Return full URL of a topic icon
6266 * @param string $imageurl (relative) topic icon URL
6267 * @return string Full URL
6270 function COM_getTopicImageUrl( $imageurl )
6272 global $_CONF, $_THEME_URL;
6276 if( !empty( $imageurl ))
6278 if( isset( $_THEME_URL ))
6280 $iconurl = $_THEME_URL . $imageurl;
6284 $stdImageLoc = true;
6285 if( !strstr( $_CONF['path_images'], $_CONF['path_html'] ))
6287 $stdImageLoc = false;
6292 $iconurl = $_CONF['site_url'] . $imageurl;
6296 $t = explode( '/', $imageurl );
6297 $topicicon = $t[count( $t ) - 1];
6298 $iconurl = $_CONF['site_url']
6299 . '/getimage.php?mode=topics&image=' . $topicicon;
6308 * Create an HTML link
6310 * @param string $content the object to be linked (text, image etc)
6311 * @param string $url the URL the link will point to
6312 * @param array $attr an array of optional attributes for the link
6313 * for example array('title' => 'whatever');
6314 * @return string the HTML link
6316 function COM_createLink($content, $url, $attr = array())
6320 $attr_str = 'href="' . $url . '"';
6321 foreach ($attr as $key => $value) {
6322 $attr_str .= " $key=\"$value\"";
6324 $retval .= "<a $attr_str>$content</a>";
6330 * Create an HTML img
6332 * @param string $url the URL of the image, either starting with
6333 * http://... or $_CONF['layout_url'] is prepended
6334 * @param string $alt the 'alt'-tag of the image
6335 * @param array $attr an array of optional attributes for the link
6336 * for example array('title' => 'whatever');
6337 * @return string the HTML img
6339 function COM_createImage($url, $alt = "", $attr = array())
6345 if (preg_match("/^(https?):/", $url) !== 1) {
6346 $url = $_CONF['layout_url'] . $url;
6348 $attr_str = 'src="' . $url . '"';
6350 foreach ($attr as $key => $value) {
6351 $attr_str .= " $key=\"$value\"";
6354 $retval = "<img $attr_str alt=\"$alt\"" . XHTML . ">";
6360 * Try to determine the user's preferred language by looking at the
6361 * "Accept-Language" header sent by their browser (assuming they bothered
6362 * to select a preferred language there).
6364 * Sample header: Accept-Language: en-us,en;q=0.7,de-de;q=0.3
6366 * @return string name of the language file to use or an empty string
6367 * @todo Bugs: Does not take the quantity ('q') parameter into account,
6368 * but only looks at the order of language codes.
6371 function COM_getLanguageFromBrowser()
6377 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
6378 $accept = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
6379 foreach ($accept as $l) {
6380 $l = explode(';', trim($l));
6382 if (array_key_exists($l, $_CONF['language_files'])) {
6383 $retval = $_CONF['language_files'][$l];
6386 $l = explode('-', $l);
6388 if (array_key_exists($l, $_CONF['language_files'])) {
6389 $retval = $_CONF['language_files'][$l];
6400 * Determine current language
6402 * @return string name of the language file (minus the '.php' extension)
6405 function COM_getLanguage()
6407 global $_CONF, $_USER;
6411 if (!empty($_USER['language'])) {
6412 $langfile = $_USER['language'];
6413 } elseif (!empty($_COOKIE[$_CONF['cookie_language']])) {
6414 $langfile = $_COOKIE[$_CONF['cookie_language']];
6415 } elseif (isset($_CONF['languages'])) {
6416 $langfile = COM_getLanguageFromBrowser();
6419 $langfile = COM_sanitizeFilename($langfile);
6420 if (!empty($langfile)) {
6421 if (is_file($_CONF['path_language'] . $langfile . '.php')) {
6426 // if all else fails, return the default language
6427 return $_CONF['language'];
6431 * Determine the ID to use for the current language
6433 * The $_CONF['language_files'] array maps language IDs to language file names.
6434 * This function returns the language ID for a certain language file, to be
6435 * used in language-dependent URLs.
6437 * @param string $language current language file name (optional)
6438 * @return string language ID, e.g 'en'; empty string on error
6441 function COM_getLanguageId($language = '')
6445 if (empty($language)) {
6446 $language = COM_getLanguage();
6450 if (isset($_CONF['language_files'])) {
6451 $lang_id = array_search($language, $_CONF['language_files']);
6453 if ($lang_id === false) {
6454 // that looks like a misconfigured $_CONF['language_files'] array
6455 COM_errorLog('Language "' . $language . '" not found in $_CONF[\'language_files\'] array!');
6457 $lang_id = ''; // not much we can do here ...
6465 * Return SQL expression to request language-specific content
6467 * Creates part of an SQL expression that can be used to request items in the
6468 * current language only.
6470 * @param string $field name of the "id" field, e.g. 'sid' for stories
6471 * @param string $type part of the SQL expression, e.g. 'WHERE', 'AND'
6472 * @param string $table table name if ambiguous, e.g. in JOINs
6473 * @return string SQL expression string (may be empty)
6476 function COM_getLangSQL( $field, $type = 'WHERE', $table = '' )
6482 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6484 if( !empty( $table ))
6489 $lang_id = COM_getLanguageId();
6491 if( !empty( $lang_id ))
6493 $sql = ' ' . $type . " ({$table}$field LIKE '%\\_$lang_id')";
6501 * Provide a block to switch languages
6503 * Provides a drop-down menu (or simple link, if you only have two languages)
6504 * to switch languages. This can be used as a PHP block or called from within
6505 * your theme's header.thtml:
6507 * <?php print phpblock_switch_language(); ?>
6510 * @return string HTML for drop-down or link to switch languages
6513 function phpblock_switch_language()
6519 if( empty( $_CONF['languages'] ) || empty( $_CONF['language_files'] ) ||
6520 ( count( $_CONF['languages'] ) != count( $_CONF['language_files'] )))
6525 $lang = COM_getLanguage();
6526 $langId = COM_getLanguageId( $lang );
6528 if( count( $_CONF['languages'] ) == 2 )
6530 foreach( $_CONF['languages'] as $key => $value )
6532 if( $key != $langId )
6540 $switchUrl = COM_buildUrl( $_CONF['site_url'] . '/switchlang.php?lang='
6542 $retval .= COM_createLink($newLang, $switchUrl);
6546 $retval .= '<form name="change" action="'. $_CONF['site_url']
6547 . '/switchlang.php" method="get">' . LB;
6548 $retval .= '<input type="hidden" name="oldlang" value="' . $langId
6549 . '"' . XHTML . '>' . LB;
6551 $retval .= '<select onchange="change.submit()" name="lang">';
6552 foreach( $_CONF['languages'] as $key => $value )
6554 if( $lang == $_CONF['language_files'][$key] )
6556 $selected = ' selected="selected"';
6562 $retval .= '<option value="' . $key . '"' . $selected . '>'
6563 . $value . '</option>' . LB;
6565 $retval .= '</select>' . LB;
6566 $retval .= '</form>' . LB;
6573 * Switch locale settings
6575 * When multi-language support is enabled, allow overwriting the default locale
6576 * settings with language-specific settings (date format, etc.). So in addition
6577 * to $_CONF['date'] you can have a $_CONF['date_en'], $_CONF['date_de'], etc.
6580 function COM_switchLocaleSettings()
6584 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6586 $overridables = array
6589 'date', 'daytime', 'shortdate', 'dateonly', 'timeonly',
6590 'week_start', 'hour_mode',
6591 'thousand_separator', 'decimal_separator'
6594 $langId = COM_getLanguageId();
6595 foreach( $overridables as $option )
6597 if( isset( $_CONF[$option . '_' . $langId] ))
6599 $_CONF[$option] = $_CONF[$option . '_' . $langId];
6606 * Get the name of the current language, minus the character set
6608 * Strips the character set from $_CONF['language'].
6610 * @return string language name
6613 function COM_getLanguageName()
6619 $charset = '_' . strtolower(COM_getCharset());
6620 if (substr($_CONF['language'], -strlen($charset)) == $charset) {
6621 $retval = substr($_CONF['language'], 0, -strlen($charset));
6623 $retval = $_CONF['language'];
6632 * Truncates a string to a max. length and optionally adds a filler string,
6633 * e.g. '...', to indicate the truncation.
6634 * This function is multi-byte string aware, based on a patch by Yusuke Sakata.
6636 * NOTE: The truncated string may be shorter but will never be longer than
6637 * $maxlen characters, i.e. the $filler string is taken into account.
6639 * @param string $text the text string to truncate
6640 * @param int $maxlen max. number of characters in the truncated string
6641 * @param string $filler optional filler string, e.g. '...'
6642 * @param int $endchars number of characters to show after the filler
6643 * @return string truncated string
6646 function COM_truncate( $text, $maxlen, $filler = '', $endchars = 0 )
6648 $newlen = $maxlen - MBYTE_strlen( $filler );
6649 $len = MBYTE_strlen( $text );
6650 if( $len > $maxlen )
6652 $text = MBYTE_substr( $text, 0, $newlen - $endchars ) . $filler . MBYTE_substr( $text, $len - $endchars, $endchars );
6659 * Get the current character set
6661 * Uses (if available, and in this order)
6662 * - $LANG_CHARSET (from the current language file)
6663 * - $_CONF['default_charset'] (from siteconfig.php)
6664 * - 'iso-8859-1' (hard-coded fallback)
6666 * @return string character set, e.g. 'utf-8'
6669 function COM_getCharset()
6671 global $_CONF, $LANG_CHARSET;
6673 if( empty( $LANG_CHARSET )) {
6674 $charset = $_CONF['default_charset'];
6675 if( empty( $charset )) {
6676 $charset = 'iso-8859-1';
6679 $charset = $LANG_CHARSET;
6688 * This function will handle all PHP errors thrown at it, without exposing
6689 * paths, and hopefully, providing much more information to Root Users than
6690 * the default white error page.
6692 * This function will call out to CUSTOM_handleError if it exists, but, be
6693 * advised, only override this function with a very, very stable function. I'd
6694 * suggest one that outputs some static, basic HTML.
6696 * The PHP feature that allows us to do so is documented here:
6697 * http://uk2.php.net/manual/en/function.set-error-handler.php
6699 * @param int $errno Error Number.
6700 * @param string $errstr Error Message.
6701 * @param string $errfile The file the error was raised in.
6702 * @param int $errline The line of the file that the error was raised at.
6703 * @param array $errcontext An array that points to the active symbol table at the point the error occurred.
6705 function COM_handleError($errno, $errstr, $errfile='', $errline=0, $errcontext='')
6707 global $_CONF, $_USER;
6709 // Handle @ operator
6710 if (error_reporting() == 0) {
6714 // If in PHP4, then respect error_reporting
6715 if ((PHP_VERSION < 5) && (($errno & error_reporting()) == 0)) {
6720 * If we have a root user, then output detailed error message:
6722 if ((is_array($_USER) && function_exists('SEC_inGroup'))
6723 || (isset($_CONF['rootdebug']) && $_CONF['rootdebug'])) {
6724 if ($_CONF['rootdebug'] || SEC_inGroup('Root')) {
6726 header('HTTP/1.1 500 Internal Server Error');
6727 header('Status: 500 Internal Server Error');
6729 $title = 'An Error Occurred';
6730 if (!empty($_CONF['site_name'])) {
6731 $title = $_CONF['site_name'] . ' - ' . $title;
6733 echo("<html><head><title>$title</title></head>\n<body>\n");
6735 echo('<h1>An error has occurred:</h1>');
6736 if ($_CONF['rootdebug']) {
6737 echo('<h2 style="color: red">This is being displayed as "Root Debugging" is enabled
6738 in your Geeklog configuration.</h2><p>If this is a production
6739 website you <strong><em>must disable</em></strong> this
6740 option once you have resolved any issues you are
6741 investigating.</p>');
6743 echo('<p>(This text is only displayed to users in the group \'Root\')</p>');
6745 echo("<p>$errno - $errstr @ $errfile line $errline</p>");
6747 if (!function_exists('SEC_inGroup') || !SEC_inGroup('Root')) {
6748 if ('force' != ''.$_CONF['rootdebug']) {
6749 $errcontext = COM_rootDebugClean($errcontext);
6751 echo('<h2 style="color: red">Root Debug is set to "force", this
6752 means that passwords and session cookies are exposed in this
6758 var_dump($errcontext);
6759 $errcontext = htmlspecialchars(ob_get_contents());
6761 echo("$errcontext</pre></body></html>");
6766 /* If there is a custom error handler, fail over to that, but only
6767 * if the error wasn't in lib-custom.php
6769 if (is_array($_CONF) && !(strstr($errfile, 'lib-custom.php'))) {
6770 if (array_key_exists('path_system', $_CONF)) {
6771 if (file_exists($_CONF['path_system'] . 'lib-custom.php')) {
6772 require_once $_CONF['path_system'] . 'lib-custom.php';
6774 if (function_exists('CUSTOM_handleError')) {
6775 CUSTOM_handleError($errno, $errstr, $errfile, $errline, $errcontext);
6781 // if we do not throw the error back to an admin, still log it in the error.log
6782 COM_errorLog("$errno - $errstr @ $errfile line $errline", 1);
6784 header('HTTP/1.1 500 Internal Server Error');
6785 header('Status: 500 Internal Server Error');
6787 // Does the theme implement an error message html file?
6788 if (!empty($_CONF['path_layout']) &&
6789 file_exists($_CONF['path_layout'] . 'errormessage.html')) {
6790 // NOTE: NOT A TEMPLATE! JUST HTML!
6791 include $_CONF['path_layout'] . 'errormessage.html';
6793 // Otherwise, display simple error message
6794 $title = 'An Error Occurred';
6795 if (!empty($_CONF['site_name'])) {
6796 $title = $_CONF['site_name'] . ' - ' . $title;
6801 <title>{$title}</title>
6804 <div style=\"width: 100%; text-align: center;\">
6805 Unfortunately, an error has occurred rendering this page. Please try
6817 * Recurse through the error context array removing/blanking password/cookie
6818 * values in case the "for development" only switch is left on in a production
6821 * [Not fit for public consumption comments about what users who enable root
6822 * debug in production should have done to them, and why making this change
6823 * defeats the point of the entire root debug feature go here.]
6825 * @param array $array Array of state info (Recursive array).
6826 * @param boolean $blank override (wouldn't that blank out everything?)
6827 * @return array Cleaned array
6829 function COM_rootDebugClean($array, $blank=false)
6831 $blankField = false;
6832 while(list($key, $value) = each($array)) {
6833 $lkey = strtolower($key);
6834 if((strpos($lkey, 'pass') !== false) || (strpos($lkey, 'cookie') !== false)) {
6837 $blankField = $blank;
6839 if(is_array($value)) {
6840 $array[$key] = COM_rootDebugClean($value, $blankField);
6841 } elseif($blankField) {
6842 $array[$key] = '[VALUE REMOVED]';
6849 * Checks to see if a specified user, or the current user if non-specified
6850 * is the anonymous user.
6852 * @param int $uid ID of the user to check, or none for the current user.
6853 * @return boolean true if the user is the anonymous user.
6855 function COM_isAnonUser($uid = '')
6859 /* If no user was specified, fail over to the current user if there is one */
6862 if( isset( $_USER['uid'] ) )
6864 $uid = $_USER['uid'];
6868 if( !empty( $uid ) )
6877 * Create Meta Tags to be used by COM_siteHeader in the headercode variable
6879 * @param string $meta_description the text for the meta description of the page being displayed
6880 * @param string $meta_keywords the text for the meta keywords of the page being displayed
6881 * @return string XHTML formatted text
6884 function COM_createMetaTags($meta_description, $meta_keywords)
6890 If ($_CONF['meta_tags'] > 0) {
6891 if ($meta_description != '') {
6892 $headercode .= LB . '<meta name="description" content="' . $meta_description . '"' . XHTML . '>';
6894 if ($meta_keywords != '') {
6895 $headercode .= LB . '<meta name="keywords" content="' . $meta_keywords . '"' . XHTML . '>';
6905 * Convert wiki-formatted text to (X)HTML
6907 * @param string $wikitext wiki-formatted text
6908 * @return string XHTML formatted text
6911 function COM_renderWikiText($wikitext)
6915 if (!$_CONF['wikitext_editor']) {
6919 require_once 'Text/Wiki.php';
6921 $wiki = new Text_Wiki();
6922 $wiki->setFormatConf('Xhtml', 'translate', HTML_SPECIALCHARS);
6923 $wiki->setRenderConf('Xhtml', 'charset', COM_getCharset());
6924 $wiki->disableRule('wikilink');
6925 $wiki->disableRule('freelink');
6926 $wiki->disableRule('interwiki');
6928 return $wiki->transform($wikitext, 'Xhtml');
6932 * Set the {lang_id} and {lang_attribute} variables for a template
6934 * NOTE: {lang_attribute} is only set in multi-language environments.
6936 * @param ref &$template template to use
6940 function COM_setLangIdAndAttribute(&$template)
6947 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6948 $langId = COM_getLanguageId();
6950 // try to derive the language id from the locale
6951 $l = explode('.', $_CONF['locale']); // get rid of character set
6953 $l = explode('@', $langId); // get rid of '@euro', etc.
6957 if (!empty($langId)) {
6958 $l = explode('-', str_replace('_', '-', $langId));
6959 if ((count($l) == 1) && (strlen($langId) == 2)) {
6960 $langAttr = 'lang="' . $langId . '"';
6961 } else if (count($l) == 2) {
6962 if (($l[0] == 'i') || ($l[0] == 'x')) {
6963 $langId = implode('-', $l);
6964 $langAttr = 'lang="' . $langId . '"';
6965 } else if (strlen($l[0]) == 2) {
6966 $langId = implode('-', $l);
6967 $langAttr = 'lang="' . $langId . '"';
6970 // this isn't a valid lang attribute, so don't set $langAttr
6974 $template->set_var('lang_id', $langId);
6976 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6977 $template->set_var('lang_attribute', ' ' . $langAttr);
6979 $template->set_var('lang_attribute', '');
6984 * Sends compressed output to browser.
6986 * Assumes that $display contains the _entire_ output for a request - no
6987 * echoes are allowed before or after this function.
6988 * Currently only supports gzip compression. Checks if zlib compression is
6989 * enabled in PHP and does uncompressed output if it is.
6991 * @param string $display Content to send to browser
6995 function COM_output($display)
6999 if (empty($display)) {
7003 if ($_CONF['compressed_output']) {
7004 $gzip_accepted = false;
7005 if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
7006 $enc = str_replace(' ', '', $_SERVER['HTTP_ACCEPT_ENCODING']);
7007 $accept = explode(',', strtolower($enc));
7008 $gzip_accepted = in_array('gzip', $accept);
7011 if ($gzip_accepted && function_exists('gzencode')) {
7013 $zlib_comp = ini_get('zlib.output_compression');
7014 if (empty($zlib_comp) || (strcasecmp($zlib_comp, 'off') == 0)) {
7016 header('Content-encoding: gzip');
7017 echo gzencode($display);
7028 * Turn a piece of HTML into continuous(!) plain text
7030 * This function removes HTML tags, line breaks, etc. and returns one long
7031 * line of text. This is useful for word counts (do an explode() on the result)
7032 * and for text excerpts.
7034 * @param string $text original text, including HTML and line breaks
7035 * @return string continuous plain text
7038 function COM_getTextContent($text)
7040 // replace <br> with spaces so that Text<br>Text becomes two words
7041 $text = preg_replace('/\<br(\s*)?\/?\>/i', ' ', $text);
7043 // add extra space between tags, e.g. <p>Text</p><p>Text</p>
7044 $text = str_replace('><', '> <', $text);
7046 // only now remove all HTML tags
7047 $text = strip_tags($text);
7049 // replace all tabs, newlines, and carrriage returns with spaces
7050 $text = str_replace(array("\011", "\012", "\015"), ' ', $text);
7052 // replace entities with plain spaces
7053 $text = str_replace(array('', ' ', ' '), ' ', $text);
7055 // collapse whitespace
7056 $text = preg_replace('/\s\s+/', ' ', $text);
7062 * Now include all plugin functions
7064 foreach ($_PLUGINS as $pi_name) {
7065 require_once $_CONF['path'] . 'plugins/' . $pi_name . '/functions.inc';
7068 // Check and see if any plugins (or custom functions)
7069 // have scheduled tasks to perform
7070 if ($_CONF['cron_schedule_interval'] > 0) {
7071 if ((DB_getItem($_TABLES['vars'], 'value', "name='last_scheduled_run'")
7072 + $_CONF['cron_schedule_interval']) <= time()) {
7073 DB_query("UPDATE {$_TABLES['vars']} SET value=UNIX_TIMESTAMP() WHERE name='last_scheduled_run'");
7074 PLG_runScheduledTask();