summaryrefslogtreecommitdiff
path: root/demos/blog-tutorial
diff options
context:
space:
mode:
authorwei <>2007-05-10 23:00:04 +0000
committerwei <>2007-05-10 23:00:04 +0000
commite2cb0b52aaa02a3f3f41d0df377d189529713738 (patch)
treeee4c2a3fece40c9a2d4dde75f6e758f7ef05f8f6 /demos/blog-tutorial
parentef2fc3942664d4d7131542080e838f7754a3081f (diff)
Update blog tutorial
Diffstat (limited to 'demos/blog-tutorial')
-rw-r--r--demos/blog-tutorial/protected/pages/Day1/CreateContact.page83
-rw-r--r--demos/blog-tutorial/protected/pages/Day1/Setup.page2
-rw-r--r--demos/blog-tutorial/protected/pages/Day1/ShareLayout.page14
-rw-r--r--demos/blog-tutorial/protected/pages/Day2/CreateAR.page105
-rw-r--r--demos/blog-tutorial/protected/pages/Day2/CreateDB.page11
-rw-r--r--demos/blog-tutorial/protected/pages/Day2/ER.gifbin5172 -> 4444 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day2/ER.vsdbin73216 -> 73216 bytes
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/Auth.page84
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/CreateAdminUser.page58
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/CreateEditUser.page177
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/CreateLoginUser.page106
-rw-r--r--demos/blog-tutorial/protected/pages/Day3/CreateNewUser.page120
-rw-r--r--demos/blog-tutorial/samples/day2/blog/protected/data/blog.dbbin4096 -> 4096 bytes
-rw-r--r--demos/blog-tutorial/samples/day2/blog/protected/database/PostRecord.php23
-rw-r--r--demos/blog-tutorial/samples/day2/blog/protected/database/UserRecord.php23
-rw-r--r--demos/blog-tutorial/samples/day2/blog/protected/schema.sql7
-rw-r--r--demos/blog-tutorial/samples/day3/blog/protected/data/blog.dbbin5120 -> 4096 bytes
-rw-r--r--demos/blog-tutorial/samples/day3/blog/protected/database/PostRecord.php23
-rw-r--r--demos/blog-tutorial/samples/day3/blog/protected/database/UserRecord.php23
-rw-r--r--demos/blog-tutorial/samples/day3/blog/protected/schema.sql7
-rw-r--r--demos/blog-tutorial/themes/PradoSoft/style.css6
21 files changed, 476 insertions, 396 deletions
diff --git a/demos/blog-tutorial/protected/pages/Day1/CreateContact.page b/demos/blog-tutorial/protected/pages/Day1/CreateContact.page
index 4daf43e3..28ad4b21 100644
--- a/demos/blog-tutorial/protected/pages/Day1/CreateContact.page
+++ b/demos/blog-tutorial/protected/pages/Day1/CreateContact.page
@@ -47,37 +47,34 @@ We use template to organize the presentational layout of the feedback form. In t
&lt;com:TForm>
-<h1>Contact</h1>
-<p>Please fill out the following form to let me know your feedback on my blog. Thanks!</p>
-
<span>Your Name:</span>
&lt;com:TRequiredFieldValidator ControlToValidate="Name"
- ErrorMessage="Please provide your name."
- Display="Dynamic" />
+ ErrorMessage="Please provide your name."
+ Display="Dynamic" />
<br/>
&lt;com:TTextBox ID="Name" />
<br/>
<span>Your Email:</span>
&lt;com:TRequiredFieldValidator ControlToValidate="Email"
- ErrorMessage="Please provide your email address."
- Display="Dynamic" />
+ ErrorMessage="Please provide your email address."
+ Display="Dynamic" />
&lt;com:TEmailAddressValidator ControlToValidate="Email"
- ErrorMessage="You entered an invalid email address."
- Display="Dynamic" />
+ ErrorMessage="You entered an invalid email address."
+ Display="Dynamic" />
<br/>
&lt;com:TTextBox ID="Email" />
<br/>
<span>Feedback:</span>
&lt;com:TRequiredFieldValidator ControlToValidate="Feedback"
- ErrorMessage="Please provide your feedback."
- Display="Dynamic" />
+ ErrorMessage="Please provide your feedback."
+ Display="Dynamic" />
<br/>
&lt;com:TTextBox ID="Feedback"
- TextMode="MultiLine"
- Rows="10"
- Columns="40" />
+ TextMode="MultiLine"
+ Rows="10"
+ Columns="40" />
<br/>
&lt;com:TButton Text="Submit" OnClick="submitButtonClicked" />
@@ -103,13 +100,13 @@ Besides <tt>TTextBox</tt> controls, the template also uses many validator contro
<com:TTextHighlighter CssClass="source" Language="prado">
<span>Your Email:</span>
&lt;com:TRequiredFieldValidator
- ControlToValidate="Email"
- ErrorMessage="Please provide your email address."
- Display="Dynamic" />
+ ControlToValidate="Email"
+ ErrorMessage="Please provide your email address."
+ Display="Dynamic" />
&lt;com:TEmailAddressValidator
- ControlToValidate="Email"
- ErrorMessage="You entered an invalid email address."
- Display="Dynamic" />
+ ControlToValidate="Email"
+ ErrorMessage="You entered an invalid email address."
+ Display="Dynamic" />
<br/>
&lt;com:TTextBox ID="Email" />
<br/>
@@ -152,29 +149,29 @@ We thus write down the page class as follows:
<?php
class Contact extends TPage
{
- /**
- * Event handler for the OnClick event of the submit button.
- * @param TButton the button triggering the event
- * @param TEventParameter event parameter (null here)
- */
- public function submitButtonClicked($sender, $param)
- {
- if ($this->IsValid) // check if input validation is successful
- {
- // obtain the user name, email, feedback from the textboxes
- $name = $this->Name->Text;
- $email = $this->Email->Text;
- $feedback = $this->Feedback->Text;
-
- // send an email to administrator with the above information
- $this->mailFeedback($name, $email, $feedback);
- }
- }
-
- protected function mailFeedback($name, $email, $feedback)
- {
- // implementation of sending the feedback email
- }
+ /**
+ * Event handler for the OnClick event of the submit button.
+ * @param TButton the button triggering the event
+ * @param TEventParameter event parameter (null here)
+ */
+ public function submitButtonClicked($sender, $param)
+ {
+ if ($this->IsValid) // check if input validation is successful
+ {
+ // obtain the user name, email, feedback from the textboxes
+ $name = $this->Name->Text;
+ $email = $this->Email->Text;
+ $feedback = $this->Feedback->Text;
+
+ // send an email to administrator with the above information
+ $this->mailFeedback($name, $email, $feedback);
+ }
+ }
+
+ protected function mailFeedback($name, $email, $feedback)
+ {
+ // implementation of sending the feedback email
+ }
}
?>
</com:TTextHighlighter>
diff --git a/demos/blog-tutorial/protected/pages/Day1/Setup.page b/demos/blog-tutorial/protected/pages/Day1/Setup.page
index 0fe877bf..e8316253 100644
--- a/demos/blog-tutorial/protected/pages/Day1/Setup.page
+++ b/demos/blog-tutorial/protected/pages/Day1/Setup.page
@@ -13,7 +13,7 @@ Assume <tt>blog</tt> is the name of the directory to hold the whole blog system,
<p>
Under the <tt>blog</tt> directory, we run the <a href="http://www.pradosoft.com/demos/quickstart/?page=GettingStarted.CommandLine">PRADO command line tool</a> with the following command (replace <tt>path/to</tt> with the actual path to the PRADO framework installation):
</p>
-<com:TTextHighlighter CssClass="source">
+<com:TTextHighlighter CssClass="source cli">
php path/to/prado-cli.php -c .
</com:TTextHighlighter>
diff --git a/demos/blog-tutorial/protected/pages/Day1/ShareLayout.page b/demos/blog-tutorial/protected/pages/Day1/ShareLayout.page
index d3d1f553..d6c4d29f 100644
--- a/demos/blog-tutorial/protected/pages/Day1/ShareLayout.page
+++ b/demos/blog-tutorial/protected/pages/Day1/ShareLayout.page
@@ -135,13 +135,13 @@ We can specify master in code like the following to enable dynamic change of lay
<?php
class Contact extends TPage
{
- public function onPreInit($param)
- {
- parent::onPreInit($param);
- $this->MasterClass='Path.To.NewLayout';
- }
-
- // ...
+ public function onPreInit($param)
+ {
+ parent::onPreInit($param);
+ $this->MasterClass='Path.To.NewLayout';
+ }
+
+ // ...
}
?>
</com:TTextHighlighter>
diff --git a/demos/blog-tutorial/protected/pages/Day2/CreateAR.page b/demos/blog-tutorial/protected/pages/Day2/CreateAR.page
index 00ac1166..c8684ee0 100644
--- a/demos/blog-tutorial/protected/pages/Day2/CreateAR.page
+++ b/demos/blog-tutorial/protected/pages/Day2/CreateAR.page
@@ -3,7 +3,7 @@
<h1>Creating Active Record Classes</h1>
<p>
-We need to create two <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Active Record</a> classes, <tt>UserRecord</tt> and <tt>PostRecord</tt>, to represent data records in the <tt>users</tt> and <tt>posts</tt> tables, respectively. Active Record classes must extend from the base class <tt>ActiveRecord</tt>, and their properties must match exactly to the fields of the corresponding tables.
+We need to create two <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Active Record</a> classes, <tt>UserRecord</tt> and <tt>PostRecord</tt>, to represent data records in the <tt>users</tt> and <tt>posts</tt> tables, respectively. Active Record classes must extend from the base class <tt>ActiveRecord</tt>, and must define property names that matches with the field names of the corresponding table.
</p>
<p>
@@ -11,9 +11,9 @@ To better organize our directories, we create a new directory <tt>protected/data
</p>
<com:TTextHighlighter CssClass="source" Language="xml">
- <paths>
- <using namespace="Application.database.*" />
- </paths>
+<paths>
+ <using namespace="Application.database.*" />
+</paths>
</com:TTextHighlighter>
<p>
@@ -24,7 +24,7 @@ Instead of writing the classes manually, we will use the <a href="http://www.pra
Under the <tt>blog</tt> directory, run the following command to enter into the interactive mode of the command line tool:
</p>
-<com:TTextHighlighter CssClass="source">
+<com:TTextHighlighter CssClass="source cli">
php path/to/prado-cli.php shell .
</com:TTextHighlighter>
@@ -32,7 +32,7 @@ php path/to/prado-cli.php shell .
We should see
</p>
-<com:TTextHighlighter CssClass="source" Language="text">
+<com:TTextHighlighter CssClass="source cli" Language="text">
Command line tools for Prado 3.1.0.
** Loaded PRADO appplication in directory "protected".
PHP-Shell - Version 0.3.1
@@ -47,7 +47,7 @@ PHP-Shell - Version 0.3.1
At the prompt, enter the following two commands to create <tt>UserRecord</tt> and <tt>PostRecord</tt> classes:
</p>
-<com:TTextHighlighter CssClass="source" Language="text">
+<com:TTextHighlighter CssClass="source cli" Language="text">
>> generate users Application.database.UserRecord
>> generate posts Application.database.PostRecord
@@ -70,17 +70,18 @@ If we check the <tt>PostRecord</tt> class file, we should see the following cont
<com:TTextHighlighter CssClass="source" Language="php">
class PostRecord extends TActiveRecord
{
- const TABLE='posts';
- public $post_id;
- public $author;
- public $create_time;
- public $title;
- public $content;
-
- public static function finder($className=__CLASS__)
- {
- return parent::finder($className);
- }
+ const TABLE='posts';
+ public $post_id;
+ public $author_id;
+ public $create_time;
+ public $title;
+ public $content;
+ public $status;
+
+ public static function finder($className=__CLASS__)
+ {
+ return parent::finder($className);
+ }
}
</com:TTextHighlighter>
@@ -92,17 +93,18 @@ As we see, for each field in the <tt>posts</tt> table, the class has a correspon
We can use the command line tool to do some testing with our newly created Active Record classes. Still in the interactive mode of the command line tool, we enter a PHP statement and should see the following. Interested readers may try some other PHP statements, such as <tt>UserRecord::finder()->findAll()</tt>.
</p>
-<com:TTextHighlighter CssClass="source" Language="text">
+<com:TTextHighlighter CssClass="source cli" Language="text">
>> PostRecord::finder()->findAll()
array
(
[0] => PostRecord#1
(
[post_id] => '1'
- [author] => 'admin'
+ [author_id] => 'admin'
[create_time] => '1175708482'
[title] => 'first post'
[content] => 'this is my first post'
+ [status] => '0'
[TActiveRecord:_readOnly] => false
[TActiveRecord:_connection] => null
[TComponent:_e] => array()
@@ -110,4 +112,67 @@ array
)
</com:TTextHighlighter>
+<h1>Relationship Between Posts and Users</h1>
+<p>
+Recall that there was a foreign key relationship between the <tt>users</tt> and <tt>posts</tt> table. The entity-relationship diagram is shown below for convienence.
+</p>
+
+<img src="<%~ ER.gif %>" class="output" />
+
+<p>
+From the entity-relationship diagram above, we see that the <tt>posts</tt> table contains a field named <tt>author_id</tt>. This <tt>author_id</tt> field is a foreign key to the reference table <tt>users</tt>. In particular, the values in the <tt>author_id</tt> field should be of that from the <tt>users</tt> table's <tt>username</tt> field. One of the consequence of this relationship, thinking in terms of objects, is that each "post" belongs to an "author" and one "author" may have many "posts".
+</p>
+
+<p>
+We can model the relationship between <tt>posts</tt> and <tt>users</tt> table in Active Record by modifying the <tt>PostRecord</tt> and <tt>UserRecord</tt> classes as follows.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class PostRecord extends TActiveRecord
+{
+ //... properties and methods as before
+
+ public $author; //holds an UserRecord
+
+ protected static $RELATIONS=array
+ (
+ 'author' => array(self::BELONGS_TO, 'UserRecord'),
+ );
+}
+</com:TTextHighlighter>
+
+<p>
+The static <tt>$RELATIONS</tt> property of <tt>PostRecord</tt> defines that the property <tt>$author</tt> belongs to an <tt>UserRecord</tt>. In <tt>array(self::BELONGS_TO, 'UserRecord')</tt>, the first element defines the relationship type, in this case <tt>self::BELONGS_TO</tt>. The second element is the name of related record, in this case an <tt>UserRecord</tt>. The <tt>UserRecord</tt> is defined similarly below, the difference is that, the user record has many <tt>PostRecord</tt>s.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class UserRecord extends TActiveRecord
+{
+ //... properties and methods as before
+
+ public $posts=array(); //holds an array of PostRecord
+
+ protected static $RELATIONS=array
+ (
+ 'posts' => array(self::HAS_MANY, 'PostRecord'),
+ );
+}
+</com:TTextHighlighter>
+
+<p>
+An array of <tt>UserRecord</tt> with and its corresponding posts may be fetched as follows.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+$users = UserRecord::finder()->withPosts()->findAll();
+</com:TTextHighlighter>
+
+<com:TipBox>
+The method <tt>withXXX()</tt> (where XXX is the relationship property name, in this case, <tt>Posts</tt>) fetches the corresponding <tt>PostRecords</tt> using a second query (not by using a join). The <tt>withXXX()</tt> method accepts the same arguments as other finder methods of TActiveRecord, e.g. <tt>withPosts('status = ?', 0)</tt>.
+</com:TipBox>
+
+<p>
+Further detailed documentation can be found in the quickstart <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Active Record</a> docs.
+</p>
+
</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day2/CreateDB.page b/demos/blog-tutorial/protected/pages/Day2/CreateDB.page
index eebda4c1..de4e2436 100644
--- a/demos/blog-tutorial/protected/pages/Day2/CreateDB.page
+++ b/demos/blog-tutorial/protected/pages/Day2/CreateDB.page
@@ -30,9 +30,11 @@ CREATE TABLE users (
/* create posts table */
CREATE TABLE posts (
post_id INTEGER NOT NULL PRIMARY KEY,
- author VARCHAR(128) NOT NULL, /* references users.username */
+ author_id VARCHAR(128) NOT NULL
+ CONSTRAINT fk_author REFERENCES users(username),
create_time INTEGER NOT NULL, /* UNIX timestamp */
title VARCHAR(256) NOT NULL, /* title of the post */
+ content TEXT, /* post body */
status INTEGER NOT NULL /* 0: published; 1: draft; 2: pending; 2: denied */
);
@@ -43,14 +45,17 @@ INSERT INTO posts VALUES (NULL, 'admin', 1175708482, 'first post', 'this is my f
</com:TTextHighlighter>
<com:NoteBox>
-SQLite does not support <a href="http://www.sqlite.org/omitted.html">foreign key constraint</a>. Therefore, we will write PHP code to ensure that the <tt>posts.author</tt> field contains valid data. Also, we are exploiting the fact that the <tt>posts.post_id</tt> field is <a href="http://www.sqlite.org/autoinc.html">auto-incremental</a> if we assign NULL to it.
+SQLite does not support <a href="http://www.sqlite.org/omitted.html">foreign key constraint</a> such that
+the constraints can still be defined but will be ignored by SQLite.
+Therefore, we will write PHP code to ensure that the <tt>posts.author_id</tt> field contains valid data.
+Also, we are exploiting the fact that the <tt>posts.post_id</tt> field is <a href="http://www.sqlite.org/autoinc.html">auto-incremental</a> if we assign NULL to it.
</com:NoteBox>
<p>
We then use the <a href="http://www.sqlite.org/download.html">SQLite command line tool</a> to create the SQLite database. We create a directory <tt>protected/data</tt> to hold the SQLite database file. We now execute the following command under the directory <tt>protected/data</tt>:
</p>
-<com:TTextHighlighter CssClass="source">
+<com:TTextHighlighter CssClass="source cli">
sqlite3 blog.db < ../schema.sql
</com:TTextHighlighter>
diff --git a/demos/blog-tutorial/protected/pages/Day2/ER.gif b/demos/blog-tutorial/protected/pages/Day2/ER.gif
index 90e4c1ea..7a5397b3 100644
--- a/demos/blog-tutorial/protected/pages/Day2/ER.gif
+++ b/demos/blog-tutorial/protected/pages/Day2/ER.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day2/ER.vsd b/demos/blog-tutorial/protected/pages/Day2/ER.vsd
index 95cf7f32..474833fd 100644
--- a/demos/blog-tutorial/protected/pages/Day2/ER.vsd
+++ b/demos/blog-tutorial/protected/pages/Day2/ER.vsd
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day3/Auth.page b/demos/blog-tutorial/protected/pages/Day3/Auth.page
index 82c667d7..8d066b24 100644
--- a/demos/blog-tutorial/protected/pages/Day3/Auth.page
+++ b/demos/blog-tutorial/protected/pages/Day3/Auth.page
@@ -52,50 +52,50 @@ Prado::using('System.Security.TDbUserManager');
*/
class BlogUser extends TDbUser
{
- /**
- * Creates a BlogUser object based on the specified username.
- * This method is required by TDbUser. It checks the database
- * to see if the specified username is there. If so, a BlogUser
- * object is created and initialized.
- * @param string the specified username
- * @return BlogUser the user object, null if username is invalid.
- */
- public function createUser($username)
- {
- // use UserRecord Active Record to look for the specified username
- $userRecord=UserRecord::finder()->findByPk($username);
- if($userRecord instanceof UserRecord) // if found
- {
- $user=new BlogUser($this->Manager);
- $user->Name=$username; // set username
- $user->Roles=($userRecord->role==1?'admin':'user'); // set role
- $user->IsGuest=false; // the user is not a guest
- return $user;
- }
- else
- return null;
- }
+ /**
+ * Creates a BlogUser object based on the specified username.
+ * This method is required by TDbUser. It checks the database
+ * to see if the specified username is there. If so, a BlogUser
+ * object is created and initialized.
+ * @param string the specified username
+ * @return BlogUser the user object, null if username is invalid.
+ */
+ public function createUser($username)
+ {
+ // use UserRecord Active Record to look for the specified username
+ $userRecord=UserRecord::finder()->findByPk($username);
+ if($userRecord instanceof UserRecord) // if found
+ {
+ $user=new BlogUser($this->Manager);
+ $user->Name=$username; // set username
+ $user->Roles=($userRecord->role==1?'admin':'user'); // set role
+ $user->IsGuest=false; // the user is not a guest
+ return $user;
+ }
+ else
+ return null;
+ }
- /**
- * Checks if the specified (username, password) is valid.
- * This method is required by TDbUser.
- * @param string username
- * @param string password
- * @return boolean whether the username and password are valid.
- */
- public function validateUser($username,$password)
- {
- // use UserRecord Active Record to look for the (username, password) pair.
- return UserRecord::finder()->findBy_username_AND_password($username,$password)!==null;
- }
+ /**
+ * Checks if the specified (username, password) is valid.
+ * This method is required by TDbUser.
+ * @param string username
+ * @param string password
+ * @return boolean whether the username and password are valid.
+ */
+ public function validateUser($username,$password)
+ {
+ // use UserRecord Active Record to look for the (username, password) pair.
+ return UserRecord::finder()->findBy_username_AND_password($username,$password)!==null;
+ }
- /**
- * @return boolean whether this user is an administrator.
- */
- public function getIsAdmin()
- {
- return $this->isInRole('admin');
- }
+ /**
+ * @return boolean whether this user is an administrator.
+ */
+ public function getIsAdmin()
+ {
+ return $this->isInRole('admin');
+ }
}
</com:TTextHighlighter>
diff --git a/demos/blog-tutorial/protected/pages/Day3/CreateAdminUser.page b/demos/blog-tutorial/protected/pages/Day3/CreateAdminUser.page
index 36b43014..95d7dac5 100644
--- a/demos/blog-tutorial/protected/pages/Day3/CreateAdminUser.page
+++ b/demos/blog-tutorial/protected/pages/Day3/CreateAdminUser.page
@@ -83,35 +83,35 @@ In the above page template, the datagrid's <tt>OnDeleteCommand</tt> event is tta
<com:TTextHighlighter CssClass="source" Language="php">
class AdminUser extends TPage
{
- /**
- * Populates the datagrid with user lists.
- * This method is invoked by the framework when initializing the page
- * @param mixed event parameter
- */
- public function onInit($param)
- {
- parent::onInit($param);
- // fetches all data account information
- $this->UserGrid->DataSource=UserRecord::finder()->findAll();
- // binds the data to interface components
- $this->UserGrid->dataBind();
- }
-
- /**
- * Deletes a specified user record.
- * This method responds to the datagrid's OnDeleteCommand event.
- * @param TDataGrid the event sender
- * @param TDataGridCommandEventParameter the event parameter
- */
- public function deleteButtonClicked($sender,$param)
- {
- // obtains the datagrid item that contains the clicked delete button
- $item=$param->Item;
- // obtains the primary key corresponding to the datagrid item
- $username=$this->UserGrid->DataKeys[$item->ItemIndex];
- // deletes the user record with the specified username primary key
- UserRecord::finder()->deleteByPk($username);
- }
+ /**
+ * Populates the datagrid with user lists.
+ * This method is invoked by the framework when initializing the page
+ * @param mixed event parameter
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ // fetches all data account information
+ $this->UserGrid->DataSource=UserRecord::finder()->findAll();
+ // binds the data to interface components
+ $this->UserGrid->dataBind();
+ }
+
+ /**
+ * Deletes a specified user record.
+ * This method responds to the datagrid's OnDeleteCommand event.
+ * @param TDataGrid the event sender
+ * @param TDataGridCommandEventParameter the event parameter
+ */
+ public function deleteButtonClicked($sender,$param)
+ {
+ // obtains the datagrid item that contains the clicked delete button
+ $item=$param->Item;
+ // obtains the primary key corresponding to the datagrid item
+ $username=$this->UserGrid->DataKeys[$item->ItemIndex];
+ // deletes the user record with the specified username primary key
+ UserRecord::finder()->deleteByPk($username);
+ }
}
</com:TTextHighlighter>
diff --git a/demos/blog-tutorial/protected/pages/Day3/CreateEditUser.page b/demos/blog-tutorial/protected/pages/Day3/CreateEditUser.page
index 64ac6798..055ea62b 100644
--- a/demos/blog-tutorial/protected/pages/Day3/CreateEditUser.page
+++ b/demos/blog-tutorial/protected/pages/Day3/CreateEditUser.page
@@ -44,23 +44,23 @@ As you may have guessed, the page template <tt>EditUser</tt> is largely the same
<br/>
<span>Re-type Password:</span>
&lt;com:TCompareValidator
- ControlToValidate="Password"
- ControlToCompare="Password2"
- ErrorMessage="Your password entries did not match."
- Display="Dynamic" />
+ 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" />
+ ControlToValidate="Email"
+ ErrorMessage="Please provide your email address."
+ Display="Dynamic" />
&lt;com:TEmailAddressValidator
- ControlToValidate="Email"
- ErrorMessage="You entered an invalid email address."
- Display="Dynamic" />
+ ControlToValidate="Email"
+ ErrorMessage="You entered an invalid email address."
+ Display="Dynamic" />
<br/>
&lt;com:TTextBox ID="Email" />
@@ -69,8 +69,8 @@ As you may have guessed, the page template <tt>EditUser</tt> is largely the same
<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:TListItem Text="Normal User" Value="0" />
+ &lt;com:TListItem Text="Administrator" Value="1" />
&lt;/com:TDropDownList>
&lt;/com:TControl>
@@ -100,82 +100,83 @@ Based on the above description and template, we need to write a page class that
<com:TTextHighlighter CssClass="source" Language="php">
class EditUser extends TPage
{
- /**
- * Initializes the inputs with existing user data.
- * This method is invoked by the framework when the page is being initialized.
- * @param mixed event parameter
- */
- public function onInit($param)
- {
- parent::onInit($param);
- if(!$this->IsPostBack) // if the page is initially requested
- {
- // Retrieves the existing user data. This is equivalent to:
- // $userRecord=$this->getUserRecord();
- $userRecord=$this->UserRecord;
-
- // Populates the input controls with the existing user data
- $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;
- }
- }
-
- /**
- * Saves the user account if all inputs are valid.
- * This method responds to the OnClick event of the "save" button.
- * @param mixed event sender
- * @param mixed event parameter
- */
- public function saveButtonClicked($sender,$param)
- {
- if($this->IsValid) // when all validations succeed
- {
- // Retrieves the existing user data. This is equivalent to:
- $userRecord=$this->UserRecord;
-
- // Fetches the input data
- $userRecord->username=$this->Username->Text;
- // update password when the input is not empty
- if(!empty($this->Password->Text))
- $userRecord->password=$this->Password->Text;
- $userRecord->email=$this->Email->Text;
- // update the role if the current user is an administrator
- if($this->User->IsAdmin)
- $userRecord->role=(int)$this->Role->SelectedValue;
- $userRecord->first_name=$this->FirstName->Text;
- $userRecord->last_name=$this->LastName->Text;
-
- // saves to the database via Active Record mechanism
- $userRecord->save();
-
- // redirects the browser to the homepage
- $this->Response->redirect($this->Service->constructUrl($this->Service->DefaultPage));
- }
- }
-
- /**
- * Returns the user data to be editted.
- * @return UserRecord the user data to be editted.
- * @throws THttpException if the user data is not found.
- */
- protected function getUserRecord()
- {
- // the user to be editted is the currently logged-in user
- $username=$this->User->Name;
- // if the 'username' GET var is not empty and the current user
- // is an administrator, we use the GET var value instead.
- if($this->User->IsAdmin && $this->Request['username']!==null)
- $username=$this->Request['username'];
-
- // use Active Record to look for the specified username
- $userRecord=UserRecord::finder()->findByPk($username);
- if(!($userRecord instanceof UserRecord))
- throw new THttpException(500,'Username is invalid.');
- return $userRecord;
- }
+ /**
+ * Initializes the inputs with existing user data.
+ * This method is invoked by the framework when the page is being initialized.
+ * @param mixed event parameter
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ if(!$this->IsPostBack) // if the page is initially requested
+ {
+ // Retrieves the existing user data. This is equivalent to:
+ // $userRecord=$this->getUserRecord();
+ $userRecord=$this->UserRecord;
+
+ // Populates the input controls with the existing user data
+ $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;
+ }
+ }
+
+ /**
+ * Saves the user account if all inputs are valid.
+ * This method responds to the OnClick event of the "save" button.
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function saveButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // when all validations succeed
+ {
+ // Retrieves the existing user data. This is equivalent to:
+ $userRecord=$this->UserRecord;
+
+ // Fetches the input data
+ $userRecord->username=$this->Username->Text;
+ // update password when the input is not empty
+ if(!empty($this->Password->Text))
+ $userRecord->password=$this->Password->Text;
+ $userRecord->email=$this->Email->Text;
+ // update the role if the current user is an administrator
+ if($this->User->IsAdmin)
+ $userRecord->role=(int)$this->Role->SelectedValue;
+ $userRecord->first_name=$this->FirstName->Text;
+ $userRecord->last_name=$this->LastName->Text;
+
+ // saves to the database via Active Record mechanism
+ $userRecord->save();
+
+ // redirects the browser to the homepage
+ $url=$this->Service->constructUrl($this->Service->DefaultPage);
+ $this->Response->redirect($url);
+ }
+ }
+
+ /**
+ * Returns the user data to be editted.
+ * @return UserRecord the user data to be editted.
+ * @throws THttpException if the user data is not found.
+ */
+ protected function getUserRecord()
+ {
+ // the user to be editted is the currently logged-in user
+ $username=$this->User->Name;
+ // if the 'username' GET var is not empty and the current user
+ // is an administrator, we use the GET var value instead.
+ if($this->User->IsAdmin && $this->Request['username']!==null)
+ $username=$this->Request['username'];
+
+ // use Active Record to look for the specified username
+ $userRecord=UserRecord::finder()->findByPk($username);
+ if(!($userRecord instanceof UserRecord))
+ throw new THttpException(500,'Username is invalid.');
+ return $userRecord;
+ }
}
</com:TTextHighlighter>
diff --git a/demos/blog-tutorial/protected/pages/Day3/CreateLoginUser.page b/demos/blog-tutorial/protected/pages/Day3/CreateLoginUser.page
index 61ce27b7..d54fc967 100644
--- a/demos/blog-tutorial/protected/pages/Day3/CreateLoginUser.page
+++ b/demos/blog-tutorial/protected/pages/Day3/CreateLoginUser.page
@@ -31,19 +31,19 @@ Below we show the template for <tt>LoginPage</tt>. As we see, the page mainly co
<span>Username:</span>
&lt;com:TRequiredFieldValidator
- ControlToValidate="Username"
- ErrorMessage="Please provide your username."
- Display="Dynamic" />
+ 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" />
+ ControlToValidate="Password"
+ ErrorMessage="Your entered an invalid password."
+ Display="Dynamic"
+ OnServerValidate="validateUser" />
<br/>
&lt;com:TTextBox ID="Password" TextMode="Password" />
@@ -62,36 +62,36 @@ Like the <a href="?page=Day1.CreateContact">Contact</a> page, the <tt>LoginUser<
<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; // tell the validator that validation fails
- }
-
- /**
- * 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) // all validations succeed
- {
- // obtain the URL of the privileged page that the user wanted to visit originally
- $url=$this->Application->getModule('auth')->ReturnUrl;
- if(empty($url)) // the user accesses the login page directly
- $url=$this->Service->constructUrl($this->Service->DefaultPage);
- $this->Response->redirect($url);
- }
- }
+ /**
+ * 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; // tell the validator that validation fails
+ }
+
+ /**
+ * 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) // all validations succeed
+ {
+ // obtain the URL of the privileged page that the user wanted to visit originally
+ $url=$this->Application->getModule('auth')->ReturnUrl;
+ if(empty($url)) // the user accesses the login page directly
+ $url=$this->Service->constructUrl($this->Service->DefaultPage);
+ $this->Response->redirect($url);
+ }
+ }
}
</com:TTextHighlighter>
@@ -117,12 +117,12 @@ We modify the footer section of the <tt>MainLayout</tt>'s template as follows. T
<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 %>" />
+ NavigateUrl="&lt;%= $this->Service->constructUrl('users.LoginUser') %>"
+ Visible="&lt;%= $this->User->IsGuest %>" />
&lt;com:TLinkButton Text="Logout"
- OnClick="logoutButtonClicked"
- Visible="&lt;%= !$this->User->IsGuest %>" />
+ OnClick="logoutButtonClicked"
+ Visible="&lt;%= !$this->User->IsGuest %>" />
<br/>
&lt;%= PRADO::poweredByPrado() %>
@@ -136,18 +136,18 @@ Since the "logout" button attaches its <tt>OnClick</tt> event with a method call
<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);
- }
+ /**
+ * 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>
diff --git a/demos/blog-tutorial/protected/pages/Day3/CreateNewUser.page b/demos/blog-tutorial/protected/pages/Day3/CreateNewUser.page
index d9ef684f..77b97f7d 100644
--- a/demos/blog-tutorial/protected/pages/Day3/CreateNewUser.page
+++ b/demos/blog-tutorial/protected/pages/Day3/CreateNewUser.page
@@ -29,46 +29,46 @@ Based on the above analysis, we write the page template as follows:
<span>Username:</span>
&lt;com:TRequiredFieldValidator
- ControlToValidate="Username"
- ErrorMessage="Please provide a username."
- Display="Dynamic" />
+ 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" />
+ 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" />
+ 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" />
+ 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" />
+ ControlToValidate="Email"
+ ErrorMessage="Please provide your email address."
+ Display="Dynamic" />
&lt;com:TEmailAddressValidator
- ControlToValidate="Email"
- ErrorMessage="You entered an invalid email address."
- Display="Dynamic" />
+ ControlToValidate="Email"
+ ErrorMessage="You entered an invalid email address."
+ Display="Dynamic" />
<br/>
&lt;com:TTextBox ID="Email" />
@@ -76,8 +76,8 @@ Based on the above analysis, we write the page template as follows:
<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:TListItem Text="Normal User" Value="0" />
+ &lt;com:TListItem Text="Administrator" Value="1" />
&lt;/com:TDropDownList>
<br/>
@@ -110,44 +110,44 @@ From the above page template, we see that we need to write a page class that imp
<com:TTextHighlighter CssClass="source" Language="php">
class NewUser extends TPage
{
- /**
- * Checks whether the username exists in the database.
- * This method responds to the OnServerValidate event of username's custom validator.
- * @param mixed event sender
- * @param mixed event parameter
- */
- public function checkUsername($sender,$param)
- {
- // valid if the username is not found in the database
- $param->IsValid=UserRecord::finder()->findByPk($this->Username->Text)===null;
- }
-
- /**
- * Creates a new user account if all inputs are valid.
- * This method responds to the OnClick event of the "create" button.
- * @param mixed event sender
- * @param mixed event parameter
- */
- public function createButtonClicked($sender,$param)
- {
- if($this->IsValid) // when all validations succeed
- {
- // populates a UserRecord object with user inputs
- $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;
-
- // saves to the database via Active Record mechanism
- $userRecord->save();
-
- // redirects the browser to the homepage
- $this->Response->redirect($this->Service->constructUrl($this->Service->DefaultPage));
- }
- }
+ /**
+ * Checks whether the username exists in the database.
+ * This method responds to the OnServerValidate event of username's custom validator.
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function checkUsername($sender,$param)
+ {
+ // valid if the username is not found in the database
+ $param->IsValid=UserRecord::finder()->findByPk($this->Username->Text)===null;
+ }
+
+ /**
+ * Creates a new user account if all inputs are valid.
+ * This method responds to the OnClick event of the "create" button.
+ * @param mixed event sender
+ * @param mixed event parameter
+ */
+ public function createButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // when all validations succeed
+ {
+ // populates a UserRecord object with user inputs
+ $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;
+
+ // saves to the database via Active Record mechanism
+ $userRecord->save();
+
+ // redirects the browser to the homepage
+ $this->Response->redirect($this->Service->constructUrl($this->Service->DefaultPage));
+ }
+ }
}
</com:TTextHighlighter>
diff --git a/demos/blog-tutorial/samples/day2/blog/protected/data/blog.db b/demos/blog-tutorial/samples/day2/blog/protected/data/blog.db
index 46e82bfb..fa48526a 100644
--- a/demos/blog-tutorial/samples/day2/blog/protected/data/blog.db
+++ b/demos/blog-tutorial/samples/day2/blog/protected/data/blog.db
Binary files differ
diff --git a/demos/blog-tutorial/samples/day2/blog/protected/database/PostRecord.php b/demos/blog-tutorial/samples/day2/blog/protected/database/PostRecord.php
index a761286a..01f84437 100644
--- a/demos/blog-tutorial/samples/day2/blog/protected/database/PostRecord.php
+++ b/demos/blog-tutorial/samples/day2/blog/protected/database/PostRecord.php
@@ -6,18 +6,19 @@ class PostRecord extends TActiveRecord
{
const TABLE='posts';
- public $post_id;
-
- public $author;
-
- public $create_time;
-
- public $title;
-
- public $content;
-
- public $status;
+ public $post_id;
+ public $author_id;
+ public $create_time;
+ public $title;
+ public $content;
+ public $status;
+ public $author;
+
+ protected static $RELATIONS=array
+ (
+ 'author' => array(self::BELONGS_TO, 'UserRecord'),
+ );
public static function finder($className=__CLASS__)
{
diff --git a/demos/blog-tutorial/samples/day2/blog/protected/database/UserRecord.php b/demos/blog-tutorial/samples/day2/blog/protected/database/UserRecord.php
index 87043894..18d5ebbe 100644
--- a/demos/blog-tutorial/samples/day2/blog/protected/database/UserRecord.php
+++ b/demos/blog-tutorial/samples/day2/blog/protected/database/UserRecord.php
@@ -6,18 +6,19 @@ class UserRecord extends TActiveRecord
{
const TABLE='users';
- public $username;
-
- public $email;
-
- public $password;
-
- public $role;
-
- public $first_name;
-
- public $last_name;
+ public $username;
+ public $email;
+ public $password;
+ public $role;
+ public $first_name;
+ public $last_name;
+ public $posts=array();
+
+ protected static $RELATIONS=array
+ (
+ 'posts' => array(self::HAS_MANY, 'PostRecord'),
+ );
public static function finder($className=__CLASS__)
{
diff --git a/demos/blog-tutorial/samples/day2/blog/protected/schema.sql b/demos/blog-tutorial/samples/day2/blog/protected/schema.sql
index d3189b40..89f7388e 100644
--- a/demos/blog-tutorial/samples/day2/blog/protected/schema.sql
+++ b/demos/blog-tutorial/samples/day2/blog/protected/schema.sql
@@ -11,11 +11,12 @@ CREATE TABLE users (
/* create posts table */
CREATE TABLE posts (
post_id INTEGER NOT NULL PRIMARY KEY,
- author VARCHAR(128) NOT NULL, /* references users.username */
+ author_id VARCHAR(128) NOT NULL
+ CONSTRAINT fk_author REFERENCES users(username),
create_time INTEGER NOT NULL, /* UNIX timestamp */
title VARCHAR(256) NOT NULL, /* title of the post */
- content TEXT NOT NULL, /* content of the post */
- status INTEGER NOT NULL /* 0: published; 1: draft; 2: pending; 2: denied */
+ content TEXT, /* post body */
+ status INTEGER NOT NULL /* 0: published; 1: draft; 2: pending; 2: denied */
);
/* insert some initial data records for testing */
diff --git a/demos/blog-tutorial/samples/day3/blog/protected/data/blog.db b/demos/blog-tutorial/samples/day3/blog/protected/data/blog.db
index 37449fd3..fa48526a 100644
--- a/demos/blog-tutorial/samples/day3/blog/protected/data/blog.db
+++ b/demos/blog-tutorial/samples/day3/blog/protected/data/blog.db
Binary files differ
diff --git a/demos/blog-tutorial/samples/day3/blog/protected/database/PostRecord.php b/demos/blog-tutorial/samples/day3/blog/protected/database/PostRecord.php
index a761286a..01f84437 100644
--- a/demos/blog-tutorial/samples/day3/blog/protected/database/PostRecord.php
+++ b/demos/blog-tutorial/samples/day3/blog/protected/database/PostRecord.php
@@ -6,18 +6,19 @@ class PostRecord extends TActiveRecord
{
const TABLE='posts';
- public $post_id;
-
- public $author;
-
- public $create_time;
-
- public $title;
-
- public $content;
-
- public $status;
+ public $post_id;
+ public $author_id;
+ public $create_time;
+ public $title;
+ public $content;
+ public $status;
+ public $author;
+
+ protected static $RELATIONS=array
+ (
+ 'author' => array(self::BELONGS_TO, 'UserRecord'),
+ );
public static function finder($className=__CLASS__)
{
diff --git a/demos/blog-tutorial/samples/day3/blog/protected/database/UserRecord.php b/demos/blog-tutorial/samples/day3/blog/protected/database/UserRecord.php
index 87043894..18d5ebbe 100644
--- a/demos/blog-tutorial/samples/day3/blog/protected/database/UserRecord.php
+++ b/demos/blog-tutorial/samples/day3/blog/protected/database/UserRecord.php
@@ -6,18 +6,19 @@ class UserRecord extends TActiveRecord
{
const TABLE='users';
- public $username;
-
- public $email;
-
- public $password;
-
- public $role;
-
- public $first_name;
-
- public $last_name;
+ public $username;
+ public $email;
+ public $password;
+ public $role;
+ public $first_name;
+ public $last_name;
+ public $posts=array();
+
+ protected static $RELATIONS=array
+ (
+ 'posts' => array(self::HAS_MANY, 'PostRecord'),
+ );
public static function finder($className=__CLASS__)
{
diff --git a/demos/blog-tutorial/samples/day3/blog/protected/schema.sql b/demos/blog-tutorial/samples/day3/blog/protected/schema.sql
index d3189b40..89f7388e 100644
--- a/demos/blog-tutorial/samples/day3/blog/protected/schema.sql
+++ b/demos/blog-tutorial/samples/day3/blog/protected/schema.sql
@@ -11,11 +11,12 @@ CREATE TABLE users (
/* create posts table */
CREATE TABLE posts (
post_id INTEGER NOT NULL PRIMARY KEY,
- author VARCHAR(128) NOT NULL, /* references users.username */
+ author_id VARCHAR(128) NOT NULL
+ CONSTRAINT fk_author REFERENCES users(username),
create_time INTEGER NOT NULL, /* UNIX timestamp */
title VARCHAR(256) NOT NULL, /* title of the post */
- content TEXT NOT NULL, /* content of the post */
- status INTEGER NOT NULL /* 0: published; 1: draft; 2: pending; 2: denied */
+ content TEXT, /* post body */
+ status INTEGER NOT NULL /* 0: published; 1: draft; 2: pending; 2: denied */
);
/* insert some initial data records for testing */
diff --git a/demos/blog-tutorial/themes/PradoSoft/style.css b/demos/blog-tutorial/themes/PradoSoft/style.css
index c9391df5..3db86744 100644
--- a/demos/blog-tutorial/themes/PradoSoft/style.css
+++ b/demos/blog-tutorial/themes/PradoSoft/style.css
@@ -186,6 +186,12 @@ a
margin: 0.2em;
}
+.cli
+{
+ color: white;
+ background-color: #555;
+}
+
.source pre {
font-family: "Courier New", Courier, mono;
margin: 0;