mercoledì, febbraio 13, 2013

Yii: click on a link, post your request

In a RESTful application written with Yii, I have an action that fixes some data in the db (like, for instance, the width and the height of an image), by checking some extra sources (the file). I needed to show a link that, when clicked, fired a POST request, and not a GET one. The obvious solution was to find out how to do it with some ajax call, but I wanted to see what Yii gave me.

I was happy to find out that Yii's CHtml::link() function already has everything needed. In particular, just adding 'submit'=>true in the $htmlOptions array, like in

<p><?php echo CHtml::link(
    'Fix picture',
    $url=CHtml::normalizeUrl(array('picture/fix','id'=>$model->id)),
    array(
      'submit' => $url,
      'title' => 'Check real size and type and fix data base entries',
      'csrf'=>true,
    )
  )
?></p>

is enough to produce a nice unobtrusive javascript code, like the following:
(in the body)

<p><a title="Check real size and type and fix data base entries" href="/yii/photoalbum/index.php?r=picture/fix&amp;id=6" id="yt0">Fix picture </a></p>

(in the jquery code produced, at the end of the page)

jQuery('body').on('click','#yt0',function(){jQuery.yii.submitForm(this,'/yii/photoalbum/index.php?r=picture/fix&id=6',{'YII_CSRF_TOKEN':'168375210910867a3dfab3db8750c4ad5f5aee2a'});return false;});

I needed to set csrf to true because my main config file has CRSF protection enabled.
I wanted the application to work even if javascript is disabled (Progressive Enhancement), so I prepared a fix.php view anyway, with a standard form containing only the submit button, like here:
<?php $form=$this->beginWidget('CActiveForm'); ?>
    <div class="row submit">
        <?php echo CHtml::submitButton('Fix'); ?>
    </div>
<?php $this->endWidget(); ?>
The form won't be shown if users have their javascript enabled.

domenica, febbraio 10, 2013

Rugby - ragby - regby

Perché in Italia la parola rugby (trascrizione fonetica: ‹ˈrʌɡbi›) viene pronunciata, il più delle volte, "regby" (‹ˈreɡbi›)? Da dove viene l'idea che la u debba essere trasformata in e?

Su forvo.com, si può vedere (e soprattutto sentire) che in tutti i paesi anglosassoni la pronuncia corretta è con una sorta di a, e quindi, vista l'origine e la diffusione dello sport, la parola dovrebbe essere pronunciata così.

Per quanto riguarda l'italiano, Dizionario italiano propone la pronuncia ‹'rugbi›, mentre il vocabolario della Treccani indica ‹rḁ′ġbi›.

Quindi, da dove viene la "e"?


(nell'immagine: particolare da Mêlée de Rugby, Bourgoin-Jallieu, Isère, Free On Line Photos)

mercoledì, febbraio 06, 2013

Screenshot di una pagina web

Se volete fare uno screenshot di una pagina web, e volete automatizzare il procedimento, magari sfruttando la riga di comando, potete usare PhantomJS, un browser comandabile via javascript, utilizzabile senza bisogno di interfaccia grafica (sotto Linux, senza che XWindow sia installato).

Fra l'altro, è possibile ottenere anche un file PDF.

Dopo aver installato PhantomJS, è sufficiente creare un file di testo con un contenuto simile al seguente:
// webpage_screenshot.js
var page = require('webpage').create();
var system = require("system");
page.open(system.args[1], function () {
    page.render(system.args[2]);
    phantom.exit();
});

e lanciare il comando
phantomjs webpage_screenshot.js http://www.example.com example.png
oppure
phantomjs webpage_screenshot.js http://www.example.com example.pdf
PhantomJS può essere usato anche per produrre documenti complessi basandosi su contenuti HTML5, come nell'esempio della fattura presentato su we-love-php.blogspot.nl.

lunedì, febbraio 04, 2013

Regr.lin() vs Linest()

Dovendo preparare un esercizio di statistica, sono andato a rivedermi la guida in linea della funzione REGR.LIN() del foglio elettronico. Personalmente uso OO Calc, ma, alla ricerca di qualche esempio interessante, mi sono fatto portare da Google alla pagina di descrizione della funzione di Microsoft Excel.

L'esempio numero 3 è chiaro nella prima parte, ma nel momento in cui si parla della stima della palazzina da acquistare, le cose si fanno un po' confuse, poiché viene detto

L'imprenditore può così calcolare il valore stimato di una palazzina nella stessa zona, costruita 25 anni prima, con una superficie di 279 metri quadri, tre uffici e due ingressi, utilizzando la seguente equazione:

y = 27,64*2500 + 12530*3 + 2553*2 - 234,24*25 + 52318 = € 158.261


Il valore di 279 metri quadri è incoerente con gli altri, e non ve ne è traccia nell'equazione. La tabella sotto poi riporta il valore 1 anziché 2500.

La stessa guida in inglese permette di chiarire un po' le cose, visto che i valori sono corretti, si fa riferimento a 2500 piedi quadrati, e tutti i conti tornano.

Rimane il mistero della provenienza del valore 279 nel testo in italiano. Una superficie di 2500 piedi quadrati corrisponde a circa 232 metri quadrati, non a 279.

sabato, febbraio 02, 2013

Full example of Ajax-powered text field with Yii

Some days ago I needed to transform a simple text field into an AJAX-powered widget, so that values could be obtained from asyncronous queries to the webserver.

I used a widget from the framework itself, zii.widgets.jui.CJuiAutoComplete, but I had to figure out how to actually use it. I will recap here what I did.

Imagine you have a "tag" field, used to add tags to something. In your view, replace your ordinary widget:

<?php echo $form->textField($tagform,'tag'); ?>

with the new one, AJAX-powered:
<?php $this->widget('zii.widgets.jui.CJuiAutoComplete', array(
  'id'=>'tag',
  'name'=>'PictureAddTagForm[tag]',
  'source'=>$this->createUrl('tag/suggest'),
   'options'=>array(
    'delay'=>500,
    'minLength'=>2,
    ),
  'htmlOptions'=>array(
     'size'=>'20'
     ),
  ))
?>

The "options" array allows you to define a delay after which the AJAX call will be executed, and a minimal number of characters to wait.
Since the source for data is defined as 'tag/suggest', you'll need to define an action in the tag controller:


/**
 * Serves a list of suggestions matching the term $term, in form of
 * a json-encoded object.
 * @param string $term the string to match
 */
public function actionSuggest($term='')
{
  $this->serveJson(Tag::model()->findMatches($term));
}

This action will call a custom function that sends the HTTP header and the JSON-encoded list of tags. The parameter's name, that we'll refer to as $term, is hardcoded in the underlying jqueryui autocomplete widget.

The custom function must be written in the controller or, even better, in the Controller class, since this way it is shared with the other controllers:
// in protected/components/Controller.php
/**
 * Serves an object as a json-encoded string via HTTP.
 * @param string $object the object to send
 */  
public function serveJson($object)
{
  $this->serveContent('application/json', CJSON::encode($object), false);
}

/**
 * Serves a content via HTTP.
 * @param string $type the Internet Media Type (MIME) of the content
 * @param string $content the content to send
 */  
public function serveContent($type, $content)
{
  $this->_serve($type, $content, false);
}

/**
 * Serves a file via HTTP.
 * @param string $type the Internet Media Type (MIME) of the file
 * @param string $file the file to send
 */  
public function serveFile($type, $file)
{
  $this->_serve($type, $file, true);
}

/**
 * Serves something via HTTP.
 * @param string $type the Internet Media Type (MIME) of the content
 * @param string $content the content to send
 * @param boolean $is_file whether the content is a file
 */  
private function _serve($type, $content, $is_file=false)
{
  header("Content-Type: " . $type);
  if ($is_file)
  {
    readfile($content);
  }
  else
  {
    echo $content;
  }
  Yii::app()->end();
}
Now, it's the model's job to get the data that match the term.

/**
 * Retrieves tags that match a term.
 * @param string $term a term to use for matching tags.
 * @return array tags titles that match the term.
 */
public function findMatches($term='')
{
  if(''==$term)
  {
   return array();
  }
  $q = new CDbCriteria();
  $q->addSearchCondition('title', $term);
  $q->select=array('title');
  $tags = self::model()->findAll($q);

  $results=array();
  foreach($tags as $tag)
  {
    $results[]=$tag->title;
  }
  return $results;
}



This function will retrieve from the table all the rows that match the criteria, and return an array with the values actually needed.

venerdì, febbraio 01, 2013

Weird screen resolutions from Firefox

When you collect information about users' screen resolution, it happens you get weird numbers, like 797x448, or 683x348. Just take a look at www.screenresolutions.org to have a list of this kind of weird resolutions, each with a little percentage of users. Where do these numbers come from?

The answer is easy, once you know that screen resolution is detected with a little javascript code accessing the values of screen.width and screen.height, respectively (like in the following example), and that Firefox computes these values taking the zoom level into account.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
 <title>Screen resolution</title>
 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>

<body>
<h1>Screen resolution</h1>

<p>Width x Height: <span id="resolution"></span></p>

<script type="text/javascript">
  window.onload = function() {
    document.getElementById('resolution').innerHTML = screen.width + 'x' + screen.height;
};
</script>
</body>
</html>
If you try this page with Firefox, you can see that changing the resolution (ctrl-plus and ctrl-minus) does affect the numbers you get (press ctrl-0 to reset standard zoom level).

Unfortunately, it seems that there are not valid generic methods for getting the zoom level, at least according to this page on StackOverflow.

lunedì, gennaio 14, 2013

I18n with Yii

Curious about Yii, I wanted to test its I18n features.

The documentation on the web site is a good starting point, but some hints are missing, and I had to google a bit to find out.

You can see which language is currently set by accessing the value of Yii::app()->language.

If needed, you can also change the value, for instance to view a string in different languages. For instance, this code in a view:

<?php foreach(array('en', 'it', 'pl') as $lang): ?>
  <?php Yii::app()->language=$lang ? ?>
  <p><?php echo $lang ? ?>:
  <?php echo Yii::t('App', 'Internationalization experiments') ? ?></p ?>
<?php endforeach ?>

would produce something like:

en: Internationalization experiments
it: Esperimenti con l'internazionalizzazione
pl: Eksperymenty z internacjonalizacją
Yii has a good and "natural" support for language plural rules. This is something everybody programming should take care of. For instance, see how easy it is to manage different plural forms for Polish:

<?php Yii::app()->language='pl' ?>  
 <ul>  
 <?php for($i=0; $i<=30; $i++): ?>  
 <li><?php echo Yii::t('App', '{n} file|{n} files', $i); ?></li>  
 <?php endfor ?>  
 </ul>  

To write a translation string like jeden plik|{n} pliki|{n} plików is enough to obtain

  • 0 plików
  • jeden plik
  • 2 pliki
  • 3 pliki
  • 4 pliki
  • 5 plików
  • 6 plików
  • ...
  • 10 plików
  • 11 plików
  • 12 plików
  • ...
  • 21 plików
  • 22 pliki
  • 23 pliki
  • 24 pliki
  • 25 plików
  • 26 plików
  • ...
Last but not least, if you want to access preferences set in the user's browser, you can follow the excellent example in Rethrown Exception.

sabato, gennaio 05, 2013

Rimuovere i byte BOM dal codice sorgente

È un classico problema che ci si può trovare ad affrontare maneggiando codice sorgente di applicazioni, costituito per lo più da semplici file di testo. Si tratta dei byte del cosiddetto BOM, aggiunti a volte dagli editor all'inizio dei file Unicode per contraddistinguere il tipo di codifica utilizzata (UTF-32, UTF-16, UTF-8).

Un editor esadecimale, o anche più semplicemente il programma od, sono in grado di mostrarci i byte inseriti. Prendiamo ad esempio un file che inizia così:

  /* questo è il punto di avvio dell'applicazione
   */
  require_once('config.php');  // inclusione del file di configurazione   require_once('functions/generic_functions.php');  // inclusione...
Se con od diamo un'occhiata, ci rendiamo conto dei caratteri inseriti nelle prime posizioni:

$ od -a index.php | head
0000000   o   ;   ?   <   ?   p   h   p  cr  nl  cr  nl  sp  sp   /   *
0000020  sp   q   u   e   s   t   o  sp   C   (  sp   i   l  sp   p   u
0000040   n   t   o  sp   d   i  sp   a   v   v   i   o  sp   d   e   l
0000060   l   '   a   p   p   l   i   c   a   z   i   o   n   e  cr  nl
0000100  sp  sp  sp   *   /  cr  nl  cr  nl  sp  sp   r   e   q   u   i
0000120   r   e   _   o   n   c   e   (   '   c   o   n   f   i   g   .
0000140   p   h   p   '   )   ;  sp  sp   /   /  sp   i   n   c   l   u
0000160   s   i   o   n   e  sp   d   e   l  sp   f   i   l   e  sp   d
0000200   i  sp   c   o   n   f   i   g   u   r   a   z   i   o   n   e
0000220  cr  nl  sp  sp   r   e   q   u   i   r   e   _   o   n   c   e

Il file dovrebbe invece iniziare direttamente con il simbolo <, come qui:
$ od -a index.php | head
0000000   <   ?   p   h   p  cr  nl  cr  nl  sp  sp   /   *  sp   q   u
0000020   e   s   t   o  sp   C   (  sp   i   l  sp   p   u   n   t   o
0000040  sp   d   i  sp   a   v   v   i   o  sp   d   e   l   l   '   a
0000060   p   p   l   i   c   a   z   i   o   n   e  cr  nl  sp  sp  sp
0000100   *   /  cr  nl  cr  nl  sp  sp   r   e   q   u   i   r   e   _
0000120   o   n   c   e   (   '   c   o   n   f   i   g   .   p   h   p
0000140   '   )   ;  sp  sp   /   /  sp   i   n   c   l   u   s   i   o
0000160   n   e  sp   d   e   l  sp   f   i   l   e  sp   d   i  sp   c
0000200   o   n   f   i   g   u   r   a   z   i   o   n   e  cr  nl  sp
0000220  sp   r   e   q   u   i   r   e   _   o   n   c   e   (   '   f
Fortunatamente, possiamo facilmente cercare i file "incriminati" per sistemarli, se ne abbiamo bisogno.

Una pagina di stackoverflow abbonda di suggerimenti al riguardo: elegant-way-to-search-for-utf-8-files-with-bom.

Su LinuxAsk si trova poi il suggerimento più carino su come ottenere tutti i byte a partire dal quarto:

tail --bytes=+4 text.txt



giovedì, dicembre 22, 2011

Seconda o terza persona per la descrizione delle funzioni?


Quando si scrive la descrizione di una funzione, a fini di documentazione, si deve indicare cosa essa fa e, in genere, che valore restituisce.

Ad esempio:
 /**
   * Returns the number of pixels in the picture.
      * @return integer the number of pixels in the picture
   */
   public function getPixels()
   {
     return $this->width * $this->height;
   }

La prima riga deve essere in seconda persona (imperativo) o in terza (indicativo presente)? La domanda sorge spontanea, visto che ci sono diverse abitudini, e diverse raccomandazioni. Vedi ad esempiohttp://drupal.org/node/487802#comment-1720946dove si trova questo "studio":

I like As If's explanation in #27. This got me thinking... Coding standards are always so personal -- there are choices in the Drupal standards (and standards at companies I've worked for in the past) that I don't like, but that I recognize as valid choices that simply differ from the style choices I prefer.
So, different people can have different valid opinions on whether function 1-line headers should be 2nd or 3rd person, and today I specifically looked, via web search, for doc standards that mentioned second person, and found one: Python http://www.python.org/dev/peps/pep-0257/ I found many more projects that prescribe 3rd person for doc header one-liners, besides the two I mentioned in my original issue report, but there is at least one thoughtful example of 2nd person being the standard.
I also thought it would be interesting to see what existing doc sets are actually using (independent of whether they have standards) -- both open-source and proprietary. Summary: They are all over the map. Here are some examples:
* The Java API uses 3rd person fairly consistently, although if you poke around, you can find 2nd-person instances: http://java.sun.com/javase/6/docs/api/
* Perl doc is all over the map, and some function doc lines don't even start with a verb (they don't have standards that I could find, and it shows): http://perldoc.perl.org/index-functions.html
* PHP doc uses 3rd person for some sets of functions http://us.php.net/manual/en/ref.filesystem.php, and 2nd person for others http://us.php.net/manual/en/book.strings.php (although there are some 3rd-perso
Opzionin instances in this list, and their standard is 3rd-person)
* MySQL uses 2nd person: http://dev.mysql.com/doc/refman/5.4/en/string-functions.html
* Python is a mix: http://docs.python.org/3.0/library/math.html
* JQuery is a mix: http://docs.jquery.com/Traversing
* Microsoft's MFC classes seem to use mostly 3rd person: http://msdn.microsoft.com/en-us/library/3azzex5f(VS.80).aspx [note to self: don't use parens () in a URL, as Drupal's URL translator doesn't seem to recognize it when pasted in from the URL bar in my browser]
* Google Maps API uses 3rd person: http://code.google.com/apis/maps/documentation/reference.html
* Amazon EC2 API uses 3rd person: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/


Il problema sorge quando si usano diversi stili nello stesso contesto. Ad esempio, in symfony ci si accorge che l'imperativo e la terza persona singolare sono mescolati anche nella descrizione dei task standard:

plugin
  :add-channel    Add a new PEAR channel
  :install             Installs a plugin
propel
  :build               Generate code based on your schema
  :build-all          Generates Propel model and form classes, SQL and initializes the database

ma la terza persona è comunque prevalente. E a me piace di più (cosa fa la funzione? calcola questo e quello, restituisce il tal valore...).

sabato, febbraio 26, 2011

Il web 2.0 in poche parole

Questo video è molto suggestivo, e illustra un sacco di concetti importanti in meno di cinque minuti: