Yeehoo 👍💃✌

WordPress finally got support for emojis.

đŸ˜ƒđŸ°đŸ€đŸ±đŸŒđŸș🚮➰

How to build an announcement system with php, mysql, jquery and jgrowl

I have been thinking of to build an announcement system to inform users on my pages instead of sending email. There are many ways to do it, but I stumbled to Stanlemons jGrowl which perfectly fit my needs, and some days you feel more serious than others so I took the chance and documented how I did it, hopefully this will help someone out there.

Demo

I use two tables in MySQL – announcements and announcements_userlog


lol

announcements – where the announcements will be stored

CREATE TABLE IF NOT EXISTS `announcements` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`time` datetime NOT NULL,
`severity` enum('1','2','3','4') NOT NULL DEFAULT '1',
`message` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);

Example data:

INSERT INTO announcements (time, severity, message) VALUES (NOW(), '1', 'Important! Dont forget to say hello to the cat.');
INSERT INTO announcements (time, severity, message) VALUES (NOW(), '2', 'People WILL doubt you. Your goal is to prove them wrong. Work hard, stay positive, and BE CONFIDENT.');
INSERT INTO announcements (time, severity, message) VALUES (NOW(), '1', 'Admitting failure is okay, but accepting failure is NOT.');

announcements_userlog – where we log when users read the announcements

CREATE TABLE IF NOT EXISTS `announcements_userlog` (
  `id` smallint(6) NOT NULL,
  `userId` varchar(100) NOT NULL,
  `time` datetime NOT NULL,
  PRIMARY KEY (`id`,`userId`)
)

SQL to list unread announcements:

SELECT * FROM announcements WHERE id NOT IN(SELECT id FROM bah_announcements_userlog WHERE userId=123)

The main thing in frontend is the jGrowl call:

$.jGrowl("Message to users", { 
	sticky: true, 
	close: function(e,m,o) { 
		$.post('save.php', {id: variable, userId: 'variable'}); 
	}, 
	themeState: 'error' // error or highlight from jquery ui
});

When users click on close it will be stored in our table announcements_userlog.

Example frontend

<?php
$DBhost = "localhost";
$DBuser = "announcementdemo";
$DBpass = "";
$DB = "";
$link = mysql_connect($DBhost, $DBuser, $DBpass) or die("Kan inte ansluta..");
mysql_select_db($DB, $link);
?>
<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<title>Announcement demo</title>
	<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.2.12/jquery.jgrowl.min.css" />
	<link href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.min.css" rel="stylesheet">
	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
	<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
	<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.2.12/jquery.jgrowl.min.js"></script>
	<script>
	$(function(){
		<?php
		if(!empty($_GET['userId'])){
			$userId = $_GET['userId'];
			/* build jgrowls */
			$sql = "SELECT id, time, message, severity FROM announcements WHERE id NOT IN(SELECT id FROM announcements_userlog WHERE userId='$userId')";
			$res = mysql_query($sql, $link);
			while($row = mysql_fetch_row($res)){
				if($row[3] == 1){ // Severity
					$themeState = "error"; // jquery ui
				} else {
					$themeState = "highlight";
				}
				print "$.jGrowl(\"<span class='ui-icon ui-icon-alert' style='float: left; margin-right: .3em;'></span>$row[2]\", { sticky: true, closer: false, close: function(e,m,o) { $.post('save.php', {id: $row[0], userId: '$userId'}); }, themeState: '$themeState' });\n";
			}
		}
		?>
	});
	</script>
</head>
<body>
<strong>Demo</strong> - Announcement System
<?php
if(empty($_GET['userId'])){
	$userId = uniqid();
	print "Click <a href='?userId=$userId'>here</a> to simulate yourself as a user";
} else {
	print "Identified as user <strong>{$_GET['userId']}</strong><br />";
	print "Refresh this page when you have closed on or more notifications.<br />";
	print "Last 5 rows in table announcements_userlog:";
	$sql = "SELECT id, userId, time FROM announcements_userlog ORDER BY time DESC LIMIT 5";
	$res = mysql_query($sql, $link);
	print "<table>";
	print "<tr><th>id</th><th>userId</th><th>time</th></tr>";
	while($row = mysql_fetch_row($res)){
		print "<tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td></tr>";
	}
	print "</table>";
	$userId = uniqid();
	print "Click <a href='?userId=$userId'>here</a> to identify yourself as another user";
}
?>
</body>
</html>

save.php example:

<?php
$DBhost = "localhost";
$DBuser = "";
$DBpass = "";
$DB = "";
$link = mysql_connect($DBhost, $DBuser, $DBpass) or die("Kan inte ansluta..");
mysql_select_db($DB, $link);

$id = $_POST['id'];
$userId = $_POST['userId'];
mysql_query("INSERT INTO announcements_userlog(id, userId, time) VALUES($id, '$userId', NOW())", $link);
?>

Cheers

How to track your snooze habits

Me and a friend of mine often talk aboute snooze habits and how cool it would be if it was possible to have statistics about it.

So I build a one.

Requirements (for my example):

  • Android phone or tablet
  • Android app: Tasker
  • Android app: Gentle Alarm
  • MySQL database
  • Webserver with PHP
Back-end – PHP and MySQL

Create a table in any database, this is my design:

CREATE TABLE IF NOT EXISTS `snoozelog` (
  `id` smallint(6) NOT NULL AUTO_INCREMENT,
  `sessionId` smallint(5) NOT NULL,
  `timestamp` int(11) NOT NULL,
  `type` tinyint(4) NOT NULL COMMENT '1=start, 2=snooze, 3=stopp',
  `location` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=29 ;

PHP-script that puts data to our table, script.php:

$DBhost = "localhost";
$DBuser = "xxx";
$DBpass = "xxx";
$DB     = "test";
$link = mysql_connect($DBhost, $DBuser, $DBpass);
mysql_select_db($DB);

// Generate och check alarm session
switch($_GET['Type']){
	case 1: // New session (alarm begin)
		$sql = "SELECT MAX(sessionId)+1 FROM snoozelog";
		$res = mysql_query($sql, $link);
		$row = mysql_fetch_row($res);
		$sessionId = $row[0];
		break;
	case 2: // Snooze
		$sql = "SELECT MAX(sessionId) FROM snoozelog";
		$res = mysql_query($sql, $link);
		$row = mysql_fetch_row($res);
		$sessionId = $row[0];
		break;
	case 3: // End of session
		$sql = "SELECT MAX(sessionId) FROM snoozelog";
		$res = mysql_query($sql, $link);
		$row = mysql_fetch_row($res);
		$sessionId = $row[0];
		tweet($sessionId); // Send to twitter
		break;
}
if(empty($sessionId)){
    $sessionId = 1;
}
$sql = "INSERT INTO snoozelog(sessionId, timestamp, type, location) VALUES($sessionId, '{$_GET['Time']}', {$_GET['Type']}, '{$_GET['Loc']}')";
$res = mysql_query($sql, $link) or die(mysql_error($link));

I recommend to have some kind of authentication to this php-script.

Gentle Alarm
  1. Install Gentle Alarm on your device
  2. Create a profile and name it to “Logtodatabase”. This is optional, but it is necessary if you want alarms not to be logged.
Tasker

You need to create four tasks (and yes, you need two snooze profiles):
Click on the links to download. You need to edit www.yoursite.com and script.php
After that you can simply import them to Tasker
Alarm Begin
Alarm Snoozed
Alarm Snoozed
Alarm Dismissed

Now you are ready to try it out. So what is happening when your alarm starts?
Tasker execute a HTTP GET to www.yoursite.com. The complete URL well be something like
http://www.yoursite.com/script.php?Type=1&Time=1350051834&Loc=62.40961319391678,17.305160156472468

Front-end
Here is an example how you can show some data from your latest alarm.
show-latest.php

$DBhost = "localhost";
$DBuser = "xxx";
$DBpass = "xxx";
$DB     = "test";
$link = mysql_connect($DBhost, $DBuser, $DBpass);
mysql_select_db($DB);

$snooze = 0;
$sql = "SELECT timestamp, type, location FROM snoozelog WHERE sessionId=(SELECT MAX(sessionId) FROM snoozelog) ORDER BY id";
$res = mysql_query($sql, $link) or die(mysql_error($link));
while($row = mysql_fetch_row($res)){
	switch($row[1]){
		case 1: // New session (alarm begin)
			$starttid = $row[0];
			break;
		case 2: // Snooze
			$snooze++;
			break;
		case 3: // End of session
			$sluttid = $row[0];
			$totaltid = $sluttid - $starttid;
			$tid = gmdate('i:s', $totaltid);
			print "Totaltid: $tid<br />Antal snooze: $snooze";
			//tweet($sessionId); // Send to twitter or whatever you want to do
			break;
	}
}

Demo: http://martin.stoll.se/snoozetracker

Howto transfer garmin 910tx files from device to computer and to garmin connect with Linux (ubuntu)

Create a working directory (i just use /home/user/garmin/):

mkdir ~/garmin
cd ~/garmin

Download Garmin-Forerunner-610-Extractor from github (you need git to follow my example, sudo apt-get install git):

git clone git://github.com/Tigge/Garmin-Forerunner-610-Extractor.git

Install PyUSB (you need pip to follow my example, sudo apt-get install python-pip):

pip install pyusb

Insert Garmin USB stick, turn on your device and run program:

cd Garmin-Forerunner-610-Extractor/
python garmin.py

You have to answer yes on your device when the pairing question appears.

This is the result from my device:

Request basic information...
  ANT version:   AJK1.04RAF
  Capabilities:  array('B', [8, 3, 0, 186, 54, 0])
  Serial number: 2585056
Starting system...
Open channel...
Searching...
String length:  16
Unit ID:        3841991215
Product name:   Forerunner 910XT
Downloading index...
 - 1:   1       1900544 1989-12-31 01:00:00
 - 2:   1       262144  1989-12-31 01:00:00
 - 3:   128     574     1989-12-31 01:00:00
 - 4:   128     680     1989-12-31 01:00:00
 - 5:   128     1159    1989-12-31 01:00:00
 - 6:   128     317     1989-12-31 01:00:00
 - 7:   128     1159    1989-12-31 01:00:00
 - 8:   128     317     1989-12-31 01:00:00
 - 9:   128     1197    1989-12-31 01:00:00
 - 10:  128     52850   2012-10-03 20:39:34
 - 11:  128     22630   2012-10-04 21:09:40
 - 12:  128     23398   2012-10-06 13:40:52
 - 13:  128     266     2012-10-03 20:44:52
 - 14:  128     266     2012-10-03 20:44:52
 - 15:  128     342     2012-10-03 20:44:52
 - 16:  128     456     2012-10-03 20:45:02
 - 17:  128     608     2012-10-03 20:44:56
 - 18:  128     456     2012-10-03 20:44:56
 - 19:  128     380     2012-10-03 20:45:02
 - 20:  128     380     2012-10-03 20:45:00
 - 21:  128     190     2012-10-03 20:44:54
 - 22:  128     266     2012-10-03 20:45:00
 - 23:  128     266     2012-10-03 20:45:00
 - 24:  128     608     2012-10-03 20:44:56
 - 25:  128     456     2012-10-03 20:44:50
 - 26:  128     456     2012-10-03 20:44:54
 - 27:  128     456     2012-10-03 20:44:52
 - 28:  128     456     2012-10-03 20:44:54
 - 29:  128     722     2012-10-03 20:44:50
 - 30:  128     304     2012-10-03 20:45:02
 - 31:  128     456     2012-10-03 20:44:50
 - 32:  128     456     2012-10-03 20:44:54
 - 33:  128     380     2012-10-03 20:44:58
 - 34:  128     380     2012-10-03 20:45:02
 - 35:  128     456     2012-10-03 20:44:50
 - 36:  128     570     2012-10-03 20:44:52
 - 37:  128     266     2012-10-03 20:44:50
 - 38:  128     23276   2012-10-03 20:45:04
 - 39:  128     72      1989-12-31 01:00:00
 - 40:  128     627     1989-12-31 01:00:00
 - 41:  128     72      1989-12-31 01:00:00
 - 42:  128     168     1989-12-31 01:00:00
 - 43:  128     72      1989-12-31 01:00:00
Downloading 1989-12-31_01-00-00-80-574.fit - File transfer completed
Downloading 1989-12-31_01-00-00-80-680.fit - File transfer completed
Downloading 1989-12-31_01-00-00-80-1159.fit - File transfer completed
Downloading 1989-12-31_01-00-00-80-317.fit - File transfer completed
Skipping 1989-12-31_01-00-00-80-1159.fit
Skipping 1989-12-31_01-00-00-80-317.fit
Downloading 1989-12-31_01-00-00-80-1197.fit - File transfer completed
Downloading 2012-10-03_20-39-34-80-52850.fit - File transfer completed
Downloading 2012-10-04_21-09-40-80-22630.fit - File transfer completed
Downloading 2012-10-06_13-40-52-80-23398.fit - File transfer completed
Downloading 2012-10-03_20-44-52-80-266.fit - File transfer completed
Skipping 2012-10-03_20-44-52-80-266.fit
Downloading 2012-10-03_20-44-52-80-342.fit - File transfer completed
Downloading 2012-10-03_20-45-02-80-456.fit - File transfer completed
Downloading 2012-10-03_20-44-56-80-608.fit - File transfer completed
Downloading 2012-10-03_20-44-56-80-456.fit - File transfer completed
Downloading 2012-10-03_20-45-02-80-380.fit - File transfer completed
Downloading 2012-10-03_20-45-00-80-380.fit - File transfer completed
Downloading 2012-10-03_20-44-54-80-190.fit - File transfer completed
Downloading 2012-10-03_20-45-00-80-266.fit - File transfer completed
Skipping 2012-10-03_20-45-00-80-266.fit
Skipping 2012-10-03_20-44-56-80-608.fit
Downloading 2012-10-03_20-44-50-80-456.fit - File transfer completed
Downloading 2012-10-03_20-44-54-80-456.fit - File transfer completed
Downloading 2012-10-03_20-44-52-80-456.fit - File transfer completed
Skipping 2012-10-03_20-44-54-80-456.fit
Downloading 2012-10-03_20-44-50-80-722.fit - File transfer completed
Downloading 2012-10-03_20-45-02-80-304.fit - File transfer completed
Skipping 2012-10-03_20-44-50-80-456.fit
Skipping 2012-10-03_20-44-54-80-456.fit
Downloading 2012-10-03_20-44-58-80-380.fit - File transfer completed
Skipping 2012-10-03_20-45-02-80-380.fit
Skipping 2012-10-03_20-44-50-80-456.fit
Downloading 2012-10-03_20-44-52-80-570.fit - File transfer completed
Downloading 2012-10-03_20-44-50-80-266.fit - File transfer completed
Downloading 2012-10-03_20-45-04-80-23276.fit - File transfer completed
Downloading 1989-12-31_01-00-00-80-72.fit - File transfer completed
Downloading 1989-12-31_01-00-00-80-627.fit - File transfer completed
Skipping 1989-12-31_01-00-00-80-72.fit
Downloading 1989-12-31_01-00-00-80-168.fit - File transfer completed
Skipping 1989-12-31_01-00-00-80-72.fit
Done!

All .fit-files are now located in ~/.config/garmin-extractor/3841991215/activities/
3841991215 is dynamic depending on device.

Create a symbolic link to activities folder to get all the data in one place (optional)

ln -s ~/.config/garmin-extractor/3841991215/activities/ ~/garmin/fit-files

If something fucks up, rename the garmin-extractor folder and rerun garmin.py

mv ~/.config/garmin-extractor mv ~/.config/garmin-extractor.old

Lögdö Vildmark och Nilsböle

Har en lĂ€ngre tid tĂ€nkt att besöka delar av Lögdö Vildmark för att skaffa en uppfattning hur det ser ut dĂ€r, men det har inte blivit av. Idag fick jag dock fingrarna ur arslet och gjorde en liten tripp. Åkte via Liden upp till StorskĂ€lsjön, sedan vidare förbi HĂ€llsjön och Oxsjön.
Har ganska mycket kvar att se av omrÄdet.. men det fÄr bli en annan dag.

[custom-facebook-feed]

Howto set up rsync for Android for automated backups

I use rsync for Android to get a daily copy of my KeePass-database located on a ssh server. This is how I set it up.

Android applications needed:

rsync backup for Android
Install rsync backup for Android on your device and start it up. “Do you wish to download required binaries (about 610kB)”, choose  “Yes”.
Create a public and private key with “Generate keys” and mail it to yourself. You need it later on the ssh-server.
Create a profile, example:
rsync backup for Android profile example
In this case i want to sync folder ~/my-documents/keepass on remote server to /sdcard/keepass on my phone.
Install public key on ssh server
Copy text that you sent to yourself with mail and put i to id_rsa.pub.

Add key to authorized_keys2:
cat id_rsa.pub >> ~/.ssh/authorized_keys2
or
edit ~/.ssh/authorized_keys2 with your favorite editor and paste it.

Delete dss_key.pub from your server.

Done.

Then you can use Tasker (great app!) combined with rsync for automated backups. For example, backup all your pictures from a gallery incrementally immediately after you take a photo or every night.

Bildlogg juli

Det kÀnns som att veckorna gÄr snabbt som satan, men nÀr man tittar tillbaka varje mÄnad sÄ mÀrker man att man hinner med en hel del ocksÄ.

NÄgra bilder frÄn juli..