summaryrefslogtreecommitdiff
path: root/demos/time-tracker/protected
diff options
context:
space:
mode:
authorwei <>2006-07-16 06:19:36 +0000
committerwei <>2006-07-16 06:19:36 +0000
commitc7d41e5bea4a5f96979a08da9cc9f79355edfe70 (patch)
tree8f21cba052c1eae7c7204ac272dd8c5e9d6fe110 /demos/time-tracker/protected
parentaf68030fcf0c266300feb2c100149ecadef7d364 (diff)
Update Time Tracker demo.
Diffstat (limited to 'demos/time-tracker/protected')
-rw-r--r--demos/time-tracker/protected/APP_CODE/BaseDao.php27
-rw-r--r--demos/time-tracker/protected/APP_CODE/DaoManager.php126
-rw-r--r--demos/time-tracker/protected/APP_CODE/Project.php18
-rw-r--r--demos/time-tracker/protected/APP_CODE/ProjectDao.php23
-rw-r--r--demos/time-tracker/protected/APP_CODE/TimeTrackerException.php19
-rw-r--r--demos/time-tracker/protected/APP_CODE/TimeTrackerUser.php51
-rw-r--r--demos/time-tracker/protected/APP_CODE/TimeTrackerUserTypeHandler.php54
-rw-r--r--demos/time-tracker/protected/APP_CODE/UserDao.php155
-rw-r--r--demos/time-tracker/protected/APP_CODE/UserManager.php68
-rw-r--r--demos/time-tracker/protected/APP_CODE/exceptions.txt6
-rw-r--r--demos/time-tracker/protected/App_Data/time_tracker.db (renamed from demos/time-tracker/protected/data/time_tracker.db)bin16384 -> 16384 bytes
-rw-r--r--demos/time-tracker/protected/pages/Docs/TopicList.tpl2
-rw-r--r--demos/time-tracker/protected/pages/Docs/WritingUnitTest.page43
-rw-r--r--demos/time-tracker/protected/pages/Docs/config.xml12
-rw-r--r--demos/time-tracker/protected/pages/Docs/db.pngbin26879 -> 26521 bytes
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/Login.page38
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/Login.php53
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/Logout.page0
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/Logout.php34
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/MainLayout.php7
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/MainLayout.tpl40
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/SiteMap.php8
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/SiteMap.tpl43
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/UserCreate.page65
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/UserCreate.php78
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/UserList.page3
-rw-r--r--demos/time-tracker/protected/pages/TimeTracker/config.xml24
-rw-r--r--demos/time-tracker/protected/pages/Welcome.page168
28 files changed, 1124 insertions, 41 deletions
diff --git a/demos/time-tracker/protected/APP_CODE/BaseDao.php b/demos/time-tracker/protected/APP_CODE/BaseDao.php
index f9146b59..63b91def 100644
--- a/demos/time-tracker/protected/APP_CODE/BaseDao.php
+++ b/demos/time-tracker/protected/APP_CODE/BaseDao.php
@@ -1,14 +1,41 @@
<?php
+/**
+ * Base DAO class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+/**
+ * Base DAO class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
class BaseDao
{
+ /**
+ * @var TSqlMapper sqlmap client.
+ */
private $_connection;
+ /**
+ * @param TSqlMapper sqlmap client.
+ */
public function setConnection($connection)
{
$this->_connection = $connection;
}
+ /**
+ * @return TSqlMapper sqlmap client.
+ */
protected function getConnection()
{
return $this->_connection;
diff --git a/demos/time-tracker/protected/APP_CODE/DaoManager.php b/demos/time-tracker/protected/APP_CODE/DaoManager.php
new file mode 100644
index 00000000..b8ac55af
--- /dev/null
+++ b/demos/time-tracker/protected/APP_CODE/DaoManager.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * DaoManager class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+
+/**
+ * DaoManager class.
+ *
+ * A Registry for Dao and an implementation of that type.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
+class DaoManager extends TModule
+{
+ /**
+ * @var TSqlMapper sqlmap client
+ */
+ private $_connection;
+ /**
+ * @var boolean if the module has been initialized
+ */
+ private $_initialized=false;
+ /**
+ * @var array registered list of dao
+ */
+ private $_dao=array();
+ /**
+ * Initializes the module.
+ * This method is required by IModule and is invoked by application.
+ * It loads dao information from the module configuration.
+ * @param TXmlElement module configuration
+ */
+ public function init($config)
+ {
+ if($this->_connection === null)
+ throw new TimeTrackerException('daomanager_connection_required');
+ $app = $this->getApplication();
+ if(is_string($this->_connection))
+ {
+ if(($conn=$app->getModule($this->_connection)->getClient())===null)
+ throw new TimeTrackerException('daomanager_undefined_connection',$this->_connection);
+ if(!($conn instanceof TSqlMapper))
+ throw new TimeTrackerException('daomanager_invalid_connection', $this->_connection);
+ $this->_connection = $conn;
+ }
+ $this->includeDaoImplementation($config->getElementsByTagName('dao'));
+ $this->_initialized = true;
+ }
+
+ /**
+ * Register the dao type and implementation class names.
+ * @param array list of TXmlDocument nodes.
+ */
+ protected function includeDaoImplementation($nodes)
+ {
+ foreach($nodes as $node)
+ {
+ $id = $node->getAttribute('id');
+ $class = $node->getAttribute('class');
+ $this->_dao[$id] = array('class' => $class);
+ }
+ }
+
+ /**
+ * @return array list of registered Daos
+ */
+ public function getDaos()
+ {
+ return $this->_dao;
+ }
+
+ /**
+ * Returns an implementation of a Dao type, implements the Registery
+ * pattern. Multiple calls returns the same Dao instance.
+ * @param string Dao type to find.
+ * @return object instance of the Dao implementation.
+ */
+ public function getDao($class)
+ {
+ if(isset($this->_dao[$class]))
+ {
+ if(!isset($this->_dao[$class]['instance']))
+ {
+ $dao = Prado::createComponent($this->_dao[$class]['class']);
+ $dao->setConnection($this->getConnection());
+ $this->_dao[$class]['instance'] = $dao;
+ }
+ return $this->_dao[$class]['instance'];
+ }
+ else
+ throw TimeTrackerException('daomanager_undefined_dao', $class);
+ }
+
+ /**
+ * @return TSqlMapper sqlmap client instance
+ */
+ public function getConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * Sets the connection for all Daos registered.
+ * @param string|TSqlMapper sqlmap client module id or TSqlMapper instance.
+ */
+ public function setConnection($client)
+ {
+ if($this->_initialized)
+ throw new TimeTrackerException('daomanager_unchangeable');
+ if(!is_string($client) && !($client instanceof TSqlMapper))
+ throw new TConfigurationException('daomanager_invalid_connection',$client);
+ $this->_connection = $client;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/APP_CODE/Project.php b/demos/time-tracker/protected/APP_CODE/Project.php
index ad9f7d19..660fad04 100644
--- a/demos/time-tracker/protected/APP_CODE/Project.php
+++ b/demos/time-tracker/protected/APP_CODE/Project.php
@@ -1,5 +1,23 @@
<?php
+/**
+ * Project class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+/**
+ * Time Tracker Project class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
class Project
{
public $ActualDuration = 0;
diff --git a/demos/time-tracker/protected/APP_CODE/ProjectDao.php b/demos/time-tracker/protected/APP_CODE/ProjectDao.php
index 25a2845d..81902e0c 100644
--- a/demos/time-tracker/protected/APP_CODE/ProjectDao.php
+++ b/demos/time-tracker/protected/APP_CODE/ProjectDao.php
@@ -1,10 +1,26 @@
<?php
+/**
+ * Project DAO class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
-Prado::using('Application.APP_CODE.BaseDao');
-
+/**
+ * Project DAO class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
class ProjectDao extends BaseDao
{
- public function createNewProject($project)
+/* public function createNewProject($project)
{
$sqlmap = $this->getConnection();
$creator = $sqlmap->queryForObject('GetUserByName', $project->CreatorUserName);
@@ -71,6 +87,7 @@ class ProjectDao extends BaseDao
return $sqlmap->insert('AddUserToProject', $param);
}
}
+*/
}
?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/APP_CODE/TimeTrackerException.php b/demos/time-tracker/protected/APP_CODE/TimeTrackerException.php
index d715eefa..64b11405 100644
--- a/demos/time-tracker/protected/APP_CODE/TimeTrackerException.php
+++ b/demos/time-tracker/protected/APP_CODE/TimeTrackerException.php
@@ -1,5 +1,24 @@
<?php
+/**
+ * TimeTrackerException class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+/**
+ * Generic time tracker application exception. Exception messages are saved in
+ * "exceptions.txt"
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
class TimeTrackerException extends TException
{
/**
diff --git a/demos/time-tracker/protected/APP_CODE/TimeTrackerUser.php b/demos/time-tracker/protected/APP_CODE/TimeTrackerUser.php
index 4b6987bd..99ac1209 100644
--- a/demos/time-tracker/protected/APP_CODE/TimeTrackerUser.php
+++ b/demos/time-tracker/protected/APP_CODE/TimeTrackerUser.php
@@ -1,31 +1,48 @@
<?php
+/**
+ * TimeTrackerUser class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+/**
+ * Import TUser and TUserManager
+ */
Prado::using('System.Security.TUser');
Prado::using('System.Security.TUserManager');
+/**
+ * User class for Time Tracker application.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
class TimeTrackerUser extends TUser
{
- private $_ID;
+ private $_emailAddress;
- public function __construct()
+ /**
+ * @param string user email address
+ */
+ public function setEmailAddress($value)
{
- parent::__construct(new TUserManager());
+ $this->_emailAddress = $value;
}
-
- public function getID(){ return $this->_ID; }
- public function setID($value)
- {
- if(is_null($this->_ID))
- $this->_ID = $value;
- else
- throw new TimeTrackerUserException(
- 'timetracker_user_readonly_id');
- }
-}
-
-class TimeTrackerUserException extends TimeTrackerException
-{
+ /**
+ * @return string user email address
+ */
+ public function getEmailAddress()
+ {
+ return $this->_emailAddress;
+ }
}
?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/APP_CODE/TimeTrackerUserTypeHandler.php b/demos/time-tracker/protected/APP_CODE/TimeTrackerUserTypeHandler.php
new file mode 100644
index 00000000..07c46acc
--- /dev/null
+++ b/demos/time-tracker/protected/APP_CODE/TimeTrackerUserTypeHandler.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * TimeTrackerUserTypeHandler class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+
+/**
+ * SQLMap type handler for TimeTrackerUser.
+ * The TimeTrackerUser requires an instance of IUserManager in constructor.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
+class TimeTrackerUserTypeHandler implements ITypeHandlerCallback
+{
+ /**
+ * Not implemented.
+ */
+ public function getParameter($object)
+ {
+ throw new TimeTrackerException('Not implemented');
+ }
+
+ /**
+ * Not implemented.
+ */
+ public function getResult($string)
+ {
+ throw new TimeTrackerException('Not implemented');
+ }
+
+ /**
+ * Creates a new instance of TimeTrackerUser
+ * @param array result data
+ * @return TimeTrackerUser new user instance
+ */
+ public function createNewInstance($row=null)
+ {
+ $manager = Prado::getApplication()->getModule('users');
+ if(is_null($manager))
+ $manager = new UserManager();
+ return new TimeTrackerUser($manager);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/APP_CODE/UserDao.php b/demos/time-tracker/protected/APP_CODE/UserDao.php
new file mode 100644
index 00000000..4dc39b2b
--- /dev/null
+++ b/demos/time-tracker/protected/APP_CODE/UserDao.php
@@ -0,0 +1,155 @@
+<?php
+/**
+ * User Dao class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+
+/**
+ * UserDao class list, create, find and delete users.
+ * In addition, it can validate username and password, and update
+ * the user roles. Furthermore, a unique new token can be generated,
+ * this token can be used to perform persistent cookie login.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
+class UserDao extends BaseDao
+{
+ /**
+ * @param string username
+ * @return TimeTrackerUser find by user name, null if not found or disabled.
+ */
+ public function getUserByName($username)
+ {
+ $sqlmap = $this->getConnection();
+ return $sqlmap->queryForObject('GetUserByName', $username);
+ }
+
+ /**
+ * @return array list of all enabled users.
+ */
+ public function getAllUsers()
+ {
+ $sqlmap = $this->getConnection();
+ return $sqlmap->queryForList('GetAllUsers');
+ }
+
+ /**
+ * @param TimeTrackerUser new user details.
+ * @param string new user password.
+ */
+ public function addNewUser($user, $password)
+ {
+ $sqlmap = $this->getConnection();
+ $param['user'] = $user;
+ $param['password'] = md5($password);
+ $sqlmap->insert('AddNewUser', $param);
+ if(count($user->getRoles()) > 0)
+ $this->updateUserRoles($user);
+ }
+
+ /**
+ * @param string username to delete
+ */
+ public function deleteUserByName($username)
+ {
+ $sqlmap = $this->getConnection();
+ $sqlmap->delete('DeleteUserByName', $username);
+ }
+
+ /**
+ * Updates the user profile details, including user roles.
+ * @param TimeTrackerUser updated user details.
+ * @param string new user password, null to avoid updating password.
+ */
+ public function updateUser($user,$password=null)
+ {
+ $sqlmap = $this->getConnection();
+ if($password !== null)
+ {
+ $param['user'] = $user;
+ $param['password'] = md5($password);
+ $sqlmap->update('UpdateUserDetailsAndPassword', $param);
+ }
+ else
+ {
+ $sqlmap->update('UpdateUserDetails', $user);
+ }
+ $this->updateUserRoles($user);
+ }
+
+ /**
+ * @param string username to be validated
+ * @param string matching password
+ * @return boolean true if the username and password matches.
+ */
+ public function validateUser($username, $password)
+ {
+ $sqlmap = $this->getConnection();
+ $param['username'] = $username;
+ $param['password'] = md5($password);
+ return $sqlmap->queryForObject('ValidateUser', $param);
+ }
+
+ /**
+ * @param string unique persistent session token
+ * @return TimeTrackerUser user details if valid token, null otherwise.
+ */
+ public function validateSignon($token)
+ {
+ $sqlmap = $this->getConnection();
+ $sqlmap->update('UpdateSignon', $token);
+ return $sqlmap->queryForObject('ValidateAutoSignon', $token);
+ }
+
+ /**
+ * @param TimeTrackerUser user details to generate the token
+ * @return string unique persistent login token.
+ */
+ public function createSignonToken($user)
+ {
+ $sqlmap = $this->getConnection();
+ $param['username'] = $user->getName();
+ $param['token'] = md5(microtime().$param['username']);
+ $sqlmap->insert('RegisterAutoSignon', $param);
+ return $param['token'];
+ }
+
+ /**
+ * @param TimeTrackerUser deletes all signon token for given user, null to delete all
+ * tokens.
+ */
+ public function clearSignonTokens($user=null)
+ {
+ $sqlmap = $this->getConnection();
+ if($user !== null)
+ $sqlmap->delete('DeleteAutoSignon', $user->getName());
+ else
+ $sqlmap->delete('DeleteAllSignon');
+ }
+
+ /**
+ * @param TimeTrackerUser user details for updating the assigned roles.
+ */
+ public function updateUserRoles($user)
+ {
+ $sqlmap = $this->getConnection();
+ $sqlmap->delete('DeleteUserRoles', $user);
+ foreach($user->getRoles() as $role)
+ {
+ $param['username'] = $user->getName();
+ $param['role'] = $role;
+ $sqlmap->update('AddUserRole', $param);
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/APP_CODE/UserManager.php b/demos/time-tracker/protected/APP_CODE/UserManager.php
new file mode 100644
index 00000000..1327dc3c
--- /dev/null
+++ b/demos/time-tracker/protected/APP_CODE/UserManager.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * UserManager class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+
+/**
+ * User manager module class for time tracker application.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
+class UserManager extends TModule implements IUserManager
+{
+ /**
+ * @return string name for a guest user.
+ */
+ public function getGuestName()
+ {
+ return 'Guest';
+ }
+
+ /**
+ * Returns a user instance given the user name.
+ * @param string user name, null if it is a guest.
+ * @return TUser the user instance, null if the specified username is not in the user database.
+ */
+ public function getUser($username=null)
+ {
+ if($username===null)
+ {
+ $user=new TUser($this);
+ $user->setIsGuest(true);
+ return $user;
+ }
+ else
+ {
+ $daos = $this->getApplication()->getModule('daos');
+ $userDao = $daos->getDao('UserDao');
+ $user = $userDao->getUserByName($username);
+ $user->setIsGuest(false);
+ return $user;
+ }
+ }
+
+ /**
+ * Validates if the username and password are correct.
+ * @param string user name
+ * @param string password
+ * @return boolean true if validation is successful, false otherwise.
+ */
+ public function validateUser($username,$password)
+ {
+ $daos = $this->getApplication()->getModule('daos');
+ $userDao = $daos->getDao('UserDao');
+ return $userDao->validateUser($username, $password);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/APP_CODE/exceptions.txt b/demos/time-tracker/protected/APP_CODE/exceptions.txt
index e948f4d0..6568cc72 100644
--- a/demos/time-tracker/protected/APP_CODE/exceptions.txt
+++ b/demos/time-tracker/protected/APP_CODE/exceptions.txt
@@ -1,3 +1,7 @@
timetracker_user_readonly_id = Time tracker user ID is read-only.
invalid_creator_and_manager = Unable to find time tracker usernames '{1}' and '{2}' for project '{0}'.
-project_exists = Project '{0}' already exists. \ No newline at end of file
+project_exists = Project '{0}' already exists.
+daomanager_connection_required = An TSqlMapper connection is required by Dao Manager.
+daomanager_undefined_connection = Connection '{0}' for Dao Manager is undefined.
+daomanager_invalid_connection = Connection '{0}' does not appear to ba a TSqlMapper in Dao Manager.
+daomanager_undefined_dao = Dao class '{0}' is not registered. \ No newline at end of file
diff --git a/demos/time-tracker/protected/data/time_tracker.db b/demos/time-tracker/protected/App_Data/time_tracker.db
index 03fe9156..03fe9156 100644
--- a/demos/time-tracker/protected/data/time_tracker.db
+++ b/demos/time-tracker/protected/App_Data/time_tracker.db
Binary files differ
diff --git a/demos/time-tracker/protected/pages/Docs/TopicList.tpl b/demos/time-tracker/protected/pages/Docs/TopicList.tpl
index 5fa2adb5..53243578 100644
--- a/demos/time-tracker/protected/pages/Docs/TopicList.tpl
+++ b/demos/time-tracker/protected/pages/Docs/TopicList.tpl
@@ -14,7 +14,7 @@
<div>Testing Business Code</div>
<ul>
- <li><a href="?page=Docs.CreateBusinessCode">Create Business Code</a></li>
+ <li><a href="?page=Docs.DatabaseDesign">Database Design</a></li>
<li><a href="?page=Docs.UsingSQLMap">Using SQLMap Data Mapper</a></li>
<li><a href="?page=Docs.UserClassAndExceptions">User Class and Exceptions</a></li>
<li><a href="?page=Docs.MoreTests">More Tests</a></li>
diff --git a/demos/time-tracker/protected/pages/Docs/WritingUnitTest.page b/demos/time-tracker/protected/pages/Docs/WritingUnitTest.page
index 32c7bc79..77bdcbe6 100644
--- a/demos/time-tracker/protected/pages/Docs/WritingUnitTest.page
+++ b/demos/time-tracker/protected/pages/Docs/WritingUnitTest.page
@@ -1,13 +1,19 @@
<com:TContent ID="body">
<h1>Writing a Unit Test</h1>
+<p>Before we begin to write our business logic and code, we shall
+proceed with the path of <a href="http://tdd.com">test driven development</a> (TDD), or at least take
+some part of that process.</p>
+
<p>Unit testing is a useful tool when we want to start to test
our individual business logic classes.
- The <tt>tests/unit</tt> directory will be used to hold the unit test cases and <tt>tests/functional</tt> directory
-to hold the function test cases.
+ The <tt>tests/unit</tt> directory will be used to hold the unit test
+ cases and <tt>tests/functional</tt> directory
+ to hold the function test cases.
</p>
<h2>Write a unit test case</h2>
-<p>We will start be writing a very simple unit test case.</p>
+<p>We will start be writing a very simple unit test case. Notice
+that we are writing the test case <b>first</b>.</p>
<com:TTextHighlighter Language="php" CssClass="source">
&lt;?php
class ProjectTestCase extends UnitTestCase
@@ -29,14 +35,16 @@ directory.</p>
<img src="<%~ unit_test1.png %>" class="figure"/>
<div class="caption"><b>Figure 1:</b> Unit test runner</div>
</p>
-<p>Clicking on the <tt>ProjectTestCase.php</tt> like, you should see
+<p>Clicking on the <tt>ProjectTestCase.php</tt> link, you should see
<img src="<%~ unit_test2.png %>" class="figure"/>
<div class="caption"><b>Figure 2:</b> Unit test failure</div>
</p>
<h2>Smallest step to make the test pass.</h2>
-<p>Obviously, we need create the class <tt>Project</tt>, so lets define the class.</p>
+<p>Since we only wrote the test case and nothing else we expected
+that the test case will fail at some point. Obviously, we need create
+a class <tt>Project</tt>, so lets define the <tt>Project</tt> class.</p>
<com:TTextHighlighter Language="php" CssClass="source">
&lt;?php
class Project
@@ -44,15 +52,11 @@ class Project
}
?&gt;
</com:TTextHighlighter>
-<p>Save the above code as <tt>time-tracker/protected/pages/APP_CODE/Project.php</tt>.
- Where the <tt>APP_CODE</tt> directory will contain most of your business logic code for the Time Tracker application.</p>
-<p>We also need to add the following line in our test case so as to include the <tt>Project</tt> class file when running the tests.</p>
-
-<p class="note">
-The statement <tt>Prado::using('Application.APP_CODE.Project')</tt> basically
-loads the <tt>Project.php</tt> class file. It assumes that a class name <tt>Project</tt> has filename <tt>Project.php</tt>.
-For futher details regarding <tt>Prado::using</tt> can be found in <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Fundamentals.Components#704">Prado Namespaces</a> documentation.
-</p>
+<p>We save the above code as <tt>time-tracker/protected/pages/APP_CODE/Project.php</tt>.
+ Where the <tt>APP_CODE</tt> directory will contain most of the business logic code
+ for the Time Tracker application.</p>
+<p>Now, we also need to add the following line in our test case so as to
+include the <tt>Project</tt> class file when running the tests.</p>
<com:TTextHighlighter Language="php" CssClass="source">
&lt;?php
@@ -63,11 +67,20 @@ class ProjectTestCase extends UnitTestCase
}
?&gt;
</com:TTextHighlighter>
+
+<div class="info"><b>Info:</b>
+The statement <tt>Prado::using('Application.APP_CODE.Project')</tt> basically
+loads the <tt>Project.php</tt> class file. It assumes that a class name <tt>Project</tt> has filename <tt>Project.php</tt>.
+For futher details regarding <tt>Prado::using</tt> can be found in <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Fundamentals.Components#704">Prado Namespaces</a> documentation.
+</div>
+
<p>Run the unit test runner again, we see that the test has passed.
<img src="<%~ unit_test3.png %>" class="figure"/>
<div class="caption"><b>Figure 3:</b> Unit test success</div>
</p>
<p>
-Later on, we shall write more test cases. See the SimpleTest documentation for detailed tutorial on writing test cases.</p>
+Later on, we shall write more test cases. See the
+<a href="http://www.lastcraft.com/simple_test.php">SimpleTest documentation</a>
+for detailed tutorial on writing test cases.</p>
</com:TContent> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/Docs/config.xml b/demos/time-tracker/protected/pages/Docs/config.xml
index da4d3bfc..e8fdc3fe 100644
--- a/demos/time-tracker/protected/pages/Docs/config.xml
+++ b/demos/time-tracker/protected/pages/Docs/config.xml
@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
- <paths>
- <alias id="Pages" path="." />
- </paths>
- <pages MasterClass="Application.pages.Docs.Layout" />
+
+ <modules>
+ <module id="theme"
+ class="System.Web.UI.TThemeManager"
+ BasePath="Quickstart.themes"
+ BaseUrl="../quickstart/themes" />
+ </modules>
+ <pages MasterClass="Application.pages.Docs.Layout" Theme="PradoSoft"/>
</configuration> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/Docs/db.png b/demos/time-tracker/protected/pages/Docs/db.png
index f2209ef4..efdcc1e5 100644
--- a/demos/time-tracker/protected/pages/Docs/db.png
+++ b/demos/time-tracker/protected/pages/Docs/db.png
Binary files differ
diff --git a/demos/time-tracker/protected/pages/TimeTracker/Login.page b/demos/time-tracker/protected/pages/TimeTracker/Login.page
new file mode 100644
index 00000000..dbc16de1
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/Login.page
@@ -0,0 +1,38 @@
+<%@ Title="My Company - Time Tracker - Site Logon" %>
+<com:TContent ID="Main">
+<h2>Time Tracker Website Login</h2>
+
+<fieldset class="login"><legend>User Login</legend>
+ <div class="username">
+ <com:TLabel ForControl="username" Text="User Name:" />
+ <span class="required">*</span>
+ <com:TTextBox ID="username" />
+ <com:TRequiredFieldValidator
+ ControlToValidate="username"
+ ErrorMessage="Please enter your username."
+ ControlCssClass="required-field" />
+ </div>
+ <div class="password">
+ <com:TLabel ForControl="password" Text="Password:" />
+ <span class="required">*</span>
+ <com:TTextBox ID="password" TextMode="Password" />
+ <com:TRequiredFieldValidator
+ ControlToValidate="password"
+ ErrorMessage="Please enter your password."
+ ControlCssClass="required-field" />
+ </div>
+ <div class="remember">
+ <com:TCheckBox ID="remember" Text="Remember me next time" />
+ </div>
+ <com:TCustomValidator
+ ControlToValidate="password"
+ Display="Dynamic"
+ Text="Your login attempt was not successful. Please try again."
+ OnServerValidate="validateUser" />
+ <div class="signin">
+ <com:TButton Text="Log In" OnClick="doLogin" />
+ </div>
+ <div class="create">
+ <a href="?page=TimeTracker.UserCreate">Create New User</a>
+ </fieldset>
+ </com:TContent> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/Login.php b/demos/time-tracker/protected/pages/TimeTracker/Login.php
new file mode 100644
index 00000000..376953a5
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/Login.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Login Page class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+
+/**
+ * Login page class.
+ *
+ * Validate the user credentials and redirect to requested page
+ * if successful.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
+class Login extends TPage
+{
+ /**
+ * Validates the username and password.
+ * @param TControl custom validator that created the event.
+ * @param TServerValidateEventParameter validation parameters.
+ */
+ public function validateUser($sender, $param)
+ {
+ $authManager=$this->Application->getModule('auth');
+ if(!$authManager->login($this->username->Text,$this->password->Text))
+ $param->IsValid=false;;
+ }
+
+ /**
+ * Redirect to the requested page if login is successful.
+ * @param TControl button control that created the event.
+ * @param TEventParameter event parameters.
+ */
+ public function doLogin($sender, $param)
+ {
+ if($this->Page->IsValid)
+ {
+ $auth = $this->Application->getModule('auth');
+ $this->Response->redirect($auth->getReturnUrl());
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/Logout.page b/demos/time-tracker/protected/pages/TimeTracker/Logout.page
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/Logout.page
diff --git a/demos/time-tracker/protected/pages/TimeTracker/Logout.php b/demos/time-tracker/protected/pages/TimeTracker/Logout.php
new file mode 100644
index 00000000..08fdfaf6
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/Logout.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Logout class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+
+/**
+ * Logout page class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
+class Logout extends TPage
+{
+ /**
+ * Logs out the current user and redirect to default page.
+ */
+ function onLoad($param)
+ {
+ $this->Application->getModule('auth')->logout();
+ $url = $this->Service->constructUrl($this->Service->DefaultPage);
+ $this->Response->redirect($url);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/MainLayout.php b/demos/time-tracker/protected/pages/TimeTracker/MainLayout.php
new file mode 100644
index 00000000..253d6c03
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/MainLayout.php
@@ -0,0 +1,7 @@
+<?php
+
+class MainLayout extends TTemplateControl
+{
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/MainLayout.tpl b/demos/time-tracker/protected/pages/TimeTracker/MainLayout.tpl
new file mode 100644
index 00000000..2d8bad44
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/MainLayout.tpl
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<com:THead Title="My Company - Time Tracker - Log" />
+<body>
+
+<com:TForm>
+
+<h1 class="heading">
+ <a href="index.php">My Company
+ <span class="subheading">Time Tracker</span>
+ </a>
+</h1>
+<div class="minheading">
+<h2 class="login">
+ <com:TLabel CssClass="name" Text="Welcome <%= $this->User->Name %>" />
+ <com:THyperLink
+ Text="Login"
+ NavigateUrl=<%= $this->Service->constructUrl('TimeTracker.Login') %>
+ Visible=<%= $this->User->getIsGuest() %> />
+ <com:THyperLink
+ Text="Logout"
+ NavigateUrl=<%= $this->Service->constructUrl('TimeTracker.Logout') %>
+ Visible=<%= !$this->User->getIsGuest() %> />
+</h2>
+<h2 class="help"><a href="?page=Welcome">Help</a></h2>
+<h2 class="guide"><a href="?page=Docs.Home">Implementation Guide</a></h2>
+</div>
+
+<com:Application.pages.TimeTracker.SiteMap
+ Visible=<%= !$this->User->getIsGuest() %> />
+
+<div class="main">
+<com:TContentPlaceHolder ID="Main" />
+</div>
+
+</com:TForm>
+
+</body>
+</html> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/SiteMap.php b/demos/time-tracker/protected/pages/TimeTracker/SiteMap.php
new file mode 100644
index 00000000..0b71eb68
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/SiteMap.php
@@ -0,0 +1,8 @@
+<?php
+
+class SiteMap extends TTemplateControl
+{
+
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/SiteMap.tpl b/demos/time-tracker/protected/pages/TimeTracker/SiteMap.tpl
new file mode 100644
index 00000000..48187b52
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/SiteMap.tpl
@@ -0,0 +1,43 @@
+<com:TPanel CssClass="sitemap" Visible="true">
+<ul class="level1">
+ <li class="active"><a class="menuitem" href="?page=TimeTracker.TimeEntry">Log</a>
+ </li>
+ <li><span class="menuitem">Reports</span>
+ <ul class="level2">
+ <li><a href="?page=TimeTracker.ReportProject">Project Reports</a></li>
+ <li><a href="?page=TimeTracker.ReportResource">Resources Report</a></li>
+ </ul>
+ </li>
+ <li>
+ <span class="menuitem">Projects</span>
+ <ul class="level2">
+ <li><a href="?page=TimeTracker.ProjectDetails">Create New Project</a></li>
+ <li><a href="?page=TimeTracker.ProjectList">List Projects</a></li>
+ </ul>
+ </li>
+ <li>
+ <span class="menuitem">Adminstration</span>
+ <ul class="level2">
+ <li><a href="?page=TimeTracker.UserCreate">Create New User</a></li>
+ <li><a href="?page=TimeTracker.UserList">List Users</a></li>
+ </ul>
+ </li>
+</ul>
+<com:TClientScript PradoScripts="prado">
+ Event.OnLoad(function()
+ {
+ menuitems = $$(".menuitem");
+ menuitems.each(function(el)
+ {
+ Event.observe(el, "mouseover", function(ev)
+ {
+ menuitems.each(function(item)
+ {
+ Element.removeClassName(item.parentNode, "active");
+ });
+ Element.addClassName(Event.element(ev).parentNode, "active");
+ });
+ });
+ });
+</com:TClientScript>
+</com:TPanel> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/UserCreate.page b/demos/time-tracker/protected/pages/TimeTracker/UserCreate.page
new file mode 100644
index 00000000..fda7ba9b
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/UserCreate.page
@@ -0,0 +1,65 @@
+<com:TContent ID="Main">
+<h2>Create New User</h2>
+
+<fieldset class="signup"><legend>User Details</legend>
+ <h4>Sign Up for Your New Account</h4>
+ <div class="username">
+ <com:TLabel ForControl="username" Text="User Name:" />
+ <span class="required">*</span>
+ <com:TTextBox ID="username" />
+ <com:TRequiredFieldValidator
+ ControlToValidate="username"
+ Display="Dynamic"
+ ErrorMessage="Please enter your username."
+ ControlCssClass="required-field" />
+ <com:TCustomValidator
+ ControlToValidate="username"
+ Display="Dynamic"
+ OnServerValidate="checkUsername" />
+ </div>
+ <div class="password">
+ <com:TLabel ForControl="password" Text="Password:" />
+ <span class="required">*</span>
+ <com:TTextBox ID="password" TextMode="Password" />
+ <com:TRequiredFieldValidator
+ ControlToValidate="password"
+ Display="Dynamic"
+ ErrorMessage="Please enter your password (6 or more characters)."
+ ControlCssClass="required-field" />
+ <com:TRegularExpressionValidator
+ ControlToValidate="password"
+ Display="Dynamic"
+ RegularExpression="\w{6,}"
+ ErrorMessage="Please enter 6 or more characters."
+ ControlCssClass="required-field" />
+ </div>
+ <div class="password">
+ <com:TLabel ForControl="password2" Text="Confirm Password:" />
+ <span class="required">*</span>
+ <com:TTextBox ID="password2" TextMode="Password" />
+ <com:TCompareValidator
+ ControlToValidate="password"
+ ControlToCompare="password2"
+ ErrorMessage="The Password and Confirmation Password must match."
+ ControlCssClass="required-field" />
+ </div>
+ <div class="email">
+ <com:TLabel ForControl="email" Text="E-Mail Address:" />
+ <span class="required">*</span>
+ <com:TTextBox ID="email" Style="width:20em"/>
+ <com:TRequiredFieldValidator
+ ControlToValidate="email"
+ Display="Dynamic"
+ ErrorMessage="Please enter your E-Mail address."
+ ControlCssClass="required-field" />
+ <com:TEmailAddressValidator
+ ControlToValidate="email"
+ Display="Dynamic"
+ ErrorMessage="E-Mail address does not seem to be valid."
+ ControlCssClass="required-field" /> </div>
+ <div class="create">
+ <com:TButton Text="Create User" OnClick="createNewUser" />
+ </div>
+</fieldset>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/UserCreate.php b/demos/time-tracker/protected/pages/TimeTracker/UserCreate.php
new file mode 100644
index 00000000..b337bfca
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/UserCreate.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * UserCreate page class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ */
+
+/**
+ * Create new user page class. Validate that the usernames are unique
+ * and set the new user credentials as the current application credentials.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $16/07/2006: $
+ * @package Demos
+ * @since 3.1
+ */
+class UserCreate extends TPage
+{
+ /**
+ * Verify that the username is not taken.
+ * @param TControl custom validator that created the event.
+ * @param TServerValidateEventParameter validation parameters.
+ */
+ public function checkUsername($sender, $param)
+ {
+ $userDao = $this->Application->Modules['daos']->getDao('UserDao');
+ $user = $userDao->getUserByName($this->username->Text);
+ if(!is_null($user))
+ {
+ $param->IsValid = false;
+ $sender->ErrorMessage =
+ "The user name is already taken, try '{$user->Name}01'";
+ }
+ }
+
+ /**
+ * Create a new user if all data entered are valid.
+ * The default user roles are obtained from "config.xml". The new user
+ * details is saved to the database and the new credentials are used as the
+ * application user. The user is redirected to the requested page.
+ * @param TControl button control that created the event.
+ * @param TEventParameter event parameters.
+ */
+ public function createNewUser($sender, $param)
+ {
+ if($this->IsValid)
+ {
+ $newUser = new TimeTrackerUser($this->User->Manager);
+ $newUser->EmailAddress = $this->email->Text;
+ $newUser->Name = $this->username->Text;
+ $newUser->IsGuest = false;
+ $newUser->Roles = $this->Application->Parameters['NewUserRoles'];
+
+ //save the user
+ $userDao = $this->Application->Modules['daos']->getDao('UserDao');
+ $userDao->addNewUser($newUser, $this->password->Text);
+
+ //update the user
+ $auth = $this->Application->getModule('auth');
+ $auth->updateSessionUser($newUser);
+ $this->Application->User = $newUser;
+
+ //return to requested page
+ $this->Response->redirect($auth->getReturnUrl());
+
+ //goto default page.
+ //$url = $this->Service->constructUrl($this->Service->DefaultPage);
+ //$this->Response->redirect($url);
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/UserList.page b/demos/time-tracker/protected/pages/TimeTracker/UserList.page
new file mode 100644
index 00000000..48b2bbc7
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/UserList.page
@@ -0,0 +1,3 @@
+<com:TContent ID="Main">
+<h1>List Users</h1>
+</com:TContent> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/TimeTracker/config.xml b/demos/time-tracker/protected/pages/TimeTracker/config.xml
new file mode 100644
index 00000000..dac6465d
--- /dev/null
+++ b/demos/time-tracker/protected/pages/TimeTracker/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<configuration>
+
+ <modules>
+ <!-- user manager module -->
+ <module id="users" class="Application.App_Code.UserManager" />
+ <!-- auth manager module -->
+ <module id="auth" class="System.Security.TAuthManager"
+ UserManager="users" LoginPage="TimeTracker.Login" />
+ </modules>
+
+ <authorization>
+ <allow roles="admin" />
+ <allow pages="UserCreate,Logout,Login" users="*" />
+ <deny users="*" />
+ </authorization>
+
+ <pages MasterClass="Application.pages.TimeTracker.MainLayout" Theme="TimeTracker" />
+
+ <parameters>
+ <parameter id="NewUserRoles" value="admin,manager,consultant" />
+ </parameters>
+</configuration> \ No newline at end of file
diff --git a/demos/time-tracker/protected/pages/Welcome.page b/demos/time-tracker/protected/pages/Welcome.page
new file mode 100644
index 00000000..65ada02d
--- /dev/null
+++ b/demos/time-tracker/protected/pages/Welcome.page
@@ -0,0 +1,168 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" >
+<head>
+ <title>Welcome</title>
+ <style type="text/css">
+ table {
+ border-collapse:collapse;
+ }
+
+ td, th {
+ border:1px solid black;
+ padding: 10px;
+ }
+
+ th {
+ text-align:left;
+ }
+ </style>
+</head>
+<body>
+ <h1>Time Tracker Starter Kit</h1>
+ <p>
+ Welcome to your new <strong>Time Tracker</strong> sample application. The key features are:</p>
+ <ul>
+ <li><strong>Projects.</strong> Define project information like due dates, hours to complete,
+ project resources, and more.</li>
+ <li><strong>Track Time.</strong> Track time spent each day by category and project.</li>
+ <li><strong>Reports.</strong> Generate progress and team resource reports across multiple projects.</li>
+ </ul>
+ <p>
+ The <a href="index.php">Time Tracker site is ready to run!</a> No changes are needed.</p>
+
+ <hr />
+
+ <h2>Site Members and Roles</h2>
+ <p>
+ Your Time Tracker Web site allows visitors to register as members and then log in.
+ Members have specific privileges defined by roles such as administrator or guest.
+ Each Time Tracker Web site defines site-specific roles.
+ The following table describes what features are available to visitors in different roles.</p>
+ <table>
+ <tr>
+ <th>Visitor</th>
+ <th>Privileges</th>
+ <th>Default Login</th>
+ </tr>
+ <tr>
+ <td>
+ Not logged in</td>
+ <td>
+ No privileges.</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Logged in</td>
+ <td>
+ No privileges. All members must be associated at least with the role <strong>Consultant</strong>.</td>
+ <td>N/A</td>
+ </tr>
+ <tr>
+ <td>Logged in as <br />
+ <strong>Consultant</strong></td>
+ <td>May log time entries only.</td>
+ <td>username: <tt>consultant</tt><br />
+ password: <tt>consultant</tt></td>
+ </tr>
+ <tr>
+ <td>
+ Logged in as<br />
+ <strong>Project Manager</strong></td>
+ <td>
+ May additionally edit all projects and view reports.</td>
+ <td>username: <tt>manager</tt><br />
+ password: <tt>manager</tt></td>
+ </tr>
+ <tr>
+ <td>
+ Logged in as<br />
+ <strong>Project Administrator</strong></td>
+ <td>
+ May additionally view the list of all users.</td>
+ <td>username: <tt>admin</tt><br />
+ password: <tt>admin</tt></td>
+ </tr>
+ </table>
+ <p>
+ Be sure to create a user name for yourself and assign yourself to a role (such as administrator) that can manage the site.</p>
+ <p>
+ Visitors can register by clicking the <strong>Create new </strong>user link on the home page.
+ New members are activated automatically, and are assigned to a role as specified in the <tt>application.xml</tt> file.
+ You can manage users (for example, assign them to a role) when login as administrator.
+ For details, see <a href="#AppendixA">Appendix A</a>.</p>
+
+ <hr />
+
+ <h2>Projects and Time Entries</h2>
+ <h3>To add a project and categories</h3>
+ <ol>
+ <li>Log in to the site as a member in the role <strong>Project Manager</strong> or <strong>Project Administrator</strong>.</li>
+ <li>Click the <strong>Projects</strong> tab and then click <strong>Create New Project</strong>.</li>
+ <li>Specify a project name, project manager, estimated complete date, estimated duration, and description.</li>
+ <li>Under <strong>Specify Project Members</strong>, select a resource. You must select at least one resource. </li>
+ <li>Click <strong>Save</strong>. A category pane is displayed on the right.</li>
+ <li>Specify a category name, category abbreviation, and duration.</li>
+ <li>Click <strong>Add</strong>. The new category is displayed in the categories list. </li>
+ <li>Repeat steps 6 and 7 to create additional categories.</li>
+ </ol>
+ <h3>Log a Time Entry</h3>
+ <ol>
+ <li>Log in to the site as a consultant (member in the role <strong>Consultant</strong>).</li>
+ <li>Click the <strong>Log</strong> tab.</li>
+ <li>Under <strong>Log your hours</strong>, choose a project and a category and fill in the day, hours, and description.</li>
+ <li>Make sure the correct consultant is selected in the <strong>Time Sheet For</strong> list. </li>
+ <li>Click <strong>Add Entry</strong>.</li>
+ </ol>
+
+ <hr />
+
+ <h2>Reports</h2>
+ <h3>To create a project report</h3>
+ <ol>
+ <li>Log in to the site as a member in the role <strong>Project Manager</strong> or <strong>Project Administrator</strong>.</li>
+ <li>Click the <strong>Reports</strong> tab and then click <strong>Project Reports</strong>. </li>
+ <li>Under <strong>Select a project</strong>,<strong> </strong>choose one or more projects.</li>
+ <li>Click <strong>Generate Report</strong>.</li>
+ </ol>
+ <h3>To create a resource report</h3>
+ <ol>
+ <li>Log in to the site as a member in the role <strong>Project Manager</strong> or <strong>Project Administrator</strong>.</li>
+ <li>Click the <strong>Reports</strong> tab and then click <strong>Resources Report</strong>. </li>
+ <li>Select one or more projects, select one or more resources, and then specify a date range. </li>
+ <li>Click <strong>Generate Report</strong>.</li>
+ </ol>
+
+ <hr />
+
+ <a name="AppendixA" />
+ <h2>Appendix A - Manually Managing Members and Roles</h2>
+ <p>
+ Your Time Tracker Web site allows visitors to register as members.
+ Members have specific privileges defined by a role you assign to them.
+ A special administrative role has rights to perform all functions in the site.</p>
+ <p>
+ To create a user (member):</p>
+ <ol>
+ <li>Login as administrator, click <strong>Create New User</strong>.
+ </li>
+ <li>...</li>
+ </ol>
+ <p>
+ To modify an existing member's role:</p>
+ <ol>
+ <li>Login as administrator, click <strong>List Users</strong>.
+ </li>
+ <li>...</li>
+ </ol>
+
+ <hr />
+
+ <h2>Appendix B - Publishing Your Site</h2>
+ <p>
+ When you are ready to share the Web site with others, you can copy it to your Web server.
+ You need to know the File Transfer Protocol (FTP) address of your server, and if required, the user name and password assigned to you.</p>
+ <ol>
+ <li>...</li>
+ </ol>
+</body>
+</html>