diff options
author | Fabio Bas <ctrlaltca@gmail.com> | 2015-12-07 15:57:51 +0100 |
---|---|---|
committer | Fabio Bas <ctrlaltca@gmail.com> | 2015-12-07 15:57:51 +0100 |
commit | 654a9cae43358c7eecf3b522e9876aa7815e2453 (patch) | |
tree | 545f2e42fdc6e824b247924adf083ad3fe51e61d /demos/quickstart/protected/pages/Tutorial/fr | |
parent | e99e35819d53dc48ae4e9a8491528a6b6011469f (diff) |
Move urls from pradosoft.com to github's project page; drop unmaintained quickstart tutorial translations
Diffstat (limited to 'demos/quickstart/protected/pages/Tutorial/fr')
-rwxr-xr-x | demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page | 755 | ||||
-rwxr-xr-x | demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page | 404 | ||||
-rwxr-xr-x | demos/quickstart/protected/pages/Tutorial/fr/chat1.png | bin | 10533 -> 0 bytes | |||
-rwxr-xr-x | demos/quickstart/protected/pages/Tutorial/fr/chat2.png | bin | 13409 -> 0 bytes | |||
-rwxr-xr-x | demos/quickstart/protected/pages/Tutorial/fr/example1.png | bin | 11139 -> 0 bytes | |||
-rwxr-xr-x | demos/quickstart/protected/pages/Tutorial/fr/example2.png | bin | 13842 -> 0 bytes |
6 files changed, 0 insertions, 1159 deletions
diff --git a/demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page b/demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page deleted file mode 100755 index 568e920f..00000000 --- a/demos/quickstart/protected/pages/Tutorial/fr/AjaxChat.page +++ /dev/null @@ -1,755 +0,0 @@ -<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 -{ - const TABLE='chat_users'; - - public $username; - public $last_activity; - - public static function finder($className=__CLASS__) - { - return parent::finder($className); - } -} -</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> -</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 -{ - const TABLE='chat_buffer'; - - public $id; - public $for_user; - public $from_user; - public $message; - private $_created_on; - - 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($className=__CLASS__) - { - return parent::finder($className); - } -} -</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> - -</com:TContent> diff --git a/demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page b/demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page deleted file mode 100755 index 6b8c7bc3..00000000 --- a/demos/quickstart/protected/pages/Tutorial/fr/CurrencyConverter.page +++ /dev/null @@ -1,404 +0,0 @@ -<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> -</com:TContent> diff --git a/demos/quickstart/protected/pages/Tutorial/fr/chat1.png b/demos/quickstart/protected/pages/Tutorial/fr/chat1.png Binary files differdeleted file mode 100755 index 8288b496..00000000 --- a/demos/quickstart/protected/pages/Tutorial/fr/chat1.png +++ /dev/null diff --git a/demos/quickstart/protected/pages/Tutorial/fr/chat2.png b/demos/quickstart/protected/pages/Tutorial/fr/chat2.png Binary files differdeleted file mode 100755 index 97cbc51d..00000000 --- a/demos/quickstart/protected/pages/Tutorial/fr/chat2.png +++ /dev/null diff --git a/demos/quickstart/protected/pages/Tutorial/fr/example1.png b/demos/quickstart/protected/pages/Tutorial/fr/example1.png Binary files differdeleted file mode 100755 index 0c7da7ba..00000000 --- a/demos/quickstart/protected/pages/Tutorial/fr/example1.png +++ /dev/null diff --git a/demos/quickstart/protected/pages/Tutorial/fr/example2.png b/demos/quickstart/protected/pages/Tutorial/fr/example2.png Binary files differdeleted file mode 100755 index 1df56cfb..00000000 --- a/demos/quickstart/protected/pages/Tutorial/fr/example2.png +++ /dev/null |