Location: PHPKode > projects > Lame Node System > lns-0.6-11/include/NodeInterface.php
<?php

/************************************************************************
 *																		*
 * Copyright (C) 2003 Stuart Reeves										*
 *																		*
 * This program is free software; you can redistribute it and/or		*
 * modify it under the terms of the GNU General Public License			*
 * as published by the Free Software Foundation; either version 2		*
 * of the License, or (at your option) any later version.				*
 *																		*
 * This program is distributed in the hope that it will be useful,		*	
 * but WITHOUT ANY WARRANTY; without even the implied warranty of		*
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the			*
 * GNU General Public License for more details.							*
 * 																		*
 * The GNU General Public License is available from:					*
 * http://www.gnu.org/copyleft/gpl.html									*
 *																		*
 ************************************************************************/

/* Stuart's lns (lame node system) */




/* NodeInterface provides everything to search for, and display, nodes. Once created, the object 
 * will display a given node, or a selection of nodes, or allow a basic regular expression assisted 
 * search of the node database. 
 *
 * I think that it really needs a clean-up, especially printNode, which is awful. */

class NodeInterface
{


	
	// Database connection information
	var $host,
		$user, 
		$passwd, 
		$db,
		$link, 						// The link to the database itself
		$table;
	
	// Various attributes of the node
	var $nodename,
		$text,
		$link,
		$protected,
		$metatype,
		$rawtype,
		$external;					// External link location (if any)

	// Other data (e.g. session information)
	var $username,
		$superuser,
		$numRows, 	 				// The number of "hits" for the requested node
		$relatedNodes,				// Create array for the related node links. The array is 
									// simply a list of strings.
		$firstEditor;				// First author to create this node
		
	
	// Display options for nodes
	var $showExtLinks,
		$showDistinct,
		$showByTS,
		$showHidden,
		$showNodenameHeader,
		$showRelatedNodes,
		$showHomeNodes,
		$truncateNodes;
	
	
	// Constructor
	function NodeInterface($host, $user, $passwd, $db, $table){
		
		// Database stuff
		$this->host 	 = $host;
		$this->user 	 = $user;
		$this->passwd 	 = $passwd;
		$this->db 		 = $db;
		$this->dblink	 = null;
		$this->table	 = $table;
		
		// Node stuff
		$this->protected = false;
		$this->metatype  = METATYPE_DEFAULT;
		$this->rawtype   = RAWTYPE_DEFAULT;
		$this->external  = "";
		$this->superuser = false;
	
		$this->relatedNodes = array();

		// Set default display options
		$this->showExtLinks       = false;
		$this->showDistinct       = false;
		$this->showByTS			  = false;
		$this->showHidden 	      = false;
		$this->showNodenameHeader = false;
		$this->showRelatedNodes	  = false;
		$this->showHomeNodes	  = false;
		$this->truncateNodes	  = false;
	}
	
	
	function setShowDistinct($flag) {
		$this->showDistinct = $flag;
	}

	function setShowByTS($flag) {
		$this->showByTS = $flag;
	}
	
	function setShowExtLinks($flag) {
		$this->showExtLinks = $flag;
	}
	
	function setShowHidden($flag) {
		$this->showHidden = $flag;
	}
	
	function setShowNodenameHeader($flag) {
		$this->showNodenameHeader = $flag;
	}
	
	function setShowRelatedNodes($flag) {
		$this->showRelatedNodes = $flag;
	}
	
	function setShowHomeNodes($flag) {
		$this->showHomeNodes = $flag;
	}
	
	function setTruncateNodes($flag) {
		$this->truncateNodes = $flag;
	}
	
	function setUsername($username) {
		$this->username = $username;
	}

	function setSuperuser($flag) {
		$this->superuser = $flag;
	}

	function isProtected() {
		return $this->protected;
	}

	function setMetatype($metatype) {
		$this->metatype = $metatype;
	}

	function getMetatype() {
		return $this->metatype;
	}

	function getRawtype() {
		return $this->rawtype;
	}

	function getLink() {
		return $this->link;
	}

	/* Returns:	$this->nodename		- The case-correct nodename */
	function getNodename() {
		return $this->nodename;
	}


	function getFirstEditor() {
		return $this->firstEditor;
	}


	/* Finds out the nodename of a user's (unique) home node.
	 *
	 * Returns: $code (string)			- The nodename of this user's home node
	 * 			0 (integer)				- No home node is found or an access error has occurred 
	 *			-1 (integer)			- Database error (i.e. > 1 METATYPE_HOME node for a 
	 *									  user) */

	function getHomeNode() {
	
		if (isset($this->username)) {
			
			$query = "SELECT nodename FROM $this->table WHERE metatype = '" . METATYPE_HOME . "' 
										   		  		AND   username = '$this->username'";
			
			$this->dblink = mysql_connect($this->host, $this->user, $this->passwd);
			$dbselect	= mysql_select_db($this->db, $this->dblink);
			$dbquery 	= mysql_query($query, $this->dblink);
			
			$numRows 	= mysql_num_rows($dbquery);
			$code		= 0;

			if ($numRows == 1) {
				$nodedata = mysql_fetch_object($dbquery);
				$code = $nodedata->nodename;
			} else if ($numRows > 1) 
				$code = -1;
				
			mysql_close($this->dblink);
			return $code;
		} else
			return 0;
	}



	/* Creates the HTML body for any nodes that match the required nodename, and sorts them by 
	 * timestamp (i.e. latest additions will appear at the top of the node).
	 * 
	 * Args:	$reqNode (string)			- The requested nodename to display
	 *			$HTMLFactory (object)		- The HTMLFactory object reference
	 *
	 * Returns: $html (string)  	 		- The node as HTML 
	 *			false (boolean)				- If no node is found */

	function getNode($reqNode, &$HTMLFactory) {

		$query = "SELECT * FROM $this->table WHERE nodename = \"$reqNode\" ORDER BY ts DESC";

		$this->dblink = mysql_connect($this->host, $this->user, $this->passwd);
		$dbselect	  = mysql_select_db($this->db, $this->dblink);
		$dbquery 	  = mysql_query($query, $this->dblink);
		
		$this->numRows = mysql_num_rows($dbquery);
		
		$html = $this->printNode($dbquery, $reqNode, &$HTMLFactory);
		
		mysql_close($this->dblink);
		
		return $html;

	}
	
	
	/* Displays a list of formatted nodenames, optionally with external link nodes displayed.
	 * 
	 * Args:	$age (string)		 		- Maximum age of nodes to be displayed (in days)
	 *			$offset (integer)	 		- Limit number of output nodes by this
	 *			$username (string)			- Restrict listing to nodes authored by this user
	 *			$metatype (string)			- Restrict listing to nodes of this meta-type
	 *
	 * Returns: $html (string)  	 		- The list of nodenames as HTML */

	function getNodeList($age, $offset, $username, $metatype) {
		
		$html  = "";
		$query = "";
		
		// Create default query
		$opts = array(SELECT   => "*",
					  WHERE    => "",
					  ORDER_BY => "ORDER BY nodename ASC",
					  LIMIT    => "");
		
		// Populate the array with the right values for the current options
	
		if ($this->showExtLinks) {
			$opts[WHERE]    = "WHERE link != '' AND hidden != 't'";
			$this->showByTS = true;
		}
		
		if ($this->showDistinct) { 
			$opts[SELECT] = "DISTINCT nodename";
			if ($opts[WHERE] == "")
				$opts[WHERE] = "WHERE hidden != 't'";
			else
				$opts[WHERE] = $opts[WHERE] . " AND hidden != 't'";
				
			$opts[ORDER_BY] = "ORDER BY nodename ASC";
		}

		if ($age > 0) {
			if ($opts[WHERE] == "")
				$opts[WHERE] = "WHERE TO_DAYS(NOW()) - TO_DAYS(ts) <= $age";
			else
				$opts[WHERE] = $opts[WHERE] . " AND TO_DAYS(NOW()) - TO_DAYS(ts) <= $age";
							
			$this->showByTS = true;
		}
		
		if ($offset > 0) {
			if ($this->showDistinct)
				$opts[WHERE] = $opts[WHERE] . " AND link = ''";
			else if (!$this->showExtLinks)
				$opts[WHERE] = "WHERE link = '' AND hidden != 't'";
				
			$this->showByTS = true;
			$opts[LIMIT]    = "LIMIT $offset";
		}

		if (isset($username)) {
			if ($opts[WHERE] == "")
				$opts[WHERE] = "WHERE username = '$username'";
			else
				$opts[WHERE] = $opts[WHERE] . " AND username = '$username'";
		}

		if (isset($metatype)) {
			if ($opts[WHERE] == "")
				$opts[WHERE] = "WHERE metatype = '$metatype'";
			else
				$opts[WHERE] = $opts[WHERE] . " AND metatype = '$metatype'";
		}
		
		if (!$this->showHomeNodes) {
			if ($opts[WHERE] != "")
				$opts[WHERE] = $opts[WHERE] . " AND metatype != '" . METATYPE_HOME . "'";
			else
				$opts[WHERE] = "metatype != '" . METATYPE_HOME . "'";
		}

		// Connect to the database
		$this->dblink  = mysql_connect($this->host, $this->user, $this->passwd);
		$dbselect	   = mysql_select_db($this->db, $this->dblink);
		$nodedataQuery = null;

		if ($this->showByTS) {
			$opts[ORDER_BY] = "ORDER BY ts DESC";
			// If the ORDER BY clause is set for timestamp and the list is for distinct 
			// nodes only, then we must create a temporary table for technical reasons.
			if ($this->showDistinct) {
				mysql_query("CREATE TEMPORARY TABLE nodelist (nodename VARCHAR(200), ts TIMESTAMP) 
											  TYPE = HEAP", $this->dblink);
				mysql_query("INSERT INTO nodelist SELECT nodename, ts FROM $this->table " . 
							$opts[WHERE] . " " . $opts[ORDER_BY], 
							$this->dblink);
				// Perform the actual DISTINCT query and delete the temporary table
				$nodedataQuery = mysql_query("SELECT DISTINCT nodename FROM nodelist " . 
											 $opts[LIMIT], 
											 $this->dblink);
				mysql_query("DROP TABLE nodelist");
			}	

		}

		// A null value for $nodedataQuery implies that the special stuff for DISTINCT listings 
		// ordered by timestamp was not needed
		if ($nodedataQuery == null) {
			$query = "SELECT " . $opts[SELECT] . " FROM $this->table " . $opts[WHERE] .
				     " " . $opts[ORDER_BY] . " " . $opts[LIMIT];
			$nodedataQuery = mysql_query($query, $this->dblink);
		}
		
		// NOTE: bit messy here... can't think of any way to clean it up
		while ($nodedata = mysql_fetch_object($nodedataQuery)) {

			/* If we are displaying distinct nodes, the latest instance of this node's data must
			 * be obtained. 
			 * 
			 * Note: The nodedata data object is overwitten. */
			if ($this->showDistinct) {
				//$nodename  = htmlentities($nodedata->nodename, ENT_QUOTES);
				$distQuery = "SELECT * FROM $this->table WHERE nodename='$nodedata->nodename'
							 								ORDER BY ts DESC LIMIT 1";
				$currNodedataQuery = mysql_query($distQuery, $this->dblink);
				$nodedata          = mysql_fetch_object($currNodedataQuery);
			}

			// Skip over protected nodes if necessary
			if ($this->showHidden || $nodedata->hidden != 't') {

				$nodename 	= $nodedata->nodename;
				$linked		= false;
				
				// Cater for picture nodes and external links
				if ($nodedata->link != "") {
					
					// Add indicative text if this is an external link or a picture
					if ($nodedata->rawtype == RAWTYPE_IMAGE) {
						$html  .= "<span class=\"light\">picture: </span> ";
						$linked = true;
					} else if ($nodedata->rawtype == RAWTYPE_EXT_LINK && $this->showExtLinks) {
						$html  .= "<span class=\"light\">external link: </span> ";
						$linked = true;
					}
				
				} 
				
				if ($nodedata->link == "" || $linked) {
					
					$echoTrailingText = false;
					
					if ($showHomeNodes || $nodedata->metatype != METATYPE_HOME) {
						// Display the right author and creation/addition indicator
						$html .= "<b><a title=\"$nodename\" href=\"?node=$nodename\">" . 
								 "$nodename</a></b> <br />\n";
				
						$html .= "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
								 "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>added by ";
				
						if ($nodedata->username == "") {
							if ($nodedata->contact != "")
								$html .= "<a href=\"$nodedata->contact\">" . 
										 "$nodedata->author</a>";
							else
								$html .= $nodedata->author;
						} else
							$html .= "<a title=\"Profile for user $nodedata->username\" " .
									 "href=\"?node=" . FINGER_NODE . "&_username=" .
									 "$nodedata->username\">$nodedata->username</a>";
						
						if ($nodedata->username == "") {
							$html .= " (<a href=\"?node=" . HELP_NODE . 
									 "#anonymous user\">Anonymous user</a>)";
						}
			
						$echoTrailingText = true;
			
					} else if ($metatype == METATYPE_HOME && $nodedata->username != "") {
						$html .= "<a title=\"$nodedata->username's home node\" " . 
								 "href=\"?node=$nodedata->nodename\"><strong>$nodedata->nodename" . 
								 "</strong></a> (<a title=\"Profile for user $nodedata->username\"".
								 "href=\"?node=" . FINGER_NODE . 
								 "&_username=$nodedata->username\">" . 
								 "$nodedata->username</a>'s home node) <em>";
						$echoTrailingText = true;
					}
	
		
					if ($echoTrailingText) {
						$html .= " edited on " . Util::fmtTimestamp($nodedata->ts) ."</em>";

						if (isset($this->username) && $this->username == $nodedata->username) {
							$html .= "&nbsp;[<a href=\"?node=" . SUBMIT_NODE . "&_nodename=" . 
									 "$nodedata->nodename&ts=$nodedata->ts\">edit this " . 
									 "$nodedata->metatype node</a>]";
						}	
						$html .= "<br />\n";
					}

				}
					
			}

			
		}

		mysql_close($this->dblink);
		return $html;
	
	}
	
	
	
	/* Checks for external links to indicate whether a redirect is necessary or allowed.
	 * 
	 * Returns: $this->external (string) 	- the physical link of the node 
	 *										  (e.g. "http://www.foo.net") */
	 
	function isExternal(){
		// Make sure that we only flag a redirect if this node is unique
		if ($this->external != "" && $this->numRows == 1) {
			return $this->external;
		} else
			return false;
	
	}
	
	
	
	/* Searches for strings inside nodes using the MySQL FULLTEXT search.
	 * 
	 * Args:	$searchStr (string)			- The search query string
	 *			$HTMLFactory (object)		- The HTMLFactory object that creates HTML widgets
	 *
	 * Returns: $html (string)  	 		- The nodes returned in the search 
	 *			false (boolean) 			- Search returned no results or search string empty */

	function search($searchStr, &$HTMLFactory) {
	
		if ($searchStr != "")
			// Get rid of any unwanted characters in search string and tokenise
			$searchStr = trim(preg_replace(SEARCH_REPLACE, " ", stripslashes($searchStr)));
		else
			return false;
			
		$query  = "SELECT *,MATCH(nodename, text) AGAINST(\"$searchStr\") AS score from " .
				  NODE_TABLE . " WHERE MATCH(nodename, text) AGAINST(\"$searchStr\") ORDER BY " .
				  "score DESC";
		
		// Connect to database
		$this->dblink = mysql_connect($this->host, $this->user, $this->passwd);
		$dbselect	  = mysql_select_db($this->db, $this->dblink);
		$dbquery 	  = mysql_query($query, $this->dblink);
	
		$this->numRows = mysql_num_rows($dbquery);

		$result = "";

		// Create the results if they exist
		if ($this->numRows != 0) {
			$html = "<a href=\"?node=" . SEARCH_NODE . "\">search</a> results for " .
					"\"<em>$searchStr</em>\":" .
					"<br /><br /><table align=\"right\" border=\"0\" width=\"90%\">" .
					"<tr><td>" . $this->printNode($dbquery, "", &$HTMLFactory) . 
					"</td></tr></table>";
						 
			$result = $html;
		} else
			$result = false;
		
		mysql_close($this->dblink);
	
		return $result;
	}
	
	
	
	/* DEPRECATED
	 * Searches for strings inside nodes and displays node bodies that satisfy the search criteria.
	 * 
	 * Args:	$str (string)				- the search query string
	 *			$mode (string)				- special flag to switch between AND and OR word 
	 *										  searches 
	 *			$range (string) 			- special flag to specify the sections of the node to 
	 *										  search in
	 *
	 * Returns: $html (string)  	 		- the nodes returned in the search 
	 *			false (boolean) 			- search returned no results or search string empty */

	function locate($str, $mode, $range, $htmlfactory){
		
		if ($str != "") {
			// Get rid of any unwanted characters in search string and tokenise
			$clean_str  = trim(preg_replace(SEARCH_REPLACE, " ", stripslashes($str)));
			$tokens 	= strtok($clean_str, " ");
			$toklist 	= ""; 						// Initialise the tokenlist string
		} else
			return false;
		
		
		// Create the token list for the regular expression search
		switch ($mode) {
			case SEARCH_LOGICAL_OR:
				// Create the token list string as this: "token1|token2|token3..."
				// THIS NEEDS TO BE FIXED
				$toklist = $tokens;
				while ($tokens) {
					$tokens  = strtok(" ");
					if ($tokens != "")
						$toklist .= "[[:space:]]|[[:space:]]$tokens.";

				}
				break;
				
			case SEARCH_LOGICAL_AND:
				// Creates a string where the token can either be at the start of the string, at
				// the end or somewhere in the middle with whitespace
				while ($tokens) {
					if ($tokens != "")
						$toklist .= $toklist .= "(^($tokens.+)+|((.+$tokens)$)|.+($tokens).+)";
						
					$tokens  = strtok(" ");

				}
				break;
		
		}
		
		
		// Check that our list of tokens actually has values in it before searching
		if ($toklist != "") {
	
			// Create the correct query string based on the range type
			switch ($range) {
				case SEARCH_NODENAME: 	
					$option = "nodename REGEXP '$toklist' ORDER BY nodename"; 
					break;
				case SEARCH_TEXT: 		
					$option = "text REGEXP '$toklist' ORDER BY nodename"; 
					break;
				case SEARCH_ALL: 		
					$option = "nodename REGEXP '$toklist' OR text REGEXP '$toklist' ORDER BY 
							   nodename"; 
					break;
			}
			
			$q_str  	= "SELECT * FROM $this->table WHERE $option";

			// Connect to database
			$this->dblink	= mysql_connect($this->host, $this->user, $this->passwd);
			$dbselect	= mysql_select_db($this->db, $this->dblink);
			$dbquery 	= mysql_query($q_str, $this->dblink);
		
			// Create the results if they exist
			if (mysql_num_rows($dbquery) != 0) {
				$html = "<a href=\"?node=".SEARCH_NODE."\">search</a> results for 
						 \"<i>$clean_str</i>\":
						 <br /><br /><table align=\"right\" border=\"0\" width=\"90%\">
						 <tr><td>" . $this->printNode($dbquery, "", $htmlfactory) . 
						 "</td></tr></table>";
						 
				return $html;
			} else
				return false;
		
			mysql_close($this->dblink);
		
		} else 
			return false;
	
	
	} 


	
	
/*function myLoadGIF ($imgname) {
    $im = @ImageCreateFromGIF ($imgname);
    if (!$im) {
        $im  = ImageCreate (150, 30);
        $bgc = ImageColorAllocate ($im, 255, 255, 255);
        $tc  = ImageColorAllocate ($im, 0, 0, 0);
        ImageFilledRectangle ($im, 0, 0, 150, 30, $bgc);

        ImageString ($im, 1, 5, 5, "Error loading $imgname", $tc);
    }
    return $im;
}*/
	
	
		
	/* Private function to format node body/date etc. output. The format of the node is:
	 *		1) Divider bar or line breaks 		 
	 *		2) Nodename header (optional)
	 *		3) Node body (external link, picture or text)
	 *		4) Contact information with date and "add to this node" link
	 *
	 * Args:	$dbquery (object)			- SQL query object to display nodes from
	 *			$reqNode (string)			- The requested node of the caller function
	 *			$HTMLFactory (object)		- Reference to the currently used HTMLFactory 
	 * 
	 * Returns:	false (boolean)				- Indicates that the node was not found 
	 *			$html (string)				- The HTML of the node body */

	function printNode($dbquery, $reqNode, &$HTMLFactory) {

		$numRows = $this->numRows;
		$html 	 = "";
		$reqNode = stripslashes($reqNode);
		
		if ($numRows == 0) {
			$this->nodename = $reqNode;
			return false;
		}
			
		/* Record last node name so that we can distinguish between display queries that return
		 * node bodies of exactly one nodename (i.e. normal node displays), and those that 
		 * return differing nodenames (i.e. searches). */
		$lastNodename  = $reqNode;
		
		$count    	  = 0;
		$lastMetatype = "";	 	// Records the last fetched node's metatype
		$isMixedType  = false;	// Flagged if this node is made up of > 1 metatype
		$isOwner	  = false;  // Flagged if node is owned by the logged in user
		$isProtected  = false;  // A node is considered protected if at least one writeup is
				 	 	       	// protected.
		$nodedata	  = null;
		$authorHTML	  = "";		// Stores the last author line so that we can set it as 
								// $lastEditor
	
		// Go through each row (i.e. writeup) in this node
		while ($nodedata = mysql_fetch_object($dbquery)) {
			
			if ($count == 0)
				$firstRawtype = $nodedata->rawtype;
			
			// Get the ``real'' nodename (i.e. case-correct nodename)
			$this->nodename = $nodedata->nodename;
				
			// Don't display hidden nodes if specified
			if ($this->showHidden || $nodedata->hidden != 't') {

				if (!$isMixedType) {
				
					// Initialise the last metatype record
					if ($lastMetatype == "")
						$lastMetatype = $nodedata->metatype;
					else if ($lastMetatype != $nodedata->metatype)
						$isMixedType = true;

					$lastMetatype = $nodedata->metatype;
				}

				$nodeHTML = ""; // The HTML for this single node
				
				// Print a divider bar if needed
				if (!$this->showNodenameHeader && 
					$count > 0 && $count < $numRows && 
					!strcasecmp($lastNodename, $nodedata->nodename)) {
					
					$nodeHTML .= "<hr width=\"75%\">\n";
				} else if ($count > 0) {
					$nodeHTML .= "<br /><br />\n";
				}
				
				// Place "#" name references for each entry
				$nodeHTML .= "\n<!-- BEGIN node text body -->\n" .
						 	 "<a name=\"$count\">&nbsp;</a><br />\n";
					  
				
				++$count;
			
			
				/* Create the HTML output for nodes, external links and pictures. On the basis 
				 * of the link field data, we decide whether to treat this node as (in 
				 * prioritised order):
				 *  	link field not empty	--> picture
				 *  						 or	--> external link 
				 *		link field empty		--> normal node */
				 
				if ($nodedata->link != "") {
					
					$physical = $nodedata->link;
					
					// Create HTML for picture nodes or external links
					if ($nodedata->rawtype == RAWTYPE_IMAGE) {
					
						// Output the image
						/*if (mkdir(IMAGE_THUMB_PATH, 0700))
							chmod(IMAGE_THUMB_PATH, 0700);
							$img = $this->myLoadGIF($physical);
					
						if($img){
						
							// Thumbnail image space
							$w  		= ImageSX($img);
							$h  		= ImageSY($img);
							$new_w  	= $w / IMAGE_SCALE_FACTOR_X;
							$new_h  	= $h / IMAGE_SCALE_FACTOR_Y;
							$new_img 	= ImageCreate($new_w, $new_h);
							ImageCopyResized($new_img, $img, 0, 0, 0, 0, 
											 $new_w, $new_h, $w, $h);
							//ImageJPG($new_img);
						
							$fp = fopen(IMAGE_THUMB_PATH . "/$picname.gif", "w+");
							ImageGIF($new_img, IMAGE_THUMB_PATH . "/$picname.gif");
							fclose($fp);
						} else {
							$html .= "Error: Unable to create image";
						}*/
						
						$picname = substr(strrchr($physical, "/"), 1);
							
						$nodeHTML .= "picture display (\"<i>$picname</i>\"):<br /><br />".
									 "<div align=\"center\"><a href=\"$physical\">".
									 "<img src=\"$physical\"></a></div><br />";
				
					} else if ($nodedata->rawtype == RAWTYPE_EXT_LINK) {
						$nodeHTML .= "<span class=\"warning\">This node is an external link ".
									 "to <a href=\"$physical\">$physical</a></span>";
						$this->external = $physical;
					}	
						
						
				} 
				
				if ($nodedata->rawtype != RAWTYPE_EXT_LINK) {
				
					if ($nodedata->text != "") {
						/* Create node textbody with optional nodename header. Node bodies that are
						 * long will be truncated if the nodename header is used to display the node
						 * textbody. */ 
						if ($this->showNodenameHeader && $this->truncateNodes &&
							(strlen($nodedata->text) > SEARCH_TRUNCATE_CHARS_AFTER)) {
															
							$nodeHTML .= $this->truncateText($nodedata->text) .
									 	 "<dl><dt>&nbsp;</dt><dd>" .
									 	 "<i><span class=\"warning\">This node entry has been " .
									 	 "truncated (<b>" .
									 	 "<a href=\"?node=$nodedata->nodename\">show all</a></b>)" .
									 	 "</span></i></dd></dl>";
							
						} else if ($this->showRelatedNodes) {

							// Search for links to nodes that exist
							$nodeHTML .= $nodedata->text;
							$this->findRelatedNodes($nodedata->text);
										
						} else
							$nodeHTML .= $nodedata->text;
					} else
						$nodeHTML .= "<span class=\"warning\">This node has been deleted</span>";
				
				} // End if
				
			
				// Add contact and timestamp information
				$nodeHTML  .= "<br /><br /><small>[$nodedata->metatype node added by ";
				$authorHTML = "";

				if ($nodedata->contact != "") {
					$title = "";
					if (isset($nodedata->username))
						$title = "title=\"Get more information about this user\"";

					$authorHTML = "<a $title href=\"$nodedata->contact\">$nodedata->author</a>";
				} else {
					if ($nodedata->author != "")
						$authorHTML .= $nodedata->author;
					else
						$authorHTML .= "<span class=\"warning\">deleted</span>";
				}
				
				if (!isset($nodedata->username) && $nodedata->username == "") {
					$authorHTML .= " (<a title=\"Help on anonymous users\" href=\"?node=" . 
								   HELP_NODE . "#anonymoususers\">Anonymous user</a>)";
				}
		
				$authorHTML .= " on " . Util::fmtTimestamp($nodedata->ts);

				$nodeHTML .= "$authorHTML]</small>";
			
				if ($this->superuser || 
					(isset($this->username) && $nodedata->username == $this->username)) {
					
					$nodeHTML .= "&nbsp;[<a href=\"?node=" . SUBMIT_NODE . "&_nodename=" .
								 "$nodedata->nodename&ts=$nodedata->ts\">edit this " .
								 "$nodedata->metatype node</a>]";
								 
					// This will override the value of $isProtected for logged in users (i.e. will
					// display [add to this node] if the protection is ``owned'' by this user).
					if ($nodedata->protected == 't')
						$isOwner = true;
				}


				if ($nodedata->protected == 't')
					$isProtected = true;

				// Optional printing of nodename box (i.e. packages up the HTML inside a nice
				// node ``miniview'' box).
				if ($this->showNodenameHeader) {
					$nodeHTML = $HTMLFactory->createNodeMiniView($nodeHTML,
																 $nodedata->nodename,
															     round($nodedata->score, 2),
																 $nodedata->metatype);
				}
					
				$nodeHTML .= "<br/><br/>\n<!-- END node text body -->\n";
					
				// Append the HTML output for a single node to the currently generated output
				$html .= $nodeHTML;
				
				$lastNodename = $nodedata->nodename;
				
			} else // Alter the recorded number of rows so hidden nodes aren't counted
				--$numRows;
	
		} // End while

	
		// Record who was the originator of this node
		$this->firstEditor = $authorHTML;
	
		// Record data about the meta-type and raw type
		
		$dispMetatype = "";
		$metaType	  = "";
			
		if ($isMixedType) {
			$metaType	  = METATYPE_DEFAULT;
			$dispMetatype = "mixed";
		} else {
			$metaType	  = $lastMetatype;
			$dispMetatype = $lastMetatype;
		}

		$this->metatype = $dispMetatype;
		$this->rawtype  = $firstRawtype;


		/* Display an "add to this node" link on the final entry for each particular 
		 * node, as long as this node is node protected, or owned by the logged in
		 * user. */
		
		if ($this->external == ""  && !$this->showNodenameHeader && 
			$this->metatype != METATYPE_HOME && ($isOwner || !$isProtected)) {
			
			$html .= "<div align=\"right\">[<a href=\"?node=" . SUBMIT_NODE . 
					 "&_nodename=$reqNode&_metatype=$metaType\">add to this " . 
					 "$dispMetatype node</a>]</div>";
		}


		if ($this->showRelatedNodes && sizeof($this->relatedNodes) > 0) {

			// Get rid of duplicate entries and strip out all empty values
			$this->relatedNodes = array_values(array_unique($this->relatedNodes));
			$HTMLFactory->setRelatedNodes($this->relatedNodes);
		}
		
		return $html;
	
	} // End method printNode()
	


	/* Private method to update the array $relatedNodes. It assumes a database link has
	 * already been established.
	 *
	 * Args:	$text (string)		- The text in which to search for node links */
	
	function findRelatedNodes($text) {
	
		$currRelatedNodes = "";
	
		if (preg_match_all("/<a[\=\"\?".ALLOWED_NODENAME_CHARS."]*node\=([" . 
						   ALLOWED_NODENAME_CHARS . 
						   "]*)\"*>/i", 
						   $text, $currRelatedNodes)) {
			
			$rnArrayPos = sizeof($this->relatedNodes); // Get current array length
			
			foreach ($currRelatedNodes[1] as $currRelatedNode) {
								
				$query = "SELECT DISTINCT nodename FROM $this->table " .
						 "WHERE nodename='" . 
						  htmlentities($currRelatedNode, ENT_QUOTES) . "'";

				$relatedNodeQuery = mysql_query($query, $this->dblink);
									
				if (mysql_num_rows($relatedNodeQuery) != 0) {
					$row = mysql_fetch_object($relatedNodeQuery);
					$this->relatedNodes[$rnArrayPos] = $row->nodename;
					++$rnArrayPos;
				}
			}	
							
		}

	}



	/* Private method to truncate node texts if they are too lengthy. 
	 *
	 * Args:	$text (string)		- The text that needs truncating 
	 *
	 * Returns:	$truncText (string)	- The HTML-safe, truncated text */

	function truncateText($text) {

		// Truncate the body of the text
		$truncText = substr($text, 0, SEARCH_TRUNCATE_CHARS_AFTER);
							
		// Search for unbalanced angle brackets and close if necessary
		$angbr1 = strrchr($truncText, "<");
		$angbr2 = strrchr($truncText, ">");
		if ((!$angbr2 && $angbr1) || ($angbr1 > $angbr2))
			$truncText .= ">";
								
		// Add "</tag>" for each opened tag found
		// TODO: alter to close tags left open, not all tags
		if (preg_match_all("/<(\w+)[\s\=\?\w\"]*>/i", $truncText, $matches)) {
			foreach ($matches[1] as $match)
				$truncText .= "</$match>";
		}

		return $truncText;

	}
	


	/* Retrieves all the relevant data when editing a node. 
	 *
	 * Args:	$timestamp		- The unique timestamp of the node to edit 
	 * 
	 * Returns: true (boolean)	- The data was retrieved
	 *			false (boolean) - The username and/or timestamp were not set */
	
	function editNode($timestamp) {

		if (isset($timestamp) && isset($this->username)) {
			
			$userClause = "";
			if (!$this->superuser)
				$userClause = "username = '$this->username' AND ";
			
			$query = "SELECT * FROM $this->table WHERE $userClause ts = '$timestamp'";

			$this->dblink = mysql_connect($this->host, $this->user, $this->passwd);
			$dbselect	= mysql_select_db($this->db, $this->dblink);
			$dbquery	= mysql_query($query, $this->dblink);
			$nodedata	= mysql_fetch_object($dbquery);
			
			// Set the protected flag
			if ($nodedata->protected == 't')
				$this->protected = true;

			$this->text 	= $nodedata->text;
			$this->link		= $nodedata->link;
			$this->metatype = $nodedata->metatype;

			mysql_close($this->dblink);
		
		} else
			return false;
	}



	/* Reverses the square bracket parsing done for node submits.
	 * 
	 * Returns:	$unparsedText		- The unparsed text (i.e. with [] in) */
	 
	function getUnparsedText() {
				
		// Perform reverse method to UserInput::getParsedText()
		$unparsedText = $this->text;
		$unparsedText = str_replace("\n", "", $unparsedText);
		$unparsedText = preg_replace("/<br\s*\/>/", "\n", $unparsedText);
		$unparsedText = preg_replace_callback(NODE_UNPARSE_REGEXP, 
											  array($this, 'unparseHTMLCallback'),
											  $unparsedText);
		return $unparsedText;
	}


	/* Private callback function to convert the two types of links into their respective
	 * lns-style node links:
	 *	
	 *		<a href="?node=link">text</a> --> [link|text]
	 *		<a href="?node=text">text</a> --> [text]
	 *
	 * Args:	$link (string)			- The matched HTML link text
	 *
	 * Returns:	$nodeLink (string) 		- The lns-style version of the link */

	function unparseHTMLCallback($link) {
		$nodeLink = "";
		if ($link[1] == $link[2])
			$nodeLink = "[" . $link[1] . "]";
		else
			$nodeLink = "[" . $link[1] . "|" . $link[2] . "]";
		return $nodeLink;
	}

	
} // End class NodeInterface


?>

Return current item: Lame Node System