Sahana Wiki Module

[article] [edit page] [discussion] [history]

From Humanitarian-FOSS Project Development Site

As an exercise to familiarize ourselves with the Sahana framework, we wrote a wiki module.

Contents

Preparing

To follow along, you'll need:

  • A working copy of Sahana
  • An IDE such as Eclipse
  • A MySQL browser such as phpMyAdmin (optional, but helpful), set up to work with your database

Set up the database

We'll be storing the articles for our wiki in a database. For our example we need just one table, which we'll call wiki_article. We will be storing past revisions of articles, so our database needs to take this into account.

Run the following SQL on your database to create the table. If you have phpMyAdmin installed, click the SQL tab at the top once you're in your database.

create table wiki_article (
 id mediumint(9) not null auto_increment,
 `name` varchar(64) not null,
 rev_date datetime default null,
 author varchar(60) default null,
 content text,
 primary key (id),
 key `name` (`name`),
 key author (author),
 fulltext key content (content)
);

You can view this in the phpMyAdmin browser and see the structure of the table it created:

Image:Wiki tablestructure.png

  • id is a unique identifier for a revision of an article.
  • rev_date is the date the revision was submitted.
  • author is the ID of the Sahana user who submitted the revision.
  • content stores the text in the article.

Creating the module

First, make the three files needed: main.inc, menu.inc, and conf.inc.

conf.inc is the simplest file, so do that first:

$conf['mod_wiki_name'] = _("Swikana");
$conf['mod_wiki_menuorder'] = 0;

These two lines are required for every module. Feel free to rename the module. Next, turn to main.inc:

include ($global ['approot']."inc/lib_menu.inc");
function shn_wiki_mainmenu(){
 global $global;
 require_once $global['approot'].'/mod/wiki/menu.inc';
}
function shn_wiki_default(){
 shn_wiki_showPage("Main Page");
}

The mainmenu() function delegates to menu.inc for organizational purposes. showPage is the function used to display any wiki page. Make the showPage function:

function shn_wiki_showPage($name = null, $revision = null){
 global $global;
}

When we go to a wiki page, the URL will have the form ....index.php?mod=wiki&act=showPage&name=.... When this happens, the showPage function will be called with no parameters. We capture it with:

if(!$name)
 $name = $_REQUEST['name'];

To ensure that the name is in the wiki-style format with underlines instead of spaces, encode it:

$eName = shn_wiki_encodeName($name);

We will have to define the encodeName function eventually. Next, we check the revision date:

$revSQL = "";
if($rev != null){
 $revSQL = "AND rev_date = '$rev'";
}else if(isset($_REQUEST['rev']) && $_REQUEST['rev'] != null){
 $revSQL = "AND rev_date = '{$_REQUEST['rev']}'";
}

Now it's time to query the database and output the page:

$res = $global['db']->getall("SELECT name, rev_date, content, author FROM wiki_article" .
 " WHERE name = '$eName' $revSQL ORDER BY rev_date desc LIMIT 1");
extract($res[0]);
if (!$rev_date){
 shn_wiki_showNonWiki($name);
}else{
 $dName = shn_wiki_decodeName($name);
 echo "<center><h1>$dName</h1></center>";
 $content = shn_wiki_wikify($content);
 echo "<p>". nl2br($content) . "</p>";
 $timestamp = strtotime($rev_date);
 $str = date("l, F j, Y",$timestamp);
 $user = $global['db']->execute("SELECT user_name FROM users WHERE p_uuid = '$author'");
 echo "<p><small><em>Last Revised $str by {$user->fields['user_name']}</em></small></p>";
}

Now, define the functions that are referenced above: encodeName, showNonWiki, wikify, and decodeName.

function shn_wiki_encodeName($name) {
 return urlencode(str_replace(" ", "_", $name));
}

Simple enough. decodeName is similar:

function shn_wiki_decodeName($name) {
 return urldecode(str_replace("_", " ", $name));
}

wikify uses complex regex expressions. You can read about them here.

function shn_wiki_wikify($content) {
 $c = preg_replace("/\[\[([^\[\]\|]+)\]\]/", '<a href="?mod=wiki&act=showPage&name=$1">$1</a>', $content);
 $c = preg_replace("/\[\[([^\[\]]+)\|([^\[\]]+)\]\]/", '<a href="?mod=wiki&act=showPage&name=$2">$1</a>', $c);
 while(preg_match("/(href=\"[^\s]*)\s([^\s]*\")/", $c))
  $c = preg_replace("/(href=\"[^\s]*)\s([^\s]*\")/","$1_$2",$c);
 return $c;
}

showNonWiki is:

function shn_wiki_showNonWiki($name){
 global $conf;
 echo "<h1>$name</h1>";
 echo "<p>".sprintf($conf['mod_wiki_nonWIKI'], "?mod=wiki&act=editPage&name=$name")."</p>";
}

We see act=editPage. Let's make that function:

function shn_wiki_editPage($name = null){
 global $global;
 if(!$name)
  $name = $_REQUEST['name'];

 $eName = shn_wiki_encodeName($name);
 $res = $global['db']->getall("SELECT name, rev_date, content FROM wiki_article" .
   " WHERE name = '$eName' ORDER BY rev_date DESC LIMIT 1");
 extract($res[0]);
 $dName = shn_wiki_decodeName($name);
 if(!$rev_date)
  $heading= "New Page";
 else
  $heading = "Edit Page";
 ?>
 <center><h1><?=$heading?></h1></center>
 <br>
 <form method=POST action="index.php">
  <input type="hidden" name="mod" value="wiki">
  <input type="hidden" name="act" value="savePage">
  <table class="layout" border="0">
   <tr><<d width="110">Page Name:</td> <td> <input type=hidden name="page_name" style="width:300px;" value="<?echo "$name"?>"><?echo "$dName"?> <br></td>
   <tr><td><br> </td></tr>
   <tr><td>Page Content:</td> <td> <textarea name="page_content" style="width:300px; height:150px;"><?echo "$content"?></textarea><br></td>
   <tr><td><br> </td></tr>
   <tr><td align="center"><input type="submit" value="Submit" style="width:110px;"></td></tr>
  </table>
 </form><?
}

We also need savePage:

function shn_wiki_savePage($name = null){
 global $global;
 extract($_REQUEST);
 if (!$page_name || !$page_content){
  echo "<center><h1>Error! Required information is missing. Please go back and try again.</h1></center>";
 }else{
  page_name = shn_wiki_encodeName($page_name);		
  $global['db']->execute("INSERT INTO wiki_article (name, content, rev_date, author) values ('$page_name', '$page_content', NOW(),'{$_SESSION['user_id']}' )") or DIE(mySQL_error());
  shn_wiki_showPage($page_name);
 }
}

and deletePage:

function shn_wiki_deletePage($name = null){
 global $global;
 extract($_REQUEST);
 if (!$name){
  echo "<center><h1>Error! You must specify the name of the page to be deleted.</h1></center>";
  return;
 }else{
  $name = addslashes($name);
  $global['db']->execute("DELETE FROM wiki_article where name='$name'");
 }
 echo "<center><h1>Delete successful!</h1></center>";
}
Finally, add functionality for showing all wiki pages and recently modified ones:
 function shn_wiki_recent($number = 10){
  global $global;
  $res = $global['db']->execute("SELECT name, rev_date FROM wiki_article ORDER BY rev_date DESC LIMIT $number");
  $pages = array();
  while(!$res->EOF){
   $pages[$res->fields['name']] = $res->fields['rev_date'];
   $res->moveNext();
  }
  echo "<h1>$number Most Recently Modified Pages</h1>";
  echo '<ul>';
  foreach($pages as $name => $date){
   $readableDate = date("l, F j, Y", strtotime($date));
   $readableName = shn_wiki_decodeName($name);
   echo "<li><a href=\"?mod=wiki&act=showPage&name=$name\">$readableName</a> - <small><em>Last modified: $readableDate</em></small></li>";
  }
  echo '</ul>';
 }
 function shn_wiki_all(){
  global $global;
  $res = $global['db']->execute("SELECT name, rev_date, author FROM wiki_article");
  $pages = array();
  while(!$res->EOF){
   $pages[$res->fields['name']] = array($res->fields['rev_date'],$res->fields['author']);
   $res->moveNext();
  }
  echo '<h1>All Pages</h1>';
  echo '<ul>';
  foreach($pages as $name => $rest){
   $readableDate = date("l, F j, Y",strtotime($rest[0]));
   $readableName = shn_wiki_decodeName($name);
   $user = $global['db']->execute("SELECT user_name FROM users WHERE p_uuid = '{$rest[1]}'");
   echo "<li><a href=\"?mod=wiki&act=showPage&name=$name\">$readableName</a> - <small><em>Last modified: $readableDate by {$user->fields[0]}</em></small></li>";
  }
  echo '</ul>';
 }

Build the Menu

The wiki needs to have a menu to access areas such as the main page, creating a new page, and the page toolbox. We'll use Sahana's menu functions to add these.

Create the file menu.inc in the wiki directory. Add the following lines.

We'll open a PHP tag, and call Sahana's built-in functions to open a menu in the navigation bar and add menu items.

<?php
shn_mod_menuopen(_('Wiki'));
shn_mod_menuitem('showPage&name=Main_Page',_('Main Page'));
/*
 * If this is an article page, show the toolkit
 */ 
if($_REQUEST['act'] == 'default'){
	shn_sub_mod_menuopen(_('Page Toolkit'));
	shn_sub_mod_menuitem("editPage&name=Main_Page",_("Edit Page"));
	shn_sub_mod_menuitem("history&name=Main_Page",_("Page History"));
	shn_sub_mod_menuitem("deletePage&name=Main_Page",_("Delete Page"));
	shn_sub_mod_menuclose();
}
if(isset($_REQUEST['name']) && $_REQUEST['name'] != null){
	shn_sub_mod_menuopen(_('Page Toolkit'));
	shn_sub_mod_menuitem("editPage&name={$_REQUEST['name']}",_("Edit Page"));
	shn_sub_mod_menuitem("history&name={$_REQUEST['name']}",_("Page History"));
	shn_sub_mod_menuitem("deletePage&name={$_REQUEST['name']}",_("Delete Page"));
	shn_sub_mod_menuclose();
}
shn_mod_menuitem('#" id="popupLink" onclick="javascript:popup()',_('New Page'));
shn_mod_menuitem('recent',_('Recently Changed'));
shn_mod_menuitem('all',_('List All Pages'));
shn_mod_menuclose();

Then include Sahana's main menu, and close the PHP tag:

/*
 * Include the main sahana menu
 */
require_once $global['approot'].'/inc/handler_mainmenu.inc';
?>

Then we add some javascript and HTML to achieve the new page pop-up:

<script>
/**
 * Changes the "New Page" link's href to not do anything, makes the new page
 * popup visible, and focuses the 'name' field.
 */
function popup(){
	document.getElementById('popupLink').href = "#";
	document.getElementById('popup').style.visibility = "visible";
	document.getElementById('name').focus();
	return false;
}
/**
 * Closes the new page popup.
 */
function closeBox(){
	document.getElementById('popup').style.visibility = "hidden";
	return false;
}
</script>
<style>
#popup{
	visibility: hidden;
	padding: 5px 5px 4px 5px;
	background: #E1DED7;
	position: absolute;
	left: 200px;
	top: 71px;
	border-right: 1px solid #000;
	border-bottom: 1px solid #000;
}
#popup form input{
	border: 1px solid #369;
	background: #fff;
	padding: 5px;
}
#popup form input:focus{
	background: #ffffcc;
}

</style>

Using it

If everything worked, you can now navigate to your new wiki module in Sahana and create your first page. Filling in Main Page might be a good idea. You can create links to other pages by surrounding their names in [[double square brackets]]. You can link to pages that don't exist yet, then follow the links to create them.

Personal tools