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++ )
2763 $plg = current( $plugin_options );
2765 $adminmenu->set_var( 'option_url', $plg->adminurl );
2766 $adminmenu->set_var( 'option_label', $plg->adminlabel );
2768 if( empty( $plg->numsubmissions ))
2770 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2774 $adminmenu->set_var( 'option_count',
2775 COM_numberFormat( $plg->numsubmissions ));
2778 $menu_item = $adminmenu->parse( 'item',
2779 ( $thisUrl == $plg->adminurl ) ? 'current' : 'option', true );
2780 $link_array[$plg->adminlabel] = $menu_item;
2782 next( $plugin_options );
2785 if(( $_CONF['allow_mysqldump'] == 1 ) AND ( $_DB_dbms == 'mysql' ) AND
2786 SEC_inGroup( 'Root' ))
2788 $url = $_CONF['site_admin_url'] . '/database.php';
2789 $adminmenu->set_var( 'option_url', $url );
2790 $adminmenu->set_var( 'option_label', $LANG01[103] );
2791 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2793 $menu_item = $adminmenu->parse( 'item',
2794 ( $thisUrl == $url ) ? 'current' : 'option' );
2795 $link_array[$LANG01[103]] = $menu_item;
2798 if ($_CONF['link_documentation'] == 1) {
2799 $doclang = COM_getLanguageName();
2800 $docs = 'docs/' . $doclang . '/index.html';
2801 if (file_exists($_CONF['path_html'] . $docs)) {
2802 $adminmenu->set_var('option_url', $_CONF['site_url']
2805 $adminmenu->set_var('option_url', $_CONF['site_url']
2806 . '/docs/english/index.html');
2808 $adminmenu->set_var('option_label', $LANG01[113]);
2809 $adminmenu->set_var('option_count', $LANG_ADMIN['na']);
2810 $menu_item = $adminmenu->parse('item', 'option');
2811 $link_array[$LANG01[113]] = $menu_item;
2814 if( $_CONF['link_versionchecker'] == 1 AND SEC_inGroup( 'Root' ))
2816 $adminmenu->set_var( 'option_url',
2817 'http://www.geeklog.net/versionchecker.php?version=' . VERSION );
2818 $adminmenu->set_var( 'option_label', $LANG01[107] );
2819 $adminmenu->set_var( 'option_count', VERSION );
2821 $menu_item = $adminmenu->parse( 'item', 'option' );
2822 $link_array[$LANG01[107]] = $menu_item;
2825 if( $_CONF['sort_admin'] )
2827 uksort( $link_array, 'strcasecmp' );
2830 $url = $_CONF['site_admin_url'] . '/moderation.php';
2831 $adminmenu->set_var('option_url', $url);
2832 $adminmenu->set_var('option_label', $LANG01[10]);
2833 $adminmenu->set_var('option_count', COM_numberFormat($modnum));
2834 $menu_item = $adminmenu->finish($adminmenu->parse('item',
2835 ($thisUrl == $url) ? 'current' : 'option'));
2836 $link_array = array($menu_item) + $link_array;
2838 foreach( $link_array as $link )
2843 $retval .= COM_endBlock( COM_getBlockTemplate( 'admin_block', 'footer', $position ));
2850 * Redirects user to a given URL
2852 * This function does a redirect using a meta refresh. This is (or at least
2853 * used to be) more compatible than using a HTTP Location: header.
2855 * NOTE: This does not need to be XHTML compliant. It may also be used
2856 * in situations where the XHTML constant is not defined yet ...
2858 * @param string $url URL to send user to
2859 * @return string HTML meta redirect
2862 function COM_refresh($url)
2864 return "<html><head><meta http-equiv=\"refresh\" content=\"0; URL=$url\"></head></html>\n";
2868 * DEPRECIATED -- see CMT_userComments in lib-comment.php
2869 * @deprecated since Geeklog 1.4.0
2870 * @see CMT_userComments
2872 function COM_userComments( $sid, $title, $type='article', $order='', $mode='', $pid = 0, $page = 1, $cid = false, $delete_option = false )
2876 require_once $_CONF['path_system'] . 'lib-comment.php';
2878 return CMT_userComments( $sid, $title, $type, $order, $mode, $pid, $page, $cid, $delete_option );
2882 * This censors inappropriate content
2884 * This will replace 'bad words' with something more appropriate
2886 * @param string $Message String to check
2887 * @see function COM_checkHTML
2888 * @return string Edited $Message
2892 function COM_checkWords( $Message )
2896 $EditedMessage = $Message;
2898 if( $_CONF['censormode'] != 0 )
2900 if( is_array( $_CONF['censorlist'] ))
2902 $Replacement = $_CONF['censorreplace'];
2904 switch( $_CONF['censormode'])
2906 case 1: # Exact match
2907 $RegExPrefix = '(\s*)';
2908 $RegExSuffix = '(\W*)';
2911 case 2: # Word beginning
2912 $RegExPrefix = '(\s*)';
2913 $RegExSuffix = '(\w*)';
2916 case 3: # Word fragment
2917 $RegExPrefix = '(\w*)';
2918 $RegExSuffix = '(\w*)';
2922 foreach ($_CONF['censorlist'] as $c) {
2924 $EditedMessage = MBYTE_eregi_replace($RegExPrefix . $c
2925 . $RegExSuffix, "\\1$Replacement\\2", $EditedMessage);
2931 return $EditedMessage;
2935 * Takes some amount of text and replaces all javascript events on*= with in
2937 * This script takes some amount of text and matches all javascript events, on*= (onBlur= onMouseClick=)
2938 * and replaces them with in*=
2939 * Essentially this will cause onBlur to become inBlur, onFocus to be inFocus
2940 * These are not valid javascript events and the browser will ignore them.
2941 * @param string $Message Text to filter
2942 * @return string $Message with javascript filtered
2943 * @see COM_checkWords
2944 * @see COM_checkHTML
2948 function COM_killJS( $Message )
2950 return( preg_replace( '/(\s)+[oO][nN](\w*) ?=/', '\1in\2=', $Message ));
2954 * Handles the part within a [code] ... [/code] section, i.e. escapes all
2955 * special characters.
2957 * @param string $str the code section to encode
2958 * @return string $str with the special characters encoded
2959 * @see COM_checkHTML
2962 function COM_handleCode( $str )
2964 $search = array( '&', '\\', '<', '>', '[', ']' );
2965 $replace = array( '&', '\', '<', '>', '[', ']' );
2967 $str = str_replace( $search, $replace, $str );
2973 * This function checks html tags.
2975 * Checks to see that the HTML tags are on the approved list and
2976 * removes them if not.
2978 * @param string $str HTML to check
2979 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
2980 * @return string Filtered HTML
2983 function COM_checkHTML( $str, $permissions = 'story.edit' )
2987 // replace any \ with \ (HTML equiv)
2988 $str = str_replace('\\', '\', COM_stripslashes($str) );
2990 // Get rid of any newline characters
2991 $str = preg_replace( "/\n/", '', $str );
2993 // Replace any $ with $ (HTML equiv)
2994 $str = str_replace( '$', '$', $str );
2995 // handle [code] ... [/code]
2998 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[code]' );
2999 if( $start_pos !== false )
3001 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/code]' );
3002 if( $end_pos !== false )
3004 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6,
3005 $end_pos - ( $start_pos + 6 )));
3006 $encoded = '<pre><code>' . $encoded . '</code></pre>';
3007 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
3008 . MBYTE_substr( $str, $end_pos + 7 );
3010 else // missing [/code]
3012 // Treat the rest of the text as code (so as not to lose any
3013 // special characters). However, the calling entity should
3014 // better be checking for missing [/code] before calling this
3016 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6 ));
3017 $encoded = '<pre><code>' . $encoded . '</code></pre>';
3018 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3022 while( $start_pos !== false );
3024 // handle [raw] ... [/raw]
3027 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[raw]' );
3028 if( $start_pos !== false )
3030 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/raw]' );
3031 if( $end_pos !== false )
3033 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5,
3034 $end_pos - ( $start_pos + 5 )));
3035 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3037 $encoded = '[raw2]' . $encoded . '[/raw2]';
3038 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
3039 . MBYTE_substr( $str, $end_pos + 6 );
3041 else // missing [/raw]
3043 // Treat the rest of the text as raw (so as not to lose any
3044 // special characters). However, the calling entity should
3045 // better be checking for missing [/raw] before calling this
3047 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5 ));
3048 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3050 $encoded = '[raw2]' . $encoded . '[/raw2]';
3051 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3055 while( $start_pos !== false );
3057 if( isset( $_CONF['skip_html_filter_for_root'] ) &&
3058 ( $_CONF['skip_html_filter_for_root'] == 1 ) &&
3059 SEC_inGroup( 'Root' ))
3064 // strip_tags() gets confused by HTML comments ...
3065 $str = preg_replace( '/<!--.+?-->/', '', $str );
3067 $filter = new kses4;
3068 if( isset( $_CONF['allowed_protocols'] ) && is_array( $_CONF['allowed_protocols'] ) && ( count( $_CONF['allowed_protocols'] ) > 0 ))
3070 $filter->SetProtocols( $_CONF['allowed_protocols'] );
3074 $filter->SetProtocols( array( 'http:', 'https:', 'ftp:' ));
3077 if( empty( $permissions) || !SEC_hasRights( $permissions ) ||
3078 empty( $_CONF['admin_html'] ))
3080 $html = $_CONF['user_html'];
3084 if ($_CONF['advanced_editor'] && is_array($_CONF['advanced_html'])) {
3085 $html = array_merge_recursive( $_CONF['user_html'],
3086 $_CONF['admin_html'],
3087 $_CONF['advanced_html'] );
3089 $html = array_merge_recursive( $_CONF['user_html'],
3090 $_CONF['admin_html'] );
3094 foreach( $html as $tag => $attr )
3096 $filter->AddHTML( $tag, $attr );
3098 /* Replace [raw][/raw] with <!--raw--><!--/raw-->, note done "late" because
3099 * of the above noted // strip_tags() gets confused by HTML comments ...
3101 $str = $filter->Parse( $str );
3102 $str = str_replace('[raw2]','<!--raw--><span class="raw">', $str);
3103 $str = str_replace('[/raw2]','</span><!--/raw-->', $str);
3109 * undo function for htmlspecialchars()
3111 * This function translates HTML entities created by htmlspecialchars() back
3112 * into their ASCII equivalents. Also handles the entities for $, {, and }.
3114 * @param string $string The string to convert.
3115 * @return string The converted string.
3118 function COM_undoSpecialChars( $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 );
3127 $string = str_replace( '&', '&', $string );
3133 * Makes an ID based on current date/time
3135 * This function creates a 17 digit sid for stories based on the 14 digit date
3136 * and a 3 digit random number that was seeded with the number of microseconds
3137 * (.000001th of a second) since the last full second.
3138 * NOTE: this is now used for more than just stories!
3140 * @return string $sid Story ID
3143 function COM_makesid()
3145 $sid = date( 'YmdHis' );
3146 $sid .= rand( 0, 999 );
3152 * Checks to see if email address is valid.
3154 * This function checks to see if an email address is in the correct from.
3156 * @param string $email Email address to verify
3157 * @return boolean True if valid otherwise false
3160 function COM_isEmail( $email )
3162 require_once( 'Mail/RFC822.php' );
3164 $rfc822 = new Mail_RFC822;
3166 return( $rfc822->isValidInetAddress( $email ) ? true : false );
3171 * Encode a string such that it can be used in an email header
3173 * @param string $string the text to be encoded
3174 * @return string encoded text
3177 function COM_emailEscape( $string )
3181 if (function_exists('CUSTOM_emailEscape')) {
3182 return CUSTOM_emailEscape($string);
3185 $charset = COM_getCharset();
3186 if(( $charset == 'utf-8' ) && ( $string != utf8_decode( $string )))
3188 if( function_exists( 'iconv_mime_encode' ))
3190 $mime_parameters = array( 'input-charset' => 'utf-8',
3191 'output-charset' => 'utf-8',
3192 // 'Q' encoding is more readable than 'B'
3195 $string = substr( iconv_mime_encode( '', $string,
3196 $mime_parameters ), 2 );
3200 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3203 else if( preg_match( '/[^0-9a-z\-\.,:;\?! ]/i', $string ))
3205 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3212 * Takes a name and an email address and returns a string that vaguely
3213 * resembles an email address specification conforming to RFC(2)822 ...
3215 * @param string $name name, e.g. John Doe
3216 * @param string $address email address only, e.g. john.doe@example.com
3217 * @return string formatted email address
3220 function COM_formatEmailAddress($name, $address)
3222 $name = trim($name);
3223 $address = trim($address);
3225 if (function_exists('CUSTOM_formatEmailAddress')) {
3226 return CUSTOM_formatEmailAddress($name, $address);
3229 $formatted_name = COM_emailEscape($name);
3231 // if the name comes back unchanged, it's not UTF-8, so preg_match is fine
3232 if (($formatted_name == $name) && preg_match('/[^0-9a-z ]/i', $name)) {
3233 $formatted_name = str_replace('"', '\\"', $formatted_name);
3234 $formatted_name = '"' . $formatted_name . '"';
3237 return $formatted_name . ' <' . $address . '>';
3243 * All emails sent by Geeklog are sent through this function.
3245 * NOTE: Please note that using CC: will expose the email addresses of
3246 * all recipients. Use with care.
3248 * @param string $to recipients name and email address
3249 * @param string $subject subject of the email
3250 * @param string $message the text of the email
3251 * @param string $from (optional) sender of the the email
3252 * @param boolean $html (optional) true if to be sent as HTML email
3253 * @param int $priority (optional) add X-Priority header, if > 0
3254 * @param mixed $optional (optional) other headers or CC:
3255 * @return boolean true if successful, otherwise false
3258 function COM_mail($to, $subject, $message, $from = '', $html = false, $priority = 0, $optional = null)
3265 $from = COM_formatEmailAddress($_CONF['site_name'], $_CONF['site_mail']);
3268 $to = substr($to, 0, strcspn($to, "\r\n"));
3269 if (($optional != null) && !is_array($optional)) {
3270 $optional = substr($optional, 0, strcspn($optional, "\r\n"));
3272 $from = substr($from, 0, strcspn($from, "\r\n"));
3273 $subject = substr($subject, 0, strcspn($subject, "\r\n"));
3274 $subject = COM_emailEscape($subject);
3276 if (function_exists('CUSTOM_mail')) {
3277 return CUSTOM_mail($to, $subject, $message, $from, $html, $priority,
3281 include_once 'Mail.php';
3282 include_once 'Mail/RFC822.php';
3284 $method = $_CONF['mail_settings']['backend'];
3286 if (! isset($mailobj)) {
3287 if (($method == 'sendmail') || ($method == 'smtp')) {
3288 $mailobj =& Mail::factory($method, $_CONF['mail_settings']);
3291 $mailobj =& Mail::factory($method);
3295 $charset = COM_getCharset();
3298 $headers['From'] = $from;
3299 if ($method != 'mail') {
3300 $headers['To'] = $to;
3302 if (($optional != null) && !is_array($optional) && !empty($optional)) {
3303 // assume old (optional) CC: header
3304 $headers['Cc'] = $optional;
3306 $headers['Date'] = date('r'); // RFC822 formatted date
3307 if($method == 'smtp') {
3308 list($usec, $sec) = explode(' ', microtime());
3309 $m = substr($usec, 2, 5);
3310 $headers['Message-Id'] = '<' . date('YmdHis') . '.' . $m
3311 . '@' . $_CONF['mail_settings']['host'] . '>';
3314 $headers['Content-Type'] = 'text/html; charset=' . $charset;
3315 $headers['Content-Transfer-Encoding'] = '8bit';
3317 $headers['Content-Type'] = 'text/plain; charset=' . $charset;
3319 $headers['Subject'] = $subject;
3320 if ($priority > 0) {
3321 $headers['X-Priority'] = $priority;
3323 $headers['X-Mailer'] = 'Geeklog ' . VERSION;
3325 if (!empty($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['SERVER_ADDR']) &&
3326 ($_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR'])) {
3327 $url = COM_getCurrentURL();
3328 if (substr($url, 0, strlen($_CONF['site_admin_url']))
3329 != $_CONF['site_admin_url']) {
3330 $headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
3334 // add optional headers last
3335 if (($optional != null) && is_array($optional)) {
3336 foreach ($optional as $h => $v) {
3341 $retval = $mailobj->send($to, $headers, $message);
3342 if ($retval !== true) {
3343 COM_errorLog($retval->toString(), 1);
3346 return($retval === true ? true : false);
3351 * Creates older stuff block
3353 * Creates the olderstuff block for display.
3354 * Actually updates the olderstuff record in the gl_blocks database.
3358 function COM_olderStuff()
3360 global $_TABLES, $_CONF;
3362 $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']}";
3363 $result = DB_query( $sql );
3364 $nrows = DB_numRows( $result );
3368 $dateonly = $_CONF['dateonly'];
3369 if( empty( $dateonly ))
3371 $dateonly = '%d-%b'; // fallback: day - abbrev. month name
3377 for( $i = 0; $i < $nrows; $i++ )
3379 $A = DB_fetchArray( $result );
3381 $daycheck = strftime( '%A', $A['day'] );
3382 if( $day != $daycheck )
3384 if( $day != 'noday' )
3386 $daylist = COM_makeList($oldnews, 'list-older-stories');
3387 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3388 $string .= $daylist . '<br' . XHTML . '>';
3391 $day2 = strftime( $dateonly, $A['day'] );
3392 $string .= '<h3>' . $daycheck . ' <small>' . $day2
3393 . '</small></h3>' . LB;
3398 $oldnews_url = COM_buildUrl( $_CONF['site_url'] . '/article.php?story='
3400 $oldnews[] = COM_createLink($A['title'], $oldnews_url)
3401 .' (' . COM_numberFormat( $A['comments'] ) . ')';
3404 if( !empty( $oldnews ))
3406 $daylist = COM_makeList($oldnews, 'list-older-stories');
3407 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3408 $string .= $daylist;
3409 $string = addslashes( $string );
3411 DB_query( "UPDATE {$_TABLES['blocks']} SET content = '$string' WHERE name = 'older_stories'" );
3417 * Shows a single Geeklog block
3419 * This shows a single block and is typically called from
3420 * COM_showBlocks OR from plugin code
3422 * @param string $name Logical name of block (not same as title) -- 'user_block', 'admin_block', 'section_block', 'whats_new_block'.
3423 * @param string $help Help file location
3424 * @param string $title Title shown in block header
3425 * @param string $position Side, 'left', 'right' or empty.
3426 * @see function COM_showBlocks
3427 * @return string HTML Formated block
3431 function COM_showBlock( $name, $help='', $title='', $position='' )
3433 global $_CONF, $topic, $_TABLES, $_USER;
3437 if( !isset( $_USER['noboxes'] ))
3439 if( !COM_isAnonUser() )
3441 $_USER['noboxes'] = DB_getItem( $_TABLES['userindex'], 'noboxes',
3442 "uid = {$_USER['uid']}" );
3446 $_USER['noboxes'] = 0;
3453 $retval .= COM_userMenu( $help,$title, $position );
3457 $retval .= COM_adminMenu( $help,$title, $position );
3460 case 'section_block':
3461 $retval .= COM_startBlock( $title, $help,
3462 COM_getBlockTemplate( $name, 'header', $position ))
3463 . COM_showTopics( $topic )
3464 . COM_endBlock( COM_getBlockTemplate( $name, 'footer', $position ));
3467 case 'whats_new_block':
3468 if( !$_USER['noboxes'] )
3470 $retval .= COM_whatsNewBlock( $help, $title, $position );
3480 * Shows Geeklog blocks
3482 * Returns HTML for blocks on a given side and, potentially, for
3483 * a given topic. Currently only used by static pages.
3485 * @param string $side Side to get blocks for (right or left for now)
3486 * @param string $topic Only get blocks for this topic
3487 * @param string $name Block name (not used)
3488 * @see function COM_showBlock
3489 * @return string HTML Formated blocks
3493 function COM_showBlocks( $side, $topic='', $name='all' )
3495 global $_CONF, $_TABLES, $_USER, $LANG21, $topic, $page;
3499 // Get user preferences on blocks
3500 if( !isset( $_USER['noboxes'] ) || !isset( $_USER['boxes'] ))
3502 if( !COM_isAnonUser() )
3504 $result = DB_query( "SELECT boxes,noboxes FROM {$_TABLES['userindex']} "
3505 ."WHERE uid = '{$_USER['uid']}'" );
3506 list($_USER['boxes'], $_USER['noboxes']) = DB_fetchArray( $result );
3510 $_USER['boxes'] = '';
3511 $_USER['noboxes'] = 0;
3515 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3516 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3517 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3519 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3521 $commonsql = "FROM {$_TABLES['blocks']} WHERE is_enabled = 1";
3523 if( $side == 'left' )
3525 $commonsql .= " AND onleft = 1";
3529 $commonsql .= " AND onleft = 0";
3532 if( !empty( $topic ))
3534 $tp = addslashes($topic);
3535 $commonsql .= " AND (tid = '$tp' OR tid = 'all')";
3539 if( COM_onFrontpage() )
3541 $commonsql .= " AND (tid = 'homeonly' OR tid = 'all')";
3545 $commonsql .= " AND (tid = 'all')";
3549 if( !empty( $_USER['boxes'] ))
3551 $BOXES = str_replace( ' ', ',', $_USER['boxes'] );
3553 $commonsql .= " AND (bid NOT IN ($BOXES) OR bid = '-1')";
3556 $commonsql .= ' ORDER BY blockorder,title ASC';
3558 $blocksql['mysql'] .= $commonsql;
3559 $blocksql['mssql'] .= $commonsql;
3560 $result = DB_query( $blocksql );
3561 $nrows = DB_numRows( $result );
3563 // convert result set to an array of associated arrays
3565 for( $i = 0; $i < $nrows; $i++ )
3567 $blocks[] = DB_fetchArray( $result );
3570 // Check and see if any plugins have blocks to show
3571 $pluginBlocks = PLG_getBlocks( $side, $topic, $name );
3572 $blocks = array_merge( $blocks, $pluginBlocks );
3574 // sort the resulting array by block order
3575 $column = 'blockorder';
3576 $sortedBlocks = $blocks;
3577 $num_sortedBlocks = count( $sortedBlocks );
3578 for( $i = 0; $i < $num_sortedBlocks - 1; $i++ )
3580 for( $j = 0; $j < $num_sortedBlocks - 1 - $i; $j++ )
3582 if( $sortedBlocks[$j][$column] > $sortedBlocks[$j+1][$column] )
3584 $tmp = $sortedBlocks[$j];
3585 $sortedBlocks[$j] = $sortedBlocks[$j + 1];
3586 $sortedBlocks[$j + 1] = $tmp;
3590 $blocks = $sortedBlocks;
3592 // Loop though resulting sorted array and pass associative arrays
3593 // to COM_formatBlock
3594 foreach( $blocks as $A )
3596 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 )
3598 $retval .= COM_formatBlock( $A, $_USER['noboxes'] );
3606 * Formats a Geeklog block
3608 * This shows a single block and is typically called from
3609 * COM_showBlocks OR from plugin code
3611 * @param array $A Block Record
3612 * @param boolean $noboxes Set to true if userpref is no blocks
3613 * @return string HTML Formated block
3616 function COM_formatBlock( $A, $noboxes = false )
3618 global $_CONF, $_TABLES, $_USER, $LANG21;
3622 $lang = COM_getLanguageId();
3623 if (!empty($lang)) {
3625 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3626 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3627 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3629 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3631 $commonsql = "FROM {$_TABLES['blocks']} WHERE name = '"
3632 . $A['name'] . '_' . $lang . "'";
3634 $blocksql['mysql'] .= $commonsql;
3635 $blocksql['mssql'] .= $commonsql;
3636 $result = DB_query( $blocksql );
3638 if (DB_numRows($result) == 1) {
3639 // overwrite with data for language-specific block
3640 $A = DB_fetchArray($result);
3644 if( array_key_exists( 'onleft', $A ) )
3646 if( $A['onleft'] == 1 )
3650 $position = 'right';
3656 if( $A['type'] == 'portal' )
3658 if( COM_rdfCheck( $A['bid'], $A['rdfurl'], $A['date'], $A['rdflimit'] ))
3660 $A['content'] = DB_getItem( $_TABLES['blocks'], 'content',
3661 "bid = '{$A['bid']}'");
3665 if( $A['type'] == 'gldefault' )
3667 $retval .= COM_showBlock( $A['name'], $A['help'], $A['title'], $position );
3670 if( $A['type'] == 'phpblock' && !$noboxes )
3672 if( !( $A['name'] == 'whosonline_block' AND DB_getItem( $_TABLES['blocks'], 'is_enabled', "name='whosonline_block'" ) == 0 ))
3674 $function = $A['phpblockfn'];
3676 if (preg_match('/^(phpblock_\w*)\\((.*)\\)$/', $function, $matches) == 1)
3678 $function = $matches[1];
3679 $args = $matches[2];
3681 $blkheader = COM_startBlock( $A['title'], $A['help'],
3682 COM_getBlockTemplate( $A['name'], 'header', $position ));
3683 $blkfooter = COM_endBlock( COM_getBlockTemplate( $A['name'],
3684 'footer', $position ));
3686 if( function_exists( $function ))
3690 $fretval = $function($A, $args);
3692 $fretval = $function();
3694 if( !empty( $fretval ))
3696 $retval .= $blkheader;
3697 $retval .= $fretval;
3698 $retval .= $blkfooter;
3703 // show error message
3704 $retval .= $blkheader;
3705 $retval .= sprintf( $LANG21[31], $function );
3706 $retval .= $blkfooter;
3711 if( !empty( $A['content'] ) && ( trim( $A['content'] ) != '' ) && !$noboxes )
3713 $blockcontent = stripslashes( $A['content'] );
3715 // Hack: If the block content starts with a '<' assume it
3716 // contains HTML and do not call nl2br() which would only add
3717 // unwanted <br> tags.
3719 if( substr( $blockcontent, 0, 1 ) != '<' )
3721 $blockcontent = nl2br( $blockcontent );
3724 // autotags are only(!) allowed in normal blocks
3725 if(( $A['allow_autotags'] == 1 ) && ( $A['type'] == 'normal' ))
3727 $blockcontent = PLG_replaceTags( $blockcontent );
3729 $blockcontent = str_replace( array( '<?', '?>' ), '', $blockcontent );
3731 $retval .= COM_startBlock( $A['title'], $A['help'],
3732 COM_getBlockTemplate( $A['name'], 'header', $position ))
3733 . $blockcontent . LB
3734 . COM_endBlock( COM_getBlockTemplate( $A['name'], 'footer', $position ));
3742 * Checks to see if it's time to import and RDF/RSS block again
3744 * Updates RDF/RSS block if needed
3746 * @param string $bid Block ID
3747 * @param string $rdfurl URL to get headlines from
3748 * @param string $date Last time the headlines were imported
3749 * @param string $maxheadlines max. number of headlines to import
3751 * @see function COM_rdfImport
3754 function COM_rdfCheck( $bid, $rdfurl, $date, $maxheadlines = 0 )
3757 $nextupdate = $date + 3600;
3759 if( $nextupdate < time() )
3761 COM_rdfImport( $bid, $rdfurl, $maxheadlines );
3769 * Syndication import function. Imports headline data to a portal block.
3771 * Rewritten December 19th 2004 by Michael Jervis (mike AT fuckingbrit DOT com).
3772 * Now utilises a Factory Pattern to open a URL and automaticaly retreive a feed
3773 * object populated with feed data. Then import it into the portal block.
3775 * @param string $bid Block ID
3776 * @param string $rdfurl URL to get content from
3777 * @param int $maxheadlines Maximum number of headlines to display
3779 * @see function COM_rdfCheck
3782 function COM_rdfImport($bid, $rdfurl, $maxheadlines = 0)
3784 global $_CONF, $_TABLES, $LANG21;
3786 // Import the feed handling classes:
3787 require_once $_CONF['path_system']
3788 . '/classes/syndication/parserfactory.class.php';
3789 require_once $_CONF['path_system']
3790 . '/classes/syndication/feedparserbase.class.php';
3792 $result = DB_query("SELECT rdf_last_modified, rdf_etag FROM {$_TABLES['blocks']} WHERE bid = $bid");
3793 list($last_modified, $etag) = DB_fetchArray($result);
3795 // Load the actual feed handlers:
3796 $factory = new FeedParserFactory($_CONF['path_system']
3797 . '/classes/syndication/');
3798 $factory->userAgent = 'Geeklog/' . VERSION;
3799 if (!empty($last_modified) && !empty($etag)) {
3800 $factory->lastModified = $last_modified;
3801 $factory->eTag = $etag;
3805 $feed = $factory->reader($rdfurl, $_CONF['default_charset']);
3808 /* We have located a reader, and populated it with the information from
3809 * the syndication file. Now we will sort out our display, and update
3812 if ($maxheadlines == 0) {
3813 if (!empty($_CONF['syndication_max_headlines'])) {
3814 $maxheadlines = $_CONF['syndication_max_headlines'];
3816 $maxheadlines = count($feed->articles);
3820 $update = date('Y-m-d H:i:s');
3821 $last_modified = '';
3822 if (!empty($factory->lastModified)) {
3823 $last_modified = addslashes($factory->lastModified);
3826 if (!empty($factory->eTag)) {
3827 $etag = addslashes($factory->eTag);
3830 if (empty($last_modified) || empty($etag)) {
3831 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = '$bid'");
3833 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = '$last_modified', rdf_etag = '$etag' WHERE bid = '$bid'");
3836 $charset = COM_getCharset();
3838 // format articles for display
3839 $readmax = min($maxheadlines, count($feed->articles));
3840 for ($i = 0; $i < $readmax; $i++) {
3841 if (empty($feed->articles[$i]['title'])) {
3842 $feed->articles[$i]['title'] = $LANG21[61];
3845 if ($charset == 'utf-8') {
3846 $title = $feed->articles[$i]['title'];
3848 $title = utf8_decode($feed->articles[$i]['title']);
3850 if ($feed->articles[$i]['link'] != '') {
3851 $content = COM_createLink($title, $feed->articles[$i]['link']);
3852 } elseif ($feed->articles[$i]['enclosureurl'] != '') {
3853 $content = COM_createLink($title, $feed->articles[$i]['enclosureurl']);
3857 $articles[] = $content;
3861 $content = COM_makeList($articles, 'list-feed');
3862 $content = str_replace(array("\015", "\012"), '', $content);
3864 if (strlen($content) > 65000) {
3865 $content = $LANG21[68];
3868 // Standard theme based function to put it in the block
3869 $result = DB_change($_TABLES['blocks'], 'content',
3870 addslashes($content), 'bid', $bid);
3871 } else if ($factory->errorStatus !== false) {
3872 // failed to aquire info, 0 out the block and log an error
3873 COM_errorLog("Unable to aquire feed reader for $rdfurl", 1);
3874 COM_errorLog($factory->errorStatus[0] . ' ' .
3875 $factory->errorStatus[1] . ' ' .
3876 $factory->errorStatus[2]);
3877 $content = addslashes($LANG21[4]);
3878 DB_query("UPDATE {$_TABLES['blocks']} SET content = '$content', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = $bid");
3884 * Returns what HTML is allowed in content
3886 * Returns what HTML tags the system allows to be used inside content.
3887 * You can modify this by changing $_CONF['user_html'] in the configuration
3888 * (for admins, see also $_CONF['admin_html']).
3890 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
3891 * @param boolean $list_only true = return only the list of HTML tags
3892 * @return string HTML <div>/<span> enclosed string
3893 * @see function COM_checkHTML
3894 * @todo Bugs: The list always includes the [code], [raw], and [page_break]
3895 * tags when story.* permissions are required, even when those tags
3896 * are not actually available (e.g. in comments on stories).
3899 function COM_allowedHTML($permissions = 'story.edit', $list_only = false)
3901 global $_CONF, $LANG01;
3905 if (isset($_CONF['skip_html_filter_for_root']) &&
3906 ($_CONF['skip_html_filter_for_root'] == 1) &&
3907 SEC_inGroup('Root')) {
3910 $retval .= '<span class="warningsmall">' . $LANG01[123]
3913 $retval .= '<div dir="ltr" class="warningsmall">';
3918 $retval .= '<span class="warningsmall">' . $LANG01[31] . '</span> ';
3921 if (empty($permissions) || !SEC_hasRights($permissions) ||
3922 empty($_CONF['admin_html'])) {
3923 $html = $_CONF['user_html'];
3925 $html = array_merge_recursive($_CONF['user_html'],
3926 $_CONF['admin_html']);
3929 $retval .= '<div dir="ltr" class="warningsmall">';
3930 foreach ($html as $tag => $attr) {
3931 $retval .= '<' . $tag . '>, ';
3935 $with_story_perms = false;
3936 $perms = explode(',', $permissions);
3937 foreach ($perms as $p) {
3938 if (substr($p, 0, 6) == 'story.') {
3939 $with_story_perms = true;
3944 if ($with_story_perms) {
3945 $retval .= '[code], [raw], ';
3947 if ($_CONF['allow_page_breaks'] == 1) {
3948 $retval .= '[page_break], ';
3953 $autotags = array_keys(PLG_collectTags());
3954 $retval .= '[' . implode(':], [', $autotags) . ':]';
3955 $retval .= '</div>';
3961 * Return the password for the given username
3963 * Fetches a password for the given user
3965 * @param string $loginname username to get password for
3966 * @return string Password or ''
3970 function COM_getPassword( $loginname )
3972 global $_TABLES, $LANG01;
3974 $result = DB_query( "SELECT passwd FROM {$_TABLES['users']} WHERE username='$loginname'" );
3976 $nrows = DB_numRows( $result );
3978 if(( $tmp == 0 ) && ( $nrows == 1 ))
3980 $U = DB_fetchArray( $result );
3981 return $U['passwd'];
3985 $tmp = $LANG01[32] . ": '" . $loginname . "'";
3986 COM_errorLog( $tmp, 1 );
3994 * Return the username or fullname for the passed member id (uid)
3996 * Allows the siteAdmin to determine if loginname (username) or fullname
3997 * should be displayed.
3999 * @param int $uid site member id
4000 * @param string $username Username, if this is set no lookup is done.
4001 * @param string $fullname Users full name.
4002 * @param string $remoteusername Username on remote service
4003 * @param string $remoteservice Remote login service.
4004 * @return string Username, fullname or username@Service
4007 function COM_getDisplayName( $uid = '', $username='', $fullname='', $remoteusername='', $remoteservice='' )
4009 global $_CONF, $_TABLES, $_USER;
4013 if( COM_isAnonUser() )
4019 $uid = $_USER['uid'];
4023 if( empty( $username ))
4025 $query = DB_query( "SELECT username, fullname, remoteusername, remoteservice FROM {$_TABLES['users']} WHERE uid='$uid'" );
4026 list( $username, $fullname, $remoteusername, $remoteservice ) = DB_fetchArray( $query );
4029 if( !empty( $fullname ) && ($_CONF['show_fullname'] == 1 ))
4033 else if(( $_CONF['user_login_method']['3rdparty'] || $_CONF['user_login_method']['openid'] ) && !empty( $remoteusername ))
4035 if( !empty( $username ))
4037 $remoteusername = $username;
4040 if( $_CONF['show_servicename'] )
4042 return "$remoteusername@$remoteservice";
4046 return $remoteusername;
4055 * Adds a hit to the system
4057 * This function is called in the footer of every page and is used to
4058 * track the number of hits to the Geeklog system. This information is
4059 * shown on stats.php
4067 DB_change($_TABLES['vars'], 'value', 'value + 1', 'name', 'totalhits', '', true);
4071 * This will email new stories in the topics that the user is interested in
4073 * In account information the user can specify which topics for which they
4074 * will receive any new article for in a daily digest.
4079 function COM_emailUserTopics()
4081 global $_CONF, $_TABLES, $LANG04, $LANG08, $LANG24;
4083 if ($_CONF['emailstories'] == 0) {
4087 $subject = strip_tags( $_CONF['site_name'] . $LANG08[30] . strftime( '%Y-%m-%d', time() ));
4091 // Get users who want stories emailed to them
4092 $usersql = "SELECT username,email,etids,{$_TABLES['users']}.uid AS uuid "
4093 . "FROM {$_TABLES['users']}, {$_TABLES['userindex']} "
4094 . "WHERE {$_TABLES['users']}.uid > 1 AND {$_TABLES['userindex']}.uid = {$_TABLES['users']}.uid AND (etids <> '-' OR etids IS NULL) ORDER BY {$_TABLES['users']}.uid";
4096 $users = DB_query( $usersql );
4097 $nrows = DB_numRows( $users );
4099 $lastrun = DB_getItem( $_TABLES['vars'], 'value', "name = 'lastemailedstories'" );
4101 // For each user, pull the stories they want and email it to them
4102 for( $x = 0; $x < $nrows; $x++ )
4104 $U = DB_fetchArray( $users );
4106 $storysql = array();
4107 $storysql['mysql'] = "SELECT sid,uid,date AS day,title,introtext,postmode";
4109 $storysql['mssql'] = "SELECT sid,uid,date AS day,title,CAST(introtext AS text) AS introtext,postmode";
4111 $commonsql = " FROM {$_TABLES['stories']} WHERE draft_flag = 0 AND date <= NOW() AND date >= '{$lastrun}'";
4113 $topicsql = "SELECT tid FROM {$_TABLES['topics']}"
4114 . COM_getPermSQL( 'WHERE', $U['uuid'] );
4115 $tresult = DB_query( $topicsql );
4116 $trows = DB_numRows( $tresult );
4120 // this user doesn't seem to have access to any topics ...
4125 for( $i = 0; $i < $trows; $i++ )
4127 $T = DB_fetchArray( $tresult );
4128 $TIDS[] = $T['tid'];
4131 if( !empty( $U['etids'] ))
4133 $ETIDS = explode( ' ', $U['etids'] );
4134 $TIDS = array_intersect( $TIDS, $ETIDS );
4137 if( count( $TIDS ) > 0)
4139 $commonsql .= " AND (tid IN ('" . implode( "','", $TIDS ) . "'))";
4142 $commonsql .= COM_getPermSQL( 'AND', $U['uuid'] );
4143 $commonsql .= ' ORDER BY featured DESC, date DESC';
4145 $storysql['mysql'] .= $commonsql;
4146 $storysql['mssql'] .= $commonsql;
4148 $stories = DB_query( $storysql );
4149 $nsrows = DB_numRows( $stories );
4153 // If no new stories where pulled for this user, continue with next
4157 $mailtext = $LANG08[29] . strftime( $_CONF['shortdate'], time() ) . "\n";
4159 for( $y = 0; $y < $nsrows; $y++ )
4161 // Loop through stories building the requested email message
4162 $S = DB_fetchArray( $stories );
4164 $mailtext .= "\n------------------------------\n\n";
4165 $mailtext .= "$LANG08[31]: "
4166 . COM_undoSpecialChars( stripslashes( $S['title'] )) . "\n";
4167 if( $_CONF['contributedbyline'] == 1 )
4169 if( empty( $authors[$S['uid']] ))
4171 $storyauthor = COM_getDisplayName ($S['uid']);
4172 $authors[$S['uid']] = $storyauthor;
4176 $storyauthor = $authors[$S['uid']];
4178 $mailtext .= "$LANG24[7]: " . $storyauthor . "\n";
4181 $mailtext .= "$LANG08[32]: " . strftime( $_CONF['date'], strtotime( $S['day' ])) . "\n\n";
4183 if( $_CONF['emailstorieslength'] > 0 )
4185 if($S['postmode']==='wikitext'){
4186 $storytext = COM_undoSpecialChars( strip_tags( COM_renderWikiText ( stripslashes( $S['introtext'] ))));
4188 $storytext = COM_undoSpecialChars( strip_tags( PLG_replaceTags( stripslashes( $S['introtext'] ))));
4191 if( $_CONF['emailstorieslength'] > 1 )
4193 $storytext = COM_truncate( $storytext,
4194 $_CONF['emailstorieslength'], '...' );
4197 $mailtext .= $storytext . "\n\n";
4200 $mailtext .= $LANG08[33] . ' ' . COM_buildUrl( $_CONF['site_url']
4201 . '/article.php?story=' . $S['sid'] ) . "\n";
4204 $mailtext .= "\n------------------------------\n";
4205 $mailtext .= "\n$LANG08[34]\n";
4206 $mailtext .= "\n------------------------------\n";
4208 $mailto = $U['username'] . ' <' . $U['email'] . '>';
4210 if ($_CONF['site_mail'] !== $_CONF['noreply_mail']) {
4211 $mailfrom = $_CONF['noreply_mail'];
4212 $mailtext .= LB . LB . $LANG04[159];
4214 $mailfrom = $_CONF['site_mail'];
4216 COM_mail( $mailto, $subject, $mailtext , $mailfrom);
4219 DB_query( "UPDATE {$_TABLES['vars']} SET value = NOW() WHERE name = 'lastemailedstories'" );
4223 * Shows any new information in a block
4225 * Return the HTML that shows any new stories, comments, etc
4227 * @param string $help Help file for block
4228 * @param string $title Title used in block header
4229 * @param string $position Position in which block is being rendered 'left', 'right' or blank (for centre)
4230 * @return string Return the HTML that shows any new stories, comments, etc
4234 function COM_whatsNewBlock( $help = '', $title = '', $position = '' )
4236 global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_WHATSNEW, $page, $newstories;
4238 $retval = COM_startBlock( $title, $help,
4239 COM_getBlockTemplate( 'whats_new_block', 'header', $position ));
4242 if(( $_CONF['hidenewstories'] == 0 ) || ( $_CONF['hidenewcomments'] == 0 )
4243 || ( $_CONF['trackback_enabled']
4244 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4246 $topicsql = COM_getTopicSql ('AND', 0, $_TABLES['stories']);
4249 if( $_CONF['hidenewstories'] == 0 )
4252 $archivetid = DB_getItem( $_TABLES['topics'], 'tid', "archive_flag=1" );
4253 if( !empty( $archivetid ))
4255 $archsql = " AND (tid <> '" . addslashes( $archivetid ) . "')";
4258 // Find the newest stories
4259 $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' );
4260 $result = DB_query( $sql );
4261 $A = DB_fetchArray( $result );
4262 $nrows = $A['count'];
4264 if( empty( $title ))
4266 $title = DB_getItem( $_TABLES['blocks'], 'title', "name='whats_new_block'" );
4269 // Any late breaking news stories?
4270 $retval .= '<h3>' . $LANG01[99] . '</h3>';
4274 $newmsg = COM_formatTimeString( $LANG_WHATSNEW['new_string'],
4275 $_CONF['newstoriesinterval'], $LANG01[11], $nrows);
4277 if( $newstories && ( $page < 2 ))
4279 $retval .= $newmsg . '<br' . XHTML . '>';
4283 $retval .= COM_createLink($newmsg, $_CONF['site_url']
4284 . '/index.php?display=new') . '<br' . XHTML . '>';
4289 $retval .= $LANG01[100] . '<br' . XHTML . '>';
4292 if(( $_CONF['hidenewcomments'] == 0 ) || ( $_CONF['trackback_enabled']
4293 && ( $_CONF['hidenewtrackbacks'] == 0 ))
4294 || ( $_CONF['hidenewplugins'] == 0 ))
4296 $retval .= '<br' . XHTML . '>';
4300 if( $_CONF['hidenewcomments'] == 0 )
4302 // Go get the newest comments
4303 $retval .= '<h3>' . $LANG01[83] . ' <small>'
4304 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4305 $_CONF['newcommentsinterval'] )
4310 if( !COM_isAnonUser() )
4312 $stwhere .= "({$_TABLES['stories']}.owner_id IS NOT NULL AND {$_TABLES['stories']}.perm_owner IS NOT NULL) OR ";
4313 $stwhere .= "({$_TABLES['stories']}.group_id IS NOT NULL AND {$_TABLES['stories']}.perm_group IS NOT NULL) OR ";
4314 $stwhere .= "({$_TABLES['stories']}.perm_members IS NOT NULL)";
4318 $stwhere .= "({$_TABLES['stories']}.perm_anon IS NOT NULL)";
4320 $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";
4322 $result = DB_query( $sql );
4324 $nrows = DB_numRows( $result );
4328 $newcomments = array();
4330 for( $x = 0; $x < $nrows; $x++ )
4332 $A = DB_fetchArray( $result );
4334 if(( $A['type'] == 'article' ) || empty( $A['type'] ))
4336 $url = COM_buildUrl( $_CONF['site_url']
4337 . '/article.php?story=' . $A['sid'] ) . '#comments';
4340 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4341 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4343 if( $title != $titletouse )
4345 $attr = array('title' => htmlspecialchars($title));
4351 $acomment = str_replace( '$', '$', $titletouse );
4352 $acomment = str_replace( ' ', ' ', $acomment );
4354 if( $A['dups'] > 1 )
4356 $acomment .= ' [+' . $A['dups'] . ']';
4359 $newcomments[] = COM_createLink($acomment, $url, $attr);
4362 $retval .= COM_makeList( $newcomments, 'list-new-comments' );
4366 $retval .= $LANG01[86] . '<br' . XHTML . '>' . LB;
4368 if(( $_CONF['hidenewplugins'] == 0 )
4369 || ( $_CONF['trackback_enabled']
4370 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4372 $retval .= '<br' . XHTML . '>';
4376 if( $_CONF['trackback_enabled'] && ( $_CONF['hidenewtrackbacks'] == 0 ))
4378 $retval .= '<h3>' . $LANG01[114] . ' <small>'
4379 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4380 $_CONF['newtrackbackinterval'] )
4383 $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";
4384 $result = DB_query( $sql );
4386 $nrows = DB_numRows( $result );
4389 $newcomments = array();
4391 for( $i = 0; $i < $nrows; $i++ )
4393 $A = DB_fetchArray( $result );
4395 $url = COM_buildUrl( $_CONF['site_url']
4396 . '/article.php?story=' . $A['sid'] ) . '#trackback';
4398 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4399 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4402 if( $title != $titletouse )
4404 $attr = array('title' => htmlspecialchars($title));
4410 $acomment = str_replace( '$', '$', $titletouse );
4411 $acomment = str_replace( ' ', ' ', $acomment );
4413 if( $A['count'] > 1 )
4415 $acomment .= ' [+' . $A['count'] . ']';
4418 $newcomments[] = COM_createLink($acomment, $url, $attr);
4421 $retval .= COM_makeList( $newcomments, 'list-new-trackbacks' );
4425 $retval .= $LANG01[115] . '<br' . XHTML . '>' . LB;
4427 if( $_CONF['hidenewplugins'] == 0 )
4429 $retval .= '<br' . XHTML . '>';
4433 if( $_CONF['hidenewplugins'] == 0 )
4435 list( $headlines, $smallheadlines, $content ) = PLG_getWhatsNew();
4436 $plugins = count( $headlines );
4439 for( $i = 0; $i < $plugins; $i++ )
4441 $retval .= '<h3>' . $headlines[$i] . ' <small>'
4442 . $smallheadlines[$i] . '</small></h3>';
4443 if( is_array( $content[$i] ))
4445 $retval .= COM_makeList( $content[$i], 'list-new-plugins' );
4449 $retval .= $content[$i];
4452 if( $i + 1 < $plugins )
4454 $retval .= '<br' . XHTML . '>';
4460 $retval .= COM_endBlock( COM_getBlockTemplate( 'whats_new_block', 'footer', $position ));
4466 * Creates the string that indicates the timespan in which new items were found
4468 * @param string $time_string template string
4469 * @param int $time number of seconds in which results are found
4470 * @param string $type type (translated string) of new item
4471 * @param int $amount amount of things that have been found.
4473 function COM_formatTimeString( $time_string, $time, $type = '', $amount = 0 )
4475 global $LANG_WHATSNEW;
4477 $retval = $time_string;
4479 // This is the amount you have to divide the previous by to get the
4480 // different time intervals: hour, day, week, months
4481 $time_divider = array( 60, 60, 24, 7, 30 );
4483 // These are the respective strings to the numbers above. They have to match
4484 // the strings in $LANG_WHATSNEW (i.e. these are the keys for the array -
4485 // the actual text strings are taken from the language file).
4486 $time_description = array( 'minute', 'hour', 'day', 'week', 'month' );
4487 $times_description = array( 'minutes', 'hours', 'days', 'weeks', 'months' );
4489 $time_dividers = count( $time_divider );
4490 for( $s = 0; $s < $time_dividers; $s++ )
4492 $time = $time / $time_divider[$s];
4493 if( $time < $time_divider[$s + 1] )
4499 $time_str = $time_description[$s];
4501 else // go back to the previous unit, e.g. 1 day -> 24 hours
4503 $time_str = $times_description[$s - 1];
4504 $time *= $time_divider[$s];
4509 $time_str = $times_description[$s];
4511 $fields = array( '%n', '%i', '%t', '%s' );
4512 $values = array( $amount, $type, $time, $LANG_WHATSNEW[$time_str] );
4513 $retval = str_replace( $fields, $values, $retval );
4522 * Displays a message text in a "System Message" block
4524 * @param string $message Message text; may contain HTML
4525 * @param string $title (optional) alternative block title
4526 * @return string HTML block with message
4527 * @see COM_showMessage
4528 * @see COM_showMessageFromParameter
4531 function COM_showMessageText($message, $title = '')
4533 global $_CONF, $MESSAGE, $_IMAGE_TYPE;
4537 if (!empty($message)) {
4538 if (empty($title)) {
4539 $title = $MESSAGE[40];
4541 $timestamp = strftime($_CONF['daytime']);
4542 $retval .= COM_startBlock($title . ' - ' . $timestamp, '',
4543 COM_getBlockTemplate('_msg_block', 'header'))
4544 . '<p class="sysmessage"><img src="' . $_CONF['layout_url']
4545 . '/images/sysmessage.' . $_IMAGE_TYPE . '" alt="" ' . XHTML
4546 . '>' . $message . '</p>'
4547 . COM_endBlock(COM_getBlockTemplate('_msg_block', 'footer'));
4554 * Displays a message on the webpage
4556 * Display one of the predefined messages from the $MESSAGE array. If a plugin
4557 * name is provided, display that plugin's message instead.
4559 * @param int $msg ID of message to show
4560 * @param string $plugin Optional name of plugin to lookup plugin defined message
4561 * @return string HTML block with message
4562 * @see COM_showMessageFromParameter
4563 * @see COM_showMessageText
4566 function COM_showMessage($msg, $plugin = '')
4573 if (!empty($plugin)) {
4574 $var = 'PLG_' . $plugin . '_MESSAGE' . $msg;
4579 $message = sprintf($MESSAGE[61], $plugin);
4580 COM_errorLog($message . ": " . $var, 1);
4583 $message = $MESSAGE[$msg];
4586 if (!empty($message)) {
4587 $retval .= COM_showMessageText($message);
4595 * Displays a message, as defined by URL parameters
4597 * Helper function to display a message, if URL parameters 'msg' and 'plugin'
4598 * (optional) are defined. Only for GET requests, but that's what Geeklog uses
4599 * everywhere anyway.
4601 * @return string HTML block with message
4602 * @see COM_showMessage
4603 * @see COM_showMessageText
4606 function COM_showMessageFromParameter()
4610 if (isset($_GET['msg'])) {
4611 $msg = COM_applyFilter($_GET['msg'], true);
4614 if (isset($_GET['plugin'])) {
4615 $plugin = COM_applyFilter($_GET['plugin']);
4617 $retval .= COM_showMessage($msg, $plugin);
4625 * Prints Google(tm)-like paging navigation
4627 * @param string $base_url base url to use for all generated links
4628 * @param int $curpage current page we are on
4629 * @param int $num_pages Total number of pages
4630 * @param string $page_str page-variable name AND '='
4631 * @param boolean $do_rewrite if true, url-rewriting is respected
4632 * @param string $msg to be displayed with the navigation
4633 * @param string $open_ended replace next/last links with this
4634 * @return string HTML formatted widget
4636 function COM_printPageNavigation( $base_url, $curpage, $num_pages,
4637 $page_str='page=', $do_rewrite=false, $msg='',
4644 if( $num_pages < 2 )
4651 $hasargs = strstr( $base_url, '?' );
4669 $retval .= COM_createLink($LANG05[7], $base_url) . ' | ';
4671 if( ( $curpage - 1 ) > 1 )
4673 $pg = $sep . $page_str . ( $curpage - 1 );
4675 $retval .= COM_createLink($LANG05[6], $base_url . $pg ) . ' | ';
4679 $retval .= $LANG05[7] . ' | ' ;
4680 $retval .= $LANG05[6] . ' | ' ;
4683 for( $pgcount = ( $curpage - 10 ); ( $pgcount <= ( $curpage + 9 )) AND ( $pgcount <= $num_pages ); $pgcount++ )
4690 if( $pgcount == $curpage )
4692 $retval .= '<b>' . $pgcount . '</b> ';
4699 $pg = $sep . $page_str . $pgcount;
4701 $retval .= COM_createLink($pgcount, $base_url . $pg) . ' ';
4705 if( !empty( $open_ended ))
4707 $retval .= '| ' . $open_ended;
4709 else if( $curpage == $num_pages )
4711 $retval .= '| ' . $LANG05[5] . ' ';
4712 $retval .= '| ' . $LANG05[8];
4716 $retval .= '| ' . COM_createLink($LANG05[5], $base_url . $sep
4717 . $page_str . ($curpage + 1));
4718 $retval .= ' | ' . COM_createLink($LANG05[8], $base_url . $sep
4719 . $page_str . $num_pages);
4722 if( !empty( $retval ))
4728 $retval = '<div class="pagenav">' . $msg . $retval . '</div>';
4735 * Returns formatted date/time for user
4737 * This function COM_takes a date in either unixtimestamp or in english and
4738 * formats it to the users preference. If the user didn't specify a format
4739 * the format in the config file is used. This returns an array where array[0]
4740 * is the formatted date and array[1] is the unixtimestamp
4742 * @param string $date date to format, otherwise we format current date/time
4743 * @return array array[0] is the formatted date and array[1] is the unixtimestamp.
4746 function COM_getUserDateTimeFormat( $date='' )
4748 global $_TABLES, $_USER, $_CONF;
4750 // Get display format for time
4752 if( !COM_isAnonUser() )
4754 if( empty( $_USER['format'] ))
4756 $dateformat = $_CONF['date'];
4760 $dateformat = $_USER['format'];
4765 $dateformat = $_CONF['date'];
4770 // Date is empty, get current date/time
4773 else if( is_numeric( $date ))
4775 // This is a timestamp
4780 // This is a string representation of a date/time
4781 $stamp = strtotime( $date );
4786 $date = strftime( $dateformat, $stamp );
4788 return array( $date, $stamp );
4792 * Returns user-defined cookie timeout
4794 * In account preferences users can specify when their long-term cookie expires.
4795 * This function returns that value.
4797 * @return int Cookie time out value in seconds
4800 function COM_getUserCookieTimeout()
4802 global $_TABLES, $_USER, $_CONF;
4804 if( empty( $_USER ))
4809 $timeoutvalue = DB_getItem( $_TABLES['users'], 'cookietimeout', "uid = {$_USER['uid']}" );
4811 if( empty( $timeoutvalue ))
4816 return $timeoutvalue;
4820 * Shows who is online in slick little block
4821 * @return string HTML string of online users seperated by line breaks.
4824 function phpblock_whosonline()
4826 global $_CONF, $_TABLES, $_USER, $LANG01, $_IMAGE_TYPE;
4830 $expire_time = time() - $_CONF['whosonline_threshold'];
4832 $byname = 'username';
4833 if( $_CONF['show_fullname'] == 1 )
4835 $byname .= ',fullname';
4837 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4839 $byname .= ',remoteusername,remoteservice';
4842 $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}" );
4843 $nrows = DB_numRows( $result );
4848 for( $i = 0; $i < $nrows; $i++ )
4850 $A = DB_fetchArray( $result );
4852 if( $A['showonline'] == 1 )
4855 if( $_CONF['show_fullname'] == 1 )
4857 $fullname = $A['fullname'];
4859 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4861 $username = COM_getDisplayName( $A['uid'], $A['username'],
4862 $fullname, $A['remoteusername'], $A['remoteservice'] );
4866 $username = COM_getDisplayName( $A['uid'], $A['username'],
4869 $url = $_CONF['site_url'] . '/users.php?mode=profile&uid=' . $A['uid'];
4870 $retval .= COM_createLink($username, $url);
4872 if( !empty( $A['photo'] ) AND $_CONF['allow_user_photo'] == 1)
4874 $usrimg = '<img src="' . $_CONF['layout_url']
4875 . '/images/smallcamera.' . $_IMAGE_TYPE
4876 . '" alt=""' . XHTML . '>';
4877 $retval .= ' ' . COM_createLink($usrimg, $url);
4879 $retval .= '<br' . XHTML . '>';
4884 // this user does not want to show up in Who's Online
4885 $num_anon++; // count as anonymous
4889 $num_anon += DB_count( $_TABLES['sessions'], 'uid', 1 );
4891 if(( $_CONF['whosonline_anonymous'] == 1 ) &&
4894 // note that we're overwriting the contents of $retval here
4897 $retval = $LANG01[112] . ': ' . COM_numberFormat($num_reg)
4898 . '<br' . XHTML . '>';
4908 $retval .= $LANG01[41] . ': ' . COM_numberFormat($num_anon)
4909 . '<br' . XHTML . '>';
4916 * Gets the <option> values for calendar months
4918 * @param string $selected Selected month
4919 * @see function COM_getDayFormOptions
4920 * @see function COM_getYearFormOptions
4921 * @see function COM_getHourFormOptions
4922 * @see function COM_getMinuteFormOptions
4923 * @return string HTML Months as option values
4926 function COM_getMonthFormOptions( $selected = '' )
4930 $month_options = '';
4932 for( $i = 1; $i <= 12; $i++ )
4935 $month_options .= '<option value="' . $mval . '"';
4937 if( $i == $selected )
4939 $month_options .= ' selected="selected"';
4942 $month_options .= '>' . $LANG_MONTH[$mval] . '</option>';
4945 return $month_options;
4949 * Gets the <option> values for calendar days
4951 * @param string $selected Selected day
4952 * @see function COM_getMonthFormOptions
4953 * @see function COM_getYearFormOptions
4954 * @see function COM_getHourFormOptions
4955 * @see function COM_getMinuteFormOptions
4956 * @return string HTML days as option values
4959 function COM_getDayFormOptions( $selected = '' )
4963 for( $i = 1; $i <= 31; $i++ )
4974 $day_options .= '<option value="' . $dval . '"';
4976 if( $i == $selected )
4978 $day_options .= ' selected="selected"';
4981 $day_options .= '>' . $dval . '</option>';
4984 return $day_options;
4988 * Gets the <option> values for calendar years
4990 * Returns Option list Containing 5 years starting with current
4991 * unless @selected is < current year then starts with @selected
4993 * @param string $selected Selected year
4994 * @param int $startoffset Optional (can be +/-) Used to determine start year for range of years
4995 * @param int $endoffset Optional (can be +/-) Used to determine end year for range of years
4996 * @see function COM_getMonthFormOptions
4997 * @see function COM_getDayFormOptions
4998 * @see function COM_getHourFormOptions
4999 * @see function COM_getMinuteFormOptions
5000 * @return string HTML years as option values
5003 function COM_getYearFormOptions($selected = '', $startoffset = -1, $endoffset = 5)
5006 $start_year = date('Y') + $startoffset;
5007 $cur_year = date('Y', time());
5008 $finish_year = $cur_year + $endoffset;
5010 if (!empty($selected)) {
5011 if ($selected < $cur_year) {
5012 $start_year = $selected;
5016 for ($i = $start_year; $i <= $finish_year; $i++) {
5017 $year_options .= '<option value="' . $i . '"';
5019 if ($i == $selected) {
5020 $year_options .= ' selected="selected"';
5023 $year_options .= '>' . $i . '</option>';
5026 return $year_options;
5030 * Gets the <option> values for clock hours
5032 * @param string $selected Selected hour
5033 * @param int $mode 12 or 24 hour mode
5034 * @return string HTML string of options
5035 * @see function COM_getMonthFormOptions
5036 * @see function COM_getDayFormOptions
5037 * @see function COM_getYearFormOptions
5038 * @see function COM_getMinuteFormOptions
5041 function COM_getHourFormOptions( $selected = '', $mode = 12 )
5047 for( $i = 1; $i <= 11; $i++ )
5060 $hour_options .= '<option value="12"';
5062 if( $selected == 12 )
5064 $hour_options .= ' selected="selected"';
5067 $hour_options .= '>12</option>';
5070 $hour_options .= '<option value="' . $hval . '"';
5072 if( $selected == $i )
5074 $hour_options .= ' selected="selected"';
5077 $hour_options .= '>' . $i . '</option>';
5080 else // if( $mode == 24 )
5082 for( $i = 0; $i < 24; $i++ )
5093 $hour_options .= '<option value="' . $hval . '"';
5095 if( $selected == $i )
5097 $hour_options .= ' selected="selected"';
5100 $hour_options .= '>' . $i . '</option>';
5104 return $hour_options;
5108 * Gets the <option> values for clock minutes
5110 * @param string $selected Selected minutes
5111 * @param int $step number of minutes between options, e.g. 15
5112 * @see function COM_getMonthFormOptions
5113 * @see function COM_getDayFormOptions
5114 * @see function COM_getHourFormOptions
5115 * @see function COM_getYearFormOptions
5116 * @return string HTML of option minutes
5119 function COM_getMinuteFormOptions( $selected = '', $step = 1 )
5121 $minute_options = '';
5123 if(( $step < 1 ) || ( $step > 30 ))
5128 for( $i = 0; $i <= 59; $i += $step )
5139 $minute_options .= '<option value="' . $mval . '"';
5141 if( $selected == $i )
5143 $minute_options .= ' selected="selected"';
5146 $minute_options .= '>' . $mval . '</option>';
5149 return $minute_options;
5153 * For backward compatibility only.
5154 * This function should always have been called COM_getMinuteFormOptions
5155 * @see COM_getMinuteFormOptions
5157 function COM_getMinuteOptions( $selected = '', $step = 1 )
5159 return COM_getMinuteFormOptions( $selected, $step );
5163 * Create an am/pm selector dropdown menu
5165 * @param string $name name of the <select>
5166 * @param string $selected preselection: 'am' or 'pm'
5167 * @return string HTML for the dropdown; empty string in 24 hour mode
5170 function COM_getAmPmFormSelection( $name, $selected = '' )
5176 if( isset( $_CONF['hour_mode'] ) && ( $_CONF['hour_mode'] == 24 ))
5182 if( empty( $selected ))
5184 $selected = date( 'a' );
5187 $retval .= '<select name="' . $name . '">' . LB;
5188 $retval .= '<option value="am"';
5189 if( $selected == 'am' )
5191 $retval .= ' selected="selected"';
5193 $retval .= '>am</option>' . LB . '<option value="pm"';
5194 if( $selected == 'pm' )
5196 $retval .= ' selected="selected"';
5198 $retval .= '>pm</option>' . LB . '</select>' . LB;
5205 * Creates an HTML unordered list from the given array.
5206 * It formats one list item per array element, using the list.thtml
5207 * and listitem.thtml templates.
5209 * @param array $listofitems Items to list out
5210 * @param string $classname optional CSS class name for the list
5211 * @return string HTML unordered list of array items
5213 function COM_makeList($listofitems, $classname = '')
5217 $list = new Template($_CONF['path_layout']);
5218 $list->set_file(array('list' => 'list.thtml',
5219 'listitem' => 'listitem.thtml'));
5220 $list->set_var( 'xhtml', XHTML );
5221 $list->set_var('site_url', $_CONF['site_url']);
5222 $list->set_var('site_admin_url', $_CONF['site_admin_url']);
5223 $list->set_var('layout_url', $_CONF['layout_url']);
5225 if (empty($classname)) {
5226 $list->set_var('list_class', '');
5227 $list->set_var('list_class_name', '');
5229 $list->set_var('list_class', 'class="' . $classname . '"');
5230 $list->set_var('list_class_name', $classname);
5233 if (is_array($listofitems)) {
5234 foreach ($listofitems as $oneitem) {
5235 $list->set_var('list_item', $oneitem);
5236 $list->parse('list_items', 'listitem', true);
5240 $list->parse('newlist', 'list', true);
5242 return $list->finish($list->get_var('newlist'));
5246 * Check if speed limit applies
5248 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5249 * @param int $max max number of allowed tries within speed limit
5250 * @param string $property IP address or other identifiable property
5251 * @return int 0: does not apply, else: seconds since last post
5253 function COM_checkSpeedlimit($type = 'submit', $max = 1, $property = '')
5259 if (empty($property)) {
5260 $property = $_SERVER['REMOTE_ADDR'];
5262 $property = addslashes($property);
5264 $res = DB_query("SELECT date FROM {$_TABLES['speedlimit']} WHERE (type = '$type') AND (ipaddress = '$property') ORDER BY date ASC");
5266 // If the number of allowed tries has not been reached,
5267 // return 0 (didn't hit limit)
5268 if (DB_numRows($res) < $max) {
5272 list($date) = DB_fetchArray($res);
5274 if (!empty($date)) {
5275 $last = time() - $date;
5277 // just in case someone manages to submit something in < 1 sec.
5286 * Store post info for speed limit
5288 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5289 * @param string $property IP address or other identifiable property
5292 function COM_updateSpeedlimit($type = 'submit', $property = '')
5296 if (empty($property)) {
5297 $property = $_SERVER['REMOTE_ADDR'];
5299 $property = addslashes($property);
5301 DB_save($_TABLES['speedlimit'], 'ipaddress,date,type',
5302 "'$property',UNIX_TIMESTAMP(),'$type'");
5306 * Clear out expired speed limits, i.e. entries older than 'x' seconds
5308 * @param speedlimit int number of seconds
5309 * @param type string type of speed limit, e.g. 'submit', 'comment'
5312 function COM_clearSpeedlimit($speedlimit = 60, $type = '')
5316 $sql = "DELETE FROM {$_TABLES['speedlimit']} WHERE ";
5317 if (!empty($type)) {
5318 $sql .= "(type = '$type') AND ";
5320 $sql .= "(date < UNIX_TIMESTAMP() - $speedlimit)";
5325 * Reset the speedlimit
5327 * @param string $type type of speed limit to reset, e.g. 'submit'
5328 * @param string $property IP address or other identifiable property
5331 function COM_resetSpeedlimit($type = 'submit', $property = '')
5335 if (empty($property)) {
5336 $property = $_SERVER['REMOTE_ADDR'];
5338 $property = addslashes($property);
5340 DB_delete($_TABLES['speedlimit'], array('type', 'ipaddress'),
5341 array($type, $property));
5345 * Wrapper function for URL class so as to not confuse people as this will
5346 * eventually get used all over the place
5348 * This function returns a crawler friendly URL (if possible)
5350 * @param string $url URL to try to build crawler friendly URL for
5351 * @return string Rewritten URL
5354 function COM_buildURL( $url )
5358 return $_URL->buildURL( $url );
5362 * Wrapper function for URL class so as to not confuse people
5364 * This function sets the name of the arguments found in url
5366 * @param array $names Names of arguments in query string to assign to values
5367 * @return boolean True if successful
5370 function COM_setArgNames( $names )
5374 return $_URL->setArgNames( $names );
5378 * Wrapper function for URL class
5380 * returns value for specified argument
5382 * @param string $name argument to get value for
5383 * @return string Argument value
5386 function COM_getArgument( $name )
5390 return $_URL->getArgument( $name );
5396 * This will take a number of occurrences, and number of seconds for the time span and return
5397 * the smallest #/time interval
5399 * @param int $occurrences how many occurrences during time interval
5400 * @param int $timespan time interval in seconds
5401 * @return int Seconds per interval
5404 function COM_getRate( $occurrences, $timespan )
5406 // want to define some common time words (yes, dirk, i need to put this in LANG)
5407 // time words and their value in seconds
5408 // week is 7 * day, month is 30 * day, year is 365.25 * day
5410 $common_time = array(
5420 if( $occurrences != 0 )
5422 $rate = ( int )( $timespan / $occurrences );
5423 $adjustedRate = $occurrences + 1;
5424 $time_unit = 'second';
5428 foreach( $common_time as $unit=>$seconds )
5430 if( $rate > $seconds )
5432 $foo = ( int )(( $rate / $seconds ) + .5 );
5434 if(( $foo < $occurrences ) && ( $foo > 0 ))
5436 $adjustedRate = $foo;
5442 $singular = '1 shout every ' . $adjustedRate . ' ' . $time_unit;
5444 if( $adjustedRate > 1 )
5451 $singular = 'No events';
5458 * Return SQL expression to check for permissions.
5460 * Creates part of an SQL expression that can be used to request items with the
5461 * standard set of Geeklog permissions.
5463 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5464 * @param int $u_id user id or 0 = current user
5465 * @param int $access access to check for (2=read, 3=r&write)
5466 * @param string $table table name if ambiguous (e.g. in JOINs)
5467 * @return string SQL expression string (may be empty)
5470 function COM_getPermSQL( $type = 'WHERE', $u_id = 0, $access = 2, $table = '' )
5472 global $_USER, $_GROUPS;
5474 if( !empty( $table ))
5480 if( COM_isAnonUser() )
5486 $uid = $_USER['uid'];
5494 $UserGroups = array();
5495 if(( empty( $_USER['uid'] ) && ( $uid == 1 )) || ( $uid == $_USER['uid'] ))
5497 if( empty( $_GROUPS ))
5499 $_GROUPS = SEC_getUserGroups( $uid );
5501 $UserGroups = $_GROUPS;
5505 $UserGroups = SEC_getUserGroups( $uid );
5508 if( empty( $UserGroups ))
5510 // this shouldn't really happen, but if it does, handle user
5511 // like an anonymous user
5515 if( SEC_inGroup( 'Root', $uid ))
5520 $sql = ' ' . $type . ' (';
5524 $sql .= "(({$table}owner_id = '{$uid}') AND ({$table}perm_owner >= $access)) OR ";
5526 $sql .= "(({$table}group_id IN (" . implode( ',', $UserGroups )
5527 . ")) AND ({$table}perm_group >= $access)) OR ";
5528 $sql .= "({$table}perm_members >= $access)";
5532 $sql .= "{$table}perm_anon >= $access";
5541 * Return SQL expression to check for allowed topics.
5543 * Creates part of an SQL expression that can be used to only request stories
5544 * from topics to which the user has access to.
5546 * Note that this function does an SQL request, so you should cache
5547 * the resulting SQL expression if you need it more than once.
5549 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5550 * @param int $u_id user id or 0 = current user
5551 * @param string $table table name if ambiguous (e.g. in JOINs)
5552 * @return string SQL expression string (may be empty)
5555 function COM_getTopicSQL( $type = 'WHERE', $u_id = 0, $table = '' )
5557 global $_TABLES, $_USER, $_GROUPS;
5559 $topicsql = ' ' . $type . ' ';
5561 if( !empty( $table ))
5566 $UserGroups = array();
5567 if(( $u_id <= 0 ) || ( isset( $_USER['uid'] ) && $u_id == $_USER['uid'] ))
5569 if( !COM_isAnonUser() )
5571 $uid = $_USER['uid'];
5577 $UserGroups = $_GROUPS;
5582 $UserGroups = SEC_getUserGroups( $uid );
5585 if( empty( $UserGroups ))
5587 // this shouldn't really happen, but if it does, handle user
5588 // like an anonymous user
5592 if( SEC_inGroup( 'Root', $uid ))
5597 $result = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
5598 . COM_getPermSQL( 'WHERE', $uid ));
5600 while( $T = DB_fetchArray( $result ))
5602 $tids[] = $T['tid'];
5605 if( count( $tids ) > 0 )
5607 $topicsql .= "({$table}tid IN ('" . implode( "','", $tids ) . "'))";
5618 * Strip slashes from a string only when magic_quotes_gpc = on.
5620 * @param string $text The text
5621 * @return string The text, possibly without slashes.
5623 function COM_stripslashes( $text )
5625 if( get_magic_quotes_gpc() == 1 )
5627 return( stripslashes( $text ));
5634 * Filter parameters passed per GET (URL) or POST.
5636 * @param string $parameter the parameter to test
5637 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5638 * @return string the filtered parameter (may now be empty or 0)
5639 * @see COM_applyBasicFilter
5642 function COM_applyFilter( $parameter, $isnumeric = false )
5644 $p = COM_stripslashes($parameter);
5646 return COM_applyBasicFilter($p, $isnumeric);
5652 * NOTE: Use this function instead of COM_applyFilter for parameters
5653 * _not_ coming in through a GET or POST request.
5655 * @param string $parameter the parameter to test
5656 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5657 * @return string the filtered parameter (may now be empty or 0)
5658 * @see COM_applyFilter
5661 function COM_applyBasicFilter( $parameter, $isnumeric = false )
5663 $log_manipulation = false; // set to true to log when the filter applied
5665 $p = strip_tags( $parameter );
5666 $p = COM_killJS( $p ); // doesn't help a lot right now, but still ...
5670 // Note: PHP's is_numeric() accepts values like 4e4 as numeric
5671 if( !is_numeric( $p ) || ( preg_match( '/^-?\d+$/', $p ) == 0 ))
5678 $p = preg_replace( '/\/\*.*/', '', $p );
5679 $pa = explode( "'", $p );
5680 $pa = explode( '"', $pa[0] );
5681 $pa = explode( '`', $pa[0] );
5682 $pa = explode( ';', $pa[0] );
5683 $pa = explode( ',', $pa[0] );
5684 $pa = explode( '\\', $pa[0] );
5688 if( $log_manipulation )
5690 if( strcmp( $p, $parameter ) != 0 )
5692 COM_errorLog( "Filter applied: >> $parameter << filtered to $p [IP {$_SERVER['REMOTE_ADDR']}]", 1);
5702 * @param string $url URL to sanitized
5703 * @param array $allowed_protocols array of allowed protocols
5704 * @param string $default_protocol replacement protocol (default: http)
5705 * @return string sanitized URL
5708 function COM_sanitizeUrl( $url, $allowed_protocols = '', $default_protocol = '' )
5712 if( empty( $allowed_protocols ))
5714 $allowed_protocols = $_CONF['allowed_protocols'];
5716 else if( !is_array( $allowed_protocols ))
5718 $allowed_protocols = array( $allowed_protocols );
5721 if( empty( $default_protocol ))
5723 $default_protocol = 'http:';
5725 else if( substr( $default_protocol, -1 ) != ':' )
5727 $default_protocol .= ':';
5730 $url = strip_tags( $url );
5733 $pos = MBYTE_strpos( $url, ':' );
5734 if( $pos === false )
5736 $url = $default_protocol . '//' . $url;
5740 $protocol = MBYTE_substr( $url, 0, $pos + 1 );
5742 foreach( $allowed_protocols as $allowed )
5744 if( substr( $allowed, -1 ) != ':' )
5748 if( $protocol == $allowed )
5756 $url = $default_protocol . MBYTE_substr( $url, $pos + 1 );
5765 * Ensure an ID contains only alphanumeric characters, dots, dashes, or underscores
5767 * @param string $id the ID to sanitize
5768 * @param boolean $new_id true = create a new ID in case we end up with an empty string
5769 * @return string the sanitized ID
5771 function COM_sanitizeID( $id, $new_id = true )
5773 $id = str_replace( ' ', '', $id );
5774 $id = str_replace( array( '/', '\\', ':', '+' ), '-', $id );
5775 $id = preg_replace( '/[^a-zA-Z0-9\-_\.]/', '', $id );
5776 if( empty( $id ) && $new_id )
5778 $id = COM_makesid();
5785 * Sanitize a filename.
5787 * NOTE: This function is pretty strict in what it allows. Meant to be used
5788 * for files to be included where part of the filename is dynamic.
5790 * @param string $filename the filename to clean up
5791 * @param boolean $allow_dots whether to allow dots in the filename or not
5792 * @return string sanitized filename
5795 function COM_sanitizeFilename($filename, $allow_dots = false)
5798 $filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $filename);
5799 $filename = str_replace('..', '', $filename);
5801 $filename = preg_replace('/[^a-zA-Z0-9\-_]/', '', $filename);
5808 * Detect links in a plain-ascii text and turn them into clickable links.
5809 * Will detect links starting with "http:", "https:", "ftp:", and "www.".
5811 * @param string $text the (plain-ascii) text string
5812 * @return string the same string, with links enclosed in <a>...</a> tags
5815 function COM_makeClickableLinks( $text )
5819 if (! $_CONF['clickable_links']) {
5823 // These regular expressions will work for this purpuse, but
5824 // they should NOT be used for validating links.
5826 // matches anything starting with http:// or https:// or ftp:// or ftps://
5827 $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:(?:ht|f)tps?:\/{2})(?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&)?)+)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
5828 $replace[] = "COM_makeClickableLinksCallback('', '\\1')";
5830 // matches anything containing a top level domain: xxx.com or xxx.yyy.net/stuff.php or xxx.yyy.zz
5831 // list taken from: http://en.wikipedia.org/wiki/List_of_Internet_TLDs
5832 $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';
5833 $replace[] = "COM_makeClickableLinksCallback('http://', '\\1')";
5835 $text = preg_replace( $regex, $replace, $text );
5841 * Callback function to help format links in COM_makeClickableLinks
5843 * @param string $http set to 'http://' when not already in the url
5844 * @param string $link the url
5845 * @return string link enclosed in <a>...</a> tags
5848 function COM_makeClickableLinksCallback( $http, $link )
5850 $text = COM_truncate( $link, 50, '...', '10' );
5852 return "<a href=\"$http$link\">$text</a>";
5856 * Undo the conversion of URLs to clickable links (in plain text posts),
5857 * e.g. so that we can present the user with the post as they entered them.
5859 * @param string $text story text
5860 * @return string story text without links
5863 function COM_undoClickableLinks( $text )
5865 $text = preg_replace( '/<a href="([^"]*)">([^<]*)<\/a>/', '\1', $text );
5871 * Highlight the words from a search query in a given text string.
5873 * @param string $text the text
5874 * @param string $query the search query
5875 * @param string $class html class to use to highlight
5876 * @return string the text with highlighted search words
5879 function COM_highlightQuery( $text, $query, $class = 'highlight' )
5881 // escape PCRE special characters
5882 $query = preg_quote($query, '/');
5884 $mywords = explode(' ', $query);
5885 foreach ($mywords as $searchword)
5887 if (!empty($searchword))
5889 $before = "/(?!(?:[^<]+>|[^>]+<\/a>))\b";
5891 if ($searchword <> utf8_encode($searchword)) {
5892 if (@preg_match('/^\pL$/u', urldecode('%C3%B1'))) { // Unicode property support
5893 $before = "/(?<!\p{L})";
5894 $after = "(?!\p{L})/u";
5900 $text = preg_replace($before . $searchword . $after, "<span class=\"$class\">\\0</span>", '<!-- x -->' . $text . '<!-- x -->' );
5907 * Determines the difference between two dates.
5909 * This will takes either unixtimestamps or English dates as input and will
5910 * automatically do the date diff on the more recent of the two dates (e.g. the
5911 * order of the two dates given doesn't matter).
5913 * @author Tony Bibbs, tony DOT bibbs AT iowa DOT gov
5915 * @param string $interval Can be:
5922 * @param string|int $date1 English date (e.g. 10 Dec 2004) or unixtimestamp
5923 * @param string|int $date2 English date (e.g. 10 Dec 2004) or unixtimestamp
5924 * @return int Difference of the two dates in the unit of time indicated by the interval
5927 function COM_dateDiff( $interval, $date1, $date2 )
5929 // Convert dates to timestamps, if needed.
5930 if( !is_numeric( $date1 ))
5932 $date1 = strtotime( $date1 );
5935 if( !is_numeric( $date2 ))
5937 $date2 = strtotime( $date2 );
5940 // Function roughly equivalent to the ASP "DateDiff" function
5941 if( $date2 > $date1 )
5943 $seconds = $date2 - $date1;
5947 $seconds = $date1 - $date2;
5953 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5954 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5955 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5956 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5957 $diff = $year2 - $year1;
5958 if($month1 > $month2) {
5960 } elseif($month1 == $month2) {
5963 } elseif($day1 == $day2) {
5964 if($time1 > $time2) {
5971 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5972 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5973 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5974 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5975 $diff = ($year2 * 12 + $month2) - ($year1 * 12 + $month1);
5978 } elseif($day1 == $day2) {
5979 if($time1 > $time2) {
5985 // Only simple seconds calculation needed from here on
5986 $diff = floor($seconds / 604800);
5989 $diff = floor($seconds / 86400);
5992 $diff = floor($seconds / 3600);
5995 $diff = floor($seconds / 60);
6006 * Try to figure out our current URL, including all parameters.
6008 * This is an ugly hack since there's no single variable that returns what
6009 * we want and the variables used here may not be available on all servers
6012 * Seems to work on Apache (1.3.x and 2.x), IIS, and Zeus ...
6014 * @return string complete URL, e.g. 'http://www.example.com/blah.php?foo=bar'
6017 function COM_getCurrentURL()
6023 if( empty( $_SERVER['SCRIPT_URI'] ))
6025 if( !empty( $_SERVER['DOCUMENT_URI'] ))
6027 $thisUrl = $_SERVER['DOCUMENT_URI'];
6032 $thisUrl = $_SERVER['SCRIPT_URI'];
6034 if( !empty( $thisUrl ) && !empty( $_SERVER['QUERY_STRING'] ))
6036 $thisUrl .= '?' . $_SERVER['QUERY_STRING'];
6038 if( empty( $thisUrl ))
6040 $requestUri = $_SERVER['REQUEST_URI'];
6041 if( empty( $_SERVER['REQUEST_URI'] ))
6043 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6044 if( empty( $_SERVER['PATH_INFO'] ))
6046 $requestUri = $_SERVER['SCRIPT_NAME'];
6050 $requestUri = $_SERVER['PATH_INFO'];
6052 if( !empty( $_SERVER['QUERY_STRING'] ))
6054 $requestUri .= '?' . $_SERVER['QUERY_STRING'];
6058 $firstslash = strpos( $_CONF['site_url'], '/' );
6059 if( $firstslash === false )
6061 // special case - assume it's okay
6062 $thisUrl = $_CONF['site_url'] . $requestUri;
6064 else if( $firstslash + 1 == strrpos( $_CONF['site_url'], '/' ))
6066 // site is in the document root
6067 $thisUrl = $_CONF['site_url'] . $requestUri;
6071 // extract server name first
6072 $pos = strpos( $_CONF['site_url'], '/', $firstslash + 2 );
6073 $thisUrl = substr( $_CONF['site_url'], 0, $pos ) . $requestUri;
6081 * Check if we're on Geeklog's index page.
6083 * See if we're on the main index page (first page, no topics selected).
6085 * @return boolean true = we're on the frontpage, false = we're not
6088 function COM_onFrontpage()
6090 global $_CONF, $topic, $page, $newstories;
6092 // Note: We can't use $PHP_SELF here since the site may not be in the
6094 $onFrontpage = false;
6096 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6097 if( empty( $_SERVER['PATH_INFO'] ))
6099 $scriptName = $_SERVER['SCRIPT_NAME'];
6103 $scriptName = $_SERVER['PATH_INFO'];
6106 preg_match( '/\/\/[^\/]*(.*)/', $_CONF['site_url'], $pathonly );
6107 if(( $scriptName == $pathonly[1] . '/index.php' ) &&
6108 empty( $topic ) && ( $page == 1 ) && !$newstories )
6110 $onFrontpage = true;
6113 return $onFrontpage;
6117 * Check if we're on Geeklog's index page [deprecated]
6119 * Note that this function returns FALSE when we're on the index page. Due to
6120 * the inverted return values, it has been deprecated and is only provided for
6121 * backward compatibility - use COM_onFrontpage() instead.
6123 * @deprecated since Geeklog 1.4.1
6124 * @see COM_onFrontpage
6127 function COM_isFrontpage()
6129 return !COM_onFrontpage();
6133 * Converts a number for output into a formatted number with thousands-
6134 * separator, comma-separator and fixed decimals if necessary
6136 * @param float $number Number that will be formatted
6137 * @return string formatted number
6140 function COM_numberFormat( $number )
6144 if( $number - floor( $number ) > 0 ) // number has decimals
6146 $dc = $_CONF['decimal_count'];
6152 $ts = $_CONF['thousand_separator'];
6153 $ds = $_CONF['decimal_separator'];
6155 return number_format( $number, $dc, $ds, $ts );
6159 * Convert a text based date YYYY-MM-DD to a unix timestamp integer value
6161 * @param string $date Date in the format YYYY-MM-DD
6162 * @param string $time Option time in the format HH:MM::SS
6163 * @return int UNIX Timestamp
6165 function COM_convertDate2Timestamp( $date, $time = '' )
6170 // Breakup the string using either a space, fwd slash, dash, bkwd slash or
6171 // colon as a delimiter
6172 $atok = strtok( $date, ' /-\\:' );
6173 while( $atok !== FALSE )
6176 $atok = strtok( ' /-\\:' ); // get the next token
6179 for( $i = 0; $i < 3; $i++ )
6181 if( !isset( $atoks[$i] ) || !is_numeric( $atoks[$i] ))
6189 $timestamp = mktime( 0, 0, 0, $atoks[1], $atoks[2], $atoks[0] );
6193 $btok = strtok( $time, ' /-\\:' );
6194 while( $btok !== FALSE )
6197 $btok = strtok( ' /-\\:' );
6200 for( $i = 0; $i < 3; $i++ )
6202 if( !isset( $btoks[$i] ) || !is_numeric( $btoks[$i] ))
6208 $timestamp = mktime( $btoks[0], $btoks[1], $btoks[2],
6209 $atoks[1], $atoks[2], $atoks[0] );
6216 * Get the HTML for an image with height & width
6218 * @param string $file full path to the file
6219 * @return string html that will be included in the img-tag
6221 function COM_getImgSizeAttributes( $file )
6223 $sizeattributes = '';
6225 if( file_exists( $file ))
6227 $dimensions = getimagesize( $file );
6228 if( !empty( $dimensions[0] ) AND !empty( $dimensions[1] ))
6230 $sizeattributes = 'width="' . $dimensions[0]
6231 . '" height="' . $dimensions[1] . '" ';
6235 return $sizeattributes;
6239 * Display a message and abort
6241 * NOTE: Displays the message and aborts the script.
6243 * @param int $msg message number
6244 * @param string $plugin plugin name, if applicable
6245 * @param int $http_status HTTP status code to send with the message
6246 * @param string $http_text Textual version of the HTTP status code
6249 function COM_displayMessageAndAbort( $msg, $plugin = '', $http_status = 200, $http_text = 'OK')
6251 $display = COM_siteHeader( 'menu' )
6252 . COM_showMessage( $msg, $plugin )
6253 . COM_siteFooter( true );
6255 if( $http_status != 200 )
6257 header( "HTTP/1.1 $http_status $http_text" );
6258 header( "Status: $http_status $http_text" );
6265 * Return full URL of a topic icon
6267 * @param string $imageurl (relative) topic icon URL
6268 * @return string Full URL
6271 function COM_getTopicImageUrl( $imageurl )
6273 global $_CONF, $_THEME_URL;
6277 if( !empty( $imageurl ))
6279 if( isset( $_THEME_URL ))
6281 $iconurl = $_THEME_URL . $imageurl;
6285 $stdImageLoc = true;
6286 if( !strstr( $_CONF['path_images'], $_CONF['path_html'] ))
6288 $stdImageLoc = false;
6293 $iconurl = $_CONF['site_url'] . $imageurl;
6297 $t = explode( '/', $imageurl );
6298 $topicicon = $t[count( $t ) - 1];
6299 $iconurl = $_CONF['site_url']
6300 . '/getimage.php?mode=topics&image=' . $topicicon;
6309 * Create an HTML link
6311 * @param string $content the object to be linked (text, image etc)
6312 * @param string $url the URL the link will point to
6313 * @param array $attr an array of optional attributes for the link
6314 * for example array('title' => 'whatever');
6315 * @return string the HTML link
6317 function COM_createLink($content, $url, $attr = array())
6321 $attr_str = 'href="' . $url . '"';
6322 foreach ($attr as $key => $value) {
6323 $attr_str .= " $key=\"$value\"";
6325 $retval .= "<a $attr_str>$content</a>";
6331 * Create an HTML img
6333 * @param string $url the URL of the image, either starting with
6334 * http://... or $_CONF['layout_url'] is prepended
6335 * @param string $alt the 'alt'-tag of the image
6336 * @param array $attr an array of optional attributes for the link
6337 * for example array('title' => 'whatever');
6338 * @return string the HTML img
6340 function COM_createImage($url, $alt = "", $attr = array())
6346 if (preg_match("/^(https?):/", $url) !== 1) {
6347 $url = $_CONF['layout_url'] . $url;
6349 $attr_str = 'src="' . $url . '"';
6351 foreach ($attr as $key => $value) {
6352 $attr_str .= " $key=\"$value\"";
6355 $retval = "<img $attr_str alt=\"$alt\"" . XHTML . ">";
6361 * Try to determine the user's preferred language by looking at the
6362 * "Accept-Language" header sent by their browser (assuming they bothered
6363 * to select a preferred language there).
6365 * Sample header: Accept-Language: en-us,en;q=0.7,de-de;q=0.3
6367 * @return string name of the language file to use or an empty string
6368 * @todo Bugs: Does not take the quantity ('q') parameter into account,
6369 * but only looks at the order of language codes.
6372 function COM_getLanguageFromBrowser()
6378 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
6379 $accept = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
6380 foreach ($accept as $l) {
6381 $l = explode(';', trim($l));
6383 if (array_key_exists($l, $_CONF['language_files'])) {
6384 $retval = $_CONF['language_files'][$l];
6387 $l = explode('-', $l);
6389 if (array_key_exists($l, $_CONF['language_files'])) {
6390 $retval = $_CONF['language_files'][$l];
6401 * Determine current language
6403 * @return string name of the language file (minus the '.php' extension)
6406 function COM_getLanguage()
6408 global $_CONF, $_USER;
6412 if (!empty($_USER['language'])) {
6413 $langfile = $_USER['language'];
6414 } elseif (!empty($_COOKIE[$_CONF['cookie_language']])) {
6415 $langfile = $_COOKIE[$_CONF['cookie_language']];
6416 } elseif (isset($_CONF['languages'])) {
6417 $langfile = COM_getLanguageFromBrowser();
6420 $langfile = COM_sanitizeFilename($langfile);
6421 if (!empty($langfile)) {
6422 if (is_file($_CONF['path_language'] . $langfile . '.php')) {
6427 // if all else fails, return the default language
6428 return $_CONF['language'];
6432 * Determine the ID to use for the current language
6434 * The $_CONF['language_files'] array maps language IDs to language file names.
6435 * This function returns the language ID for a certain language file, to be
6436 * used in language-dependent URLs.
6438 * @param string $language current language file name (optional)
6439 * @return string language ID, e.g 'en'; empty string on error
6442 function COM_getLanguageId($language = '')
6446 if (empty($language)) {
6447 $language = COM_getLanguage();
6451 if (isset($_CONF['language_files'])) {
6452 $lang_id = array_search($language, $_CONF['language_files']);
6454 if ($lang_id === false) {
6455 // that looks like a misconfigured $_CONF['language_files'] array
6456 COM_errorLog('Language "' . $language . '" not found in $_CONF[\'language_files\'] array!');
6458 $lang_id = ''; // not much we can do here ...
6466 * Return SQL expression to request language-specific content
6468 * Creates part of an SQL expression that can be used to request items in the
6469 * current language only.
6471 * @param string $field name of the "id" field, e.g. 'sid' for stories
6472 * @param string $type part of the SQL expression, e.g. 'WHERE', 'AND'
6473 * @param string $table table name if ambiguous, e.g. in JOINs
6474 * @return string SQL expression string (may be empty)
6477 function COM_getLangSQL( $field, $type = 'WHERE', $table = '' )
6483 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6485 if( !empty( $table ))
6490 $lang_id = COM_getLanguageId();
6492 if( !empty( $lang_id ))
6494 $sql = ' ' . $type . " ({$table}$field LIKE '%\\_$lang_id')";
6502 * Provide a block to switch languages
6504 * Provides a drop-down menu (or simple link, if you only have two languages)
6505 * to switch languages. This can be used as a PHP block or called from within
6506 * your theme's header.thtml:
6508 * <?php print phpblock_switch_language(); ?>
6511 * @return string HTML for drop-down or link to switch languages
6514 function phpblock_switch_language()
6520 if( empty( $_CONF['languages'] ) || empty( $_CONF['language_files'] ) ||
6521 ( count( $_CONF['languages'] ) != count( $_CONF['language_files'] )))
6526 $lang = COM_getLanguage();
6527 $langId = COM_getLanguageId( $lang );
6529 if( count( $_CONF['languages'] ) == 2 )
6531 foreach( $_CONF['languages'] as $key => $value )
6533 if( $key != $langId )
6541 $switchUrl = COM_buildUrl( $_CONF['site_url'] . '/switchlang.php?lang='
6543 $retval .= COM_createLink($newLang, $switchUrl);
6547 $retval .= '<form name="change" action="'. $_CONF['site_url']
6548 . '/switchlang.php" method="get">' . LB;
6549 $retval .= '<input type="hidden" name="oldlang" value="' . $langId
6550 . '"' . XHTML . '>' . LB;
6552 $retval .= '<select onchange="change.submit()" name="lang">';
6553 foreach( $_CONF['languages'] as $key => $value )
6555 if( $lang == $_CONF['language_files'][$key] )
6557 $selected = ' selected="selected"';
6563 $retval .= '<option value="' . $key . '"' . $selected . '>'
6564 . $value . '</option>' . LB;
6566 $retval .= '</select>' . LB;
6567 $retval .= '</form>' . LB;
6574 * Switch locale settings
6576 * When multi-language support is enabled, allow overwriting the default locale
6577 * settings with language-specific settings (date format, etc.). So in addition
6578 * to $_CONF['date'] you can have a $_CONF['date_en'], $_CONF['date_de'], etc.
6581 function COM_switchLocaleSettings()
6585 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6587 $overridables = array
6590 'date', 'daytime', 'shortdate', 'dateonly', 'timeonly',
6591 'week_start', 'hour_mode',
6592 'thousand_separator', 'decimal_separator'
6595 $langId = COM_getLanguageId();
6596 foreach( $overridables as $option )
6598 if( isset( $_CONF[$option . '_' . $langId] ))
6600 $_CONF[$option] = $_CONF[$option . '_' . $langId];
6607 * Get the name of the current language, minus the character set
6609 * Strips the character set from $_CONF['language'].
6611 * @return string language name
6614 function COM_getLanguageName()
6620 $charset = '_' . strtolower(COM_getCharset());
6621 if (substr($_CONF['language'], -strlen($charset)) == $charset) {
6622 $retval = substr($_CONF['language'], 0, -strlen($charset));
6624 $retval = $_CONF['language'];
6633 * Truncates a string to a max. length and optionally adds a filler string,
6634 * e.g. '...', to indicate the truncation.
6635 * This function is multi-byte string aware, based on a patch by Yusuke Sakata.
6637 * NOTE: The truncated string may be shorter but will never be longer than
6638 * $maxlen characters, i.e. the $filler string is taken into account.
6640 * @param string $text the text string to truncate
6641 * @param int $maxlen max. number of characters in the truncated string
6642 * @param string $filler optional filler string, e.g. '...'
6643 * @param int $endchars number of characters to show after the filler
6644 * @return string truncated string
6647 function COM_truncate( $text, $maxlen, $filler = '', $endchars = 0 )
6649 $newlen = $maxlen - MBYTE_strlen( $filler );
6650 $len = MBYTE_strlen( $text );
6651 if( $len > $maxlen )
6653 $text = MBYTE_substr( $text, 0, $newlen - $endchars ) . $filler . MBYTE_substr( $text, $len - $endchars, $endchars );
6660 * Get the current character set
6662 * Uses (if available, and in this order)
6663 * - $LANG_CHARSET (from the current language file)
6664 * - $_CONF['default_charset'] (from siteconfig.php)
6665 * - 'iso-8859-1' (hard-coded fallback)
6667 * @return string character set, e.g. 'utf-8'
6670 function COM_getCharset()
6672 global $_CONF, $LANG_CHARSET;
6674 if( empty( $LANG_CHARSET )) {
6675 $charset = $_CONF['default_charset'];
6676 if( empty( $charset )) {
6677 $charset = 'iso-8859-1';
6680 $charset = $LANG_CHARSET;
6689 * This function will handle all PHP errors thrown at it, without exposing
6690 * paths, and hopefully, providing much more information to Root Users than
6691 * the default white error page.
6693 * This function will call out to CUSTOM_handleError if it exists, but, be
6694 * advised, only override this function with a very, very stable function. I'd
6695 * suggest one that outputs some static, basic HTML.
6697 * The PHP feature that allows us to do so is documented here:
6698 * http://uk2.php.net/manual/en/function.set-error-handler.php
6700 * @param int $errno Error Number.
6701 * @param string $errstr Error Message.
6702 * @param string $errfile The file the error was raised in.
6703 * @param int $errline The line of the file that the error was raised at.
6704 * @param array $errcontext An array that points to the active symbol table at the point the error occurred.
6706 function COM_handleError($errno, $errstr, $errfile='', $errline=0, $errcontext='')
6708 global $_CONF, $_USER;
6710 // Handle @ operator
6711 if (error_reporting() == 0) {
6715 // If in PHP4, then respect error_reporting
6716 if ((PHP_VERSION < 5) && (($errno & error_reporting()) == 0)) {
6721 * If we have a root user, then output detailed error message:
6723 if ((is_array($_USER) && function_exists('SEC_inGroup'))
6724 || (isset($_CONF['rootdebug']) && $_CONF['rootdebug'])) {
6725 if ($_CONF['rootdebug'] || SEC_inGroup('Root')) {
6727 header('HTTP/1.1 500 Internal Server Error');
6728 header('Status: 500 Internal Server Error');
6730 $title = 'An Error Occurred';
6731 if (!empty($_CONF['site_name'])) {
6732 $title = $_CONF['site_name'] . ' - ' . $title;
6734 echo("<html><head><title>$title</title></head>\n<body>\n");
6736 echo('<h1>An error has occurred:</h1>');
6737 if ($_CONF['rootdebug']) {
6738 echo('<h2 style="color: red">This is being displayed as "Root Debugging" is enabled
6739 in your Geeklog configuration.</h2><p>If this is a production
6740 website you <strong><em>must disable</em></strong> this
6741 option once you have resolved any issues you are
6742 investigating.</p>');
6744 echo('<p>(This text is only displayed to users in the group \'Root\')</p>');
6746 echo("<p>$errno - $errstr @ $errfile line $errline</p>");
6748 if (!function_exists('SEC_inGroup') || !SEC_inGroup('Root')) {
6749 if ('force' != ''.$_CONF['rootdebug']) {
6750 $errcontext = COM_rootDebugClean($errcontext);
6752 echo('<h2 style="color: red">Root Debug is set to "force", this
6753 means that passwords and session cookies are exposed in this
6759 var_dump($errcontext);
6760 $errcontext = htmlspecialchars(ob_get_contents());
6762 echo("$errcontext</pre></body></html>");
6767 /* If there is a custom error handler, fail over to that, but only
6768 * if the error wasn't in lib-custom.php
6770 if (is_array($_CONF) && !(strstr($errfile, 'lib-custom.php'))) {
6771 if (array_key_exists('path_system', $_CONF)) {
6772 if (file_exists($_CONF['path_system'] . 'lib-custom.php')) {
6773 require_once $_CONF['path_system'] . 'lib-custom.php';
6775 if (function_exists('CUSTOM_handleError')) {
6776 CUSTOM_handleError($errno, $errstr, $errfile, $errline, $errcontext);
6782 // if we do not throw the error back to an admin, still log it in the error.log
6783 COM_errorLog("$errno - $errstr @ $errfile line $errline", 1);
6785 header('HTTP/1.1 500 Internal Server Error');
6786 header('Status: 500 Internal Server Error');
6788 // Does the theme implement an error message html file?
6789 if (!empty($_CONF['path_layout']) &&
6790 file_exists($_CONF['path_layout'] . 'errormessage.html')) {
6791 // NOTE: NOT A TEMPLATE! JUST HTML!
6792 include $_CONF['path_layout'] . 'errormessage.html';
6794 // Otherwise, display simple error message
6795 $title = 'An Error Occurred';
6796 if (!empty($_CONF['site_name'])) {
6797 $title = $_CONF['site_name'] . ' - ' . $title;
6802 <title>{$title}</title>
6805 <div style=\"width: 100%; text-align: center;\">
6806 Unfortunately, an error has occurred rendering this page. Please try
6818 * Recurse through the error context array removing/blanking password/cookie
6819 * values in case the "for development" only switch is left on in a production
6822 * [Not fit for public consumption comments about what users who enable root
6823 * debug in production should have done to them, and why making this change
6824 * defeats the point of the entire root debug feature go here.]
6826 * @param array $array Array of state info (Recursive array).
6827 * @param boolean $blank override (wouldn't that blank out everything?)
6828 * @return array Cleaned array
6830 function COM_rootDebugClean($array, $blank=false)
6832 $blankField = false;
6833 while(list($key, $value) = each($array)) {
6834 $lkey = strtolower($key);
6835 if((strpos($lkey, 'pass') !== false) || (strpos($lkey, 'cookie') !== false)) {
6838 $blankField = $blank;
6840 if(is_array($value)) {
6841 $array[$key] = COM_rootDebugClean($value, $blankField);
6842 } elseif($blankField) {
6843 $array[$key] = '[VALUE REMOVED]';
6850 * Checks to see if a specified user, or the current user if non-specified
6851 * is the anonymous user.
6853 * @param int $uid ID of the user to check, or none for the current user.
6854 * @return boolean true if the user is the anonymous user.
6856 function COM_isAnonUser($uid = '')
6860 /* If no user was specified, fail over to the current user if there is one */
6863 if( isset( $_USER['uid'] ) )
6865 $uid = $_USER['uid'];
6869 if( !empty( $uid ) )
6878 * Create Meta Tags to be used by COM_siteHeader in the headercode variable
6880 * @param string $meta_description the text for the meta description of the page being displayed
6881 * @param string $meta_keywords the text for the meta keywords of the page being displayed
6882 * @return string XHTML formatted text
6885 function COM_createMetaTags($meta_description, $meta_keywords)
6891 If ($_CONF['meta_tags'] > 0) {
6892 if ($meta_description != '') {
6893 $headercode .= LB . '<meta name="description" content="' . $meta_description . '"' . XHTML . '>';
6895 if ($meta_keywords != '') {
6896 $headercode .= LB . '<meta name="keywords" content="' . $meta_keywords . '"' . XHTML . '>';
6906 * Convert wiki-formatted text to (X)HTML
6908 * @param string $wikitext wiki-formatted text
6909 * @return string XHTML formatted text
6912 function COM_renderWikiText($wikitext)
6916 if (!$_CONF['wikitext_editor']) {
6920 require_once 'Text/Wiki.php';
6922 $wiki = new Text_Wiki();
6923 $wiki->setFormatConf('Xhtml', 'translate', HTML_SPECIALCHARS);
6924 $wiki->setRenderConf('Xhtml', 'charset', COM_getCharset());
6925 $wiki->disableRule('wikilink');
6926 $wiki->disableRule('freelink');
6927 $wiki->disableRule('interwiki');
6929 return $wiki->transform($wikitext, 'Xhtml');
6933 * Set the {lang_id} and {lang_attribute} variables for a template
6935 * NOTE: {lang_attribute} is only set in multi-language environments.
6937 * @param ref &$template template to use
6941 function COM_setLangIdAndAttribute(&$template)
6948 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6949 $langId = COM_getLanguageId();
6951 // try to derive the language id from the locale
6952 $l = explode('.', $_CONF['locale']); // get rid of character set
6954 $l = explode('@', $langId); // get rid of '@euro', etc.
6958 if (!empty($langId)) {
6959 $l = explode('-', str_replace('_', '-', $langId));
6960 if ((count($l) == 1) && (strlen($langId) == 2)) {
6961 $langAttr = 'lang="' . $langId . '"';
6962 } else if (count($l) == 2) {
6963 if (($l[0] == 'i') || ($l[0] == 'x')) {
6964 $langId = implode('-', $l);
6965 $langAttr = 'lang="' . $langId . '"';
6966 } else if (strlen($l[0]) == 2) {
6967 $langId = implode('-', $l);
6968 $langAttr = 'lang="' . $langId . '"';
6971 // this isn't a valid lang attribute, so don't set $langAttr
6975 $template->set_var('lang_id', $langId);
6977 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6978 $template->set_var('lang_attribute', ' ' . $langAttr);
6980 $template->set_var('lang_attribute', '');
6985 * Sends compressed output to browser.
6987 * Assumes that $display contains the _entire_ output for a request - no
6988 * echoes are allowed before or after this function.
6989 * Currently only supports gzip compression. Checks if zlib compression is
6990 * enabled in PHP and does uncompressed output if it is.
6992 * @param string $display Content to send to browser
6996 function COM_output($display)
7000 if (empty($display)) {
7004 if ($_CONF['compressed_output']) {
7005 $gzip_accepted = false;
7006 if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
7007 $enc = str_replace(' ', '', $_SERVER['HTTP_ACCEPT_ENCODING']);
7008 $accept = explode(',', strtolower($enc));
7009 $gzip_accepted = in_array('gzip', $accept);
7012 if ($gzip_accepted && function_exists('gzencode')) {
7014 $zlib_comp = ini_get('zlib.output_compression');
7015 if (empty($zlib_comp) || (strcasecmp($zlib_comp, 'off') == 0)) {
7017 header('Content-encoding: gzip');
7018 echo gzencode($display);
7029 * Turn a piece of HTML into continuous(!) plain text
7031 * This function removes HTML tags, line breaks, etc. and returns one long
7032 * line of text. This is useful for word counts (do an explode() on the result)
7033 * and for text excerpts.
7035 * @param string $text original text, including HTML and line breaks
7036 * @return string continuous plain text
7039 function COM_getTextContent($text)
7041 // replace <br> with spaces so that Text<br>Text becomes two words
7042 $text = preg_replace('/\<br(\s*)?\/?\>/i', ' ', $text);
7044 // add extra space between tags, e.g. <p>Text</p><p>Text</p>
7045 $text = str_replace('><', '> <', $text);
7047 // only now remove all HTML tags
7048 $text = strip_tags($text);
7050 // replace all tabs, newlines, and carrriage returns with spaces
7051 $text = str_replace(array("\011", "\012", "\015"), ' ', $text);
7053 // replace entities with plain spaces
7054 $text = str_replace(array('', ' ', ' '), ' ', $text);
7056 // collapse whitespace
7057 $text = preg_replace('/\s\s+/', ' ', $text);
7063 * Now include all plugin functions
7065 foreach ($_PLUGINS as $pi_name) {
7066 require_once $_CONF['path'] . 'plugins/' . $pi_name . '/functions.inc';
7069 // Check and see if any plugins (or custom functions)
7070 // have scheduled tasks to perform
7071 if ($_CONF['cron_schedule_interval'] > 0) {
7072 if ((DB_getItem($_TABLES['vars'], 'value', "name='last_scheduled_run'")
7073 + $_CONF['cron_schedule_interval']) <= time()) {
7074 DB_query("UPDATE {$_TABLES['vars']} SET value=UNIX_TIMESTAMP() WHERE name='last_scheduled_run'");
7075 PLG_runScheduledTask();