jueves, 10 de marzo de 2011

Making IIS 7 serve MrSID Files

Until IIS 6 in order to serve MrSID files all you had to do was assign the proper NTFS permissions to the folder where the files were located and create a virtual directory in IIS. If you only do that and try to access a URL to an MrSID file on II 7 you´ll get a “404 - File or directory not found.” . The error message is confusing since the directory does exist. Finally figured there where restrictions with the MIME type, it seems IIS 7 only serves MIME you specifically declared.

To declare a new MIME type for MrSID you have to add an additional step.

1. Open the IIS Manager

2. Click on the virtual directory or directory

3. Click on MIME Types

4. Click on Add…

5. Fill the File name extension as .sid and the MIME type as application/octet-stream


6. Click Ok.

Now you should be able to access your MrSID via an URL.

martes, 8 de marzo de 2011

Using Pagination with Symfony and Doctrine 2

When I blogged Using Pagination with Symfony and Doctrine I had being trying to understand the inner working of the Symfony's Admin Generator. At the time it seem a good solution. But, when I started implementing the idea, I realized that I had to repeat very similar code on each of the of the actions of the modules I was working.
So I decided to try a different aproach. I put all de code I was using in the actions in a utility class, created a widget to render the header and added a partial to render how many items in the table and the total number of pages. The final goal was to create my first Symfony plugin, but I'm not quite there yet, so I decided to blog first this improvement.
The Pagination Control
Since it's the same control I proposed in my previous blog Using Pagination with Symfony and Doctrine I will not repeat the code. You still need to get the images for buttons and place them in your web\images folder.
The Pagination Info Control
Create a file named /myproject/apps/myapplication/templates/_pagination_info.php and copy the following code on it. Then save it.
<div class="pagination_info">
<?php echo format_number_choice('[0] no result|[1] 1 result|(1,+Inf] %1% results',
array('%1%' => $pager->getNbResults()), $pager->getNbResults(), 'sf_admin') ?>
<?php if ($pager->haveToPaginate()): ?>
<?php echo __('(page %%page%%/%%nb_pages%%)', array('%%page%%' => $pager->getPage(),
'%%nb_pages%%' => $pager->getLastPage()), 'sf_admin') ?>
<?php endif; ?>
</div>
Creating the Paging Utility
Create a file named /myproject/lib/model/PagingUtility.class.php and copy the following code on it. Then save it.
This class will take care of all the code previously had to copy into the actions.
class PagingUtility {
private $modelname;
private $sortkey;
private $user;
private $modulename;

public function __construct($modelname, $user) {
$this->modelname=$modelname;
$this->user =$user;
$this->sortkey =strtolower($this->modelname).".sort";
$this->modulename = strtolower($this->modelname) . "_module";
}

public function buildPager($request, $items_x_page,$query=NULL){
if ($request->getParameter('sort') && $this->isValidSortColumn(
$request->getParameter('sort'))) {
$this->setSort(array($request->getParameter('sort'),
$request->getParameter('sort_type')));
}

// $items_x_page = sfConfig::get('app_max_items_x_page');
$pager = new sfDoctrinePager($this->modelname, $items_x_page);
//$device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
$query = is_null($query) ? $this->buildQuery() : $query;
$pager->setQuery($query);
$pager->setPage($request->getParameter('page', 1));
$pager->init();
return $pager;
// $this->sort = $this->getSort();
}
protected function buildQuery() {
$et = Doctrine_Core::getTable($this->modelname);

$query = $et->createQuery();

$this->addSortQuery($query);

return $query;
}

protected function addSortQuery($query) {
if (array(null, null) == ($sort = $this->getSort())) {
return;
}

if (!in_array(strtolower($sort[1]), array('asc', 'desc'))) {
$sort[1] = 'asc';
}
$query->addOrderBy($sort[0] . ' ' . $sort[1]);
}

public function getSort() {
if (null !== $sort = $this->user->getAttribute($this->sortkey,
null, $this->modulename)) {
return $sort;
}

$this->setSort(array(null, null));

return $this->user->getAttribute($this->sortkey,
null, $this->modulename);
}

protected function setSort(array $sort) {
if (null !== $sort[0] && null === $sort[1]) {
$sort[1] = 'asc';
}
$this->user->setAttribute($this->sortkey, $sort, $this->modulename);
}

protected function isValidSortColumn($column) {
return Doctrine::getTable($this->modelname)->hasColumn($column);
}
}
Creating the Sortable Header Widget
I'm not sure if creating a widget for this purpose would be the best approach, but I wanted to make my own widget a see how it worked. The code is an adaption of the code from a previous blog entry Sorting Lists in Symfony.
Create a file named /myproject/lib/widget/maSortableHeader.php and copy the following code on it. Then save it.
class maSortableHeader extends sfWidgetForm {

protected function configure($options = array(), $attributes = array()) {
$this->addOption('fieldname');
$this->addOption('fieldlabel');
$this->addOption('routename');
$this->addOption('sort');
}

public function render($name, $value = null, $attributes = array(), $errors = array()) {
$sort = $this->getOption('sort');

if ($this->getOption('fieldname') == $sort[0]) {
$config = array('query_string' => 'sort=' . $this->getOption('fieldname') . '&sort_type=' .
($sort[1] == 'asc' ? 'desc' : 'asc'));
$uri = link_to($this->getOption('fieldlabel'), $this->getOption('routename'),
$config);
$uri .= image_tag('/images/' .
$sort[1] . '.png', array('alt' => $sort[1],
'title' => $sort[1]));
} else {
$uri = link_to($this->getOption('fieldlabel'), $this->getOption('routename'),
array('query_string' => 'sort=' . $this->getOption('fieldname') . '&sort_type=asc'));
}

return $uri;
//optional - for easy css styling
}

}
Editing the Action


On our action we're going to implement our PagingUtiliy and add widets for two columns (Code and Name). Our executeIndex function looks like this:
public function executeIndex(sfWebRequest $request)
{
$pu = new PagingUtility("Category", $this->getUser());
$items_x_page = sfConfig::get('app_max_items_x_page');
$this->pager = $pu->buildPager($request, $items_x_page);

$this->sort = $pu->getSort();

//Adding SortableHeaders
$this->form = new sfForm();
$this->form->setWidget('code_sorted',
new maSortableHeader(array('fieldname'=>'code',
'fieldlabel'=>'Code', 'routename'=>'@category','sort'=>$pu->getSort())));
$this->form->setWidget('name_sorted',
new maSortableHeader(array('fieldname'=>'Name',
'fieldlabel'=>'Name', 'routename'=>'@category','sort'=>$pu->getSort())));

}
You still need to get the asc.png and desc.png into your web/images folder for the widget to show icon besides the header.

Editing the indexSuccess.php
What we need to do now is show the Sortable Headers, change the default loop to use the pager, add the pagination control and the pagination info partials.
<thead>
<tr>
<th><?php echo $form['code_sorted']; ?></th>
<th><?php echo $form['name_sorted']; ?></th>
<th>Description</th>
<th>Created at</th>
<th>Updated at</th>
<th>Created by</th>
<th>Updated by</th>
</tr>
</thead>
<tbody>
<?php foreach ($pager->getResults() as $i => $category): ?>
<tr>
<td><a href="<?php echo url_for('category/edit?id='.$category->getId()) ?>"><?php echo $category->getCode() ?></a></td>
<td><?php echo $category->getName() ?></td>
<td><?php echo $category->getDescription() ?></td>
<td><?php echo $category->getCreatedAt() ?></td>
<td><?php echo $category->getUpdatedAt() ?></td>
<td><?php echo $category->getCreator() ?></td>
<td><?php echo $category->getUpdator() ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<td colspan="7">
<?php include_partial('global/pagination_info', array('pager' => $pager)) ?>
<?php include_partial('global/pagination_control', array('pager' => $pager, 'module' => 'category')) ?>
</td>
</tr>

Editing the Routing.yml
This is the step I always forget. Mainly because I don't quite undertand what it does. I will look into it an post my findings. For now it will not work unless you add the follwing code to your routing.yml.
category:
class: sfDoctrineRouteCollection
options:
model: Category
module: category
prefix_path: /category
column: id
with_wildcard_routes: true

jueves, 3 de marzo de 2011

QR Codes a Cool Way to Share Data

I recently discovered QR Codes. QR Codes (QR is short for Quick Read) were created by Denso-Wave a subsidiary of Toyota. They are a type of barcode that can be read by both barcode readers and smartphones with cameras. This is my blogs url encoded in QR Code:

These codes are being used extensively in advertising and even in games. But the thing I like the most is there are several QR code generator online.

The jewel of the crown is that a QR Code are set down in the ISO-18004 standard so they are the same all over the world.

QR Code Online Generators

There are several online QR Code Generators. The followings are the ones I took a peek at:

  1. I found Kaywa QR Code to be very good.

  2. QR Code Generator from the ZXing Project: Is based on the Googles Chart API and has more types of contents for example has a WIFI Network format. It also has a geo location format which is great.

    This the URL for the Biodiversity Museum in Panama.
  3. QRStuff : has more data types than XZing Project but my QR reader doesn't work with all of them. I create a QR a code for an email message and even though the reader read it, I got an error when I tried to send the email. I like this QR generator site because it has a lot of information on QR codes.
Now if you're sort of a nerd (like me) you are probable thinking how to embed the online creators into your own application. The coolest thing about QR codes it's that they are supported the Google Chart API. For example to create a QR code to encode the phrase Hola Panama (Hello Panama) all you have to do is build an URL like this one:

http://chart.apis.google.com/chart?cht=qr&chs=350x350&chl=Hola+Panama

You'll get a QR code like this


QR Code Reader

Since I have Blackberry I just focused on readers for BBs. But you can find readers for iPhone, Android and certain versions of Nokias OSs. I found a list of barcode readers can be found at http://www.mobile-barcodes.com/qr-code-software/blackberry/.
I chose i-nigma only because it supported many other phones aside from Blackberry. Once installed it has been working great.

miércoles, 19 de enero de 2011

Working with Microsoft SQL Server 2008 and Symfony without sa Login

On a previous post Running Symfony 1.4 with MS SQL Server 2008 I described how to connect to MS SQL Server 2008 using Symfony 1.4.8.
I did all my tests using the sa (superadmin) login and SQL Server Express 2008 R2. I figured once I had my model stable enough all I would have to do would be create a new login and work with it. As usual I was sorely mistaken. The problem is that to operate on a database you need to create a user (in the database) to give it permissions to do things like create table or create data. When you drop the database (for example while running a symfony doctrine:build --all) you eliminate the user, and the login resets to the dbo user. After the database is recreated there is no user associate to the current login and you can’t do anything.
Working with sa on a development environment is not a very good idea, but using it in a production environment is even worst. I found a work around not to use sa as login, not a very elegant one, but it works. The permissions and roles I’am giving the login and user are probably overkill but I need to make this work sooner than later so if you´re going to a production environment I would suggest to take a closer look a the permissions.

Creating the Database


Open Microsoft SQL Server Management Studio and connect to the Instance using sa or other administrative login.
Right click on Databases and select New Database... Create a new database called sfex_db and choose as owner a default for now.

Creating an Administrative Login

Create a Login. Right click on MYMachine\SQLEXPRESS\Security\Logins select New Login. Use as Login name sfex_salogin, assign a password and select as Default database sfex_db.

On Server Roles add the following:



Select the User Mappings page select sfex_db change the user to sfex_sauser and leave the default schema blank. I tried creating a schema an assigning it as the default schema to the user but it will still create the tables in the dbo schema. I´m still trying to figure out how to change the default schema to something else than dbo.
Assign the role membership as shown:

In Your Symfony Project

After you have created the database, the login and the user you will be able to use the database with Symfony.
symfony doctrine:build --all-classes --model --forms --filters --sql
symfony doctrine:insert-sql
symfony doctrine:data-load

But what happens if you make changes to your schema. You cannot run the insert-sql task because the tables already exist. You´ll have to use the create-model-tables task. The inconvenience with this is that you have to explicitly tell the task what model tables to recreate; this is kind of a hassle. I’m currently working in a way to create the equivalent task to doctrine: build --all for MS SQL Server, as soon as I´ve a working task I´ll post it.
symfony doctrine:build --all-classes --model --forms --filters --sql
symfony doctrine:create-model-tables User Phone
symfony doctrine:data-load

jueves, 16 de diciembre de 2010

Running Symfony 1.4 with MS SQL Server 2008

I have used Symfony with Oracle and MySQL and was very happy that I could switch seamlessly from one RBDMS to another. When I had to switch to SQL Server 2008 I thought it would be pretty much the same thing. Well… I was wrong.
I found very little on Symfony and SQL Server on the world library (Google). I got bits and pieces that gave me an idea of what was wrong.

Issues

1. It seems Doctrine 1.2.x doesn´t support the new Microsoft Driver and PHP 5.3 doesn´t support the mssql driver any more. You have two choices connecting via ODBC to SQL Server and keep PHP 5.3 or switch to PHP 5.2 and use mssql driver.
2. I haven´t been able to run the doctrine:build task with existing tables.
3. Cannot use comment on the schema.yml
4. Problems using UTF-8 encoding on data. The data loaded from the fixtures.yml is encoded incorrectly when loaded to the database. This is because the fixtures.yml is encoded in UTF-8 and the database is expecting cp1252. The work around is switch the fixtures.yml encoding to cp1252.

The sfexample Project

I created a Symfony project to test the behavior of MS SQL Server 2008 with the ODBC driver and the mssql driver.
I´m using MS SQL Server 2008 Express R2 and WAMP 2.0i
I created a simple clients table. My schema.yml:
client:
tableName: clients
columns:
id:
type: integer(4)
primary: true
autoincrement: true
firstname:
type: string(30)
lastname:
type: string(40)
country:
type: string(40)

My fixtures are:
client:
vj:
firstname: Víctor
lastname: Asunción
country: Panamá
bc:
firstname: Benjamón
lastname: Coclé
country: Panamá

Symnfony 1.4, PHP 5.3.0 and ODBC

To use the PDO ODBC driver you need to:
1. Uncomment the extension on your php.ini. If you´re using WAMP be sure to edit the php.ini that Apache uses and the one that is in your php home directory.
extension=php_pdo_odbc.dll
2. If you´re using Netbeans make sure you´re using PHP 5.3.0. Go to Tool\Options, click on the PHP icon and verify in that PHP 5 Interpreter is pointing to the PHP 5.3 executable (in my case C:\wamp\bin\php\php5.3.0\php.exe)
3. Edit the Path environment variable to point to the PHP 5.3.0 home directory (in my case C:\wamp\bin\php\php5.3.0).
Change the databases.yml to look like this:
all:
doctrine:
class: sfDoctrineDatabase
param:
dsn: odbc:DRIVER={SQL Server};Server=MYMACHINE\SQLEXPRESS;Database=sfexample;dbname=sfexample;
username: sa
password: mypassword

Run
symfony doctrine:build --all --no-confirmation --and-load
The first time I ran it works. It creates the tables and loads the data. If I run it a second time I get this error:
SQLSTATE[25000]: Invalid transaction state: 3902 [Microsoft][ODBC SQL Server Driver][SQL Server]The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION. (SQLExecDirect[3902] at ext\pdo_odbc\odbc_driver.c:247)
The work around to it is manually delete the table. This is an inconvenience to say the least. If you have many tables with relations it can become a hassle.
I you take a look at my fixtures you´ll notice I use accented vowels. Since SQL Server doesn´t understand UTF-8 when I run a select from the clients table I get this



The collation for my database is Modern_Spanish_CI_AS. The work around I found was saving the fixtures.yml in cp1252 encoding. The problem with this, is that Netbeans assigns a single encoding for the whole project. So I decided to keep all my project in UTF-8 and edit the fixtures.yml externally (outside Netbeans).
I created a doctrine module for clients.
symfony doctrine:generate-module frontend clients Client

When I try t save a record I get a warning like this (with the whole call stack):
Deprecated: Function spliti() is deprecated in C:\symfony-1.4.8\lib\plugins\sfDoctrinePlugin\lib\vendor\doctrine\Doctrine\Connection\Mssql.php on line 192


It seems the spliti() function has been deprecated in PHP 5.3 and PHP decided to “share” it with us. To solve this go to the settings.yml and change the error reporting to:
.settings:
error_reporting: <?php echo E_ALL & ~E_DEPRECATED ."\n" ?>

To avoid the encoding problem from the client module change the character set to cp1252 on the setting.yml
all:
.settings:
charset: cp1252

You have to clear the cache to make the charset take.

Symnfony 1.4, PHP 5.2.11 and mssql


To use the PDO mssql driver you need to:
1. Uncomment the mssql and pdp mssql extensions on your php.ini. If you´re using WAMP be sure to edit the php.ini that Apache uses and the one that is in your php home directory.
extension=php_mssql.dll
extension=php_pdo_mssql.dll
2. If you´re using Netbeans make sure you´re using PHP 5.2.11. Go to Tool\Options, click on the PHP icon and verify in that PHP 5 Interpreter is pointing to the PHP 5.3 executable (in my case C:\wamp\bin\php\php5.2.11\php.exe)
3. Edit the Path environment variable to point to the PHP 5.2.11 home directory (in my case C:\wamp\bin\php\php5.2.11).
Change the databases.yml to look like this:
all:
doctrine:
class: sfDoctrineDatabase
param:
dsn: mssql:host=MYMACHINE\SQLEXPRESS;dbname=sfexample
username: sa
password: mypassword

Run
symfony doctrine:build --all --no-confirmation --and-load

The mssql driver doesn´t seem to have the problem with rerunning the task that the odbc driver has.
The client module was already created.
There was no problem with the spliti function since it´s still supported in PHP 5.2.x
To avoid the encoding problem from the client module change the character set to cp1252 on the setting.yml
all:
.settings:
charset: cp1252

You have to clear the cache to make the charset take.

References


http://forums.asp.net/t/1200021.aspx

martes, 14 de diciembre de 2010

Installing Microsoft SQL Server 2008 Drivers for PHP

Installing the Microsoft Drivers for SQL Server 2008 is a not as simple as one would think. One of the things that had me confused was the fact that I had previously made a conection to SQL Server 2005 with no problem. Now, using SQL Server 2008 (Express) nothing worked.

After some research I realized the problem was tha in my previous connections I was using PHP 5.2.x which used the mssql driver. For some reason the mssql server is no longer supported for PHP 5.3 and we are supposed to use the Microsoft Driver.

Installation

Download the drivers from http://www.microsoft.com/downloads/en/details.aspx?FamilyID=80e44913-24b4-4113-8807-caae6cf2ca05&displaylang=en .

When you install you´ll need to choose which dlls to use. Use the following table to decide:

PHP Version

Web Server

Dlls

5.2

IIS

php_pdo_sqlsrv_52_nts_vc6.dll

php_sqlsrv_52_nts_vc6.dll

5.2

Apache

php_pdo_sqlsrv_52_ts_vc6.dll

php_sqlsrv_52_ts_vc6.dll

5.3

IIS with FastCGI

php_pdo_sqlsrv_53_nts_vc9.dll

php_sqlsrv_53_nts_vc9.dll

5.3

Apache as mod_php

php_pdo_sqlsrv_53_ts_vc6.dll

php_sqlsrv_53_ts_vc6.dll

5.3

Apache as FastCGI

php_pdo_sqlsrv_53_nts_vc6.dll

php_sqlsrv_53_nts_vc6.dll

Double click on SQLSRV20.EXE

Click yes.

Select your output directory and click Ok.


Click Ok.

Copy the file c:\temp\php_pdo_sqlsrv_53_ts_vc6.dll and c:\temp\php_sqlsrv_53_ts_vc6.dll to your PHP_HOME\ext folder (since I´m using WAMP my path is C:\wamp\bin\php\php5.3.0\ext)

Edit your php.ini (mine is at C:\wamp\bin\php\php5.3.0\) adding the following lines

extension=php_sqlsrv_53_ts_vc6.dll

extension=php_pdo_sqlsrv_53_ts_vc6.dll

If you´re using WAMP you have to edit the php.ini that Apache uses. Double click on php.ini add the extensions and restart Apache.


Testing Installation

Open command prompt a run

php –v

If you get an error like this one:





You´re using the wrong dlls for your php installation. Verify you´re using php_pdo_sqlsrv_53_ts_vc6.dll and php_sqlsrv_53_ts_vc6.dll

Create a directory c:\temp\sqlservertest.

In Apache create an alias for this directory with the name sqlservertest.

Create a file in c:\temp\sqlservertest with the name phpinfo.php and the following content:

<?php phpinfo(); ?>

We had already created a Table on SQL Server called users to test the driver.

Create and index.php file write the following code.


<?php
/*
* Specify the server and connection string attributes.
*/
$serverName = "MYMACHINE\SQLEXPRESS";
$uid = "username";
$pwd = "password";
$database = "sqlservertest";
try {
$conn = new PDO( "sqlsrv:server=$serverName;database=$database", $uid, $pwd);
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$conn->setAttribute(PDO::SQLSRV_ATTR_DIRECT_QUERY , true);

echo "Connected to SQL Server<br /><br />\n";

$sql = "select * from myschema.users";
$results = $conn->query( $sql );
outputRows($results);

// Free statement and connection resources.
$stmt = null;
$conn = null;
} catch( PDOException $e ) {
echo "<h1>Error connecting to SQL Server</h1><pre>";
echo $e->getMEssage();
echo "</pre>";
exit();

}
function outputRows($results)
{
echo "<table border='1'>\n";
while ( $row = $results->fetch( PDO::FETCH_ASSOC ) ){
echo "<tr><td>{$row['id']}</td><td>{$row['firstname']}</td><td>{$row['lastname']}</td>\n";
}
echo "</table>\n";
return;
}


References

http://www.php.net/manual/en/ref.pdo-dblib.php

http://www.ditii.com/2010/11/16/sql-pdo-and-microsoft-sql-server-whitepaper-php-drivers-for-sql-server/

lunes, 6 de diciembre de 2010

Writing to a Properties File with Apache ANT

I needed to create properties file every time I made a migration in order to know who, when and from where the migration was made.
The easiest way to do this is using Apache ANT.

To gain access to the environment variables you must declare the environment property. This can be done under the project tag.
<property environment="env" />

Inside your target use the following code:

When you run the make-properties target if will generate a file named build.properties with something like this:
<target name="make-properties " >
<propertyfile file="build.properties" comment="A comment to appear in the properties file.">
<entry key="program.BUILDDATE" type="date" value="now" pattern="yyyyMMdd-HHmmss" />
<entry key="program.BUILDHOST" value="${env.COMPUTERNAME}" />
<entry key="program.BUILDUSER" value="${user.name}" />
</propertyfile>
</target>

When you run the make-properties target if will generate a file named build.properties with something like this:
# A comment to appear  in the properties file.
#Wed, 01 Dec 2010 11:45:24 -0500
program.BUILDDATE=20101201-114524
program.BUILDHOST=GISHOST
program.BUILDUSER=LBerrocal