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 if( DB_getItem( $_TABLES['stories'], 'COUNT(*)', "featured = 1 AND draft_flag = 0 AND date <= '$curdate'" ) > 1 )
1957 // OK, we have two featured stories, fix that
1959 $sid = DB_getItem( $_TABLES['stories'], 'sid', "featured = 1 AND draft_flag = 0 ORDER BY date LIMIT 1" );
1960 DB_query( "UPDATE {$_TABLES['stories']} SET featured = 0 WHERE sid = '$sid'" );
1966 * Logs messages to error.log or the web page or both
1968 * Prints a well formatted message to either the web page, error log
1971 * @param string $logentry Text to log to error log
1972 * @param int $actionid where 1 = write to log file, 2 = write to screen (default) both
1973 * @see function COM_accessLog
1974 * @return string If $actionid = 2 or '' then HTML formatted string (wrapped in block) else nothing
1978 function COM_errorLog( $logentry, $actionid = '' )
1980 global $_CONF, $LANG01;
1984 if( !empty( $logentry ))
1986 $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
1989 $timestamp = @strftime( '%c' );
1991 if (!isset($_CONF['path_layout']) &&
1992 (($actionid == 2) || empty($actionid))) {
1995 if (!isset($_CONF['path_log']) && ($actionid != 2)) {
2002 $logfile = $_CONF['path_log'] . 'error.log';
2004 if( !$file = fopen( $logfile, 'a' ))
2006 $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2010 fputs( $file, "$timestamp - $logentry \n" );
2015 $retval .= COM_startBlock( $LANG01[55] . ' ' . $timestamp, '',
2016 COM_getBlockTemplate( '_msg_block', 'header' ))
2017 . nl2br( $logentry )
2018 . COM_endBlock( COM_getBlockTemplate( '_msg_block',
2023 $retval = nl2br($logentry);
2027 $logfile = $_CONF['path_log'] . 'error.log';
2029 if( !$file = fopen( $logfile, 'a' ))
2031 $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2035 fputs( $file, "$timestamp - $logentry \n" );
2036 $retval .= COM_startBlock( $LANG01[34] . ' - ' . $timestamp,
2037 '', COM_getBlockTemplate( '_msg_block',
2039 . nl2br( $logentry )
2040 . COM_endBlock( COM_getBlockTemplate( '_msg_block',
2051 * Logs message to access.log
2053 * This will print a message to the Geeklog access log
2055 * @param string $logentry Message to write to access log
2060 function COM_accessLog( $logentry )
2062 global $_CONF, $_USER, $LANG01;
2066 if( !empty( $logentry ))
2068 $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
2071 $timestamp = @strftime( '%c' );
2072 $logfile = $_CONF['path_log'] . 'access.log';
2074 if( !$file = fopen( $logfile, 'a' ))
2076 return $LANG01[33] . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2079 if( isset( $_USER['uid'] ))
2081 $byuser = $_USER['uid'] . '@' . $_SERVER['REMOTE_ADDR'];
2085 $byuser = 'anon@' . $_SERVER['REMOTE_ADDR'];
2088 fputs( $file, "$timestamp ($byuser) - $logentry\n" );
2095 * Shows all available topics
2097 * Show the topics in the system the user has access to and prints them in HTML.
2098 * This function is used to show the topics in the topics block.
2100 * @param string $topic ID of currently selected topic
2101 * @return string HTML formatted topic list
2105 function COM_showTopics( $topic='' )
2107 global $_CONF, $_TABLES, $_USER, $LANG01, $_BLOCK_TEMPLATE, $page;
2109 $langsql = COM_getLangSQL( 'tid' );
2110 if( empty( $langsql ))
2119 $sql = "SELECT tid,topic,imageurl FROM {$_TABLES['topics']}" . $langsql;
2120 if( !COM_isAnonUser() )
2122 $tids = DB_getItem( $_TABLES['userindex'], 'tids',
2123 "uid = '{$_USER['uid']}'" );
2124 if( !empty( $tids ))
2126 $sql .= " $op (tid NOT IN ('" . str_replace( ' ', "','", $tids )
2127 . "'))" . COM_getPermSQL( 'AND' );
2131 $sql .= COM_getPermSQL( $op );
2136 $sql .= COM_getPermSQL( $op );
2138 if( $_CONF['sortmethod'] == 'alpha' )
2140 $sql .= ' ORDER BY topic ASC';
2144 $sql .= ' ORDER BY sortnum';
2146 $result = DB_query( $sql );
2149 $sections = new Template( $_CONF['path_layout'] );
2150 if( isset( $_BLOCK_TEMPLATE['topicoption'] ))
2152 $templates = explode( ',', $_BLOCK_TEMPLATE['topicoption'] );
2153 $sections->set_file( array( 'option' => $templates[0],
2154 'current' => $templates[1] ));
2158 $sections->set_file( array( 'option' => 'topicoption.thtml',
2159 'inactive' => 'topicoption_off.thtml' ));
2162 $sections->set_var( 'xhtml', XHTML );
2163 $sections->set_var( 'site_url', $_CONF['site_url'] );
2164 $sections->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2165 $sections->set_var( 'layout_url', $_CONF['layout_url'] );
2166 $sections->set_var( 'block_name', str_replace( '_', '-', 'section_block' ));
2168 if( $_CONF['hide_home_link'] == 0 )
2170 // Give a link to the homepage here since a lot of people use this for
2171 // navigating the site
2173 if( COM_onFrontpage() )
2175 $sections->set_var( 'option_url', '' );
2176 $sections->set_var( 'option_label', $LANG01[90] );
2177 $sections->set_var( 'option_count', '' );
2178 $sections->set_var( 'topic_image', '' );
2179 $retval .= $sections->parse( 'item', 'inactive' );
2183 $sections->set_var( 'option_url',
2184 $_CONF['site_url'] . '/index.php' );
2185 $sections->set_var( 'option_label', $LANG01[90] );
2186 $sections->set_var( 'option_count', '' );
2187 $sections->set_var( 'topic_image', '' );
2188 $retval .= $sections->parse( 'item', 'option' );
2192 if( $_CONF['showstorycount'] )
2194 $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['stories']} "
2195 . 'WHERE (draft_flag = 0) AND (date <= NOW()) '
2196 . COM_getPermSQL( 'AND' )
2198 $rcount = DB_query( $sql );
2199 while( $C = DB_fetchArray( $rcount ))
2201 $storycount[$C['tid']] = $C['count'];
2205 if( $_CONF['showsubmissioncount'] )
2207 $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['storysubmission']} "
2209 $rcount = DB_query( $sql );
2210 while( $C = DB_fetchArray( $rcount ))
2212 $submissioncount[$C['tid']] = $C['count'];
2216 while( $A = DB_fetchArray( $result ) )
2218 $topicname = stripslashes( $A['topic'] );
2219 $sections->set_var( 'option_url', $_CONF['site_url']
2220 . '/index.php?topic=' . $A['tid'] );
2221 $sections->set_var( 'option_label', $topicname );
2224 if( $_CONF['showstorycount'] || $_CONF['showsubmissioncount'] )
2226 $countstring .= '(';
2228 if( $_CONF['showstorycount'] )
2230 if( empty( $storycount[$A['tid']] ))
2236 $countstring .= COM_numberFormat( $storycount[$A['tid']] );
2240 if( $_CONF['showsubmissioncount'] )
2242 if( $_CONF['showstorycount'] )
2244 $countstring .= '/';
2246 if( empty( $submissioncount[$A['tid']] ))
2252 $countstring .= COM_numberFormat( $submissioncount[$A['tid']] );
2256 $countstring .= ')';
2258 $sections->set_var( 'option_count', $countstring );
2261 if( !empty( $A['imageurl'] ))
2263 $imageurl = COM_getTopicImageUrl( $A['imageurl'] );
2264 $topicimage = '<img src="' . $imageurl . '" alt="' . $topicname
2265 . '" title="' . $topicname . '"' . XHTML . '>';
2267 $sections->set_var( 'topic_image', $topicimage );
2269 if(( $A['tid'] == $topic ) && ( $page == 1 ))
2271 $retval .= $sections->parse( 'item', 'inactive' );
2275 $retval .= $sections->parse( 'item', 'option' );
2283 * Shows the user their menu options
2285 * This shows the average Joe User their menu options. This is the user block on the left side
2287 * @param string $help Help file to show
2288 * @param string $title Title of Menu
2289 * @param string $position Side being shown on 'left', 'right'. Though blank works not likely.
2290 * @see function COM_adminMenu
2293 function COM_userMenu( $help='', $title='', $position='' )
2295 global $_TABLES, $_USER, $_CONF, $LANG01, $LANG04, $_BLOCK_TEMPLATE;
2299 if( !COM_isAnonUser() )
2301 $usermenu = new Template( $_CONF['path_layout'] );
2302 if( isset( $_BLOCK_TEMPLATE['useroption'] ))
2304 $templates = explode( ',', $_BLOCK_TEMPLATE['useroption'] );
2305 $usermenu->set_file( array( 'option' => $templates[0],
2306 'current' => $templates[1] ));
2310 $usermenu->set_file( array( 'option' => 'useroption.thtml',
2311 'current' => 'useroption_off.thtml' ));
2313 $usermenu->set_var( 'xhtml', XHTML );
2314 $usermenu->set_var( 'site_url', $_CONF['site_url'] );
2315 $usermenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2316 $usermenu->set_var( 'layout_url', $_CONF['layout_url'] );
2317 $usermenu->set_var( 'block_name', str_replace( '_', '-', 'user_block' ));
2319 if( empty( $title ))
2321 $title = DB_getItem( $_TABLES['blocks'], 'title',
2322 "name='user_block'" );
2325 // what's our current URL?
2326 $thisUrl = COM_getCurrentURL();
2328 $retval .= COM_startBlock( $title, $help,
2329 COM_getBlockTemplate( 'user_block', 'header', $position ));
2331 // This function will show the user options for all installed plugins
2334 $plugin_options = PLG_getUserOptions();
2335 $nrows = count( $plugin_options );
2337 for( $i = 0; $i < $nrows; $i++ )
2339 $plg = current( $plugin_options );
2340 $usermenu->set_var( 'option_label', $plg->adminlabel );
2342 if( !empty( $plg->numsubmissions ))
2344 $usermenu->set_var( 'option_count', '(' . $plg->numsubmissions . ')' );
2348 $usermenu->set_var( 'option_count', '' );
2351 $usermenu->set_var( 'option_url', $plg->adminurl );
2352 if( $thisUrl == $plg->adminurl )
2354 $retval .= $usermenu->parse( 'item', 'current' );
2358 $retval .= $usermenu->parse( 'item', 'option' );
2360 next( $plugin_options );
2363 $url = $_CONF['site_url'] . '/usersettings.php';
2364 $usermenu->set_var( 'option_label', $LANG01[48] );
2365 $usermenu->set_var( 'option_count', '' );
2366 $usermenu->set_var( 'option_url', $url );
2367 if( $thisUrl == $url )
2369 $retval .= $usermenu->parse( 'item', 'current' );
2373 $retval .= $usermenu->parse( 'item', 'option' );
2376 $url = $_CONF['site_url'] . '/users.php?mode=logout';
2377 $usermenu->set_var( 'option_label', $LANG01[19] );
2378 $usermenu->set_var( 'option_count', '' );
2379 $usermenu->set_var( 'option_url', $url );
2380 $retval .= $usermenu->finish($usermenu->parse('item', 'option'));
2381 $retval .= COM_endBlock(COM_getBlockTemplate('user_block', 'footer', $position));
2385 $retval .= COM_startBlock( $LANG01[47], $help,
2386 COM_getBlockTemplate( 'user_block', 'header', $position ));
2387 $login = new Template( $_CONF['path_layout'] );
2388 $login->set_file( 'form', 'loginform.thtml' );
2389 $login->set_var( 'xhtml', XHTML );
2390 $login->set_var( 'site_url', $_CONF['site_url'] );
2391 $login->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2392 $login->set_var( 'layout_url', $_CONF['layout_url'] );
2393 $login->set_var( 'lang_username', $LANG01[21] );
2394 $login->set_var( 'lang_password', $LANG01[57] );
2395 $login->set_var( 'lang_forgetpassword', $LANG01[119] );
2396 $login->set_var( 'lang_login', $LANG01[58] );
2397 if( $_CONF['disable_new_user_registration'] == 1 )
2399 $login->set_var( 'lang_signup', '' );
2403 $login->set_var( 'lang_signup', $LANG01[59] );
2406 // 3rd party remote authentification.
2407 if ($_CONF['user_login_method']['3rdparty'] && !$_CONF['usersubmission']) {
2408 $modules = SEC_collectRemoteAuthenticationModules();
2409 if (count($modules) == 0) {
2410 $user_templates->set_var('services', '');
2412 if (!$_CONF['user_login_method']['standard'] &&
2413 (count($modules) == 1)) {
2414 $select = '<input type="hidden" name="service" value="'
2415 . $modules[0] . '"' . XHTML . '>' . $modules[0];
2418 $select = '<select name="service" id="service">';
2419 if ($_CONF['user_login_method']['standard']) {
2420 $select .= '<option value="">' . $_CONF['site_name']
2423 foreach ($modules as $service) {
2424 $select .= '<option value="' . $service . '">'
2425 . $service . '</option>';
2427 $select .= '</select>';
2430 $login->set_file('services', 'blockservices.thtml');
2431 $login->set_var('lang_service', $LANG04[121]);
2432 $login->set_var('select_service', $select);
2433 $login->parse('output', 'services');
2434 $login->set_var('services',
2435 $login->finish($login->get_var('output')));
2438 $login->set_var('services', '');
2441 // OpenID remote authentification.
2442 if ($_CONF['user_login_method']['openid'] && ($_CONF['usersubmission'] == 0) && !$_CONF['disable_new_user_registration']) {
2443 $login->set_file('openid_login', 'loginform_openid.thtml');
2444 $login->set_var('lang_openid_login', $LANG01[128]);
2445 $login->set_var('input_field_size', 18);
2446 $login->set_var('app_url', $_CONF['site_url'] . '/users.php');
2447 $login->parse('output', 'openid_login');
2448 $login->set_var('openid_login',
2449 $login->finish($login->get_var('output')));
2451 $login->set_var('openid_login', '');
2454 $retval .= $login->finish($login->parse('output', 'form'));
2455 $retval .= COM_endBlock( COM_getBlockTemplate('user_block', 'footer', $position));
2462 * Prints administration menu
2464 * This will return the administration menu items that the user has
2465 * sufficient rights to -- Admin Block on the left side.
2467 * @param string $help Help file to show
2468 * @param string $title Menu Title
2469 * @param string $position Side being shown on 'left', 'right' or blank.
2470 * @see function COM_userMenu
2473 function COM_adminMenu( $help = '', $title = '', $position = '' )
2475 global $_TABLES, $_USER, $_CONF, $LANG01, $LANG_ADMIN, $_BLOCK_TEMPLATE,
2480 if( empty( $_USER['username'] ))
2485 $plugin_options = PLG_getAdminOptions();
2486 $num_plugins = count( $plugin_options );
2488 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 ))
2490 // what's our current URL?
2491 $thisUrl = COM_getCurrentURL();
2493 $adminmenu = new Template( $_CONF['path_layout'] );
2494 if( isset( $_BLOCK_TEMPLATE['adminoption'] ))
2496 $templates = explode( ',', $_BLOCK_TEMPLATE['adminoption'] );
2497 $adminmenu->set_file( array( 'option' => $templates[0],
2498 'current' => $templates[1] ));
2502 $adminmenu->set_file( array( 'option' => 'adminoption.thtml',
2503 'current' => 'adminoption_off.thtml' ));
2505 $adminmenu->set_var( 'xhtml', XHTML );
2506 $adminmenu->set_var( 'site_url', $_CONF['site_url'] );
2507 $adminmenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2508 $adminmenu->set_var( 'layout_url', $_CONF['layout_url'] );
2509 $adminmenu->set_var( 'block_name', str_replace( '_', '-', 'admin_block' ));
2511 if( empty( $title ))
2513 $title = DB_getItem( $_TABLES['blocks'], 'title',
2514 "name = 'admin_block'" );
2517 $retval .= COM_startBlock( $title, $help,
2518 COM_getBlockTemplate( 'admin_block', 'header', $position ));
2521 if( SEC_isModerator() || SEC_hasRights( 'story.edit' ))
2523 $tresult = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
2524 . COM_getPermSQL() );
2525 $trows = DB_numRows( $tresult );
2529 for( $i = 0; $i < $trows; $i++ )
2531 $T = DB_fetchArray( $tresult );
2532 $tids[] = $T['tid'];
2534 if( count( $tids ) > 0 )
2536 $topicsql = " (tid IN ('" . implode( "','", $tids ) . "'))";
2542 if (SEC_hasRights('story.edit,story.moderate', 'OR') ||
2543 (($_CONF['commentsubmission'] == 1) &&
2544 SEC_hasRights('comment.moderate')) ||
2545 (($_CONF['usersubmission'] == 1) &&
2546 SEC_hasRights('user.edit,user.delete'))) {
2548 if (SEC_hasRights('story.moderate')) {
2549 if (empty($topicsql)) {
2550 $modnum += DB_count($_TABLES['storysubmission']);
2552 $sresult = DB_query("SELECT COUNT(*) AS count FROM {$_TABLES['storysubmission']} WHERE" . $topicsql);
2553 $S = DB_fetchArray($sresult);
2554 $modnum += $S['count'];
2558 if (($_CONF['listdraftstories'] == 1) && SEC_hasRights('story.edit')) {
2559 $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (draft_flag = 1)";
2560 if (!empty($topicsql)) {
2561 $sql .= ' AND' . $topicsql;
2563 $result = DB_query($sql . COM_getPermSQL('AND', 0, 3));
2564 $A = DB_fetchArray($result);
2565 $modnum += $A['count'];
2568 if (($_CONF['commentsubmission'] == 1) && SEC_hasRights('comment.moderate')) {
2569 $modnum += DB_count($_TABLES['commentsubmissions']);
2572 if ($_CONF['usersubmission'] == 1) {
2573 if (SEC_hasRights('user.edit') && SEC_hasRights('user.delete')) {
2574 $modnum += DB_count($_TABLES['users'], 'status', '2');
2579 if (SEC_inGroup('Root')) {
2580 $url = $_CONF['site_admin_url'] . '/configuration.php';
2581 $adminmenu->set_var('option_url', $url);
2582 $adminmenu->set_var('option_label', $LANG01[129]);
2583 $adminmenu->set_var('option_count', count($config->_get_groups()));
2584 $menu_item = $adminmenu->parse('item',
2585 ($thisUrl == $url) ? 'current' :
2587 $link_array[$LANG01[129]] = $menu_item;
2591 // now handle submissions for plugins
2592 $modnum += PLG_getSubmissionCount();
2594 if( SEC_hasRights( 'story.edit' ))
2596 $url = $_CONF['site_admin_url'] . '/story.php';
2597 $adminmenu->set_var( 'option_url', $url );
2598 $adminmenu->set_var( 'option_label', $LANG01[11] );
2599 if( empty( $topicsql ))
2601 $numstories = DB_count( $_TABLES['stories'] );
2605 $nresult = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE" . $topicsql . COM_getPermSql( 'AND' ));
2606 $N = DB_fetchArray( $nresult );
2607 $numstories = $N['count'];
2609 $adminmenu->set_var( 'option_count',
2610 COM_numberFormat( $numstories ));
2611 $menu_item = $adminmenu->parse( 'item',
2612 ( $thisUrl == $url ) ? 'current' : 'option' );
2613 $link_array[$LANG01[11]] = $menu_item;
2616 if( SEC_hasRights( 'block.edit' ))
2618 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['blocks']}" . COM_getPermSql());
2619 list( $count ) = DB_fetchArray( $result );
2621 $url = $_CONF['site_admin_url'] . '/block.php';
2622 $adminmenu->set_var( 'option_url', $url );
2623 $adminmenu->set_var( 'option_label', $LANG01[12] );
2624 $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
2626 $menu_item = $adminmenu->parse( 'item',
2627 ( $thisUrl == $url ) ? 'current' : 'option' );
2628 $link_array[$LANG01[12]] = $menu_item;
2631 if( SEC_hasRights( 'topic.edit' ))
2633 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['topics']}" . COM_getPermSql());
2634 list( $count ) = DB_fetchArray( $result );
2636 $url = $_CONF['site_admin_url'] . '/topic.php';
2637 $adminmenu->set_var( 'option_url', $url );
2638 $adminmenu->set_var( 'option_label', $LANG01[13] );
2639 $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
2641 $menu_item = $adminmenu->parse( 'item',
2642 ( $thisUrl == $url ) ? 'current' : 'option' );
2643 $link_array[$LANG01[13]] = $menu_item;
2646 if( SEC_hasRights( 'user.edit' ))
2648 $url = $_CONF['site_admin_url'] . '/user.php';
2649 $adminmenu->set_var( 'option_url', $url );
2650 $adminmenu->set_var( 'option_label', $LANG01[17] );
2651 $adminmenu->set_var( 'option_count',
2652 COM_numberFormat( DB_count( $_TABLES['users'] ) -1 ));
2654 $menu_item = $adminmenu->parse( 'item',
2655 ( $thisUrl == $url ) ? 'current' : 'option' );
2656 $link_array[$LANG01[17]] = $menu_item;
2659 if( SEC_hasRights( 'group.edit' ))
2661 if (SEC_inGroup('Root')) {
2664 $thisUsersGroups = SEC_getUserGroups ();
2665 $grpFilter = 'WHERE (grp_id IN (' . implode (',', $thisUsersGroups) . '))';
2667 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['groups']} $grpFilter;" );
2668 $A = DB_fetchArray( $result );
2670 $url = $_CONF['site_admin_url'] . '/group.php';
2671 $adminmenu->set_var( 'option_url', $url );
2672 $adminmenu->set_var( 'option_label', $LANG01[96] );
2673 $adminmenu->set_var( 'option_count',
2674 COM_numberFormat( $A['count'] ));
2676 $menu_item = $adminmenu->parse( 'item',
2677 ( $thisUrl == $url ) ? 'current' : 'option' );
2678 $link_array[$LANG01[96]] = $menu_item;
2681 if( SEC_hasRights( 'user.mail' ))
2683 $url = $_CONF['site_admin_url'] . '/mail.php';
2684 $adminmenu->set_var( 'option_url', $url );
2685 $adminmenu->set_var( 'option_label', $LANG01[105] );
2686 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2688 $menu_item = $adminmenu->parse( 'item',
2689 ( $thisUrl == $url ) ? 'current' : 'option' );
2690 $link_array[$LANG01[105]] = $menu_item;
2693 if(( $_CONF['backend'] == 1 ) && SEC_hasRights( 'syndication.edit' ))
2695 $url = $_CONF['site_admin_url'] . '/syndication.php';
2696 $adminmenu->set_var( 'option_url', $url );
2697 $adminmenu->set_var( 'option_label', $LANG01[38] );
2698 $count = COM_numberFormat( DB_count( $_TABLES['syndication'] ));
2699 $adminmenu->set_var( 'option_count', $count );
2701 $menu_item = $adminmenu->parse( 'item',
2702 ( $thisUrl == $url ) ? 'current' : 'option' );
2703 $link_array[$LANG01[38]] = $menu_item;
2706 if(( $_CONF['trackback_enabled'] || $_CONF['pingback_enabled'] ||
2707 $_CONF['ping_enabled'] ) && SEC_hasRights( 'story.ping' ))
2709 $url = $_CONF['site_admin_url'] . '/trackback.php';
2710 $adminmenu->set_var( 'option_url', $url );
2711 $adminmenu->set_var( 'option_label', $LANG01[116] );
2712 if( $_CONF['ping_enabled'] )
2714 $count = COM_numberFormat( DB_count( $_TABLES['pingservice'] ));
2715 $adminmenu->set_var( 'option_count', $count );
2719 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2722 $menu_item = $adminmenu->parse( 'item',
2723 ( $thisUrl == $url ) ? 'current' : 'option' );
2724 $link_array[$LANG01[116]] = $menu_item;
2727 if (SEC_hasRights('plugin.edit')) {
2728 $url = $_CONF['site_admin_url'] . '/plugins.php';
2729 $adminmenu->set_var('option_url', $url);
2730 $adminmenu->set_var('option_label', $LANG01[77]);
2731 $adminmenu->set_var('option_count',
2732 COM_numberFormat(DB_count($_TABLES['plugins'],
2735 $menu_item = $adminmenu->parse('item',
2736 ($thisUrl == $url) ? 'current' : 'option');
2737 $link_array[$LANG01[77]] = $menu_item;
2740 // This will show the admin options for all installed plugins (if any)
2742 for( $i = 0; $i < $num_plugins; $i++ )
2744 $plg = current( $plugin_options );
2746 $adminmenu->set_var( 'option_url', $plg->adminurl );
2747 $adminmenu->set_var( 'option_label', $plg->adminlabel );
2749 if( empty( $plg->numsubmissions ))
2751 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2755 $adminmenu->set_var( 'option_count',
2756 COM_numberFormat( $plg->numsubmissions ));
2759 $menu_item = $adminmenu->parse( 'item',
2760 ( $thisUrl == $plg->adminurl ) ? 'current' : 'option', true );
2761 $link_array[$plg->adminlabel] = $menu_item;
2763 next( $plugin_options );
2766 if(( $_CONF['allow_mysqldump'] == 1 ) AND ( $_DB_dbms == 'mysql' ) AND
2767 SEC_inGroup( 'Root' ))
2769 $url = $_CONF['site_admin_url'] . '/database.php';
2770 $adminmenu->set_var( 'option_url', $url );
2771 $adminmenu->set_var( 'option_label', $LANG01[103] );
2772 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2774 $menu_item = $adminmenu->parse( 'item',
2775 ( $thisUrl == $url ) ? 'current' : 'option' );
2776 $link_array[$LANG01[103]] = $menu_item;
2779 if ($_CONF['link_documentation'] == 1) {
2780 $doclang = COM_getLanguageName();
2781 $docs = 'docs/' . $doclang . '/index.html';
2782 if (file_exists($_CONF['path_html'] . $docs)) {
2783 $adminmenu->set_var('option_url', $_CONF['site_url']
2786 $adminmenu->set_var('option_url', $_CONF['site_url']
2787 . '/docs/english/index.html');
2789 $adminmenu->set_var('option_label', $LANG01[113]);
2790 $adminmenu->set_var('option_count', $LANG_ADMIN['na']);
2791 $menu_item = $adminmenu->parse('item', 'option');
2792 $link_array[$LANG01[113]] = $menu_item;
2795 if( $_CONF['link_versionchecker'] == 1 AND SEC_inGroup( 'Root' ))
2797 $adminmenu->set_var( 'option_url',
2798 'http://www.geeklog.net/versionchecker.php?version=' . VERSION );
2799 $adminmenu->set_var( 'option_label', $LANG01[107] );
2800 $adminmenu->set_var( 'option_count', VERSION );
2802 $menu_item = $adminmenu->parse( 'item', 'option' );
2803 $link_array[$LANG01[107]] = $menu_item;
2806 if( $_CONF['sort_admin'] )
2808 uksort( $link_array, 'strcasecmp' );
2811 $url = $_CONF['site_admin_url'] . '/moderation.php';
2812 $adminmenu->set_var('option_url', $url);
2813 $adminmenu->set_var('option_label', $LANG01[10]);
2814 $adminmenu->set_var('option_count', COM_numberFormat($modnum));
2815 $menu_item = $adminmenu->finish($adminmenu->parse('item',
2816 ($thisUrl == $url) ? 'current' : 'option'));
2817 $link_array = array($menu_item) + $link_array;
2819 foreach( $link_array as $link )
2824 $retval .= COM_endBlock( COM_getBlockTemplate( 'admin_block', 'footer', $position ));
2831 * Redirects user to a given URL
2833 * This function does a redirect using a meta refresh. This is (or at least
2834 * used to be) more compatible than using a HTTP Location: header.
2836 * NOTE: This does not need to be XHTML compliant. It may also be used
2837 * in situations where the XHTML constant is not defined yet ...
2839 * @param string $url URL to send user to
2840 * @return string HTML meta redirect
2843 function COM_refresh($url)
2845 return "<html><head><meta http-equiv=\"refresh\" content=\"0; URL=$url\"></head></html>\n";
2849 * DEPRECIATED -- see CMT_userComments in lib-comment.php
2850 * @deprecated since Geeklog 1.4.0
2851 * @see CMT_userComments
2853 function COM_userComments( $sid, $title, $type='article', $order='', $mode='', $pid = 0, $page = 1, $cid = false, $delete_option = false )
2857 require_once $_CONF['path_system'] . 'lib-comment.php';
2859 return CMT_userComments( $sid, $title, $type, $order, $mode, $pid, $page, $cid, $delete_option );
2863 * This censors inappropriate content
2865 * This will replace 'bad words' with something more appropriate
2867 * @param string $Message String to check
2868 * @see function COM_checkHTML
2869 * @return string Edited $Message
2873 function COM_checkWords( $Message )
2877 $EditedMessage = $Message;
2879 if( $_CONF['censormode'] != 0 )
2881 if( is_array( $_CONF['censorlist'] ))
2883 $Replacement = $_CONF['censorreplace'];
2885 switch( $_CONF['censormode'])
2887 case 1: # Exact match
2888 $RegExPrefix = '(\s*)';
2889 $RegExSuffix = '(\W*)';
2892 case 2: # Word beginning
2893 $RegExPrefix = '(\s*)';
2894 $RegExSuffix = '(\w*)';
2897 case 3: # Word fragment
2898 $RegExPrefix = '(\w*)';
2899 $RegExSuffix = '(\w*)';
2903 foreach ($_CONF['censorlist'] as $c) {
2905 $EditedMessage = MBYTE_eregi_replace($RegExPrefix . $c
2906 . $RegExSuffix, "\\1$Replacement\\2", $EditedMessage);
2912 return $EditedMessage;
2916 * Takes some amount of text and replaces all javascript events on*= with in
2918 * This script takes some amount of text and matches all javascript events, on*= (onBlur= onMouseClick=)
2919 * and replaces them with in*=
2920 * Essentially this will cause onBlur to become inBlur, onFocus to be inFocus
2921 * These are not valid javascript events and the browser will ignore them.
2922 * @param string $Message Text to filter
2923 * @return string $Message with javascript filtered
2924 * @see COM_checkWords
2925 * @see COM_checkHTML
2929 function COM_killJS( $Message )
2931 return( preg_replace( '/(\s)+[oO][nN](\w*) ?=/', '\1in\2=', $Message ));
2935 * Handles the part within a [code] ... [/code] section, i.e. escapes all
2936 * special characters.
2938 * @param string $str the code section to encode
2939 * @return string $str with the special characters encoded
2940 * @see COM_checkHTML
2943 function COM_handleCode( $str )
2945 $search = array( '&', '\\', '<', '>', '[', ']' );
2946 $replace = array( '&', '\', '<', '>', '[', ']' );
2948 $str = str_replace( $search, $replace, $str );
2954 * This function checks html tags.
2956 * Checks to see that the HTML tags are on the approved list and
2957 * removes them if not.
2959 * @param string $str HTML to check
2960 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
2961 * @return string Filtered HTML
2964 function COM_checkHTML( $str, $permissions = 'story.edit' )
2968 // replace any \ with \ (HTML equiv)
2969 $str = str_replace('\\', '\', COM_stripslashes($str) );
2971 // Get rid of any newline characters
2972 $str = preg_replace( "/\n/", '', $str );
2974 // Replace any $ with $ (HTML equiv)
2975 $str = str_replace( '$', '$', $str );
2976 // handle [code] ... [/code]
2979 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[code]' );
2980 if( $start_pos !== false )
2982 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/code]' );
2983 if( $end_pos !== false )
2985 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6,
2986 $end_pos - ( $start_pos + 6 )));
2987 $encoded = '<pre><code>' . $encoded . '</code></pre>';
2988 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
2989 . MBYTE_substr( $str, $end_pos + 7 );
2991 else // missing [/code]
2993 // Treat the rest of the text as code (so as not to lose any
2994 // special characters). However, the calling entity should
2995 // better be checking for missing [/code] before calling this
2997 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6 ));
2998 $encoded = '<pre><code>' . $encoded . '</code></pre>';
2999 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3003 while( $start_pos !== false );
3005 // handle [raw] ... [/raw]
3008 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[raw]' );
3009 if( $start_pos !== false )
3011 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/raw]' );
3012 if( $end_pos !== false )
3014 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5,
3015 $end_pos - ( $start_pos + 5 )));
3016 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3018 $encoded = '[raw2]' . $encoded . '[/raw2]';
3019 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
3020 . MBYTE_substr( $str, $end_pos + 6 );
3022 else // missing [/raw]
3024 // Treat the rest of the text as raw (so as not to lose any
3025 // special characters). However, the calling entity should
3026 // better be checking for missing [/raw] before calling this
3028 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5 ));
3029 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3031 $encoded = '[raw2]' . $encoded . '[/raw2]';
3032 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3036 while( $start_pos !== false );
3038 if( isset( $_CONF['skip_html_filter_for_root'] ) &&
3039 ( $_CONF['skip_html_filter_for_root'] == 1 ) &&
3040 SEC_inGroup( 'Root' ))
3045 // strip_tags() gets confused by HTML comments ...
3046 $str = preg_replace( '/<!--.+?-->/', '', $str );
3048 $filter = new kses4;
3049 if( isset( $_CONF['allowed_protocols'] ) && is_array( $_CONF['allowed_protocols'] ) && ( count( $_CONF['allowed_protocols'] ) > 0 ))
3051 $filter->SetProtocols( $_CONF['allowed_protocols'] );
3055 $filter->SetProtocols( array( 'http:', 'https:', 'ftp:' ));
3058 if( empty( $permissions) || !SEC_hasRights( $permissions ) ||
3059 empty( $_CONF['admin_html'] ))
3061 $html = $_CONF['user_html'];
3065 if ($_CONF['advanced_editor'] && is_array($_CONF['advanced_html'])) {
3066 $html = array_merge_recursive( $_CONF['user_html'],
3067 $_CONF['admin_html'],
3068 $_CONF['advanced_html'] );
3070 $html = array_merge_recursive( $_CONF['user_html'],
3071 $_CONF['admin_html'] );
3075 foreach( $html as $tag => $attr )
3077 $filter->AddHTML( $tag, $attr );
3079 /* Replace [raw][/raw] with <!--raw--><!--/raw-->, note done "late" because
3080 * of the above noted // strip_tags() gets confused by HTML comments ...
3082 $str = $filter->Parse( $str );
3083 $str = str_replace('[raw2]','<!--raw--><span class="raw">', $str);
3084 $str = str_replace('[/raw2]','</span><!--/raw-->', $str);
3090 * undo function for htmlspecialchars()
3092 * This function translates HTML entities created by htmlspecialchars() back
3093 * into their ASCII equivalents. Also handles the entities for $, {, and }.
3095 * @param string $string The string to convert.
3096 * @return string The converted string.
3099 function COM_undoSpecialChars( $string )
3101 $string = str_replace( '$', '$', $string );
3102 $string = str_replace( '{', '{', $string );
3103 $string = str_replace( '}', '}', $string );
3104 $string = str_replace( '>', '>', $string );
3105 $string = str_replace( '<', '<', $string );
3106 $string = str_replace( '"', '"', $string );
3107 $string = str_replace( ' ', ' ', $string );
3108 $string = str_replace( '&', '&', $string );
3114 * Makes an ID based on current date/time
3116 * This function creates a 17 digit sid for stories based on the 14 digit date
3117 * and a 3 digit random number that was seeded with the number of microseconds
3118 * (.000001th of a second) since the last full second.
3119 * NOTE: this is now used for more than just stories!
3121 * @return string $sid Story ID
3124 function COM_makesid()
3126 $sid = date( 'YmdHis' );
3127 $sid .= rand( 0, 999 );
3133 * Checks to see if email address is valid.
3135 * This function checks to see if an email address is in the correct from.
3137 * @param string $email Email address to verify
3138 * @return boolean True if valid otherwise false
3141 function COM_isEmail( $email )
3143 require_once( 'Mail/RFC822.php' );
3145 $rfc822 = new Mail_RFC822;
3147 return( $rfc822->isValidInetAddress( $email ) ? true : false );
3152 * Encode a string such that it can be used in an email header
3154 * @param string $string the text to be encoded
3155 * @return string encoded text
3158 function COM_emailEscape( $string )
3162 if (function_exists('CUSTOM_emailEscape')) {
3163 return CUSTOM_emailEscape($string);
3166 $charset = COM_getCharset();
3167 if(( $charset == 'utf-8' ) && ( $string != utf8_decode( $string )))
3169 if( function_exists( 'iconv_mime_encode' ))
3171 $mime_parameters = array( 'input-charset' => 'utf-8',
3172 'output-charset' => 'utf-8',
3173 // 'Q' encoding is more readable than 'B'
3176 $string = substr( iconv_mime_encode( '', $string,
3177 $mime_parameters ), 2 );
3181 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3184 else if( preg_match( '/[^0-9a-z\-\.,:;\?! ]/i', $string ))
3186 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3193 * Takes a name and an email address and returns a string that vaguely
3194 * resembles an email address specification conforming to RFC(2)822 ...
3196 * @param string $name name, e.g. John Doe
3197 * @param string $address email address only, e.g. john.doe@example.com
3198 * @return string formatted email address
3201 function COM_formatEmailAddress($name, $address)
3203 $name = trim($name);
3204 $address = trim($address);
3206 if (function_exists('CUSTOM_formatEmailAddress')) {
3207 return CUSTOM_formatEmailAddress($name, $address);
3210 $formatted_name = COM_emailEscape($name);
3212 // if the name comes back unchanged, it's not UTF-8, so preg_match is fine
3213 if (($formatted_name == $name) && preg_match('/[^0-9a-z ]/i', $name)) {
3214 $formatted_name = str_replace('"', '\\"', $formatted_name);
3215 $formatted_name = '"' . $formatted_name . '"';
3218 return $formatted_name . ' <' . $address . '>';
3224 * All emails sent by Geeklog are sent through this function.
3226 * NOTE: Please note that using CC: will expose the email addresses of
3227 * all recipients. Use with care.
3229 * @param string $to recipients name and email address
3230 * @param string $subject subject of the email
3231 * @param string $message the text of the email
3232 * @param string $from (optional) sender of the the email
3233 * @param boolean $html (optional) true if to be sent as HTML email
3234 * @param int $priority (optional) add X-Priority header, if > 0
3235 * @param mixed $optional (optional) other headers or CC:
3236 * @return boolean true if successful, otherwise false
3239 function COM_mail($to, $subject, $message, $from = '', $html = false, $priority = 0, $optional = null)
3246 $from = COM_formatEmailAddress($_CONF['site_name'], $_CONF['site_mail']);
3249 $to = substr($to, 0, strcspn($to, "\r\n"));
3250 if (($optional != null) && !is_array($optional)) {
3251 $optional = substr($optional, 0, strcspn($optional, "\r\n"));
3253 $from = substr($from, 0, strcspn($from, "\r\n"));
3254 $subject = substr($subject, 0, strcspn($subject, "\r\n"));
3255 $subject = COM_emailEscape($subject);
3257 if (function_exists('CUSTOM_mail')) {
3258 return CUSTOM_mail($to, $subject, $message, $from, $html, $priority,
3262 include_once 'Mail.php';
3263 include_once 'Mail/RFC822.php';
3265 $method = $_CONF['mail_settings']['backend'];
3267 if (! isset($mailobj)) {
3268 if (($method == 'sendmail') || ($method == 'smtp')) {
3269 $mailobj =& Mail::factory($method, $_CONF['mail_settings']);
3272 $mailobj =& Mail::factory($method);
3276 $charset = COM_getCharset();
3279 $headers['From'] = $from;
3280 if ($method != 'mail') {
3281 $headers['To'] = $to;
3283 if (($optional != null) && !is_array($optional) && !empty($optional)) {
3284 // assume old (optional) CC: header
3285 $headers['Cc'] = $optional;
3287 $headers['Date'] = date('r'); // RFC822 formatted date
3288 if($method == 'smtp') {
3289 list($usec, $sec) = explode(' ', microtime());
3290 $m = substr($usec, 2, 5);
3291 $headers['Message-Id'] = '<' . date('YmdHis') . '.' . $m
3292 . '@' . $_CONF['mail_settings']['host'] . '>';
3295 $headers['Content-Type'] = 'text/html; charset=' . $charset;
3296 $headers['Content-Transfer-Encoding'] = '8bit';
3298 $headers['Content-Type'] = 'text/plain; charset=' . $charset;
3300 $headers['Subject'] = $subject;
3301 if ($priority > 0) {
3302 $headers['X-Priority'] = $priority;
3304 $headers['X-Mailer'] = 'Geeklog ' . VERSION;
3306 if (!empty($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['SERVER_ADDR']) &&
3307 ($_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR'])) {
3308 $url = COM_getCurrentURL();
3309 if (substr($url, 0, strlen($_CONF['site_admin_url']))
3310 != $_CONF['site_admin_url']) {
3311 $headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
3315 // add optional headers last
3316 if (($optional != null) && is_array($optional)) {
3317 foreach ($optional as $h => $v) {
3322 $retval = $mailobj->send($to, $headers, $message);
3323 if ($retval !== true) {
3324 COM_errorLog($retval->toString(), 1);
3327 return($retval === true ? true : false);
3332 * Creates older stuff block
3334 * Creates the olderstuff block for display.
3335 * Actually updates the olderstuff record in the gl_blocks database.
3339 function COM_olderStuff()
3341 global $_TABLES, $_CONF;
3343 $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']}";
3344 $result = DB_query( $sql );
3345 $nrows = DB_numRows( $result );
3349 $dateonly = $_CONF['dateonly'];
3350 if( empty( $dateonly ))
3352 $dateonly = '%d-%b'; // fallback: day - abbrev. month name
3358 for( $i = 0; $i < $nrows; $i++ )
3360 $A = DB_fetchArray( $result );
3362 $daycheck = strftime( '%A', $A['day'] );
3363 if( $day != $daycheck )
3365 if( $day != 'noday' )
3367 $daylist = COM_makeList($oldnews, 'list-older-stories');
3368 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3369 $string .= $daylist . '<br' . XHTML . '>';
3372 $day2 = strftime( $dateonly, $A['day'] );
3373 $string .= '<h3>' . $daycheck . ' <small>' . $day2
3374 . '</small></h3>' . LB;
3379 $oldnews_url = COM_buildUrl( $_CONF['site_url'] . '/article.php?story='
3381 $oldnews[] = COM_createLink($A['title'], $oldnews_url)
3382 .' (' . COM_numberFormat( $A['comments'] ) . ')';
3385 if( !empty( $oldnews ))
3387 $daylist = COM_makeList($oldnews, 'list-older-stories');
3388 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3389 $string .= $daylist;
3390 $string = addslashes( $string );
3392 DB_query( "UPDATE {$_TABLES['blocks']} SET content = '$string' WHERE name = 'older_stories'" );
3398 * Shows a single Geeklog block
3400 * This shows a single block and is typically called from
3401 * COM_showBlocks OR from plugin code
3403 * @param string $name Logical name of block (not same as title) -- 'user_block', 'admin_block', 'section_block', 'whats_new_block'.
3404 * @param string $help Help file location
3405 * @param string $title Title shown in block header
3406 * @param string $position Side, 'left', 'right' or empty.
3407 * @see function COM_showBlocks
3408 * @return string HTML Formated block
3412 function COM_showBlock( $name, $help='', $title='', $position='' )
3414 global $_CONF, $topic, $_TABLES, $_USER;
3418 if( !isset( $_USER['noboxes'] ))
3420 if( !COM_isAnonUser() )
3422 $_USER['noboxes'] = DB_getItem( $_TABLES['userindex'], 'noboxes',
3423 "uid = {$_USER['uid']}" );
3427 $_USER['noboxes'] = 0;
3434 $retval .= COM_userMenu( $help,$title, $position );
3438 $retval .= COM_adminMenu( $help,$title, $position );
3441 case 'section_block':
3442 $retval .= COM_startBlock( $title, $help,
3443 COM_getBlockTemplate( $name, 'header', $position ))
3444 . COM_showTopics( $topic )
3445 . COM_endBlock( COM_getBlockTemplate( $name, 'footer', $position ));
3448 case 'whats_new_block':
3449 if( !$_USER['noboxes'] )
3451 $retval .= COM_whatsNewBlock( $help, $title, $position );
3461 * Shows Geeklog blocks
3463 * Returns HTML for blocks on a given side and, potentially, for
3464 * a given topic. Currently only used by static pages.
3466 * @param string $side Side to get blocks for (right or left for now)
3467 * @param string $topic Only get blocks for this topic
3468 * @param string $name Block name (not used)
3469 * @see function COM_showBlock
3470 * @return string HTML Formated blocks
3474 function COM_showBlocks( $side, $topic='', $name='all' )
3476 global $_CONF, $_TABLES, $_USER, $LANG21, $topic, $page;
3480 // Get user preferences on blocks
3481 if( !isset( $_USER['noboxes'] ) || !isset( $_USER['boxes'] ))
3483 if( !COM_isAnonUser() )
3485 $result = DB_query( "SELECT boxes,noboxes FROM {$_TABLES['userindex']} "
3486 ."WHERE uid = '{$_USER['uid']}'" );
3487 list($_USER['boxes'], $_USER['noboxes']) = DB_fetchArray( $result );
3491 $_USER['boxes'] = '';
3492 $_USER['noboxes'] = 0;
3496 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3497 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3498 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3500 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3502 $commonsql = "FROM {$_TABLES['blocks']} WHERE is_enabled = 1";
3504 if( $side == 'left' )
3506 $commonsql .= " AND onleft = 1";
3510 $commonsql .= " AND onleft = 0";
3513 if( !empty( $topic ))
3515 $tp = addslashes($topic);
3516 $commonsql .= " AND (tid = '$tp' OR tid = 'all')";
3520 if( COM_onFrontpage() )
3522 $commonsql .= " AND (tid = 'homeonly' OR tid = 'all')";
3526 $commonsql .= " AND (tid = 'all')";
3530 if( !empty( $_USER['boxes'] ))
3532 $BOXES = str_replace( ' ', ',', $_USER['boxes'] );
3534 $commonsql .= " AND (bid NOT IN ($BOXES) OR bid = '-1')";
3537 $commonsql .= ' ORDER BY blockorder,title ASC';
3539 $blocksql['mysql'] .= $commonsql;
3540 $blocksql['mssql'] .= $commonsql;
3541 $result = DB_query( $blocksql );
3542 $nrows = DB_numRows( $result );
3544 // convert result set to an array of associated arrays
3546 for( $i = 0; $i < $nrows; $i++ )
3548 $blocks[] = DB_fetchArray( $result );
3551 // Check and see if any plugins have blocks to show
3552 $pluginBlocks = PLG_getBlocks( $side, $topic, $name );
3553 $blocks = array_merge( $blocks, $pluginBlocks );
3555 // sort the resulting array by block order
3556 $column = 'blockorder';
3557 $sortedBlocks = $blocks;
3558 $num_sortedBlocks = count( $sortedBlocks );
3559 for( $i = 0; $i < $num_sortedBlocks - 1; $i++ )
3561 for( $j = 0; $j < $num_sortedBlocks - 1 - $i; $j++ )
3563 if( $sortedBlocks[$j][$column] > $sortedBlocks[$j+1][$column] )
3565 $tmp = $sortedBlocks[$j];
3566 $sortedBlocks[$j] = $sortedBlocks[$j + 1];
3567 $sortedBlocks[$j + 1] = $tmp;
3571 $blocks = $sortedBlocks;
3573 // Loop though resulting sorted array and pass associative arrays
3574 // to COM_formatBlock
3575 foreach( $blocks as $A )
3577 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 )
3579 $retval .= COM_formatBlock( $A, $_USER['noboxes'] );
3587 * Formats a Geeklog block
3589 * This shows a single block and is typically called from
3590 * COM_showBlocks OR from plugin code
3592 * @param array $A Block Record
3593 * @param boolean $noboxes Set to true if userpref is no blocks
3594 * @return string HTML Formated block
3597 function COM_formatBlock( $A, $noboxes = false )
3599 global $_CONF, $_TABLES, $_USER, $LANG21;
3603 $lang = COM_getLanguageId();
3604 if (!empty($lang)) {
3606 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3607 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3608 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3610 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3612 $commonsql = "FROM {$_TABLES['blocks']} WHERE name = '"
3613 . $A['name'] . '_' . $lang . "'";
3615 $blocksql['mysql'] .= $commonsql;
3616 $blocksql['mssql'] .= $commonsql;
3617 $result = DB_query( $blocksql );
3619 if (DB_numRows($result) == 1) {
3620 // overwrite with data for language-specific block
3621 $A = DB_fetchArray($result);
3625 if( array_key_exists( 'onleft', $A ) )
3627 if( $A['onleft'] == 1 )
3631 $position = 'right';
3637 if( $A['type'] == 'portal' )
3639 if( COM_rdfCheck( $A['bid'], $A['rdfurl'], $A['date'], $A['rdflimit'] ))
3641 $A['content'] = DB_getItem( $_TABLES['blocks'], 'content',
3642 "bid = '{$A['bid']}'");
3646 if( $A['type'] == 'gldefault' )
3648 $retval .= COM_showBlock( $A['name'], $A['help'], $A['title'], $position );
3651 if( $A['type'] == 'phpblock' && !$noboxes )
3653 if( !( $A['name'] == 'whosonline_block' AND DB_getItem( $_TABLES['blocks'], 'is_enabled', "name='whosonline_block'" ) == 0 ))
3655 $function = $A['phpblockfn'];
3657 if (preg_match('/^(phpblock_\w*)\\((.*)\\)$/', $function, $matches) == 1)
3659 $function = $matches[1];
3660 $args = $matches[2];
3662 $blkheader = COM_startBlock( $A['title'], $A['help'],
3663 COM_getBlockTemplate( $A['name'], 'header', $position ));
3664 $blkfooter = COM_endBlock( COM_getBlockTemplate( $A['name'],
3665 'footer', $position ));
3667 if( function_exists( $function ))
3671 $fretval = $function($A, $args);
3673 $fretval = $function();
3675 if( !empty( $fretval ))
3677 $retval .= $blkheader;
3678 $retval .= $fretval;
3679 $retval .= $blkfooter;
3684 // show error message
3685 $retval .= $blkheader;
3686 $retval .= sprintf( $LANG21[31], $function );
3687 $retval .= $blkfooter;
3692 if( !empty( $A['content'] ) && ( trim( $A['content'] ) != '' ) && !$noboxes )
3694 $blockcontent = stripslashes( $A['content'] );
3696 // Hack: If the block content starts with a '<' assume it
3697 // contains HTML and do not call nl2br() which would only add
3698 // unwanted <br> tags.
3700 if( substr( $blockcontent, 0, 1 ) != '<' )
3702 $blockcontent = nl2br( $blockcontent );
3705 // autotags are only(!) allowed in normal blocks
3706 if(( $A['allow_autotags'] == 1 ) && ( $A['type'] == 'normal' ))
3708 $blockcontent = PLG_replaceTags( $blockcontent );
3710 $blockcontent = str_replace( array( '<?', '?>' ), '', $blockcontent );
3712 $retval .= COM_startBlock( $A['title'], $A['help'],
3713 COM_getBlockTemplate( $A['name'], 'header', $position ))
3714 . $blockcontent . LB
3715 . COM_endBlock( COM_getBlockTemplate( $A['name'], 'footer', $position ));
3723 * Checks to see if it's time to import and RDF/RSS block again
3725 * Updates RDF/RSS block if needed
3727 * @param string $bid Block ID
3728 * @param string $rdfurl URL to get headlines from
3729 * @param string $date Last time the headlines were imported
3730 * @param string $maxheadlines max. number of headlines to import
3732 * @see function COM_rdfImport
3735 function COM_rdfCheck( $bid, $rdfurl, $date, $maxheadlines = 0 )
3738 $nextupdate = $date + 3600;
3740 if( $nextupdate < time() )
3742 COM_rdfImport( $bid, $rdfurl, $maxheadlines );
3750 * Syndication import function. Imports headline data to a portal block.
3752 * Rewritten December 19th 2004 by Michael Jervis (mike AT fuckingbrit DOT com).
3753 * Now utilises a Factory Pattern to open a URL and automaticaly retreive a feed
3754 * object populated with feed data. Then import it into the portal block.
3756 * @param string $bid Block ID
3757 * @param string $rdfurl URL to get content from
3758 * @param int $maxheadlines Maximum number of headlines to display
3760 * @see function COM_rdfCheck
3763 function COM_rdfImport($bid, $rdfurl, $maxheadlines = 0)
3765 global $_CONF, $_TABLES, $LANG21;
3767 // Import the feed handling classes:
3768 require_once $_CONF['path_system']
3769 . '/classes/syndication/parserfactory.class.php';
3770 require_once $_CONF['path_system']
3771 . '/classes/syndication/feedparserbase.class.php';
3773 $result = DB_query("SELECT rdf_last_modified, rdf_etag FROM {$_TABLES['blocks']} WHERE bid = $bid");
3774 list($last_modified, $etag) = DB_fetchArray($result);
3776 // Load the actual feed handlers:
3777 $factory = new FeedParserFactory($_CONF['path_system']
3778 . '/classes/syndication/');
3779 $factory->userAgent = 'Geeklog/' . VERSION;
3780 if (!empty($last_modified) && !empty($etag)) {
3781 $factory->lastModified = $last_modified;
3782 $factory->eTag = $etag;
3786 $feed = $factory->reader($rdfurl, $_CONF['default_charset']);
3789 /* We have located a reader, and populated it with the information from
3790 * the syndication file. Now we will sort out our display, and update
3793 if ($maxheadlines == 0) {
3794 if (!empty($_CONF['syndication_max_headlines'])) {
3795 $maxheadlines = $_CONF['syndication_max_headlines'];
3797 $maxheadlines = count($feed->articles);
3801 $update = date('Y-m-d H:i:s');
3802 $last_modified = '';
3803 if (!empty($factory->lastModified)) {
3804 $last_modified = addslashes($factory->lastModified);
3807 if (!empty($factory->eTag)) {
3808 $etag = addslashes($factory->eTag);
3811 if (empty($last_modified) || empty($etag)) {
3812 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = '$bid'");
3814 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = '$last_modified', rdf_etag = '$etag' WHERE bid = '$bid'");
3817 $charset = COM_getCharset();
3819 // format articles for display
3820 $readmax = min($maxheadlines, count($feed->articles));
3821 for ($i = 0; $i < $readmax; $i++) {
3822 if (empty($feed->articles[$i]['title'])) {
3823 $feed->articles[$i]['title'] = $LANG21[61];
3826 if ($charset == 'utf-8') {
3827 $title = $feed->articles[$i]['title'];
3829 $title = utf8_decode($feed->articles[$i]['title']);
3831 if ($feed->articles[$i]['link'] != '') {
3832 $content = COM_createLink($title, $feed->articles[$i]['link']);
3833 } elseif ($feed->articles[$i]['enclosureurl'] != '') {
3834 $content = COM_createLink($title, $feed->articles[$i]['enclosureurl']);
3838 $articles[] = $content;
3842 $content = COM_makeList($articles, 'list-feed');
3843 $content = str_replace(array("\015", "\012"), '', $content);
3845 if (strlen($content) > 65000) {
3846 $content = $LANG21[68];
3849 // Standard theme based function to put it in the block
3850 $result = DB_change($_TABLES['blocks'], 'content',
3851 addslashes($content), 'bid', $bid);
3852 } else if ($factory->errorStatus !== false) {
3853 // failed to aquire info, 0 out the block and log an error
3854 COM_errorLog("Unable to aquire feed reader for $rdfurl", 1);
3855 COM_errorLog($factory->errorStatus[0] . ' ' .
3856 $factory->errorStatus[1] . ' ' .
3857 $factory->errorStatus[2]);
3858 $content = addslashes($LANG21[4]);
3859 DB_query("UPDATE {$_TABLES['blocks']} SET content = '$content', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = $bid");
3865 * Returns what HTML is allowed in content
3867 * Returns what HTML tags the system allows to be used inside content.
3868 * You can modify this by changing $_CONF['user_html'] in the configuration
3869 * (for admins, see also $_CONF['admin_html']).
3871 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
3872 * @param boolean $list_only true = return only the list of HTML tags
3873 * @return string HTML <div>/<span> enclosed string
3874 * @see function COM_checkHTML
3875 * @todo Bugs: The list always includes the [code], [raw], and [page_break]
3876 * tags when story.* permissions are required, even when those tags
3877 * are not actually available (e.g. in comments on stories).
3880 function COM_allowedHTML($permissions = 'story.edit', $list_only = false)
3882 global $_CONF, $LANG01;
3886 if (isset($_CONF['skip_html_filter_for_root']) &&
3887 ($_CONF['skip_html_filter_for_root'] == 1) &&
3888 SEC_inGroup('Root')) {
3891 $retval .= '<span class="warningsmall">' . $LANG01[123]
3894 $retval .= '<div dir="ltr" class="warningsmall">';
3899 $retval .= '<span class="warningsmall">' . $LANG01[31] . '</span> ';
3902 if (empty($permissions) || !SEC_hasRights($permissions) ||
3903 empty($_CONF['admin_html'])) {
3904 $html = $_CONF['user_html'];
3906 $html = array_merge_recursive($_CONF['user_html'],
3907 $_CONF['admin_html']);
3910 $retval .= '<div dir="ltr" class="warningsmall">';
3911 foreach ($html as $tag => $attr) {
3912 $retval .= '<' . $tag . '>, ';
3916 $with_story_perms = false;
3917 $perms = explode(',', $permissions);
3918 foreach ($perms as $p) {
3919 if (substr($p, 0, 6) == 'story.') {
3920 $with_story_perms = true;
3925 if ($with_story_perms) {
3926 $retval .= '[code], [raw], ';
3928 if ($_CONF['allow_page_breaks'] == 1) {
3929 $retval .= '[page_break], ';
3934 $autotags = array_keys(PLG_collectTags());
3935 $retval .= '[' . implode(':], [', $autotags) . ':]';
3936 $retval .= '</div>';
3942 * Return the password for the given username
3944 * Fetches a password for the given user
3946 * @param string $loginname username to get password for
3947 * @return string Password or ''
3951 function COM_getPassword( $loginname )
3953 global $_TABLES, $LANG01;
3955 $result = DB_query( "SELECT passwd FROM {$_TABLES['users']} WHERE username='$loginname'" );
3957 $nrows = DB_numRows( $result );
3959 if(( $tmp == 0 ) && ( $nrows == 1 ))
3961 $U = DB_fetchArray( $result );
3962 return $U['passwd'];
3966 $tmp = $LANG01[32] . ": '" . $loginname . "'";
3967 COM_errorLog( $tmp, 1 );
3975 * Return the username or fullname for the passed member id (uid)
3977 * Allows the siteAdmin to determine if loginname (username) or fullname
3978 * should be displayed.
3980 * @param int $uid site member id
3981 * @param string $username Username, if this is set no lookup is done.
3982 * @param string $fullname Users full name.
3983 * @param string $remoteusername Username on remote service
3984 * @param string $remoteservice Remote login service.
3985 * @return string Username, fullname or username@Service
3988 function COM_getDisplayName( $uid = '', $username='', $fullname='', $remoteusername='', $remoteservice='' )
3990 global $_CONF, $_TABLES, $_USER;
3994 if( COM_isAnonUser() )
4000 $uid = $_USER['uid'];
4004 if( empty( $username ))
4006 $query = DB_query( "SELECT username, fullname, remoteusername, remoteservice FROM {$_TABLES['users']} WHERE uid='$uid'" );
4007 list( $username, $fullname, $remoteusername, $remoteservice ) = DB_fetchArray( $query );
4010 if( !empty( $fullname ) && ($_CONF['show_fullname'] == 1 ))
4014 else if(( $_CONF['user_login_method']['3rdparty'] || $_CONF['user_login_method']['openid'] ) && !empty( $remoteusername ))
4016 if( !empty( $username ))
4018 $remoteusername = $username;
4021 if( $_CONF['show_servicename'] )
4023 return "$remoteusername@$remoteservice";
4027 return $remoteusername;
4036 * Adds a hit to the system
4038 * This function is called in the footer of every page and is used to
4039 * track the number of hits to the Geeklog system. This information is
4040 * shown on stats.php
4048 DB_change($_TABLES['vars'], 'value', 'value + 1', 'name', 'totalhits', '', true);
4052 * This will email new stories in the topics that the user is interested in
4054 * In account information the user can specify which topics for which they
4055 * will receive any new article for in a daily digest.
4060 function COM_emailUserTopics()
4062 global $_CONF, $_TABLES, $LANG04, $LANG08, $LANG24;
4064 if ($_CONF['emailstories'] == 0) {
4068 $subject = strip_tags( $_CONF['site_name'] . $LANG08[30] . strftime( '%Y-%m-%d', time() ));
4072 // Get users who want stories emailed to them
4073 $usersql = "SELECT username,email,etids,{$_TABLES['users']}.uid AS uuid "
4074 . "FROM {$_TABLES['users']}, {$_TABLES['userindex']} "
4075 . "WHERE {$_TABLES['users']}.uid > 1 AND {$_TABLES['userindex']}.uid = {$_TABLES['users']}.uid AND (etids <> '-' OR etids IS NULL) ORDER BY {$_TABLES['users']}.uid";
4077 $users = DB_query( $usersql );
4078 $nrows = DB_numRows( $users );
4080 $lastrun = DB_getItem( $_TABLES['vars'], 'value', "name = 'lastemailedstories'" );
4082 // For each user, pull the stories they want and email it to them
4083 for( $x = 0; $x < $nrows; $x++ )
4085 $U = DB_fetchArray( $users );
4087 $storysql = array();
4088 $storysql['mysql'] = "SELECT sid,uid,date AS day,title,introtext,postmode";
4090 $storysql['mssql'] = "SELECT sid,uid,date AS day,title,CAST(introtext AS text) AS introtext,postmode";
4092 $commonsql = " FROM {$_TABLES['stories']} WHERE draft_flag = 0 AND date <= NOW() AND date >= '{$lastrun}'";
4094 $topicsql = "SELECT tid FROM {$_TABLES['topics']}"
4095 . COM_getPermSQL( 'WHERE', $U['uuid'] );
4096 $tresult = DB_query( $topicsql );
4097 $trows = DB_numRows( $tresult );
4101 // this user doesn't seem to have access to any topics ...
4106 for( $i = 0; $i < $trows; $i++ )
4108 $T = DB_fetchArray( $tresult );
4109 $TIDS[] = $T['tid'];
4112 if( !empty( $U['etids'] ))
4114 $ETIDS = explode( ' ', $U['etids'] );
4115 $TIDS = array_intersect( $TIDS, $ETIDS );
4118 if( count( $TIDS ) > 0)
4120 $commonsql .= " AND (tid IN ('" . implode( "','", $TIDS ) . "'))";
4123 $commonsql .= COM_getPermSQL( 'AND', $U['uuid'] );
4124 $commonsql .= ' ORDER BY featured DESC, date DESC';
4126 $storysql['mysql'] .= $commonsql;
4127 $storysql['mssql'] .= $commonsql;
4129 $stories = DB_query( $storysql );
4130 $nsrows = DB_numRows( $stories );
4134 // If no new stories where pulled for this user, continue with next
4138 $mailtext = $LANG08[29] . strftime( $_CONF['shortdate'], time() ) . "\n";
4140 for( $y = 0; $y < $nsrows; $y++ )
4142 // Loop through stories building the requested email message
4143 $S = DB_fetchArray( $stories );
4145 $mailtext .= "\n------------------------------\n\n";
4146 $mailtext .= "$LANG08[31]: "
4147 . COM_undoSpecialChars( stripslashes( $S['title'] )) . "\n";
4148 if( $_CONF['contributedbyline'] == 1 )
4150 if( empty( $authors[$S['uid']] ))
4152 $storyauthor = COM_getDisplayName ($S['uid']);
4153 $authors[$S['uid']] = $storyauthor;
4157 $storyauthor = $authors[$S['uid']];
4159 $mailtext .= "$LANG24[7]: " . $storyauthor . "\n";
4162 $mailtext .= "$LANG08[32]: " . strftime( $_CONF['date'], strtotime( $S['day' ])) . "\n\n";
4164 if( $_CONF['emailstorieslength'] > 0 )
4166 if($S['postmode']==='wikitext'){
4167 $storytext = COM_undoSpecialChars( strip_tags( COM_renderWikiText ( stripslashes( $S['introtext'] ))));
4169 $storytext = COM_undoSpecialChars( strip_tags( PLG_replaceTags( stripslashes( $S['introtext'] ))));
4172 if( $_CONF['emailstorieslength'] > 1 )
4174 $storytext = COM_truncate( $storytext,
4175 $_CONF['emailstorieslength'], '...' );
4178 $mailtext .= $storytext . "\n\n";
4181 $mailtext .= $LANG08[33] . ' ' . COM_buildUrl( $_CONF['site_url']
4182 . '/article.php?story=' . $S['sid'] ) . "\n";
4185 $mailtext .= "\n------------------------------\n";
4186 $mailtext .= "\n$LANG08[34]\n";
4187 $mailtext .= "\n------------------------------\n";
4189 $mailto = $U['username'] . ' <' . $U['email'] . '>';
4191 if ($_CONF['site_mail'] !== $_CONF['noreply_mail']) {
4192 $mailfrom = $_CONF['noreply_mail'];
4193 $mailtext .= LB . LB . $LANG04[159];
4195 $mailfrom = $_CONF['site_mail'];
4197 COM_mail( $mailto, $subject, $mailtext , $mailfrom);
4200 DB_query( "UPDATE {$_TABLES['vars']} SET value = NOW() WHERE name = 'lastemailedstories'" );
4204 * Shows any new information in a block
4206 * Return the HTML that shows any new stories, comments, etc
4208 * @param string $help Help file for block
4209 * @param string $title Title used in block header
4210 * @param string $position Position in which block is being rendered 'left', 'right' or blank (for centre)
4211 * @return string Return the HTML that shows any new stories, comments, etc
4215 function COM_whatsNewBlock( $help = '', $title = '', $position = '' )
4217 global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_WHATSNEW, $page, $newstories;
4219 $retval = COM_startBlock( $title, $help,
4220 COM_getBlockTemplate( 'whats_new_block', 'header', $position ));
4223 if(( $_CONF['hidenewstories'] == 0 ) || ( $_CONF['hidenewcomments'] == 0 )
4224 || ( $_CONF['trackback_enabled']
4225 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4227 $topicsql = COM_getTopicSql ('AND', 0, $_TABLES['stories']);
4230 if( $_CONF['hidenewstories'] == 0 )
4233 $archivetid = DB_getItem( $_TABLES['topics'], 'tid', "archive_flag=1" );
4234 if( !empty( $archivetid ))
4236 $archsql = " AND (tid <> '" . addslashes( $archivetid ) . "')";
4239 // Find the newest stories
4240 $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' );
4241 $result = DB_query( $sql );
4242 $A = DB_fetchArray( $result );
4243 $nrows = $A['count'];
4245 if( empty( $title ))
4247 $title = DB_getItem( $_TABLES['blocks'], 'title', "name='whats_new_block'" );
4250 // Any late breaking news stories?
4251 $retval .= '<h3>' . $LANG01[99] . '</h3>';
4255 $newmsg = COM_formatTimeString( $LANG_WHATSNEW['new_string'],
4256 $_CONF['newstoriesinterval'], $LANG01[11], $nrows);
4258 if( $newstories && ( $page < 2 ))
4260 $retval .= $newmsg . '<br' . XHTML . '>';
4264 $retval .= COM_createLink($newmsg, $_CONF['site_url']
4265 . '/index.php?display=new') . '<br' . XHTML . '>';
4270 $retval .= $LANG01[100] . '<br' . XHTML . '>';
4273 if(( $_CONF['hidenewcomments'] == 0 ) || ( $_CONF['trackback_enabled']
4274 && ( $_CONF['hidenewtrackbacks'] == 0 ))
4275 || ( $_CONF['hidenewplugins'] == 0 ))
4277 $retval .= '<br' . XHTML . '>';
4281 if( $_CONF['hidenewcomments'] == 0 )
4283 // Go get the newest comments
4284 $retval .= '<h3>' . $LANG01[83] . ' <small>'
4285 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4286 $_CONF['newcommentsinterval'] )
4291 if( !COM_isAnonUser() )
4293 $stwhere .= "({$_TABLES['stories']}.owner_id IS NOT NULL AND {$_TABLES['stories']}.perm_owner IS NOT NULL) OR ";
4294 $stwhere .= "({$_TABLES['stories']}.group_id IS NOT NULL AND {$_TABLES['stories']}.perm_group IS NOT NULL) OR ";
4295 $stwhere .= "({$_TABLES['stories']}.perm_members IS NOT NULL)";
4299 $stwhere .= "({$_TABLES['stories']}.perm_anon IS NOT NULL)";
4301 $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";
4303 $result = DB_query( $sql );
4305 $nrows = DB_numRows( $result );
4309 $newcomments = array();
4311 for( $x = 0; $x < $nrows; $x++ )
4313 $A = DB_fetchArray( $result );
4315 if(( $A['type'] == 'article' ) || empty( $A['type'] ))
4317 $url = COM_buildUrl( $_CONF['site_url']
4318 . '/article.php?story=' . $A['sid'] ) . '#comments';
4321 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4322 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4324 if( $title != $titletouse )
4326 $attr = array('title' => htmlspecialchars($title));
4332 $acomment = str_replace( '$', '$', $titletouse );
4333 $acomment = str_replace( ' ', ' ', $acomment );
4335 if( $A['dups'] > 1 )
4337 $acomment .= ' [+' . $A['dups'] . ']';
4340 $newcomments[] = COM_createLink($acomment, $url, $attr);
4343 $retval .= COM_makeList( $newcomments, 'list-new-comments' );
4347 $retval .= $LANG01[86] . '<br' . XHTML . '>' . LB;
4349 if(( $_CONF['hidenewplugins'] == 0 )
4350 || ( $_CONF['trackback_enabled']
4351 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4353 $retval .= '<br' . XHTML . '>';
4357 if( $_CONF['trackback_enabled'] && ( $_CONF['hidenewtrackbacks'] == 0 ))
4359 $retval .= '<h3>' . $LANG01[114] . ' <small>'
4360 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4361 $_CONF['newtrackbackinterval'] )
4364 $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";
4365 $result = DB_query( $sql );
4367 $nrows = DB_numRows( $result );
4370 $newcomments = array();
4372 for( $i = 0; $i < $nrows; $i++ )
4374 $A = DB_fetchArray( $result );
4376 $url = COM_buildUrl( $_CONF['site_url']
4377 . '/article.php?story=' . $A['sid'] ) . '#trackback';
4379 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4380 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4383 if( $title != $titletouse )
4385 $attr = array('title' => htmlspecialchars($title));
4391 $acomment = str_replace( '$', '$', $titletouse );
4392 $acomment = str_replace( ' ', ' ', $acomment );
4394 if( $A['count'] > 1 )
4396 $acomment .= ' [+' . $A['count'] . ']';
4399 $newcomments[] = COM_createLink($acomment, $url, $attr);
4402 $retval .= COM_makeList( $newcomments, 'list-new-trackbacks' );
4406 $retval .= $LANG01[115] . '<br' . XHTML . '>' . LB;
4408 if( $_CONF['hidenewplugins'] == 0 )
4410 $retval .= '<br' . XHTML . '>';
4414 if( $_CONF['hidenewplugins'] == 0 )
4416 list( $headlines, $smallheadlines, $content ) = PLG_getWhatsNew();
4417 $plugins = count( $headlines );
4420 for( $i = 0; $i < $plugins; $i++ )
4422 $retval .= '<h3>' . $headlines[$i] . ' <small>'
4423 . $smallheadlines[$i] . '</small></h3>';
4424 if( is_array( $content[$i] ))
4426 $retval .= COM_makeList( $content[$i], 'list-new-plugins' );
4430 $retval .= $content[$i];
4433 if( $i + 1 < $plugins )
4435 $retval .= '<br' . XHTML . '>';
4441 $retval .= COM_endBlock( COM_getBlockTemplate( 'whats_new_block', 'footer', $position ));
4447 * Creates the string that indicates the timespan in which new items were found
4449 * @param string $time_string template string
4450 * @param int $time number of seconds in which results are found
4451 * @param string $type type (translated string) of new item
4452 * @param int $amount amount of things that have been found.
4454 function COM_formatTimeString( $time_string, $time, $type = '', $amount = 0 )
4456 global $LANG_WHATSNEW;
4458 $retval = $time_string;
4460 // This is the amount you have to divide the previous by to get the
4461 // different time intervals: hour, day, week, months
4462 $time_divider = array( 60, 60, 24, 7, 30 );
4464 // These are the respective strings to the numbers above. They have to match
4465 // the strings in $LANG_WHATSNEW (i.e. these are the keys for the array -
4466 // the actual text strings are taken from the language file).
4467 $time_description = array( 'minute', 'hour', 'day', 'week', 'month' );
4468 $times_description = array( 'minutes', 'hours', 'days', 'weeks', 'months' );
4470 $time_dividers = count( $time_divider );
4471 for( $s = 0; $s < $time_dividers; $s++ )
4473 $time = $time / $time_divider[$s];
4474 if( $time < $time_divider[$s + 1] )
4480 $time_str = $time_description[$s];
4482 else // go back to the previous unit, e.g. 1 day -> 24 hours
4484 $time_str = $times_description[$s - 1];
4485 $time *= $time_divider[$s];
4490 $time_str = $times_description[$s];
4492 $fields = array( '%n', '%i', '%t', '%s' );
4493 $values = array( $amount, $type, $time, $LANG_WHATSNEW[$time_str] );
4494 $retval = str_replace( $fields, $values, $retval );
4503 * Displays a message text in a "System Message" block
4505 * @param string $message Message text; may contain HTML
4506 * @param string $title (optional) alternative block title
4507 * @return string HTML block with message
4508 * @see COM_showMessage
4509 * @see COM_showMessageFromParameter
4512 function COM_showMessageText($message, $title = '')
4514 global $_CONF, $MESSAGE, $_IMAGE_TYPE;
4518 if (!empty($message)) {
4519 if (empty($title)) {
4520 $title = $MESSAGE[40];
4522 $timestamp = strftime($_CONF['daytime']);
4523 $retval .= COM_startBlock($title . ' - ' . $timestamp, '',
4524 COM_getBlockTemplate('_msg_block', 'header'))
4525 . '<p class="sysmessage"><img src="' . $_CONF['layout_url']
4526 . '/images/sysmessage.' . $_IMAGE_TYPE . '" alt="" ' . XHTML
4527 . '>' . $message . '</p>'
4528 . COM_endBlock(COM_getBlockTemplate('_msg_block', 'footer'));
4535 * Displays a message on the webpage
4537 * Display one of the predefined messages from the $MESSAGE array. If a plugin
4538 * name is provided, display that plugin's message instead.
4540 * @param int $msg ID of message to show
4541 * @param string $plugin Optional name of plugin to lookup plugin defined message
4542 * @return string HTML block with message
4543 * @see COM_showMessageFromParameter
4544 * @see COM_showMessageText
4547 function COM_showMessage($msg, $plugin = '')
4554 if (!empty($plugin)) {
4555 $var = 'PLG_' . $plugin . '_MESSAGE' . $msg;
4560 $message = sprintf($MESSAGE[61], $plugin);
4561 COM_errorLog($message . ": " . $var, 1);
4564 $message = $MESSAGE[$msg];
4567 if (!empty($message)) {
4568 $retval .= COM_showMessageText($message);
4576 * Displays a message, as defined by URL parameters
4578 * Helper function to display a message, if URL parameters 'msg' and 'plugin'
4579 * (optional) are defined. Only for GET requests, but that's what Geeklog uses
4580 * everywhere anyway.
4582 * @return string HTML block with message
4583 * @see COM_showMessage
4584 * @see COM_showMessageText
4587 function COM_showMessageFromParameter()
4591 if (isset($_GET['msg'])) {
4592 $msg = COM_applyFilter($_GET['msg'], true);
4595 if (isset($_GET['plugin'])) {
4596 $plugin = COM_applyFilter($_GET['plugin']);
4598 $retval .= COM_showMessage($msg, $plugin);
4606 * Prints Google(tm)-like paging navigation
4608 * @param string $base_url base url to use for all generated links
4609 * @param int $curpage current page we are on
4610 * @param int $num_pages Total number of pages
4611 * @param string $page_str page-variable name AND '='
4612 * @param boolean $do_rewrite if true, url-rewriting is respected
4613 * @param string $msg to be displayed with the navigation
4614 * @param string $open_ended replace next/last links with this
4615 * @return string HTML formatted widget
4617 function COM_printPageNavigation( $base_url, $curpage, $num_pages,
4618 $page_str='page=', $do_rewrite=false, $msg='',
4625 if( $num_pages < 2 )
4632 $hasargs = strstr( $base_url, '?' );
4650 $retval .= COM_createLink($LANG05[7], $base_url) . ' | ';
4652 if( ( $curpage - 1 ) > 1 )
4654 $pg = $sep . $page_str . ( $curpage - 1 );
4656 $retval .= COM_createLink($LANG05[6], $base_url . $pg ) . ' | ';
4660 $retval .= $LANG05[7] . ' | ' ;
4661 $retval .= $LANG05[6] . ' | ' ;
4664 for( $pgcount = ( $curpage - 10 ); ( $pgcount <= ( $curpage + 9 )) AND ( $pgcount <= $num_pages ); $pgcount++ )
4671 if( $pgcount == $curpage )
4673 $retval .= '<b>' . $pgcount . '</b> ';
4680 $pg = $sep . $page_str . $pgcount;
4682 $retval .= COM_createLink($pgcount, $base_url . $pg) . ' ';
4686 if( !empty( $open_ended ))
4688 $retval .= '| ' . $open_ended;
4690 else if( $curpage == $num_pages )
4692 $retval .= '| ' . $LANG05[5] . ' ';
4693 $retval .= '| ' . $LANG05[8];
4697 $retval .= '| ' . COM_createLink($LANG05[5], $base_url . $sep
4698 . $page_str . ($curpage + 1));
4699 $retval .= ' | ' . COM_createLink($LANG05[8], $base_url . $sep
4700 . $page_str . $num_pages);
4703 if( !empty( $retval ))
4709 $retval = '<div class="pagenav">' . $msg . $retval . '</div>';
4716 * Returns formatted date/time for user
4718 * This function COM_takes a date in either unixtimestamp or in english and
4719 * formats it to the users preference. If the user didn't specify a format
4720 * the format in the config file is used. This returns an array where array[0]
4721 * is the formatted date and array[1] is the unixtimestamp
4723 * @param string $date date to format, otherwise we format current date/time
4724 * @return array array[0] is the formatted date and array[1] is the unixtimestamp.
4727 function COM_getUserDateTimeFormat( $date='' )
4729 global $_TABLES, $_USER, $_CONF;
4731 // Get display format for time
4733 if( !COM_isAnonUser() )
4735 if( empty( $_USER['format'] ))
4737 $dateformat = $_CONF['date'];
4741 $dateformat = $_USER['format'];
4746 $dateformat = $_CONF['date'];
4751 // Date is empty, get current date/time
4754 else if( is_numeric( $date ))
4756 // This is a timestamp
4761 // This is a string representation of a date/time
4762 $stamp = strtotime( $date );
4767 $date = strftime( $dateformat, $stamp );
4769 return array( $date, $stamp );
4773 * Returns user-defined cookie timeout
4775 * In account preferences users can specify when their long-term cookie expires.
4776 * This function returns that value.
4778 * @return int Cookie time out value in seconds
4781 function COM_getUserCookieTimeout()
4783 global $_TABLES, $_USER, $_CONF;
4785 if( empty( $_USER ))
4790 $timeoutvalue = DB_getItem( $_TABLES['users'], 'cookietimeout', "uid = {$_USER['uid']}" );
4792 if( empty( $timeoutvalue ))
4797 return $timeoutvalue;
4801 * Shows who is online in slick little block
4802 * @return string HTML string of online users seperated by line breaks.
4805 function phpblock_whosonline()
4807 global $_CONF, $_TABLES, $_USER, $LANG01, $_IMAGE_TYPE;
4811 $expire_time = time() - $_CONF['whosonline_threshold'];
4813 $byname = 'username';
4814 if( $_CONF['show_fullname'] == 1 )
4816 $byname .= ',fullname';
4818 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4820 $byname .= ',remoteusername,remoteservice';
4823 $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}" );
4824 $nrows = DB_numRows( $result );
4829 for( $i = 0; $i < $nrows; $i++ )
4831 $A = DB_fetchArray( $result );
4833 if( $A['showonline'] == 1 )
4836 if( $_CONF['show_fullname'] == 1 )
4838 $fullname = $A['fullname'];
4840 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4842 $username = COM_getDisplayName( $A['uid'], $A['username'],
4843 $fullname, $A['remoteusername'], $A['remoteservice'] );
4847 $username = COM_getDisplayName( $A['uid'], $A['username'],
4850 $url = $_CONF['site_url'] . '/users.php?mode=profile&uid=' . $A['uid'];
4851 $retval .= COM_createLink($username, $url);
4853 if( !empty( $A['photo'] ) AND $_CONF['allow_user_photo'] == 1)
4855 $usrimg = '<img src="' . $_CONF['layout_url']
4856 . '/images/smallcamera.' . $_IMAGE_TYPE
4857 . '" alt=""' . XHTML . '>';
4858 $retval .= ' ' . COM_createLink($usrimg, $url);
4860 $retval .= '<br' . XHTML . '>';
4865 // this user does not want to show up in Who's Online
4866 $num_anon++; // count as anonymous
4870 $num_anon += DB_count( $_TABLES['sessions'], 'uid', 1 );
4872 if(( $_CONF['whosonline_anonymous'] == 1 ) &&
4875 // note that we're overwriting the contents of $retval here
4878 $retval = $LANG01[112] . ': ' . COM_numberFormat($num_reg)
4879 . '<br' . XHTML . '>';
4889 $retval .= $LANG01[41] . ': ' . COM_numberFormat($num_anon)
4890 . '<br' . XHTML . '>';
4897 * Gets the <option> values for calendar months
4899 * @param string $selected Selected month
4900 * @see function COM_getDayFormOptions
4901 * @see function COM_getYearFormOptions
4902 * @see function COM_getHourFormOptions
4903 * @see function COM_getMinuteFormOptions
4904 * @return string HTML Months as option values
4907 function COM_getMonthFormOptions( $selected = '' )
4911 $month_options = '';
4913 for( $i = 1; $i <= 12; $i++ )
4916 $month_options .= '<option value="' . $mval . '"';
4918 if( $i == $selected )
4920 $month_options .= ' selected="selected"';
4923 $month_options .= '>' . $LANG_MONTH[$mval] . '</option>';
4926 return $month_options;
4930 * Gets the <option> values for calendar days
4932 * @param string $selected Selected day
4933 * @see function COM_getMonthFormOptions
4934 * @see function COM_getYearFormOptions
4935 * @see function COM_getHourFormOptions
4936 * @see function COM_getMinuteFormOptions
4937 * @return string HTML days as option values
4940 function COM_getDayFormOptions( $selected = '' )
4944 for( $i = 1; $i <= 31; $i++ )
4955 $day_options .= '<option value="' . $dval . '"';
4957 if( $i == $selected )
4959 $day_options .= ' selected="selected"';
4962 $day_options .= '>' . $dval . '</option>';
4965 return $day_options;
4969 * Gets the <option> values for calendar years
4971 * Returns Option list Containing 5 years starting with current
4972 * unless @selected is < current year then starts with @selected
4974 * @param string $selected Selected year
4975 * @param int $startoffset Optional (can be +/-) Used to determine start year for range of years
4976 * @param int $endoffset Optional (can be +/-) Used to determine end year for range of years
4977 * @see function COM_getMonthFormOptions
4978 * @see function COM_getDayFormOptions
4979 * @see function COM_getHourFormOptions
4980 * @see function COM_getMinuteFormOptions
4981 * @return string HTML years as option values
4984 function COM_getYearFormOptions($selected = '', $startoffset = -1, $endoffset = 5)
4987 $start_year = date('Y') + $startoffset;
4988 $cur_year = date('Y', time());
4989 $finish_year = $cur_year + $endoffset;
4991 if (!empty($selected)) {
4992 if ($selected < $cur_year) {
4993 $start_year = $selected;
4997 for ($i = $start_year; $i <= $finish_year; $i++) {
4998 $year_options .= '<option value="' . $i . '"';
5000 if ($i == $selected) {
5001 $year_options .= ' selected="selected"';
5004 $year_options .= '>' . $i . '</option>';
5007 return $year_options;
5011 * Gets the <option> values for clock hours
5013 * @param string $selected Selected hour
5014 * @param int $mode 12 or 24 hour mode
5015 * @return string HTML string of options
5016 * @see function COM_getMonthFormOptions
5017 * @see function COM_getDayFormOptions
5018 * @see function COM_getYearFormOptions
5019 * @see function COM_getMinuteFormOptions
5022 function COM_getHourFormOptions( $selected = '', $mode = 12 )
5028 for( $i = 1; $i <= 11; $i++ )
5041 $hour_options .= '<option value="12"';
5043 if( $selected == 12 )
5045 $hour_options .= ' selected="selected"';
5048 $hour_options .= '>12</option>';
5051 $hour_options .= '<option value="' . $hval . '"';
5053 if( $selected == $i )
5055 $hour_options .= ' selected="selected"';
5058 $hour_options .= '>' . $i . '</option>';
5061 else // if( $mode == 24 )
5063 for( $i = 0; $i < 24; $i++ )
5074 $hour_options .= '<option value="' . $hval . '"';
5076 if( $selected == $i )
5078 $hour_options .= ' selected="selected"';
5081 $hour_options .= '>' . $i . '</option>';
5085 return $hour_options;
5089 * Gets the <option> values for clock minutes
5091 * @param string $selected Selected minutes
5092 * @param int $step number of minutes between options, e.g. 15
5093 * @see function COM_getMonthFormOptions
5094 * @see function COM_getDayFormOptions
5095 * @see function COM_getHourFormOptions
5096 * @see function COM_getYearFormOptions
5097 * @return string HTML of option minutes
5100 function COM_getMinuteFormOptions( $selected = '', $step = 1 )
5102 $minute_options = '';
5104 if(( $step < 1 ) || ( $step > 30 ))
5109 for( $i = 0; $i <= 59; $i += $step )
5120 $minute_options .= '<option value="' . $mval . '"';
5122 if( $selected == $i )
5124 $minute_options .= ' selected="selected"';
5127 $minute_options .= '>' . $mval . '</option>';
5130 return $minute_options;
5134 * For backward compatibility only.
5135 * This function should always have been called COM_getMinuteFormOptions
5136 * @see COM_getMinuteFormOptions
5138 function COM_getMinuteOptions( $selected = '', $step = 1 )
5140 return COM_getMinuteFormOptions( $selected, $step );
5144 * Create an am/pm selector dropdown menu
5146 * @param string $name name of the <select>
5147 * @param string $selected preselection: 'am' or 'pm'
5148 * @return string HTML for the dropdown; empty string in 24 hour mode
5151 function COM_getAmPmFormSelection( $name, $selected = '' )
5157 if( isset( $_CONF['hour_mode'] ) && ( $_CONF['hour_mode'] == 24 ))
5163 if( empty( $selected ))
5165 $selected = date( 'a' );
5168 $retval .= '<select name="' . $name . '">' . LB;
5169 $retval .= '<option value="am"';
5170 if( $selected == 'am' )
5172 $retval .= ' selected="selected"';
5174 $retval .= '>am</option>' . LB . '<option value="pm"';
5175 if( $selected == 'pm' )
5177 $retval .= ' selected="selected"';
5179 $retval .= '>pm</option>' . LB . '</select>' . LB;
5186 * Creates an HTML unordered list from the given array.
5187 * It formats one list item per array element, using the list.thtml
5188 * and listitem.thtml templates.
5190 * @param array $listofitems Items to list out
5191 * @param string $classname optional CSS class name for the list
5192 * @return string HTML unordered list of array items
5194 function COM_makeList($listofitems, $classname = '')
5198 $list = new Template($_CONF['path_layout']);
5199 $list->set_file(array('list' => 'list.thtml',
5200 'listitem' => 'listitem.thtml'));
5201 $list->set_var( 'xhtml', XHTML );
5202 $list->set_var('site_url', $_CONF['site_url']);
5203 $list->set_var('site_admin_url', $_CONF['site_admin_url']);
5204 $list->set_var('layout_url', $_CONF['layout_url']);
5206 if (empty($classname)) {
5207 $list->set_var('list_class', '');
5208 $list->set_var('list_class_name', '');
5210 $list->set_var('list_class', 'class="' . $classname . '"');
5211 $list->set_var('list_class_name', $classname);
5214 if (is_array($listofitems)) {
5215 foreach ($listofitems as $oneitem) {
5216 $list->set_var('list_item', $oneitem);
5217 $list->parse('list_items', 'listitem', true);
5221 $list->parse('newlist', 'list', true);
5223 return $list->finish($list->get_var('newlist'));
5227 * Check if speed limit applies
5229 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5230 * @param int $max max number of allowed tries within speed limit
5231 * @param string $property IP address or other identifiable property
5232 * @return int 0: does not apply, else: seconds since last post
5234 function COM_checkSpeedlimit($type = 'submit', $max = 1, $property = '')
5240 if (empty($property)) {
5241 $property = $_SERVER['REMOTE_ADDR'];
5243 $property = addslashes($property);
5245 $res = DB_query("SELECT date FROM {$_TABLES['speedlimit']} WHERE (type = '$type') AND (ipaddress = '$property') ORDER BY date ASC");
5247 // If the number of allowed tries has not been reached,
5248 // return 0 (didn't hit limit)
5249 if (DB_numRows($res) < $max) {
5253 list($date) = DB_fetchArray($res);
5255 if (!empty($date)) {
5256 $last = time() - $date;
5258 // just in case someone manages to submit something in < 1 sec.
5267 * Store post info for speed limit
5269 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5270 * @param string $property IP address or other identifiable property
5273 function COM_updateSpeedlimit($type = 'submit', $property = '')
5277 if (empty($property)) {
5278 $property = $_SERVER['REMOTE_ADDR'];
5280 $property = addslashes($property);
5282 DB_save($_TABLES['speedlimit'], 'ipaddress,date,type',
5283 "'$property',UNIX_TIMESTAMP(),'$type'");
5287 * Clear out expired speed limits, i.e. entries older than 'x' seconds
5289 * @param speedlimit int number of seconds
5290 * @param type string type of speed limit, e.g. 'submit', 'comment'
5293 function COM_clearSpeedlimit($speedlimit = 60, $type = '')
5297 $sql = "DELETE FROM {$_TABLES['speedlimit']} WHERE ";
5298 if (!empty($type)) {
5299 $sql .= "(type = '$type') AND ";
5301 $sql .= "(date < UNIX_TIMESTAMP() - $speedlimit)";
5306 * Reset the speedlimit
5308 * @param string $type type of speed limit to reset, e.g. 'submit'
5309 * @param string $property IP address or other identifiable property
5312 function COM_resetSpeedlimit($type = 'submit', $property = '')
5316 if (empty($property)) {
5317 $property = $_SERVER['REMOTE_ADDR'];
5319 $property = addslashes($property);
5321 DB_delete($_TABLES['speedlimit'], array('type', 'ipaddress'),
5322 array($type, $property));
5326 * Wrapper function for URL class so as to not confuse people as this will
5327 * eventually get used all over the place
5329 * This function returns a crawler friendly URL (if possible)
5331 * @param string $url URL to try to build crawler friendly URL for
5332 * @return string Rewritten URL
5335 function COM_buildURL( $url )
5339 return $_URL->buildURL( $url );
5343 * Wrapper function for URL class so as to not confuse people
5345 * This function sets the name of the arguments found in url
5347 * @param array $names Names of arguments in query string to assign to values
5348 * @return boolean True if successful
5351 function COM_setArgNames( $names )
5355 return $_URL->setArgNames( $names );
5359 * Wrapper function for URL class
5361 * returns value for specified argument
5363 * @param string $name argument to get value for
5364 * @return string Argument value
5367 function COM_getArgument( $name )
5371 return $_URL->getArgument( $name );
5377 * This will take a number of occurrences, and number of seconds for the time span and return
5378 * the smallest #/time interval
5380 * @param int $occurrences how many occurrences during time interval
5381 * @param int $timespan time interval in seconds
5382 * @return int Seconds per interval
5385 function COM_getRate( $occurrences, $timespan )
5387 // want to define some common time words (yes, dirk, i need to put this in LANG)
5388 // time words and their value in seconds
5389 // week is 7 * day, month is 30 * day, year is 365.25 * day
5391 $common_time = array(
5401 if( $occurrences != 0 )
5403 $rate = ( int )( $timespan / $occurrences );
5404 $adjustedRate = $occurrences + 1;
5405 $time_unit = 'second';
5409 foreach( $common_time as $unit=>$seconds )
5411 if( $rate > $seconds )
5413 $foo = ( int )(( $rate / $seconds ) + .5 );
5415 if(( $foo < $occurrences ) && ( $foo > 0 ))
5417 $adjustedRate = $foo;
5423 $singular = '1 shout every ' . $adjustedRate . ' ' . $time_unit;
5425 if( $adjustedRate > 1 )
5432 $singular = 'No events';
5439 * Return SQL expression to check for permissions.
5441 * Creates part of an SQL expression that can be used to request items with the
5442 * standard set of Geeklog permissions.
5444 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5445 * @param int $u_id user id or 0 = current user
5446 * @param int $access access to check for (2=read, 3=r&write)
5447 * @param string $table table name if ambiguous (e.g. in JOINs)
5448 * @return string SQL expression string (may be empty)
5451 function COM_getPermSQL( $type = 'WHERE', $u_id = 0, $access = 2, $table = '' )
5453 global $_USER, $_GROUPS;
5455 if( !empty( $table ))
5461 if( COM_isAnonUser() )
5467 $uid = $_USER['uid'];
5475 $UserGroups = array();
5476 if(( empty( $_USER['uid'] ) && ( $uid == 1 )) || ( $uid == $_USER['uid'] ))
5478 if( empty( $_GROUPS ))
5480 $_GROUPS = SEC_getUserGroups( $uid );
5482 $UserGroups = $_GROUPS;
5486 $UserGroups = SEC_getUserGroups( $uid );
5489 if( empty( $UserGroups ))
5491 // this shouldn't really happen, but if it does, handle user
5492 // like an anonymous user
5496 if( SEC_inGroup( 'Root', $uid ))
5501 $sql = ' ' . $type . ' (';
5505 $sql .= "(({$table}owner_id = '{$uid}') AND ({$table}perm_owner >= $access)) OR ";
5507 $sql .= "(({$table}group_id IN (" . implode( ',', $UserGroups )
5508 . ")) AND ({$table}perm_group >= $access)) OR ";
5509 $sql .= "({$table}perm_members >= $access)";
5513 $sql .= "{$table}perm_anon >= $access";
5522 * Return SQL expression to check for allowed topics.
5524 * Creates part of an SQL expression that can be used to only request stories
5525 * from topics to which the user has access to.
5527 * Note that this function does an SQL request, so you should cache
5528 * the resulting SQL expression if you need it more than once.
5530 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5531 * @param int $u_id user id or 0 = current user
5532 * @param string $table table name if ambiguous (e.g. in JOINs)
5533 * @return string SQL expression string (may be empty)
5536 function COM_getTopicSQL( $type = 'WHERE', $u_id = 0, $table = '' )
5538 global $_TABLES, $_USER, $_GROUPS;
5540 $topicsql = ' ' . $type . ' ';
5542 if( !empty( $table ))
5547 $UserGroups = array();
5548 if(( $u_id <= 0 ) || ( isset( $_USER['uid'] ) && $u_id == $_USER['uid'] ))
5550 if( !COM_isAnonUser() )
5552 $uid = $_USER['uid'];
5558 $UserGroups = $_GROUPS;
5563 $UserGroups = SEC_getUserGroups( $uid );
5566 if( empty( $UserGroups ))
5568 // this shouldn't really happen, but if it does, handle user
5569 // like an anonymous user
5573 if( SEC_inGroup( 'Root', $uid ))
5578 $result = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
5579 . COM_getPermSQL( 'WHERE', $uid ));
5581 while( $T = DB_fetchArray( $result ))
5583 $tids[] = $T['tid'];
5586 if( count( $tids ) > 0 )
5588 $topicsql .= "({$table}tid IN ('" . implode( "','", $tids ) . "'))";
5599 * Strip slashes from a string only when magic_quotes_gpc = on.
5601 * @param string $text The text
5602 * @return string The text, possibly without slashes.
5604 function COM_stripslashes( $text )
5606 if( get_magic_quotes_gpc() == 1 )
5608 return( stripslashes( $text ));
5615 * Filter parameters passed per GET (URL) or POST.
5617 * @param string $parameter the parameter to test
5618 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5619 * @return string the filtered parameter (may now be empty or 0)
5620 * @see COM_applyBasicFilter
5623 function COM_applyFilter( $parameter, $isnumeric = false )
5625 $p = COM_stripslashes($parameter);
5627 return COM_applyBasicFilter($p, $isnumeric);
5633 * NOTE: Use this function instead of COM_applyFilter for parameters
5634 * _not_ coming in through a GET or POST request.
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_applyFilter
5642 function COM_applyBasicFilter( $parameter, $isnumeric = false )
5644 $log_manipulation = false; // set to true to log when the filter applied
5646 $p = strip_tags( $parameter );
5647 $p = COM_killJS( $p ); // doesn't help a lot right now, but still ...
5651 // Note: PHP's is_numeric() accepts values like 4e4 as numeric
5652 if( !is_numeric( $p ) || ( preg_match( '/^-?\d+$/', $p ) == 0 ))
5659 $p = preg_replace( '/\/\*.*/', '', $p );
5660 $pa = explode( "'", $p );
5661 $pa = explode( '"', $pa[0] );
5662 $pa = explode( '`', $pa[0] );
5663 $pa = explode( ';', $pa[0] );
5664 $pa = explode( ',', $pa[0] );
5665 $pa = explode( '\\', $pa[0] );
5669 if( $log_manipulation )
5671 if( strcmp( $p, $parameter ) != 0 )
5673 COM_errorLog( "Filter applied: >> $parameter << filtered to $p [IP {$_SERVER['REMOTE_ADDR']}]", 1);
5683 * @param string $url URL to sanitized
5684 * @param array $allowed_protocols array of allowed protocols
5685 * @param string $default_protocol replacement protocol (default: http)
5686 * @return string sanitized URL
5689 function COM_sanitizeUrl( $url, $allowed_protocols = '', $default_protocol = '' )
5693 if( empty( $allowed_protocols ))
5695 $allowed_protocols = $_CONF['allowed_protocols'];
5697 else if( !is_array( $allowed_protocols ))
5699 $allowed_protocols = array( $allowed_protocols );
5702 if( empty( $default_protocol ))
5704 $default_protocol = 'http:';
5706 else if( substr( $default_protocol, -1 ) != ':' )
5708 $default_protocol .= ':';
5711 $url = strip_tags( $url );
5714 $pos = MBYTE_strpos( $url, ':' );
5715 if( $pos === false )
5717 $url = $default_protocol . '//' . $url;
5721 $protocol = MBYTE_substr( $url, 0, $pos + 1 );
5723 foreach( $allowed_protocols as $allowed )
5725 if( substr( $allowed, -1 ) != ':' )
5729 if( $protocol == $allowed )
5737 $url = $default_protocol . MBYTE_substr( $url, $pos + 1 );
5746 * Ensure an ID contains only alphanumeric characters, dots, dashes, or underscores
5748 * @param string $id the ID to sanitize
5749 * @param boolean $new_id true = create a new ID in case we end up with an empty string
5750 * @return string the sanitized ID
5752 function COM_sanitizeID( $id, $new_id = true )
5754 $id = str_replace( ' ', '', $id );
5755 $id = str_replace( array( '/', '\\', ':', '+' ), '-', $id );
5756 $id = preg_replace( '/[^a-zA-Z0-9\-_\.]/', '', $id );
5757 if( empty( $id ) && $new_id )
5759 $id = COM_makesid();
5766 * Sanitize a filename.
5768 * NOTE: This function is pretty strict in what it allows. Meant to be used
5769 * for files to be included where part of the filename is dynamic.
5771 * @param string $filename the filename to clean up
5772 * @param boolean $allow_dots whether to allow dots in the filename or not
5773 * @return string sanitized filename
5776 function COM_sanitizeFilename($filename, $allow_dots = false)
5779 $filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $filename);
5780 $filename = str_replace('..', '', $filename);
5782 $filename = preg_replace('/[^a-zA-Z0-9\-_]/', '', $filename);
5789 * Detect links in a plain-ascii text and turn them into clickable links.
5790 * Will detect links starting with "http:", "https:", "ftp:", and "www.".
5792 * @param string $text the (plain-ascii) text string
5793 * @return string the same string, with links enclosed in <a>...</a> tags
5796 function COM_makeClickableLinks( $text )
5800 if (! $_CONF['clickable_links']) {
5804 // These regular expressions will work for this purpuse, but
5805 // they should NOT be used for validating links.
5807 // matches anything starting with http:// or https:// or ftp:// or ftps://
5808 $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:(?:ht|f)tps?:\/{2})(?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&)?)+)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
5809 $replace[] = "COM_makeClickableLinksCallback('', '\\1')";
5811 // matches anything containing a top level domain: xxx.com or xxx.yyy.net/stuff.php or xxx.yyy.zz
5812 // list taken from: http://en.wikipedia.org/wiki/List_of_Internet_TLDs
5813 $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';
5814 $replace[] = "COM_makeClickableLinksCallback('http://', '\\1')";
5816 $text = preg_replace( $regex, $replace, $text );
5822 * Callback function to help format links in COM_makeClickableLinks
5824 * @param string $http set to 'http://' when not already in the url
5825 * @param string $link the url
5826 * @return string link enclosed in <a>...</a> tags
5829 function COM_makeClickableLinksCallback( $http, $link )
5831 $text = COM_truncate( $link, 50, '...', '10' );
5833 return "<a href=\"$http$link\">$text</a>";
5837 * Undo the conversion of URLs to clickable links (in plain text posts),
5838 * e.g. so that we can present the user with the post as they entered them.
5840 * @param string $text story text
5841 * @return string story text without links
5844 function COM_undoClickableLinks( $text )
5846 $text = preg_replace( '/<a href="([^"]*)">([^<]*)<\/a>/', '\1', $text );
5852 * Highlight the words from a search query in a given text string.
5854 * @param string $text the text
5855 * @param string $query the search query
5856 * @param string $class html class to use to highlight
5857 * @return string the text with highlighted search words
5860 function COM_highlightQuery( $text, $query, $class = 'highlight' )
5862 // escape PCRE special characters
5863 $query = preg_quote($query, '/');
5865 $mywords = explode(' ', $query);
5866 foreach ($mywords as $searchword)
5868 if (!empty($searchword))
5870 $before = "/(?!(?:[^<]+>|[^>]+<\/a>))\b";
5872 if ($searchword <> utf8_encode($searchword)) {
5873 if (@preg_match('/^\pL$/u', urldecode('%C3%B1'))) { // Unicode property support
5874 $before = "/(?<!\p{L})";
5875 $after = "(?!\p{L})/u";
5881 $text = preg_replace($before . $searchword . $after, "<span class=\"$class\">\\0</span>", '<!-- x -->' . $text . '<!-- x -->' );
5888 * Determines the difference between two dates.
5890 * This will takes either unixtimestamps or English dates as input and will
5891 * automatically do the date diff on the more recent of the two dates (e.g. the
5892 * order of the two dates given doesn't matter).
5894 * @author Tony Bibbs, tony DOT bibbs AT iowa DOT gov
5896 * @param string $interval Can be:
5903 * @param string|int $date1 English date (e.g. 10 Dec 2004) or unixtimestamp
5904 * @param string|int $date2 English date (e.g. 10 Dec 2004) or unixtimestamp
5905 * @return int Difference of the two dates in the unit of time indicated by the interval
5908 function COM_dateDiff( $interval, $date1, $date2 )
5910 // Convert dates to timestamps, if needed.
5911 if( !is_numeric( $date1 ))
5913 $date1 = strtotime( $date1 );
5916 if( !is_numeric( $date2 ))
5918 $date2 = strtotime( $date2 );
5921 // Function roughly equivalent to the ASP "DateDiff" function
5922 if( $date2 > $date1 )
5924 $seconds = $date2 - $date1;
5928 $seconds = $date1 - $date2;
5934 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5935 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5936 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5937 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5938 $diff = $year2 - $year1;
5939 if($month1 > $month2) {
5941 } elseif($month1 == $month2) {
5944 } elseif($day1 == $day2) {
5945 if($time1 > $time2) {
5952 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5953 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5954 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5955 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5956 $diff = ($year2 * 12 + $month2) - ($year1 * 12 + $month1);
5959 } elseif($day1 == $day2) {
5960 if($time1 > $time2) {
5966 // Only simple seconds calculation needed from here on
5967 $diff = floor($seconds / 604800);
5970 $diff = floor($seconds / 86400);
5973 $diff = floor($seconds / 3600);
5976 $diff = floor($seconds / 60);
5987 * Try to figure out our current URL, including all parameters.
5989 * This is an ugly hack since there's no single variable that returns what
5990 * we want and the variables used here may not be available on all servers
5993 * Seems to work on Apache (1.3.x and 2.x), IIS, and Zeus ...
5995 * @return string complete URL, e.g. 'http://www.example.com/blah.php?foo=bar'
5998 function COM_getCurrentURL()
6004 if( empty( $_SERVER['SCRIPT_URI'] ))
6006 if( !empty( $_SERVER['DOCUMENT_URI'] ))
6008 $thisUrl = $_SERVER['DOCUMENT_URI'];
6013 $thisUrl = $_SERVER['SCRIPT_URI'];
6015 if( !empty( $thisUrl ) && !empty( $_SERVER['QUERY_STRING'] ))
6017 $thisUrl .= '?' . $_SERVER['QUERY_STRING'];
6019 if( empty( $thisUrl ))
6021 $requestUri = $_SERVER['REQUEST_URI'];
6022 if( empty( $_SERVER['REQUEST_URI'] ))
6024 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6025 if( empty( $_SERVER['PATH_INFO'] ))
6027 $requestUri = $_SERVER['SCRIPT_NAME'];
6031 $requestUri = $_SERVER['PATH_INFO'];
6033 if( !empty( $_SERVER['QUERY_STRING'] ))
6035 $requestUri .= '?' . $_SERVER['QUERY_STRING'];
6039 $firstslash = strpos( $_CONF['site_url'], '/' );
6040 if( $firstslash === false )
6042 // special case - assume it's okay
6043 $thisUrl = $_CONF['site_url'] . $requestUri;
6045 else if( $firstslash + 1 == strrpos( $_CONF['site_url'], '/' ))
6047 // site is in the document root
6048 $thisUrl = $_CONF['site_url'] . $requestUri;
6052 // extract server name first
6053 $pos = strpos( $_CONF['site_url'], '/', $firstslash + 2 );
6054 $thisUrl = substr( $_CONF['site_url'], 0, $pos ) . $requestUri;
6062 * Check if we're on Geeklog's index page.
6064 * See if we're on the main index page (first page, no topics selected).
6066 * @return boolean true = we're on the frontpage, false = we're not
6069 function COM_onFrontpage()
6071 global $_CONF, $topic, $page, $newstories;
6073 // Note: We can't use $PHP_SELF here since the site may not be in the
6075 $onFrontpage = false;
6077 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6078 if( empty( $_SERVER['PATH_INFO'] ))
6080 $scriptName = $_SERVER['SCRIPT_NAME'];
6084 $scriptName = $_SERVER['PATH_INFO'];
6087 preg_match( '/\/\/[^\/]*(.*)/', $_CONF['site_url'], $pathonly );
6088 if(( $scriptName == $pathonly[1] . '/index.php' ) &&
6089 empty( $topic ) && ( $page == 1 ) && !$newstories )
6091 $onFrontpage = true;
6094 return $onFrontpage;
6098 * Check if we're on Geeklog's index page [deprecated]
6100 * Note that this function returns FALSE when we're on the index page. Due to
6101 * the inverted return values, it has been deprecated and is only provided for
6102 * backward compatibility - use COM_onFrontpage() instead.
6104 * @deprecated since Geeklog 1.4.1
6105 * @see COM_onFrontpage
6108 function COM_isFrontpage()
6110 return !COM_onFrontpage();
6114 * Converts a number for output into a formatted number with thousands-
6115 * separator, comma-separator and fixed decimals if necessary
6117 * @param float $number Number that will be formatted
6118 * @return string formatted number
6121 function COM_numberFormat( $number )
6125 if( $number - floor( $number ) > 0 ) // number has decimals
6127 $dc = $_CONF['decimal_count'];
6133 $ts = $_CONF['thousand_separator'];
6134 $ds = $_CONF['decimal_separator'];
6136 return number_format( $number, $dc, $ds, $ts );
6140 * Convert a text based date YYYY-MM-DD to a unix timestamp integer value
6142 * @param string $date Date in the format YYYY-MM-DD
6143 * @param string $time Option time in the format HH:MM::SS
6144 * @return int UNIX Timestamp
6146 function COM_convertDate2Timestamp( $date, $time = '' )
6151 // Breakup the string using either a space, fwd slash, dash, bkwd slash or
6152 // colon as a delimiter
6153 $atok = strtok( $date, ' /-\\:' );
6154 while( $atok !== FALSE )
6157 $atok = strtok( ' /-\\:' ); // get the next token
6160 for( $i = 0; $i < 3; $i++ )
6162 if( !isset( $atoks[$i] ) || !is_numeric( $atoks[$i] ))
6170 $timestamp = mktime( 0, 0, 0, $atoks[1], $atoks[2], $atoks[0] );
6174 $btok = strtok( $time, ' /-\\:' );
6175 while( $btok !== FALSE )
6178 $btok = strtok( ' /-\\:' );
6181 for( $i = 0; $i < 3; $i++ )
6183 if( !isset( $btoks[$i] ) || !is_numeric( $btoks[$i] ))
6189 $timestamp = mktime( $btoks[0], $btoks[1], $btoks[2],
6190 $atoks[1], $atoks[2], $atoks[0] );
6197 * Get the HTML for an image with height & width
6199 * @param string $file full path to the file
6200 * @return string html that will be included in the img-tag
6202 function COM_getImgSizeAttributes( $file )
6204 $sizeattributes = '';
6206 if( file_exists( $file ))
6208 $dimensions = getimagesize( $file );
6209 if( !empty( $dimensions[0] ) AND !empty( $dimensions[1] ))
6211 $sizeattributes = 'width="' . $dimensions[0]
6212 . '" height="' . $dimensions[1] . '" ';
6216 return $sizeattributes;
6220 * Display a message and abort
6222 * NOTE: Displays the message and aborts the script.
6224 * @param int $msg message number
6225 * @param string $plugin plugin name, if applicable
6226 * @param int $http_status HTTP status code to send with the message
6227 * @param string $http_text Textual version of the HTTP status code
6230 function COM_displayMessageAndAbort( $msg, $plugin = '', $http_status = 200, $http_text = 'OK')
6232 $display = COM_siteHeader( 'menu' )
6233 . COM_showMessage( $msg, $plugin )
6234 . COM_siteFooter( true );
6236 if( $http_status != 200 )
6238 header( "HTTP/1.1 $http_status $http_text" );
6239 header( "Status: $http_status $http_text" );
6246 * Return full URL of a topic icon
6248 * @param string $imageurl (relative) topic icon URL
6249 * @return string Full URL
6252 function COM_getTopicImageUrl( $imageurl )
6254 global $_CONF, $_THEME_URL;
6258 if( !empty( $imageurl ))
6260 if( isset( $_THEME_URL ))
6262 $iconurl = $_THEME_URL . $imageurl;
6266 $stdImageLoc = true;
6267 if( !strstr( $_CONF['path_images'], $_CONF['path_html'] ))
6269 $stdImageLoc = false;
6274 $iconurl = $_CONF['site_url'] . $imageurl;
6278 $t = explode( '/', $imageurl );
6279 $topicicon = $t[count( $t ) - 1];
6280 $iconurl = $_CONF['site_url']
6281 . '/getimage.php?mode=topics&image=' . $topicicon;
6290 * Create an HTML link
6292 * @param string $content the object to be linked (text, image etc)
6293 * @param string $url the URL the link will point to
6294 * @param array $attr an array of optional attributes for the link
6295 * for example array('title' => 'whatever');
6296 * @return string the HTML link
6298 function COM_createLink($content, $url, $attr = array())
6302 $attr_str = 'href="' . $url . '"';
6303 foreach ($attr as $key => $value) {
6304 $attr_str .= " $key=\"$value\"";
6306 $retval .= "<a $attr_str>$content</a>";
6312 * Create an HTML img
6314 * @param string $url the URL of the image, either starting with
6315 * http://... or $_CONF['layout_url'] is prepended
6316 * @param string $alt the 'alt'-tag of the image
6317 * @param array $attr an array of optional attributes for the link
6318 * for example array('title' => 'whatever');
6319 * @return string the HTML img
6321 function COM_createImage($url, $alt = "", $attr = array())
6327 if (preg_match("/^(https?):/", $url) !== 1) {
6328 $url = $_CONF['layout_url'] . $url;
6330 $attr_str = 'src="' . $url . '"';
6332 foreach ($attr as $key => $value) {
6333 $attr_str .= " $key=\"$value\"";
6336 $retval = "<img $attr_str alt=\"$alt\"" . XHTML . ">";
6342 * Try to determine the user's preferred language by looking at the
6343 * "Accept-Language" header sent by their browser (assuming they bothered
6344 * to select a preferred language there).
6346 * Sample header: Accept-Language: en-us,en;q=0.7,de-de;q=0.3
6348 * @return string name of the language file to use or an empty string
6349 * @todo Bugs: Does not take the quantity ('q') parameter into account,
6350 * but only looks at the order of language codes.
6353 function COM_getLanguageFromBrowser()
6359 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
6360 $accept = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
6361 foreach ($accept as $l) {
6362 $l = explode(';', trim($l));
6364 if (array_key_exists($l, $_CONF['language_files'])) {
6365 $retval = $_CONF['language_files'][$l];
6368 $l = explode('-', $l);
6370 if (array_key_exists($l, $_CONF['language_files'])) {
6371 $retval = $_CONF['language_files'][$l];
6382 * Determine current language
6384 * @return string name of the language file (minus the '.php' extension)
6387 function COM_getLanguage()
6389 global $_CONF, $_USER;
6393 if (!empty($_USER['language'])) {
6394 $langfile = $_USER['language'];
6395 } elseif (!empty($_COOKIE[$_CONF['cookie_language']])) {
6396 $langfile = $_COOKIE[$_CONF['cookie_language']];
6397 } elseif (isset($_CONF['languages'])) {
6398 $langfile = COM_getLanguageFromBrowser();
6401 $langfile = COM_sanitizeFilename($langfile);
6402 if (!empty($langfile)) {
6403 if (is_file($_CONF['path_language'] . $langfile . '.php')) {
6408 // if all else fails, return the default language
6409 return $_CONF['language'];
6413 * Determine the ID to use for the current language
6415 * The $_CONF['language_files'] array maps language IDs to language file names.
6416 * This function returns the language ID for a certain language file, to be
6417 * used in language-dependent URLs.
6419 * @param string $language current language file name (optional)
6420 * @return string language ID, e.g 'en'; empty string on error
6423 function COM_getLanguageId($language = '')
6427 if (empty($language)) {
6428 $language = COM_getLanguage();
6432 if (isset($_CONF['language_files'])) {
6433 $lang_id = array_search($language, $_CONF['language_files']);
6435 if ($lang_id === false) {
6436 // that looks like a misconfigured $_CONF['language_files'] array
6437 COM_errorLog('Language "' . $language . '" not found in $_CONF[\'language_files\'] array!');
6439 $lang_id = ''; // not much we can do here ...
6447 * Return SQL expression to request language-specific content
6449 * Creates part of an SQL expression that can be used to request items in the
6450 * current language only.
6452 * @param string $field name of the "id" field, e.g. 'sid' for stories
6453 * @param string $type part of the SQL expression, e.g. 'WHERE', 'AND'
6454 * @param string $table table name if ambiguous, e.g. in JOINs
6455 * @return string SQL expression string (may be empty)
6458 function COM_getLangSQL( $field, $type = 'WHERE', $table = '' )
6464 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6466 if( !empty( $table ))
6471 $lang_id = COM_getLanguageId();
6473 if( !empty( $lang_id ))
6475 $sql = ' ' . $type . " ({$table}$field LIKE '%\\_$lang_id')";
6483 * Provide a block to switch languages
6485 * Provides a drop-down menu (or simple link, if you only have two languages)
6486 * to switch languages. This can be used as a PHP block or called from within
6487 * your theme's header.thtml:
6489 * <?php print phpblock_switch_language(); ?>
6492 * @return string HTML for drop-down or link to switch languages
6495 function phpblock_switch_language()
6501 if( empty( $_CONF['languages'] ) || empty( $_CONF['language_files'] ) ||
6502 ( count( $_CONF['languages'] ) != count( $_CONF['language_files'] )))
6507 $lang = COM_getLanguage();
6508 $langId = COM_getLanguageId( $lang );
6510 if( count( $_CONF['languages'] ) == 2 )
6512 foreach( $_CONF['languages'] as $key => $value )
6514 if( $key != $langId )
6522 $switchUrl = COM_buildUrl( $_CONF['site_url'] . '/switchlang.php?lang='
6524 $retval .= COM_createLink($newLang, $switchUrl);
6528 $retval .= '<form name="change" action="'. $_CONF['site_url']
6529 . '/switchlang.php" method="get">' . LB;
6530 $retval .= '<input type="hidden" name="oldlang" value="' . $langId
6531 . '"' . XHTML . '>' . LB;
6533 $retval .= '<select onchange="change.submit()" name="lang">';
6534 foreach( $_CONF['languages'] as $key => $value )
6536 if( $lang == $_CONF['language_files'][$key] )
6538 $selected = ' selected="selected"';
6544 $retval .= '<option value="' . $key . '"' . $selected . '>'
6545 . $value . '</option>' . LB;
6547 $retval .= '</select>' . LB;
6548 $retval .= '</form>' . LB;
6555 * Switch locale settings
6557 * When multi-language support is enabled, allow overwriting the default locale
6558 * settings with language-specific settings (date format, etc.). So in addition
6559 * to $_CONF['date'] you can have a $_CONF['date_en'], $_CONF['date_de'], etc.
6562 function COM_switchLocaleSettings()
6566 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6568 $overridables = array
6571 'date', 'daytime', 'shortdate', 'dateonly', 'timeonly',
6572 'week_start', 'hour_mode',
6573 'thousand_separator', 'decimal_separator'
6576 $langId = COM_getLanguageId();
6577 foreach( $overridables as $option )
6579 if( isset( $_CONF[$option . '_' . $langId] ))
6581 $_CONF[$option] = $_CONF[$option . '_' . $langId];
6588 * Get the name of the current language, minus the character set
6590 * Strips the character set from $_CONF['language'].
6592 * @return string language name
6595 function COM_getLanguageName()
6601 $charset = '_' . strtolower(COM_getCharset());
6602 if (substr($_CONF['language'], -strlen($charset)) == $charset) {
6603 $retval = substr($_CONF['language'], 0, -strlen($charset));
6605 $retval = $_CONF['language'];
6614 * Truncates a string to a max. length and optionally adds a filler string,
6615 * e.g. '...', to indicate the truncation.
6616 * This function is multi-byte string aware, based on a patch by Yusuke Sakata.
6618 * NOTE: The truncated string may be shorter but will never be longer than
6619 * $maxlen characters, i.e. the $filler string is taken into account.
6621 * @param string $text the text string to truncate
6622 * @param int $maxlen max. number of characters in the truncated string
6623 * @param string $filler optional filler string, e.g. '...'
6624 * @param int $endchars number of characters to show after the filler
6625 * @return string truncated string
6628 function COM_truncate( $text, $maxlen, $filler = '', $endchars = 0 )
6630 $newlen = $maxlen - MBYTE_strlen( $filler );
6631 $len = MBYTE_strlen( $text );
6632 if( $len > $maxlen )
6634 $text = MBYTE_substr( $text, 0, $newlen - $endchars ) . $filler . MBYTE_substr( $text, $len - $endchars, $endchars );
6641 * Get the current character set
6643 * Uses (if available, and in this order)
6644 * - $LANG_CHARSET (from the current language file)
6645 * - $_CONF['default_charset'] (from siteconfig.php)
6646 * - 'iso-8859-1' (hard-coded fallback)
6648 * @return string character set, e.g. 'utf-8'
6651 function COM_getCharset()
6653 global $_CONF, $LANG_CHARSET;
6655 if( empty( $LANG_CHARSET )) {
6656 $charset = $_CONF['default_charset'];
6657 if( empty( $charset )) {
6658 $charset = 'iso-8859-1';
6661 $charset = $LANG_CHARSET;
6670 * This function will handle all PHP errors thrown at it, without exposing
6671 * paths, and hopefully, providing much more information to Root Users than
6672 * the default white error page.
6674 * This function will call out to CUSTOM_handleError if it exists, but, be
6675 * advised, only override this function with a very, very stable function. I'd
6676 * suggest one that outputs some static, basic HTML.
6678 * The PHP feature that allows us to do so is documented here:
6679 * http://uk2.php.net/manual/en/function.set-error-handler.php
6681 * @param int $errno Error Number.
6682 * @param string $errstr Error Message.
6683 * @param string $errfile The file the error was raised in.
6684 * @param int $errline The line of the file that the error was raised at.
6685 * @param array $errcontext An array that points to the active symbol table at the point the error occurred.
6687 function COM_handleError($errno, $errstr, $errfile='', $errline=0, $errcontext='')
6689 global $_CONF, $_USER;
6691 // Handle @ operator
6692 if (error_reporting() == 0) {
6696 // If in PHP4, then respect error_reporting
6697 if ((PHP_VERSION < 5) && (($errno & error_reporting()) == 0)) {
6702 * If we have a root user, then output detailed error message:
6704 if ((is_array($_USER) && function_exists('SEC_inGroup'))
6705 || (isset($_CONF['rootdebug']) && $_CONF['rootdebug'])) {
6706 if ($_CONF['rootdebug'] || SEC_inGroup('Root')) {
6708 header('HTTP/1.1 500 Internal Server Error');
6709 header('Status: 500 Internal Server Error');
6711 $title = 'An Error Occurred';
6712 if (!empty($_CONF['site_name'])) {
6713 $title = $_CONF['site_name'] . ' - ' . $title;
6715 echo("<html><head><title>$title</title></head>\n<body>\n");
6717 echo('<h1>An error has occurred:</h1>');
6718 if ($_CONF['rootdebug']) {
6719 echo('<h2 style="color: red">This is being displayed as "Root Debugging" is enabled
6720 in your Geeklog configuration.</h2><p>If this is a production
6721 website you <strong><em>must disable</em></strong> this
6722 option once you have resolved any issues you are
6723 investigating.</p>');
6725 echo('<p>(This text is only displayed to users in the group \'Root\')</p>');
6727 echo("<p>$errno - $errstr @ $errfile line $errline</p>");
6729 if (!function_exists('SEC_inGroup') || !SEC_inGroup('Root')) {
6730 if ('force' != ''.$_CONF['rootdebug']) {
6731 $errcontext = COM_rootDebugClean($errcontext);
6733 echo('<h2 style="color: red">Root Debug is set to "force", this
6734 means that passwords and session cookies are exposed in this
6740 var_dump($errcontext);
6741 $errcontext = htmlspecialchars(ob_get_contents());
6743 echo("$errcontext</pre></body></html>");
6748 /* If there is a custom error handler, fail over to that, but only
6749 * if the error wasn't in lib-custom.php
6751 if (is_array($_CONF) && !(strstr($errfile, 'lib-custom.php'))) {
6752 if (array_key_exists('path_system', $_CONF)) {
6753 if (file_exists($_CONF['path_system'] . 'lib-custom.php')) {
6754 require_once $_CONF['path_system'] . 'lib-custom.php';
6756 if (function_exists('CUSTOM_handleError')) {
6757 CUSTOM_handleError($errno, $errstr, $errfile, $errline, $errcontext);
6763 // if we do not throw the error back to an admin, still log it in the error.log
6764 COM_errorLog("$errno - $errstr @ $errfile line $errline", 1);
6766 header('HTTP/1.1 500 Internal Server Error');
6767 header('Status: 500 Internal Server Error');
6769 // Does the theme implement an error message html file?
6770 if (!empty($_CONF['path_layout']) &&
6771 file_exists($_CONF['path_layout'] . 'errormessage.html')) {
6772 // NOTE: NOT A TEMPLATE! JUST HTML!
6773 include $_CONF['path_layout'] . 'errormessage.html';
6775 // Otherwise, display simple error message
6776 $title = 'An Error Occurred';
6777 if (!empty($_CONF['site_name'])) {
6778 $title = $_CONF['site_name'] . ' - ' . $title;
6783 <title>{$title}</title>
6786 <div style=\"width: 100%; text-align: center;\">
6787 Unfortunately, an error has occurred rendering this page. Please try
6799 * Recurse through the error context array removing/blanking password/cookie
6800 * values in case the "for development" only switch is left on in a production
6803 * [Not fit for public consumption comments about what users who enable root
6804 * debug in production should have done to them, and why making this change
6805 * defeats the point of the entire root debug feature go here.]
6807 * @param array $array Array of state info (Recursive array).
6808 * @param boolean $blank override (wouldn't that blank out everything?)
6809 * @return array Cleaned array
6811 function COM_rootDebugClean($array, $blank=false)
6813 $blankField = false;
6814 while(list($key, $value) = each($array)) {
6815 $lkey = strtolower($key);
6816 if((strpos($lkey, 'pass') !== false) || (strpos($lkey, 'cookie') !== false)) {
6819 $blankField = $blank;
6821 if(is_array($value)) {
6822 $array[$key] = COM_rootDebugClean($value, $blankField);
6823 } elseif($blankField) {
6824 $array[$key] = '[VALUE REMOVED]';
6831 * Checks to see if a specified user, or the current user if non-specified
6832 * is the anonymous user.
6834 * @param int $uid ID of the user to check, or none for the current user.
6835 * @return boolean true if the user is the anonymous user.
6837 function COM_isAnonUser($uid = '')
6841 /* If no user was specified, fail over to the current user if there is one */
6844 if( isset( $_USER['uid'] ) )
6846 $uid = $_USER['uid'];
6850 if( !empty( $uid ) )
6859 * Create Meta Tags to be used by COM_siteHeader in the headercode variable
6861 * @param string $meta_description the text for the meta description of the page being displayed
6862 * @param string $meta_keywords the text for the meta keywords of the page being displayed
6863 * @return string XHTML formatted text
6866 function COM_createMetaTags($meta_description, $meta_keywords)
6872 If ($_CONF['meta_tags'] > 0) {
6873 if ($meta_description != '') {
6874 $headercode .= LB . '<meta name="description" content="' . $meta_description . '"' . XHTML . '>';
6876 if ($meta_keywords != '') {
6877 $headercode .= LB . '<meta name="keywords" content="' . $meta_keywords . '"' . XHTML . '>';
6887 * Convert wiki-formatted text to (X)HTML
6889 * @param string $wikitext wiki-formatted text
6890 * @return string XHTML formatted text
6893 function COM_renderWikiText($wikitext)
6897 if (!$_CONF['wikitext_editor']) {
6901 require_once 'Text/Wiki.php';
6903 $wiki = new Text_Wiki();
6904 $wiki->setFormatConf('Xhtml', 'translate', HTML_SPECIALCHARS);
6905 $wiki->setRenderConf('Xhtml', 'charset', COM_getCharset());
6906 $wiki->disableRule('wikilink');
6907 $wiki->disableRule('freelink');
6908 $wiki->disableRule('interwiki');
6910 return $wiki->transform($wikitext, 'Xhtml');
6914 * Set the {lang_id} and {lang_attribute} variables for a template
6916 * NOTE: {lang_attribute} is only set in multi-language environments.
6918 * @param ref &$template template to use
6922 function COM_setLangIdAndAttribute(&$template)
6929 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6930 $langId = COM_getLanguageId();
6932 // try to derive the language id from the locale
6933 $l = explode('.', $_CONF['locale']); // get rid of character set
6935 $l = explode('@', $langId); // get rid of '@euro', etc.
6939 if (!empty($langId)) {
6940 $l = explode('-', str_replace('_', '-', $langId));
6941 if ((count($l) == 1) && (strlen($langId) == 2)) {
6942 $langAttr = 'lang="' . $langId . '"';
6943 } else if (count($l) == 2) {
6944 if (($l[0] == 'i') || ($l[0] == 'x')) {
6945 $langId = implode('-', $l);
6946 $langAttr = 'lang="' . $langId . '"';
6947 } else if (strlen($l[0]) == 2) {
6948 $langId = implode('-', $l);
6949 $langAttr = 'lang="' . $langId . '"';
6952 // this isn't a valid lang attribute, so don't set $langAttr
6956 $template->set_var('lang_id', $langId);
6958 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6959 $template->set_var('lang_attribute', ' ' . $langAttr);
6961 $template->set_var('lang_attribute', '');
6966 * Sends compressed output to browser.
6968 * Assumes that $display contains the _entire_ output for a request - no
6969 * echoes are allowed before or after this function.
6970 * Currently only supports gzip compression. Checks if zlib compression is
6971 * enabled in PHP and does uncompressed output if it is.
6973 * @param string $display Content to send to browser
6977 function COM_output($display)
6981 if (empty($display)) {
6985 if ($_CONF['compressed_output']) {
6986 $gzip_accepted = false;
6987 if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
6988 $enc = str_replace(' ', '', $_SERVER['HTTP_ACCEPT_ENCODING']);
6989 $accept = explode(',', strtolower($enc));
6990 $gzip_accepted = in_array('gzip', $accept);
6993 if ($gzip_accepted && function_exists('gzencode')) {
6995 $zlib_comp = ini_get('zlib.output_compression');
6996 if (empty($zlib_comp) || (strcasecmp($zlib_comp, 'off') == 0)) {
6998 header('Content-encoding: gzip');
6999 echo gzencode($display);
7010 * Turn a piece of HTML into continuous(!) plain text
7012 * This function removes HTML tags, line breaks, etc. and returns one long
7013 * line of text. This is useful for word counts (do an explode() on the result)
7014 * and for text excerpts.
7016 * @param string $text original text, including HTML and line breaks
7017 * @return string continuous plain text
7020 function COM_getTextContent($text)
7022 // replace <br> with spaces so that Text<br>Text becomes two words
7023 $text = preg_replace('/\<br(\s*)?\/?\>/i', ' ', $text);
7025 // add extra space between tags, e.g. <p>Text</p><p>Text</p>
7026 $text = str_replace('><', '> <', $text);
7028 // only now remove all HTML tags
7029 $text = strip_tags($text);
7031 // replace all tabs, newlines, and carrriage returns with spaces
7032 $text = str_replace(array("\011", "\012", "\015"), ' ', $text);
7034 // replace entities with plain spaces
7035 $text = str_replace(array('', ' ', ' '), ' ', $text);
7037 // collapse whitespace
7038 $text = preg_replace('/\s\s+/', ' ', $text);
7044 * Now include all plugin functions
7046 foreach ($_PLUGINS as $pi_name) {
7047 require_once $_CONF['path'] . 'plugins/' . $pi_name . '/functions.inc';
7050 // Check and see if any plugins (or custom functions)
7051 // have scheduled tasks to perform
7052 if ($_CONF['cron_schedule_interval'] > 0) {
7053 if ((DB_getItem($_TABLES['vars'], 'value', "name='last_scheduled_run'")
7054 + $_CONF['cron_schedule_interval']) <= time()) {
7055 DB_query("UPDATE {$_TABLES['vars']} SET value=UNIX_TIMESTAMP() WHERE name='last_scheduled_run'");
7056 PLG_runScheduledTask();