summaryrefslogtreecommitdiff
path: root/demos/blog-tutorial
diff options
context:
space:
mode:
authorkabayan <>2007-08-29 11:55:06 +0000
committerkabayan <>2007-08-29 11:55:06 +0000
commit30795649ead5b7212cad32f9d5a9ca6152beba27 (patch)
treed2cb2e60736d967d55bb7b8ef23de1149f31ae3f /demos/blog-tutorial
parent91536b68a8eee2e2849949f605d3e5428f71cd9c (diff)
Indonesian Blog tutorial day 3,4, and 5 3rd tries
Diffstat (limited to 'demos/blog-tutorial')
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/Auth.page102
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/CreateAdminUser.page151
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/CreateEditUser.page195
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/CreateLoginUser.page162
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/CreateNewUser.page210
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/Overview.page26
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/directories.gifbin0 -> 10329 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/output.gifbin0 -> 10006 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/output2.gifbin0 -> 9222 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/id/output3.gifbin0 -> 9464 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/CreateEditPost.page133
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/CreateListPost.page185
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/CreateNewPost.page136
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/CreateReadPost.page135
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/Overview.page26
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/directories.gifbin0 -> 11129 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/output.gifbin0 -> 3406 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/output2.gifbin0 -> 6326 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/output3.gifbin0 -> 11874 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day4/id/output4.gifbin0 -> 11916 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day5/id/ErrorLogging.page159
-rw-r--r--demos/blog-tutorial/protected/pages/Day5/id/Performance.page67
-rw-r--r--demos/blog-tutorial/protected/pages/Day5/id/Summary.page36
-rw-r--r--demos/blog-tutorial/protected/pages/Day5/id/UseTheme.page138
-rw-r--r--demos/blog-tutorial/protected/pages/Day5/id/output.gifbin0 -> 4282 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day5/id/output2.gifbin0 -> 7798 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day5/id/output3.gifbin0 -> 5190 bytes
27 files changed, 1861 insertions, 0 deletions
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/Auth.page b/demos/blog-tutorial/protected/pages/Day3/id/Auth.page
new file mode 100644
index 00000000..c4728cdf
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/Auth.page
@@ -0,0 +1,102 @@
+<com:TContent ID="Main">
+
+<h1>Otentikasi dan Otorisasi</h1>
+
+<p>
+Sebelum kita siap mengimplementasikan halaman pengguna, kita perlu melakukan beberapa pekerjaan guna menghidupkan <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Advanced.Auth">otentikasi dan otorisasi</a>.
+</p>
+
+<p>
+Kita menambah dua modul ke konfigurasi aplikasi seperti berikut:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<modules>
+ ...TDataSourceConfig and TActiveRecordConfig modules...
+
+ <module id="auth"
+ class="System.Security.TAuthManager"
+ UserManager="users"
+ LoginPage="users.LoginUser" />
+
+ <module id="users"
+ class="System.Security.TDbUserManager"
+ UserClass="Application.BlogUser" />
+</modules>
+</com:TTextHighlighter>
+
+<p>
+Modul <a href="http://www.pradosoft.com/docs/classdoc/TAuthManager">TAuthManager</a> mengatur seluruh alur kerja otentikasi dan otorisasi. Ia menggunakan modul <tt>users</tt> sebagai manajer penggunanya (lihat di bawah). Dengan menetapkan properti <tt>LoginPage</tt>, kita menginformasikan manajer otentikasi untuk mengalihkan browser pengguna ke halaman <tt>LoginUser</tt> saat otorisasi gagal. Kami akan menjelaskan bagaimana untuk membuat <tt>LoginUser</tt> dalam subbagian berikutnya.
+</p>
+
+<p>
+Modul <tt>user</tt> adalah kelas <a href="http://www.pradosoft.com/docs/classdoc/TDbUserManager">TDbUserManager</a> yang bertanggung jawab untuk memverifikasi keabsahan pengguna dan memelihara data dasar dalam sesi PHP. Properti <tt>UserClass</tt> diinisialisasi sebagai <tt>Application.BlogUser</tt>, yang menunjukan bahwa manajer pengguna akan melihar kelas <tt>BlogUser</tt> di bawah direktori <tt>protected</tt> (ingat alias <tt>Application</tt> merujuk ke direktori <tt>protected</tt>) dan menggunakannya untuk memelihara data sesi pengguna.
+</p>
+
+<p>
+Seperti yang akan kita lihat dalam bagian nanti, dalam kontrol dan halaman, kita dapat menggunakan <tt>$this->User</tt> untuk memperoleh obyek <tt>BlogUser</tt> yang berisi informasi pengguna yang saat ini mengakses siistem.
+</p>
+
+<p>
+Di bawah ini adalah rincian implementasi dari <tt>BlogUser</tt>. Perhatikan, <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Database.ActiveRecord">Rekaman Aktif</a> digunakan untuk melakukan query DB. Sebagai contoh, kita menggunakan <tt>UserRecord::finder()->findByPk($username)</tt> untuk mencari kunci primer yang ditetapkan oleh <tt>$username</tt> dalam tabel <tt>users</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+// Sertakan file TDbUserManager.php yang mendefinisikan TDbUser
+Prado::using('System.Security.TDbUserManager');
+
+/**
+ * BlogUser Class.
+ * BlogUser mewakili data pengguna yang perlu dijaga dalam sesi.
+ * Implementasi standar memelihara informasi aturan dan pengguna.
+ */
+class BlogUser extends TDbUser
+{
+ /**
+ * Membuat obyek BlogUser berdasarkan username yang ditetapkan.
+ * Metode ini diperlukan oleh TDbUser. Ia memeriksa database
+ * untuk melihat apakah username yang ditetapkan ada di sana. Jika ada,
+ * obyek BlogUser dibuat dan diinisialisasi.
+ * @param string username yang ditetapkan
+ * @return BlogUser obyek pengguna, null jika username tidak benar.
+ */
+ public function createUser($username)
+ {
+ // gunakan Rekaman Aktif UserRecord untuk mencari username yang ditetapkan
+ $userRecord=UserRecord::finder()->findByPk($username);
+ if($userRecord instanceof UserRecord) // jika ditemukan
+ {
+ $user=new BlogUser($this->Manager);
+ $user->Name=$username; // setel nama pengguna
+ $user->Roles=($userRecord->role==1?'admin':'user'); // setel aturan
+ $user->IsGuest=false; // pengguna bukan seorang tamu
+ return $user;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Memeriksa apakah (username, password) yang ditetapkan sudah benar.
+ * Metode ini diperlukan oleh TDbUser.
+ * @param string username
+ * @param string password
+ * @return boolean apakah username dan password sudah benar.
+ */
+ public function validateUser($username,$password)
+ {
+ // pakai Rekaman Aktif UserRecord untuk mencari pasangan (username, password).
+ return UserRecord::finder()->findBy_username_AND_password($username,$password)!==null;
+ }
+
+ /**
+ * @return boolean apakah pengguna ini adalah seorang administrator.
+ */
+ public function getIsAdmin()
+ {
+ return $this->isInRole('admin');
+ }
+}
+</com:TTextHighlighter>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/CreateAdminUser.page b/demos/blog-tutorial/protected/pages/Day3/id/CreateAdminUser.page
new file mode 100644
index 00000000..c9ee7c5a
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/CreateAdminUser.page
@@ -0,0 +1,151 @@
+<com:TContent ID="Main">
+
+<h1>Membuat Halaman <tt>AdminUser</tt></h1>
+
+<p>
+Halaman <tt>AdminUser</tt> menampilkan semua akun pengguna dalam sebuah daftar agar administrator dapat melakukan beberapa pekerjaan administratif. Demi kemudahan, pekerjaan administratif sistem blog kita mendukung termasuk mengedit dan menghapus akun pengguna.
+</p>
+
+<p>
+Kita akan menampilkan daftar pengguna dalam sebuah tabel. Setiap baris tabel mewakili satu akun pengguna, dan kolom berikut ditampilkan:
+</p>
+<ul>
+<li>Username - menampilkan nama pengguna. Dalam setiap sel sebuah hiperlink ditampilkan membawa ke halaman <a href="?page=Day3.CreateEditUser">EditUser</a> terkait.</li>
+<li>Email - menampilkan email.</li>
+<li>Administrator - menampilkan apakah akun pengguna adalah aturan administrator.</li>
+<li>Command - menampilkan kolom dengan tombol "Delete". Mengklik pada setiap tombol akan mengakibatkan penghapusan akun pengguna terkait.</li>
+</ul>
+
+<p>
+Kita membuat dua file <tt>protected/pages/users/AdminUser.page</tt> dan <tt>protected/pages/users/AdminUser.php</tt> masing-masing untuk menyimpan template halaman dan kelas halaman.
+</p>
+
+<h2>Membuat Template Halaman</h2>
+<p>
+Kita menggunakan <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.DataGrid">TDataGrid</a> untuk menampilkan akun pengguna. Berdasarkan pada analisis di atas, kita mengkonfigurasi empat kolom berikut:
+</p>
+<ul>
+<li><a href="http://www.pradosoft.com/docs/classdoc/THyperLinkColumn">THyperLinkColumn</a> menampilkan kolom nama pengguna. URL dibentuk berdasarkan pada ekstensi PHP yang ditetapkan dalam properti <tt>DataNavigateUrlFormatString</tt>.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TBoundColumn">TBoundColumn</a> menampilkan kolom email.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TCheckBoxColumn">TCheckBoxColumn</a> menggunakan kotak centang untuk menunjukan apakah akun pengguna adalah aturan administrator.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TButtonColumn">TButtonColumn</a> menampilkan kolom dengan tombol "Delete".</li>
+</ul>
+
+<p>Template halaman lengkap ditampilkan seperti berikut:</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="My Blog - Manage User Accounts" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Manage User Accounts</h1>
+
+<a href="&lt;%= $this->Service->constructUrl('users.NewUser')%>">Create New User</a>
+<br/>
+
+&lt;com:TDataGrid ID="UserGrid"
+ DataKeyField="username"
+ AutoGenerateColumns="false"
+ OnDeleteCommand="deleteButtonClicked">
+
+ &lt;com:THyperLinkColumn
+ HeaderText="Username"
+ DataTextField="username"
+ DataNavigateUrlField="username">
+ &lt;prop:DataNavigateUrlFormatString>#
+ $this->Service->constructUrl('users.EditUser',array('username'=>{0}))
+ &lt;/prop:DataNavigateUrlFormatString>
+ &lt;/com:THyperLinkColumn>
+
+ &lt;com:TBoundColumn
+ HeaderText="Email"
+ DataField="email" />
+
+ &lt;com:TCheckBoxColumn
+ HeaderText="Administrator"
+ DataField="role" />
+
+ &lt;com:TButtonColumn
+ HeaderText="Command"
+ Text="Delete"
+ ButtonType="PushButton"
+ CommandName="delete" />
+
+&lt;/com:TDataGrid>
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+
+<h2>Membuat Kelas Halaman</h2>
+
+<p>
+Dalam template halaman di atas, event <tt>OnDeleteCommand</tt> datagrid dengan metode <tt>deleteButtonClicked()</tt> akan kita implementasikan dalam kelas halaman. Sebagai tambahan, datagrid perlu dipopulasi dengan data akun pengguna saat halaman diinisialisasi. Oleh karena itu, kita menulis kelas halaman seperti berikut:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class AdminUser extends TPage
+{
+ /**
+ * Mempopulai datagrid dengan daftar pengguna.
+ * Metode ini dipanggil oleh kerangka kerja saat inisialisasi halaman
+ * @param mixed event parameter
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ // ambil semua informasi data akun
+ $this->UserGrid->DataSource=UserRecord::finder()->findAll();
+ // mengikat data ke komponen antarmua
+ $this->UserGrid->dataBind();
+ }
+
+ /**
+ * Menghapus rekaman pengguna yang ditetapkan.
+ * Metode ini merespon ke event OnDeleteCommand pada datagrid.
+ * @param TDataGrid pengirim event
+ * @param TDataGridCommandEventParameter parameter event
+ */
+ public function deleteButtonClicked($sender,$param)
+ {
+ // dapatkan item datagrid yang berisi tombol hapus yang diklik
+ $item=$param->Item;
+ // dapatkan kunci primer terkait ke item datagrid
+ $username=$this->UserGrid->DataKeys[$item->ItemIndex];
+ // menghapus rekaman pengguna dengan kunci primer nama pengguna
+ UserRecord::finder()->deleteByPk($username);
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Dalam kode di atas, metode <tt>deleteButtonClicked()</tt> dipanggil kapan saja tombol "Delete" diklik. untuk menentukan baris mana pada tombol yang diklik, kita memeriksa properti <tt>Item.ItemIndex</tt> pada parameter event. Untuk selanjutnya mengidentifikasi akun pengguna mana yang akan dihapus, kita mengambil nilai kunci primer (username) melalui properti <tt>DataKeys</tt> pada DataGrid.
+</p>
+
+<com:TipBox>
+Semua kontrol <a href="http://www.pradosoft.com/docs/classdoc/TDataBoundControl">data-bound</a> mempunyai pola penggunaan yang sama. Yaitu, Setel properti <tt>DataSource</tt> dengan data dan panggil metode <tt>dataBind()</tt> untuk mengikat data ke struktur internal kontrol.
+</com:TipBox>
+
+
+<h2>Menambahkan Pemeriksaan Perijinan</h2>
+<p>
+Karena <tt>AdminUser</tt> hanya bisa diakses oleh administrator, maka kita perlu menyesuaikan file konfigurasi halaman <tt>protected/pages/users/config.xml</tt>.
+</p>
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <authorization>
+ <allow pages="NewUser,AdminUser" roles="admin" />
+ <deny users="?" />
+ </authorization>
+</configuration>
+</com:TTextHighlighter>
+
+<h2>Pengujian</h2>
+<p>
+Untuk menguji halaman <tt>AdminUser</tt>, kunjungi URL <tt>http://hostname/blog/index.php?page=users.AdminUser</tt>. Anda mungkin perlu masuk sebagai administrator lebih dahulu jika anda belum melakukannya. Kita akan melihat hasil seperti berikut.
+</p>
+
+<img src="<%~ output3.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/CreateEditUser.page b/demos/blog-tutorial/protected/pages/Day3/id/CreateEditUser.page
new file mode 100644
index 00000000..e15df047
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/CreateEditUser.page
@@ -0,0 +1,195 @@
+<com:TContent ID="Main">
+
+<h1>Membuat Halaman <tt>EditUser</tt></h1>
+
+<p>
+Halaman <tt>EditUser</tt> sangat mirip dengan <a href="?page=Day3.CreateNewUser">NewUser</a>. Perbedaan utamanya adalah bahwa saat <tt>EditUser</tt> awalnya diminta, field input harus diinisialisasi dengan informasi pengguna yang sudah ada. Sedikit perbedaan lainnya adalah bahwa <tt>EditUser</tt> juga bisa diakses oleh pengguna normal.
+</p>
+
+<p>
+Untuk menentukan akun pengguna mana yang diedit, kita menggunakan kebijakan berikut:
+</p>
+<ul>
+<li>Jika pengguna saat ini adalah administrator, dia dapat mengedit setiap akun pengguna dengan menetapkan akun nama pengguna dalam variabel GET bernama 'username'. Sebagai contoh, <tt>http://hostname/blog/index.php?page=users.EditUser&username=demo</tt>.</li>
+<li>Jika pengguna saat ini adalah administrator dan URL tidak berisi 'username', data administrator sendiri sedang dimutakhirkan.</li>
+<li>Jika pengguna saat ini adalah pengguna normal, dia hanya bisa mengedit informasi akunnya sendiri, dan dia tidak bisa memodifikasi data aturannya.</li>
+</ul>
+
+<p>
+Kita membuat dua file <tt>protected/pages/users/EditUser.page</tt> dan <tt>protected/pages/users/EditUser.php</tt> masing-masing untuk menyimpan template halaman dan kelas halaman.
+</p>
+
+<h2>Membuat Template Halaman</h2>
+<p>
+Seperti yang telah anda tebak, template halaman <tt>EditUser</tt> sebagian besar sama seperti pada <tt>NewUser</tt>. Selain perbedaan dalam judul halaman dan judul tombol submit, ada tiga perbedaan utama.
+</p>
+<ul>
+<li>Kotak teks "username" diganti oleh kontrol <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Label">TLabel</a> karena kita tidak membolehkan memodifikasi nama pengguna;</li>
+<li>Validator untuk input "password" dihilangkan. Ini dikarenakan jika pengguna tidak melengkapi kata sandi selama mengedit, itu berarti pengguna tidak ingin mengubah kata sandi.</li>
+<li>Input "role" dikelilingi oleh <tt>TControl</tt> yang penampakannya dihidup matikan berdasarkan aturan pengguna yang masuk saat ini. Jika pengguna bukan administrator, input "role" tidak akan ditampilkan karena pengguna normal tidak diperbolehkan untuk memodifikasi aturannya.</li>
+</ul>
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="My Blog - Edit User" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Edit User</h1>
+
+<span>Username:</span>
+&lt;com:TLabel ID="Username" />
+
+<br/>
+<span>Password:</span>
+<br/>
+&lt;com:TTextBox ID="Password" TextMode="Password" />
+
+<br/>
+<span>Re-type Password:</span>
+&lt;com:TCompareValidator
+ ControlToValidate="Password"
+ ControlToCompare="Password2"
+ ErrorMessage="Your password entries did not match."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Password2" TextMode="Password" />
+
+<br/>
+<span>Email Address:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Email"
+ ErrorMessage="Please provide your email address."
+ Display="Dynamic" />
+&lt;com:TEmailAddressValidator
+ ControlToValidate="Email"
+ ErrorMessage="You entered an invalid email address."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Email" />
+
+&lt;com:TControl Visible="&lt;%= $this->User->IsAdmin %>">
+<br/>
+<span>Role:</span>
+<br/>
+&lt;com:TDropDownList ID="Role">
+ &lt;com:TListItem Text="Normal User" Value="0" />
+ &lt;com:TListItem Text="Administrator" Value="1" />
+&lt;/com:TDropDownList>
+&lt;/com:TControl>
+
+<br/>
+<span>First Name:</span>
+<br/>
+&lt;com:TTextBox ID="FirstName" />
+
+<br/>
+<span>Last Name:</span>
+<br/>
+&lt;com:TTextBox ID="LastName" />
+
+<br/>
+&lt;com:TButton Text="Save" OnClick="saveButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+
+<h2>Membuat Kelas Halaman</h2>
+
+<p>
+Berdasarkan pada deskripsi di atas dan template, kita perlu menulis kelas halaman yang mengawali input dengan informasi pengguna yang sudah ada. Sebagai tambahan, kelas halaman juga perlu untuk mengimplementasikan metode <tt>saveButtonClicked()</tt> yang disertakan pada event tombol "save" <tt>OnClick</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class EditUser extends TPage
+{
+ /**
+ * Menginisialisasi input dengan data pengguna yang sudah ada.
+ * Metode ini dipanggil oleh kerangka kerja saat halaman diinisialisasi.
+ * @param mixed event parameter
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ if(!$this->IsPostBack) // jika halaman awalnya diminta
+ {
+ // Ambil data pengguna yang sudah ada. Ini sama dengan:
+ // $userRecord=$this->getUserRecord();
+ $userRecord=$this->UserRecord;
+
+ // Populasikan kontrol input dengan data pengguna yang sudah ada
+ $this->Username->Text=$userRecord->username;
+ $this->Email->Text=$userRecord->email;
+ $this->Role->SelectedValue=$userRecord->role;
+ $this->FirstName->Text=$userRecord->first_name;
+ $this->LastName->Text=$userRecord->last_name;
+ }
+ }
+
+ /**
+ * Menyimpan akun pengguna jika semua input sudah benar.
+ * Metode ini merespon event OnClick pada tombol "save".
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function saveButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // saat semua validasi sukses
+ {
+ // Ambil data pengguna yang sudah ada. Ini sama dengan:
+ $userRecord=$this->UserRecord;
+
+ // Ambil data input
+ $userRecord->username=$this->Username->Text;
+ // mutakhirkan kata sandi saat input tidak kosong
+ if(!empty($this->Password->Text))
+ $userRecord->password=$this->Password->Text;
+ $userRecord->email=$this->Email->Text;
+ // mutakhirkan aturan jika pengguna saat ini adalah administrator
+ if($this->User->IsAdmin)
+ $userRecord->role=(int)$this->Role->SelectedValue;
+ $userRecord->first_name=$this->FirstName->Text;
+ $userRecord->last_name=$this->LastName->Text;
+
+ // simpan ke database melalui mekanisme Rekaman Aktif
+ $userRecord->save();
+
+ // alihkan browser ke homepage
+ $this->Response->redirect($this->Service->DefaultPageUrl);
+ }
+ }
+
+ /**
+ * Mengembalikan data pengguna yang diedit.
+ * @return UserRecord data pengguna yang diedit.
+ * @throws THttpException jika data pengguna tidak ditemukan.
+ */
+ protected function getUserRecord()
+ {
+ // pengguna yang diedit adalah pengguna yang saat ini sudah masuk
+ $username=$this->User->Name;
+ // jika var GET 'username' tidak kosong dan pengguna saat ini adalah
+ // administrator, lebih baik kita menggunakan nilai var GET.
+ if($this->User->IsAdmin && $this->Request['username']!==null)
+ $username=$this->Request['username'];
+
+ // gunakan Rekaman Aktif untuk melihat nama pengguna yang ditetapkan
+ $userRecord=UserRecord::finder()->findByPk($username);
+ if(!($userRecord instanceof UserRecord))
+ throw new THttpException(500,'Username is invalid.');
+ return $userRecord;
+ }
+}
+</com:TTextHighlighter>
+
+<com:TipBox>
+Metode <tt>onInit()</tt> dipanggil oleh PRADO selama salah satu <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Pages">masa hidup halaman</a>. Metode masa hidup umum lainnya ditimpa termasuk <tt>onPreInit()</tt>, <tt>onLoad()</tt> dan <tt>onPreRender()</tt>.
+</com:TipBox>
+
+<h2>Pengujian</h2>
+<p>
+Untuk menguji halaman <tt>EditUser</tt>, kunjungi URL <tt>http://hostname/blog/index.php?page=users.EditUser&username=demo</tt>. Anda mungkin perlu masuk lebih dulu jika anda belum melakukannya. Coba masuk dengan akun berbeda (e.g. admin/demo, demo/demo) dan lihat bagaimana halaman ditampilkan secara berbeda.
+</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/CreateLoginUser.page b/demos/blog-tutorial/protected/pages/Day3/id/CreateLoginUser.page
new file mode 100644
index 00000000..1debbec0
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/CreateLoginUser.page
@@ -0,0 +1,162 @@
+<com:TContent ID="Main">
+
+<h1>Membuat Halaman <tt>LoginUser</tt></h1>
+
+<p>
+Halaman <tt>LoginUser</tt> menampilkan formulir masuk dan mengotentikasi pengguna yang mencoba untuk masuk. Seperti dijelaskan dalam <a href="?page=Day3.Auth">otentikasi dan otorisasi</a>, browser pengguna secara otomatis dialihkan ke halaman <tt>LoginUser</tt> ketika pengguna mencoba untuk mengakses halaman istimewa, seperti misalhnya halaman admin pengguna.
+</p>
+
+<p>
+Alur kerja <tt>LoginUser</tt> sangat mirip dengan halaman <a href="?page=Day1.CreateContact">Contact</a>:
+</p>
+<ol>
+<li>Ketika pengguna mengakses halaman <tt>LoginUser</tt>, formulir masuk ditampilkan;</li>
+<li>Pengguna mengisi nama pengguna dan kata sandi serta mengklik pada tombol "login";</li>
+<li><tt>LoginUser</tt> menerima event "login" dan memicu rangkaian otentikasi;</li>
+<li>Jika pengguna memasukan nama pengguna dan kata sandi yang benar, sistem menempatkan identitas yang sah dan mengalihkan browser-nya ke halaman istimewa yang diinginkan; Jika tidak, pesan "password invalid" akan ditampilkan.
+</ol>
+
+<p>
+Kita membuat dua file <tt>protected/pages/users/LoginUser.page</tt> dan <tt>protected/pages/users/LoginUser.php</tt> masing-masing untuk menyimpan template halaman dan kelas halaman.
+</p>
+
+<h2>Membuat Template Halaman</h2>
+
+<p>
+Di bawah ini kita menampilkan template untuk <tt>LoginUser</tt>. Seperti kita lihat, halaman sebagian besar berisi kotak teks untuk mengumpulkan nama pengguna dan kotak teks untuk kata sandi. Input nama pengguna diperlukan, yang dipastikan oleh <tt>TRequiredFieldValidator</tt>. Kebenaran dari input kata sandi dipastikan oleh <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Controls.Validation">TCustomValidator</a> yang memanggil metode <tt>validateUser()</tt> halaman ketika validasi dilakukan. Halaman juga mempunyai tombol "login" yang memanggil <tt>loginButtonClicked()</tt> halaman ketika itu diklik.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="My Blog - Login" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Login</h1>
+
+<span>Username:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Username"
+ ErrorMessage="Please provide your username."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Username" />
+
+<br/>
+<span>Password:</span>
+&lt;com:TCustomValidator
+ ControlToValidate="Password"
+ ErrorMessage="Your entered an invalid password."
+ Display="Dynamic"
+ OnServerValidate="validateUser" />
+<br/>
+&lt;com:TTextBox ID="Password" TextMode="Password" />
+
+<br/>
+&lt;com:TButton Text="Login" OnClick="loginButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<h2>Membuat Kelas Halaman</h2>
+
+<p>
+Seperti halaman <a href="?page=Day1.CreateContact">Contact</a>, halaman <tt>LoginUser</tt> juga memerlukan file kelas yang sebagian besar berisi implementasi pengendali event yang disertakan dalam template halaman. Di sini, kita perlu mengimplementasikan dua metode: <tt>validateUser()</tt> dan <tt>loginButtonClicked()</tt>. Dalam <tt>validateUser()</tt>, kita menggunakan <a href="?page=Day3.Auth">manajer otentikasi</a> untuk memverifikasi apakan nama pengguna serta kata sandinya sudah benar. Jika benar, manajer otentikasi akan secara otomatis membuat sesi pengguna dengan informasi identitas pengguna terkait.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class LoginUser extends TPage
+{
+ /**
+ * Validates whether the username and password are correct.
+ * This method responds to the TCustomValidator's OnServerValidate event.
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function validateUser($sender,$param)
+ {
+ $authManager=$this->Application->getModule('auth');
+ if(!$authManager->login($this->Username->Text,$this->Password->Text))
+ $param->IsValid=false; // beritahu validator bawa validasi gagal
+ }
+
+ /**
+ * Redirects the user's browser to appropriate URL if login succeeds.
+ * This method responds to the login button's OnClick event.
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function loginButtonClicked($sender,$param)
+ {
+ if($this->Page->IsValid) // semua validasi sukses
+ {
+ // Dapatkan URL pada halaman istimewa yang diinginkan pengguna yang akan dikunjungi
+ $url=$this->Application->getModule('auth')->ReturnUrl;
+ if(empty($url)) // pengguna mengakses halaman masuk secara langsung
+ $url=$this->Service->DefaultPageUrl;
+ $this->Response->redirect($url);
+ }
+ }
+}
+</com:TTextHighlighter>
+
+
+<h2>Pengujian</h2>
+
+<p>
+Kita telah membuat halaman <tt>LoginUser</tt>. Kita dapat mengujinya dengan mengunjungi URL <tt>http://hostname/blog/index.php?page=users.LoginUser</tt>. Ingat dalam subbagian <a href="?page=Day2.CreateDB">Membuat Database</a>, kita sudah membuat dua akun pengguna (username/password): <tt>admin/demo</tt> dan <tt>demo/demo</tt>. Kita dapat menggunakannya untuk menguji halaman masuk kita.
+</p>
+
+<img src="<%~ output.gif %>" class="output"/>
+
+<h2>Menambahkan Link Login/Logout ke Master</h2>
+
+<p>
+Guna melengkapi cara langsung bagi pengguna untuk masuk dan keluar, kita memodifikasi sedikit kontrol master <tt>MainLayout</tt>. Dalam keadaan tertentu, kita menambahkan hiperlink "login" yang mengaitkan halaman <tt>LoginUser</tt>. Kita juga menambahkan tombol link "logout" yang mengeluarkan pengguna saat ia diklik.
+</p>
+
+<p>
+Kita memodifikasi bagian footer dari template <tt>MainLayout</tt> sebagai berikut. Penampakan "login" dan "logout" ditentukan berdasarkan status pengguna. Jika pengguna belum masuk, misalnya <tt>$this->User->IsGuest</tt> adalah true, link "login" terlihat sementara link "logout" tidak; dan begitu sebaliknya.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<div id="footer">
+&lt;com:THyperLink Text="Login"
+ NavigateUrl="&lt;%= $this->Service->constructUrl('users.LoginUser') %>"
+ Visible="&lt;%= $this->User->IsGuest %>" />
+
+&lt;com:TLinkButton Text="Logout"
+ OnClick="logoutButtonClicked"
+ Visible="&lt;%= !$this->User->IsGuest %>" />
+
+<br/>
+&lt;%= PRADO::poweredByPrado() %>
+</div>
+</com:TTextHighlighter>
+
+<p>
+Karena tombol "logout" menempelkan event <tt>OnClick</tt> dengan metode yang dipanggil <tt>logoutButtonClicked()</tt>, kita juga perlu mengubah file kelas <tt>MainLayout</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class MainLayout extends TTemplateControl
+{
+ /**
+ * Logs out a user.
+ * This method responds to the "logout" button's OnClick event.
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function logoutButtonClicked($sender,$param)
+ {
+ $this->Application->getModule('auth')->logout();
+ $url=$this->Service->constructUrl($this->Service->DefaultPage);
+ $this->Response->redirect($url);
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Sekarang jika kita mengunjungi setiap halaman pada sistem blog kita, kita akah melihat apakah ada link di bawah halaman. Link menampilkan "Login" jika kita belum masuk dan "Logout" jika telah masuk. Jika kita mengklik pada "Logout", browser akan mengalihkannya ke homepage dan "Login" ditampilkan yang berarti kita sudah dikeluarkan.
+</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/CreateNewUser.page b/demos/blog-tutorial/protected/pages/Day3/id/CreateNewUser.page
new file mode 100644
index 00000000..905f7489
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/CreateNewUser.page
@@ -0,0 +1,210 @@
+<com:TContent ID="Main">
+
+<h1>Membuat Halaman <tt>NewUser</tt></h1>
+
+<p>
+Halaman <tt>NewUser</tt> disediakan untuk pengguna administrator guna membuat akun pengguna baru. Ini perlu menampilkan sebuah formulir yang mengumpulkan informasi mengenai akun pengguna baru. Berdasarkan <a href="?page=Day2.CreateDB">definisi database</a>, kita akan perlu mengumpulkan informasi berikut:
+</p>
+
+<ul>
+<li><tt>username</tt> - string, diperlukan dan unik</li>
+<li><tt>email</tt> - string, diperlukan dan unik</li>
+<li><tt>password</tt> - string, diperlukan</li>
+<li><tt>role</tt> - integer, diperlukan (baik 0 ataupun 1)</li>
+<li><tt>first_name</tt> - string, opsional</li>
+<li><tt>last_name</tt> - string, opsional</li>
+</ul>
+
+<p>
+Kita membuat dua file <tt>protected/pages/users/NewUser.page</tt> dan <tt>protected/pages/users/NewUser.php</tt> masing-masing untuk menyimpan template halaman dan kelas halaman.
+</p>
+
+<h2>Membuat Template Halaman</h2>
+<p>
+Berdasarkan pada analisis di atas, kita menulis template halaman seperti berikut:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="My Blog - New User" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Create New User</h1>
+
+<span>Username:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Username"
+ ErrorMessage="Please provide a username."
+ Display="Dynamic" />
+&lt;com:TCustomValidator
+ ControlToValidate="Username"
+ ErrorMessage="Sorry, your username is taken by someone else. Please choose another username."
+ OnServerValidate="checkUsername"
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Username" />
+
+<br/>
+<span>Password:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Password"
+ ErrorMessage="Please provide a password."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Password" TextMode="Password" />
+
+<br/>
+<span>Re-type Password:</span>
+&lt;com:TCompareValidator
+ ControlToValidate="Password"
+ ControlToCompare="Password2"
+ ErrorMessage="Your password entries did not match."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Password2" TextMode="Password" />
+
+<br/>
+<span>Email Address:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Email"
+ ErrorMessage="Please provide your email address."
+ Display="Dynamic" />
+&lt;com:TEmailAddressValidator
+ ControlToValidate="Email"
+ ErrorMessage="You entered an invalid email address."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Email" />
+
+<br/>
+<span>Role:</span>
+<br/>
+&lt;com:TDropDownList ID="Role">
+ &lt;com:TListItem Text="Normal User" Value="0" />
+ &lt;com:TListItem Text="Administrator" Value="1" />
+&lt;/com:TDropDownList>
+
+<br/>
+<span>First Name:</span>
+<br/>
+&lt;com:TTextBox ID="FirstName" />
+
+<br/>
+<span>Last Name:</span>
+<br/>
+&lt;com:TTextBox ID="LastName" />
+
+<br/>
+&lt;com:TButton Text="Create" OnClick="createButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<p>
+Template tidak jauh berbeda dari template <tt>Contact</tt> dan halaman <tt>LoginUser</tt>. Sebagian besar terdiri dari kotak teks dan validator. Beberapa kotak teks, seperti nama pengguna, dikaitkan dengan dua validator karena keterlibatan aturan multipel validasi.
+</p>
+
+
+<h2>Membuat Kelas Halaman</h2>
+
+<p>
+Dari template halaman di atas, kita melihat bahwa kita perlu untuk menulis sebuah kelas halaman yang mengimplementasikan dua pengendali event: <tt>checkUsername()</tt> (disertakan ke event validator kustom <tt>OnServerValidate</tt>) dan <tt>createButtonClicked()</tt> (disertakan ke event tombol "create" <tt>OnClick</tt>). Oleh karena itu, kita menulis kelas halaman seperti berikut:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class NewUser extends TPage
+{
+ /**
+ * Memeriksa apakah nama pengguna sudah ada dalam database.
+ * Metode ini merespon ke event OnServerValidate pada validator kustom nama pengguna.
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function checkUsername($sender,$param)
+ {
+ // benar jika nama pengguna tidak ditemukan dalam database
+ $param->IsValid=UserRecord::finder()->findByPk($this->Username->Text)===null;
+ }
+
+ /**
+ * Membuat akun pengguna baru jika semua input benar.
+ * Metode ini merespon ke event OnClick pada tombol "create".
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function createButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // ketika semua validasi sukses
+ {
+ // populasikan obyek UserRecord dengan input pengguna
+ $userRecord=new UserRecord;
+ $userRecord->username=$this->Username->Text;
+ $userRecord->password=$this->Password->Text;
+ $userRecord->email=$this->Email->Text;
+ $userRecord->role=(int)$this->Role->SelectedValue;
+ $userRecord->first_name=$this->FirstName->Text;
+ $userRecord->last_name=$this->LastName->Text;
+
+ // simpan database melalui mekanisme Rekaman Aktif
+ $userRecord->save();
+
+ // alihkan browser ke homepage
+ $this->Response->redirect($this->Service->DefaultPageUrl);
+ }
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Dalam kode di atas, memanggil <tt>save()</tt> akan menyisipkan baris baru dalam tabel <tt>users</tt>. Fitur intuitif ini dihidupkan oleh <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Rekaman Aktif</a>.
+</p>
+
+<com:NoteBox>
+Untuk memudahkan, nama pengguna dalam sistem blg kita adalah tidak sensitif-huruf! Dalam banyak sistem praktis, nama pengguna mungkin perlu sensitif-huruf. Maka penanganan khusus perlu dibuat saat pembuatan akun pengguna baru dan juga <a href="?page=Day3.Auth">melakukan otentikasi</a>. Juga, ruang kosong dalam nama pengguna perlu dipotong ketika membuat akun baru dengan itu.
+</com:NoteBox>
+
+
+<h2>Pengujian</h2>
+<p>
+Untuk menguji halaman <tt>NewUser</tt>, kunjungi URL <tt>http://hostname/blog/index.php?page=users.NewUser</tt>. Kita akan melihat output halaman berikut. Coba masukan informasi berbeda ke dalam formulir dan lihat bagaimana input divalidasi. Jika semua aturan validasi memuaskan, kita akan mengharapkan akun pengguna dibuat dan browser dialihkan ke homepage.
+</p>
+
+<img src="<%~ output2.gif %>" class="output"/>
+
+
+<h2>Menambahkan Pemeriksaan Perijinan</h2>
+<p>
+Selama pengujian, anda mungkin bertanya: apakah halaman <tt>NewUser</tt> seharusnya hanya bisa diakses oleh pengguna administrator? Ya, ini disebut <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Auth">otorisasi</a>. Sekarang kami menjelaskan bagaimana kita menambahkan pemeriksaan perijinan ini ke halaman <tt>NewUser</tt>.
+</p>
+
+<p>
+Cara langsung pada pemeriksaan perijinan adalah dalam kelas halaman di mana kita memeriksa apakah <tt>$this->User->IsAdmin</tt> adalah true, dan jika tidak, kita mengalihkan browser ke halaman <tt>LoginUser</tt>.
+</p>
+
+<p>
+PRADO menawarkan cara lebih sistematis terhadap pemeriksaan perijinan akses halaman. Untuk melakukannyaTo , kita perlu menggunakan <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.PageConfig">konfigurasi halaman</a>. Membuat sebuah file <tt>protected/pages/users/config.xml</tt> dengan konten seperti berikut:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <authorization>
+ <allow pages="NewUser" roles="admin" />
+ <deny users="?" />
+ </authorization>
+</configuration>
+</com:TTextHighlighter>
+
+<p>
+Konfigurasi halaman berisi aturan otorisasi yang berlaku untuk halaman di bawah direktori <tt>protected/pages/users</tt>. Konfigurasi di atas terbaca bahwa <tt>NewUser</tt> bisa diakses oleh para pengguna dengan aturan <tt>admin</tt> (lihat <a href="?page=Day3.Auth">BlogUser.createUser()</a> untuk alasan kenapa kata "admin"), dan menolak akses anonim (<tt>users="?"</tt> berarti pengguna tamu) untuk seluruh halaman di bawah direktori.
+</p>
+
+<p>
+Sekarang jika kita mengunjungi halaman <tt>NewUser</tt> sebagai seorang tamu, kita akan dialihkan ke halaman <tt>LoginUser</tt> terlebih dahulu. Jika kita masuk dengan sukses, kita akan mengalihkan kembali ke halaman<tt>NewUser</tt>.
+</p>
+
+<com:TipBox>
+Konfigurasi halaman dapat berisi lebih dari satu aturan otorisasi. Sebagai contoh, ia dapat menyertakan <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Modules">modul</a> seperti yang ktia lakukan dalam <a href="?page=Day2.ConnectDB">konfigurasi aplikasi</a>. Untuk aplikasi PRADO, setiap direktori halaman dapat mempunyai konfigurasi halaman yang berlaku bagi halaman dalam direktori yang sama dan seluruh subdirektorinya.
+</com:TipBox>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/Overview.page b/demos/blog-tutorial/protected/pages/Day3/id/Overview.page
new file mode 100644
index 00000000..62c29438
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/Overview.page
@@ -0,0 +1,26 @@
+<com:TContent ID="Main">
+
+<h1>Tinjauan Manajemen Pengguna</h1>
+
+<p>
+Dalam bagian ini, kita membuat halaman yang terkait dengan manajemen pengguna. Dalam keadaan tertentu, kita mengimplementasikan fitur yang dibutuhkan ini: pengguna masuk/keluar, membuat akun pengguna baru dan memutakhirkan/menghapus akun pengguna.
+</p>
+
+<p>
+Berdasarkan kebutuhan, kita perlu membuat halaman berikut. Guna mengatur lebih baik kode kita, halaman terkait-pengguna ini akan dibuat di bawah direktori baru <tt>protected/pages/users</tt>.
+</p>
+
+<ul>
+ <li><tt>LoginUser</tt> menampilkan formulir login untuk memasukan pengguna.</li>
+ <li><tt>NewUser</tt> membuat akun pengguna baru.</li>
+ <li><tt>EditUser</tt> membolehkan pengguna teregistrasi memutakhirkan profilnya.</li>
+ <li><tt>AdminUser</tt> membolehkan administrator untuk mengatur akun pengguna, termasuk setelan tingkat perijinan dan menghapus akun pengguna.</li>
+</ul>
+
+<p>
+Setelah menyelesaikan bagian ini, kita mengharapkan bisa melihat direktori dan file berikut:
+</p>
+
+<img src="<%~ directories.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/directories.gif b/demos/blog-tutorial/protected/pages/Day3/id/directories.gif
new file mode 100644
index 00000000..f59fda58
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/directories.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/output.gif b/demos/blog-tutorial/protected/pages/Day3/id/output.gif
new file mode 100644
index 00000000..0d812dd0
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/output.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/output2.gif b/demos/blog-tutorial/protected/pages/Day3/id/output2.gif
new file mode 100644
index 00000000..749255d6
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/output2.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day3/id/output3.gif b/demos/blog-tutorial/protected/pages/Day3/id/output3.gif
new file mode 100644
index 00000000..a11ee653
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/id/output3.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/CreateEditPost.page b/demos/blog-tutorial/protected/pages/Day4/id/CreateEditPost.page
new file mode 100644
index 00000000..b7ded3b3
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/CreateEditPost.page
@@ -0,0 +1,133 @@
+<com:TContent ID="Main">
+
+<h1>Membuat Halaman <tt>EditPost</tt></h1>
+
+<p>
+Halaman <tt>EditPost</tt> disediakan bagi para pembuat dan administrator untuk mengedit tulisan blog yang sudah ada. Seperti halaman <a href="?page=Day4.CreateNewPost">NewPost</a> , ia menampilkan sebuah formulir untuk mengumpulkan perubahan terhadap judul dan konten tulisan.
+</p>
+
+<p>
+Kita membuat dua file <tt>protected/pages/posts/EditPost.page</tt> dan <tt>protected/pages/posts/EditPost.php</tt> masing-masing untuk menyimpan template halaman dan kelas halaman.
+</p>
+
+<h2>Membuat Template Halaman</h2>
+<p>
+Template halaman <tt>EditPost</tt> sangat mirip dengan template <tt>NewPost</tt>. Hanya judul halaman dan tombol yang berbeda.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="My Blog - Edit Post" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Edit Post</h1>
+
+<span>Title:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="TitleEdit"
+ ErrorMessage="Please provide a title."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="TitleEdit" Columns="50" />
+
+<br/>
+<span>Content:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="ContentEdit"
+ ErrorMessage="Please provide content."
+ Display="Dynamic" />
+<br/>
+&lt;com:THtmlArea ID="ContentEdit" />
+
+<br/>
+&lt;com:TButton Text="Save" OnClick="saveButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+
+<h2>Membuat Kelas Halaman</h2>
+
+<p>
+Kelas halaman <tt>EditPage</tt> lebih kompleks dibanding <tt>NewPage</tt> karena ia perlu mengambil data tulisan yang ditetapkan terlebih dulu. Ia juga perlu melakukan pemeriksaan otorisasi tambahan. Ada kalanya ia perlu memastikan bahwa tulisan hanya bisa diedit oleh pembuat atau administrator. Pemeriksaan otorisasi sudah disediakan oleh PRADO.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class EditPost extends TPage
+{
+ /**
+ * Menginisialisasi input dengan data tulisan yang sudah ada.
+ * Metode ini dipanggil oleh kerangka kerja saat halaman diinisialisasi.
+ * @param parameter event campuran
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ // Menambil data pengguna yang sudah ada. Ini sama dengan:
+ // $postRecord=$this->getPost();
+ $postRecord=$this->Post;
+ // Pemeriksaan otorisasi: hanya pembuat atau administrator dapat mengedit tulisan
+ if($postRecord->author_id!==$this->User->Name && !$this->User->IsAdmin)
+ throw new THttpException(500,'You are not allowed to edit this post.');
+
+ if(!$this->IsPostBack) // jika halaman pertama kali diminta
+ {
+ // Mempopulasikan kontrol input dengan data tulisan yang sudah ada
+ $this->TitleEdit->Text=$postRecord->title;
+ $this->ContentEdit->Text=$postRecord->content;
+ }
+ }
+
+ /**
+ * Menyimpan tulisan jika semua input sudah benar.
+ * Metode ini merespon event OnClick pada tombol "Save".
+ * @param pengirim event campuran
+ * @param parameter event campuran
+ */
+ public function saveButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // jika semua validasi sukses
+ {
+ // Mengambil data pengguna yang sudah ada. Ini sama dengan:
+ // $postRecord=$this->getPost();
+ $postRecord=$this->Post;
+
+ // Mengambil data input
+ $postRecord->title=$this->TitleEdit->SafeText;
+ $postRecord->content=$this->ContentEdit->SafeText;
+
+ // menyimpan ke database via mekanisme Rekaman Aktif
+ $postRecord->save();
+
+ // mengalihkan browser ke halaman ReadPost
+ $url=$this->Service->constructUrl('posts.ReadPost',array('id'=>$postRecord->post_id));
+ $this->Response->redirect($url);
+ }
+ }
+
+ /**
+ * Mengembalikan data tulisan yang akan diedit.
+ * @return PostRecord data tulisan yang akan diedit.
+ * @throws THttpException jika data tulisan tidak ada.
+ */
+ protected function getPost()
+ {
+ // ID tulisan yang diedit dikirimkan via parameter GET 'id'
+ $postID=(int)$this->Request['id'];
+ // gunakan Rekaman Aktif untuk mencari ID tulisan tertentu
+ $postRecord=PostRecord::finder()->findByPk($postID);
+ if($postRecord===null)
+ throw new THttpException(500,'Post is not found.');
+ return $postRecord;
+ }
+}
+</com:TTextHighlighter>
+
+<h2>Pengujian</h2>
+<p>
+Untuk menguji halaman <tt>EditPost</tt>, masuk lebih dulu dan kemudian kunjungi URL berikut: <tt>http://hostname/blog/index.php?page=EditPost&id=1</tt>. URL ini juga bisa dijangkau dengan mengklik link <tt>Edit</tt> pada halaman rician tulisan.
+</p>
+
+<img src="<%~ output4.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/CreateListPost.page b/demos/blog-tutorial/protected/pages/Day4/id/CreateListPost.page
new file mode 100644
index 00000000..d682535e
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/CreateListPost.page
@@ -0,0 +1,185 @@
+<com:TContent ID="Main">
+
+<h1>Membuat Halaman <tt>ListPost</tt></h1>
+
+<p>
+Halaman <tt>ListPost</tt> menampilkan tulisan blog terakhir dalam sebuah daftar. Jika di sana terlalu banyak tulisan, maka akan ditampilkan dalam beberapa lembar halaman.
+</p>
+
+<p>
+Sebelum kita berlanjut dengan implementasi, kita ingin mengarahkan homepage kita ke halaman <tt>ListPage</tt> mendatang, karena kita ingin para pengguna melihat tulisan terakhir saat mereka menyentuh website. Untuk melakukannya, kita mengubah konfigurasi aplikasi <tt>protected/application.xml</tt> sebagai berikut,
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+<services>
+ <service id="page" class="TPageService" DefaultPage="posts.ListPost">
+ <pages MasterClass="Application.layouts.MainLayout" />
+ </service>
+</services>
+</com:TTextHighlighter>
+
+<p>
+Sekarang kita membuat file template dan kelas untuk halaman <tt>ListPost</tt>: <tt>protected/pages/posts/ListPost.page</tt> dan <tt>protected/pages/posts/ListPost.php</tt>.
+</p>
+
+<h2>Membuat Template Halaman</h2>
+<p>
+Berdasarkan pada kebutuhan fungsionalitas halaman <tt>ListPost</tt>, kita akan menggunakan dua kontrol dalam template halaman:
+</p>
+<ul>
+<li><a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Repeater">TRepeater</a>: kontrol ini dipakai terutama untuk menampilkan daftar atas item data. Penyajian dari setiap item data bisa ditetapkan melalui template inline atau kontrol template eksternal (pendekatan yang akan kita gunakan di sini).</li>
+<li><a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Pager">TPager</a>: kontrol ini dipakai untuk memecah daftar item data. Ia berinteraksi dengan pengguna-akhir untuk menentukan halaman data mana yang ditampilkan dalam <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.List">kontrol daftar</a> (misalnya <tt>TListBox</tt>) pada <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Data">kontrol data</a> (misalnya <tt>TRepeater</tt>).</li>
+</ul>
+
+<p>
+Di bawah ini adalah konten dalam template halaman:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="My Blog" %>
+
+&lt;com:TContent ID="Main">
+
+&lt;com:TRepeater ID="Repeater"
+ ItemRenderer="Application.pages.posts.PostRenderer"
+ AllowPaging="true"
+ AllowCustomPaging="true"
+ PageSize="5"
+ />
+
+&lt;com:TPager ControlToPaginate="Repeater" OnPageIndexChanged="pageChanged" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<p>
+Dalam pengulang, kita menetapkan bahwa konten yang diulang ditampilkan menggunakan penyaji item <tt>PostRenderer</tt> yang akan kita buat nantinya. Agar PRADO bisa menemukan kelas ini, kita memberikan namespace <tt>Application.pages.posts.PostRenderer</tt>, berarti file kelasnya adalah <tt>protected/pages/posts/PostRenderer.php</tt>.
+</p>
+
+<p>
+Kita juga menyetel beberapa properti lain pada pengulang untuk menghidupkan lembaran halaman. Dan kita menyetel properti <tt>ControlToPaginate</tt> pada lembaran agar ia mengetahui konten mana yang diulang harus dibuat lembaran.
+</p>
+
+
+<h2>Membuat Kelas Halaman</h2>
+
+<p>
+Dari template halaman di atas, kita melihat bahwa kita perlu menulis kelas halaman yang mengimplementasikan pengendali event: <tt>pageChanged()</tt> (ditempelkan ke event lembaran <tt>OnPageIndexChanged</tt>). Kita juga perlu mempopulasikan data tulisan ke dalam pengulang berdasarkan pada setelan lembaran saat ini. Berikut ini adalah kode sumber lengkap kelas halaman:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class ListPost extends TPage
+{
+ /**
+ * Menginisialisasi pengulang.
+ * Metode ini dipanggil oleh kerangka kerja saat menginisialisasi halaman
+ * @param mixed event parameter
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ if(!$this->IsPostBack) // jika halaman diminta pertama kali
+ {
+ // ambil jumlah total tulisan yang tersedia
+ $this->Repeater->VirtualItemCount=PostRecord::finder()->count();
+ // populasikan data tulisan ke dalam pengulang
+ $this->populateData();
+ }
+ }
+
+ /**
+ * Pengendali event untuk event OnPageIndexChanged pada lembaran.
+ * Metode ini dipanggil saat pengguna mengklik tombol halaman
+ * dan kemudian mengubah halaman tulisan yang ditampilkan.
+ */
+ public function pageChanged($sender,$param)
+ {
+ // ubah indeks halaman sekarang ke yang baru
+ $this->Repeater->CurrentPageIndex=$param->NewPageIndex;
+ // re-populasi data ke dalam pengulang
+ $this->populateData();
+ }
+
+ /**
+ * Menentukan halaman tulisan mana yang ditampilkan dan
+ * mempopulasi pengulang dengan data yang sudah diambil.
+ */
+ protected function populateData()
+ {
+ $offset=$this->Repeater->CurrentPageIndex*$this->Repeater->PageSize;
+ $limit=$this->Repeater->PageSize;
+ if($offset+$limit>$this->Repeater->VirtualItemCount)
+ $limit=$this->Repeater->VirtualItemCount-$offset;
+ $this->Repeater->DataSource=$this->getPosts($offset,$limit);
+ $this->Repeater->dataBind();
+ }
+
+ /**
+ * Mengambil tulisan dari database dengan ofset dan limit.
+ */
+ protected function getPosts($offset, $limit)
+ {
+ // Bentuk kriteria query
+ $criteria=new TActiveRecordCriteria;
+ $criteria->OrdersBy['create_time']='desc';
+ $criteria->Limit=$limit;
+ $criteria->Offset=$offset;
+ // query untuk tulisan dengan kriteria di atas dan informasi pembuat
+ return PostRecord::finder()->withAuthor()->findAll($criteria);
+ }
+}
+</com:TTextHighlighter>
+
+<h2>Membuat <tt>PostRenderer</tt></h2>
+
+<p>
+Kita masih perlu untuk membuat kelas penyaji item <tt>PostRenderer</tt>. Ia mendefinisikan bagaimana setiap tulisan harus ditampilkan dalam pengulang. Kita membuatnya sebagai kontrol template yang membolehkan kita untuk menetapkan penyajian tulisan menggunakan sintaks template fleksibel kita. Template dan file kelas masing-masing disimpan sebagai <tt>PostRenderer.tpl</tt> dan <tt>PostRenderer.php</tt> di bawah direktori <tt>protected/pages/posts</tt>.
+</p>
+
+<h3>Membuat Template Penyaji</h3>
+<p>
+Template penyaji menetapkan penyajian berbagai field dalam sebuah tulisan, termasuk judul, nama pembuat, waktu penulisan dan kontennya. Kita me-link judul tulisan ke <tt>ReadPost</tt> yang menampilkan lebih rinci atas tulisan yang dipilih.
+</p>
+<p>
+Ekspresi <tt>$this->Data</tt> merujuk ke item data yang dikirimkan ke pengulang. Dalam kasus kita, ia adalah obyek <tt>PostRecord</tt>. Perhatikan bagaimana kita mengambil nama pembuat pada tulisan dengan <tt>$this->Data->author->username</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<div class="post-box">
+<h3>
+&lt;com:THyperLink Text="&lt;%# $this->Data->title %>"
+ NavigateUrl="&lt;%# $this->Service->constructUrl('posts.ReadPost',array('id'=>$this->Data->post_id)) %>" />
+</h3>
+
+<p>
+Author:
+&lt;com:TLiteral Text="&lt;%# $this->Data->author->username %>" /><br/>
+Time:
+&lt;com:TLiteral Text="&lt;%# date('m/d/Y h:m:sa', $this->Data->create_time) %>" />
+</p>
+
+<p>
+&lt;com:TLiteral Text="&lt;%# $this->Data->content %>" />
+</p>
+</div>
+</com:TTextHighlighter>
+
+<h3>Membuat Kelas Penyaji</h3>
+<p>
+Kelas penyaji sangat sederhana. Ia diperluas dari <tt>TRepeaterItemRenderer</tt> dan tidak berisi kode apapun.
+</p>
+<com:TTextHighlighter CssClass="source" Language="php">
+class PostRenderer extends TRepeaterItemRenderer
+{
+}
+</com:TTextHighlighter>
+
+<h2>Pengujian</h2>
+<p>
+Untuk menguji halaman <tt>ListPost</tt>, kunjungi URL <tt>http://hostname/blog/index.php</tt> (ingat kita telah menyetel <tt>ListPost</tt> sebagai homepage baru kita). Kita akan melihat hasil seperti berikut. Karena kita hanya mempunyai satu tulisan saat ini, lembaran tidak akan muncul. Nantinya ketika menyelesaikan <tt>NewPost</tt>, kita dapat menambah tulisan lebih banyak dan datang kembali untuk menguji lembaran halaman lagi.
+</p>
+
+<img src="<%~ output.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/CreateNewPost.page b/demos/blog-tutorial/protected/pages/Day4/id/CreateNewPost.page
new file mode 100644
index 00000000..68dcde44
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/CreateNewPost.page
@@ -0,0 +1,136 @@
+<com:TContent ID="Main">
+
+<h1>Membuat Halaman <tt>NewPost</tt></h1>
+
+<p>
+Halaman <tt>NewPost</tt> disediakan untuk mengotentikasi pengguna untuk pembuatan tulisan blog baru. Ia perlu untuk menampilkan formulir yang mengumpulkan informasi mengenai tulisan baru, termasuk judul tulisan dan konten badan tulisan.
+</p>
+
+<p>
+Karena <tt>NewPost</tt> hanya bisa diakses oleh pengguna terotentikasi, kita menambahkan file konfigurasi <tt>config.xml</tt> di bawah direktori <tt>protected/pages/posts</tt>. Konfigurasi menetapkan bahwa para pengguna tidak bisa mengakses <tt>NewPost</tt> dan <tt>EditPost</tt> yang akan diperkenalkan dalam bagian berikutnya.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <authorization>
+ <deny pages="NewPost,EditPost" users="?" />
+ </authorization>
+</configuration>
+</com:TTextHighlighter>
+
+<p>
+Karena jumlah halaman kita berkembang, kita ingin memodifikasi <tt>MainLayout</tt> agar dalam footer pada halaman blog kita ada link ke berbagai halaman, termasuk homepage, halaman <a href="?page=Day3.CreateNewUser">NewUser</a> (hanya terlihat oleh administrator), dan halaman <tt>NewPost</tt> mendatang (hanya terlihat oleh pengguna terotentikasi).
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<div id="footer">
+&lt;com:THyperLink Text="Home"
+ NavigateUrl="&lt;%= $this->Service->DefaultPageUrl %>" />
+
+&lt;com:THyperLink Text="New Post"
+ NavigateUrl="&lt;%= $this->Service->constructUrl('posts.NewPost') %>"
+ Visible="&lt;%= !$this->User->IsGuest %>" />
+
+&lt;com:THyperLink Text="New User"
+ NavigateUrl="&lt;%= $this->Service->constructUrl('users.NewUser') %>"
+ Visible="&lt;%= $this->User->IsAdmin %>" />
+...other links...
+</div>
+</com:TTextHighlighter>
+
+<p>
+Sekarang kita membuat dua file <tt>protected/pages/posts/NewPost.page</tt> dan <tt>protected/pages/posts/NewPost.php</tt> masing-masing untuk menyimpan template halaman dan kelas halaman.
+</p>
+
+<h2>Membuat Template Halaman</h2>
+<p>
+Template halaman <tt>NewPost</tt> berisi <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.TextBox">TTextBox</a> untuk mengumpulkan judul tulisan dan <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.HtmlArea">THtmlArea</a> untuk mengumpulkan konten tulisan. Yang terakhir adalah editor HTML WYSIWYG. Guna memastikan bahwa input pengguna sudah benar, kita mengaitkan validator dengan kontrol input ini.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="My Blog - New Post" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Create New Post</h1>
+
+<span>Title:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="TitleEdit"
+ ErrorMessage="Please provide a title."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="TitleEdit" Columns="50" />
+
+<br/>
+<span>Content:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="ContentEdit"
+ ErrorMessage="Please provide content."
+ Display="Dynamic" />
+<br/>
+&lt;com:THtmlArea ID="ContentEdit" />
+
+<br/>
+&lt;com:TButton Text="Create" OnClick="createButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+
+<h2>Membuat Kelas Halaman</h2>
+
+<p>
+Dari template halaman di atas, kita melihat bahwa sebagian besar kita perlu menulis sebuah kelas halaman yang mengimplementasikan pengendali event: <tt>createButtonClicked()</tt> (ditempelkan ke tombo <tt>Create</tt> dalam event <tt>OnClick</tt>).
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class NewPost extends TPage
+{
+ /**
+ * Membuat tulisan baru jika semua input benar.
+ * Metode ini merespon event OnClick pada tombol "create".
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function createButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // bila semua validasi sukses
+ {
+ // populasikan obyek PostRecord dengan input pengguna
+ $postRecord=new PostRecord;
+ // menggunakan SafeText datipada Text guna menghindari serangan Penaskahan Situs Silang
+ $postRecord->title=$this->TitleEdit->SafeText;
+ $postRecord->content=$this->ContentEdit->SafeText;
+ $postRecord->author_id=$this->User->Name;
+ $postRecord->create_time=time();
+ $postRecord->status=0;
+
+ // simpan ke database lewat mekanisme Rekaman Aktif
+ $postRecord->save();
+
+ // alihkan browser ke halaman tulisan yang baru dibuat
+ $url=$this->Service->constructUrl('posts.ReadPost',array('id'=>$postRecord->post_id));
+ $this->Response->redirect($url);
+ }
+ }
+}
+</com:TTextHighlighter>
+
+<h2>Pengujian</h2>
+<p>
+Untuk menguji halaman <tt>NewPost</tt>, masuk lebih dulu dan klik pada link tombol <tt>New Post</tt> dalam footer pada homepage. Browser kita akan menampilkan hasil berikut dengan URL <tt>http://hostname/blog/index.php?page=NewPost</tt>.
+</p>
+
+<com:InfoBox>
+Ketika anda mengunjungi halaman <tt>NewPost</tt> untuk pertama kali, anda bisa melihat bahwa ia memerlukan beberapa detik sebelum halaman ditampilkan. Ini dikarenakan PRADO perlu mengurai dan mempublikasikan kode javascript dan gambar untuk kontrol <tt>THtmlArea</tt> yang dipakai dalam halaman. Ini dikerjakan sekali dan untuk semuanya.
+</com:InfoBox>
+
+<com:TipBox>
+Untuk menguji fitur lembaran yang kita kembangkan untuk halaman <a href="?page=Day4.CreateListPost">ListPost</a>, kita dapat membuat lima atau lebih tulisan dan melihat apa yang terjadi pada homepage. Lembar dalam <tt>ListPost</tt> menampilkan lima tulisan setiap halamannya.
+</com:TipBox>
+
+<img src="<%~ output3.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/CreateReadPost.page b/demos/blog-tutorial/protected/pages/Day4/id/CreateReadPost.page
new file mode 100644
index 00000000..83c059e7
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/CreateReadPost.page
@@ -0,0 +1,135 @@
+<com:TContent ID="Main">
+
+<h1>Membuat Halaman <tt>ReadPost</tt></h1>
+
+<p>
+Halaman <tt>ReadPost</tt> menampilkan rincian konten tulisan blog. Untuk para pengguna yang diotorisasi, akan ditampilkan tombol link yang membolehkan mereka untuk mengedit atau menghapus tulisan.
+</p>
+
+<p>
+Kita membuat dua file <tt>protected/pages/posts/ReadPost.page</tt> dan <tt>protected/pages/posts/ReadPost.php</tt> masing-masing untuk menyimpan template halaman dan kelas halaman.
+</p>
+
+<h2>Membuat Template Halaman</h2>
+<p>
+Template halaman <tt>ReadPost</tt> sangat mirip dengan template <tt>PostRenderer</tt>, keduanya menyajikan konten tulisan. Perbedaannya adalah bahwa <tt>ReadPost</tt> perlu menampilkan dua tombol link ketika pengguna saat ini diotorisasi untuk mengedit atau menghapus tulisan.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;com:TContent ID="Main">
+
+<h2>
+&lt;com:TLiteral Text="&lt;%= $this->Post->title %>" />
+</h2>
+
+&lt;com:TControl Visible="&lt;%= $this->canEdit() %>">
+ <a href="&lt;%= $this->Service->constructUrl('EditPost',array('id'=>$this->Post->post_id))%>">Edit</a> |
+ &lt;com:TLinkButton Text="Delete"
+ OnClick="deletePost"
+ Attributes.onclick="javascript:if(!confirm('Are you sure?')) return false;" />
+&lt;/com:TControl>
+
+<p>
+Author:
+&lt;com:TLiteral Text="&lt;%= $this->Post->author->username %>" /><br/>
+Time:
+&lt;com:TLiteral Text="&lt;%= date('m/d/Y h:m:sa', $this->Post->create_time) %>" />
+</p>
+
+<p>
+&lt;com:TLiteral Text="&lt;%= $this->Post->content %>" />
+</p>
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<p>
+Banyak ekspresi PHP dipakai dalam template di atas. Ekspresi <tt>$this->Post</tt> merujuk ke properti yang didefinisikan dalam kelas halaman <tt>ReadPost</tt>. Ia mewakili obyek <tt>PostRecord</tt> yang terkait dengan tulisan yang saat ini sedang dilihat.
+</p>
+
+<com:InfoBox>
+Meskipun sebagian besar kita menggunakan ekspresi dalam template, we do not overuse them. A major guideline in determining whether we should use an expression in a template is that <i>the expression should be a property or a simple presentational transformation of the property</i>. By following this guideline, we ensure content and presentation are well separated without losing sufficient flexibility.
+</com:InfoBox>
+
+<p>
+Kita juga mencatatan dalam template di atas bahwa dua link tombol dikurung di dalam <tt>TControl</tt> yang penampakannya ditentukan oleh ekspresi <tt>$this->canEdit()</tt>. Untuk link tombol <tt>Delete</tt>, kita menggunakan dialog konfirmasi javascript untuk memperoleh konfirmasi pengguna saat ia mengklik untuk menghapus tulisan.
+</p>
+
+<com:InfoBox>
+Seluruh kontrol PRADO mempunyai properti yang sangat berguna bernama <tt>Attributes</tt> yang dapat menerima pasangan nama-nilai bebas. Kebanyakan kontrol PRADO akan menyajikan pasangan nama-nilai dalam <tt>Attributes</tt> secara literal terkait tag HTML. Sebagai contoh, daam link tombol <tt>Delete</tt> di atas, kita mendefinisikan sebuah <tt>onclick</tt> yang disajikan sebagai atribut <tt>onclick</tt> yang menghasilkan tag <tt>&lt;a&gt;</tt>.
+</com:InfoBox>
+
+
+<h2>Membuat Kelas Halaman</h2>
+
+<p>
+Dari template halaman di atas, kita melihat bahwa kita perlu menulis kelas halaman yang mengimplementasikan pengendali event: <tt>deletePost()</tt> (ditempelkan ke tombol <tt>Delete</tt> dalam event <tt>OnClick</tt>). Kita juga perlu untuk mengambil data tulisan yang ditetapkan oleh ID tulisan melalui parameter GET <tt>id</tt>. </p>
+
+<com:InfoBox>
+Kita mengimplementasikan fitur penghapusan tulisan dalam halaman <tt>ReadPost</tt> karena ini sangat alami untuk melakukannya di sini. Ketika pengguna mengklik pada tombol <tt>Delete</tt>, dialog konfirmasi javascript akan muncul. Jika pengguna mengkonfirmasinya, penghapusan akan dibawa dalam respon terhadap event <tt>OnClick</tt> dari tombol <tt>Delete</tt>.
+</com:InfoBox>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class ReadPost extends TPage
+{
+ private $_post;
+ /**
+ * Mengambil data tulisan.
+ * Metode ini dipanggil oleh kerangka kerja saat inisialisasi halaman
+ * @param mixed event parameter
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ // id tulisan dikirimkan via parameter GET 'id'
+ $postID=(int)$this->Request['id'];
+ // mengambil PostRecord dengan informasi pembuat terisi dalam
+ $this->_post=PostRecord::finder()->withAuthor()->findByPk($postID);
+ if($this->_post===null) // jika id tulisan tidak benar
+ throw new THttpException(500,'Unable to find the specified post.');
+ // setel judul halaman sebagai judul tulisan
+ $this->Title=$this->_post->title;
+ }
+
+ /**
+ * @return PostRecord yang saat ini sedang dilihat
+ */
+ public function getPost()
+ {
+ return $this->_post;
+ }
+
+ /**
+ * Menghapus tulisan yang saat ini sedang dilihat
+ * Metode ini dipanggil saat pengguna mengklik tombol "Delete"
+ */
+ public function deletePost($sender,$param)
+ {
+ // hanya pembuat atau administrator bisa menghapus tulisan
+ if(!$this->canEdit())
+ throw new THttpException('You are not allowed to perform this action.');
+ // hapus dari DB
+ $this->_post->delete();
+ // alihkan browser ke homepage
+ $this->Response->redirect($this->Service->DefaultPageUrl);
+ }
+
+ /**
+ * @return boolean apakah pengguna saat ini bisa mengedit/menghapus tulisan yg sedang dilihat
+ */
+ public function canEdit()
+ {
+ // hanya pembuat atau administrator bisa mengedi/menghapus tulisan
+ return $this->User->Name===$this->Post->author_id || $this->User->IsAdmin;
+ }
+}
+</com:TTextHighlighter>
+
+<h2>Pengujian</h2>
+<p>
+Untuk menguji halaman <tt>ReadPost</tt>, kunjungi URL <tt>http://hostname/blog/index.php</tt> dan klik pada judul tulisan. Browser kita akan menampilkan hasil berikut dengan URL <tt>http://hostname/blog/index.php?page=ReadPost&id=1</tt>. Catatan, jika kita tidak masuk, dua tombol link tidak akan terlihat.
+</p>
+
+<img src="<%~ output2.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/Overview.page b/demos/blog-tutorial/protected/pages/Day4/id/Overview.page
new file mode 100644
index 00000000..3427e11f
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/Overview.page
@@ -0,0 +1,26 @@
+<com:TContent ID="Main">
+
+<h1>Tinjauan Manajemen Tulisan</h1>
+
+<p>
+Pada bagian ini, kita membuat halaman yang terkait dengan manajemen tulisan. Dalam keadaan tertentu, kita mengimplementasikan operasi CRUD (Create-Retrieve-Update-Delete) dengan memperhatikan tulisan blog.
+</p>
+
+<p>
+Berdasarkan pada kebutuhan, kita perlu membuat halaman berikut yang diatur di bawah direktori baru <tt>protected/pages/posts</tt>.
+</p>
+
+<ul>
+ <li><tt>ListPost</tt> menampilkan tulisan dengan waktu pembuatan dalam urutan mengecil.</li>
+ <li><tt>ReadPost</tt> menampilkan rincian tulisan.</li>
+ <li><tt>NewPost</tt> membolehkan pengguna teregistrasi untuk membuat tulisan baru.</li>
+ <li><tt>EditPost</tt> membolehkan pembuat atau administrator untuk mengedit tulisan yang sudah ada.</li>
+</ul>
+
+<p>
+Setelah menyelesaikan bagian ini, kita akan mengharapkan untuk melihat direktori dan file berikut:
+</p>
+
+<img src="<%~ directories.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/directories.gif b/demos/blog-tutorial/protected/pages/Day4/id/directories.gif
new file mode 100644
index 00000000..5ba55184
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/directories.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/output.gif b/demos/blog-tutorial/protected/pages/Day4/id/output.gif
new file mode 100644
index 00000000..8c1caea8
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/output.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/output2.gif b/demos/blog-tutorial/protected/pages/Day4/id/output2.gif
new file mode 100644
index 00000000..7078e6c6
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/output2.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/output3.gif b/demos/blog-tutorial/protected/pages/Day4/id/output3.gif
new file mode 100644
index 00000000..ff1834a4
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/output3.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/id/output4.gif b/demos/blog-tutorial/protected/pages/Day4/id/output4.gif
new file mode 100644
index 00000000..b1208a0d
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/id/output4.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day5/id/ErrorLogging.page b/demos/blog-tutorial/protected/pages/Day5/id/ErrorLogging.page
new file mode 100644
index 00000000..9d69f1dd
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/id/ErrorLogging.page
@@ -0,0 +1,159 @@
+<com:TContent ID="Main">
+
+<h1>Penanganan Kesalahan dan Pencatatan</h1>
+
+<p>
+Jika kita mencoba mengakses URL <tt>http://hostname/blog/index.php?page=EditPost&id=100</tt>, kita akan melihat halaman kesalahan berikut karena tulisan dengan ID 100 belum ada dalam sistem blog kita. Kita ingin mengkustomisasi halaman kesalahan ini agar ia terlihat lebih konsisten dengan tata letak halaman blog lain. Kita juga ingin mencatat jenis kesalahan ini untuk mempelajari kebiasaan pengguna. Dalam bagian ini, kita akan melaksanakan dua tugas ini.
+</p>
+
+<img src="<%~ output2.gif %>" class="output" />
+
+<com:InfoBox>
+Tugas penting dalam aplikasi Web adalah <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Error">penanganan kesalahan</a> yang sering dikaitkan dengan <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Logging">pencatatan</a>. Ada dua jenis kesalahan yang bisa terjadi dalam aplikasi PRADO: yang disebabkan oleh para pengembang dan yang disebabkan oleh pengguna-akhir. Pembentuk harus dipecahkan sebelum aplikasi dijadikan tahap produksi, sementara yang terakhir biasanya di dalam lingkup desain awal dan harus ditangani dengan baik (misalnya mencatat kesalahan dan menampilkan halaman khusus yang menginstruksikan pengguna-akhir apa yang harus dilakukan selanjutnya). PRADO mengimplementasikan kerangka kerja yang fleksibel serta bertenaga untuk menangani kesalahan dan pencatatan.
+</com:InfoBox>
+
+
+<h2>Mengkustomisasi Penanganan Kesalahan</h2>
+
+<p>
+PRADO secara implisit mengambil modul <tt>TErrorHandler</tt> untuk menangani kesalahan. Kita ingin mengkustomisasi modul ini agar sistem blog kita dapat menampilkan halaman terkustomisasi untuk kesalahan yang disebabkan oleh pengguna-akhir. Selanjutnya memodifikasi konfigurasi aplikasi seperti berikut:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+<modules>
+ ......
+ <module class="Application.BlogErrorHandler" />
+ ......
+</modules>
+......
+</com:TTextHighlighter>
+
+<p>
+Kelas <tt>BlogErrorHandler</tt> seperti ditetapkan di atas adalah modul pengendali kesalahan baru as yang akan dibuat berikutnya. Ia memperluas dan mengganti modul standar <tt>TErrorHandler</tt>.
+</p>
+
+<p>
+Kita membuat sebuah file bernama <tt>protected/BlogErrorHandler.php</tt> seperti berikut. Kelas <tt>BlogErrorHandler</tt> mengganti dua metode <tt>TErrorHandler</tt>:
+</p>
+<ul>
+ <li><tt>getErrorTemplate()</tt> - metode ini mengembalikan string template yang dipakai untuk menampilkan kesalahan pengguna tertentu.</li>
+ <li><tt>handleExternalError()</tt> - metode ini dipanggil saat kesalahan pengguna terjadi dan menampilkan kesalahannya.</li>
+</ul>
+<com:TTextHighlighter CssClass="source" Language="php">
+Prado::using('System.Exceptions.TErrorHandler');
+Prado::using('Application.BlogException');
+
+class BlogErrorHandler extends TErrorHandler
+{
+ /**
+ * Mengambil template yang dipakai untuk menampilkan eksepsi eksternal.
+ * Metode ini mengganti implementasi leluhurnya.
+ */
+ protected function getErrorTemplate($statusCode,$exception)
+ {
+ // gunakan template sendiri untuk BlogException
+ if($exception instanceof BlogException)
+ {
+ // ddapatkan path file template kesalahan: protected/error.html
+ $templateFile=Prado::getPathOfNamespace('Application.error','.html');
+ return file_get_contents($templateFile);
+ }
+ else // sebaliknya gunakan template yang didefinisikan oleh PRADO
+ return parent::getErrorTemplate($statusCode,$exception);
+ }
+
+ /**
+ * Menangani kesalahan eksternal yang disebabkan oleh pengguna-akhir.
+ * Metode ini mengganti implementasi leluhurnya.
+ * Ini dipanggil oleh PRADO saat eksepsi eksternal dikeluarkan.
+ */
+ protected function handleExternalError($statusCode,$exception)
+ {
+ // catat kesalahan (hanya untuk BlogException)
+ if($exception instanceof BlogException)
+ Prado::log($exception->getErrorMessage(),TLogger::ERROR,'BlogApplication');
+ // panggil implementasi leluhur untuk menampilkan kesalahan
+ parent::handleExternalError($statusCode,$exception);
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Dalam kode di atas, kita menetapkan bahwa saat <tt>BlogException</tt> dikeluarkan, kita menggunakan template baru <tt>protected/error.html</tt> untuk menampilkan kesalahan. Oleh karena itu, kita perlu membuat kelas <tt>BlogException</tt> dan mengganti semua <tt>THttpException</tt> yang ada dalam kode kita (seperti misalnya <a href="?page=Day3.CreateEditUser">EditUser</a> dan halaman <a href="?page=Day4.CreateReadPost">ReadPost</a>). Kita juga perlu membuat template kesalahan <tt>protected/error.html</tt>. Kelas <tt>BlogException</tt> memperluas <tt>THttpException</tt> dan kosong. File kelas disimpan sebagai <tt>protected/BlogException.php</tt>.
+
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class BlogException extends THttpException
+{
+}
+</com:TTextHighlighter>
+
+<p>
+Di bawah ini konten dalam template kesalahan kita <tt>protected/error.html</tt>. Catatan, template bukan template PRADO karena ia hanya mengenal jumlah token yang sangat terbatas, seperti <tt>%%ErrorMessage%%</tt>, <tt>%%ServerAdmin%%</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<html>
+<head>
+<title>%%ErrorMessage%%</title>
+</head>
+<body>
+<div id="page">
+<div id="header">
+<h1>My PRADO Blog</h1>
+</div>
+<div id="main">
+<p style="color:red">%%ErrorMessage%%</p>
+<p>
+The above error happened when the server was processing your request.
+</p>
+<p>
+If you think this is a server error, please contact the <a href="mailto:%%ServerAdmin%%">webmaster</a>.
+</p>
+</div>
+</body>
+</html>
+</com:TTextHighlighter>
+
+
+<h2>Mencatat Kesalahan</h2>
+
+<p>
+Dalam metode <tt>handleExternalError()</tt> pada <tt>BlogErrorHandler</tt>, kita memanggil <tt>Prado::log()</tt> untuk mencatat kesalahan jika tipenya adalah <tt>BlogException</tt>. Kesalahan dicatat dalam memori. Untuk menyimpan catatan ke dalam medium permanen seperti file atau database, kita perlu menghidupkan rute pencatatan kesalahan yang sesuai. Ini dikerjakan dalam konfigurasi aplikasi seperti berikut:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+<modules>
+ ......
+ <module id="log" class="System.Util.TLogRouter">
+ <route class="TFileLogRoute" Categories="BlogApplication" />
+ </module>
+ ......
+</modules>
+......
+</com:TTextHighlighter>
+
+<p>
+Dalam konfigurasi di atas, kita menambahkan rute log yang menyimpan catatan ke dalam sebuah file. Kita juga menetapkan filter kategori sebagai <tt>BlogApplication</tt> agar hanya mencatat pesan pada kategori yang dipilih yang disimpan. Ini membantu mengurangi ukuran file catatan dan juga meningkatkan pembacaannya.
+</p>
+
+<h2>Pengujian</h2>
+<p>
+Untuk melihat bagaimana sistem blog kita merespon permintaan tidak benar dari pengguna, kita menguji URL <tt>http://hostname/blog/index.php?page=posts.ReadPost&id=100</tt>. Kita akan melihat halaman kesalahan berikut yang berbeda dari apa yang telah kita lihat sebelumnya.
+</p>
+
+<img src="<%~ output3.gif %>" class="output" />
+
+<p>
+Jika kita mencari di bawah direktori <tt>protected/runtime</tt>, kita akan menemukan file bernama <tt>prado.log</tt>. Ini adalah file log yang baru dikonfigurasi untuk menyimpan pesan kesalahan. File dapat berisi konten seperti berikut,
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+Jun 28 22:15:27 [Error] [BlogApplication] Unable to find the specified post.
+Jun 29 08:42:57 [Error] [BlogApplication] Unable to find the specified post.
+</com:TTextHighlighter>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day5/id/Performance.page b/demos/blog-tutorial/protected/pages/Day5/id/Performance.page
new file mode 100644
index 00000000..62a195b3
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/id/Performance.page
@@ -0,0 +1,67 @@
+<com:TContent ID="Main">
+
+<h1>Penyesuaian Performansi</h1>
+
+<p>
+Sebelum kita menyebarkan sistem blog, kita ingin menyesuaikan performansi sistem.
+</p>
+
+<h2>Mengubah Mode Aplikasi</h2>
+
+<p>
+Aplikasi PRADO dapat dikonfigurasi untuk berjalan dalam mode berbeda. Standarnya, ia berjalan dalam mode debug yang membuat banyak catatan pesan dan seandainya ada kesalahan, menampilkan pangilan stack lengkap atas tempat kesalahan. Perilaku demikian lebih disukai selama pengembangan, tapi tidak jika sistem sudah dalam produksi. Untuk mengubah mode aplikasi dari <tt>Debug</tt> ke <tt>Normal</tt> (berarti mode produksi), kita memodifikasi konfigurasi aplikasi seperti berikut:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<application id="blog" mode="Normal">
+ ......
+</application>
+</com:TTextHighlighter>
+
+<h2>Menghidupkan Cache</h2>
+
+<p>
+Ada banyak pekerjaan penguraian terkait dalam sebuah aplikasi PRADO: konfigurasi XML, template, tema, skin, dll. Untuk setiap permintaan pengguna, PRADO perlu melakukan ulang penguraian. Untuk menghemat usaha ini, kita dapat menghidupkan cache. Untuk melakukannya, kita mengubah konfigurasi aplikasi seperti berikut,
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+<modules>
+ ......
+ <module id="cache" class="System.Caching.TDbCache" />
+ ......
+</modules>
+......
+</com:TTextHighlighter>
+
+<p>
+Sekarang setelah mengakses setiap halaman dalam sistem blog kita, kita akan dapat menemukan file bernama <tt>sqlite3.cache</tt>. Ini adalah file database yang memelihara template halaman yang diuraikan, konfigurasi, dll.
+</p>
+
+<com:InfoBox>
+Modul cache yang baru kita hidupkan menggunakan database sebagai medium cache persisten. PRADO juga mempunyai modul cache lain yang menggunakan medium cache lebih cepat, seperti misalnya <tt>TMemCache</tt>, <tt>TAPCCache</tt>. Diperlukan instalasi dari ekstensi PHP terkait.
+</com:InfoBox>
+
+
+<h2>Menggunakan <tt>pradolite.php</tt></h2>
+
+<p>
+Menjalankan halaman PRADO melibatkan puluhan file PHP, yang dapat menghabiskan waktu. File-file ini juga membawa banyak komentar untuk membuat dokumentasi APU ramah-pengguna. Untuk mengurangi beban ini, kita memodifikasi <tt>index.php</tt> dan mengganti inklusi <tt>prado.php</tt> dengan <tt>pradolite.php</tt>. Yang kedua adalah file besar yang dibuat dengan menggabung file kode tertentu dan membuang komentar. Selanjutnya kita bisa mengubah <tt>index.php</tt> seperti berikut,
+</p>
+
+<h2>Teknik Lain</h2>
+
+<p>
+Ada teknik lain untuk meningkatkan performansi lebih lanjut terhadap aplikasi PRADO. Berdasarkan pengalaman kami, salah satu lubang botol dalam aplikasi Web adalah database tier. Query database seringkali membutuhkan waktu lama untuk menyelesaikannya, yang memperlambat waktu respon terhadap permintaan sebuah halaman. Cache adalah solusi utama untuk masalah ini. Modul cache yang dihidupkan dalam konfigurasi aplikasi kita juga bisa dipakai untuk keperluan ini.
+</p>
+
+<p>
+Untuk halaman yang relatif stabil dan jarang diakses, <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.OutputCache">output caching</a> bisa dipertimbangkan. Output caching melakukan cache output HTML dari bagian yang dipilih pada sebuah halaman. Ini dapat meningkatkan performansi dari halaman yang di-cache secara signifikan.
+</p>
+
+<p>
+Teknik cache server terbukti sangat efektif dalam meningkatkan performansi atas aplikasi PRADO. Sebagai contoh, kita telah mengamati bahwa dengan menggunakan Zend Optimizer, RPS (request per second) dari aplikasi PRADO bisa ditingkatkan lebih dari sepuluh kali lipat. Tentu saja, ini pada beban dari output lama, sementara teknik cache PRADO selalu memastikan kebenaran dari output.
+</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day5/id/Summary.page b/demos/blog-tutorial/protected/pages/Day5/id/Summary.page
new file mode 100644
index 00000000..eff9c2a9
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/id/Summary.page
@@ -0,0 +1,36 @@
+<com:TContent ID="Main">
+
+<h1>Ringkasan</h1>
+
+<p>
+Akhirnya kita dapat menyebarkan sistem blog kita. Untuk melakukannya, kita hanya perlu meng-copy seluruh direktori <tt>blog</tt> directory ke direktori Web sasaran. Kita perlu untuk memodifikasi <tt>index.php</tt> agar ia dapat mencari path yang benar di mana kerangka kerja PRADO diinstalasi.
+</p>
+
+<p>
+Dengan demikian kita telah menyelesaikan sistem blog kita. Proses terlihat tidak gampang karena ia memerlukan hampir lima hari untuk sampai di sini. Akan tetapi, karena kita memulainya dari awal, tujuan utama dari tutorial ini adalah untuk membantu para pengembang PRADO terbiasa dengan teknik yang umum digunakan dalam PRADO. Tutorial tidak berarti menyelesaikan sistem blog dalam waktu lima menit dan selanjutnya tidak mempelajari apapun.
+</p>
+
+<p>
+Secara ringkas, mengembangkan aplikasi PRADO dengan kendali-DB melibatkan langkah-langkah berikut:
+</p>
+<ol>
+ <li>Mendesain dan membuat database</li>
+ <li>Membuat tata letak aplikasi awal menggunakan <tt>prado-cli</tt></li>
+ <li>Menyiapkan proses pengendalian kesalahan untuk menghadapi kesalahan pengguna-akhir</li>
+ <li>Membuat dan menyiapkan tema</li>
+ <li>Mendesain dan membuat kelas master untuk berbagi tata letak umum halaman</li>
+ <li>Membuat kelas database dan menyiapkan koneksi database</li>
+ <li>Mendesain dan membuat bermacam-macam halaman</li>
+ <li>Menguji dan menyesuaikan performansi</li>
+ <li>Menyebarkan aplikasi</li>
+</ol>
+
+<p>
+Tidak seperti urutan dalam tutorial kita, pengendalian kesalahan dan pembuatan tema dikerjakan lebih awal dalam proses di atas. Ini dikarenakan seringkali diperlukan perubahan besar dalam kode kelas dan template. Sebagai contoh, kita perlu mengganti <tt>THttpException</tt> dengan <tt>BlogException</tt> dalam tutorial kita. Jika kita mendefinisikan kelas stylesheet lebih awal, kita dapat dengan mudah menggunakannya saat membuat template halaman.
+</p>
+
+<p>
+Sebagai tips terakhir, coba untuk berpikir dalam cara terorientasi-obyek selama mendesain dan mengimplementasikan. Gunakan turunan kelas dan komposisi secara luas, dan anda akan menemukan seluruh proyek lebih mudah dikembangkan secara paralel oleh multipel pengembang. Kode juga lebih banya memiliki kesempatan dipakai ulang agar proyek mendatang dapat diselesaikan dalam waktu yang lebih cepat.
+</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day5/id/UseTheme.page b/demos/blog-tutorial/protected/pages/Day5/id/UseTheme.page
new file mode 100644
index 00000000..6766d659
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/id/UseTheme.page
@@ -0,0 +1,138 @@
+<com:TContent ID="Main">
+
+<h1>Menggunakan Tema dan Skin</h1>
+
+<p>
+PRADO memiliki dukungan dasar untuk <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Themes">tema</a>. Dengan menggunakan tema, kita dapat memisahkan logika dan penyajian secara lebih baik, dan kita juga bisa mengubah penampilan keseluruhan pada sistem blog kita jauh lebih mudah.
+</p>
+
+<h2>Membuat Tema</h2>
+
+<p>
+Pertama kita buat direktori baru bernama <tt>themes</tt>. Ini adalah direktori leluhur bagi semua tema terkait aplikasi PRADO tertentu. Setiap subdirektori di bawah direktori ini yang nama temanya adalah nama subdirektori.
+</p>
+
+<p>
+Untuk membuat tema bernama <tt>Basic</tt>, kita membuat subdirektori <tt>theme/Basic</tt>. Di bawah direktori ini, kita dapat menempatkan file stylesheet dependen-tema, file Javascript, gambar, dan file skin.
+</p>
+
+<com:InfoBox>
+Direktori <tt>themes</tt> harus bisa diakses dari Web, seperti halnya direktori <tt>assets</tt>. Jangan menempatkan file data sensitif di bawah direktori ini. Anda bisa mengubah nama atau lokasi dari direktori ini dengan mengkonfigurasi modul <a href="http://www.pradosoft.com/docs/classdoc/TThemeManager">TThemeManager</a> dalam konfigurasi aplikasi.
+</com:InfoBox>
+
+
+<h3>Membuat File Stylesheet</h2>
+
+<p>
+Di bawah direktori <tt>themes/Basic</tt>, kita membuat sebuah file CSS stylesheet bernama <tt>style.css</tt>. ketika sebuah halaman menggunakan tema ini, PRADO secara otomatis akan mengimpor stylesheet ini ke halaman. Hal yang sama terjadi untuk file Javascript.
+</p>
+
+<p>
+File CSS ditampilkan seperti berikut.
+</p>
+
+<com:TTextHighlighter CssClass="source">
+body {
+ font-family: verdana, 'trebuchet ms', sans-serif;
+ font-size: 10pt;
+ background: white;
+}
+#page {
+ margin: 0 auto 0 auto;
+ width: 600px;
+}
+#footer {
+ text-align: center;
+ margin-top: 10px;
+ padding: 10px;
+ border-top: 1px solid silver;
+}
+.post-box {
+ margin-bottom: 10px;
+ padding: 5px;
+}
+.post-box h3 {
+ padding: 5px;
+ font-size: 13pt;
+ background: lightgray;
+}
+.post-box a {
+ color: black;
+ text-decoration: none;
+}
+.post-box a:hover {
+ color: red;
+}
+</com:TTextHighlighter>
+
+
+<h3>Membuat File Skin</h2>
+
+<p>
+Kita menggunakan skin untuk menginisialisasi properti kontrol PRADO. Skin disimpan sebagai file skin (nama berakhiran <tt>.skin</tt>) di bawah direktori tema. Setiap file skin bisa berisi multipel skin untuk satu atau beberapa tipe kontrol.
+</p>
+
+<p>
+Sebagai pengujian, kita akan mencoba membuat sebuah skin yang mengubah warna latar belakang link tombol dalam footer halaman. Kita membuat sebuah file bernama <tt>button.skin</tt> di bawah direktori tema <tt>themes/Basic</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;com:THyperLink SkinID="MainMenu" BackColor="lightgreen" />
+</com:TTextHighlighter>
+
+<p>
+File skin <tt>button.skin</tt> hanya berisi satu skin untuk kontrol <tt>THyperLink</tt> yang properti <tt>SkinID</tt>-nya adalah <tt>MainMenu</tt>. Skin menyetel warna latar belakang kontrol ke hijau-terang.
+</p>
+
+<p>
+Sejalan dengan itu, kita perlu memodifikasi <tt>protected/common/MainLayout.tpl</tt> agar link tombol dalam footer menggunakan <tt>MainMenu</tt> sebagai <tt>SkinID</tt>-nya.
+</p>
+<com:TTextHighlighter CssClass="source" Language="prado">
+......
+<div id="footer">
+......
+&lt;com:THyperLink Text="Home" SkinID="MainMenu"
+ NavigateUrl="&lt;%= $this->Service->DefaultPageUrl %>" />
+
+&lt;com:THyperLink Text="New Post" SkinID="MainMenu"
+ NavigateUrl="&lt;%= $this->Service->constructUrl('posts.NewPost') %>"
+ Visible="&lt;%= !$this->User->IsGuest %>" />
+......
+</div>
+......
+</com:TTextHighlighter>
+
+<com:InfoBox>
+Sintaks untuk file skin sangat mirip dengan template PRADO. Setiap tag <tt>&lt;com:&gt;</tt> mendefinisikan sebuah skin untuk tipe kontrol tertentu. PRADO secara otomatis mengumpulkan seluruh file skin dalam sebuah tema dan menerapkannya ketika halaman bertema sedang disajikan.
+</com:InfoBox>
+
+
+<h2>Menggunakan Tema</h2>
+
+<p>
+Untuk menggunakan tema yang baru kita buat, kita memodifikasi konfigurasi aplikasi seperti berikut. Seperti kita lihat, properti <tt>Theme</tt> untuk semua halaman disetel sebagai <tt>Basic</tt>, nama tema yang baru saja kita buat.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+ <services>
+ <service id="page" class="TPageService" DefaultPage="posts.ListPost">
+ <pages MasterClass="Application.layouts.MainLayout" Theme="Basic" />
+ </service>
+ </services>
+......
+</com:TTextHighlighter>
+
+<com:InfoBox>
+Dimungkinkan untuk menetapkan tema berbeda untuk halaman yang berbeda, dan ini dapat dikerjakan baik dalam konfigurasi aplikasi/halaman ataupun secara programatis (perhatikan <tt>Theme</tt> adalah properti halaman). Untuk yan gterakhir, ia harus dikerjakan dalam metode <tt>onPreInit()</tt> pada halaman karena PRADO menerapkan tema ke halaman sebelumnya dalam masa hidup halaman.
+</com:InfoBox>
+
+
+<h2>Pengujian</h2>
+<p>
+Untuk melihat bagaimana halaman blog terlihat, kunjungi URL <tt>http://hostname/blog/index.php</tt>. Kita akan melihat font, tata letak, bingkai beruah dalam halaman. Juga, link tombol dalam footer mempunyai latar belakang hijau terang.
+</p>
+
+<img src="<%~ output.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day5/id/output.gif b/demos/blog-tutorial/protected/pages/Day5/id/output.gif
new file mode 100644
index 00000000..67bd18a3
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/id/output.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day5/id/output2.gif b/demos/blog-tutorial/protected/pages/Day5/id/output2.gif
new file mode 100644
index 00000000..16c81704
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/id/output2.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day5/id/output3.gif b/demos/blog-tutorial/protected/pages/Day5/id/output3.gif
new file mode 100644
index 00000000..6879bbdf
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/id/output3.gif
Binary files differ