diff options
| author | mickymax <> | 2007-02-18 18:08:46 +0000 | 
|---|---|---|
| committer | mickymax <> | 2007-02-18 18:08:46 +0000 | 
| commit | db1a9b1af8aee6d5967e96d3f8928d1576c45daf (patch) | |
| tree | 7ca238721e6e44a6f33c714c7a73df746bff2d36 | |
| parent | 988ae03c5f5f035a9aeeb411285a3a6aaa4e73b4 (diff) | |
"Traduction francaise"
| -rw-r--r-- | .gitattributes | 6 | ||||
| -rw-r--r-- | demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page | 757 | ||||
| -rw-r--r-- | demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page | 405 | ||||
| -rw-r--r-- | demos/quickstart/protected/pages/Tutorial/fr/chat1.png | bin | 0 -> 10533 bytes | |||
| -rw-r--r-- | demos/quickstart/protected/pages/Tutorial/fr/chat2.png | bin | 0 -> 13409 bytes | |||
| -rw-r--r-- | demos/quickstart/protected/pages/Tutorial/fr/example1.png | bin | 0 -> 11139 bytes | |||
| -rw-r--r-- | demos/quickstart/protected/pages/Tutorial/fr/example2.png | bin | 0 -> 13842 bytes | 
7 files changed, 1168 insertions, 0 deletions
| diff --git a/.gitattributes b/.gitattributes index a2ce04e5..833d83e4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1217,6 +1217,12 @@ demos/quickstart/protected/pages/Tutorial/chat1.png -text  demos/quickstart/protected/pages/Tutorial/chat2.png -text  demos/quickstart/protected/pages/Tutorial/example1.png -text  demos/quickstart/protected/pages/Tutorial/example2.png -text +demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page -text +demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page -text +demos/quickstart/protected/pages/Tutorial/fr/chat1.png -text +demos/quickstart/protected/pages/Tutorial/fr/chat2.png -text +demos/quickstart/protected/pages/Tutorial/fr/example1.png -text +demos/quickstart/protected/pages/Tutorial/fr/example2.png -text  demos/quickstart/protected/pages/ViewSource.page -text  demos/quickstart/protected/pages/ViewSource.php -text  demos/quickstart/protected/pages/config.xml -text diff --git a/demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page b/demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page new file mode 100644 index 00000000..fc1997c8 --- /dev/null +++ b/demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page @@ -0,0 +1,757 @@ +<com:TContent ID="body"> +	<h1 id="18008">Building an AJAX Chat Application</h1> +	<p id="90081" class="block-content">This tutorial introduces the Prado web application framework's +	<a href="?page=Database.ActiveRecord">ActiveRecord</a> +	and <a href="?page=ActiveControls.Home">Active Controls</a> to build a Chat +	web application. It is assumed that you +	are familiar with PHP and you have access to a web server that is able to serve PHP5 scripts. +	This basic chat application will utilize the following ideas/components in Prado. +	</p> +	<ul id="u1" class="block-content"> +		<li>Building a custom User Manager class.</li> +		<li>Authenticating and adding a new user to the database.</li> +		<li>Using ActiveRecord to interact with the database.</li> +		<li>Using Active Controls and callbacks to implement the user interface.</li> +		<li>Separating application logic and application flow.</li> +	</ul> + +	<p id="90082" class="block-content">In this tutorial you will build an AJAX Chat web application that allows +		multiple users to communicate through their web browser. +		The application consists of two pages: a login page +		that asks the user to enter their nickname and the main application chat +		page. +		You can try the application <a href="../chat/index.php">locally</a> or at +		<a href="http://www.pradosoft.com/demos/chat/">Pradosoft.com</a>. +		The main application chat page is shown bellow. +		<img src=<%~ chat1.png %> class="figure" /> +	</p> + +	<h1 id="18009">Download, Install and Create a New Application</h1> +	<p id="90083" class="block-content">The download and installation steps are similar to those in +	the <a href="?page=Tutorial.CurrencyConverter#download">Currency converter tutorial</a>. +	To create the application, we run from the command line the following. +	See the <a href="?page=GettingStarted.CommandLine">Command Line Tool</a> +		for more details. +<com:TTextHighlighter Language="text" CssClass="source block-content" id="code_90027"> +php prado/framework/prado-cli.php -c chat +</com:TTextHighlighter> +	</p> + +	<p id="90084" class="block-content">The above command creates the necessary directory structure and minimal +		files (including "index.php" and "Home.page") to run a Prado  web application. +		Now you can point your browser's URL to the web server to serve up +		the <tt>index.php</tt> script in the <tt>chat</tt> directory. +		You should see the message "Welcome to Prado!" +	</p> + +	<h1 id="18010">Authentication and Authorization</h1> +	<p id="90085" class="block-content">The first task for this application is to ensure that each user +	of the chat application is assigned with a unique (chosen by the user) +	username. To achieve this, we can secure the main chat application +	page to deny access to anonymous users. First, let us create the <tt>Login</tt> +	page with the following code. We save the <tt>Login.php</tt> and <tt>Login.page</tt> +	in the <tt>chat/protected/pages/</tt> directory (there should be a <tt>Home.page</tt> +	file created by the command line tool). +	</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90028"> +<?php +class Login extends TPage +{ +} +?> +</com:TTextHighlighter> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code_90029"> +<!doctype html public "-//W3C//DTD XHTML 1.0 Strict//EN" +    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +    <title>Prado Chat Demo Login</title> +</head> +<body> +<com:TForm> +    <h1 class="login">Prado Chat Demo Login</h1> +    <fieldset class="login"> +        <legend>Please enter your name:</legend> +        <div class="username"> +            <com:TLabel ForControl="username" Text="Username:" /> +            <com:TTextBox ID="username" MaxLength="20" /> +            <com:TRequiredFieldValidator +                ControlToValidate="username" +                Display="Dynamic" +                ErrorMessage="Please provide a username." /> +        </div> +        <div class="login-button"> +            <com:TButton Text="Login" /> +        </div> +</com:TForm> +</body> +</html> +</com:TTextHighlighter> +	<p id="90086" class="block-content">The login page contains +	a <com:DocLink ClassPath="System.Web.UI.TForm" Text="TForm" />, +	a <com:DocLink ClassPath="System.Web.UI.WebControls.TTextBox" Text="TTextBox" />, +	a <com:DocLink ClassPath="System.Web.UI.WebControls.TRequiredFieldValidator" Text="TRequiredFieldValidator" /> +	and a <com:DocLink ClassPath="System.Web.UI.WebControls.TButton" Text="TButton" />. The resulting +	page looks like the following (after applying some a style sheet). +	<img src=<%~ chat2.png %> class="figure" /> +	If you click on the <tt>Login</tt> button without entering any +	text in the username textbox, an error message is displayed. This is +	due to the <com:DocLink ClassPath="System.Web.UI.WebControls.TRequiredFieldValidator" Text="TRequiredFieldValidator" /> +	requiring the user to enter some text in the textbox before proceeding. +	</p> +<h2 id="18019">Securing the <tt>Home</tt> page</h2> +<p id="90087" class="block-content">Now we wish that if the user is trying to access the main application +page, <tt>Home.page</tt>, before they have logged in, the user is presented with +the <tt>Login.page</tt> first. We add a <tt>chat/protected/application.xml</tt> configuration +file to import some classes that we shall use later. +<com:TTextHighlighter Language="xml" CssClass="source block-content" id="code_90030"> +<?xml version="1.0" encoding="utf-8"?> +<application id="Chat" Mode="Debug"> +  <paths> +    <using namespace="System.Data.*" /> +    <using namespace="System.Data.ActiveRecord.*" /> +    <using namespace="System.Security.*" /> +    <using namespace="System.Web.UI.ActiveControls.*" /> +  </paths> +</application> +</com:TTextHighlighter> +Next, we add a <tt>chat/protected/pages/config.xml</tt> configuration file to +secure the <tt>pages</tt> directory. +<com:TTextHighlighter Language="xml" CssClass="source block-content" id="code_90031"> +<?xml version="1.0" encoding="utf-8"?> +<configuration> +  <modules> +    <module id="users" class="TUserManager" /> +    <module id="auth" class="TAuthManager" UserManager="users" LoginPage="Login" /> +  </modules> +  <authorization> +    <allow pages="Login" users="?" /> +    <allow roles="normal" /> +    <deny users="*" /> +  </authorization> +</configuration> +</com:TTextHighlighter> +We setup the authentication using the default classes as explained in the +<a href="?page=Advanced.Auth">authentication/authorization quickstart</a>. +In the authorization definition, we allow anonymous users to access the +<tt>Login</tt> page (anonymous users is specified by the <tt>?</tt> question mark). +We allow any users with role equal to "normal" (to be defined later) +to access all the pages, that is, the <tt>Login</tt> and <tt>Home</tt> pages. +Lastly, we deny all users without any roles to access any page. The authorization +rules are executed on first match basis. +</p> + +<p id="90088" class="block-content">If you now try to access the <tt>Home</tt> page by pointing your browser +to the <tt>index.php</tt> you will be redirected to the <tt>Login</tt> page. +</p> + +<h1 id="18011">Active Record for <tt>chat_users</tt> table</h1> +<p id="90089" class="block-content">The <com:DocLink ClassPath="System.Secutity.TUserManager" Text="TUserManager" /> +class only provides a read-only list of users. We need to be able to add or +login new users dynamically. So we need to create our own user manager class. +First, we shall setup a database with a <tt>chat_users</tt> table and create an ActiveRecord +that can work with the <tt>chat_users</tt> table with ease. For the demo, we +use <tt>sqlite</tt> as our database for ease of distributing the demo. The demo +can be extended to use other databases such as MySQL or Postgres SQL easily. +We define the <tt>chat_users</tt> table as follows. +<com:TTextHighlighter Language="text" CssClass="source block-content" id="code_90032"> +CREATE TABLE chat_users +( +	username VARCHAR(20) NOT NULL PRIMARY KEY, +	last_activity INTEGER NOT NULL DEFAULT "0" +); +</com:TTextHighlighter> +Next we define the corresponding <tt>ChatUserRecord</tt> class and save it as +<tt>chat/protected/App_Code/ChatUserRecord.php</tt> (you need to create the +<tt>App_Code</tt> directory as well). We also save the sqlite database file +as <tt>App_Code/chat.db</tt>. +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90033"> +class ChatUserRecord extends TActiveRecord +{ +    public $username; +    public $last_activity; + +    public static $_tablename='chat_users'; + +    public static function finder() +    { +        return parent::getRecordFinder('ChatUserRecord'); +    } +} +</com:TTextHighlighter> +Before using the <tt>ChatUserRecord</tt> class we to configure a default +database connection for ActiveRecord to function. In the <tt>chat/protected/application.xml</tt> +we import classes from the <tt>App_Code</tt> directory and add an +<a href="?page=Database.ActiveRecord">ActiveRecord configuration module</a>. +<com:TTextHighlighter Language="xml" CssClass="source block-content" id="code_90034"> +<?xml version="1.0" encoding="utf-8"?> +<application id="Chat" Mode="Debug"> +  <paths> +    <using namespace="Application.App_Code.*" /> +    <using namespace="System.Data.*" /> +    <using namespace="System.Data.ActiveRecord.*" /> +    <using namespace="System.Security.*" /> +    <using namespace="System.Web.UI.ActiveControls.*" /> +  </paths> +  <modules> +    <module class="TActiveRecordConfig" EnableCache="true" +        Database.ConnectionString="sqlite:protected/App_Code/chat.db" /> +  </modules> +</application> +</com:TTextHighlighter> +</p> + +<h2 id="18020">Custom User Manager class</h2> +<p id="90090" class="block-content">To implement a custom user manager module class we just need +to extends the <tt>TModule</tt> class and implement the <tt>IUserManager</tt> +interface. The <tt>getGuestName()</tt>, <tt>getUser()</tt> and <tt>validateUser()</tt> +methods are required by the <tt>IUserManager</tt> interface. +We save the custom user manager class as <tt>App_Code/ChatUserManager.php</tt>. +</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90035"> +class ChatUserManager extends TModule implements IUserManager +{ +    public function getGuestName() +    { +        return 'Guest'; +    } + +    public function getUser($username=null) +    { +        $user=new TUser($this); +        $user->setIsGuest(true); +        if($username !== null && $this->usernameExists($username)) +        { +            $user->setIsGuest(false); +            $user->setName($username); +            $user->setRoles(array('normal')); +        } +        return $user; +    } + +    public function addNewUser($username) +    { +        $user = new ChatUserRecord(); +        $user->username = $username; +        $user->save(); +    } + +    public function usernameExists($username) +    { +        $finder = ChatUserRecord::finder(); +        $record = $finder->findByUsername($username); +        return $record instanceof ChatUserRecord; +    } + +    public function validateUser($username,$password) +    { +        return $this->usernameExists($username); +    } +} +</com:TTextHighlighter> +<p id="90091" class="block-content"> +The <tt>getGuestName()</tt> +method simply returns the name for a guest user and is not used in our application. +The <tt>getUser()</tt> method returns a <tt>TUser</tt> object if the username +exists in the database, the <tt>TUser</tt> object is set with role of "normal" +that corresponds to the <tt><authorization></tt> rules defined in our +<tt>config.xml</tt> file. </p> + +<p id="90092" class="block-content">The <tt>addNewUser()</tt> and <tt>usernameExists()</tt> +method uses the ActiveRecord corresponding to the <tt>chat_users</tt> table to +add a new user and to check if a username already exists, respectively. +</p> + +<p id="90093" class="block-content">The next thing to do is change the <tt>config.xml</tt> configuration to use +our new custom user manager class. We simply change the <tt><module></tt> +configuration with <tt>id="users"</tt>.</p> +<com:TTextHighlighter Language="xml" CssClass="source block-content" id="code_90036"> +<module id="users" class="ChatUserManager" /> +</com:TTextHighlighter> + +<h1 id="18012">Authentication</h1> +<p id="90094" class="block-content">To perform authentication, we just want the user to enter a unique +username. We add a +<com:DocLink ClassPath="System.Web.UI.WebControls.TCustomValidator" Text="TCustomValidator" /> +for validate the uniqueness of the username and add an <tt>OnClick</tt> event handler +for the login button.</p> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code_90037"> +<com:TCustomValidator +    ControlToValidate="username" +    Display="Dynamic" +    OnServerValidate="checkUsername" +    ErrorMessage="The username is already taken." /> + +... + +<com:TButton Text="Login" OnClick="createNewUser" /> +</com:TTextHighlighter> +In the <tt>Login.php</tt> file, we add the following 2 methods. +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90038"> +function checkUsername($sender, $param) +{ +    $manager = $this->Application->Modules['users']; +    if($manager->usernameExists($this->username->Text)) +        $param->IsValid = false; +} + +function createNewUser($sender, $param) +{ +    if($this->Page->IsValid) +    { +        $manager = $this->Application->Modules['users']; +        $manager->addNewUser($this->username->Text); + +        //do manual login +        $user = $manager->getUser($this->username->Text); +        $auth = $this->Application->Modules['auth']; +        $auth->updateSessionUser($user); +        $this->Application->User = $user; + +        $url = $this->Service->constructUrl($this->Service->DefaultPage); +        $this->Response->redirect($url); +    } +} +</com:TTextHighlighter> +The <tt>checkUserName()</tt> method uses the <tt>ChatUserManager</tt> class +(recall that in the <tt>config.xml</tt> configuration we set the +ID of the custom user manager class as "users") to validate the username +is not taken. +</p> +<p id="90095" class="block-content"> +In the <tt>createNewUser</tt> method, when the validation passes (that is, +when the user name is not taken) we add a new user. Afterward we perform +a manual login process:</p> +<ul id="u2" class="block-content"> +	<li>First we obtain a <tt>TUser</tt> instance from +our custom user manager class using the <tt>$manager->getUser(...)</tt> method.</li> +	<li>Using the <tt>TAuthManager</tt> we set/update the user object in the +	current session data.</li> +	<li>Then we set/update the <tt>Application</tt>'s user instance with our +	new user object.</li> +</ul> +</p> +<p id="finally" class="block-content"> +Finally, we redirect the client to the default <tt>Home</tt> page. +</p> + +<h2 id="18021">Default Values for ActiveRecord</h2> +<p id="90096" class="block-content">If you try to perform a login now, you will receive an error message like +"<i>Property '<tt>ChatUserRecord::$last_activity</tt>' must not be null as defined +by column '<tt>last_activity</tt>' in table '<tt>chat_users</tt>'.</i>". This means that the <tt>$last_activity</tt> +property value was null when we tried to insert a new record. We need to either +define a default value in the corresponding column in the table and allow null values or set the default +value in the <tt>ChatUserRecord</tt> class. We shall demonstrate the later by +altering the <tt>ChatUserRecord</tt> with the addition of a set getter/setter +methods for the <tt>last_activity</tt> property. + +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90039"> +private $_last_activity; + +public function getLast_Activity() +{ +    if($this->_last_activity === null) +        $this->_last_activity = time(); +    return $this->_last_activity; +} + +public function setLast_Activity($value) +{ +    $this->_last_activity = $value; +} +</com:TTextHighlighter> +Notice that we renamed <tt>$last_activity</tt> to <tt>$_last_activity</tt> (note +the underscore after the dollar sign). +</p> + +<h1 id="18013">Main Chat Application</h1> +<p id="90097" class="block-content">Now we are ready to build the main chat application. We use a simple +layout that consist of one panel holding the chat messages, one panel +to hold the users list, a textarea for the user to enter the text message +and a button to send the message. +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code_90040"> +<!doctype html public "-//W3C//DTD XHTML 1.0 Strict//EN" +    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +    <title>Prado Chat Demo</title> +<style> +.messages +{ +    width: 500px; +    height: 300px; +    float: left; +    border: 1px solid ButtonFace; +    overflow: auto; +} +.user-list +{ +    margin-left: 2px; +    float: left; +    width: 180px; +    height: 300px; +    border: 1px solid ButtonFace; +    overflow: auto; +    font-size: 0.85em; +} +.message-input +{ +    float: left; +} + +.message-input textarea +{ +    margin-top: 3px; +    padding: 0.4em 0.2em; +    width: 493px; +    font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; +    font-size: 0.85em; +    height: 40px; +} +.send-button +{ +    margin: 0.5em; +} +</style> +</head> +<body> +<com:TForm> +<h1 id="18014">Prado Chat Demo</h1> +<div id="messages" class="messages"> +    <com:TPlaceHolder ID="messageList" /> +</div> +<div id="users" class="user-list"> +    <com:TPlaceHolder ID="userList" /> +</div> +<div class="message-input"> +    <com:TActiveTextBox ID="userinput" +        Columns="40" Rows="2" TextMode="MultiLine" /> +    <com:TActiveButton ID="sendButton" CssClass="send-button" +        Text="Send" /> +</div> +</com:TForm> +<com:TJavascriptLogger /> +</body> +</html> +</com:TTextHighlighter> +We added two Active Control components: a +<com:DocLink ClassPath="System.Web.UI.ActiveControls.TActiveTextBox" Text="TActiveTextBox" /> +and a +<com:DocLink ClassPath="System.Web.UI.ActiveControls.TActiveButton" Text="TActiveButton" />. +We also added a +<com:DocLink ClassPath="System.Web.UI.WebControls.TJavascriptLogger" Text="TJavascriptLogger" /> +that will be very useful for understanding how the Active Controls work. +</p> + +<h2 id="18022">Exploring the Active Controls</h2> +<p id="90098" class="block-content">We should have some fun before we proceeding with setting up the chat buffering. We want +to see how we can update the current page when we receive a message. First, we add +an <tt>OnClick</tt> event handler for the <tt>Send</tt> button. + +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code_90041"> +<com:TActiveButton ID="sendButton" CssClass="send-button" +	Text="Send" OnClick="processMessage"/> +</com:TTextHighlighter> +And the corresponding event handler method in the <tt>Home.php</tt> class (we +need to create this new file too). +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90042"> +class Home extends TPage +{ +    function processMessage($sender, $param) +    { +        echo $this->userinput->Text; +    } +} +</com:TTextHighlighter> +If you now type something in the main application textbox and click the send button +you should see whatever you have typed echoed in the <tt>TJavascriptLogger</tt> console. +</p> + +<p id="90099" class="block-content">To append or add some content to the message list panel, we need to use +some methods in the +<com:DocLink ClassPath="System.Web.UI.ActiveControls.TCallbackClientScript" Text="TCallbackClientScript" /> +class which is available through the <tt>CallbackClient</tt> property of the +current <tt>TPage</tt> object. For example, we do can do +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90043"> +function processMessage($sender, $param) +{ +    $this->CallbackClient->appendContent("messages", $this->userinput->Text); +} +</com:TTextHighlighter> +This is one way to update some part of the existing page during a callback (AJAX style events) +and will be the primary way we will use to implement the chat application. +</p> + +<h1 id="18015">Active Record for <tt>chat_buffer</tt> table</h1> +<p id="90100" class="block-content">To send a message to all the connected users we need to buffer or store +the message for each user. We can use the database to buffer the messages. The +<tt>chat_buffer</tt> table is defined as follows. +<com:TTextHighlighter Language="text" CssClass="source block-content" id="code_90044"> +CREATE TABLE chat_buffer +( +	id INTEGER PRIMARY KEY, +	for_user VARCHAR(20) NOT NULL, +	from_user VARCHAR(20) NOT NULL, +	message TEXT NOT NULL, +	created_on INTEGER NOT NULL DEFAULT "0" +); +</com:TTextHighlighter> +The corresponding <tt>ChatBufferRecord</tt> class is saved as +<tt>App_Code/ChatBufferRecord.php</tt>. + +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90045"> +class ChatBufferRecord extends TActiveRecord +{ +    public $id; +    public $for_user; +    public $from_user; +    public $message; +    private $_created_on; + +    public static $_tablename='chat_buffer'; + +    public function getCreated_On() +    { +        if($this->_created_on === null) +            $this->_created_on = time(); +        return $this->_created_on; +    } + +    public function setCreated_On($value) +    { +        $this->_created_on = $value; +    } + +    public static function finder() +    { +        return parent::getRecordFinder('ChatBufferRecord'); +    } +} +</com:TTextHighlighter> +</p> + +<h1 id="18016">Chat Application Logic</h1> +<p id="90101" class="block-content">We finally arrive at the guts of the chat application logic. First, we +need to save a received message into the chat buffer for <b>all</b> the +current users. We add this logic in the <tt>ChatBufferRecord</tt> class. + +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90046"> +public function saveMessage() +{ +    foreach(ChatUserRecord::finder()->findAll() as $user) +    { +        $message = new self; +        $message->for_user = $user->username; +        $message->from_user = $this->from_user; +        $message->message = $this->message; +        $message->save(); +        if($user->username == $this->from_user) +        { +            $user->last_activity = time(); //update the last activity; +            $user->save(); +        } +    } +} +</com:TTextHighlighter> +We first find all the current users using the <tt>ChatUserRecord</tt> finder +methods. Then we duplicate the message and save it into the database. In addition, +we update the message sender's last activity timestamp. The above piece of code +demonstrates the simplicity and succinctness of using ActiveRecords for simple database designs. +</p> + +<p id="90102" class="block-content">The next piece of the logic is to retrieve the users' messages from the buffer. +We simply load all the messages for a particular username and format that message +appropriately (remember to escape the output to prevent Cross-Site Scripting attacks). +After we load the messages, we delete those loaded messages and any older +messages that may have been left in the buffer. +</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90047"> +public function getUserMessages($user) +{ +    $content = ''; +    foreach($this->findAll('for_user = ?', $user) as $message) +        $content .= $this->formatMessage($message); +    $this->deleteAll('for_user = ? OR created_on < ?',
 +                          $user, time() - 300); //5 min inactivity +    return $content; +} + +protected function formatMessage($message) +{ +    $user = htmlspecialchars($message->from_user); +    $content = htmlspecialchars($message->message); +    return "<div class=\"message\"><strong>{$user}:</strong>"
 +                  ." <span>{$content}</span></div>"; +} +</com:TTextHighlighter> + +To retrieve a list of current users (formatted), we add this logic to the +<tt>ChatUserRecord</tt> class. We delete any users that may have been inactive +for awhile. +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90048"> +public function getUserList() +{ +    $this->deleteAll('last_activity < ?', time()-300); //5 min inactivity +    $content = '<ul>'; +    foreach($this->findAll() as $user) +        $content .= '<li>'.htmlspecialchars($user->username).'</li>'; +    $content .= '</ul>'; +    return $content; +} +</com:TTextHighlighter> + +<div class="note"><b class="tip">Note:</b> +For simplicity +we formatted the messages in these Active Record classes. For large applications, +these message formatting tasks should be done using Prado components (e.g. using +a TRepeater in the template or a custom component). +</div> +</p> + +<h1 id="18017">Putting It Together</h1> +<p id="90103" class="block-content">Now comes to put the application flow together. In the <tt>Home.php</tt> we update +the <tt>Send</tt> buttons <tt>OnClick</tt> event handler to use the application +logic we just implemented. +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90049"> +function processMessage($sender, $param) +{ +    if(strlen($this->userinput->Text) > 0) +    { +        $record = new ChatBufferRecord(); +        $record->message = $this->userinput->Text; +        $record->from_user = $this->Application->User->Name; +        $record->saveMessage(); + +        $this->userinput->Text = ''; +        $messages = $record->getUserMessages($this->Application->User->Name); +        $this->CallbackClient->appendContent("messages", $messages); +        $this->CallbackClient->focus($this->userinput); +    } +} +</com:TTextHighlighter> +We simply save the message to the chat buffer and then ask for all the messages +for the current user and update the client side message list using a callback +response (AJAX style). +</p> + +<p id="90104" class="block-content">At this point the application is actually already functional, just not very +user friendly. If you open two different browsers, you should be able to communicate +between the two users whenever the <tt>Send</tt> button is clicked. +</p> + +<p id="90105" class="block-content">The next part is perhaps the more tricker and fiddly than the other tasks. We +need to improve the user experience. First, we want a list of current users +as well. So we add the following method to <tt>Home.php</tt>, we can call +this method when ever some callback event is raised, e.g. when the <tt>Send</tt> +button is clicked. +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90050"> +protected function refreshUserList() +{ +    $lastUpdate = $this->getViewState('userList',''); +    $users = ChatUserRecord::finder()->getUserList(); +    if($lastUpdate != $users) +    { +        $this->CallbackClient->update('users', $users); +        $this->setViewstate('userList', $users); +    } +} +</com:TTextHighlighter> +</p> + +<p id="90106" class="block-content">Actually, we want to periodically update the messages and user list as new +users join in and new message may arrive from other users. So we need to refresh +the message list as well.</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90051"> +function processMessage($sender, $param) +{ +    ... +    $this->refreshUserList(); +    $this->refreshMessageList(); +    ... +} + +protected function refreshMessageList() +{ +    //refresh the message list +    $finder = ChatBufferRecord::finder(); +    $content = $finder->getUserMessages($this->Application->User->Name); +    if(strlen($content) > 0) +    { +        $anchor = (string)time(); +        $content .= "<a href=\"#\" id=\"{$anchor}\"> </a>"; +        $this->CallbackClient->appendContent("messages", $content); +        $this->CallbackClient->focus($anchor); +    } +} +</com:TTextHighlighter> +The anchor using <tt>time()</tt> as ID for a focus point is so that when the +message list on the client side gets very long, the focus method will +scroll the message list to the latest message (well, it works in most browsers). +</p> + +<p id="90107" class="block-content">Next, we need to redirect the user back to the login page if the user has +been inactive for some time, say about 5 mins, we can add this check to any stage +of the page life-cycle. Lets add it to the <tt>onLoad()</tt> stage. +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90052"> +public function onLoad($param) +{ +    $username = $this->Application->User->Name; +    if(!$this->Application->Modules['users']->usernameExists($username)) +    { +        $auth = $this->Application->Modules['auth']; +        $auth->logout(); + +        //redirect to login page. +        $this->Response->Redirect($this->Service->ConstructUrl($auth->LoginPage)); +    } +} +</com:TTextHighlighter> +</p> + +<h1 id="18018">Improving User Experience</h1> +<p id="90108" class="block-content">The last few details are to periodically check for new messages and +refresh the user list. We can accomplish this by polling the server using a +<com:DocLink ClassPath="System.Web.UI.ActiveControls.TTimeTriggeredCallback" Text="TTimeTriggeredCallback" /> +control. We add a <tt>TTimeTriggeredCallback</tt> to the <tt>Home.page</tt> +and call the <tt>refresh</tt> handler method defined in <tt>Home.php</tt>. +We set the polling interval to 2 seconds. +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code_90053"> +<com:TTimeTriggeredCallback OnCallback="refresh" +	Interval="2" StartTimerOnLoad="true" /> +</com:TTextHighlighter> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_90054"> +function refresh($sender, $param) +{ +    $this->refreshUserList(); +    $this->refreshMessageList(); +} +</com:TTextHighlighter> +</p> + +<p id="90109" class="block-content">The final piece requires us to use some javascript. We want that when the +user type some text in the textarea and press the <tt>Enter</tt> key, we want it +to send the message without clicking on the <tt>Send</tt> button. We add to the +<tt>Home.page</tt> some javascript. + +<com:TTextHighlighter Language="javascript" CssClass="source block-content" id="code_90055"> +<com:TClientScript> +Event.observe($("<%= $this->userinput->ClientID %>"), "keypress", function(ev) +{ +    if(Event.keyCode(ev) == Event.KEY_RETURN) +    { +        if(Event.element(ev).value.length > 0) +            new Prado.Callback("<%= $this->sendButton->UniqueID %>"); +        Event.stop(ev); +    } +}); +</com:TClientScript> +</com:TTextHighlighter> +Details regarding the javascript can be explored in the +<a href="?page=Advanced.Scripts">Introduction to Javascript</a> section of the quickstart. +</p> + +<p id="90110" class="block-content">This completes the tutorial on making a basic chat web application using +the Prado framework. Hope you have enjoyed it. +</p> + +<div class="last-modified">$Id: AjaxChat.page 1650 2007-01-24 06:55:32Z wei $</div></com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page b/demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page new file mode 100644 index 00000000..071a13d7 --- /dev/null +++ b/demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page @@ -0,0 +1,405 @@ +<com:TContent ID="body"> +	<h1 id="16001">Building a Simple Currency Converter</h1> +	<p id="80053" class="block-content">This tutorial introduces the Prado web application framework and teaches +		you how to build a simple web application in a few simple steps. This +		tutorial assumes that you are familiar with PHP and you have access +		to a web server that is able to serve PHP5 scripts. +	</p> + +	<p id="80054" class="block-content">In this tutorial you will build a simple web application that converts +		a dollar amount to an other currency, given the rate of that currency +		relative to the dollar. The completed application is shown bellow. +		<img src=<%~ example2.png %> class="figure" /> +		You can try the application <a href="../currency-converter/index.php">locally</a> or at +		<a href="http://www.pradosoft.com/demos/currency-converter/">Pradosoft.com</a>. +		Notice that the application still functions exactly the same if javascript +		is not available on the user's browser. +	</p> + +	<h1 id="download">Downloading and Installing Prado</h1> +	<p id="80055" class="block-content">To install Prado, simply download the latest version of Prado from +		<a href="http://www.pradosoft.com/">http://www.pradosoft.com</a> +		and unzip the file to a directory <b>not</b> accessible by your web server +		(you may unzip it to a directory accessible by the web server if you wish +		to see the demos and test). For further detailed installation, see the +		<a href="?page=GettingStarted.Installation">Quickstart Installation</a> guide. +	</p> + +	<h1 id="16002">Creating a new Prado web Application</h1> +	<p id="80056" class="block-content">The quickest and simplest way to create a new Prado web application is +		to use the command tool <tt>prado-cli.php</tt> found in the <tt>framework</tt> +		directory of the Prado distribution.  We create a new application by running +		the  following command in your +		command prompt or console. The command creates a new directory named +		<tt>currency-converter</tt> in your current working directory. +		You may need to change to the appropriate directory +		first. +		See the <a href="?page=GettingStarted.CommandLine">Command Line Tool</a>
 +		for more details.
 +	</p>
 +<com:TTextHighlighter Language="text" CssClass="source block-content" id="code111"> +php prado/framework/prado-cli.php -c currency-converter +</com:TTextHighlighter> + +	<p id="80057" class="block-content">The above command creates the necessary directory structure and minimal +		files (including "index.php" and "Home.page") to run a Prado  web application. +		Now you can point your browser's url to the web server to serve up +		the <tt>index.php</tt> script in the <tt>currency-converter</tt> directory. +		You should see the message "Welcome to Prado!" +	</p> + +	<h1 id="16003">Creating the Currency Converter User Interface</h1> +	<p id="80058" class="block-content">We start by editing the <tt>Home.page</tt> file found in the +		<tt>currency-converter/protected/pages/</tt> directory. Files ending +		with ".page" are page templates that contains HTML and Prado controls. +		We simply add two textboxes, three labels and one button as follows.
 +	</p> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="form1"> +<com:TForm> +    <fieldset> +        <legend>Currency Converter</legend> +        <div class="rate-field"> +            <com:TLabel ForControl="currencyRate" Text="Exchange Rate per $1:" /> +            <com:TTextBox ID="currencyRate" /> +        </div> +        <div class="dollar-field"> +            <com:TLabel ForControl="dollars" Text="Dollars to Convert:" /> +            <com:TTextBox ID="dollars" /> +        </div> +        <div class="total-field"> +            <span class="total-label">Amount in Other Currency:</span> +            <com:TLabel ID="total" CssClass="result" /> +        </div> +        <div class="convert-button"> +            <com:TButton Text="Convert" /> +        </div> +    </fieldset> +</com:TForm> +</com:TTextHighlighter>
 +	<p id="refresh" class="block-content"> +	If you refresh the page, you should see something similar to the following figure. +	It may not look very pretty or orderly, but we shall change that later using CSS. +	<img src=<%~ example1.png %> class="figure" /> +	</p> + +	<p id="80059" class="block-content"> +		The first component we add is a +		<com:DocLink ClassPath="System.Web.UI.TForm" Text="TForm" /> +		that basically corresponds to the HTML <tt><form></tt> element. +		In Prado, only <b>one</b> <tt>TForm</tt> element is allowed per page. +	</p> + +	<p id="80060" class="block-content">The next two pair of component we add is the +		<com:DocLink ClassPath="System.Web.UI.WebControls.TLabel" Text="TLabel" /> +		and +		<com:DocLink ClassPath="System.Web.UI.WebControls.TTextBox" Text="TTextBox" /> +		that basically defines a label and a textbox for the user of the application +		to enter the currency exchange rate. +		The <tt>ForControl</tt> property value determines which component +		that the label is for. This allows the user of the application to click +		on the label to focus on the field (a good thing). You could have used +		a plain HTML <tt><label></tt> element to do the same thing, but +		you would have to find the correct <tt>ID</tt> of the textbox (or +		<tt><input></tt> in HTML) as Prado components may/will render the +		<tt>ID</tt> value differently in the HTML output. +	</p> + +	<p id="80061" class="block-content">The next pair of components are similar and defines the textbox +		to hold the dollar value to be converted. +		The <tt>TLabel</tt> with <tt>ID</tt> value "total" defines a simple label. +		Notice that the <tt>ForControl</tt> property is absent. This means that this +		label is simply a simple label which we are going to use to display the +		converted total amount. +	</p> + +	<p id="80062" class="block-content">The final component is a +		<com:DocLink ClassPath="System.Web.UI.WebControls.TButton" Text="TButton" /> +		that the user will click to calculate the results. The <tt>Text</tt> +		property sets the button label. +	</p> + +	<h1 id="16004">Implementing Currency Conversion</h1> + +	<p id="80063" class="block-content">If you tried clicking on the "Convert" button then the page will refresh +		and does not do anything else. For the button to do some work, we need +		to add a "Home.php" to where "Home.page" is. The <tt>Home</tt> class +		should extends the +		<com:DocLink ClassPath="System.Web.UI.TPage" Text="TPage" />, the default base +		class for all Prado pages.
 +	</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code3"> +<?php +class Home extends TPage +{ + +} +?> +</com:TTextHighlighter>
 +	<p id="1111" class="block-content"> +		Prado uses PHP's <tt>__autoload</tt> method to load classes. The convention +		is to use the class name with ".php" extension as filename. +	</p> + +	<p id="80064" class="block-content">So far there is nothing interesting about Prado, we just declared some +		"web components" in some template file named Home.page and created +		a "Home.php" file with a <tt>Home</tt> class. The more interesting +		bits are in Prado's event-driven architecture as we shall see next. +	</p> + +	<p id="80065" class="block-content">We want that when the user click on the "Convert" button, we take the +		values in the textbox, do some calculation and present the user with +		the converted total. To handle the user clicking of the "Convert" button +		we simply add an <tt>OnClick</tt> property to the "Convert" button in +	 	the "Home.page" template and add a corresponding event handler method +		in the "Home.php".
 +	</p> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code4"> +<com:TButton Text="Convert" OnClick="convert_clicked" /> +</com:TTextHighlighter> +	<p id="222" class="block-content">
 +		The value of the <tt>OnClick</tt>, "<tt>convert_clicked</tt>", will be the method +		name in the "Home.php" that will called when the user clicks on the +		"Convert" button.
 +	</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code5"> +class Home extends TPage +{ +    public function convert_clicked($sender, $param) +    { +        $rate = floatval($this->currencyRate->Text); +        $dollars = floatval($this->dollars->Text); +        $this->total->Text = $rate * $dollars; +    } +} +</com:TTextHighlighter>
 +<div id="3332" class="block-content">
 +	<p id="333"> +		If you run the application in your web browser, enter some values and click +		the "Convert" button then you should see that calculated value displayed next +		to the "Amount in Other Currency" label. +	</p> + +	<p id="80066">In the "<tt>convert_clicked</tt>" method the first parameter, <tt>$sender</tt>, +		corresponds to the object that raised the event, in this case, +		the "Convert" button. The second parameter, <tt>$param</tt> contains +		any additional data that the <tt>$sender</tt> object may wish to have added. +	</p> + +	<p id="80067">We shall now examine, the three lines that implements the simply currency +		conversion in the "<tt>convert_clicked</tt>" method.
 +	</p>
 +</div> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code6" > +$rate = floatval($this->currencyRate->Text); +</com:TTextHighlighter>
 +	<p id="444" class="block-content"> +		The statement <tt>$this->currencyRate</tt> corresponds to the +		<tt>TTextBox</tt> component with <tt>ID</tt> value "currencyRate" in the +		"Home.page" template. The <tt>Text</tt> property of the <tt>TTextBox</tt> +		contains the value that the user entered. So, we obtain this +		value by <tt>$this->currencyRate->Text</tt> which we convert the +		value to a float value.
 +	</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code7"> +$dollars = floatval($this->dollars->Text); +</com:TTextHighlighter>
 +<div id="5551" class="block-content">
 +	<p id="555"> +		The next line does a similar things, it takes the user value from +		the <tt>TTextBox</tt> with <tt>ID</tt> value "dollars and converts it to +		a float value. +	</p> + +	<p id="80068">The third line calculates the new amount and set this value in the +		<tt>Text</tt> property of the <tt>TLabel</tt> with <tt>ID="total"</tt>. +		Thus, we display the new amount to the user in the label.
 +	</p> +</div>
 +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code8"> +$this->total->Text = $rate * $dollars; +</com:TTextHighlighter> + +	<h1 id="16005">Adding Validation</h1> +	<p id="80069" class="block-content">The way we convert the user entered value to float ensures that the +		total amount is always a number. So the user is free to enter what +		ever they like, they could even enter letters. The user's experience +		in using the application can be improved by adding validators +		to inform the user of the allowed values in the currency rate and the +		amount to be calcuated. +	</p> + +	<p id="80070">For the currency rate, we should ensure that</p> +		<ol id="o111" class="block-content"> +			<li>the user enters a value,</li> +			<li>the currency rate is a valid number,</li> +			<li>the currency rate is positive.</li> +		</ol>
 +	<p id="666" class="block-content"> +	 To ensure 1 we add one +	<com:DocLink ClassPath="System.Web.UI.WebControls.TRequiredFieldValidator" Text="TRequiredFieldValidator" />. To ensure 2 and 3, we add one +	<com:DocLink ClassPath="System.Web.UI.WebControls.TCompareValidator" Text="TCompareValidator" />. We may add these validators any where within +	the "Home.page" template. Further details regarding these validator and other +	validators can be found in the +	<a href="?page=Controls.Validation">Validation Controls</a> page.
 +	</p> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code9"> +<com:TRequiredFieldValidator +	ControlToValidate="currencyRate" +	ErrorMessage="Please enter a currency rate." /> +<com:TCompareValidator +	ControlToValidate="currencyRate" +	DataType="Float" +	ValueToCompare="0" +	Operator="GreaterThan" +	ErrorMessage="Please enter a positive currency rate." /> +</com:TTextHighlighter> + +	<p id="80071" >For the amount to be calculated, we should ensure that</p> +		<ol id="o222" class="block-content"> +			<li>the user enters a value,</li> +			<li>the value is a valid number (not including any currency or dollar signs).</li> +		</ol>
 +	<p id="777" class="block-content"> +	To ensure 1 we just add another <tt>TRequiredFieldValidator</tt>, for 2 +	we could use a +	<com:DocLink ClassPath="System.Web.UI.WebControls.TDataTypeValidator" Text="TDataTypeValidator" />. For simplicity we only allow the user to enter +	a number for the amount they wish to convert.
 +	</p> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code9a"> +<com:TRequiredFieldValidator +	ControlToValidate="dollars" +	ErrorMessage="Please enter the amount you wish to calculate." /> +<com:TDataTypeValidator +	ControlToValidate="dollars" +	DataType="Float" +	ErrorMessage="Please enter a number." /> +</com:TTextHighlighter> +	</p> + +	<p id="80072" class="block-content">Now if you try to enter some invalid data in the application or left out +		any of the fields the validators will be activated and present the user +		with error messages. Notice that the error messages are presented +		without reloading the page. Prado's validators by default validates +		using both javascript and server side. The server side validation +		is <b>always performed</b>. For the server side, we +		should skip the calculation if the validators are not satisfied. This can +		done as follows.
 +	</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code10" > +public function convert_clicked($sender, $param) +{ +    if($this->Page->IsValid) +    { +        $rate = floatval($this->currencyRate->Text); +        $dollars = floatval($this->dollars->Text); +        $this->total->Text = $rate * $dollars; +    } +} +</com:TTextHighlighter> + +	<h1 id="16006">Improve User Experience With Active Controls</h1> +	<p id="80073" class="block-content">In this simple application we may further improve the user experience +		by increasing the responsiveness of the application. One way to achieve +		a faster response is calculate and present the results without reloading +		the whole page. +	</p> + +	<p id="80074" class="block-content">We can replace the <tt>TButton</tt> with the Active Control counter part, +		<com:DocLink ClassPath="System.Web.UI.ActiveControls.TActiveButton" Text="TActiveButton" />, +		that can trigger a server side click event without reloading the page. +		In addition, we can change the "totals" <tt>TLabel</tt> with the +		Active Control counter part, +		<com:DocLink ClassPath="System.Web.UI.ActiveControls.TActiveLabel" Text="TActiveLabel" />, such that the server side can update the browser without +		reloading the page.
 +	</p> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code11"> +<div class="total-field"> +    <span class="total-label">Amount in Other Currency:</span> +         <com:TActiveLabel ID="total" CssClass="result" /> +    </div> +    <div class="convert-button"> +        <com:TActiveButton Text="Convert" OnClick="convert_clicked" /> +</div> +</com:TTextHighlighter>
 +	<p id="1232" class="block-content"> +		The server side logic remains the same, we just need to import the +		Active Controls name space as they are not included by default. We +		add the following line to the begin of "Home.php".
 +	</p> +<com:TTextHighlighter Language="php" CssClass="source block-content" id="code12"> +Prado::using('System.Web.UI.ActiveControls.*'); +</com:TTextHighlighter> + +	<p id="80075" class="block-content">If you try the application now, you may notice that the page no longer +		needs to reload to calculate and display the converted total amount. +		However, since there is not page reload, there is no indication or not obvious +		that by clicking on the "Convert" button any has happened. +		We can further refine the user experience by change the text of "total" label +		to "calculating..." when the user clicks on the "Convert" button. The text of +		the "total" label will still be updated with the new calculate amount as before. +	</p> + +	<p id="80076" class="block-content">To indicate that the calculation is in progress, we can change the text +		of the "total" label as follows. We add a <tt>ClientSide.OnLoading</tt> property +		to the "Convert" button (since this button is responsible for requesting +		the calculation).
 +	</p> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code13"> +<com:TActiveButton Text="Convert" OnClick="convert_clicked" > +    <prop:ClientSide.OnLoading> +        $('<%= $this->total->ClientID %>').innerHTML = "calculating..." +    </prop:ClientSide.OnLoading> +</com:TActiveButton> +</com:TTextHighlighter> + +	<p id="80077" class="block-content">The <tt>ClientSide.OnLoading</tt> and various +	<com:DocLink ClassPath="System.Web.UI.ActiveControls.TCallbackClientSide" Text="other properties" /> accept a javascript block as their content or value. +	The javascript code <tt>$('...')</tt> is a javascript function that is +	equivalent to <tt>document.getElementById('...')</tt> that takes a string +	with the ID of an HTML element. Since Prado renders its components's IDs, we need +	to use the rendered ID of the "total" label, that is, <tt>$this->total->ClientID</tt>. We place this bit of code within a <tt><%= %></tt> to obtain the rendered HTML ID for the "total" label. The rest of the +	javascript code <tt>innerHTML = "calculating..."</tt> simply changes +	the content of the "total" label. +	</p> + +	<h1 id="16007">Adding Final Touches</h1> +	<p id="80078" class="block-content">So far we have built a simple currency converter web application with +		little attention of the looks and feel. Now we can add a stylesheet +		to improve the overall appearance of the application. We can simply +		add the stylesheet inline with the template code or we may create +		a "theme". +	</p> + +	<p id="80079" class="block-content">To create and use a theme with Prado applications, we simply create a new +		directory "themes/Basic" in the <tt>currency-converter</tt> directory. +		You may need to create the <tt>themes</tt> directory first. Any +		directory within the <tt>themes</tt> are considered as a theme with the +		name of the theme being the directory name. See the +		<a href="?page=Advanced.Themes">Themes and Skins</a> for further details. +	</p> + +	<p id="80080" class="block-content">We simply create a CSS file named "common.css" and save it in the +		<tt>themes/Basic</tt> directory. Then we add the following code +		to the beginning of "Home.page" (we add a little more HTML as well).
 +	</p> +<com:TTextHighlighter Language="prado" CssClass="source block-content" id="code14"> +<%@ Theme="Basic" %> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" > +<com:THead Title="Currency Converter" /> +<body> +</com:TTextHighlighter>
 +	<p id="4334" class="block-content"> +		The first line <tt><%@ Theme="Basic" %></tt> defines the +		theme to be used for this page. The +		<com:DocLink ClassPath="System.Web.UI.WebControls.THead" Text="THead" /> +		corresponds to the HTML <tt><head></tt> element. In addition +		to display the <tt>Title</tt> property by the <tt>THead</tt>, all CSS +		files in the <tt>themes/Basic</tt> directory are also rendered/linked +		for the current page. Our final currency converter web application +		looks like the following. +		<img src=<%~ example2.png %> class="figure" /> +		This completes introduction tutorial to the Prado web application framework. +	</p> +<div class="last-modified">$Id: CurrencyConverter.page 1654 2007-01-25 07:24:40Z wei $</div></com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/Tutorial/fr/chat1.png b/demos/quickstart/protected/pages/Tutorial/fr/chat1.pngBinary files differ new file mode 100644 index 00000000..8288b496 --- /dev/null +++ b/demos/quickstart/protected/pages/Tutorial/fr/chat1.png diff --git a/demos/quickstart/protected/pages/Tutorial/fr/chat2.png b/demos/quickstart/protected/pages/Tutorial/fr/chat2.pngBinary files differ new file mode 100644 index 00000000..97cbc51d --- /dev/null +++ b/demos/quickstart/protected/pages/Tutorial/fr/chat2.png diff --git a/demos/quickstart/protected/pages/Tutorial/fr/example1.png b/demos/quickstart/protected/pages/Tutorial/fr/example1.pngBinary files differ new file mode 100644 index 00000000..0c7da7ba --- /dev/null +++ b/demos/quickstart/protected/pages/Tutorial/fr/example1.png diff --git a/demos/quickstart/protected/pages/Tutorial/fr/example2.png b/demos/quickstart/protected/pages/Tutorial/fr/example2.pngBinary files differ new file mode 100644 index 00000000..1df56cfb --- /dev/null +++ b/demos/quickstart/protected/pages/Tutorial/fr/example2.png | 
