TheDrunkenEpic - The drunken ramblings of a code monkey ...

Imaggregator is Dead, Long Live Snag.gr

I think I made a great decision the other day when I purchased a new domain name; snag.gr. I figured it would be the perfect choice to replace the name of my poorly-titled pet project; Imaggregator.

For those of you who don't know what Imaggregator is, think of it as an image search aggregator (hence the name). Say, for instance, you're looking for a nice stock photo of a Mustang. What do you do? Well, you search for one on the web. But, where do you search? Flickr, Google, Bing, Picasa or Photobucket? In most cases, you'll end up searching multiple sites individually until you find a suitable image with the appropriate license. Not that big of a deal unless you find yourself doing this multiple times a day. Searching all these individual sites can be a serious inconvenience.

I figured people shouldn't have to search in all these places. So, with a bit of thought and research, I came up with a way to pull results from all these sites with a single search. It's much easier just typing in what you want and then have the ability to browse the results from multiple sites on one page.

So, what's the problem? The problem lies with the name of the service. In it's most basic form, Imaggregator is an image search aggregator; it pulls search results from multiple sources and aggregates them into a single page. The name makes sense, but you have to admit it's a pretty poor choice. It's too long, difficult to spell and say, not very attractive and a bit too nerdy. When I refer to the service by name, people usually give me that "what the hell were you thinking" look.

"It's a brilliant idea, but the name is fucking terrible.", is the usual verbal response. So, I set out in search of an appropriate replacement. The new name needed to be short, catchy, fun and, most importantly, inexpensive. I mean, let's face it, the project isn't making any money at the moment, so I don't really feel like dropping $1,200 USD a year (ahem, swi.pr) on a domain name. After a few hours of brain storming with mind maps, a thesaurus and the help of Doma.nr, I came up with a brilliant name: snag.gr!

It's a play on the word "snagger", as in to "snag" or "take" something. In this case, at least for the first phase of the site, it snags images from multiple sources. I think this is a perfect replacement for the service. Lucky, too, as it isn't even finished yet which means I won't have to worry about search engine indexing issues and what not.

It will take a few days for the registrar to complete the process and I'm stoked you can register Greek domain names without any registration restrictions. Got it at a very fair price from 101domain.com for $79 USD for 2 years, too, so I can't complain. Personally, I think it's a great investment as I plan on spending quite a bit of time on this service. It's a great idea and I believe people will find it to be useful.

I also went ahead and threw together a little logo for the site. I wanted to keep it simple and I think I pulled it off. Maybe later on down the road I will hire a real designer to give it, and the rest of the site, some flair. I guess we'll see what happens!

Comments and critique are always welcome!

Filed under  Development   Snag.gr  

Comments [1]

Words To Live By, With A Visual Aide ...

I forget who the source of the following quote is as it was a pretty long time ago when I first heard it, but someone posted it in a Digg comment earlier today. I thought I'd go ahead and share it to spread the wealth:

"Morality is doing what's right, despite what you are told. Religion is doing what you're told, despite what is right."

Upon reading it, I chuckled as it reminded me of the following comic:

image source

I'm an atheist, but I like to think that if there was a god, he'd be more like the one in the strip. What say you?

Filed under  General Nonsense   The Funny  

Comments [0]

HOW MUCH FOR THE DOG PLANT?!?!

We went to Balmain yesterday for a few drinks and a bite to eat. On our way back to the car we decided to stop and check out the local Sunday market. I was looking through the plants when I suddenly noticed two black beady little eyes staring right at me. Thought it was one of the funniest things I have seen in a while.

I want a pomeranian now. :(

Comments [2]

Saw this in a public bathroom.

Comments [0]

Import Content Into Posterous Using PHP + cURL + RSS ...

Before I moved to Posterous from Symphony, I had to take a serious thought as to how I was going to transfer my old content into my new account. I didn't want to manually transfer my articles over and Posterous doesn't offer any kind of import feature for my system. Besides, either way, I was going to lose all of my visitor's comments. I actually considered trashing all my old content and start fresh just so I wouldn't have to deal with the issue.

So, obviously, I decided to go ahead and find an easy way to move my stuff to a new home. By using a combination of PHP, cURL and RSS I was able to easily accomplish this within minutes. Since I'm such a nice guy, I'll go ahead and show you how I did it.

After digging around this service, I found that Posterous provides a very simple REST-based API which, among other things, allows you to post new content to one of your sites.

I then realized I could use my current site's RSS feed as the source of my content. I went ahead and changed the settings to this feed to display every article I ever wrote on the site.

Next, I cracked my knuckles and got to work writing the following script:

<?php 

set_time_limit(0);

define('IMPORT_RSS', 'http://www.mysite.com/'); // A direct link to the RSS feed you wish to import posts from ...
define('IMPORT_SITE_ID', 69); // The id of one of your Posterous sites ...
define('IMPORT_SITE_EMAIL', 'my@email.com'); // The email address assigned to your Posterous account ...
define('IMPORT_SITE_PASSWORD', 'password'); // The password assigned to your Posterous account ...

$RSS = new SimpleXMLElement(file_get_contents(IMPORT_RSS));

foreach($RSS->channel->item as $Entry)
{
$values = array
(
'site_id' => IMPORT_SITE_ID,
'title' => $Entry->title,
'body' => $Entry->description,
'date' => $Entry->pubDate,
);

$curl = curl_init('posterous.com/api/newpost');

curl_setopt($curl, CURLOPT_USERPWD, IMPORT_SITE_EMAIL . ':' . IMPORT_SITE_PASSWORD);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $values);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);

curl_close($curl);
}

Before you run this script, be sure you have PHP5 compiled with cURL support. Simply change the above constants to their appropriate values, make sure your RSS feed is valid, upload the script and execute it in your browser. If your values are correct, you will start noticing your articles appearing in the proper order in your Posterous account.

Sweet!

Clearly, there are fancier ways to do this, but there's no need to create overly elaborate code I'm only going to be running once.

NOTE:

One thing you should realize when using the API, the REST end points should NOT include any part of the 'http://www.' prefix. Doing so will cause authentication errors even if you're using the correct credentials. This stalled me for a good hour or so and with the limited documentation, I was left to resolve the issue with no help. Hopefully, if you're having the same problem, this will help you out. If not, contact the Posterous support peeps.

Till next time!

Filed under  api   curl   php   posterous   rss   xml  

Comments [0]

So, I Switched Platforms ...

As some of you might have noticed, I've switched everything from the old site over to my new Posterous account. The decision didn't come easy, but I'm afraid it had to be made. The spam on my old Symphony install was getting way out of hand and maintaining the system was become a bit more laborious than it should have been.

Don't get me wrong, I still love Symphony as a platform, but with no upgrade path to the latest version I had to face losing everything I've posted so far. I fully realize that there's always the alternative of manually transferring over all the old content, but seriously, who really has the time to do that shit?

Why Posterous? Well, for one, it's clean. Everything within the management console has a clear and defined purpose. It's also dead simple to use. All I have to do send my account an email and it's posted, ready to go. My old version of Symphony was getting so slow and buggy, I just wanted to leave it behind and start fresh. Fresh without having to manually transfer over all my content.

Simply put, I chose Posterous over all the other blogging services because it is the most low-touch solution I've found that has everything I need to blog and nothing I don't.

Now, with all the maintenance crap out of the way, I can focus more on writing new stuff more often! And what better way to start than to explain how you can import content from any blog directly into your Posterous account.

Stay tuned!

Filed under  General Nonsense  

Comments [0]

MyTopix is still out there ...

Yeah, yeah, I know it's been a while, but I've been working on a super-secret project for the past few months. After which, I'll find the time to finish Great British Walks. Speaking of which, I should probably shoot myself in the face because it's been in development in some form or another for the past few years. Not sure what my problem is as I usually finish my contracted projects post haste. Honestly, Pete is the most patient guy in the universe... and I suck.

ANYWAYS... A lot of people have been asking about MyTopix lately and where they can download a copy. I'm assuming the memo didn't go out to everyone, so I'll just say it again. MyTopix is no longer supported by me in any shape or form. It is an open source project now and can be forked by anyone who wants to keep the project alive.

For those of you who are interested, you can ALWAYS find the LATEST build tucked away in its Google Code repository.

Seriously, it's right there. Check it out, create a fork and publish your own distro. If you people like it so much, go ahead and keep it alive and you'll definitely have my full backing.

Filed under  Code Monkey   Development   Free Goodies   MyTopix   Rants and Raves  

Comments [0]

MySQL: Sorting records by order they were requested ...

Ok, probably not the best title for this article, but it's Monday, so you'll just have to make due. Hopefully, it will make sense by the end of this entry and you will have walked away with a neat trick to hide up your sleeve.

When working with insert language here and MySQL, have you ever had a collected list of record ids that needed the rest of their corresponding record data? Probably. If you've been doing insert language here and MySQL development for more than a few months, it's likely you've done something like the following:

PHP:
$ids = array(33, 22, 545, 234, 6, 343, 99);

$result = $this->db->query
("
    SELECT
        `id`,
        `name`,
        `email`,
        `password`
    FROM `members`
    WHERE `id` IN (" . implode(',', $ids) . ")
    ORDER BY `id` DESC
");

In this example, the above should return the corresponding data assigned to each id provided, if the record exists, that is. The results are sorted by id in descending order.

Well, that's great, but what if you want the database to return the data in the exact order you requested it? Not possible by any normal means outside of using insert language here to manually sort the returned data for you.

Lucky for us, the big swiging dicks over at MySQL have added an indispensable tool to the massive list of the database server's intrinsic functions. Let me introduce to you a little secret; the FIELD() function. Using the FIELD() function in place of the column name within the ORDER BY clause, you can force MySQL to return the data in the order you requested.

Using a modified version of the example above:

PHP:
$ids = array(33, 22, 545, 234, 6, 343, 99);

$result = $this->db->query
("
    SELECT
        `id`,
        `name`,
        `email`,
        `password`
    FROM `members`
    WHERE `id` IN (" . implode(',', $ids) . ")
    ORDER BY FIELD(id, " . implode(',', $ids) . ")
");

Et viola! All the requested records are returned in the same order as the ids within the $ids array. How neat is that? I wish I knew about this a few years ago.

Well, that's it for this article. Enjoy.

Filed under  Code Monkey   Development  

Comments [0]

DomQuery Alpha Released!

Well, I did some cleaning up of the code and appropriately commented everything. I'll be adding this to a Google Code repository very soon and get a wiki going. The documentation is pretty poor, but there are inline comments to help you for the interim; this project is only a few days old, mind you.

I added another method I mentioned in the previous DomQuery article. There was an issue with implementing a jQuery-like 'each()' method. I wanted users to be able to pass a runtime-generated callback function as an argument to be applied to all members of an xpath result. I found the solution to this problem by using PHP's 'create_function' function.

So, you can now do stuff like the following.

The Xml:
<root>
    <item>Item One</item>
    <item>Item Two</item>
    <items>
        <subitem>Sub-Item One</subitem>
        <subitem>Sub-Item Two</subitem>
        <subitem>Sub-Item Three</subitem>
    </items>
    <item>Item Four</item>
    <item>Item Five</item>
</root>
The Code:
$Xml = new DomQuery;

$lambda = 'echo $context['element']->nodeValue;'
        . 'echo ' ' . $param1 . $param2 . '<br/>';';

$Xml->load($xml)
    ->path('//item/*')
    ->each($lambda, 'foo', 'bar');

header('Content-Type: text/xml');

die($Xml->saveXml());
The Result:
Item One foo bar
Item Two foo bar
Item Four foo bar
Item Five foo bar

The 'walk' method is still quite useful as you can apply a previously defined function or class method to each member of an xpath result. This allows you to create plugins on-the-fly that allow you to manipulate your DOM in anyway you please!

Again, I'm always open to suggestions and critisms. This is a work-in-progress, so please keep that in mind! I'll be adding massive amounts of documentation and examples as I have time.

Download: Latest Version

Filed under  DomQuery  

Comments [0]

Introducing DomQuery!!!

I'v been doing a great deal of work on the latest version of my XML/XSL-based framework, Sauc'd, and came to the conclusion that the DOMDocument class, and others related to the PHP library, while VERY useful, are fucking terrible to work with. By themselves, it takes a serious amount of code to iterate across an xml document just to find a node, or group of nodes, that you're looking for. And when you finally find what you need, you have to implement migraine-inducing logic just to manipulate the resulting nodes.

There's gotta be an easier way and, guess what, THERE IS! It's called XPath. XPath allows you to traverse over a DOM with ease and select only the data you want with a simple querying language. Think of XPath as the CSS selectors you use with popular javascript frameworks like jQuery, Mootools or Prototype. PHP 5.x comes with another library that you can use along side DOMDocument that makes applying XPath to your DOM possible.

DOMXPath, apparently, was the answer I was looking for. Using it is simple enough:

$DOM = new DOMDocument();
$DOM->loadXml('<root><item>one</item><item>two</item></root>');
$XPath = new DOMXPath($DOM);

$results = $XPath->query('//root/item[2]');
  1. Create an instance of the DOMDocument class
  2. Feed it some XML
  3. Create an instance of DOMXPath and feed it the instance of DOMDocument
  4. Query the XML document with an XPath expression. The one above returns the second 'item' node.

See? Simple. Now you can iterate through $results like you would any array.

But still, while it makes searching through XML documents much easier, it's still be lacking in simplicity and ease of use. There is definitely a learning curve. There aren't a whole lot of convenience methods included and the poorly contrived documentation in the PHP manual pretty much leaves a newbie in the dark. You REALLY have to know what you're doing when it comes to playing with PHP's native DOM functionality.

So, long story short, I thought of how cool it would be if there was a utility that mimicked the functionality and usability of jQuery. So, last night I created the DomQuery library. It functions almost exactly like jQuery does even with chainable commands!

Here is a small example of what it can do:

The XML:
<root>
    <item>Item One</item>
    <item>Item Two</item>
    <item test="omg">Item Three</item>
    <item>Item Four</item>
    <item>Item Five</item>
    <parent>
        <child>omg</child>
        <child>
            <test/>
        </child>
        <child test="hai">omg</child>
    </parent>
    <copy>
        <default/>
    </copy>
</root>
The Code:
$Xml = new DomQuery;

$Xml->load($xml)
    ->path('//*[@test]')
    ->removeAttr('test')
    ->path('//root/parent/child[3]')
    ->attr('foo', 'bar')
    ->replicate('//root/copy/default');

header('Content-Type: text/xml');

die($Xml->saveXml());
The Result:
<root>
    <item>Item One</item>
    <item>Item Two</item>
    <item>Item Three</item>
    <item>Item Four</item>
    <item>Item Five</item>
    <parent>
        <child>omg</child>
        <child>
            <test/>
        </child>
        <child foo="bar">omg</child>
    </parent>
    <copy>
        <default>
            <child foo="bar">omg</child>
        </default>
    </copy>
</root>
What The?

Yes, it does exactly what it looks like it's doing:

  1. Instantiate an instance of DomQuery
  2. Feed it some XML (HTML too!)
  3. Search for all nodes who have attributes entitled 'test'
  4. REMOVE THEM!
  5. Get the 3rd 'child' node within 'parent'
  6. Give it the attribute of 'foo' with the value of 'bar'
  7. Copy the last used result and move it to '/root/copy/default/'

How's About This:

Unfortunately, PHP < 5.3.x doesn't support lambda functions or closures like javascript does, so we can't do something fancy like:

$Xml->load($xml)->path('//root/item/*')->each(function(&$Element){ echo $Element->nodeName; });

In PHP 5.3.x this would apply a callback function to each of the results, allowing you to modify them in any way you wish. Since 99% of the people out there who would be using this don't have that version of PHP in production, I had to lower the functionality a bit to accommodate the most popular setups. So, instead of the above, you can do this:

$Xml->load($xml)->path('//root/item/*')->walk('function_name', 'param1', 'param2');

This will apply a callback to every matched node. Simply put, this works by invoking call_user_func_array, executing the function and passing along any parameter you included within the execution of the 'walk' method. Walk also passes through the current result's context by reference so you can manipulate the current state of the document, the entire result set of the last pattern and the currently matched element:

$context = array
(
    'results' => &$this->results,
    'element' => &$Element,
    'context' => &$this
)

$context will always be the first parameter included in your callback. All other passed arguments will be included after.

What Else?

This library is still in development, but a ton of functionality is already included. Here is a list of fully supported functions:

  1. after() Insert content after each of the matched elements.
  2. append() Append content to the inside of every matched element.
  3. appendTo() Append all of the matched elements to another, specified, set of elements.
  4. attr() Returns, adds and edits the specified attributes of a matched element.
  5. before() Insert content before each of the matched elements
  6. clear() Clears the contents of a matched element.
  7. path() Apply an XPath pattern to a DOM and save the results.
  8. prepend() Prepend content to the inside of every matched element.
  9. prependTo() Prepend all of the matched elements to another, specified, set of elements.
  10. remove() Removes a matched element
  11. removeAttr() Removes a specified attribute from a matched element.
  12. replace() Replaces a matched element with another one.
  13. replicate() Copies a set of matched elements to a destination represented by another path.
  14. walk() Apply a user-defined callback function to a matched element.

I'll be posting more examples and tutorials down the road. Hopefully, I'll get this library to the point where I'll release it for all to use in the next few weeks. It's been a while since I released something fun and useful. I hope you guys will enjoy it.

If you're interested in testing this thing out with me or if you have any suggestions, post them in the comment area of this article.

Filed under  DomQuery  

Comments [0]