Guestbook using myPHP Guestbook

From TNG_Wiki
Jump to navigation Jump to search

A very good option for creating a guestbook for a TNG site, especially given the security and support issues of Lazarus, is the software entitled "myPHP Guestbook." It is developed by Wolfgang Leverberg, who is very conscientious and helpful. myPHP Guestbook provides very good security and spam protection and handles recent versions of PHP. You can see the guestbook in action on the genealogy site at (but please be sure to sign the guestbook! ;-)

You can download the myPHP Guestbook software at There are two zip files to download (the second one needed for the embedded version described below). The installation instructions are partly in German.

To include within your TNG site, you can use myPHP Guestoook on your site via iframes, or with a little more work, you can embed the PHP directly into a TNG template. Both approaches use a template file modeled after featuretemplate.php.

iframe Version

The iframe version is easier/cleaner, but there may be some scrolling issues on some systems. For example, on a Mac desktop, you may have to contend with an inner and an outer scrolling bar. On an iPhone, things work seem to work perfectly with the iframe approach. You can test a working version at

To implement the iframe version, install the myPHP Guestbook code by following Wolfgang Leverberg's instructions, including putting the file iframe-style.css into your top-level TNG folder (where your TNG files reside). In that same folder, put the file gbookiframe.php. You can get a copy of a working version of gbookiframe.php by going to and then changing the name of the file to end with .php rather than .txt. It assumes that the guestbook folder (which contains the other myPHP Guestbook files) is named myPHPGuestbook, so you should follow that convention. In other words, gbookiframe.php and the folder myPHPGuestbook should both be in the top-level TNG folder.

The gbookiframe.php file is based upon the featuretemplate.php TNG file. It includes the necessary CSS definitions, iframe tag, and javascript call.

The only other thing you need to do is to do what is described in the section further below on "Splitting the tng_header function."

Embedded PHP Version

The website uses the embedded PHP version of myPHP Guestbook. You can test it out at

To implement the embedded PHP version, use the two files gbook.php and gbook_insert.php, which have to be placed in the top-level TNG folder (where your TNG files reside). You can download a working version at and and then change the filenames to end with .php rather than .txt. They assume that the guestbook subfolder is named myPHPGuestbook, so you should follow that convention.

Splitting the tng_header function

The other thing you need to do for both the iframe version and the embedded PHP version is to split up the function tng_header that appears in genlib.php into two subfunctions, which called tng_header_head and tng_header_body. (A call to the function tng_header will still work the same as before by calling the two subfunctions. You also have the flexibility to make the calls to the two sub functions separately, which allows you to put stuff in between the two calls, which you want to do for myPHPGuestbook in order to put some stuff into the <head> section. The <body> section doesn't start until tng_header_body is called.)

The updated code for tng_header is provided below. The yellow-highlighted code is the new part added; the rest is the same as the original:

// Broke up tng_header into two parts: 
// the first to open and fill head tag, 
// the second for closing head tag and opening 
// body tag and filling info
function tng_header( $title, $flags ) {
 	global $custommeta, $customheader, $cms, $session_charset, $tngprint, $sitename, $site_desc, $tngconfig, $tngdomain, $responsivetables;
 	global $text, $map, $browser, $templatepath, $tng_title, $tng_version, $tng_date, $tng_copyright, $sitever, $templatenum, $tmp, $http, $isConnected;
 	global $fbOGimage, $pageURL;
	tng_header_head( $title, $flags ); 
	tng_header_body( $title, $flags ); 
// JSV: New subfunction
function tng_header_head( $title, $flags ) {
	global $custommeta, $customheader, $cms, $session_charset, $tngprint, $sitename, $site_desc, $tngconfig, $tngdomain, $responsivetables;
	global $text, $map, $browser, $templatepath, $tng_title, $tng_version, $tng_date, $tng_copyright, $sitever, $templatenum, $tmp, $http, $isConnected;
	global $fbOGimage, $pageURL, $icons; // declare $icons as global 

	//echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\">\n\n";
	//echo "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n\n";
	header("Content-type:text/html;charset=" . $session_charset);
	echo !empty($tngconfig['doctype']) ? $tngconfig['doctype'] . "\n\n" : "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \n\"\">\n\n";
	if( !$cms['support'] )
		echo "<html xmlns=\"\">\n<head>\n";
		echo $cms['credits'];
	//$siteprefix = $sitename ? ($title ? ": " . stripslashes($sitename) : stripslashes($sitename)) : "";
	//$title = preg_replace("/\"/", "",$title);
	$siteprefix = $sitename ? @htmlspecialchars($title ? ": " . $sitename : $sitename, ENT_QUOTES, $session_charset) : "";
	$title = @htmlspecialchars($title, ENT_QUOTES, $session_charset);
	echo "<title>$title$siteprefix</title>\n";
	echo "<meta name=\"Keywords\" content=\"$site_desc\" />\n";
	//echo "<meta name=\"Description\" content=\"" . preg_replace("/\"/", "", $title . $siteprefix) . "\" />\n";
	echo "<meta name=\"Description\" content=\"" . $title . $siteprefix . "\" />\n";

	if($fbOGimage) {
	    echo "<meta property=\"og:title\" content=\"" . $sitename . "\"/>\n";
	    echo "<meta property=\"og:description\" content=\"" . $title . "\"/>\n";
	    echo "<meta property=\"og:url\" content=\"" . $tngdomain . "/" . $pageURL . "\" />\n";
	    echo $fbOGimage."\n";

	if( $session_charset )
		echo "<meta http-equiv=\"Content-type\" content=\"text/html; charset=$session_charset\" />\n";
	if( isset( $flags['norobots'] ) )
		echo $flags['norobots'];
	if( isset( $flags['autorefresh'] ) && $flags['autorefresh'] == 1 )
		echo "<meta http-equiv=\"refresh\" content=\"30\" />\n";
	if($sitever == "mobile" || $sitever == "tablet") {
		echo "<meta name=\"HandheldFriendly\" content=\"True\" />\n";
		echo "<meta name=\"MobileOptimized\" content=\"320\" />\n";
		echo "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n";
		echo "<meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\n";
		echo "<meta http-equiv=\"cleartype\" content=\"on\" />\n";

		echo "<link rel=\"apple-touch-icon-precomposed\" sizes=\"144x144\" href=\"$tngdomain/img/tng-apple-icon-144.png\"/>\n";
		echo "<link rel=\"apple-touch-icon-precomposed\" sizes=\"114x114\" href=\"$tngdomain/img/tng-apple-icon-114.png\"/>\n";
		echo "<link rel=\"apple-touch-icon-precomposed\" sizes=\"72x72\" href=\"$tngdomain/img/tng-apple-icon-72.png\"/>\n";
		echo "<link rel=\"apple-touch-icon-precomposed\" href=\"$tngdomain/img/tng-apple-icon.png\"/>\n";
		echo "<link rel=\"shortcut icon\" href=\"$tngdomain/img/tng-apple-icon.png\"/>\n";
		echo "<link rel=\"shortcut icon\" href=\"$tngdomain/{$tngconfig['favicon']}\"/>\n";

	if(!$tng_version) $tng_version = "13.0.1";
	if($sitever != "standard" && $responsivetables) 
		echo "<link href=\"{$cms['tngpath']}css/tablesaw.bare.css\" rel=\"stylesheet\" type=\"text/css\" />\n";
	echo "<link href=\"{$cms['tngpath']}css/genstyle.css?v=$tng_version\" rel=\"stylesheet\" type=\"text/css\" />\n";
	if( isset( $flags['tabs'] ) )
		echo "<link href=\"{$cms['tngpath']}{$templatepath}css/{$flags['tabs']}?v=$tng_version\" rel=\"stylesheet\" type=\"text/css\" />\n";
	echo "<link href=\"{$cms['tngpath']}{$templatepath}css/templatestyle.css?v=$tng_version\" rel=\"stylesheet\" type=\"text/css\" />\n";
	if($sitever == "mobile") {
		echo "<link href=\"{$cms['tngpath']}css/tngmobile.css?v=$tng_version\" rel=\"stylesheet\" type=\"text/css\" />\n";
		echo "<link href=\"{$cms['tngpath']}{$templatepath}css/tngmobile.css?v=$tng_version\" rel=\"stylesheet\" type=\"text/css\" />\n";
	if($isConnected) {
		echo "<script src=\"\" type=\"text/javascript\" integrity=\"sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh\" crossorigin=\"anonymous\"></script>\n";
		echo "<script src=\"\" type=\"text/javascript\" integrity=\"sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=\" crossorigin=\"anonymous\"></script>\n";
	else {
		echo "<script type=\"text/javascript\">// <![CDATA[\nwindow.jQuery || document.write(\"<script src='{$cms['tngpath']}js/jquery-3.4.1.min.js?v=910'>\\x3C/script>\")\n//]]></script>\n";
		echo "<script type=\"text/javascript\">// <![CDATA[\nwindow.jQuery.ui || document.write(\"<script src='{$cms['tngpath']}js/jquery-ui-1.12.1.min.js?v=910'>\\x3C/script>\")\n//]]></script>\n";
	echo "<script type=\"text/javascript\" src=\"{$cms['tngpath']}js/net.js\"></script>\n";

 	if( isset( $flags['scripting'] ) )
		echo $flags['scripting'];
	echo "<link href=\"{$cms['tngpath']}{$templatepath}css/mytngstyle.css?v=$tng_version\" rel=\"stylesheet\" type=\"text/css\" />\n";

	if(!empty($tngconfig['showshare']) && $isConnected && $sitever != "mobile") {
		$w = $http == "https" ? "ws" : "w";
		echo "<script type=\"text/javascript\" src=\"{$http}://{$w}\"></script>\n";
		echo "<script type=\"text/javascript\">stLight.options({publisher: \"be4e16ed-3cf4-460b-aaa4-6ac3d0e3004b\",doNotHash:true,doNotCopy:true,hashAddressBar:false});</script>\n";

	//echo "<script type=\"text/javascript\" src=\"{$http}://\"></script>\n";
	if( $tngconfig['menu'] < 2 && !$tngprint && $sitever != "mobile" ) {
		echo "<script type=\"text/javascript\" src=\"{$cms['tngpath']}js/tngmenuhover2.js\"></script>\n";

	if ($sitever != "standard" && $responsivetables) {
		echo "<script type=\"text/javascript\" src=\"{$cms['tngpath']}js/tablesaw.js\"></script>\n";
		echo "<!--[if lt IE 9]>";
		echo "<script type=\"text/javascript\" src=\"{$cms['tngpath']}js/respond.js\"></script>\n";
		echo "<![endif]-->";

	echo "<script type=\"text/javascript\">\n// <![CDATA[\nvar tnglitbox;\nvar share = 0;\nvar closeimg = \"{$cms['tngpath']}img/tng_close.gif\";\n";
	echo "var smallimage_url = '" . getURL("ajx_smallimage",1) . "';\nvar cmstngpath='{$cms['tngpath']}';\nvar loadingmsg = '{$text['loading']}';\n";
	echo "var expand_msg = \"{$text['expand']}\";\nvar collapse_msg = \"{$text['collapse']}\";\n";
	if(isset($flags['error']) && $flags['error']) {
		$login_url = getURL( "ajx_login", 1 );
		echo "jQuery(document).ready(function(){openLogin('{$login_url}p=" . urlencode($cms['tngpath']) . "&message={$flags['error']}');});\n";
	echo "//]]>\n</script>\n";
	//echo "<style type=\"text/css\">.media-prev {background: transparent url({$cms['tngpath']}img/media-prevbg.png) no-repeat 0 0;}\n.person-prev {background: transparent url({$cms['tngpath']}img/person-prevbg.png) no-repeat 0 0;}</style>\n";
	echo "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"{$cms['tngpath']}tngrss.php\" />\n";

	if(!empty($tngconfig['cookieapproval']) && strpos($_SERVER['REQUEST_URI'],"/data_protection_policy.php") === FALSE)
		include($cms['tngpath'] . "cookie_approval.php");

	@include( $custommeta );
	if( $tngprint )
		echo "<link href=\"{$cms['tngpath']}css/tngprint.css\" rel=\"stylesheet\" type=\"text/css\" />\n";
	echo "<!-- $tng_title, v.$tng_version ($tng_date), Written by Darrin Lythgoe, $tng_copyright -->\n";

	$icons = "";
	if($sitever == "mobile") {
		if(!isset($flags['nomobile']) || !$flags['nomobile']) {
			$icons = tng_mobileicons($title);
			$icons .= "<div id=\"mcontent\">\n";
		echo "<style>\n{$tngconfig['mmenustyle']}</style>\n";

// JSV: New subfunction
function tng_header_body( $title, $flags ) {
	global $custommeta, $customheader, $cms, $session_charset, $tngprint, $sitename, $site_desc, $tngconfig, $tngdomain, $responsivetables;
	global $text, $map, $browser, $templatepath, $tng_title, $tng_version, $tng_date, $tng_copyright, $sitever, $templatenum, $tmp, $http, $isConnected;
	global $fbOGimage, $pageURL, $icons;  // declare $icons as global

	// JSV: The rest is the remainder of code from the original tng_header
	if( !$cms['support'] ) {
		echo "</head>\n";
		if( $sitever != "mobile" && !$tngprint && (!isset($flags['noheader']) || !$flags['noheader']) )
			include( $templatepath . $customheader );
		elseif(!isset($flags['nobody']) || !$flags['nobody'] || $sitever == "mobile") {
			$class = !empty($flags['homeclass']) ? $flags['homeclass'] : "publicbody";
			echo "<body class=\"{$class}\">\n";
			echo "<div class=\"scroll-to-top\"><a href=\"#\"><img src=\"{$cms['tngpath']}img/backtotop.png\" alt=\"\" /></a></div>\n";
	if( $sitever != "mobile" && (!isset($flags['noicons']) || !$flags['noicons']) )
		$icons = tng_icons( 1, $title );
	echo $icons;  //from above

	if(!$cms['support'] && $sitever == "mobile" && !$tngprint && (!isset($flags['noheader']) || !$flags['noheader'])) {
		$ttitle = "t{$templatenum}_maintitle";
			$mtitle = str_replace(array("<br />","<br>")," ", getTemplateMessage($ttitle));
		else {
			$ttitle = "t{$templatenum}_headtitle";
			$i = 1;
			$mtitle = "";
			while(isset($tmp[$ttitle . $i])) {
				$mtitle .= getTemplateMessage($ttitle . $i) . " ";
			echo "<h3 class=\"mmaintitle\">" . $mtitle . "</h3><hr class=\"mtitlehr\"/><br/>\n";
		echo "<span class=\"fieldnameback yellow\" style=\"padding:3px\"><strong>{$text['mainton']}</strong></span><br /><br />\n";

Other Tweaks

If desired (but not necessary), here are some other tweaks that appear in the guestbook implementation at

  • I added some modified language to the posting form and added a few blank lines to the top of the reply box so that the user will more clearly know where to type his/her posting. The following modified code goes into files insert.body.php (replacing lines 994–1000) for the embedded PHP version and to insert.php (replacing lines 1247–1253) for the iframe version. (Note that these particular line numbers are for version 4.10.6 of myPHP Guestbook; the line numbers may change for subsequent versions.)
// Modified by JSV to add a hint to user where to type message
//		echo"
//			<div class=\"break\"></div>
//			<div id=\"msText\"".$mark_text2."><textarea id=\"text\" name=\"text\"".
//			  $mark_text." oninput=\"".$textCounter."checkText()\" rows=\"".$rows.
//			  "\" cols=\"25\" placeholder=\"(".$fmsg[348].")\" ".$textareaMarg.">";
			<div class=\"break\"></div>
			<div id=\"msText\"".$mark_text2.">"."Please type your message immediately below";
        if (isset($text_quote) AND $text_quote != "") echo " (i.e., above the text already there):";
        else echo ":";
        echo "<textarea id=\"text\" name=\"text\"".
			  $mark_text." oninput=\"".$textCounter."checkText()\" rows=\"".$rows.
			  "\" cols=\"25\" placeholder=\"(".$fmsg[348].")\" ".$textareaMarg.">";

	if (isset($text_quote) AND $text_quote != "") {
// Modified by JSV to add some lines at top of text box so user knows where to reply
//		echo "[zitat=".$name_quote." ".$amsg[86]." ".$date_quote.
//		  "| ".$time_quote."]".$text_quote."".$comment_quote."[/zitat]";
		echo "\r\n\r\n\r\n\r\n\r\n"."[zitat=".$name_quote." ".$amsg[86]." ".$date_quote.
		  " | ".$time_quote."]".$text_quote."".$comment_quote."[/zitat]";
  • Another modification is that I changed the word "comment" to "message" in several of the terms defined in the file myPHPGuestbook/lang/en.php.
  • For the PHP-embedded version of the guestbook, the site credits at the very bottom of the webpage do not print correctly on the posting page because of conflicting use of the PHP variable $temp by both TNG and myPHP Guestbook. I modified stdsitecredit.php to use the PHP variable $JSVtext rather than $text for various text strings. The strings involving $JSVtext are defined in the cust_text.php files in the various language directories, such as languages/English-UTF8.

TNG Sites using myPHP Guestbook

If you have installed myPHP Guestbook, please add your TNG site to the table below:

URL User Note myPHP Guestbook Version TNG Version TNG User languages
Vitter-Weaver Genealogy Jeff Vitter PHP 7.3.27 4.10.6 13.0.3 EN, FR, DE, IT, NO, CH, ES
Wilkinson Family Nick Wilkinson 4.10.6 13.0.3 EN