Shortcodes for Drupal

WordPress' Shortcode API is a really cool thing, and since I'm working on a Drupal site these days I've been looking for something similar. Unfortunately I couldn't find anything... There are some implementations out there but the ones I found and tested always come pre-bundled with specific tags and don't always provide an extensible and stable logic.

So I made mine!

Most of my work simply consisted in copying/pasting the code in WordPress' shortcodes.php and binding it into a Drupal filter. Easy enough.

Now if you want to use it, you have to:

  1. Get the module
  2. Add it to your "modules" directory
  3. Enable it
  4. Add it to one of your setup's filter bundle
  5. Write your own module where you'd implement one or more Shortcodes (via add_shortcode) and make sure you add "dependencies[] = shortcodes" in your .info file

You might experience a nasty "Call to undefined function add_shortcode". If so, you have to change you module implementation's weight, either directly in your database, or thanks to the cool Utility module. Set it to 10 and you're set!

Misc. UI Elements

It's an interesting thing to see how Google is working on its Chrome OS, in an OpenSource fashion, letting everyone know where they are going and how all this will/may look like. As a UI nerd I find it absolutely lovely to be able to sneak a peek at what's coming next, and have techy details on it, such as hexadecimal values and gradient stops...

Firefox also provides similar documents on its Wiki, and Chromatic Pixel tells you everything about it.

Speaking of UI design, here's a nice article about crafting subtle and realistic ones. You'll find gems like this one in it, and how it is achieved...

Got similar links to share? Do so!

Hey, did you notice I didn't say anything about Flash and/or the iPad? Thank me.

Meebo Cleaner

Using Google Chrome? Using Meebo? Annoyed by the ad at the bottom and the blog window when you log in?

I just wrote and published my first Chrome Extension - Meebo Cleaner, to clean that up!

Pretty simple code, nothing revolutionary here. Go get it and let me know what you think, or if you have suggestions/ideas...

The Scroll Effect

Facts

So the other day I had an hour to waste, I decided to learn how to use MooTools classes and, as the best way to learn something is to play with it, I played with it.

About 30 minutes after, I had something  running. Dirty and buggy but still, looking cool. It took me an extra 30 minutes to clean that up, make sure it was working on most browsers, add the Google Analytics tracker, push it online and add it to the projects page...

You probably guessed it already, I'm talking about the Scroll Clock. Well this hour might be the least wasted one of my life. Think I'm radical? Read on!

Results

After tweeting about it twice (once to someone who's interested in netart, and once to the MooTools team) things went big. I don't know exactly how this happened but it (the Scroll Clock page) got love from Gizmodo, swissmiss, QBN, Ajaxian, Neatorama and many more. Wow.

A few days after the tsunami, here's what my Analytics page looks like. My daily average of 600 page views has seriously been put to shame, with a climax on the 19th of November: 313,000+.

Pageviews

Side effects

This is probably the funniest part, not only did I worry for the server the site is hosted on, but a few things happened...

I got 3 job offers ; many friend requests on Facebook, Flickr, Vimeo and so on ; received a donation ; was contacted by a Google guy to add the project to chromeexperiments.com, which I did ; and of course had to bother my friends and colleagues about my new web-fame...

Conclusion

The short one: don't spit on Twitter, it might make you a star. Just joking, keep spitting on it.

The long one: the simplest and shortest project of mine is the one which received the most visitors and love, ironic right? What does that mean? Should I stop working on full-fledged AIR apps?

Prove me wrong!

Scroll Clock

I played with MooTools a bit and did that. Useless, opensource, nerdy.

Scroll Clock

I also set up a gallery, so if you've got nice scrollbars that aren't here yet, send' em right away!

BaseObject

I was talking about it, here it is!

This class will come in handy every time you work with synchronous SQLite databases in an AIR project. It's a real-world implementation of the Dynam.ize utility I presented earlier... I will show you an example of how to work with it, with simple sub-classes. Let's go!

First things first.

I need to open my database and provide that connection to BaseObect:

  1. var sqlCon:SQLConnection=new SQLConnection();
  2. sqlCon.open(File.applicationDirectory.resolvePath('data.sqlite'));
  3. BaseObject.defaultConnection=sqlCon;

We need clients!

Now, let's consider a database with a "client" table (pretty creative, right?), those clients have a name, a URL and a gender (male/female), and of course, an ID. So we have a "client" table with 4 fields: id, name, url, male (which is a Boolean: true=male, false=female).

I have mapped a BaseObject sub-class to this table (I will show you how in a few moments), so I can now create a new client in a pretty simple fashion:

  1. var myClient:Client=Client.create({name:'Joe', url:'http://www.yep.com', male:true});

Cool, I now have a Client instance I can work with and a line has been added in my "client" table. What if I wanted to change its name? Easy.

  1. myClient.setName('Mark');

The "name" field has been updated. Let's check that.

  1. trace(myClient.getName());

Traces "Mark"? Good. OK, what about updating the URL and name at the same time? Two queries? Nope!

  1. myClient.update({name:'Jack', url:'http://www.yo.net'});

Now let's grab a client via his ID.

  1. var c1:Client=Client.getFromID(1);

Simple. But what if I wanted to grab more than one client, fox example what if I wanted all male clients?

  1. var males:Array=Client.getFromQuery('SELECT * FROM client WHERE male=@male', {'@male':true});

You can now loop through this array and interact with the Client instances it contains...

What's the trick?

Nothing too hard to understand. Client is a sub-class of BaseObject that simply exposes some of its static and instance methods (getFromID, getFromQuery, create, update and so on). It also casts BaseObject return values to the right type: Client. For example, this is how getFromID works:

  1. public static function getFromID(id:uint):Client {
  2. return BaseObject._getFromID(getTableData(), id) as Client;
  3. }

Now you might wonder what this getTableData method is. Well it returns data about the table to which Client is bound to, like that:

  1. public static function getTableData():TableData {
  2. if (!_tableData) _tableData=new TableData('client', Client, ['name', 'url', 'male']);
  3. return _tableData;
  4. }

See? We define the table name (client), the current class (Client) and the fields we need to handle (name, url and male)... That's it.

Our clients bought cars...

That's right, so we should have a "car" table, right? With id, name, brand, clientID and purchaseDate we should be good. Its TableData would look like that:

  1. new TableData('car', Car, ['name', 'brand', 'clientID', 'purchaseDate']);

Note: I didn't list id as BaseObject supposes there's always an id field.

OK, so our clients bought cars, and we'd like to know who bought what. Let's write a method in Client to do that:

  1. public function getCars():Array {
  2. return Car.getFromQuery('SELECT * FROM car WHERE clientID=@id', {'@id':id}));
  3. }

That works, cool. But we can do better, we could cache this Array so we don't query every time we need it ; there's a technique for that:

  1. public function getCars(force:Boolean=false):Array {
  2. return (hasCache('cars') && !force) ?
  3. getCache('cars') :
  4. setCache('cars', Car.getFromQuery('SELECT * FROM car WHERE clientID=@id', {'@id':id}));
  5. }

Done. Optimized.

We can now see which cars our first client bought:

  1. var c1cars:Array=c1.getCars();

We could also implement the opposite: retrieving a Client form a Car instance:

  1. public function getClient():Client {
  2. return Client.getFromID(this.getClientID());
  3. }

Notice how I used getClientID? This refers to the table's clientID field and returns it.

That's a wrap.

Well that's pretty much it, I think I covered the basics!

If you want to play with it, get the class/demo project and tell me what you think. I've bundled Client, Car and some example uses...

Dynam.ize

Still while working on this app I was talking about earlier I realized I was quite used to working with PHP classes' Magic Methods, and more precisely the way it handles overloading. Those lovely __get, __set and __call methods are called whenever you try to access a property/method that is not explicitly defined, making your class dynamic. Of course you don't need this all the time, and it might even be dangerous, but it some cases that's simply perfect.

You can do that with AS3 if you extend flash.utils.Proxy, cool. But wait, no! Not cool! What if I wanted to extend something else? Nah, let's find something else...

I came up with the idea of dynamically creating getter and setter functions on a class instance for predefined properties. Now those aren't really getters and setters but let me show you what it looks like so you understand better... Here's an example:

  1. package {
  2. import net.tw.util.Dynam;
  3. // This is my class, that has to be dynamic
  4. public dynamic class DynClass {
  5. // This Array will be used to store the properties' values...
  6. protected var _data:Array=[];
  7. public function DynClass() {
  8. // In the constructor I call the Dynam.ize method on this, so it builds the getters and setters for the supplied properties
  9. Dynam.ize(this, ['abc', 'def', 'ghi'], getter, setter);
  10. }
  11. // Here are the functions that will be called when I try to get or set one of the properties I listed in Dynam.ize
  12. protected function getter(key:String):* {
  13. return _data[key];
  14. }
  15. protected function setter(key:String, v:*):void {
  16. _data[key]=v;
  17. }
  18. }
  19. }

Now let's instantiate this class and play with it:

  1. var dc:DynClass=new DynClass();

We can call getAbc() or setAbc() on it:

  1. dc.setAbc('my value');
  2. trace('get abc', dc.getAbc());

Everything should be OK. It would work the same with get/setDef and get/setGhi... This example might not seem very useful but you could do anything within the getters and setters ; for example access SharedObjects, sending data to a server and so on...

Find the class and a demo project on as3-bits!

Coming up next: my implementation of this concept for AIR's SQLite databases!

Thoughts?

Flex – GradientLabel

If you've ever wondered whether it's possible to apply a gradient on a Flex Label, well it is ; but that's not very straight forward... As I'm working on a app that requires this kind of glitter I decided to try and see what could be done.

I started with a basic ActionScript project (no Flex involved) and came up with this. Quite functional, could probably be optimized but my goal was actually a Flex component and I knew that was technically feasible. I then simply extended Flex's Label class and basically copied/pasted the logic into it. Just had to figure out which event to listen to and I was good to go...

Here's a demo app for your playing pleasure.

Go get Flash!

You may notice that in this example I embed the font so it looks nicer, but this is not mandatory...

All this is opensource (class+project) and you can grab it at my brand new Google Code dump: as3-bits. Help yourself.

As always, feedback welcome.

FileWatch

I wanted to try Flex 4 with a "real life" project so I wrote this little thing. Rough draft.

FileWatch

The app will allow you to monitor files changes: select a text file and you'll be prompted when it's updated. Basic. By the way, did I say "rough draft" before?

Everything in it is OpenSource, from the Illustrator files to the MXML and CSS stuff, so play with it! For those who only want the .air, here it is.

Now, why is it cool?

  • Build with the brand new Flex 4 (Gumbo) framework
  • Uses a home-brewed IconButton class, because Gumbo doesn't provide any
  • Uses the new Spark custom skins logic (on Button, ScrollBar and more...)
  • Uses the new states' logic and transitions
  • Based on as3corelib's FileMonitor class
  • Shows you how to build a multi-instance AIR app
  • Contains Illustrator/Photoshop UI files

And why is it not that cool?

  • Probably still buggy, wait for updates!
  • Flex 4 is not done yet, so the code might break at any time
  • No icons yet
  • No or very few comments
  • Pretty useless app!

If you're trying to learn Flex/Flex 4 or wondering what's new in it and how to use it you should definitively give it a go...

Have questions? Drop'em!

Snips2

Remember my first and only WordPress plugin? Well it just got better.

Snips2

Snips 2.0 is basically Snips rewritten from scratch, updated to fit WordPress' Shortcode API. Let's see how good it is...

Tired of having to copy paste nasty HTML code from sites such as YouTube, Vimeo and so on? Looking for a way to insert recurrent content in your posts without having to type it every time? Would like to be able to inject parameters in these snippets? Well, now you can.

Just install Snips and use the bundled shortcodes or create/edit your own!

Each Snip is a combination of a name, a model and optional parameters. The name defines the shortcode tag you will be able to use, the model is the content that will replace your shortcode calls and the parameters (that look like #the-parameter# in the model) are portions of the model that can change from a call to an other. Those parameters can have default values or can be left empty and then become mandatory.

All of this can be edited via a simple UI, found under Settings > Snips... Here's screenshot:

Snips2 screenshot

For those that used Snips' first version: (0.2) everything should be OK, the previous syntax will work ; but I recommend using the new one and the corresponding updated model files (ex: yt becomes yt2). Also, the #up# parameter (used in mp3 and swf snips) has to be set manually to your blog's upload path.

If you use it and find bugs, please let me know! This is the first time I use jQuery behavior (for the parameters' detection in the Settings page) so it could act a little weird.