CSS easy layout

10 06 2009

My advice si to forget the ruler and download Blueprint . Blueprint is a CSS framework, which gives you a grid to place your elements and basic, clean typography.

One issue with the grid provided by Blueprint is that the footer doesn’t stick to the bottom of the page.

However, there is a hack for this, which described below.

How to keep the footer at the bottom of the page

Your CSS:

* {
margin: 0;
}
html, body {
height: 100%;
}
.wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -4em;
}
.footer, .push {
height: 4em;

}

The HTML:

<html>
<head>
<link rel=”stylesheet” href=”layout.css” … />
</head>
<body>
<div>
<p>Your website content here.</p>
<div></div>
</div>
<div>
<p>Copyright (c) 2008</p>
</div>
</body>
</html>

from Ryan Fait’s blog post .





make MySQL transactions work in PHP

27 03 2009

Requirements:

  1. All tables involved in the transactions in your database must be TYPE=InnoDB.
  2. I am using a library to connect to the database. However, if you are familiar with the PHP mysql API  functions , you can replace the library calls with the API calls. The general concept is still valid.

Step.1 The PHP wrapper

Create php functions to wrap the mysql queries that implement the transaction syntax.

<?php

//transactions
function begin()
{
@mysql_query("BEGIN");
}
function commit()
{
@mysql_query("COMMIT");
}
function rollback()
{
@mysql_query("ROLLBACK");
}

?>

Implement a function to return the value of the last id inserted (this will be used to check that the query has been performed successfully).

//get id from the last inserted record
function get_last_inserted_id($table)
{
    $q = "SELECT LAST_INSERT_ID() FROM $table";
    return mysql_num_rows(mysql_query($q));
}

Step.2 The main code

To test it, say you have two tables ‘project’ and ’street’. You want to insert a new project in the project table and a new street for that project  into the street table.

If any of the two transactions don’t work, mysql will rollback, and therefore no records will be inserted.

Note: you can put as many queries as you want between begin() and commit()/rollback().

begin(); // transaction begins
$db=open_db();
//add to projects
$db->add_record(’project’,$fields);
$prj_id=$db->get_inserted_id();
//add to street
$db->add_record(’street’,$fields);
$street_id=get_last_inserted_id(’street’);
echo (”<BR>”.$prj_id.” “.$street_id);
$db->close();
if(!$prj_id||!$street_id)
{
rollback(); // transaction rolls back
echo “you rolled back”;
exit;
}
else
{
commit(); // transaction is committed
echo “your insertion was successful”;
exit;
}





Get the difference between two arrays in PHP

26 03 2009

This is a little useful function which gets all the elements that are in one array but not in another.

/*
It returns all the elements in array $b that do not appear in array $a.
*/
function array_sub_array($a, $b) {
$tot=count($b);
       for($i=0;$i<$tot;$i++){
              if(!in_array($b[$i],$a)){$c[]=$b[$i];}

        }
        $c=array_filter($c);//HACK: delete NULL values
        return $c;
}




prev/next navigation in Plone

16 03 2009

Problem:

being able to navigate through the items of a Plone folder as you browse them.

case-study:

I had several images and other documents all in the same folder. As they  landed on a page, I wanted users to be able to navigate to the previous / next image without having to go back to the root folder.

Solution:

  1. retrieve the objects  from the folder
  2. place them in a list
  3. get the position of the current object in the list
  4. calculate the previous and next positions from the current position (-1 and +1, respectively)
  5. create the links to these objects (remember to add the template at the end of the object’s url if you have a customised view for such object)

Implementation:

<div metal:fill-slot="main">
<div  tal:define=".....
chapters python:here.aq_parent.getFolderContents(contentFilter = {'portal_type' : ['BevanLetterImage']});
 sibl     python:[p for p in chapters];
 numsibl python:len(sibl);
 pos     python: [i for i in range(numsibl)if sibl[i].getId == here.getId()][0]" >
<!--prev next nav-->
 <div class="listingBar" tal:condition="python: next or prev"
 tal:define="next python:pos < numsibl-1;
 prev python:pos != 0;">
 <span tal:condition="next">
 <a class="listingNext"
 tal:define="nextsib python:sibl[pos+1]"
 tal:attributes="href string:${nextsib/getURL}/view"
 tabindex="1"
 href="">
 <span i18n:translate="label_next">
 Next: </span>
 <span tal:replace="nextsib/Title" />
 </a>
 </span>
 <span tal:condition="prev">
 <a class="listingPrevious"
 tal:define="prevsib python:sibl[pos-1]"
 tal:attributes="href string:${prevsib/getURL}/view"
 tabindex="2"
 href="">
 <span i18n:translate="label_previous">
 Previous: </span>
 <span tal:replace="prevsib/Title" />
 </a>
 </span>
 </div>
<div style="clear:both"></div>
<!--end prev next nav-->




Displaying folder contents in Plone

16 02 2009

Problem:

retrieve only specific contents of a Plone folder, order the list by a customised criteria (i.e. date, title) and display some of their related info.

case-study:

I have a letter with related images ( a customised product that I created, called “BevanLetterImage”)  in one folder. I want to display the images below the letter and order them by title.

Solution:

create a list which displays from the folder contents only the images and specify the title as the  sortable criteria.

Implementation:

Insert the following code in the part of the template where you want the list to show.

The code retrieves only the object of type aType and according to criteria sortable_criteria (examples of criteria: ‘sortable_title’, ‘Date’).

For each image it displays the title and the image as a thumbnail. Both are also rendered as links to the real image.

<!--image list-->
<div id="photo-holder">
 <tal:foldercontents define="contentFilter contentFilter|request/contentFilter|nothing;
                            limit_display limit_display|request/limit_display|nothing;
                            more_url more_url|request/more_url|string:folder_contents;
                            contentsMethod python:test(here.portal_type=='Topic', here.queryCatalog, here.getFolderContents);
                            folderContents folderContents|python:contentsMethod(contentFilter={'sort_on':'sortable_criteria','portal_type':['aType']}, batch=True);
                            use_view_action site_properties/typesUseViewActionInListings|python:();
                            over_limit python: limit_display and len(folderContents) > limit_display;
                            folderContents python: (over_limit and folderContents[:limit_display]) or folderContents;
                            batch folderContents">
<h3>Photos of the original letter (<span tal:content="python:len(folderContents)"></span> in total)</h3>

 <tal:listing condition="folderContents">

            <tal:block tal:repeat="item folderContents">
                <div  tal:define="item_url item/getURL|item/absolute_url;
                                       item_id item/getId|item/id;
                                       item_title_or_id item/pretty_title_or_id;
                                       item_description item/Description;
                                       item_type item/portal_type;
                                       item_type_title item/Type;">
<!-- custom -->                  
 <a  href="#"
                       tal:attributes="href python:item_url+'/view'"><p class="photo-title" tal:content="item_title_or_id"></p>
  <img src="" alt="" tal:attributes="src python:item_url+'/image_thumb'"  />
                    </a>
<!-- end of custom -->
               </div>
 </tal:block>
 </tal:listing>
 </tal:foldercontents>
 </div>  <div style="clear:both"></div>
<!--end of image list-->




Rotate and zoom images with Javascript

17 11 2008

A Javascript script to rotate and zoom images using the canvas element.

Installation

in the BODY of your html page insert the commands which will trigger the transformations and the image you want to rotate, embedded in a canvas element:

<body onload="CanvasOnload()">
<h1>Image rotate and zoom</h1>
    <span style="text-align: center; width: 65px">
        <a href="javascript: location.href = 'index.html'" style="text-decoration: none">
            <img src="reset.png" alt="Reset the image" border="0" />
        </a>
    </span>

<p>
	rotate:
	<input type="button" value="0°" onclick="rotate(0);">
	<input type="button" value="90°" onclick="rotate(90);">
	<input type="button" value="180°" onclick="rotate(180);">
	<input type="button" value="-90°" onclick="rotate(-90);">
</p>

<div>
    <span style="text-align: center; width: 65px">
        <a href="#"
           onmousedown="mousedown = true; ZoomOut(document.getElementById('myimage'))"
           onmouseout="mousedown = false"
           onmouseup="mousedown = false"
           style="text-decoration: none">
            <img src="minus.jpg" alt="Zoom out" border="0" />
        </a>
    </span>
    <span style="text-align: center; width: 65px">
        <a href="#"
           onmousedown="mousedown = true; ZoomIn(document.getElementById('myimage'))"
           onmouseup="mousedown = false"
           onmouseout="mousedown = false"
           style="text-decoration: none">
            <img src="plus.jpg" alt="Zoom in" border="0" />
        </a>
    </span>
</div>

<canvas id="ff_canvas" width="159" height="198">
    <img src="/js-tests/rotateImage/fashionable.jpg"
         alt="Zend"
         width="159"
         height="198"
         id="myimage"
         style="margin-left: 50px; position: absolute; top: 110px; left: 250px; cursor: hand" />
</canvas>
</body>

In the HEAD of your html page, you need to insert the references to your scripts:

<script type="text/javascript" src="imgRotateCanvas.js"></script>
<script language="javascript" src="imgZoomCanvas.js"></script>

This is the code in “imgRotateCanvas.js”:

/*Author: Val Cartei,  val.cartei@gmail.com.
Year:2008
Description: rotates an image embedded in a canvas element.

This script is an adaptation of a script by: Benoit Asselin | http://www.ab-d.fr. The original script can be found at:
The JavaScript Source!! http://javascript.internet.com
*/

/**
*function: rotate
* @param int p_deg - the degrees of the rotation (e.g. -90,180,270,0).
*/
function rotate(p_deg) {
	if(document.getElementById('ff_canvas')) {
		/*
		Ok!: Firefox 2, Safari 3, Opera 9.5b2
		No: Opera 9.27
		*/
		var image = document.getElementById('myimage');
		var canvas = document.getElementById('ff_canvas');
		var canvasContext = canvas.getContext('2d');

		switch(p_deg) {
			default :
			case 0 :
				canvas.setAttribute('width', image.width);
				canvas.setAttribute('height', image.height);
				canvasContext.rotate(p_deg * Math.PI / 180);
				canvasContext.drawImage(image, 0, 0);
				break;
			case 90 :
				canvas.setAttribute('width', image.height);
				canvas.setAttribute('height', image.width);
				canvasContext.rotate(p_deg * Math.PI / 180);
				canvasContext.drawImage(image, 0, -image.height);
				break;
			case 180 :
				canvas.setAttribute('width', image.width);
				canvas.setAttribute('height', image.height);
				canvasContext.rotate(p_deg * Math.PI / 180);
				canvasContext.drawImage(image, -image.width, -image.height);
				break;
			case 270 :
			case -90 :
				canvas.setAttribute('width', image.height);
				canvas.setAttribute('height', image.width);
				canvasContext.rotate(p_deg * Math.PI / 180);
				canvasContext.drawImage(image, -image.width, 0);
				break;
		};

	} else {
		/*
		Ok!: MSIE 6 et 7
		*/
		var image = document.getElementById('myimage');
		switch(p_deg) {
			default :
			case 0 :
				image.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)';
				break;
			case 90 :
				image.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)';
				break;
			case 180 :
				image.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2)';
				break;
			case 270 :
			case -90 :
				image.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)';
				break;
		};

	};
};

// Multiple onload function created by: Simon Willison
// http://simonwillison.net/2004/May/26/addLoadEvent/
function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}

addLoadEvent(function() {
	var image = document.getElementById('myimage');
	var canvas = document.getElementById('ff_canvas');
	if(canvas.getContext) {
		image.style.visibility = 'hidden';
		image.style.position = 'absolute';
	} else {
		canvas.parentNode.removeChild(canvas);
	};

	rotate(0);
});

This is the code in “imgZoomCanvas.js”:

/*Author: Val Cartei,  val.cartei@gmail.com.
 Year:2008
 Description: rotates an image embedded in a canvas element.
This script is an adaptation of a script that can be found at:
 http://www.phpguru.org/downloads/image_rotate/rotate.html
 )*/
 <!--
 mousedown = false; // An important global variable
/**
 * The zoom out function
 *
 * @param object obj The image object
 */
 function ZoomOut(obj)
 {
 if (document.all) {
 // In case the zoom property is not set
 if (obj.style.zoom == '') {
 obj.style.zoom = 1;
 }
if (parseFloat(obj.style.zoom) <= 0.1) {
 return;
 }
obj.style.zoom = obj.style.zoom - 0.1;
if (mousedown) {
 setTimeout("ZoomOut(document.getElementById('" + obj.id + "'))", obj.height);
 }
 } else {
 var canvas  = document.getElementById('ff_canvas');
 var context = canvas.getContext('2d');
canvas.style.width = ((parseInt(canvas.style.width) ? parseInt(canvas.style.width) : obj.width) - 10) + 'px';
 canvas.style.height = ((parseInt(canvas.style.height) ? parseInt(canvas.style.height) : obj.height) - 10) + 'px';
if (mousedown) {
 setTimeout("ZoomOut(document.getElementById('" + obj.id + "'))", 50);
 }
 }
 }
/**
 * The zoom in function
 *
 * @param object obj The image object
 */
 function ZoomIn(obj)
 {
 if (document.all) {
 // In case the zoom property is not set
 if (obj.style.zoom == '') {
 obj.style.zoom = 1;
 }
// Lower limit is 0.1
 if (parseFloat(obj.style.zoom) <= 0.1) {
 obj.style.zoom = 0.1;
 }
obj.style.zoom = Number(obj.style.zoom) + 0.1;
if (mousedown) {
 setTimeout("ZoomIn(document.getElementById('" + obj.id + "'))", obj.width);
 }
 } else {
 var canvas  = document.getElementById('ff_canvas');
 var context = canvas.getContext('2d');
canvas.style.width = ((parseInt(canvas.style.width) ? parseInt(canvas.style.width) : obj.width)+ 10) + 'px';
 canvas.style.height = ((parseInt(canvas.style.height) ? parseInt(canvas.style.height) : obj.height)+ 10) + 'px';
if (mousedown) {
 setTimeout("ZoomIn(document.getElementById('" + obj.id + "'))", 50);
 }
 }
 }
/**
 *
 */
 function Reset(img)
 {
 img.style.zoom = 1;
 img.rotation = -0.01;
 Rotate(img, 0.01);
 }
function CanvasOnload()
 {
 // IE
 if (document.all) {
 return;
 }
var canvas  = document.getElementById('ff_canvas');
 var context = canvas.getContext('2d');
 var img     = document.getElementById('myimage');
context.drawImage(img, 0, 0, img.width, img.height);
 }
/**
 * Shows the status of the rotation
 */
 function ShowStatus()
 {
 var img  = document.getElementById('myimage');
 var zoom = Number(img.style.zoom);
alert('Zoom factor: ' + (zoom ? zoom : 1) + '\nRotation factor: ' + (img.rotation ? img.rotation : '0') );
 }
 // -->

These are image files used. Put them in the root directory together with your html page and the .js scripts.

Zoom in:

plus

ZoomOut:

minus

and the sample image:

fashionable





Jquery, Ajax forms, PHP and Mysql at the table

3 11 2008

Before starting, if you have PHP.x < PHP.2, you need to put into your includes folder the library jsonwrapper (this encodes/decodes json objects).

Imagine you have a form to add an item to a shop and you want to create a new category for that shop without leaving that page.

One way to do it is to have a link “add a new category” that when clicked, shows some input fields for adding the new category (e.g. name and description) and a button to submit these data.

When the button is clicked, javascript detects that you do not want to send the whole form, but instead it retrieves these fields and sends them as an Ajax request to the server. The php code on the server side will get the data, perform the request (adding the category) and return some data (e.g. the new category object) to the form. Javascript waits for the response and processes it.

This is the general idea, now let’s have a look at the code:

Client-side: form.php

in the HEAD insert javascript:

<script language="javascript" type="text/javascript" src="/assets/inc/jquery.js"></script>

<script language="javascript" type="text/javascript">
$(document).ready(function(){
$("ul#add-category-fields").hide();
$("a#add-category").click(function(){
     // Show subform to add a new category
     $("ul#add-category-fields").show();
     return false;
    });

    $("#btn-add-category").click(function(){
        $("#action").val("add category");
        $("#fm_edit").submit();
    });

    $("#fm_edit").submit(function(){
        if($("#action").val()=="add category")
        {
            //data to send
            var data={'catname': $("#catname").val(),'catdescription':$("#catdescription").val(),'add': "add"};
               $.post("manage_category.php",data, function(data) {
               if(data.idshopcategory>0)
               {
                    $("new category has been added").insertAfter("a#add-category");
                    $("ul#add-category-fields").hide();//hide again the fields
               }else $("<br><p class='alert'>something went wrong, category could not be added, try adding the new category from the Categories menu.</p>").insertAfter("a#add-category");
               },"json");//end post
               $("#action").val("");
               return false;
        }else{
           //send to this page for edit item processing
            $("#action").val("");
           return true;
        }
   });
});//end document ready
</script>

The first line imports jquery library

The second script tag includes Ajax interaction:

it dynamically displays the fields in the form that will be sent as Ajax (hidden at the beginning) and when submitted it discriminates between the whole form and the sending of these fields (’action’).

it retrieves those fields within the form whose values we want to send to the server and encodes them as a json object.

it  sends a simple POST request to a server with the data as json.

it handles the response by veryifing the data sent back by the server. If successful, it modifies the form and add a positive feedback, otherwise it shows a negative feedback.

in the BODY:

within the HTML form

....
 <input type="hidden" id="action" name="action" value="">
.....

<a href="#" id="add-category">add a new category</a>
   <ul id="add-category-fields">
        <li><label for="catname"></label> <input type="text" id="catname" name="catname" value=""></li>
        <li><label for="catdescription"></label> <input type="text" id="catdescription" name="catdescription" value=""></li>
        <li><input type="button" name="add category" id="btn-add-category" value="Add category"></li>
  </ul>

Server-side: manage_data.php

PHP code:

<?php
//initialise vars
$new_record_id=0;$name="";$description="";
//json object returned to the calling page
$foo= array(
  'idshopcategory'  =>0,
  'catname'   =>0,
);

require_once incpath("/jsonwrapper/jsonwrapper.php"); //encode/decode json objects for AJAX calls
//get the data from the post request sent through javascript
if(!empty($_POST["add"])){

$name=$_POST['catname'];
$description=$_POST['catdescription'];
$new_record_id= $ShopCategory->newRecord($data);
}

 //json object returned to the calling page
$foo= array(
 'idshopcategory'  =>1,
 'catname'   =>$name,
'catdescription'   =>$description,
);
// Now encode the array to JSON format
$json = json_encode($foo);
// encode array $json to JSON string
echo $json;
die();
?>

it creates an array of values to be returned

it encodes them as a json object (using json_encode as defined in the library jsonwrapper)

it sends the object to the page form.php.





Know your users – web stats made easy

29 10 2008

Google Analytics offers an easy way to get statistics of your website, but one of his major drawbacks is that

you can’t analyze historical data. Luckly, I found a software that can do that, Urchin 5.

Feed your logs into Urchin

Feed your log files into Urchin

zip your log files (e.g. logfiles.zip)

in Profiles>Log manager add a new profile

the popup will ask you the following:

Log file location – choose local

Log file path – select the path to your zipped file ( in my case, logfiles.zip).

Log format – choose auto

Get your reports

Now click on Profiles menu again and in the table you’ll see the newly created profile listed. Click on run (the lightning icon), this will run the analysis of the log source.

Then, on view (the zoom icon) to see the results. These open in a separate tab and on the left you can see the menu to navigate through the different reports. The most important for me are:

Traffic>Summary, which shows totals and averages for Sessions, Pageviews, Hits, and Byte. You can see details for each of these by clicking on the corresponding item in the Traffic menu.

Page&Files> Requested Pages, which  ranks the popularity of the Pages

Page&Files> Downloads, ranks the popularity of all Downloads on your site by number of requests

All Files> All Files by Hits, ranks the popularity of all files on your site by number of requests, which is important to detect for example how many times a given document has been downloaded or an image been viewed.

Navigation> Entrance pages, which indicates which is thepage from which Visitors entered your website (the first page they saw).

Referrals>Referrals, which list the referring URLs (external web pages) that brought traffic to your site.

Browers &Robots> Browsers, ranks the browsers used by the public to access your site.

Browers &Robots> Platforms, ranks the platforms (e.g. Windows,Mac…) used by the public to access your site.

Each statistic can also be exported in different formats. I personally choose .doc as it appears to be the easiest to read, but you can have your data in Excel or tab separated, plain text. And of course, you can also print them.

Advantages and Disadvantages

A major drawback is that Urchin software is not free! The demo licence expires after 90 days.

A major advantage, it can be used with Google Analytics:

  1. Copy your Google Analytics tracking code into a text editor.
  2. Add the line in bold below to your tracking code (your actual code will contain your account and profile number in place of xxxx-x):


    <script type=quot;text/javascript">
    var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
    document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
    </script>
    <script type="text/javascript">
     var pageTracker = _gat._getTracker("UA-xxxxxx-x");
     pageTracker._setLocalRemoteServerMode();
    pageTracker._trackPageview();
    </script>

    This additional line will serve the pageview to Urchin Software as well as Google Analytics, allowing you to gather data with both products.

  3. Then, remove this line of code from your webpages: <script src=”__utm.js” type=”text/javascript”>
  4. Add the tracking code to each of your pages.




Dev and Live version of a WordPress site

1 08 2008

Setting up the DEV and LIVE sites

Download the wordpress installation:  http://wordpress.org/download/.

In the dev and live servers, create a new empty folder in your websites roots.

Unzip the wordpress installation and copy the files in the newly created folders.

Set up the Apache’s virtual host for your dev and remote sites.

Create a new empty database i.e. culturegeeks in the dev server and another with the same name in your remote server.

Rename wp-example.php in wp-config.php.

Open wp-config.php and set the database settings so that if you are in your dev server you will connect to the dev database, otherwise it will connect to the remote database. In my case that’s what I got:

switch($_SERVER['SERVER_NAME']){
 case '192.168.0.2':
 //dev site (fileserver)
 define('DB_USER', 'I am not gonna publish it here!');     // Your MySQL username
 define('DB_PASSWORD', 'I am not gonna publish it here!'); // ...and password
 define('DB_HOST', '192.168.0.2');    // 99% chance you won't need to change this value
 break;
 default:
 //live site (dl320) //remote user:admin pwd:*g11eiKu9Y#E
 define('DB_USER', 'I am not gonna publish it here!');     // Your MySQL username
 define('DB_PASSWORD', 'I am not gonna publish it here!'); // ...and password
 define('DB_HOST', '212.42.165.194');    // 99% chance you won't need to change this value
 break;
}
define('DB_NAME', 'culturegeeks');    // The name of the database

Go to your dev website address and run the installation script:
devyourwebsite/wp-admin/wp-install.php

This will set up the dev website.

Go to your live website address and run the installation script:
devyourwebsite/wp-admin/wp-install.php

This will set up the live website.

You will be asked to give your blog a title and an email address. Next, you will be prompted with a username and password. Save these details, as you’ll require them when you access the website’s dashboard (admin area).

Set up the new site in Dreamweaver, where the local settings refer to the dev site and the remote settings your remote site’s ftp details. Test the connection is successful.

The two sites have now been initialized and should be exactly the same.

Update your remote site

Let’s say we want to make some changes to the look&feel of the website. First, we will apply a new template to the dev site, twick it and then upload the changes to the live site.

First, download the new template. A range of free templates can be found at: WordPress Theme Viewer

Unzip the package i.e. dummy_Theme into the themes/ folder of your dev site. 

Go to your dev site’s dashboard, the new theme should appear as one of the available templates.

Make changes, i.e. change the logo (which can be found in the images/ folder of the theme).

In Dreamweaver, right-click on your chosen theme, theme/dummy_Theme  and click Put. This will automatically upload the folder in your live site.

Go to your remote site’s dashboard, the new theme should appear as one of the available templates.

References

http://codex.wordpress.org/Main_Page - wordpress guide

http://codex.wordpress.org/Installing_WordPress#Detailed_Instructions - detailed installation’s instructions





Photoalbum with Flickr – part 1 testing the API

29 07 2008

The aim

I wanted to have a photoalbum for the buildings and the people who lived in a street. However, I wanted the photoalbum to be as shareable and robust as possible. Thus, instead of building my own, I opted for using Flickr.

Apart from the fact that Flickr is possibly the most popular photosharing network, it has also the great advantage of offering an API which allows seamless integration with your applications. Furthermore, lots of people are contributing to create wrappers on different languages which simplify the API calls.

On this regard, since the project is written in PHP, I decided to adopt phpFlickr for my purposes. At the time of this post, the version is 2.2.0.

Run a preliminary test

The first step was to get the API key . This is free and you can obtain it by logging into your Flickr’s account and clicking on: http://www.flickr.com/services/api/keys/

Once you have completed the registration process, you will be given two codes, which you will need later to access Flickr, an api key and a secret.

I always do my experiments in a local website, which acts like a sandbox (”tests”) for my experiments so that I do damage my actual applications. Thus to test Flickr API, I have created a subfolder “flickrapi” in my tests folder. I have also created an empty file index.php and checked the server sees the new folder:

localhost:8020/flickapi (where localhost:8020/ is my virtual host for the tests folder in Apache)

Next, I have downloaded phpFlickr in the “flickrapi” folder.

To check that the package is seen by php, I have modified flickrapi/index.php with:

<?
include "phpFlickr/phpFlickr.php";
?>

If no errors are displayed, the package has successfully accessed.

The second step was to start using the API itself. phpFlickr implements all the API methods as methods of a Flickr class. So you need to instantiate a Flickr object first, by doing:

$f = new phpFlickr($api_key,$secret,$die_on_error);

where:

$api_key and $secret – are the API key and secret code obtained earlier.

$die_on_error – is a boolean value and determines whether operations should cease if the API returns an error (default is false).

If no error occurred, you can start quering Flickr using the specified methods on $f ( these are defined in phpFilckr.php).

Since the test was successful, I moved on to play a bit with Flickr’s calls. In particular I wanted to:

1. access my photos

2. retrieve pictures according to their tags and machine tags

access to my photos

First, I searched among the people’s database on Flickr, my own account. Then, from my account’s details, I retrieved my user id and using this I got access to my photos.

$username="myusername_example";
  // Find the NSID of the username inputted via the form
    $person = $f->people_findByUsername($username);

    // Get the friendly URL of the user's photos
    $photos_url = $f->urls_getUserPhotos($person['id']);

get photos with specific tags and machine tags

To run a search, I used photos_search method and as the $tags variable I specified the tags/machine tags I wanted to filter my photos by.

//Get photos that have tag "flat"
$photos =$f->photos_search(array("user_id"=>$person['id'],
"tags"=>"flat",
"per_page"=>6));
//Get photos with machine tag building num 2, which represents a building number.
      $photos =$f->photos_search(array("user_id"=>$person['id'], "machine_tags"=>"myhousemystreet:buildingnum=\"2\"", "per_page"=>6));

The last (optional) step for me was to move the phpFlickr package from my test folder “flickrapi” to the folder which contains all my php packages, in my PHP5 installation. Then, I had to modify my code accordingly to check it still worked. This final test allowed me to see that I can now use phpFlickr in any site I want from now on, by just inserting:

require_once incpath("/phpFlickr/phpFlickr.php");

At the top of my php files. This is an optional step, but if I didn’t do that, every time I needed to use Flickr API, I would have had to copy phpFlickr as a folder within my new website.

So here’s my final index.php:

<?
/**
 * Testing connection to Flickr, using API.
 * inspired by http://phpflickr.com/
*/
require_once incpath("/phpFlickr/phpFlickr.php");

$api_key ="c0fde778bd98ac8701f2d4e8ef0be50c";
$secret ="dcf4ae887a9b1a06";
$die_on_error =true;// cease operation) if the API returns an error statement
$f = new phpFlickr($api_key,$secret,$die_on_error);
//SEARCH FOR PHOTOS
$username="val.cartei";
  // Find the NSID of the username inputted via the form
    $person = $f->people_findByUsername($username);

    // Get the friendly URL of the user's photos
    $photos_url = $f->urls_getUserPhotos($person['id']);

	//Get first 10 photos with specified tag in $extras
	  $photos =$f->photos_search(array("user_id"=>$person['id'], "machine_tags"=>"myhousemystreet:bnum=\"2\"", "per_page"=>6));
/*
    // Get the user's first 10 public photos
    $photos = $f->people_getPublicPhotos($person['id'], NULL, 10);
*/
	$i=0;
	  // Loop through the photos and output the html
    foreach ((array)$photos['photo'] as $photo) {
        echo "<a href=$photos_url$photo[id]>";
        echo "<img border='0' alt='$photo[title]' ".
            "src=" . $f->buildPhotoURL($photo, "Square") . ">";
        echo "</a>";
        $i++;
        // If it reaches the sixth photo, insert a line break
        if ($i % 6 == 0) {
            echo "<br>\n";
        }
	}

?>

Resources

http://www.flickr.com/services/api/ – Flickr’s API Official Documentation

http://phpflickr.com/ – the official website for the phpFlickr package.