Modernized the "timezone hack", made the config option a dropdown, and moved all timezone-related code into a new TimeZoneConfig class
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' );
241 * Ulf Harnhammar's kses class
245 require_once( $_CONF['path_system'] . 'classes/kses.class.php' );
248 * Multibyte functions
251 require_once( $_CONF['path_system'] . 'lib-mbyte.php' );
256 if( isset( $_POST['usetheme'] ))
258 $usetheme = COM_sanitizeFilename($_POST['usetheme'], true);
260 if( !empty( $usetheme ) && is_dir( $_CONF['path_themes'] . $usetheme ))
262 $_CONF['theme'] = $usetheme;
263 $_CONF['path_layout'] = $_CONF['path_themes'] . $_CONF['theme'] . '/';
264 $_CONF['layout_url'] = $_CONF['site_url'] . '/layout/' . $_CONF['theme'];
266 else if( $_CONF['allow_user_themes'] == 1 )
268 if( isset( $_COOKIE[$_CONF['cookie_theme']] ) && empty( $_USER['theme'] ))
270 $theme = COM_sanitizeFilename($_COOKIE[$_CONF['cookie_theme']], true);
271 if( is_dir( $_CONF['path_themes'] . $theme ))
273 $_USER['theme'] = $theme;
277 if( !empty( $_USER['theme'] ))
279 if( is_dir( $_CONF['path_themes'] . $_USER['theme'] ))
281 $_CONF['theme'] = $_USER['theme'];
282 $_CONF['path_layout'] = $_CONF['path_themes'] . $_CONF['theme'] . '/';
283 $_CONF['layout_url'] = $_CONF['site_url'] . '/layout/' . $_CONF['theme'];
287 $_USER['theme'] = $_CONF['theme'];
293 * Include theme functions file
296 // Include theme functions file which may/may not do anything
298 if (file_exists($_CONF['path_layout'] . 'functions.php')) {
299 require_once $_CONF['path_layout'] . 'functions.php';
303 * ensure XHTML constant is defined to avoid problems elsewhere
305 if (!defined('XHTML')) {
306 switch ($_CONF['doctype']) {
307 case 'xhtml10transitional':
308 case 'xhtml10strict':
309 define('XHTML', ' /');
321 // themes can now specify the default image type
322 // fall back to 'gif' if they don't
324 if (empty($_IMAGE_TYPE)) {
325 $_IMAGE_TYPE = 'gif';
328 // Similarly set language
330 if( isset( $_COOKIE[$_CONF['cookie_language']] ) && empty( $_USER['language'] ))
332 $language = COM_sanitizeFilename($_COOKIE[$_CONF['cookie_language']]);
333 if( is_file( $_CONF['path_language'] . $language . '.php' ) &&
334 ( $_CONF['allow_user_language'] == 1 ))
336 $_USER['language'] = $language;
337 $_CONF['language'] = $language;
340 else if( !empty( $_USER['language'] ))
342 if( is_file( $_CONF['path_language'] . $_USER['language'] . '.php' ) &&
343 ( $_CONF['allow_user_language'] == 1 ))
345 $_CONF['language'] = $_USER['language'];
348 else if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
350 $_CONF['language'] = COM_getLanguage();
353 // Handle Who's Online block
354 if (COM_isAnonUser() && isset($_SERVER['REMOTE_ADDR'])) {
355 // The following code handles anonymous users so they show up properly
356 DB_delete($_TABLES['sessions'], array('remote_ip', 'uid'),
357 array($_SERVER['REMOTE_ADDR'], 1));
362 // Build a useless sess_id (needed for insert to work properly)
363 $sess_id = mt_rand();
366 // Insert anonymous user session
367 $result = DB_query( "INSERT INTO {$_TABLES['sessions']} (sess_id, start_time, remote_ip, uid) VALUES ($sess_id, $curtime, '{$_SERVER['REMOTE_ADDR']}', 1)", 1 );
370 while(( $result === false) && ( $tries < 5 ));
373 // Clear out any expired sessions
374 DB_query( "DELETE FROM {$_TABLES['sessions']} WHERE start_time < " . ( time() - $_CONF['whosonline_threshold'] ));
382 require_once $_CONF['path_language'] . $_CONF['language'] . '.php';
384 if (empty($LANG_DIRECTION)) {
385 // default to left-to-right
386 $LANG_DIRECTION = 'ltr';
389 COM_switchLocaleSettings();
391 if( setlocale( LC_ALL, $_CONF['locale'] ) === false )
393 setlocale( LC_TIME, $_CONF['locale'] );
397 * Global array of groups current user belongs to
399 * @global array $_GROUPS
403 if( !COM_isAnonUser() )
405 $_GROUPS = SEC_getUserGroups( $_USER['uid'] );
409 $_GROUPS = SEC_getUserGroups( 1 );
413 * Global array of current user permissions [read,edit]
415 * @global array $_RIGHTS
419 $_RIGHTS = explode( ',', SEC_getUserPermissions() );
421 if( isset( $_GET['topic'] ))
423 $topic = COM_applyFilter( $_GET['topic'] );
425 else if( isset( $_POST['topic'] ))
427 $topic = COM_applyFilter( $_POST['topic'] );
435 // +---------------------------------------------------------------------------+
437 // +---------------------------------------------------------------------------+
440 * Return the file to use for a block template.
442 * This returns the template needed to build the HTML for a block. This function
443 * allows designers to give a block it's own custom look and feel. If no
444 * templates for the block are specified, the default blockheader.html and
445 * blockfooter.html will be used.
447 * @param string $blockname corresponds to name field in block table
448 * @param string $which can be either 'header' or 'footer' for corresponding template
449 * @param string $position can be 'left', 'right' or blank. If set, will be used to find a side specific override template.
450 * @see function COM_startBlock
451 * @see function COM_endBlock
452 * @see function COM_showBlocks
453 * @see function COM_showBlock
454 * @return string template name
456 function COM_getBlockTemplate( $blockname, $which, $position='' )
458 global $_BLOCK_TEMPLATE, $_COM_VERBOSE, $_CONF;
462 COM_errorLog( "_BLOCK_TEMPLATE[$blockname] = " . $_BLOCK_TEMPLATE[$blockname], 1 );
465 if( !empty( $_BLOCK_TEMPLATE[$blockname] ))
467 $templates = explode( ',', $_BLOCK_TEMPLATE[$blockname] );
468 if( $which == 'header' )
470 if( !empty( $templates[0] ))
472 $template = $templates[0];
476 $template = 'blockheader.thtml';
481 if( !empty( $templates[1] ))
483 $template = $templates[1];
487 $template = 'blockfooter.thtml';
493 if( $which == 'header' )
495 $template = 'blockheader.thtml';
499 $template = 'blockfooter.thtml';
503 // If we have a position specific request, and the template is not already
504 // position specific then look to see if there is a position specific
506 $templateLC = strtolower($template);
507 if( !empty($position) && ( strpos($templateLC, $position) === false ) )
509 // Trim .thtml from the end.
510 $positionSpecific = substr($template, 0, strlen($template) - 6);
511 $positionSpecific .= '-' . $position . '.thtml';
512 if( file_exists( $_CONF['path_layout'] . $positionSpecific ) )
514 $template = $positionSpecific;
520 COM_errorLog( "Block template for the $which of $blockname is: $template", 1 );
527 * Gets all installed themes
529 * Returns a list of all the directory names in $_CONF['path_themes'], i.e.
530 * a list of all the theme names.
532 * @param boolean $all if true, return all themes even if users aren't allowed to change their default themes
533 * @return array All installed themes
536 function COM_getThemes( $all = false )
544 // If users aren't allowed to change their theme then only return the default theme
546 if(( $_CONF['allow_user_themes'] == 0 ) && !$all )
548 $themes[$index] = $_CONF['theme'];
552 $fd = opendir( $_CONF['path_themes'] );
554 while(( $dir = @readdir( $fd )) == TRUE )
556 if( is_dir( $_CONF['path_themes'] . $dir) && $dir <> '.' && $dir <> '..' && $dir <> 'CVS' && substr( $dir, 0 , 1 ) <> '.' )
559 $themes[$index] = $dir;
569 * Create the menu, i.e. replace {menu_elements} in the site header with the
570 * actual menu entries.
572 * @param Template &$header reference to the header template
573 * @param array $plugin_menu array of plugin menu entries, if any
576 function COM_renderMenu( &$header, $plugin_menu )
578 global $_CONF, $_USER, $LANG01, $topic;
580 if( empty( $_CONF['menu_elements'] ))
582 $_CONF['menu_elements'] = array( // default set of links
583 'contribute', 'search', 'stats', 'directory', 'plugins' );
586 $anon = COM_isAnonUser();
591 $num_plugins = count( $plugin_menu );
592 if( ( $num_plugins == 0 ) && in_array( 'plugins', $_CONF['menu_elements'] ))
594 $key = array_search( 'plugins', $_CONF['menu_elements'] );
595 unset( $_CONF['menu_elements'][$key] );
598 if( in_array( 'custom', $_CONF['menu_elements'] ))
600 $custom_entries = array();
601 if( function_exists( 'CUSTOM_menuEntries' ))
603 $custom_entries = CUSTOM_menuEntries();
605 if( count( $custom_entries ) == 0 )
607 $key = array_search( 'custom', $_CONF['menu_elements'] );
608 unset( $_CONF['menu_elements'][$key] );
612 $num_elements = count( $_CONF['menu_elements'] );
614 foreach( $_CONF['menu_elements'] as $item )
618 $last_entry = ( $counter == $num_elements ) ? true : false;
625 $url = $_CONF['site_url'] . '/submit.php?type=story';
626 $header->set_var( 'current_topic', '' );
630 $url = $_CONF['site_url']
631 . '/submit.php?type=story&topic=' . $topic;
632 $header->set_var( 'current_topic', '&topic=' . $topic );
634 $label = $LANG01[71];
635 if( $anon && ( $_CONF['loginrequired'] ||
636 $_CONF['submitloginrequired'] ))
643 if (function_exists('CUSTOM_renderMenu')) {
644 CUSTOM_renderMenu($header, $custom_entries, $menuCounter);
647 $custom_size = count($custom_entries);
648 foreach ($custom_entries as $entry) {
651 if (empty($entry['url']) || empty($entry['label'])) {
655 $header->set_var('menuitem_url', $entry['url']);
656 $header->set_var('menuitem_text', $entry['label']);
658 if ($last_entry && ($custom_count == $custom_size)) {
659 $header->parse('menu_elements', 'menuitem_last',
662 $header->parse('menu_elements', 'menuitem', true);
672 $url = $_CONF['site_url'] . '/directory.php';
673 if( !empty( $topic ))
675 $url = COM_buildUrl( $url . '?topic='
676 . urlencode( $topic ));
678 $label = $LANG01[117];
679 if( $anon && ( $_CONF['loginrequired'] ||
680 $_CONF['directoryloginrequired'] ))
687 $url = $_CONF['site_url'] . '/';
688 $label = $LANG01[90];
692 for( $i = 1; $i <= $num_plugins; $i++ )
694 $header->set_var( 'menuitem_url', current( $plugin_menu ));
695 $header->set_var( 'menuitem_text', key( $plugin_menu ));
697 if( $last_entry && ( $i == $num_plugins ))
699 $header->parse( 'menu_elements', 'menuitem_last',
704 $header->parse( 'menu_elements', 'menuitem', true );
708 next( $plugin_menu );
715 $url = $_CONF['site_url'] . '/usersettings.php';
716 $label = $LANG01[48];
720 $url = $_CONF['site_url'] . '/search.php';
721 $label = $LANG01[75];
722 if( $anon && ( $_CONF['loginrequired'] ||
723 $_CONF['searchloginrequired'] ))
730 $url = $_CONF['site_url'] . '/stats.php';
731 $label = $LANG01[76];
733 ( $_CONF['loginrequired'] || $_CONF['statsloginrequired'] ))
739 default: // unknown entry
745 if( !empty( $url ) && !empty( $label ))
747 $header->set_var( 'menuitem_url', $url );
748 $header->set_var( 'menuitem_text', $label );
751 $header->parse( 'menu_elements', 'menuitem_last', true );
755 $header->parse( 'menu_elements', 'menuitem', true );
763 $header->parse( 'allowed_menu_elements', 'menuitem_last',
768 $header->parse( 'allowed_menu_elements', 'menuitem', true );
775 if( $menuCounter == 0 )
777 $header->parse( 'menu_elements', 'menuitem_none', true );
779 if( $allowedCounter == 0 )
781 $header->parse( 'allowed_menu_elements', 'menuitem_none', true );
786 * Returns the site header
788 * This loads the proper templates, does variable substitution and returns the
789 * HTML for the site header with or without blocks depending on the value of $what
793 * The two functions COM_siteHeader and COM_siteFooter provide the framework for
794 * page display in Geeklog. COM_siteHeader controls the display of the Header
795 * and left blocks and COM_siteFooter controls the dsiplay of the right blocks
796 * and the footer. You use them like a sandwich. Thus the following code will
797 * display a Geeklog page with both right and left blocks displayed.
801 * require_once 'lib-common.php';
802 * // Change to COM_siteHeader('none') to not display left blocks
803 * $display .= COM_siteHeader();
804 * $display .= "Here is your html for display";
805 * // Change to COM_siteFooter() to not display right blocks
806 * $display .= COM_siteFooter(true);
811 * Note that the default for the header is to display the left blocks and the
812 * default of the footer is to not display the right blocks.
814 * This sandwich produces code like this (greatly simplified)
817 * <table><tr><td colspan="3">Header</td></tr>
818 * <tr><td>Left Blocks</td><td>
820 * // Your HTML goes here
821 * Here is your html for display
824 * </td><td>Right Blocks</td></tr>
825 * <tr><td colspan="3">Footer</td></table>
828 * @param string $what If 'none' then no left blocks are returned, if 'menu' (default) then right blocks are returned
829 * @param string $pagetitle optional content for the page's <title>
830 * @param string $headercode optional code to go into the page's <head>
831 * @return string Formatted HTML containing the site header
832 * @see function COM_siteFooter
835 function COM_siteHeader( $what = 'menu', $pagetitle = '', $headercode = '' )
837 global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_BUTTONS, $LANG_DIRECTION,
838 $_IMAGE_TYPE, $topic, $_COM_VERBOSE;
840 // If the theme implemented this for us then call their version instead.
842 $function = $_CONF['theme'] . '_siteHeader';
844 if( function_exists( $function ))
846 return $function( $what, $pagetitle, $headercode );
849 // If we reach here then either we have the default theme OR
850 // the current theme only needs the default variable substitutions
852 switch ($_CONF['doctype']) {
853 case 'html401transitional':
854 $doctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
857 case 'html401strict':
858 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
861 case 'xhtml10transitional':
862 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
865 case 'xhtml10strict':
866 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
869 default: // fallback: HTML 4.01 Transitional w/o system identifier
870 $doctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">';
874 // send out the charset header
875 header('Content-Type: text/html; charset=' . COM_getCharset());
877 if (!empty($_CONF['frame_options'])) {
878 header('X-FRAME-OPTIONS: ' . $_CONF['frame_options']);
881 $header = new Template( $_CONF['path_layout'] );
882 $header->set_file( array(
883 'header' => 'header.thtml',
884 'menuitem' => 'menuitem.thtml',
885 'menuitem_last' => 'menuitem_last.thtml',
886 'menuitem_none' => 'menuitem_none.thtml',
887 'leftblocks' => 'leftblocks.thtml',
888 'rightblocks' => 'rightblocks.thtml'
890 $header->set_var('doctype', $doctype);
891 $header->set_var('xhtml', XHTML);
893 $header->set_var('xmlns', '');
895 $header->set_var('xmlns', ' xmlns="http://www.w3.org/1999/xhtml"');
898 // get topic if not on home page
899 if( !isset( $_GET['topic'] ))
901 if( isset( $_GET['story'] ))
903 $sid = COM_applyFilter( $_GET['story'] );
905 elseif( isset( $_GET['sid'] ))
907 $sid = COM_applyFilter( $_GET['sid'] );
909 elseif( isset( $_POST['story'] ))
911 $sid = COM_applyFilter( $_POST['story'] );
913 if( empty( $sid ) && $_CONF['url_rewrite'] &&
914 ( strpos( $_SERVER['PHP_SELF'], 'article.php' ) !== false ))
916 COM_setArgNames( array( 'story', 'mode' ));
917 $sid = COM_applyFilter( COM_getArgument( 'story' ));
921 $topic = DB_getItem( $_TABLES['stories'], 'tid', "sid='$sid'" );
926 $topic = COM_applyFilter( $_GET['topic'] );
930 if( $_CONF['backend'] == 1 ) // add feed-link to header if applicable
932 $baseurl = SYND_getFeedUrl();
934 $sql = 'SELECT format, filename, title, language FROM '
935 . $_TABLES['syndication'] . " WHERE (header_tid = 'all')";
936 if( !empty( $topic ))
938 $sql .= " OR (header_tid = '" . addslashes( $topic ) . "')";
940 $result = DB_query( $sql );
941 $numRows = DB_numRows( $result );
942 for( $i = 0; $i < $numRows; $i++ )
944 $A = DB_fetchArray( $result );
945 if ( !empty( $A['filename'] ))
947 $format = explode( '-', $A['format'] );
948 $format_type = strtolower( $format[0] );
949 $format_name = ucwords( $format[0] );
951 $feed_url[] = '<link rel="alternate" type="application/'
952 . $format_type . '+xml" hreflang="' . $A['language']
953 . '" href="' . $baseurl . $A['filename'] . '" title="'
954 . $format_name . ' Feed: ' . $A['title'] . '"' . XHTML . '>';
958 $header->set_var( 'feed_url', implode( LB, $feed_url ));
961 if( !COM_onFrontpage() )
963 $relLinks['home'] = '<link rel="home" href="' . $_CONF['site_url']
964 . '/" title="' . $LANG01[90] . '"' . XHTML . '>';
966 $loggedInUser = !COM_isAnonUser();
967 if( $loggedInUser || (( $_CONF['loginrequired'] == 0 ) &&
968 ( $_CONF['searchloginrequired'] == 0 )))
970 if(( substr( $_SERVER['PHP_SELF'], -strlen( '/search.php' ))
971 != '/search.php' ) || isset( $_GET['mode'] ))
973 $relLinks['search'] = '<link rel="search" href="'
974 . $_CONF['site_url'] . '/search.php" title="'
975 . $LANG01[75] . '"' . XHTML . '>';
978 if( $loggedInUser || (( $_CONF['loginrequired'] == 0 ) &&
979 ( $_CONF['directoryloginrequired'] == 0 )))
981 if( strpos( $_SERVER['PHP_SELF'], '/article.php' ) !== false ) {
982 $relLinks['contents'] = '<link rel="contents" href="'
983 . $_CONF['site_url'] . '/directory.php" title="'
984 . $LANG01[117] . '"' . XHTML . '>';
987 if (!$_CONF['disable_webservices']) {
988 $relLinks['service'] = '<link rel="service" '
989 . 'type="application/atomsvc+xml" ' . 'href="'
990 . $_CONF['site_url'] . '/webservices/atom/?introspection" '
991 . 'title="' . $LANG01[130] . '"' . XHTML . '>';
993 // TBD: add a plugin API and a lib-custom.php function
994 $header->set_var( 'rel_links', implode( LB, $relLinks ));
996 $pagetitle_siteslogan = false;
997 if( empty( $pagetitle ))
1001 $pagetitle = $_CONF['site_slogan'];
1002 $pagetitle_siteslogan = true;
1006 $pagetitle = stripslashes( DB_getItem( $_TABLES['topics'], 'topic',
1007 "tid = '$topic'" ));
1010 if( !empty( $pagetitle ))
1012 $header->set_var( 'page_site_splitter', ' - ');
1016 $header->set_var( 'page_site_splitter', '');
1018 $header->set_var( 'page_title', $pagetitle );
1019 $header->set_var( 'site_name', $_CONF['site_name']);
1021 if (COM_onFrontpage() OR $pagetitle_siteslogan) {
1022 $title_and_name = $_CONF['site_name'];
1023 if (!empty($pagetitle)) {
1024 $title_and_name .= ' - ' . $pagetitle;
1027 $title_and_name = '';
1028 if (!empty($pagetitle)) {
1029 $title_and_name = $pagetitle . ' - ';
1031 $title_and_name .= $_CONF['site_name'];
1033 $header->set_var('page_title_and_site_name', $title_and_name);
1035 COM_setLangIdAndAttribute($header);
1037 $header->set_var( 'background_image', $_CONF['layout_url']
1038 . '/images/bg.' . $_IMAGE_TYPE );
1039 $header->set_var( 'site_url', $_CONF['site_url'] );
1040 $header->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1041 $header->set_var( 'layout_url', $_CONF['layout_url'] );
1042 $header->set_var( 'site_mail', "mailto:{$_CONF['site_mail']}" );
1043 $header->set_var( 'site_name', $_CONF['site_name'] );
1044 $header->set_var( 'site_slogan', $_CONF['site_slogan'] );
1045 $rdf = substr_replace( $_CONF['rdf_file'], $_CONF['site_url'], 0,
1046 strlen( $_CONF['path_html'] ) - 1 );
1047 $header->set_var( 'rdf_file', $rdf );
1048 $header->set_var( 'rss_url', $rdf );
1050 $msg = rtrim($LANG01[67]) . ' ' . $_CONF['site_name'];
1052 if( !empty( $_USER['username'] ))
1054 $msg .= ', ' . COM_getDisplayName( $_USER['uid'], $_USER['username'],
1055 $_USER['fullname'] );
1058 $curtime = COM_getUserDateTimeFormat();
1060 $header->set_var( 'welcome_msg', $msg );
1061 $header->set_var( 'datetime', $curtime[0] );
1062 $header->set_var( 'site_logo', $_CONF['layout_url']
1063 . '/images/logo.' . $_IMAGE_TYPE );
1064 $header->set_var( 'css_url', $_CONF['layout_url'] . '/style.css' );
1065 $header->set_var( 'theme', $_CONF['theme'] );
1067 $header->set_var('charset', COM_getCharset());
1068 $header->set_var('direction', $LANG_DIRECTION);
1070 // Now add variables for buttons like e.g. those used by the Yahoo theme
1071 $header->set_var( 'button_home', $LANG_BUTTONS[1] );
1072 $header->set_var( 'button_contact', $LANG_BUTTONS[2] );
1073 $header->set_var( 'button_contribute', $LANG_BUTTONS[3] );
1074 $header->set_var( 'button_sitestats', $LANG_BUTTONS[7] );
1075 $header->set_var( 'button_personalize', $LANG_BUTTONS[8] );
1076 $header->set_var( 'button_search', $LANG_BUTTONS[9] );
1077 $header->set_var( 'button_advsearch', $LANG_BUTTONS[10] );
1078 $header->set_var( 'button_directory', $LANG_BUTTONS[11] );
1080 // Get plugin menu options
1081 $plugin_menu = PLG_getMenuItems();
1085 COM_errorLog( 'num plugin menu items in header = ' . count( $plugin_menu ), 1 );
1088 // Now add nested template for menu items
1089 COM_renderMenu( $header, $plugin_menu );
1091 if( count( $plugin_menu ) == 0 )
1093 $header->parse( 'plg_menu_elements', 'menuitem_none', true );
1097 $count_plugin_menu = count( $plugin_menu );
1098 for( $i = 1; $i <= $count_plugin_menu; $i++ )
1100 $header->set_var( 'menuitem_url', current( $plugin_menu ));
1101 $header->set_var( 'menuitem_text', key( $plugin_menu ));
1103 if( $i == $count_plugin_menu )
1105 $header->parse( 'plg_menu_elements', 'menuitem_last', true );
1109 $header->parse( 'plg_menu_elements', 'menuitem', true );
1112 next( $plugin_menu );
1116 // Call to plugins to set template variables in the header
1117 PLG_templateSetVars( 'header', $header );
1119 if( $_CONF['left_blocks_in_footer'] == 1 )
1121 $header->set_var( 'left_blocks', '' );
1122 $header->set_var( 'geeklog_blocks', '' );
1128 /* Check if an array has been passed that includes the name of a plugin
1129 * function or custom function
1130 * This can be used to take control over what blocks are then displayed
1132 if( is_array( $what ))
1134 $function = $what[0];
1135 if( function_exists( $function ))
1137 $lblocks = $function( $what[1], 'left' );
1141 $lblocks = COM_showBlocks( 'left', $topic );
1144 else if( $what <> 'none' )
1146 // Now show any blocks -- need to get the topic if not on home page
1147 $lblocks = COM_showBlocks( 'left', $topic );
1150 if( empty( $lblocks ))
1152 $header->set_var( 'left_blocks', '' );
1153 $header->set_var( 'geeklog_blocks', '' );
1157 $header->set_var( 'geeklog_blocks', $lblocks );
1158 $header->parse( 'left_blocks', 'leftblocks', true );
1159 $header->set_var( 'geeklog_blocks', '');
1163 if( $_CONF['right_blocks_in_footer'] == 1 )
1165 $header->set_var( 'right_blocks', '' );
1166 $header->set_var( 'geeklog_blocks', '' );
1172 /* Check if an array has been passed that includes the name of a plugin
1173 * function or custom function
1174 * This can be used to take control over what blocks are then displayed
1176 if( is_array( $what ))
1178 $function = $what[0];
1179 if( function_exists( $function ))
1181 $rblocks = $function( $what[1], 'right' );
1185 $rblocks = COM_showBlocks( 'right', $topic );
1188 else if( $what <> 'none' )
1190 // Now show any blocks -- need to get the topic if not on home page
1191 $rblocks = COM_showBlocks( 'right', $topic );
1194 if( empty( $rblocks ))
1196 $header->set_var( 'right_blocks', '' );
1197 $header->set_var( 'geeklog_blocks', '' );
1201 $header->set_var( 'geeklog_blocks', $rblocks, true );
1202 $header->parse( 'right_blocks', 'rightblocks', true );
1206 if( isset( $_CONF['advanced_editor'] ) && ( $_CONF['advanced_editor'] == 1 )
1207 && file_exists( $_CONF['path_layout']
1208 . 'advanced_editor_header.thtml' ))
1210 $header->set_file( 'editor' , 'advanced_editor_header.thtml');
1211 $header->parse( 'advanced_editor', 'editor' );
1216 $header->set_var( 'advanced_editor', '' );
1219 // Call any plugin that may want to include extra Meta tags
1220 // or Javascript functions
1221 $headercode .= PLG_getHeaderCode();
1224 // 0 = Disabled, 1 = Enabled, 2 = Enabled but default just for homepage
1225 if ($_CONF['meta_tags'] > 0) {
1226 $meta_description = '';
1227 $meta_keywords = '';
1228 $no_meta_description = 1;
1229 $no_meta_keywords = 1;
1231 //Find out if the meta tag description or keywords already exist in the headercode
1232 if ($headercode != '') {
1233 $pattern = '/<meta ([^>]*)name="([^"\'>]*)"([^>]*)/im';
1234 if (preg_match_all($pattern, $headercode, $matches, PREG_SET_ORDER)) {
1235 // Loop through all meta tags looking for description and keywords
1236 for ($i = 0; $i<count($matches) && (($no_meta_description == 1) || ($no_meta_keywords == 1)); $i++) {
1237 $str_matches = strtolower($matches[$i][0]);
1238 $pos = strpos($str_matches,'name=');
1239 if (!(is_bool($pos) && !$pos)) {
1240 $name = trim(substr($str_matches,$pos+5),'"');
1241 $pos = strpos($name,'"');
1242 $name = substr($name,0,$pos);
1244 if (strcasecmp("description",$name) == 0) {
1245 $pos = strpos($str_matches,'content=');
1246 if (!(is_bool($pos) && !$pos)) {
1247 $no_meta_description = 0;
1250 if (strcasecmp("keywords",$name) == 0) {
1251 $pos = strpos($str_matches,'content=');
1252 if (!(is_bool($pos) && !$pos)) {
1253 $no_meta_keywords = 0;
1262 If (COM_onFrontpage() && $_CONF['meta_tags'] == 2) { // Display default meta tags only on home page
1263 If ($no_meta_description) {
1264 $meta_description = $_CONF['meta_description'];
1266 If ($no_meta_keywords) {
1267 $meta_keywords = $_CONF['meta_keywords'];
1269 } else if ( $_CONF['meta_tags'] == 1 ) { // Display default meta tags anywhere there are no tags
1270 If ($no_meta_description) {
1271 $meta_description = $_CONF['meta_description'];
1273 If ($no_meta_keywords) {
1274 $meta_keywords = $_CONF['meta_keywords'];
1278 If ($no_meta_description OR $no_meta_keywords) {
1279 $headercode .= COM_createMetaTags($meta_description, $meta_keywords);
1283 $header->set_var( 'plg_headercode', $headercode );
1285 // The following lines allow users to embed PHP in their templates. This
1286 // is almost a contradition to the reasons for using templates but this may
1287 // prove useful at times ...
1288 // Don't use PHP in templates if you can live without it!
1290 $tmp = $header->finish($header->parse('index_header', 'header'));
1292 $xml_declaration = '';
1293 if ( get_cfg_var('short_open_tag') == '1' )
1295 if ( preg_match( '/(<\?xml[^>]*>)(.*)/s', $tmp, $match ) )
1297 $xml_declaration = $match[1] . LB;
1303 eval( '?>' . $tmp );
1304 $retval = $xml_declaration . ob_get_contents();
1312 * Returns the site footer
1314 * This loads the proper templates, does variable substitution and returns the
1315 * HTML for the site footer.
1317 * @param boolean $rightblock Whether or not to show blocks on right hand side default is no
1318 * @param array $custom An array defining custom function to be used to format Rightblocks
1319 * @see function COM_siteHeader
1320 * @return string Formated HTML containing site footer and optionally right blocks
1323 function COM_siteFooter( $rightblock = -1, $custom = '' )
1325 global $_CONF, $_TABLES, $LANG01, $_PAGE_TIMER, $topic, $LANG_BUTTONS;
1327 // If the theme implemented this for us then call their version instead.
1329 $function = $_CONF['theme'] . '_siteFooter';
1331 if( function_exists( $function ))
1333 return $function( $rightblock, $custom );
1338 // Set template directory
1339 $footer = new Template( $_CONF['path_layout'] );
1341 // Set template file
1342 $footer->set_file( array(
1343 'footer' => 'footer.thtml',
1344 'rightblocks' => 'rightblocks.thtml',
1345 'leftblocks' => 'leftblocks.thtml'
1348 // Do variable assignments
1349 $footer->set_var( 'xhtml', XHTML );
1350 $footer->set_var( 'site_url', $_CONF['site_url']);
1351 $footer->set_var( 'site_admin_url', $_CONF['site_admin_url']);
1352 $footer->set_var( 'layout_url',$_CONF['layout_url']);
1353 $footer->set_var( 'site_mail', "mailto:{$_CONF['site_mail']}" );
1354 $footer->set_var( 'site_name', $_CONF['site_name'] );
1355 $footer->set_var( 'site_slogan', $_CONF['site_slogan'] );
1356 $rdf = substr_replace( $_CONF['rdf_file'], $_CONF['site_url'], 0,
1357 strlen( $_CONF['path_html'] ) - 1 );
1358 $footer->set_var( 'rdf_file', $rdf );
1359 $footer->set_var( 'rss_url', $rdf );
1361 $year = date( 'Y' );
1362 $copyrightyear = $year;
1363 if( !empty( $_CONF['copyrightyear'] ))
1365 $copyrightyear = $_CONF['copyrightyear'];
1367 $footer->set_var( 'copyright_notice', ' ' . $LANG01[93] . ' © '
1368 . $copyrightyear . ' ' . $_CONF['site_name'] . '<br' . XHTML . '> '
1370 $footer->set_var( 'copyright_msg', $LANG01[93] . ' © '
1371 . $copyrightyear . ' ' . $_CONF['site_name'] );
1372 $footer->set_var( 'current_year', $year );
1373 $footer->set_var( 'lang_copyright', $LANG01[93] );
1374 $footer->set_var( 'trademark_msg', $LANG01[94] );
1375 $footer->set_var( 'powered_by', $LANG01[95] );
1376 $footer->set_var( 'geeklog_url', 'http://www.geeklog.net/' );
1377 $footer->set_var( 'geeklog_version', VERSION );
1378 // Now add variables for buttons like e.g. those used by the Yahoo theme
1379 $footer->set_var( 'button_home', $LANG_BUTTONS[1] );
1380 $footer->set_var( 'button_contact', $LANG_BUTTONS[2] );
1381 $footer->set_var( 'button_contribute', $LANG_BUTTONS[3] );
1382 $footer->set_var( 'button_sitestats', $LANG_BUTTONS[7] );
1383 $footer->set_var( 'button_personalize', $LANG_BUTTONS[8] );
1384 $footer->set_var( 'button_search', $LANG_BUTTONS[9] );
1385 $footer->set_var( 'button_advsearch', $LANG_BUTTONS[10] );
1386 $footer->set_var( 'button_directory', $LANG_BUTTONS[11] );
1388 /* Right blocks. Argh. Don't talk to me about right blocks...
1389 * Right blocks will be displayed if Right_blocks_in_footer is set [1],
1390 * AND (this function has been asked to show them (first param) OR the
1391 * show_right_blocks conf variable has been set to override what the code
1394 * If $custom sets an array (containing functionname and first argument)
1395 * then this is used instead of the default (COM_showBlocks) to render
1396 * the right blocks (and left).
1398 * [1] - if it isn't, they'll be in the header already.
1401 $displayRightBlocks = true;
1402 if ($_CONF['right_blocks_in_footer'] == 1)
1404 if( ($rightblock < 0) || !$rightblock )
1406 if( isset( $_CONF['show_right_blocks'] ) )
1408 $displayRightBlocks = $_CONF['show_right_blocks'];
1412 $displayRightBlocks = false;
1415 $displayRightBlocks = true;
1418 $displayRightBlocks = false;
1421 if ($displayRightBlocks)
1423 /* Check if an array has been passed that includes the name of a plugin
1424 * function or custom function.
1425 * This can be used to take control over what blocks are then displayed
1427 if( is_array( $custom ))
1429 $function = $custom['0'];
1430 if( function_exists( $function ))
1432 $rblocks = $function( $custom['1'], 'right' );
1434 $rblocks = COM_showBlocks( 'right', $topic );
1437 $rblocks = COM_showBlocks( 'right', $topic );
1440 if( empty( $rblocks ))
1442 $footer->set_var( 'geeklog_blocks', '');
1443 $footer->set_var( 'right_blocks', '' );
1445 $footer->set_var( 'geeklog_blocks', $rblocks);
1446 $footer->parse( 'right_blocks', 'rightblocks', true );
1447 $footer->set_var( 'geeklog_blocks', '');
1450 $footer->set_var( 'geeklog_blocks', '');
1451 $footer->set_var( 'right_blocks', '' );
1454 if( $_CONF['left_blocks_in_footer'] == 1 )
1458 /* Check if an array has been passed that includes the name of a plugin
1459 * function or custom function
1460 * This can be used to take control over what blocks are then displayed
1462 if( is_array( $custom ))
1464 $function = $custom[0];
1465 if( function_exists( $function ))
1467 $lblocks = $function( $custom[1], 'left' );
1472 $lblocks = COM_showBlocks( 'left', $topic );
1475 if( empty( $lblocks ))
1477 $footer->set_var( 'left_blocks', '' );
1478 $footer->set_var( 'geeklog_blocks', '');
1482 $footer->set_var( 'geeklog_blocks', $lblocks);
1483 $footer->parse( 'left_blocks', 'leftblocks', true );
1484 $footer->set_var( 'geeklog_blocks', '');
1488 // Global centerspan variable set in index.php
1489 if( isset( $GLOBALS['centerspan'] ))
1491 $footer->set_var( 'centerblockfooter-span', '</td></tr></table>' );
1494 $exectime = $_PAGE_TIMER->stopTimer();
1495 $exectext = $LANG01[91] . ' ' . $exectime . ' ' . $LANG01[92];
1497 $footer->set_var( 'execution_time', $exectime );
1498 $footer->set_var( 'execution_textandtime', $exectext );
1500 // Call to plugins to set template variables in the footer
1501 PLG_templateSetVars( 'footer', $footer );
1503 // Actually parse the template and make variable substitutions
1504 $footer->parse( 'index_footer', 'footer' );
1506 // Return resulting HTML
1507 return $footer->finish( $footer->get_var( 'index_footer' ));
1511 * Prints out standard block header
1513 * Prints out standard block header but pulling header HTML formatting from
1516 * Programming Note: The two functions COM_startBlock and COM_endBlock are used
1517 * to sandwich your block content. These functions are not used only for blocks
1518 * but anything that uses that format, e.g. Stats page. They are used like
1519 * COM_siteHeader and COM_siteFooter but for internal page elements.
1521 * @param string $title Value to set block title to
1522 * @param string $helpfile Help file, if one exists
1523 * @param string $template HTML template file to use to format the block
1524 * @return string Formatted HTML containing block header
1526 * @see COM_siteHeader
1530 function COM_startBlock( $title='', $helpfile='', $template='blockheader.thtml' )
1532 global $_CONF, $LANG01, $_IMAGE_TYPE;
1534 $block = new Template( $_CONF['path_layout'] );
1535 $block->set_file( 'block', $template );
1537 $block->set_var( 'xhtml', XHTML );
1538 $block->set_var( 'site_url', $_CONF['site_url'] );
1539 $block->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1540 $block->set_var( 'layout_url', $_CONF['layout_url'] );
1541 $block->set_var( 'block_title', stripslashes( $title ));
1543 if( !empty( $helpfile ))
1545 $helpimg = $_CONF['layout_url'] . '/images/button_help.' . $_IMAGE_TYPE;
1546 $help_content = '<img src="' . $helpimg. '" alt="?"' . XHTML . '>';
1547 $help_attr = array('class'=>'blocktitle');
1548 if( !stristr( $helpfile, 'http://' ))
1550 $help_url = $_CONF['site_url'] . "/help/$helpfile";
1554 $help_url = $helpfile;
1556 $help = COM_createLink($help_content, $help_url, $help_attr);
1557 $block->set_var( 'block_help', $help );
1560 $block->parse( 'startHTML', 'block' );
1562 return $block->finish( $block->get_var( 'startHTML' ));
1566 * Closes out COM_startBlock
1568 * @param string $template HTML template file used to format block footer
1569 * @return string Formatted HTML to close block
1570 * @see function COM_startBlock
1573 function COM_endBlock( $template='blockfooter.thtml' )
1577 $block = new Template( $_CONF['path_layout'] );
1578 $block->set_file( 'block', $template );
1580 $block->set_var( 'xhtml', XHTML );
1581 $block->set_var( 'site_url', $_CONF['site_url'] );
1582 $block->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
1583 $block->set_var( 'layout_url', $_CONF['layout_url'] );
1584 $block->parse( 'endHTML', 'block' );
1586 return $block->finish( $block->get_var( 'endHTML' ));
1591 * Creates a <option> list from a database list for use in forms
1593 * Creates option list form field using given arguments
1595 * @param string $table Database Table to get data from
1596 * @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.
1597 * @param string/array $selected Value (from $selection) to set to SELECTED or default
1598 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1599 * @param string $where Optional WHERE clause to use in the SQL Selection
1600 * @see function COM_checkList
1601 * @return string Formated HTML of option values
1604 function COM_optionList( $table, $selection, $selected='', $sortcol=1, $where='' )
1606 global $_DB_table_prefix;
1610 $LangTableName = '';
1611 if( substr( $table, 0, strlen( $_DB_table_prefix )) == $_DB_table_prefix )
1613 $LangTableName = 'LANG_' . substr( $table, strlen( $_DB_table_prefix ));
1617 $LangTableName = 'LANG_' . $table;
1620 global $$LangTableName;
1622 if( isset( $$LangTableName ))
1624 $LangTable = $$LangTableName;
1628 $LangTable = array();
1631 $tmp = str_replace( 'DISTINCT ', '', $selection );
1632 $select_set = explode( ',', $tmp );
1634 $sql = "SELECT $selection FROM $table";
1637 $sql .= " WHERE $where";
1639 $sql .= " ORDER BY {$select_set[$sortcol]}";
1640 $result = DB_query( $sql );
1641 $nrows = DB_numRows( $result );
1643 for( $i = 0; $i < $nrows; $i++ )
1645 $A = DB_fetchArray( $result, true );
1646 $retval .= '<option value="' . $A[0] . '"';
1648 if( is_array( $selected ) AND count( $selected ) > 0 )
1650 foreach( $selected as $selected_item )
1652 if( $A[0] == $selected_item )
1654 $retval .= ' selected="selected"';
1658 elseif( !is_array( $selected ) AND $A[0] == $selected )
1660 $retval .= ' selected="selected"';
1664 if( empty( $LangTable[$A[0]] ))
1670 $retval .= $LangTable[$A[0]];
1672 $retval .= '</option>' . LB;
1679 * Create and return a dropdown-list of available topics
1681 * This is a variation of COM_optionList() from lib-common.php. It will add
1682 * only those topics to the option list which are accessible by the current
1685 * @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.
1686 * @param string $selected Value (from $selection) to set to SELECTED or default
1687 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1688 * @param boolean $ignorelang Whether to return all topics (true) or only the ones for the current language (false)
1689 * @see function COM_optionList
1690 * @return string Formated HTML of option values
1693 function COM_topicList( $selection, $selected = '', $sortcol = 1, $ignorelang = false )
1699 $topics = COM_topicArray($selection, $sortcol, $ignorelang);
1700 foreach ($topics as $tid => $topic) {
1701 $retval .= '<option value="' . $tid . '"';
1702 if ($tid == $selected) {
1703 $retval .= ' selected="selected"';
1705 $retval .= '>' . $topic . '</option>' . LB;
1712 * Return a list of topics in an array
1713 * (derived from COM_topicList - API may change)
1715 * @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.
1716 * @param int $sortcol Which field to sort option list by 0 (value) or 1 (label)
1717 * @param boolean $ignorelang Whether to return all topics (true) or only the ones for the current language (false)
1718 * @return array Array of topics
1719 * @see function COM_topicList
1722 function COM_topicArray($selection, $sortcol = 0, $ignorelang = false)
1728 $tmp = str_replace('DISTINCT ', '', $selection);
1729 $select_set = explode(',', $tmp);
1731 $sql = "SELECT $selection FROM {$_TABLES['topics']}";
1733 $sql .= COM_getPermSQL();
1735 $permsql = COM_getPermSQL();
1736 if (empty($permsql)) {
1737 $sql .= COM_getLangSQL('tid');
1739 $sql .= $permsql . COM_getLangSQL('tid', 'AND');
1742 $sql .= " ORDER BY $select_set[$sortcol]";
1744 $result = DB_query($sql);
1745 $nrows = DB_numRows($result);
1747 if (count($select_set) > 1) {
1748 for ($i = 0; $i < $nrows; $i++) {
1749 $A = DB_fetchArray($result, true);
1750 $retval[$A[0]] = stripslashes($A[1]);
1753 for ($i = 0; $i < $nrows; $i++) {
1754 $A = DB_fetchArray($result, true);
1763 * Creates a <input> checklist from a database list for use in forms
1765 * Creates a group of checkbox form fields with given arguments
1767 * @param string $table DB Table to pull data from
1768 * @param string $selection Comma delimited list of fields to pull from table
1769 * @param string $where Where clause of SQL statement
1770 * @param string $selected Value to set to CHECKED
1771 * @param string $fieldname Name to use for the checkbox array
1772 * @return string HTML with Checkbox code
1773 * @see COM_optionList
1776 function COM_checkList($table, $selection, $where = '', $selected = '', $fieldname = '')
1778 global $_TABLES, $_COM_VERBOSE;
1780 $sql = "SELECT $selection FROM $table";
1782 if( !empty( $where ))
1784 $sql .= " WHERE $where";
1787 $result = DB_query( $sql );
1788 $nrows = DB_numRows( $result );
1790 if( !empty( $selected ))
1794 COM_errorLog( "exploding selected array: $selected in COM_checkList", 1 );
1797 $S = explode( ' ', $selected );
1803 COM_errorLog( 'selected string was empty COM_checkList', 1 );
1808 $retval = '<ul class="checkboxes-list">' . LB;
1809 for( $i = 0; $i < $nrows; $i++ )
1812 $A = DB_fetchArray( $result, true );
1814 if( $table == $_TABLES['topics'] AND SEC_hasTopicAccess( $A['tid'] ) == 0 )
1819 if (empty($fieldname)) {
1820 // Not a good idea, as that will expose our table name and prefix!
1821 // Make sure you pass a distinct field name!
1822 $fieldname = $table;
1827 $retval .= '<li><input type="checkbox" name="' . $fieldname . '[]" value="' . $A[0] . '"';
1829 $sizeS = count( $S );
1830 for( $x = 0; $x < $sizeS; $x++ )
1832 if( $A[0] == $S[$x] )
1834 $retval .= ' checked="checked"';
1839 if(( $table == $_TABLES['blocks'] ) && isset( $A[2] ) && ( $A[2] == 'gldefault' ))
1841 $retval .= XHTML . '><span class="gldefault">' . stripslashes( $A[1] ) . '</span></li>' . LB;
1845 $retval .= XHTML . '><span>' . stripslashes( $A[1] ) . '</span></li>' . LB;
1849 $retval .= '</ul>' . LB;
1855 * Prints out an associative array for debugging
1857 * The core of this code has been lifted from phpweblog which is licenced
1858 * under the GPL. This is not used very much in the code but you can use it
1861 * @param array $array Array to loop through and print values for
1862 * @return string $retval Formatted HTML List
1866 function COM_debug($array)
1869 if(!empty($array)) {
1870 $retval = '<ul><pre><p>---- DEBUG ----</p>';
1871 foreach($array as $k => $v) {
1872 $retval .= sprintf("<li>%13s [%s]</li>\n", $k, $v);
1874 $retval .= '<p>---------------</p></pre></ul>';
1881 * Checks to see if RDF file needs updating and updates it if so.
1882 * Checks to see if we need to update the RDF as a result
1883 * of an article with a future publish date reaching it's
1884 * publish time and if so updates the RDF file.
1886 * NOTE: When called without parameters, this will only check for new entries to
1887 * include in the feeds. Pass the $updated_XXX parameters when the content
1888 * of an existing entry has changed.
1890 * @param string $updated_type (optional) feed type to update
1891 * @param string $updated_topic (optional) feed topic to update
1892 * @param string $updated_id (optional) feed id to update
1894 * @see file lib-syndication.php
1897 function COM_rdfUpToDateCheck( $updated_type = '', $updated_topic = '', $updated_id = '' )
1899 global $_CONF, $_TABLES;
1901 if( $_CONF['backend'] > 0 )
1903 if( !empty( $updated_type ) && ( $updated_type != 'article' ))
1905 // when a plugin's feed is to be updated, skip Geeklog's own feeds
1906 $sql = "SELECT fid,type,topic,limits,update_info FROM {$_TABLES['syndication']} WHERE (is_enabled = 1) AND (type <> 'article')";
1910 $sql = "SELECT fid,type,topic,limits,update_info FROM {$_TABLES['syndication']} WHERE is_enabled = 1";
1912 $result = DB_query( $sql );
1913 $num = DB_numRows( $result );
1914 for( $i = 0; $i < $num; $i++)
1916 $A = DB_fetchArray( $result );
1919 if( $A['type'] == 'article' )
1921 $is_current = SYND_feedUpdateCheck( $A['topic'],
1922 $A['update_info'], $A['limits'],
1923 $updated_topic, $updated_id );
1927 $is_current = PLG_feedUpdateCheck( $A['type'], $A['fid'],
1928 $A['topic'], $A['update_info'], $A['limits'],
1929 $updated_type, $updated_topic, $updated_id );
1933 SYND_updateFeed( $A['fid'] );
1940 * Checks and Updates the featured status of all articles.
1942 * Checks to see if any articles that were published for the future have been
1943 * published and, if so, will see if they are featured. If they are featured,
1944 * this will set old featured article (if there is one) to normal
1948 function COM_featuredCheck()
1952 $curdate = date( "Y-m-d H:i:s", time() );
1954 if( DB_getItem( $_TABLES['stories'], 'COUNT(*)', "featured = 1 AND draft_flag = 0 AND date <= '$curdate'" ) > 1 )
1956 // OK, we have two featured stories, fix that
1958 $sid = DB_getItem( $_TABLES['stories'], 'sid', "featured = 1 AND draft_flag = 0 ORDER BY date LIMIT 1" );
1959 DB_query( "UPDATE {$_TABLES['stories']} SET featured = 0 WHERE sid = '$sid'" );
1965 * Logs messages to error.log or the web page or both
1967 * Prints a well formatted message to either the web page, error log
1970 * @param string $logentry Text to log to error log
1971 * @param int $actionid where 1 = write to log file, 2 = write to screen (default) both
1972 * @see function COM_accessLog
1973 * @return string If $actionid = 2 or '' then HTML formatted string (wrapped in block) else nothing
1977 function COM_errorLog( $logentry, $actionid = '' )
1979 global $_CONF, $LANG01;
1983 if( !empty( $logentry ))
1985 $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
1988 $timestamp = @strftime( '%c' );
1990 if (!isset($_CONF['path_layout']) &&
1991 (($actionid == 2) || empty($actionid))) {
1994 if (!isset($_CONF['path_log']) && ($actionid != 2)) {
2001 $logfile = $_CONF['path_log'] . 'error.log';
2003 if( !$file = fopen( $logfile, 'a' ))
2005 $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2009 fputs( $file, "$timestamp - $logentry \n" );
2014 $retval .= COM_startBlock( $LANG01[55] . ' ' . $timestamp, '',
2015 COM_getBlockTemplate( '_msg_block', 'header' ))
2016 . nl2br( $logentry )
2017 . COM_endBlock( COM_getBlockTemplate( '_msg_block',
2022 $retval = nl2br($logentry);
2026 $logfile = $_CONF['path_log'] . 'error.log';
2028 if( !$file = fopen( $logfile, 'a' ))
2030 $retval .= $LANG01[33] . ' ' . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2034 fputs( $file, "$timestamp - $logentry \n" );
2035 $retval .= COM_startBlock( $LANG01[34] . ' - ' . $timestamp,
2036 '', COM_getBlockTemplate( '_msg_block',
2038 . nl2br( $logentry )
2039 . COM_endBlock( COM_getBlockTemplate( '_msg_block',
2050 * Logs message to access.log
2052 * This will print a message to the Geeklog access log
2054 * @param string $logentry Message to write to access log
2059 function COM_accessLog( $logentry )
2061 global $_CONF, $_USER, $LANG01;
2065 if( !empty( $logentry ))
2067 $logentry = str_replace( array( '<?', '?>' ), array( '(@', '@)' ),
2070 $timestamp = @strftime( '%c' );
2071 $logfile = $_CONF['path_log'] . 'access.log';
2073 if( !$file = fopen( $logfile, 'a' ))
2075 return $LANG01[33] . $logfile . ' (' . $timestamp . ')<br' . XHTML . '>' . LB;
2078 if( isset( $_USER['uid'] ))
2080 $byuser = $_USER['uid'] . '@' . $_SERVER['REMOTE_ADDR'];
2084 $byuser = 'anon@' . $_SERVER['REMOTE_ADDR'];
2087 fputs( $file, "$timestamp ($byuser) - $logentry\n" );
2094 * Shows all available topics
2096 * Show the topics in the system the user has access to and prints them in HTML.
2097 * This function is used to show the topics in the topics block.
2099 * @param string $topic ID of currently selected topic
2100 * @return string HTML formatted topic list
2104 function COM_showTopics( $topic='' )
2106 global $_CONF, $_TABLES, $_USER, $LANG01, $_BLOCK_TEMPLATE, $page;
2108 $langsql = COM_getLangSQL( 'tid' );
2109 if( empty( $langsql ))
2118 $sql = "SELECT tid,topic,imageurl FROM {$_TABLES['topics']}" . $langsql;
2119 if( !COM_isAnonUser() )
2121 $tids = DB_getItem( $_TABLES['userindex'], 'tids',
2122 "uid = '{$_USER['uid']}'" );
2123 if( !empty( $tids ))
2125 $sql .= " $op (tid NOT IN ('" . str_replace( ' ', "','", $tids )
2126 . "'))" . COM_getPermSQL( 'AND' );
2130 $sql .= COM_getPermSQL( $op );
2135 $sql .= COM_getPermSQL( $op );
2137 if( $_CONF['sortmethod'] == 'alpha' )
2139 $sql .= ' ORDER BY topic ASC';
2143 $sql .= ' ORDER BY sortnum';
2145 $result = DB_query( $sql );
2148 $sections = new Template( $_CONF['path_layout'] );
2149 if( isset( $_BLOCK_TEMPLATE['topicoption'] ))
2151 $templates = explode( ',', $_BLOCK_TEMPLATE['topicoption'] );
2152 $sections->set_file( array( 'option' => $templates[0],
2153 'current' => $templates[1] ));
2157 $sections->set_file( array( 'option' => 'topicoption.thtml',
2158 'inactive' => 'topicoption_off.thtml' ));
2161 $sections->set_var( 'xhtml', XHTML );
2162 $sections->set_var( 'site_url', $_CONF['site_url'] );
2163 $sections->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2164 $sections->set_var( 'layout_url', $_CONF['layout_url'] );
2165 $sections->set_var( 'block_name', str_replace( '_', '-', 'section_block' ));
2167 if( $_CONF['hide_home_link'] == 0 )
2169 // Give a link to the homepage here since a lot of people use this for
2170 // navigating the site
2172 if( COM_onFrontpage() )
2174 $sections->set_var( 'option_url', '' );
2175 $sections->set_var( 'option_label', $LANG01[90] );
2176 $sections->set_var( 'option_count', '' );
2177 $sections->set_var( 'topic_image', '' );
2178 $retval .= $sections->parse( 'item', 'inactive' );
2182 $sections->set_var( 'option_url',
2183 $_CONF['site_url'] . '/index.php' );
2184 $sections->set_var( 'option_label', $LANG01[90] );
2185 $sections->set_var( 'option_count', '' );
2186 $sections->set_var( 'topic_image', '' );
2187 $retval .= $sections->parse( 'item', 'option' );
2191 if( $_CONF['showstorycount'] )
2193 $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['stories']} "
2194 . 'WHERE (draft_flag = 0) AND (date <= NOW()) '
2195 . COM_getPermSQL( 'AND' )
2197 $rcount = DB_query( $sql );
2198 while( $C = DB_fetchArray( $rcount ))
2200 $storycount[$C['tid']] = $C['count'];
2204 if( $_CONF['showsubmissioncount'] )
2206 $sql = "SELECT tid, COUNT(*) AS count FROM {$_TABLES['storysubmission']} "
2208 $rcount = DB_query( $sql );
2209 while( $C = DB_fetchArray( $rcount ))
2211 $submissioncount[$C['tid']] = $C['count'];
2215 while( $A = DB_fetchArray( $result ) )
2217 $topicname = stripslashes( $A['topic'] );
2218 $sections->set_var( 'option_url', $_CONF['site_url']
2219 . '/index.php?topic=' . $A['tid'] );
2220 $sections->set_var( 'option_label', $topicname );
2223 if( $_CONF['showstorycount'] || $_CONF['showsubmissioncount'] )
2225 $countstring .= '(';
2227 if( $_CONF['showstorycount'] )
2229 if( empty( $storycount[$A['tid']] ))
2235 $countstring .= COM_numberFormat( $storycount[$A['tid']] );
2239 if( $_CONF['showsubmissioncount'] )
2241 if( $_CONF['showstorycount'] )
2243 $countstring .= '/';
2245 if( empty( $submissioncount[$A['tid']] ))
2251 $countstring .= COM_numberFormat( $submissioncount[$A['tid']] );
2255 $countstring .= ')';
2257 $sections->set_var( 'option_count', $countstring );
2260 if( !empty( $A['imageurl'] ))
2262 $imageurl = COM_getTopicImageUrl( $A['imageurl'] );
2263 $topicimage = '<img src="' . $imageurl . '" alt="' . $topicname
2264 . '" title="' . $topicname . '"' . XHTML . '>';
2266 $sections->set_var( 'topic_image', $topicimage );
2268 if(( $A['tid'] == $topic ) && ( $page == 1 ))
2270 $retval .= $sections->parse( 'item', 'inactive' );
2274 $retval .= $sections->parse( 'item', 'option' );
2282 * Shows the user their menu options
2284 * This shows the average Joe User their menu options. This is the user block on the left side
2286 * @param string $help Help file to show
2287 * @param string $title Title of Menu
2288 * @param string $position Side being shown on 'left', 'right'. Though blank works not likely.
2289 * @see function COM_adminMenu
2292 function COM_userMenu( $help='', $title='', $position='' )
2294 global $_TABLES, $_USER, $_CONF, $LANG01, $LANG04, $_BLOCK_TEMPLATE;
2298 if( !COM_isAnonUser() )
2300 $usermenu = new Template( $_CONF['path_layout'] );
2301 if( isset( $_BLOCK_TEMPLATE['useroption'] ))
2303 $templates = explode( ',', $_BLOCK_TEMPLATE['useroption'] );
2304 $usermenu->set_file( array( 'option' => $templates[0],
2305 'current' => $templates[1] ));
2309 $usermenu->set_file( array( 'option' => 'useroption.thtml',
2310 'current' => 'useroption_off.thtml' ));
2312 $usermenu->set_var( 'xhtml', XHTML );
2313 $usermenu->set_var( 'site_url', $_CONF['site_url'] );
2314 $usermenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2315 $usermenu->set_var( 'layout_url', $_CONF['layout_url'] );
2316 $usermenu->set_var( 'block_name', str_replace( '_', '-', 'user_block' ));
2318 if( empty( $title ))
2320 $title = DB_getItem( $_TABLES['blocks'], 'title',
2321 "name='user_block'" );
2324 // what's our current URL?
2325 $thisUrl = COM_getCurrentURL();
2327 $retval .= COM_startBlock( $title, $help,
2328 COM_getBlockTemplate( 'user_block', 'header', $position ));
2330 // This function will show the user options for all installed plugins
2333 $plugin_options = PLG_getUserOptions();
2334 $nrows = count( $plugin_options );
2336 for( $i = 0; $i < $nrows; $i++ )
2338 $plg = current( $plugin_options );
2339 $usermenu->set_var( 'option_label', $plg->adminlabel );
2341 if( !empty( $plg->numsubmissions ))
2343 $usermenu->set_var( 'option_count', '(' . $plg->numsubmissions . ')' );
2347 $usermenu->set_var( 'option_count', '' );
2350 $usermenu->set_var( 'option_url', $plg->adminurl );
2351 if( $thisUrl == $plg->adminurl )
2353 $retval .= $usermenu->parse( 'item', 'current' );
2357 $retval .= $usermenu->parse( 'item', 'option' );
2359 next( $plugin_options );
2362 $url = $_CONF['site_url'] . '/usersettings.php';
2363 $usermenu->set_var( 'option_label', $LANG01[48] );
2364 $usermenu->set_var( 'option_count', '' );
2365 $usermenu->set_var( 'option_url', $url );
2366 if( $thisUrl == $url )
2368 $retval .= $usermenu->parse( 'item', 'current' );
2372 $retval .= $usermenu->parse( 'item', 'option' );
2375 $url = $_CONF['site_url'] . '/users.php?mode=logout';
2376 $usermenu->set_var( 'option_label', $LANG01[19] );
2377 $usermenu->set_var( 'option_count', '' );
2378 $usermenu->set_var( 'option_url', $url );
2379 $retval .= $usermenu->finish($usermenu->parse('item', 'option'));
2380 $retval .= COM_endBlock(COM_getBlockTemplate('user_block', 'footer', $position));
2384 $retval .= COM_startBlock( $LANG01[47], $help,
2385 COM_getBlockTemplate( 'user_block', 'header', $position ));
2386 $login = new Template( $_CONF['path_layout'] );
2387 $login->set_file( 'form', 'loginform.thtml' );
2388 $login->set_var( 'xhtml', XHTML );
2389 $login->set_var( 'site_url', $_CONF['site_url'] );
2390 $login->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2391 $login->set_var( 'layout_url', $_CONF['layout_url'] );
2392 $login->set_var( 'lang_username', $LANG01[21] );
2393 $login->set_var( 'lang_password', $LANG01[57] );
2394 $login->set_var( 'lang_forgetpassword', $LANG01[119] );
2395 $login->set_var( 'lang_login', $LANG01[58] );
2396 if( $_CONF['disable_new_user_registration'] == 1 )
2398 $login->set_var( 'lang_signup', '' );
2402 $login->set_var( 'lang_signup', $LANG01[59] );
2405 // 3rd party remote authentification.
2406 if ($_CONF['user_login_method']['3rdparty'] && !$_CONF['usersubmission']) {
2407 $modules = SEC_collectRemoteAuthenticationModules();
2408 if (count($modules) == 0) {
2409 $user_templates->set_var('services', '');
2411 if (!$_CONF['user_login_method']['standard'] &&
2412 (count($modules) == 1)) {
2413 $select = '<input type="hidden" name="service" value="'
2414 . $modules[0] . '"' . XHTML . '>' . $modules[0];
2417 $select = '<select name="service" id="service">';
2418 if ($_CONF['user_login_method']['standard']) {
2419 $select .= '<option value="">' . $_CONF['site_name']
2422 foreach ($modules as $service) {
2423 $select .= '<option value="' . $service . '">'
2424 . $service . '</option>';
2426 $select .= '</select>';
2429 $login->set_file('services', 'blockservices.thtml');
2430 $login->set_var('lang_service', $LANG04[121]);
2431 $login->set_var('select_service', $select);
2432 $login->parse('output', 'services');
2433 $login->set_var('services',
2434 $login->finish($login->get_var('output')));
2437 $login->set_var('services', '');
2440 // OpenID remote authentification.
2441 if ($_CONF['user_login_method']['openid'] && ($_CONF['usersubmission'] == 0) && !$_CONF['disable_new_user_registration']) {
2442 $login->set_file('openid_login', 'loginform_openid.thtml');
2443 $login->set_var('lang_openid_login', $LANG01[128]);
2444 $login->set_var('input_field_size', 18);
2445 $login->set_var('app_url', $_CONF['site_url'] . '/users.php');
2446 $login->parse('output', 'openid_login');
2447 $login->set_var('openid_login',
2448 $login->finish($login->get_var('output')));
2450 $login->set_var('openid_login', '');
2453 $retval .= $login->finish($login->parse('output', 'form'));
2454 $retval .= COM_endBlock( COM_getBlockTemplate('user_block', 'footer', $position));
2461 * Prints administration menu
2463 * This will return the administration menu items that the user has
2464 * sufficient rights to -- Admin Block on the left side.
2466 * @param string $help Help file to show
2467 * @param string $title Menu Title
2468 * @param string $position Side being shown on 'left', 'right' or blank.
2469 * @see function COM_userMenu
2472 function COM_adminMenu( $help = '', $title = '', $position = '' )
2474 global $_TABLES, $_USER, $_CONF, $LANG01, $LANG_ADMIN, $_BLOCK_TEMPLATE,
2479 if( empty( $_USER['username'] ))
2484 $plugin_options = PLG_getAdminOptions();
2485 $num_plugins = count( $plugin_options );
2487 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 ))
2489 // what's our current URL?
2490 $thisUrl = COM_getCurrentURL();
2492 $adminmenu = new Template( $_CONF['path_layout'] );
2493 if( isset( $_BLOCK_TEMPLATE['adminoption'] ))
2495 $templates = explode( ',', $_BLOCK_TEMPLATE['adminoption'] );
2496 $adminmenu->set_file( array( 'option' => $templates[0],
2497 'current' => $templates[1] ));
2501 $adminmenu->set_file( array( 'option' => 'adminoption.thtml',
2502 'current' => 'adminoption_off.thtml' ));
2504 $adminmenu->set_var( 'xhtml', XHTML );
2505 $adminmenu->set_var( 'site_url', $_CONF['site_url'] );
2506 $adminmenu->set_var( 'site_admin_url', $_CONF['site_admin_url'] );
2507 $adminmenu->set_var( 'layout_url', $_CONF['layout_url'] );
2508 $adminmenu->set_var( 'block_name', str_replace( '_', '-', 'admin_block' ));
2510 if( empty( $title ))
2512 $title = DB_getItem( $_TABLES['blocks'], 'title',
2513 "name = 'admin_block'" );
2516 $retval .= COM_startBlock( $title, $help,
2517 COM_getBlockTemplate( 'admin_block', 'header', $position ));
2520 if( SEC_isModerator() || SEC_hasRights( 'story.edit' ))
2522 $tresult = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
2523 . COM_getPermSQL() );
2524 $trows = DB_numRows( $tresult );
2528 for( $i = 0; $i < $trows; $i++ )
2530 $T = DB_fetchArray( $tresult );
2531 $tids[] = $T['tid'];
2533 if( count( $tids ) > 0 )
2535 $topicsql = " (tid IN ('" . implode( "','", $tids ) . "'))";
2541 if (SEC_hasRights('story.edit,story.moderate', 'OR') ||
2542 (($_CONF['commentsubmission'] == 1) &&
2543 SEC_hasRights('comment.moderate')) ||
2544 (($_CONF['usersubmission'] == 1) &&
2545 SEC_hasRights('user.edit,user.delete'))) {
2547 if (SEC_hasRights('story.moderate')) {
2548 if (empty($topicsql)) {
2549 $modnum += DB_count($_TABLES['storysubmission']);
2551 $sresult = DB_query("SELECT COUNT(*) AS count FROM {$_TABLES['storysubmission']} WHERE" . $topicsql);
2552 $S = DB_fetchArray($sresult);
2553 $modnum += $S['count'];
2557 if (($_CONF['listdraftstories'] == 1) && SEC_hasRights('story.edit')) {
2558 $sql = "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE (draft_flag = 1)";
2559 if (!empty($topicsql)) {
2560 $sql .= ' AND' . $topicsql;
2562 $result = DB_query($sql . COM_getPermSQL('AND', 0, 3));
2563 $A = DB_fetchArray($result);
2564 $modnum += $A['count'];
2567 if (($_CONF['commentsubmission'] == 1) && SEC_hasRights('comment.moderate')) {
2568 $modnum += DB_count($_TABLES['commentsubmissions']);
2571 if ($_CONF['usersubmission'] == 1) {
2572 if (SEC_hasRights('user.edit') && SEC_hasRights('user.delete')) {
2573 $modnum += DB_count($_TABLES['users'], 'status', '2');
2578 if (SEC_inGroup('Root')) {
2579 $url = $_CONF['site_admin_url'] . '/configuration.php';
2580 $adminmenu->set_var('option_url', $url);
2581 $adminmenu->set_var('option_label', $LANG01[129]);
2582 $adminmenu->set_var('option_count', count($config->_get_groups()));
2583 $menu_item = $adminmenu->parse('item',
2584 ($thisUrl == $url) ? 'current' :
2586 $link_array[$LANG01[129]] = $menu_item;
2590 // now handle submissions for plugins
2591 $modnum += PLG_getSubmissionCount();
2593 if( SEC_hasRights( 'story.edit' ))
2595 $url = $_CONF['site_admin_url'] . '/story.php';
2596 $adminmenu->set_var( 'option_url', $url );
2597 $adminmenu->set_var( 'option_label', $LANG01[11] );
2598 if( empty( $topicsql ))
2600 $numstories = DB_count( $_TABLES['stories'] );
2604 $nresult = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['stories']} WHERE" . $topicsql . COM_getPermSql( 'AND' ));
2605 $N = DB_fetchArray( $nresult );
2606 $numstories = $N['count'];
2608 $adminmenu->set_var( 'option_count',
2609 COM_numberFormat( $numstories ));
2610 $menu_item = $adminmenu->parse( 'item',
2611 ( $thisUrl == $url ) ? 'current' : 'option' );
2612 $link_array[$LANG01[11]] = $menu_item;
2615 if( SEC_hasRights( 'block.edit' ))
2617 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['blocks']}" . COM_getPermSql());
2618 list( $count ) = DB_fetchArray( $result );
2620 $url = $_CONF['site_admin_url'] . '/block.php';
2621 $adminmenu->set_var( 'option_url', $url );
2622 $adminmenu->set_var( 'option_label', $LANG01[12] );
2623 $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
2625 $menu_item = $adminmenu->parse( 'item',
2626 ( $thisUrl == $url ) ? 'current' : 'option' );
2627 $link_array[$LANG01[12]] = $menu_item;
2630 if( SEC_hasRights( 'topic.edit' ))
2632 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['topics']}" . COM_getPermSql());
2633 list( $count ) = DB_fetchArray( $result );
2635 $url = $_CONF['site_admin_url'] . '/topic.php';
2636 $adminmenu->set_var( 'option_url', $url );
2637 $adminmenu->set_var( 'option_label', $LANG01[13] );
2638 $adminmenu->set_var( 'option_count', COM_numberFormat( $count ));
2640 $menu_item = $adminmenu->parse( 'item',
2641 ( $thisUrl == $url ) ? 'current' : 'option' );
2642 $link_array[$LANG01[13]] = $menu_item;
2645 if( SEC_hasRights( 'user.edit' ))
2647 $url = $_CONF['site_admin_url'] . '/user.php';
2648 $adminmenu->set_var( 'option_url', $url );
2649 $adminmenu->set_var( 'option_label', $LANG01[17] );
2650 $adminmenu->set_var( 'option_count',
2651 COM_numberFormat( DB_count( $_TABLES['users'] ) -1 ));
2653 $menu_item = $adminmenu->parse( 'item',
2654 ( $thisUrl == $url ) ? 'current' : 'option' );
2655 $link_array[$LANG01[17]] = $menu_item;
2658 if( SEC_hasRights( 'group.edit' ))
2660 if (SEC_inGroup('Root')) {
2663 $thisUsersGroups = SEC_getUserGroups ();
2664 $grpFilter = 'WHERE (grp_id IN (' . implode (',', $thisUsersGroups) . '))';
2666 $result = DB_query( "SELECT COUNT(*) AS count FROM {$_TABLES['groups']} $grpFilter;" );
2667 $A = DB_fetchArray( $result );
2669 $url = $_CONF['site_admin_url'] . '/group.php';
2670 $adminmenu->set_var( 'option_url', $url );
2671 $adminmenu->set_var( 'option_label', $LANG01[96] );
2672 $adminmenu->set_var( 'option_count',
2673 COM_numberFormat( $A['count'] ));
2675 $menu_item = $adminmenu->parse( 'item',
2676 ( $thisUrl == $url ) ? 'current' : 'option' );
2677 $link_array[$LANG01[96]] = $menu_item;
2680 if( SEC_hasRights( 'user.mail' ))
2682 $url = $_CONF['site_admin_url'] . '/mail.php';
2683 $adminmenu->set_var( 'option_url', $url );
2684 $adminmenu->set_var( 'option_label', $LANG01[105] );
2685 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2687 $menu_item = $adminmenu->parse( 'item',
2688 ( $thisUrl == $url ) ? 'current' : 'option' );
2689 $link_array[$LANG01[105]] = $menu_item;
2692 if(( $_CONF['backend'] == 1 ) && SEC_hasRights( 'syndication.edit' ))
2694 $url = $_CONF['site_admin_url'] . '/syndication.php';
2695 $adminmenu->set_var( 'option_url', $url );
2696 $adminmenu->set_var( 'option_label', $LANG01[38] );
2697 $count = COM_numberFormat( DB_count( $_TABLES['syndication'] ));
2698 $adminmenu->set_var( 'option_count', $count );
2700 $menu_item = $adminmenu->parse( 'item',
2701 ( $thisUrl == $url ) ? 'current' : 'option' );
2702 $link_array[$LANG01[38]] = $menu_item;
2705 if(( $_CONF['trackback_enabled'] || $_CONF['pingback_enabled'] ||
2706 $_CONF['ping_enabled'] ) && SEC_hasRights( 'story.ping' ))
2708 $url = $_CONF['site_admin_url'] . '/trackback.php';
2709 $adminmenu->set_var( 'option_url', $url );
2710 $adminmenu->set_var( 'option_label', $LANG01[116] );
2711 if( $_CONF['ping_enabled'] )
2713 $count = COM_numberFormat( DB_count( $_TABLES['pingservice'] ));
2714 $adminmenu->set_var( 'option_count', $count );
2718 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2721 $menu_item = $adminmenu->parse( 'item',
2722 ( $thisUrl == $url ) ? 'current' : 'option' );
2723 $link_array[$LANG01[116]] = $menu_item;
2726 if (SEC_hasRights('plugin.edit')) {
2727 $url = $_CONF['site_admin_url'] . '/plugins.php';
2728 $adminmenu->set_var('option_url', $url);
2729 $adminmenu->set_var('option_label', $LANG01[77]);
2730 $adminmenu->set_var('option_count',
2731 COM_numberFormat(DB_count($_TABLES['plugins'],
2734 $menu_item = $adminmenu->parse('item',
2735 ($thisUrl == $url) ? 'current' : 'option');
2736 $link_array[$LANG01[77]] = $menu_item;
2739 // This will show the admin options for all installed plugins (if any)
2741 for( $i = 0; $i < $num_plugins; $i++ )
2743 $plg = current( $plugin_options );
2745 $adminmenu->set_var( 'option_url', $plg->adminurl );
2746 $adminmenu->set_var( 'option_label', $plg->adminlabel );
2748 if( empty( $plg->numsubmissions ))
2750 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2754 $adminmenu->set_var( 'option_count',
2755 COM_numberFormat( $plg->numsubmissions ));
2758 $menu_item = $adminmenu->parse( 'item',
2759 ( $thisUrl == $plg->adminurl ) ? 'current' : 'option', true );
2760 $link_array[$plg->adminlabel] = $menu_item;
2762 next( $plugin_options );
2765 if(( $_CONF['allow_mysqldump'] == 1 ) AND ( $_DB_dbms == 'mysql' ) AND
2766 SEC_inGroup( 'Root' ))
2768 $url = $_CONF['site_admin_url'] . '/database.php';
2769 $adminmenu->set_var( 'option_url', $url );
2770 $adminmenu->set_var( 'option_label', $LANG01[103] );
2771 $adminmenu->set_var( 'option_count', $LANG_ADMIN['na'] );
2773 $menu_item = $adminmenu->parse( 'item',
2774 ( $thisUrl == $url ) ? 'current' : 'option' );
2775 $link_array[$LANG01[103]] = $menu_item;
2778 if ($_CONF['link_documentation'] == 1) {
2779 $doclang = COM_getLanguageName();
2780 $docs = 'docs/' . $doclang . '/index.html';
2781 if (file_exists($_CONF['path_html'] . $docs)) {
2782 $adminmenu->set_var('option_url', $_CONF['site_url']
2785 $adminmenu->set_var('option_url', $_CONF['site_url']
2786 . '/docs/english/index.html');
2788 $adminmenu->set_var('option_label', $LANG01[113]);
2789 $adminmenu->set_var('option_count', $LANG_ADMIN['na']);
2790 $menu_item = $adminmenu->parse('item', 'option');
2791 $link_array[$LANG01[113]] = $menu_item;
2794 if( $_CONF['link_versionchecker'] == 1 AND SEC_inGroup( 'Root' ))
2796 $adminmenu->set_var( 'option_url',
2797 'http://www.geeklog.net/versionchecker.php?version=' . VERSION );
2798 $adminmenu->set_var( 'option_label', $LANG01[107] );
2799 $adminmenu->set_var( 'option_count', VERSION );
2801 $menu_item = $adminmenu->parse( 'item', 'option' );
2802 $link_array[$LANG01[107]] = $menu_item;
2805 if( $_CONF['sort_admin'] )
2807 uksort( $link_array, 'strcasecmp' );
2810 $url = $_CONF['site_admin_url'] . '/moderation.php';
2811 $adminmenu->set_var('option_url', $url);
2812 $adminmenu->set_var('option_label', $LANG01[10]);
2813 $adminmenu->set_var('option_count', COM_numberFormat($modnum));
2814 $menu_item = $adminmenu->finish($adminmenu->parse('item',
2815 ($thisUrl == $url) ? 'current' : 'option'));
2816 $link_array = array($menu_item) + $link_array;
2818 foreach( $link_array as $link )
2823 $retval .= COM_endBlock( COM_getBlockTemplate( 'admin_block', 'footer', $position ));
2830 * Redirects user to a given URL
2832 * This function does a redirect using a meta refresh. This is (or at least
2833 * used to be) more compatible than using a HTTP Location: header.
2835 * NOTE: This does not need to be XHTML compliant. It may also be used
2836 * in situations where the XHTML constant is not defined yet ...
2838 * @param string $url URL to send user to
2839 * @return string HTML meta redirect
2842 function COM_refresh($url)
2844 return "<html><head><meta http-equiv=\"refresh\" content=\"0; URL=$url\"></head></html>\n";
2848 * DEPRECIATED -- see CMT_userComments in lib-comment.php
2849 * @deprecated since Geeklog 1.4.0
2850 * @see CMT_userComments
2852 function COM_userComments( $sid, $title, $type='article', $order='', $mode='', $pid = 0, $page = 1, $cid = false, $delete_option = false )
2856 require_once $_CONF['path_system'] . 'lib-comment.php';
2858 return CMT_userComments( $sid, $title, $type, $order, $mode, $pid, $page, $cid, $delete_option );
2862 * This censors inappropriate content
2864 * This will replace 'bad words' with something more appropriate
2866 * @param string $Message String to check
2867 * @see function COM_checkHTML
2868 * @return string Edited $Message
2872 function COM_checkWords( $Message )
2876 $EditedMessage = $Message;
2878 if( $_CONF['censormode'] != 0 )
2880 if( is_array( $_CONF['censorlist'] ))
2882 $Replacement = $_CONF['censorreplace'];
2884 switch( $_CONF['censormode'])
2886 case 1: # Exact match
2887 $RegExPrefix = '(\s*)';
2888 $RegExSuffix = '(\W*)';
2891 case 2: # Word beginning
2892 $RegExPrefix = '(\s*)';
2893 $RegExSuffix = '(\w*)';
2896 case 3: # Word fragment
2897 $RegExPrefix = '(\w*)';
2898 $RegExSuffix = '(\w*)';
2902 foreach ($_CONF['censorlist'] as $c) {
2904 $EditedMessage = MBYTE_eregi_replace($RegExPrefix . $c
2905 . $RegExSuffix, "\\1$Replacement\\2", $EditedMessage);
2911 return $EditedMessage;
2915 * Takes some amount of text and replaces all javascript events on*= with in
2917 * This script takes some amount of text and matches all javascript events, on*= (onBlur= onMouseClick=)
2918 * and replaces them with in*=
2919 * Essentially this will cause onBlur to become inBlur, onFocus to be inFocus
2920 * These are not valid javascript events and the browser will ignore them.
2921 * @param string $Message Text to filter
2922 * @return string $Message with javascript filtered
2923 * @see COM_checkWords
2924 * @see COM_checkHTML
2928 function COM_killJS( $Message )
2930 return( preg_replace( '/(\s)+[oO][nN](\w*) ?=/', '\1in\2=', $Message ));
2934 * Handles the part within a [code] ... [/code] section, i.e. escapes all
2935 * special characters.
2937 * @param string $str the code section to encode
2938 * @return string $str with the special characters encoded
2939 * @see COM_checkHTML
2942 function COM_handleCode( $str )
2944 $search = array( '&', '\\', '<', '>', '[', ']' );
2945 $replace = array( '&', '\', '<', '>', '[', ']' );
2947 $str = str_replace( $search, $replace, $str );
2953 * This function checks html tags.
2955 * Checks to see that the HTML tags are on the approved list and
2956 * removes them if not.
2958 * @param string $str HTML to check
2959 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
2960 * @return string Filtered HTML
2963 function COM_checkHTML( $str, $permissions = 'story.edit' )
2967 // replace any \ with \ (HTML equiv)
2968 $str = str_replace('\\', '\', COM_stripslashes($str) );
2970 // Get rid of any newline characters
2971 $str = preg_replace( "/\n/", '', $str );
2973 // Replace any $ with $ (HTML equiv)
2974 $str = str_replace( '$', '$', $str );
2975 // handle [code] ... [/code]
2978 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[code]' );
2979 if( $start_pos !== false )
2981 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/code]' );
2982 if( $end_pos !== false )
2984 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6,
2985 $end_pos - ( $start_pos + 6 )));
2986 $encoded = '<pre><code>' . $encoded . '</code></pre>';
2987 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
2988 . MBYTE_substr( $str, $end_pos + 7 );
2990 else // missing [/code]
2992 // Treat the rest of the text as code (so as not to lose any
2993 // special characters). However, the calling entity should
2994 // better be checking for missing [/code] before calling this
2996 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 6 ));
2997 $encoded = '<pre><code>' . $encoded . '</code></pre>';
2998 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3002 while( $start_pos !== false );
3004 // handle [raw] ... [/raw]
3007 $start_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[raw]' );
3008 if( $start_pos !== false )
3010 $end_pos = MBYTE_strpos( MBYTE_strtolower( $str ), '[/raw]' );
3011 if( $end_pos !== false )
3013 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5,
3014 $end_pos - ( $start_pos + 5 )));
3015 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3017 $encoded = '[raw2]' . $encoded . '[/raw2]';
3018 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded
3019 . MBYTE_substr( $str, $end_pos + 6 );
3021 else // missing [/raw]
3023 // Treat the rest of the text as raw (so as not to lose any
3024 // special characters). However, the calling entity should
3025 // better be checking for missing [/raw] before calling this
3027 $encoded = COM_handleCode( MBYTE_substr( $str, $start_pos + 5 ));
3028 // [raw2] to avoid infinite loop. Not HTML comment as we strip
3030 $encoded = '[raw2]' . $encoded . '[/raw2]';
3031 $str = MBYTE_substr( $str, 0, $start_pos ) . $encoded;
3035 while( $start_pos !== false );
3037 if( isset( $_CONF['skip_html_filter_for_root'] ) &&
3038 ( $_CONF['skip_html_filter_for_root'] == 1 ) &&
3039 SEC_inGroup( 'Root' ))
3044 // strip_tags() gets confused by HTML comments ...
3045 $str = preg_replace( '/<!--.+?-->/', '', $str );
3047 $filter = new kses4;
3048 if( isset( $_CONF['allowed_protocols'] ) && is_array( $_CONF['allowed_protocols'] ) && ( count( $_CONF['allowed_protocols'] ) > 0 ))
3050 $filter->SetProtocols( $_CONF['allowed_protocols'] );
3054 $filter->SetProtocols( array( 'http:', 'https:', 'ftp:' ));
3057 if( empty( $permissions) || !SEC_hasRights( $permissions ) ||
3058 empty( $_CONF['admin_html'] ))
3060 $html = $_CONF['user_html'];
3064 if ($_CONF['advanced_editor'] && is_array($_CONF['advanced_html'])) {
3065 $html = array_merge_recursive( $_CONF['user_html'],
3066 $_CONF['admin_html'],
3067 $_CONF['advanced_html'] );
3069 $html = array_merge_recursive( $_CONF['user_html'],
3070 $_CONF['admin_html'] );
3074 foreach( $html as $tag => $attr )
3076 $filter->AddHTML( $tag, $attr );
3078 /* Replace [raw][/raw] with <!--raw--><!--/raw-->, note done "late" because
3079 * of the above noted // strip_tags() gets confused by HTML comments ...
3081 $str = $filter->Parse( $str );
3082 $str = str_replace('[raw2]','<!--raw--><span class="raw">', $str);
3083 $str = str_replace('[/raw2]','</span><!--/raw-->', $str);
3089 * undo function for htmlspecialchars()
3091 * This function translates HTML entities created by htmlspecialchars() back
3092 * into their ASCII equivalents. Also handles the entities for $, {, and }.
3094 * @param string $string The string to convert.
3095 * @return string The converted string.
3098 function COM_undoSpecialChars( $string )
3100 $string = str_replace( '$', '$', $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 );
3113 * Makes an ID based on current date/time
3115 * This function creates a 17 digit sid for stories based on the 14 digit date
3116 * and a 3 digit random number that was seeded with the number of microseconds
3117 * (.000001th of a second) since the last full second.
3118 * NOTE: this is now used for more than just stories!
3120 * @return string $sid Story ID
3123 function COM_makesid()
3125 $sid = date( 'YmdHis' );
3126 $sid .= rand( 0, 999 );
3132 * Checks to see if email address is valid.
3134 * This function checks to see if an email address is in the correct from.
3136 * @param string $email Email address to verify
3137 * @return boolean True if valid otherwise false
3140 function COM_isEmail( $email )
3142 require_once( 'Mail/RFC822.php' );
3144 $rfc822 = new Mail_RFC822;
3146 return( $rfc822->isValidInetAddress( $email ) ? true : false );
3151 * Encode a string such that it can be used in an email header
3153 * @param string $string the text to be encoded
3154 * @return string encoded text
3157 function COM_emailEscape( $string )
3161 if (function_exists('CUSTOM_emailEscape')) {
3162 return CUSTOM_emailEscape($string);
3165 $charset = COM_getCharset();
3166 if(( $charset == 'utf-8' ) && ( $string != utf8_decode( $string )))
3168 if( function_exists( 'iconv_mime_encode' ))
3170 $mime_parameters = array( 'input-charset' => 'utf-8',
3171 'output-charset' => 'utf-8',
3172 // 'Q' encoding is more readable than 'B'
3175 $string = substr( iconv_mime_encode( '', $string,
3176 $mime_parameters ), 2 );
3180 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3183 else if( preg_match( '/[^0-9a-z\-\.,:;\?! ]/i', $string ))
3185 $string = '=?' . $charset . '?B?' . base64_encode( $string ) . '?=';
3192 * Takes a name and an email address and returns a string that vaguely
3193 * resembles an email address specification conforming to RFC(2)822 ...
3195 * @param string $name name, e.g. John Doe
3196 * @param string $address email address only, e.g. john.doe@example.com
3197 * @return string formatted email address
3200 function COM_formatEmailAddress($name, $address)
3202 $name = trim($name);
3203 $address = trim($address);
3205 if (function_exists('CUSTOM_formatEmailAddress')) {
3206 return CUSTOM_formatEmailAddress($name, $address);
3209 $formatted_name = COM_emailEscape($name);
3211 // if the name comes back unchanged, it's not UTF-8, so preg_match is fine
3212 if (($formatted_name == $name) && preg_match('/[^0-9a-z ]/i', $name)) {
3213 $formatted_name = str_replace('"', '\\"', $formatted_name);
3214 $formatted_name = '"' . $formatted_name . '"';
3217 return $formatted_name . ' <' . $address . '>';
3223 * All emails sent by Geeklog are sent through this function.
3225 * NOTE: Please note that using CC: will expose the email addresses of
3226 * all recipients. Use with care.
3228 * @param string $to recipients name and email address
3229 * @param string $subject subject of the email
3230 * @param string $message the text of the email
3231 * @param string $from (optional) sender of the the email
3232 * @param boolean $html (optional) true if to be sent as HTML email
3233 * @param int $priority (optional) add X-Priority header, if > 0
3234 * @param mixed $optional (optional) other headers or CC:
3235 * @return boolean true if successful, otherwise false
3238 function COM_mail($to, $subject, $message, $from = '', $html = false, $priority = 0, $optional = null)
3245 $from = COM_formatEmailAddress($_CONF['site_name'], $_CONF['site_mail']);
3248 $to = substr($to, 0, strcspn($to, "\r\n"));
3249 if (($optional != null) && !is_array($optional)) {
3250 $optional = substr($optional, 0, strcspn($optional, "\r\n"));
3252 $from = substr($from, 0, strcspn($from, "\r\n"));
3253 $subject = substr($subject, 0, strcspn($subject, "\r\n"));
3254 $subject = COM_emailEscape($subject);
3256 if (function_exists('CUSTOM_mail')) {
3257 return CUSTOM_mail($to, $subject, $message, $from, $html, $priority,
3261 include_once 'Mail.php';
3262 include_once 'Mail/RFC822.php';
3264 $method = $_CONF['mail_settings']['backend'];
3266 if (! isset($mailobj)) {
3267 if (($method == 'sendmail') || ($method == 'smtp')) {
3268 $mailobj =& Mail::factory($method, $_CONF['mail_settings']);
3271 $mailobj =& Mail::factory($method);
3275 $charset = COM_getCharset();
3278 $headers['From'] = $from;
3279 if ($method != 'mail') {
3280 $headers['To'] = $to;
3282 if (($optional != null) && !is_array($optional) && !empty($optional)) {
3283 // assume old (optional) CC: header
3284 $headers['Cc'] = $optional;
3286 $headers['Date'] = date('r'); // RFC822 formatted date
3287 if($method == 'smtp') {
3288 list($usec, $sec) = explode(' ', microtime());
3289 $m = substr($usec, 2, 5);
3290 $headers['Message-Id'] = '<' . date('YmdHis') . '.' . $m
3291 . '@' . $_CONF['mail_settings']['host'] . '>';
3294 $headers['Content-Type'] = 'text/html; charset=' . $charset;
3295 $headers['Content-Transfer-Encoding'] = '8bit';
3297 $headers['Content-Type'] = 'text/plain; charset=' . $charset;
3299 $headers['Subject'] = $subject;
3300 if ($priority > 0) {
3301 $headers['X-Priority'] = $priority;
3303 $headers['X-Mailer'] = 'Geeklog ' . VERSION;
3305 if (!empty($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['SERVER_ADDR']) &&
3306 ($_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR'])) {
3307 $url = COM_getCurrentURL();
3308 if (substr($url, 0, strlen($_CONF['site_admin_url']))
3309 != $_CONF['site_admin_url']) {
3310 $headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
3314 // add optional headers last
3315 if (($optional != null) && is_array($optional)) {
3316 foreach ($optional as $h => $v) {
3321 $retval = $mailobj->send($to, $headers, $message);
3322 if ($retval !== true) {
3323 COM_errorLog($retval->toString(), 1);
3326 return($retval === true ? true : false);
3331 * Creates older stuff block
3333 * Creates the olderstuff block for display.
3334 * Actually updates the olderstuff record in the gl_blocks database.
3338 function COM_olderStuff()
3340 global $_TABLES, $_CONF;
3342 $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']}";
3343 $result = DB_query( $sql );
3344 $nrows = DB_numRows( $result );
3348 $dateonly = $_CONF['dateonly'];
3349 if( empty( $dateonly ))
3351 $dateonly = '%d-%b'; // fallback: day - abbrev. month name
3357 for( $i = 0; $i < $nrows; $i++ )
3359 $A = DB_fetchArray( $result );
3361 $daycheck = strftime( '%A', $A['day'] );
3362 if( $day != $daycheck )
3364 if( $day != 'noday' )
3366 $daylist = COM_makeList($oldnews, 'list-older-stories');
3367 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3368 $string .= $daylist . '<br' . XHTML . '>';
3371 $day2 = strftime( $dateonly, $A['day'] );
3372 $string .= '<h3>' . $daycheck . ' <small>' . $day2
3373 . '</small></h3>' . LB;
3378 $oldnews_url = COM_buildUrl( $_CONF['site_url'] . '/article.php?story='
3380 $oldnews[] = COM_createLink($A['title'], $oldnews_url)
3381 .' (' . COM_numberFormat( $A['comments'] ) . ')';
3384 if( !empty( $oldnews ))
3386 $daylist = COM_makeList($oldnews, 'list-older-stories');
3387 $daylist = str_replace(array("\015", "\012"), '', $daylist);
3388 $string .= $daylist;
3389 $string = addslashes( $string );
3391 DB_query( "UPDATE {$_TABLES['blocks']} SET content = '$string' WHERE name = 'older_stories'" );
3397 * Shows a single Geeklog block
3399 * This shows a single block and is typically called from
3400 * COM_showBlocks OR from plugin code
3402 * @param string $name Logical name of block (not same as title) -- 'user_block', 'admin_block', 'section_block', 'whats_new_block'.
3403 * @param string $help Help file location
3404 * @param string $title Title shown in block header
3405 * @param string $position Side, 'left', 'right' or empty.
3406 * @see function COM_showBlocks
3407 * @return string HTML Formated block
3411 function COM_showBlock( $name, $help='', $title='', $position='' )
3413 global $_CONF, $topic, $_TABLES, $_USER;
3417 if( !isset( $_USER['noboxes'] ))
3419 if( !COM_isAnonUser() )
3421 $_USER['noboxes'] = DB_getItem( $_TABLES['userindex'], 'noboxes',
3422 "uid = {$_USER['uid']}" );
3426 $_USER['noboxes'] = 0;
3433 $retval .= COM_userMenu( $help,$title, $position );
3437 $retval .= COM_adminMenu( $help,$title, $position );
3440 case 'section_block':
3441 $retval .= COM_startBlock( $title, $help,
3442 COM_getBlockTemplate( $name, 'header', $position ))
3443 . COM_showTopics( $topic )
3444 . COM_endBlock( COM_getBlockTemplate( $name, 'footer', $position ));
3447 case 'whats_new_block':
3448 if( !$_USER['noboxes'] )
3450 $retval .= COM_whatsNewBlock( $help, $title, $position );
3460 * Shows Geeklog blocks
3462 * Returns HTML for blocks on a given side and, potentially, for
3463 * a given topic. Currently only used by static pages.
3465 * @param string $side Side to get blocks for (right or left for now)
3466 * @param string $topic Only get blocks for this topic
3467 * @param string $name Block name (not used)
3468 * @see function COM_showBlock
3469 * @return string HTML Formated blocks
3473 function COM_showBlocks( $side, $topic='', $name='all' )
3475 global $_CONF, $_TABLES, $_USER, $LANG21, $topic, $page;
3479 // Get user preferences on blocks
3480 if( !isset( $_USER['noboxes'] ) || !isset( $_USER['boxes'] ))
3482 if( !COM_isAnonUser() )
3484 $result = DB_query( "SELECT boxes,noboxes FROM {$_TABLES['userindex']} "
3485 ."WHERE uid = '{$_USER['uid']}'" );
3486 list($_USER['boxes'], $_USER['noboxes']) = DB_fetchArray( $result );
3490 $_USER['boxes'] = '';
3491 $_USER['noboxes'] = 0;
3495 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3496 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3497 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3499 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3501 $commonsql = "FROM {$_TABLES['blocks']} WHERE is_enabled = 1";
3503 if( $side == 'left' )
3505 $commonsql .= " AND onleft = 1";
3509 $commonsql .= " AND onleft = 0";
3512 if( !empty( $topic ))
3514 $tp = addslashes($topic);
3515 $commonsql .= " AND (tid = '$tp' OR tid = 'all')";
3519 if( COM_onFrontpage() )
3521 $commonsql .= " AND (tid = 'homeonly' OR tid = 'all')";
3525 $commonsql .= " AND (tid = 'all')";
3529 if( !empty( $_USER['boxes'] ))
3531 $BOXES = str_replace( ' ', ',', $_USER['boxes'] );
3533 $commonsql .= " AND (bid NOT IN ($BOXES) OR bid = '-1')";
3536 $commonsql .= ' ORDER BY blockorder,title ASC';
3538 $blocksql['mysql'] .= $commonsql;
3539 $blocksql['mssql'] .= $commonsql;
3540 $result = DB_query( $blocksql );
3541 $nrows = DB_numRows( $result );
3543 // convert result set to an array of associated arrays
3545 for( $i = 0; $i < $nrows; $i++ )
3547 $blocks[] = DB_fetchArray( $result );
3550 // Check and see if any plugins have blocks to show
3551 $pluginBlocks = PLG_getBlocks( $side, $topic, $name );
3552 $blocks = array_merge( $blocks, $pluginBlocks );
3554 // sort the resulting array by block order
3555 $column = 'blockorder';
3556 $sortedBlocks = $blocks;
3557 $num_sortedBlocks = count( $sortedBlocks );
3558 for( $i = 0; $i < $num_sortedBlocks - 1; $i++ )
3560 for( $j = 0; $j < $num_sortedBlocks - 1 - $i; $j++ )
3562 if( $sortedBlocks[$j][$column] > $sortedBlocks[$j+1][$column] )
3564 $tmp = $sortedBlocks[$j];
3565 $sortedBlocks[$j] = $sortedBlocks[$j + 1];
3566 $sortedBlocks[$j + 1] = $tmp;
3570 $blocks = $sortedBlocks;
3572 // Loop though resulting sorted array and pass associative arrays
3573 // to COM_formatBlock
3574 foreach( $blocks as $A )
3576 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 )
3578 $retval .= COM_formatBlock( $A, $_USER['noboxes'] );
3586 * Formats a Geeklog block
3588 * This shows a single block and is typically called from
3589 * COM_showBlocks OR from plugin code
3591 * @param array $A Block Record
3592 * @param boolean $noboxes Set to true if userpref is no blocks
3593 * @return string HTML Formated block
3596 function COM_formatBlock( $A, $noboxes = false )
3598 global $_CONF, $_TABLES, $_USER, $LANG21;
3602 $lang = COM_getLanguageId();
3603 if (!empty($lang)) {
3605 $blocksql['mssql'] = "SELECT bid, is_enabled, name, type, title, tid, blockorder, cast(content as text) as content, ";
3606 $blocksql['mssql'] .= "rdfurl, rdfupdated, rdflimit, onleft, phpblockfn, help, owner_id, ";
3607 $blocksql['mssql'] .= "group_id, perm_owner, perm_group, perm_members, perm_anon, allow_autotags,UNIX_TIMESTAMP(rdfupdated) AS date ";
3609 $blocksql['mysql'] = "SELECT *,UNIX_TIMESTAMP(rdfupdated) AS date ";
3611 $commonsql = "FROM {$_TABLES['blocks']} WHERE name = '"
3612 . $A['name'] . '_' . $lang . "'";
3614 $blocksql['mysql'] .= $commonsql;
3615 $blocksql['mssql'] .= $commonsql;
3616 $result = DB_query( $blocksql );
3618 if (DB_numRows($result) == 1) {
3619 // overwrite with data for language-specific block
3620 $A = DB_fetchArray($result);
3624 if( array_key_exists( 'onleft', $A ) )
3626 if( $A['onleft'] == 1 )
3630 $position = 'right';
3636 if( $A['type'] == 'portal' )
3638 if( COM_rdfCheck( $A['bid'], $A['rdfurl'], $A['date'], $A['rdflimit'] ))
3640 $A['content'] = DB_getItem( $_TABLES['blocks'], 'content',
3641 "bid = '{$A['bid']}'");
3645 if( $A['type'] == 'gldefault' )
3647 $retval .= COM_showBlock( $A['name'], $A['help'], $A['title'], $position );
3650 if( $A['type'] == 'phpblock' && !$noboxes )
3652 if( !( $A['name'] == 'whosonline_block' AND DB_getItem( $_TABLES['blocks'], 'is_enabled', "name='whosonline_block'" ) == 0 ))
3654 $function = $A['phpblockfn'];
3656 if (preg_match('/^(phpblock_\w*)\\((.*)\\)$/', $function, $matches) == 1)
3658 $function = $matches[1];
3659 $args = $matches[2];
3661 $blkheader = COM_startBlock( $A['title'], $A['help'],
3662 COM_getBlockTemplate( $A['name'], 'header', $position ));
3663 $blkfooter = COM_endBlock( COM_getBlockTemplate( $A['name'],
3664 'footer', $position ));
3666 if( function_exists( $function ))
3670 $fretval = $function($A, $args);
3672 $fretval = $function();
3674 if( !empty( $fretval ))
3676 $retval .= $blkheader;
3677 $retval .= $fretval;
3678 $retval .= $blkfooter;
3683 // show error message
3684 $retval .= $blkheader;
3685 $retval .= sprintf( $LANG21[31], $function );
3686 $retval .= $blkfooter;
3691 if( !empty( $A['content'] ) && ( trim( $A['content'] ) != '' ) && !$noboxes )
3693 $blockcontent = stripslashes( $A['content'] );
3695 // Hack: If the block content starts with a '<' assume it
3696 // contains HTML and do not call nl2br() which would only add
3697 // unwanted <br> tags.
3699 if( substr( $blockcontent, 0, 1 ) != '<' )
3701 $blockcontent = nl2br( $blockcontent );
3704 // autotags are only(!) allowed in normal blocks
3705 if(( $A['allow_autotags'] == 1 ) && ( $A['type'] == 'normal' ))
3707 $blockcontent = PLG_replaceTags( $blockcontent );
3709 $blockcontent = str_replace( array( '<?', '?>' ), '', $blockcontent );
3711 $retval .= COM_startBlock( $A['title'], $A['help'],
3712 COM_getBlockTemplate( $A['name'], 'header', $position ))
3713 . $blockcontent . LB
3714 . COM_endBlock( COM_getBlockTemplate( $A['name'], 'footer', $position ));
3722 * Checks to see if it's time to import and RDF/RSS block again
3724 * Updates RDF/RSS block if needed
3726 * @param string $bid Block ID
3727 * @param string $rdfurl URL to get headlines from
3728 * @param string $date Last time the headlines were imported
3729 * @param string $maxheadlines max. number of headlines to import
3731 * @see function COM_rdfImport
3734 function COM_rdfCheck( $bid, $rdfurl, $date, $maxheadlines = 0 )
3737 $nextupdate = $date + 3600;
3739 if( $nextupdate < time() )
3741 COM_rdfImport( $bid, $rdfurl, $maxheadlines );
3749 * Syndication import function. Imports headline data to a portal block.
3751 * Rewritten December 19th 2004 by Michael Jervis (mike AT fuckingbrit DOT com).
3752 * Now utilises a Factory Pattern to open a URL and automaticaly retreive a feed
3753 * object populated with feed data. Then import it into the portal block.
3755 * @param string $bid Block ID
3756 * @param string $rdfurl URL to get content from
3757 * @param int $maxheadlines Maximum number of headlines to display
3759 * @see function COM_rdfCheck
3762 function COM_rdfImport($bid, $rdfurl, $maxheadlines = 0)
3764 global $_CONF, $_TABLES, $LANG21;
3766 // Import the feed handling classes:
3767 require_once $_CONF['path_system']
3768 . '/classes/syndication/parserfactory.class.php';
3769 require_once $_CONF['path_system']
3770 . '/classes/syndication/feedparserbase.class.php';
3772 $result = DB_query("SELECT rdf_last_modified, rdf_etag FROM {$_TABLES['blocks']} WHERE bid = $bid");
3773 list($last_modified, $etag) = DB_fetchArray($result);
3775 // Load the actual feed handlers:
3776 $factory = new FeedParserFactory($_CONF['path_system']
3777 . '/classes/syndication/');
3778 $factory->userAgent = 'Geeklog/' . VERSION;
3779 if (!empty($last_modified) && !empty($etag)) {
3780 $factory->lastModified = $last_modified;
3781 $factory->eTag = $etag;
3785 $feed = $factory->reader($rdfurl, $_CONF['default_charset']);
3788 /* We have located a reader, and populated it with the information from
3789 * the syndication file. Now we will sort out our display, and update
3792 if ($maxheadlines == 0) {
3793 if (!empty($_CONF['syndication_max_headlines'])) {
3794 $maxheadlines = $_CONF['syndication_max_headlines'];
3796 $maxheadlines = count($feed->articles);
3800 $update = date('Y-m-d H:i:s');
3801 $last_modified = '';
3802 if (!empty($factory->lastModified)) {
3803 $last_modified = addslashes($factory->lastModified);
3806 if (!empty($factory->eTag)) {
3807 $etag = addslashes($factory->eTag);
3810 if (empty($last_modified) || empty($etag)) {
3811 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = '$bid'");
3813 DB_query("UPDATE {$_TABLES['blocks']} SET rdfupdated = '$update', rdf_last_modified = '$last_modified', rdf_etag = '$etag' WHERE bid = '$bid'");
3816 $charset = COM_getCharset();
3818 // format articles for display
3819 $readmax = min($maxheadlines, count($feed->articles));
3820 for ($i = 0; $i < $readmax; $i++) {
3821 if (empty($feed->articles[$i]['title'])) {
3822 $feed->articles[$i]['title'] = $LANG21[61];
3825 if ($charset == 'utf-8') {
3826 $title = $feed->articles[$i]['title'];
3828 $title = utf8_decode($feed->articles[$i]['title']);
3830 if ($feed->articles[$i]['link'] != '') {
3831 $content = COM_createLink($title, $feed->articles[$i]['link']);
3832 } elseif ($feed->articles[$i]['enclosureurl'] != '') {
3833 $content = COM_createLink($title, $feed->articles[$i]['enclosureurl']);
3837 $articles[] = $content;
3841 $content = COM_makeList($articles, 'list-feed');
3842 $content = str_replace(array("\015", "\012"), '', $content);
3844 if (strlen($content) > 65000) {
3845 $content = $LANG21[68];
3848 // Standard theme based function to put it in the block
3849 $result = DB_change($_TABLES['blocks'], 'content',
3850 addslashes($content), 'bid', $bid);
3851 } else if ($factory->errorStatus !== false) {
3852 // failed to aquire info, 0 out the block and log an error
3853 COM_errorLog("Unable to aquire feed reader for $rdfurl", 1);
3854 COM_errorLog($factory->errorStatus[0] . ' ' .
3855 $factory->errorStatus[1] . ' ' .
3856 $factory->errorStatus[2]);
3857 $content = addslashes($LANG21[4]);
3858 DB_query("UPDATE {$_TABLES['blocks']} SET content = '$content', rdf_last_modified = NULL, rdf_etag = NULL WHERE bid = $bid");
3864 * Returns what HTML is allowed in content
3866 * Returns what HTML tags the system allows to be used inside content.
3867 * You can modify this by changing $_CONF['user_html'] in the configuration
3868 * (for admins, see also $_CONF['admin_html']).
3870 * @param string $permissions comma-separated list of rights which identify the current user as an "Admin"
3871 * @param boolean $list_only true = return only the list of HTML tags
3872 * @return string HTML <div>/<span> enclosed string
3873 * @see function COM_checkHTML
3874 * @todo Bugs: The list always includes the [code], [raw], and [page_break]
3875 * tags when story.* permissions are required, even when those tags
3876 * are not actually available (e.g. in comments on stories).
3879 function COM_allowedHTML($permissions = 'story.edit', $list_only = false)
3881 global $_CONF, $LANG01;
3885 if (isset($_CONF['skip_html_filter_for_root']) &&
3886 ($_CONF['skip_html_filter_for_root'] == 1) &&
3887 SEC_inGroup('Root')) {
3890 $retval .= '<span class="warningsmall">' . $LANG01[123]
3893 $retval .= '<div dir="ltr" class="warningsmall">';
3898 $retval .= '<span class="warningsmall">' . $LANG01[31] . '</span> ';
3901 if (empty($permissions) || !SEC_hasRights($permissions) ||
3902 empty($_CONF['admin_html'])) {
3903 $html = $_CONF['user_html'];
3905 $html = array_merge_recursive($_CONF['user_html'],
3906 $_CONF['admin_html']);
3909 $retval .= '<div dir="ltr" class="warningsmall">';
3910 foreach ($html as $tag => $attr) {
3911 $retval .= '<' . $tag . '>, ';
3915 $with_story_perms = false;
3916 $perms = explode(',', $permissions);
3917 foreach ($perms as $p) {
3918 if (substr($p, 0, 6) == 'story.') {
3919 $with_story_perms = true;
3924 if ($with_story_perms) {
3925 $retval .= '[code], [raw], ';
3927 if ($_CONF['allow_page_breaks'] == 1) {
3928 $retval .= '[page_break], ';
3933 $autotags = array_keys(PLG_collectTags());
3934 $retval .= '[' . implode(':], [', $autotags) . ':]';
3935 $retval .= '</div>';
3941 * Return the password for the given username
3943 * Fetches a password for the given user
3945 * @param string $loginname username to get password for
3946 * @return string Password or ''
3950 function COM_getPassword( $loginname )
3952 global $_TABLES, $LANG01;
3954 $result = DB_query( "SELECT passwd FROM {$_TABLES['users']} WHERE username='$loginname'" );
3956 $nrows = DB_numRows( $result );
3958 if(( $tmp == 0 ) && ( $nrows == 1 ))
3960 $U = DB_fetchArray( $result );
3961 return $U['passwd'];
3965 $tmp = $LANG01[32] . ": '" . $loginname . "'";
3966 COM_errorLog( $tmp, 1 );
3974 * Return the username or fullname for the passed member id (uid)
3976 * Allows the siteAdmin to determine if loginname (username) or fullname
3977 * should be displayed.
3979 * @param int $uid site member id
3980 * @param string $username Username, if this is set no lookup is done.
3981 * @param string $fullname Users full name.
3982 * @param string $remoteusername Username on remote service
3983 * @param string $remoteservice Remote login service.
3984 * @return string Username, fullname or username@Service
3987 function COM_getDisplayName( $uid = '', $username='', $fullname='', $remoteusername='', $remoteservice='' )
3989 global $_CONF, $_TABLES, $_USER;
3993 if( COM_isAnonUser() )
3999 $uid = $_USER['uid'];
4003 if( empty( $username ))
4005 $query = DB_query( "SELECT username, fullname, remoteusername, remoteservice FROM {$_TABLES['users']} WHERE uid='$uid'" );
4006 list( $username, $fullname, $remoteusername, $remoteservice ) = DB_fetchArray( $query );
4009 if( !empty( $fullname ) && ($_CONF['show_fullname'] == 1 ))
4013 else if(( $_CONF['user_login_method']['3rdparty'] || $_CONF['user_login_method']['openid'] ) && !empty( $remoteusername ))
4015 if( !empty( $username ))
4017 $remoteusername = $username;
4020 if( $_CONF['show_servicename'] )
4022 return "$remoteusername@$remoteservice";
4026 return $remoteusername;
4035 * Adds a hit to the system
4037 * This function is called in the footer of every page and is used to
4038 * track the number of hits to the Geeklog system. This information is
4039 * shown on stats.php
4047 DB_change($_TABLES['vars'], 'value', 'value + 1', 'name', 'totalhits', '', true);
4051 * This will email new stories in the topics that the user is interested in
4053 * In account information the user can specify which topics for which they
4054 * will receive any new article for in a daily digest.
4059 function COM_emailUserTopics()
4061 global $_CONF, $_TABLES, $LANG04, $LANG08, $LANG24;
4063 if ($_CONF['emailstories'] == 0) {
4067 $subject = strip_tags( $_CONF['site_name'] . $LANG08[30] . strftime( '%Y-%m-%d', time() ));
4071 // Get users who want stories emailed to them
4072 $usersql = "SELECT username,email,etids,{$_TABLES['users']}.uid AS uuid "
4073 . "FROM {$_TABLES['users']}, {$_TABLES['userindex']} "
4074 . "WHERE {$_TABLES['users']}.uid > 1 AND {$_TABLES['userindex']}.uid = {$_TABLES['users']}.uid AND (etids <> '-' OR etids IS NULL) ORDER BY {$_TABLES['users']}.uid";
4076 $users = DB_query( $usersql );
4077 $nrows = DB_numRows( $users );
4079 $lastrun = DB_getItem( $_TABLES['vars'], 'value', "name = 'lastemailedstories'" );
4081 // For each user, pull the stories they want and email it to them
4082 for( $x = 0; $x < $nrows; $x++ )
4084 $U = DB_fetchArray( $users );
4086 $storysql = array();
4087 $storysql['mysql'] = "SELECT sid,uid,date AS day,title,introtext,postmode";
4089 $storysql['mssql'] = "SELECT sid,uid,date AS day,title,CAST(introtext AS text) AS introtext,postmode";
4091 $commonsql = " FROM {$_TABLES['stories']} WHERE draft_flag = 0 AND date <= NOW() AND date >= '{$lastrun}'";
4093 $topicsql = "SELECT tid FROM {$_TABLES['topics']}"
4094 . COM_getPermSQL( 'WHERE', $U['uuid'] );
4095 $tresult = DB_query( $topicsql );
4096 $trows = DB_numRows( $tresult );
4100 // this user doesn't seem to have access to any topics ...
4105 for( $i = 0; $i < $trows; $i++ )
4107 $T = DB_fetchArray( $tresult );
4108 $TIDS[] = $T['tid'];
4111 if( !empty( $U['etids'] ))
4113 $ETIDS = explode( ' ', $U['etids'] );
4114 $TIDS = array_intersect( $TIDS, $ETIDS );
4117 if( count( $TIDS ) > 0)
4119 $commonsql .= " AND (tid IN ('" . implode( "','", $TIDS ) . "'))";
4122 $commonsql .= COM_getPermSQL( 'AND', $U['uuid'] );
4123 $commonsql .= ' ORDER BY featured DESC, date DESC';
4125 $storysql['mysql'] .= $commonsql;
4126 $storysql['mssql'] .= $commonsql;
4128 $stories = DB_query( $storysql );
4129 $nsrows = DB_numRows( $stories );
4133 // If no new stories where pulled for this user, continue with next
4137 $mailtext = $LANG08[29] . strftime( $_CONF['shortdate'], time() ) . "\n";
4139 for( $y = 0; $y < $nsrows; $y++ )
4141 // Loop through stories building the requested email message
4142 $S = DB_fetchArray( $stories );
4144 $mailtext .= "\n------------------------------\n\n";
4145 $mailtext .= "$LANG08[31]: "
4146 . COM_undoSpecialChars( stripslashes( $S['title'] )) . "\n";
4147 if( $_CONF['contributedbyline'] == 1 )
4149 if( empty( $authors[$S['uid']] ))
4151 $storyauthor = COM_getDisplayName ($S['uid']);
4152 $authors[$S['uid']] = $storyauthor;
4156 $storyauthor = $authors[$S['uid']];
4158 $mailtext .= "$LANG24[7]: " . $storyauthor . "\n";
4161 $mailtext .= "$LANG08[32]: " . strftime( $_CONF['date'], strtotime( $S['day' ])) . "\n\n";
4163 if( $_CONF['emailstorieslength'] > 0 )
4165 if($S['postmode']==='wikitext'){
4166 $storytext = COM_undoSpecialChars( strip_tags( COM_renderWikiText ( stripslashes( $S['introtext'] ))));
4168 $storytext = COM_undoSpecialChars( strip_tags( PLG_replaceTags( stripslashes( $S['introtext'] ))));
4171 if( $_CONF['emailstorieslength'] > 1 )
4173 $storytext = COM_truncate( $storytext,
4174 $_CONF['emailstorieslength'], '...' );
4177 $mailtext .= $storytext . "\n\n";
4180 $mailtext .= $LANG08[33] . ' ' . COM_buildUrl( $_CONF['site_url']
4181 . '/article.php?story=' . $S['sid'] ) . "\n";
4184 $mailtext .= "\n------------------------------\n";
4185 $mailtext .= "\n$LANG08[34]\n";
4186 $mailtext .= "\n------------------------------\n";
4188 $mailto = $U['username'] . ' <' . $U['email'] . '>';
4190 if ($_CONF['site_mail'] !== $_CONF['noreply_mail']) {
4191 $mailfrom = $_CONF['noreply_mail'];
4192 $mailtext .= LB . LB . $LANG04[159];
4194 $mailfrom = $_CONF['site_mail'];
4196 COM_mail( $mailto, $subject, $mailtext , $mailfrom);
4199 DB_query( "UPDATE {$_TABLES['vars']} SET value = NOW() WHERE name = 'lastemailedstories'" );
4203 * Shows any new information in a block
4205 * Return the HTML that shows any new stories, comments, etc
4207 * @param string $help Help file for block
4208 * @param string $title Title used in block header
4209 * @param string $position Position in which block is being rendered 'left', 'right' or blank (for centre)
4210 * @return string Return the HTML that shows any new stories, comments, etc
4214 function COM_whatsNewBlock( $help = '', $title = '', $position = '' )
4216 global $_CONF, $_TABLES, $_USER, $LANG01, $LANG_WHATSNEW, $page, $newstories;
4218 $retval = COM_startBlock( $title, $help,
4219 COM_getBlockTemplate( 'whats_new_block', 'header', $position ));
4222 if(( $_CONF['hidenewstories'] == 0 ) || ( $_CONF['hidenewcomments'] == 0 )
4223 || ( $_CONF['trackback_enabled']
4224 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4226 $topicsql = COM_getTopicSql ('AND', 0, $_TABLES['stories']);
4229 if( $_CONF['hidenewstories'] == 0 )
4232 $archivetid = DB_getItem( $_TABLES['topics'], 'tid', "archive_flag=1" );
4233 if( !empty( $archivetid ))
4235 $archsql = " AND (tid <> '" . addslashes( $archivetid ) . "')";
4238 // Find the newest stories
4239 $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' );
4240 $result = DB_query( $sql );
4241 $A = DB_fetchArray( $result );
4242 $nrows = $A['count'];
4244 if( empty( $title ))
4246 $title = DB_getItem( $_TABLES['blocks'], 'title', "name='whats_new_block'" );
4249 // Any late breaking news stories?
4250 $retval .= '<h3>' . $LANG01[99] . '</h3>';
4254 $newmsg = COM_formatTimeString( $LANG_WHATSNEW['new_string'],
4255 $_CONF['newstoriesinterval'], $LANG01[11], $nrows);
4257 if( $newstories && ( $page < 2 ))
4259 $retval .= $newmsg . '<br' . XHTML . '>';
4263 $retval .= COM_createLink($newmsg, $_CONF['site_url']
4264 . '/index.php?display=new') . '<br' . XHTML . '>';
4269 $retval .= $LANG01[100] . '<br' . XHTML . '>';
4272 if(( $_CONF['hidenewcomments'] == 0 ) || ( $_CONF['trackback_enabled']
4273 && ( $_CONF['hidenewtrackbacks'] == 0 ))
4274 || ( $_CONF['hidenewplugins'] == 0 ))
4276 $retval .= '<br' . XHTML . '>';
4280 if( $_CONF['hidenewcomments'] == 0 )
4282 // Go get the newest comments
4283 $retval .= '<h3>' . $LANG01[83] . ' <small>'
4284 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4285 $_CONF['newcommentsinterval'] )
4290 if( !COM_isAnonUser() )
4292 $stwhere .= "({$_TABLES['stories']}.owner_id IS NOT NULL AND {$_TABLES['stories']}.perm_owner IS NOT NULL) OR ";
4293 $stwhere .= "({$_TABLES['stories']}.group_id IS NOT NULL AND {$_TABLES['stories']}.perm_group IS NOT NULL) OR ";
4294 $stwhere .= "({$_TABLES['stories']}.perm_members IS NOT NULL)";
4298 $stwhere .= "({$_TABLES['stories']}.perm_anon IS NOT NULL)";
4300 $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";
4302 $result = DB_query( $sql );
4304 $nrows = DB_numRows( $result );
4308 $newcomments = array();
4310 for( $x = 0; $x < $nrows; $x++ )
4312 $A = DB_fetchArray( $result );
4314 if(( $A['type'] == 'article' ) || empty( $A['type'] ))
4316 $url = COM_buildUrl( $_CONF['site_url']
4317 . '/article.php?story=' . $A['sid'] ) . '#comments';
4320 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4321 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4323 if( $title != $titletouse )
4325 $attr = array('title' => htmlspecialchars($title));
4331 $acomment = str_replace( '$', '$', $titletouse );
4332 $acomment = str_replace( ' ', ' ', $acomment );
4334 if( $A['dups'] > 1 )
4336 $acomment .= ' [+' . $A['dups'] . ']';
4339 $newcomments[] = COM_createLink($acomment, $url, $attr);
4342 $retval .= COM_makeList( $newcomments, 'list-new-comments' );
4346 $retval .= $LANG01[86] . '<br' . XHTML . '>' . LB;
4348 if(( $_CONF['hidenewplugins'] == 0 )
4349 || ( $_CONF['trackback_enabled']
4350 && ( $_CONF['hidenewtrackbacks'] == 0 )))
4352 $retval .= '<br' . XHTML . '>';
4356 if( $_CONF['trackback_enabled'] && ( $_CONF['hidenewtrackbacks'] == 0 ))
4358 $retval .= '<h3>' . $LANG01[114] . ' <small>'
4359 . COM_formatTimeString( $LANG_WHATSNEW['new_last'],
4360 $_CONF['newtrackbackinterval'] )
4363 $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";
4364 $result = DB_query( $sql );
4366 $nrows = DB_numRows( $result );
4369 $newcomments = array();
4371 for( $i = 0; $i < $nrows; $i++ )
4373 $A = DB_fetchArray( $result );
4375 $url = COM_buildUrl( $_CONF['site_url']
4376 . '/article.php?story=' . $A['sid'] ) . '#trackback';
4378 $title = COM_undoSpecialChars( stripslashes( $A['title'] ));
4379 $titletouse = COM_truncate( $title, $_CONF['title_trim_length'],
4382 if( $title != $titletouse )
4384 $attr = array('title' => htmlspecialchars($title));
4390 $acomment = str_replace( '$', '$', $titletouse );
4391 $acomment = str_replace( ' ', ' ', $acomment );
4393 if( $A['count'] > 1 )
4395 $acomment .= ' [+' . $A['count'] . ']';
4398 $newcomments[] = COM_createLink($acomment, $url, $attr);
4401 $retval .= COM_makeList( $newcomments, 'list-new-trackbacks' );
4405 $retval .= $LANG01[115] . '<br' . XHTML . '>' . LB;
4407 if( $_CONF['hidenewplugins'] == 0 )
4409 $retval .= '<br' . XHTML . '>';
4413 if( $_CONF['hidenewplugins'] == 0 )
4415 list( $headlines, $smallheadlines, $content ) = PLG_getWhatsNew();
4416 $plugins = count( $headlines );
4419 for( $i = 0; $i < $plugins; $i++ )
4421 $retval .= '<h3>' . $headlines[$i] . ' <small>'
4422 . $smallheadlines[$i] . '</small></h3>';
4423 if( is_array( $content[$i] ))
4425 $retval .= COM_makeList( $content[$i], 'list-new-plugins' );
4429 $retval .= $content[$i];
4432 if( $i + 1 < $plugins )
4434 $retval .= '<br' . XHTML . '>';
4440 $retval .= COM_endBlock( COM_getBlockTemplate( 'whats_new_block', 'footer', $position ));
4446 * Creates the string that indicates the timespan in which new items were found
4448 * @param string $time_string template string
4449 * @param int $time number of seconds in which results are found
4450 * @param string $type type (translated string) of new item
4451 * @param int $amount amount of things that have been found.
4453 function COM_formatTimeString( $time_string, $time, $type = '', $amount = 0 )
4455 global $LANG_WHATSNEW;
4457 $retval = $time_string;
4459 // This is the amount you have to divide the previous by to get the
4460 // different time intervals: hour, day, week, months
4461 $time_divider = array( 60, 60, 24, 7, 30 );
4463 // These are the respective strings to the numbers above. They have to match
4464 // the strings in $LANG_WHATSNEW (i.e. these are the keys for the array -
4465 // the actual text strings are taken from the language file).
4466 $time_description = array( 'minute', 'hour', 'day', 'week', 'month' );
4467 $times_description = array( 'minutes', 'hours', 'days', 'weeks', 'months' );
4469 $time_dividers = count( $time_divider );
4470 for( $s = 0; $s < $time_dividers; $s++ )
4472 $time = $time / $time_divider[$s];
4473 if( $time < $time_divider[$s + 1] )
4479 $time_str = $time_description[$s];
4481 else // go back to the previous unit, e.g. 1 day -> 24 hours
4483 $time_str = $times_description[$s - 1];
4484 $time *= $time_divider[$s];
4489 $time_str = $times_description[$s];
4491 $fields = array( '%n', '%i', '%t', '%s' );
4492 $values = array( $amount, $type, $time, $LANG_WHATSNEW[$time_str] );
4493 $retval = str_replace( $fields, $values, $retval );
4502 * Displays a message text in a "System Message" block
4504 * @param string $message Message text; may contain HTML
4505 * @param string $title (optional) alternative block title
4506 * @return string HTML block with message
4507 * @see COM_showMessage
4508 * @see COM_showMessageFromParameter
4511 function COM_showMessageText($message, $title = '')
4513 global $_CONF, $MESSAGE, $_IMAGE_TYPE;
4517 if (!empty($message)) {
4518 if (empty($title)) {
4519 $title = $MESSAGE[40];
4521 $timestamp = strftime($_CONF['daytime']);
4522 $retval .= COM_startBlock($title . ' - ' . $timestamp, '',
4523 COM_getBlockTemplate('_msg_block', 'header'))
4524 . '<p class="sysmessage"><img src="' . $_CONF['layout_url']
4525 . '/images/sysmessage.' . $_IMAGE_TYPE . '" alt="" ' . XHTML
4526 . '>' . $message . '</p>'
4527 . COM_endBlock(COM_getBlockTemplate('_msg_block', 'footer'));
4534 * Displays a message on the webpage
4536 * Display one of the predefined messages from the $MESSAGE array. If a plugin
4537 * name is provided, display that plugin's message instead.
4539 * @param int $msg ID of message to show
4540 * @param string $plugin Optional name of plugin to lookup plugin defined message
4541 * @return string HTML block with message
4542 * @see COM_showMessageFromParameter
4543 * @see COM_showMessageText
4546 function COM_showMessage($msg, $plugin = '')
4553 if (!empty($plugin)) {
4554 $var = 'PLG_' . $plugin . '_MESSAGE' . $msg;
4559 $message = sprintf($MESSAGE[61], $plugin);
4560 COM_errorLog($message . ": " . $var, 1);
4563 $message = $MESSAGE[$msg];
4566 if (!empty($message)) {
4567 $retval .= COM_showMessageText($message);
4575 * Displays a message, as defined by URL parameters
4577 * Helper function to display a message, if URL parameters 'msg' and 'plugin'
4578 * (optional) are defined. Only for GET requests, but that's what Geeklog uses
4579 * everywhere anyway.
4581 * @return string HTML block with message
4582 * @see COM_showMessage
4583 * @see COM_showMessageText
4586 function COM_showMessageFromParameter()
4590 if (isset($_GET['msg'])) {
4591 $msg = COM_applyFilter($_GET['msg'], true);
4594 if (isset($_GET['plugin'])) {
4595 $plugin = COM_applyFilter($_GET['plugin']);
4597 $retval .= COM_showMessage($msg, $plugin);
4605 * Prints Google(tm)-like paging navigation
4607 * @param string $base_url base url to use for all generated links
4608 * @param int $curpage current page we are on
4609 * @param int $num_pages Total number of pages
4610 * @param string $page_str page-variable name AND '='
4611 * @param boolean $do_rewrite if true, url-rewriting is respected
4612 * @param string $msg to be displayed with the navigation
4613 * @param string $open_ended replace next/last links with this
4614 * @return string HTML formatted widget
4616 function COM_printPageNavigation( $base_url, $curpage, $num_pages,
4617 $page_str='page=', $do_rewrite=false, $msg='',
4624 if( $num_pages < 2 )
4631 $hasargs = strstr( $base_url, '?' );
4649 $retval .= COM_createLink($LANG05[7], $base_url) . ' | ';
4651 if( ( $curpage - 1 ) > 1 )
4653 $pg = $sep . $page_str . ( $curpage - 1 );
4655 $retval .= COM_createLink($LANG05[6], $base_url . $pg ) . ' | ';
4659 $retval .= $LANG05[7] . ' | ' ;
4660 $retval .= $LANG05[6] . ' | ' ;
4663 for( $pgcount = ( $curpage - 10 ); ( $pgcount <= ( $curpage + 9 )) AND ( $pgcount <= $num_pages ); $pgcount++ )
4670 if( $pgcount == $curpage )
4672 $retval .= '<b>' . $pgcount . '</b> ';
4679 $pg = $sep . $page_str . $pgcount;
4681 $retval .= COM_createLink($pgcount, $base_url . $pg) . ' ';
4685 if( !empty( $open_ended ))
4687 $retval .= '| ' . $open_ended;
4689 else if( $curpage == $num_pages )
4691 $retval .= '| ' . $LANG05[5] . ' ';
4692 $retval .= '| ' . $LANG05[8];
4696 $retval .= '| ' . COM_createLink($LANG05[5], $base_url . $sep
4697 . $page_str . ($curpage + 1));
4698 $retval .= ' | ' . COM_createLink($LANG05[8], $base_url . $sep
4699 . $page_str . $num_pages);
4702 if( !empty( $retval ))
4708 $retval = '<div class="pagenav">' . $msg . $retval . '</div>';
4715 * Returns formatted date/time for user
4717 * This function COM_takes a date in either unixtimestamp or in english and
4718 * formats it to the users preference. If the user didn't specify a format
4719 * the format in the config file is used. This returns an array where array[0]
4720 * is the formatted date and array[1] is the unixtimestamp
4722 * @param string $date date to format, otherwise we format current date/time
4723 * @return array array[0] is the formatted date and array[1] is the unixtimestamp.
4726 function COM_getUserDateTimeFormat( $date='' )
4728 global $_TABLES, $_USER, $_CONF;
4730 // Get display format for time
4732 if( !COM_isAnonUser() )
4734 if( empty( $_USER['format'] ))
4736 $dateformat = $_CONF['date'];
4740 $dateformat = $_USER['format'];
4745 $dateformat = $_CONF['date'];
4750 // Date is empty, get current date/time
4753 else if( is_numeric( $date ))
4755 // This is a timestamp
4760 // This is a string representation of a date/time
4761 $stamp = strtotime( $date );
4766 $date = strftime( $dateformat, $stamp );
4768 return array( $date, $stamp );
4772 * Returns user-defined cookie timeout
4774 * In account preferences users can specify when their long-term cookie expires.
4775 * This function returns that value.
4777 * @return int Cookie time out value in seconds
4780 function COM_getUserCookieTimeout()
4782 global $_TABLES, $_USER, $_CONF;
4784 if( empty( $_USER ))
4789 $timeoutvalue = DB_getItem( $_TABLES['users'], 'cookietimeout', "uid = {$_USER['uid']}" );
4791 if( empty( $timeoutvalue ))
4796 return $timeoutvalue;
4800 * Shows who is online in slick little block
4801 * @return string HTML string of online users seperated by line breaks.
4804 function phpblock_whosonline()
4806 global $_CONF, $_TABLES, $_USER, $LANG01, $_IMAGE_TYPE;
4810 $expire_time = time() - $_CONF['whosonline_threshold'];
4812 $byname = 'username';
4813 if( $_CONF['show_fullname'] == 1 )
4815 $byname .= ',fullname';
4817 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4819 $byname .= ',remoteusername,remoteservice';
4822 $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}" );
4823 $nrows = DB_numRows( $result );
4828 for( $i = 0; $i < $nrows; $i++ )
4830 $A = DB_fetchArray( $result );
4832 if( $A['showonline'] == 1 )
4835 if( $_CONF['show_fullname'] == 1 )
4837 $fullname = $A['fullname'];
4839 if( $_CONF['user_login_method']['openid'] || $_CONF['user_login_method']['3rdparty'] )
4841 $username = COM_getDisplayName( $A['uid'], $A['username'],
4842 $fullname, $A['remoteusername'], $A['remoteservice'] );
4846 $username = COM_getDisplayName( $A['uid'], $A['username'],
4849 $url = $_CONF['site_url'] . '/users.php?mode=profile&uid=' . $A['uid'];
4850 $retval .= COM_createLink($username, $url);
4852 if( !empty( $A['photo'] ) AND $_CONF['allow_user_photo'] == 1)
4854 $usrimg = '<img src="' . $_CONF['layout_url']
4855 . '/images/smallcamera.' . $_IMAGE_TYPE
4856 . '" alt=""' . XHTML . '>';
4857 $retval .= ' ' . COM_createLink($usrimg, $url);
4859 $retval .= '<br' . XHTML . '>';
4864 // this user does not want to show up in Who's Online
4865 $num_anon++; // count as anonymous
4869 $num_anon += DB_count( $_TABLES['sessions'], 'uid', 1 );
4871 if(( $_CONF['whosonline_anonymous'] == 1 ) &&
4874 // note that we're overwriting the contents of $retval here
4877 $retval = $LANG01[112] . ': ' . COM_numberFormat($num_reg)
4878 . '<br' . XHTML . '>';
4888 $retval .= $LANG01[41] . ': ' . COM_numberFormat($num_anon)
4889 . '<br' . XHTML . '>';
4896 * Gets the <option> values for calendar months
4898 * @param string $selected Selected month
4899 * @see function COM_getDayFormOptions
4900 * @see function COM_getYearFormOptions
4901 * @see function COM_getHourFormOptions
4902 * @see function COM_getMinuteFormOptions
4903 * @return string HTML Months as option values
4906 function COM_getMonthFormOptions( $selected = '' )
4910 $month_options = '';
4912 for( $i = 1; $i <= 12; $i++ )
4915 $month_options .= '<option value="' . $mval . '"';
4917 if( $i == $selected )
4919 $month_options .= ' selected="selected"';
4922 $month_options .= '>' . $LANG_MONTH[$mval] . '</option>';
4925 return $month_options;
4929 * Gets the <option> values for calendar days
4931 * @param string $selected Selected day
4932 * @see function COM_getMonthFormOptions
4933 * @see function COM_getYearFormOptions
4934 * @see function COM_getHourFormOptions
4935 * @see function COM_getMinuteFormOptions
4936 * @return string HTML days as option values
4939 function COM_getDayFormOptions( $selected = '' )
4943 for( $i = 1; $i <= 31; $i++ )
4954 $day_options .= '<option value="' . $dval . '"';
4956 if( $i == $selected )
4958 $day_options .= ' selected="selected"';
4961 $day_options .= '>' . $dval . '</option>';
4964 return $day_options;
4968 * Gets the <option> values for calendar years
4970 * Returns Option list Containing 5 years starting with current
4971 * unless @selected is < current year then starts with @selected
4973 * @param string $selected Selected year
4974 * @param int $startoffset Optional (can be +/-) Used to determine start year for range of years
4975 * @param int $endoffset Optional (can be +/-) Used to determine end year for range of years
4976 * @see function COM_getMonthFormOptions
4977 * @see function COM_getDayFormOptions
4978 * @see function COM_getHourFormOptions
4979 * @see function COM_getMinuteFormOptions
4980 * @return string HTML years as option values
4983 function COM_getYearFormOptions($selected = '', $startoffset = -1, $endoffset = 5)
4986 $start_year = date('Y') + $startoffset;
4987 $cur_year = date('Y', time());
4988 $finish_year = $cur_year + $endoffset;
4990 if (!empty($selected)) {
4991 if ($selected < $cur_year) {
4992 $start_year = $selected;
4996 for ($i = $start_year; $i <= $finish_year; $i++) {
4997 $year_options .= '<option value="' . $i . '"';
4999 if ($i == $selected) {
5000 $year_options .= ' selected="selected"';
5003 $year_options .= '>' . $i . '</option>';
5006 return $year_options;
5010 * Gets the <option> values for clock hours
5012 * @param string $selected Selected hour
5013 * @param int $mode 12 or 24 hour mode
5014 * @return string HTML string of options
5015 * @see function COM_getMonthFormOptions
5016 * @see function COM_getDayFormOptions
5017 * @see function COM_getYearFormOptions
5018 * @see function COM_getMinuteFormOptions
5021 function COM_getHourFormOptions( $selected = '', $mode = 12 )
5027 for( $i = 1; $i <= 11; $i++ )
5040 $hour_options .= '<option value="12"';
5042 if( $selected == 12 )
5044 $hour_options .= ' selected="selected"';
5047 $hour_options .= '>12</option>';
5050 $hour_options .= '<option value="' . $hval . '"';
5052 if( $selected == $i )
5054 $hour_options .= ' selected="selected"';
5057 $hour_options .= '>' . $i . '</option>';
5060 else // if( $mode == 24 )
5062 for( $i = 0; $i < 24; $i++ )
5073 $hour_options .= '<option value="' . $hval . '"';
5075 if( $selected == $i )
5077 $hour_options .= ' selected="selected"';
5080 $hour_options .= '>' . $i . '</option>';
5084 return $hour_options;
5088 * Gets the <option> values for clock minutes
5090 * @param string $selected Selected minutes
5091 * @param int $step number of minutes between options, e.g. 15
5092 * @see function COM_getMonthFormOptions
5093 * @see function COM_getDayFormOptions
5094 * @see function COM_getHourFormOptions
5095 * @see function COM_getYearFormOptions
5096 * @return string HTML of option minutes
5099 function COM_getMinuteFormOptions( $selected = '', $step = 1 )
5101 $minute_options = '';
5103 if(( $step < 1 ) || ( $step > 30 ))
5108 for( $i = 0; $i <= 59; $i += $step )
5119 $minute_options .= '<option value="' . $mval . '"';
5121 if( $selected == $i )
5123 $minute_options .= ' selected="selected"';
5126 $minute_options .= '>' . $mval . '</option>';
5129 return $minute_options;
5133 * For backward compatibility only.
5134 * This function should always have been called COM_getMinuteFormOptions
5135 * @see COM_getMinuteFormOptions
5137 function COM_getMinuteOptions( $selected = '', $step = 1 )
5139 return COM_getMinuteFormOptions( $selected, $step );
5143 * Create an am/pm selector dropdown menu
5145 * @param string $name name of the <select>
5146 * @param string $selected preselection: 'am' or 'pm'
5147 * @return string HTML for the dropdown; empty string in 24 hour mode
5150 function COM_getAmPmFormSelection( $name, $selected = '' )
5156 if( isset( $_CONF['hour_mode'] ) && ( $_CONF['hour_mode'] == 24 ))
5162 if( empty( $selected ))
5164 $selected = date( 'a' );
5167 $retval .= '<select name="' . $name . '">' . LB;
5168 $retval .= '<option value="am"';
5169 if( $selected == 'am' )
5171 $retval .= ' selected="selected"';
5173 $retval .= '>am</option>' . LB . '<option value="pm"';
5174 if( $selected == 'pm' )
5176 $retval .= ' selected="selected"';
5178 $retval .= '>pm</option>' . LB . '</select>' . LB;
5185 * Creates an HTML unordered list from the given array.
5186 * It formats one list item per array element, using the list.thtml
5187 * and listitem.thtml templates.
5189 * @param array $listofitems Items to list out
5190 * @param string $classname optional CSS class name for the list
5191 * @return string HTML unordered list of array items
5193 function COM_makeList($listofitems, $classname = '')
5197 $list = new Template($_CONF['path_layout']);
5198 $list->set_file(array('list' => 'list.thtml',
5199 'listitem' => 'listitem.thtml'));
5200 $list->set_var( 'xhtml', XHTML );
5201 $list->set_var('site_url', $_CONF['site_url']);
5202 $list->set_var('site_admin_url', $_CONF['site_admin_url']);
5203 $list->set_var('layout_url', $_CONF['layout_url']);
5205 if (empty($classname)) {
5206 $list->set_var('list_class', '');
5207 $list->set_var('list_class_name', '');
5209 $list->set_var('list_class', 'class="' . $classname . '"');
5210 $list->set_var('list_class_name', $classname);
5213 if (is_array($listofitems)) {
5214 foreach ($listofitems as $oneitem) {
5215 $list->set_var('list_item', $oneitem);
5216 $list->parse('list_items', 'listitem', true);
5220 $list->parse('newlist', 'list', true);
5222 return $list->finish($list->get_var('newlist'));
5226 * Check if speed limit applies
5228 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5229 * @param int $max max number of allowed tries within speed limit
5230 * @param string $property IP address or other identifiable property
5231 * @return int 0: does not apply, else: seconds since last post
5233 function COM_checkSpeedlimit($type = 'submit', $max = 1, $property = '')
5239 if (empty($property)) {
5240 $property = $_SERVER['REMOTE_ADDR'];
5242 $property = addslashes($property);
5244 $res = DB_query("SELECT date FROM {$_TABLES['speedlimit']} WHERE (type = '$type') AND (ipaddress = '$property') ORDER BY date ASC");
5246 // If the number of allowed tries has not been reached,
5247 // return 0 (didn't hit limit)
5248 if (DB_numRows($res) < $max) {
5252 list($date) = DB_fetchArray($res);
5254 if (!empty($date)) {
5255 $last = time() - $date;
5257 // just in case someone manages to submit something in < 1 sec.
5266 * Store post info for speed limit
5268 * @param string $type type of speed limit, e.g. 'submit', 'comment'
5269 * @param string $property IP address or other identifiable property
5272 function COM_updateSpeedlimit($type = 'submit', $property = '')
5276 if (empty($property)) {
5277 $property = $_SERVER['REMOTE_ADDR'];
5279 $property = addslashes($property);
5281 DB_save($_TABLES['speedlimit'], 'ipaddress,date,type',
5282 "'$property',UNIX_TIMESTAMP(),'$type'");
5286 * Clear out expired speed limits, i.e. entries older than 'x' seconds
5288 * @param speedlimit int number of seconds
5289 * @param type string type of speed limit, e.g. 'submit', 'comment'
5292 function COM_clearSpeedlimit($speedlimit = 60, $type = '')
5296 $sql = "DELETE FROM {$_TABLES['speedlimit']} WHERE ";
5297 if (!empty($type)) {
5298 $sql .= "(type = '$type') AND ";
5300 $sql .= "(date < UNIX_TIMESTAMP() - $speedlimit)";
5305 * Reset the speedlimit
5307 * @param string $type type of speed limit to reset, e.g. 'submit'
5308 * @param string $property IP address or other identifiable property
5311 function COM_resetSpeedlimit($type = 'submit', $property = '')
5315 if (empty($property)) {
5316 $property = $_SERVER['REMOTE_ADDR'];
5318 $property = addslashes($property);
5320 DB_delete($_TABLES['speedlimit'], array('type', 'ipaddress'),
5321 array($type, $property));
5325 * Wrapper function for URL class so as to not confuse people as this will
5326 * eventually get used all over the place
5328 * This function returns a crawler friendly URL (if possible)
5330 * @param string $url URL to try to build crawler friendly URL for
5331 * @return string Rewritten URL
5334 function COM_buildURL( $url )
5338 return $_URL->buildURL( $url );
5342 * Wrapper function for URL class so as to not confuse people
5344 * This function sets the name of the arguments found in url
5346 * @param array $names Names of arguments in query string to assign to values
5347 * @return boolean True if successful
5350 function COM_setArgNames( $names )
5354 return $_URL->setArgNames( $names );
5358 * Wrapper function for URL class
5360 * returns value for specified argument
5362 * @param string $name argument to get value for
5363 * @return string Argument value
5366 function COM_getArgument( $name )
5370 return $_URL->getArgument( $name );
5376 * This will take a number of occurrences, and number of seconds for the time span and return
5377 * the smallest #/time interval
5379 * @param int $occurrences how many occurrences during time interval
5380 * @param int $timespan time interval in seconds
5381 * @return int Seconds per interval
5384 function COM_getRate( $occurrences, $timespan )
5386 // want to define some common time words (yes, dirk, i need to put this in LANG)
5387 // time words and their value in seconds
5388 // week is 7 * day, month is 30 * day, year is 365.25 * day
5390 $common_time = array(
5400 if( $occurrences != 0 )
5402 $rate = ( int )( $timespan / $occurrences );
5403 $adjustedRate = $occurrences + 1;
5404 $time_unit = 'second';
5408 foreach( $common_time as $unit=>$seconds )
5410 if( $rate > $seconds )
5412 $foo = ( int )(( $rate / $seconds ) + .5 );
5414 if(( $foo < $occurrences ) && ( $foo > 0 ))
5416 $adjustedRate = $foo;
5422 $singular = '1 shout every ' . $adjustedRate . ' ' . $time_unit;
5424 if( $adjustedRate > 1 )
5431 $singular = 'No events';
5438 * Return SQL expression to check for permissions.
5440 * Creates part of an SQL expression that can be used to request items with the
5441 * standard set of Geeklog permissions.
5443 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5444 * @param int $u_id user id or 0 = current user
5445 * @param int $access access to check for (2=read, 3=r&write)
5446 * @param string $table table name if ambiguous (e.g. in JOINs)
5447 * @return string SQL expression string (may be empty)
5450 function COM_getPermSQL( $type = 'WHERE', $u_id = 0, $access = 2, $table = '' )
5452 global $_USER, $_GROUPS;
5454 if( !empty( $table ))
5460 if( COM_isAnonUser() )
5466 $uid = $_USER['uid'];
5474 $UserGroups = array();
5475 if(( empty( $_USER['uid'] ) && ( $uid == 1 )) || ( $uid == $_USER['uid'] ))
5477 if( empty( $_GROUPS ))
5479 $_GROUPS = SEC_getUserGroups( $uid );
5481 $UserGroups = $_GROUPS;
5485 $UserGroups = SEC_getUserGroups( $uid );
5488 if( empty( $UserGroups ))
5490 // this shouldn't really happen, but if it does, handle user
5491 // like an anonymous user
5495 if( SEC_inGroup( 'Root', $uid ))
5500 $sql = ' ' . $type . ' (';
5504 $sql .= "(({$table}owner_id = '{$uid}') AND ({$table}perm_owner >= $access)) OR ";
5506 $sql .= "(({$table}group_id IN (" . implode( ',', $UserGroups )
5507 . ")) AND ({$table}perm_group >= $access)) OR ";
5508 $sql .= "({$table}perm_members >= $access)";
5512 $sql .= "{$table}perm_anon >= $access";
5521 * Return SQL expression to check for allowed topics.
5523 * Creates part of an SQL expression that can be used to only request stories
5524 * from topics to which the user has access to.
5526 * Note that this function does an SQL request, so you should cache
5527 * the resulting SQL expression if you need it more than once.
5529 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
5530 * @param int $u_id user id or 0 = current user
5531 * @param string $table table name if ambiguous (e.g. in JOINs)
5532 * @return string SQL expression string (may be empty)
5535 function COM_getTopicSQL( $type = 'WHERE', $u_id = 0, $table = '' )
5537 global $_TABLES, $_USER, $_GROUPS;
5539 $topicsql = ' ' . $type . ' ';
5541 if( !empty( $table ))
5546 $UserGroups = array();
5547 if(( $u_id <= 0 ) || ( isset( $_USER['uid'] ) && $u_id == $_USER['uid'] ))
5549 if( !COM_isAnonUser() )
5551 $uid = $_USER['uid'];
5557 $UserGroups = $_GROUPS;
5562 $UserGroups = SEC_getUserGroups( $uid );
5565 if( empty( $UserGroups ))
5567 // this shouldn't really happen, but if it does, handle user
5568 // like an anonymous user
5572 if( SEC_inGroup( 'Root', $uid ))
5577 $result = DB_query( "SELECT tid FROM {$_TABLES['topics']}"
5578 . COM_getPermSQL( 'WHERE', $uid ));
5580 while( $T = DB_fetchArray( $result ))
5582 $tids[] = $T['tid'];
5585 if( count( $tids ) > 0 )
5587 $topicsql .= "({$table}tid IN ('" . implode( "','", $tids ) . "'))";
5598 * Strip slashes from a string only when magic_quotes_gpc = on.
5600 * @param string $text The text
5601 * @return string The text, possibly without slashes.
5603 function COM_stripslashes( $text )
5605 if( get_magic_quotes_gpc() == 1 )
5607 return( stripslashes( $text ));
5614 * Filter parameters passed per GET (URL) or POST.
5616 * @param string $parameter the parameter to test
5617 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5618 * @return string the filtered parameter (may now be empty or 0)
5619 * @see COM_applyBasicFilter
5622 function COM_applyFilter( $parameter, $isnumeric = false )
5624 $p = COM_stripslashes($parameter);
5626 return COM_applyBasicFilter($p, $isnumeric);
5632 * NOTE: Use this function instead of COM_applyFilter for parameters
5633 * _not_ coming in through a GET or POST request.
5635 * @param string $parameter the parameter to test
5636 * @param boolean $isnumeric true if $parameter is supposed to be numeric
5637 * @return string the filtered parameter (may now be empty or 0)
5638 * @see COM_applyFilter
5641 function COM_applyBasicFilter( $parameter, $isnumeric = false )
5643 $log_manipulation = false; // set to true to log when the filter applied
5645 $p = strip_tags( $parameter );
5646 $p = COM_killJS( $p ); // doesn't help a lot right now, but still ...
5650 // Note: PHP's is_numeric() accepts values like 4e4 as numeric
5651 if( !is_numeric( $p ) || ( preg_match( '/^-?\d+$/', $p ) == 0 ))
5658 $p = preg_replace( '/\/\*.*/', '', $p );
5659 $pa = explode( "'", $p );
5660 $pa = explode( '"', $pa[0] );
5661 $pa = explode( '`', $pa[0] );
5662 $pa = explode( ';', $pa[0] );
5663 $pa = explode( ',', $pa[0] );
5664 $pa = explode( '\\', $pa[0] );
5668 if( $log_manipulation )
5670 if( strcmp( $p, $parameter ) != 0 )
5672 COM_errorLog( "Filter applied: >> $parameter << filtered to $p [IP {$_SERVER['REMOTE_ADDR']}]", 1);
5682 * @param string $url URL to sanitized
5683 * @param array $allowed_protocols array of allowed protocols
5684 * @param string $default_protocol replacement protocol (default: http)
5685 * @return string sanitized URL
5688 function COM_sanitizeUrl( $url, $allowed_protocols = '', $default_protocol = '' )
5692 if( empty( $allowed_protocols ))
5694 $allowed_protocols = $_CONF['allowed_protocols'];
5696 else if( !is_array( $allowed_protocols ))
5698 $allowed_protocols = array( $allowed_protocols );
5701 if( empty( $default_protocol ))
5703 $default_protocol = 'http:';
5705 else if( substr( $default_protocol, -1 ) != ':' )
5707 $default_protocol .= ':';
5710 $url = strip_tags( $url );
5713 $pos = MBYTE_strpos( $url, ':' );
5714 if( $pos === false )
5716 $url = $default_protocol . '//' . $url;
5720 $protocol = MBYTE_substr( $url, 0, $pos + 1 );
5722 foreach( $allowed_protocols as $allowed )
5724 if( substr( $allowed, -1 ) != ':' )
5728 if( $protocol == $allowed )
5736 $url = $default_protocol . MBYTE_substr( $url, $pos + 1 );
5745 * Ensure an ID contains only alphanumeric characters, dots, dashes, or underscores
5747 * @param string $id the ID to sanitize
5748 * @param boolean $new_id true = create a new ID in case we end up with an empty string
5749 * @return string the sanitized ID
5751 function COM_sanitizeID( $id, $new_id = true )
5753 $id = str_replace( ' ', '', $id );
5754 $id = str_replace( array( '/', '\\', ':', '+' ), '-', $id );
5755 $id = preg_replace( '/[^a-zA-Z0-9\-_\.]/', '', $id );
5756 if( empty( $id ) && $new_id )
5758 $id = COM_makesid();
5765 * Sanitize a filename.
5767 * NOTE: This function is pretty strict in what it allows. Meant to be used
5768 * for files to be included where part of the filename is dynamic.
5770 * @param string $filename the filename to clean up
5771 * @param boolean $allow_dots whether to allow dots in the filename or not
5772 * @return string sanitized filename
5775 function COM_sanitizeFilename($filename, $allow_dots = false)
5778 $filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $filename);
5779 $filename = str_replace('..', '', $filename);
5781 $filename = preg_replace('/[^a-zA-Z0-9\-_]/', '', $filename);
5788 * Detect links in a plain-ascii text and turn them into clickable links.
5789 * Will detect links starting with "http:", "https:", "ftp:", and "www.".
5791 * @param string $text the (plain-ascii) text string
5792 * @return string the same string, with links enclosed in <a>...</a> tags
5795 function COM_makeClickableLinks( $text )
5799 if (! $_CONF['clickable_links']) {
5803 // These regular expressions will work for this purpuse, but
5804 // they should NOT be used for validating links.
5806 // matches anything starting with http:// or https:// or ftp:// or ftps://
5807 $regex[] = '/(?<=^|[\n\r\t\s\(\)\[\]<>";])((?:(?:ht|f)tps?:\/{2})(?:[^\n\r\t\s\(\)\[\]<>"&]+(?:&)?)+)(?=[\n\r\t\s\(\)\[\]<>"&]|$)/ei';
5808 $replace[] = "COM_makeClickableLinksCallback('', '\\1')";
5810 // matches anything containing a top level domain: xxx.com or xxx.yyy.net/stuff.php or xxx.yyy.zz
5811 // list taken from: http://en.wikipedia.org/wiki/List_of_Internet_TLDs
5812 $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';
5813 $replace[] = "COM_makeClickableLinksCallback('http://', '\\1')";
5815 $text = preg_replace( $regex, $replace, $text );
5821 * Callback function to help format links in COM_makeClickableLinks
5823 * @param string $http set to 'http://' when not already in the url
5824 * @param string $link the url
5825 * @return string link enclosed in <a>...</a> tags
5828 function COM_makeClickableLinksCallback( $http, $link )
5830 $text = COM_truncate( $link, 50, '...', '10' );
5832 return "<a href=\"$http$link\">$text</a>";
5836 * Undo the conversion of URLs to clickable links (in plain text posts),
5837 * e.g. so that we can present the user with the post as they entered them.
5839 * @param string $text story text
5840 * @return string story text without links
5843 function COM_undoClickableLinks( $text )
5845 $text = preg_replace( '/<a href="([^"]*)">([^<]*)<\/a>/', '\1', $text );
5851 * Highlight the words from a search query in a given text string.
5853 * @param string $text the text
5854 * @param string $query the search query
5855 * @param string $class html class to use to highlight
5856 * @return string the text with highlighted search words
5859 function COM_highlightQuery( $text, $query, $class = 'highlight' )
5861 // escape PCRE special characters
5862 $query = preg_quote($query, '/');
5864 $mywords = explode(' ', $query);
5865 foreach ($mywords as $searchword)
5867 if (!empty($searchword))
5869 $before = "/(?!(?:[^<]+>|[^>]+<\/a>))\b";
5871 if ($searchword <> utf8_encode($searchword)) {
5872 if (@preg_match('/^\pL$/u', urldecode('%C3%B1'))) { // Unicode property support
5873 $before = "/(?<!\p{L})";
5874 $after = "(?!\p{L})/u";
5880 $text = preg_replace($before . $searchword . $after, "<span class=\"$class\">\\0</span>", '<!-- x -->' . $text . '<!-- x -->' );
5887 * Determines the difference between two dates.
5889 * This will takes either unixtimestamps or English dates as input and will
5890 * automatically do the date diff on the more recent of the two dates (e.g. the
5891 * order of the two dates given doesn't matter).
5893 * @author Tony Bibbs, tony DOT bibbs AT iowa DOT gov
5895 * @param string $interval Can be:
5902 * @param string|int $date1 English date (e.g. 10 Dec 2004) or unixtimestamp
5903 * @param string|int $date2 English date (e.g. 10 Dec 2004) or unixtimestamp
5904 * @return int Difference of the two dates in the unit of time indicated by the interval
5907 function COM_dateDiff( $interval, $date1, $date2 )
5909 // Convert dates to timestamps, if needed.
5910 if( !is_numeric( $date1 ))
5912 $date1 = strtotime( $date1 );
5915 if( !is_numeric( $date2 ))
5917 $date2 = strtotime( $date2 );
5920 // Function roughly equivalent to the ASP "DateDiff" function
5921 if( $date2 > $date1 )
5923 $seconds = $date2 - $date1;
5927 $seconds = $date1 - $date2;
5933 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5934 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5935 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5936 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5937 $diff = $year2 - $year1;
5938 if($month1 > $month2) {
5940 } elseif($month1 == $month2) {
5943 } elseif($day1 == $day2) {
5944 if($time1 > $time2) {
5951 list($year1, $month1, $day1) = split('-', date('Y-m-d', $date1));
5952 list($year2, $month2, $day2) = split('-', date('Y-m-d', $date2));
5953 $time1 = (date('H',$date1)*3600) + (date('i',$date1)*60) + (date('s',$date1));
5954 $time2 = (date('H',$date2)*3600) + (date('i',$date2)*60) + (date('s',$date2));
5955 $diff = ($year2 * 12 + $month2) - ($year1 * 12 + $month1);
5958 } elseif($day1 == $day2) {
5959 if($time1 > $time2) {
5965 // Only simple seconds calculation needed from here on
5966 $diff = floor($seconds / 604800);
5969 $diff = floor($seconds / 86400);
5972 $diff = floor($seconds / 3600);
5975 $diff = floor($seconds / 60);
5986 * Try to figure out our current URL, including all parameters.
5988 * This is an ugly hack since there's no single variable that returns what
5989 * we want and the variables used here may not be available on all servers
5992 * Seems to work on Apache (1.3.x and 2.x), IIS, and Zeus ...
5994 * @return string complete URL, e.g. 'http://www.example.com/blah.php?foo=bar'
5997 function COM_getCurrentURL()
6003 if( empty( $_SERVER['SCRIPT_URI'] ))
6005 if( !empty( $_SERVER['DOCUMENT_URI'] ))
6007 $thisUrl = $_SERVER['DOCUMENT_URI'];
6012 $thisUrl = $_SERVER['SCRIPT_URI'];
6014 if( !empty( $thisUrl ) && !empty( $_SERVER['QUERY_STRING'] ))
6016 $thisUrl .= '?' . $_SERVER['QUERY_STRING'];
6018 if( empty( $thisUrl ))
6020 $requestUri = $_SERVER['REQUEST_URI'];
6021 if( empty( $_SERVER['REQUEST_URI'] ))
6023 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6024 if( empty( $_SERVER['PATH_INFO'] ))
6026 $requestUri = $_SERVER['SCRIPT_NAME'];
6030 $requestUri = $_SERVER['PATH_INFO'];
6032 if( !empty( $_SERVER['QUERY_STRING'] ))
6034 $requestUri .= '?' . $_SERVER['QUERY_STRING'];
6038 $firstslash = strpos( $_CONF['site_url'], '/' );
6039 if( $firstslash === false )
6041 // special case - assume it's okay
6042 $thisUrl = $_CONF['site_url'] . $requestUri;
6044 else if( $firstslash + 1 == strrpos( $_CONF['site_url'], '/' ))
6046 // site is in the document root
6047 $thisUrl = $_CONF['site_url'] . $requestUri;
6051 // extract server name first
6052 $pos = strpos( $_CONF['site_url'], '/', $firstslash + 2 );
6053 $thisUrl = substr( $_CONF['site_url'], 0, $pos ) . $requestUri;
6061 * Check if we're on Geeklog's index page.
6063 * See if we're on the main index page (first page, no topics selected).
6065 * @return boolean true = we're on the frontpage, false = we're not
6068 function COM_onFrontpage()
6070 global $_CONF, $topic, $page, $newstories;
6072 // Note: We can't use $PHP_SELF here since the site may not be in the
6074 $onFrontpage = false;
6076 // on a Zeus webserver, prefer PATH_INFO over SCRIPT_NAME
6077 if( empty( $_SERVER['PATH_INFO'] ))
6079 $scriptName = $_SERVER['SCRIPT_NAME'];
6083 $scriptName = $_SERVER['PATH_INFO'];
6086 preg_match( '/\/\/[^\/]*(.*)/', $_CONF['site_url'], $pathonly );
6087 if(( $scriptName == $pathonly[1] . '/index.php' ) &&
6088 empty( $topic ) && ( $page == 1 ) && !$newstories )
6090 $onFrontpage = true;
6093 return $onFrontpage;
6097 * Check if we're on Geeklog's index page [deprecated]
6099 * Note that this function returns FALSE when we're on the index page. Due to
6100 * the inverted return values, it has been deprecated and is only provided for
6101 * backward compatibility - use COM_onFrontpage() instead.
6103 * @deprecated since Geeklog 1.4.1
6104 * @see COM_onFrontpage
6107 function COM_isFrontpage()
6109 return !COM_onFrontpage();
6113 * Converts a number for output into a formatted number with thousands-
6114 * separator, comma-separator and fixed decimals if necessary
6116 * @param float $number Number that will be formatted
6117 * @return string formatted number
6120 function COM_numberFormat( $number )
6124 if( $number - floor( $number ) > 0 ) // number has decimals
6126 $dc = $_CONF['decimal_count'];
6132 $ts = $_CONF['thousand_separator'];
6133 $ds = $_CONF['decimal_separator'];
6135 return number_format( $number, $dc, $ds, $ts );
6139 * Convert a text based date YYYY-MM-DD to a unix timestamp integer value
6141 * @param string $date Date in the format YYYY-MM-DD
6142 * @param string $time Option time in the format HH:MM::SS
6143 * @return int UNIX Timestamp
6145 function COM_convertDate2Timestamp( $date, $time = '' )
6150 // Breakup the string using either a space, fwd slash, dash, bkwd slash or
6151 // colon as a delimiter
6152 $atok = strtok( $date, ' /-\\:' );
6153 while( $atok !== FALSE )
6156 $atok = strtok( ' /-\\:' ); // get the next token
6159 for( $i = 0; $i < 3; $i++ )
6161 if( !isset( $atoks[$i] ) || !is_numeric( $atoks[$i] ))
6169 $timestamp = mktime( 0, 0, 0, $atoks[1], $atoks[2], $atoks[0] );
6173 $btok = strtok( $time, ' /-\\:' );
6174 while( $btok !== FALSE )
6177 $btok = strtok( ' /-\\:' );
6180 for( $i = 0; $i < 3; $i++ )
6182 if( !isset( $btoks[$i] ) || !is_numeric( $btoks[$i] ))
6188 $timestamp = mktime( $btoks[0], $btoks[1], $btoks[2],
6189 $atoks[1], $atoks[2], $atoks[0] );
6196 * Get the HTML for an image with height & width
6198 * @param string $file full path to the file
6199 * @return string html that will be included in the img-tag
6201 function COM_getImgSizeAttributes( $file )
6203 $sizeattributes = '';
6205 if( file_exists( $file ))
6207 $dimensions = getimagesize( $file );
6208 if( !empty( $dimensions[0] ) AND !empty( $dimensions[1] ))
6210 $sizeattributes = 'width="' . $dimensions[0]
6211 . '" height="' . $dimensions[1] . '" ';
6215 return $sizeattributes;
6219 * Display a message and abort
6221 * NOTE: Displays the message and aborts the script.
6223 * @param int $msg message number
6224 * @param string $plugin plugin name, if applicable
6225 * @param int $http_status HTTP status code to send with the message
6226 * @param string $http_text Textual version of the HTTP status code
6229 function COM_displayMessageAndAbort( $msg, $plugin = '', $http_status = 200, $http_text = 'OK')
6231 $display = COM_siteHeader( 'menu' )
6232 . COM_showMessage( $msg, $plugin )
6233 . COM_siteFooter( true );
6235 if( $http_status != 200 )
6237 header( "HTTP/1.1 $http_status $http_text" );
6238 header( "Status: $http_status $http_text" );
6245 * Return full URL of a topic icon
6247 * @param string $imageurl (relative) topic icon URL
6248 * @return string Full URL
6251 function COM_getTopicImageUrl( $imageurl )
6253 global $_CONF, $_THEME_URL;
6257 if( !empty( $imageurl ))
6259 if( isset( $_THEME_URL ))
6261 $iconurl = $_THEME_URL . $imageurl;
6265 $stdImageLoc = true;
6266 if( !strstr( $_CONF['path_images'], $_CONF['path_html'] ))
6268 $stdImageLoc = false;
6273 $iconurl = $_CONF['site_url'] . $imageurl;
6277 $t = explode( '/', $imageurl );
6278 $topicicon = $t[count( $t ) - 1];
6279 $iconurl = $_CONF['site_url']
6280 . '/getimage.php?mode=topics&image=' . $topicicon;
6289 * Create an HTML link
6291 * @param string $content the object to be linked (text, image etc)
6292 * @param string $url the URL the link will point to
6293 * @param array $attr an array of optional attributes for the link
6294 * for example array('title' => 'whatever');
6295 * @return string the HTML link
6297 function COM_createLink($content, $url, $attr = array())
6301 $attr_str = 'href="' . $url . '"';
6302 foreach ($attr as $key => $value) {
6303 $attr_str .= " $key=\"$value\"";
6305 $retval .= "<a $attr_str>$content</a>";
6311 * Create an HTML img
6313 * @param string $url the URL of the image, either starting with
6314 * http://... or $_CONF['layout_url'] is prepended
6315 * @param string $alt the 'alt'-tag of the image
6316 * @param array $attr an array of optional attributes for the link
6317 * for example array('title' => 'whatever');
6318 * @return string the HTML img
6320 function COM_createImage($url, $alt = "", $attr = array())
6326 if (preg_match("/^(https?):/", $url) !== 1) {
6327 $url = $_CONF['layout_url'] . $url;
6329 $attr_str = 'src="' . $url . '"';
6331 foreach ($attr as $key => $value) {
6332 $attr_str .= " $key=\"$value\"";
6335 $retval = "<img $attr_str alt=\"$alt\"" . XHTML . ">";
6341 * Try to determine the user's preferred language by looking at the
6342 * "Accept-Language" header sent by their browser (assuming they bothered
6343 * to select a preferred language there).
6345 * Sample header: Accept-Language: en-us,en;q=0.7,de-de;q=0.3
6347 * @return string name of the language file to use or an empty string
6348 * @todo Bugs: Does not take the quantity ('q') parameter into account,
6349 * but only looks at the order of language codes.
6352 function COM_getLanguageFromBrowser()
6358 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
6359 $accept = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
6360 foreach ($accept as $l) {
6361 $l = explode(';', trim($l));
6363 if (array_key_exists($l, $_CONF['language_files'])) {
6364 $retval = $_CONF['language_files'][$l];
6367 $l = explode('-', $l);
6369 if (array_key_exists($l, $_CONF['language_files'])) {
6370 $retval = $_CONF['language_files'][$l];
6381 * Determine current language
6383 * @return string name of the language file (minus the '.php' extension)
6386 function COM_getLanguage()
6388 global $_CONF, $_USER;
6392 if (!empty($_USER['language'])) {
6393 $langfile = $_USER['language'];
6394 } elseif (!empty($_COOKIE[$_CONF['cookie_language']])) {
6395 $langfile = $_COOKIE[$_CONF['cookie_language']];
6396 } elseif (isset($_CONF['languages'])) {
6397 $langfile = COM_getLanguageFromBrowser();
6400 $langfile = COM_sanitizeFilename($langfile);
6401 if (!empty($langfile)) {
6402 if (is_file($_CONF['path_language'] . $langfile . '.php')) {
6407 // if all else fails, return the default language
6408 return $_CONF['language'];
6412 * Determine the ID to use for the current language
6414 * The $_CONF['language_files'] array maps language IDs to language file names.
6415 * This function returns the language ID for a certain language file, to be
6416 * used in language-dependent URLs.
6418 * @param string $language current language file name (optional)
6419 * @return string language ID, e.g 'en'; empty string on error
6422 function COM_getLanguageId($language = '')
6426 if (empty($language)) {
6427 $language = COM_getLanguage();
6431 if (isset($_CONF['language_files'])) {
6432 $lang_id = array_search($language, $_CONF['language_files']);
6434 if ($lang_id === false) {
6435 // that looks like a misconfigured $_CONF['language_files'] array
6436 COM_errorLog('Language "' . $language . '" not found in $_CONF[\'language_files\'] array!');
6438 $lang_id = ''; // not much we can do here ...
6446 * Return SQL expression to request language-specific content
6448 * Creates part of an SQL expression that can be used to request items in the
6449 * current language only.
6451 * @param string $field name of the "id" field, e.g. 'sid' for stories
6452 * @param string $type part of the SQL expression, e.g. 'WHERE', 'AND'
6453 * @param string $table table name if ambiguous, e.g. in JOINs
6454 * @return string SQL expression string (may be empty)
6457 function COM_getLangSQL( $field, $type = 'WHERE', $table = '' )
6463 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6465 if( !empty( $table ))
6470 $lang_id = COM_getLanguageId();
6472 if( !empty( $lang_id ))
6474 $sql = ' ' . $type . " ({$table}$field LIKE '%\\_$lang_id')";
6482 * Provide a block to switch languages
6484 * Provides a drop-down menu (or simple link, if you only have two languages)
6485 * to switch languages. This can be used as a PHP block or called from within
6486 * your theme's header.thtml:
6488 * <?php print phpblock_switch_language(); ?>
6491 * @return string HTML for drop-down or link to switch languages
6494 function phpblock_switch_language()
6500 if( empty( $_CONF['languages'] ) || empty( $_CONF['language_files'] ) ||
6501 ( count( $_CONF['languages'] ) != count( $_CONF['language_files'] )))
6506 $lang = COM_getLanguage();
6507 $langId = COM_getLanguageId( $lang );
6509 if( count( $_CONF['languages'] ) == 2 )
6511 foreach( $_CONF['languages'] as $key => $value )
6513 if( $key != $langId )
6521 $switchUrl = COM_buildUrl( $_CONF['site_url'] . '/switchlang.php?lang='
6523 $retval .= COM_createLink($newLang, $switchUrl);
6527 $retval .= '<form name="change" action="'. $_CONF['site_url']
6528 . '/switchlang.php" method="get">' . LB;
6529 $retval .= '<input type="hidden" name="oldlang" value="' . $langId
6530 . '"' . XHTML . '>' . LB;
6532 $retval .= '<select onchange="change.submit()" name="lang">';
6533 foreach( $_CONF['languages'] as $key => $value )
6535 if( $lang == $_CONF['language_files'][$key] )
6537 $selected = ' selected="selected"';
6543 $retval .= '<option value="' . $key . '"' . $selected . '>'
6544 . $value . '</option>' . LB;
6546 $retval .= '</select>' . LB;
6547 $retval .= '</form>' . LB;
6554 * Switch locale settings
6556 * When multi-language support is enabled, allow overwriting the default locale
6557 * settings with language-specific settings (date format, etc.). So in addition
6558 * to $_CONF['date'] you can have a $_CONF['date_en'], $_CONF['date_de'], etc.
6561 function COM_switchLocaleSettings()
6565 if( !empty( $_CONF['languages'] ) && !empty( $_CONF['language_files'] ))
6567 $overridables = array
6570 'date', 'daytime', 'shortdate', 'dateonly', 'timeonly',
6571 'week_start', 'hour_mode',
6572 'thousand_separator', 'decimal_separator'
6575 $langId = COM_getLanguageId();
6576 foreach( $overridables as $option )
6578 if( isset( $_CONF[$option . '_' . $langId] ))
6580 $_CONF[$option] = $_CONF[$option . '_' . $langId];
6587 * Get the name of the current language, minus the character set
6589 * Strips the character set from $_CONF['language'].
6591 * @return string language name
6594 function COM_getLanguageName()
6600 $charset = '_' . strtolower(COM_getCharset());
6601 if (substr($_CONF['language'], -strlen($charset)) == $charset) {
6602 $retval = substr($_CONF['language'], 0, -strlen($charset));
6604 $retval = $_CONF['language'];
6613 * Truncates a string to a max. length and optionally adds a filler string,
6614 * e.g. '...', to indicate the truncation.
6615 * This function is multi-byte string aware, based on a patch by Yusuke Sakata.
6617 * NOTE: The truncated string may be shorter but will never be longer than
6618 * $maxlen characters, i.e. the $filler string is taken into account.
6620 * @param string $text the text string to truncate
6621 * @param int $maxlen max. number of characters in the truncated string
6622 * @param string $filler optional filler string, e.g. '...'
6623 * @param int $endchars number of characters to show after the filler
6624 * @return string truncated string
6627 function COM_truncate( $text, $maxlen, $filler = '', $endchars = 0 )
6629 $newlen = $maxlen - MBYTE_strlen( $filler );
6630 $len = MBYTE_strlen( $text );
6631 if( $len > $maxlen )
6633 $text = MBYTE_substr( $text, 0, $newlen - $endchars ) . $filler . MBYTE_substr( $text, $len - $endchars, $endchars );
6640 * Get the current character set
6642 * Uses (if available, and in this order)
6643 * - $LANG_CHARSET (from the current language file)
6644 * - $_CONF['default_charset'] (from siteconfig.php)
6645 * - 'iso-8859-1' (hard-coded fallback)
6647 * @return string character set, e.g. 'utf-8'
6650 function COM_getCharset()
6652 global $_CONF, $LANG_CHARSET;
6654 if( empty( $LANG_CHARSET )) {
6655 $charset = $_CONF['default_charset'];
6656 if( empty( $charset )) {
6657 $charset = 'iso-8859-1';
6660 $charset = $LANG_CHARSET;
6669 * This function will handle all PHP errors thrown at it, without exposing
6670 * paths, and hopefully, providing much more information to Root Users than
6671 * the default white error page.
6673 * This function will call out to CUSTOM_handleError if it exists, but, be
6674 * advised, only override this function with a very, very stable function. I'd
6675 * suggest one that outputs some static, basic HTML.
6677 * The PHP feature that allows us to do so is documented here:
6678 * http://uk2.php.net/manual/en/function.set-error-handler.php
6680 * @param int $errno Error Number.
6681 * @param string $errstr Error Message.
6682 * @param string $errfile The file the error was raised in.
6683 * @param int $errline The line of the file that the error was raised at.
6684 * @param array $errcontext An array that points to the active symbol table at the point the error occurred.
6686 function COM_handleError($errno, $errstr, $errfile='', $errline=0, $errcontext='')
6688 global $_CONF, $_USER;
6690 // Handle @ operator
6691 if (error_reporting() == 0) {
6695 // If in PHP4, then respect error_reporting
6696 if ((PHP_VERSION < 5) && (($errno & error_reporting()) == 0)) {
6701 * If we have a root user, then output detailed error message:
6703 if ((is_array($_USER) && function_exists('SEC_inGroup'))
6704 || (isset($_CONF['rootdebug']) && $_CONF['rootdebug'])) {
6705 if ($_CONF['rootdebug'] || SEC_inGroup('Root')) {
6707 header('HTTP/1.1 500 Internal Server Error');
6708 header('Status: 500 Internal Server Error');
6710 $title = 'An Error Occurred';
6711 if (!empty($_CONF['site_name'])) {
6712 $title = $_CONF['site_name'] . ' - ' . $title;
6714 echo("<html><head><title>$title</title></head>\n<body>\n");
6716 echo('<h1>An error has occurred:</h1>');
6717 if ($_CONF['rootdebug']) {
6718 echo('<h2 style="color: red">This is being displayed as "Root Debugging" is enabled
6719 in your Geeklog configuration.</h2><p>If this is a production
6720 website you <strong><em>must disable</em></strong> this
6721 option once you have resolved any issues you are
6722 investigating.</p>');
6724 echo('<p>(This text is only displayed to users in the group \'Root\')</p>');
6726 echo("<p>$errno - $errstr @ $errfile line $errline</p>");
6728 if (!function_exists('SEC_inGroup') || !SEC_inGroup('Root')) {
6729 if ('force' != ''.$_CONF['rootdebug']) {
6730 $errcontext = COM_rootDebugClean($errcontext);
6732 echo('<h2 style="color: red">Root Debug is set to "force", this
6733 means that passwords and session cookies are exposed in this
6739 var_dump($errcontext);
6740 $errcontext = htmlspecialchars(ob_get_contents());
6742 echo("$errcontext</pre></body></html>");
6747 /* If there is a custom error handler, fail over to that, but only
6748 * if the error wasn't in lib-custom.php
6750 if (is_array($_CONF) && !(strstr($errfile, 'lib-custom.php'))) {
6751 if (array_key_exists('path_system', $_CONF)) {
6752 if (file_exists($_CONF['path_system'] . 'lib-custom.php')) {
6753 require_once $_CONF['path_system'] . 'lib-custom.php';
6755 if (function_exists('CUSTOM_handleError')) {
6756 CUSTOM_handleError($errno, $errstr, $errfile, $errline, $errcontext);
6762 // if we do not throw the error back to an admin, still log it in the error.log
6763 COM_errorLog("$errno - $errstr @ $errfile line $errline", 1);
6765 header('HTTP/1.1 500 Internal Server Error');
6766 header('Status: 500 Internal Server Error');
6768 // Does the theme implement an error message html file?
6769 if (!empty($_CONF['path_layout']) &&
6770 file_exists($_CONF['path_layout'] . 'errormessage.html')) {
6771 // NOTE: NOT A TEMPLATE! JUST HTML!
6772 include $_CONF['path_layout'] . 'errormessage.html';
6774 // Otherwise, display simple error message
6775 $title = 'An Error Occurred';
6776 if (!empty($_CONF['site_name'])) {
6777 $title = $_CONF['site_name'] . ' - ' . $title;
6782 <title>{$title}</title>
6785 <div style=\"width: 100%; text-align: center;\">
6786 Unfortunately, an error has occurred rendering this page. Please try
6798 * Recurse through the error context array removing/blanking password/cookie
6799 * values in case the "for development" only switch is left on in a production
6802 * [Not fit for public consumption comments about what users who enable root
6803 * debug in production should have done to them, and why making this change
6804 * defeats the point of the entire root debug feature go here.]
6806 * @param array $array Array of state info (Recursive array).
6807 * @param boolean $blank override (wouldn't that blank out everything?)
6808 * @return array Cleaned array
6810 function COM_rootDebugClean($array, $blank=false)
6812 $blankField = false;
6813 while(list($key, $value) = each($array)) {
6814 $lkey = strtolower($key);
6815 if((strpos($lkey, 'pass') !== false) || (strpos($lkey, 'cookie') !== false)) {
6818 $blankField = $blank;
6820 if(is_array($value)) {
6821 $array[$key] = COM_rootDebugClean($value, $blankField);
6822 } elseif($blankField) {
6823 $array[$key] = '[VALUE REMOVED]';
6830 * Checks to see if a specified user, or the current user if non-specified
6831 * is the anonymous user.
6833 * @param int $uid ID of the user to check, or none for the current user.
6834 * @return boolean true if the user is the anonymous user.
6836 function COM_isAnonUser($uid = '')
6840 /* If no user was specified, fail over to the current user if there is one */
6843 if( isset( $_USER['uid'] ) )
6845 $uid = $_USER['uid'];
6849 if( !empty( $uid ) )
6858 * Create Meta Tags to be used by COM_siteHeader in the headercode variable
6860 * @param string $meta_description the text for the meta description of the page being displayed
6861 * @param string $meta_keywords the text for the meta keywords of the page being displayed
6862 * @return string XHTML formatted text
6865 function COM_createMetaTags($meta_description, $meta_keywords)
6871 If ($_CONF['meta_tags'] > 0) {
6872 if ($meta_description != '') {
6873 $headercode .= LB . '<meta name="description" content="' . $meta_description . '"' . XHTML . '>';
6875 if ($meta_keywords != '') {
6876 $headercode .= LB . '<meta name="keywords" content="' . $meta_keywords . '"' . XHTML . '>';
6886 * Convert wiki-formatted text to (X)HTML
6888 * @param string $wikitext wiki-formatted text
6889 * @return string XHTML formatted text
6892 function COM_renderWikiText($wikitext)
6896 if (!$_CONF['wikitext_editor']) {
6900 require_once 'Text/Wiki.php';
6902 $wiki = new Text_Wiki();
6903 $wiki->setFormatConf('Xhtml', 'translate', HTML_SPECIALCHARS);
6904 $wiki->setRenderConf('Xhtml', 'charset', COM_getCharset());
6905 $wiki->disableRule('wikilink');
6906 $wiki->disableRule('freelink');
6907 $wiki->disableRule('interwiki');
6909 return $wiki->transform($wikitext, 'Xhtml');
6913 * Set the {lang_id} and {lang_attribute} variables for a template
6915 * NOTE: {lang_attribute} is only set in multi-language environments.
6917 * @param ref &$template template to use
6921 function COM_setLangIdAndAttribute(&$template)
6928 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6929 $langId = COM_getLanguageId();
6931 // try to derive the language id from the locale
6932 $l = explode('.', $_CONF['locale']); // get rid of character set
6934 $l = explode('@', $langId); // get rid of '@euro', etc.
6938 if (!empty($langId)) {
6939 $l = explode('-', str_replace('_', '-', $langId));
6940 if ((count($l) == 1) && (strlen($langId) == 2)) {
6941 $langAttr = 'lang="' . $langId . '"';
6942 } else if (count($l) == 2) {
6943 if (($l[0] == 'i') || ($l[0] == 'x')) {
6944 $langId = implode('-', $l);
6945 $langAttr = 'lang="' . $langId . '"';
6946 } else if (strlen($l[0]) == 2) {
6947 $langId = implode('-', $l);
6948 $langAttr = 'lang="' . $langId . '"';
6951 // this isn't a valid lang attribute, so don't set $langAttr
6955 $template->set_var('lang_id', $langId);
6957 if (!empty($_CONF['languages']) && !empty($_CONF['language_files'])) {
6958 $template->set_var('lang_attribute', ' ' . $langAttr);
6960 $template->set_var('lang_attribute', '');
6965 * Sends compressed output to browser.
6967 * Assumes that $display contains the _entire_ output for a request - no
6968 * echoes are allowed before or after this function.
6969 * Currently only supports gzip compression. Checks if zlib compression is
6970 * enabled in PHP and does uncompressed output if it is.
6972 * @param string $display Content to send to browser
6976 function COM_output($display)
6980 if (empty($display)) {
6984 if ($_CONF['compressed_output']) {
6985 $gzip_accepted = false;
6986 if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
6987 $enc = str_replace(' ', '', $_SERVER['HTTP_ACCEPT_ENCODING']);
6988 $accept = explode(',', strtolower($enc));
6989 $gzip_accepted = in_array('gzip', $accept);
6992 if ($gzip_accepted && function_exists('gzencode')) {
6994 $zlib_comp = ini_get('zlib.output_compression');
6995 if (empty($zlib_comp) || (strcasecmp($zlib_comp, 'off') == 0)) {
6997 header('Content-encoding: gzip');
6998 echo gzencode($display);
7009 * Turn a piece of HTML into continuous(!) plain text
7011 * This function removes HTML tags, line breaks, etc. and returns one long
7012 * line of text. This is useful for word counts (do an explode() on the result)
7013 * and for text excerpts.
7015 * @param string $text original text, including HTML and line breaks
7016 * @return string continuous plain text
7019 function COM_getTextContent($text)
7021 // replace <br> with spaces so that Text<br>Text becomes two words
7022 $text = preg_replace('/\<br(\s*)?\/?\>/i', ' ', $text);
7024 // add extra space between tags, e.g. <p>Text</p><p>Text</p>
7025 $text = str_replace('><', '> <', $text);
7027 // only now remove all HTML tags
7028 $text = strip_tags($text);
7030 // replace all tabs, newlines, and carrriage returns with spaces
7031 $text = str_replace(array("\011", "\012", "\015"), ' ', $text);
7033 // replace entities with plain spaces
7034 $text = str_replace(array('', ' ', ' '), ' ', $text);
7036 // collapse whitespace
7037 $text = preg_replace('/\s\s+/', ' ', $text);
7043 * Now include all plugin functions
7045 foreach ($_PLUGINS as $pi_name) {
7046 require_once $_CONF['path'] . 'plugins/' . $pi_name . '/functions.inc';
7049 // Check and see if any plugins (or custom functions)
7050 // have scheduled tasks to perform
7051 if ($_CONF['cron_schedule_interval'] > 0) {
7052 if ((DB_getItem($_TABLES['vars'], 'value', "name='last_scheduled_run'")
7053 + $_CONF['cron_schedule_interval']) <= time()) {
7054 DB_query("UPDATE {$_TABLES['vars']} SET value=UNIX_TIMESTAMP() WHERE name='last_scheduled_run'");
7055 PLG_runScheduledTask();