http://wiki.geeklog.net/api.php?action=feedcontributions&user=Rasade&feedformat=atomGeeklogWiki - User contributions [en]2024-03-28T21:48:03ZUser contributionsMediaWiki 1.27.5http://wiki.geeklog.net/index.php?title=Autoinstall_Feature&diff=5812Autoinstall Feature2010-04-21T20:21:59Z<p>Rasade: /* Autoinstall Feature */</p>
<hr />
<div>From Geeklog 1.6 onwards, plugins have an alternate installation method, the 'Autoinstall'. This is implemented in the form of a file named <tt>autoinstall.php</tt> in the same location within the plugin directory as the <tt>functions.inc</tt> file. To install a plugin using the Autoinstall function, all users need to do is go to the 'Plugins' section of the Admin panel and choose the relevant tarball for the plugin and upload it. The Autoinstall function will take care of the rest, as in creation of database tables, new plugin-specific user groups, as well as moving the required plugin files to the relevant locations within the Geeklog installation. However, this only works with Geeklog 1.6 and above. <br />
For backward compatibility, plugin developers might feel the need to include the old <tt>install.php</tt> script along with their plugins as well.<br />
The main function included in <tt>autoinstall.php</tt> is <tt>plugin_autoinstall_{plugin}</tt>, where {plugin} is replaced by the name of the plugin. There are optional functions available for inclusion within <tt>authoinstall.php</tt>. You can read about them in the [documentation : http://wiki.geeklog.net/index.php/Plugin_Autoinstall]. As an example we will take a look at the <tt>autoinstall.php</tt> file written by Javed Khan for the Boilerplate plugin:<br />
<pre><br />
<?php<br />
<br />
/**<br />
* Plugin autoinstall function<br />
*<br />
* @param string $pi_name Plugin name<br />
* @return array Plugin information<br />
*<br />
*/<br />
function plugin_autoinstall_sample($pi_name)<br />
{<br />
$pi_name = 'sample';<br />
$pi_display_name = 'Boilerplate Plugin'; <br />
<br />
$info = array(<br />
'pi_name' => $pi_name,<br />
'pi_display_name' => $pi_display_name,<br />
'pi_version' => '1.6.3',<br />
'pi_gl_version' => '1.6.1',<br />
'pi_homepage' => 'http://www.geeklog.net/'<br />
);<br />
<br />
$tables = array(<br />
'base',<br />
'financialData'<br />
);<br />
<br />
$inst_parms = array(<br />
'info' => $info,<br />
'tables' => $tables<br />
);<br />
<br />
return $inst_parms;<br />
}<br />
<br />
?><br />
</pre><br />
<br />
As you can see, it's fairly simple. For further clarification, we will take a look at each set of values implemented here.<br />
<br />
----<br />
<br />
<pre><br />
$info = array(<br />
'pi_name' => $pi_name,<br />
'pi_display_name' => $pi_display_name,<br />
'pi_version' => '1.6.3',<br />
'pi_gl_version' => '1.6.1',<br />
'pi_homepage' => 'http://www.geeklog.net/'<br />
);<br />
</pre><br />
This array holds general information regarding the plugin and should be fairly self-explanatory.<br />
<br />
----<br />
<pre><br />
$tables = array(<br />
'base',<br />
'financialData'<br />
);<br />
</pre><br />
<br />
This array holds the table names that should be created in the database when the plugin is installed. The table creation scripts would normally be included in <tt>/admin/install.php</tt>. For the Boilerplate plugin, the table creation scripts are included in special install files based on the type of database query language being used. (The Boilerplate plugin has been built to demonstrate compatibility with different query languages)<br />
<br />
----<br />
<pre><br />
$inst_parms = array(<br />
'info' => $info,<br />
'tables' => $tables<br />
);<br />
</pre><br />
<br />
This is the array in the autoinstall script which holds all the parameters that the plugin refers to. It assigns all the variables defined above to special array keys which are recognized by Geeklog during installation.<br />
<br />
----<br />
Of course, more parameters could be added for usergroups, features, permissions etc.<br />
<br />
As always, the best way to get a good idea about how Geeklog handles plugins would be to take a good look at <tt>lib-plugins.php</tt>.</div>Rasadehttp://wiki.geeklog.net/index.php?title=Plugin_Developers_Handbook&diff=5811Plugin Developers Handbook2010-04-21T19:53:04Z<p>Rasade: /* Contents */ Added link to page on autoinstall</p>
<hr />
<div>== Geeklog Plugin Developers Handbook ==<br />
<br />
By [[User:Blaine|Blaine Lang]] and [[User:Tomw| Tom Willett]] December, 2002<br />
<br />
<br />
== Contents ==<br />
<br />
# [[Plugin_Developers_Handbook#Introduction and Foreword|Introduction and Foreword]]<br />
# [[Programming Style]]<br />
# [[Using Templates and Language Files]]<br />
# [[Geeklog's way of doing things]] <br />
# [[Blocks and Plugins]] <br />
# [[How to get Started: A Development Overview]] <br />
# [[Suggested and Required Directory Layout and Files]] <br />
# [[Plugin Installation]] <br />
## [[Sample Install Documentation]] <br />
## [[Sample install.php]]<br />
## [[Autoinstall Feature]]<br />
# Advanced Integration with Geeklog<br />
## [[Extending Site Statistics with your plugin]] <br />
## [[Integrating the Comment Engine]] <br />
## [[Using Geeklog's Search Engine]] <br />
## [[Adding Moderation Capability]] <br />
# [[Distribution]] <br />
# [[Universal Plugin ToolKit]] <br />
# [[functions.inc - Listing of the Plugin Functions]] <br />
# Other Documentation Files<br />
## [http://project.geeklog.net/src/Geeklog/_public_html---lib-common.php.html lib-common.php documentation] <br />
## [http://project.geeklog.net/src/Geeklog/_system---lib-security.php.html lib-security.php documentation] <br />
## [[Plugin Development|Geeklog Plugin Development Documentation]]<br />
<br />
== Introduction and Foreword ==<br />
<br />
This document and accompanying files are intended to make developing plugins and blocks for Geeklog easier. Our work is dependent upon the developers who wrote Geeklog and continue to expand and support it: Tony Bibbs, Dirk Haun, Jeffrey Schoolcraft, Jason Whittenburg, Matt Braafhart, Mark Limburg, Gene Wood, Yanick Bourbeau, Tane Piper and others. It is based on the earlier documentation written by Tony Bibbs and the collective knowledge contained in the mailing lists and websites devoted to Geeklog. The Moderation API documentation was provided by Vincent Furia.<br />
<br />
This documentation was developed with Geeklog version 1.3.7 in mind, but it should work with minor variations for earlier 1.3 versions and should apply to future versions in the 1.x series.<br />
<br />
The following suggestions about Programming Style, the use of Templates and Language Files, and Geeklog's Way of Doing Things are simply that -- suggestions. You can choose not to follow them and have a perfectly valid plugin or block. Following these suggestions will make your job easier in the long run and make your code more usable by others.<br />
<br />
This excerpt from the original Geeklog Plugin Developer Documentation by Tony Bibbs aptly summarizes the place plugins play in the Geeklog project:<br />
<br />
-----<br />
<br />
:Geeklog is becoming more and more popular each day and we, the Geeklog developers, are amazed at some of the great hacks people have made to extend their Geeklog installation to fit their own needs. At the same time, the Geeklog development team is continually adding new features that make Geeklog even better. We have realized the need for Geeklog to support two threads of development: core Geeklog code and plugin code. By building in the infrastructure needed to extend Geeklog's functionality through plugins we can make a clean separation between the Geeklog codebase and plugin code so that we can concentrate on making Geeklog's core code better while others can develop plugins so that Geeklog fits their needs. With that said, Geeklog now has a Plugin application program interface (API). <br />
<br />
:At the highest level, the Geeklog Plugin API is generic code that is called in strategic places in the Geeklog codebase that allow function of plugins to be called. This will allow your plugin the following features: <br />
<br />
:* Ability for your plugin to be submission-based so that users can submit objects to your plugin. You can then visit the command and control center in Geeklog to moderate the submissions for your plugin. <br />
:* Allow your plugin to show up in the Admin block and User block on each Geeklog page. <br />
:* Allow your plugin to be searched via the Geeklog search page. <br />
:* Allow stats for your plugin to show up on the site statistics page. <br />
:* Allow your plugin the ability to use Geeklog's comment engine. <br />
:* Allow you to use the power of Geeklog's code library (lib-common.php) in your own plugin code. <br />
:* Allow you full flexibility on what your plugin does. Geeklog does not dictate your plugin's power.<br />
<br />
-----<br />
<br />
[[Category:Plugin Developers Handbook]] [[Category:Plugin Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Sample_install.php&diff=5810Sample install.php2010-04-19T13:27:15Z<p>Rasade: Added COM_output()</p>
<hr />
<div><pre><br />
<?php <br />
/* Reminder: always indent with 4 spaces (no tabs). */ <br />
// +---------------------------------------------------------------------------+ <br />
// | File Management Plugin v1.0 for Geeklog - The Ultimate Weblog | <br />
// +---------------------------------------------------------------------------+ <br />
// | install.php | <br />
// | This is the main install and de-install program for the File Management | <br />
// | Plugin. By default this program will automatically create all required | <br />
// | Tables, groups, security rights and group assignments. | <br />
// | | <br />
// | I have extended the standard PLUGIN Install script with a admin controls | <br />
// | These are set in the install.cfg file. By default - all are set to true | <br />
// | for auto install/deinstall operation. But if you are upgrading or had | <br />
// | some problems, these switches let you control the install/deinstall | <br />
// | | <br />
// | Added display output to users on succes/failure and redirect to main page | <br />
// | | <br />
// | Additionally, Check your error.log for process execution status | <br />
// +---------------------------------------------------------------------------+ <br />
// | Copyright (C) 2000,2001 by the following authors: | <br />
// | | <br />
// | Authors: Blaine Lang <aka: efarmboy> - langmail AT sympatico DOT ca | <br />
// | August 2002 | <br />
// +---------------------------------------------------------------------------+ <br />
// | | <br />
// | This program is free software; you can redistribute it and/or | <br />
// | modify it under the terms of the GNU General Public License | <br />
// | as published by the Free Software Foundation; either version 2 | <br />
// | of the License, or (at your option) any later version. | <br />
// | | <br />
// | This program is distributed in the hope that it will be useful, | <br />
// | but WITHOUT ANY WARRANTY; without even the implied warranty of | <br />
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | <br />
// | GNU General Public License for more details. | <br />
// | | <br />
// | You should have received a copy of the GNU General Public License | <br />
// | along with this program; if not, write to the Free Software Foundation, | <br />
// | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | <br />
// | | <br />
// +---------------------------------------------------------------------------+ <br />
// <br />
// $Id: install.php,v 2.0 2002/08/23 18:22:02 blang Exp $ <br />
<br />
include_once('../../../lib-common.php'); <br />
include_once($_CONF['path'] . 'plugins/filemgmt/lang.php'); <br />
include_once($_CONF['path'] . 'plugins/filemgmt/config.php'); <br />
include_once($_CONF['path'] . 'plugins/filemgmt/install.cfg'); <br />
include_once($_CONF['path'] . 'plugins/filemgmt/functions.inc'); <br />
<br />
// Note: The de-install function is in the main plugin functions.inc file <br />
// This allows it to be called from the plugin admin page where you can enable/disable and delete the plugin <br />
<br />
<br />
// Only let Root users access this page <br />
if (!SEC_inGroup('Root')) { <br />
// Someone is trying to illegally access this page <br />
COM_errorLog($LANG_FILEMGMT['invalid_install'] . $_USER['uid'] . ", Username: {$_USER['username']}, IP: $REMOTE_ADDR",1); <br />
$display = COM_siteHeader(); <br />
$display .= COM_startBlock($LANG_FILEMGMT['access_denied']); <br />
$display .= $LANG_FILEMGMT['access_denied_msg']; <br />
$display .= COM_endBlock(); <br />
$display .= COM_siteFooter(); <br />
COM_output($display); <br />
exit; <br />
} <br />
<br />
$steps = array(); <br />
/** <br />
* Puts the datastructures for this plugin into the Geeklog database <br />
* <br />
*/ <br />
function plugin_install_filemgmt() { <br />
global $_TABLES, $_CONF, $LANG_FILEMGMT, $filemgmt_version; <br />
global $filemgmt_createtables, $filemgmt_creategroups, $filemgmt_createrights, $filemgmt_assigngroups, $filemgmt_assignrights, $filemgmt_registerPI; <br />
<br />
COM_errorLog($LANG_FILEMGMT['start_install'],1); <br />
<br />
// Installs the File Management Tables <br />
$steps['createtable'] = 1; <br />
COM_errorLog("Attempting to create FileMgmt databases", 1); <br />
if ($filemgmt_createtables) { <br />
if (!plugin_createtables_filemgmt()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
} else { <br />
COM_errorLog($LANG_FILEMGMT['install_skip'], 1); <br />
} <br />
<br />
// Create the FileMgmt security groups <br />
COM_errorLog("Attempting to create FileMgmt groups", 1); <br />
if ($filemgmt_creategroups) { <br />
DB_query("INSERT INTO {$_TABLES['groups']} (grp_name, grp_descr) " <br />
. "VALUES ('FileMgmt-Admin', 'Users in this group can administer the Filemgmt plugin')",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} else { <br />
COM_errorLog('About to save Filemgmt-Admin group_id to vars table for use during uninstall',1); <br />
// Remove any existing records <br />
DB_query("DELETE FROM {$_TABLES['vars']} WHERE name = 'fm_admingrp_id'"); <br />
DB_query("INSERT INTO {$_TABLES['vars']} VALUES ('fm_admingrp_id', LAST_INSERT_ID())",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
$group1_id = DB_getItem($_TABLES['vars'], 'value', "name = 'fm_admingrp_id'"); <br />
$steps['savedgroup1id'] = 1; <br />
} <br />
DB_query("INSERT INTO {$_TABLES['groups']} (grp_name, grp_descr) " <br />
. "VALUES ('FileMgmt-Users', 'Users in this group can use the Filemgmt plugin')",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
}else { <br />
COM_errorLog('About to save Filemgmt-Users group_id to vars table for use during uninstall',1); <br />
// Remove any existing records <br />
DB_query("DELETE FROM {$_TABLES['vars']} WHERE name = 'fm_usersgrp_id'"); <br />
DB_query("INSERT INTO {$_TABLES['vars']} VALUES ('fm_usersgrp_id', LAST_INSERT_ID())",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
$group2_id = DB_getItem($_TABLES['vars'], 'value', "name = 'fm_usersgrp_id'"); <br />
COM_errorLog('...success',1); <br />
$steps['savedgroup2id'] = 1; <br />
} <br />
COM_errorLog('...success',1); <br />
$steps['creategroups'] = 1; <br />
COM_errorLog('Groups added successfully',1); <br />
} else { <br />
COM_errorLog($LANG_FILEMGMT['install_skip'], 1); <br />
} <br />
<br />
// Add Filemgmmt plugin security features <br />
COM_errorLog('Attempting to create new Security Rights',1); <br />
if ($filemgmt_createrights) { <br />
COM_errorLog('Attempting to add filemgmt.edit feature',1); <br />
DB_query("INSERT INTO {$_TABLES['features']} (ft_name, ft_descr) " <br />
. "VALUES ('filemgmt.edit','Filemgmt plugin Administration')",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
$edit_id = DB_insertId(); <br />
<br />
COM_errorLog('Attempting to add filemgmt.user feature',1); <br />
DB_query("INSERT INTO {$_TABLES['features']} (ft_name, ft_descr) " <br />
. "VALUES ('filemgmt.user','Filemgmt User Access')",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
$user_id = DB_insertId(); <br />
<br />
COM_errorLog('Attempting to add filemgmt.upload feature',1); <br />
DB_query("INSERT INTO {$_TABLES['features']} (ft_name, ft_descr) " <br />
. "VALUES ('filemgmt.upload','Filemgmt ability to upload a file')",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
$upload_id = DB_insertId(); <br />
COM_errorLog('...success',1); <br />
COM_errorLog('Access Rights (features) added successfully',1); <br />
$steps['createrights'] = 1; <br />
} else { <br />
COM_errorLog($LANG_FILEMGMT['install_skip'], 1); <br />
} <br />
<br />
// Now assign the features to the FileMgmt groups <br />
COM_errorLog('Attempting to give FileMgmt-Admin group access to filemgmt.edit feature',1); <br />
if ($filemgmt_assignrights) { <br />
DB_query("INSERT INTO {$_TABLES['access']} (acc_ft_id, acc_grp_id) VALUES ($edit_id, $group1_id)"); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
<br />
COM_errorLog('Attempting to give FileMgmt-Admin group access to filemgmt.user feature',1); <br />
DB_query("INSERT INTO {$_TABLES['access']} (acc_ft_id, acc_grp_id) VALUES ($user_id, $group1_id)",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
<br />
COM_errorLog('Attempting to give FileMgmt-Admin group access to filemgmt.upload feature',1); <br />
DB_query("INSERT INTO {$_TABLES['access']} (acc_ft_id, acc_grp_id) VALUES ($upload_id, $group1_id)",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
$steps['addedusertoadmingroup'] = 1; <br />
<br />
COM_errorLog('Attempting to give FileMgmt-User group access to filemgmt.user feature',1); <br />
DB_query("INSERT INTO {$_TABLES['access']} (acc_ft_id, acc_grp_id) VALUES ($user_id, $group2_id)",1); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
COM_errorLog('Access Rights assigned to the Filemgnt groups successfully',1); <br />
$steps['assignrights'] = 1; <br />
<br />
} else { <br />
COM_errorLog($LANG_FILEMGMT['install_skip'], 1); <br />
} <br />
<br />
// OK, now give Root users access to both new groups for this plugin now! NOTE: Root group should always be 1 <br />
COM_errorLog('Attempting to give all users in Root group access Filemgmt-Admin group',1); <br />
if ($filemgmt_assigngroups) { <br />
COM_errorLog('Attempting to assign Filemgmt-Admin to ROOT group',1); <br />
DB_query("INSERT INTO {$_TABLES['group_assignments']} VALUES ($group1_id, NULL, 1)"); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('Attempting to assign Filemgmt-Users to ROOT group',1); <br />
DB_query("INSERT INTO {$_TABLES['group_assignments']} VALUES ($group2_id, NULL, 1)"); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
$steps['assigngroup'] = 1; <br />
} else { <br />
COM_errorLog($LANG_FILEMGMT['install_skip'], 1); <br />
} <br />
<br />
COM_errorLog('Registering Filemgmt plugin with Geeklog', 1); <br />
if ($filemgmt_registerPI) { // Register the plugin with Geeklog <br />
if (DB_count($_TABLES['plugins'],'pi_name','filemgmt') > 0) { <br />
COM_errorLog('Attempting to remove filemgmt plugin entry prior to adding an updated entry',1); <br />
DB_query("DELETE FROM {$_TABLES['plugins']} WHERE pi_name = 'filemgmt'"); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
} else { <br />
// Only install data on a fresh installation <br />
// This plugin has no install data <br />
} <br />
COM_errorLog('Attempting to create filemgmt plugin entry',1); <br />
DB_query("INSERT INTO {$_TABLES['plugins']} (pi_name, pi_version, pi_gl_version, pi_homepage, pi_enabled) " <br />
. "VALUES ('filemgmt', {$filemgmt_version}, '1.3.6', 'http://www.langfamily.ca', 1)"); <br />
DB_query("INSERT INTO {$_TABLES['vars']} VALUES ('filemgmt','1')"); <br />
if (DB_error()) { <br />
plugin_uninstall_filemgmt($steps); <br />
return false; <br />
exit; <br />
} <br />
COM_errorLog('...success',1); <br />
$steps['registerdPI'] = 1; <br />
<br />
} else { <br />
COM_errorLog($LANG_FILEMGMT['install_skip'], 1); <br />
} <br />
<br />
COM_errorLog('Succesfully completed FileMgmt Plugin installation!',1); <br />
return true; <br />
} <br />
<br />
<br />
/** <br />
* Create the requried database tables for this plugin into the Geeklog database <br />
* <br />
*/ <br />
function plugin_createtables_filemgmt() { <br />
global $LANG_FILEMGMT, $_FM_TABLES; <br />
<br />
# <br />
# Table structure for table to hold reported broken links <br />
# <br />
$createsql = "CREATE TABLE {$_FM_TABLES['filemgmt_brokenlinks']} ( <br />
reportid int(5) NOT NULL auto_increment, <br />
lid int(11) NOT NULL default '0', <br />
sender int(11) NOT NULL default '0', <br />
ip varchar(20) NOT NULL default '', <br />
PRIMARY KEY (reportid), <br />
KEY lid (lid), <br />
KEY sender (sender), <br />
KEY ip (ip) <br />
) TYPE=MyISAM"; <br />
<br />
//COM_errorLOG("SQL is:". $createsql); <br />
<br />
DB_query($createsql,1); <br />
if (DB_error()) { <br />
return false; <br />
exit; <br />
} <br />
<br />
# <br />
# Table structure for filemgmt categories - Top Level and subcategories <br />
# <br />
<br />
$createsql = "CREATE TABLE {$_FM_TABLES['filemgmt_cat']} ( <br />
cid int(5) unsigned NOT NULL auto_increment, <br />
pid int(5) unsigned NOT NULL default '0', <br />
title varchar(50) NOT NULL default '', <br />
imgurl varchar(150) NOT NULL default '', <br />
PRIMARY KEY (cid), <br />
KEY pid (pid) <br />
) TYPE=MyISAM"; <br />
<br />
DB_query($createsql,1); <br />
if (DB_error()) { <br />
return false; <br />
exit; <br />
} <br />
<br />
# <br />
# Table structure for filemgmt description details <br />
# <br />
<br />
$createsql = "CREATE TABLE {$_FM_TABLES['filemgmt_filedesc']} ( <br />
lid int(11) unsigned NOT NULL default '0', <br />
description text NOT NULL, <br />
KEY lid (lid) <br />
) TYPE=MyISAM"; <br />
<br />
DB_query($createsql,1); <br />
if (DB_error()) { <br />
return false; <br />
exit; <br />
} <br />
<br />
# <br />
# Table structure for filemgmt file details - main table <br />
# <br />
<br />
$createsql = "CREATE TABLE {$_FM_TABLES['filemgmt_filedetail']} ( <br />
lid int(11) unsigned NOT NULL auto_increment, <br />
cid int(5) unsigned NOT NULL default '0', <br />
title varchar(100) NOT NULL default '', <br />
url varchar(250) NOT NULL default '', <br />
homepage varchar(100) NOT NULL default '', <br />
version varchar(10) NOT NULL default '', <br />
size int(8) NOT NULL default '0', <br />
platform varchar(50) NOT NULL default '', <br />
logourl varchar(250) NOT NULL default '', <br />
submitter int(11) NOT NULL default '0', <br />
status tinyint(2) NOT NULL default '0', <br />
date int(10) NOT NULL default '0', <br />
hits int(11) unsigned NOT NULL default '0', <br />
rating double(6,4) NOT NULL default '0.0000', <br />
votes int(11) unsigned NOT NULL default '0', <br />
comments tinyint(2) NOT NULL default '1', <br />
PRIMARY KEY (lid), <br />
KEY cid (cid), <br />
KEY status (status), <br />
KEY title (title(40)) <br />
) TYPE=MyISAM"; <br />
<br />
DB_query($createsql,1); <br />
if (DB_error()) { <br />
return false; <br />
exit; <br />
} <br />
<br />
# <br />
# Table structure for filemgmt voting detail records <br />
# <br />
<br />
$createsql = "CREATE TABLE {$_FM_TABLES['filemgmt_votedata']} ( <br />
ratingid int(11) unsigned NOT NULL auto_increment, <br />
lid int(11) unsigned NOT NULL default '0', <br />
ratinguser int(11) NOT NULL default '0', <br />
rating tinyint(3) unsigned NOT NULL default '0', <br />
ratinghostname varchar(60) NOT NULL default '', <br />
ratingtimestamp int(10) NOT NULL default '0', <br />
PRIMARY KEY (ratingid), <br />
KEY ratinguser (ratinguser), <br />
KEY ratinghostname (ratinghostname), <br />
KEY lid (lid) <br />
) TYPE=MyISAM"; <br />
<br />
<br />
DB_query($createsql,1); <br />
if (DB_error()) { <br />
return false; <br />
} <br />
<br />
# <br />
# Table structure for filemgmt download history <br />
# <br />
<br />
$createsql = "CREATE TABLE {$_FM_TABLES['filemgmt_history']} ( <br />
uid mediumint(8) NOT NULL default '0', <br />
lid int(11) NOT NULL default '0', <br />
remote_ip varchar(15) NOT NULL default '', <br />
date datetime NOT NULL default '0000-00-00 00:00:00', <br />
KEY lid (lid), <br />
KEY uid (uid) <br />
) TYPE=MyISAM"; <br />
<br />
<br />
DB_query($createsql,1); <br />
if (DB_error()) { <br />
return false; <br />
} <br />
<br />
<br />
return true; <br />
<br />
} <br />
<br />
function redirect_header($url, $time=4, $message=""){ <br />
echo "<html><head>\n"; <br />
echo "<meta http-equiv='Content-Type' content='text/html; charset="._CHARSET."' />\n"; <br />
echo "<meta http-equiv='Refresh' content='$time; url=$url' />\n"; <br />
echo "<style> <br />
body { <br />
margin:50px 0px; padding:0px; <br />
text-align:center; <br />
} <br />
#Content { <br />
width:100%; <br />
margin:0px auto; <br />
text-align:center; <br />
padding:15px; <br />
border:1px dashed #333; <br />
background-color:#eee; <br />
} <br />
</style>"; <br />
echo "</head><body><div id='content'>\n"; <br />
if ( $message!="" ) { <br />
echo "<h4>".$message."</h4>\n"; <br />
} else { <br />
echo "<h4>"._TAKINGBACK."</h4>\n"; <br />
} <br />
echo "<br /><b>\n"; <br />
printf(_IFNOTRELOAD,$url); <br />
echo "</b>\n"; <br />
echo "</div>\n"; <br />
echo "</body></html>"; <br />
} <br />
<br />
<br />
/* MAIN <br />
*/ <br />
<br />
$display = COM_siteHeader(); <br />
// User can set the filemgmt_autoinstall variable to override automatic install <br />
// By Default the varaibles in filemgmt.cfg are all set to true <br />
COM_errorLOG("Filemgmt Plugin Install Script executing"); <br />
<br />
if($_POST['submit']) { // True if ForceDeinstall set and user confirms they want to proceed <br />
COM_errorLOG("Filemgmt Plugin De-Install initiating - Removing filemgmt record in vars table"); <br />
$res = @mysql_query("SELECT 1 FROM {$_FM_TABLES['filemgmt_cat']} LIMIT 1"); <br />
if ($res) { <br />
DB_query("DELETE FROM {$_TABLES['vars']} WHERE name = 'filemgmt'"); <br />
} <br />
if (plugin_uninstall_filemgmt()) { <br />
// Uninstall worked <br />
COM_errorLOG("Plugin de-install completed - exiting"); <br />
redirect_header($_CONF['site_url'] ."/index.php",2,$LANG_FILEMGMT['uninstall_complete_msg']); <br />
exit(); <br />
} else { <br />
// Uninstall failed <br />
COM_errorLOG("Plugin de-install failed - exiting"); <br />
redirect_header($_CONF['site_url'] ."/index.php",2,$LANG_FILEMGMT['uninstall_failed_msg']); <br />
exit(); <br />
} <br />
<br />
} else { <br />
<br />
if ((DB_count($_TABLES['vars'], 'name', 'filemgmt') == 0) AND ($filemgmt_autoinstall)) { <br />
// Record in vars table doesn' exit, install this plugin <br />
COM_errorLOG("Autoinstall initiated"); <br />
if (plugin_install_filemgmt()) { <br />
COM_errorLOG("Autoinstall completed"); <br />
redirect_header($_CONF['site_url'] ."/index.php",2,$LANG_FILEMGMT['installation_complete_msg']); <br />
exit(); <br />
} else { <br />
// Error occured <br />
COM_errorLOG("Autoinstall Failed"); <br />
redirect_header($_CONF['site_url'] ."/index.php",2,$LANG_FILEMGMT['installation_failed_msg']); <br />
exit(); <br />
} <br />
} else { <br />
// Check if this plugin is installed and enabled <br />
if (DB_getItem($_TABLES['plugins'],'pi_enabled',"pi_name = 'filemgmt'") == 1) { <br />
COM_errorLOG("Plugin allready installed"); <br />
// Check to see if user has set override to force de-install of plugin and then prompt user to confirm de-install <br />
if($filemgmt_forceDeinstall){ <br />
// Uninstall plugin <br />
$display .= COM_startBlock("<b>Filemgmt Plugin De-Install</b>"); <br />
$display .= "<table border=0 cellpadding=1 cellspacing=0 width='80%'>"; <br />
$display .= "<tr><td align='center'>"; <br />
$display .= "<br><p><H2>Do you really want to forceably remove all tables and data associated with the File Mgmt Plugin?</H2><p>"; <br />
$display .= "</td></tr><tr><td align = 'center'>"; <br />
$display .= "<form action='install.php' method='post'>"; <br />
$display .= "<br><br><input type='submit' name='submit' value='Yes-Deinstall Now!'\n>"; <br />
$display .= "&nbsp;<input type='button' value='Cancel' onclick='javascript:history.go(-1)'>\n"; <br />
$display .= "</form></td></tr></table>"; <br />
$display .= COM_endBlock(); <br />
COM_output($display); <br />
<br />
} else { <br />
// This is locked, do nothing <br />
COM_errorLOG("Plugin is enabled and Force De-install was false. Exiting"); <br />
redirect_header($_CONF['site_url'] ."/index.php",2,$LANG_FILEMGMT['system_locked_msg']); <br />
exit(); <br />
} <br />
} else { <br />
// Uninstall plugin <br />
COM_errorLOG("Plugin not enabled, De-Install initiating"); <br />
if ($filemgmt_autodeinstall) { // Check to see if option has been disabled <br />
$display .= COM_startBlock("<b>Filemgmt Plugin De-Install</b>"); <br />
$display .= "<table border=0 cellpadding=1 cellspacing=0 width='80%'>"; <br />
$display .= "<tr><td align='center'>"; <br />
$display .= "<br><p><H2>Do you really want to forceably remove all tables and data associated with the File Mgmt Plugin?</H2><p>"; <br />
$display .= "</td></tr><tr><td align = 'center'>"; <br />
$display .= "<form action='install.php' method='post'>"; <br />
$display .= "<br><br><input type='submit' name='submit' value='Yes-Deinstall Now!'\n>"; <br />
$display .= "&nbsp;<input type='button' value='Cancel' onclick='javascript:history.go(-1)'>\n"; <br />
$display .= "</form></td></tr></table>"; <br />
$display .= COM_endBlock(); <br />
COM_output($display) <br />
} else { <br />
COM_errorLOG("Plugin de-install, Nothing to do. Check your plugin install.cfg settings"); <br />
redirect_header($_CONF['site_url'] ."/index.php",2,$LANG_FILEMGMT['install_noop_msg']); <br />
exit(); <br />
} <br />
<br />
} <br />
} <br />
<br />
} <br />
<br />
?> <br />
</pre><br />
<br />
<br />
[[Category:Plugin Developers Handbook]] [[Category:Plugin Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Functions.inc_-_Listing_of_the_Plugin_Functions&diff=5809Functions.inc - Listing of the Plugin Functions2010-04-16T18:45:42Z<p>Rasade: /* Core Plugin Functions */ added plugin_chkVersion_</p>
<hr />
<div>The following section outlines the Geeklog [[Plugin API]] functions which you <br />
have available to integrate your project or extend your projects features. <br />
The only mandatory function is the uninstall function. If any of the functions are not implemented they will be be skipped when a plugin feature is checked from within Geeklog. For example, when building the user menu, all enabled plugins are checked to see if the plugin_getuseroption_pluginname exists. If for plugin <i>mycontacts</i> the plugin_getuseroption_mycontacts() does not exist, it is skipped and the next enabled plugin is checked.<br />
<br />
<br />
== Core Plugin Functions ==<br />
<br />
There are several core functions that are normally always implemented for <br />
plugins. Function prototypes and sample code for all these functions are available in the [[Universal Plugin ToolKit]]. If you want to enable any of them just modify the code to fit your plugin and uncomment the code.<br />
<br />
<table border="2" cellspacing="0" cellpadding="3" width="100%"><tr><br />
<th width="310">Function</th><br />
<th width="582">Description of Function</th></tr><br />
<tr><td width="310">plugin_getmenuitems_{plugin}</td><br />
<td width="582">This places items on the Site Menu</td></tr><br />
<tr><td width="310">plugin_commentsupport_{plugin}</td><br />
<td width="582">Indicates to Geeklog whether or not this plugin supports Comments</td></tr><br />
<tr><td width="310">plugin_showstats_{plugin}</td><br />
<td width="582">This expands the Site Stats page.</td></tr><br />
<tr><td width="310">plugin_cclabel_{plugin}</td><br />
<td width="582">This puts an option for the Plugin in the Command and Control Block</td></tr><br />
<tr><td width="310">plugin_getadminoption_{plugin}</td><br />
<td width="582">This places a menu option in the Admin Block</td></tr><br />
<tr><td width="310">plugin_getuseroption_{plugin}</td><br />
<td width="582">This places a menu option in the User Block</td></tr><br />
<tr><td width="310">plugin_chkVersion_{plugin}</td><br />
<td width="582">Checks whether the plugin's version is current and asks the admin to upgrade the plugin if not.</td></tr><br />
<tr><td width="310">plugin_uninstall_{plugin}</td><br />
<td width="582">This uninstalls the plugin and is called by both your install page and the plugin administration page. Also see [[Plugin Auto-Uninstall]].</td></tr></table><br />
<br />
== Optional Functions ==<br />
<br />
The following optional plugin functions are broken out into the following <br />
related groupings. The first group are the functions required to support <br />
moderation within your plugin. If you want your plugin to be moderated like Geeklog stories and links then you must implement these functions.<br />
<br />
<table border="2" width="100%" cellpadding="3" cellspacing="0"><br />
<tr><br />
<th width="288"><br />
Function</th><br />
<th width="541">Description of Function</th></tr><br />
<tr><br />
<td width="288" >plugin_ismoderator_<plugin name></td><br />
<td width="541" >Checks if the current user has rights to moderate for the plugin and returns true if this is the case, false otherwise.</td><br />
</tr><br />
<tr><br />
<td width="288" >plugin_submissioncount_<plugin name></td><br />
<td width="541" >Calculates the current number of submissions awaiting moderation and returns that number.</td><br />
</tr><br />
<tr><br />
<td width="288" >plugin_savesubmission_{plugin name}</td><br />
<td width="541" >Saves submitted item from a user in {plugin name}submission table</td><br />
</tr><br />
<tr><br />
<td width="288" >plugin_moderationvalues_{plugin name} </td><br />
<td width="541" >Returns the primary key column name, the main table name (called {plugin name}) and the list of fields from that table that you'd like to have show up on the moderation page.</td><br />
</tr><br />
<tr><br />
<td width="288" >plugin_itemlist_{plugin name}</td><br />
<td width="541" >Shows any items needing moderation for your plugin on moderation.php</td><br />
</tr><br />
<tr><br />
<td width="288" >plugin_submit_{plugin name}</td><br />
<td width="541" >Shows the submission form for your plugin. Returns a string containing the HTML to display the plugin submission form.</td><br />
</tr><br />
<tr><br />
<td width="288">plugin_moderationapprove_{plugin name}</td><br />
<td width="541">Takes an ID into {plugin name}submission and moves it to the main table called {plugin name}. This optional function supplements moderation.php. While moderation.php actually moves data from the <plugin name>submission table to the main <plugin name> table, this function executes all other submission approval tasks including any other database updates required by your plugin.</td><br />
</tr><br />
<tr><br />
<td width="288" >plugin_moderationdelete_{plugin name} </td><br />
<td width="541" >Takes an ID into {plugin name}submission table and deletes it. This optional function supplements moderation.php. While moderation.php actually removes data from the <plugin name>submission table, this function executes all other submission removal tasks including any other database updates required by your plugin.</td><br />
</tr><br />
</table><br />
<br />
<br />
If you want your plugin to retrieve search results when the Geeklog search is used or present your plugin as a possible search type then you will want to implement these functions.<br />
<br />
<table border="2" width="100%" cellspacing="0" cellpadding="3"><br />
<tr><br />
<th width="288">Function</th><br />
<th width="541">Description of Function</th><br />
</tr><br />
<tr><br />
<td width="288">plugin_getsearchtypes_{plugin <br />
name}</td><br />
<td width="541">You will probably want to add a new type in the Type drop down on search.php. This function prints the option tags needed. Make sure that the value tag is {plugin name}</td><br />
</tr><br />
<tr><br />
<td width="288">plugin_dopluginsearch_{plugin <br />
name}</td><br />
<td width="541">Takes the search criteria and lets you build search results for your plugin. This returns a string array of table rows, one row for each record returned by your search.</td><br />
</tr><br />
</table><br />
Also see [[Using Geeklog's Improved Search Engine]].<br />
<br />
<br />
If you want your plugin to support comments and use the Geeklog comment <br />
engine, then you need to implement these functions in your plugin functions.inc file<br />
<br />
<table cellpadding="3" cellspacing="0" border="2" width="100%"><br />
<tr><br />
<th width="287">Function</th><br />
<th width="602">Description of Function</th><br />
</tr><br />
<tr><br />
<td width="287">plugin_commentsupport_<plugin name></td><br />
<td width="602">This function does not take any parameters but simply returns true if this plugin supports comments. This call is made in Geeklog code (example article.php) to determine if it should redirect handling to the plugin</td><br />
</tr><br />
<tr><br />
<td width="287">plugin_handlecomment_<plugin <br />
name></td><br />
<td width="602">This function expects a parameter for the comment id and operation. The operation parameter is either 'save' or 'delete'. This function will update the plugin record with the total number of comments for this plugin item and the then redirect the user back to the plugin instead of the main site page</td><br />
</tr><br />
<tr><br />
<td width="287">plugin_commentform_<plugin name></td><br />
<td width="602">This function expects a number of parameters and is called from Geeklog article.php and comment.php. Parameters are: comment_id (primary key), comment_mode (nested, flat, threaded, none), order (Ascending or Descending) and reply (was the reply submit button used on the comment bar). Only comment_id is mandatory.</td><br />
</tr><br />
<tr><br />
<td width="287">plugin_commentparent_<plugin name></td><br />
<td width="602">Optional function which can be called from your plugin_commentform function to also display the plugin parent above the comments. This is how Geeklog articles are displayed with the story and then the comment bar and associated comments.</td><br />
</tr><br />
</table><br />
<br />
<br />
[[Category:Plugin Developers Handbook]] [[Category:Plugin Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Geeklog%27s_way_of_doing_things&diff=5808Geeklog's way of doing things2010-04-16T18:37:58Z<p>Rasade: /* Commonly used Geeklog functions */ added COM_setArgNames and COM_getArgument</p>
<hr />
<div>All of Geeklog's PHP files are pure PHP. PHP allows you to use PHP as a scripting language embedded in HTML files but this is not how Geeklog does it. All of Geeklog's PHP files must have <?php as the first five characters and end with ?>. Failure to structure your Geeklog files this way will cause session and header errors. Geeklog attempts as much as possible to have only PHP code in the <tt>.php</tt> files. Two of the conventions used in Geeklog to accomplish this are the use of [[Using Templates and Language Files#Templates|templates]] and [[Using Templates and Language Files#Language Files|language files]].<br />
<br />
Time will be well spent by the Geeklog plugin or block developer to become familiar with several Geeklog libraries. The following section outlines the more important or frequently used libraries, functions and standards. <br />
<br />
<br />
== lib-common.php ==<br />
<br />
The Geeklog plugin or block developer should have a good understanding of <tt>lib-common.php</tt> which contains most of the display routines as well as some other general purpose routines. All of the Geeklog display routines return HTML. They do not contain echos or prints themselves. The way the Geeklog system is designed; you usually put your code between Geeklog display routines. For example: code to display a page would look like this: <br />
<br />
<pre><br />
$display = COM_siteHeader();<br />
$display .= COM_startBlock("Title of Your Block");<br />
$display .= "Some words";<br />
$display .= somefunction();<br />
$display .= COM_endBlock;<br />
$display .= COM_siteFooter(true);<br />
COM_output($display);<br />
</pre><br />
<br />
A summary of commonly used Geeklog functions are described below but reference the [http://project.geeklog.net/src/Geeklog/_public_html---lib-common.php.html <tt>lib-common.php</tt> documentation] for further examples.<br />
<br />
== lib-security.php ==<br />
<br />
The <tt>lib-security.php</tt> file contains the interface routines to the security system used in Geeklog. See the [http://project.geeklog.net/src/Geeklog/_system---lib-security.php.html <tt>lib-security.php</tt> documentation]. There are several SEC type functions that you may want to use in your projects. The table [[Geeklog's way of doing things#Commonly used Geeklog functions|Commonly Used Geeklog Functions]] at the end of this section defines a couple of the more commonly used functions.<br />
<br />
<br />
== lib-database.php ==<br />
<br />
All database access should take place through the database abstraction in <tt>lib-database.php</tt> and table names should be accessed through the <code>$_TABLES</code> array. The table definitions in this array will then include whatever table prefix this site is using. You should ensure that your plugin or block uses the <code>$_TABLES['mytable']</code> for all database access related functions.<br />
<br />
* Block developers and site admins installing the block need to make sure that any tables needed by a block are added to the <code>$_TABLES</code> array. Ensure you follow the same standard by using the $_DB_table_prefix. However, you should '''not''' edit <tt>lib-database.php</tt>. If no suitable place can be found in the block's code, the addition should be done in <tt>lib-custom.php</tt> instead.<br />
* Plugin developers need to define their tables in the plugin <tt>config.php</tt> file (or some other suitable file belonging to the plugin, e.g. <tt>functions.inc</tt>). You have the ability to use the <code>$_TABLES</code> array or use your own plugin specific array to maintain the table definitions. In the latter case it's recommend that your arrays use a name such as <code>_XX_TABLES</code>, where XX are two letters to describe your plugin. Once the plugin is installed and enabled, the tables defined for the plugin are automatically known as globals.<br />
<br />
<br />
== lib-plugins.php ==<br />
<br />
This library contains all the code used to interface your plugin or block to Geeklog. You will not call any code from here directly. The functions in this file are called by the Geeklog main programs as they are used to call all plugins and are used to resolve the plugin name and check if your plugin has a particular function. You will usually only have to look at this file to understand some nuance of the implementation. See the [[Functions.inc - Listing of the Plugin Functions|documentation for <tt>functions.inc</tt>]] and the original [[Plugin Development|plugin documentation]] for further help. <br />
<br />
<br />
== How your Plugin or Block Interacts with Geeklog ==<br />
<br />
The program <tt>lib-common.php</tt> is the key to all interaction with Geeklog. It includes all of the Geeklog code libraries.<br />
<br />
* At the top of <tt>lib-common.php</tt> is an include of <tt>lib-custom.php</tt>. This is where you place all your block functions. They are then included when the site <tt>index.php</tt> is called. <br />
* At the very end of <tt>lib-common.php</tt> is a check for all enabled plugins. A plugin is registered with Geeklog when it has a record in the plugins table and the field <tt>pi_enabled</tt> is <tt>1</tt>. For all registered and enabled plugins, the code in <tt>lib-common.php</tt> will include the <tt>functions.inc</tt> file for each plugin. This is why the naming convention and location for each plugin file is strictly defined. All code libraries or configuration files your plugin needs should be included in your <tt>functions.inc</tt>. Since the plugin's <tt>config.php</tt> (if it exists) is now also included this way - via the include in <tt>functions.inc</tt> - the variables in your plugin <tt>config.php</tt> become global and can be referenced in your plugin functions.<br />
<br />
<br />
== Common Global Variables ==<br />
<br />
There are a few commonly used globals within Geeklog that you will want to use and reference. The following table outlines the more frequently used ones and its recommended that these be used instead of hard coding paths and table names in your links or project code.<br />
<br />
<table border="1" cellpadding="3" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="75%" id="AutoNumber2" height="72"><br />
<tr><br />
<td width="27%" bgcolor="#000000" height="16"><br />
<font color="#FFFFFF" size="2">&nbsp;Variable</font></td><br />
<td width="73%" bgcolor="#000000" height="16"><br />
<font color="#FFFFFF" size="2">&nbsp; Description</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="17"><font size="2">$_CONF['path_html']</font></td><br />
<td width="73%" height="17"><font size="2">Fully qualified path to your site's public_html path</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_CONF['site_url']</font></td><br />
<td width="73%" height="18"><font size="2">Full URL to your site's public_html directory</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_CONF['site_admin_url']</font></td><br />
<td width="73%" height="18"><font size="2">Full URL to your site's admin directory</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_USER['uid']</font></td><br />
<td width="73%" height="18"><font size="2">Current user ID. A uid of 1 is an anonymous user.<br/>Note: This variable may not be set, which also should be handled as an anonymous user. Use the function <code>COM_isAnonUser()</code> to find out whether the user is anonymous.</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_TABLES['tablename']</font></td><br />
<td width="73%" height="18"><font size="2">An array of Geeklog tables with the site prefix defined. This can be used to access specific tables for queries.</font></td><br />
</tr><br />
</table><br />
<br />
== Commonly used Geeklog functions ==<br />
<br />
The following is a list of commonly used Geeklog functions that as developers we use and recommend you become more familiar with and use in your development projects. You will find example usage of these functions throughout the Geeklog code. <br />
<br />
<table border="1" style="border-collapse: collapse" bordercolor="#111111" width="100%" id="AutoNumber1" height="197" cellpadding="3" cellspacing="0"><br />
<tr><br />
<td width="13%" bgcolor="#000000" height="18"><br />
<font color="#FFFFFF" size="2">&nbsp;Function Name</font></td><br />
<td width="73%" bgcolor="#000000" height="18"><br />
<font color="#FFFFFF" size="2">&nbsp;&nbsp; Description</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_siteHeader</font></td><br />
<td width="73%" height="16"><font size="2">Display the main site header and optionally if 'none' is passed <b>do not </b>display the left blocks</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_siteFooter</font></td><br />
<td width="73%" height="16"><font size="2">Display the main site footer and optionally if 'true' is passed display the right blocks</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="32"><br />
<font size="2">&nbsp;COM_startBlock</font></td><br />
<td width="73%" height="32"><font size="2">Formats the block title using the selected theme - pass the title, helpfile if any and optional block theme to apply if you do not want to use default</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_endBlock</font></td><br />
<td width="73%" height="16"><font size="2">Formats the block footer using the selected theme or the optional block theme if you do not want to use default</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_output</font></td><br />
<td width="73%" height="16"><font size="2">Takes the <code>$display</code> HTML string and echoes it out to the page. This function has been defined to allow compatibility with compressed content. </font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="12"><font size="2">&nbsp;COM_errorLog</font></td><br />
<td width="73%" height="12"><font size="2">Use to format an error message or use for debugging - view output in &lt;geeklog_dir&gt;/logs/error.log</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="14"><br />
<font size="2">&nbsp;DB_query</font></td><br />
<td width="73%" height="14"><font size="2">Execute a formatted SQL query and return the record set to an array of records (which is also an array)</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;DB_fetchArray</font></td><br />
<td width="73%" height="16"><font size="2">Retrieve a record as an array from the returned record set - which DB_query returned.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;DB_numRows</font></td><br />
<td width="73%" height="16"><font size="2">Returns the number of records retrieved by the DB_query result</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><font size="2">&nbsp;DB_getItem</font></td><br />
<td width="73%" height="16"><font size="2">Retrieve one record as an array.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_checkHTML</font></td><br />
<td width="73%" height="15"><font size="2">Use to strip out $, &lt;, &gt; , [code] and replace with the HTML codes</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_checkWords</font></td><br />
<td width="73%" height="15"><font size="2">Use to check passed text for any HTML tags that are not allowed as per the site config.php setting</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_getArgument</font></td><br />
<td width="73%" height="15"><font size="2">Use to get a specified variable from the query string</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_setArgNames</font></td><br />
<td width="73%" height="15"><font size="2">Set the names of the global variables that your code expects to see in the query string.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;SEC_inGroup</font></td><br />
<td width="73%" height="15"><font size="2">Used to check if user has passed group rights.<br/>Example: SEC_inGroup('Root') - returns true if user is a member of the "Root" group</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;SEC_hasRights</font></td><br />
<td width="73%" height="15"><font size="2">Used to check if user has access right (feature).<br/>Example: SEC_hasRights('myplugin.edit')</font></td><br />
</tr><br />
</table><br />
<br />
<br />
[[Category:Plugin Developers Handbook]] [[Category:Plugin Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Geeklog%27s_way_of_doing_things&diff=5807Geeklog's way of doing things2010-04-16T18:28:47Z<p>Rasade: /* Commonly used Geeklog functions */ Added COM_output() commonly used functions table</p>
<hr />
<div>All of Geeklog's PHP files are pure PHP. PHP allows you to use PHP as a scripting language embedded in HTML files but this is not how Geeklog does it. All of Geeklog's PHP files must have <?php as the first five characters and end with ?>. Failure to structure your Geeklog files this way will cause session and header errors. Geeklog attempts as much as possible to have only PHP code in the <tt>.php</tt> files. Two of the conventions used in Geeklog to accomplish this are the use of [[Using Templates and Language Files#Templates|templates]] and [[Using Templates and Language Files#Language Files|language files]].<br />
<br />
Time will be well spent by the Geeklog plugin or block developer to become familiar with several Geeklog libraries. The following section outlines the more important or frequently used libraries, functions and standards. <br />
<br />
<br />
== lib-common.php ==<br />
<br />
The Geeklog plugin or block developer should have a good understanding of <tt>lib-common.php</tt> which contains most of the display routines as well as some other general purpose routines. All of the Geeklog display routines return HTML. They do not contain echos or prints themselves. The way the Geeklog system is designed; you usually put your code between Geeklog display routines. For example: code to display a page would look like this: <br />
<br />
<pre><br />
$display = COM_siteHeader();<br />
$display .= COM_startBlock("Title of Your Block");<br />
$display .= "Some words";<br />
$display .= somefunction();<br />
$display .= COM_endBlock;<br />
$display .= COM_siteFooter(true);<br />
COM_output($display);<br />
</pre><br />
<br />
A summary of commonly used Geeklog functions are described below but reference the [http://project.geeklog.net/src/Geeklog/_public_html---lib-common.php.html <tt>lib-common.php</tt> documentation] for further examples.<br />
<br />
== lib-security.php ==<br />
<br />
The <tt>lib-security.php</tt> file contains the interface routines to the security system used in Geeklog. See the [http://project.geeklog.net/src/Geeklog/_system---lib-security.php.html <tt>lib-security.php</tt> documentation]. There are several SEC type functions that you may want to use in your projects. The table [[Geeklog's way of doing things#Commonly used Geeklog functions|Commonly Used Geeklog Functions]] at the end of this section defines a couple of the more commonly used functions.<br />
<br />
<br />
== lib-database.php ==<br />
<br />
All database access should take place through the database abstraction in <tt>lib-database.php</tt> and table names should be accessed through the <code>$_TABLES</code> array. The table definitions in this array will then include whatever table prefix this site is using. You should ensure that your plugin or block uses the <code>$_TABLES['mytable']</code> for all database access related functions.<br />
<br />
* Block developers and site admins installing the block need to make sure that any tables needed by a block are added to the <code>$_TABLES</code> array. Ensure you follow the same standard by using the $_DB_table_prefix. However, you should '''not''' edit <tt>lib-database.php</tt>. If no suitable place can be found in the block's code, the addition should be done in <tt>lib-custom.php</tt> instead.<br />
* Plugin developers need to define their tables in the plugin <tt>config.php</tt> file (or some other suitable file belonging to the plugin, e.g. <tt>functions.inc</tt>). You have the ability to use the <code>$_TABLES</code> array or use your own plugin specific array to maintain the table definitions. In the latter case it's recommend that your arrays use a name such as <code>_XX_TABLES</code>, where XX are two letters to describe your plugin. Once the plugin is installed and enabled, the tables defined for the plugin are automatically known as globals.<br />
<br />
<br />
== lib-plugins.php ==<br />
<br />
This library contains all the code used to interface your plugin or block to Geeklog. You will not call any code from here directly. The functions in this file are called by the Geeklog main programs as they are used to call all plugins and are used to resolve the plugin name and check if your plugin has a particular function. You will usually only have to look at this file to understand some nuance of the implementation. See the [[Functions.inc - Listing of the Plugin Functions|documentation for <tt>functions.inc</tt>]] and the original [[Plugin Development|plugin documentation]] for further help. <br />
<br />
<br />
== How your Plugin or Block Interacts with Geeklog ==<br />
<br />
The program <tt>lib-common.php</tt> is the key to all interaction with Geeklog. It includes all of the Geeklog code libraries.<br />
<br />
* At the top of <tt>lib-common.php</tt> is an include of <tt>lib-custom.php</tt>. This is where you place all your block functions. They are then included when the site <tt>index.php</tt> is called. <br />
* At the very end of <tt>lib-common.php</tt> is a check for all enabled plugins. A plugin is registered with Geeklog when it has a record in the plugins table and the field <tt>pi_enabled</tt> is <tt>1</tt>. For all registered and enabled plugins, the code in <tt>lib-common.php</tt> will include the <tt>functions.inc</tt> file for each plugin. This is why the naming convention and location for each plugin file is strictly defined. All code libraries or configuration files your plugin needs should be included in your <tt>functions.inc</tt>. Since the plugin's <tt>config.php</tt> (if it exists) is now also included this way - via the include in <tt>functions.inc</tt> - the variables in your plugin <tt>config.php</tt> become global and can be referenced in your plugin functions.<br />
<br />
<br />
== Common Global Variables ==<br />
<br />
There are a few commonly used globals within Geeklog that you will want to use and reference. The following table outlines the more frequently used ones and its recommended that these be used instead of hard coding paths and table names in your links or project code.<br />
<br />
<table border="1" cellpadding="3" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="75%" id="AutoNumber2" height="72"><br />
<tr><br />
<td width="27%" bgcolor="#000000" height="16"><br />
<font color="#FFFFFF" size="2">&nbsp;Variable</font></td><br />
<td width="73%" bgcolor="#000000" height="16"><br />
<font color="#FFFFFF" size="2">&nbsp; Description</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="17"><font size="2">$_CONF['path_html']</font></td><br />
<td width="73%" height="17"><font size="2">Fully qualified path to your site's public_html path</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_CONF['site_url']</font></td><br />
<td width="73%" height="18"><font size="2">Full URL to your site's public_html directory</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_CONF['site_admin_url']</font></td><br />
<td width="73%" height="18"><font size="2">Full URL to your site's admin directory</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_USER['uid']</font></td><br />
<td width="73%" height="18"><font size="2">Current user ID. A uid of 1 is an anonymous user.<br/>Note: This variable may not be set, which also should be handled as an anonymous user. Use the function <code>COM_isAnonUser()</code> to find out whether the user is anonymous.</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_TABLES['tablename']</font></td><br />
<td width="73%" height="18"><font size="2">An array of Geeklog tables with the site prefix defined. This can be used to access specific tables for queries.</font></td><br />
</tr><br />
</table><br />
<br />
== Commonly used Geeklog functions ==<br />
<br />
The following is a list of commonly used Geeklog functions that as developers we use and recommend you become more familiar with and use in your development projects. You will find example usage of these functions throughout the Geeklog code. <br />
<br />
<table border="1" style="border-collapse: collapse" bordercolor="#111111" width="100%" id="AutoNumber1" height="197" cellpadding="3" cellspacing="0"><br />
<tr><br />
<td width="13%" bgcolor="#000000" height="18"><br />
<font color="#FFFFFF" size="2">&nbsp;Function Name</font></td><br />
<td width="73%" bgcolor="#000000" height="18"><br />
<font color="#FFFFFF" size="2">&nbsp;&nbsp; Description</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_siteHeader</font></td><br />
<td width="73%" height="16"><font size="2">Display the main site header and optionally if 'none' is passed <b>do not </b>display the left blocks</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_siteFooter</font></td><br />
<td width="73%" height="16"><font size="2">Display the main site footer and optionally if 'true' is passed display the right blocks</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="32"><br />
<font size="2">&nbsp;COM_startBlock</font></td><br />
<td width="73%" height="32"><font size="2">Formats the block title using the selected theme - pass the title, helpfile if any and optional block theme to apply if you do not want to use default</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_endBlock</font></td><br />
<td width="73%" height="16"><font size="2">Formats the block footer using the selected theme or the optional block theme if you do not want to use default</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_output</font></td><br />
<td width="73%" height="16"><font size="2">Takes the <code>$display</code> HTML string and echoes it out to the page. This function has been defined to allow compatibility with compressed content. </font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="12"><font size="2">&nbsp;COM_errorLog</font></td><br />
<td width="73%" height="12"><font size="2">Use to format an error message or use for debugging - view output in &lt;geeklog_dir&gt;/logs/error.log</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="14"><br />
<font size="2">&nbsp;DB_query</font></td><br />
<td width="73%" height="14"><font size="2">Execute a formatted SQL query and return the record set to an array of records (which is also an array)</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;DB_fetchArray</font></td><br />
<td width="73%" height="16"><font size="2">Retrieve a record as an array from the returned record set - which DB_query returned.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;DB_numRows</font></td><br />
<td width="73%" height="16"><font size="2">Returns the number of records retrieved by the DB_query result</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><font size="2">&nbsp;DB_getItem</font></td><br />
<td width="73%" height="16"><font size="2">Retrieve one record as an array.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_checkHTML</font></td><br />
<td width="73%" height="15"><font size="2">Use to strip out $, &lt;, &gt; , [code] and replace with the HTML codes</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_checkWords</font></td><br />
<td width="73%" height="15"><font size="2">Use to check passed text for any HTML tags that are not allowed as per the site config.php setting</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;SEC_inGroup</font></td><br />
<td width="73%" height="15"><font size="2">Used to check if user has passed group rights.<br/>Example: SEC_inGroup('Root') - returns true if user is a member of the "Root" group</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;SEC_hasRights</font></td><br />
<td width="73%" height="15"><font size="2">Used to check if user has access right (feature).<br/>Example: SEC_hasRights('myplugin.edit')</font></td><br />
</tr><br />
</table><br />
<br />
<br />
[[Category:Plugin Developers Handbook]] [[Category:Plugin Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Geeklog%27s_way_of_doing_things&diff=5806Geeklog's way of doing things2010-04-16T18:25:00Z<p>Rasade: /* Common Global Variables */ - added some extra information</p>
<hr />
<div>All of Geeklog's PHP files are pure PHP. PHP allows you to use PHP as a scripting language embedded in HTML files but this is not how Geeklog does it. All of Geeklog's PHP files must have <?php as the first five characters and end with ?>. Failure to structure your Geeklog files this way will cause session and header errors. Geeklog attempts as much as possible to have only PHP code in the <tt>.php</tt> files. Two of the conventions used in Geeklog to accomplish this are the use of [[Using Templates and Language Files#Templates|templates]] and [[Using Templates and Language Files#Language Files|language files]].<br />
<br />
Time will be well spent by the Geeklog plugin or block developer to become familiar with several Geeklog libraries. The following section outlines the more important or frequently used libraries, functions and standards. <br />
<br />
<br />
== lib-common.php ==<br />
<br />
The Geeklog plugin or block developer should have a good understanding of <tt>lib-common.php</tt> which contains most of the display routines as well as some other general purpose routines. All of the Geeklog display routines return HTML. They do not contain echos or prints themselves. The way the Geeklog system is designed; you usually put your code between Geeklog display routines. For example: code to display a page would look like this: <br />
<br />
<pre><br />
$display = COM_siteHeader();<br />
$display .= COM_startBlock("Title of Your Block");<br />
$display .= "Some words";<br />
$display .= somefunction();<br />
$display .= COM_endBlock;<br />
$display .= COM_siteFooter(true);<br />
COM_output($display);<br />
</pre><br />
<br />
A summary of commonly used Geeklog functions are described below but reference the [http://project.geeklog.net/src/Geeklog/_public_html---lib-common.php.html <tt>lib-common.php</tt> documentation] for further examples.<br />
<br />
== lib-security.php ==<br />
<br />
The <tt>lib-security.php</tt> file contains the interface routines to the security system used in Geeklog. See the [http://project.geeklog.net/src/Geeklog/_system---lib-security.php.html <tt>lib-security.php</tt> documentation]. There are several SEC type functions that you may want to use in your projects. The table [[Geeklog's way of doing things#Commonly used Geeklog functions|Commonly Used Geeklog Functions]] at the end of this section defines a couple of the more commonly used functions.<br />
<br />
<br />
== lib-database.php ==<br />
<br />
All database access should take place through the database abstraction in <tt>lib-database.php</tt> and table names should be accessed through the <code>$_TABLES</code> array. The table definitions in this array will then include whatever table prefix this site is using. You should ensure that your plugin or block uses the <code>$_TABLES['mytable']</code> for all database access related functions.<br />
<br />
* Block developers and site admins installing the block need to make sure that any tables needed by a block are added to the <code>$_TABLES</code> array. Ensure you follow the same standard by using the $_DB_table_prefix. However, you should '''not''' edit <tt>lib-database.php</tt>. If no suitable place can be found in the block's code, the addition should be done in <tt>lib-custom.php</tt> instead.<br />
* Plugin developers need to define their tables in the plugin <tt>config.php</tt> file (or some other suitable file belonging to the plugin, e.g. <tt>functions.inc</tt>). You have the ability to use the <code>$_TABLES</code> array or use your own plugin specific array to maintain the table definitions. In the latter case it's recommend that your arrays use a name such as <code>_XX_TABLES</code>, where XX are two letters to describe your plugin. Once the plugin is installed and enabled, the tables defined for the plugin are automatically known as globals.<br />
<br />
<br />
== lib-plugins.php ==<br />
<br />
This library contains all the code used to interface your plugin or block to Geeklog. You will not call any code from here directly. The functions in this file are called by the Geeklog main programs as they are used to call all plugins and are used to resolve the plugin name and check if your plugin has a particular function. You will usually only have to look at this file to understand some nuance of the implementation. See the [[Functions.inc - Listing of the Plugin Functions|documentation for <tt>functions.inc</tt>]] and the original [[Plugin Development|plugin documentation]] for further help. <br />
<br />
<br />
== How your Plugin or Block Interacts with Geeklog ==<br />
<br />
The program <tt>lib-common.php</tt> is the key to all interaction with Geeklog. It includes all of the Geeklog code libraries.<br />
<br />
* At the top of <tt>lib-common.php</tt> is an include of <tt>lib-custom.php</tt>. This is where you place all your block functions. They are then included when the site <tt>index.php</tt> is called. <br />
* At the very end of <tt>lib-common.php</tt> is a check for all enabled plugins. A plugin is registered with Geeklog when it has a record in the plugins table and the field <tt>pi_enabled</tt> is <tt>1</tt>. For all registered and enabled plugins, the code in <tt>lib-common.php</tt> will include the <tt>functions.inc</tt> file for each plugin. This is why the naming convention and location for each plugin file is strictly defined. All code libraries or configuration files your plugin needs should be included in your <tt>functions.inc</tt>. Since the plugin's <tt>config.php</tt> (if it exists) is now also included this way - via the include in <tt>functions.inc</tt> - the variables in your plugin <tt>config.php</tt> become global and can be referenced in your plugin functions.<br />
<br />
<br />
== Common Global Variables ==<br />
<br />
There are a few commonly used globals within Geeklog that you will want to use and reference. The following table outlines the more frequently used ones and its recommended that these be used instead of hard coding paths and table names in your links or project code.<br />
<br />
<table border="1" cellpadding="3" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="75%" id="AutoNumber2" height="72"><br />
<tr><br />
<td width="27%" bgcolor="#000000" height="16"><br />
<font color="#FFFFFF" size="2">&nbsp;Variable</font></td><br />
<td width="73%" bgcolor="#000000" height="16"><br />
<font color="#FFFFFF" size="2">&nbsp; Description</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="17"><font size="2">$_CONF['path_html']</font></td><br />
<td width="73%" height="17"><font size="2">Fully qualified path to your site's public_html path</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_CONF['site_url']</font></td><br />
<td width="73%" height="18"><font size="2">Full URL to your site's public_html directory</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_CONF['site_admin_url']</font></td><br />
<td width="73%" height="18"><font size="2">Full URL to your site's admin directory</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_USER['uid']</font></td><br />
<td width="73%" height="18"><font size="2">Current user ID. A uid of 1 is an anonymous user.<br/>Note: This variable may not be set, which also should be handled as an anonymous user. Use the function <code>COM_isAnonUser()</code> to find out whether the user is anonymous.</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_TABLES['tablename']</font></td><br />
<td width="73%" height="18"><font size="2">An array of Geeklog tables with the site prefix defined. This can be used to access specific tables for queries.</font></td><br />
</tr><br />
</table><br />
<br />
== Commonly used Geeklog functions ==<br />
<br />
The following is a list of commonly used Geeklog functions that as developers we use and recommend you become more familiar with and use in your development projects. You will find example usage of these functions throughout the Geeklog code. <br />
<br />
<table border="1" style="border-collapse: collapse" bordercolor="#111111" width="100%" id="AutoNumber1" height="197" cellpadding="3" cellspacing="0"><br />
<tr><br />
<td width="13%" bgcolor="#000000" height="18"><br />
<font color="#FFFFFF" size="2">&nbsp;Function Name</font></td><br />
<td width="73%" bgcolor="#000000" height="18"><br />
<font color="#FFFFFF" size="2">&nbsp;&nbsp; Description</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_siteHeader</font></td><br />
<td width="73%" height="16"><font size="2">Display the main site header and optionally if 'none' is passed <b>do not </b>display the left blocks</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_siteFooter</font></td><br />
<td width="73%" height="16"><font size="2">Display the main site footer and optionally if 'true' is passed display the right blocks</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="32"><br />
<font size="2">&nbsp;COM_startBlock</font></td><br />
<td width="73%" height="32"><font size="2">Formats the block title using the selected theme - pass the title, helpfile if any and optional block theme to apply if you do not want to use default</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_endBlock</font></td><br />
<td width="73%" height="16"><font size="2">Formats the block footer using the selected theme or the optional block theme if you do not want to use default</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="12"><font size="2">&nbsp;COM_errorLog</font></td><br />
<td width="73%" height="12"><font size="2">Use to format an error message or use for debugging - view output in &lt;geeklog_dir&gt;/logs/error.log</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="14"><br />
<font size="2">&nbsp;DB_query</font></td><br />
<td width="73%" height="14"><font size="2">Execute a formatted SQL query and return the record set to an array of records (which is also an array)</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;DB_fetchArray</font></td><br />
<td width="73%" height="16"><font size="2">Retrieve a record as an array from the returned record set - which DB_query returned.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;DB_numRows</font></td><br />
<td width="73%" height="16"><font size="2">Returns the number of records retrieved by the DB_query result</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><font size="2">&nbsp;DB_getItem</font></td><br />
<td width="73%" height="16"><font size="2">Retrieve one record as an array.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_checkHTML</font></td><br />
<td width="73%" height="15"><font size="2">Use to strip out $, &lt;, &gt; , [code] and replace with the HTML codes</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_checkWords</font></td><br />
<td width="73%" height="15"><font size="2">Use to check passed text for any HTML tags that are not allowed as per the site config.php setting</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;SEC_inGroup</font></td><br />
<td width="73%" height="15"><font size="2">Used to check if user has passed group rights.<br/>Example: SEC_inGroup('Root') - returns true if user is a member of the "Root" group</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;SEC_hasRights</font></td><br />
<td width="73%" height="15"><font size="2">Used to check if user has access right (feature).<br/>Example: SEC_hasRights('myplugin.edit')</font></td><br />
</tr><br />
</table><br />
<br />
<br />
[[Category:Plugin Developers Handbook]] [[Category:Plugin Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Geeklog%27s_way_of_doing_things&diff=5805Geeklog's way of doing things2010-04-16T18:20:58Z<p>Rasade: /* lib-common.php */ added COM_output()</p>
<hr />
<div>All of Geeklog's PHP files are pure PHP. PHP allows you to use PHP as a scripting language embedded in HTML files but this is not how Geeklog does it. All of Geeklog's PHP files must have <?php as the first five characters and end with ?>. Failure to structure your Geeklog files this way will cause session and header errors. Geeklog attempts as much as possible to have only PHP code in the <tt>.php</tt> files. Two of the conventions used in Geeklog to accomplish this are the use of [[Using Templates and Language Files#Templates|templates]] and [[Using Templates and Language Files#Language Files|language files]].<br />
<br />
Time will be well spent by the Geeklog plugin or block developer to become familiar with several Geeklog libraries. The following section outlines the more important or frequently used libraries, functions and standards. <br />
<br />
<br />
== lib-common.php ==<br />
<br />
The Geeklog plugin or block developer should have a good understanding of <tt>lib-common.php</tt> which contains most of the display routines as well as some other general purpose routines. All of the Geeklog display routines return HTML. They do not contain echos or prints themselves. The way the Geeklog system is designed; you usually put your code between Geeklog display routines. For example: code to display a page would look like this: <br />
<br />
<pre><br />
$display = COM_siteHeader();<br />
$display .= COM_startBlock("Title of Your Block");<br />
$display .= "Some words";<br />
$display .= somefunction();<br />
$display .= COM_endBlock;<br />
$display .= COM_siteFooter(true);<br />
COM_output($display);<br />
</pre><br />
<br />
A summary of commonly used Geeklog functions are described below but reference the [http://project.geeklog.net/src/Geeklog/_public_html---lib-common.php.html <tt>lib-common.php</tt> documentation] for further examples.<br />
<br />
== lib-security.php ==<br />
<br />
The <tt>lib-security.php</tt> file contains the interface routines to the security system used in Geeklog. See the [http://project.geeklog.net/src/Geeklog/_system---lib-security.php.html <tt>lib-security.php</tt> documentation]. There are several SEC type functions that you may want to use in your projects. The table [[Geeklog's way of doing things#Commonly used Geeklog functions|Commonly Used Geeklog Functions]] at the end of this section defines a couple of the more commonly used functions.<br />
<br />
<br />
== lib-database.php ==<br />
<br />
All database access should take place through the database abstraction in <tt>lib-database.php</tt> and table names should be accessed through the <code>$_TABLES</code> array. The table definitions in this array will then include whatever table prefix this site is using. You should ensure that your plugin or block uses the <code>$_TABLES['mytable']</code> for all database access related functions.<br />
<br />
* Block developers and site admins installing the block need to make sure that any tables needed by a block are added to the <code>$_TABLES</code> array. Ensure you follow the same standard by using the $_DB_table_prefix. However, you should '''not''' edit <tt>lib-database.php</tt>. If no suitable place can be found in the block's code, the addition should be done in <tt>lib-custom.php</tt> instead.<br />
* Plugin developers need to define their tables in the plugin <tt>config.php</tt> file (or some other suitable file belonging to the plugin, e.g. <tt>functions.inc</tt>). You have the ability to use the <code>$_TABLES</code> array or use your own plugin specific array to maintain the table definitions. In the latter case it's recommend that your arrays use a name such as <code>_XX_TABLES</code>, where XX are two letters to describe your plugin. Once the plugin is installed and enabled, the tables defined for the plugin are automatically known as globals.<br />
<br />
<br />
== lib-plugins.php ==<br />
<br />
This library contains all the code used to interface your plugin or block to Geeklog. You will not call any code from here directly. The functions in this file are called by the Geeklog main programs as they are used to call all plugins and are used to resolve the plugin name and check if your plugin has a particular function. You will usually only have to look at this file to understand some nuance of the implementation. See the [[Functions.inc - Listing of the Plugin Functions|documentation for <tt>functions.inc</tt>]] and the original [[Plugin Development|plugin documentation]] for further help. <br />
<br />
<br />
== How your Plugin or Block Interacts with Geeklog ==<br />
<br />
The program <tt>lib-common.php</tt> is the key to all interaction with Geeklog. It includes all of the Geeklog code libraries.<br />
<br />
* At the top of <tt>lib-common.php</tt> is an include of <tt>lib-custom.php</tt>. This is where you place all your block functions. They are then included when the site <tt>index.php</tt> is called. <br />
* At the very end of <tt>lib-common.php</tt> is a check for all enabled plugins. A plugin is registered with Geeklog when it has a record in the plugins table and the field <tt>pi_enabled</tt> is <tt>1</tt>. For all registered and enabled plugins, the code in <tt>lib-common.php</tt> will include the <tt>functions.inc</tt> file for each plugin. This is why the naming convention and location for each plugin file is strictly defined. All code libraries or configuration files your plugin needs should be included in your <tt>functions.inc</tt>. Since the plugin's <tt>config.php</tt> (if it exists) is now also included this way - via the include in <tt>functions.inc</tt> - the variables in your plugin <tt>config.php</tt> become global and can be referenced in your plugin functions.<br />
<br />
<br />
== Common Global Variables ==<br />
<br />
There are a few commonly used globals within Geeklog that you will want to use and reference. The following table outlines the more frequently used ones and its recommended that these be used instead of hard coding paths and table names in your links or project code.<br />
<br />
<table border="1" cellpadding="3" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="75%" id="AutoNumber2" height="72"><br />
<tr><br />
<td width="27%" bgcolor="#000000" height="16"><br />
<font color="#FFFFFF" size="2">&nbsp;Variable</font></td><br />
<td width="73%" bgcolor="#000000" height="16"><br />
<font color="#FFFFFF" size="2">&nbsp; Description</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="17"><font size="2">$_CONF['path_html']</font></td><br />
<td width="73%" height="17"><font size="2">Fully qualified path to your site's public_html path</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_CONF['site_url']</font></td><br />
<td width="73%" height="18"><font size="2">Full URL to your site's public_html directory</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_CONF['site_admin_url']</font></td><br />
<td width="73%" height="18"><font size="2">Full URL to your site's admin directory</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_USER['uid']</font></td><br />
<td width="73%" height="18"><font size="2">Current user ID. A uid of 1 is an anonymous user.<br/>Note: This variable may not be set, which also should be handled as an anonymous user. Use the function <code>COM_isAnonUser()</code>.</font></td><br />
</tr><br />
<tr><br />
<td width="27%" height="18"><font size="2">$_TABLES['tablename']</font></td><br />
<td width="73%" height="18"><font size="2">An array of Geeklog tables with the site prefix defined.</font></td><br />
</tr><br />
</table><br />
<br />
<br />
== Commonly used Geeklog functions ==<br />
<br />
The following is a list of commonly used Geeklog functions that as developers we use and recommend you become more familiar with and use in your development projects. You will find example usage of these functions throughout the Geeklog code. <br />
<br />
<table border="1" style="border-collapse: collapse" bordercolor="#111111" width="100%" id="AutoNumber1" height="197" cellpadding="3" cellspacing="0"><br />
<tr><br />
<td width="13%" bgcolor="#000000" height="18"><br />
<font color="#FFFFFF" size="2">&nbsp;Function Name</font></td><br />
<td width="73%" bgcolor="#000000" height="18"><br />
<font color="#FFFFFF" size="2">&nbsp;&nbsp; Description</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_siteHeader</font></td><br />
<td width="73%" height="16"><font size="2">Display the main site header and optionally if 'none' is passed <b>do not </b>display the left blocks</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_siteFooter</font></td><br />
<td width="73%" height="16"><font size="2">Display the main site footer and optionally if 'true' is passed display the right blocks</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="32"><br />
<font size="2">&nbsp;COM_startBlock</font></td><br />
<td width="73%" height="32"><font size="2">Formats the block title using the selected theme - pass the title, helpfile if any and optional block theme to apply if you do not want to use default</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;COM_endBlock</font></td><br />
<td width="73%" height="16"><font size="2">Formats the block footer using the selected theme or the optional block theme if you do not want to use default</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="12"><font size="2">&nbsp;COM_errorLog</font></td><br />
<td width="73%" height="12"><font size="2">Use to format an error message or use for debugging - view output in &lt;geeklog_dir&gt;/logs/error.log</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="14"><br />
<font size="2">&nbsp;DB_query</font></td><br />
<td width="73%" height="14"><font size="2">Execute a formatted SQL query and return the record set to an array of records (which is also an array)</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;DB_fetchArray</font></td><br />
<td width="73%" height="16"><font size="2">Retrieve a record as an array from the returned record set - which DB_query returned.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><br />
<font size="2">&nbsp;DB_numRows</font></td><br />
<td width="73%" height="16"><font size="2">Returns the number of records retrieved by the DB_query result</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="16"><font size="2">&nbsp;DB_getItem</font></td><br />
<td width="73%" height="16"><font size="2">Retrieve one record as an array.</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_checkHTML</font></td><br />
<td width="73%" height="15"><font size="2">Use to strip out $, &lt;, &gt; , [code] and replace with the HTML codes</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;COM_checkWords</font></td><br />
<td width="73%" height="15"><font size="2">Use to check passed text for any HTML tags that are not allowed as per the site config.php setting</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;SEC_inGroup</font></td><br />
<td width="73%" height="15"><font size="2">Used to check if user has passed group rights.<br/>Example: SEC_inGroup('Root') - returns true if user is a member of the "Root" group</font></td><br />
</tr><br />
<tr><br />
<td width="13%" height="15"><font size="2">&nbsp;SEC_hasRights</font></td><br />
<td width="73%" height="15"><font size="2">Used to check if user has access right (feature).<br/>Example: SEC_hasRights('myplugin.edit')</font></td><br />
</tr><br />
</table><br />
<br />
<br />
[[Category:Plugin Developers Handbook]] [[Category:Plugin Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Beginner%27s_Guide_to_Programming&diff=5804Beginner's Guide to Programming2010-04-14T18:27:02Z<p>Rasade: /* Support and Such */</p>
<hr />
<div>By [[User:Amckay|Alan McKay]]<br />
<br />
[http://www.geeklog.net Geeklog] is a powerful weblog (blog) content management system (CMS) which is written in the popular programming language [http://www.php.net PHP], and uses the popular [http://www.mysql.com MySQL] database. While Geeklog is powerful enough that many users will not have a need to write their own applications for it, it is flexible enough to allow those who do require extra functionality to do so easily. These people write their programs in PHP, with some minor restrictions and using the Geeklog function library.<br />
<br />
<br />
==Hello, World==<br />
<br />
The first program you write in any computer language is "Hello World", and here it is in Geeklog. This is saved in a file "hello.php" in Geeklog's "public_html" directory, and so is surfable at http://www.example.com/hello.php<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
$display = COM_siteHeader();<br />
$display .= "Hello World";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
There are a few important things to be noted from the given program.<br />
<br />
* geeklog programs stay in PHP mode - there is no flipping back and forth between PHP and HTML as is possible in the PHP language. This means that all programs have "<?php" on the first line, and "?>" on the last line, and everything in between is PHP code.<br />
* the HTML is generated using a single function, COM_output() as on the last line of the above code. Although it is possible to use a simple 'echo' statement to spit out the HTML, newer versions of Geeklog use the COM_output() function. COM_output() takes the $display variable to which all your HTML output is appended throughout the code (note the ".=" which is used for the append) as an argument and simply echoes it out in this case. (Newer versions of Geeklog use the COM_output() function to allow for any output that may use compression. More on that later)<br />
* "lib-common.php" is the single file which must be included in all your Geeklog programs. It includes everything else you need unless you decide to make your own include files, so it's one-stop shopping.<br />
* there are functions in Geeklog to do lots of stuff for you - like for example COM_siteHeader() and COM_siteFooter(). In general Geeklog functions start with 2 or 3 capital letters and an understore - this tells you what type of function it is. Then the name of the function. COM_ functions are "common" functions - not a terribly meaningful name. But SEC_ functions like SEC_inGroup() which allows you to test if a user is in a particular group, allow your programs to access some of the powerful security features of Geeklog. And DB_ commands allow you to access the Geeklog database.<br />
<br />
==Security==<br />
Speaking of the powerful Geeklog security model (one of the key reasons I originally chose Geeklog for my sites), let's alter the hello world program such that any user in the "geeker" user group will see the "hello world" message, but anyone not in that group (which includes users not logged in) will get a "permission denied" error.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( SEC_inGroup( 'geeker' ) )<br />
$display .= "Hello World";<br />
else<br />
$display .= "Access Denied";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
To check out the full range of security functions available to you, and how to use them, read the /path/to/geeklog/system/lib-security.php file, which is where they are implemented. <br />
<br />
Though the above code format is a bit clunky and not terribly useful, so let's make another change which shows us how most programs deal with group permissions issues.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( ! SEC_inGroup( 'geeker' ) )<br />
{<br />
$display .= "Access Denied";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
exit;<br />
}<br />
<br />
$display .= "Hello World";<br />
<br />
// do some other stuff here<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
The big difference in this version of the program is that right at the top of the program we test for group permissions, and if the user does not have them we display the site footer, then exit. So a user not in the 'geeker' group will end right there and never see what the rest of the program does. Very simple, but very powerful! This is precisely how you control access to your pages in Geeklog!<br />
<br />
==Where to put it==<br />
<br />
If you are only writing a small program, then sticking a single file in the public_html directory as shown above will work fine. As soon as you get to the point, however, when you start having your own include files and so on, you probably want to create a directory for it. In our case we could create a directory in "public_html" called "hello", and then create a file "index.php" with the above program. This will require a very minor change to the original program - see if you can pick it out before peeking :<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( '../lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= "Hello World";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
Yup, that's right, we had to add "../" to the "lib-common.php" in the "require_once" (which BTW is a type of "include" in PHP). The reason is simple: lib-common.php lives in public_html, and our first program was in that directory as well. This new program is in a subdirectory of public_html, so we have to go to the parent directory to get our include file.<br />
<br />
If you want to keep your geeklog installation "pure", as I usually do, you can also put your program directory somewhere outside of the geeklog directory, and use directives for your webserver to map that directory into the web space of your geeklog installation. That's easier than it sounds - with Apache just use the "Alias" directive in your apache config file, like this :<br />
<br />
<pre><br />
Alias /hello/ "/path/to/your/hello/"<br />
</pre><br />
<br />
Of course, this means that your require_once statement will have to contain the full path to lib-common.php <br />
<br />
<pre><br />
require_once( '/path/to/geeklog/public_html/lib-common.php' );<br />
</pre><br />
<br />
==To Plug it in, or not==<br />
<br />
This is a bit of an advanced topic which in some ways is out of place at this point, but just about everyone who knows Geeklog and has used it a bit, knows about [[Plugin Developers Handbook|Geeklog plugins]]. And when writing your own Geeklog programs, this will obviously be something in your mind. Not all Geeklog programs are plugins - and the above examples are not. Plugins involve writing your program in a specific way, and defining specific functions which Geeklog will expect to find. It also involves making some entries in the Geeklog database to let Geeklog know that your plugin is there.<br />
<br />
In general if you want to use the Geeklog comment engine, the Geeklog search engine (i.e. integrate your program data into the search feature of Geeklog), or the Geeklog submission engine, you must write a plugin. Otherwise you can just write code. Size doesn't matter. There is no limit after which you have to make it a plugin.<br />
<br />
==Some Odds and Ends==<br />
<br />
A couple of more quick points on some basic Geeklog stuff<br />
<br />
* the $_USER array comes pre-populated for you by Geeklog. If $_USER['uid'] is greater than 1, then you know your user is logged on. Otherwise they are anonymous. So in the above example if you wanted to test for "logged on user" rather than "member of geeker group", just change the 'if' statement accordingly. This array contains all of the user table from geeklog, the next most useful subscript being $_USER['username']<br />
* the $_CONF array contains everything you set in your config.php, if you need it. Just check config.php for what all is there.<br />
* both of the above arrays are global, and as such if you use them in a function you must declare them in the function with the 'global' directive, as is normal for PHP. Outside of any function in the main body of the program they can be just used.<br />
<br />
==Functions, Bring Me Functions!==<br />
<br />
We've already seen three of the most widely used functions that Geeklog has to offer - COM_siteHeader(), COM_siteFooter() and COM_output(). It is important to note with this that there are optional parameters you can pass to each of them to achieve certain results. COM_siteHeader() displays the header and the left blocks, while its partner controls the footer and the right blocks. By default COM_siteHeader() displays the left blocks, and by default COM_siteFooter() does not display the right blocks. COM_output takes your $display variable which contains all your HTML and displays it, allowing for compressed data to be decompressed and displayed in the process. Check the source code in lib-common.php for details on how to change this behavior.<br />
<br />
Another set of similar workhorse functions which are also defined in lib-common.php along with the aforementioned functions are COM_startBlock() and COM_endBlock(). COM_startBlock() accepts 3 optional parameters: title, helpfile and template. The most useful and almost always used is title, which is a text string which will appear in the title bar of the block. If a helpfile is specified, Geeklog will display the help question mark icon and link to a help file for that block. And by default the 'blockheader.thtml' template is used unless another is specified. COM_endBlock() '''must''' be called once for each call to COM_startBlock(), and its only optional parameter is template, the default being blockfooter.thtml.<br />
<br />
Blocks can be nested inside of each other, which is obvious by simply looking at just about any geeklog website. <br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner block"<br />
. COM_startBlock("Inner Block")<br />
. "This text should be inside the inner block"<br />
. COM_endBlock()<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
When using nested blocks inside of HTML tables, one simply has to be certain to call COM_endBlock() in the right place since these functions output HTML tables as well, and otherwise the display may not render properly. COM_startBlock() and COM_endBlock() are used like COM_siteHeader() and COM_siteFooter() but for internal page elements.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner blocks"<br />
. "<table align=center width=100% border=0>"<br />
. "<tr><td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the left inner block"<br />
. COM_endBlock()<br />
. "</td>"<br />
. "<td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the right inner block"<br />
. COM_endBlock()<br />
. "</td></tr></table>"<br />
. "This text should be below the inner blocks but inside the outer block"<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
The great advantage of using these two functions is that whenever the site admin or user changes their Geeklog theme, your GUI will change to match. Your program will always retain the same look-and-feel of the site in general.<br />
<br />
There are also some useful HTML form functions found in lib-common.php which come in very handy and make life a bit easier. <pre>COM_optionList( $table, $selection, $selected='', $sortcol=1 )</pre>. This creates an HTML "<option" list generated from the given database table, using the passed variable "$selected" in the SELECT statement of the HTML query. See source code for a better idea of what the function does, but it is very useful.<br />
<br />
In a similar vein, <pre>COM_checkList( $table, $selection, $where='', $selected='' )</pre> creates a list of check boxes from the given database table, with the given select and where clauses being passed to the SQL statement inside the function.<br />
<br />
One more useful function is the <pre>COM_errorLog( $logentry, $actionid = '')</pre> function which logs to the Geeklog logfile if $actionid is 1, or to the screen if it is set to 2.<br />
<br />
<pre>COM_checkWords( $Message )</pre> gives you access to Geeklog's (somewhat rudimentary) profanity filter. We find it to be not terribly useful since if you include for example the word "cock" in your filter, you will also filter out the completely innocuous word "peacock". If you nonetheless want to use the geeklog profanity filter, simply do this:<br />
<br />
<pre><br />
$text = COM_checkWords( $text );<br />
</pre><br />
<br />
<pre>COM_mail( $to, $subject, $message, $from = '', $html = false, $priority = 0 )</pre> does exactly what the name suggests and lets you send mail to someone.<br />
<br />
There are far too many functions in lib-common.php to discuss here, so we'll end off with two very important ones which can be used for accessing query-string variables. What's a query string? If you have a URL like this:<br />
<br />
<pre><br />
http://www.example.com/someprogram.php?variable=value&othervariable=othervalue<br />
</pre><br />
<br />
The query string is the part after the question mark - the stuff you pass into your program. In this example, inside the text of someprogram.php, if the PHP installation has "register_globals" turned on, the variable "$variable" will automagically exist in the program and will have the value "value". But there are certain security problems with using "register_globals" in PHP so a lot of people do not like to have it turned on. Unfortunately Geeklog requires that it be turned on (at least for now until the programmers get it rewritten to eliminate the need), so to mitigate the risks involved you can use special functions to obtain your query string variables.<br />
<br />
Near the top of your program simply insert something like the following, first to define which are the only global variables your program expects to see, then finally to safely obtain the value of those variables:<br />
<pre><br />
COM_setArgNames(array('variable','othervariable'));<br />
$variable = COM_getArgument('variable');<br />
$othervariable = COM_getArgument('othervariable');<br />
</pre><br />
<br />
==Using the Database==<br />
<br />
Geeklog has a database abstraction layer which in theory makes it possible for you to use any database as the backend for it. Though in practice the Geeklog team has only implemented a backend for the popular [http://www.mysql.com MySQL] database. In any case, when programming Geeklog you do not use the regular PHP database functions - instead you use the DB_ functions which behave almost idenically to the PHP functions that have similar names.<br />
<br />
Another important thing to note about Geeklog is that you should never use table names directly in your queries. Instead, you should use the $_TABLES global variable, and add your own table names to it if you make your own tables. The reason for this is simply that Geeklog allows the installer to specify a "table prefix", so if you use table names directly your code will not run on another Geeklog installation that uses a different table prefix. Even if you think you'll never want to run your code on another Geeklog installation, we recommend you do things properly because you never do know. I've written code that I thought would never have to run in another installation, and sure enough 2 years later I have to go back and convert it all to use the $_TABLES array because now I do indeed want to run it on another installation that is using a different table prefix.<br />
<br />
One final very important thing to state about using the Geeklog database is that '''under no circumstances whatsoever should you ever alter the default Geeklog tables'''. One example of where you might be tempted to do this is if you want to track a specific option for users - you may be tempted to add a field or two to the Geeklog "users" table. Say for example you are writing a program "buysell" which allows users to enter items into the database to put them up for sale to other users. And when browsing the database to see what is for sale, you want each user to decide whether or not they want to see their own items. You may be tempted to add a boolean field "seeown" to the Geeklog users table, but don't do it! Instead, create your own table "buysell_userprefs" and add whatever fields you require to this new table. At very least we need a field for the userid - so we'll call it "bsp_uid", and we need a field for "see your own items" so we'll call it "bsp_seeown".<br />
<br />
In general we like to give table fields names that have an abbreviation of the table name at the beginning of every field. So in our case this is a table which contains "buy sell preferences" for each user, so we'll name all the fields "bsp_". This is optional, but we've found it to be a good practice so that you do not end up with fields from various tables with the same name - something that can under circumstances cause problems in your queries, or unexpected results.<br />
<br />
===Adding to $_TABLES===<br />
As already mentioned, if you define your own tables, you have to add them to the global $_TABLES variable. <br />
<br />
<pre><br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
</pre><br />
<br />
Note that we've included the Geeklog global variable for table prefix, so that our code will work in all Geeklog installations. And of course you need one line for every table you are adding to the Geeklog database. And finally, like any global variable in Geelog you must declare it global in a function if you want to use it in that function.<br />
<br />
When doing a plugin you usually put this into the config.php for your plugin. If not doing a plugin you have several options on where to put it, depending upon how you have your code organised. If you have one big file, then put it at the top of that file. If you have an include file that gets included by all the programs you are writing, put it there. Basically you have to put it whereever you can that will ensure it gets executed by all of your programs and is visible by all of your programs.<br />
<br />
===Get on with it!===<br />
And finally we can show you how to put it all together. Let's write a simple little program that does nothing more than show you what your 'bsp_seeown' preference is set to. <br />
<br />
<pre><br />
<?php<br />
require_once('lib-common.php');<br />
<br />
$display = COM_siteHeader(); <br />
<br />
if ( $_USER['uid'] < 2 ) {<br />
$display .= "You are not logged in";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
exit;<br />
}<br />
<br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
<br />
$sql = "SELECT bsp_seeown FROM {$_TABLES['buysell_userprefs']} "<br />
. " WHERE {$_TABLES['buysell_userprefs']}.bsp_uid = {$_USER['uid']} ";<br />
$result = DB_query( $sql );<br />
if ( ! $result ) {<br />
// some error condition and possibly exit<br />
}<br />
if ( DB_numRows( $result ) <> 1 ) {<br />
// there should be precisely one entry for each user<br />
// otherwise you may want to flag an error condition<br />
// or you may want to alternately check to see if this<br />
// value is less than 1 first, in which case they user<br />
// has not yet set their preferences<br />
}<br />
$bsp = DB_fetchArray( $result );<br />
if ( ! $bsp ) {<br />
// some error condition<br />
}<br />
<br />
$display .= COM_startBlock("Your Preference is") <br />
. $bsp['bsp_seeown']<br />
. COM_endBlock(); <br />
<br />
$display .= COM_siteFooter(); <br />
<br />
COM_output($display); <br />
<br />
?><br />
</pre><br />
<br />
Wow! There's lots going on in this program! A lot more than what we might have expected! First and foremost note that there are lots of potential error conditions to check for, when using the Geeklog database. This is no different from just programming MySQL with the normal PHP functions, actually. It is always a best practice to check for error conditions and react accordingly.<br />
<br />
Before we looked up the user's preference, we of course first did a check to make sure they were logged on, and if not we exited. Then, you can see how we added our table to the $_TABLES global variable, and then inside of the SELECT statement used the $_TABLES variable to ensure our code is portable. If you wanted to move this to another system you do not have to change a thing!<br />
<br />
As for the specific DB_ functions we used, they behave in the same way as the PHP MySQL functions with similar names. If you aren't familiar with how they work, check the Geeklog source code as well as the PHP manual. For a full listing of all the DB_ functions available to you, check out /path/to/geeklog/system/lib-database.php<br />
<br />
==Defining Functions==<br />
Defining functions in Geeklog is of course no different from doing so in PHP. Though there are a few lessons to be learned from the Geeklog coding style. One handy thing to do is pick a 3 to 5 character prefix for all of your functions. This will help prevent you and some other developer from walking on each others toes and writing plugins or other Geeklog programs which are incompatible with each other. For example in my User Pages Plugin I chose the prefix "UPAGE_" for every one of my own functions.<br />
<br />
Figuring out how to do return codes from functions is never easy in Geeklog or PHP in general. Many functions will return strings of HTML formatted text, and so returning error conditions is not easy. There is no one solution for every circumstance - though I've found 2 solutions work most of the time. If you hit an error condition in your function you can either return a NULL string so the caller can check for NULL string, or you can just return a string with an error message about the problem encountered, in which case the caller will not really know something went wrong - which may or may not matter. It depends on your caller.<br />
<br />
Let's have a look at a couple of functions for making HTML select boxes out of the database. The first function is essentially the same as the Geeklog function COM_optionList although it does get called with different parameters, and the Geeklog function is a bit more powerful. <br />
<br />
One thing you will see first off in the below function definition is that PHP gives you a means to specify default values for function parameters. This means that when calling the function, only the first two parameters "myName" and "myOptions" need be specified. myName is the name this element will have (variable name) and myOptions is a list of options separated by the "mySep" character which by default is "|".<br />
<br />
An important aspect to understand about default values for parameters is that you can only allow the X right-most parameters to have a default value. That is to say you cannot specify a default value for the 1st parameter, then none for the 2nd, then one for the 3rd and so on. The first zero or more parameters will have no default, then after the first one that has a default value all the rest must also have default values. And also when calling the function that we have below, if I wanted to override the default for "mySep" for example by passing a value in, then I also have to override the defaults for every parameter to the left of it - so I must also specify overrides for "myDefault", "myMulti" and "mySize".<br />
<br />
So I could call the function in any of the following ways :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0 );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0, 1 );<br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", "one", 0, 1, ":" );<br />
</pre><br />
<br />
and so on. But I could not do this if all I wanted to specify was "mySep" :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", ,,, ":" );<br />
</pre><br />
<br />
So the moral of the story is that if you are having parameters with default values you have to give some consideration to the order of the arguments. You want the ones least likey to be overridden to be the right-most, and the ones most likely to be overridden to be left-most.<br />
<br />
<pre><br />
function SSM_inputSelect( $myName, $myOptions, $myDefault="", $myMulti=0,<br />
$mySize=1, $mySep="|", $visible=true )<br />
{<br />
$retval .= ""<br />
. "<SELECT size=\"" . $mySize . "\" name=\"" . $myName . "\"";<br />
<br />
$retval .= ($myMulti == 0) ? ">" : " multiple>";<br />
<br />
$arrayOptions = explode($mySep,$myOptions);<br />
<br />
foreach ($arrayOptions as $oneOption) {<br />
$oneOption = trim($oneOption);<br />
if ( $myMulti == 0 )<br />
if ( $oneOption == $myDefault )<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
else<br />
if ( in_array( $oneOption, $myDefault ))<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
}<br />
<br />
$retval .= ""<br />
. "</SELECT>"<br />
. "";<br />
<br />
return $retval;<br />
<br />
}<br />
</pre><br />
<br />
Now let's have a look at another function which builds on the above by allowing us to pull stuff out of the database and present it in an options list.<br />
<br />
<pre><br />
function SSM_inputSelectDBField( $myName, $myTable, $myField, $myDefault="",<br />
$mySize=1, $myMulti=0, $extra="", $mySep="|" )<br />
{<br />
// select distinct entries from the given field of given table<br />
$sql = "SELECT DISTINCT " . $myField . " FROM " . $myTable<br />
. " ORDER BY " . $myField;<br />
<br />
// allows us to add an extra entry that was not in the DB<br />
<br />
if ( $extra != "" )<br />
$myOpts = $extra . $myOpts;<br />
<br />
$result = DB_query($sql);<br />
<br />
// format the data as required by SSM_inputSelect()<br />
<br />
while ( $R = DB_fetchArray( $result ) )<br />
if ( $myOpts == "" )<br />
$myOpts .= $R[$myField];<br />
else<br />
$myOpts .= $mySep . $R[$myField];<br />
<br />
// now call the guy doing the actual work<br />
<br />
$retstr .= SSM_inputSelect( $myName, $myOpts, $myDefault, $myMulti,<br />
$mySize, $mySep );<br />
<br />
return $retstr;<br />
}<br />
<br />
</pre><br />
<br />
And finally here is a similar function which once again builds upon "SSM_inputSelect" but this time it takes an field of type ENUM and builds a SELECT box out of all the possible preset values of the ENUM.<br />
<br />
<pre><br />
// Does not yet allow multi select but should be rewritten to do this<br />
<br />
function SSM_inputEnumDBField( $myName, $myTable, $myField, $myDefault="", $visible=true )<br />
{<br />
// query the DB to extract the enum values<br />
$qqq = "DESCRIBE $myTable $myField";<br />
$result = DB_query( $qqq );<br />
$arow = DB_fetchArray( $result );<br />
$myArr = explode( ",", trim( strstr( $arow['Type'], "(" ), "()")) ;<br />
<br />
// now format the values as required by SSM_inputSelect()<br />
$idx=0;<br />
$cnt = count($myArr);<br />
while($idx<$cnt)<br />
{<br />
$myArr[$idx] = trim( $myArr[$idx], "'" );<br />
$idx++;<br />
}<br />
sort( $myArr );<br />
$myList = implode( "|", $myArr );<br />
<br />
// now call our workhorse<br />
<br />
return SSM_inputSelect( $myName, $myList, $myDefault );<br />
}<br />
</pre><br />
<br />
The lesson here is that your functions should be well-defined and reusable. Here we could have written 2 different functions which have nothing to do with each other, but instead we wrote a 3rd base function first which the other 2 rely on to get the job done. Now if there is some substantial change in how I want the SELECT boxes drawn, I only have to make the change in one place.<br />
<br />
==Support and Such==<br />
<br />
The best place for Geeklog support is of course [http://www.geeklog.net the main Geeklog site]. But there are a few other great places to check including [http://www.squatty.com Squatty] and [http://www.portalparts.com Portal Parts]. Squatty and Blaine are hard-core Geeklog developers and are responsible for several popular themes, plugins and hacks.<br />
<br />
If you want to report a bug or request a feature, set yourself up an account [http://project.geeklog.net/tracking/signup_page.php here] and do so. If they don't know it is broken, they can't fix it. I've reported several bugs and have had them fixed promptly. I've also tracked down and fixed several bugs and simply submitted the code which was accepted. And I've also requested several features which have been added over the years at my request. The Geeklog development team is small, but very dedicated and they love to get feedback from the user base.<br />
<br />
<br />
[[Category:Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Beginner%27s_Guide_to_Programming&diff=5803Beginner's Guide to Programming2010-04-14T18:25:23Z<p>Rasade: /* Get on with it! */ - added usage of COM_output()</p>
<hr />
<div>By [[User:Amckay|Alan McKay]]<br />
<br />
[http://www.geeklog.net Geeklog] is a powerful weblog (blog) content management system (CMS) which is written in the popular programming language [http://www.php.net PHP], and uses the popular [http://www.mysql.com MySQL] database. While Geeklog is powerful enough that many users will not have a need to write their own applications for it, it is flexible enough to allow those who do require extra functionality to do so easily. These people write their programs in PHP, with some minor restrictions and using the Geeklog function library.<br />
<br />
<br />
==Hello, World==<br />
<br />
The first program you write in any computer language is "Hello World", and here it is in Geeklog. This is saved in a file "hello.php" in Geeklog's "public_html" directory, and so is surfable at http://www.example.com/hello.php<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
$display = COM_siteHeader();<br />
$display .= "Hello World";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
There are a few important things to be noted from the given program.<br />
<br />
* geeklog programs stay in PHP mode - there is no flipping back and forth between PHP and HTML as is possible in the PHP language. This means that all programs have "<?php" on the first line, and "?>" on the last line, and everything in between is PHP code.<br />
* the HTML is generated using a single function, COM_output() as on the last line of the above code. Although it is possible to use a simple 'echo' statement to spit out the HTML, newer versions of Geeklog use the COM_output() function. COM_output() takes the $display variable to which all your HTML output is appended throughout the code (note the ".=" which is used for the append) as an argument and simply echoes it out in this case. (Newer versions of Geeklog use the COM_output() function to allow for any output that may use compression. More on that later)<br />
* "lib-common.php" is the single file which must be included in all your Geeklog programs. It includes everything else you need unless you decide to make your own include files, so it's one-stop shopping.<br />
* there are functions in Geeklog to do lots of stuff for you - like for example COM_siteHeader() and COM_siteFooter(). In general Geeklog functions start with 2 or 3 capital letters and an understore - this tells you what type of function it is. Then the name of the function. COM_ functions are "common" functions - not a terribly meaningful name. But SEC_ functions like SEC_inGroup() which allows you to test if a user is in a particular group, allow your programs to access some of the powerful security features of Geeklog. And DB_ commands allow you to access the Geeklog database.<br />
<br />
==Security==<br />
Speaking of the powerful Geeklog security model (one of the key reasons I originally chose Geeklog for my sites), let's alter the hello world program such that any user in the "geeker" user group will see the "hello world" message, but anyone not in that group (which includes users not logged in) will get a "permission denied" error.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( SEC_inGroup( 'geeker' ) )<br />
$display .= "Hello World";<br />
else<br />
$display .= "Access Denied";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
To check out the full range of security functions available to you, and how to use them, read the /path/to/geeklog/system/lib-security.php file, which is where they are implemented. <br />
<br />
Though the above code format is a bit clunky and not terribly useful, so let's make another change which shows us how most programs deal with group permissions issues.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( ! SEC_inGroup( 'geeker' ) )<br />
{<br />
$display .= "Access Denied";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
exit;<br />
}<br />
<br />
$display .= "Hello World";<br />
<br />
// do some other stuff here<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
The big difference in this version of the program is that right at the top of the program we test for group permissions, and if the user does not have them we display the site footer, then exit. So a user not in the 'geeker' group will end right there and never see what the rest of the program does. Very simple, but very powerful! This is precisely how you control access to your pages in Geeklog!<br />
<br />
==Where to put it==<br />
<br />
If you are only writing a small program, then sticking a single file in the public_html directory as shown above will work fine. As soon as you get to the point, however, when you start having your own include files and so on, you probably want to create a directory for it. In our case we could create a directory in "public_html" called "hello", and then create a file "index.php" with the above program. This will require a very minor change to the original program - see if you can pick it out before peeking :<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( '../lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= "Hello World";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
Yup, that's right, we had to add "../" to the "lib-common.php" in the "require_once" (which BTW is a type of "include" in PHP). The reason is simple: lib-common.php lives in public_html, and our first program was in that directory as well. This new program is in a subdirectory of public_html, so we have to go to the parent directory to get our include file.<br />
<br />
If you want to keep your geeklog installation "pure", as I usually do, you can also put your program directory somewhere outside of the geeklog directory, and use directives for your webserver to map that directory into the web space of your geeklog installation. That's easier than it sounds - with Apache just use the "Alias" directive in your apache config file, like this :<br />
<br />
<pre><br />
Alias /hello/ "/path/to/your/hello/"<br />
</pre><br />
<br />
Of course, this means that your require_once statement will have to contain the full path to lib-common.php <br />
<br />
<pre><br />
require_once( '/path/to/geeklog/public_html/lib-common.php' );<br />
</pre><br />
<br />
==To Plug it in, or not==<br />
<br />
This is a bit of an advanced topic which in some ways is out of place at this point, but just about everyone who knows Geeklog and has used it a bit, knows about [[Plugin Developers Handbook|Geeklog plugins]]. And when writing your own Geeklog programs, this will obviously be something in your mind. Not all Geeklog programs are plugins - and the above examples are not. Plugins involve writing your program in a specific way, and defining specific functions which Geeklog will expect to find. It also involves making some entries in the Geeklog database to let Geeklog know that your plugin is there.<br />
<br />
In general if you want to use the Geeklog comment engine, the Geeklog search engine (i.e. integrate your program data into the search feature of Geeklog), or the Geeklog submission engine, you must write a plugin. Otherwise you can just write code. Size doesn't matter. There is no limit after which you have to make it a plugin.<br />
<br />
==Some Odds and Ends==<br />
<br />
A couple of more quick points on some basic Geeklog stuff<br />
<br />
* the $_USER array comes pre-populated for you by Geeklog. If $_USER['uid'] is greater than 1, then you know your user is logged on. Otherwise they are anonymous. So in the above example if you wanted to test for "logged on user" rather than "member of geeker group", just change the 'if' statement accordingly. This array contains all of the user table from geeklog, the next most useful subscript being $_USER['username']<br />
* the $_CONF array contains everything you set in your config.php, if you need it. Just check config.php for what all is there.<br />
* both of the above arrays are global, and as such if you use them in a function you must declare them in the function with the 'global' directive, as is normal for PHP. Outside of any function in the main body of the program they can be just used.<br />
<br />
==Functions, Bring Me Functions!==<br />
<br />
We've already seen three of the most widely used functions that Geeklog has to offer - COM_siteHeader(), COM_siteFooter() and COM_output(). It is important to note with this that there are optional parameters you can pass to each of them to achieve certain results. COM_siteHeader() displays the header and the left blocks, while its partner controls the footer and the right blocks. By default COM_siteHeader() displays the left blocks, and by default COM_siteFooter() does not display the right blocks. COM_output takes your $display variable which contains all your HTML and displays it, allowing for compressed data to be decompressed and displayed in the process. Check the source code in lib-common.php for details on how to change this behavior.<br />
<br />
Another set of similar workhorse functions which are also defined in lib-common.php along with the aforementioned functions are COM_startBlock() and COM_endBlock(). COM_startBlock() accepts 3 optional parameters: title, helpfile and template. The most useful and almost always used is title, which is a text string which will appear in the title bar of the block. If a helpfile is specified, Geeklog will display the help question mark icon and link to a help file for that block. And by default the 'blockheader.thtml' template is used unless another is specified. COM_endBlock() '''must''' be called once for each call to COM_startBlock(), and its only optional parameter is template, the default being blockfooter.thtml.<br />
<br />
Blocks can be nested inside of each other, which is obvious by simply looking at just about any geeklog website. <br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner block"<br />
. COM_startBlock("Inner Block")<br />
. "This text should be inside the inner block"<br />
. COM_endBlock()<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
When using nested blocks inside of HTML tables, one simply has to be certain to call COM_endBlock() in the right place since these functions output HTML tables as well, and otherwise the display may not render properly. COM_startBlock() and COM_endBlock() are used like COM_siteHeader() and COM_siteFooter() but for internal page elements.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner blocks"<br />
. "<table align=center width=100% border=0>"<br />
. "<tr><td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the left inner block"<br />
. COM_endBlock()<br />
. "</td>"<br />
. "<td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the right inner block"<br />
. COM_endBlock()<br />
. "</td></tr></table>"<br />
. "This text should be below the inner blocks but inside the outer block"<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
The great advantage of using these two functions is that whenever the site admin or user changes their Geeklog theme, your GUI will change to match. Your program will always retain the same look-and-feel of the site in general.<br />
<br />
There are also some useful HTML form functions found in lib-common.php which come in very handy and make life a bit easier. <pre>COM_optionList( $table, $selection, $selected='', $sortcol=1 )</pre>. This creates an HTML "<option" list generated from the given database table, using the passed variable "$selected" in the SELECT statement of the HTML query. See source code for a better idea of what the function does, but it is very useful.<br />
<br />
In a similar vein, <pre>COM_checkList( $table, $selection, $where='', $selected='' )</pre> creates a list of check boxes from the given database table, with the given select and where clauses being passed to the SQL statement inside the function.<br />
<br />
One more useful function is the <pre>COM_errorLog( $logentry, $actionid = '')</pre> function which logs to the Geeklog logfile if $actionid is 1, or to the screen if it is set to 2.<br />
<br />
<pre>COM_checkWords( $Message )</pre> gives you access to Geeklog's (somewhat rudimentary) profanity filter. We find it to be not terribly useful since if you include for example the word "cock" in your filter, you will also filter out the completely innocuous word "peacock". If you nonetheless want to use the geeklog profanity filter, simply do this:<br />
<br />
<pre><br />
$text = COM_checkWords( $text );<br />
</pre><br />
<br />
<pre>COM_mail( $to, $subject, $message, $from = '', $html = false, $priority = 0 )</pre> does exactly what the name suggests and lets you send mail to someone.<br />
<br />
There are far too many functions in lib-common.php to discuss here, so we'll end off with two very important ones which can be used for accessing query-string variables. What's a query string? If you have a URL like this:<br />
<br />
<pre><br />
http://www.example.com/someprogram.php?variable=value&othervariable=othervalue<br />
</pre><br />
<br />
The query string is the part after the question mark - the stuff you pass into your program. In this example, inside the text of someprogram.php, if the PHP installation has "register_globals" turned on, the variable "$variable" will automagically exist in the program and will have the value "value". But there are certain security problems with using "register_globals" in PHP so a lot of people do not like to have it turned on. Unfortunately Geeklog requires that it be turned on (at least for now until the programmers get it rewritten to eliminate the need), so to mitigate the risks involved you can use special functions to obtain your query string variables.<br />
<br />
Near the top of your program simply insert something like the following, first to define which are the only global variables your program expects to see, then finally to safely obtain the value of those variables:<br />
<pre><br />
COM_setArgNames(array('variable','othervariable'));<br />
$variable = COM_getArgument('variable');<br />
$othervariable = COM_getArgument('othervariable');<br />
</pre><br />
<br />
==Using the Database==<br />
<br />
Geeklog has a database abstraction layer which in theory makes it possible for you to use any database as the backend for it. Though in practice the Geeklog team has only implemented a backend for the popular [http://www.mysql.com MySQL] database. In any case, when programming Geeklog you do not use the regular PHP database functions - instead you use the DB_ functions which behave almost idenically to the PHP functions that have similar names.<br />
<br />
Another important thing to note about Geeklog is that you should never use table names directly in your queries. Instead, you should use the $_TABLES global variable, and add your own table names to it if you make your own tables. The reason for this is simply that Geeklog allows the installer to specify a "table prefix", so if you use table names directly your code will not run on another Geeklog installation that uses a different table prefix. Even if you think you'll never want to run your code on another Geeklog installation, we recommend you do things properly because you never do know. I've written code that I thought would never have to run in another installation, and sure enough 2 years later I have to go back and convert it all to use the $_TABLES array because now I do indeed want to run it on another installation that is using a different table prefix.<br />
<br />
One final very important thing to state about using the Geeklog database is that '''under no circumstances whatsoever should you ever alter the default Geeklog tables'''. One example of where you might be tempted to do this is if you want to track a specific option for users - you may be tempted to add a field or two to the Geeklog "users" table. Say for example you are writing a program "buysell" which allows users to enter items into the database to put them up for sale to other users. And when browsing the database to see what is for sale, you want each user to decide whether or not they want to see their own items. You may be tempted to add a boolean field "seeown" to the Geeklog users table, but don't do it! Instead, create your own table "buysell_userprefs" and add whatever fields you require to this new table. At very least we need a field for the userid - so we'll call it "bsp_uid", and we need a field for "see your own items" so we'll call it "bsp_seeown".<br />
<br />
In general we like to give table fields names that have an abbreviation of the table name at the beginning of every field. So in our case this is a table which contains "buy sell preferences" for each user, so we'll name all the fields "bsp_". This is optional, but we've found it to be a good practice so that you do not end up with fields from various tables with the same name - something that can under circumstances cause problems in your queries, or unexpected results.<br />
<br />
===Adding to $_TABLES===<br />
As already mentioned, if you define your own tables, you have to add them to the global $_TABLES variable. <br />
<br />
<pre><br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
</pre><br />
<br />
Note that we've included the Geeklog global variable for table prefix, so that our code will work in all Geeklog installations. And of course you need one line for every table you are adding to the Geeklog database. And finally, like any global variable in Geelog you must declare it global in a function if you want to use it in that function.<br />
<br />
When doing a plugin you usually put this into the config.php for your plugin. If not doing a plugin you have several options on where to put it, depending upon how you have your code organised. If you have one big file, then put it at the top of that file. If you have an include file that gets included by all the programs you are writing, put it there. Basically you have to put it whereever you can that will ensure it gets executed by all of your programs and is visible by all of your programs.<br />
<br />
===Get on with it!===<br />
And finally we can show you how to put it all together. Let's write a simple little program that does nothing more than show you what your 'bsp_seeown' preference is set to. <br />
<br />
<pre><br />
<?php<br />
require_once('lib-common.php');<br />
<br />
$display = COM_siteHeader(); <br />
<br />
if ( $_USER['uid'] < 2 ) {<br />
$display .= "You are not logged in";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
exit;<br />
}<br />
<br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
<br />
$sql = "SELECT bsp_seeown FROM {$_TABLES['buysell_userprefs']} "<br />
. " WHERE {$_TABLES['buysell_userprefs']}.bsp_uid = {$_USER['uid']} ";<br />
$result = DB_query( $sql );<br />
if ( ! $result ) {<br />
// some error condition and possibly exit<br />
}<br />
if ( DB_numRows( $result ) <> 1 ) {<br />
// there should be precisely one entry for each user<br />
// otherwise you may want to flag an error condition<br />
// or you may want to alternately check to see if this<br />
// value is less than 1 first, in which case they user<br />
// has not yet set their preferences<br />
}<br />
$bsp = DB_fetchArray( $result );<br />
if ( ! $bsp ) {<br />
// some error condition<br />
}<br />
<br />
$display .= COM_startBlock("Your Preference is") <br />
. $bsp['bsp_seeown']<br />
. COM_endBlock(); <br />
<br />
$display .= COM_siteFooter(); <br />
<br />
COM_output($display); <br />
<br />
?><br />
</pre><br />
<br />
Wow! There's lots going on in this program! A lot more than what we might have expected! First and foremost note that there are lots of potential error conditions to check for, when using the Geeklog database. This is no different from just programming MySQL with the normal PHP functions, actually. It is always a best practice to check for error conditions and react accordingly.<br />
<br />
Before we looked up the user's preference, we of course first did a check to make sure they were logged on, and if not we exited. Then, you can see how we added our table to the $_TABLES global variable, and then inside of the SELECT statement used the $_TABLES variable to ensure our code is portable. If you wanted to move this to another system you do not have to change a thing!<br />
<br />
As for the specific DB_ functions we used, they behave in the same way as the PHP MySQL functions with similar names. If you aren't familiar with how they work, check the Geeklog source code as well as the PHP manual. For a full listing of all the DB_ functions available to you, check out /path/to/geeklog/system/lib-database.php<br />
<br />
==Defining Functions==<br />
Defining functions in Geeklog is of course no different from doing so in PHP. Though there are a few lessons to be learned from the Geeklog coding style. One handy thing to do is pick a 3 to 5 character prefix for all of your functions. This will help prevent you and some other developer from walking on each others toes and writing plugins or other Geeklog programs which are incompatible with each other. For example in my User Pages Plugin I chose the prefix "UPAGE_" for every one of my own functions.<br />
<br />
Figuring out how to do return codes from functions is never easy in Geeklog or PHP in general. Many functions will return strings of HTML formatted text, and so returning error conditions is not easy. There is no one solution for every circumstance - though I've found 2 solutions work most of the time. If you hit an error condition in your function you can either return a NULL string so the caller can check for NULL string, or you can just return a string with an error message about the problem encountered, in which case the caller will not really know something went wrong - which may or may not matter. It depends on your caller.<br />
<br />
Let's have a look at a couple of functions for making HTML select boxes out of the database. The first function is essentially the same as the Geeklog function COM_optionList although it does get called with different parameters, and the Geeklog function is a bit more powerful. <br />
<br />
One thing you will see first off in the below function definition is that PHP gives you a means to specify default values for function parameters. This means that when calling the function, only the first two parameters "myName" and "myOptions" need be specified. myName is the name this element will have (variable name) and myOptions is a list of options separated by the "mySep" character which by default is "|".<br />
<br />
An important aspect to understand about default values for parameters is that you can only allow the X right-most parameters to have a default value. That is to say you cannot specify a default value for the 1st parameter, then none for the 2nd, then one for the 3rd and so on. The first zero or more parameters will have no default, then after the first one that has a default value all the rest must also have default values. And also when calling the function that we have below, if I wanted to override the default for "mySep" for example by passing a value in, then I also have to override the defaults for every parameter to the left of it - so I must also specify overrides for "myDefault", "myMulti" and "mySize".<br />
<br />
So I could call the function in any of the following ways :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0 );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0, 1 );<br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", "one", 0, 1, ":" );<br />
</pre><br />
<br />
and so on. But I could not do this if all I wanted to specify was "mySep" :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", ,,, ":" );<br />
</pre><br />
<br />
So the moral of the story is that if you are having parameters with default values you have to give some consideration to the order of the arguments. You want the ones least likey to be overridden to be the right-most, and the ones most likely to be overridden to be left-most.<br />
<br />
<pre><br />
function SSM_inputSelect( $myName, $myOptions, $myDefault="", $myMulti=0,<br />
$mySize=1, $mySep="|", $visible=true )<br />
{<br />
$retval .= ""<br />
. "<SELECT size=\"" . $mySize . "\" name=\"" . $myName . "\"";<br />
<br />
$retval .= ($myMulti == 0) ? ">" : " multiple>";<br />
<br />
$arrayOptions = explode($mySep,$myOptions);<br />
<br />
foreach ($arrayOptions as $oneOption) {<br />
$oneOption = trim($oneOption);<br />
if ( $myMulti == 0 )<br />
if ( $oneOption == $myDefault )<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
else<br />
if ( in_array( $oneOption, $myDefault ))<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
}<br />
<br />
$retval .= ""<br />
. "</SELECT>"<br />
. "";<br />
<br />
return $retval;<br />
<br />
}<br />
</pre><br />
<br />
Now let's have a look at another function which builds on the above by allowing us to pull stuff out of the database and present it in an options list.<br />
<br />
<pre><br />
function SSM_inputSelectDBField( $myName, $myTable, $myField, $myDefault="",<br />
$mySize=1, $myMulti=0, $extra="", $mySep="|" )<br />
{<br />
// select distinct entries from the given field of given table<br />
$sql = "SELECT DISTINCT " . $myField . " FROM " . $myTable<br />
. " ORDER BY " . $myField;<br />
<br />
// allows us to add an extra entry that was not in the DB<br />
<br />
if ( $extra != "" )<br />
$myOpts = $extra . $myOpts;<br />
<br />
$result = DB_query($sql);<br />
<br />
// format the data as required by SSM_inputSelect()<br />
<br />
while ( $R = DB_fetchArray( $result ) )<br />
if ( $myOpts == "" )<br />
$myOpts .= $R[$myField];<br />
else<br />
$myOpts .= $mySep . $R[$myField];<br />
<br />
// now call the guy doing the actual work<br />
<br />
$retstr .= SSM_inputSelect( $myName, $myOpts, $myDefault, $myMulti,<br />
$mySize, $mySep );<br />
<br />
return $retstr;<br />
}<br />
<br />
</pre><br />
<br />
And finally here is a similar function which once again builds upon "SSM_inputSelect" but this time it takes an field of type ENUM and builds a SELECT box out of all the possible preset values of the ENUM.<br />
<br />
<pre><br />
// Does not yet allow multi select but should be rewritten to do this<br />
<br />
function SSM_inputEnumDBField( $myName, $myTable, $myField, $myDefault="", $visible=true )<br />
{<br />
// query the DB to extract the enum values<br />
$qqq = "DESCRIBE $myTable $myField";<br />
$result = DB_query( $qqq );<br />
$arow = DB_fetchArray( $result );<br />
$myArr = explode( ",", trim( strstr( $arow['Type'], "(" ), "()")) ;<br />
<br />
// now format the values as required by SSM_inputSelect()<br />
$idx=0;<br />
$cnt = count($myArr);<br />
while($idx<$cnt)<br />
{<br />
$myArr[$idx] = trim( $myArr[$idx], "'" );<br />
$idx++;<br />
}<br />
sort( $myArr );<br />
$myList = implode( "|", $myArr );<br />
<br />
// now call our workhorse<br />
<br />
return SSM_inputSelect( $myName, $myList, $myDefault );<br />
}<br />
</pre><br />
<br />
The lesson here is that your functions should be well-defined and reusable. Here we could have written 2 different functions which have nothing to do with each other, but instead we wrote a 3rd base function first which the other 2 rely on to get the job done. Now if there is some substantial change in how I want the SELECT boxes drawn, I only have to make the change in one place.<br />
<br />
==Support and Such==<br />
<br />
The best place for Geeklog support is of course [http://www.geeklog.net the main Geeklog site]. But there are a few other great places to check including [http://www.squatty.com Squatty] and [http://www.portalparts.com Portal Parts]. Squatty and Blaine are hard-core Geeklog developers and are responsible for several popular themes, plugins and hacks.<br />
<br />
If you want to report a bug or request a feature, set yourself up an account [http://project.geeklog.net/tracking/signup_page.php here] and do so. If they don't know it is broken, the cannot fix it. I've reported several bugs and have had them fixed prompty. I've also tracked down and fixed several bugs and simply submitted the code which was accepted. And I've also requested several features which have been added over the years at my request. The Geeklog development team is small, but very dedicated and they love to get feedback from the user base.<br />
<br />
<br />
[[Category:Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Beginner%27s_Guide_to_Programming&diff=5802Beginner's Guide to Programming2010-04-14T18:21:39Z<p>Rasade: /* Functions, Bring Me Functions! */ - corrected some mistakes, added use of COM_output()</p>
<hr />
<div>By [[User:Amckay|Alan McKay]]<br />
<br />
[http://www.geeklog.net Geeklog] is a powerful weblog (blog) content management system (CMS) which is written in the popular programming language [http://www.php.net PHP], and uses the popular [http://www.mysql.com MySQL] database. While Geeklog is powerful enough that many users will not have a need to write their own applications for it, it is flexible enough to allow those who do require extra functionality to do so easily. These people write their programs in PHP, with some minor restrictions and using the Geeklog function library.<br />
<br />
<br />
==Hello, World==<br />
<br />
The first program you write in any computer language is "Hello World", and here it is in Geeklog. This is saved in a file "hello.php" in Geeklog's "public_html" directory, and so is surfable at http://www.example.com/hello.php<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
$display = COM_siteHeader();<br />
$display .= "Hello World";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
There are a few important things to be noted from the given program.<br />
<br />
* geeklog programs stay in PHP mode - there is no flipping back and forth between PHP and HTML as is possible in the PHP language. This means that all programs have "<?php" on the first line, and "?>" on the last line, and everything in between is PHP code.<br />
* the HTML is generated using a single function, COM_output() as on the last line of the above code. Although it is possible to use a simple 'echo' statement to spit out the HTML, newer versions of Geeklog use the COM_output() function. COM_output() takes the $display variable to which all your HTML output is appended throughout the code (note the ".=" which is used for the append) as an argument and simply echoes it out in this case. (Newer versions of Geeklog use the COM_output() function to allow for any output that may use compression. More on that later)<br />
* "lib-common.php" is the single file which must be included in all your Geeklog programs. It includes everything else you need unless you decide to make your own include files, so it's one-stop shopping.<br />
* there are functions in Geeklog to do lots of stuff for you - like for example COM_siteHeader() and COM_siteFooter(). In general Geeklog functions start with 2 or 3 capital letters and an understore - this tells you what type of function it is. Then the name of the function. COM_ functions are "common" functions - not a terribly meaningful name. But SEC_ functions like SEC_inGroup() which allows you to test if a user is in a particular group, allow your programs to access some of the powerful security features of Geeklog. And DB_ commands allow you to access the Geeklog database.<br />
<br />
==Security==<br />
Speaking of the powerful Geeklog security model (one of the key reasons I originally chose Geeklog for my sites), let's alter the hello world program such that any user in the "geeker" user group will see the "hello world" message, but anyone not in that group (which includes users not logged in) will get a "permission denied" error.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( SEC_inGroup( 'geeker' ) )<br />
$display .= "Hello World";<br />
else<br />
$display .= "Access Denied";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
To check out the full range of security functions available to you, and how to use them, read the /path/to/geeklog/system/lib-security.php file, which is where they are implemented. <br />
<br />
Though the above code format is a bit clunky and not terribly useful, so let's make another change which shows us how most programs deal with group permissions issues.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( ! SEC_inGroup( 'geeker' ) )<br />
{<br />
$display .= "Access Denied";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
exit;<br />
}<br />
<br />
$display .= "Hello World";<br />
<br />
// do some other stuff here<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
The big difference in this version of the program is that right at the top of the program we test for group permissions, and if the user does not have them we display the site footer, then exit. So a user not in the 'geeker' group will end right there and never see what the rest of the program does. Very simple, but very powerful! This is precisely how you control access to your pages in Geeklog!<br />
<br />
==Where to put it==<br />
<br />
If you are only writing a small program, then sticking a single file in the public_html directory as shown above will work fine. As soon as you get to the point, however, when you start having your own include files and so on, you probably want to create a directory for it. In our case we could create a directory in "public_html" called "hello", and then create a file "index.php" with the above program. This will require a very minor change to the original program - see if you can pick it out before peeking :<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( '../lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= "Hello World";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
Yup, that's right, we had to add "../" to the "lib-common.php" in the "require_once" (which BTW is a type of "include" in PHP). The reason is simple: lib-common.php lives in public_html, and our first program was in that directory as well. This new program is in a subdirectory of public_html, so we have to go to the parent directory to get our include file.<br />
<br />
If you want to keep your geeklog installation "pure", as I usually do, you can also put your program directory somewhere outside of the geeklog directory, and use directives for your webserver to map that directory into the web space of your geeklog installation. That's easier than it sounds - with Apache just use the "Alias" directive in your apache config file, like this :<br />
<br />
<pre><br />
Alias /hello/ "/path/to/your/hello/"<br />
</pre><br />
<br />
Of course, this means that your require_once statement will have to contain the full path to lib-common.php <br />
<br />
<pre><br />
require_once( '/path/to/geeklog/public_html/lib-common.php' );<br />
</pre><br />
<br />
==To Plug it in, or not==<br />
<br />
This is a bit of an advanced topic which in some ways is out of place at this point, but just about everyone who knows Geeklog and has used it a bit, knows about [[Plugin Developers Handbook|Geeklog plugins]]. And when writing your own Geeklog programs, this will obviously be something in your mind. Not all Geeklog programs are plugins - and the above examples are not. Plugins involve writing your program in a specific way, and defining specific functions which Geeklog will expect to find. It also involves making some entries in the Geeklog database to let Geeklog know that your plugin is there.<br />
<br />
In general if you want to use the Geeklog comment engine, the Geeklog search engine (i.e. integrate your program data into the search feature of Geeklog), or the Geeklog submission engine, you must write a plugin. Otherwise you can just write code. Size doesn't matter. There is no limit after which you have to make it a plugin.<br />
<br />
==Some Odds and Ends==<br />
<br />
A couple of more quick points on some basic Geeklog stuff<br />
<br />
* the $_USER array comes pre-populated for you by Geeklog. If $_USER['uid'] is greater than 1, then you know your user is logged on. Otherwise they are anonymous. So in the above example if you wanted to test for "logged on user" rather than "member of geeker group", just change the 'if' statement accordingly. This array contains all of the user table from geeklog, the next most useful subscript being $_USER['username']<br />
* the $_CONF array contains everything you set in your config.php, if you need it. Just check config.php for what all is there.<br />
* both of the above arrays are global, and as such if you use them in a function you must declare them in the function with the 'global' directive, as is normal for PHP. Outside of any function in the main body of the program they can be just used.<br />
<br />
==Functions, Bring Me Functions!==<br />
<br />
We've already seen three of the most widely used functions that Geeklog has to offer - COM_siteHeader(), COM_siteFooter() and COM_output(). It is important to note with this that there are optional parameters you can pass to each of them to achieve certain results. COM_siteHeader() displays the header and the left blocks, while its partner controls the footer and the right blocks. By default COM_siteHeader() displays the left blocks, and by default COM_siteFooter() does not display the right blocks. COM_output takes your $display variable which contains all your HTML and displays it, allowing for compressed data to be decompressed and displayed in the process. Check the source code in lib-common.php for details on how to change this behavior.<br />
<br />
Another set of similar workhorse functions which are also defined in lib-common.php along with the aforementioned functions are COM_startBlock() and COM_endBlock(). COM_startBlock() accepts 3 optional parameters: title, helpfile and template. The most useful and almost always used is title, which is a text string which will appear in the title bar of the block. If a helpfile is specified, Geeklog will display the help question mark icon and link to a help file for that block. And by default the 'blockheader.thtml' template is used unless another is specified. COM_endBlock() '''must''' be called once for each call to COM_startBlock(), and its only optional parameter is template, the default being blockfooter.thtml.<br />
<br />
Blocks can be nested inside of each other, which is obvious by simply looking at just about any geeklog website. <br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner block"<br />
. COM_startBlock("Inner Block")<br />
. "This text should be inside the inner block"<br />
. COM_endBlock()<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
When using nested blocks inside of HTML tables, one simply has to be certain to call COM_endBlock() in the right place since these functions output HTML tables as well, and otherwise the display may not render properly. COM_startBlock() and COM_endBlock() are used like COM_siteHeader() and COM_siteFooter() but for internal page elements.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner blocks"<br />
. "<table align=center width=100% border=0>"<br />
. "<tr><td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the left inner block"<br />
. COM_endBlock()<br />
. "</td>"<br />
. "<td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the right inner block"<br />
. COM_endBlock()<br />
. "</td></tr></table>"<br />
. "This text should be below the inner blocks but inside the outer block"<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
The great advantage of using these two functions is that whenever the site admin or user changes their Geeklog theme, your GUI will change to match. Your program will always retain the same look-and-feel of the site in general.<br />
<br />
There are also some useful HTML form functions found in lib-common.php which come in very handy and make life a bit easier. <pre>COM_optionList( $table, $selection, $selected='', $sortcol=1 )</pre>. This creates an HTML "<option" list generated from the given database table, using the passed variable "$selected" in the SELECT statement of the HTML query. See source code for a better idea of what the function does, but it is very useful.<br />
<br />
In a similar vein, <pre>COM_checkList( $table, $selection, $where='', $selected='' )</pre> creates a list of check boxes from the given database table, with the given select and where clauses being passed to the SQL statement inside the function.<br />
<br />
One more useful function is the <pre>COM_errorLog( $logentry, $actionid = '')</pre> function which logs to the Geeklog logfile if $actionid is 1, or to the screen if it is set to 2.<br />
<br />
<pre>COM_checkWords( $Message )</pre> gives you access to Geeklog's (somewhat rudimentary) profanity filter. We find it to be not terribly useful since if you include for example the word "cock" in your filter, you will also filter out the completely innocuous word "peacock". If you nonetheless want to use the geeklog profanity filter, simply do this:<br />
<br />
<pre><br />
$text = COM_checkWords( $text );<br />
</pre><br />
<br />
<pre>COM_mail( $to, $subject, $message, $from = '', $html = false, $priority = 0 )</pre> does exactly what the name suggests and lets you send mail to someone.<br />
<br />
There are far too many functions in lib-common.php to discuss here, so we'll end off with two very important ones which can be used for accessing query-string variables. What's a query string? If you have a URL like this:<br />
<br />
<pre><br />
http://www.example.com/someprogram.php?variable=value&othervariable=othervalue<br />
</pre><br />
<br />
The query string is the part after the question mark - the stuff you pass into your program. In this example, inside the text of someprogram.php, if the PHP installation has "register_globals" turned on, the variable "$variable" will automagically exist in the program and will have the value "value". But there are certain security problems with using "register_globals" in PHP so a lot of people do not like to have it turned on. Unfortunately Geeklog requires that it be turned on (at least for now until the programmers get it rewritten to eliminate the need), so to mitigate the risks involved you can use special functions to obtain your query string variables.<br />
<br />
Near the top of your program simply insert something like the following, first to define which are the only global variables your program expects to see, then finally to safely obtain the value of those variables:<br />
<pre><br />
COM_setArgNames(array('variable','othervariable'));<br />
$variable = COM_getArgument('variable');<br />
$othervariable = COM_getArgument('othervariable');<br />
</pre><br />
<br />
==Using the Database==<br />
<br />
Geeklog has a database abstraction layer which in theory makes it possible for you to use any database as the backend for it. Though in practice the Geeklog team has only implemented a backend for the popular [http://www.mysql.com MySQL] database. In any case, when programming Geeklog you do not use the regular PHP database functions - instead you use the DB_ functions which behave almost idenically to the PHP functions that have similar names.<br />
<br />
Another important thing to note about Geeklog is that you should never use table names directly in your queries. Instead, you should use the $_TABLES global variable, and add your own table names to it if you make your own tables. The reason for this is simply that Geeklog allows the installer to specify a "table prefix", so if you use table names directly your code will not run on another Geeklog installation that uses a different table prefix. Even if you think you'll never want to run your code on another Geeklog installation, we recommend you do things properly because you never do know. I've written code that I thought would never have to run in another installation, and sure enough 2 years later I have to go back and convert it all to use the $_TABLES array because now I do indeed want to run it on another installation that is using a different table prefix.<br />
<br />
One final very important thing to state about using the Geeklog database is that '''under no circumstances whatsoever should you ever alter the default Geeklog tables'''. One example of where you might be tempted to do this is if you want to track a specific option for users - you may be tempted to add a field or two to the Geeklog "users" table. Say for example you are writing a program "buysell" which allows users to enter items into the database to put them up for sale to other users. And when browsing the database to see what is for sale, you want each user to decide whether or not they want to see their own items. You may be tempted to add a boolean field "seeown" to the Geeklog users table, but don't do it! Instead, create your own table "buysell_userprefs" and add whatever fields you require to this new table. At very least we need a field for the userid - so we'll call it "bsp_uid", and we need a field for "see your own items" so we'll call it "bsp_seeown".<br />
<br />
In general we like to give table fields names that have an abbreviation of the table name at the beginning of every field. So in our case this is a table which contains "buy sell preferences" for each user, so we'll name all the fields "bsp_". This is optional, but we've found it to be a good practice so that you do not end up with fields from various tables with the same name - something that can under circumstances cause problems in your queries, or unexpected results.<br />
<br />
===Adding to $_TABLES===<br />
As already mentioned, if you define your own tables, you have to add them to the global $_TABLES variable. <br />
<br />
<pre><br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
</pre><br />
<br />
Note that we've included the Geeklog global variable for table prefix, so that our code will work in all Geeklog installations. And of course you need one line for every table you are adding to the Geeklog database. And finally, like any global variable in Geelog you must declare it global in a function if you want to use it in that function.<br />
<br />
When doing a plugin you usually put this into the config.php for your plugin. If not doing a plugin you have several options on where to put it, depending upon how you have your code organised. If you have one big file, then put it at the top of that file. If you have an include file that gets included by all the programs you are writing, put it there. Basically you have to put it whereever you can that will ensure it gets executed by all of your programs and is visible by all of your programs.<br />
<br />
===Get on with it!===<br />
And finally we can show you how to put it all together. Let's write a simple little program that does nothing more than show you what your 'bsp_seeown' preference is set to. <br />
<br />
<pre><br />
<?php<br />
require_once('lib-common.php');<br />
<br />
$display = COM_siteHeader(); <br />
<br />
if ( $_USER['uid'] < 2 ) {<br />
$display .= "You are not logged in";<br />
$display .= COM_siteFooter();<br />
echo $display;<br />
exit;<br />
}<br />
<br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
<br />
$sql = "SELECT bsp_seeown FROM {$_TABLES['buysell_userprefs']} "<br />
. " WHERE {$_TABLES['buysell_userprefs']}.bsp_uid = {$_USER['uid']} ";<br />
$result = DB_query( $sql );<br />
if ( ! $result ) {<br />
// some error condition and possibly exit<br />
}<br />
if ( DB_numRows( $result ) <> 1 ) {<br />
// there should be precisely one entry for each user<br />
// otherwise you may want to flag an error condition<br />
// or you may want to alternately check to see if this<br />
// value is less than 1 first, in which case they user<br />
// has not yet set their preferences<br />
}<br />
$bsp = DB_fetchArray( $result );<br />
if ( ! $bsp ) {<br />
// some error condition<br />
}<br />
<br />
$display .= COM_startBlock("Your Preference is") <br />
. $bsp['bsp_seeown']<br />
. COM_endBlock(); <br />
<br />
$display .= COM_siteFooter(); <br />
<br />
echo $display; <br />
<br />
?><br />
</pre><br />
<br />
Wow! There's lots going on in this program! A lot more than what we might have expected! First and foremost note that there are lots of potential error conditions to check for, when using the Geeklog database. This is no different from just programming MySQL with the normal PHP functions, actually. It is always a best practice to check for error conditions and react accordingly.<br />
<br />
Before we looked up the user's preference, we of course first did a check to make sure they were logged on, and if not we exited. Then, you can see how we added our table to the $_TABLES global variable, and then inside of the SELECT statement used the $_TABLES variable to ensure our code is portable. If you wanted to move this to another system you do not have to change a thing!<br />
<br />
As for the specific DB_ functions we used, they behave in the same way as the PHP MySQL functions with similar names. If you aren't familiar with how they work, check the Geeklog source code as well as the PHP manual. For a full listing of all the DB_ functions available to you, check out /path/to/geeklog/system/lib-database.php<br />
<br />
==Defining Functions==<br />
Defining functions in Geeklog is of course no different from doing so in PHP. Though there are a few lessons to be learned from the Geeklog coding style. One handy thing to do is pick a 3 to 5 character prefix for all of your functions. This will help prevent you and some other developer from walking on each others toes and writing plugins or other Geeklog programs which are incompatible with each other. For example in my User Pages Plugin I chose the prefix "UPAGE_" for every one of my own functions.<br />
<br />
Figuring out how to do return codes from functions is never easy in Geeklog or PHP in general. Many functions will return strings of HTML formatted text, and so returning error conditions is not easy. There is no one solution for every circumstance - though I've found 2 solutions work most of the time. If you hit an error condition in your function you can either return a NULL string so the caller can check for NULL string, or you can just return a string with an error message about the problem encountered, in which case the caller will not really know something went wrong - which may or may not matter. It depends on your caller.<br />
<br />
Let's have a look at a couple of functions for making HTML select boxes out of the database. The first function is essentially the same as the Geeklog function COM_optionList although it does get called with different parameters, and the Geeklog function is a bit more powerful. <br />
<br />
One thing you will see first off in the below function definition is that PHP gives you a means to specify default values for function parameters. This means that when calling the function, only the first two parameters "myName" and "myOptions" need be specified. myName is the name this element will have (variable name) and myOptions is a list of options separated by the "mySep" character which by default is "|".<br />
<br />
An important aspect to understand about default values for parameters is that you can only allow the X right-most parameters to have a default value. That is to say you cannot specify a default value for the 1st parameter, then none for the 2nd, then one for the 3rd and so on. The first zero or more parameters will have no default, then after the first one that has a default value all the rest must also have default values. And also when calling the function that we have below, if I wanted to override the default for "mySep" for example by passing a value in, then I also have to override the defaults for every parameter to the left of it - so I must also specify overrides for "myDefault", "myMulti" and "mySize".<br />
<br />
So I could call the function in any of the following ways :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0 );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0, 1 );<br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", "one", 0, 1, ":" );<br />
</pre><br />
<br />
and so on. But I could not do this if all I wanted to specify was "mySep" :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", ,,, ":" );<br />
</pre><br />
<br />
So the moral of the story is that if you are having parameters with default values you have to give some consideration to the order of the arguments. You want the ones least likey to be overridden to be the right-most, and the ones most likely to be overridden to be left-most.<br />
<br />
<pre><br />
function SSM_inputSelect( $myName, $myOptions, $myDefault="", $myMulti=0,<br />
$mySize=1, $mySep="|", $visible=true )<br />
{<br />
$retval .= ""<br />
. "<SELECT size=\"" . $mySize . "\" name=\"" . $myName . "\"";<br />
<br />
$retval .= ($myMulti == 0) ? ">" : " multiple>";<br />
<br />
$arrayOptions = explode($mySep,$myOptions);<br />
<br />
foreach ($arrayOptions as $oneOption) {<br />
$oneOption = trim($oneOption);<br />
if ( $myMulti == 0 )<br />
if ( $oneOption == $myDefault )<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
else<br />
if ( in_array( $oneOption, $myDefault ))<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
}<br />
<br />
$retval .= ""<br />
. "</SELECT>"<br />
. "";<br />
<br />
return $retval;<br />
<br />
}<br />
</pre><br />
<br />
Now let's have a look at another function which builds on the above by allowing us to pull stuff out of the database and present it in an options list.<br />
<br />
<pre><br />
function SSM_inputSelectDBField( $myName, $myTable, $myField, $myDefault="",<br />
$mySize=1, $myMulti=0, $extra="", $mySep="|" )<br />
{<br />
// select distinct entries from the given field of given table<br />
$sql = "SELECT DISTINCT " . $myField . " FROM " . $myTable<br />
. " ORDER BY " . $myField;<br />
<br />
// allows us to add an extra entry that was not in the DB<br />
<br />
if ( $extra != "" )<br />
$myOpts = $extra . $myOpts;<br />
<br />
$result = DB_query($sql);<br />
<br />
// format the data as required by SSM_inputSelect()<br />
<br />
while ( $R = DB_fetchArray( $result ) )<br />
if ( $myOpts == "" )<br />
$myOpts .= $R[$myField];<br />
else<br />
$myOpts .= $mySep . $R[$myField];<br />
<br />
// now call the guy doing the actual work<br />
<br />
$retstr .= SSM_inputSelect( $myName, $myOpts, $myDefault, $myMulti,<br />
$mySize, $mySep );<br />
<br />
return $retstr;<br />
}<br />
<br />
</pre><br />
<br />
And finally here is a similar function which once again builds upon "SSM_inputSelect" but this time it takes an field of type ENUM and builds a SELECT box out of all the possible preset values of the ENUM.<br />
<br />
<pre><br />
// Does not yet allow multi select but should be rewritten to do this<br />
<br />
function SSM_inputEnumDBField( $myName, $myTable, $myField, $myDefault="", $visible=true )<br />
{<br />
// query the DB to extract the enum values<br />
$qqq = "DESCRIBE $myTable $myField";<br />
$result = DB_query( $qqq );<br />
$arow = DB_fetchArray( $result );<br />
$myArr = explode( ",", trim( strstr( $arow['Type'], "(" ), "()")) ;<br />
<br />
// now format the values as required by SSM_inputSelect()<br />
$idx=0;<br />
$cnt = count($myArr);<br />
while($idx<$cnt)<br />
{<br />
$myArr[$idx] = trim( $myArr[$idx], "'" );<br />
$idx++;<br />
}<br />
sort( $myArr );<br />
$myList = implode( "|", $myArr );<br />
<br />
// now call our workhorse<br />
<br />
return SSM_inputSelect( $myName, $myList, $myDefault );<br />
}<br />
</pre><br />
<br />
The lesson here is that your functions should be well-defined and reusable. Here we could have written 2 different functions which have nothing to do with each other, but instead we wrote a 3rd base function first which the other 2 rely on to get the job done. Now if there is some substantial change in how I want the SELECT boxes drawn, I only have to make the change in one place.<br />
<br />
==Support and Such==<br />
<br />
The best place for Geeklog support is of course [http://www.geeklog.net the main Geeklog site]. But there are a few other great places to check including [http://www.squatty.com Squatty] and [http://www.portalparts.com Portal Parts]. Squatty and Blaine are hard-core Geeklog developers and are responsible for several popular themes, plugins and hacks.<br />
<br />
If you want to report a bug or request a feature, set yourself up an account [http://project.geeklog.net/tracking/signup_page.php here] and do so. If they don't know it is broken, the cannot fix it. I've reported several bugs and have had them fixed prompty. I've also tracked down and fixed several bugs and simply submitted the code which was accepted. And I've also requested several features which have been added over the years at my request. The Geeklog development team is small, but very dedicated and they love to get feedback from the user base.<br />
<br />
<br />
[[Category:Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Beginner%27s_Guide_to_Programming&diff=5801Beginner's Guide to Programming2010-04-14T18:02:39Z<p>Rasade: /* Where to put it */ -added usage of COM_output()</p>
<hr />
<div>By [[User:Amckay|Alan McKay]]<br />
<br />
[http://www.geeklog.net Geeklog] is a powerful weblog (blog) content management system (CMS) which is written in the popular programming language [http://www.php.net PHP], and uses the popular [http://www.mysql.com MySQL] database. While Geeklog is powerful enough that many users will not have a need to write their own applications for it, it is flexible enough to allow those who do require extra functionality to do so easily. These people write their programs in PHP, with some minor restrictions and using the Geeklog function library.<br />
<br />
<br />
==Hello, World==<br />
<br />
The first program you write in any computer language is "Hello World", and here it is in Geeklog. This is saved in a file "hello.php" in Geeklog's "public_html" directory, and so is surfable at http://www.example.com/hello.php<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
$display = COM_siteHeader();<br />
$display .= "Hello World";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
There are a few important things to be noted from the given program.<br />
<br />
* geeklog programs stay in PHP mode - there is no flipping back and forth between PHP and HTML as is possible in the PHP language. This means that all programs have "<?php" on the first line, and "?>" on the last line, and everything in between is PHP code.<br />
* the HTML is generated using a single function, COM_output() as on the last line of the above code. Although it is possible to use a simple 'echo' statement to spit out the HTML, newer versions of Geeklog use the COM_output() function. COM_output() takes the $display variable to which all your HTML output is appended throughout the code (note the ".=" which is used for the append) as an argument and simply echoes it out in this case. (Newer versions of Geeklog use the COM_output() function to allow for any output that may use compression. More on that later)<br />
* "lib-common.php" is the single file which must be included in all your Geeklog programs. It includes everything else you need unless you decide to make your own include files, so it's one-stop shopping.<br />
* there are functions in Geeklog to do lots of stuff for you - like for example COM_siteHeader() and COM_siteFooter(). In general Geeklog functions start with 2 or 3 capital letters and an understore - this tells you what type of function it is. Then the name of the function. COM_ functions are "common" functions - not a terribly meaningful name. But SEC_ functions like SEC_inGroup() which allows you to test if a user is in a particular group, allow your programs to access some of the powerful security features of Geeklog. And DB_ commands allow you to access the Geeklog database.<br />
<br />
==Security==<br />
Speaking of the powerful Geeklog security model (one of the key reasons I originally chose Geeklog for my sites), let's alter the hello world program such that any user in the "geeker" user group will see the "hello world" message, but anyone not in that group (which includes users not logged in) will get a "permission denied" error.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( SEC_inGroup( 'geeker' ) )<br />
$display .= "Hello World";<br />
else<br />
$display .= "Access Denied";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
To check out the full range of security functions available to you, and how to use them, read the /path/to/geeklog/system/lib-security.php file, which is where they are implemented. <br />
<br />
Though the above code format is a bit clunky and not terribly useful, so let's make another change which shows us how most programs deal with group permissions issues.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( ! SEC_inGroup( 'geeker' ) )<br />
{<br />
$display .= "Access Denied";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
exit;<br />
}<br />
<br />
$display .= "Hello World";<br />
<br />
// do some other stuff here<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
The big difference in this version of the program is that right at the top of the program we test for group permissions, and if the user does not have them we display the site footer, then exit. So a user not in the 'geeker' group will end right there and never see what the rest of the program does. Very simple, but very powerful! This is precisely how you control access to your pages in Geeklog!<br />
<br />
==Where to put it==<br />
<br />
If you are only writing a small program, then sticking a single file in the public_html directory as shown above will work fine. As soon as you get to the point, however, when you start having your own include files and so on, you probably want to create a directory for it. In our case we could create a directory in "public_html" called "hello", and then create a file "index.php" with the above program. This will require a very minor change to the original program - see if you can pick it out before peeking :<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( '../lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= "Hello World";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
Yup, that's right, we had to add "../" to the "lib-common.php" in the "require_once" (which BTW is a type of "include" in PHP). The reason is simple: lib-common.php lives in public_html, and our first program was in that directory as well. This new program is in a subdirectory of public_html, so we have to go to the parent directory to get our include file.<br />
<br />
If you want to keep your geeklog installation "pure", as I usually do, you can also put your program directory somewhere outside of the geeklog directory, and use directives for your webserver to map that directory into the web space of your geeklog installation. That's easier than it sounds - with Apache just use the "Alias" directive in your apache config file, like this :<br />
<br />
<pre><br />
Alias /hello/ "/path/to/your/hello/"<br />
</pre><br />
<br />
Of course, this means that your require_once statement will have to contain the full path to lib-common.php <br />
<br />
<pre><br />
require_once( '/path/to/geeklog/public_html/lib-common.php' );<br />
</pre><br />
<br />
==To Plug it in, or not==<br />
<br />
This is a bit of an advanced topic which in some ways is out of place at this point, but just about everyone who knows Geeklog and has used it a bit, knows about [[Plugin Developers Handbook|Geeklog plugins]]. And when writing your own Geeklog programs, this will obviously be something in your mind. Not all Geeklog programs are plugins - and the above examples are not. Plugins involve writing your program in a specific way, and defining specific functions which Geeklog will expect to find. It also involves making some entries in the Geeklog database to let Geeklog know that your plugin is there.<br />
<br />
In general if you want to use the Geeklog comment engine, the Geeklog search engine (i.e. integrate your program data into the search feature of Geeklog), or the Geeklog submission engine, you must write a plugin. Otherwise you can just write code. Size doesn't matter. There is no limit after which you have to make it a plugin.<br />
<br />
==Some Odds and Ends==<br />
<br />
A couple of more quick points on some basic Geeklog stuff<br />
<br />
* the $_USER array comes pre-populated for you by Geeklog. If $_USER['uid'] is greater than 1, then you know your user is logged on. Otherwise they are anonymous. So in the above example if you wanted to test for "logged on user" rather than "member of geeker group", just change the 'if' statement accordingly. This array contains all of the user table from geeklog, the next most useful subscript being $_USER['username']<br />
* the $_CONF array contains everything you set in your config.php, if you need it. Just check config.php for what all is there.<br />
* both of the above arrays are global, and as such if you use them in a function you must declare them in the function with the 'global' directive, as is normal for PHP. Outside of any function in the main body of the program they can be just used.<br />
<br />
==Functions, Bring Me Functions!==<br />
<br />
We've already seen two of the most widely used functions that Geeklog has to offer - COM_siteHeader() and COM_siteFooter(). It is important to note with this that there are optional parameters you can pass to each of them to achieve certain results. COM_siteHeader() displays the header and the left blocks, while its partner controls the footer and the right blocks. By default COM_siteHeader() displays the left blocks, and by default COM_siteFooter() does not display the right blocks. Check the source code in lib-common.php for details on how to change this behavior.<br />
<br />
Another set of similar workhorse functions which are also defined in lib-common.php along with the aforementioned functions are COM_startBlock() and COM_endBlock(). COM_startBlock() accepts 3 optional parameters: title, helpfile and template. The most useful and almost always used is title, which is a text string which will appear in the title bar of the block. If a helpfile is specified, Geeklog will display the help question mark icon and link to a help file for that block. And by default the 'blockheader.thtml' template is used unless another is specified. COM_endBlock() '''must''' be called once for each call to COM_startBlock(), and its only optional parameter is template, the default being blockfooter.thtml.<br />
<br />
Blocks can be nested inside of each other, which is obvious by simply looking at just about any geeklog website. <br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner block"<br />
. COM_startBlock("Inner Block")<br />
. "This text should be inside the inner block"<br />
. COM_endBlock()<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
When using nested blocks inside of HTML tables, one simply has to be certain to call COM_endBlock() in the right place since these functions output HTML tables as well, and otherwise the display may not render properly.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner blocks"<br />
. "<table align=center width=100% border=0>"<br />
. "<tr><td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the left inner block"<br />
. COM_endBlock()<br />
. "</td>"<br />
. "<td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the right inner block"<br />
. COM_endBlock()<br />
. "</td></tr></table>"<br />
. "This text should be below the inner blocks but inside the outer block"<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
The great advantage of using these two functions is that whenever the site admin or user changes their Geeklog theme, your GUI will change to match. Your program will always retain the same look-and-feel of the site in general.<br />
<br />
There are also some useful HTML form functions found in lib-common.php which come in very handy and make life a bit easier. <pre>COM_optionList( $table, $selection, $selected='', $sortcol=1 )</pre>. This creates an HTML "<option" list generated from the given table, using the passed variable "$selected" in the SELECT statement of the HTML query. See source code for a better idea of what the function does, but it is very useful.<br />
<br />
In a similar vein, <pre>COM_checkList( $table, $selection, $where='', $selected='' )</pre> creates a list of check boxes from the given table, with the given select and where clauses being passed to the SQL statement inside the function.<br />
<br />
One more useful function is the <pre>COM_errorLog( $logentry, $actionid = '')</pre> function which logs to the Geeklog logfile if $actionid is 1, or to the screen if it is set to 2.<br />
<br />
<pre>COM_checkWords( $Message )</pre> gives you access to Geeklog's (somewhat rudementary) profanity filter. We find it to be not terribly useful since if you include for example the word "cock" in your filter, you will also filter out the completely innocuous word "peacock". If you nonetheless want to use the geeklog profanity filter, simply do this:<br />
<br />
<pre><br />
$text = COM_checkWords( $text )<br />
</pre><br />
<br />
<pre>COM_mail( $to, $subject, $message, $from = '', $html = false, $priority = 0 )</pre> does exactly what the name suggests and lets you send mail to someone.<br />
<br />
There are far too many functions in lib-common.php to discuss here, so we'll end off with two very important ones which can be used for accessing query-string variables. What's a query string? If you have a URL like this:<br />
<br />
<pre><br />
http://www.example.com/someprogram.php?variable=value&othervariable=othervalue<br />
</pre><br />
<br />
The query string is the part after the question mark - the stuff you pass into your program. In this example, inside the text of someprogram.php, if the PHP installation has "register_globals" turned on, the variable "$variable" will automagically exist in the program and will have the value "value". But there are certain security problems with using "register_globals" in PHP so a lot of people do not like to have it turned on. Unfortunately Geeklog requires that it be turned on (at least for now until the programmers get it rewritten to eliminate the need), so to mitigate the risks involved you can use special functions to obtain your query string variables.<br />
<br />
Near the top of your program simply insert something like the following, first to define which are the only global variables your program expects to see, then finally to safely obtain the value of those variables:<br />
<pre><br />
COM_setArgNames(array('variable','othervariable'));<br />
$variable = COM_getArgument('variable');<br />
$othervariable = COM_getArgument('othervariable');<br />
</pre><br />
<br />
==Using the Database==<br />
<br />
Geeklog has a database abstraction layer which in theory makes it possible for you to use any database as the backend for it. Though in practice the Geeklog team has only implemented a backend for the popular [http://www.mysql.com MySQL] database. In any case, when programming Geeklog you do not use the regular PHP database functions - instead you use the DB_ functions which behave almost idenically to the PHP functions that have similar names.<br />
<br />
Another important thing to note about Geeklog is that you should never use table names directly in your queries. Instead, you should use the $_TABLES global variable, and add your own table names to it if you make your own tables. The reason for this is simply that Geeklog allows the installer to specify a "table prefix", so if you use table names directly your code will not run on another Geeklog installation that uses a different table prefix. Even if you think you'll never want to run your code on another Geeklog installation, we recommend you do things properly because you never do know. I've written code that I thought would never have to run in another installation, and sure enough 2 years later I have to go back and convert it all to use the $_TABLES array because now I do indeed want to run it on another installation that is using a different table prefix.<br />
<br />
One final very important thing to state about using the Geeklog database is that '''under no circumstances whatsoever should you ever alter the default Geeklog tables'''. One example of where you might be tempted to do this is if you want to track a specific option for users - you may be tempted to add a field or two to the Geeklog "users" table. Say for example you are writing a program "buysell" which allows users to enter items into the database to put them up for sale to other users. And when browsing the database to see what is for sale, you want each user to decide whether or not they want to see their own items. You may be tempted to add a boolean field "seeown" to the Geeklog users table, but don't do it! Instead, create your own table "buysell_userprefs" and add whatever fields you require to this new table. At very least we need a field for the userid - so we'll call it "bsp_uid", and we need a field for "see your own items" so we'll call it "bsp_seeown".<br />
<br />
In general we like to give table fields names that have an abbreviation of the table name at the beginning of every field. So in our case this is a table which contains "buy sell preferences" for each user, so we'll name all the fields "bsp_". This is optional, but we've found it to be a good practice so that you do not end up with fields from various tables with the same name - something that can under circumstances cause problems in your queries, or unexpected results.<br />
<br />
===Adding to $_TABLES===<br />
As already mentioned, if you define your own tables, you have to add them to the global $_TABLES variable. <br />
<br />
<pre><br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
</pre><br />
<br />
Note that we've included the Geeklog global variable for table prefix, so that our code will work in all Geeklog installations. And of course you need one line for every table you are adding to the Geeklog database. And finally, like any global variable in Geelog you must declare it global in a function if you want to use it in that function.<br />
<br />
When doing a plugin you usually put this into the config.php for your plugin. If not doing a plugin you have several options on where to put it, depending upon how you have your code organised. If you have one big file, then put it at the top of that file. If you have an include file that gets included by all the programs you are writing, put it there. Basically you have to put it whereever you can that will ensure it gets executed by all of your programs and is visible by all of your programs.<br />
<br />
===Get on with it!===<br />
And finally we can show you how to put it all together. Let's write a simple little program that does nothing more than show you what your 'bsp_seeown' preference is set to. <br />
<br />
<pre><br />
<?php<br />
require_once('lib-common.php');<br />
<br />
$display = COM_siteHeader(); <br />
<br />
if ( $_USER['uid'] < 2 ) {<br />
$display .= "You are not logged in";<br />
$display .= COM_siteFooter();<br />
echo $display;<br />
exit;<br />
}<br />
<br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
<br />
$sql = "SELECT bsp_seeown FROM {$_TABLES['buysell_userprefs']} "<br />
. " WHERE {$_TABLES['buysell_userprefs']}.bsp_uid = {$_USER['uid']} ";<br />
$result = DB_query( $sql );<br />
if ( ! $result ) {<br />
// some error condition and possibly exit<br />
}<br />
if ( DB_numRows( $result ) <> 1 ) {<br />
// there should be precisely one entry for each user<br />
// otherwise you may want to flag an error condition<br />
// or you may want to alternately check to see if this<br />
// value is less than 1 first, in which case they user<br />
// has not yet set their preferences<br />
}<br />
$bsp = DB_fetchArray( $result );<br />
if ( ! $bsp ) {<br />
// some error condition<br />
}<br />
<br />
$display .= COM_startBlock("Your Preference is") <br />
. $bsp['bsp_seeown']<br />
. COM_endBlock(); <br />
<br />
$display .= COM_siteFooter(); <br />
<br />
echo $display; <br />
<br />
?><br />
</pre><br />
<br />
Wow! There's lots going on in this program! A lot more than what we might have expected! First and foremost note that there are lots of potential error conditions to check for, when using the Geeklog database. This is no different from just programming MySQL with the normal PHP functions, actually. It is always a best practice to check for error conditions and react accordingly.<br />
<br />
Before we looked up the user's preference, we of course first did a check to make sure they were logged on, and if not we exited. Then, you can see how we added our table to the $_TABLES global variable, and then inside of the SELECT statement used the $_TABLES variable to ensure our code is portable. If you wanted to move this to another system you do not have to change a thing!<br />
<br />
As for the specific DB_ functions we used, they behave in the same way as the PHP MySQL functions with similar names. If you aren't familiar with how they work, check the Geeklog source code as well as the PHP manual. For a full listing of all the DB_ functions available to you, check out /path/to/geeklog/system/lib-database.php<br />
<br />
==Defining Functions==<br />
Defining functions in Geeklog is of course no different from doing so in PHP. Though there are a few lessons to be learned from the Geeklog coding style. One handy thing to do is pick a 3 to 5 character prefix for all of your functions. This will help prevent you and some other developer from walking on each others toes and writing plugins or other Geeklog programs which are incompatible with each other. For example in my User Pages Plugin I chose the prefix "UPAGE_" for every one of my own functions.<br />
<br />
Figuring out how to do return codes from functions is never easy in Geeklog or PHP in general. Many functions will return strings of HTML formatted text, and so returning error conditions is not easy. There is no one solution for every circumstance - though I've found 2 solutions work most of the time. If you hit an error condition in your function you can either return a NULL string so the caller can check for NULL string, or you can just return a string with an error message about the problem encountered, in which case the caller will not really know something went wrong - which may or may not matter. It depends on your caller.<br />
<br />
Let's have a look at a couple of functions for making HTML select boxes out of the database. The first function is essentially the same as the Geeklog function COM_optionList although it does get called with different parameters, and the Geeklog function is a bit more powerful. <br />
<br />
One thing you will see first off in the below function definition is that PHP gives you a means to specify default values for function parameters. This means that when calling the function, only the first two parameters "myName" and "myOptions" need be specified. myName is the name this element will have (variable name) and myOptions is a list of options separated by the "mySep" character which by default is "|".<br />
<br />
An important aspect to understand about default values for parameters is that you can only allow the X right-most parameters to have a default value. That is to say you cannot specify a default value for the 1st parameter, then none for the 2nd, then one for the 3rd and so on. The first zero or more parameters will have no default, then after the first one that has a default value all the rest must also have default values. And also when calling the function that we have below, if I wanted to override the default for "mySep" for example by passing a value in, then I also have to override the defaults for every parameter to the left of it - so I must also specify overrides for "myDefault", "myMulti" and "mySize".<br />
<br />
So I could call the function in any of the following ways :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0 );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0, 1 );<br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", "one", 0, 1, ":" );<br />
</pre><br />
<br />
and so on. But I could not do this if all I wanted to specify was "mySep" :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", ,,, ":" );<br />
</pre><br />
<br />
So the moral of the story is that if you are having parameters with default values you have to give some consideration to the order of the arguments. You want the ones least likey to be overridden to be the right-most, and the ones most likely to be overridden to be left-most.<br />
<br />
<pre><br />
function SSM_inputSelect( $myName, $myOptions, $myDefault="", $myMulti=0,<br />
$mySize=1, $mySep="|", $visible=true )<br />
{<br />
$retval .= ""<br />
. "<SELECT size=\"" . $mySize . "\" name=\"" . $myName . "\"";<br />
<br />
$retval .= ($myMulti == 0) ? ">" : " multiple>";<br />
<br />
$arrayOptions = explode($mySep,$myOptions);<br />
<br />
foreach ($arrayOptions as $oneOption) {<br />
$oneOption = trim($oneOption);<br />
if ( $myMulti == 0 )<br />
if ( $oneOption == $myDefault )<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
else<br />
if ( in_array( $oneOption, $myDefault ))<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
}<br />
<br />
$retval .= ""<br />
. "</SELECT>"<br />
. "";<br />
<br />
return $retval;<br />
<br />
}<br />
</pre><br />
<br />
Now let's have a look at another function which builds on the above by allowing us to pull stuff out of the database and present it in an options list.<br />
<br />
<pre><br />
function SSM_inputSelectDBField( $myName, $myTable, $myField, $myDefault="",<br />
$mySize=1, $myMulti=0, $extra="", $mySep="|" )<br />
{<br />
// select distinct entries from the given field of given table<br />
$sql = "SELECT DISTINCT " . $myField . " FROM " . $myTable<br />
. " ORDER BY " . $myField;<br />
<br />
// allows us to add an extra entry that was not in the DB<br />
<br />
if ( $extra != "" )<br />
$myOpts = $extra . $myOpts;<br />
<br />
$result = DB_query($sql);<br />
<br />
// format the data as required by SSM_inputSelect()<br />
<br />
while ( $R = DB_fetchArray( $result ) )<br />
if ( $myOpts == "" )<br />
$myOpts .= $R[$myField];<br />
else<br />
$myOpts .= $mySep . $R[$myField];<br />
<br />
// now call the guy doing the actual work<br />
<br />
$retstr .= SSM_inputSelect( $myName, $myOpts, $myDefault, $myMulti,<br />
$mySize, $mySep );<br />
<br />
return $retstr;<br />
}<br />
<br />
</pre><br />
<br />
And finally here is a similar function which once again builds upon "SSM_inputSelect" but this time it takes an field of type ENUM and builds a SELECT box out of all the possible preset values of the ENUM.<br />
<br />
<pre><br />
// Does not yet allow multi select but should be rewritten to do this<br />
<br />
function SSM_inputEnumDBField( $myName, $myTable, $myField, $myDefault="", $visible=true )<br />
{<br />
// query the DB to extract the enum values<br />
$qqq = "DESCRIBE $myTable $myField";<br />
$result = DB_query( $qqq );<br />
$arow = DB_fetchArray( $result );<br />
$myArr = explode( ",", trim( strstr( $arow['Type'], "(" ), "()")) ;<br />
<br />
// now format the values as required by SSM_inputSelect()<br />
$idx=0;<br />
$cnt = count($myArr);<br />
while($idx<$cnt)<br />
{<br />
$myArr[$idx] = trim( $myArr[$idx], "'" );<br />
$idx++;<br />
}<br />
sort( $myArr );<br />
$myList = implode( "|", $myArr );<br />
<br />
// now call our workhorse<br />
<br />
return SSM_inputSelect( $myName, $myList, $myDefault );<br />
}<br />
</pre><br />
<br />
The lesson here is that your functions should be well-defined and reusable. Here we could have written 2 different functions which have nothing to do with each other, but instead we wrote a 3rd base function first which the other 2 rely on to get the job done. Now if there is some substantial change in how I want the SELECT boxes drawn, I only have to make the change in one place.<br />
<br />
==Support and Such==<br />
<br />
The best place for Geeklog support is of course [http://www.geeklog.net the main Geeklog site]. But there are a few other great places to check including [http://www.squatty.com Squatty] and [http://www.portalparts.com Portal Parts]. Squatty and Blaine are hard-core Geeklog developers and are responsible for several popular themes, plugins and hacks.<br />
<br />
If you want to report a bug or request a feature, set yourself up an account [http://project.geeklog.net/tracking/signup_page.php here] and do so. If they don't know it is broken, the cannot fix it. I've reported several bugs and have had them fixed prompty. I've also tracked down and fixed several bugs and simply submitted the code which was accepted. And I've also requested several features which have been added over the years at my request. The Geeklog development team is small, but very dedicated and they love to get feedback from the user base.<br />
<br />
<br />
[[Category:Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Beginner%27s_Guide_to_Programming&diff=5800Beginner's Guide to Programming2010-04-14T17:55:08Z<p>Rasade: /* Security */ - added COM_output() usage</p>
<hr />
<div>By [[User:Amckay|Alan McKay]]<br />
<br />
[http://www.geeklog.net Geeklog] is a powerful weblog (blog) content management system (CMS) which is written in the popular programming language [http://www.php.net PHP], and uses the popular [http://www.mysql.com MySQL] database. While Geeklog is powerful enough that many users will not have a need to write their own applications for it, it is flexible enough to allow those who do require extra functionality to do so easily. These people write their programs in PHP, with some minor restrictions and using the Geeklog function library.<br />
<br />
<br />
==Hello, World==<br />
<br />
The first program you write in any computer language is "Hello World", and here it is in Geeklog. This is saved in a file "hello.php" in Geeklog's "public_html" directory, and so is surfable at http://www.example.com/hello.php<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
$display = COM_siteHeader();<br />
$display .= "Hello World";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
There are a few important things to be noted from the given program.<br />
<br />
* geeklog programs stay in PHP mode - there is no flipping back and forth between PHP and HTML as is possible in the PHP language. This means that all programs have "<?php" on the first line, and "?>" on the last line, and everything in between is PHP code.<br />
* the HTML is generated using a single function, COM_output() as on the last line of the above code. Although it is possible to use a simple 'echo' statement to spit out the HTML, newer versions of Geeklog use the COM_output() function. COM_output() takes the $display variable to which all your HTML output is appended throughout the code (note the ".=" which is used for the append) as an argument and simply echoes it out in this case. (Newer versions of Geeklog use the COM_output() function to allow for any output that may use compression. More on that later)<br />
* "lib-common.php" is the single file which must be included in all your Geeklog programs. It includes everything else you need unless you decide to make your own include files, so it's one-stop shopping.<br />
* there are functions in Geeklog to do lots of stuff for you - like for example COM_siteHeader() and COM_siteFooter(). In general Geeklog functions start with 2 or 3 capital letters and an understore - this tells you what type of function it is. Then the name of the function. COM_ functions are "common" functions - not a terribly meaningful name. But SEC_ functions like SEC_inGroup() which allows you to test if a user is in a particular group, allow your programs to access some of the powerful security features of Geeklog. And DB_ commands allow you to access the Geeklog database.<br />
<br />
==Security==<br />
Speaking of the powerful Geeklog security model (one of the key reasons I originally chose Geeklog for my sites), let's alter the hello world program such that any user in the "geeker" user group will see the "hello world" message, but anyone not in that group (which includes users not logged in) will get a "permission denied" error.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( SEC_inGroup( 'geeker' ) )<br />
$display .= "Hello World";<br />
else<br />
$display .= "Access Denied";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
To check out the full range of security functions available to you, and how to use them, read the /path/to/geeklog/system/lib-security.php file, which is where they are implemented. <br />
<br />
Though the above code format is a bit clunky and not terribly useful, so let's make another change which shows us how most programs deal with group permissions issues.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( ! SEC_inGroup( 'geeker' ) )<br />
{<br />
$display .= "Access Denied";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
exit;<br />
}<br />
<br />
$display .= "Hello World";<br />
<br />
// do some other stuff here<br />
<br />
$display .= COM_siteFooter();<br />
<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
The big difference in this version of the program is that right at the top of the program we test for group permissions, and if the user does not have them we display the site footer, then exit. So a user not in the 'geeker' group will end right there and never see what the rest of the program does. Very simple, but very powerful! This is precisely how you control access to your pages in Geeklog!<br />
<br />
==Where to put it==<br />
<br />
If you are only writing a small program, then sticking a single file in the public_html directory as shown above will work fine. As soon as you get to the point, however, when you start having your own include files and so on, you probably want to create a directory for it. In our case we could create a directory in "public_html" called "hello", and then create a file "index.php" with the above program. This will require a very minor change to the original program - see if you can pick it out before peeking :<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( '../lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= "Hello World";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
Yup, that's right, we had to add "../" to the "lib-common.php" in the "require_once" (which BTW is a type of "include" in PHP). The reason is simple: lib-common.php lives in public_html, and our first program was in that directory as well. This new program is in a subdirectory of public_html, so we have to go to the parent directory to get our include file.<br />
<br />
If you want to keep your geeklog installation "pure", as I usually do, you can also put your program directory somewhere outside of the geeklog directory, and use directives for your webserver to map that directory into the web space of your geeklog installation. That's easier than it sounds - with Apache just use the "Alias" directive in your apache config file, like this :<br />
<br />
<pre><br />
Alias /hello/ "/path/to/your/hello/"<br />
</pre><br />
<br />
Of course, this means that your require_once statement will have to contain the full path to lib-common.php <br />
<br />
<pre><br />
require_once( '/path/to/geeklog/public_html/lib-common.php' );<br />
</pre><br />
<br />
==To Plug it in, or not==<br />
<br />
This is a bit of an advanced topic which in some ways is out of place at this point, but just about everyone who knows Geeklog and has used it a bit, knows about [[Plugin Developers Handbook|Geeklog plugins]]. And when writing your own Geeklog programs, this will obviously be something in your mind. Not all Geeklog programs are plugins - and the above examples are not. Plugins involve writing your program in a specific way, and defining specific functions which Geeklog will expect to find. It also involves making some entries in the Geeklog database to let Geeklog know that your plugin is there.<br />
<br />
In general if you want to use the Geeklog comment engine, the Geeklog search engine (i.e. integrate your program data into the search feature of Geeklog), or the Geeklog submission engine, you must write a plugin. Otherwise you can just write code. Size doesn't matter. There is no limit after which you have to make it a plugin.<br />
<br />
==Some Odds and Ends==<br />
<br />
A couple of more quick points on some basic Geeklog stuff<br />
<br />
* the $_USER array comes pre-populated for you by Geeklog. If $_USER['uid'] is greater than 1, then you know your user is logged on. Otherwise they are anonymous. So in the above example if you wanted to test for "logged on user" rather than "member of geeker group", just change the 'if' statement accordingly. This array contains all of the user table from geeklog, the next most useful subscript being $_USER['username']<br />
* the $_CONF array contains everything you set in your config.php, if you need it. Just check config.php for what all is there.<br />
* both of the above arrays are global, and as such if you use them in a function you must declare them in the function with the 'global' directive, as is normal for PHP. Outside of any function in the main body of the program they can be just used.<br />
<br />
==Functions, Bring Me Functions!==<br />
<br />
We've already seen two of the most widely used functions that Geeklog has to offer - COM_siteHeader() and COM_siteFooter(). It is important to note with this that there are optional parameters you can pass to each of them to achieve certain results. COM_siteHeader() displays the header and the left blocks, while its partner controls the footer and the right blocks. By default COM_siteHeader() displays the left blocks, and by default COM_siteFooter() does not display the right blocks. Check the source code in lib-common.php for details on how to change this behavior.<br />
<br />
Another set of similar workhorse functions which are also defined in lib-common.php along with the aforementioned functions are COM_startBlock() and COM_endBlock(). COM_startBlock() accepts 3 optional parameters: title, helpfile and template. The most useful and almost always used is title, which is a text string which will appear in the title bar of the block. If a helpfile is specified, Geeklog will display the help question mark icon and link to a help file for that block. And by default the 'blockheader.thtml' template is used unless another is specified. COM_endBlock() '''must''' be called once for each call to COM_startBlock(), and its only optional parameter is template, the default being blockfooter.thtml.<br />
<br />
Blocks can be nested inside of each other, which is obvious by simply looking at just about any geeklog website. <br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner block"<br />
. COM_startBlock("Inner Block")<br />
. "This text should be inside the inner block"<br />
. COM_endBlock()<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
When using nested blocks inside of HTML tables, one simply has to be certain to call COM_endBlock() in the right place since these functions output HTML tables as well, and otherwise the display may not render properly.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner blocks"<br />
. "<table align=center width=100% border=0>"<br />
. "<tr><td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the left inner block"<br />
. COM_endBlock()<br />
. "</td>"<br />
. "<td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the right inner block"<br />
. COM_endBlock()<br />
. "</td></tr></table>"<br />
. "This text should be below the inner blocks but inside the outer block"<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
The great advantage of using these two functions is that whenever the site admin or user changes their Geeklog theme, your GUI will change to match. Your program will always retain the same look-and-feel of the site in general.<br />
<br />
There are also some useful HTML form functions found in lib-common.php which come in very handy and make life a bit easier. <pre>COM_optionList( $table, $selection, $selected='', $sortcol=1 )</pre>. This creates an HTML "<option" list generated from the given table, using the passed variable "$selected" in the SELECT statement of the HTML query. See source code for a better idea of what the function does, but it is very useful.<br />
<br />
In a similar vein, <pre>COM_checkList( $table, $selection, $where='', $selected='' )</pre> creates a list of check boxes from the given table, with the given select and where clauses being passed to the SQL statement inside the function.<br />
<br />
One more useful function is the <pre>COM_errorLog( $logentry, $actionid = '')</pre> function which logs to the Geeklog logfile if $actionid is 1, or to the screen if it is set to 2.<br />
<br />
<pre>COM_checkWords( $Message )</pre> gives you access to Geeklog's (somewhat rudementary) profanity filter. We find it to be not terribly useful since if you include for example the word "cock" in your filter, you will also filter out the completely innocuous word "peacock". If you nonetheless want to use the geeklog profanity filter, simply do this:<br />
<br />
<pre><br />
$text = COM_checkWords( $text )<br />
</pre><br />
<br />
<pre>COM_mail( $to, $subject, $message, $from = '', $html = false, $priority = 0 )</pre> does exactly what the name suggests and lets you send mail to someone.<br />
<br />
There are far too many functions in lib-common.php to discuss here, so we'll end off with two very important ones which can be used for accessing query-string variables. What's a query string? If you have a URL like this:<br />
<br />
<pre><br />
http://www.example.com/someprogram.php?variable=value&othervariable=othervalue<br />
</pre><br />
<br />
The query string is the part after the question mark - the stuff you pass into your program. In this example, inside the text of someprogram.php, if the PHP installation has "register_globals" turned on, the variable "$variable" will automagically exist in the program and will have the value "value". But there are certain security problems with using "register_globals" in PHP so a lot of people do not like to have it turned on. Unfortunately Geeklog requires that it be turned on (at least for now until the programmers get it rewritten to eliminate the need), so to mitigate the risks involved you can use special functions to obtain your query string variables.<br />
<br />
Near the top of your program simply insert something like the following, first to define which are the only global variables your program expects to see, then finally to safely obtain the value of those variables:<br />
<pre><br />
COM_setArgNames(array('variable','othervariable'));<br />
$variable = COM_getArgument('variable');<br />
$othervariable = COM_getArgument('othervariable');<br />
</pre><br />
<br />
==Using the Database==<br />
<br />
Geeklog has a database abstraction layer which in theory makes it possible for you to use any database as the backend for it. Though in practice the Geeklog team has only implemented a backend for the popular [http://www.mysql.com MySQL] database. In any case, when programming Geeklog you do not use the regular PHP database functions - instead you use the DB_ functions which behave almost idenically to the PHP functions that have similar names.<br />
<br />
Another important thing to note about Geeklog is that you should never use table names directly in your queries. Instead, you should use the $_TABLES global variable, and add your own table names to it if you make your own tables. The reason for this is simply that Geeklog allows the installer to specify a "table prefix", so if you use table names directly your code will not run on another Geeklog installation that uses a different table prefix. Even if you think you'll never want to run your code on another Geeklog installation, we recommend you do things properly because you never do know. I've written code that I thought would never have to run in another installation, and sure enough 2 years later I have to go back and convert it all to use the $_TABLES array because now I do indeed want to run it on another installation that is using a different table prefix.<br />
<br />
One final very important thing to state about using the Geeklog database is that '''under no circumstances whatsoever should you ever alter the default Geeklog tables'''. One example of where you might be tempted to do this is if you want to track a specific option for users - you may be tempted to add a field or two to the Geeklog "users" table. Say for example you are writing a program "buysell" which allows users to enter items into the database to put them up for sale to other users. And when browsing the database to see what is for sale, you want each user to decide whether or not they want to see their own items. You may be tempted to add a boolean field "seeown" to the Geeklog users table, but don't do it! Instead, create your own table "buysell_userprefs" and add whatever fields you require to this new table. At very least we need a field for the userid - so we'll call it "bsp_uid", and we need a field for "see your own items" so we'll call it "bsp_seeown".<br />
<br />
In general we like to give table fields names that have an abbreviation of the table name at the beginning of every field. So in our case this is a table which contains "buy sell preferences" for each user, so we'll name all the fields "bsp_". This is optional, but we've found it to be a good practice so that you do not end up with fields from various tables with the same name - something that can under circumstances cause problems in your queries, or unexpected results.<br />
<br />
===Adding to $_TABLES===<br />
As already mentioned, if you define your own tables, you have to add them to the global $_TABLES variable. <br />
<br />
<pre><br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
</pre><br />
<br />
Note that we've included the Geeklog global variable for table prefix, so that our code will work in all Geeklog installations. And of course you need one line for every table you are adding to the Geeklog database. And finally, like any global variable in Geelog you must declare it global in a function if you want to use it in that function.<br />
<br />
When doing a plugin you usually put this into the config.php for your plugin. If not doing a plugin you have several options on where to put it, depending upon how you have your code organised. If you have one big file, then put it at the top of that file. If you have an include file that gets included by all the programs you are writing, put it there. Basically you have to put it whereever you can that will ensure it gets executed by all of your programs and is visible by all of your programs.<br />
<br />
===Get on with it!===<br />
And finally we can show you how to put it all together. Let's write a simple little program that does nothing more than show you what your 'bsp_seeown' preference is set to. <br />
<br />
<pre><br />
<?php<br />
require_once('lib-common.php');<br />
<br />
$display = COM_siteHeader(); <br />
<br />
if ( $_USER['uid'] < 2 ) {<br />
$display .= "You are not logged in";<br />
$display .= COM_siteFooter();<br />
echo $display;<br />
exit;<br />
}<br />
<br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
<br />
$sql = "SELECT bsp_seeown FROM {$_TABLES['buysell_userprefs']} "<br />
. " WHERE {$_TABLES['buysell_userprefs']}.bsp_uid = {$_USER['uid']} ";<br />
$result = DB_query( $sql );<br />
if ( ! $result ) {<br />
// some error condition and possibly exit<br />
}<br />
if ( DB_numRows( $result ) <> 1 ) {<br />
// there should be precisely one entry for each user<br />
// otherwise you may want to flag an error condition<br />
// or you may want to alternately check to see if this<br />
// value is less than 1 first, in which case they user<br />
// has not yet set their preferences<br />
}<br />
$bsp = DB_fetchArray( $result );<br />
if ( ! $bsp ) {<br />
// some error condition<br />
}<br />
<br />
$display .= COM_startBlock("Your Preference is") <br />
. $bsp['bsp_seeown']<br />
. COM_endBlock(); <br />
<br />
$display .= COM_siteFooter(); <br />
<br />
echo $display; <br />
<br />
?><br />
</pre><br />
<br />
Wow! There's lots going on in this program! A lot more than what we might have expected! First and foremost note that there are lots of potential error conditions to check for, when using the Geeklog database. This is no different from just programming MySQL with the normal PHP functions, actually. It is always a best practice to check for error conditions and react accordingly.<br />
<br />
Before we looked up the user's preference, we of course first did a check to make sure they were logged on, and if not we exited. Then, you can see how we added our table to the $_TABLES global variable, and then inside of the SELECT statement used the $_TABLES variable to ensure our code is portable. If you wanted to move this to another system you do not have to change a thing!<br />
<br />
As for the specific DB_ functions we used, they behave in the same way as the PHP MySQL functions with similar names. If you aren't familiar with how they work, check the Geeklog source code as well as the PHP manual. For a full listing of all the DB_ functions available to you, check out /path/to/geeklog/system/lib-database.php<br />
<br />
==Defining Functions==<br />
Defining functions in Geeklog is of course no different from doing so in PHP. Though there are a few lessons to be learned from the Geeklog coding style. One handy thing to do is pick a 3 to 5 character prefix for all of your functions. This will help prevent you and some other developer from walking on each others toes and writing plugins or other Geeklog programs which are incompatible with each other. For example in my User Pages Plugin I chose the prefix "UPAGE_" for every one of my own functions.<br />
<br />
Figuring out how to do return codes from functions is never easy in Geeklog or PHP in general. Many functions will return strings of HTML formatted text, and so returning error conditions is not easy. There is no one solution for every circumstance - though I've found 2 solutions work most of the time. If you hit an error condition in your function you can either return a NULL string so the caller can check for NULL string, or you can just return a string with an error message about the problem encountered, in which case the caller will not really know something went wrong - which may or may not matter. It depends on your caller.<br />
<br />
Let's have a look at a couple of functions for making HTML select boxes out of the database. The first function is essentially the same as the Geeklog function COM_optionList although it does get called with different parameters, and the Geeklog function is a bit more powerful. <br />
<br />
One thing you will see first off in the below function definition is that PHP gives you a means to specify default values for function parameters. This means that when calling the function, only the first two parameters "myName" and "myOptions" need be specified. myName is the name this element will have (variable name) and myOptions is a list of options separated by the "mySep" character which by default is "|".<br />
<br />
An important aspect to understand about default values for parameters is that you can only allow the X right-most parameters to have a default value. That is to say you cannot specify a default value for the 1st parameter, then none for the 2nd, then one for the 3rd and so on. The first zero or more parameters will have no default, then after the first one that has a default value all the rest must also have default values. And also when calling the function that we have below, if I wanted to override the default for "mySep" for example by passing a value in, then I also have to override the defaults for every parameter to the left of it - so I must also specify overrides for "myDefault", "myMulti" and "mySize".<br />
<br />
So I could call the function in any of the following ways :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0 );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0, 1 );<br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", "one", 0, 1, ":" );<br />
</pre><br />
<br />
and so on. But I could not do this if all I wanted to specify was "mySep" :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", ,,, ":" );<br />
</pre><br />
<br />
So the moral of the story is that if you are having parameters with default values you have to give some consideration to the order of the arguments. You want the ones least likey to be overridden to be the right-most, and the ones most likely to be overridden to be left-most.<br />
<br />
<pre><br />
function SSM_inputSelect( $myName, $myOptions, $myDefault="", $myMulti=0,<br />
$mySize=1, $mySep="|", $visible=true )<br />
{<br />
$retval .= ""<br />
. "<SELECT size=\"" . $mySize . "\" name=\"" . $myName . "\"";<br />
<br />
$retval .= ($myMulti == 0) ? ">" : " multiple>";<br />
<br />
$arrayOptions = explode($mySep,$myOptions);<br />
<br />
foreach ($arrayOptions as $oneOption) {<br />
$oneOption = trim($oneOption);<br />
if ( $myMulti == 0 )<br />
if ( $oneOption == $myDefault )<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
else<br />
if ( in_array( $oneOption, $myDefault ))<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
}<br />
<br />
$retval .= ""<br />
. "</SELECT>"<br />
. "";<br />
<br />
return $retval;<br />
<br />
}<br />
</pre><br />
<br />
Now let's have a look at another function which builds on the above by allowing us to pull stuff out of the database and present it in an options list.<br />
<br />
<pre><br />
function SSM_inputSelectDBField( $myName, $myTable, $myField, $myDefault="",<br />
$mySize=1, $myMulti=0, $extra="", $mySep="|" )<br />
{<br />
// select distinct entries from the given field of given table<br />
$sql = "SELECT DISTINCT " . $myField . " FROM " . $myTable<br />
. " ORDER BY " . $myField;<br />
<br />
// allows us to add an extra entry that was not in the DB<br />
<br />
if ( $extra != "" )<br />
$myOpts = $extra . $myOpts;<br />
<br />
$result = DB_query($sql);<br />
<br />
// format the data as required by SSM_inputSelect()<br />
<br />
while ( $R = DB_fetchArray( $result ) )<br />
if ( $myOpts == "" )<br />
$myOpts .= $R[$myField];<br />
else<br />
$myOpts .= $mySep . $R[$myField];<br />
<br />
// now call the guy doing the actual work<br />
<br />
$retstr .= SSM_inputSelect( $myName, $myOpts, $myDefault, $myMulti,<br />
$mySize, $mySep );<br />
<br />
return $retstr;<br />
}<br />
<br />
</pre><br />
<br />
And finally here is a similar function which once again builds upon "SSM_inputSelect" but this time it takes an field of type ENUM and builds a SELECT box out of all the possible preset values of the ENUM.<br />
<br />
<pre><br />
// Does not yet allow multi select but should be rewritten to do this<br />
<br />
function SSM_inputEnumDBField( $myName, $myTable, $myField, $myDefault="", $visible=true )<br />
{<br />
// query the DB to extract the enum values<br />
$qqq = "DESCRIBE $myTable $myField";<br />
$result = DB_query( $qqq );<br />
$arow = DB_fetchArray( $result );<br />
$myArr = explode( ",", trim( strstr( $arow['Type'], "(" ), "()")) ;<br />
<br />
// now format the values as required by SSM_inputSelect()<br />
$idx=0;<br />
$cnt = count($myArr);<br />
while($idx<$cnt)<br />
{<br />
$myArr[$idx] = trim( $myArr[$idx], "'" );<br />
$idx++;<br />
}<br />
sort( $myArr );<br />
$myList = implode( "|", $myArr );<br />
<br />
// now call our workhorse<br />
<br />
return SSM_inputSelect( $myName, $myList, $myDefault );<br />
}<br />
</pre><br />
<br />
The lesson here is that your functions should be well-defined and reusable. Here we could have written 2 different functions which have nothing to do with each other, but instead we wrote a 3rd base function first which the other 2 rely on to get the job done. Now if there is some substantial change in how I want the SELECT boxes drawn, I only have to make the change in one place.<br />
<br />
==Support and Such==<br />
<br />
The best place for Geeklog support is of course [http://www.geeklog.net the main Geeklog site]. But there are a few other great places to check including [http://www.squatty.com Squatty] and [http://www.portalparts.com Portal Parts]. Squatty and Blaine are hard-core Geeklog developers and are responsible for several popular themes, plugins and hacks.<br />
<br />
If you want to report a bug or request a feature, set yourself up an account [http://project.geeklog.net/tracking/signup_page.php here] and do so. If they don't know it is broken, the cannot fix it. I've reported several bugs and have had them fixed prompty. I've also tracked down and fixed several bugs and simply submitted the code which was accepted. And I've also requested several features which have been added over the years at my request. The Geeklog development team is small, but very dedicated and they love to get feedback from the user base.<br />
<br />
<br />
[[Category:Development]]</div>Rasadehttp://wiki.geeklog.net/index.php?title=Beginner%27s_Guide_to_Programming&diff=5799Beginner's Guide to Programming2010-04-14T17:46:24Z<p>Rasade: /* Hello, World */ - added usage of COM_output()</p>
<hr />
<div>By [[User:Amckay|Alan McKay]]<br />
<br />
[http://www.geeklog.net Geeklog] is a powerful weblog (blog) content management system (CMS) which is written in the popular programming language [http://www.php.net PHP], and uses the popular [http://www.mysql.com MySQL] database. While Geeklog is powerful enough that many users will not have a need to write their own applications for it, it is flexible enough to allow those who do require extra functionality to do so easily. These people write their programs in PHP, with some minor restrictions and using the Geeklog function library.<br />
<br />
<br />
==Hello, World==<br />
<br />
The first program you write in any computer language is "Hello World", and here it is in Geeklog. This is saved in a file "hello.php" in Geeklog's "public_html" directory, and so is surfable at http://www.example.com/hello.php<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
$display = COM_siteHeader();<br />
$display .= "Hello World";<br />
$display .= COM_siteFooter();<br />
COM_output($display);<br />
<br />
?><br />
</pre><br />
<br />
There are a few important things to be noted from the given program.<br />
<br />
* geeklog programs stay in PHP mode - there is no flipping back and forth between PHP and HTML as is possible in the PHP language. This means that all programs have "<?php" on the first line, and "?>" on the last line, and everything in between is PHP code.<br />
* the HTML is generated using a single function, COM_output() as on the last line of the above code. Although it is possible to use a simple 'echo' statement to spit out the HTML, newer versions of Geeklog use the COM_output() function. COM_output() takes the $display variable to which all your HTML output is appended throughout the code (note the ".=" which is used for the append) as an argument and simply echoes it out in this case. (Newer versions of Geeklog use the COM_output() function to allow for any output that may use compression. More on that later)<br />
* "lib-common.php" is the single file which must be included in all your Geeklog programs. It includes everything else you need unless you decide to make your own include files, so it's one-stop shopping.<br />
* there are functions in Geeklog to do lots of stuff for you - like for example COM_siteHeader() and COM_siteFooter(). In general Geeklog functions start with 2 or 3 capital letters and an understore - this tells you what type of function it is. Then the name of the function. COM_ functions are "common" functions - not a terribly meaningful name. But SEC_ functions like SEC_inGroup() which allows you to test if a user is in a particular group, allow your programs to access some of the powerful security features of Geeklog. And DB_ commands allow you to access the Geeklog database.<br />
<br />
==Security==<br />
Speaking of the powerful Geeklog security model (one of the key reasons I originally chose Geeklog for my sites), let's alter the hello world program such that any user in the "geeker" user group will see the "hello world" message, but anyone not in that group (which includes users not logged in) will get a "permission denied" error.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( SEC_inGroup( 'geeker' ) )<br />
$display .= "Hello World";<br />
else<br />
$display .= "Access Denied";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
To check out the full range of security functions available to you, and how to use them, read the /path/to/geeklog/system/lib-security.php file, which is where they are implemented. <br />
<br />
Though the above code format is a bit clunky and not terribly useful, so let's make another change which shows us how most programs deal with group permissions issues.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
if ( ! SEC_inGroup( 'geeker' ) )<br />
{<br />
$display .= "Access Denied";<br />
$display .= COM_siteFooter();<br />
echo $display;<br />
exit;<br />
}<br />
<br />
$display .= "Hello World";<br />
<br />
// do some other stuff here<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
The big difference in this version of the program is that right at the top of the program we test for group permissions, and if the user does not have them we display the site footer, then exit. So a user not in the 'geeker' group will end right there and never see what the rest of the program does. Very simple, but very powerful! This is precisely how you control access to your pages in Geeklog!<br />
<br />
==Where to put it==<br />
<br />
If you are only writing a small program, then sticking a single file in the public_html directory as shown above will work fine. As soon as you get to the point, however, when you start having your own include files and so on, you probably want to create a directory for it. In our case we could create a directory in "public_html" called "hello", and then create a file "index.php" with the above program. This will require a very minor change to the original program - see if you can pick it out before peeking :<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( '../lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= "Hello World";<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
Yup, that's right, we had to add "../" to the "lib-common.php" in the "require_once" (which BTW is a type of "include" in PHP). The reason is simple: lib-common.php lives in public_html, and our first program was in that directory as well. This new program is in a subdirectory of public_html, so we have to go to the parent directory to get our include file.<br />
<br />
If you want to keep your geeklog installation "pure", as I usually do, you can also put your program directory somewhere outside of the geeklog directory, and use directives for your webserver to map that directory into the web space of your geeklog installation. That's easier than it sounds - with Apache just use the "Alias" directive in your apache config file, like this :<br />
<br />
<pre><br />
Alias /hello/ "/path/to/your/hello/"<br />
</pre><br />
<br />
Of course, this means that your require_once statement will have to contain the full path to lib-common.php <br />
<br />
<pre><br />
require_once( '/path/to/geeklog/public_html/lib-common.php' );<br />
</pre><br />
<br />
==To Plug it in, or not==<br />
<br />
This is a bit of an advanced topic which in some ways is out of place at this point, but just about everyone who knows Geeklog and has used it a bit, knows about [[Plugin Developers Handbook|Geeklog plugins]]. And when writing your own Geeklog programs, this will obviously be something in your mind. Not all Geeklog programs are plugins - and the above examples are not. Plugins involve writing your program in a specific way, and defining specific functions which Geeklog will expect to find. It also involves making some entries in the Geeklog database to let Geeklog know that your plugin is there.<br />
<br />
In general if you want to use the Geeklog comment engine, the Geeklog search engine (i.e. integrate your program data into the search feature of Geeklog), or the Geeklog submission engine, you must write a plugin. Otherwise you can just write code. Size doesn't matter. There is no limit after which you have to make it a plugin.<br />
<br />
==Some Odds and Ends==<br />
<br />
A couple of more quick points on some basic Geeklog stuff<br />
<br />
* the $_USER array comes pre-populated for you by Geeklog. If $_USER['uid'] is greater than 1, then you know your user is logged on. Otherwise they are anonymous. So in the above example if you wanted to test for "logged on user" rather than "member of geeker group", just change the 'if' statement accordingly. This array contains all of the user table from geeklog, the next most useful subscript being $_USER['username']<br />
* the $_CONF array contains everything you set in your config.php, if you need it. Just check config.php for what all is there.<br />
* both of the above arrays are global, and as such if you use them in a function you must declare them in the function with the 'global' directive, as is normal for PHP. Outside of any function in the main body of the program they can be just used.<br />
<br />
==Functions, Bring Me Functions!==<br />
<br />
We've already seen two of the most widely used functions that Geeklog has to offer - COM_siteHeader() and COM_siteFooter(). It is important to note with this that there are optional parameters you can pass to each of them to achieve certain results. COM_siteHeader() displays the header and the left blocks, while its partner controls the footer and the right blocks. By default COM_siteHeader() displays the left blocks, and by default COM_siteFooter() does not display the right blocks. Check the source code in lib-common.php for details on how to change this behavior.<br />
<br />
Another set of similar workhorse functions which are also defined in lib-common.php along with the aforementioned functions are COM_startBlock() and COM_endBlock(). COM_startBlock() accepts 3 optional parameters: title, helpfile and template. The most useful and almost always used is title, which is a text string which will appear in the title bar of the block. If a helpfile is specified, Geeklog will display the help question mark icon and link to a help file for that block. And by default the 'blockheader.thtml' template is used unless another is specified. COM_endBlock() '''must''' be called once for each call to COM_startBlock(), and its only optional parameter is template, the default being blockfooter.thtml.<br />
<br />
Blocks can be nested inside of each other, which is obvious by simply looking at just about any geeklog website. <br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner block"<br />
. COM_startBlock("Inner Block")<br />
. "This text should be inside the inner block"<br />
. COM_endBlock()<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
When using nested blocks inside of HTML tables, one simply has to be certain to call COM_endBlock() in the right place since these functions output HTML tables as well, and otherwise the display may not render properly.<br />
<br />
<pre><br />
<?php<br />
<br />
require_once( 'lib-common.php' );<br />
<br />
$display = COM_siteHeader();<br />
<br />
$display .= COM_startBlock("Outer Block")<br />
. "This text should be inside the outer block but outside the inner blocks"<br />
. "<table align=center width=100% border=0>"<br />
. "<tr><td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the left inner block"<br />
. COM_endBlock()<br />
. "</td>"<br />
. "<td align=center width=50%>"<br />
. COM_startBlock("Left Inner Block")<br />
. "This text should be inside the right inner block"<br />
. COM_endBlock()<br />
. "</td></tr></table>"<br />
. "This text should be below the inner blocks but inside the outer block"<br />
. COM_endBlock();<br />
<br />
$display .= COM_siteFooter();<br />
<br />
echo $display;<br />
<br />
?><br />
</pre><br />
<br />
The great advantage of using these two functions is that whenever the site admin or user changes their Geeklog theme, your GUI will change to match. Your program will always retain the same look-and-feel of the site in general.<br />
<br />
There are also some useful HTML form functions found in lib-common.php which come in very handy and make life a bit easier. <pre>COM_optionList( $table, $selection, $selected='', $sortcol=1 )</pre>. This creates an HTML "<option" list generated from the given table, using the passed variable "$selected" in the SELECT statement of the HTML query. See source code for a better idea of what the function does, but it is very useful.<br />
<br />
In a similar vein, <pre>COM_checkList( $table, $selection, $where='', $selected='' )</pre> creates a list of check boxes from the given table, with the given select and where clauses being passed to the SQL statement inside the function.<br />
<br />
One more useful function is the <pre>COM_errorLog( $logentry, $actionid = '')</pre> function which logs to the Geeklog logfile if $actionid is 1, or to the screen if it is set to 2.<br />
<br />
<pre>COM_checkWords( $Message )</pre> gives you access to Geeklog's (somewhat rudementary) profanity filter. We find it to be not terribly useful since if you include for example the word "cock" in your filter, you will also filter out the completely innocuous word "peacock". If you nonetheless want to use the geeklog profanity filter, simply do this:<br />
<br />
<pre><br />
$text = COM_checkWords( $text )<br />
</pre><br />
<br />
<pre>COM_mail( $to, $subject, $message, $from = '', $html = false, $priority = 0 )</pre> does exactly what the name suggests and lets you send mail to someone.<br />
<br />
There are far too many functions in lib-common.php to discuss here, so we'll end off with two very important ones which can be used for accessing query-string variables. What's a query string? If you have a URL like this:<br />
<br />
<pre><br />
http://www.example.com/someprogram.php?variable=value&othervariable=othervalue<br />
</pre><br />
<br />
The query string is the part after the question mark - the stuff you pass into your program. In this example, inside the text of someprogram.php, if the PHP installation has "register_globals" turned on, the variable "$variable" will automagically exist in the program and will have the value "value". But there are certain security problems with using "register_globals" in PHP so a lot of people do not like to have it turned on. Unfortunately Geeklog requires that it be turned on (at least for now until the programmers get it rewritten to eliminate the need), so to mitigate the risks involved you can use special functions to obtain your query string variables.<br />
<br />
Near the top of your program simply insert something like the following, first to define which are the only global variables your program expects to see, then finally to safely obtain the value of those variables:<br />
<pre><br />
COM_setArgNames(array('variable','othervariable'));<br />
$variable = COM_getArgument('variable');<br />
$othervariable = COM_getArgument('othervariable');<br />
</pre><br />
<br />
==Using the Database==<br />
<br />
Geeklog has a database abstraction layer which in theory makes it possible for you to use any database as the backend for it. Though in practice the Geeklog team has only implemented a backend for the popular [http://www.mysql.com MySQL] database. In any case, when programming Geeklog you do not use the regular PHP database functions - instead you use the DB_ functions which behave almost idenically to the PHP functions that have similar names.<br />
<br />
Another important thing to note about Geeklog is that you should never use table names directly in your queries. Instead, you should use the $_TABLES global variable, and add your own table names to it if you make your own tables. The reason for this is simply that Geeklog allows the installer to specify a "table prefix", so if you use table names directly your code will not run on another Geeklog installation that uses a different table prefix. Even if you think you'll never want to run your code on another Geeklog installation, we recommend you do things properly because you never do know. I've written code that I thought would never have to run in another installation, and sure enough 2 years later I have to go back and convert it all to use the $_TABLES array because now I do indeed want to run it on another installation that is using a different table prefix.<br />
<br />
One final very important thing to state about using the Geeklog database is that '''under no circumstances whatsoever should you ever alter the default Geeklog tables'''. One example of where you might be tempted to do this is if you want to track a specific option for users - you may be tempted to add a field or two to the Geeklog "users" table. Say for example you are writing a program "buysell" which allows users to enter items into the database to put them up for sale to other users. And when browsing the database to see what is for sale, you want each user to decide whether or not they want to see their own items. You may be tempted to add a boolean field "seeown" to the Geeklog users table, but don't do it! Instead, create your own table "buysell_userprefs" and add whatever fields you require to this new table. At very least we need a field for the userid - so we'll call it "bsp_uid", and we need a field for "see your own items" so we'll call it "bsp_seeown".<br />
<br />
In general we like to give table fields names that have an abbreviation of the table name at the beginning of every field. So in our case this is a table which contains "buy sell preferences" for each user, so we'll name all the fields "bsp_". This is optional, but we've found it to be a good practice so that you do not end up with fields from various tables with the same name - something that can under circumstances cause problems in your queries, or unexpected results.<br />
<br />
===Adding to $_TABLES===<br />
As already mentioned, if you define your own tables, you have to add them to the global $_TABLES variable. <br />
<br />
<pre><br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
</pre><br />
<br />
Note that we've included the Geeklog global variable for table prefix, so that our code will work in all Geeklog installations. And of course you need one line for every table you are adding to the Geeklog database. And finally, like any global variable in Geelog you must declare it global in a function if you want to use it in that function.<br />
<br />
When doing a plugin you usually put this into the config.php for your plugin. If not doing a plugin you have several options on where to put it, depending upon how you have your code organised. If you have one big file, then put it at the top of that file. If you have an include file that gets included by all the programs you are writing, put it there. Basically you have to put it whereever you can that will ensure it gets executed by all of your programs and is visible by all of your programs.<br />
<br />
===Get on with it!===<br />
And finally we can show you how to put it all together. Let's write a simple little program that does nothing more than show you what your 'bsp_seeown' preference is set to. <br />
<br />
<pre><br />
<?php<br />
require_once('lib-common.php');<br />
<br />
$display = COM_siteHeader(); <br />
<br />
if ( $_USER['uid'] < 2 ) {<br />
$display .= "You are not logged in";<br />
$display .= COM_siteFooter();<br />
echo $display;<br />
exit;<br />
}<br />
<br />
$_TABLES['buysell_userprefs'] = $_DB_table_prefix . 'buysell_userprefs';<br />
<br />
$sql = "SELECT bsp_seeown FROM {$_TABLES['buysell_userprefs']} "<br />
. " WHERE {$_TABLES['buysell_userprefs']}.bsp_uid = {$_USER['uid']} ";<br />
$result = DB_query( $sql );<br />
if ( ! $result ) {<br />
// some error condition and possibly exit<br />
}<br />
if ( DB_numRows( $result ) <> 1 ) {<br />
// there should be precisely one entry for each user<br />
// otherwise you may want to flag an error condition<br />
// or you may want to alternately check to see if this<br />
// value is less than 1 first, in which case they user<br />
// has not yet set their preferences<br />
}<br />
$bsp = DB_fetchArray( $result );<br />
if ( ! $bsp ) {<br />
// some error condition<br />
}<br />
<br />
$display .= COM_startBlock("Your Preference is") <br />
. $bsp['bsp_seeown']<br />
. COM_endBlock(); <br />
<br />
$display .= COM_siteFooter(); <br />
<br />
echo $display; <br />
<br />
?><br />
</pre><br />
<br />
Wow! There's lots going on in this program! A lot more than what we might have expected! First and foremost note that there are lots of potential error conditions to check for, when using the Geeklog database. This is no different from just programming MySQL with the normal PHP functions, actually. It is always a best practice to check for error conditions and react accordingly.<br />
<br />
Before we looked up the user's preference, we of course first did a check to make sure they were logged on, and if not we exited. Then, you can see how we added our table to the $_TABLES global variable, and then inside of the SELECT statement used the $_TABLES variable to ensure our code is portable. If you wanted to move this to another system you do not have to change a thing!<br />
<br />
As for the specific DB_ functions we used, they behave in the same way as the PHP MySQL functions with similar names. If you aren't familiar with how they work, check the Geeklog source code as well as the PHP manual. For a full listing of all the DB_ functions available to you, check out /path/to/geeklog/system/lib-database.php<br />
<br />
==Defining Functions==<br />
Defining functions in Geeklog is of course no different from doing so in PHP. Though there are a few lessons to be learned from the Geeklog coding style. One handy thing to do is pick a 3 to 5 character prefix for all of your functions. This will help prevent you and some other developer from walking on each others toes and writing plugins or other Geeklog programs which are incompatible with each other. For example in my User Pages Plugin I chose the prefix "UPAGE_" for every one of my own functions.<br />
<br />
Figuring out how to do return codes from functions is never easy in Geeklog or PHP in general. Many functions will return strings of HTML formatted text, and so returning error conditions is not easy. There is no one solution for every circumstance - though I've found 2 solutions work most of the time. If you hit an error condition in your function you can either return a NULL string so the caller can check for NULL string, or you can just return a string with an error message about the problem encountered, in which case the caller will not really know something went wrong - which may or may not matter. It depends on your caller.<br />
<br />
Let's have a look at a couple of functions for making HTML select boxes out of the database. The first function is essentially the same as the Geeklog function COM_optionList although it does get called with different parameters, and the Geeklog function is a bit more powerful. <br />
<br />
One thing you will see first off in the below function definition is that PHP gives you a means to specify default values for function parameters. This means that when calling the function, only the first two parameters "myName" and "myOptions" need be specified. myName is the name this element will have (variable name) and myOptions is a list of options separated by the "mySep" character which by default is "|".<br />
<br />
An important aspect to understand about default values for parameters is that you can only allow the X right-most parameters to have a default value. That is to say you cannot specify a default value for the 1st parameter, then none for the 2nd, then one for the 3rd and so on. The first zero or more parameters will have no default, then after the first one that has a default value all the rest must also have default values. And also when calling the function that we have below, if I wanted to override the default for "mySep" for example by passing a value in, then I also have to override the defaults for every parameter to the left of it - so I must also specify overrides for "myDefault", "myMulti" and "mySize".<br />
<br />
So I could call the function in any of the following ways :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one" );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0 );<br />
$display .= SSM_inputSelect( "SelectBox", "one|two|three", "one", 0, 1 );<br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", "one", 0, 1, ":" );<br />
</pre><br />
<br />
and so on. But I could not do this if all I wanted to specify was "mySep" :<br />
<br />
<pre><br />
$display .= SSM_inputSelect( "SelectBox", "one:two:three", ,,, ":" );<br />
</pre><br />
<br />
So the moral of the story is that if you are having parameters with default values you have to give some consideration to the order of the arguments. You want the ones least likey to be overridden to be the right-most, and the ones most likely to be overridden to be left-most.<br />
<br />
<pre><br />
function SSM_inputSelect( $myName, $myOptions, $myDefault="", $myMulti=0,<br />
$mySize=1, $mySep="|", $visible=true )<br />
{<br />
$retval .= ""<br />
. "<SELECT size=\"" . $mySize . "\" name=\"" . $myName . "\"";<br />
<br />
$retval .= ($myMulti == 0) ? ">" : " multiple>";<br />
<br />
$arrayOptions = explode($mySep,$myOptions);<br />
<br />
foreach ($arrayOptions as $oneOption) {<br />
$oneOption = trim($oneOption);<br />
if ( $myMulti == 0 )<br />
if ( $oneOption == $myDefault )<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
else<br />
if ( in_array( $oneOption, $myDefault ))<br />
$retval .= "<OPTION SELECTED>" . $oneOption . "</OPTION>";<br />
else<br />
$retval .= "<OPTION>" . $oneOption . "</OPTION>";<br />
}<br />
<br />
$retval .= ""<br />
. "</SELECT>"<br />
. "";<br />
<br />
return $retval;<br />
<br />
}<br />
</pre><br />
<br />
Now let's have a look at another function which builds on the above by allowing us to pull stuff out of the database and present it in an options list.<br />
<br />
<pre><br />
function SSM_inputSelectDBField( $myName, $myTable, $myField, $myDefault="",<br />
$mySize=1, $myMulti=0, $extra="", $mySep="|" )<br />
{<br />
// select distinct entries from the given field of given table<br />
$sql = "SELECT DISTINCT " . $myField . " FROM " . $myTable<br />
. " ORDER BY " . $myField;<br />
<br />
// allows us to add an extra entry that was not in the DB<br />
<br />
if ( $extra != "" )<br />
$myOpts = $extra . $myOpts;<br />
<br />
$result = DB_query($sql);<br />
<br />
// format the data as required by SSM_inputSelect()<br />
<br />
while ( $R = DB_fetchArray( $result ) )<br />
if ( $myOpts == "" )<br />
$myOpts .= $R[$myField];<br />
else<br />
$myOpts .= $mySep . $R[$myField];<br />
<br />
// now call the guy doing the actual work<br />
<br />
$retstr .= SSM_inputSelect( $myName, $myOpts, $myDefault, $myMulti,<br />
$mySize, $mySep );<br />
<br />
return $retstr;<br />
}<br />
<br />
</pre><br />
<br />
And finally here is a similar function which once again builds upon "SSM_inputSelect" but this time it takes an field of type ENUM and builds a SELECT box out of all the possible preset values of the ENUM.<br />
<br />
<pre><br />
// Does not yet allow multi select but should be rewritten to do this<br />
<br />
function SSM_inputEnumDBField( $myName, $myTable, $myField, $myDefault="", $visible=true )<br />
{<br />
// query the DB to extract the enum values<br />
$qqq = "DESCRIBE $myTable $myField";<br />
$result = DB_query( $qqq );<br />
$arow = DB_fetchArray( $result );<br />
$myArr = explode( ",", trim( strstr( $arow['Type'], "(" ), "()")) ;<br />
<br />
// now format the values as required by SSM_inputSelect()<br />
$idx=0;<br />
$cnt = count($myArr);<br />
while($idx<$cnt)<br />
{<br />
$myArr[$idx] = trim( $myArr[$idx], "'" );<br />
$idx++;<br />
}<br />
sort( $myArr );<br />
$myList = implode( "|", $myArr );<br />
<br />
// now call our workhorse<br />
<br />
return SSM_inputSelect( $myName, $myList, $myDefault );<br />
}<br />
</pre><br />
<br />
The lesson here is that your functions should be well-defined and reusable. Here we could have written 2 different functions which have nothing to do with each other, but instead we wrote a 3rd base function first which the other 2 rely on to get the job done. Now if there is some substantial change in how I want the SELECT boxes drawn, I only have to make the change in one place.<br />
<br />
==Support and Such==<br />
<br />
The best place for Geeklog support is of course [http://www.geeklog.net the main Geeklog site]. But there are a few other great places to check including [http://www.squatty.com Squatty] and [http://www.portalparts.com Portal Parts]. Squatty and Blaine are hard-core Geeklog developers and are responsible for several popular themes, plugins and hacks.<br />
<br />
If you want to report a bug or request a feature, set yourself up an account [http://project.geeklog.net/tracking/signup_page.php here] and do so. If they don't know it is broken, the cannot fix it. I've reported several bugs and have had them fixed prompty. I've also tracked down and fixed several bugs and simply submitted the code which was accepted. And I've also requested several features which have been added over the years at my request. The Geeklog development team is small, but very dedicated and they love to get feedback from the user base.<br />
<br />
<br />
[[Category:Development]]</div>Rasade