From 2ea02214b2fb6bedb58dbbd318ef171a9e146524 Mon Sep 17 00:00:00 2001 From: xue <> Date: Mon, 29 May 2006 03:08:07 +0000 Subject: Merge from 3.0 branch till 1099. --- .gitattributes | 80 +++ HISTORY | 5 + buildscripts/jsbuilder/build.php | 25 +- demos/blog/index.php | 18 + demos/blog/protected/Common/BlogDataModule.php | 535 +++++++++++++++++++++ demos/blog/protected/Common/BlogErrors.php | 23 + demos/blog/protected/Common/BlogException.php | 14 + demos/blog/protected/Common/BlogPage.php | 26 + demos/blog/protected/Common/BlogUser.php | 38 ++ demos/blog/protected/Common/BlogUserManager.php | 56 +++ demos/blog/protected/Common/XListMenu.php | 127 +++++ demos/blog/protected/Common/messages.txt | 4 + demos/blog/protected/Common/schema.sql | 70 +++ demos/blog/protected/Data/Options.xml | 8 + demos/blog/protected/Layouts/MainLayout.php | 7 + demos/blog/protected/Layouts/MainLayout.tpl | 47 ++ demos/blog/protected/Pages/Admin/AdminMenu.php | 7 + demos/blog/protected/Pages/Admin/AdminMenu.tpl | 16 + demos/blog/protected/Pages/Admin/ConfigMan.page | 56 +++ demos/blog/protected/Pages/Admin/ConfigMan.php | 15 + demos/blog/protected/Pages/Admin/PostMan.page | 76 +++ demos/blog/protected/Pages/Admin/PostMan.php | 56 +++ demos/blog/protected/Pages/Admin/Settings.page | 4 + demos/blog/protected/Pages/Admin/UserMan.page | 95 ++++ demos/blog/protected/Pages/Admin/UserMan.php | 58 +++ demos/blog/protected/Pages/Admin/config.xml | 8 + demos/blog/protected/Pages/ErrorReport.page | 15 + demos/blog/protected/Pages/ErrorReport.php | 12 + demos/blog/protected/Pages/Posts/EditCategory.page | 36 ++ demos/blog/protected/Pages/Posts/EditCategory.php | 44 ++ demos/blog/protected/Pages/Posts/EditPost.page | 41 ++ demos/blog/protected/Pages/Posts/EditPost.php | 51 ++ demos/blog/protected/Pages/Posts/ListPost.page | 27 ++ demos/blog/protected/Pages/Posts/ListPost.php | 44 ++ demos/blog/protected/Pages/Posts/MyPost.page | 46 ++ demos/blog/protected/Pages/Posts/MyPost.php | 34 ++ demos/blog/protected/Pages/Posts/NewCategory.page | 36 ++ demos/blog/protected/Pages/Posts/NewCategory.php | 24 + demos/blog/protected/Pages/Posts/NewPost.page | 41 ++ demos/blog/protected/Pages/Posts/NewPost.php | 34 ++ demos/blog/protected/Pages/Posts/ViewPost.page | 113 +++++ demos/blog/protected/Pages/Posts/ViewPost.php | 73 +++ demos/blog/protected/Pages/Posts/config.xml | 7 + demos/blog/protected/Pages/Users/EditUser.page | 74 +++ demos/blog/protected/Pages/Users/EditUser.php | 43 ++ demos/blog/protected/Pages/Users/NewUser.page | 104 ++++ demos/blog/protected/Pages/Users/NewUser.php | 31 ++ demos/blog/protected/Pages/Users/ViewUser.page | 21 + demos/blog/protected/Pages/Users/ViewUser.php | 19 + demos/blog/protected/Pages/Users/config.xml | 7 + demos/blog/protected/Portlets/AccountPortlet.php | 14 + demos/blog/protected/Portlets/AccountPortlet.tpl | 20 + demos/blog/protected/Portlets/ArchivePortlet.php | 45 ++ demos/blog/protected/Portlets/ArchivePortlet.tpl | 15 + demos/blog/protected/Portlets/CategoryPortlet.php | 15 + demos/blog/protected/Portlets/CategoryPortlet.tpl | 24 + demos/blog/protected/Portlets/LoginPortlet.php | 22 + demos/blog/protected/Portlets/LoginPortlet.tpl | 36 ++ demos/blog/protected/Portlets/Portlet.php | 7 + demos/blog/protected/Portlets/SearchPortlet.php | 22 + demos/blog/protected/Portlets/SearchPortlet.tpl | 21 + demos/blog/protected/application.xml | 38 ++ demos/blog/sitemap.txt | 106 ++++ demos/blog/themes/Basic/style.css | 261 ++++++++++ .../Controls/Samples/TDataTypeValidator/Home.page | 7 +- framework/I18N/core/NumberFormat.php | 5 +- framework/Security/TUser.php | 1 + .../Web/Javascripts/colorpicker/colorpicker.js | 2 +- framework/Web/Javascripts/datepicker/datepicker.js | 29 +- framework/Web/Javascripts/datepicker/default.css | 25 +- framework/Web/Javascripts/datepicker/spacer.gif | Bin 0 -> 43 bytes framework/Web/Javascripts/extended/event.js | 7 +- framework/Web/Javascripts/js/colorpicker.js | 2 +- framework/Web/Javascripts/js/datepicker.js | 16 +- framework/Web/Javascripts/js/prado.js | 3 - framework/Web/Javascripts/prado/validation3.js | 4 +- framework/Web/UI/TThemeManager.php | 17 + framework/Web/UI/WebControls/TBaseValidator.php | 76 +-- framework/Web/UI/WebControls/TDatePicker.php | 30 +- framework/Web/UI/WebControls/TListControl.php | 39 ++ .../tickets/protected/pages/TestHtmlArea.php | 8 + .../tickets/protected/pages/Ticket121.page | 16 + .../tickets/protected/pages/Ticket121.php | 11 + .../tickets/protected/pages/Ticket163.page | 9 + .../tickets/protected/pages/Ticket169.page | 8 + .../tickets/protected/pages/Ticket191.page | 19 + .../tickets/protected/pages/Ticket191.php | 22 + .../tickets/protected/pages/Ticket28.page | 6 + .../tickets/protected/pages/Ticket28.php | 12 + .../tickets/protected/pages/Ticket93.page | 61 +++ .../tickets/protected/pages/Ticket93.php | 14 + .../tickets/protected/pages/config.xml | 3 + .../tickets/protected/pages/hotspot.jpg | Bin 0 -> 12206 bytes .../tickets/tests/Ticket121TestCase.php | 19 + .../tickets/tests/Ticket163TestCase.php | 14 + .../tickets/tests/Ticket169TestCase.php | 14 + .../tickets/tests/Ticket191TestCase.php | 16 + .../tickets/tests/Ticket28TestCase.php | 15 + .../tickets/tests/Ticket93TestCase.php | 16 + 99 files changed, 3557 insertions(+), 86 deletions(-) create mode 100644 demos/blog/index.php create mode 100644 demos/blog/protected/Common/BlogDataModule.php create mode 100644 demos/blog/protected/Common/BlogErrors.php create mode 100644 demos/blog/protected/Common/BlogException.php create mode 100644 demos/blog/protected/Common/BlogPage.php create mode 100644 demos/blog/protected/Common/BlogUser.php create mode 100644 demos/blog/protected/Common/BlogUserManager.php create mode 100644 demos/blog/protected/Common/XListMenu.php create mode 100644 demos/blog/protected/Common/messages.txt create mode 100644 demos/blog/protected/Common/schema.sql create mode 100644 demos/blog/protected/Data/Options.xml create mode 100644 demos/blog/protected/Layouts/MainLayout.php create mode 100644 demos/blog/protected/Layouts/MainLayout.tpl create mode 100644 demos/blog/protected/Pages/Admin/AdminMenu.php create mode 100644 demos/blog/protected/Pages/Admin/AdminMenu.tpl create mode 100644 demos/blog/protected/Pages/Admin/ConfigMan.page create mode 100644 demos/blog/protected/Pages/Admin/ConfigMan.php create mode 100644 demos/blog/protected/Pages/Admin/PostMan.page create mode 100644 demos/blog/protected/Pages/Admin/PostMan.php create mode 100644 demos/blog/protected/Pages/Admin/Settings.page create mode 100644 demos/blog/protected/Pages/Admin/UserMan.page create mode 100644 demos/blog/protected/Pages/Admin/UserMan.php create mode 100644 demos/blog/protected/Pages/Admin/config.xml create mode 100644 demos/blog/protected/Pages/ErrorReport.page create mode 100644 demos/blog/protected/Pages/ErrorReport.php create mode 100644 demos/blog/protected/Pages/Posts/EditCategory.page create mode 100644 demos/blog/protected/Pages/Posts/EditCategory.php create mode 100644 demos/blog/protected/Pages/Posts/EditPost.page create mode 100644 demos/blog/protected/Pages/Posts/EditPost.php create mode 100644 demos/blog/protected/Pages/Posts/ListPost.page create mode 100644 demos/blog/protected/Pages/Posts/ListPost.php create mode 100644 demos/blog/protected/Pages/Posts/MyPost.page create mode 100644 demos/blog/protected/Pages/Posts/MyPost.php create mode 100644 demos/blog/protected/Pages/Posts/NewCategory.page create mode 100644 demos/blog/protected/Pages/Posts/NewCategory.php create mode 100644 demos/blog/protected/Pages/Posts/NewPost.page create mode 100644 demos/blog/protected/Pages/Posts/NewPost.php create mode 100644 demos/blog/protected/Pages/Posts/ViewPost.page create mode 100644 demos/blog/protected/Pages/Posts/ViewPost.php create mode 100644 demos/blog/protected/Pages/Posts/config.xml create mode 100644 demos/blog/protected/Pages/Users/EditUser.page create mode 100644 demos/blog/protected/Pages/Users/EditUser.php create mode 100644 demos/blog/protected/Pages/Users/NewUser.page create mode 100644 demos/blog/protected/Pages/Users/NewUser.php create mode 100644 demos/blog/protected/Pages/Users/ViewUser.page create mode 100644 demos/blog/protected/Pages/Users/ViewUser.php create mode 100644 demos/blog/protected/Pages/Users/config.xml create mode 100644 demos/blog/protected/Portlets/AccountPortlet.php create mode 100644 demos/blog/protected/Portlets/AccountPortlet.tpl create mode 100644 demos/blog/protected/Portlets/ArchivePortlet.php create mode 100644 demos/blog/protected/Portlets/ArchivePortlet.tpl create mode 100644 demos/blog/protected/Portlets/CategoryPortlet.php create mode 100644 demos/blog/protected/Portlets/CategoryPortlet.tpl create mode 100644 demos/blog/protected/Portlets/LoginPortlet.php create mode 100644 demos/blog/protected/Portlets/LoginPortlet.tpl create mode 100644 demos/blog/protected/Portlets/Portlet.php create mode 100644 demos/blog/protected/Portlets/SearchPortlet.php create mode 100644 demos/blog/protected/Portlets/SearchPortlet.tpl create mode 100644 demos/blog/protected/application.xml create mode 100644 demos/blog/sitemap.txt create mode 100644 demos/blog/themes/Basic/style.css create mode 100755 framework/Web/Javascripts/datepicker/spacer.gif create mode 100644 tests/FunctionalTests/tickets/protected/pages/TestHtmlArea.php create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket121.page create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket121.php create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket163.page create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket169.page create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket191.page create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket191.php create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket28.page create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket28.php create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket93.page create mode 100644 tests/FunctionalTests/tickets/protected/pages/Ticket93.php create mode 100644 tests/FunctionalTests/tickets/protected/pages/hotspot.jpg create mode 100644 tests/FunctionalTests/tickets/tests/Ticket121TestCase.php create mode 100644 tests/FunctionalTests/tickets/tests/Ticket163TestCase.php create mode 100644 tests/FunctionalTests/tickets/tests/Ticket169TestCase.php create mode 100644 tests/FunctionalTests/tickets/tests/Ticket191TestCase.php create mode 100644 tests/FunctionalTests/tickets/tests/Ticket28TestCase.php create mode 100644 tests/FunctionalTests/tickets/tests/Ticket93TestCase.php diff --git a/.gitattributes b/.gitattributes index 93ce7505..6b709dfd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -24,6 +24,67 @@ buildscripts/setup.php -text buildscripts/texbuilder/build.php -text buildscripts/texbuilder/pages.php -text buildscripts/texbuilder/prado3_quick_start.tex -text +demos/blog/index.php -text +demos/blog/protected/Common/BlogDataModule.php -text +demos/blog/protected/Common/BlogErrors.php -text +demos/blog/protected/Common/BlogException.php -text +demos/blog/protected/Common/BlogPage.php -text +demos/blog/protected/Common/BlogUser.php -text +demos/blog/protected/Common/BlogUserManager.php -text +demos/blog/protected/Common/XListMenu.php -text +demos/blog/protected/Common/messages.txt -text +demos/blog/protected/Common/schema.sql -text +demos/blog/protected/Data/Options.xml -text +demos/blog/protected/Layouts/MainLayout.php -text +demos/blog/protected/Layouts/MainLayout.tpl -text +demos/blog/protected/Pages/Admin/AdminMenu.php -text +demos/blog/protected/Pages/Admin/AdminMenu.tpl -text +demos/blog/protected/Pages/Admin/ConfigMan.page -text +demos/blog/protected/Pages/Admin/ConfigMan.php -text +demos/blog/protected/Pages/Admin/PostMan.page -text +demos/blog/protected/Pages/Admin/PostMan.php -text +demos/blog/protected/Pages/Admin/Settings.page -text +demos/blog/protected/Pages/Admin/UserMan.page -text +demos/blog/protected/Pages/Admin/UserMan.php -text +demos/blog/protected/Pages/Admin/config.xml -text +demos/blog/protected/Pages/ErrorReport.page -text +demos/blog/protected/Pages/ErrorReport.php -text +demos/blog/protected/Pages/Posts/EditCategory.page -text +demos/blog/protected/Pages/Posts/EditCategory.php -text +demos/blog/protected/Pages/Posts/EditPost.page -text +demos/blog/protected/Pages/Posts/EditPost.php -text +demos/blog/protected/Pages/Posts/ListPost.page -text +demos/blog/protected/Pages/Posts/ListPost.php -text +demos/blog/protected/Pages/Posts/MyPost.page -text +demos/blog/protected/Pages/Posts/MyPost.php -text +demos/blog/protected/Pages/Posts/NewCategory.page -text +demos/blog/protected/Pages/Posts/NewCategory.php -text +demos/blog/protected/Pages/Posts/NewPost.page -text +demos/blog/protected/Pages/Posts/NewPost.php -text +demos/blog/protected/Pages/Posts/ViewPost.page -text +demos/blog/protected/Pages/Posts/ViewPost.php -text +demos/blog/protected/Pages/Posts/config.xml -text +demos/blog/protected/Pages/Users/EditUser.page -text +demos/blog/protected/Pages/Users/EditUser.php -text +demos/blog/protected/Pages/Users/NewUser.page -text +demos/blog/protected/Pages/Users/NewUser.php -text +demos/blog/protected/Pages/Users/ViewUser.page -text +demos/blog/protected/Pages/Users/ViewUser.php -text +demos/blog/protected/Pages/Users/config.xml -text +demos/blog/protected/Portlets/AccountPortlet.php -text +demos/blog/protected/Portlets/AccountPortlet.tpl -text +demos/blog/protected/Portlets/ArchivePortlet.php -text +demos/blog/protected/Portlets/ArchivePortlet.tpl -text +demos/blog/protected/Portlets/CategoryPortlet.php -text +demos/blog/protected/Portlets/CategoryPortlet.tpl -text +demos/blog/protected/Portlets/LoginPortlet.php -text +demos/blog/protected/Portlets/LoginPortlet.tpl -text +demos/blog/protected/Portlets/Portlet.php -text +demos/blog/protected/Portlets/SearchPortlet.php -text +demos/blog/protected/Portlets/SearchPortlet.tpl -text +demos/blog/protected/application.xml -text +demos/blog/sitemap.txt -text +demos/blog/themes/Basic/style.css -text demos/composer/index.php -text demos/composer/index2.php -text demos/composer/protected/pages/ClassDefinition.php -text @@ -863,6 +924,7 @@ framework/Web/Javascripts/colorpicker/target_white.gif -text framework/Web/Javascripts/datepicker/calendar.png -text framework/Web/Javascripts/datepicker/datepicker.js -text framework/Web/Javascripts/datepicker/default.css -text +framework/Web/Javascripts/datepicker/spacer.gif -text framework/Web/Javascripts/effects/CHANGELOG -text framework/Web/Javascripts/effects/MIT-LICENSE -text framework/Web/Javascripts/effects/README -text @@ -1196,20 +1258,38 @@ tests/FunctionalTests/tickets.php -text tests/FunctionalTests/tickets/index.php -text tests/FunctionalTests/tickets/protected/pages/Layout.php -text tests/FunctionalTests/tickets/protected/pages/Layout.tpl -text +tests/FunctionalTests/tickets/protected/pages/TestHtmlArea.php -text +tests/FunctionalTests/tickets/protected/pages/Ticket121.page -text +tests/FunctionalTests/tickets/protected/pages/Ticket121.php -text +tests/FunctionalTests/tickets/protected/pages/Ticket163.page -text +tests/FunctionalTests/tickets/protected/pages/Ticket169.page -text +tests/FunctionalTests/tickets/protected/pages/Ticket191.page -text +tests/FunctionalTests/tickets/protected/pages/Ticket191.php -text tests/FunctionalTests/tickets/protected/pages/Ticket21.page -text tests/FunctionalTests/tickets/protected/pages/Ticket21.php -text tests/FunctionalTests/tickets/protected/pages/Ticket27.page -text +tests/FunctionalTests/tickets/protected/pages/Ticket28.page -text +tests/FunctionalTests/tickets/protected/pages/Ticket28.php -text tests/FunctionalTests/tickets/protected/pages/Ticket54.page -text tests/FunctionalTests/tickets/protected/pages/Ticket54Master.php -text tests/FunctionalTests/tickets/protected/pages/Ticket54Master.tpl -text tests/FunctionalTests/tickets/protected/pages/Ticket68.page -text tests/FunctionalTests/tickets/protected/pages/Ticket72.page -text tests/FunctionalTests/tickets/protected/pages/Ticket72.php -text +tests/FunctionalTests/tickets/protected/pages/Ticket93.page -text +tests/FunctionalTests/tickets/protected/pages/Ticket93.php -text tests/FunctionalTests/tickets/protected/pages/config.xml -text +tests/FunctionalTests/tickets/protected/pages/hotspot.jpg -text +tests/FunctionalTests/tickets/tests/Ticket121TestCase.php -text +tests/FunctionalTests/tickets/tests/Ticket163TestCase.php -text +tests/FunctionalTests/tickets/tests/Ticket169TestCase.php -text +tests/FunctionalTests/tickets/tests/Ticket191TestCase.php -text tests/FunctionalTests/tickets/tests/Ticket21TestCase.php -text tests/FunctionalTests/tickets/tests/Ticket27TestCase.php -text +tests/FunctionalTests/tickets/tests/Ticket28TestCase.php -text tests/FunctionalTests/tickets/tests/Ticket54TestCase.php -text tests/FunctionalTests/tickets/tests/Ticket72TestCase.php -text +tests/FunctionalTests/tickets/tests/Ticket93TestCase.php -text tests/FunctionalTests/validators.php -text tests/FunctionalTests/validators/index.php -text tests/FunctionalTests/validators/protected/pages/Button.page -text diff --git a/HISTORY b/HISTORY index 40e91f92..a7690af7 100644 --- a/HISTORY +++ b/HISTORY @@ -9,9 +9,12 @@ NEW: SQLMap (Wei) Version 3.0.1 June 1, 2006 ========================== +BUG: Ticket#28 - OnClick does not work with Safari/KHTML (Wei) BUG: Ticket#37 - Changes of config files do not trigger cache update (Qiang) BUG: Ticket#44 - THtmlArea (tiny_mce) not working on some systems (Qiang) +BUG: Ticket#162 - Missing currency sign in TNumberFormat if Value is 0 (Wei) BUG: Ticket#167 - TSecurityManager issues warning when trying to encrypt/decrypt strings (Qiang) +BUG: Ticket#169 - Validation of subclass of THtmlArea/TDatePicker fails (Wei) BUG: Ticket#179 - CGI incompatibility causing clientscripts.php failure (Qiang) BUG: Ticket#181 - Unable to change Content-Type in response header if charset is not set (Qiang) ENH: Ticket#150 - TDataGrid and TDataList now render table section tags (Qiang) @@ -21,6 +24,8 @@ ENH: added search for quickstart tutorials (Wei) ENH: added support to property tags for template owner control (Qiang) ENH: added Bulgarian requirement checker messages (StanProg) ENH: added TTheme.BaseUrl and TTheme.BasePath property (Qiang) +ENH: added TListControl.SelectedValues property (Qiang) +ENH: added TThemeManager.AvailableThemes property (Qiang) ENH: refactored TUserManager and TAuthManager so that they are easier to be extended (Qiang) CHG: Ticket#151 - URL format is modified to handle empty GET values (Qiang) CHG: Ticket#153 - TAssetManager now ignores .svn directories (Qiang) diff --git a/buildscripts/jsbuilder/build.php b/buildscripts/jsbuilder/build.php index 33e5d133..50fb8b56 100644 --- a/buildscripts/jsbuilder/build.php +++ b/buildscripts/jsbuilder/build.php @@ -22,11 +22,11 @@ /** * The root directory for storing all source js files */ -define('SOURCE_DIR',realpath(dirname(__FILE__).'/../../framework/Web/JavaScripts')); +define('SOURCE_DIR',realpath(dirname(__FILE__).'/../../framework/Web/Javascripts')); /** * The directory for storing compressed js files */ -define('TARGET_DIR',realpath(dirname(__FILE__).'/../../framework/Web/JavaScripts/js')); +define('TARGET_DIR',realpath(dirname(__FILE__).'/../../framework/Web/Javascripts/js')); /** * Location of the perl JS doc generator. */ @@ -148,20 +148,20 @@ $builds = 0; /** * loop through all target files and build them one by one */ -foreach($libraries as $libFile => $sourceFiles) +foreach($libraries as $jsFile => $sourceFiles) { - if(!empty($requestedLibs) && !in_array($libFile,$requestedLibs)) + if(!empty($requestedLibs) && !in_array($jsFile,$requestedLibs)) continue; - $libFile=TARGET_DIR.'/'.$libFile; - echo "\nBuilding $libFile...\n"; + $libFile=TARGET_DIR.'/'.$jsFile; + echo "\nBuilding $jsFile...\n"; $contents=''; - foreach($sourceFiles as $sourceFile) + foreach($sourceFiles as $sourceJsFile) { - $sourceFile=SOURCE_DIR.'/'.$sourceFile; + $sourceFile=SOURCE_DIR.'/'.$sourceJsFile; if(!is_file($sourceFile)) echo "Source file not found: $sourceFile\n"; - echo "...adding $sourceFile\n"; + echo "...adding $sourceJsFile\n"; $contents.=file_get_contents($sourceFile)."\n\n"; } $tempFile=$libFile.'.tmp'; @@ -170,10 +170,11 @@ foreach($libraries as $libFile => $sourceFiles) $jsMin -> minify(); unset($jsMin); @unlink($tempFile); - echo "Saving file {$libFile}\n"; + echo "Saving file {$jsFile}\n"; $builds++; } -if(isset($argv[1]) && preg_match('/doc*/', $argv[1])) + +if(isset($argv[1]) && preg_match('/(doc)+/', $argv[1])) { $files = ""; foreach($libraries as $lib) @@ -189,4 +190,4 @@ else if($builds > 0) else echo "No files to build."; -?> \ No newline at end of file +?> diff --git a/demos/blog/index.php b/demos/blog/index.php new file mode 100644 index 00000000..43c0b436 --- /dev/null +++ b/demos/blog/index.php @@ -0,0 +1,18 @@ +run(); + +?> \ No newline at end of file diff --git a/demos/blog/protected/Common/BlogDataModule.php b/demos/blog/protected/Common/BlogDataModule.php new file mode 100644 index 00000000..714743e7 --- /dev/null +++ b/demos/blog/protected/Common/BlogDataModule.php @@ -0,0 +1,535 @@ +connectDatabase(); + } + + public function getDbFile() + { + if($this->_dbFile===null) + $this->_dbFile=Prado::getPathOfNamespace(self::DEFAULT_DB_FILE,self::DB_FILE_EXT); + return $this->_dbFile; + } + + public function setDbFile($value) + { + if(($this->_dbFile=Prado::getPathOfNamespace($value,self::DB_FILE_EXT))===null) + throw new BlogException('blogdatamodule_dbfile_invalid',$value); + } + + protected function createDatabase() + { + $schemaFile=dirname(__FILE__).'/schema.sql'; + $statements=explode(';',file_get_contents($schemaFile)); + foreach($statements as $statement) + { + if(trim($statement)!=='') + { + if(@sqlite_query($this->_db,$statement)===false) + throw new BlogException('blogdatamodule_createdatabase_failed',sqlite_error_string(sqlite_last_error($this->_db)),$statement); + } + } + } + + protected function connectDatabase() + { + $dbFile=$this->getDbFile(); + $newDb=!is_file($dbFile); + $error=''; + if(($this->_db=sqlite_open($dbFile,0666,$error))===false) + throw new BlogException('blogdatamodule_dbconnect_failed',$error); + if($newDb) + $this->createDatabase(); + } + + protected function generateModifier($filter,$orderBy,$limit) + { + $modifier=''; + if($filter!=='') + $modifier=' WHERE '.$filter; + if($orderBy!=='') + $modifier.=' ORDER BY '.$orderBy; + if($limit!=='') + $modifier.=' LIMIT '.$limit; + return $modifier; + } + + public function query($sql) + { + if(($result=@sqlite_query($this->_db,$sql))!==false) + return $result; + else + throw new BlogException('blogdatamodule_query_failed',sqlite_error_string(sqlite_last_error($this->_db)),$sql); + } + + protected function populateUserRecord($row) + { + $userRecord=new UserRecord; + $userRecord->ID=(integer)$row['id']; + $userRecord->Name=$row['name']; + $userRecord->FullName=$row['full_name']; + $userRecord->Role=(integer)$row['role']; + $userRecord->Password=$row['passwd']; + $userRecord->VerifyCode=$row['vcode']; + $userRecord->Email=$row['email']; + $userRecord->CreateTime=(integer)$row['reg_time']; + $userRecord->Status=(integer)$row['status']; + $userRecord->Website=$row['website']; + return $userRecord; + } + + public function queryUsers($filter='',$orderBy='',$limit='') + { + if($filter!=='') + $filter='WHERE '.$filter; + $sql="SELECT * FROM tblUsers $filter $orderBy $limit"; + $result=$this->query($sql); + $rows=sqlite_fetch_all($result,SQLITE_ASSOC); + $users=array(); + foreach($rows as $row) + $users[]=$this->populateUserRecord($row); + return $users; + } + + public function queryUserCount($filter) + { + if($filter!=='') + $filter='WHERE '.$filter; + $sql="SELECT COUNT(id) AS user_count FROM tblUsers $filter"; + $result=$this->query($sql); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + return $row['user_count']; + else + return 0; + } + + public function queryUserByID($id) + { + $sql="SELECT * FROM tblUsers WHERE id=$id"; + $result=$this->query($sql); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + return $this->populateUserRecord($row); + else + return null; + } + + public function queryUserByName($name) + { + $name=sqlite_escape_string($name); + $sql="SELECT * FROM tblUsers WHERE name='$name'"; + $result=$this->query($sql); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + return $this->populateUserRecord($row); + else + return null; + } + + public function insertUser($user) + { + $name=sqlite_escape_string($user->Name); + $fullName=sqlite_escape_string($user->FullName); + $passwd=sqlite_escape_string($user->Password); + $email=sqlite_escape_string($user->Email); + $website=sqlite_escape_string($user->Website); + $createTime=time(); + $sql="INSERT INTO tblUsers ". + "(name,full_name,role,passwd,email,reg_time,website) ". + "VALUES ('$name','$fullName',{$user->Role},'$passwd','$email',$createTime,'$website')"; + $this->query($sql); + $user->ID=sqlite_last_insert_rowid($this->_db); + } + + public function updateUser($user) + { + $name=sqlite_escape_string($user->Name); + $fullName=sqlite_escape_string($user->FullName); + $passwd=sqlite_escape_string($user->Password); + $email=sqlite_escape_string($user->Email); + $website=sqlite_escape_string($user->Website); + $sql="UPDATE tblUsers SET + name='$name', + full_name='$fullName', + role={$user->Role}, + passwd='$passwd', + vcode='{$user->VerifyCode}', + email='$email', + status={$user->Status}, + website='$website' + WHERE id={$user->ID}"; + $this->query($sql); + } + + public function deleteUser($id) + { + $this->query("DELETE FROM tblUsers WHERE id=$id"); + } + + protected function populatePostRecord($row) + { + $postRecord=new PostRecord; + $postRecord->ID=(integer)$row['id']; + $postRecord->AuthorID=(integer)$row['author_id']; + if($row['author_full_name']!=='') + $postRecord->AuthorName=$row['author_full_name']; + else + $postRecord->AuthorName=$row['author_name']; + $postRecord->CreateTime=(integer)$row['create_time']; + $postRecord->ModifyTime=(integer)$row['modify_time']; + $postRecord->Title=$row['title']; + $postRecord->Content=$row['content']; + $postRecord->Status=(integer)$row['status']; + $postRecord->CommentCount=(integer)$row['comment_count']; + return $postRecord; + } + + public function queryPosts($authorFilter,$timeFilter,$categoryFilter,$orderBy,$limit) + { + $filter=''; + if($authorFilter!=='') + $filter.=" AND $authorFilter"; + if($timeFilter!=='') + $filter.=" AND $timeFilter"; + if($categoryFilter!=='') + $filter.=" AND a.id IN (SELECT post_id AS id FROM tblPost2Category WHERE $categoryFilter)"; + $sql="SELECT a.id AS id, + a.author_id AS author_id, + b.name AS author_name, + b.full_name AS author_full_name, + a.create_time AS create_time, + a.modify_time AS modify_time, + a.title AS title, + a.content AS content, + a.status AS status, + a.comment_count AS comment_count + FROM tblPosts a, tblUsers b + WHERE a.author_id=b.id $filter $orderBy $limit"; + $result=$this->query($sql); + $rows=sqlite_fetch_all($result,SQLITE_ASSOC); + $posts=array(); + foreach($rows as $row) + $posts[]=$this->populatePostRecord($row); + return $posts; + } + + public function queryPostCount($authorFilter,$timeFilter,$categoryFilter) + { + $filter=''; + if($authorFilter!=='') + $filter.=" AND $authorFilter"; + if($timeFilter!=='') + $filter.=" AND $timeFilter"; + if($categoryFilter!=='') + $filter.=" AND a.id IN (SELECT post_id AS id FROM tblPost2Category WHERE $categoryFilter)"; + $sql="SELECT COUNT(a.id) AS post_count + FROM tblPosts a, tblUsers b + WHERE a.author_id=b.id $filter"; + $result=$this->query($sql); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + return $row['post_count']; + else + return 0; + } + + public function queryPostByID($id) + { + $sql="SELECT a.id AS id, + a.author_id AS author_id, + b.name AS author_name, + b.full_name AS author_full_name, + a.create_time AS create_time, + a.modify_time AS modify_time, + a.title AS title, + a.content AS content, + a.status AS status, + a.comment_count AS comment_count + FROM tblPosts a, tblUsers b + WHERE a.id=$id AND a.author_id=b.id"; + $result=$this->query($sql); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + return $this->populatePostRecord($row); + else + return null; + } + + public function insertPost($post,$catIDs) + { + $title=sqlite_escape_string($post->Title); + $content=sqlite_escape_string($post->Content); + $sql="INSERT INTO tblPosts + (author_id,create_time,title,content,status) + VALUES ({$post->AuthorID},{$post->CreateTime},'$title','$content',{$post->Status})"; + $this->query($sql); + $post->ID=sqlite_last_insert_rowid($this->_db); + foreach($catIDs as $catID) + $this->insertPostCategory($post->ID,$catID); + } + + public function updatePost($post,$newCatIDs=null) + { + if($newCatIDs!==null) + { + $cats=$this->queryCategoriesByPostID($post->ID); + $catIDs=array(); + foreach($cats as $cat) + $catIDs[]=$cat->ID; + $deleteIDs=array_diff($catIDs,$newCatIDs); + foreach($deleteIDs as $id) + $this->deletePostCategory($post->ID,$id); + $insertIDs=array_diff($newCatIDs,$catIDs); + foreach($insertIDs as $id) + $this->insertPostCategory($post->ID,$id); + } + + $title=sqlite_escape_string($post->Title); + $content=sqlite_escape_string($post->Content); + $sql="UPDATE tblPosts SET + modify_time={$post->ModifyTime}, + title='$title', + content='$content', + status={$post->Status} + WHERE id={$post->ID}"; + $this->query($sql); + } + + public function deletePost($id) + { + $cats=$this->queryCategoriesByPostID($id); + foreach($cats as $cat) + $this->deletePostCategory($id,$cat->ID); + $this->query("DELETE FROM tblComments WHERE post_id=$id"); + $this->query("DELETE FROM tblPosts WHERE id=$id"); + } + + protected function populateCommentRecord($row) + { + $commentRecord=new CommentRecord; + $commentRecord->ID=(integer)$row['id']; + $commentRecord->PostID=(integer)$row['post_id']; + $commentRecord->AuthorName=$row['author_name']; + $commentRecord->AuthorEmail=$row['author_email']; + $commentRecord->AuthorWebsite=$row['author_website']; + $commentRecord->AuthorIP=$row['author_ip']; + $commentRecord->CreateTime=(integer)$row['create_time']; + $commentRecord->Content=$row['content']; + $commentRecord->Status=(integer)$row['status']; + return $commentRecord; + } + + public function queryCommentsByPostID($id) + { + $sql="SELECT * FROM tblComments WHERE post_id=$id"; + $result=$this->query($sql); + $rows=sqlite_fetch_all($result,SQLITE_ASSOC); + $comments=array(); + foreach($rows as $row) + $comments[]=$this->populateCommentRecord($row); + return $comments; + } + + public function insertComment($comment) + { + $authorName=sqlite_escape_string($comment->AuthorName); + $authorEmail=sqlite_escape_string($comment->AuthorEmail); + $authorWebsite=sqlite_escape_string($comment->AuthorWebsite); + $content=sqlite_escape_string($comment->Content); + $sql="INSERT INTO tblComments + (post_id,author_name,author_email,author_website,author_ip,create_time,status,content) + VALUES ({$comment->PostID},'$authorName','$authorEmail','$authorWebsite','{$comment->AuthorIP}',{$comment->CreateTime},{$comment->Status},'$content')"; + $this->query($sql); + $comment->ID=sqlite_last_insert_rowid($this->_db); + $this->query("UPDATE tblPosts SET comment_count=comment_count+1 WHERE id={$comment->PostID}"); + } + + public function updateComment($comment) + { + $authorName=sqlite_escape_string($comment->AuthorName); + $authorEmail=sqlite_escape_string($comment->AuthorEmail); + $content=sqlite_escape_string($comment->Content); + $sql="UPDATE tblComments SET status={$comment->Status} WHERE id={$comment->ID}"; + $this->query($sql); + } + + public function deleteComment($id) + { + $result=$this->query("SELECT post_id FROM tblComments WHERE id=$id"); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + { + $postID=$row['post_id']; + $this->query("DELETE FROM tblComments WHERE id=$id"); + $this->query("UPDATE tblPosts SET comment_count=comment_count-1 WHERE id=$postID"); + } + } + + protected function populateCategoryRecord($row) + { + $catRecord=new CategoryRecord; + $catRecord->ID=(integer)$row['id']; + $catRecord->Name=$row['name']; + $catRecord->Description=$row['description']; + $catRecord->PostCount=$row['post_count']; + return $catRecord; + } + + public function queryCategories() + { + $sql="SELECT * FROM tblCategories"; + $result=$this->query($sql); + $rows=sqlite_fetch_all($result,SQLITE_ASSOC); + $cats=array(); + foreach($rows as $row) + $cats[]=$this->populateCategoryRecord($row); + return $cats; + } + + public function queryCategoriesByPostID($postID) + { + $sql="SELECT a.id AS id, + a.name AS name, + a.description AS description, + a.post_count AS post_count + FROM tblCategories a, tblPost2Category b + WHERE a.id=b.category_id AND b.post_id=$postID"; + $result=$this->query($sql); + $rows=sqlite_fetch_all($result,SQLITE_ASSOC); + $cats=array(); + foreach($rows as $row) + $cats[]=$this->populateCategoryRecord($row); + return $cats; + } + + public function queryCategoryByID($id) + { + $sql="SELECT * FROM tblCategories WHERE id=$id"; + $result=$this->query($sql); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + return $this->populateCategoryRecord($row); + else + return null; + } + + public function queryCategoryByName($name) + { + $name=sqlite_escape_string($name); + $sql="SELECT * FROM tblCategories WHERE name='$name'"; + $result=$this->query($sql); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + return $this->populateCategoryRecord($row); + else + return null; + } + + public function insertCategory($category) + { + $name=sqlite_escape_string($category->Name); + $description=sqlite_escape_string($category->Description); + $sql="INSERT INTO tblCategories + (name,description) + VALUES ('$name','$description')"; + $this->query($sql); + $category->ID=sqlite_last_insert_rowid($this->_db); + } + + public function updateCategory($category) + { + $name=sqlite_escape_string($category->Name); + $description=sqlite_escape_string($category->Description); + $sql="UPDATE tblCategories SET name='$name', description='$description', post_count={$category->PostCount} WHERE id={$category->ID}"; + $this->query($sql); + } + + public function deleteCategory($id) + { + $sql="DELETE FROM tblPost2Category WHERE category_id=$id"; + $this->query($sql); + $sql="DELETE FROM tblCategories WHERE id=$id"; + $this->query($sql); + } + + public function insertPostCategory($postID,$categoryID) + { + $sql="INSERT INTO tblPost2Category (post_id, category_id) VALUES ($postID, $categoryID)"; + $this->query($sql); + $sql="UPDATE tblCategories SET post_count=post_count+1 WHERE id=$categoryID"; + $this->query($sql); + } + + public function deletePostCategory($postID,$categoryID) + { + $sql="DELETE FROM tblPost2Category WHERE post_id=$postID AND category_id=$categoryID"; + if($this->query($sql)>0) + { + $sql="UPDATE tblCategories SET post_count=post_count-1 WHERE id=$categoryID"; + $this->query($sql); + } + } + + public function queryEarliestPostTime() + { + $sql="SELECT MIN(create_time) AS create_time FROM tblPosts"; + $result=$this->query($sql); + if(($row=sqlite_fetch_array($result,SQLITE_ASSOC))!==false) + return $row['create_time']; + else + return time(); + } +} + +class UserRecord +{ + public $ID; + public $Name; + public $FullName; + public $Role; + public $Password; + public $VerifyCode; + public $Email; + public $CreateTime; + public $Status; + public $Website; +} + +class PostRecord +{ + public $ID; + public $AuthorID; + public $AuthorName; + public $CreateTime; + public $ModifyTime; + public $Title; + public $Content; + public $Status; + public $CommentCount; +} + +class CommentRecord +{ + public $ID; + public $PostID; + public $AuthorName; + public $AuthorEmail; + public $AuthorWebsite; + public $AuthorIP; + public $CreateTime; + public $Status; + public $Content; +} + +class CategoryRecord +{ + public $ID; + public $Name; + public $Description; + public $PostCount; +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Common/BlogErrors.php b/demos/blog/protected/Common/BlogErrors.php new file mode 100644 index 00000000..501ec1c9 --- /dev/null +++ b/demos/blog/protected/Common/BlogErrors.php @@ -0,0 +1,23 @@ +'Unknown error.', + self::ERROR_POST_NOT_FOUND=>'The specified post cannot be found.', + self::ERROR_USER_NOT_FOUND=>'The specified user account cannot be found.', + self::ERROR_PERMISSION_DENIED=>'Sorry, you do not have permission to perform this action.', + ); + + public static function getMessage($errorCode) + { + return isset(self::$_errorMessages[$errorCode])?self::$_errorMessages[$errorCode]:self::$_errorMessages[0]; + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Common/BlogException.php b/demos/blog/protected/Common/BlogException.php new file mode 100644 index 00000000..ab8020d1 --- /dev/null +++ b/demos/blog/protected/Common/BlogException.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/demos/blog/protected/Common/BlogPage.php b/demos/blog/protected/Common/BlogPage.php new file mode 100644 index 00000000..f1634a80 --- /dev/null +++ b/demos/blog/protected/Common/BlogPage.php @@ -0,0 +1,26 @@ +getApplication()->getModule('data'); + } + + public function gotoDefaultPage() + { + $this->Response->redirect($this->Service->constructUrl($this->Service->DefaultPage)); + } + + public function gotoPage($pagePath,$getParameters=null) + { + $this->Response->redirect($this->Service->constructUrl($pagePath,$getParameters)); + } + + public function reportError($errorCode) + { + $this->gotoPage('ErrorReport',array('id'=>$errorCode)); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Common/BlogUser.php b/demos/blog/protected/Common/BlogUser.php new file mode 100644 index 00000000..af49c8d7 --- /dev/null +++ b/demos/blog/protected/Common/BlogUser.php @@ -0,0 +1,38 @@ +_id; + } + + public function setID($value) + { + $this->_id=$value; + } + + public function saveToString() + { + $a=array($this->_id,parent::saveToString()); + return serialize($a); + } + + public function loadFromString($data) + { + if(!empty($data)) + { + list($id,$str)=unserialize($data); + $this->_id=$id; + return parent::loadFromString($str); + } + else + return $this; + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Common/BlogUserManager.php b/demos/blog/protected/Common/BlogUserManager.php new file mode 100644 index 00000000..c3ddb80b --- /dev/null +++ b/demos/blog/protected/Common/BlogUserManager.php @@ -0,0 +1,56 @@ +Application->getModule('data'); + if(($userRecord=$db->queryUserByName($username))!==null) + { + $user=new BlogUser($this); + $user->setID($userRecord->ID); + $user->setName($username); + $user->setIsGuest(false); + $user->setRoles($userRecord->Role===0?'user':'admin'); + return $user; + } + else + return null; + } + } + + /** + * 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) + { + $db=$this->Application->getModule('data'); + if(($userRecord=$db->queryUserByName($username))!==null) + return $userRecord->Password===md5($password); + else + return false; + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Common/XListMenu.php b/demos/blog/protected/Common/XListMenu.php new file mode 100644 index 00000000..f8223585 --- /dev/null +++ b/demos/blog/protected/Common/XListMenu.php @@ -0,0 +1,127 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + */ + +Prado::using('System.Web.UI.WebControls.TListControl'); + +/** + * XListMenu class + * + * XListMenu displays a list of hyperlinks that can be used for page menus. + * Menu items adjust their css class automatically according to the current + * page displayed. In particular, a menu item is considered as active if + * the URL it represents is for the page currently displayed. + * + * Usage of XListMenu is similar to PRADO list controls. Each list item has + * two extra properties: {@link XListMenuItem::setPagePath PagePath} and + * {@link XListMenuItem::setNavigateUrl NavigateUrl}. The former is used to + * determine if the item is active or not, while the latter specifies the + * URL for the item. If the latter is not specified, a URL to the page is + * generated automatically. + * + * In template, you may use the following tags to specify a menu: + * + * + * + * + * + * + * + * @author Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + */ +class XListMenu extends TListControl +{ + public function addParsedObject($object) + { + if($object instanceof XListMenuItem) + parent::addParsedObject($object); + } + + public function getActiveCssClass() + { + return $this->getViewState('ActiveCssClass',''); + } + + public function setActiveCssClass($value) + { + $this->setViewState('ActiveCssClass',$value,''); + } + + public function getInactiveCssClass() + { + return $this->getViewState('InactiveCssClass',''); + } + + public function setInactiveCssClass($value) + { + $this->setViewState('InactiveCssClass',$value,''); + } + + public function render($writer) + { + if(($activeClass=$this->getActiveCssClass())!=='') + $activeClass=' class="'.$activeClass.'"'; + if(($inactiveClass=$this->getInactiveCssClass())!=='') + $inactiveClass=' class="'.$inactiveClass.'"'; + $currentPagePath=$this->getPage()->getPagePath(); + $writer->write(""); + } +} + +class XListMenuItem extends TListItem +{ + public function getPagePath() + { + return $this->getValue(); + } + + public function setPagePath($value) + { + $this->setValue($value); + } + + public function getNavigateUrl() + { + return $this->hasAttribute('NavigateUrl')?$this->getAttribute('NavigateUrl'):''; + } + + public function setNavigateUrl($value) + { + $this->setAttribute('NavigateUrl',$value); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Common/messages.txt b/demos/blog/protected/Common/messages.txt new file mode 100644 index 00000000..deb15ee3 --- /dev/null +++ b/demos/blog/protected/Common/messages.txt @@ -0,0 +1,4 @@ +blogdatamodule_dbconnect_failed = Unable to connect to database: {0} +blogdatamodule_dbfile_invalid = BlogDataModule.DbFile='{0}' is invalid. +blogdatamodule_createdatabase_failed = BlogDataModule failed to create database when executing SQL: {1}. Last SQL error is: {0}. +blogdatamodule_query_failed = Failed to execute SQL: {1}. Last SQL error is: {0}. \ No newline at end of file diff --git a/demos/blog/protected/Common/schema.sql b/demos/blog/protected/Common/schema.sql new file mode 100644 index 00000000..49f6f429 --- /dev/null +++ b/demos/blog/protected/Common/schema.sql @@ -0,0 +1,70 @@ +CREATE TABLE tblUsers ( + id INTEGER NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL UNIQUE, + full_name VARCHAR(128) DEFAULT '', + role INTEGER NOT NULL DEFAULT 0, /* 0: user, 1: admin */ + passwd VARCHAR(128) NOT NULL, + vcode VARCHAR(128) DEFAULT '', + email VARCHAR(128) NOT NULL, + reg_time INTEGER NOT NULL, + status INTEGER NOT NULL DEFAULT 0, /* 0: normal, 1: disabled, 2: pending approval */ + website VARCHAR(128) DEFAULT '' +); + +CREATE TABLE tblPosts ( + id INTEGER NOT NULL PRIMARY KEY, + author_id INTEGER NOT NULL, + create_time INTEGER NOT NULL, + modify_time INTEGER DEFAULT 0, + title VARCHAR(256) NOT NULL, + content TEXT NOT NULL, + status INTEGER NOT NULL DEFAULT 0, /* 0: published, 1: draft, 2: pending approval */ + comment_count INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE tblComments ( + id INTEGER NOT NULL PRIMARY KEY, + post_id INTEGER NOT NULL, + author_name VARCHAR(64) NOT NULL, + author_email VARCHAR(128) NOT NULL, + author_website VARCHAR(128) DEFAULT '', + author_ip CHAR(16) NOT NULL, + create_time INTEGER NOT NULL, + status INTEGER NOT NULL DEFAULT 0, /* 0: published, 1: pending approval */ + content TEXT NOT NULL +); + +CREATE TABLE tblCategories ( + id INTEGER NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL UNIQUE, + description TEXT DEFAULT '', + post_count INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE tblAttachments ( + id VARCHAR(128) NOT NULL PRIMARY KEY, + post_id INTEGER NOT NULL, + create_time INTEGER NOT NULL, + file_name VARCHAR(128) NOT NULL, + file_size INTEGER NOT NULL, + mime_type VARCHAR(32) NOT NULL DEFAULT 'text/html', + download_count INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE tblPost2Category ( + post_id INTEGER NOT NULL, + category_id INTEGER NOT NULL, + PRIMARY KEY (post_id, category_id) +); + +INSERT INTO tblUsers (id,name,full_name,role,status,passwd,email,reg_time,website) + VALUES (1,'admin','Prado User',1,0,'4d688da592969d0a56b5accec3ce8554','admin@example.com',1148819681,'http://www.pradosoft.com'); + +INSERT INTO tblPosts (id,author_id,create_time,title,content,status) + VALUES (1,1,1148819691,'Welcome to Prado Weblog','Congratulations! You have successfully installed Prado Weblog. An administrator account has been created. Please login with admin/prado and update your password as soon as possible.',0); + +INSERT INTO tblCategories (name,description,post_count) + VALUES ('Miscellaneous','This category holds posts on any topic.',1); + +INSERT INTO tblPost2Category (post_id,category_id) + VALUES (1,1); diff --git a/demos/blog/protected/Data/Options.xml b/demos/blog/protected/Data/Options.xml new file mode 100644 index 00000000..02e51a98 --- /dev/null +++ b/demos/blog/protected/Data/Options.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/demos/blog/protected/Layouts/MainLayout.php b/demos/blog/protected/Layouts/MainLayout.php new file mode 100644 index 00000000..253d6c03 --- /dev/null +++ b/demos/blog/protected/Layouts/MainLayout.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/demos/blog/protected/Layouts/MainLayout.tpl b/demos/blog/protected/Layouts/MainLayout.tpl new file mode 100644 index 00000000..f171de7f --- /dev/null +++ b/demos/blog/protected/Layouts/MainLayout.tpl @@ -0,0 +1,47 @@ + + + + > + + + + + + + + +
+ + + + +
+ +
+ + + + + +
+
+ + \ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/AdminMenu.php b/demos/blog/protected/Pages/Admin/AdminMenu.php new file mode 100644 index 00000000..40f40b88 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/AdminMenu.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/AdminMenu.tpl b/demos/blog/protected/Pages/Admin/AdminMenu.tpl new file mode 100644 index 00000000..596f3ed2 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/AdminMenu.tpl @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/ConfigMan.page b/demos/blog/protected/Pages/Admin/ConfigMan.page new file mode 100644 index 00000000..ad728284 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/ConfigMan.page @@ -0,0 +1,56 @@ + + +

Administration Center

+ + + + + +Title +
+ +
+ +Subtitle +
+ +
+ +Owner name +
+ +
+ +Owner email +
+ +
+ +Site theme +
+ +
+ +
+ + + + + +
+ + +
+ +
+ + + + +
+ +
+ + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/ConfigMan.php b/demos/blog/protected/Pages/Admin/ConfigMan.php new file mode 100644 index 00000000..dcbe1537 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/ConfigMan.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/PostMan.page b/demos/blog/protected/Pages/Admin/PostMan.page new file mode 100644 index 00000000..8ba8ef29 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/PostMan.page @@ -0,0 +1,76 @@ + + +

Administration Center

+ + + + + + + + + <%# + $this->Parent->DataItem->Status===0 ? + 'Published' : + ($this->Parent->DataItem->Status===1 ? 'Draft' : 'Pending') + %> + + + Parent->DataItem->Status %> > + + + + + + + + + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/PostMan.php b/demos/blog/protected/Pages/Admin/PostMan.php new file mode 100644 index 00000000..a99332eb --- /dev/null +++ b/demos/blog/protected/Pages/Admin/PostMan.php @@ -0,0 +1,56 @@ +PostGrid->CurrentPageIndex*$this->PostGrid->PageSize; + $limit=$this->PostGrid->PageSize; + $this->PostGrid->DataSource=$this->DataAccess->queryPosts('','','','ORDER BY a.status DESC, create_time DESC',"LIMIT $offset,$limit"); + $this->PostGrid->VirtualItemCount=$this->DataAccess->queryPostCount('','',''); + $this->PostGrid->dataBind(); + } + + public function onLoad($param) + { + parent::onLoad($param); + if(!$this->IsPostBack) + $this->bindData(); + } + + public function changePage($sender,$param) + { + $this->PostGrid->CurrentPageIndex=$param->NewPageIndex; + $this->bindData(); + } + + public function pagerCreated($sender,$param) + { + $param->Pager->Controls->insertAt(0,'Page: '); + } + + public function editItem($sender,$param) + { + $this->PostGrid->EditItemIndex=$param->Item->ItemIndex; + $this->bindData(); + } + + public function saveItem($sender,$param) + { + $item=$param->Item; + $postID=$this->PostGrid->DataKeys[$item->ItemIndex]; + $postRecord=$this->DataAccess->queryPostByID($postID); + $postRecord->Status=TPropertyValue::ensureInteger($item->Cells[2]->PostStatus->SelectedValue); + $this->DataAccess->updatePost($postRecord); + $this->PostGrid->EditItemIndex=-1; + $this->bindData(); + } + + public function cancelItem($sender,$param) + { + $this->PostGrid->EditItemIndex=-1; + $this->bindData(); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/Settings.page b/demos/blog/protected/Pages/Admin/Settings.page new file mode 100644 index 00000000..48dfde96 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/Settings.page @@ -0,0 +1,4 @@ + +Welcome, User->Name %> />! +This page contains site settings accessible only to site admin. + \ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/UserMan.page b/demos/blog/protected/Pages/Admin/UserMan.page new file mode 100644 index 00000000..a8b634c6 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/UserMan.page @@ -0,0 +1,95 @@ + + +

Administration Center

+ + + + + + + + <%# $this->Parent->DataItem->Role===0 ? 'User' : 'Admin' %> + + + Parent->DataItem->Role %> > + + + + + + + + <%# + $this->Parent->DataItem->Status===0 ? + 'Normal' : + ($this->Parent->DataItem->Status===1 ? 'Disabled' : 'Pending') + %> + + + Parent->DataItem->Status %> > + + + + + + + + # + ''.{0}.'' + + + + + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/UserMan.php b/demos/blog/protected/Pages/Admin/UserMan.php new file mode 100644 index 00000000..1cb62482 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/UserMan.php @@ -0,0 +1,58 @@ +User->ID; + $offset=$this->UserGrid->CurrentPageIndex*$this->UserGrid->PageSize; + $limit=$this->UserGrid->PageSize; + $this->UserGrid->DataSource=$this->DataAccess->queryUsers('','ORDER BY status DESC, name ASC',"LIMIT $offset,$limit"); + $this->UserGrid->VirtualItemCount=$this->DataAccess->queryUserCount(''); + $this->UserGrid->dataBind(); + } + + public function onLoad($param) + { + parent::onLoad($param); + if(!$this->IsPostBack) + $this->bindData(); + } + + public function changePage($sender,$param) + { + $this->UserGrid->CurrentPageIndex=$param->NewPageIndex; + $this->bindData(); + } + + public function pagerCreated($sender,$param) + { + $param->Pager->Controls->insertAt(0,'Page: '); + } + + public function editItem($sender,$param) + { + $this->UserGrid->EditItemIndex=$param->Item->ItemIndex; + $this->bindData(); + } + + public function saveItem($sender,$param) + { + $item=$param->Item; + $userID=$this->UserGrid->DataKeys[$item->ItemIndex]; + $userRecord=$this->DataAccess->queryUserByID($userID); + $userRecord->Role=TPropertyValue::ensureInteger($item->Cells[1]->UserRole->SelectedValue); + $userRecord->Status=TPropertyValue::ensureInteger($item->Cells[2]->UserStatus->SelectedValue); + $this->DataAccess->updateUser($userRecord); + $this->UserGrid->EditItemIndex=-1; + $this->bindData(); + } + + public function cancelItem($sender,$param) + { + $this->UserGrid->EditItemIndex=-1; + $this->bindData(); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Admin/config.xml b/demos/blog/protected/Pages/Admin/config.xml new file mode 100644 index 00000000..c99e5892 --- /dev/null +++ b/demos/blog/protected/Pages/Admin/config.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/demos/blog/protected/Pages/ErrorReport.page b/demos/blog/protected/Pages/ErrorReport.page new file mode 100644 index 00000000..a9b461d9 --- /dev/null +++ b/demos/blog/protected/Pages/ErrorReport.page @@ -0,0 +1,15 @@ + + +

Error

+ +

+<%= $this->ErrorMessage %> +

+ +

+Please report to us +if you believe this error is caused by our system. Thanks! +

+ + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/ErrorReport.php b/demos/blog/protected/Pages/ErrorReport.php new file mode 100644 index 00000000..3b24170f --- /dev/null +++ b/demos/blog/protected/Pages/ErrorReport.php @@ -0,0 +1,12 @@ +Request['id']); + return BlogErrors::getMessage($id); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/EditCategory.page b/demos/blog/protected/Pages/Posts/EditCategory.page new file mode 100644 index 00000000..fdde2648 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/EditCategory.page @@ -0,0 +1,36 @@ + + +

Update Post Category

+ +Category name + + +
+ +
+ +Description +
+ +
+ + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/EditCategory.php b/demos/blog/protected/Pages/Posts/EditCategory.php new file mode 100644 index 00000000..fd2d0707 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/EditCategory.php @@ -0,0 +1,44 @@ +Request['id']); + if(($cat=$this->DataAccess->queryCategoryByID($id))!==null) + return $cat; + else + throw new BlogException('xxx'); + } + + public function onLoad($param) + { + parent::onLoad($param); + if(!$this->IsPostBack) + { + $catRecord=$this->getCurrentCategory(); + $this->CategoryName->Text=$catRecord->Name; + $this->CategoryDescription->Text=$catRecord->Description; + } + } + + public function saveButtonClicked($sender,$param) + { + if($this->IsValid) + { + $categoryRecord=$this->getCurrentCategory(); + $categoryRecord->Name=$this->CategoryName->Text; + $categoryRecord->Description=$this->CategoryDescription->Text; + $this->DataAccess->updateCategory($categoryRecord); + $this->gotoPage('Posts.ListPost',array('cat'=>$categoryRecord->ID)); + } + } + + public function checkCategoryName($sender,$param) + { + $name=$this->CategoryName->Text; + $param->IsValid=$this->DataAccess->queryCategoryByName($name)===null; + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/EditPost.page b/demos/blog/protected/Pages/Posts/EditPost.page new file mode 100644 index 00000000..591f5945 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/EditPost.page @@ -0,0 +1,41 @@ + + +

Update Post

+ +Title + +
+ +
+ +Content + +
+ +
+ +Categories
+ +
+ + +
+ + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/EditPost.php b/demos/blog/protected/Pages/Posts/EditPost.php new file mode 100644 index 00000000..57e92b1c --- /dev/null +++ b/demos/blog/protected/Pages/Posts/EditPost.php @@ -0,0 +1,51 @@ +Request['id']); + if(($post=$this->DataAccess->queryPostByID($id))!==null) + return $post; + else + throw new BlogException('xxx'); + } + + public function onLoad($param) + { + parent::onLoad($param); + if(!$this->IsPostBack) + { + $postRecord=$this->getCurrentPost(); + $this->Title->Text=$postRecord->Title; + $this->Content->Text=$postRecord->Content; + $this->DraftMode->Checked=$postRecord->Status===0; + $this->Categories->DataSource=$this->DataAccess->queryCategories(); + $this->Categories->dataBind(); + $cats=$this->DataAccess->queryCategoriesByPostID($postRecord->ID); + $catIDs=array(); + foreach($cats as $cat) + $catIDs[]=$cat->ID; + $this->Categories->SelectedValues=$catIDs; + } + } + + public function saveButtonClicked($sender,$param) + { + if($this->IsValid) + { + $postRecord=$this->getCurrentPost(); + $postRecord->Title=$this->Title->Text; + $postRecord->Content=$this->Content->Text; + $postRecord->Status=$this->DraftMode->Checked?0:1; + $postRecord->ModifyTime=time(); + $cats=array(); + foreach($this->Categories->SelectedValues as $value) + $cats[]=TPropertyValue::ensureInteger($value); + $this->DataAccess->updatePost($postRecord,$cats); + $this->gotoPage('Posts.ViewPost',array('id'=>$postRecord->ID)); + } + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/ListPost.page b/demos/blog/protected/Pages/Posts/ListPost.page new file mode 100644 index 00000000..15fc3d0c --- /dev/null +++ b/demos/blog/protected/Pages/Posts/ListPost.page @@ -0,0 +1,27 @@ + + + + +
+
+<%# $this->DataItem->Title %> +
+
+<%# date('l, F j, Y \a\t h:i:s a',$this->DataItem->CreateTime) %> +
+
+<%# $this->DataItem->Content %> +
+ +
+
+
+ +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/ListPost.php b/demos/blog/protected/Pages/Posts/ListPost.php new file mode 100644 index 00000000..6d56b543 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/ListPost.php @@ -0,0 +1,44 @@ +Request['time']))>0) + { + $year=(integer)($time/100); + $month=$time%100; + $startTime=mktime(0,0,0,$month,1,$year); + if(++$month>12) + { + $month=1; + $year++; + } + $endTime=mktime(0,0,0,$month,1,$year); + $timeFilter="create_time>=$startTime AND create_time<$endTime"; + } + if(($catID=$this->Request['cat'])!==null) + { + $catID=TPropertyValue::ensureInteger($catID); + $catFilter="category_id=$catID"; + } + if(($offset=TPropertyValue::ensureInteger($this->Request['offset']))<=0) + $offset=0; + if(($limit=TPropertyValue::ensureInteger($this->Request['limit']))<=0) + $limit=self::DEFAULT_LIMIT; + return $this->DataAccess->queryPosts('',$timeFilter,$catFilter,'ORDER BY create_time DESC',"LIMIT $offset,$limit"); + } + + public function onLoad($param) + { + parent::onLoad($param); + $this->PostList->DataSource=$this->getPosts(); + $this->PostList->dataBind(); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/MyPost.page b/demos/blog/protected/Pages/Posts/MyPost.page new file mode 100644 index 00000000..95a32ac9 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/MyPost.page @@ -0,0 +1,46 @@ + + +

My Posts

+ + + + + + + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/MyPost.php b/demos/blog/protected/Pages/Posts/MyPost.php new file mode 100644 index 00000000..be03ca63 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/MyPost.php @@ -0,0 +1,34 @@ +User->ID; + $offset=$this->PostGrid->CurrentPageIndex*$this->PostGrid->PageSize; + $limit=$this->PostGrid->PageSize; + $this->PostGrid->DataSource=$this->DataAccess->queryPosts("author_id=$author",'','','ORDER BY a.status ASC, create_time DESC',"LIMIT $offset,$limit"); + $this->PostGrid->VirtualItemCount=$this->DataAccess->queryPostCount("author_id=$author",'',''); + $this->PostGrid->dataBind(); + } + + public function onLoad($param) + { + parent::onLoad($param); + if(!$this->IsPostBack) + $this->bindData(); + } + + public function changePage($sender,$param) + { + $this->PostGrid->CurrentPageIndex=$param->NewPageIndex; + $this->bindData(); + } + + public function pagerCreated($sender,$param) + { + $param->Pager->Controls->insertAt(0,'Page: '); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/NewCategory.page b/demos/blog/protected/Pages/Posts/NewCategory.page new file mode 100644 index 00000000..92fe1468 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/NewCategory.page @@ -0,0 +1,36 @@ + + +

New Post Category

+ +Category name + + +
+ +
+ +Description +
+ +
+ + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/NewCategory.php b/demos/blog/protected/Pages/Posts/NewCategory.php new file mode 100644 index 00000000..d36f6af1 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/NewCategory.php @@ -0,0 +1,24 @@ +IsValid) + { + $categoryRecord=new CategoryRecord; + $categoryRecord->Name=$this->CategoryName->Text; + $categoryRecord->Description=$this->CategoryDescription->Text; + $this->DataAccess->insertCategory($categoryRecord); + $this->gotoPage('Posts.ListPost',array('cat'=>$categoryRecord->ID)); + } + } + + public function checkCategoryName($sender,$param) + { + $name=$this->CategoryName->Text; + $param->IsValid=$this->DataAccess->queryCategoryByName($name)===null; + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/NewPost.page b/demos/blog/protected/Pages/Posts/NewPost.page new file mode 100644 index 00000000..a49188f6 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/NewPost.page @@ -0,0 +1,41 @@ + + +

Write a New Post

+ +Title + +
+ +
+ +Content + +
+ +
+ +Categories
+ +
+ + +
+ + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/NewPost.php b/demos/blog/protected/Pages/Posts/NewPost.php new file mode 100644 index 00000000..055c7f92 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/NewPost.php @@ -0,0 +1,34 @@ +IsPostBack) + { + $this->Categories->DataSource=$this->DataAccess->queryCategories(); + $this->Categories->dataBind(); + } + } + + public function saveButtonClicked($sender,$param) + { + if($this->IsValid) + { + $postRecord=new PostRecord; + $postRecord->Title=$this->Title->Text; + $postRecord->Content=$this->Content->Text; + $postRecord->Status=$this->DraftMode->Checked?0:1; + $postRecord->CreateTime=time(); + $postRecord->AuthorID=$this->User->ID; + $cats=array(); + foreach($this->Categories->SelectedValues as $value) + $cats[]=TPropertyValue::ensureInteger($value); + $this->DataAccess->insertPost($postRecord,$cats); + $this->gotoPage('Posts.ViewPost',array('id'=>$postRecord->ID)); + } + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/ViewPost.page b/demos/blog/protected/Pages/Posts/ViewPost.page new file mode 100644 index 00000000..4b233615 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/ViewPost.page @@ -0,0 +1,113 @@ + + +
+
+<%= $this->CurrentPost->Title %> +
+
+<%= date('l, F j, Y \a\t h:i:s a',$this->CurrentPost->CreateTime) %> +by +<%= '' . $this->CurrentPost->AuthorName . '' %> +<%= $this->CanEditPost ? '| Edit | ' : ''; +%> +CanEditPost %> + Attributes.onclick="if(!confirm('Are you sure to delete this post? This will also delete all related comments.')) return false;" + /> +
+
+<%= $this->CurrentPost->Content %> +
+ +
+ +
+ +

Comments

+ + + +
+
+DataItem->ID %> + Visible=<%= $this->Page->CanEditPost %> Style="float:right"/> +<%# date('F j, Y \a\t h:i:s a',$this->DataItem->CreateTime) %> +by +<%# $this->DataItem->AuthorWebsite==='' ? + $this->DataItem->AuthorName : + '' . $this->DataItem->AuthorName . '' %> +
+
+<%# $this->DataItem->Content %> +
+
+
+
+ +

Leave your comment

+ +Name + +
+ +
+ +Email address + + +
+ +
+ +Personal website +
+ +
+ +Comment + +
+ +
+ + + +
+
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/ViewPost.php b/demos/blog/protected/Pages/Posts/ViewPost.php new file mode 100644 index 00000000..309bedc1 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/ViewPost.php @@ -0,0 +1,73 @@ +_postID===null) + $this->_postID=TPropertyValue::ensureInteger($this->Request['id']); + return $this->_postID; + } + + public function getCurrentPost() + { + if($this->_post===null) + { + if(($this->_post=$this->DataAccess->queryPostByID($this->getPostID()))===null) + $this->reportError(BlogErrors::ERROR_POST_NOT_FOUND); + } + return $this->_post; + } + + public function getCanEditPost() + { + $user=$this->getUser(); + $authorID=$this->getCurrentPost()->AuthorID; + return $authorID===$user->getID() || $user->isInRole('admin'); + } + + public function onLoad($param) + { + parent::onLoad($param); + $this->CategoryList->DataSource=$this->DataAccess->queryCategoriesByPostID($this->getPostID()); + $this->CategoryList->dataBind(); + $this->CommentList->DataSource=$this->DataAccess->queryCommentsByPostID($this->getPostID()); + $this->CommentList->dataBind(); + } + + public function submitCommentButtonClicked($sender,$param) + { + if($this->IsValid) + { + $commentRecord=new CommentRecord; + $commentRecord->PostID=$this->CurrentPost->ID; + $commentRecord->AuthorName=$this->CommentAuthor->Text; + $commentRecord->AuthorEmail=$this->CommentEmail->Text; + $commentRecord->AuthorWebsite=$this->CommentWebsite->Text; + $commentRecord->AuthorIP=$this->Request->UserHostAddress; + $commentRecord->Content=$this->CommentContent->Text; + $commentRecord->CreateTime=time(); + $commentRecord->Status=0; + $this->DataAccess->insertComment($commentRecord); + $this->Response->reload(); + } + } + + public function deleteButtonClicked($sender,$param) + { + $this->DataAccess->deletePost($this->PostID); + $this->gotoDefaultPage(); + } + + public function repeaterItemCommand($sender,$param) + { + $id=TPropertyValue::ensureInteger($param->CommandParameter); + $this->DataAccess->deleteComment($id); + $this->Response->reload(); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Posts/config.xml b/demos/blog/protected/Pages/Posts/config.xml new file mode 100644 index 00000000..1c04e946 --- /dev/null +++ b/demos/blog/protected/Pages/Posts/config.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/demos/blog/protected/Pages/Users/EditUser.page b/demos/blog/protected/Pages/Users/EditUser.page new file mode 100644 index 00000000..8c21fd50 --- /dev/null +++ b/demos/blog/protected/Pages/Users/EditUser.page @@ -0,0 +1,74 @@ + + +

Update Profile

+ + + +Username +
+ + +
+ +Full name +
+ + +
+ +Password +
+ + + +
+ +Re-type Password +
+ + + +
+ +Email Address +
+ + + + +
+ +Personal Website +
+ + +
+ + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Users/EditUser.php b/demos/blog/protected/Pages/Users/EditUser.php new file mode 100644 index 00000000..e3efcfd1 --- /dev/null +++ b/demos/blog/protected/Pages/Users/EditUser.php @@ -0,0 +1,43 @@ +DataAccess->queryUserByID($this->User->ID))!==null) + return $user; + else + throw new BlogException('xxx'); + } + + public function onLoad($param) + { + parent::onLoad($param); + if(!$this->IsPostBack) + { + $userRecord=$this->getCurrentUser(); + $this->Username->Text=$userRecord->Name; + $this->FullName->Text=$userRecord->FullName; + $this->Email->Text=$userRecord->Email; + $this->Website->Text=$userRecord->Website; + } + } + + public function saveButtonClicked($sender,$param) + { + if($this->IsValid) + { + $userRecord=$this->getCurrentUser(); + if($this->Password->Text!=='') + $userRecord->Password=md5($this->Password->Text); + $userRecord->FullName=$this->FullName->Text; + $userRecord->Email=$this->Email->Text; + $userRecord->Website=$this->Website->Text; + $this->DataAccess->updateUser($userRecord); + $authManager=$this->Application->getModule('auth'); + $this->gotoPage('Users.ViewUser',array('id'=>$userRecord->ID)); + } + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Users/NewUser.page b/demos/blog/protected/Pages/Users/NewUser.page new file mode 100644 index 00000000..eba2dcec --- /dev/null +++ b/demos/blog/protected/Pages/Users/NewUser.page @@ -0,0 +1,104 @@ + + +

Create New Account

+ + + +Username +
+ + + + + +
+ +Full name +
+ + +
+ +Password +
+ + + + +
+ +Re-type Password +
+ + + +
+ +Email Address +
+ + + + +
+ +Personal Website +
+ + +
+ + + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Users/NewUser.php b/demos/blog/protected/Pages/Users/NewUser.php new file mode 100644 index 00000000..166abf66 --- /dev/null +++ b/demos/blog/protected/Pages/Users/NewUser.php @@ -0,0 +1,31 @@ +Username->Text; + $param->IsValid=$this->DataAccess->queryUserByName($username)===null; + } + + public function createUser($sender,$param) + { + if($this->IsValid) + { + $userRecord=new UserRecord; + $userRecord->Name=$this->Username->Text; + $userRecord->FullName=$this->FullName->Text; + $userRecord->Role=0; + $userRecord->Password=md5($this->Password->Text); + $userRecord->Email=$this->Email->Text; + $userRecord->CreateTime=time(); + $userRecord->Website=$this->Website->Text; + $this->DataAccess->insertUser($userRecord); + $authManager=$this->Application->getModule('auth'); + $authManager->login($this->Username->Text,$this->Password->Text); + $this->gotoDefaultPage(); + } + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Users/ViewUser.page b/demos/blog/protected/Pages/Users/ViewUser.page new file mode 100644 index 00000000..2dba6b77 --- /dev/null +++ b/demos/blog/protected/Pages/Users/ViewUser.page @@ -0,0 +1,21 @@ + + +

User Profile

+ +Username: <%= $this->CurrentUser->Name %> +
+ +Full name: <%= $this->CurrentUser->FullName %> +
+ +Email: <%= $this->CurrentUser->Email %> +
+ +Privilege: <%= $this->CurrentUser->Role===0? 'User':'Administrator' %> +
+ +Personal website: <%= $this->CurrentUser->Website %> +
+ + +
\ No newline at end of file diff --git a/demos/blog/protected/Pages/Users/ViewUser.php b/demos/blog/protected/Pages/Users/ViewUser.php new file mode 100644 index 00000000..3485f56b --- /dev/null +++ b/demos/blog/protected/Pages/Users/ViewUser.php @@ -0,0 +1,19 @@ +_currentUser===null) + { + $id=TPropertyValue::ensureInteger($this->Request['id']); + if(($this->_currentUser=$this->DataAccess->queryUserByID($id))===null) + throw new BlogException('xxx'); + } + return $this->_currentUser; + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Pages/Users/config.xml b/demos/blog/protected/Pages/Users/config.xml new file mode 100644 index 00000000..df8e4ad1 --- /dev/null +++ b/demos/blog/protected/Pages/Users/config.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/demos/blog/protected/Portlets/AccountPortlet.php b/demos/blog/protected/Portlets/AccountPortlet.php new file mode 100644 index 00000000..0f0e60c6 --- /dev/null +++ b/demos/blog/protected/Portlets/AccountPortlet.php @@ -0,0 +1,14 @@ +Application->getModule('auth')->logout(); + $this->Response->reload(); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Portlets/AccountPortlet.tpl b/demos/blog/protected/Portlets/AccountPortlet.tpl new file mode 100644 index 00000000..2a401f41 --- /dev/null +++ b/demos/blog/protected/Portlets/AccountPortlet.tpl @@ -0,0 +1,20 @@ +
+ +

Account

+ +
+Welcome, <%= $this->User->Name %>! + + +
+ +
diff --git a/demos/blog/protected/Portlets/ArchivePortlet.php b/demos/blog/protected/Portlets/ArchivePortlet.php new file mode 100644 index 00000000..a004c7a9 --- /dev/null +++ b/demos/blog/protected/Portlets/ArchivePortlet.php @@ -0,0 +1,45 @@ +Application->getModule('data')->queryEarliestPostTime(); + if(empty($startTime)) // if no posts + $startTime=$currentTime; + + // obtain the timestamp for the initial month + $date=getdate($startTime); + $startTime=mktime(0,0,0,$date['mon'],1,$date['year']); + + $date=getdate($currentTime); + $month=$date['mon']; + $year=$date['year']; + + $timestamps=array(); + while(true) + { + if(($timestamp=mktime(0,0,0,$month,1,$year))<$startTime) + break; + $timestamps[]=$timestamp; + if(--$month===0) + { + $month=12; + $year--; + } + } + $this->MonthList->DataSource=$timestamps; + $this->MonthList->dataBind(); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Portlets/ArchivePortlet.tpl b/demos/blog/protected/Portlets/ArchivePortlet.tpl new file mode 100644 index 00000000..c576e9f5 --- /dev/null +++ b/demos/blog/protected/Portlets/ArchivePortlet.tpl @@ -0,0 +1,15 @@ +
+ +

Archives

+ + + +
diff --git a/demos/blog/protected/Portlets/CategoryPortlet.php b/demos/blog/protected/Portlets/CategoryPortlet.php new file mode 100644 index 00000000..9c2033aa --- /dev/null +++ b/demos/blog/protected/Portlets/CategoryPortlet.php @@ -0,0 +1,15 @@ +CategoryList->DataSource=$this->Application->getModule('data')->queryCategories(); + $this->CategoryList->dataBind(); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Portlets/CategoryPortlet.tpl b/demos/blog/protected/Portlets/CategoryPortlet.tpl new file mode 100644 index 00000000..acbd3bec --- /dev/null +++ b/demos/blog/protected/Portlets/CategoryPortlet.tpl @@ -0,0 +1,24 @@ +
+ +

+Categories +Service->constructUrl('Posts.NewCategory') %> + Visible=<%= $this->User->isInRole('admin') %> /> +

+ + + +
diff --git a/demos/blog/protected/Portlets/LoginPortlet.php b/demos/blog/protected/Portlets/LoginPortlet.php new file mode 100644 index 00000000..0085c17f --- /dev/null +++ b/demos/blog/protected/Portlets/LoginPortlet.php @@ -0,0 +1,22 @@ +Application->getModule('auth'); + if(!$authManager->login($this->Username->Text,$this->Password->Text)) + $param->IsValid=false; + } + + public function loginButtonClicked($sender,$param) + { + if($this->Page->IsValid) + $this->Response->reload(); + //$this->Response->redirect($this->Application->getModule('auth')->getReturnUrl()); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Portlets/LoginPortlet.tpl b/demos/blog/protected/Portlets/LoginPortlet.tpl new file mode 100644 index 00000000..6f8c5d4a --- /dev/null +++ b/demos/blog/protected/Portlets/LoginPortlet.tpl @@ -0,0 +1,36 @@ +
+ +

Login

+ + +Username + +
+ +
+ +Password + +
+ + +
+ +| Register + +
+ +
diff --git a/demos/blog/protected/Portlets/Portlet.php b/demos/blog/protected/Portlets/Portlet.php new file mode 100644 index 00000000..4b1c80e9 --- /dev/null +++ b/demos/blog/protected/Portlets/Portlet.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/demos/blog/protected/Portlets/SearchPortlet.php b/demos/blog/protected/Portlets/SearchPortlet.php new file mode 100644 index 00000000..1bad7f1c --- /dev/null +++ b/demos/blog/protected/Portlets/SearchPortlet.php @@ -0,0 +1,22 @@ +Page->IsPostBack && ($keyword=$this->Request['keyword'])!==null) + $this->Keyword->Text=$keyword; + } + + public function search($sender,$param) + { + $keyword=$this->Keyword->Text; + $url=$this->Service->constructUrl('SearchPost',array('keyword'=>$keyword)); + $this->Response->redirect($url); + } +} + +?> \ No newline at end of file diff --git a/demos/blog/protected/Portlets/SearchPortlet.tpl b/demos/blog/protected/Portlets/SearchPortlet.tpl new file mode 100644 index 00000000..f88fca7e --- /dev/null +++ b/demos/blog/protected/Portlets/SearchPortlet.tpl @@ -0,0 +1,21 @@ +
+ +

Search

+ + +Keyword + +
+ + +
+ +
diff --git a/demos/blog/protected/application.xml b/demos/blog/protected/application.xml new file mode 100644 index 00000000..9bca115c --- /dev/null +++ b/demos/blog/protected/application.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/blog/sitemap.txt b/demos/blog/sitemap.txt new file mode 100644 index 00000000..8326855b --- /dev/null +++ b/demos/blog/sitemap.txt @@ -0,0 +1,106 @@ +Be careful about username case sensitivity!! + +Home : list of blogs filtered by a category or time range, with paging + +ViewBlog : read a single blog with all comments and a comment input form +NewBlog : create a new blog, with file attachment form and THtmlArea +EditBlog : edit an existing blog + +LoginUser : login page +NewUser : create a new user +EditUser : edit the current user + +Admin : whether allow multiple users, whether HTML is allowed (first user is always the admin) + +URL design: + +index.php?page=ListBlog×pan=123,456&limit=123,456 : list of latest blogs, equivalent to: +index.php?page=NewBlog +index.php?page=EditBlog&id=123 +index.php?page=ViewBlog&id=123 +index.php?page=NewUser +index.php?page=EditUser +index.php?page=ViewUser +index.php?page=Admin + + +Use Case 1: Add a post +1. Authorization check +2. display UI for adding post +3. input validation +4. add post to DB +5. display UI for post list + + +DB Logic needed: + +class Post extends DataObject +{ + public $xxx; +} + +class Comment extends DataObject +{ +} + +class UserProfile extends DataObject +{ + +} + +class DataObject extends TComponent +{ + protected static $mapping=array(); + + public function __construct($db) + { + } + + protected static function generateModifier($filter,$orderBy,$limit) + { + $modifier=''; + if($filter!=='') + $modifier=' WHERE '.$filter; + if($orderBy!=='') + $modifier.=' ORDER BY '.$orderBy; + if($limit!=='') + $modifier.=' LIMIT '.$limit; + return $modifier; + } + + public static function queryRow($filter='') + { + $modifier=self::generateModifier($filter,'',''); + } + + public static function query($filter='',$orderBy='',$limit='') + { + $modifier=self::generateModifier($filter,$orderBy,$limit); + } + + public function save() + { + } + + public function delete() + { + } +} + +public function queryUsers($filter='',$sortBy='',$limit='') +public function queryUser($id) +public function insertUser($user) +public function updateUser($user) +public function deleteUser($id) + +public function queryPosts($filter='',$sortBy='',$limit='') +public function queryPost($id) +public function insertPost($post) +public function updatePost($post) +public function deletePost($id) + +public function queryComments($filter='',$sortBy='',$limit='') +public function queryComment($id) +public function insertComment($comment) +public function updateComment($comment) +public function deleteComment($id) diff --git a/demos/blog/themes/Basic/style.css b/demos/blog/themes/Basic/style.css new file mode 100644 index 00000000..b8e9ca89 --- /dev/null +++ b/demos/blog/themes/Basic/style.css @@ -0,0 +1,261 @@ +html { + margin: 0; + padding: 0; +} + +body { + margin: 0; + padding: 0; + font-family: verdana, 'trebuchet ms', sans-serif; + font-size: 12px; + color: #333; + background: #36414d; + min-width:750px; +} + +form { + margin: 0; + padding: 0; +} + +a { + text-decoration: underline; +} + +a img { + border: 0px none; +} + +#page { + background:#fff; + margin:0 auto; + width:800px; +} + +#header { + background: #a3b8cc; + border-bottom: 1px solid silver; +} + +#header h1 { + padding:5px; + padding-left: 20px; + margin:0; + font-size: 16pt; +} + +#header h2 { + padding:5px; + padding-left: 20px; + margin:0; + font-size: 12pt; +} + +#main { + background:#fff; + float:left; + width:560px; + padding: 20px; +} + +#main h2 { + border-bottom: 1px solid silver; +} + +#sidebar { + background:#e6ecf2; + float:right; + width:200px; +} + +#sidebar ul { + margin-bottom:0; +} + +#sidebar h3, #sidebar p { + padding:0 10px 0 0; +} + +#footer { + background:#fff; + clear:both; + color: gray; + font-size:8pt; + text-align:center; + padding-top:25px; + padding-bottom:10px; +} + +.portlet { + margin: 10px; + border-bottom: 1px solid silver; + border-right: 1px solid silver; + background: #dae0e6; +} + +.portlet-title { + /* ie win (5, 5.5, 6) bugfix */ + p\osition: relative; + width: 100%; + w\idth: auto; + + margin: 0; + border-left: 5px solid #36414d; + border-bottom: 1px solid silver; + padding: 5px; + color: #fff; + background: #a3b8cc; + font-size: 8pt; + font-weight: bold; + line-height: 1; + text-transform: uppercase; +} + +.portlet-title a { + color: yellow; + text-decoration: none; + text-transform: none; +} + +.portlet-title a:hover { + color: red; +} + +.portlet-content { + margin: 0 0 10px 0; + border-top: 1px solid #cfd4d9; + padding: 10px 10px 0 10px; + font-size: 10px; +} + +.portlet-content ul { + margin: 0 15px 10px 15px; + padding: 0; + list-style: square; +} + +.portlet-content li { + color: #666; + margin-top: 3px; +} + +.portlet-content a { + color: #36414d; + text-decoration: none; +} + +.portlet-content a:hover { + color: red; +} + +.post { + margin-bottom: 15px; +} + +.post-title { + font-size: 14pt; + border-bottom: 1px silver solid; +} + +.post-time { + font-size: 8pt; + color: gray; +} + +.post-content { + margin-top: 10px; + margin-bottom: 10px; +} + +.post-footer { + text-align: right; + font-size: 8pt; +} + +.post-footer a { + color: #36414d; +} + +.comments { + border-top: 1px silver solid; +} + +.comments h3 { + font-size: 12pt; +} + +.comment { + margin-bottom: 10px; +} + +.comment-header { + background-color: #e6ecf2; + padding: 3px; + font-size: 8pt; +} + +.grid { + width: 100%; +} + +.grid td { + padding: 3px; +} + +.grid-header { + color: #fff; + background: #a3b8cc; +} + +.grid-row1 { + background: #dae0e6; +} + +.grid-row2 { + background: #e6ecf2; +} + +.grid-row-selected { + background: lightyellow; +} + +.grid-pager { + text-align: right; + color: silver; +} + +.grid-pager a { + color: green; + text-decoration: none; +} + +.submenu { + margin-bottom: 10px; + border-bottom: 5px solid #a3b8cc; + padding-right: 10px; + text-align: right; +} + +.submenu ul { + margin:0; + padding:0; + list-style:none; +} + +.submenu li { + display:inline; + margin:0; + padding:0; +} + +.submenu-active { + text-decoration: none; + background: #a3b8cc; + padding: 3px 5px 0 5px; +} + +.submenu-inactive { + text-decoration: none; + background: #dae0e6; + padding: 3px 5px 0 5px; +} \ No newline at end of file diff --git a/demos/quickstart/protected/pages/Controls/Samples/TDataTypeValidator/Home.page b/demos/quickstart/protected/pages/Controls/Samples/TDataTypeValidator/Home.page index 783769ff..ac83f745 100644 --- a/demos/quickstart/protected/pages/Controls/Samples/TDataTypeValidator/Home.page +++ b/demos/quickstart/protected/pages/Controls/Samples/TDataTypeValidator/Home.page @@ -31,8 +31,8 @@ Data type validator with client-side validation disabled: ValidationGroup="Group2" EnableClientScript="false" ControlToValidate="TextBox2" - DataType="Currency" - Text="You must enter a currency." /> + DataType="Integer" + Text="You must enter an integer." /> @@ -47,7 +47,8 @@ Validating a date input: ValidationGroup="Group3" ControlToValidate="TextBox3" DataType="Date" - Text="You must enter a valid date." /> + DateFormat="d/M/yyyy" + Text="You must enter a valid date (d/M/yyyy)." /> diff --git a/framework/I18N/core/NumberFormat.php b/framework/I18N/core/NumberFormat.php index d1184f86..8e715f15 100644 --- a/framework/I18N/core/NumberFormat.php +++ b/framework/I18N/core/NumberFormat.php @@ -122,7 +122,6 @@ class NumberFormat $string = (string)$number; - $decimal = $this->formatDecimal($string); $integer = $this->formatInteger(abs($number)); @@ -130,9 +129,9 @@ class NumberFormat $result = $integer.$decimal; else $result = $integer; - + //get the suffix - if($number > 0) + if($number >= 0) $suffix = $this->formatInfo->PositivePattern; else if($number < 0) $suffix = $this->formatInfo->NegativePattern; diff --git a/framework/Security/TUser.php b/framework/Security/TUser.php index 7b785add..ee52ceaa 100644 --- a/framework/Security/TUser.php +++ b/framework/Security/TUser.php @@ -57,6 +57,7 @@ class TUser extends TComponent implements IUser public function __construct(IUserManager $manager) { $this->_manager=$manager; + $this->_name=$manager->getGuestName(); } /** diff --git a/framework/Web/Javascripts/colorpicker/colorpicker.js b/framework/Web/Javascripts/colorpicker/colorpicker.js index dc80f0c7..06cfb037 100644 --- a/framework/Web/Javascripts/colorpicker/colorpicker.js +++ b/framework/Web/Javascripts/colorpicker/colorpicker.js @@ -74,7 +74,7 @@ Object.extend(Prado.WebUI.TColorPicker.prototype, if(Prado.Browser().ie) { this.iePopUp = document.createElement('iframe'); - this.iePopUp.src = ""; + this.iePopUp.src = Prado.WebUI.TColorPicker.UIImages['button.gif']; this.iePopUp.style.position = "absolute" this.iePopUp.scrolling="no" this.iePopUp.frameBorder="0" diff --git a/framework/Web/Javascripts/datepicker/datepicker.js b/framework/Web/Javascripts/datepicker/datepicker.js index 79763811..d5d9496c 100644 --- a/framework/Web/Javascripts/datepicker/datepicker.js +++ b/framework/Web/Javascripts/datepicker/datepicker.js @@ -81,11 +81,14 @@ Prado.WebUI.TDatePicker.prototype = Object.extend(this,options); Event.observe(this.trigger, triggerEvent, this.show.bindEvent(this)); - this.create(); + }, create : function() { + if(typeof(this._calDiv) != "undefined") + return; + var div; var table; var tbody; @@ -115,9 +118,10 @@ Prado.WebUI.TDatePicker.prototype = // Previous Month Button td = document.createElement("td"); - var previousMonth = document.createElement("button"); - previousMonth.className = "prevMonthButton"; - previousMonth.appendChild(document.createTextNode("<<")); + var previousMonth = document.createElement("input"); + previousMonth.className = "prevMonthButton button"; + previousMonth.type = "button" + previousMonth.value = "<<"; td.appendChild(previousMonth); tr.appendChild(td); @@ -162,9 +166,10 @@ Prado.WebUI.TDatePicker.prototype = td = document.createElement("td"); - td.className = "nextMonthButton"; - var nextMonth = document.createElement("button"); - nextMonth.appendChild(document.createTextNode(">>")); + var nextMonth = document.createElement("input"); + nextMonth.className = "nextMonthButton button"; + nextMonth.type = "button"; + nextMonth.value = ">>"; td.appendChild(nextMonth); tr.appendChild(td); @@ -178,6 +183,7 @@ Prado.WebUI.TDatePicker.prototype = var text; table = document.createElement("table"); + table.align="center"; table.className = "grid"; div.appendChild(table); @@ -226,11 +232,12 @@ Prado.WebUI.TDatePicker.prototype = div.className = "calendarFooter"; this._calDiv.appendChild(div); - var todayButton = document.createElement("button"); + var todayButton = document.createElement("input"); + todayButton.type="button"; todayButton.className = "todayButton"; var today = this.newDate(); var buttonText = today.SimpleFormat(this.Format,this); - todayButton.appendChild(document.createTextNode(buttonText)); + todayButton.value = buttonText; div.appendChild(todayButton); /*var clearButton = document.createElement("button"); @@ -243,7 +250,7 @@ Prado.WebUI.TDatePicker.prototype = if(Prado.Browser().ie) { this.iePopUp = document.createElement('iframe'); - this.iePopUp.src = ""; + this.iePopUp.src = Prado.WebUI.TDatePicker.spacer; this.iePopUp.style.position = "absolute" this.iePopUp.scrolling="no" this.iePopUp.frameBorder="0" @@ -521,6 +528,8 @@ Prado.WebUI.TDatePicker.prototype = show : function() { + this.create(); + if(!this.showing) { var pos = Position.cumulativeOffset(this.control); diff --git a/framework/Web/Javascripts/datepicker/default.css b/framework/Web/Javascripts/datepicker/default.css index e5d327e1..7e920f1b 100644 --- a/framework/Web/Javascripts/datepicker/default.css +++ b/framework/Web/Javascripts/datepicker/default.css @@ -21,6 +21,7 @@ margin-left: 1px; } + .TDatePickerImageButton:hover { border-color: #ddd; @@ -31,9 +32,9 @@ font-size: 11px; } -.TDatePicker .calendarHeader button +.TDatePicker input.button { - font-size: 12px; + font-size: 11px; width: 32px; } @@ -41,6 +42,7 @@ { padding: 4px 0; border: 1px solid white; + text-align: center; } .TDatePicker .hover { @@ -61,25 +63,36 @@ { border: 1px solid white; cursor: default; - height: 23px; + height: 22px; } + .TDatePicker th { width: 28px; } + +.TDatePicker .calendarBody +{ + text-align: center; + width: 210px; + margin: 3px 6px; +} + .TDatePicker .grid { border-spacing: 0px; - margin: 3px; } + .TDatePicker .calendarFooter { margin: 2px; border-top: 1px solid #919EA9; padding-top: 2px; } -.TDatePicker .calendarFooter button +.TDatePicker .todayButton { - font-size: 12px; + font-size: 11px; margin: 4px; + padding-left: 1em; + padding-right: 1em; } \ No newline at end of file diff --git a/framework/Web/Javascripts/datepicker/spacer.gif b/framework/Web/Javascripts/datepicker/spacer.gif new file mode 100755 index 00000000..fc256098 Binary files /dev/null and b/framework/Web/Javascripts/datepicker/spacer.gif differ diff --git a/framework/Web/Javascripts/extended/event.js b/framework/Web/Javascripts/extended/event.js index 29a8d5aa..4fd041d8 100644 --- a/framework/Web/Javascripts/extended/event.js +++ b/framework/Web/Javascripts/extended/event.js @@ -64,6 +64,7 @@ Object.extend(Event, * element. Only HTMLEvent and MouseEvent can be * dispatched, keyboard events or UIEvent can not be dispatch * via javascript consistently. + * For the "submit" event the submit() method is called. * @param {Object} element id string or a DOM element. * @param {String} event type to dispatch. */ @@ -86,12 +87,6 @@ Object.extend(Event, document.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null); } - else - { - if(typeof(Logger) != "undefined") - Logger.error("undefined event", type); - return; - } element.dispatchEvent(event); } else if(document.createEventObject) diff --git a/framework/Web/Javascripts/js/colorpicker.js b/framework/Web/Javascripts/js/colorpicker.js index 5bfab96d..47949239 100644 --- a/framework/Web/Javascripts/js/colorpicker.js +++ b/framework/Web/Javascripts/js/colorpicker.js @@ -34,7 +34,7 @@ Event.observe(this.button,"click",this._buttonOnClick);Event.observe(this.input, {var constructor=mode=="Basic"?"getBasicPickerContainer":"getFullPickerContainer" this.element=this[constructor](this.options['ID'],this.options['Palette']) document.body.appendChild(this.element);this.element.style.display="none";if(Prado.Browser().ie) -{this.iePopUp=document.createElement('iframe');this.iePopUp.src="";this.iePopUp.style.position="absolute" +{this.iePopUp=document.createElement('iframe');this.iePopUp.src=Prado.WebUI.TColorPicker.UIImages['button.gif'];this.iePopUp.style.position="absolute" this.iePopUp.scrolling="no" this.iePopUp.frameBorder="0" this.input.parentNode.appendChild(this.iePopUp);} diff --git a/framework/Web/Javascripts/js/datepicker.js b/framework/Web/Javascripts/js/datepicker.js index 6a28c061..a81bbeb4 100644 --- a/framework/Web/Javascripts/js/datepicker.js +++ b/framework/Web/Javascripts/js/datepicker.js @@ -8,16 +8,18 @@ Prado.WebUI.TDatePicker=Class.create();Object.extend(Prado.WebUI.TDatePicker,{ge {this.trigger=$(this.options.Trigger);var triggerEvent=this.options.TriggerEvent||"click";} else {this.trigger=this.control;var triggerEvent=this.options.TriggerEvent||"focus";} -Object.extend(this,options);Event.observe(this.trigger,triggerEvent,this.show.bindEvent(this));this.create();},create:function() -{var div;var table;var tbody;var tr;var td;this._calDiv=document.createElement("div");this._calDiv.className=this.ClassName;this._calDiv.style.display="none";this._calDiv.style.position="absolute" -div=document.createElement("div");div.className="calendarHeader";this._calDiv.appendChild(div);table=document.createElement("table");table.style.cellSpacing=0;div.appendChild(table);tbody=document.createElement("tbody");table.appendChild(tbody);tr=document.createElement("tr");tbody.appendChild(tr);td=document.createElement("td");var previousMonth=document.createElement("button");previousMonth.className="prevMonthButton";previousMonth.appendChild(document.createTextNode("<<"));td.appendChild(previousMonth);tr.appendChild(td);td=document.createElement("td");tr.appendChild(td);this._monthSelect=document.createElement("select");this._monthSelect.className="months";for(var i=0;i>"));td.appendChild(nextMonth);tr.appendChild(td);div=document.createElement("div");div.className="calendarBody";this._calDiv.appendChild(div);var calendarBody=div;var text;table=document.createElement("table");table.className="grid";div.appendChild(table);var thead=document.createElement("thead");table.appendChild(thead);tr=document.createElement("tr");thead.appendChild(tr);for(i=0;i<7;++i){td=document.createElement("th");text=document.createTextNode(this.ShortWeekDayNames[(i+this.FirstDayOfWeek)%7]);td.appendChild(text);td.className="weekDayHead";tr.appendChild(td);} +td.appendChild(this._yearSelect);td=document.createElement("td");var nextMonth=document.createElement("input");nextMonth.className="nextMonthButton button";nextMonth.type="button";nextMonth.value=">>";td.appendChild(nextMonth);tr.appendChild(td);div=document.createElement("div");div.className="calendarBody";this._calDiv.appendChild(div);var calendarBody=div;var text;table=document.createElement("table");table.align="center";table.className="grid";div.appendChild(table);var thead=document.createElement("thead");table.appendChild(thead);tr=document.createElement("tr");thead.appendChild(tr);for(i=0;i<7;++i){td=document.createElement("th");text=document.createTextNode(this.ShortWeekDayNames[(i+this.FirstDayOfWeek)%7]);td.appendChild(text);td.className="weekDayHead";tr.appendChild(td);} tbody=document.createElement("tbody");table.appendChild(tbody);for(week=0;week<6;++week){tr=document.createElement("tr");tbody.appendChild(tr);for(day=0;day<7;++day){td=document.createElement("td");td.className="calendarDate";text=document.createTextNode(String.fromCharCode(160));td.appendChild(text);tr.appendChild(td);var tmp=new Object();tmp.tag="DATE";tmp.value=-1;tmp.data=text;this.dateSlot[(week*7)+day]=tmp;Event.observe(td,"mouseover",this.hover.bindEvent(this));Event.observe(td,"mouseout",this.hover.bindEvent(this));}} -div=document.createElement("div");div.className="calendarFooter";this._calDiv.appendChild(div);var todayButton=document.createElement("button");todayButton.className="todayButton";var today=this.newDate();var buttonText=today.SimpleFormat(this.Format,this);todayButton.appendChild(document.createTextNode(buttonText));div.appendChild(todayButton);if(Prado.Browser().ie) -{this.iePopUp=document.createElement('iframe');this.iePopUp.src="";this.iePopUp.style.position="absolute" +div=document.createElement("div");div.className="calendarFooter";this._calDiv.appendChild(div);var todayButton=document.createElement("input");todayButton.type="button";todayButton.className="todayButton";var today=this.newDate();var buttonText=today.SimpleFormat(this.Format,this);todayButton.value=buttonText;div.appendChild(todayButton);if(Prado.Browser().ie) +{this.iePopUp=document.createElement('iframe');this.iePopUp.src=Prado.WebUI.TDatePicker.spacer;this.iePopUp.style.position="absolute" this.iePopUp.scrolling="no" this.iePopUp.frameBorder="0" document.body.appendChild(this.iePopUp);} @@ -87,7 +89,7 @@ this.onChange();},getElement:function() {var d=this.newDate(this.selectedDate);d.setMonth(month);this.setSelectedDate(d);},nextMonth:function() {this.setMonth(this.selectedDate.getMonth()+1);},prevMonth:function() {this.setMonth(this.selectedDate.getMonth()-1);},show:function() -{if(!this.showing) +{this.create();if(!this.showing) {var pos=Position.cumulativeOffset(this.control);if(this.options.InputMode=="TextBox") pos[1]+=this.control.offsetHeight;else {var dayList=Prado.WebUI.TDatePicker.getDayListControl(this.control);if(dayList) diff --git a/framework/Web/Javascripts/js/prado.js b/framework/Web/Javascripts/js/prado.js index c09cf64c..aa02e98e 100644 --- a/framework/Web/Javascripts/js/prado.js +++ b/framework/Web/Javascripts/js/prado.js @@ -129,9 +129,6 @@ return element.submit();if(document.createEvent) {var event=document.createEvent('HTMLEvents');event.initEvent(type,true,true);} else if(Event.isMouseEvent(type)) {var event=document.createEvent('MouseEvents');event.initMouseEvent(type,true,true,document.defaultView,1,0,0,0,0,false,false,false,false,0,null);} -else -{if(typeof(Logger)!="undefined") -Logger.error("undefined event",type);return;} element.dispatchEvent(event);} else if(document.createEventObject) {var evObj=document.createEventObject();element.fireEvent('on'+type,evObj);} diff --git a/framework/Web/Javascripts/prado/validation3.js b/framework/Web/Javascripts/prado/validation3.js index ad55f96b..6285dd52 100644 --- a/framework/Web/Javascripts/prado/validation3.js +++ b/framework/Web/Javascripts/prado/validation3.js @@ -763,7 +763,9 @@ Prado.WebUI.TBaseValidator.prototype = return value; }, - /** + /** + * The ControlType property comes from TBaseValidator::getClientControlClass() + * Be sure to update the TBaseValidator::$_clientClass if new cases are added. * @return mixed control value to validate */ getValidationValue : function(control) diff --git a/framework/Web/UI/TThemeManager.php b/framework/Web/UI/TThemeManager.php index 6a908759..fcb20a81 100644 --- a/framework/Web/UI/TThemeManager.php +++ b/framework/Web/UI/TThemeManager.php @@ -78,6 +78,23 @@ class TThemeManager extends TModule } + /** + * @return array list of available theme names + */ + public function getAvailableThemes() + { + $themes=array(); + $basePath=$this->getBasePath(); + $folder=@opendir($basePath); + while($file=@readdir($folder)) + { + if($file!=='.' && $file!=='..' && $file!=='.svn' && is_dir($basePath.'/'.$file)) + $themes[]=$file; + } + closedir($folder); + return $themes; + } + /** * @return string the base path for all themes. It is returned as an absolute path. * @throws TConfigurationException if base path is not set and "themes" directory does not exist. diff --git a/framework/Web/UI/WebControls/TBaseValidator.php b/framework/Web/UI/WebControls/TBaseValidator.php index ffdfd057..3c76db30 100644 --- a/framework/Web/UI/WebControls/TBaseValidator.php +++ b/framework/Web/UI/WebControls/TBaseValidator.php @@ -83,6 +83,12 @@ abstract class TBaseValidator extends TLabel implements IValidator * @var TClientSideValidatorOptions validator client-script options. */ private $_clientSide; + /** + * Controls for which the client-side validation3.js file needs to handle + * them specially. + * @var array list of control class names + */ + private static $_clientClass = array('THtmlArea', 'TDatePicker'); /** * Constructor. @@ -152,28 +158,44 @@ abstract class TBaseValidator extends TLabel implements IValidator $options['ValidationGroup'] = $this->getValidationGroup(); $options['ControlToValidate'] = $control->getClientID(); $options['ControlCssClass'] = $this->getControlCssClass(); - $options['ControlType'] = get_class($control); - + + $options['ControlType'] = $this->getClientControlClass($control); + if(!is_null($this->_clientSide)) $options = array_merge($options,$this->_clientSide->getOptions()->toArray()); - + return $options; } - + + /** + * Gets the Control type for client-side validation. If new cases exists in + * TBaseValidator::$_clientClass, be sure to update the corresponding + * "Javascript/validation3.js" file as well. + * @param TControl control to validate. + * @return string control type for client-side validation. + */ + private function getClientControlClass($control) + { + foreach(self::$_clientClass as $type) + if($control instanceof $type) + return $type; + return get_class($control); + } + /** * Gets the TClientSideValidatorOptions that allows modification of the client- - * side validator events. - * + * side validator events. + * * The client-side validator supports the following events. * # OnValidate -- raised before client-side validation is - * executed. + * executed. * # OnSuccess -- raised after client-side validation is completed * and is successfull, overrides default validator error messages updates. * # OnError -- raised after client-side validation is completed - * and failed, overrides default validator error message updates. - * + * and failed, overrides default validator error message updates. + * * You can attach custom javascript code to each of these events - * + * * @return TClientSideValidatorOptions javascript validator event options. */ public function getClientSide() @@ -182,7 +204,7 @@ abstract class TBaseValidator extends TLabel implements IValidator $this->_clientSide = $this->createClientSideOptions(); return $this->_clientSide; } - + /** * @return TClientSideValidatorOptions javascript validator event options. */ @@ -206,7 +228,7 @@ abstract class TBaseValidator extends TLabel implements IValidator if($this->getEnableClientScript() && !$scripts->isEndScriptRegistered($scriptKey)) { $manager['FormID'] = $formID; - $options = TJavaScript::encode($manager); + $options = TJavaScript::encode($manager); $scripts->registerPradoScript('validator'); $scripts->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});"); } @@ -493,22 +515,22 @@ abstract class TBaseValidator extends TLabel implements IValidator /** * TClientSideValidatorOptions class. - * + * * Client-side validator events can be modified through the {@link * TBaseValidator::getClientSide ClientSide} property of a validator. The * subproperties of ClientSide are those of the TClientSideValidatorOptions * properties. The client-side validator supports the following events. - * + * * The OnValidate event is raise before the validator validation * functions are called. - * + * * The OnSuccess event is raised after the validator has successfully * validate the control. - * + * * The OnError event is raised after the validator fails validation. - * + * * See the quickstart documentation for further details. - * + * * @author Wei Zhuo * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls @@ -521,19 +543,19 @@ class TClientSideValidatorOptions extends TClientSideOptions */ public function getOnValidate() { - return $this->getOption('OnValidate'); + return $this->getOption('OnValidate'); } - + /** * Client-side OnValidate validator event is raise before the validators - * validation functions are called. + * validation functions are called. * @param string javascript code for client-side OnValidate event. */ public function setOnValidate($javascript) { $this->setFunction('OnValidate', $javascript); } - + /** * Client-side OnSuccess event is raise after validation is successfull. * This will override the default client-side validator behaviour. @@ -543,7 +565,7 @@ class TClientSideValidatorOptions extends TClientSideOptions { $this->setFunction('OnSuccess', $javascript); } - + /** * @return string javascript code for client-side OnSuccess event. */ @@ -551,7 +573,7 @@ class TClientSideValidatorOptions extends TClientSideOptions { return $this->getOption('OnSuccess'); } - + /** * Client-side OnError event is raised after validation failure. * This will override the default client-side validator behaviour. @@ -561,7 +583,7 @@ class TClientSideValidatorOptions extends TClientSideOptions { $this->setFunction('OnError', $javascript); } - + /** * @return string javascript code for client-side OnError event. */ @@ -569,7 +591,7 @@ class TClientSideValidatorOptions extends TClientSideOptions { return $this->getOption('OnError'); } - + /** * Ensure the string is a valid javascript function. If the string begins * with "javascript:" valid javascript function is assumed, otherwise the @@ -583,4 +605,4 @@ class TClientSideValidatorOptions extends TClientSideOptions } } -?> \ No newline at end of file +?> diff --git a/framework/Web/UI/WebControls/TDatePicker.php b/framework/Web/UI/WebControls/TDatePicker.php index 59a71c90..a746437b 100644 --- a/framework/Web/UI/WebControls/TDatePicker.php +++ b/framework/Web/UI/WebControls/TDatePicker.php @@ -681,6 +681,18 @@ class TDatePicker extends TTextBox else throw new TConfigurationException('datepicker_calendarstyle_invalid',$style); } + + /** + * Publish the spacer.gif for IE iframe source. + * @return string the URL for the spacer.gif. + */ + protected function publishIFrameSpacer() + { + $cs = $this->getPage()->getClientScript(); + $spacer = 'System.Web.Javascripts.datepicker.spacer'; + if(($file = Prado::getPathOfNamespace($spacer,'.gif')) != null) + return $this->publishFilePath($file); + } /** * Add the client id to the input textbox, and register the client scripts. @@ -696,18 +708,24 @@ class TDatePicker extends TTextBox /** * Registers the javascript code to initialize the date picker. - * Must use "Event.OnLoad" to initialize the date picker when the - * full page is loaded, otherwise IE will throw an error. */ protected function registerCalendarClientScript() { if($this->getShowCalendar()) { - $scripts = $this->getPage()->getClientScript(); - $scripts->registerPradoScript("datepicker"); + $cs = $this->getPage()->getClientScript(); + $cs->registerPradoScript("datepicker"); + + if(!$cs->isEndScriptRegistered('TDatePicker.spacer')) + { + $spacer = $this->publishIFrameSpacer(); + $code = "Prado.WebUI.TDatePicker.spacer = '$spacer';"; + $cs->registerEndScript('TDatePicker.spacer', $code); + } + $options = TJavaScript::encode($this->getDatePickerOptions()); - $code = "Event.OnLoad(function(){ new Prado.WebUI.TDatePicker($options); });"; - $scripts->registerEndScript("prado:".$this->getClientID(), $code); + $code = "new Prado.WebUI.TDatePicker($options);"; + $cs->registerEndScript("prado:".$this->getClientID(), $code); } } } diff --git a/framework/Web/UI/WebControls/TListControl.php b/framework/Web/UI/WebControls/TListControl.php index 57d8563a..42f09aad 100644 --- a/framework/Web/UI/WebControls/TListControl.php +++ b/framework/Web/UI/WebControls/TListControl.php @@ -502,6 +502,45 @@ abstract class TListControl extends TDataBoundControl $this->_cachedSelectedValue=$value; } + + /** + * @return array list of the selected item values (strings) + */ + public function getSelectedValues() + { + $values=array(); + if($this->_items) + { + foreach($this->_items as $item) + { + if($item->getSelected()) + $values[]=$item->getValue(); + } + } + return $values; + } + + /** + * @param array list of the selected item values + */ + public function setSelectedValues($values) + { + if($this->_items) + { + $this->clearSelection(); + $lookup=array(); + foreach($this->_items as $item) + $lookup[$item->getValue()]=$item; + foreach($values as $value) + { + if(isset($lookup["$value"])) + $lookup["$value"]->setSelected(true); + else + throw new TInvalidDataValueException('listcontrol_selectedvalue_invalid',get_class($this),$value); + } + } + } + /** * @return string selected value */ diff --git a/tests/FunctionalTests/tickets/protected/pages/TestHtmlArea.php b/tests/FunctionalTests/tickets/protected/pages/TestHtmlArea.php new file mode 100644 index 00000000..13a4189b --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/TestHtmlArea.php @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket121.page b/tests/FunctionalTests/tickets/protected/pages/Ticket121.page new file mode 100644 index 00000000..9c9633d3 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket121.page @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket121.php b/tests/FunctionalTests/tickets/protected/pages/Ticket121.php new file mode 100644 index 00000000..1a625d83 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket121.php @@ -0,0 +1,11 @@ +Result->Text="clicked at ({$param->X},{$param->Y})"; + } +} + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket163.page b/tests/FunctionalTests/tickets/protected/pages/Ticket163.page new file mode 100644 index 00000000..aa05e601 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket163.page @@ -0,0 +1,9 @@ + +

Test Ticket #163

+

Missing currency sign in TNumberFormat if Value is 0

+ +
+ +
+ +
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket169.page b/tests/FunctionalTests/tickets/protected/pages/Ticket169.page new file mode 100644 index 00000000..24c447d5 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket169.page @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket191.page b/tests/FunctionalTests/tickets/protected/pages/Ticket191.page new file mode 100644 index 00000000..2d673b8d --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket191.page @@ -0,0 +1,19 @@ + + +Global state: + + + +
+ +Your input: + + + +
+ +
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket191.php b/tests/FunctionalTests/tickets/protected/pages/Ticket191.php new file mode 100644 index 00000000..61c2a253 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket191.php @@ -0,0 +1,22 @@ +IsValid) + $this->Application->clearGlobalState('ticket190'); + } + + public function customValidation($sender,$param) + { + $param->IsValid=$this->Application->getGlobalState('ticket190')===$this->TextBox->Text; + } + + public function updateGlobal($sender,$param) + { + $this->Application->setGlobalState('ticket190',$this->TextBox2->Text); + } +} + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket28.page b/tests/FunctionalTests/tickets/protected/pages/Ticket28.page new file mode 100644 index 00000000..056e9985 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket28.page @@ -0,0 +1,6 @@ + + +

Safari LinkButton Test

+ + +
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket28.php b/tests/FunctionalTests/tickets/protected/pages/Ticket28.php new file mode 100644 index 00000000..7980119e --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket28.php @@ -0,0 +1,12 @@ +label1->setText("Link Button 1 Clicked!"); + } +} + + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket93.page b/tests/FunctionalTests/tickets/protected/pages/Ticket93.page new file mode 100644 index 00000000..2217ab15 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket93.page @@ -0,0 +1,61 @@ + + +

ValidationGroups without any inputs with grouping

+

This test can not be tested using automation.

+ AlternateText="Navigate buttons" OnClick="buttonClicked" > + + + + + + + + +
+ + + + + + + +
+ +
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket93.php b/tests/FunctionalTests/tickets/protected/pages/Ticket93.php new file mode 100644 index 00000000..dbdf2cab --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket93.php @@ -0,0 +1,14 @@ +PostBackValue; + } +} + +?> diff --git a/tests/FunctionalTests/tickets/protected/pages/config.xml b/tests/FunctionalTests/tickets/protected/pages/config.xml index 83bb5791..48a0114c 100644 --- a/tests/FunctionalTests/tickets/protected/pages/config.xml +++ b/tests/FunctionalTests/tickets/protected/pages/config.xml @@ -1,5 +1,8 @@ + + + \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected/pages/hotspot.jpg b/tests/FunctionalTests/tickets/protected/pages/hotspot.jpg new file mode 100644 index 00000000..3491813f Binary files /dev/null and b/tests/FunctionalTests/tickets/protected/pages/hotspot.jpg differ diff --git a/tests/FunctionalTests/tickets/tests/Ticket121TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket121TestCase.php new file mode 100644 index 00000000..7453aeef --- /dev/null +++ b/tests/FunctionalTests/tickets/tests/Ticket121TestCase.php @@ -0,0 +1,19 @@ +open('tickets/index.php?page=Ticket121'); + $this->type("ctl0\$Content\$FooTextBox", ""); + $this->verifyNotVisible('ctl0_Content_ctl1'); + $this->click("//input[@type='image' and @id='ctl0_Content_ctl0']", ""); + $this->verifyVisible('ctl0_Content_ctl1'); + $this->type("ctl0\$Content\$FooTextBox", "content"); + $this->clickAndWait("//input[@type='image' and @id='ctl0_Content_ctl0']", ""); + $this->verifyNotVisible('ctl0_Content_ctl1'); + $this->verifyTextPresent("clicked at", ""); + } +} + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/tests/Ticket163TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket163TestCase.php new file mode 100644 index 00000000..cc78c466 --- /dev/null +++ b/tests/FunctionalTests/tickets/tests/Ticket163TestCase.php @@ -0,0 +1,14 @@ +open('tickets/index.php?page=Ticket163'); + $this->assertTextPresent('kr 100,00'); + $this->assertTextPresent('kr 0,00'); + $this->assertTextPresent('-kr 100,00'); + } +} + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/tests/Ticket169TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket169TestCase.php new file mode 100644 index 00000000..30bbc92d --- /dev/null +++ b/tests/FunctionalTests/tickets/tests/Ticket169TestCase.php @@ -0,0 +1,14 @@ +open('tickets/index.php?page=Ticket169'); + $this->assertNotVisible('ctl0_Content_validator1'); + $this->click('ctl0_Content_ctl0'); + $this->assertVisible('ctl0_Content_validator1'); + } +} + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/tests/Ticket191TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket191TestCase.php new file mode 100644 index 00000000..cc4c1571 --- /dev/null +++ b/tests/FunctionalTests/tickets/tests/Ticket191TestCase.php @@ -0,0 +1,16 @@ +open('tickets/index.php?page=Ticket191'); + $this->type("ctl0\$Content\$TextBox2", "test"); + $this->clickAndWait("//input[@type='submit' and @name='ctl0\$Content\$ctl0']", ""); + $this->type("ctl0\$Content\$TextBox", "test"); + $this->clickAndWait("//input[@type='submit' and @name='ctl0\$Content\$ctl1']", ""); + $this->verifyNotVisible('ctl0_Content_ctl2'); + } +} + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/tests/Ticket28TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket28TestCase.php new file mode 100644 index 00000000..960dd2d8 --- /dev/null +++ b/tests/FunctionalTests/tickets/tests/Ticket28TestCase.php @@ -0,0 +1,15 @@ +open('tickets/index.php?page=Ticket28'); + $this->assertTextPresent('Label 1'); + $this->clickAndWait('link=Click Me'); + $this->assertTextPresent('Link Button 1 Clicked!'); + } +} + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/tickets/tests/Ticket93TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket93TestCase.php new file mode 100644 index 00000000..6a286ffe --- /dev/null +++ b/tests/FunctionalTests/tickets/tests/Ticket93TestCase.php @@ -0,0 +1,16 @@ +open('tickets/index.php?page=Ticket93'); + $this->verifyTextPresent("ValidationGroups without any inputs with grouping"); + } + +} +?> -- cgit v1.2.3