diff options
21 files changed, 1427 insertions, 1 deletions
| diff --git a/.gitattributes b/.gitattributes index 97551364..651baea6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1133,6 +1133,11 @@ demos/quickstart/protected/pages/Controls/Wizard.page -text  demos/quickstart/protected/pages/Controls/wizard.gif -text  demos/quickstart/protected/pages/Database/ActiveRecord.page -text  demos/quickstart/protected/pages/Database/DAO.page -text +demos/quickstart/protected/pages/Database/Samples/Scaffold/Home.page -text +demos/quickstart/protected/pages/Database/Samples/Scaffold/Home.php -text +demos/quickstart/protected/pages/Database/Samples/Scaffold/sqlite.db -text +demos/quickstart/protected/pages/Database/Samples/config.xml -text +demos/quickstart/protected/pages/Database/Scaffold.page -text  demos/quickstart/protected/pages/Database/SqlMap.page -text  demos/quickstart/protected/pages/Database/diagram.png -text  demos/quickstart/protected/pages/Database/object_states.png -text @@ -1441,6 +1446,19 @@ framework/Configuration/Provider/TProviderException.php -text  framework/Configuration/TProtectedConfiguration.php -text  framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php -text  framework/Data/ActiveRecord/Exceptions/messages.txt -text +framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php -text +framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php -text +framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php -text +framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php -text +framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php -text +framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php -text +framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php -text +framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl -text +framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php -text +framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl -text +framework/Data/ActiveRecord/Scaffold/TScaffoldView.php -text +framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl -text +framework/Data/ActiveRecord/Scaffold/style.css -text  framework/Data/ActiveRecord/TActiveRecord.php -text  framework/Data/ActiveRecord/TActiveRecordConfig.php -text  framework/Data/ActiveRecord/TActiveRecordCriteria.php -text diff --git a/demos/quickstart/protected/controls/TopicList.tpl b/demos/quickstart/protected/controls/TopicList.tpl index 698cbeea..8d68c2e2 100644 --- a/demos/quickstart/protected/controls/TopicList.tpl +++ b/demos/quickstart/protected/controls/TopicList.tpl @@ -72,6 +72,7 @@  <ul>
      <li><a href="?page=Database.DAO">Data Access Objects</a></li>
      <li><a href="?page=Database.ActiveRecord">Active Record</a></li>
 +    <li><a href="?page=Database.Scaffold">Active Record Scaffold</a></li>
      <li><a href="?page=Database.SqlMap">SqlMap Data Mapper</a></li>
  </ul>
  </div>
 diff --git a/demos/quickstart/protected/pages/Database/Samples/Scaffold/Home.page b/demos/quickstart/protected/pages/Database/Samples/Scaffold/Home.page new file mode 100644 index 00000000..21f508f7 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/Samples/Scaffold/Home.page @@ -0,0 +1,7 @@ +<com:TContent ID="body" >
 +
 +<h1>Active Record Scaffold Example</h1>
 +
 +<com:TScaffoldView RecordClass="AddressRecord" />
 +
 +</com:TContent>
 diff --git a/demos/quickstart/protected/pages/Database/Samples/Scaffold/Home.php b/demos/quickstart/protected/pages/Database/Samples/Scaffold/Home.php new file mode 100644 index 00000000..9bfe3f6d --- /dev/null +++ b/demos/quickstart/protected/pages/Database/Samples/Scaffold/Home.php @@ -0,0 +1,33 @@ +<?php
 +
 +Prado::using('System.Data.ActiveRecord.TActiveRecord');
 +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldView');
 +
 +class AddressRecord extends TActiveRecord
 +{
 +	public $id;
 +	public $username;
 +	public $phone;
 +
 +	public static $_tablename='addresses';
 +
 +	//for demo, we use static db here
 +	//otherwise we should use TActiveRecordConfig in application.xml
 +	private static $_db;
 +	public function getDbConnection()
 +	{
 +		if(self::$_db===null)
 +		{
 +			$file = dirname(__FILE__).'/sqlite.db';
 +			self::$_db = new TDbConnection("sqlite:{$file}");
 +		}
 +		return self::$_db;
 +	}
 +}
 +
 +class Home extends TPage
 +{
 +
 +}
 +
 +?>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/Database/Samples/Scaffold/sqlite.db b/demos/quickstart/protected/pages/Database/Samples/Scaffold/sqlite.dbBinary files differ new file mode 100644 index 00000000..65d77a66 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/Samples/Scaffold/sqlite.db diff --git a/demos/quickstart/protected/pages/Database/Samples/config.xml b/demos/quickstart/protected/pages/Database/Samples/config.xml new file mode 100644 index 00000000..51da9ed0 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/Samples/config.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?>
 +
 +<configuration>
 +  <pages MasterClass="SampleLayout" />
 +</configuration>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/Database/Scaffold.page b/demos/quickstart/protected/pages/Database/Scaffold.page new file mode 100644 index 00000000..36a0ec21 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/Scaffold.page @@ -0,0 +1,99 @@ +<com:TContent ID="body" >
 +<!-- $Id$ -->
 +<h1>Active Record Scaffold Views</h1>
 +<p><a href="?page=Database.ActiveRecord">Active Record</a> classes can be used together with 
 +<com:DocLink ClassPath="System.Data.ActiveRecord.Scaffold.TScaffoldListView" Text="TScaffoldListView"/>
 +and
 +<com:DocLink ClassPath="System.Data.ActiveRecord.Scaffold.TScaffoldEditView" Text="TScaffoldEditView"/>
 +( <com:DocLink ClassPath="System.Data.ActiveRecord.Scaffold.TScaffoldView" Text="TScaffoldView"/>
 +links both <tt>TScaffoldListView</tt> and <tt>TScaffoldEditView</tt>) to create 
 +<i>simple</i> Create/Read/Update/Delete (CRUD) web applications.</p>
 +
 +<p>The scaffold views are intended to assist in prototyping web application, 
 +they are not designed to be as customiziable as more complex components such as
 +<a href="?page=Controls.DataGrid">TDataGrid</a>. The scaffold views provide
 +the following builtin functionality:
 +</p>
 +
 +<ul>
 +	<li>Listing of all active record items.</li>
 +	<li>Paging and sorting.</li>
 +	<li>Deleting an item.</li>
 +	<li>Inserting a new item.</li>
 +	<li>Updating an existing item.</li>
 +	<li>Validates required fields and basic data types.</li>
 +	<li>Presents specialized controls such as date pickers.</li>
 +</ul>
 +
 +<p>Scaffold views are dependent on Active Records and currently supports
 +the following databases: Mysql, Sqlite and Postgres SQL. Support for other databases
 +can be considered when there are sufficient demand.</p>
 +
 +<h2>Setting up a Scaffold View</h2>
 +<p>To use the scaffold view, we first define an <a href="?page=Database.ActiveRecord">Active Record</a> 
 +class that represents a table or view in the database. Consider the following
 +Active Record class that corresponds to the <tt>users</tt>
 +table as defined in the <a href="?page=Database.ActiveRecord">Active Record</a> quickstart page.
 +</p>
 +
 +<com:TTextHighlighter Language="php" CssClass="source">
 +class UserRecord extends TActiveRecord
 +{
 +    public $username;
 +    public $email;
 +    
 +    public static $_tablename='users';
 +}
 +</com:TTextHighlighter>
 +
 +<p>The scaffold view classes are in the <tt>System.Data.ActiveRecord.Scaffold.*</tt>
 +<a href="?page=Fundamentals.Components#704">namespace</a>. 
 +This <a href="?page=Fundamentals.Components#704">namespace</a> can be "imported" in the 
 +<a href="?page=Configurations.AppConfig">Application Configuration</a>
 +using the <tt>application.xml</tt> file or through the php code using the <tt>Prado::using()</tt>
 +method. The simplest way to provide CRUD functional is to use the 
 +<com:DocLink ClassPath="System.Data.ActiveRecord.Scaffold.TScaffoldView" Text="TScaffoldView"/>
 +where the <tt>RecordClass</tt> property value equals to an Active Record
 +class name.
 +</p>
 +
 +<com:TTextHighlighter Language="prado" CssClass="source">
 +<com:TScaffoldView RecordClass="UserRecord" />
 +</com:TTextHighlighter>
 +
 +<h2>Todo...</h2>
 +
 +<p>Other views... list view</p>
 +
 +<com:TTextHighlighter Language="prado" CssClass="source">
 +<com:TScaffoldListView RecordClass="UserRecord" />
 +</com:TTextHighlighter>
 +
 +<p>edit view...</p>
 +<com:TTextHighlighter Language="prado" CssClass="source">
 +<com:TScaffoldEditView RecordPk="user1" RecordClass="UserRecord" />
 +</com:TTextHighlighter>
 +
 +<p>Combining list + edit views</p>
 +
 +<com:TTextHighlighter Language="prado" CssClass="source">
 +<com:TScaffoldEditView ID="edit_view" RecordClass="UserRecord" />
 +<com:TScaffoldListView EditViewID="edit_view" RecordClass="UserRecord" />
 +</com:TTextHighlighter>
 +
 +<p>custom list view...</p>
 +<com:TTextHighlighter Language="prado" CssClass="source">
 +<com:TScaffoldView RecordClass="UserRecord" >
 +    <prop:ListView.List.ItemTemplate>
 +        <%# $this->DataItem->username %>
 +        <com:TLinkButton Text="Edit" CommandName="edit" />
 +    </prop:ListView.List.ItemTemplate>
 +</com:TScaffoldView/>
 +</com:TTextHighlighter>
 +<p>To be completed...</p>
 +
 +<p>Address book example...</p>
 +
 +<com:RunBar PagePath="Database.Samples.Scaffold.Home" />
 +
 +<div class="last-modified">$Id$</div></com:TContent>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php new file mode 100644 index 00000000..cb56d598 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php @@ -0,0 +1,75 @@ +<?php
 +
 +Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputCommon');
 +
 +class MysqlScaffoldInput extends ScaffoldInputCommon
 +{
 +	protected function createControl($container, $column, $record)
 +	{
 +		switch(strtolower($column->getType()))
 +		{
 +			case 'date':
 +				return $this->createDateControl($container, $column, $record);
 +			case 'blob': case 'tinyblob': case 'mediumblob': case 'longblob':
 +			case 'text': case 'tinytext': case 'mediumtext': case 'longtext':
 +				return $this->createMultiLineControl($container, $column, $record);
 +			case 'year':
 +				return $this->createYearControl($container, $column, $record);
 +			case 'int': case 'integer': case 'tinyint': case 'smallint': case 'mediumint': case 'bigint':
 +				return $this->createIntegerControl($container, $column, $record);
 +			case 'decimal': case 'double': case 'float':
 +				return $this->createFloatControl($container, $column, $record);
 +			case 'time' :
 +				return $this->createTimeControl($container, $column, $record);
 +			case 'datetime': case 'timestamp':
 +				return $this->createDateTimeControl($container, $column, $record);
 +			case 'set':
 +				return $this->createSetControl($container, $column, $record);
 +			case 'enum':
 +				return $this->createEnumControl($container, $column, $record);
 +			default:
 +				return $this->createDefaultControl($container, $column, $record);
 +		}
 +	}
 +
 +	protected function getControlValue($container, $column, $record)
 +	{
 +		switch(strtolower($column->getType()))
 +		{
 +			case 'date':
 +				return $container->findControl(self::DEFAULT_ID)->getDate();
 +			case 'year':
 +				return $container->findControl(self::DEFAULT_ID)->getSelectedValue();
 +			case 'time':
 +				return $this->getTimeValue($container, $column, $record);
 +			case 'datetime': case 'timestamp':
 +				return $this->getDateTimeValue($container,$column, $record);
 +			case 'tinyint':
 +				return $this->getIntBooleanValue($container,$column, $record);
 +			case 'set':
 +				return $this->getSetValue($container, $column, $record);
 +			case 'enum':
 +				return $this->getEnumValue($container, $column, $record);
 +			default:
 +				return $this->getDefaultControlValue($container,$column, $record);
 +		}
 +	}
 +
 +	protected function createIntegerControl($container, $column, $record)
 +	{
 +		if($column->getLength()==1)
 +			return $this->createBooleanControl($container, $column, $record);
 +		else
 +			parent::createIntegerControl($container, $column, $record);
 +	}
 +
 +	protected function getIntBooleanValue($container,$column, $record)
 +	{
 +		if($column->getLength()==1)
 +			return (int)$container->findControl(self::DEFAULT_ID)->getChecked();
 +		else
 +			return $this->getDefaultControlValue($container,$column, $record);
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php new file mode 100644 index 00000000..40a14fbc --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php @@ -0,0 +1,48 @@ +<?php
 +
 +Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputCommon');
 +
 +class PgsqlScaffoldInput extends ScaffoldInputCommon
 +{
 +	protected function createControl($container, $column, $record)
 +	{
 +		switch(strtolower($column->getType()))
 +		{
 +			case 'boolean':
 +				return $this->createBooleanControl($container, $column, $record);
 +			case 'date':
 +				return $this->createDateControl($container, $column, $record);
 +			case 'text':
 +				return $this->createMultiLineControl($container, $column, $record);
 +			case 'smallint': case 'integer': case 'bigint':
 +				return $this->createIntegerControl($container, $column, $record);
 +			case 'decimal': case 'numeric': case 'real': case 'double precision':
 +				return $this->createFloatControl($container, $column, $record);
 +			case 'time without time zone' :
 +				return $this->createTimeControl($container, $column, $record);
 +			case 'timestamp without time zone':
 +				return $this->createDateTimeControl($container, $column, $record);
 +			default:
 +				return $this->createDefaultControl($container,$column, $record);
 +		}
 +	}
 +
 +	protected function getControlValue($container, $column, $record)
 +	{
 +		switch(strtolower($column->getType()))
 +		{
 +			case 'boolean':
 +				return $container->findControl(self::DEFAULT_ID)->getChecked();
 +			case 'date':
 +				return $container->findControl(self::DEFAULT_ID)->getDate();
 +			case 'time without time zone':
 +				return $this->getTimeValue($container, $column, $record);
 +			case 'timestamp without time zone':
 +				return $this->getDateTimeValue($container,$column, $record);
 +			default:
 +				return $this->getDefaultControlValue($container,$column, $record);
 +		}
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php new file mode 100644 index 00000000..c06f2fb6 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php @@ -0,0 +1,87 @@ +<?php
 +
 +class TScaffoldInputBase
 +{
 +	const DEFAULT_ID = 'scaffold_input';
 +	private $_parent;
 +
 +	protected function getParent()
 +	{
 +		return $this->_parent;
 +	}
 +
 +	public static function createInputBuilder($record)
 +	{
 +		$record->getDbConnection()->setActive(true); //must be connected before retrieving driver name!
 +		$driver = $record->getDbConnection()->getDriverName();
 +		switch(strtolower($driver))
 +		{
 +			case 'sqlite': //sqlite 3
 +			case 'sqlite2': //sqlite 2
 +				require_once(dirname(__FILE__).'/TSqliteScaffoldInput.php');
 +				return new TSqliteScaffoldInput($conn);
 +			case 'mysqli':
 +			case 'mysql':
 +				require_once(dirname(__FILE__).'/TMysqlScaffoldInput.php');
 +				return new TMysqlScaffoldInput($conn);
 +			case 'pgsql':
 +				require_once(dirname(__FILE__).'/TPgsqlScaffoldInput.php');
 +				return new TPgsqlScaffoldInput($conn);
 +			default:
 +				throw new TConfigurationException(
 +					'scaffold_invalid_database_driver',$driver);
 +		}
 +	}
 +
 +	public function createScaffoldInput($parent, $item, $column, $record)
 +	{
 +		$this->_parent=$parent;
 +		$item->setCustomData($column->getProperty());
 +		$this->createControl($item->_input, $column, $record);
 +		if($item->_input->findControl(self::DEFAULT_ID))
 +			$this->createControlLabel($item->_label, $column, $record);
 +	}
 +
 +	protected function createControlLabel($label, $column, $record)
 +	{
 +		$fieldname = ucwords(str_replace('_', ' ', $column->getProperty())).':';
 +		$label->setText($fieldname);
 +		$label->setForControl(self::DEFAULT_ID);
 +	}
 +
 +	public function loadScaffoldInput($parent, $item, $column, $record)
 +	{
 +		$this->_parent=$parent;
 +		if($this->getIsEnabled($column, $record))
 +		{
 +			$prop = $column->getProperty();
 +			$record->{$prop} = $this->getControlValue($item->_input, $column, $record);
 +		}
 +	}
 +
 +	protected function getIsEnabled($column, $record)
 +	{
 +		return !($this->getParent()->getRecordPk() !== null
 +				&& $column->getIsPrimaryKey() || $column->hasSequence());
 +	}
 +
 +	protected function getRecordPropertyValue($column, $record)
 +	{
 +		return $record->{$column->getProperty()};
 +	}
 +
 +	protected function setRecordPropertyValue($item, $record, $input)
 +	{
 +		$record->{$item->getCustomData()} = $input->getText();
 +	}
 +
 +	protected function createControl($container, $column, $record)
 +	{
 +	}
 +
 +	protected function getControlValue($container, $column, $record)
 +	{
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php new file mode 100644 index 00000000..049ceb1f --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php @@ -0,0 +1,280 @@ +<?php
 +
 +class TScaffoldInputCommon extends TScaffoldInputBase
 +{
 +	protected function setDefaultProperty($container, $control, $column, $record)
 +	{
 +		$control->setID(self::DEFAULT_ID);
 +		$control->setEnabled($this->getIsEnabled($column, $record));
 +		$container->Controls[] = $control;
 +	}
 +
 +	protected function setNotNullProperty($container, $control, $column, $record)
 +	{
 +		$this->setDefaultProperty($container, $control, $column, $record);
 +		if($column->getNotNull() && !$column->hasSequence())
 +			$this->createRequiredValidator($container, $column, $record);
 +	}
 +
 +	protected function createBooleanControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$control = new TCheckBox();
 +		$control->setChecked(TPropertyValue::ensureBoolean($value));
 +		$control->setCssClass('boolean-checkbox');
 +		$this->setDefaultProperty($container, $control, $column, $record);
 +		return $control;
 +	}
 +
 +	protected function createDefaultControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$control = new TTextBox();
 +		$control->setText($value);
 +		$control->setCssClass('default-textbox scaffold_input');
 +		if(($len=$column->getLength())!==null)
 +			$control->setMaxLength($len);
 +		$this->setNotNullProperty($container, $control, $column, $record);
 +		return $control;
 +	}
 +
 +	protected function getDefaultControlValue($container,$column, $record)
 +	{
 +		$control = $container->findControl(self::DEFAULT_ID);
 +		if($control instanceof TCheckBox)
 +			return $control->getChecked();
 +		else if($control instanceof TControl)
 +			return $control->getText();
 +	}
 +
 +	protected function createMultiLineControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$control = new TTextBox();
 +		$control->setText($value);
 +		$control->setTextMode(TTextBoxMode::MultiLine);
 +		$control->setCssClass('multiline-textbox scaffold_input');
 +		$this->setNotNullProperty($container, $control, $column, $record);
 +		return $control;
 +	}
 +
 +	protected function createYearControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$control = new TDropDownList();
 +		$years = array();
 +		$current = intval(@date('Y'));
 +		$from = $current-10; $to=$current+10;
 +		for($i = $from; $i <= $to; $i++)
 +			$years[$i] = $i;
 +		$control->setDataSource($years);
 +		$control->setSelectedValue(empty($value) ? $current : $value);
 +		$control->setCssClass('year-dropdown');
 +		$this->setNotNullProperty($container, $control, $column, $record);
 +		return $control;
 +	}
 +
 +	protected function createIntegerControl($container, $column, $record)
 +	{
 +		$control = $this->createDefaultControl($container, $column, $record);
 +		$val = $this->createTypeValidator($container, $column, $record);
 +		$val->setDataType(TValidationDataType::Integer);
 +		$val->setErrorMessage('Please entery an integer.');
 +		return $control;
 +	}
 +
 +	protected function createFloatControl($container, $column, $record)
 +	{
 +		$control = $this->createDefaultControl($container, $column, $record);
 +		$val = $this->createTypeValidator($container, $column, $record);
 +		$val->setDataType(TValidationDataType::Float);
 +		$val->setErrorMessage('Please entery a decimal number.');
 +		return $control;
 +	}
 +
 +	protected function createRequiredValidator($container, $column, $record)
 +	{
 +		$val = new TRequiredFieldValidator();
 +		$val->setErrorMessage('*');
 +		$val->setControlCssClass('required-input');
 +		$val->setCssClass('required');
 +		$val->setControlToValidate(self::DEFAULT_ID);
 +		$val->setValidationGroup($this->getParent()->getValidationGroup());
 +		$val->setDisplay(TValidatorDisplayStyle::Dynamic);
 +		$container->Controls[] = $val;
 +		return $val;
 +	}
 +
 +	protected function createTypeValidator($container, $column, $record)
 +	{
 +		$val = new TDataTypeValidator();
 +		$val->setControlCssClass('required-input2');
 +		$val->setCssClass('required');
 +		$val->setControlToValidate(self::DEFAULT_ID);
 +		$val->setValidationGroup($this->getParent()->getValidationGroup());
 +		$val->setDisplay(TValidatorDisplayStyle::Dynamic);
 +		$container->Controls[] = $val;
 +		return $val;
 +	}
 +
 +	protected function createTimeControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$hours=array();
 +		for($i=0;$i<24;$i++) $hours[] = str_pad($i,2,'0',STR_PAD_LEFT);
 +		$mins=array();
 +		for($i=0;$i<60;$i++) $mins[] = str_pad($i,2,'0',STR_PAD_LEFT);
 +		$hour = intval(@date('H'));
 +		$min = intval(@date('i'));
 +		$sec = intval(@date('s'));
 +		if(!empty($value))
 +		{
 +			$match=array();
 +			if(preg_match('/(\d+):(\d+):?(\d+)?/', $value, $match))
 +			{
 +				$hour = $match[1];
 +				$min = $match[2];
 +				if(isset($match[3]))
 +					$sec=$match[3];
 +			}
 +		}
 +
 +		$hcontrol = new TDropDownList();
 +		$hcontrol->setDataSource($hours);
 +		$hcontrol->setID(self::DEFAULT_ID);
 +		$hcontrol->dataBind();
 +		$hcontrol->setSelectedValue(intval($hour));
 +		$container->Controls[] = $hcontrol;
 +		$container->Controls[] = ' : ';
 +
 +		$mcontrol = new TDropDownList();
 +		$mcontrol->setDataSource($mins);
 +		$mcontrol->dataBind();
 +		$mcontrol->setID('scaffold_time_min');
 +		$mcontrol->setSelectedValue(intval($min));
 +		$container->Controls[] = $mcontrol;
 +		$container->Controls[] = ' : ';
 +
 +		$scontrol = new TDropDownList();
 +		$scontrol->setDataSource($mins);
 +		$scontrol->dataBind();
 +		$scontrol->setID('scaffold_time_sec');
 +		$scontrol->setSelectedValue(intval($sec));
 +		$container->Controls[] = $scontrol;
 +
 +		return array($hcontrol,$mcontrol,$scontrol);
 +	}
 +
 +
 +	protected function createDateControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$control = new TDatePicker();
 +		$control->setInputMode(TDatePickerInputMode::DropDownList);
 +		$control->setDateFormat('yyyy-MM-dd');
 +		if(!empty($value))
 +			$control->setDate(substr($value,0,10));
 +		$control->setCssClass('date-dropdown');
 +		$this->setNotNullProperty($container, $control, $column, $record);
 +		return $control;
 +	}
 +
 +	protected function createDateTimeControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$control = $this->createDateControl($container, $column, $record);
 +		$container->Controls[] = ' @ ';
 +		$time = $this->createTimeControl($container, $column, $record);
 +		if(!empty($value))
 +		{
 +			$match=array();
 +			if(preg_match('/(\d+):(\d+):?(\d+)?/', substr($value, 11), $match))
 +			{
 +				$time[0]->setSelectedValue(intval($match[1]));
 +				$time[1]->setSelectedValue(intval($match[2]));
 +				if(isset($match[3]))
 +					$time[2]->setSelectedValue(intval($match[3]));
 +			}
 +		}
 +		$time[0]->setID('scaffold_time_hour');
 +		return array($control, $time[0], $time[1], $time[2]);
 +	}
 +
 +	protected function getDateTimeValue($container, $column, $record)
 +	{
 +		$date = $container->findControl(self::DEFAULT_ID)->getDate();
 +		$hour = $container->findControl('scaffold_time_hour')->getSelectedValue();
 +		$mins = $container->findControl('scaffold_time_min')->getSelectedValue();
 +		$secs = $container->findControl('scaffold_time_sec')->getSelectedValue();
 +		return "{$date} {$hour}:{$mins}:{$secs}";
 +	}
 +
 +	protected function getTimeValue($container, $column, $record)
 +	{
 +		$hour = $container->findControl(self::DEFAULT_ID)->getSelectedValue();
 +		$mins = $container->findControl('scaffold_time_min')->getSelectedValue();
 +		$secs = $container->findControl('scaffold_time_sec')->getSelectedValue();
 +		return "{$hour}:{$mins}:{$secs}";
 +	}
 +
 +	protected function createSetControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$selectedValues = preg_split('/\s*,\s*/', $value);
 +		$control = new TCheckBoxList();
 +		$values = $column->getTypeValues();
 +		$control->setDataSource($values);
 +		$control->dataBind();
 +		$control->setSelectedIndices($this->getMatchingIndices($selectedValues, $values));
 +		$control->setID(self::DEFAULT_ID);
 +		$control->setCssClass('set-checkboxes');
 +		$this->setNotNullProperty($container, $control, $column, $record);
 +		return $control;
 +	}
 +
 +	protected function getMatchingIndices($checks, $values)
 +	{
 +		$index=array();
 +		for($i=0, $k=count($checks); $i<$k; $i++)
 +		{
 +			if(in_array($checks[$i], $values))
 +				$index[] = $i;
 +		}
 +		return $index;
 +	}
 +
 +	protected function createEnumControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$selectedValues = preg_split('/\s*,\s*/', $value);
 +		$control = new TRadioButtonList();
 +		$values = $column->getTypeValues();
 +		$control->setDataSource($values);
 +		$control->dataBind();
 +		$index = $this->getMatchingIndices($selectedValues, $values);
 +		if(count($index) > 0)
 +			$control->setSelectedIndex($index[0]);
 +		$control->setID(self::DEFAULT_ID);
 +		$control->setCssClass('enum-radio-buttons');
 +		$this->setNotNullProperty($container, $control, $column, $record);
 +		return $control;
 +	}
 +
 +	protected function getSetValue($container, $column, $record)
 +	{
 +		$value=array();
 +		foreach($container->findControl(self::DEFAULT_ID)->getItems() as $item)
 +		{
 +			if($item->getSelected())
 +				$value[] = $item->getText();
 +		}
 +		return implode(',', $value);
 +	}
 +
 +	protected function getEnumValue($container, $column, $record)
 +	{
 +		return $container->findControl(self::DEFAULT_ID)->getSelectedItem()->getText();
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php new file mode 100644 index 00000000..b26d44fd --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php @@ -0,0 +1,93 @@ +<?php
 +
 +Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputCommon');
 +
 +class TSqliteScaffoldInput extends TScaffoldInputCommon
 +{
 +	protected function createControl($container, $column, $record)
 +	{
 +		switch(strtolower($column->getType()))
 +		{
 +			case 'boolean':
 +				return $this->createBooleanControl($container, $column, $record);
 +			case 'date':
 +				return $this->createDateControl($container, $column, $record);
 +			case 'blob': case 'tinyblob': case 'mediumblob': case 'longblob':
 +			case 'text': case 'tinytext': case 'mediumtext': case 'longtext':
 +				return $this->createMultiLineControl($container, $column, $record);
 +			case 'year':
 +				return $this->createYearControl($container, $column, $record);
 +			case 'int': case 'integer': case 'tinyint': case 'smallint': case 'mediumint': case 'bigint':
 +				return $this->createIntegerControl($container, $column, $record);
 +			case 'decimal': case 'double': case 'float':
 +				return $this->createFloatControl($container, $column, $record);
 +			case 'time' :
 +				return $this->createTimeControl($container, $column, $record);
 +			case 'datetime': case 'timestamp':
 +				return $this->createDateTimeControl($container, $column, $record);
 +			default:
 +				return $this->createDefaultControl($container,$column, $record);
 +		}
 +	}
 +
 +	protected function getControlValue($container, $column, $record)
 +	{
 +		switch(strtolower($column->getType()))
 +		{
 +			case 'boolean':
 +				return $container->findControl(self::DEFAULT_ID)->getChecked();
 +			case 'date':
 +				return $container->findControl(self::DEFAULT_ID)->getDate();
 +			case 'year':
 +				return $container->findControl(self::DEFAULT_ID)->getSelectedValue();
 +			case 'time':
 +				return $this->getTimeValue($container, $column, $record);
 +			case 'datetime': case 'timestamp':
 +				return $this->getDateTimeValue($container,$column, $record);
 +			default:
 +				return $this->getDefaultControlValue($container,$column, $record);
 +		}
 +	}
 +
 +	protected function createDateControl($container, $column, $record)
 +	{
 +		$control = parent::createDateControl($container, $column, $record);
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		if(!empty($value) && preg_match('/timestamp/i', $column->getType()))
 +			$control->setTimestamp(intval($value));
 +		return $control;
 +	}
 +
 +	protected function createDateTimeControl($container, $column, $record)
 +	{
 +		$value = $this->getRecordPropertyValue($column, $record);
 +		$time = parent::createDateTimeControl($container, $column, $record);
 +		if(!empty($value) && preg_match('/timestamp/i', $column->getType()))
 +		{
 +			$s = Prado::createComponent('System.Util.TDateTimeStamp');
 +			$date = $s->getDate(intval($value));
 +			$time[1]->setSelectedValue($date['hours']);
 +			$time[2]->setSelectedValue($date['minutes']);
 +			$time[3]->setSelectedValue($date['seconds']);
 +		}
 +		return $time;
 +	}
 +
 +	protected function getDateTimeValue($container, $column, $record)
 +	{
 +		if(preg_match('/timestamp/i', $column->getType()))
 +		{
 +			$time = $container->findControl(self::DEFAULT_ID)->getTimestamp();
 +			$s = Prado::createComponent('System.Util.TDateTimeStamp');
 +			$date = $s->getDate($time);
 +			$hour = $container->findControl('scaffold_time_hour')->getSelectedValue();
 +			$mins = $container->findControl('scaffold_time_min')->getSelectedValue();
 +			$secs = $container->findControl('scaffold_time_sec')->getSelectedValue();
 +			return $s->getTimeStamp($hour,$mins,$secs,$date['mon'],$date['mday'],$date['year']);
 +		}
 +		else
 +			return parent::getDateTimeValue($container, $column, $record);
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php new file mode 100644 index 00000000..dc464245 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php @@ -0,0 +1,113 @@ +<?php
 +
 +Prado::using('System.Data.ActiveRecord.TActiveRecord');
 +
 +abstract class TScaffoldBase extends TTemplateControl
 +{
 +	private $_record;
 +	private $_meta;
 +
 +	protected function getTableMetaData()
 +	{
 +		if($this->_meta===null)
 +		{
 +			$finder = $this->getRecordFinder();
 +			$gateway = $finder->getRecordManager()->getRecordGateWay();
 +			$this->_meta = $gateway->getMetaData($finder);
 +		}
 +		return $this->_meta;
 +	}
 +
 +	protected function getRecordProperties($record)
 +	{
 +		$data = array();
 +		foreach($this->getTableMetaData()->getColumns() as $name=>$column)
 +			$data[] = $record->{$name};
 +		return $data;
 +	}
 +
 +	public function getRecordObjectPk($record)
 +	{
 +		$pk = array();
 +		foreach($this->getTableMetaData()->getColumns() as $name=>$column)
 +		{
 +			if($column->getIsPrimaryKey())
 +				$data[] = $record->{$name};
 +		}
 +		return $data;
 +	}
 +
 +	public function getRecordClass()
 +	{
 +		return $this->getViewState('RecordClass');
 +	}
 +
 +	public function setRecordClass($value)
 +	{
 +		$this->setViewState('RecordClass', $value);
 +	}
 +
 +	public function copyFrom(TScaffoldBase $obj)
 +	{
 +		$this->_record = $obj->_record;
 +		$this->_meta = $obj->_meta;
 +		$this->setRecordClass($obj->getRecordClass());
 +	}
 +
 +	protected function clearRecordObject()
 +	{
 +		$this->_record=null;
 +		$this->_meta=null;
 +	}
 +
 +	public function getRecordObject($pk=null)
 +	{
 +		if($this->_record===null)
 +		{
 +			if($pk!==null)
 +				$this->_record=$this->getRecordFinder()->findByPk($pk);
 +			else
 +			{
 +				$class = $this->getRecordClass();
 +				if($class!==null)
 +					$this->_record=Prado::createComponent($class);
 +				else
 +					throw new TConfigurationException('scaffold_invalid_record_class', $this->getID());
 +			}
 +		}
 +		return $this->_record;
 +	}
 +
 +	public function getRecordFinder()
 +	{
 +		return TActiveRecord::getRecordFinder(get_class($this->getRecordObject()));
 +	}
 +
 +	public function setRecordObject($value)
 +	{
 +		if($value instanceof TActiveRecord)
 +			$this->_record=$value;
 +		else
 +			throw new TConfigurationException('scaffold_object_must_be_tactiverecord', $this->getID());
 +	}
 +
 +	public function getDefaultStyle()
 +	{
 +		return $this->getViewState('DefaultStyle', 'style');
 +	}
 +
 +	public function setDefaultStyle($value)
 +	{
 +		$this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style');
 +	}
 +
 +	public function onPreRender($param)
 +	{
 +		parent::onPreRender($param);
 +		$url = $this->publishAsset($this->getDefaultStyle().'.css');
 +		$cs = $this->getPage()->getClientScript();
 +		$cs->registerStyleSheetFile($url,$url);
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php new file mode 100644 index 00000000..28fc8fb9 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php @@ -0,0 +1,119 @@ +<?php
 +
 +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
 +Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase');
 +
 +class TScaffoldEditView extends TScaffoldBase
 +{
 +	private static $_builders=array();
 +
 +	public function onLoad($param)
 +	{
 +		$this->initializeEditForm();
 +	}
 +
 +	public function setRecordPk($value)
 +	{
 +		$this->clearRecordObject();
 +		$val = TPropertyValue::ensureArray($value);
 +		$this->setViewState('PK', count($val) > 0 ? $val : null);
 +	}
 +
 +	public function getRecordPk()
 +	{
 +		return $this->getViewState('PK');
 +	}
 +
 +	protected function getCurrentRecord()
 +	{
 +		return $this->getRecordObject($this->getRecordPk());
 +	}
 +
 +	public function initializeEditForm()
 +	{
 +		$this->getCurrentRecord();
 +		$columns = $this->getTableMetaData()->getColumns();
 +		$this->_repeater->setDataSource($columns);
 +		$this->_repeater->dataBind();
 +	}
 +
 +	public function repeaterItemCreated($sender, $param)
 +	{
 +		$type = $param->getItem()->getItemType();
 +		if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
 +		{
 +			$item = $param->getItem();
 +			$column = $item->getDataItem();
 +			if($column===null)
 +				return;
 +
 +			$record = $this->getCurrentRecord();
 +			$builder = $this->getScaffoldInputBuilder($record);
 +			$builder->createScaffoldInput($this, $item, $column, $record);
 +		}
 +	}
 +
 +	public function bubbleEvent($sender, $param)
 +	{
 +		switch(strtolower($param->getCommandName()))
 +		{
 +			case 'save':
 +				if($this->getPage()->getIsValid())
 +					return $this->doSave() === true ? false : true;
 +				return true;
 +			case 'clear':
 +				$this->setRecordPk(null);
 +				$this->initializeEditForm();
 +				return false;
 +			default:
 +				return false;
 +		}
 +	}
 +
 +	protected function doSave()
 +	{
 +		$record = $this->getCurrentRecord();
 +		$table = $this->getTableMetaData();
 +		$builder = $this->getScaffoldInputBuilder($record);
 +		foreach($this->_repeater->getItems() as $item)
 +		{
 +			$column = $table->getColumn($item->getCustomData());
 +			$builder->loadScaffoldInput($this, $item, $column, $record);
 +		}
 +		$record->save();
 +		return true;
 +	}
 +
 +	public function getSaveButton()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_save');
 +	}
 +
 +	public function getClearButton()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_clear');
 +	}
 +
 +	public function getCancelButton()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_cancel');
 +	}
 +
 +	protected function getScaffoldInputBuilder($record)
 +	{
 +		$class = get_class($record);
 +		if(!isset(self::$_builders[$class]))
 +			self::$_builders[$class] = TScaffoldInputBase::createInputBuilder($record);
 +		return self::$_builders[$class];
 +	}
 +
 +	public function getValidationGroup()
 +	{
 +		return 'group_'.$this->getUniqueID();
 +	}
 +}
 + +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl new file mode 100644 index 00000000..fd599a20 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl @@ -0,0 +1,18 @@ +<div class="edit-inputs">
 +<com:TRepeater ID="_repeater" onItemCreated="repeaterItemCreated">
 +	<prop:ItemTemplate>
 +	<div class="edit-item item_<%# $this->ItemIndex % 2 %> input_<%# $this->ItemIndex %>">
 +		<com:TLabel ID="_label" CssClass="item-label"/>
 +		<span class="item-input">
 +			<com:TPlaceHolder ID="_input" />
 +		</span>
 +	</div>
 +	</prop:ItemTemplate>
 +</com:TRepeater>
 +</div>
 +
 +<div class="edit-page-buttons">
 +<com:TButton ID="_save" Text="Save" CommandName="save" ValidationGroup=<%= $this->ValidationGroup %>/>
 +<com:TButton ID="_clear" Text="Clear" CommandName="clear" CausesValidation="false"/>
 +<com:TButton ID="_cancel" Text="Cancel" CommandName="cancel" CausesValidation="false" Visible="false"/>
 +</div>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php new file mode 100644 index 00000000..98c0aab8 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php @@ -0,0 +1,172 @@ +<?php
 +
 +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
 +
 +class TScaffoldListView extends TScaffoldBase
 +{
 +	public function onLoad($param)
 +	{
 +		parent::onLoad($param);
 +		if(!$this->getPage()->getIsPostBack())
 +			$this->initializeSort();
 +	}
 +
 +	protected function initializeSort()
 +	{
 +		$table = $this->getTableMetaData();
 +		$sorts = array('Sorty By', str_repeat('-',15));
 +		$headers = array();
 +		foreach($table->getColumns() as $name=>$colum)
 +		{
 +			$fname = ucwords(str_replace('_', ' ', $name));
 +			$sorts[$name.' ASC'] = $fname .' Ascending';
 +			$sorts[$name.' DESC'] = $fname .' Descending';
 +			$headers[] = $fname ;
 +		}
 +		$this->_sort->setDataSource($sorts);
 +		$this->_sort->dataBind();
 +		$this->_header->setDataSource($headers);
 +		$this->_header->dataBind();
 +	}
 +
 +	public function onPreRender($param)
 +	{
 +		parent::onPreRender($param);
 +		$this->initializeItemCount();
 +		$this->loadRecordData();
 +	}
 +
 +	protected function initializeItemCount()
 +	{
 +		$this->_list->setVirtualItemCount($this->getRecordFinder()->count());
 +	}
 +
 +	protected function loadRecordData()
 +	{
 +		$finder = $this->getRecordFinder();
 +		$criteria = $this->getPagingCriteria();
 +		$this->_list->setDataSource($finder->findAll($criteria));
 +		$this->_list->dataBind();
 +	}
 +
 +	protected function getPagingCriteria()
 +	{
 +		$total = $this->_list->getVirtualItemCount();
 +		$limit = $this->_list->getPageSize();
 +		$offset = $this->_list->getCurrentPageIndex()*$limit;
 +		if($offset + $limit > $total)
 +			$limit = $total - $offset;
 +		$criteria = new TActiveRecordCriteria;
 +		$criteria->setLimit($limit);
 +		$criteria->setOffset($offset);
 +		$order = explode(' ',$this->_sort->getSelectedValue(), 2);
 +		if(is_array($order) && count($order) === 2)
 +			$criteria->OrdersBy[$order[0]] = $order[1];
 +		return $criteria;
 +	}
 +
 +	public function bubbleEvent($sender, $param)
 +	{
 +		switch(strtolower($param->getCommandName()))
 +		{
 +			case 'delete':
 +				return $this->deleteRecord($sender, $param);
 +			case 'edit':
 +				if(($ctrl=$this->getEditViewControl())!==null)
 +				{
 +					if($param instanceof TRepeaterCommandEventParameter)
 +					{
 +						$pk = $param->getItem()->getCustomData();
 +						$ctrl->setRecordPk($pk);
 +						$ctrl->initializeEditForm();
 +					}
 +				}
 +		}
 +		$this->raiseBubbleEvent($this, $param);
 +		return true;
 +	}
 +
 +	public function deleteRecord($sender, $param)
 +	{
 +		if($param instanceof TRepeaterCommandEventParameter)
 +		{
 +			$pk = $param->getItem()->getCustomData();
 +			$this->getRecordFinder()->deleteByPk($pk);
 +		}
 +	}
 +
 +	protected function listItemCreated($sender, $param)
 +	{
 +		$type = $param->getItem()->getItemType();
 +		if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
 +			$this->populateField($sender, $param);
 +	}
 +
 +	protected function populateField($sender, $param)
 +	{
 +		$item = $param->getItem();
 +		$data = $item->getDataItem();
 +		if($data !== null)
 +		{
 +			$item->setCustomData($this->getRecordObjectPk($data));
 +
 +			if(($prop = $item->findControl('_properties'))!==null)
 +			{
 +				$item->_properties->setDataSource($this->getRecordProperties($data));
 +				$item->_properties->dataBind();
 +			}
 +		}
 +	}
 +
 +	protected function pageChanged($sender, $param)
 +	{
 +		$this->_list->setCurrentPageIndex($param->getNewPageIndex());
 +	}
 +
 +	public function getList()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_list');
 +	}
 +
 +	public function getPager()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_pager');
 +	}
 +
 +	public function getSort()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_sort');
 +	}
 +
 +	public function getHeader()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_header');
 +	}
 +
 +	public function getEditViewID()
 +	{
 +		return $this->getViewState('EditViewID');
 +	}
 +
 +	public function setEditViewID($value)
 +	{
 +		$this->setViewState('EditViewID', $value);
 +	}
 +
 +	protected function getEditViewControl()
 +	{
 +		if(($id=$this->getEditViewID())!==null)
 +		{
 +			$ctrl = $this->getParent()->findControl($id);
 +			if($ctrl===null)
 +				throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id);
 +			return $ctrl;
 +		}
 +	}
 +}
 + +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl new file mode 100644 index 00000000..7b8854f0 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl @@ -0,0 +1,56 @@ +
 +<div class="item-header">
 +<com:TRepeater ID="_header">
 +	<prop:ItemTemplate>
 +		<com:TLabel Text=<%# $this->DataItem %> CssClass="field field_<%# $this->ItemIndex %>"/>
 +	</prop:ItemTemplate>
 +</com:TRepeater>
 +
 +<span class="sort-options">
 +	<com:TDropDownList ID="_sort" AutoPostBack="true"/>
 +</span>
 +
 +</div>
 +
 +<div class="item-list">
 +<com:TRepeater ID="_list"
 +	AllowPaging="true"
 +     AllowCustomPaging="true"
 +     onItemCommand="bubbleEvent"
 +     onItemCreated="listItemCreated"
 +	 PageSize="10">
 +	<prop:ItemTemplate>
 +	<div class="item item_<%# $this->ItemIndex % 2 %>">
 +	
 +	<com:TRepeater ID="_properties">
 +		<prop:ItemTemplate>
 +		<span class="field field_<%# $this->ItemIndex %>">
 +			<%# htmlspecialchars($this->DataItem) %>
 +		</span>
 +		</prop:ItemTemplate>
 +	</com:TRepeater>
 +	
 +	<span class="edit-delete-buttons">
 +		<com:TButton Text="Edit" 
 +			Visible=<%# $this->NamingContainer->Parent->EditViewID !== Null %>
 +			CommandName="edit" 
 +			CssClass="edit-button"
 +			CausesValidation="false" />
 +		<com:TButton Text="Delete" 
 +			CommandName="delete" 
 +			CssClass="delete-button"
 +			CausesValidation="false"
 +			Attributes.onclick="if(!confirm('Are you sure?')) return false;"  />
 +	</span>
 +	
 +	</div>
 +	</prop:ItemTemplate>
 +</com:TRepeater>
 +</div>
 +
 +<com:TPager ID="_pager"
 +	CssClass="pager"
 +	ControlToPaginate="_list"
 +	PageButtonCount="10"
 +	Mode="Numeric"
 +	OnPageIndexChanged="pageChanged" />
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php new file mode 100644 index 00000000..668ede61 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php @@ -0,0 +1,72 @@ +<?php
 +
 +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
 +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView');
 +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView');
 +
 +class TScaffoldView extends TScaffoldBase
 +{
 +	public function onLoad($param)
 +	{
 +		parent::onLoad($param);
 +		$this->getListView()->copyFrom($this);
 +		$this->getEditView()->copyFrom($this);
 +	}
 +
 +	public function getListView()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_listView');
 +	}
 +
 +	public function getEditView()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_editView');
 +	}
 +
 +	public function getAddButton()
 +	{
 +		$this->ensureChildControls();
 +		return $this->getRegisteredObject('_newButton');
 +	}
 +
 +	public function bubbleEvent($sender,$param)
 +	{
 +		switch(strtolower($param->getCommandName()))
 +		{
 +			case 'edit':
 +				return $this->showEditView($sender, $param);
 +			case 'new':
 +				return $this->showAddView($sender, $param);
 +			default:
 +				return $this->showListView($sender, $param);
 +		}
 +		return false;
 +	}
 +
 +	protected function showEditView($sender, $param)
 +	{
 +		$this->getListView()->setVisible(false);
 +		$this->getEditView()->setVisible(true);
 +		$this->getAddButton()->setVisible(false);
 +		$this->getEditView()->getCancelButton()->setVisible(true);
 +		$this->getEditView()->getClearButton()->setVisible(false);
 +	}
 +
 +	protected function showListView($sender, $param)
 +	{
 +		$this->getListView()->setVisible(true);
 +		$this->getEditView()->setVisible(false);
 +		$this->getAddButton()->setVisible(true);
 +	}
 +
 +	protected function showAddView($sender, $param)
 +	{
 +		$this->getEditView()->setRecordPk(null);
 +		$this->getEditView()->initializeEditForm();
 +		$this->showEditView($sender, $param);
 +	}
 +}
 + +?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl new file mode 100644 index 00000000..0ae8b8b0 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl @@ -0,0 +1,7 @@ +<com:TScaffoldListView ID="_listView" EditViewID="_editView" />
 +
 +<div class="auxilary-button buttons">
 +<com:TButton ID="_newButton" Text="Add new record" CssClass="new-button" CommandName="new" />
 +</div>
 +
 +<com:TScaffoldEditView ID="_editView"  Visible="false"/>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/style.css b/framework/Data/ActiveRecord/Scaffold/style.css new file mode 100644 index 00000000..eb31e9a5 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/style.css @@ -0,0 +1,123 @@ +body
 +{
 +	font-family: Cambria, Georgia, "Times New Roman", Times, serif;
 +}
 +
 +.pager
 +{
 +	display: block;
 +	border-top: 1px solid #ccc;
 +	padding: 1em;
 +}
 +
 +.pager span, .pager a
 +{
 +	border: 1px solid #ccc;
 +	padding: 0.3em 0.7em;
 +	font-weight: bold;
 +}
 +
 +.pager a
 +{
 +	background-color: #E0FFFF;
 +	border-color: #87CEFA;
 +}
 +
 +.pager a:hover
 +{
 +	background-color: White;
 +}
 +
 +
 +.item, .item-header
 +{
 +	border-top: 1px dashed #B0C4DE;
 +	padding: 0.5em;
 +	clear: both;
 +}
 +
 +.item-header
 +{
 +	border-top: 0 none;
 +	font-weight: bold;
 +}
 +
 +.item_1
 +{
 +	background-color: #F0F8FF;
 +}
 +
 +.field_0, .field
 +{
 +	padding: 0.2em;
 +	float: left;
 +	width: 150px;
 +}
 +
 +.field_0
 +{
 +	width: 40px;
 +	text-align: center;
 +}
 +
 +.auxilary-button
 +{
 +	padding: 1em;
 +}
 +
 +.edit-inputs label
 +{
 +	width: 150px;
 +	float: left;
 +	text-align: right;
 +	padding: 0 0.5em;
 +	font-weight: bold;
 +}
 +
 +
 +.item-input label
 +{
 +	float: none;
 +	font-weight: normal;
 +}
 +
 +
 +.edit-page-buttons
 +{
 +	padding-left: 120px;
 +	padding-top: 20px;
 +}
 +
 +.edit-item
 +{
 +	padding: 0.4em;
 +}
 +
 +.edit-inputs .scaffold_input
 +{
 +	width: 250px;
 +	border: 1px solid Highlight;
 +	padding: 0.2em;
 +}
 +
 +.edit-inputs .multiline-textbox
 +{
 +	width: 500px;
 +	height: 100px;
 +}
 +
 +.edit-inputs .input_0 .scaffold_input
 +{
 +	width: 50px;
 +}
 +
 +.edit-inputs .required
 +{
 +	font-weight: bold;
 +	padding: 0.2em;
 +}
 +.edit-inputs .required-input
 +{
 +	border: 1px solid red;
 +	background-color: #FFF5EE;
 +}
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php b/framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php index 6f787942..2f7202cf 100644 --- a/framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php +++ b/framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php @@ -146,7 +146,7 @@ EOD;  		$type = $col['type'];
  		// A specific constant in the 7.0 source, the length is offset by 4.
 -		$length = $col['atttypmod'] > 0 ? $col['atttypmod'] - 4 : -1;
 +		$length = $col['atttypmod'] > 0 ? $col['atttypmod'] - 4 : null;
  		$notNull = $col['attnotnull'];
  		$serial = $col['attisserial'] ? $schema.'.'.$this->getSerialName($col['adsrc']) : null;
  		$default = $serial === null && $col['atthasdef'] ? $col['adsrc'] : null;
 | 
