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.





Error and Feedback Handling for PHP

10 07 2008

This is a simple way to handle your errors and warnings and provide users with a feedbackfor their actions.

The logic

Write an error handler and assign it to the php built-in error handling mechanism (set_error_handler(“yourhandler”)) where ever you want it to operate. In my case I put it together with the function definition in the file where I keep all my other defaults as such file gets included in all the files of my project, so I avoid to rewrite code for each other file.

The error handler will throw an exception for every error or warning encountered. So, whenever you wish your user to get a message (whether for error or success), put the critical code in a try/catch construct and in the catch body set two session variables, one “alerts” (String) containing the positive/negative messages and one called “input_errors” (Array) which contains the error parameters if any (e.g. if input from forms). Be sure you exit the script or the code that follows will be executed (usually you don’t want that especially if a nasty error had occurred).

In the html pages, retrieve the content of the session variables for disaplay (if the variable “input_errors” is empty, we assume that it is a success message) and then delete them from the session.

Implementation

in the file where you keep all your settings and therefore is contained in all the others (def_inc.php susually):

<?
……..
//ERROR HANDLING
//setting general error handler
function handleError($errno, $errstr,$error_file,$error_line,$errcontext)
{
$msg= “Sorry some error occurred. “;
$msg.= “<b>Error:</b> [$errno] $errstr – $error_file:$error_line”;
$msg.= “<br />”;
putsession(“alerts”,$msg);
putsession(“input_errors”, “some error”);
throw new Exception($msg);
}
//set error handler
set_error_handler(“handleError”);
………..
?>

In the template for html files which display the feedback messages to the user:
<HEAD>
……….
//handle alerts
$alerts=null;
//check for alerts or errors back from the processing code (putsession(‘alerts’,'bla,bla…’),putsession(‘errors’,parameter_errors()))
$input_rules["input_errors"][_structure]=_either_structure;
$input_rules["input_errors"][_sources]=_session;
$input_rules["input_errors"][_required]=false;
$input_rules["input_errors"][_fatal]=false;
$input_rules["alerts"][_structure]=_either_structure;
$input_rules["alerts"][_sources]=_session;
$input_rules["alerts"][_required]=false;
$input_rules["alerts"][_fatal]=false;
$parameters = validate_parameters($input_rules);
$errors = isset($parameters["input_errors"])?$parameters["input_errors"]:null;
$alerts = isset($parameters["alerts"])?$parameters["alerts"]:null;
delsession(“input_errors”); //reset input errors to null
delsession(“alerts”); //reset alerts to null
…….
</HEAD>

<BODY>
….
<? //alert and error system
if(isset($alerts)||isset($errors)){?>
<div id=”alerts”><?
if(isset($errors)){?>
<div class=”fail”><?=$alerts?></div>
<? }else{?>
<div class=”success”><?=$alerts?></div>
<? } ?>
</div>
<?
if(!empty($errors["access"]))return;
}//end alerts box
?>
…..
</BODY>





Museums and IT? Let’s sit around the table…

20 06 2008

So many museums have now websites and collections online, that starting a discussion about the use of IT and the advantages it may bring to the promotion of culture, could be labelled as outdated, petty rethoric.

However it looks like the cultural sector’s main interest in IT is on the digitisation of historical assets and their online availability, more for a need of internal organisation, better preservation and a hope of attracting larger audiences, than anything else.

Needless to say, IT can do much, much more and do it better.

For example, why not using technology to communicate, rather than just present, your collections online?

Would technology enable to create a closer relationship between cultural organisations and the general public?

Who would benefit from this two-way process?

Both would.

This was the general conclusion that other fellow geeks and I came out with on a Friday afternoon, admittedly after a few beers. We know that there are initiatives and projects out there which are dealing with these issues and maybe it’s time to dig them out and contribute to the discussion.

So we decided to start a local group which will bring together geeks, museums and other cultural organsations devoted to the preservation and communication of our heritage…put down the glasses and off we go!





Adopt A Street – interact with the database (day 2)

9 06 2008

Define test data

This is done by creating a yml file which contains for the tables you want the data to be uploaded, specified as objects.

convert your xml schema to its yaml version, to use it as template for your sample that will populate your db, this will be saved in config\schema.yml

C:\websites\aas\>symfony propel-convert-xml-schema

in data folder, create a folder “fixtures”

Inside fixturex, create a yml file “test_data.yml”

copy and paste the content of schema.yml in test_data.yml.

Delete schema.yml (or it conflicts with schema.xml).

Delete all attributes specifications except their names so you will end up with the skeleton of the db i.e.:

street

idstreet:

streetname:

building:

idbuilding:

buildingname:

buildingnum:

Edit the file to enter the data (each object is defined by the name you choose and the list of attributes) i.e.:

Street:
foundry_st:
streetname: Foundry Street
city: Brighton
postcode: BN1
county: East Sussex
nation: UK
latitude:
longitude:

george_st:
streetname: George Street
city: Brighton
postcode: BN1
county: East Sussex
nation: UK
latitude:
longitude:
….

Create a batch to populate the database

The next step is to actually populate the database, and we wish to do that with a PHP script that can be called with a command line – a batch.

Batch script

Create a file called load_data.php in the askeet/batch/ directory with the following content:

<?php

//defines a path, an application and an environment to get to a
//configuration, loads that configuration, and initializes the database manager

define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..'));
define('SF_APP',         'frontend');
define('SF_ENVIRONMENT', 'dev');
define('SF_DEBUG',       true);

require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');

// initialize database manager
$databaseManager = new sfDatabaseManager();
$databaseManager->initialize();

// sfPropelData object is created, and told to load all the data of a specific directory
//our fixtures directory - into the database defined in the databases.yml configuration file

$data = new sfPropelData();
$data->loadData(sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.'fixtures');
?>

from command line:

C:\websites\aas\>php batch/load_data.php

References

http://www.symfony-project.org/askeet/1_0/en/3


Problems found and solved

after db modification, when you load the batch again, sometimes you get exceptions because symfony is looking for tables that no longer exist / have been modified. You need to clear the cache first (symfony cc) and then reload the batch.

If this doesn’t work, then:

clear the cache

delete the old schema: config/schema.xml

re-init the schema from the database (symfony propel-build-schema xml)

re-create the model (symfony propel-build-model )

clear the cache again

now load the data.





Adopt A Street – the backend (day 3)

29 05 2008

General aims:

1. Create the backend interface for administrators.

2. Start by mapping out the main tables, creating for each an interface which would allows us to see,edit,delete the records.

3. Map out

the relationships between tables and see how we can use a smaller number of interfaces to effect more tables at once.

1. Create the interface (from command line)

Create the backend application

symfony init-app backend

Create the backend admin interface for each table we need :

symfony propel-init-admin backend street Street

symfony propel-init-admin backend building Building

symfony propel-init-admin backend occupant Occupant

symfony propel-init-admin backend occupancy Occupancy

go to one of them to check everything is fine: http://localhost:8020/backend_dev.php/street

2. Map out the tables

goal1: add actions to each record

show the list of streets and for each street add the capability to edit the street details with an icon (_edit), apart from clicking on the street id.

Modify the button to create a new record ( _create)

Open generator.yml in the config folder of your street module (backend/street/config):

generator:

  class: sfPropelAdminGenerator
  param:
    model_class: Street
    theme: default

fields:

idstreet: {name: Street id}

       streetname:{name: Street name}
    list:
       title: Adopted streets
       object_actions:
         _edit: -
       actions:
         _create: {name: add a new street}

goal2: Modify the column headers

Replace the buildings table column headers which currently display idbuilding and buildingnum, with something more readable, i.e. Building ID and Civic Number. To do so, open the generator.yml for the building module and add the following:

fields:
      idbuilding:{name: Building ID}
      buildingnum:{name: Civic number}

goal3: modify how a value is displayed inside a column

The buildings table shows the id of the street each building belongs to. We would like to have the name of the street displayed instead. To do so, we need to create a partial (a piece of layout) and call this in the generator instead of the id.

Define a default method on the Street object (__toString) which returns the street name, in class Street.php.

public function __toString(){
       return $this->getStreetname();
        }

Define the partial: create _street.php in the templates folder of the building module. Call the toString method of the street object, as previously defined (to do so, is getClassname).

<?php
  echo $building->getStreet();
?>

Call the partial: open the generator.yml for building module and insert a display action where instead of using idstreet (which calls the value of that column) we call the partial (_street) we have defined.

    list:
      title: list of buildings
      display:[idbuilding, buildingnum, _street, buildingname, latitude, longitude]
      filters: [idstreet]

goal4: filter the table records

In the generator, under display add the filter according to the street, which will display only those buildings registered for that street.

display:....
filters: [idstreet]

3. Map out the relationships

goal 1: for each street in the streets table view, add a link which redirects to see all the buildings related to that street.

Create a custom object action (to be placed under _edit), with a tag name (which will appear on rollover), the name of the action to be called (which will be defined in the actions.class.php of street module (1)) and an icon (this is one of the collection provided by the system). This is the code:

list:

object_actions:

_edit: -

all_buildings:{name: see all buildings for this street, action: Buildings, icon: /sf/sf_admin/images/filter.png }

In streetActions class (action.class.php in the street module), define an action Buildings (so it has to be executeBuildings), which gets the id of the current record and redirects to the list of buildings using that record as a filter.

public function executeBuildings(){
   $this->redirect('building/list?filter=filter&filters[idstreet]='.$this->getRequestParameter('idstreet'));
  }

That’s what you end up with:

street table screenshot

Problems solved

symfony does not show foreign keys

this is because in your db model, you the foreign key is also primary. Define it solely as a foreign key, recreate schema xml and the model.





Adopt a Street – a Symfony project

15 05 2008

Create Adopt a Street (aas)

This will be a new Symfony project. Thus, we need to create an empty folder for it and then initialize it:

C:\websites>mkdir aas

C:\websites>symfony init-project aas

Create the project’s front-end

Adopt a street will have a public area front-and, to query the database and browse the data, an administrative area, a back end, with editing permissions on the database. These will be developed as two separate applications.

c:\websites\aas>symfony init-app frontend

now you just need toset up the virtual host and check it works.

3. Set up the Database

Go to the /config/ folder of the project and delete the schema.yml file (this is a dummy db definition file, so you don’t need it)

Create your database with your preferred method (I used DBDesigner4 to design it, thne exported it as a SQL script. Then, I created an empty db with DBTools and run the script as a query. This creates all the tables.)

Note: the db must be InnoDB, if you want your model to keep the references to foreign keys (see load of test data in the next tutorial)

Define the connection in config/databases.yml:

all:
propel:
class: sfPropelDatabase
param:
dsn: mysql://root:@localhost:3306/aas

Define the connection in config/propel

propel.project = aas
propel.database = mysql
propel.database.createUrl = mysql://root@localhost:3306/
propel.database.url = mysql://root@localhost:3306/aas

Create the schema from the database:

C:\websites\aas>symfony propel-build-schema xml

This creates an xml file, the schema representing your database, in config/

4. From the database to the objects

Symfony is object-oriented, thus it uses the generated schema to create for the tables and their relationships the corresponding classes:

C:\websites\askeet>symfony propel-build-model
If it’s all ok, you’ll get the php classes for all your tables in lib/model/

To test it all works, create a CRUD module for one of the tables (this is a simple management interface):

C:\websites\askeet>symfony propel-generate-crud frontend crud_module Class

In my case I defined it for the a table which contains a list of streets, called Street. So I had the crud_module as street and class Street

I checked it at: http://localhost:8020/aas/frontend_dev.php/street

The page shows the Street (for now) empty table and the link to create a new record.

Next
interact with the database

Sources

db-structure1

Problems found and solved

Creating the database model

If you get a mysql error, it might be your PHP installation does not have mysql installed as an extension. Thus, rerun the installation, choosing “Change…” and then select the mysql extention to be installed.

Generating the CRUD

When you generate the CRUD interface and you get an error “Cannot redeclare…”, it’s because some fields of that table have the same name of other fields in a related table, which means that the getter and setter methods will have the same declaration, thus the error. For example I had “maritalstatus” in Person table and Maritalstatus table. Thus, when I created the crud for Person I got the error.

Solution: change one of the clashing field names.





Set Dreamweaver as your Zope External Editor in 3 steps (Windows)

1 05 2008

1. Get the packages

Download the archived Zope External Editor ( ExternalEditor-0.9.3-src.tgz )

Download the Helper installer (zopeedit-win32-0.9.3.exe)

For more info and updated versions go to the Plope  website

2. Install

Stop Zope

Decompress the ZopeExternalEditor archive and place the resulting file in the Products folder of your Zope distribution.

Restart Zope

Install the helper : run the installer, this will do the job automatically for you, such as configuring the browser to open the editor when the “pencil” icon next to a file in the ZMI is clicked, and adding Zope External Editor to manage the .zope extension.

3. Set the editor

Go to your ZopeExternalEditor folder (i.e. c:/Program Files (x86)/ZopeExternalEditor) and edit the file ZopeEdit, which contains the configuration settings.

In my case, I want Dreamweaver to edit html files which are listed in the ZMI folders. So I look for the “text/html” content type and set Dreamweaver as the default editor like this:

[content-type:text/html]
extension=.html
editor=C:\Program Files (x86)\Macromedia\Dreamweaver MX 2004\Dreamweaver.exe

 If I want to use Dreamwever to open other extensions, I would look for them and follow the same procedure.

References

For more information on how to set up other programs (Word, etc.) as external editors for your zope files, look at:

http://learnplone.onenw.org/documentation/how-to/how-to-setup-an-external-editor





Plone 3.0 – customise the view for a given folder

30 04 2008

1. The concept

We want the main template to show a particular piece of code (i.e. a search box) when we are in a given folder, say “X”. To do so, we need to attach a property (which acts as a flag) to what we want to customise so that we can apply the customizations only for those items which have such property.

So, if we want to apply the customizations to all the items in a folder “X”, we need to apply the property to the folder. If they relate only to a number of pages, then we need to apply the property to each page.

Then in the main_template we will check for the existence of such property. If the property exists, it means we are inside our folder X and therefore we will show the custom code.

2. Implementation

In the ZMI, go to the Properties tab of the folder (X) you want to customize.

Add a property, i.e.”is_my_folder”, with boolean value 1.

Go to portal_skins and customize main_template.pt.

Edit the template (which now appears in the custom folder) so that it checks in which folder you are: if you are in X, show your customised code, otherwise the code will be ignored.

Insert elements in the body

after the starting tag with id=”content”, insert:

<tal:mycheck condition=”here/is_my_folder|nothing”>

<!–your code…–>

</tal>

Insert your CSS

<!–custom: if in the archive of bevans letters–>
<tal:bevanletter_searchform condition=”here/bevanletter_archive |nothing”>
<style type=”text/css”
media=”all”
tal:condition=”python:exists(‘portal/bevanletter.css’)”
tal:content=”structure string:<!– @import url($portal_url/bevanletter.css); –>”>
</style>
</tal:bevanletter_searchform>
<!–end custom–>





Building a plone product: from UML design to code-level customization

24 04 2008

This tutorial explains how to build a new plone product from its design to its lower level refinments.

The product here described is the ‘BevanLetter’, a content-type which models a letter. It was developed for a project regarding a batch of family letters exchanged during the Regency Period between the members of the The Bevan family.

It requires:

Plone 3.0

ArgoUML

ArchgenXML

Step 1. Design

In Argo UML , open a new project and save it as ‘BevanLetter.zargo’.

Rename the model as ‘BevanLetter’ (which will be the name of the product).

The product has two classes:

BevanLetter – which represents the letter itself with its metadata i.e. from,to…

BevanLetterImage – an image of the letter.

Add a class and call it ‘ BevanLetter ‘. Add the following attributes (id):

from

to

location

date

class no

other_info

keywords

content

For each of them we also defined tagged values (their properties). An example:

tagged values for from

  • widget:label = Your Name
    This is what the form field will be labeled. If it is not specified, the label will default to the name of that field.
  • widget:description = Enter your name, or ‘Anonymous’
    This is the description of the form field displayed on the page.
  • required = True
    Whether or not the field is required.
  • searchable = python: True
    This entry will ensure that the field can be searched o

Add another class, ‘ BevanLetterImage ‘. All it has to do, is to allow us upload images to the letter, thus we don’t need to design it from scratch. In fact, all the basic functionality we want is already in ATImage prototype which we find in Plone. So defining BevanLetterImage as a subclass of ATImage will do the job for now. Further customisations will be added later at code level.

Now we have to define the relationship between the two classes. A BevanLetter can contain more than one image. Thus we link the two classes with an Aggregation and specify the multiplicity 0…* for the image and 1 for the letter.

The diagram should be like that:

UML diagram for the BevanLetter product

Now, from our UML file, we create the product using archgenXML:

cd yourfolder/archgenxml/build/lib/archgenxml

python archgenxml.py /Users/Val/Archetypes/BevanLetter.zargo

This produces a python product called ‘BevanLetter’ in the ‘archgenxml/build/lib/archgenxml’ folder.

Once we have moved the folder ‘BevanLetter’ into the Products directory of our Plone distribution, we can start Zope and install the product in our site through the quickinstaller.

CHECKPOINT: in your menu tab, you should see the BevanLetter and BevanLetterImage content-types. Add a BevanLetter and then from its editing menu, add a BevanLetterImage. If you go to BevanLetter/view, you should see the BevanLetter document and the link to the image underneath.

Step 2. Modify the code

we can add a bit of customisation to the code.

Specifically, ATImage has an attribute ’sizes’, which is an array containing the various sizes for the image, which are then used to dynamically scale the image according to the size specified. You can see how it works by uploading an image into your plone site, and adding at the end of the imgurl the views called image_thumb,image_icon,image_large… for example:

myimage/image_thumb

We want to overwrite this property, defining our own array of sizes. Therefore, we open BevanLetterImage.py, and where it says ##code-section class-header #fill in your manual code here

we add:
schema['image'].sizes={‘32′:(32,32),’200′:(200,200),’768′:(768,768)}

so we have:

….
schema = BevanLetterImage_schema

##code-section class-header #fill in your manual code here
schema['image'].sizes={‘32′:(32,32),’200′:(200,200),’768′:(768,768)}
##/code-section class-header

# Methods

………

Restart Zope.

CHECKPOINT: Once Zope has restarted, upload a new BevanLetterImage and go to its url/image_200. You should see the image scaled to such dimension.





Using Google code as a repository

22 04 2008

I have just created two new projects on Google code and decided to host all their code there.

BevanLetter is my first newborn in the family of customised products for Plone 3. Initially written for a project funded by the Regency Town House, (an online archive of the Bevans’family correspondence during the Regency Period), this product allows to add a basic letter content type to your website in a few clicks.

Yapl, that’ s the project owned by the company I work for, is a PHP library which was developed here at ATL three years ago. Despite being quite hold, it suits the needs of those clients who still want to a PHP based website surprisingly well. It works with PHP5 onwards.

How to use Google code as a repository for your projects?

We need a subersion client to communicate with the Google repository.

I have installed Subversion for Mac OS X (which comes with server and client).

The package has not a graphical interface, so won’t appear in your Applications folder.

As specified by RubyRobot’s tutorial that is what you need to do from command-line:

Start by creating a new text file called ‘.bash_profile’, i.e. with the command line text editor pico:

$ pico .bash_profile

Add the following line to the text file:

export PATH=/usr/local/bin:$PATH

Now hit Control-X, then confirm saving the file with ‘y’, followed by return to Save it.

Connect to the svn repository:

go to your Google code page, in the “source” section. Copy and paste the line starting with svn checkout and give your username i.e.:

svn checkout https://bevanletter.googlecode.com/svn/trunk/ bevanletter --username myusername

You will be asked for the pwd, which Google will provide for you through a link under the command you have just copied.

If everything went ok, you will see a new folder called in my case ‘bevanletter’ in your home directory. This is your working copy. When you want to commit your changes back to the repository you do:

svn commit -m “any message you want to add goes here”

If you need to add new files do:

svn add filename.ext

svn commit -m “any message you want to add goes here”

To check any changes between the working copy and the trunk:

svn status

//this outputs what’s changed