• You are currently browsing the Christian Heilmann blog archives for October, 2007.

  • Archive for October, 2007

    Showing off your presentation slides with slideshare, PHP and a bit of JavaScript

    Wednesday, October 31st, 2007

    First of all, I am a big fan of slideshare, a web app that allows you to upload presentations in powerpoint, open office or PDF and share it on the web. Slideshare converts the presentation (sadly enough not 100% when it comes to fonts and kerning :-( ) and people can comment on them, there is a text version of all slides and you can embed the slides in your blog or other sites.

    When I checked my slides I had a look at the API of slideshare but I am always a bit bored with having to go through a developer ID and then do everything on the server. That’s why I put on my “ethical hacker” hat and took a look at the RSS feed of my slides and found everything I need there! If you look at the source of the feed you’ll see that it contains not only the titles and descriptions but also the media code, in this case the HTML to embed the right flash movies.

    Taking this information it is pretty easy to build a viewer that allows people to click through all your presentations without having to leave your site. This can look something like this:

    Interface to click through different slide shows

    When JavaScript is available this will be the look and feel and functionality. When JS is turned off all you’ll get is an unstyled list of links pointing to the presentations on slideshare.net.

    You can check the slideshare show in action and get a zip to download and use on your site if you don’t want to know how it is done. If you do, read on…

    The code necessary is really easy and done in about 70 lines. Let’s go through it bit by bit. I am using PHP4 together with cURL, DOMXML and some JavaScript using the YUI.

    
    <?php
    $url = 'http://www.slideshare.net/rss/user/cheilmann';
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $slides = curl_exec($ch);
    curl_close($ch);
    

    It starts with the URL we want to load and a CURL call to pull this file and store it in the variable $slides.

    
    $slides = str_replace('slideshare:embed','slideshareembed',$slides);
    $slides = str_replace('media:title','mediatitle',$slides);
    $xml = domxml_xmltree($slides);
    

    To make things easier (as DOMXML is a terribly hacky piece of kit – much easier with PHP5 and SimpleXML that one) I rename the namespaced attributes in the feed containing the embed code and the title of the media to simple elements and create an object collection from the XML using domxml_xmltree.

    
    $json = array();
    $slidesharelist = '';
    $links = $xml->get_elements_by_tagname('link');
    $img = $xml->get_elements_by_tagname('url');
    $titles = $xml->get_elements_by_tagname('mediatitle');
    $embeds = $xml->get_elements_by_tagname('slideshareembed');
    

    Then I need to preset an array to contain the embed code for each slides and a string to contain the list of links pointing to presentations on slideshare. I use the get_elements_by_tagname method of DOMXML to get arrays of the different bits of content that I need from the RSS feed.

    
    foreach ($embeds as $key=>$el) {
    $l = $links[$key+2]->children[0]->content;
    $t = $titles[$key]->children[0]->content;
    $slidesharelist .= '<li><a href="'.$l.'">'.$t.'</a></li>';
    $emb = $el->children[0]->content;
    if(strpos($emb,'<div')===false){$emb = $el->children[1]->content;}
    preg_match_all('/.*(<object.*>.*</object>).*/msi',$emb,$obj);
    $json[]='''.$obj[1][0].''';
    }
    ?>
    

    By looping throught the “embeds” array I assembling a list of links pointing to the different presentations and add the embed code to the JSON array. I need this one later to show the different flash movies when visitors click the presentation links. Notice that I need to skip the first two LINK elements as that is the one pointing to the main URL of the RSS feed. For some reason the order of embeds was different on my localhost and my live server, which is why I added that extra if statement. Annoying, that.

    That is all the PHP we need! Now it is time to make it pretty and add the rest of the HTML.

    
    <style type="text/css">
    @import 'slideshareshowstyles.css';
    #slideshareshowslideshow{background:url(<?php echo $img[0]->children[0]->content;?>) no-repeat center center;}
    </style>
    <div id="slideshareshow">
    <ul id="slideshareslides"><?php echo $slidesharelist; ?></ul>
    </div>
    

    As it is hacky enough to mix PHP and JavaScript I put all the CSS fun in an own document and only add the logo of the RSS feed as the background of the slideshow container. The markup is a main DIV with an unordered list that gets the HTML assembled earlier in the PHP script. This shows the links but doesn’t do the dynamic showing yet. For that we need JavaScript.

    
    <script type="text/javascript" src="http://yui.yahooapis.com/2.3.1/build/yahoo-dom-event/yahoo-dom-event.js"></script>
    <script type="text/javascript">
    YAHOO.example.slideshareshow = function(){
    

    Technically we wouldn’t need the YUI for this, but I got quite used to its perks like namespacing and proper event handling without browsers hacks so I just went for it. I also started a namespaced function which uses the module pattern to contain all the variables inside its scope.

    
    var container = document.getElementById('slideshareshow');
    YAHOO.util.Dom.addClass(container,'jsenabled');
    var list = document.getElementById('slideshareslides');
    var links = list.getElementsByTagName('a');
    

    I get the container element and add a CSS class called “jsenabled”. This allows me to use it as a hook in the CSS file to style only when JavaScript is available. I take the list and get all the links inside it.

    
    var displayContainer = document.createElement('div');
    displayContainer.id = 'slideshareshowslideshow';
    container.appendChild(displayContainer);
    var current = null;
    

    I then create a new DIV that will contain the Flash movies of the different presentations when the user clicked a link. I give it an ID to allow for styling and append it to the main container element. I predefine “current” as “null”, this will later on be the currently selected link in the list.

    
    for(var i=0;links[i];i++){
    YAHOO.util.Event.on(links[i],'click',show,i);
    }
    

    I loop through all the links and add an event listener pointing to the show() method and sending the number of the link as a parameter. This allows me to get the correct Flash embedding code from the JSON array I assembled in PHP.

    
    function show(e,i){
    YAHOO.util.Dom.removeClass(current,'current');
    current = this;
    displayContainer.innerHTML = slides[i];
    YAHOO.util.Dom.addClass(current,'current');
    YAHOO.util.Event.stopEvent(e);
    }
    

    In the show method I remove the “current” CSS class (another hook for styling) from the last link that was clicked and set the new one as current. I set the innerHTML of the container DIV to the right code from the JSON array, add the “current” class to the link that was clicked and stop the link from being followed by invoking stopEvent.

    This is where the YUI Event utility rocks, I have the correct number to get as I sent it as an own object, I know which link was clicked as it is stored in “this” and I have the whole event object in e. What more do you need?

    
    var slides=[<?php echo implode($json,','); ?>];
    }();
    </script>
    

    That’s all, except for putting the data from the RSS feed into a “slides” array and closing the module pattern.

    Together with the right style sheet this is enough to have a clickable list of your latest presentations on slideshare. Enjoy.

    [tags]domxml, hack, portfolio, presentation, rss, slideshare, workaround[/tags]

    Upgraded the blog to a new wordpress

    Tuesday, October 30th, 2007

    As I had a lot of spam problems and other attacks I upgraded the blog to a new wordpress. This is why this looks terrible and unfinished but I guess most of you haven’t realized it as you read the RSS feed anyway (which I consider the right way to read a blog).

    I don’t have the time to make this pretty right now, but all the content is stil here. Please give me a shout when something stopped working.

    The social part of blogging is broken

    Sunday, October 28th, 2007

    Having just spent another hour deleting trackback spam advertising a non-existent medication I am thoroughly sick and tired of playing catch-up with the evildoers of the web and turned off trackbacks on this blog. I can get the same information from my logs, although it is a shame that I have to go there.

    The initial idea of blogging was awesome: you publish information, get immediate feedback with comments and you’ll also get notified by trackbacks when other bloggers talk about your stuff or build upon it.

    However, this seems to be over. If you have a blog that managed to get high up in Google’s pagerank (and you didn’t get demoted last month for daring to try to make some money with it) you will get the following:

    • trackbacks from web sites that don’t exist (for whatever reason)
    • trackbacks from web sites that were built for SEO (like http://learnitself.info/ – check the footer – SEO WordPress theme Ad Flex Blog ( Using v0.8.9.6a – v0.8.9.8h available ) by VK Solutions sponsorized by Digital Proof – sponsorized???)
    • trackbacks from blogs that scrape other blogs (both Ajaxian and Smashing magazines are cloned throughout the web when they have new posts)
    • comments from clumsy spammers
    • comments that were written on order ( as Marco ranted on about lately – online and IRL :) ) to advertise a certain site

    And that’s the evil spammers of this world. There is however a lot more noise on the line:

    • trackbacks from people who consider a list of their latest del.icio.us bookmarks a blog post
    • comments from people who don’t really want to comment but advertise their own blogs by hinting they have a solution
    • comments from people who do not read up on the context but only on what they get by scanning over the post (this could be because of them reading the RSS and not getting that a post is just pointing to a larger article or product – you get a lot of those on sites like Ajaxian)
    • comments from people who are fanboys and -girls of certain technologies and by default consider everything else wrong and people who talk about it morons
    • comments from people who try to annoy you or take you on as you are “the expert”

    This is nothing new either (I’ve written about this three years ago) but it has become so much you have to spend more time swallowing very short and rude remarks than writing your blog.

    As said I am turning off trackbacks, as I am not showing URLs they were pretty pointless here anyway. I will instead publish URLs that I consider good enough to link back to. For the moment I am still allowing comments as I do get the occasional good one (in a vast sea of rubbish) but I am toying with the idea of getting rid of those, too. This would be really sad as I did start blogging because of the option to get immediate feedback from people anyone can read.

    I am not at all subscribing to the school of people who claim comments are dead and if you wanted to comment blog about it yourself or twitter about it and we use technorati as the feedback consolidator. I don’t expect everybody to blog or use twitter and consider it very dangerous indeed to go down that route as it does smell of inbreeding to me. People find our blog posts via search engines and should not have to be part of the “blogging scene” to be able to comment.

    That said, we need a remedy to fix the social aspect of blogging, one that is easy to access and secure enough to stop the spammers. That is very tough indeed, cause anything that is findable is also spammable.

    [tags]blogging,spamming,spam,trackback,bastards,socialblogging,social web,social[/tags]

    Progressively enhancing autocomplete

    Friday, October 26th, 2007

    I like Autocomplete as a design pattern. It allows you to find things a lot faster than using a normal search box and saves you having to go through a search result page. Autocomplete means progressively enhancing a normal search box to provide a faster and more channeled way to find information. Good autocomplete controls do that in a totally unobtrusive manner and load the extra information only on demand and when the user requires it.

    However, there is another use case I haven’t seen covered properly yet: what if I want to offer a defined, small amount of options and a full search but not rely on JavaScript? The solution is to use a list of links followed by a search box:

    <ul id="options">
    <li><a href="myapp.php?s=Apples">Apples</a></li>
    <li><a href="myapp.php?s=Bananas">Bananas</a></li>
    <li><a href="myapp.php?s=Carrots">Carrots</a></li>
    <li><a href="myapp.php?s=Dates">Dates</a></li>
    <li><a href="myapp.php?s=And%20so%20on">And so on</a></li>
    </ul>
    <form action="myapp.php">
    <p>
    <label for="s">Search:</label>
    <input id="s" name="s">
    <input type="submit" id="submit" value="find it!">
    </p>
    </form>
    

    Wouldn’t it be cool to use this as a fallback when JavaScript is not available and use this information to seed an autocomplete control when it is? Well, you can.

    Naturally at work I keep running into the YUI Autocomplete control and this one has the option to use a JavaScript array or even a JavaScript function as the data source for the options. We can easily use this to get the information from the list of options:

    
    function doAutoComplete(){
    var optionsLinks = opts.getElementsByTagName('a');
    var ds = [];
    for(var i=0;optionsLinks[i];i++){
    ds.push(optionsLinks[i].firstChild.nodeValue);
    };
    var acDataset = new YAHOO.widget.DS_JSArray(ds);
    acDataset.maxCacheEntries = 0;
    var ac = new YAHOO.widget.AutoComplete('s','autocompcontainer', acDataset);
    };
    
    The thing about YUI autocomplete that always annoyed me is the amount of dependencies you have to include to get the whole thing working. This is of course by design as it builds on other parts of the YUI, but it was a bit of an annoyance to me. There is however a really cool way to make YUI controls load the YUI components necessary: you can use an object called YAHOO_config and define a listener method. This listener method gets called every time you include a YUI component and gets its name as a parameter. Couple this with dynamically created script nodes and you can call the dependencies one after the other:
    function YAHOO_config_ac_run(o){
    if(o===undefined){
    addJS('yahoo-dom-event/yahoo-dom-event.js');
    } else {
    switch(o.name){
    case 'yahoo-dom-event':
    addJS('animation/animation-min.js');
    break;
    case 'animation':
    addJS('autocomplete/autocomplete-min.js');
    break;
    case 'autocomplete':
    var l = document.createElement('link');
    l.type = 'text/css';
    l.rel = 'stylesheet';
    l.href = 'http://yui.yahooapis.com/2.3.0/build/autocomplete/assets/skins/sam/autocomplete.css';
    document.getElementsByTagName('head')[0].appendChild(l);
    YAHOO.util.Dom.addClass(document.body,'yui-skin-sam');
    doAutoComplete();
    break;
    };
    };
    function addJS(url){
    var head = document.getElementsByTagName('head')[0];
    var s=document.createElement('script');
    s.src = 'http://yui.yahooapis.com/2.3.0/build/' + url;
    s.type='text/javascript';
    head.appendChild(s);
    };
    };
    YAHOO_config = {
    listener:YAHOO_config_ac_run
    };
    YAHOO_config_ac_run();
    

    Putting it all together you get an autocomplete control with a list as a fallback. Try turning JavaScript on and off to see the difference.

    [tags]autocomplete,yui,progressiveenhancement,unobtrusive,webdevtrick[/tags]

    Hacking Flickr the JSON way

    Monday, October 22nd, 2007

    This was part of my presentation at the Open Hack Day in India and I just got the time to write it up.

    Here you’ll learn how to get Flickr photos into your JavaScript solutions without having to resort to using the full API. As this is a hack you will only get the latest 20 photos, if you need more detailed data like restricted to sets or more at once you’ll need to resort to the flickr API.

    I mentioned it earlier for some of the Flickr badges shown here and my 24 ways article on how to use Flickr in JavaScript but it is good to have a quick step-by-step to learn how to use Flickr with JavaScript without needing to resort to a full-fledged API call.

    Step 1: Locate the photos you want to use on flickr.com

    Just surf around the site and find a site that does have an RSS feed. For example, my Flickr stream at http://flickr.com/photos/codepo8 or all the photos of Open Hack Day India at http://flickr.com/photos/tags/hackdayindia/.

    Notice that only tags work, not search!

    Step2: Click the RSS link or RSS icon (in Firefox) and change the ending of the URL call

    This will get you the URL to the RSS feed, for example
    http://api.flickr.com/services/feeds/photos_public.gne?tags=hackdayindia&lang=en-us&format=atom
    and all you need to do to use this information directly in JavaScript is rename the “atom” at the end to “json”.

    Do this and you’ll get a bunch of JavaScript instead of the atom feed:

    http://api.flickr.com/services/feeds/photos_public.gne?tags=hackdayindia&lang=en-us&format=json
    
    jsonFlickrFeed({
    "title": "Photos from everyone tagged hackdayindia",
    "link": "http://www.flickr.com/photos/tags/hackdayindia/",
    "description": "",
    "modified": "2007-10-12T14:08:47Z",
    "generator": "http://www.flickr.com/",
    "items": [
    {
    "title": "Open Hack Day coverage in Indian newspaper",
    "link": "http://www.flickr.com/photos/codepo8/1552753330/",
    "media": {<a href=""http://farm3.static.flickr.com/2116/1552753330_d4c40d1132_m.jpg" title="">m</a>"},
    "date_taken": "2007-10-12T15:08:47-08:00",
    "description": "<p><a href="http://www.flickr.com/people/codepo8/">codepo8</a> posted a photo:</p> <p><a href="http://www.flickr.com/photos/codepo8/1552753330/" title="Open Hack Day coverage in Indian newspaper"><img src="http://farm3.static.flickr.com/2116/1552753330_d4c40d1132_m.jpg" width="209" height="240" alt="Open Hack Day coverage in Indian newspaper" /></a></p> <p>The Bangalore newspaper Midday put up this article about the Open Hack Day and apparently I am an ethical Hacker surrounded by Brad and David :)</p>",
    "published": "2007-10-12T14:08:47Z",
    "author": "nobody@flickr.com (codepo8)",
    "author_id": "11414938@N00",
    "tags": "india newspaper bangalore fame midday bradleyhorowitz davidfilo hackday christianheilmann joearnold hackdayindia"
    },
    {
    "title": "Break It",
    "link": "http://www.flickr.com/photos/code_martial/1536271756/",
    "media": {<a href=""http://farm3.static.flickr.com/2283/1536271756_c0da3d5f9b_m.jpg" title="">m</a>"},
    "date_taken": "2007-10-06T23:18:58-08:00",
    "description": "<p><a href="http://www.flickr.com/people/code_martial/">code_martial</a> posted a photo:</p> <p><a href="http://www.flickr.com/photos/code_martial/1536271756/" title="Break It"><img src="http://farm3.static.flickr.com/2283/1536271756_c0da3d5f9b_m.jpg" width="160" height="240" alt="Break It" /></a></p> <p>Joe Arnold does the Break Dance.</p>",
    "published": "2007-10-10T20:43:01Z",
    "author": "nobody@flickr.com (code_martial)",
    "author_id": "61697474@N00",
    "tags": "yahoo dance bangalore taj hackers hacking 50mmf18af hax0rz tajresidency joearnold hackdayindia"
    },
    
    // ... and so on ....
    ]
    })
    

    Step 3: Write your display function

    As you can see, the Flickr API encloses the dataset inside a function call that executes jsonFlickrFeed, which allows you to define a function with that name:

    
    <script>
    function jsonFlickrFeed(o){
    alert(o);
    }
    </script>
    <script src="http://api.flickr.com/services/feeds/photos_public.gne?tags=hackdayindia&lang=en-us&format=json"></script>
    

    The data of the JSON object is a tad askew, especially the encoded output inside the description is pretty much useless in JavaScript. However it is really easy to get the image data and display all the images using the media.m property of each item:

    
    <script>
    function jsonFlickrFeed(o){
    var i=0;
    while(o.items[i]){
    document.write('<img src="' + o.items[i].media.m + '" alt="' + o.items[i].title +'">')
    i++;
    }
    }
    </script>
    <script src="http://api.flickr.com/services/feeds/photos_public.gne?tags=hackdayindia&lang=en-us&format=json"></script>
    

    I trust you know however that using document.write is the main reason for hair loss and other problems in this world, so use some nice DOM scripting instead. This was just for the sake of this example.

    Step4: Choose the size of the photos with different file endings.

    The only remaining annoyance is the size of the photos. Once again, hacking the URLs of flickr helps you there. By default, flickr will choose m which is the medium size of the photos. Simply rename the something to the size you need:

    • _b is the big size (1024)
    • _t is the thumbnail
    • _s is the 75×75 pixels square
    • _m is the medium size
    • removing the _? extenstion means you choose the small size.

    You can also get the sizes by clicking around the image detail pages, for example http://www.flickr.com/photo_zoom.gne?id=1536271606&size=s and checking the image properties.

    Happy Hacking!

    [tags]flickr,hacks,openhackdayindia,JSON,howto,trick,webdevtrick[/tags]