Shutting down stgallenwiki.ch

Localwiki was one of the very early projects on Kickstarter and succeeded in their funding way back in 2010. Since then they have created several great model communities to show off how (hyper) local wikis can be a community enabler. The idea still seems so obvious to me, like Wikipedia is in hindsight, that I am still confused why the idea has not taken off exponentially.

Running a localwiki

Creating a localwiki was really easy with the documentation provided by the maintainers, it amounted to spinning up a virtual machine with Ubuntu on one's preferred hosting plattform, adding one repository and installing one package, to get up and running. Which is just what I did under stgallenwiki.ch to get to know the platform itself (not having any Django knowledge) and to get to know my current city better. 

In the beginning of 2014 the service providing free mapping tiles went away and individual localwikis could choose to either migrate to the centrally managed architecture of the core development team or to acquire a license key to continue receiving tiles for maps. Since I have not been able to find enough people in St. Gallen to work on this project and am alone not motivated enough to work on it further I have now taken the site down.

I am writing this post to document this canceled project and provide an archive of the few pages which were created (see below) should anyone care to try something similar in the future.

First look at CentOS 7

Screenshot of CentOS 7 installer

In July, a month after the release of RHEL7, the CentOS team had released their clone of the 7 series. Time to take a look, since this will likely be the standard version for new projects I will be building, at the very latest by the middle of next year when a minor release has further stabilized the series.

New and noteworthy

  • The installer now aligns with what we have become familiar with based on the Gnome 3 desktop in Fedora, which is overall a good experience.
  • XFS is now the default root file-system including boot. That decision seems fairly bizarre when EXT4 is the default everywhere else and has an upstream path towards Btrfs.
  • Systemd is now in full effect, so I will have to take some time to finally learn the startup script changes properly.
  • Network interfaces are now carrying "Predictable Network Interface names". While that's a nice idea it also means that old common tricks such as "dhclient eth0" no longer work. Side note: Minimal install does not even contain ifconfig, so find your interfaces in /etc/sysconfig/network-scripts/ifcfg*, if you went with minimal. 
  • PHP 5.4.16, this is the part I'm most excited about, since this was the primary reason in my work to install third-party repositories such as IUS to get above ancient 5.3 versions present in the RHEL6 series.
  • You will look for the package mysql-server in vain, MySQL is now to be found via the fork mariadb-server.

 

Three lessons from amateur PHP code

Hacks cans
Photo CC-BY-SA by tnarik

I did not get a CS degree and thus learned web development as a self-taught amateur. While that was fun, it also meant that in the process of building things without a curriculum or a mentor to provide a foundation I wrote pretty bad code in the beginning.

I have dug up the PHP code for a site which has been superseded with a Drupal site since 2009 and it's a great way to show failure by example without anyone getting hurt.

The following shows common mistakes made by amateur PHP programmers and a few pointers on how to fix them and become a better developer.

Mistake #1: Repeating yourself

Every time you see redundant code it should give you pause. Even a decade ago I recognized this and tried to move the page header into a separate file. Here is the start of item.php and dozens of other files like it:

<?php
$pagetitle="Item Details​";
include "main.php";
echo "<div id=\"content\">";
// [...]

Viewing an item would thus be the URL /item.php?id=123. This structure exemplifies that the author has not thought enough about inheritance and overall structure. Furthermore, we can see in main.php that the file is just dumping content to the output like the all other PHP files in that application:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<?php
include "settings.php";

It's clear that the author has not even fully internalized procedural programming and is using includes simply to reduce redundancy in a static, linear script.

Fixing the mistakes

Point all requests to index.php as the sole entry point and use parameters to call the respective logic. Not only will you reduce errors but you can also centralize parameter checking and sanitization, as well as authentication and authorization. You can also easily add nice, readable URLs with a one-liner in htaccess.

Convert the static pages such as item.php into functions. Which is basically a given, if you restructure everything around your index.php as recommended above. In this case if we had all item.php logic wrapped in a function (or more likely a collection of them), we could call it where required through our routing logic and not attempt to build a linear script for each page. Ideally, you would follow established patterns like MVC for your program structure when doing so.

Don't solve this yourself. Seriously consider if your homebrew application should continue to exist. The above points explain how one would fix something like it if you cannot ditch it but consider carefully if a framework such as Symfony2 or a CMS like Drupal is not the better solution to start from scratch, since they provide all that essential work of handling I/O, routing pages, abstracting templating, etc. for you, if you invest the time to learn how these systems work.

Mistake #2: Mixing logic, data and display

Here is an additional excerpt from the item.php shown above:

if ($row[isbn] != NULL)
echo "<tr><td>ISBN/ISSN #:</td><td> $row[isbn] </td></tr>";
echo "<tr><td>Section:</td><td> $row[section]</td></tr>";
// [...] 20 more <tr> like that
$commentquery = mysql_query("SELECT comment FROM comments WHERE item='$row[item_id]'");
if (!mysql_num_rows($commentquery)==0)
echo "<br /><br />Comments by members about this item:";

while($row = mysql_fetch_array( $commentquery )) {
    echo "<div id=\"comdiv\">";
    echo nl2br($row['comment']);
    echo "</div>";
}

Thankfully, the query parameter was properly sanitized, so it is not a case of little Bobby Tables but it is a Smörgåsbord of bad patterns. Not only is redundancy a concern but the indiscriminate mix of data, html markup and query logic shows missing levels of abstraction. The weird string double escaping we'll just ignore.

Fixing the mistakes

Use PDO and prepared statements. If you write your SQL by hand and work with user input, you are asking for trouble. PDO tutorials abound.

Functions, functions, functions. The author should have seen the redundancy in his or her code and concluded that a helper function should deal with creating the table (let's ignore whether a table is the right solution here). The block on comments could equally as well be encapsulated in a function. However, that function alone would not add much readability (at least as long as it is as short as it is now), unless one takes out the markup, which the following recommendation encompasses.

Create templates. Even if you do not follow the general advice from mistake #1 on using an established base to build upon, you can improve legacy code by stripping out the markup into templates. Even if you don't want to fully commit to Symfony, you could use their Twig engine to do so and achieve much more readable and maintainable code. Again, consider not reinventing the wheel.

Mistake #3: Sloppy code conventions

if (!mysql_num_rows($commentquery)==0)
echo "<br /><br />Comments by members about this item:";

The line above we already saw in mistake #2. The problem here is that through missing brackets, spaces, and indentations the code becomes significantly harder to read. It's easy to overlook the scope of the if-loop and for example incorrectly assume that the while-loop following it is still in it's scope. The many formatting problems in the code were mainly due to the fact that I didn't use software to help me stick to a standard, since I wrote it all by hand.

Fixing the mistakes

Use an IDE. A text editor with syntax highlighting is just not enough, it's too easy to make mistakes. Your environment, whichever you decide upon, should do auto-formatting and code checking as-you-type. I use PHPStorm but the same can be achieved with free tools such as Eclipse. 

Follow PSR-1/2. If you have until now followed your personal preferences, just get over yourself and standardize to PSR-2, that way all your code and all modern libraries look the same. There are also tools to help you convert your existing code

All of the above will not make you into a great developer but I hope to have provided helpful advice to others that I wish I had been given at the time.

Goodbye comments

various spam containers
Photo CC-BY by arndog

Today I removed this blog's comment functionality after having it for over seven years. I do not host debates and do not have content which regularly inspires comments apart from "thanks" so I don't think that this will in any way diminish this blog. I'm sure if someone actually needs help or has information they want to give me, they will find the about page. 

And on the other hand, comment spam is simply out of control. At first I had Drupal's image captcha which worked well for a while but had to be exchanged for Recaptcha but when that let too many spam posts through I switched to Mollom and they have provided an excellent service but still let about one false negative through every other day for the 500+ attempts every day. 

So, to follow the spirit of @AvoidComments, no more more comments here.

Using a Kindle as a monitoring tool (or not)

I had an old Kindle 3 lying around and really wanted to do something fun with the e-ink display in it, especially since its Linux underneath anyway.

If you browse around the web you'll find plenty of information to get you started with the prerequisite steps of rooting the device, enabling network and ssh. Since a lot of the information is hidden in forums (ugh), I'll point to Liraz Siri's tutorial which covers all important points.

Developing a kindlet

Unless you want to directly work on the framebuffer you will want to create a java application which the Kindle can run, called a kindlet. I strongly suggest you follow David Given's guide. Getting his Hello World to run consistently is a good starting point for everyone like me who only dabbles with Java. Additionally, the MobileRead wiki has additional information.

Since developing a kindlet without any resources from Amazon basically amounts to compiling, copying, opening and repeating that process it's essential to automate that process, thus I recommend ammending Given's excellent makekindlet script with the following  two lines (for OS X):

[...]
jarsigner -keystore $KEYSTORE -storepass password $JAR dn$USER

cp $JAR /Volumes/Kindle/documents/
diskutil eject /Volumes/Kindle 

Network communication in a Kindlet

To do anything useful in a Kindlet apparently requires one to have the ability to work with sockets, so the security settings need to be adjusted. A blog post by Alex Hutter explains how, i.e. add the following in external.policy:

grant signedBy "Kindlet" {
 permission java.net.SocketPermission "*:443-", "accept,connect,listen,resolve";
 permission java.net.SocketPermission "*:80-", "accept,connect,listen,resolve";

If you are stuck at such a point it's often helpful to visit the crash.log in developer/APPNAME/work to point you in the right direction of the problem.

Rendering content

Now, the rest should be easy, right? Well, rendering local images worked fine with this example snippet, however any permutation of loading network resources into images went exactly nowhere, getWidth() returns 0 and no image can be rendered and that's where I'm currently stuck and discontinuing this project.

Summary

The Kindle is NOT intended as a general use computing device. 

The quote above is from Hutter's blog post, I don't have much to add to that. This was a fun project but it became clear that without getting to know Java a lot better this would not be worth the effort, compared to solving the problem with an Android app, no matter how nice the display is.

Track when you lock your desktop

a stopwatch
Photo CC-BY by Jerry

If you do any sort of work where you have to report your activities in 15-minute increments, you have probably experienced that it is very easy to miss things, especially if you often have to deal with interruptions.

There are numerous solutions to parts of this problem such as apps which continuously poke you to update a tracker, to ones which simply log everything and sum it up. I use some of them but I also add another check to my routine to have more accurate timing information when I wasn't paying attention. What I'm doing is writing timestamps to a file which register screen locks/unlocks as well as suspend and it has in the past year helped me to significantly become more accurate in my tracking by having reference timestamps such as the following excerpt:

Thu Jan 23 09:53:48 CET 2014: SCREEN_UNLOCKED
Thu Jan 23 10:02:21 CET 2014: SCREEN_LOCKED
Thu Jan 23 10:18:16 CET 2014: SCREEN_UNLOCKED
Thu Jan 23 10:27:02 CET 2014: SCREEN_LOCKED

If you want to do this, too, just follow one of the examples below. If you want to do something similar and customize it, the problem is rather trivial if you break it down into a trigger and an action. The first is heavily dependent on your environment, the latter could be anything.

Linux/GNOME

To react to events you'll have to run a command at login which then continuously monitors DBUS to see if the relevant event has fired. Here is a working example:

$ #!/bin/bash

function write_log {
  if [ -z $1 ]; then
    1="unspecified"
  fi
  echo -e "$1\t$(date)" >> "/home/youruser/time.log"
}

write_log "LOGGED_IN"

# Monitor gnome for screen locking. Log these events.
dbus-monitor --session "type='signal',interface='org.gnome.ScreenSaver'" | \
  (
    while true; do
      read X;
      if echo $X | grep "boolean true" &> /dev/null; then
        write_log "SCREEN_LOCKED"
      elif echo $X | grep "boolean false" &> /dev/null; then
        write_log "SCREEN_UNLOCKED"
      fi
    done
  )

OS X

The only decent trigger method I found (albeit after only 15 minutes of googling) is the paid app Scenario. The app itself should be self-explanatory and allows you to put one or several scripts into the respective folders for sleep, lock, log out, etc. Since I'm much more familiar with shell scripting than Applescript, I'm just using the later to call the former. Obviously, this could be simplified.

Applescript in Scenario:

do shell script "~/.bin/screenlock.sh unlock"

Shell script:

#!/bin/bash

if [ $1 = "lock" ]
then
  echo -e "`date`: SCREEN_LOCKED" >> ~/time.log
elif [ "$1" = "unlock" ]
then
  echo -e "`date`: SCREEN_UNLOCKED" >> ~/time.log
else
  echo -e "`date`: UNKNOWN_ACTION" >> ~/time.log
fi

 

Have a better way to do this? Let me know in the comments.

Pages

Subscribe to Front page feed