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;
}




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–>