Everything2
Near Matches
Ignore Exact
Full Text
Everything2

So you want to write your own Wiki?

created by baethan

(idea) by baethan (3.7 wk) (print)   ?   (I like it!) 2 C!s Fri Nov 05 2004 at 10:54:17

So you want to write your own wiki script? Look no further - soon you'll have a wiki application all of your own, in less than 100 lines of PHP. (If you don't know what a wiki is, simply put, it's a dynamic collection of articles, each authored by anonymous users. It allows provides for existing articles to be edited by any other user.) Doesn't sound too difficult, does it? I didn't think so. I'm not very good with words, so let's just jump right in. First off, open up your favorite PHP/HTML editor and throw this in there:

<?php
define('TITLE',"QuikWIKI");
define('TEMPLATE',"TEMPLATE");
define('ROOT',"pages/");
define('INDEX',"Index");
?>

What that little bit of code did was define a few global settings. TITLE is the title of your wiki, TEMPLATE is the filename of your page template, ROOT is the root directory where all of the wiki page files will be kept, and INDEX is the default wiki page. These settings will be used often throughout the script, so become familiar with them.

Next thing we're going to want to do is some quick error checking. Add this to your script:

if (!is_dir(ROOT) || !is_writeable(ROOT))
{
 printf("Either the directory %s doesn't exist, or you don't have write privileges.",ROOT);
 exit();
}

This section makes sure that you have full access to the ROOT folder we specified earlier. If not, it quits processing the script. Now that we have that out of the way, we can get to some of the juicier parts.

Now, we're going to lay out the function for displaying the pages according to a template. The template filename was defined earlier in TEMPLATE. Throw this peice of code in your script:

function renderPage($page, $content)
{
 if (!$fp = fopen(TEMPLATE,"r")) { return "Could not open template."; }
 $res = @fread($fp,filesize(TEMPLATE)); fclose($fp);
 $res = preg_replace("/\{TITLE\}/",TITLE,$res);
 $res = preg_replace("/\{PAGE\}/",$page,$res);
 $res = preg_replace("/\{HOME\}/",sprintf("<a href=\"?%s\">%s</a>",INDEX,INDEX),$res);
 if (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING']) - 1) == "$")
 {
  $res = preg_replace("/\{EDIT\}/",sprintf("<a href=\"?%s\">%s</a>",$page,"Cancel"),$res);
 } else {
  $res = preg_replace("/\{EDIT\}/",sprintf("<a href=\"?%s\$\">%s</a>",$page,"Edit"),$res);
 }
 $res = preg_replace("/\{GO\}/","<form method=post action=?><input type=text name=go>" .
  "<input type=submit value=Go></form>",$res);
 $res = preg_replace("/\{CONTENT\}/",$content,$res);
 return $res;
}

Yes, this piece of code is a bit longer, and a bit more complex than the previous ones, but the idea behind it is pretty simple. All it does is read the data of the template file in, and parse it according to directives marked like {DIRECTIVE}. {TITLE} displays the Wiki's title, {PAGE} displays the page's title, {HOME} makes a link to the INDEX page, {EDIT} makes a link to edit the current page, and {CONTENT} will hold the main page content. Simple enough, moving on.

Here comes the real workhorse of the script - the content formatter. This guy is the one that does the nasty regular expressions and replacements to make wiki pages look the way they do.

function parseContent($page)
{
 $pagefile = ROOT . $page . ".txt";
 if (!$fp = @fopen($pagefile,"r")) { $fp = @fopen($pagefile,"w"); }
 $content = @fread($fp,filesize($pagefile)); fclose($fp);
 $content = htmlentities($content);
 $content = preg_replace("/\[([0-9a-zA-Z\- \_]+)\]/","<a href=\"?$1\">$1</a>",$content);
 $content = str_replace("{b}","<b>",$content);
 $content = str_replace("{/b}","</b>",$content);
 $content = str_replace("{i}","<i>",$content);
 $content = str_replace("{/i}","</i>",$content);
 $content = str_replace("{u}","<u>",$content);
 $content = str_replace("{/u}","</u>",$content);
 $content = str_replace("{s}","<s>",$content);
 $content = str_replace("{/s}","</s>",$content);
 $content = str_replace("{pre}","<pre>",$content);
 $content = str_replace("{/pre}","</pre>",$content);
 $content = preg_replace('/^\*(.*)\n/Um',"<li>$1</li><br>", $content);
 $content = str_replace("\n","<br>",$content);
 return $content;
}

If you look hard enough, you'll also notice that this controls the actual creation of pages. If a page doesn't exist, it's created dynamically.

Here is the last big block of code... the brains of the operation. This peice decides what's going to happen; whether or not the user is going to see a page, or see it's edit screen.

if ((!$_SERVER['QUERY_STRING'] || preg_match("/[(\.+|\\+)|\/+]/", $_SERVER['QUERY_STRING'])) .
 && !@$_POST['go'])
{
 printf("%s",renderPage(INDEX, parseContent(INDEX)));
} else {
 if (@$_POST['go'])
 {
  if (!preg_match("/[(\.+|\\+)|\/+]/", @$_POST['go']))
  {
   header(sprintf("Location: ?%s",@$_POST['go']));
  } else {
   header("Location: ?");
  }
 } elseif (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING']) - 1) == "$")
 {
  $page = substr($_SERVER['QUERY_STRING'], 0, strlen($_SERVER['QUERY_STRING']) - 1);
  if (!@$_POST['newcontent'])
  {
   $content = sprintf("<center><form method=post action=\"?%s\">",$_SERVER['QUERY_STRING']);
   $content .= "<textarea name=newcontent rows=15 cols=40>";
   $pagefile = ROOT . $page . ".txt";
   if (!$fp = @fopen($pagefile,"r")) { $fp = @fopen($pagefile,"w"); }
   $editcontent = @fread($fp,filesize($pagefile)); fclose($fp);
   $content .= $editcontent . "</textarea><br><input type=submit value='Edit $page'>" . 
    "</form></center>";
   printf("%s",renderPage($page,$content));
  } else {
   $pagefile = ROOT . $page . ".txt";
   if ($fp = @fopen($pagefile,"w")) { @fwrite($fp,$_POST['newcontent']); }
   header(sprintf("Location: ?%s",$page));
  }
 } else {
  printf("%s",renderPage($_SERVER['QUERY_STRING'], parseContent($_SERVER['QUERY_STRING'])));
 }
}

Alright... what doesn't this do? This takes care of malicious page addresses (it makes sure that people don't try to access folders that shouldn't be accessed), and it decides whether or not the user wants to edit or view a page.

Now, let's take a look at the code, all put together, to see what it should look like:

<?php
define('TITLE',"QuikWIKI");
define('TEMPLATE',"TEMPLATE");
define('ROOT',"pages/");
define('INDEX',"Index");

if (!is_dir(ROOT) || !is_writeable(ROOT))
{
 printf("Either the directory %s doesn't exist, or you don't have write privileges.",ROOT);
 exit();
}

function renderPage($page, $content)
{
 if (!$fp = fopen(TEMPLATE,"r")) { return "Could not open template."; }
 $res = @fread($fp,filesize(TEMPLATE)); fclose($fp);
 $res = preg_replace("/\{TITLE\}/",TITLE,$res);
 $res = preg_replace("/\{PAGE\}/",$page,$res);
 $res = preg_replace("/\{HOME\}/",sprintf("<a href=\"?%s\">%s</a>",INDEX,INDEX),$res);
 if (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING']) - 1) == "$")
 {
  $res = preg_replace("/\{EDIT\}/",sprintf("<a href=\"?%s\">%s</a>",$page,"Cancel"),$res);
 } else {
  $res = preg_replace("/\{EDIT\}/",sprintf("<a href=\"?%s\$\">%s</a>",$page,"Edit"),$res);
 }
 $res = preg_replace("/\{GO\}/","<form method=post action=?><input type=text name=go>" .
  "<input type=submit value=Go></form>",$res);
 $res = preg_replace("/\{CONTENT\}/",$content,$res);
 return $res;
}

function parseContent($page)
{
 $pagefile = ROOT . $page . ".txt";
 if (!$fp = @fopen($pagefile,"r")) { $fp = @fopen($pagefile,"w"); }
 $content = @fread($fp,filesize($pagefile)); fclose($fp);
 $content = htmlentities($content);
 $content = preg_replace("/\[([0-9a-zA-Z\- \_]+)\]/","<a href=\"?$1\">$1</a>",$content);
 $content = str_replace("{b}","<b>",$content);
 $content = str_replace("{/b}","</b>",$content);
 $content = str_replace("{i}","<i>",$content);
 $content = str_replace("{/i}","</i>",$content);
 $content = str_replace("{u}","<u>",$content);
 $content = str_replace("{/u}","</u>",$content);
 $content = str_replace("{s}","<s>",$content);
 $content = str_replace("{/s}","</s>",$content);
 $content = str_replace("{pre}","<pre>",$content);
 $content = str_replace("{/pre}","</pre>",$content);
 $content = preg_replace('/^\*(.*)\n/Um',"<li>$1</li><br>", $content);
 $content = str_replace("\n","<br>",$content);
 return $content;
}

if ((!$_SERVER['QUERY_STRING'] || preg_match("/[(\.+|\\+)|\/+]/", $_SERVER['QUERY_STRING'])) .
 && !@$_POST['go'])
{
 printf("%s",renderPage(INDEX, parseContent(INDEX)));
} else {
 if (@$_POST['go'])
 {
  if (!preg_match("/[(\.+|\\+)|\/+]/", @$_POST['go']))
  {
   header(sprintf("Location: ?%s",@$_POST['go']));
  } else {
   header("Location: ?");
  }
 } elseif (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING']) - 1) == "$")
 {
  $page = substr($_SERVER['QUERY_STRING'], 0, strlen($_SERVER['QUERY_STRING']) - 1);
  if (!@$_POST['newcontent'])
  {
   $content = sprintf("<center><form method=post action=\"?%s\">",$_SERVER['QUERY_STRING']);
   $content .= "<textarea name=newcontent rows=15 cols=40>";
   $pagefile = ROOT . $page . ".txt";
   if (!$fp = @fopen($pagefile,"r")) { $fp = @fopen($pagefile,"w"); }
   $editcontent = @fread($fp,filesize($pagefile)); fclose($fp);
   $content .= $editcontent . "</textarea><br><input type=submit value='Edit $page'>" . 
    "</form></center>";
   printf("%s",renderPage($page,$content));
  } else {
   $pagefile = ROOT . $page . ".txt";
   if ($fp = @fopen($pagefile,"w")) { @fwrite($fp,$_POST['newcontent']); }
   header(sprintf("Location: ?%s",$page));
  }
 } else {
  printf("%s",renderPage($_SERVER['QUERY_STRING'], parseContent($_SERVER['QUERY_STRING'])));
 }
}
?>

It's a beauty, isn't it? That's enough of that. Hopefully, now you more understand the internal workings of a wiki script, and you probably realize that creating one isn't as complicated as you thought it would be. Just for reference, here's an example template file that you could use for your brand-new wiki:

<html>
 <head>
  <title>{TITLE} - {PAGE}</title>
  <style>
   * { font-family: "courier new"; font-size: 12; }
   a { color: gray; }
   form { display: inline; }
   .header { border: none; }
   .content { border: solid gray 1px; padding: 10px 10px 10px 10px; }
   .footer { border: none; }
  </style>
 </head>
 <body>
  <div class=header>
   <b>{TITLE} - {PAGE}</b>
  </div>
  <br>
  <div class=content>
   {CONTENT} 
  </div>
  <br>
  <div class=footer>
   Options: {HOME} | {EDIT} | {GO}
  </div>
 </body>
</html> 

Well, there you have it. A fully-functional wiki ready to go. As far as using your wiki goes, it's pretty straightforward. Look at the code to see the markup available. You can use the 'Go' bar to jump to certain pages, and if they don't exist, they will be created. If you pay attention, you'll notice that a $ at the end of a URL means that the page is to be edited. Just study the source, you'll see what's going on.

The Wiki used in this tutorial is QuikWIKI, a small wiki script that I wrote. It's available at http://box43.net/phunk/qw/ - enjoy.

UPDATE: I removed the tabs, and replaced them with spaces. It now does not stretch the screen, even on 800x600.


printable version
chaos

A cool short C program: output Learn to Program: Producing Output Wiki Wikalong
Script I want to be a pirate when I grow up Moinmoin MediaWiki
No PHP So you want to wear a Trench Coat? So you want to be a DJ?
So, you want to be a philosopher The Murder of Roger Ackroyd PornQuest 2006 terror in the parking lot
This place needs more actual content. Let's begin. Glenn Gould Node .45 Tom Waits
Chary Repast tutorial So You Want to Be a Wizard
Y'know, if you log in, you can write something here, or contact authors directly on the site. Create a New User if you don't already have an account.
  Epicenter
Login
Password

password reminder
register

Everything2 Help

Cool Staff Picks
Just another sprinkling of indeterminacy
Morrowind
Bozo the Clown
Für Elise
feline hedonism
Angelheaded Hipsters
Take now your son, your only son, Isaac
porridge
Choosing and applying to a Graduate School
The Love Song of J. Alfred Prufrock
United States Sex Laws
Music For Supermarkets
Everything Quests - Discover India
Information doesn't want to be anything
New Writeups
octillion369
Frost wyrm(person)
kalen
Three "T"s(idea)
octillion369
Undead(idea)
archiewood
Ico(fiction)
Heisenberg
Why I love Everything2(log)
octillion369
Death Knight(person)
XWiz
Are you hoping for a miracle?(review)
santo
The Host(review)
LostPsion
"Shut the Fuck Up" Theaters(idea)
Vanish
The line between normal and not(place)
Vanish
insanity(thing)
beatrice
You've been slowly taking me over for nearly a year, do you know that?(idea)
Berek
YouTube(thing)
shaogo
How to Pretend to Have a Job(idea)
hapax
Les Provinciales(review)
E2 is a by-product of the existence of The Everything Development Company