From ab6effd64caefa732ef0994d6c3048f5cd474bf6 Mon Sep 17 00:00:00 2001
From: Daniel <darthdaniel85@gmail.com>
Date: Fri, 22 Nov 2013 15:25:30 -0500
Subject: WSAT beta release.

---
 framework/Wsat/TWsatARGenerator.php       | 368 +++++++++++++++++++-----------
 framework/Wsat/TWsatService.php           | 123 +++++-----
 framework/Wsat/pages/TWsatGenerateAR.page |  20 +-
 framework/Wsat/pages/TWsatGenerateAR.php  |  80 ++++---
 framework/Wsat/pages/TWsatLogin.page      |   7 +-
 framework/Wsat/pages/TWsatLogin.php       |  59 ++---
 framework/Wsat/themes/PradoSoft/main.css  | 282 ++++++++++++-----------
 7 files changed, 541 insertions(+), 398 deletions(-)

diff --git a/framework/Wsat/TWsatARGenerator.php b/framework/Wsat/TWsatARGenerator.php
index 7b39fce1..433e1640 100644
--- a/framework/Wsat/TWsatARGenerator.php
+++ b/framework/Wsat/TWsatARGenerator.php
@@ -1,136 +1,232 @@
-<?php
-
-/**
- * Description of TWsatARGenerator
- *
- * @author Daniel
- */
-class TWsatARGenerator {
-
-    /**
-     * @return TActiveRecordManager static instance of record manager.
-     */
-    private $_ar_manager;
-
-    /**
-     * Output folder (by namespace) where AR classes will be generated.
-     */
-    private $_opnamespace;
-
-    function __construct($_opnamespace = "Application.App_Data.AR_Classes") {
-        $this->_ar_manager = TActiveRecordManager::getInstance();
-        $this->_opnamespace = $_opnamespace;
-    }
-
-    // <editor-fold defaultstate="collapsed" desc="Main APIs">
-    public function generate($tableName, $clasName = '') {
-        $conn = $this->_ar_manager->getDbConnection();
-        $gateway = $this->_ar_manager->getRecordGateway();
-        $tableInfo = $gateway->getTableInfo($conn, $tableName);
-
-        if (count($tableInfo->getColumns()) === 0) {
-            throw new TIOException("Unable to find table or view $tableName in " . $conn->getConnectionString() . ".");
-        } else {
-            $properties = array();
-            foreach ($tableInfo->getColumns() as $field => $column)
-                $properties[] = $this->generateProperty($field, $column);
-        }
-
-        $clasName = empty($clasName) ? $this->_getClassName($tableName) : $clasName;
-        $class = $this->generateClass($properties, $tableName, $clasName);
-        $op_file = Prado::getPathOfNamespace($this->_opnamespace);
-        if (!is_dir($op_file)) {
-            mkdir($op_file, 0777, true);
-        }
-
-        $output = $op_file . DIRECTORY_SEPARATOR . $clasName . ".php";
-        file_put_contents($output, $class);
-    }
-
-    public function generateAll() {
-        foreach ($this->_getAllTableNames() as $tableName) {
-            $this->generate($tableName);
-        }
-    }
-
-// </editor-fold>
-//-----------------------------------------------------------------------------
-    // <editor-fold defaultstate="collapsed" desc="Common Methods">
-    private function getDbConnection() {
-        $con = $this->_ar_manager->getDbConnection();
-        $con->Active = true;
-        return $con;
-    }
-
-    private function _getAllTableNames() {
-        $con = $this->getDbConnection();
-        $command = $con->createCommand("Show Tables");
-        $dataReader = $command->query();
-        $dataReader->bindColumn(1, $table);
-        $tables = array();
-        while ($dataReader->read()) {
-            $tables[] = $table;
-        }
-        $con->setActive(false);
-        return $tables;
-    }
-
-    private function _getClassName($tableName) {
-        return ucfirst($tableName);
-    }
-
-    public function renderAllTablesInformation() {
-        $conn = $this->_ar_manager->getDbConnection();
-        $gateway = $this->_ar_manager->getRecordGateway();
-
-        foreach ($this->_getAllTableNames() as $table_name) {
-            echo $table_name . "<br>";
-
-            $tableInfo = $gateway->getTableInfo($conn, $table_name);
-            echo "Table info:" . "<br>";
-            echo "<pre>";
-            var_dump($tableInfo);
-            echo "</pre>";
-        }
-    }
-
-//-----------------------------------------------------------------------------
-
-    protected function generateProperty($field, $column) {
-        $prop = '';
-        $name = '$' . $field;
-
-        /* TODO use in version 2.0 */
-        // $type = $column->getPHPType();
-
-        $prop .= "\tpublic $name;";
-        return $prop;
-    }
-
-    protected function generateClass($properties, $tablename, $class) {
-        $props = implode("\n", $properties);
-        $date = date('Y-m-d h:i:s');
-        return <<<EOD
-<?php
-/**
- * Auto generated by prado-cli.php on $date.
- */
-class $class extends TActiveRecord
-{
-	const TABLE='$tablename';
-
-$props
-
-	public static function finder(\$className=__CLASS__)
-	{
-		return parent::finder(\$className);
-	}
-}
-?>
-EOD;
-    }
-
-// </editor-fold>
-}
-
-?>
+<?php
+
+/**
+ * Description of TWsatARGenerator
+ *
+ * @author Daniel
+ */
+class TWsatARGenerator {
+
+    /**
+     * Gets the current Db connection, the connection object is obtained from
+     * the TActiveRecordManager if connection is currently null.
+     * @return TDbConnection current db connection for this object.
+     */
+    private $_conn;
+
+    /**
+     * @return TActiveRecordGateway record table gateway.
+     */
+    private $_gateway;
+
+    /**
+     * Output folder where AR classes will be generated.
+     */
+    private $_op_file;
+
+    /**
+     * Class name prefix
+     */
+    private $_clas_prefix;
+
+    /**
+     * all table relations array
+     */
+    private $_relations;
+
+    function __construct() {
+        $ar_manager = TActiveRecordManager::getInstance();
+        $this->_conn = $ar_manager->getDbConnection();
+        $this->_conn->Active = true;
+        $this->_gateway = $ar_manager->getRecordGateway();
+    }
+
+    /**
+     * Destructor.
+     * Disconnect the db connection.
+     */
+    public function __destruct() {
+        if ($this->_conn !== null)
+            $this->_conn->Active = false;
+    }
+
+    public function setOpFile($op_file_namespace) {
+        $op_file = Prado::getPathOfNamespace($op_file_namespace);
+        if (empty($op_file)) {
+            throw new Exception("You need to fix your output folder namespace.");
+        }
+        if (!is_dir($op_file)) {
+            mkdir($op_file, 0777, true);
+        }
+        $this->_op_file = $op_file;
+    }
+
+    public function setClasPrefix($_clas_prefix) {
+        $this->_clas_prefix = $_clas_prefix;
+    }
+
+//-----------------------------------------------------------------------------    
+    // <editor-fold defaultstate="collapsed" desc="Main APIs">
+    public function generate($tableName) {
+        $tableInfo = $this->_gateway->getTableInfo($this->_conn, $tableName);
+        if (count($tableInfo->getColumns()) === 0) {
+            throw new Exception("Unable to find table or view $tableName in " . $this->_conn->getConnectionString() . ".");
+        } else {
+            $properties = array();
+            foreach ($tableInfo->getColumns() as $field => $column)
+                $properties[] = $this->generateProperty($field, $column);
+            $toString = $this->_buildSmartToString($tableInfo);
+        }
+
+        $clasName = $this->_getProperClassName($tableName);
+        $class = $this->generateClass($properties, $tableName, $clasName, $toString);
+        $output = $this->_op_file . DIRECTORY_SEPARATOR . $clasName . ".php";
+        file_put_contents($output, $class);
+    }
+
+    public function generateAll() {
+        foreach ($this->_getAllTableNames() as $tableName) {
+            if ($tableName == "pradocache") {
+                continue;
+            }
+            $this->generate($tableName);
+        }
+    }
+
+    public function buildRelations() {
+        $this->_relations = array();
+        foreach ($this->_getAllTableNames() as $table_name) {
+            $tableInfo = $this->_gateway->getTableInfo($this->_conn, $table_name);
+            foreach ($tableInfo->getForeignKeys() as $fk_data) {
+                $owner_table = $fk_data["table"];
+                $slave_table = $table_name;
+                $fk_prop = key($fk_data["keys"]);
+
+                $this->_relations[$owner_table][] = array(
+                    "prop_name" => strtolower($slave_table),
+                    "rel_type" => "self::HAS_MANY",
+                    "ref_class_name" => $this->_getProperClassName($slave_table),
+                    "prop_ref" => $fk_prop
+                );
+
+                $this->_relations[$slave_table][] = array(
+                    "prop_name" => strtolower($owner_table),
+                    "rel_type" => "self::BELONGS_TO",
+                    "ref_class_name" => $this->_getProperClassName($owner_table),
+                    "prop_ref" => $fk_prop
+                );
+            }
+        }
+    }
+
+// </editor-fold>
+//-----------------------------------------------------------------------------
+    // <editor-fold defaultstate="collapsed" desc="Common Methods">
+
+    private function _getAllTableNames() {
+        $command = $this->_conn->createCommand("Show Tables");
+        $dataReader = $command->query();
+        $dataReader->bindColumn(1, $table);
+        $tables = array();
+        while ($dataReader->read()) {
+            $tables[] = $table;
+        }
+        return $tables;
+    }
+
+    private function _getProperClassName($tableName) {
+        $table_name_words = str_replace("_", " ", strtolower($tableName));
+        $final_conversion = str_replace(" ", "", ucwords($table_name_words));
+        return $this->_clas_prefix . $final_conversion;
+    }
+
+    public function renderAllTablesInformation() {
+        foreach ($this->_getAllTableNames() as $table_name) {
+            echo $table_name . "<br>";
+
+            $tableInfo = $this->_gateway->getTableInfo($this->_conn, $table_name);
+            echo "Table info:" . "<br>";
+            echo "<pre>";
+            var_dump($tableInfo);
+            echo "</pre>";
+        }
+    }
+
+//-----------------------------------------------------------------------------
+
+    protected function generateProperty($field, $column) {
+        $prop = '';
+        $name = '$' . $field;
+
+        /* TODO use in version 2.0 */
+        // $type = $column->getPHPType();
+
+        $prop .= "\tpublic $name;";
+        return $prop;
+    }
+
+    private function _renderRelations($tablename) {
+        if (!isset($this->_relations[$tablename])) {
+            return "";
+        }
+        $code = "\tpublic static \$RELATIONS = array (";
+        foreach ($this->_relations[$tablename] as $rel_data) {
+            $code .= "\n\t\t'" . $rel_data["prop_name"] . "' => array(" . $rel_data["rel_type"] . ", '" . $rel_data["ref_class_name"] . "', '" . $rel_data["prop_ref"] . "'),";
+        }
+        $code = substr($code, 0, -1);
+        $code .= "\n\t);";
+        return $code;
+    }
+
+    private function _buildSmartToString($tableInfo) {
+        $code = "\tpublic function __toString() {";
+        $property = "throw new THttpException(500, 'Not implemented yet.');";
+        try {
+            foreach ($tableInfo->getColumns() as $column) {
+                if (isset($column->IsPrimaryKey) && $column->IsPrimaryKey) {
+                    $property = str_replace(array("`", "'", '"'), "", $column->ColumnName);
+                } elseif ($column->DbType == "varchar") {
+                    $property = str_replace(array("`", "'", '"'), "", $column->ColumnName);
+                    break;
+                }
+            }
+        } catch (Exception $ex) {
+            
+        }
+        $code .= "\n\t\treturn \$this->$property;";
+        $code .= "\n\t}";
+        return $code;
+    }
+
+    protected function generateClass($properties, $tablename, $classname, $toString) {
+        $props = implode("\n", $properties);
+        $relations = $this->_renderRelations($tablename);
+        $date = date('Y-m-d h:i:s');
+        return <<<EOD
+<?php
+/**
+ * Auto generated by PRADO - WSAT on $date.
+ * @author prado_user_name               
+ */
+class $classname extends TActiveRecord {
+                
+	const TABLE='$tablename';
+
+$props
+
+	public static function finder(\$className=__CLASS__) {
+                return parent::finder(\$className);
+	}
+                
+$relations
+                
+$toString
+}
+?>
+EOD;
+    }
+
+// </editor-fold>
+}
+
+?>
diff --git a/framework/Wsat/TWsatService.php b/framework/Wsat/TWsatService.php
index de2bbdf6..3af34d49 100644
--- a/framework/Wsat/TWsatService.php
+++ b/framework/Wsat/TWsatService.php
@@ -1,61 +1,62 @@
-<?php
-
-/**
- * Description of TWsat
- * Inspired in both Microsoft Web Site Administration Tool(WSAT) and Yii's Gii.
- * @version 1.0
- * @author Daniel Sampedro darthdaniel85@gmail.com
- * @since Prado 3.3
- * 
- * To use TWsatService, configure it in the application specification like following:
- * <code>
- *   <services>
- *    <service id="wsat" class="System.Wsat.TWsatService" Password="my_secret_password" />
- *   </services>
- * </code>
- * ...and then you need to go to http://localhost/yoursite/index.php?wsat=TWsatLogin
- * and generate code and configure your site.
- */
-class TWsatService extends TPageService {
-
-    private $_pass = '';
-
-//-----------------------------------------------------------------------------    
-    public function init($config) {
-        if ($this->getApplication()->getMode() === TApplicationMode::Performance || $this->getApplication()->getMode() === TApplicationMode::Normal) {
-            throw new TInvalidOperationException("You should not use Prado WSAT in any of the production modes.");
-        }
-        if (empty($this->_pass)) {
-            throw new TConfigurationException("You need to specify the Password attribute.");
-        }
-        $this->setDefaultPage("TWsatHome");
-        $this->_startThemeManager();
-        parent::init($config);
-    }
-
-    public function getBasePath() {
-        $basePath = Prado::getPathOfNamespace("System.Wsat.pages");
-        return realpath($basePath);
-    }
-
-    private function _startThemeManager() {
-        $themeManager = new TThemeManager;
-        $themeManager->BasePath = "System.Wsat.themes";
-        $url = Prado::getApplication()->getAssetManager()->publishFilePath(Prado::getPathOfNamespace('System.Wsat'));
-        $themeManager->BaseUrl = $url . DIRECTORY_SEPARATOR . "themes";
-
-        $themeManager->init(null);
-        $this->setThemeManager($themeManager);
-    }
-
-    public function getPassword() {
-        return $this->_pass;
-    }
-
-    public function setPassword($_pass) {
-        $this->_pass = $_pass;
-    }
-
-}
-
-?>
+<?php
+
+/**
+ * Description of TWsat
+ * Inspired in both Microsoft Web Site Administration Tool(WSAT) and Yii's Gii.
+ * @version 1.0
+ * @author Daniel Sampedro darthdaniel85@gmail.com
+ * @since Prado 3.3
+ * 
+ * To use TWsatService, configure it in the application configuration file like following:
+ * <code>
+ *   <services>
+ *     ...
+ *     <service id="wsat" class="System.Wsat.TWsatService" Password="my_secret_password" />
+ *   </services>
+ * </code>
+ * ...and then you need to go to http://localhost/yoursite/index.php?wsat=TWsatLogin
+ * and generate code and configure your site.
+ */
+class TWsatService extends TPageService {
+
+    private $_pass = '';
+
+//-----------------------------------------------------------------------------    
+    public function init($config) {
+        if ($this->getApplication()->getMode() === TApplicationMode::Performance || $this->getApplication()->getMode() === TApplicationMode::Normal) {
+            throw new TInvalidOperationException("You should not use Prado WSAT in any of the production modes.");
+        }
+        if (empty($this->_pass)) {
+            throw new TConfigurationException("You need to specify the Password attribute.");
+        }
+        $this->setDefaultPage("TWsatHome");
+        $this->_startThemeManager();
+        parent::init($config);
+    }
+
+    public function getBasePath() {
+        $basePath = Prado::getPathOfNamespace("System.Wsat.pages");
+        return realpath($basePath);
+    }
+
+    private function _startThemeManager() {
+        $themeManager = new TThemeManager;
+        $themeManager->BasePath = "System.Wsat.themes";
+        $url = Prado::getApplication()->getAssetManager()->publishFilePath(Prado::getPathOfNamespace('System.Wsat'));
+        $themeManager->BaseUrl = $url . DIRECTORY_SEPARATOR . "themes";
+
+        $themeManager->init(null);
+        $this->setThemeManager($themeManager);
+    }
+
+    public function getPassword() {
+        return $this->_pass;
+    }
+
+    public function setPassword($_pass) {
+        $this->_pass = $_pass;
+    }
+
+}
+
+?>
diff --git a/framework/Wsat/pages/TWsatGenerateAR.page b/framework/Wsat/pages/TWsatGenerateAR.page
index 3905d023..acd456fd 100644
--- a/framework/Wsat/pages/TWsatGenerateAR.page
+++ b/framework/Wsat/pages/TWsatGenerateAR.page
@@ -1,21 +1,31 @@
 <com:TContent ID="Content">
-    <div style="margin-left: 220px">
+    <div style="margin-left: 220px; font-size: 14px">
         <div class="form_row">
-        <com:TLabel Text="Table Name:" ForControl="table_name" style="margin-right: 14px" />
+        <com:TLabel Text="Table Name:" ForControl="table_name" style="margin-right: 24px" />
         <com:TTextBox ID="table_name" Text="*" CssClass="in_text" />
+        <com:TRequiredFieldValidator ControlToValidate="table_name" Text="Table name cannot be blank." Display="Dynamic" />
         </div>
         <div class="form_row">
-            <com:TLabel Text="Class Prefix:" ForControl="class_prefix" style="margin-right: 16px"/>
+            <com:TLabel Text="Class Prefix:" ForControl="class_prefix" style="margin-right: 25px"/>
             <com:TTextBox ID="class_prefix" Text="AR_" CssClass="in_text" />
         </div>
         <div class="form_row">
-            <com:TLabel Text="Output Folder:" ForControl="output_folder"/>
+            <com:TLabel Text="Output Folder:" ForControl="output_folder" style="margin-right: 8px"/>
             <com:TTextBox ID="output_folder" Text="Application.App_Data.AR_Classes" CssClass="in_text" />
         </div>
+        
+        <div class="form_row">
+            <com:TLabel Text="Build Relations:" ForControl="build_rel"/>
+            <com:TCheckBox ID="build_rel" Checked="true" />
+        </div>
+        
+        <com:TPanel ID="success_panel" Visible="false">
+           <com:TLabel ID="generation_msg" />
+        </com:TPanel>
 
         <br/> 
         <div style="text-align: center">
-            <com:TButton Text="Preview" OnClick="preview" />
+            <com:TButton Text="Preview" OnClick="preview" Visible="false" />
             <com:TButton Text="Generate" OnClick="generate" />
         </div>        
     </div>    
diff --git a/framework/Wsat/pages/TWsatGenerateAR.php b/framework/Wsat/pages/TWsatGenerateAR.php
index 754cac5c..3d4291e2 100644
--- a/framework/Wsat/pages/TWsatGenerateAR.php
+++ b/framework/Wsat/pages/TWsatGenerateAR.php
@@ -1,33 +1,49 @@
-<?php
-
-/**
- * Description of Inicio
- *
- * @author daniels
- */
-Prado::using("System.Wsat.TWsatARGenerator");
-
-class TWsatGenerateAR extends TPage {
-
-    public function onInit($param) {
-        parent::onInit($param);
-    }
-
-    public function generate($sender) {
-        $table_name = $this->table_name->Text;
-
-        $ar_generator = new TWsatARGenerator();
-        if ($table_name != "*") {
-            $ar_generator->generate($table_name);
-        } else {
-            $ar_generator->generateAll();
-        }
-    }
-
-    public function preview($sender) {
-        
-    }
-
-}
-
+<?php
+
+/**
+ * Description of Inicio
+ *
+ * @author daniels
+ */
+Prado::using("System.Wsat.TWsatARGenerator");
+
+class TWsatGenerateAR extends TPage {
+
+    public function generate($sender) {
+        if ($this->IsValid) {
+            $table_name = $this->table_name->Text;
+            $class_prefix = $this->class_prefix->Text;
+            $output_folder_ns = $this->output_folder->Text;
+
+            try {
+                $ar_generator = new TWsatARGenerator();
+                $ar_generator->setOpFile($output_folder_ns);
+                $ar_generator->setClasPrefix($class_prefix);
+
+                if ($this->build_rel->Checked) {
+                    $ar_generator->buildRelations();
+                }
+                if ($table_name != "*") {
+                    $ar_generator->generate($table_name);
+                } else {
+                    $ar_generator->generateAll();
+                }
+                $this->success_panel->CssClass = "success_panel";
+                $this->generation_msg->Text = "The code has been generated successfully.";
+            } catch (Exception $ex) {
+                $this->success_panel->CssClass = "exception_panel";
+                $this->generation_msg->Text = $ex->getMessage();
+            }
+            $this->success_panel->Visible = true;
+        }
+    }
+
+    public function preview($sender) {
+//        $ar_generator = new TWsatARGenerator();
+//        $ar_generator->renderAllTablesInformation();
+         throw new THttpException(500, "Not implemented yet.");
+    }
+
+}
+
 ?>
\ No newline at end of file
diff --git a/framework/Wsat/pages/TWsatLogin.page b/framework/Wsat/pages/TWsatLogin.page
index b256d719..d0a94f2a 100644
--- a/framework/Wsat/pages/TWsatLogin.page
+++ b/framework/Wsat/pages/TWsatLogin.page
@@ -29,9 +29,10 @@
             <div class="login_form">
                 <com:TLabel Text="Please enter your password:" ForControl="password"/><br/>  
                 <com:TTextBox ID="password" TextMode="Password" style="margin: 5px" /><br/>  
-                <com:TRequiredFieldValidator ControlToValidate="password" Text="Password cannot be blank." /><br/>  
-                
-                <com:TButton Text="Enter" OnClick="login" />  
+                <com:TRequiredFieldValidator ControlToValidate="password" ValidationGroup="loginGroup" Text="Password cannot be blank." /><br/>  
+                <com:TCustomValidator ControlToValidate="password" ValidationGroup="loginGroup" OnServerValidate="validatePassword" Text="Incorrect password." />
+
+                <com:TButton Text="Enter" ValidationGroup="loginGroup" OnClick="login" />  
             </div>                       
 
             <div id="footer">
diff --git a/framework/Wsat/pages/TWsatLogin.php b/framework/Wsat/pages/TWsatLogin.php
index 7435d75d..0bbdc53f 100644
--- a/framework/Wsat/pages/TWsatLogin.php
+++ b/framework/Wsat/pages/TWsatLogin.php
@@ -1,30 +1,31 @@
-<?php
-
-/**
- * Description of Inicio
- *
- * @author daniels
- */
-class TWsatLogin extends TPage {
-
-    public function login() {
-        $config_pass = $this->getService()->getPassword();
-        $user_pass = $this->password->Text;
-
-        if ($user_pass === $config_pass) {
-            $this->Session["wsat_password"] = $config_pass;
-
-            $authManager = $this->Application->getModule('auth');
-            $url = $authManager->ReturnUrl;
-            if (empty($url)) {
-                $url = $this->Service->constructUrl('TWsatHome');
-            }
-            $this->Response->redirect($url);
-        } else {
-            echo "user or pass wrong";
-        }
-    }
-
-}
-
+<?php
+
+/**
+ * Description of Inicio
+ *
+ * @author daniels
+ */
+class TWsatLogin extends TPage {
+
+    public function login() {
+        if ($this->IsValid) {
+            $this->Session["wsat_password"] = $this->getService()->getPassword();
+
+            $authManager = $this->Application->getModule('auth');
+            $url = $authManager->ReturnUrl;
+            if (empty($url)) {
+                $url = $this->Service->constructUrl('TWsatHome');
+            }
+            $this->Response->redirect($url);
+        }
+    }
+
+    public function validatePassword($sender, $param) {
+        $config_pass = $this->getService()->getPassword();
+        $user_pass = $this->password->Text;
+        $param->IsValid = $user_pass === $config_pass;
+    }
+
+}
+
 ?>
\ No newline at end of file
diff --git a/framework/Wsat/themes/PradoSoft/main.css b/framework/Wsat/themes/PradoSoft/main.css
index 6c609e95..c35a87d8 100644
--- a/framework/Wsat/themes/PradoSoft/main.css
+++ b/framework/Wsat/themes/PradoSoft/main.css
@@ -1,133 +1,151 @@
-html, body{
-    margin: 0px;
-    padding: 0px;
-    font-family: 'Lucida Grande', Verdana, Geneva, Lucida, Helvetica, Arial, sans-serif;
-    font-weight: normal;
-}
-
-#header {
-    font-size:25px;
-    font-weight:bold;
-    color: #666;
-}
-
-.logo{
-    width: 204px;
-    height:100px;
-    background-image: url('imgs/pradologo.gif');
-    background-repeat: no-repeat;
-    float: left;
-}
-
-.mantisbg{
-    width: 221px;
-    height: 100px;
-    background-image: url('imgs/mantisbg.jpg');
-    background-repeat: no-repeat;
-    float: right;
-}
-
-.mainmenu {
-    padding:10px;
-    padding-right:10px;
-    background:#EDEDED;
-    border-bottom: 1px solid #A6A6A6;
-    border-top: 1px solid #DCDCDC;
-    color:white;
-    text-align:right;
-    font-size: 10pt;
-}
-
-.mainmenu a {
-    color:#737373;
-    text-decoration:none;
-}
-
-.mainmenu a:hover {
-    color: #FF0000;
-}
-
-#toc {
-    background-color: #F3F3F3;
-    width:220px;
-    padding:0px 10px 0px 10px;
-    float: left;
-}
-
-#content {
-    padding: 1em 1em 1em 1em;
-    line-height: 135%;
-    float: left;
-}
-
-.topic {
-    font-size: 9pt;
-    padding: 0px 0px 0px 0px;
-}
-
-.topic div {
-    background-image: url('imgs/arrowdown.gif');
-    background-repeat: no-repeat;
-    background-position: left center;
-    margin: 0px;
-    font-size: 8pt;
-    font-weight:bold;
-    color:#2A480A;
-    padding: 5px;
-    padding-left: 15px;
-    border-top: 1px solid #fff;
-    border-bottom: 1px solid #E2E2E2;
-}
-
-.topic ul
-{
-    margin: 0px;
-    padding: 0px;
-}
-
-.topic ul li
-{
-    list-style: none;
-    margin: 0px;
-    padding: 5px;
-    padding-left: 15px;
-    border-bottom: 1px dotted #D8D8D8;
-}
-
-.topic a {
-    color:#4F811A;
-    font-size: 8pt;
-    text-decoration: none;
-}
-
-.topic a:hover {
-    color:#2A480A;
-}
-
-/* form styles */
-.form_row{
-    margin: 10px;
-}
-
-.in_text{
-    width: 250px; 
-}
-
-.login_form{
-    text-align: center;
-    margin: 30px auto;
-    border: 1px solid red;
-    border-radius: 5px;
-    padding: 10px;
-    width: 250px;
-    font-size: 11px;
-}
-
-#footer {
-    clear:both;
-    color: gray;
-    font-size:8pt;
-    text-align:center;
-    margin-top:25px;
-    padding:10px;
+html, body{
+    margin: 0px;
+    padding: 0px;
+    font-family: 'Lucida Grande', Verdana, Geneva, Lucida, Helvetica, Arial, sans-serif;
+    font-weight: normal;
+}
+
+#header {
+    font-size:25px;
+    font-weight:bold;
+    color: #666;
+}
+
+.logo{
+    width: 204px;
+    height:100px;
+    background-image: url('imgs/pradologo.gif');
+    background-repeat: no-repeat;
+    float: left;
+}
+
+.mantisbg{
+    width: 221px;
+    height: 100px;
+    background-image: url('imgs/mantisbg.jpg');
+    background-repeat: no-repeat;
+    float: right;
+}
+
+.mainmenu {
+    padding:10px;
+    padding-right:10px;
+    background:#EDEDED;
+    border-bottom: 1px solid #A6A6A6;
+    border-top: 1px solid #DCDCDC;
+    color:white;
+    text-align:right;
+    font-size: 10pt;
+}
+
+.mainmenu a {
+    color:#737373;
+    text-decoration:none;
+}
+
+.mainmenu a:hover {
+    color: #FF0000;
+}
+
+#toc {
+    background-color: #F3F3F3;
+    width:220px;
+    padding:0px 10px 0px 10px;
+    float: left;
+}
+
+#content {
+    padding: 1em 1em 1em 1em;
+    line-height: 135%;
+    float: left;
+}
+
+.topic {
+    font-size: 9pt;
+    padding: 0px 0px 0px 0px;
+}
+
+.topic div {
+    background-image: url('imgs/arrowdown.gif');
+    background-repeat: no-repeat;
+    background-position: left center;
+    margin: 0px;
+    font-size: 8pt;
+    font-weight:bold;
+    color:#2A480A;
+    padding: 5px;
+    padding-left: 15px;
+    border-top: 1px solid #fff;
+    border-bottom: 1px solid #E2E2E2;
+}
+
+.topic ul
+{
+    margin: 0px;
+    padding: 0px;
+}
+
+.topic ul li
+{
+    list-style: none;
+    margin: 0px;
+    padding: 5px;
+    padding-left: 15px;
+    border-bottom: 1px dotted #D8D8D8;
+}
+
+.topic a {
+    color:#4F811A;
+    font-size: 8pt;
+    text-decoration: none;
+}
+
+.topic a:hover {
+    color:#2A480A;
+}
+
+/* form styles */
+.form_row{
+    margin: 10px;
+}
+
+.in_text{
+    width: 250px; 
+}
+
+.login_form{
+    text-align: center;
+    margin: 30px auto;
+    border: 1px solid red;
+    border-radius: 5px;
+    padding: 10px;
+    width: 250px;
+    font-size: 11px;
+}
+
+.success_panel{
+    background-color: #C5FBBD;
+    border: 1px solid #76C376;
+    padding: 10px;
+    margin: 10px 0;
+    border-radius: 5px;
+    text-align: center;
+}
+
+.exception_panel{
+    background-color: #ff6666;
+    border: 1px solid red;
+    padding: 10px;
+    margin: 10px 0;
+    border-radius: 5px;
+    text-align: center;
+}
+
+#footer {
+    clear:both;
+    color: gray;
+    font-size:8pt;
+    text-align:center;
+    margin-top:25px;
+    padding:10px;
 }
\ No newline at end of file
-- 
cgit v1.2.3