Templates, TypoScript & beyond - Hackländer: Willkommen · Templates, TypoScript & beyond ... (F5...

58
Templates, TypoScript & beyond Snowboard Seminar, Monday 29/1 2002 Copyright 2000-2002, , <[email protected]> This document is published under the Open Content License available from http://www.opencontent.org/opl.shtml The content of this document is related to Typo3 - a GNU/GPL CMS/Framework available from www.typo3.com Table of Contents Templates, TypoScript & beyond... 1 The Frontend hierarchy................. 2 Introduction................................................. 2 The mysterious ways of the Front End......... 2 Frontend, Backend and in-between............... 2 dummy.tgz................................................ 2 F2: Alternative to index.php......................... 3 F4: Still in the rebel, but taking advantage of the TSFE object............................................ 4 For PHP-junkies........................................ 10 Advantages of the minimum TypoScript setup.. 10 F5+: Finally surrendered to TypoScript...... 10 All custom tables vs. default table (F5 or F6)... 11 Naming of custom tables............................12 F5: Default content tables.......................... 12 F7: Standard templates.............................. 12 F8: The pro's choice................................... 15 From scratch............................................ 15 Including static templates.......................... 16 Webpage with content and a menu..............18 Customizing the content layout................... 20 Including custom PHP code......... 26 The strategy............................................... 26 Example resource..................................... 26 PHP_SCRIPT cObject................................. 28 The _INT and _EXT concepts...................... 29 The better life with USER / USER_INT....... 29 HELLO WORLD - from our own PHP-class..... 30 $content and $conf................................... 30 IMPORTANT NOTICE regarding caching!....... 32 Available API............................................ 32 Accessing data in TSFE and cObj................. 33 Making queries to Typo3 configured tables... 33 TypoScript cObjects from within your PHP- class........................................................... 34 The TypoScript debugger........................... 35 Images.................................................... 37 Click-enlarging images...............................39 Webpage with content and a menu - revisited.. 41 Ways to call your custom methods............. 43 The “Script” content element...................... 47 Introduction to the API.............................. 48 Cases........................................... 50 The Photo Marathon................................... 50 Comments............................................... 52 Parameters.............................................. 53 The Humidor cam....................................... 53 Part I: The server-to-server communication. 55 Part II: Rendering the Humidor image......... 56 TEMPLATES, TYPOSCRIPT & BEYOND - 1

Transcript of Templates, TypoScript & beyond - Hackländer: Willkommen · Templates, TypoScript & beyond ... (F5...

Templates, TypoScript & beyond

Snowboard Seminar, Monday 29/1 2002

Copyright 2000-2002, , <[email protected]>

This document is published under the Open Content License

available from http://www.opencontent.org/opl.shtml

The content of this document is related to Typo3

- a GNU/GPL CMS/Framework available from www.typo3.com

Table of ContentsTemplates, TypoScript & beyond... 1The Frontend hierarchy................. 2

Introduction.................................................2The mysterious ways of the Front End......... 2

Frontend, Backend and in-between............... 2dummy.tgz................................................ 2

F2: Alternative to index.php.........................3F4: Still in the rebel, but taking advantage ofthe TSFE object............................................ 4

For PHP-junkies........................................ 10Advantages of the minimum TypoScript setup..10

F5+: Finally surrendered to TypoScript...... 10All custom tables vs. default table (F5 or F6)...11Naming of custom tables............................12F5: Default content tables..........................12

F7: Standard templates..............................12F8: The pro's choice................................... 15

From scratch............................................ 15Including static templates.......................... 16Webpage with content and a menu..............18Customizing the content layout...................20

Including custom PHP code......... 26The strategy...............................................26

Example resource..................................... 26

PHP_SCRIPT cObject................................. 28The _INT and _EXT concepts...................... 29

The better life with USER / USER_INT....... 29HELLO WORLD - from our own PHP-class..... 30$content and $conf................................... 30IMPORTANT NOTICE regarding caching!....... 32Available API............................................32Accessing data in TSFE and cObj.................33Making queries to Typo3 configured tables... 33

TypoScript cObjects from within your PHP-class...........................................................34

The TypoScript debugger........................... 35Images....................................................37Click-enlarging images...............................39Webpage with content and a menu - revisited..41

Ways to call your custom methods.............43The “Script” content element......................47

Introduction to the API.............................. 48

Cases...........................................50The Photo Marathon...................................50

Comments............................................... 52Parameters.............................................. 53

The Humidor cam....................................... 53Part I: The server-to-server communication. 55Part II: Rendering the Humidor image......... 56

TEMPLATES, TYPOSCRIPT & BEYOND - 1

The Frontend hierarchy

IntroductionThis paper discusses how to produce customly designed output with Typo3 through the use of what we all have come tolove – PHP! The style of PHP coding is however strictly based on functions and classes and all content must be collected ina variable and finally returned to the Typo3 framework. If you're used to a simple PHP coding style ever escaping PHP intoHTML, this is somewhat different. However using output buffering, you can tweek this if you wish. But that's your problem.

To begin with I would like to comment the FrontEnd Half of the Typo3 Overview document which is found as an externalPDF file. I'm refering to all the Fx blocks on the drawing below.

The mysterious ways of the Front End

Frontend, Backend and in-betweenIn Typo3 “Front-end” refers to the website (The F-blocks on the drawing from above). I have also seen people calling thispart of the CMS for the “Publishing Engine”. Basically this is the part responsible for displaying the content from thesystem, ei. Database, in the webbrowser. The Typo3 "Back-end" is the administrative interface to the content in thesystem (B-block on the drawing). We do not really distinguish between the PHP-code on the server and the interface. Thebackend is typically accessed through the scripts in the subdir "typo3/" to a given websites frontend.

In between is some shared configuration files (typo3conf/) which first and foremost ensures that the database connectionis established with the same username, password and hostname in both frontend and backend.

dummy.tgzThe file dummy.tgz is a totally blank Typo3 installation. It includes only the default directorystructure (in the tgz file withsymlinks for unix, windows users always use the zip-files!). This looks like:

-rwxr-xr-x 1 httpd httpd 46 Sep 7 1999 clear.gifdrwxr-xr-x 4 httpd httpd 4096 Jun 29 2001 fileadminlrwxrwxrwx 1 httpd httpd 18 Jan 22 09:37 index.php -> tslib/index_ts.phplrwxrwxrwx 1 httpd httpd 12 Jan 22 09:37 media -> tslib/media/-rwxr--r-- 1 httpd httpd 56 Sep 25 2000 mod_rewrite.htaccess

TEMPLATES, TYPOSCRIPT & BEYOND - 2

lrwxrwxrwx 1 httpd httpd 17 Jan 22 09:37 showpic.php -> tslib/showpic.phplrwxrwxrwx 1 httpd httpd 19 Jan 22 09:37 t3lib -> ../typo3_src/t3lib/lrwxrwxrwx 1 httpd httpd 19 Jan 22 09:37 tslib -> ../typo3_src/tslib/lrwxrwxrwx 1 httpd httpd 19 Jan 22 09:37 typo3 -> ../typo3_src/typo3/drwxr-xr-x 2 httpd httpd 4096 Jun 29 2001 typo3confdrwxr-xr-x 2 httpd httpd 4096 Jun 29 2001 typo3tempdrwxr-xr-x 6 httpd httpd 4096 Nov 12 1999 uploads

The red directories are frontend only, while the blue directoryes are backend only (t3lib/ is also found inside typo3/ folder,therefore the t3lib/ directory in the root is used by the frontend exclusively although it's the very same!). The greendirectories are directories shared by the frontend and backend.

F2: Alternative to index.phpWith a default Typo3 setup using the TypoScript based frontend, the index.php script receives all requests andsubsequently determines what to do. However you may also choose to substitute this script with your own index.php file(or whatever) and write a frontend implementation all on your own if you like. This is what the box F2 on the overviewdrawing is about.

Imagine this script implemented with the Typo3 testsite:

<?php

include("typo3conf/localconf.php");mysql_pconnect($typo_db_host,$typo_db_username,$typo_db_password);$page_id = isset($HTTP_GET_VARS["id"]) ? $HTTP_GET_VARS["id"] : 0;$res = mysql($typo_db,"SELECT * FROM pages WHERE uid=".intval($page_id)." AND NOT deleted");if ($page_row = mysql_fetch_assoc($res)) { echo "<H2>".$page_row["title"]."</H2>"; $res = mysql($typo_db,"SELECT * FROM tt_content WHERE pid=".intval($page_id)." AND NOT deleted ORDER BY sorting"); while ($content_element_row = mysql_fetch_assoc($res)) { echo '<font size=2 face=verdana><strong>'.$content_element_row["header"].'</strong></font><BR>'. '<font size=1 face=verdana>'.nl2br($content_element_row["bodytext"]).'</font><BR><BR>'; }}

?>

(fileadmin/sem1_3_examples/alternative_index.php)

This would produce an output like this with id=1:

TEMPLATES, TYPOSCRIPT & BEYOND - 3

However it quickly becomes clear that this is not the most straightforward way to implement a frontend for Typo3. It'sobvious that it'll be a road filled to the brim with obstacles, errors etc. because we'll have to reinvent all the evaluation ofincoming data, issue queries that also takes starttimes, endtimes, users and groups plus hidden field settings into account.We'll have to build more advanced content presentation than the one seen here merely producing bodytext with a simpleheader. The true number of options in tt_content table amounts to over 50 fields with each a specific task within the scopeof a specific type, be it Text, Text w/image, Bulletlist etc.

So instead we revert to F1.

F4: Still in the rebel, but taking advantage of theTSFE objectAs soon as we accept F1 as our basis we also accept the whole framework of TypoScript, id and type numbers, defaultfrontend user authentication, caching etc. Now this is supposed to be good thing. And when this foundation has been laidyou're still free to choose how to go on from there!

An overview of the tasks performend by F1 includes (basically a walkthrough of index_ts.php):

• Setting the PHP-error reporting, a bunch of constants (paths), checking PHP version and OS.

• Including classes for the FrontEnd.

• Including t3lib/config_default.php which in turn includes the typo3conf/localconf.php file. These set up the databaseconstants in addition to the global $TYPO3_CONF_VARS.

• Handles magic_quotes configuration of PHP, adding slashes to HTTP_GET_VARS and HTTP_POST_VARS in case it isturned off. The reason for this is historic.

• Creates an instance of the class tslib_fe in the global variable $TSFE. This object will control the frontend activities.

TEMPLATES, TYPOSCRIPT & BEYOND - 4

• Connects to the MySQL database.

• Initializes the front end user session.

• If a backend user is logged in, all the frontend implemented backend-features are initialized. This includes theadminPanel and preview facilities.

• The id and type of the request is determined. Typo3 supports both direct GET parameters along with alternativemethods using either PATH_INFO (Apache) or preferably the mod_rewrite trick of Apache which allows us to mapvirtual HTML-filenames to the index.php script. Also if there are any domain records set in Typo3, these are evaluatedaccording to the current domain. Page access restrictions are also evaluated.

• The TypoScript template engine is initialized.

• Page is retrieved from MySQL based cache, if possible.

• The config-array is initialized along with a partly $TCA array.

• Language settings are initialized.

• If any data has been submitted, eg. a mail form or forum/guestbook entry this is processed.

• The page is rendered in case it was not retrieved from cache. *

• Any non-cached cObjects are rendered (PHP_SCRIPT_INT, USER_INT, PHP_SCRIPT_EXT)

• The $TSFE->content variable is finally outputted.

• Session data is stored.

• Statistics are written to logfile and/or database table.

• The jumpurl redirect feature is evaluated and possibly the script redirects to another url at this point.

• The “preview” info text is possibly outputted.

• Publishing to static files is initiated, if requested.

• The adminPanel is inserted, if requested.

(For more information on which global variables are set, please refer to Inside Typo3 which provides the authoritative list)

In this example we'll look at the advantages of F1 but quickly escape into our own PHP-script. The red line in the list aboveis where our “pagegen” script is included. This is approx. at line 315 in index_ts.php.

The basic requirements for this is a single page in Typo3 with a template on it. If you're doing this is practice yourself,install a blank Typo3 site by the dummy.tgz file, hit the index.php file and you're redirected to the install tool. Enterusername and password and go through the Install Tool like this:

TEMPLATES, TYPOSCRIPT & BEYOND - 5

When you have successfully installed a blank set of tables, imported the content of static_templates and sys_tabledescrsql-files and created an admin-user, then log in, create a new page in the page tree and go to the Web>Template module:

TEMPLATES, TYPOSCRIPT & BEYOND - 6

Create a new template record by the button.

If you go to the Web>List module, you'll see the TypoScript template represented as a record in the list of recordsbelonging to the current page, “Root page”:

Going back to the Web>Template module, it looks like this...

TEMPLATES, TYPOSCRIPT & BEYOND - 7

...and you can now begin to edit the TypoScript:

Looking at this default code (and classic example) in the frontend, you'll see what is basically offered by level F8 from theoverview. This is custom made TypoScript, though a very simple example.

Looking at the HTML source code this is revealed:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html><head><!-- This is a Typo3/TypoScript generated document Typo3 is a free Content Management System (see www.typo3.com) developed by Kasper Skaarhoej and licensed under GNU/GPL.--><title>Root page</title><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"><meta name="generator" content="Typo 3.2 CMS"><script language="javascript"><!--

browserName = navigator.appName;browserVer = parseInt(navigator.appVersion);var msie4 = (browserName == "Microsoft Internet Explorer" && browserVer >= 4);if ((browserName == "Netscape" && browserVer >= 3) || msie4) {version = "n3";} else {version =

"n2";}function blurLink(theObject) {

if (msie4) {theObject.blur();}}

// --></script></head><body bgcolor="#FFFFFF">HELLO WORLD!</body></html>

As you can see, the HELLO WORLD! text is inserted between two automatically made bodytags in addition to a complete

TEMPLATES, TYPOSCRIPT & BEYOND - 8

header section! The whole header section which we did not specifically design was inserted thanks to level F3 in theOverview drawing.

However, in our rebel against another technology (TypoScript...) we're going to bypass this headersection and include ourown content totally:

So first of all we modify the TypoScript template record in the backend:

# Default PAGE object:page = PAGEpage.typeNum = 0page.config.pageGenScript = fileadmin/alternative_pagegen.php

This modification tells TypoScript to include the file “fileadmin/alternative_pagegen.php” instead of the defaulttslib/pagegen.php script (see TSref for details). Basically this happens only if the &type-parameter in the URL is zero or notset.

Then we write the alternative “pagegen”-PHP-script (maybe inspired by the default tslib/pagegen.php) and place it infileadmin/alternative_pagegen.php:

<?php

if (!is_object($TSFE)) {die("You cannot execute this file directly. It's meant to be included from index_ts.php");

}

$GLOBALS["TSFE"]->content="This page has id #".$GLOBALS["TSFE"]->id;echo "DIRECT output here!<HR>";?>(fileadmin/sem1_3_examples/alternative_pagegen.php)

Notice how we start out checking that this script is certainly included from tslib/index_ts.php script (the same as index.phpin the root). The F1 section in the overview offers the global TSFE object to us. And by checking the existence of thisobject, we can make sure, that our script here is not invalidly called directly by users.

After this little validation, the content “This page has id#” is set and the id number of the current page is shown. This givesus two informations:

1. All content must be returned into the variable $GLOBALS[“TSFE”]->content

2. The object $GLOBALS[“TSFE”] seems to have a number of internal variables, including such as the actual evaluatedpage id! (see the class tslib/class.tslib_fe.php for details)

Finally we try to output some content directly and we do succeed it seems on the output:

However when we hit the page a second time, our direct output is not shown:

Bummer dude, or what? Nope, this is the great caching mechanisme of F1 you see in action. You can turn of caching inmany ways, by setting a flag for the page, sending the URL-parameter &no_cache=1 or by calling the method $GLOBALS[“TSFE”]->set_no_cache() from your script, in this case fileadmin/alternative_pagegen.php. However caching is a goodthing, because it lets you render the content once and subsequently display the once-rendered content without invokingthe sometimes heavy process of compiling the page content. Of course this does also limit your possibilities with truelydynamic content. However this problem is solved by the USER_INT cObject discussed later on.

By the way, looking at the HTML source code this is all you see:

TEMPLATES, TYPOSCRIPT & BEYOND - 9

This page has id #1

Now the automatically generated header section is left out! Of course - it's all in your hand at level F4!

For PHP-junkies...... there is even a way to get around the coding of the formerly proposed script:

<?php

if (!is_object($TSFE)) {die("You cannot execute this file directly. It's meant to be included from index_ts.php");

}

ob_start();?>This page has id #<?php echo $GLOBALS["TSFE"]->id; ?><HR>DIRECT output here!<?php$GLOBALS["TSFE"]->content = ob_get_contents(); ob_end_clean();?>(fileadmin/sem1_3_examples/alternative_pagegen2.php)

In this example above we operate totally within the good old well-known (but largely shortterm in my opinion) scope ofPHP. Turning on output buffering lets us “keep back the content” and finally deliver it to the $TSFE->content variable andsubsequently clear the output buffer. Thus the whole caching concept is still utilized fully!

Advantages of the minimum TypoScript setupOn level F4 we merely use the TypoScript template to invoke our own PHP script as fast as possible. But notice the greatadvantage this concept offers us: Because the TypoScript template record invokes our script, we can place thoseTypoScript template records whereever we like in the page tree and instantly that will determine which script controlswhich branch of the page tree. Imagine a template record on the pages “Website 1”, “Website 2” and “Website 3” in thepage tree below. Each page including all subpages (until a new TypoScript template may form a new “root”) will be controlby their own individual script.

F5+: Finally surrendered to TypoScriptBy using the default tslib/pagegen.php script of Typo3, we gain these advantages (mainly extract fromclass.tslib_pagegen.php):

• A host of internal variables in $TSFE is configured. Incoming search-words are processed. “linkVars” are set.

• Files configured in TypoScript for inclusion are included.

• The advanced combination of USER_INT objects with the cached content is managed.

• The header section is automatically generated. This includes management of JavaScript prepared images and layers setfrom content creating objects like the GMENU, TMENU etc.

• Stylesheet may be included by setting a TypoScript option.

TEMPLATES, TYPOSCRIPT & BEYOND - 10

• Additional content rendering related libraries are included.

• Page content is now in the hands of the content object array of the PAGE TypoScript object.

All custom tables vs. default table (F5 or F6)It's strongly encouraged that you consider using your own custom tables with Typo3. They are relatively easy to configure.This is not discussed here however (refer to Inside Typo document and the “Extending Typo3...” seminar paper). But itwould be appropriate to put a note on how to handle the default tables:

F6 represents a situation where you discard the default tables.php file distributed with Typo3. However certain tables aretightly integrated in the system, both frontend and backend and there is currently no complete list of the dependencies.Generally speaking all the tables which are not named tt_* is somehow integrated in the system (and thereby required) bydefault whereas all “tt_*”-tables represent the content presented on the website and thus they may be substituted withwhatever you like.

It's recommended to include the default tables.php file. If you do not, you'll have to patch this file with each new Typo3version with all configurations which relates to the required tables. And if you do keep it, it's extremely easy to upgrade -just replace the old tables.php with the new.

The solution to the problem is to specify a custom file to include subsequently to the tables.php file. This is done inlocalconf.php like this:

(From the testsite:)

$typo_db_extTableDef_script = "extTables.php";

... and in the typo3conf/ directory, you put this file the file named “extTables.php”. It could look like this:

<?php

unset($TCA["tt_content"]);unset($TCA["tt_news"]);// ... etc.

$TCA["user_photomarathon"] = Array ( "ctrl" => Array ( // ......... etc. ), "columns" => Array ( // Title is set up as an ordinary input-field, one line. "title" => Array ( "label" => "Image title:|Billedtitel:|Bildtitel:", "config" => Array ( "type" => "input", "size" => "40", "max" => "80", "eval" => "trim" ) ), // Image description is set up as a textarea form element in the backend "description" => Array ( // ... etc. ) "types" => Array ( "0" => Array("showitem" => "title,description") ));?>

In this example, the default tables tt_content and tt_news are removed from Typo3's sphere. Typo3 will not know of thesetables and cannot operate on them.

In addition a user defined table is added, “user_photomarathon”, and fully configured, which in turn makes it visible toTypo3.

You can also manipulate the existing values in the $TCA array. In this example, a fifth element is added to thett_content.colPos field (provided you didn't remove the tt_content table as shown above ;-)

/** * This adds an extra tt_content column (colPos=4), before the last column (Border): * The function array_splice works directly on the input array! */array_splice( $TCA["tt_content"]["columns"]["colPos"]["config"]["items"], // The list of items -1, // To insert one position from the end of the list 0, // Don't remove any items, just insert array(array("Extra Column|Ekstra kolonne",4)) // Element to be inserted.);

TEMPLATES, TYPOSCRIPT & BEYOND - 11

And finally the extTables.php file can also be used to add modules to backend interface:

/** * Setting up a user defined submodule in the Web module of the backend * * See typo3conf/web/uPhotomarathon/ for the PHP code of the module. */$TBE_MODULES["web"].=",uPhotomarathon";

(Please investigate the extTables.php file found with the testsite.)

These examples show a vital concept: Manipulating the $TCA array through a custom script, thus keeping compatibilitywith future upgrades, still expanding with custom tables.

Naming of custom tablesIt's highly recommended to prepend all custom fields and tables with the prefix “user_”. Not only will this prefix never beused for any default tables and fields, but the prefix is also recognized by the Install Tool thus warning you that this tableor field is apparently a user-added field or table and thus should not be removed during updates of the database structure:

This picture shows how the install tool recognizes the custom table “user_posts” and places a warning sign. This makes iteasy to get an overview of your database upgrade operations, because it's obvious which fields and tables are custom andwhich are not.

F5: Default content tablesEven if you choose to include the default tables.php file and extend it further, you should also consider to keep the defaultcontent tables, in particular tt_content.

The reason for this recommendation is that this table has been extended through the years and represents a very matureconcept of building pages with complex content. All the rendering code is made up with TypoScript and is accessible bysimply including a so called static template. Thus all the rendering is done automatically, but still customizable partlythrough TypoScript constants - or for advanced purposes through direct manipulation of the Setup object tree.

Whether or not your entry point is F7 or F8 on the overview, you'll probably take advantage of the default contentrendering TypoScript code found in the static templates.

F7: Standard templatesThe absolutely quickest way to create a new fullblown website with Typo3 is to include one of the default templates. This isdone in very few steps like this:

TEMPLATES, TYPOSCRIPT & BEYOND - 12

Looking at the website (putting some dummy content in first...) it instantly looks like this:

Of course the default template chosen is not adequate. We need to customize it. For this purpose the Constant Editorfound in the Web>Tempalte module is perfect. It allows direct access to selected constants and offers explanations to eachtopic:

TEMPLATES, TYPOSCRIPT & BEYOND - 13

Still the concept is fully reliant on the TypoScript core and the basic template record looks like this:

The use of standard templates is accomplished by an initial inclusion of the static template as shown here, possibly with a“cSet” extension which accumulates related constants into new constants. Of course the obligatory rootlevel and clearconstants/setup flags must be set so the template record represents a truely fresh starting point where it's positioned inthe webtree. By time the Setup and Constants fields in the record will be filled with the TypoScript code which isresponsible for the customizations etc.:

TEMPLATES, TYPOSCRIPT & BEYOND - 14

F8: The pro's choiceWhile the default templates provided with Typo3 is quick ways to get running, most webdesign starts another place: Withplanning, design, definition of goals etc. Therefore it's vital to be able to implement any frontend design for your site. Thismay be and obvious point. However CMS solutions tend to solve this in various manners. Some solutions has a designermodule which - by concept - will limit the number of options available to the implementor. Others seems to deliver a prettyblank frontend where you have to develop everything yourself. Typo3 tries to deliver both. Not only by the frontendhierarchy with numerous entry points to choose from, but also by allowing the existence of standard templates, whichcomes close to the “designer” solution: Pick a frameset, choose a background color. But the standard templates alsoreveals the very concept in Typo3: To first provide the solid technical foundation, then to add a layer on top of this forusers with less needs.

In Typo3 you have total freedom if you pursue it; You can include content from your own PHP-scripts. But the frameworkfor implementing a frontend design also offers a lot of custom TypoScript “objects” to use and take advantage of. Thesecond part of this document discusses into detail how to fully escape into PHP - the very programming language of Typo3.But for now we'll try to give insight into how you can create a website fully with TypoScript, taking advantage of thestatic_templates as far as possible.

From scratchDuring the editing of TypoScript template records, you should always work in the Web>Template module. Not only is itdesigned to provide you with tools regarding this task, but it'll also make sure that the template cache is cleared for youeach time you modify your template!

Go to the root page, “Website 1” (or whatever) in your almost blank dummy-site. If any template is still around, go to thelist module and delete the record. Then enter the Web>Template module and create a new, blank template:

By the end of this process, you can click the pencil icon next to “Setup” and this default TypoScript is revealed:

TEMPLATES, TYPOSCRIPT & BEYOND - 15

Select “Object Browser” in the Menu (and expand the “page” node and the “page.10” node):

If you did your homework well (reading other documents like TSref or TypoScript By Example) you'll know that TypoScriptis not so mysterious. It's a “declarational programming language” if you insist on calling TypoScript a programminglanguage. I my self prefer to think about it as a large hierarchy of configuration values that in turn makes the frontendcode libraries carry out certain actions. Thus it becomes “programming”, but it basically is “configuration of behaviour”.

The Object Browser is a brilliant tool. You can click the nodes in the tree and alter values. Very, very comfortable way tomake sure you don't mispell anything. The object browser also neatly shows you how the TypoScript from the Setup fieldis parsed into the hierarchy, doesn't it?

Including static templatesGoing back to the “Info/Modify” section of Web>Template, we now wish to edit the whole template record, in particularinclude the static template which is going to render our content:

As you see the three obligatory checkboxes are set. This is the typical settings which makes this template record anabsolutely new root of a website. In addition we include the static template “content (default)”.

TEMPLATES, TYPOSCRIPT & BEYOND - 16

The static template basically includes some TypoScript which is parsed before the TypoScript you enter yourself in themain template. The TypoScript is altogether concatenated and subsequently parsed. You can also include other ordinarytemplate records with small custom “libraries” of TypoScript configuration you may desire to establish.

In any case the order in which the TypoScript is included is clearly shown with the “Template analyzer” section of theWeb>Template module:

From this perspective we can see our template, “NEW SITE”, includes “content (default)”. However the static template“content (default)” further includes other static templates, namely “styles.header.gfx1”, “styles.sitemap.text” and“styles.content (default)”. Should we at any point wish to access TypoScript configuration found in these static templateswe can now rest assured that they are in fact included already!

OK, going back to the “Object Browser” this node tree has changed:

TEMPLATES, TYPOSCRIPT & BEYOND - 17

A new toplevel object (TLO) has arisen, which is basically not a surprise, if we care to look inside the static template“content (default)” which we just included (Picture to the right: This view is seen by clicking the template title inWeb>Template, Template Analyser section. Line numbers and comments are enabled here.)

If we ever wish to alter the behaviour of the default content rendering, we can always override the TypoScriptconfiguration inside “tt_content.” with whatever we like (eg. give over control an external PHP-script).

Webpage with content and a menuGoing back to the “Info/Modify” section, click the “Setup” pencil icon, we're now going to modify the default HELLO WORLDTypoScript:

# Default PAGE object:page = PAGEpage.typeNum = 0page.wrap = <TABLE border=1><TR> | </TR></TABLE>page.10 = TEXTpage.10.value = <TD nowrap valign=top>page.20 = HMENUpage.20.1 = TMENUpage.20.1.NO { linkWrap = <B>|</B><BR>}page.30 = TEXTpage.30.value = </TD><TD><IMG src=clear.gif width=10 height=1></TD><TD>page.40 < styles.content.getpage.50 = TEXTpage.50.value = </TD>

TEMPLATES, TYPOSCRIPT & BEYOND - 18

Basically as soon as we're above level F3 in the Typo3 Overview drawing, content is created by establishing “cObjects”(short for “Content Objects”). Those content objects are inserted at numerical positions on the TLO (Top Level Object)“page” (in this case). They are rendered in the numerical order of their keys.

It's pretty obvious from this code that we make a table with three columns, the middle column filled with a 10 pixels wideclear gif-file. In fact the content objects only specifies the table columns - the table-tags and row tags are in this casewrapped around by the “page.wrap” attribute.

In the first column a cObject called HMENU is inserted, rendering a textual menu which basically wraps the page titles inbold-tags and a break-tag at the end.

In the third column something interesting happens. Until now our simple TypoScript has shown the very principle ofconfiguring the behaviour of the rendering engine. Now we include the page content by specifying one single line:

page.40 < styles.content.get

What happens here is that the TypoScript content of the object-reference “styles.content.get” is copied to “page.40”. Sothe next question is, where is “styles.content.get” defined? Well, somewhere in our static templates just included! Andgoing to the “Template Analyser” clicking around a bit reveals that it's found in “styles.content (default)”:

So what actually happens is that “page.40” becomes an exact copy of “styles.conten.get” as seen here - in effect aCONTENT cObject with a little more configuration. This is clearly seen in the Object Browser:

(The TLO “styles” is not seen in the Object Browser as you might expect. This is because “styles” and “temp” TLO's aretemporarily available during parsing and finally unset to save space. They must be copied to other positions in the objecttree in order to work)

TEMPLATES, TYPOSCRIPT & BEYOND - 19

The final output of all this is seen in the frontend:

As expected. From here it's basically a question of extending the TypoScript structure begun.

Customizing the content layoutYou have four options:

1. Go to the constant editor, select CONTENT in the menu, there are 60+ options to choose from. Enable them as youneed it and define alternative values.

2. You can also include the static template “cSet (default)”, which accumulates the values of selected constants into fewerconstants and displays them in a menu of it's own. You can always override individual constants anyways.

3. You can include the static template “cSet stylesheet” which provides a framework for layouting the content throughclasses in a stylesheet.

4. Finally you can mix these options with hard-style overriding of the tt_content.* cObjects as seen in the Object Browser.

1: The Constant EditorThe constant editor provides a streamlined user interface to the constants field. In the category “CONTENT” you'll find a lotof options all regarding the layout of content elements.

TEMPLATES, TYPOSCRIPT & BEYOND - 20

In this case we changed the color of the default header layout 1 to maroon

2. Include “cSet (default)”Include the static template “cSet (default)”:

... and go back to the Constant Editor. You'll see a new category, CSET, in which you have some options:

TEMPLATES, TYPOSCRIPT & BEYOND - 21

Here's how the frontend looks:

Notice how cSet works: It defines a constant which is then again inserted as the value of another constant:

At this point the Constant field of our template looks like this:

3. Include “cSet stylesheet”A more modern approach is to include the “cSet stylesheet” static template:

TEMPLATES, TYPOSCRIPT & BEYOND - 22

By doing this, the Constant Editor provides an entry for a stylesheet file:

It's encouraged to make a copy of media/scripts/defaultstylesheet.css and modify this as you need. You can do that onlineas well:

First upload the new stylesheet, go to “Info/Modify”, click the Resources icon, then the edit-icon of the uploadedstylesheet:

... and edit it!

TEMPLATES, TYPOSCRIPT & BEYOND - 23

This is how the frontend reacts:

4. Override TypoScriptNot a recommended option, but deserves to be mentioned; Traversing the object tree, you can identify certain tings tochange. I this case, the <P> tags used for styling in the above example:

TEMPLATES, TYPOSCRIPT & BEYOND - 24

... and by a single click, edit the value:

TEMPLATES, TYPOSCRIPT & BEYOND - 25

Including custom PHP code

The strategyOriginally TypoScript (TS) was a framework needed to invoke a limited set of content types. By time it has been extendedenormously. And certain tasks are definitely solved much, much easier with PHP, in particular all kinds of logic andintelligence. However some core parts of TypoScript remains very powerful. In particular this is the menu objects GMENU,TMENU and derivates hereof, all kinds of image manipulation, the IMGTEXT cObject (although seldomly used except withregular content).

Today TypoScript has reached the end regarding new “intelligent” features. Rather users are encouraged to branch of intotheir own PHP functions and TypoScript remains as the foundational framework for custom code. Continuously TypoScriptis developed but primarily with regard to service features for external code including the internal API. For instance, thestdWrap function if TS allows you to proces a value and even fetch a value from the database purely by setting TSparameters. But instead of implementing new features on request, the stdWrap function has been enriched with options tocall user defined functions and thus processing is not in hand of TypoScript but in the PHP-coder. Which ensures endlesspossibilities. This concept is also implemented with many of the menu objects allowing for extensive manipulation.

So, TypoScript lays a foundation and when it's appropriate (due to lack of features in TS or due to your limited knowledgeof TS) you escape into PHP.

Example resourceThe testsite is a testbench including a whole section dedicated to feature show-off. In this section you'll find a growingnumber of TypoScript features demonstrated. It's learning by peeking in and modifying the code provided to demonstratethe features.

Basically I'm not going through each of these examples. They must speak for themselves.

However I'll quickly demonstrate the nature of one single example, the “Fake menu items”:

The page “Fake menu items” has a bunch of empty subpages, is it self empty and has a single template record attached:

TEMPLATES, TYPOSCRIPT & BEYOND - 26

Looking in the template record we see this straight forward TypoScript:

## Include function includeLibs.fakemenuitems = media/scripts/example_itemArrayProcFunc.phppage = PAGEpage.typeNum = 0page.5 = TEXTpage.5.field = titlepage.5.wrap = <h3>|</h3>page.7 = TEXTpage.7.field = abstractpage.7.wrap = <i>|</i><HR>page.10 = HMENUpage.10.1 = TMENUpage.10.1.expAll = 1page.10.1.NO { allWrap = | <BR> linkWrap = <b>|</b>}page.10.2 = TMENUpage.10.2.itemArrayProcFunc = user_itemArrayProcFuncTestpage.10.2.NO { allWrap = | <BR> linkWrap = <b> - |</b>}

The green lines simply establish some pretty trivial output, simply stating the page title along with the content of the“Abstract” field of the page record, which is used for the description of the demo.

The blue lines is a simple TMENU menu object.

The red lines are the ones of real interest TypoScript wise: The top line shows how to include an external php-file. Thiscontain the function we're going to call and must be included before the whole rendering proces is started. The second redline shows how we're calling this function from the TMENU menu object (refer to TSref).

Of course we're now going to look inside of media/scripts/example_itemArrayProcFunc.php because we're so curious whathappens in there:

function user_itemArrayProcFuncTest($menuArr,$conf) {// debug($menuArr); if ($conf["demoItemStates"]) { // Used in the example of item states reset($menuArr); $c=0; $teststates=explode(",","NO,ACT,IFSUB,CUR,USR,SPC,USERDEF1,USERDEF2"); while(list($k,$v)=each($menuArr)) { $menuArr[$k]["ITEM_STATE"]=$teststates[$c]; $menuArr[$k]["title"].= ($teststates[$c] ? " [".$teststates[$c]."]" : ""); $c++; } } else { // used in the fake menu item example! if (!count($menuArr)) { // There must be no menu items if we add the parent page to the submenu: $parentPageId = $conf["parentObj"]->id; // id of the parent page $parentPageRow = $GLOBALS["TSFE"]->sys_page->getPage($parentPageId); // ... and get the record... if (is_array($parentPageRow)) { // ... and if that page existed (a row was returned) then add it! $menuArr[]=$parentPageRow; } } } return $menuArr;}

Apparently the function does two separate things depending on TypoScript configuration passed to it; If the property .demoItemStates of the TS object specifying the function name is set, then it performs the part gray'ed out above. Thiscould have been invoked by setting the TypoScript configuration like this:

TEMPLATES, TYPOSCRIPT & BEYOND - 27

page.10.2.itemArrayProcFunc.demoItemStates = 1

But it's not set in our case and from looking at the php-code above we can see that ifthere are no menu items in the $menuArr then we add one, namely the parent page tothe current. So we insert a fake menu item if there is no menu item on beforehand. Thefact that $menuArr passed to the function as the content variable (always the firstparameters passed to function calls via TypoScript) contains the menu items isdocumented in the TSref document. However you may often resort to desicating thesevariables yourself in which case the debug() function is of great help. It outputs thevariable content - especially arrays - in a nice visual manner as seen on the image to theright.

(This view is achieved by un-commenting the section line in the PHP code listing above.)

Just like the “Fake menu item” there are a multitude of similar examples on the testsiteand the goal is to grow this collection so it becomes a comprehensive resource forexample TypoScript which is known to work.

PHP_SCRIPT cObjectThis was the first ever cObject introduced to invoke custom PHP-code. Today it'sdiscouraged to use this for larger projects. Basically the script is included from a functionwithin the class tslib_cObj:

function PHP_SCRIPT($conf,$ext="") {$incFile = $GLOBALS["TSFE"]->tmpl->getFileName($conf["file"]);$content="";if ($incFile &&

(!$GLOBALS["TYPO3_CONF_VARS"]["FE"]["noPHPscriptInclude"] || substr($incFile,0,14)=="media/scripts/")) {

$this->oldData = $this->data;include($incFile);if ($RESTORE_OLD_DATA) {

$this->data = $this->oldData;}

}return $content;

}

(Listing from tslib/class.tslib_content.php. Shortend version for display here.)

The requirements for using the PHP_SCRIPT cObject is still that you return the content inthe variable $content. As you can see, this is simply returned from the function here whichincludes the script.

By the PHP_SCRIPT cObject the script is included into the scope of an instance of the tslib_cObj class. This is great fornumerous reasons regarding the API internally. However it's quite annoying that the script is not allowed to includefunctions and classes which has to be present through inclusion of a function library prior to inclusion of this script - andthus two scripts would be needed.

If you wish to study an example of the PHP_SCRIPT being used, the media/scripts/calendar.inc is an example hereof:

// getting configuration values:$config["pid_list"] = trim($this->stdWrap($conf["pid_list"],$conf["pid_list."]));$config["pid_list"] = $config[pid_list] ? $config["pid_list"] : $GLOBALS["TSFE"]->id;

$config["recursive"] = $this->stdWrap($conf["recursive"],$conf["recursive."]);

$config["font"] = $this->stdWrap($conf["fontFace"],$conf["fontFace."]);$config["font"] = $config["font"] ? $config["font"] : "verdana";

// If the current record should be displayed.$config["displayCurrentRecord"] = $conf["displayCurrentRecord"];

$templateCode = $this->fileResource($conf["templateFile"]);

// Fetching catagories:$categories = Array();$query = "select * from tt_calender_cat where 1=1".$this->enableFields("tt_calender_cat");$res = mysql(TYPO3_db,$query);echo mysql_error();while($row = mysql_fetch_assoc($res)) { $categories[$row[uid]] = $row[title];}..... etc.Notice from this snippet how methods in the object is called, eg. $this->enableFields(), $this->fileResource(), $this-

TEMPLATES, TYPOSCRIPT & BEYOND - 28

>stdWrap() etc. The $conf-array contains the TypoScript configuration of all properties to the PHP_SCRIPT cObject andthose may be customly used to pass configuration from the TS template into the PHP_SCRIPT cObject. Also notice how theid-number of the current page is fetched from the global $TSFE object!

The _INT and _EXT conceptsThis topic is sufficiently covered in TSref but deserves a quick note here. While the PHP_SCRIPT is directly included duringrendering time, the PHP_SCRIPT_INT cObject is not. In fact the current tslib_cObj instance is at this point serialized andlater saved with the cached page. A marker is inserted in the page content instead and when the page is displayed thecached content is combined with the dynamically rendered content from the PHP_SCRIPT_INT object which is brought tolife by unserializing the saved instance. The same principle is used with USER_INT.

The _EXT is more freaky. Where you have the TypoScript API right at your hand with the *_INT cObjects, the _EXT isbasically included in the global scope of index_ts.php and you have no significant API available. However you may use thisto include PHP-scripts which outputs code directly.

Examples of these types are seen on the testsite in this section at page 113:

The better life with USER / USER_INTThe most significant change here is that you include only one php-script, which in turn contains classes and functions asyou like. Class and function names must be prefixed “user_” (by default), methods in classes can be name however youlike.

The difference of USER and USER_INT is outlined above; If your content is cachable within the scope of a day which is thedefault, no problem - use USER. If your content must be truely dynamic, use USER_INT. When USER_INT objects arefound on a page, you must be aware that there will be an overhead of initializing and including certain libraries in additionto the default. So it's not totally without a cost to mix cached and dynamic content. However it may deliver much fasterpage rendering than without any caching - especially if the page has a lot of content in it self.

Using functions or classes are again a question of the scope; Are you making some simple processing or are you dealingwith complex processing, maybe creating a fullblown web application?

One significant advantage of using classes is that the tslib_cObj instance from which the method is called is set by

TEMPLATES, TYPOSCRIPT & BEYOND - 29

reference in the internal var, $cObj, of the instance. This means that access to the 'mother-API' of the tslib_cObj class isavailable like this example shows:

Getting the enable field clause for tt_content:

“SELECT * FROM tt_content WHERE pid=”.$id.$this->cObj->enableFields(“tt_content”);

... or accessing the record content of the current record:

$currentRecord = $this->cObj->data;

Finally function/method calls from TypoScript always passes two variables: $content and $conf.

$content makes no sense when cObject like USER and USER_INT calls the function. The reason is that the function ormethod is in this case expected to produces output (as opposed to process input). $content is on the other hand used bythe many function calls meant for processing. One example was the “Fake menu items” above where the $menuArr arraywas passed and had to be returned again. Another example is the function calls from stdWrap function which is also aboutprocessing a input values (or of course returning something totally different if needed).

$conf is an array which contains the TypoScript configuration array of the object which defined the function name (or thecObject).

HELLO WORLD - from our own PHP-classThis is the TypoScript part:

# Default PAGE object:page = PAGEpage.typeNum = 0

includeLibs.test_library = fileadmin/test_library.phppage.10 = USERpage.10.userFunc = user_test_library_class->test

... and here goes the content of fileadmin/test_library.php:

<?php

class user_test_library_class { var $cObj; // reference to the calling object. function test($content,$conf) { global $TSFE; $TSFE->set_no_cache(); return "HELLO WORLD"; }}

?>(fileadmin/sem1_3_examples/test_library.php)

... and finally the highly sofisticated output:

$content and $confBecause the method is called as a cObject $content is simply an empty string. It's there for compatibility with similarfunctions and methods when they are called for processing of data instead.

However $conf contains interesting information. This is best viewed by printing out the content of $conf through the onlyglobal function available in the TypoScript based frontend - debug():

<?php

class user_test_library_class {

TEMPLATES, TYPOSCRIPT & BEYOND - 30

var $cObj; // reference to the calling object. function test($content,$conf) { global $TSFE; $TSFE->set_no_cache(); debug($conf); return "HELLO WORLD"; }}

?>

This is the output on the front end:

This is obviously the TypoScript parameter of the USER object. Adding more parameters like below will equally be reflectedin the $conf-array:

page.10 = USERpage.10.userFunc = user_test_library_class->testpage.10.fontStyle { color = red face = verdana}page.10.border = 1page.10.border.color = black

... and this is how it looks:

Printed by the print_r() PHP function it looks like this:

Array( [userFunc] => user_test_library_class->test [fontStyle.] => Array ( [color] => red [face] => verdana ) [border] => 1 [border.] => Array ( [color] => black ))

... and in terms of access, this is how you extract, say, the border color:

$bordercolor = $conf[“border.”][“color”];

Take a moment to grasp this - basically what you see is the heart of TypoScript: Configuration values passed to a function- what comes out of it is determined by the function acting on the input configuration. And in this case, you're in charge ofwhat comes out! (.userFunc and .includeLibs are the only reserved properties for USER and USER_INT objects - theremaining scope is for you to define).

Now, lets try to combine this into a little application utilizing these configurations:

TEMPLATES, TYPOSCRIPT & BEYOND - 31

<?php

class user_test_library_class { var $cObj; // reference to the calling object. function test($content,$conf) { global $TSFE; $TSFE->set_no_cache(); // Setting the output string: $outputString = "HELLO WORLD"; // If set, add the font-tag parameters to this associative array: $fontTagParams = array(); if ($conf["fontStyle."]["color"]) $fontTagParams["color"]=$conf["fontStyle."]["color"]; if ($conf["fontStyle."]["face"]) $fontTagParams["face"]=$conf["fontStyle."]["face"];

// If any font-tag parameters was set, wrap the output string: if (count($fontTagParams)) { // Compile array to attribute string $fontTagAttributes = t3lib_div::implodeParams($fontTagParams); $outputString = '<font '.$fontTagAttributes.'>'.$outputString.'</font>'; } // If border is enabled: if ($conf["border"]) { $outputString = '<table border=1 bordercolor="'. ($conf["border."]["color"]?$conf["border."]["color"]:"teal"). '"><tr><td>'. $outputString. '</td></tr></table>'; } return $outputString; }}

?>(fileadmin/sem1_3_examples/test_library2.php)

Basically this will wrap the output string HELLO WORLD in a font face with the attributes provided through TypoScript andthereafter wrap a table around if a border is configured for.

It looks like this in the browser:

... and the HTML source is like this:

<table border=1 bordercolor="black"><tr><td><font color="red" face="verdana">HELLOWORLD</font></td></tr></table>

(Ugly, but you can enable the tidy-option in TYPO3_CONF_VARS if you like to format it better.)

Available APIThere are a number of functions and methods you can use from your own functions. Unfortunately the API still needs to befully documented. Although it should be done by the author, it is an area of contribution - should you ever learn the API byyourself.

TEMPLATES, TYPOSCRIPT & BEYOND - 32

IMPORTANT NOTICE regarding caching!When calling the function $TSFE->set_no_cache(), the document will not be cached after rendering.However it does not prevent the index_ts.php script from searching the cache for a stored version ofthe page before rendering. You'll need to set the no_cache parameter either for the page, in the URLor as a config-option in TypoScript (eg. config.no_cache=1) to avoid this.

The reason why this is important is, that if you have a parsing error in your included scripts, theset_no_cache() function is never called and so the page is cached. When you have found your errorand corrected it, trying again, you'll find that because the page was cached due to the parsing error,your script is not parsed (your functions are not called) before you clear the cache of the currentpage!

One central class in Typo3 - both frontend and backend - is the class t3lib_div. This class is never instantiated - it's merelya namespace for the functions in it. In the above script we see a single example of it's use, compiling a associative arrayinto a attribute list for inclusion in the <font>-tag:

$fontTagAttributes = t3lib_div::implodeParams($fontTagParams);

More API information follows later in this document.

Accessing data in TSFE and cObjLets say we wish to insert the current page title, this little change in line 11 of the PHP script will do the trick:

// Setting the output string: $outputString = $TSFE->page["title"];

... or alternatively, we can access the fields of the current page record from the calling cObj object, because it's internaldata array is currently loaded with the page record as well (at other times, eg. when rendering tt_content records, it holdsthe tt_content record instead of course!):

// Setting the output string: $outputString = $this->cObj->data["title"];

Generally you should peek inside the classes, tslib_fe (TSFE) and tslib_cObj (class.tslib_content.php) to see other internalvariables you can access in addition to the function available in general.

Making queries to Typo3 configured tablesTaking this one step further we could render a little menu of the current subpages. This brings up the topic of quering thedatabase:

Requirements to Typo3 managed tables:

• When configuring tables for use with Typo3 there are two fields which are at any time required, both 32 bit integers:uid and pid. uid must be automatically incremented and unique. pid points to the page id (uid of a page record) towhich the record belongs. This relationship must be consistent within a database in order for the data structure to beintact (integrity check in DBint).

• Optionally you can configure a certain field to indicate that the record should at ANY TIME be regarded as deleted. Thisis configured with [ctrl][deleted] in $TCA. If the table has such a flag - and most tables have by default - theappropriate WHERE-clause is returned by $TSFE->sys_page->deleteClause(“tablename”) (in the frontend only) andt3lib_BEfunc::deleteClause(“tablename”) (in the backend only).

• Optionally you can configure so called “enableFields”. These may include 1) a hidden field, 2) a starttime date, 3) aendtime date and 4) access for a specific website usergroup. This is configured in [ctrl][enablecolumns] in $TCA foreach table. These fields must be taken into consideration when displaying output in the frontend. In the backend theseproperties generally does not prevent the records from being displayed or edited - it's a strict frontend feature.However the record icon in the backend will be modified according to the status of the record.The enableFields are returned by a call to the function $TSFE->sys_page->enableFields(“tablename”). However forfrontend applications running in the scope of a calling cObj object it's more appropriate to call $this->cObj->enableFields(“tablename”) which basically does the same. Calling this function automatically returns a properappendix to a WHERE-clause and also takes into account if any usergroup, show hidden or time manipulation has beenset by the admin panel in the frontend. The result of the enableField function call also includes a clause for any “deleted” field configured.

This is the modifications we make to the script:

<?php

class user_test_library_class { var $cObj; // reference to the calling object. function test($content,$conf) { global $TSFE; $TSFE->set_no_cache(); // Turning caching off - good while developing. // Query to select pages for the menu $query = "SELECT uid,title FROM pages WHERE pid=".intval($TSFE->id). // Subpages to current page $this->cObj->enableFields("pages"). // Include conditions for enableFields " ORDER by sorting"; // Sort by the sorting field so the order is correct // debug($query); $res = mysql(TYPO3_db,$query); // Performing query $menuItems=array(); // Collect each element in this array while($row=mysql_fetch_assoc($res)) { $menuItems[]='<a href="?id='.$row["uid"].'">'.htmlspecialchars($row["title"]).'</a>'; } // Imploding the elements into the output string: $outputString = implode("<BR>",$menuItems); // If set, add the font-tag parameters to this associative array: $fontTagParams = array(); if ($conf["fontStyle."]["color"]) $fontTagParams["color"]=$conf["fontStyle."]["color"];

TEMPLATES, TYPOSCRIPT & BEYOND - 33

if ($conf["fontStyle."]["face"]) $fontTagParams["face"]=$conf["fontStyle."]["face"];

// If any font-tag parameters was set, wrap the output string: if (count($fontTagParams)) { // Compile array to attribute string $fontTagAttributes = t3lib_div::implodeParams($fontTagParams); $outputString = '<font '.$fontTagAttributes.'>'.$outputString.'</font>'; } // If border is enabled: if ($conf["border"]) { $outputString = '<table border=1 bordercolor="'. ($conf["border."]["color"]?$conf["border."]["color"]:"teal"). '"><tr><td>'. $outputString. '</td></tr></table>'; } return $outputString; }}

?>(fileadmin/sem1_3_examples/test_library3.php)

The output looks like this:

In the case above, the call to enableFields returns this SQL condition:

AND NOT pages.deleted AND NOT pages.hidden AND (pages.starttime<=1011726642) AND (pages.endtime=0 OR pages.endtime>1011726642) AND pages.fe_group IN (0,-1)

So the total query looks like:

SELECT uid,title FROM pages WHERE pid=1 AND NOT pages.deleted AND NOT pages.hidden AND(pages.starttime<=1011726642) AND (pages.endtime=0 OR pages.endtime>1011726642) AND pages.fe_group IN(0,-1) ORDER by sorting

(Try to un-comment line 15, the debug() function, and you'll see this!)

If you consistently apply the output from the enableFields function to your queries on Typo3 configured tables - includingyour own of course - you'll automatically enjoy the benefit of hiding, user access control to records and time-controlledpublishing!

TypoScript cObjects from within your PHP-classThe true power of this application lies not in this little example. However it demonstrates the concept:

We substitute the TypoScript used until now, creating a new custom defined property 'a_content_object'. Of course thisdoesn't do anything before we make it do something!

# Default PAGE object:page = PAGEpage.typeNum = 0includeLibs.test_library = fileadmin/test_library.phppage.10 = USERpage.10.userFunc = user_test_library_class->testpage.10.a_content_object = TEXTpage.10.a_content_object.value = HELLO WORLD!page.10.a_content_object.wrap = <B>|</B>

In our PHP-class we are now going to fetch the content of this content object:

<?php

TEMPLATES, TYPOSCRIPT & BEYOND - 34

class user_test_library_class { var $cObj; // reference to the calling object. function test($content,$conf) { global $TSFE; $TSFE->set_no_cache(); // Turning caching off - good while developing. $outputString = $this->cObj->cObjGetSingle( $conf["a_content_object"], // Contains the name, here "TEXT" $conf["a_content_object."], // Contains the properties of "TEXT" "a_content_object" // Basically just information for the TypoScript debugger );

return $outputString; }}

?>(fileadmin/sem1_3_examples/test_library4.php)

And the output on the frontend looks like this:

Amazing.

The great thing about this is not “HELLO WORLD” but rather the fact that we can actually render a TypoScript cObject -any content object available in TypoScript - and even manipulate the TypoScript array passed before the rendering begins.This opens great ways of mixing PHP with TypoScript so you get exactly the balance you like. Consider this example:

<?php

class user_test_library_class { var $cObj; // reference to the calling object. function test($content,$conf) { global $TSFE; $TSFE->set_no_cache(); // Turning caching off - good while developing. $conf["a_content_object."]["value"] = "The page title is: ".$TSFE->page["title"]; $conf["a_content_object."]["case"] = "lower"; $outputString = $this->cObj->cObjGetSingle( $conf["a_content_object"], // Contains the name, here "TEXT" $conf["a_content_object."], // Contains the properties of "TEXT" "a_content_object" // Basically just information for the TypoScript debugger );

return $outputString; }}

?>(fileadmin/sem1_3_examples/test_library5.php)

In this example an existing propery .value (“HELLO WORLD”) - coming from the TypoScript template - is overridden withdata like the page title. In addition a .case property (from stdWrap function) is added and set to “lower” - so it renders inlowercase:

The TypoScript debuggerNow would be a great time to demonstrate the TypoScript debugger. This is available from the adminPanel of the front-end, so we must enable the adminPanel in TypoScript:

config.admPanel = 1

... and this is now available in the front end:

TEMPLATES, TYPOSCRIPT & BEYOND - 35

Expand the panel and enter these settings in the TypoScript section:

As you can see, it becomes clear how the rendering process traverses the object tree of the “page” TLO, rendering theUSER cObject. You can see how long the parsing times has been in milliseconds and any errors will be displayed as well.Now try to check off “Display content”:

TEMPLATES, TYPOSCRIPT & BEYOND - 36

This shows how the content of the content object page.10.a_content_object is in fact the full content of the USER cObject.The USER cObject (page.10) happens to be the only content between the bodytags in turn. Great way to track the contentthrough the hierarchy. However as a warning, this gets complex when the information on a page is just a tiny bit largerthan this :-)

Anyways, the tool itself will soon prove to be useful.

ImagesA really brilliant way to mix PHP and TypoScript is when working with images. The key to success is to configure the wholeimage object in TypoScript and then override the file-name value with your own filename. That filename can come fromthe filename field of a custom table record or be from a fixed repository like fileadmin/. It doesn't matter. The point is,you've got the control.

In this case we start by uploading two images to the page record of the current page:

Then we define a very basic IMAGE cObject instead of the TEXT cObject, but leave out the filename:

# Default PAGE object:page = PAGEpage.typeNum = 0includeLibs.test_library = fileadmin/test_library.phppage.10 = USERpage.10.userFunc = user_test_library_class->testpage.10.a_content_object = IMAGEpage.10.a_content_object { file = ??? file.width = 100 params = hspace=10 vspace=10}config.admPanel = 1

If we look at the frontend this makes no sense so far. Of course not, we haven't defined a proper image file. But looking atthe adminPanel/TypoScript section, this is also told us by the debugger:

TEMPLATES, TYPOSCRIPT & BEYOND - 37

Back to our PHP-method, which is accordingly modified:

<?php

class user_test_library_class { var $cObj; // reference to the calling object. function test($content,$conf) { global $TSFE; $TSFE->set_no_cache(); // Turning caching off - good while developing.

// Setting the path of files attached for page records, media field $imagePath = "uploads/media/";

// exploding the list of files from the page record into this array $imageFilesArray = explode(",",$this->cObj->data["media"]);

$accum=array(); // Collecting the image code in this array reset($imageFilesArray); while(list(,$theImage)=each($imageFilesArray)) { // traverse the image list // Setting the file name, overriding ??? from TS: $conf["a_content_object."]["file"] = $imagePath.$theImage; // Getting the content object: $accum[] = $this->cObj->cObjGetSingle( $conf["a_content_object"], // Contains the name, here "TEXT" $conf["a_content_object."], // Contains the properties of "TEXT" "a_content_object" // Basically just information for the TypoScript debugger ); }

// implode the files produced separated by a horizontal ruler $outputString = implode("<HR>",$accum);

return $outputString; }}

?>(fileadmin/sem1_3_examples/test_library6.php)

The output in the browser looks like this:

Two images, 100 pixels wide each! HTML source is this (cleaned):

<img src="typo3temp/2e5ebbf50e.jpg" width="100" height="70" border="0" hspace=10 vspace=10><HR><img src="typo3temp/6519dd0253.jpg" width="100" height="67" border="0" hspace=10 vspace=10>

Talking about the TypoScript debugger, notice how we see two renderings of content objects. No surprise:

TEMPLATES, TYPOSCRIPT & BEYOND - 38

Click-enlarging imagesThis is pretty straight forward. It doesn't even require modifications in the PHP-code (notice that modifying PHP now seemslike a hindrance!):

Just add this TypoScript (the red lines) to the TS template record:

# Default PAGE object:page = PAGEpage.typeNum = 0includeLibs.test_library = fileadmin/test_library.phppage.10 = USERpage.10.userFunc = user_test_library_class->testpage.10.a_content_object = IMAGEpage.10.a_content_object { file = ??? file.width = 100 params = hspace=10 vspace=10 imageLinkWrap = 1 imageLinkWrap { bodyTag = <BODY bgColor=red> wrap = <A href="javascript:close();"> | </A> width = 200m height = 200m JSwindow = 1 JSwindow.expand = 17,20 enable=1 }}config.admPanel = 1

... and your images are instantly click-enlargable. It doesn't get any easier through PHP and as you see theconfigurationwise requirements are in fact minimal.

A final example to show would be a simple GIFBUILDER example:

This is the TypoScript involved:

TEMPLATES, TYPOSCRIPT & BEYOND - 39

# Default PAGE object:page = PAGEpage.typeNum = 0includeLibs.test_library = fileadmin/test_library.phppage.10 = USERpage.10.userFunc = user_test_library_class->testpage.10.a_content_object = IMAGEpage.10.a_content_object.file = GIFBUILDERpage.10.a_content_object.file { XY = [5.w],[5.h] 5 = IMAGE 5.file = ??? 5.file.width=100 7 = BOX 7.dimensions = 0,0,[10.w]+6,[10.h]+4 10 = TEXT 10.text = ??? 10.offset = 3,10 10.fontColor = white}config.admPanel = 1

This is the PHP-script:

<?php

class user_test_library_class { var $cObj; // reference to the calling object. function test($content,$conf) { global $TSFE; $TSFE->set_no_cache(); // Turning caching off - good while developing.

// Setting the path of files attached for page records, media field $imagePath = "uploads/media/";

// exploding the list of files from the page record into this array $imageFilesArray = explode(",",$this->cObj->data["media"]);

$accum=array(); // Collecting the image code in this array reset($imageFilesArray); while(list(,$theImage)=each($imageFilesArray)) { // traverse the image list // Setting the file name, overriding ??? from TS: $conf["a_content_object."]["file."]["5."]["file"] = $imagePath.$theImage;

// Printing the image filename on the image: $conf["a_content_object."]["file."]["10."]["text"] = $theImage; // Getting the content object: $accum[] = $this->cObj->cObjGetSingle( $conf["a_content_object"], // Contains the name, here "TEXT" $conf["a_content_object."], // Contains the properties of "TEXT" "a_content_object" // Basically just information for the TypoScript debugger ); }

// implode the files produced separated by a horizontal ruler $outputString = implode("<HR>",$accum);

return $outputString; }}

?>(Colored lines are changed, fileadmin/sem1_3_examples/test_library7.php)

... and finally this is the output in the webbrowser:

TEMPLATES, TYPOSCRIPT & BEYOND - 40

Webpage with content and a menu - revisitedFinally I would like to demonstrate a very PHP oriented approach to the simple webpage we made earlier throughTypoScript:

This time however I would like to implement it centered around PHP, include TypoScript as it may fit. First of all, this is myTypoScript:

# Default PAGE object:page = PAGEpage.typeNum = 0includeLibs.test_library = fileadmin/test_library.php# *****************************# Establishing the USER cObject:# *****************************page.10 = USERpage.10.userFunc = user_test_library_class->init# Setting up the HMENUpage.10.cObj_main_menu = HMENUpage.10.cObj_main_menu.1 = TMENUpage.10.cObj_main_menu.1.NO { linkWrap = <B>|</B><BR>}# Setting up the CONTENT cObjpage.10.cObj_normal_content < styles.content.get# Admin Panel:config.admPanel = 1

... and the PHP script:

<?php

class user_test_library_class { var $cObj; // reference to the calling object. var $conf; // Set to the TypoScript config array passed function init($content,$conf) { global $TSFE; $TSFE->set_no_cache(); // Turning caching off - good while developing.

TEMPLATES, TYPOSCRIPT & BEYOND - 41

// Setting internal variable $conf so TS is available from all methods $this->conf = $conf;

// Main body section rendered and returned return $this->getMainBody(); }

/** * Renders and returns the main body section */ function getMainBody() { ob_start(); // Start output buffering ?><TABLE border=1> <TR> <TD nowrap valign=top><?php echo $this->insertMenu();?></TD> <TD><IMG src=clear.gif width=10 height=1></TD> <TD><?php echo $this->insertContent();?></TD> </TR></TABLE> <?php $outputString = ob_get_contents(); ob_end_clean(); return $outputString; }

/** * Renders and returns main menu */ function insertMenu() { return $this->cObj->cObjGetSingle( $this->conf["cObj_main_menu"], $this->conf["cObj_main_menu."], "cObj_main_menu" ); }

/** * Renders and returns content for normal column */ function insertContent() { return $this->cObj->cObjGetSingle( $this->conf["cObj_normal_content"], $this->conf["cObj_normal_content."], "cObj_normal_content" ); }}

?>(fileadmin/sem1_3_examples/test_library8.php)

... and finally the output in the webbrowser:

TEMPLATES, TYPOSCRIPT & BEYOND - 42

Notice how that concept strived to combine traditional PHP programming with content rendered through TypoScriptcontrolled cObjects!

I believe the concept of mixing TypoScript and PHP code should be sufficiently demonstrated now. All that remains is tofind your own way of doing the things.

Ways to call your custom methodsThe previous examples doesn't leave much doubt how to call userdefined functions and methods. The scope here has beenstaticly inserted content objects in the main template. So this is clearly one way to apply dynamic content from PHPscripts.

However the main “content management principle” in Typo3 is the content elements; Individual pages are comprised ofcontent elements being rendered in the order they appear in the backend. And this principle of content elements is a greatvehicle for inserting custom content in an arbitrary way. For the host of semi-default elements (those optionally renderedby the static template series plugin.*) the “Insert module” content element has been reserved. This is pre-programmed toinvoke the function calls for the many plugins as they are included as static templates.

This example shows from the testsite how a forum element is inserted:

TEMPLATES, TYPOSCRIPT & BEYOND - 43

This element is in charge of inserting the board on this particular page. The board looks like this on the testsite:

TEMPLATES, TYPOSCRIPT & BEYOND - 44

TEMPLATES, TYPOSCRIPT & BEYOND - 45

Looking at the content element on the previous page we see that the type “Insert module” is chosen. If we dig down intothe $TCA array we find that the field “CType” of the tt_content table holds this value and that it is a string value (seeimage above to the left. This view is captured in the Tools>Configuration module).

The TypoScript default content rendering code reflects this in a beautiful manner. The TLO “tt_content” is by defaultinvoked when tt_content records are supposed to be rendered. This is in turn a CASE cObject this basically invokesanother cObject based on some condition, in this case the content of the field tt_content.CType.

If the CType field is “list”, then another CASE cObject is invoked at position 20 in the cObject array. In this step theTypoScript configuration is set to evaluate the value of the field tt_content.list_type. This is just an integer representingone of the “Module:” options from the content element on the previous page. The number “4” happens to be the value ofthe “Board” module. In fact yet a CASE cObject is rendered and based on the value of the field tt_content.layout, either alist style or tree style forum is inserted! And when that decision is made the control is... passed on to the TLOplugin.tt_board_tree for instance:

TEMPLATES, TYPOSCRIPT & BEYOND - 46

On this picture it is shown how the plugin.tt_board_list object is configured and this looks pretty familiar to us...

Imagine we would like to take over the control of the board with our own PHP-script. We could either substitute theplugin.tt_board_list USER cObject or break the connect from the point in the tt_content-branch. We'll try the latter:

Expand the object tree, click the node of the “Board” module type. Then select CLEAR object - even if you really don'twant to clear the object, it's a pretty quick way to get the correct reference to the object inserted in the template's Setupfield, which is the next thing we edit:

We stick to clearing the object, replacing it with a simple TEXT cObject. And as we see, we've now taken over control ofthe content element type “List” whenever the module “Board” is selected! Nothing keeps us from calling our customfunctions and methods now!

The “Script” content elementThe “Insert module” content element type is reserved for default modules or at least regularly used standard modules.However there is a content element type which by default does nothing - the “Script” type. This is totally in your hands toconfigure.

On the testsite you can see an example of how it's used in the “TypoScript Examples” section, namely on the page “Mixingcached and dynamic content”:

TEMPLATES, TYPOSCRIPT & BEYOND - 47

Looking in the template record reveals this basic TypoScript configuration:

page = PAGEpage.typeNum=0page.config.admPanel = 1includeLibs.something = media/scripts/example_callfunction.phppage.5 = TEXTpage.5.field = titlepage.5.wrap = <h3>|</h3>page.7 = TEXTpage.7.field = abstractpage.7.wrap = <i>|</i><HR>page.10 < styles.content.get

tt_content.script = CASEtt_content.script.key.field = select_keytt_content.script.dynamic_test = USER_INTtt_content.script.dynamic_test { userFunc = user_printTime}

(The static template “content (default)” is of course included as well)

Notice the lines in red; First and foremost the function library is included (line 4).

Then the tt_content.script cObject is defined as a CASE type object. The key value determining which cObject to furtherrender is fetched from the field tt_content.select_key. If the value of this field (normally labeled CODE) matches“dynamic_test” then the function user_printTime() is called.

Looking in the content element of the script type (see picture above) reveals that this is in fact the case:

Using the script element combined with a “triggervalue” in the code field is a great way to invoke your own content,placing it at any position among regular page content elements. In most situations however your “application” in thefunction call may require the whole page - in which case you simply refrain from placing other content elements on thepage as well.

Introduction to the APIThe documentation of the Typo3 API - both frontend and backend - is partly incomplete and eventually it should bedocumented through comments in the PHP-scripts which is then again parsed by some external program compiling acomplete documentation based on Javadoc style comments (see document Inside Typo3).

TEMPLATES, TYPOSCRIPT & BEYOND - 48

Until then you are welcome to explore the functions available. The best thing you can do anyway is to look how it's usedfrom all the 'external' scripts found in media/scripts/ folder. This is a list of the most prominent classes to look in:

• $TSFE global object (Frontend only). Offers a few functions but most importantly it holds a list of internal variableswhich you can read out, eg. the page id, type, current page record, fe_user, rootLine etc. Look inside the filetslib/class.tslib_fe.php

• tslib_cObj (class.tslib_content.php) is the heart of TypoScript and provides most of the API you should use from yourfrontend scripts. The most vital functions are demonstrated in this document.

• class t3lib_div, calling methods without instantiation. This class is basically a collection of general functions foruniversal purposes. Very few - if any - of these functions are specifically for Typo3 tasks. (Both Frontend and Backend)

• t3lib_BEfunc, a backend minded function library. Use it without instantiation (Backend only) like t3lib_div.

TEMPLATES, TYPOSCRIPT & BEYOND - 49

Cases

The Photo MarathonThe Photo marathon example on the testsite is a brilliant example of how to deploy custom code for rendering etc. Themanagement of uploads etc. is controlled by the fe_adminLib.inc class (see TSref). However the display of the images ishandled by a custom written PHP function inserted with the content element type “Script”:

Looking at the templates of the site, we can see that another template is included in the main template. This is a commonway of organizing TypoScript splitting related parts up into separate Template records. In this case all configuration relatedto the Photo Marathon specific features is contained herein:

Looking at the content of this template, we see how the 'tt_content.script' object is used exactly as previously suggested:

1641: [GLOBAL]1642: # ******************************************** 1643: # SETTING UP the Photo Marathon special TypoScript 1644: # ******************************************** 1645: 1646: # Include the Photo Marathon library 1647: includeLibs.photomarathon = fileadmin/photomarathon/pm_functions.php 1648: 1649: 1650: # ------------------------------------------------------------------...1651: # Setting up the SCRIPT content element type: 1652: # ------------------------------------------------------------------...

TEMPLATES, TYPOSCRIPT & BEYOND - 50

1653: 1654: tt_content.script = CASE 1655: tt_content.script.key.field = select_key 1656: 1657: 1658: 1659: 1660: # CODE: 'showPMI' - shows the content of a category 1661: tt_content.script.showPMI = USER 1662: tt_content.script.showPMI { 1663: userFunc = user_photomarathon->showMarathonCategory 1664: 1665: # IMAGE cObject configuration used to render the images. 1666: imageConf.file = [this value is set hardcoded in the function] 1667: imageConf.file { 1668: width = 200 1669: maxH = 400 1670: } 1671: imageConf.imageLinkWrap = 1 1672: imageConf.imageLinkWrap { 1673: bodyTag = <BODY bgColor=black topmargin=0 leftmargin=0 margi...1674: wrap = <A href="javascript:close();"> | </A&...1675: width = 500 1676: height = 500m 1677: JSwindow = 1 1678: JSwindow.newWindow = 1 1679: JSwindow.expand = 0,0 1680: enable = 1 1681: } 1682: } 1683: 1684: 1685: 1686: 1687: 1688: # CODE: 'showFORM' - shows the form for uploading images 1689: tt_content.script.showFORM = USER_INT 1690: tt_content.script.showFORM { 1691: userFunc = user_feAdmin->init 1692: includeLibs = media/scripts/fe_adminLib.inc 1693: templateFile = fileadmin/photomarathon/fe_admin_photomarathon.tmpl 1694: 1695: // Tables + Commands 1696: table=user_photomarathon 1697: defaultCmd=create 1698: clearCacheOfPages = 134,135,136 1699: debug=0 1700: 1701: delete=1 1702: infomail = 0 1703: 1704: // Info-mail setup 1705: setfixed=1 1706: setfixed.approve { 1707: hidden = 0 1708: _FIELDLIST = uid,pid,title 1709: } 1710: setfixed.DELETE = 1 1711: setfixed.DELETE._FIELDLIST = uid,pid,title 1712: 1713: 1714: 1715: 1716: // Creating: Allow to enter a new image. This record is hidden b...1717: create = 1 1718: create.preview = 1 1719: create.fields = title,description,images,photodate,hidden 1720: create.required = title,images 1721: create.overrideValues { 1722: hidden = 1 1723: } 1724: 1725: // Editing: The edited record is not hidden because the user can...1726: edit = 1 1727: edit.preview = 1 1728: edit.fields = title,description,photodate 1729: edit.required = title 1730: edit.menuLockPid = 1 1731: 1732: // Field parsing and evaluation error messages: 1733: parseValues { 1734: title = trim 1735: description = trim 1736: images = files [jpg; jpeg; gif][300] 1737: } 1738: 1739: // Set pid to current page of the form. Thus the records go into...

TEMPLATES, TYPOSCRIPT & BEYOND - 51

1740: pid = 1741: 1742: email.from = {$photomarathon.email} 1743: email.fromName = {$photomarathon.emailName} 1744: email.admin = {$photomarathon.email} 1745: email.field = fe_cruser_id 1746: 1747: 1748: // This is the cObject rendering the preview version of the phot...1749: // The rendering is done with a PHP user function from the user_...1750: cObjects.PREVIEWIMAGES = USER 1751: cObjects.PREVIEWIMAGES { 1752: userFunc = user_photomarathon->showPreviewImage 1753: imageConf.file = [this value is set hardcoded in the function] 1754: imageConf.file { 1755: width = 200 1756: maxH = 400 1757: } 1758: } 1759: 1760: // Similar to above, but this configuration renders the image a ...1761: cObjects.LISTIMAGES = USER 1762: cObjects.LISTIMAGES { 1763: userFunc = user_photomarathon->showPreviewImage 1764: imageConf.file = [this value is set hardcoded in the function] 1765: imageConf.file { 1766: width = 100 1767: maxH = 200 1768: } 1769: } 1770: 1771: // This simply outputs the class-name used in the a form dependi...1772: cObjects.CLASS = TEXT 1773: cObjects.CLASS.value = regform 1774: cObjects.CLASS.override = regformhidden 1775: cObjects.CLASS.override.if.isTrue.field = hidden 1776: } 1777: 1778: 1779: 1780: 1781: 1782: # CODE: 'showSETFIXED' - shows the form for uploading images 1783: tt_content.script.showSETFIXED = < tt_content.script.showFORM 1784: tt_content.script.showSETFIXED { 1785: infomail=0 1786: edit=0 1787: create=0 1788: delete=0 1789: }(listing from Template Analyzer, including linenumbers and comment, cropping lines)

CommentsLine 1647: including the function library.

1654,55: Setting up the framework for the “script” content element.

1661,1689,1783: showPMI, showFORM, showSETFIXED are all configured as valid CODE values in the script content element, each invoking it's own function. showPMI invokes a totally userdefined

function while showFORM and showSETFIXED invokes default functions from the fe_adminLib found in media/scripts/. In any case these features are regarded as specific for this site and thus inserted by the script element provided that the CODE field has a value that matches:

TEMPLATES, TYPOSCRIPT & BEYOND - 52

ParametersIn the case above (see image) the code “showPMI” is inserted. This invokes a call to the method showMarathonCategory()in class user_photomarathon (see line 1663). Looking inside this class(fileadmin/fileadmin/photomarathon/pm_functions.php) shows a function call to $this->cObj->processParams() whichapparently parsed the content of the bodytext field (with the script content element it's used for parameters) and returnsthe parameters in an associative array:

<?php

/** * This class has functions which is used to display the Photo Marathon example */class user_photomarathon { var $cObj; // This is set to a copy of the parent cObj calling this function!

/** * Renders the list of images in the marathon */ function showMarathonCategory($dummy, $conf) { // debug($parameterArray); // debug($conf);

// Getting parameters from the tt_content record 'Parameters' field: $parameterArray = $this->cObj->processParams($this->cObj->data["bodytext"]); // The parameter 'categoryPid' determines which folder to look up for the images.... $catPid = $parameterArray["categoryPid"]; $pageRec = $GLOBALS["TSFE"]->sys_page->getRawRecord("pages",$catPid); if ($pageRec["pid"]==137) { // Page is ok! // Prepare image configuration for the IMAGE cObject in "imageConf." AND ... $imgConf = $conf["imageConf."]; unset($imgConf["file."]["import"]); // Unset if anything set from TS unset($imgConf["file."]["import."]); // Unset if anything set from TS $imgConf["file"] = ""; // Set empty for now...

// Print header: $content.='<h2>Category "'.$pageRec["title"].'"</h2>'; // Select the records:[... etc. ]

The Photomarathon application on the testsite is a great application to study. It includes both frontend implementations, acustom table and a backend module, so it's a great base to learn from!

The Seminar paper “Extending Typo3...” discusses the backend module and custom table use of the Photo Marathonapplication in details.

The Humidor camTEMPLATES, TYPOSCRIPT & BEYOND - 53

The Humidor-cam is a part of the Typo3.com website. Here people can buy cigars which is said to empower the Typo3development. The humidor-cam shows the contents of the humidor (a special container for cigars). This “cam”-image isnot a live picture but rather automatically manipulated - it does of course not show an open Humidor anywhere. But theimage is not evenmanipulated in Photoshop - it's in fact manipulated on-the-fly (although cached) by the powerfulTypoScript graphics function, calculated by a custom PHP function. The data defining the content comes from a userdefined database table. The contents here is connected to the shopping system at another server (courtesy of Inter-Photo.dk).

Thus this case included two challenges: To communicate the purchased amount of cigars from one server to the other andcreating the humidor image based on the database content of purchased cigars.

The whole thing took 6 hours to make one saturday night.

This is a screen shot from the cigar shopping basket. You can pay with VISA, Mastercard, Diners og Dankort:

TEMPLATES, TYPOSCRIPT & BEYOND - 54

Part I: The server-to-server communicationAt the Typo3 server with the shopping solution (which is not the same as the www.typo3.com server), this was added tothe TypoScript template:

includeLibs.ts_products_cigars = fileadmin/cigarLib.incplugin.tt_products.externalFinalizing= USERplugin.tt_products.externalFinalizing{ userFunc = user_cigars->addCigars}

This will invoke a method addCigars() from the class user_cigars when the order is finalized and the basket is beingemptied! This function must then communicate the contents of the basket to the typo3.com server. This is done with aPHP-function like this:

<?

class user_cigars { function addCigars($content,$conf) { // Initializes the basket from the session data $this->cObj->regObj->initBasket($GLOBALS["TSFE"]->fe_user->getKey("ses","recs")); // Internal accumulation in this array: $basket =array(); if (is_array($this->cObj->regObj->calculatedBasket)) { reset($this->cObj->regObj->calculatedBasket); while(list(,$r)=each($this->cObj->regObj->calculatedBasket)) { $basket[$r["rec"]["uid"]] = $r["count"]; } }

// Setting the URL to fetch. // The basket content is send very simply as a serialized array in a URL parameter! $url = "http://www.typo3.com/[remote script].php?dat=". rawurlencode(serialize(array($basket,$this->cObj->regObj->personInfo)));

// Opens the URL (which in turn passes the content of $basket to the remote script if($fd = @fopen($url,"r")) { while (!feof($fd)) { fread($fd, 5000); } fclose($fd); } }

}

?>

The remote script (below) at typo3.com unserializes the incoming $dat-array and subsequently updates the local databasewith the purchased cigars:

<?php

// Initializingerror_reporting (E_ALL ^ E_NOTICE);

// Including most needed class, including configurationinclude ("t3lib/class.t3lib_div.php");include ("typo3conf/localconf.php");

// Connecting to MySQLmysql_pconnect($typo_db_host, $typo_db_username, $typo_db_password); // Unserializing incoming data$dat = unserialize(t3lib_div::GPvar("dat"));

// If this data was valid, insert the elements in the user_cigars tableif (is_array($dat[0])) { reset($dat[0]); while(list($pUid,$count)=each($dat[0])) { $count = t3lib_div::intInRange($count,0,10); for ($a=0;$a<$count;$a++) { $query = " INSERT INTO user_cigars (tstamp,item,donation_by,smoked,removed) VALUES (".time().",'".addslashes($pUid)."','".addslashes($dat[1]["name"])."',0,0)"; $res = mysql($typo_db,$query); } }}

// Clearing the page cache of the page.

TEMPLATES, TYPOSCRIPT & BEYOND - 55

$query = "DELETE FROM cache_pages WHERE page_id=1208";$res = mysql($typo_db,$query);

?>

Part II: Rendering the Humidor imageAs soon as the purchased cigars is transferred to the database at typo3.com, the cache is cleared for the page with theHumidor-cam (page id 1208, see PHP code listing above).

When the Humidor-page is viewed again, a classic “Script” type content element is configured to render the box contents.This is the TypoScript included to make this happen:

content.includeLibs.humidor_php = fileadmin/cigars/humidor.phptt_content.script= CASEtt_content.script.key.field = select_keytt_content.script.humidor = USERtt_content.script.humidor { userFunc = user_humidor->makeHumidor GB { XY = [5.w],[5.h] 5 = IMAGE 5.file = fileadmin/cigars/humidor_l.jpg

999 = WORKAREA 999.clear = 1000 = IMAGE 1000.file = fileadmin/cigars/overlay.jpg 1000.mask = fileadmin/cigars/overlay_mask.jpg }

image.file = [set from php] image.file.ext = jpg}

Notice how certain TypoScript values are set to aid the PHP-function. The GB-object above is being further manipulated inthe humidor-class in fileadmin/cigars/humidor.php.

This is the PHP-class from fileadmin/cigars/humidor.php:

<?

class user_humidor { function makeHumidor($content,$conf) { // begin: $offsetBegin=explode(",","21,91"); // end: $offsetEnd=explode(",","255,71"); // steps: $steps = intval(sqrt(pow($offsetEnd[0]-$offsetBegin[0],2)+pow($offsetBegin[1]-$offsetEnd[1],2)));

// debug($steps);// rotation: 1 -> -13

$mainCigars = array( "233" => array("mob01",19,0,"My Own Blend, Robusto"), "229" => array("mob02",16,0,"My Own Blend, Churchill"), "234" => array("cohiba1",22,0,"COHIBA, Robusto"), "232" => array("arturo1",16,0,"Arturo Fuente, Corona Grande"), "231" => array("arturo2",20,0,"Arturo Fuente, Robusto"), "230" => array("punch1",19,0,"Punch, Churchill") );

$cigars=array(); $tableOutput=array();

$tableOutput[]='<tr bgColor="#ACBCC5"> <td><font face=verdana size=2><strong>Cigar:</strong></font></td> <td nowrap><font face=verdana size=2><strong>Donated by:</strong></font></td> <td><font face=verdana size=2><strong>Smoked?</strong></font></td> <td><font face=verdana size=2></font></td> </tr>';

$query = "SELECT * FROM user_cigars WHERE removed=0 ORDER BY tstamp LIMIT 30";

TEMPLATES, TYPOSCRIPT & BEYOND - 56

$res = mysql(TYPO3_db,$query); while($row=mysql_fetch_assoc($res)) { if (isset($mainCigars[$row["item"]])) { $cRow = $mainCigars[$row["item"]]; if ($row["smoked"]) { $cRow[2]=1; } $cigars[] = $cRow; $tableOutput[]='<tr bgColor="#D2D8DB"> <td nowrap><font face=verdana size=1>'.$cRow[3].'</font></td> <td nowrap><font face=verdana size=1>'.t3lib_div::fixed_lgd($row["donation_by"],25).'</font></td> <td nowrap><font face=verdana size=1>'.($row["smoked"]?"Yes":"No, not yet").'</font></td> <td><font face=verdana size=1><em>'.$row["comment"].'</em></font></td> </tr>'; } }

$a=99; $xOff=0; $yOff=0; $totalWidth=0; reset($cigars); while(list(,$cigarArr)=each($cigars)) { if ($totalWidth>$steps) { $xOff=0; $yOff=0; $totalWidth=0; $offsetBegin[0]+=10; $offsetEnd[0]+=10; $offsetBegin[1]-=4; $offsetEnd[1]-=4; }

// calculate the width of the rotated image: $rotation = -(t3lib_div::intInRange(floor($totalWidth/$steps*14),0,14)-1); $imgH = 194; $imgW = 100; $width = cos(2*pi()/360*abs($rotation))*$imgW + sin(2*pi()/360*abs($rotation))*$imgH; $height = cos(2*pi()/360*abs($rotation))*$imgH + sin(2*pi()/360*abs($rotation))*$imgW; $centerBoxW = 63; $centerBoxH = 170;

if (!$cigarArr[2]) { $a++; $conf["GB."][$a]="WORKAREA"; $conf["GB."][$a."."]["set"]= ($offsetBegin[0]+$xOff).",".($offsetBegin[1]+$yOff). ",".$centerBoxW.",".$centerBoxH; $a++; $conf["GB."][$a]="IMAGE"; $conf["GB."][$a."."]["offset"]= intval(-($width-$centerBoxW)/2).",". intval(-($height-$centerBoxH)/2); $conf["GB."][$a."."]["file"]="fileadmin/cigars/".$cigarArr[0].".jpg"; $conf["GB."][$a."."]["file."]["params"]="-rotate ".$rotation; $conf["GB."][$a."."]["mask"]="fileadmin/cigars/".$cigarArr[0]."_mask.jpg"; $conf["GB."][$a."."]["mask."]["params"]="-rotate ".$rotation; } $totalWidth +=$cigarArr[1]+1; $xOff = intval(($offsetEnd[0]-$offsetBegin[0])*$totalWidth/$steps); $yOff = intval(($offsetEnd[1]-$offsetBegin[1])*$totalWidth/$steps); } $picArr = $this->cObj->getImgResource("GIFBUILDER",$conf["GB."]); $conf["image."]["file"] = $picArr[3]; $content = $this->cObj->IMAGE($conf["image."]); $content.='<BR><BR><font face=verdana size=3><strong>Humidor contents:</strong></font><BR>'; $content.='<table border=0 cellpadding=2 cellspacing=2>'.implode("",$tableOutput).'</table>'; // debug($conf["GB."]);// debug($width."x".$height);// debug($content); return $content; }}

?>

This is a quite comprehensive script. This is a brief overview of what happens in the PHP script above:

TEMPLATES, TYPOSCRIPT & BEYOND - 57

1. The cigars are available as a photo-file and a mask-file each. The cigars must be overlaid on the image through themask. This is a few of the masks and cigar-images:

2. The image of the humidor is seen from a perspective. We cannot manipulate perspective into the cigars. But tocompensate we rotate the cigars more and more the closer they come to the right end of the box. The rotation isbetween -1 and 13 degrees at each end respectively.

3. In addition the cigars must be laid down following a slanted line. Furthermore the space between each cigar must be

calculated based on their individual width.

4. A new row of cigars must be started if the bottom row is full.

5. Cigars which are marked “smoked” are not rendered, however space are reserved for them so it looks like they aregone from the box.

6. When all cigars are laid down in the box, we superimpose the bottom part of the humidor in order to make sure thatthe long cigars does not exceed the box-boundaries. And finally the shadow casted from the right must be reflected onthe cigars. This is done by a black overlay with an opacity:

TEMPLATES, TYPOSCRIPT & BEYOND - 58