September 24, 2020
PHP Markdown by Michel Fortin

Today I implemented Michel Fortin's PHP Markdown utility. This will help to drastically speed up writing these posts when I don't have to worry about embedding HTML for proper spacing of paragraphs, emphasizing text or inserting links. All posts on this site have now been converted to Markdown syntax. They're still being loaded from a database but all the syntax is Markdown.

I've been toying with the idea of converting the site to "static content" whereas all entries are .md files and eliminating the database, but have not convinced myself that's where I want to go just yet. If I were to do that I'd also have to get more creative with the cool search feature I talked about in the previous post.

If you're like me and have never used Markdown before, or are wondering whether it would be beneficial to you, here are some articles about the benefits of Markdown and here's a cool tutorial and a nice getting started guide.

I want to give a big shout out to Michel for developing the PHP utility and also for responding to my email within 10 minutes when I had questions about it. And of course a big thanks to Tim--from my wife most of all, I'm sure--for keeping my attention focused on my website at night instead of anything else useful. :D


September 23, 2020
Walkthrough of the search feature of this site

This site is my playground and I'm getting a kick out of building on it every day. Last night I made a search feature and showed Tim. He asked me about how it's done and then referred to me as "very data programmer" when I said I use stored procedures. :) I'll take it as a compliment. (I later learned about "static site generators" which is what his is and that's why he was wondering. His site has no database! What a concept!)

I'll give you an outline of how I'm doing the search here. Although the Smarty template system integration is new (I like Smarty; it just works), some classes are still using code I wrote over a decade ago.

First I create the stored procedure in the database. Along with search, this stored procedure will take care of displaying the entries as well as filtering and paging records, etc.

CREATE PROCEDURE `up_GetLog2Entries`(EID VARCHAR(255), OST INT, LMT INT, iST VARCHAR(25))
BEGIN
	SET @o = OST * LMT;
	SET @l = LMT;
	SET @s = iST;
	IF iST != '' THEN
		-- commented out the LIMIT clause to show all entries (paging disabled on the site, to mimic Tim's template. Might put back later.
		SET @sql = CONCAT("SELECT * FROM vw_GetLog2Entries WHERE MATCH(Body,Title) AGAINST('",@s,"*' IN BOOLEAN MODE)"); -- LIMIT ",@o,",",@l);
		PREPARE qs FROM @sql;
		EXECUTE qs;
		DROP PREPARE qs;
	ELSE
		IF EID = '' THEN
			IF @o = 0 and @l = 0 THEN -- once again, no limit clause; show all entries
				PREPARE qs FROM 'SELECT * FROM vw_GetLog2Entries';
              			EXECUTE qs;
      			ELSE 
				PREPARE qs FROM 'SELECT * FROM vw_GetLog2Entries LIMIT ?,?'; -- set limit and offset, ie: front page shows top 5 entries
				EXECUTE qs USING @o,@l;
			END IF;
			DROP PREPARE qs;
		ELSE
			SELECT * FROM vw_GetLog2Entries WHERE `Key` = EID OR PermaLink = EID; -- single entry
		END IF;
	END IF;
END

In PHP we start with an SQLWrapper class which sets up the connection and prepares the procedure caller. (This is the entire file, you're welcome to it.)

class SQLWrapper 
{
    private $mysqli;
    private $rs;
	protected $params = array();
    
    function __construct() {
        $this->mysqli = @new mysqli ("host","user","pass","database");
        if (mysqli_connect_errno()) {
			printf("Yes, I'm missing one database. Return it immediately!");
			exit();
        }
		$this->mysqli->set_charset('utf8');
	}

	function __destruct() {
		@$this->mysqli->close();
	}
	
	public function AddParameter($param) {
		$this->params[] = $param;
	}
	
	public function RunDBProcedure($p,$force_quote=false) {
		$pnum = 0;
		$ptot = count($this->params);
		$proc = "SET NAMES UTF8; ";
		$proc .= "CALL " . $p . "(";

		if ($ptot) {
			foreach ($this->params as $key=>$val) {
				if (!is_numeric($val) || $force_quote === true) {
					$quote = chr(34);
				}
				$v = (get_magic_quotes_gpc()==1?$val:addslashes($val));
				$proc .= $quote . $v . $quote;
				if ($pnum != $ptot-1)
				{
					$proc .= ",";
				}
				$pnum++;
			}
		}
		$proc .= ");";
				
		$arr = array();
		
		if ($this->mysqli->multi_query($proc)) {
			do {
				if ($result = $this->mysqli->store_result()) {
					while ($row = $result->fetch_array()) {
						$arr[] = $row;
					}
					$result->close();
				}
			} while ($this->mysqli->more_results() && $this->mysqli->next_result());
		}
		$this->params = array();
		return $arr;
	}
}

Next up, the Log class that uses the SQLWrapper to make calls to the database to retrieve the entries. Most of this class is reused from my old log.

require_once "SQLWrapper.class.php";

class Log
{
	
	/* PRIVATE VARS */
	private $title = "";
	private $base;
	private $sql = "";

	function __construct() {
		$this->base = "/writing";
		$this->sql = new SQLWrapper();
	}

	/* RETREIVE METHODS */
	function get($id='',$page=1,$limit=20,$searchterm='') {
		$this->sql->AddParameter(($id==0?'':$id));
		$this->sql->AddParameter($page-1);
		$this->sql->AddParameter($limit);
		$this->sql->AddParameter($searchterm);
		return $this->sql->RunDBProcedure("up_GetLog2Entries");
	}

There are additional functions in there to save, delete, add entries, etc., which I won't bore you with.

Next, in index.php I call the Log class and the template engine.

require_once "Smarty.class.php";
require_once "Log.class.php";

$smarty = new Smarty();
$log = new Log();

$search_term = strlen($_GET['q'])>2 ? $_GET['q'] : "";

/* WRITING */
if (isset($_GET['writing'])) {
	$smarty->assign("page","writing");
	/* SINGLE ENTRY */
	if (isset($_GET['id'])) {
		if ($search_term)
			$smarty->assign("search_term",$search_term);
		$smarty->assign("next_link",$log->get_next($_GET['id']));
		$smarty->assign("prev_link",$log->get_prev($_GET['id']));
		$smarty->assign("single",1);
		$entries = $log->get($_GET['id']);
		$smarty->assign("site_title", $entries[0]['Title'] . " - " . $page->title());
	/* LIST ENTRIES */
	} else {
		if ($search_term) {
			$smarty->assign("search_term",$search_term);
			$smarty->assign("site_title","Entries containing '" . $search_term . "' - " . $page->title());
		}
		$entries = $log->get('',0,0,$search_term);
	}
	$smarty->assign("entries_array",$entries);

/* so on and so forth */

Finally, the template file to display it all, which is a simple Smarty {section} to loop through the entries.

{if $search_term}
<p class='text-success mt-2'>Entries containing '{$search_term}'</p>
{/if}
{section name=sec1 loop=$entries_array}
<div class='mt-3'><b><a href='/writing/{$entries_array[sec1]['PermaLink']}{if $search_term}/{$search_term}{/if}'>{$entries_array[sec1]['Title']}</a></b> ยท {$entries_array[sec1]['TimeAdded']|relative_time}</div>
{/section}

That concludes the walk-through of how I do the search on this site. :) Drop me a line if you want to share your thoughts or comments.


September 20, 2020
Code to Learn with Lynx Coding

Back in the summer John participated in Code to Learn, an online course consisting of 5 one-hour sessions where he learned some very valuable skills, not only about programming logic but social skills, language and maths.

They used a platform called Lynx Coding which at the time was funded by the Canadian Government and available to students for free. I found it very intuitive and well-laid-out and there was plenty of documentation available to support.

I am happy we were able to participate in this course. John really enjoyed it as well and wanted to share his newfound skills with the world. ๐Ÿ˜Š

๐Ÿ“บ John demonstrates Code to Learn with Lynx Coding


September 19, 2020
I hit a milestone and achieved a goal

Update: 6 months later, another milestone! ๐Ÿ˜ƒ

30lbs lost in 4 months

Sidetracking for a second here to share some exciting news. Today I both hit a milestone and achieved a goal. The goal was to have my website back up and running by the time I reach the "30lbs lost" milestone.

Today both of these things were realized.

This for me is a monumental personal achievement! Today I weigh 30lbs less than back when I started this journey 4 months ago. I have not been this weight in over a decade.

If you're wondering how, this article will outline what worked for me. Please note that I am not a nutritionist or a dietary expert so take my advice with a grain of salt (hah).

The secret is very simple: I write down everything I eat now. I don't imagine I'll do this forever, as months go on I have a better grasp of what to eat and how much to eat but it's helped me keep on track. I use MyFitnessPal Premium. This app allows me to visualize macro-nutrients (carbs, fats, proteins), see my history, and keeps me pretty motivated day after day. I'm not being 100% accurate at tracking every single calorie and I suggest you don't either if you decide to do this, as it's not good for your mental health. It's easy to fall into a trap and develop a disorder if you become obsessed. Do your best and try to be as accurate as possible without sweating the small details.

The best way to get started is setting a daily limit (macros or calories). The app will help you set a goal (which you can adjust as you go along). For example let's say you set yourself on a "2000 calories a day" limit, your goal is to consume as much healthy foods as you can in a day to add up to ~2000 calories. I say healthy as you will soon learn a lot about how you eat, what kind of crap is in your food but most of all, how much you have been overeating every day. That's it. Stick to that and you'll reach your weight goal.

My wife and I have also been "meat-free" for nearly a year now. For me this decision was personal as my cholesterol was high and my doctor said--in these words--"change something or I will have to put you on a pill".

We tried the plant-based approach, which I have to tell you has worked out really well. We still eat fish and eggs, and some cheese/dairy (so it's more of a pescatarian lifestyle) but have been replacing for example a juicy, fatty beef burger with a form of this amazing black bean burger recipe. There's tons of options for non-meat-lovers out there these days.

I'm not saying you should stop eating meat, even though there's a lot of resources out there that may sway you to do so for a lot of different reasons, but I am saying be conscious of what you put in your body.

Cutting meat out of our diet did not reduce my weight and it barely reduced my cholesterol, though that's hard to judge as I found myself snacking on junk more, which probably contributed to the elevated cholesterol still.

The only thing that has worked so far was setting a limit and sticking to it. Like I said, if you do this you'll soon find out how to pack yourself with nutrients to keep you full and feeling good. Your appetite will also change and over time the cravings for crap go away.

Our plant-based diet (lifestyle, not diet) also consists of eating very minimal fried foods and processed foods. I consider "Beyond Meat" as processed food too so we don't use it as a daily alternate to meat. We are limiting pastas, breads and junk foods (chips, cookies etc). We still indulge every now and then, but stick to the recommended serving size--which for chips (crisps) is a bowl of chips, not half the bag ๐Ÿ˜„. Indulge, but indulge responsibly. It keeps good mental balance as well as physical balance.

These are the main contributors to where I am at today. Cholesterol has now dropped by 30% and I'm 30lbs lighter. The magic number 30!

That's all for now. Hope you have a great weekend!


September 19, 2020
Calculating Good Friday in T-SQL

A few years back I had to generate a table using T-SQL that lists Canadian holidays for the upcoming x number of years. This was part of our migration from our old Nortel phone system to Skype for Business Public Telephony. This requirement was for programming the after-hours and holiday menus for the upcoming number of years. I think back to this task every now and then because it was a fun little challenge.

Most of the holidays are simple to calculate because they fall on the same day every year but Easter or Good Friday are based on the Paschal Full Moon following the spring equinox, which changes year to year. So here's a script based on this post, which is based on a script from NOAA, to calculate Good Friday:

CREATE FUNCTION dbo.uf_GetGoodFriday (@Year INT)
RETURNS datetime 
WITH EXECUTE AS CALLER
AS 
BEGIN 
	DECLARE @intYear INT, @EpactCalc INT, @PaschalDaysCalc INT, @NumOfDaysToSunday INT, @EasterMonth INT, @EasterDay INT 

	SET @EpactCalc = (24 + 19 * (@Year % 19)) % 30 
	SET @PaschalDaysCalc = @EpactCalc - (@EpactCalc / 28) 
	SET @NumOfDaysToSunday = @PaschalDaysCalc - ((@Year + @Year / 4 + @PaschalDaysCalc - 13) % 7) 

	SET @EasterMonth = 3 + (@NumOfDaysToSunday + 40) / 44 
	SET @EasterDay = @NumOfDaysToSunday + 28 - (31 * (@EasterMonth / 4)) 

	RETURN ( 
		SELECT DATEADD(dd,-2,CONVERT(DATETIME, CONVERT(VARCHAR(2),@EasterMonth) + '/' + CONVERT(VARCHAR(2),@EasterDay) + '/' + CONVERT(VARCHAR(4), @Year)))
	) 
END
GO

Trying it out for next year:

SELECT uf_GetGoodFriday (2021)

Returns:

2021-04-02 00:00:00.000

September 17, 2020
Hello, world!

Welcome to the newest rendition of my website. This is version 4--or maybe 24 by now, it's hard to keep track--and if you're saying to yourself "hey, this layout looks familiar" you must've seen my friend Tim's page and realized that this is pretty much a copy of his site. And you'd be right, the layout definitely is a direct copy (the back end, not so much). His site is actually available at GitHub if you're cool enough to be in the Ruby on Rails crowd. I am unfortunately not so it's just PHP in the back end, Smarty in the middle, and Bootstrap in the front here.

I plan to build on this in the future (some more functionality, etc.) but as a foundation, this pretty much suits what I wanted to get out of this space perfectly. Thanks Tim!

If you want, you can find the previous iteration of my site, now frozen in time, over at 2020.miklos.ca

I can't believe it's been over 7 years since I've done anything in this space. So much has happened! The last I left it, I got married, we then had a baby, bought a new car, moved to a new house, yadda yadda yadda and here we are.

Anyway, I hope you enjoy your stay.