Feedback script

There's a new component on the estate!

It will be named the feedback script.

The idea is simple, it should be non-intrusive, and easy to respond to.

That's why there is only a small edge in the bottom to indicate there is something, and as the user hover over it, it will show the entire div

So how is this done?

This sounds like one of those cases that should be simple, we just wish somebody else already made it and not having to bother with it.

Well, here it is

CSS

#tabfeedback
{
  text-align:center;
  height: 10px; 
  width: 100%; 
  position: fixed; 
  bottom:0px;
  left:0px; 
  z-index: 1;
  background-color:white;
}
#tabfeedback:hover
{
  height: 50px; /* Increase height on hover */ 
  z-index: 1;
  transition:height 0.5s /* Introduces animation */
}

Now on to the HTMLAdd this anywhere in the page

<div id="tabfeedback">
<span id="spanfeedback">Do you like this page?</span>
	<a href="#" onclick="upvote();return false;">✔</a> 
	<a href="#" onclick="downvote();return false;">x</a>
  <div id="comment" class="hidden">
    	<input id="txt" type="text" onkeypress="sendfbtext(this, event);" />
  </div>
</div>

The magic in upvote(), downvote() and sendbftext() is in the javascript

function vote(data)
{
$.ajax({
      url: "php/feedback.php", //use your own php path here
      type: 'POST',
      data: data,
      dataType: 'json'
      });
}
function sendfbtext(obj, event)
{
	if (event.keyCode == 13)
	{
		//send data
		var data = {action:'down', comment: obj.value};
		vote(data);
		$("#comment").addClass("hidden");
	}
}

function upvote()
{
     var data = {action:'up'};
     vote(data);     
     $("#spanfeedback").text("Thank you for the vote of confidence.");
     $("#comment").css("display:inline-block");
}
function downvote()
{
    var data = {action:'up'};
    vote(data);
    $("#spanfeedback").text("Thank you for your feedback.");
    $("#comment").removeClass("hidden");
}

Don't forget to include jQuery

Now onto your php script.

This will be a multi-part script

1) The database script (db.php) is used to simplify connection, you might already have your own database module, so it's best to use your judgment for that

<?php
class DBConn
{
   private $url;
   private $user;
   private $pass;
   private $db;
   private $conn;

   function __construct($url, $user, $pass, $db)
   {
    $this->url = $url;
    $this->user = $user;
    $this->pass = $pass;
    $this->db = $db;
   }

   function __destruct()
   {
     mysqli_close($this->conn);
   }

   function connect()
   {
     $this->conn = mysqli_connect($this->url, $this->user,$this->pass,$this->db) or die("Error ".mysqli_error($this->conn));
     return $this;
   }
  
   function query($query)
   {
	return $this->conn->query($query);
   }

   function execute($code, $a_param_type, $a_bind_params)
   {
      $stmt = $this->conn->prepare($code);
      if (isset($a_param_type) && !empty($a_param_type) && isset($a_bind_params) && !empty($a_bind_params))
      {
	
	//curtesy of http://www.pontikis.net/blog/dynamically-bind_param-array-mysqli
	/* Bind parameters. Types: s = string, i = integer, d = double,  b = blob */
	$a_params = array();
	 
	$param_type = '';
	$n = count($a_param_type);
	for($i = 0; $i < $n; $i++) {
	  $param_type .= $a_param_type[$i];
	}
	 
	/* with call_user_func_array, array params must be passed by reference */
	$a_params[] = & $param_type;
	 
	for($i = 0; $i < $n; $i++) {
	  /* with call_user_func_array, array params must be passed by reference */
	  $a_params[] = & $a_bind_params[$i];
	}
      	call_user_func_array(array($stmt, 'bind_param'), $a_params);
      }

      if (!$stmt)
      {
         echo "Error:".$this->conn->error;
      }
      return $stmt->execute();
   }
   
   function executeNoParam($code)
   {
      return $this->execute($code, null, null);
   }

   function getConn()
   {
     return $this->conn;
   }
}

?>

2) Install script

<?php
include('common/db.php');
$configs = parse_ini_file("configs/configs");

$db = new DBConn($configs['path'],$configs['user'],
		   $configs['password'], '');
$conn = $db->connect()->getConn();
$dbname = $configs['database'];

$query = "CREATE DATABASE IF NOT EXISTS ".$dbname;

$db->executeNoParam($query) or die ("ERROR ".mysqli_error($conn));
mysqli_select_db($conn, $dbname);

$query = "CREATE TABLE counter(
  id INT(11) NOT NULL AUTO_INCREMENT,
  action VARCHAR(5),
  source VARCHAR(500),
  comment VARCHAR(500),
  ip VARCHAR(20),
  PRIMARY KEY(id)
)";

$db->executeNoParam($query) or die ("ERROR ".mysqli_error($conn));;
echo "Install complete";
?>

3) Insert Script (feedback.php)

<?php
include('common/db.php');
$configs = parse_ini_file("configs/configs");

$db = new DBConn($configs['path'],$configs['user'],
		   $configs['password'], $configs['database']);

$conn = $db->connect()->getConn();
$query = "INSERT INTO counter (action, source, comment,ip) VALUES (?,?,?,?)";

$us_action = '';
$us_source = $_SERVER['HTTP_REFERER'];
$us_comment = '';

if (isset($_POST['action']))
{
  $us_action = $_POST['action'];
}
if (isset($_POST['comment']))
{
  $us_comment = $_POST['comment'];
}

$action = mysqli_real_escape_string($conn, $us_action);
$comment = mysqli_real_escape_string($conn, $us_comment);
$source = $us_source;

$db->execute($query, array('s','s','s','s'), 
		     array($action, $source, $comment, $_SERVER['REMOTE_ADDR'])
	    );
?>

4) View Script (viewfeedback.php)

<?php
include('common/db.php');
$configs = parse_ini_file("configs/configs");

$db = new DBConn($configs['path'],$configs['user'],
		   $configs['password'], $configs['database']);

$conn = $db->connect()->getConn();
$query = "SELECT action, source, comment FROM counter";

$res = $db->query($query);

if ($res)
{
?>
	<HTML>
	<HEAD>
	<BODY>
		<table>
<?php
   while ($obj = $res->fetch_object())
   {
	echo "<tr><td>".$obj->action."</td><td>".$obj->source."</td><td>".$obj->comment."</td></tr>";
   }
?>		</table>
<?php
}
?>

 

 

 

Case for a static page generator

In case you haven't read about it, I made myself a static page generator.

But why? Why go through all the trouble?

Well, it started out to be very naive. I didn't like the idea of having to maintain the menu on every page, and it was really not so much trouble. I spent about a weekend to put together what I'm using now, there are still many tweaks I'd like to do but it's sufficient for now. Now, it feels very fluid.

So why not use a dynamic language like php?

The thing is I didn't want to use a framework, it would have been a much bigger learning curve. Plus I had to choose one and stick with it. In fact, I'm really wondering why use dynamic pages for personal pages anymore because we can use JQuery to manipulate the DOM objects just as easily on the client side.

And if a call to the server is needed, make an Ajax call to the .php page. Since this isn't an enterprise app, I won't need a lot of database dependent logic on the backend, a HTML site with light backend is more than enough. In fact, NibbleBlog doesn't even use a database

What does the static generator do?

Wow, I just realized I haven't described what it does. Well, it reduces amount of maintenance I have to do, now that I trust the content it generates, every time I make a change to the menu. Running the script will rebuild the menu to every page that needs it, consistently.

So before I FTP it, I could run a script to change localhost to stanleywu.ca....... I suppose awk would do just as well, but oh well, I use the generators to update my links when a new page is created.

There is also a bit of templating going on now, my home page is now updated using a Dashboard generation script.

How does it work?

Currently I have two generation scripts that are packaged into a bash script.
One used for menu generation, and another for general templating purpose.

Every time I create a new page, I would modify a listing file to add the new page. The file looks something like this

Text Templating::codereplace.html
Magic 8 Ball::magicball.html
Memory Test::memory.html

Very simple syntax, I use :: as delimiters. There surely will be corner cases where it will break things, but hey, there are plenty of other static generators that you can use.

The main script looks at its own file and figure out which directory it needs to traverse to.
And it goes into those directories to build the menu.

Sample

Extensions:html;php;htm;
DirExclude:style;js
Logo:<a href="{ROOTPATH}"><img src="{ROOTPATH}/img/logo.png" /></a>

~BEGIN:
Home
--d::0
--l::index.html
Utilities
--d::2
--f::utilities
Swu's lab
--d::2
--f::labs
Notes
--d::0
--l::nibble

Of course, I'm not using Separation of Concern very well. It wasn't really my concern :)

So far, only the homepage uses the templating script since I use that page like a menu. Surprisingly it was made generic enough to use a Model-View architecture

Next up: To allow each page to use different templates. I might never have to do that, so it will be done when I need it.

What language?

I chose Python, not that I like Python a lot or that I'm an expert, I just wanted to be familiar with it since everyone knows Python now (but hey, WebAssembly is increasing applicability of C++, YAY~~) and it came pre-installed with my new Linux-Mint installation.

Conclusion?

I didn't feel that strongly about it, but now that I've written this, I feel EVERYONE should write a static page generator for the following reasons

  • Seemless integration with nibbleblog :)
  • You get the practice and experience
  • Doesn't take a whole lot of time because it's okay for it to be less than perfect
  • Changing environments is as easy as running a script
  • Updates pages consistently
  • It fits YOUR style exactly
  • No need to learn another generator unless you desire fancy functionalities, in that case, isn't it better to use a dynamic language like php or RoR
  • It might turn out to be more powerful than expected!

Github: Static Generator Repository

Home
Do you like this page? x