Entries tagged with “code”.


A slug is a SEO-friendly, human-readable version of a URL. Generally used on most blog software for permalinks via a blog’s title (exactly like my blog here), or basically any string you want to turn into a friendly URL. Sure you could just use PHP’s urlencode (or other language equivalent) but then you’re stuck with unfriendly characters translated into hex codes: %2F%20

The problem is greater when the content you want to Slug is UTF-8 encoded and contains non-ASCII characters. How do you slug a word like: Iñtërnâtiônàlizætiøn?

My ongoing redo of Footstops, which now creates slug’d URLs from UTF-8 user generated content, has ventured me into such territory and I’ll share my slug method with you. The one caveat is that its power relies on the awesome iconv library, which has come enabled by default since PHP 5.0.0, and easily installable in PHP 4.2+, so make sure you have that, if not, remove the line – it still works, just not nearly as well. I also make the assumption that your data is encoded in UTF-8, which is fairly safe because it is pretty backward compatible, but if you are working in a different charset, please adjust as necessary.

The method is short and sweet.

1. First we use iconv to translit the UTF-8 string into ASCII. This converts the UTF-8 string into an ASCII equivalent, but also translate non-ASCII characters into their ASCII appearing equivalents: ë becomes e.

iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);

2. We then remove all the unwanted characters from the URL.

preg_replace('/[^a-zA-Z0-9 -]/', '', $url);

3. We convert it to lowercase (which is just a preference for consistency), make sure its between our max string length (we don’t want a 64 character slug, 40-50 characters is probably lots), and remove any surrounding whitespace.

trim(substr(strtolower($url), 0, $maxLength));

4. Finally we replace any whitespace or our separator character with a single instance of the separator character, to remove multiples. I prefer an underscore as a word separator rather than a dash (traditional slug separator) as it may conflict with an actual hyphen in the string but in the final version you’ll see it’s easy to default to your own preference.

preg_replace('/[s' . $separator . ']+/', $separator, $url);

So we put it all together with a couple of options:

public static function ToSlug($string, $maxLength=40, $separator='_') {
	$url = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);
	$url = preg_replace('/[^a-zA-Z0-9 -]/', '', $url);
	$url = trim(substr(strtolower($url), 0, $maxLength));
	$url = preg_replace('/[s' . $separator . ']+/', $separator, $url);
	return $url;
}

Calling ToSlug('Iñtërnâtiônàlizætiøn    is the greatest! ') produces the slug:

internationalizaetion_is_the_greatest

perfect for a friendly URL!

I’ve had tons of people ask me how to Select DISTINCT rows from a DataSet. Why not use SQL? Well sometimes you just can’t, and sometimes its much more efficient to do it webserver side then database side.

For some reason Microsoft has an article on how to write your own helper class, which is fine and dandy, but you don’t need to bother. While this implementation isn’t exactly the same as a SQL call, its very simple and works extremely well when working with medium to small datasets.

Say we have a DataSet ds like so:

recordID groupID value
1 100 abc
2 100 def
3 220 ghi
4 333 jkl

So to select distinct groupIDs we simply:

DataTable distinctDT = ds.Tables[0].DefaultView.ToTable(true, new string [] { "groupID" });

This returns:

groupID
100
220
333

We can now use this distinct DataTable to make our queries on our original DataSet:

ds.Tables[0].Select("groupID = " + distinctDT.Rows[0]["groupID"].toString());

That looks a little more complicated then it actually is… oh well, its actually quite a nice solution when you can’t use SQL to do your DISTINCT selects for you because you can use the same DataSet over and over reducing time to your SQL Server or FileIO.

I often have the need to get the decimal degrees of a longitude and latitude and generally use Google Maps (and Earth) to find the location, but there is no easy way to get the decimal degrees for a coordinate – you only get Degrees/Minutes/Seconds.

I was messing around with Google Maps and the Mapping API the other day and found a simple way to get decimal degrees (without parsing them out of the “Link Here” url).

Simply centre the Google Map on the spot you’re interested in (rightclick -> Centre Map here) and enter this as the browser’s URL:

javascript:void(prompt("", gApplication.getMap().getCenter()));

Instead of writing about whats been going on, I’m going to delay again by adding a little programming nugget.

In my paying job, I do a lot of work with Microsoft and ASP.Net (Alkaloid members be quiet). I’ve recently started using the AJAX library for ASP.Net 2.0 and, while it has gone surprising well for the most part, there was one problem in general I couldn’t find an answer for online so I thought I’d post my solution.

Working with a GridView in an Update Panel is awesome for many reasons, but when you require a full postback instead of an asynchronous one you can run into some trouble. If the actual object to cause the full postback has a set ID, no problem simply add:

<asp:UpdatePanel ...>
   <ContentTemplate>...</ContentTemplate>
   <Triggers>
      <asp:PostBackTrigger ControlID="CONTROL_ID" />
   </Triggers>
</asp:UpdatePanel>

The PostBackTrigger defined there will issue a full page postback. The problem arises on run-time controls such as a TemplateField or ButtonField within the GridView. There is no (easy) way to assign them as PostBackTrigger as either you don’t know the ID (in a ButtonField example) or the ID changes based on any MasterPages or panels. The solution is simple though.

Basically all we have to do is add the control to the Trigger collection in the code behind on the DataBind event of the item. This will work with any type of control you want to put in your GridView. In this example, I’ll use a LinkButton in a TemplateField.

... UpdatePanel definition etc. ...
<asp:GridView ...>
   <Columns>
      <asp:TemplateField>
         <ItemTemplate>
            <asp:LinkButton CommandName="Select" ID="PostBackButton" 
                  runat="Server" Text="Do PostBack" 
                  OnDataBinding="PostBackBind_DataBinding">
            </asp:LinkButton>
         </ItemTemplate>
     </asp:TemplateField>
     ... Any other Columns ...
   </Columns>
</asp:GridView>

Then in the codebehind:

protected void PostBackBind_DataBinding(object sender, EventArgs e)
{
   LinkButton lb = (LinkButton) sender;
   ScriptManager sm = (ScriptManager)Page.Master.FindControl("SM_ID");
   sm.RegisterPostBackControl(lb);
}

The script manager has a handy RegisterPostBackControl method and the link buttons are dynamically set as full postback calls.

Note: If you aren’t using a Master Page, you can just get the scriptmanager via its local reference: this.SM_ID.RegisterPostBackControl(lb);

Hope this helps someone out!

While setting up this new blog, I noticed that Lightbox 2.02, the javascript that opens images in a cool way (if you haven’t seen it, click any of the images in the blog!) screws up when Google Video and YouTube Videos overlap it.

A lot of HTML elements are not rendered properly and don’t respect the z-index in all the browsers so they always appear as the topmost overlay. The lightbox script looks to this for HTML select controls but the same thing occurs with online videos. To fix, edit the lightbox2.js:

• Add the following to the showSelectBoxes() method:

objects = document.getElementsByTagName("object");
for (i = 0; i != objects.length; i++) {
   objects[i].style.visibility = "visible";
}

• And the following to the hideSelectBoxes() method:

objects = document.getElementsByTagName("object");
for (i = 0; i != objects.length; i++) {
   objects[i].style.visibility = "hidden";
}

Simple enough fix. It might be resolved in the newest 2.03, not sure though.

While adding YouTube and Google Video support to Footstops, I wrote a little plugin for the WYSIWYG editor used on Footstops, TinyMCE.

There are lots of other plugins that do similar or more then mceOnlineVideos, but mceOnlineVideos was written to be secure and safe when presenting an editor to end users. Most plugins open up the content to allow for dangerous tags like <object> and <embed> but mine uses an image placeholder and the video is inserted when output; not while stored.

Read more about mceOnlineVideos at projects.alkaloid.net, see it in action at Footstops, or download it here

I wrote a quick tutorial on my lunch break about how to use PHP and PEAR to create simple yet effective Captchas to keep those damn robots off your webforms.

On the Alkaloid Networks Docupedia: Embedding Captchas

Ta da! We unveiled our newest software project just moments ago: PodMail!

PodMail allows you to produce secure Podcasts of your voicemail. It also unchains Podcasting from the computer, now just leave yourself a voicemail and you’ve created a new episode for your Podcast subscribers! PodMail is released under the GPL and completely free.

Be sure to come see our Live Demo. Call our voicemail, leave a message and hear your message immediately through the PodMail Podcast!

Live Demo at: http://podmail.alkaloid.net
or download PodMail at: http://projects.alkaloid.net

Put up another Google Earth tutorial today on the The Docupedia. It shows what you can do with your own information and how to code it up.

Still working away at CMap, hit some snags but I should be able to post some source code by the end of this week.

I just finished a nice, detailed article about cracking wireless networks on The Docupedia. Its awesome but may seem like be gibberish if you don’t know anything about computers.