Tuesday, July 21, 2009

Error: There was a problem reading this document (109)

Usually browsers armed with Acrobat Reader plugin can open .pdf files directly. Unfortunately sometimes it so happens that a user gets the 109 error and is unable to open the document before they save it locally.

There are a couple of solutions suggested all around the Web, one of them being to give a user choice between saving file locally or open it in Adobe Reader window. One of the ways to do it is changing MIME type of the .pdf file and Content-Disposition header. Instead of default application/pdf we would be serving more generic application/octet-stream. Content-Disposition header has to be set as attachment. Here is an example PHP script which sends any existing .pdf file from the script's directory:
$file = $_GET['file'] . '.pdf';

if (preg_match('/^[a-zA-Z0-9_\-]+\.pdf$/', $file)
&& file_exists($file)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . $file);
readfile($file);
} else {
header("HTTP/1.0 404 Not Found");
}
Even though it works, it is not very elegant. We might want to link directly to .pdf documents instead of pdf.php?file=document.pdf and serve the files transparently through the php script.
For Apache users it is quite easy to achieve. An example .htaccess file:
RewriteEngine On
RewriteBase /your.directory.with.documents/pdf
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.+)\.pdf$ ./pdf.php?file=$1 [L,NC,QSA]
A few words of explanation:
  • Third line says to redirect to pdf.php script only valid requests for existing files.
  • Fourth line rewrites requests for .pdf files only as this is all we are interested in. The script gets filename without extension, eg. "document.pdf" is passed to the script as "document".
  • More about rewrite_mod for apache you can find in the documentation.

Tuesday, July 14, 2009

Custom Paragraph Classes in Typo3 RTE

Sometimes it is important to apply custom classes to paragraphs in RTE. Typo3 allows you to customize the "Block style" list easily. All it takes is a few lines of Typo Script in page TSconfig.

Let's add class lead for leading paragraph. We want this class to appear as "Leading paragraph" and be indicated with slightly bigger font on the "Block style" list. Here is how to do it in page TSconfig:
RTE {
classes.lead {
name = Leading paragraph
value = font-size:1.4em;
}

default {
contentCSS = fileadmin/templates/css/rte.css
proc.allowedClasses = lead
classesParagraph = lead
}
}
A few words of explanation:
  1. RTE.default.contentCSS defines css file used by RTE to render text in the editing window. It is required that the class we want to add is defined in this file
  2. RTE.classes.lead defines the entry for the "Block style" list; the value is inline style used to preview the class in the drop-down
  3. RTE.default.proc.allowedClasses tells RTE not to strip these classes when saving content to the database
  4. RTE.default.classesParagraph associates given class with paragraph, so you can see the selector on the "Block style" list when you choose "Paragraph" from the "Type of block" drop-down.
Further details (and much more) you can find in the page TSconfing documentation.

Saturday, July 11, 2009

Stopping PHP Script Execution

Do you know all the ways of stopping PHP script execution? Put your knowledge to a test with this question from the Zend PHP 5 Certification Mock Exam.

The question is as follows. Correct answers in bold.
What would go in place of ?????? below to make this script execute without a fatal error?
<?php

$a = 1;
$b = 0;

??????

$c = $a / $b;
?>
  • quit();
  • die();
  • stop();
  • __halt_compiler();
  • exit();
die() and exit() are quite obvious, stop() and quit() do not exist and what about __halt_compiler()? The latter was introduces in PHP5. Manual page says it stops the execution of the compiler and nothing after this call is executed.

Tuesday, July 7, 2009

Custom Frames for Content Elements in Typo3

The "Frame" option for Content Element in Typo3 becomes handy. What if default list does not suit your needs? You need to customize it then.

It is not difficult, event though not very easy to find. The functionality is built upon two different lists. One is a list of frame ids and corresponding labels visible in the BE. For each of these ids the other list defines actual "frame" - wrapper visible in the FE.

The first list is defined in page TSconfig as it is dealing with the back end. Here is an example:
TCEFORM.tt_content.section_frame {
removeItems = 5,6,10,11,12,20,21
altLabels.1 = Rounded
addItems.2 = Highlighted
}
Second line removes all the default items from the list, except for the one with id=1. Third line amends label of that element setting it to 'Rounded'. Finally, the fourth line adds new item to the list with id=2 and label 'Highlighted'.

The second list is defined in TS template as it is dealing with the front end. The example:
tt_content.stdWrap.innerWrap.cObject {
1 = TEXT
1.value = <div class="rounded">|</div>
2 = TEXT
2.value = <div class="highlighted">|</div>
}

Sunday, July 5, 2009

Find Missing Value in a Sequence in SQLite

Sometimes it is important to find gaps in sequenced values in your table, for instance id. How do you do it in SQLite? How do you do it in an efficient manner?

First things first. Google it up. Doing so I found very useful article "
How to find missing values in a sequence with SQL". Very inspiring. The idea is to check the list against itself only shifted by one. I tried the following query:
SELECT DISTINCT m1.id + 1
FROM mytable as m1
LEFT JOIN mytable as m2 ON m1.id + 1 = m2.id
WHERE m2.id IS NULL;
I had to use DISTINCT as mytable is just a helper table for many-to-many relationship and same id may appear many times. The query worked straightaway and was quite quick as I had and index on id field. Here are the basic information about the data:
  • no of records: 3360
  • no of unique ids: 228
The query above took 0.030s to execute on average.

That is not bad. Although I thought I would try a sub-query as well, even though they are usually discouraged as being too slow. I have re-written the query slightly:
SELECT DISTINCT id +1
FROM mytable
WHERE id + 1 NOT IN (SELECT DISTINCT id FROM mytable);
The query above took 0.014s to execute on average.

That means sub-query was faster than the join! I do not think it is a big surprise, though. Given the id column does not held unique values, and there are around 15 occurrences of each id, apparently it takes longer to create a join on all the values than to create a temporary table for the sub-query.

Concerns
  • Biggest missing value returned
    It is always max(id) + 1, so unless you need it, you might want to deal with it differently.
  • Lowest missing value returned
    The query does not recognize a gap between 0 and the lowest value in the sequence. Therefore the lowest missing value would be always bigger than the minimum.

Wednesday, July 1, 2009

Generating Custom Session ID in PHP 5

Another one of my favorite questions from Zend PHP 5 Certification Mock Exam. Correct answer in bold.
If you would like to change the session ID generation function, which of the following is the best approach for PHP 5?
  • Set the session.hash_function INI configuration directive
  • Use the session_set_id_generator() function
  • Set the session id by force using the session_id() function
  • Use the session_regenerate_id() function
  • Implement a custom session handler
Surprisingly, the most obvious answer, made sound like something not really reasonable, is the correct one. A few words of explanation.
And session_id() is the only one on the list allowing to set custom session id and definitely it is the correct answer.