Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

widgets

  • Loading branch information...
commit 5aafa8198e015aba5f5009b5749db2fcbe5fabdc 1 parent 31a56db
@Neerrar Neerrar authored
View
1  global.inc.php.tpl
@@ -148,6 +148,7 @@
.ONPHP_MAIN_PATH.'UI'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'UI'.DIRECTORY_SEPARATOR.'View'.PATH_SEPARATOR
+ .ONPHP_MAIN_PATH.'UI'.DIRECTORY_SEPARATOR.'Widget'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Utils' .PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'TuringTest'.PATH_SEPARATOR
View
150 main/UI/Widget/BaseWidget.class.php
@@ -0,0 +1,150 @@
+<?php
+/**
+ * Базовый виджет
+ * @author Alex Gorbylev <alex@gorbylev.ru>
+ * @date 2012.03.11
+ */
+abstract class BaseWidget implements IWidget
+{
+ protected $name = null;
+ protected $templatePath = null;
+ protected $templateName = null;
+ protected $list = null;
+
+ /**
+ * @var PartViewer
+ */
+ protected $viewer = null;
+
+ public function __construct($name = null)
+ {
+ $this->name = $name;
+ $this->viewer = Viewer::get();
+ }
+
+ /**
+ * @param string $key
+ * @param mixed $value
+ * @return BaseWidget
+ */
+ public function set($key, $value)
+ {
+ if( ( mb_substr($key, 0, 1) != '_' ) )
+ $key = '_'.$key;
+
+ $this->list[$key] = $value;
+
+
+ return $this;
+ }
+
+ /**
+ * @return BaseWidget
+ */
+ public function setTemplateName($value)
+ {
+ $this->templateName = $value;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTemplateName()
+ {
+ return $this->templateName;
+ }
+
+ /**
+ * @return BaseWidget
+ */
+ public function setViewer(PartViewer $viewer)
+ {
+ $this->viewer = $viewer;
+
+ return $this;
+ }
+
+ /**
+ * @return Model
+ */
+ protected function makeModel()
+ {
+ $model = $this->viewer->getModel();
+ $model->set('_name', $this->name);
+
+ if(
+ is_array( $this->list ) &&
+ count( $this->list )
+ ){
+ foreach ( $this->list as $key => $value ) {
+ $model->set($key, $value);
+ }
+ }
+
+ return $model;
+ }
+
+ /** (non-PHPdoc)
+ * @see core/Base/Stringable::toString()
+ * @return string
+ */
+ public function toString()
+ {
+ try {
+ $this->makeModel();
+
+ ob_start();
+
+ $this->viewer->view(
+ $this->templatePath. DIRECTORY_SEPARATOR. $this->templateName
+ );
+
+ $source = ob_get_contents();
+
+ ob_end_clean();
+ }
+ catch (Exception $e) {
+ // FIXME
+ error_log(__METHOD__ . ': '.$e->__toString() );
+ throw $e;
+ }
+
+ return (string) $source;
+ }
+
+ /**
+ * @return PartViewer
+ */
+ public function getViewer()
+ {
+ return $this->viewer;
+ }
+
+ /** (non-PHPdoc)
+ * @see main/Flow/View::render()
+ * @param Model|null $model
+ * @return void
+ */
+ public function render($model = null)
+ {
+ if( $model )
+ $this->getViewer()->
+ getModel()->
+ merge($model);
+
+ echo $this->toString();
+
+ return /*void*/;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+}
+
View
721 main/UI/Widget/DataGrid.class.php
@@ -0,0 +1,721 @@
+<?php
+/**
+ * Виджет для отображения информации в виде таблицы или сводки
+ * @author Михаил Кулаковский <m@klkvsk.ru>
+ * @date 2012.03.19
+ */
+class DataGrid extends BaseWidget
+{
+ /** @var DataGrid для вложенных таблиц */
+ protected $parent = null;
+
+ /** @var int предел вложенности */
+ protected static $maxNesting = 1;
+
+ /** @var array список дочерних объектов */
+ protected $objects = array();
+
+
+ /** @var array список 'fieldID' => 'Field name' */
+ protected $fields = array();
+
+ /** @var array список полей, по которым можно сортировать.
+ * Фактически, исключает поля, добавленные через addColumn */
+ protected $sortingFields = array();
+
+ /** @var array список 'fieldID' => callback() */
+ protected $renderers = array();
+
+ /** @var array массив строк таблицы */
+ protected $rows = array();
+
+
+ /** @var array аттрибуты заголовков таблицы <th> */
+ protected $fieldHtmlOptions = array();
+
+ /** @var array аттрибуты <form> */
+ protected $formHtmlOptions = array();
+
+ /** @var bool показывать заголовки или нет */
+ protected $showHeader = true;
+
+ /** @var bool показывать кнопки сортировки */
+ protected $showSorting = true;
+
+ /** @var bool делать редактируемые поля */
+ protected $isEditor = false;
+
+ /** @var Form для вывода ошибок в заполнении полей */
+ protected $form = null;
+
+ /** @var string текстовый вывод булевого типа */
+ public $trueName = null;
+
+ /** @var string текстовый вывод булевого типа */
+ public $falseName = null;
+
+ /**
+ * Создает таблицу вида сводки (заголовок слева)
+ * @static
+ * @return DataGrid
+ */
+ public static function details() {
+ $self = self::create();
+ $self->templateName = 'DataGrid_details';
+ $self->showSorting = false;
+ return $self;
+ }
+
+ /**
+ * Создает обычную таблицу
+ * @static
+ * @return DataGrid
+ */
+ public static function table() {
+ $self = self::create();
+ $self->templateName = 'DataGrid_table';
+ $self->showSorting = true;
+ return $self;
+ }
+
+ /**
+ * DataGrid::details() в виде редактируемой формы.
+ * Следует добавлять только 1 объект.
+ * @static
+ * @return DataGrid
+ */
+ public static function editor($subfield = null) {
+ $self = self::details();
+ $self->isEditor = true;
+ return $self;
+ }
+
+ /**
+ * @static
+ * @return DataGrid
+ */
+ private static function create() {
+ $self = new self;
+ $self->trueName = __('Да');
+ $self->falseName = __('Нет');
+ return $self;
+ }
+
+ /**
+ * Вызывает addRow для каждой из переданных строк
+ * @param array $data
+ * @return DataGrid
+ * @throws WrongArgumentException
+ */
+ public function addRows(array $data) {
+ foreach ($data as $row) {
+ $this->addRow($row);
+ }
+ return $this;
+ }
+
+ /**
+ * Добавляет строку в таблицу, определяет тип и имена ее полей
+ * @param $data
+ * @return DataGrid
+ * @throws WrongArgumentException
+ */
+ public function addRow($data) {
+ $rowId = count($this->rows); // id следующей строки
+
+ // если это объект, то смотрим его поля в протипе
+ // и через геттеры получаем все параметры, а если
+ // это массив, то берем его как есть.
+
+ if (is_object($data) && $data instanceof Prototyped) {
+ /** @var $data Prototyped */
+ $this->objects[$rowId] = $data;
+ $fieldIds = array();
+ $row = array();
+ foreach ($data->proto()->getPropertyList() as $property) {
+ try {
+ $getter = $property->getGetter();
+ $value = $this->objects[$rowId]->$getter();
+ } catch (BadMethodCallException $e) {
+ continue;
+ } catch (ObjectNotFoundException $e) {
+ $value = null;
+ }
+ $fieldIds[] = $property->getName();
+ $row[$property->getName()] = $value;
+ }
+ } else if (is_array($data)) {
+ $fieldIds = array_keys($data);
+ $row = $data;
+ } else {
+ throw new WrongArgumentException('$data should be either array or prototyped object');
+ }
+ // сохраним в список сортируемых полей
+ foreach ($fieldIds as $fieldId) {
+ if (!in_array($fieldId, $this->sortingFields)) {
+ $this->sortingFields[] = $fieldId;
+ }
+ }
+
+ // построим массив полей в виде 'имяПоля' => 'Имя поля'
+ // ключ - имя параметра, значение - имя для отображения
+ $fields = array();
+ foreach($fieldIds as $fieldId) {
+ $fieldName = self::beautifyFieldName($fieldId);
+ $fields[$fieldId] = $fieldName;
+ }
+ // сливаем с существующим списком, чтобы ничего не потерять,
+ // если например отработали не все геттеры, и поле пропущено
+ $this->fields = array_merge($this->fields, $fields);
+
+ // записываем данные
+ foreach($row as $fieldId => $value) {
+ $this->setField($rowId, $fieldId, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Выставляет значение конкретного поля конкретной строки
+ * @param $rowId
+ * @param $fieldId
+ * @param $value
+ * @param $property LightMetaProperty если null, определится
+ * @return DataGrid
+ */
+ private function setField($rowId, $fieldId, $value, $property = null) {
+ $this->rows[$rowId][$fieldId] = $value;
+ if (!isset($this->renderers[$fieldId])) {
+ if ($this->isEditor) {
+ if ($property == null) {
+ /** @var $object Prototyped */
+ $object = $this->objects[$rowId];
+ if ( !($object instanceof Prototyped) ) {
+ throw new WrongArgumentException;
+ }
+ $property = $object->proto()->getPropertyByName($fieldId);
+ }
+
+ $this->renderers[$fieldId] = $this->getEditRenderer($fieldId, $property);
+ } else {
+ if ($value !== null) {
+ $this->renderers[$fieldId] = $this->getViewRenderer($value);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Дополнительная колонка
+ * @param string $fieldName
+ * @param Closure $renderer callback
+ * @param string|null $fieldId
+ * @return DataGrid
+ */
+ public function addColumn($fieldName, $renderer, $fieldId = null) {
+ // если это поле не для данных (иконки действий, например)
+ // можно сгенерить рандомное имя поля, т.к. оно не важно
+ if ($fieldId === null) {
+ $fieldId = md5($fieldName);
+ } else {
+ // Если поле указано явно, добавим сразу для него сортировку
+ $this->sortingFields[] = $fieldId;
+ }
+
+ $this->fields[$fieldId] = $fieldName;
+ $this->setRenderer($fieldId, $renderer);
+ return $this;
+ }
+
+ /**
+ * @param string $fieldId
+ * @param LightMetaProperty $property
+ * @return closure
+ * @throws ClassNotFoundException
+ */
+ protected function getEditRenderer($fieldId, LightMetaProperty $property) {
+ switch($property->getType()) {
+ case 'integer':
+ case 'float':
+ case 'string':
+ return function ($value) use ($fieldId, $property) {
+ if ($value instanceof Stringable) $value = $value->toString();
+ if ($property->getType() == 'string' && !$property->getMax()) {
+ return '<textarea rows="4" cols="50" name="'
+ . $property->getName() . '">'
+ . $value
+ . '</textarea>';
+ } else {
+ $styleWidth = $property->getType() == 'string' ? 250 : 80;
+ $length = $property->getType() == 'string' ? $property->getMax() : 16;
+ return '<input style="width:'.$styleWidth.'px" type="text" name="'. $fieldId
+ .'" value="' . $value . '" length="' . $length . '" />';
+ }
+
+ };
+
+ case 'timestamp':
+ case 'date':
+ return function ($value) use ($fieldId, $property) {
+ if ($value instanceof Date)
+ $val = $value->toDate('-');
+ else $val = '';
+
+ return '<input type="text" name="'. $fieldId . '" value="' . $val . '" />';
+ };
+
+ case 'boolean':
+ return function ($value) use ($fieldId) {
+ return '<input type="checkbox" name="' . $fieldId . '"'
+ . ($value == true ? ' checked="checked"' : '') .'" />';
+ };
+
+ case 'enumeration':
+ return function ($value) use ($fieldId, $property) {
+ $class = $property->getClassName();
+ if (!class_exists($class, true)) {
+ throw new ClassNotFoundException;
+ }
+ $list = $class::makeObjectList();
+ $html = '<select name="' . $fieldId . '">';
+ if (!$property->isRequired()) {
+ $html .= '<option value="">' . __('Нет') . '</option>';
+ }
+ foreach ($list as $item) {
+ $checked = ($item == $value) ? ' selected="selected"' : '';
+ $html .= '<option value="' . $item->getId() . '"' . $checked . '>' . $item->getName() . '</option>';
+ }
+ $html .= '</select>';
+ return $html;
+ };
+
+ case 'integerIdentifier':
+ return function ($value) use ($property) {
+ if ($value instanceof Identifiable) {
+ $value = $value->getId();
+ }
+ return $property->getClassName() . ' ID: ' . $value;
+ };
+
+ case 'identifierList':
+ return function ($value) use ($property) {
+ //if (is_subclass_of($property->getClassName(), 'Enumeration')) {
+ // return 'enum';
+ //} else {
+ return $property->getClassName();
+ //}
+ };
+
+ default:
+ return function ($value) use ($property) {
+ // DEBUG
+ $props[] = 'name: ' . $property->getName();
+ $props[] = 'className: ' . $property->getClassName();
+ $props[] = 'type: ' . $property->getType();
+ $props[] = 'min: ' . $property->getMin();
+ $props[] = 'max: ' . $property->getMax();
+ $props[] = 'relation: ' . $property->getRelationId();
+ $props[] = 'fetch: ' . $property->getFetchStrategyId();
+ //$props[] = 'value: ' . $value;
+ return implode(', ', $props);
+ };
+ }
+ }
+
+ /**
+ * Находит подходящий рендерер в соответствии с типом значения
+ * @param $value
+ * @return Closure
+ */
+ protected function getViewRenderer($value) {
+ // переменные для замыканий, т.к. они не биндятся к this
+ $self = $this;
+ $trueName = $this->trueName;
+ $falseName = $this->falseName;
+
+ // для прототипированного объекта можно построить
+ // вложенную табличку. Важно запомнить родителя,
+ // чтобы избежать бесконечной рекурсии
+ if ($value instanceof Prototyped)
+ return function($value, $object) use ($self) {
+ if ($self->hasParent($object)) {
+ return get_class($value) . ' ID:' . $value->getId();
+ } else {
+ try {
+ return DataGrid::details()->setParent($self)->addRow($value)->ToString();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+ };
+
+
+ // Это в основном для тех случаев, когда у объекта есть
+ // OneToMany свойство. UnifiedContainer позволяет получить
+ // список дочерних объектов
+ if ($value instanceof UnifiedContainer)
+ return function(UnifiedContainer $value, $object) use ($self) {
+ if ($self->hasParent($object)) {
+ return get_class($value) . ' count:' . $value->fetch()->getCount();
+ } else {
+ try {
+ return DataGrid::table()->setParent($self)->addRows($value->fetch()->getList())->ToString();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+ };
+
+
+ // Булевы выведем для удобства словами "Да" или "Нет"
+ if (is_bool($value))
+ return function ($value) use ($trueName, $falseName) {
+ return $value ? $trueName : $falseName;
+ };
+
+
+ // Встроенная табличка
+ if (is_array($value))
+ return function($value) use ($self) {
+ try {
+ return DataGrid::table()->setParent($self)->addRows($value)->ToString();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ };
+
+
+ // Заглушка для всех прочих объектов
+ if (is_object($value))
+ return function ($value) {
+ if (is_null($value)) {
+ return '';
+ }
+ if ($value instanceof Stringable)
+ return $value->toString();
+ if (method_exists($value, '__toString'))
+ return (string)$value;
+ return 'object('.get_class($value).')';
+ };
+
+ if (is_string($value))
+ return function ($value) {
+ return nl2br($value);
+ };
+
+ // прочие случаи
+ return function($value) { return $value; };
+ }
+
+ /**
+ * 'somePropertyName' => 'Some property name'
+ * @param $camelCaseString
+ * @return string
+ */
+ public static function beautifyFieldName($camelCaseString) {
+ return ucfirst(
+ preg_replace_callback(
+ '/([a-z])([A-Z])/',
+ function($x) {
+ return $x[1] . ' ' . strtolower($x[2]);
+ },
+ $camelCaseString
+ )
+ );
+ }
+
+ /**
+ * @param DataGrid $parent
+ * @return DataGrid
+ */
+ public function setParent(DataGrid $parent) {
+ $this->parent = $parent;
+ return $this;
+ }
+
+ /**
+ * Рекурсивная проверка
+ * @param $object
+ * @return bool
+ */
+ public function hasParent($object) {
+ $dataGrid = $this;
+ $nesting = 0;
+ while ($dataGrid = $dataGrid->parent) {
+ if ($nesting++ > self::$maxNesting)
+ return true;
+ foreach ($dataGrid->objects as $o) {
+ if ($o == $object) {
+ return true;
+ }
+ if ($o instanceof Identifiable && $object instanceof Identifiable
+ && get_class($o) == get_class($object)
+ && $o->getId() == $object->getId()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param $number
+ * @return DataGrid
+ */
+ public function setMaxNesting($number) {
+ self::$maxNesting = $number;
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public static function getMaxNesting() {
+ return self::$maxNesting;
+ }
+
+ /**
+ * Задает массив столбцов таблицы
+ * @param array $fields array('fieldID' => 'Field name', ...)
+ * @return DataGrid
+ */
+ public function setFields(array $fields) {
+ $this->fields = $fields;
+
+ /* При добавлении объекта, из него выгребаются все свойства,
+ * но только его собственные, а зависимые объекты не подгружаются
+ * Допустим, мы хотим добавить employee.user.name для отображении
+ * имени менеджера, когда выводим инфу по рекламодателю. Тогда
+ * следующий код сделает следующую цепочку вызовов:
+ * $advertiser->getEmployee()->getUser()->getName()
+ * и заполнит соответствующие поля таблицы значениями.
+ * */
+
+ foreach ($this->fields as $fieldId => $fieldName) {
+ if (strpos($fieldId, '.') !== false) {
+ $path = explode('.', $fieldId);
+ foreach ($this->rows as $rowId => $row) {
+ $object = $this->objects[$rowId];
+
+ $failed = false;
+ foreach ($path as $propertyName) {
+ if (!$object || !($object instanceof Prototyped)) {
+ $failed = true;
+ break;
+ }
+ $property = $object->proto()->getPropertyByName($propertyName);
+ if ($property != null) {
+ $getter = $property->getGetter();
+ $object = $object->$getter();
+ } else {
+ $failed = true;
+ break;
+ }
+ }
+
+ if (!$failed) {
+ $this->setField($rowId, $fieldId, $object, $property);
+ $this->sortingFields[] = $fieldId;
+ }
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Исключает столбцы из таблицы
+ * можно использовать маски, напр: unsetFields('webmaster.*');
+ * @param $_ string, string, ... OR array
+ * @return DataGrid
+ */
+ public function unsetFields($_) {
+ if (is_array($_)) $fields = $_;
+ else $fields = func_get_args();
+ foreach($fields as $fieldId) {
+ if (substr($fieldId, -1) == '*') {
+ $prefix = substr($fieldId, 0, strlen($fieldId) - 1);
+ foreach ($this->fields as $fieldId => $fieldName) {
+ if (substr($fieldId, 0, strlen($prefix)) == $prefix) {
+ unset($this->fields[$fieldId]);
+ }
+ }
+ } else {
+ unset($this->fields[$fieldId]);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * @param $fieldId string
+ * @param $callback Closure function($value, $rowObject) {}
+ * @return DataGrid
+ */
+ public function setRenderer($fieldId, $callback) {
+ $this->renderers[$fieldId] = $callback;
+ return $this;
+ }
+
+ /**
+ * Показать названия колонок
+ * @return DataGrid
+ */
+ public function showHeader() {
+ $this->showHeader = true;
+ return $this;
+ }
+
+ /**
+ * Скрыть названия колонок
+ * @return DataGrid
+ */
+ public function hideHeader() {
+ $this->showHeader = false;
+ return $this;
+ }
+
+ /**
+ * @param array $fields
+ */
+ public function setSortingFields(array $fields) {
+ $this->sortingFields = $fields;
+ return $this;
+ }
+
+ /**
+ * Исключает столбцы из списка столбцов для сортировки
+ * @param $_ string, string, ... OR array
+ * @return DataGrid
+ */
+ public function unsetSortingFields($_) {
+ if (is_array($_)) $fields = $_;
+ else $fields = func_get_args();
+ foreach($fields as $fieldId) {
+ foreach($this->sortingFields as $k=>$v) {
+ if ($v == $fieldId) {
+ unset($this->sortingFields[$k]);
+ }
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * @return DataGrid
+ */
+ public function showSorting() {
+ $this->showSorting = true;
+ return $this;
+ }
+
+ /**
+ * @return DataGrid
+ */
+ public function hideSorting() {
+ $this->showSorting = false;
+ return $this;
+ }
+
+ /**
+ * @param string $fieldId
+ * @param array $htmlOptions
+ * @return DataGrid
+ */
+ public function setHeaderOptions($fieldId, $htmlOptions) {
+ $this->fieldHtmlOptions[$fieldId] = $htmlOptions;
+ return $this;
+ }
+
+ /**
+ * @param array $htmlOptions
+ * @return DataGrid
+ */
+ public function setFormOptions($htmlOptions) {
+ $this->formHtmlOptions = $htmlOptions;
+ return $this;
+ }
+
+ /**
+ * @param $formErrors
+ * @return DataGrid
+ */
+ public function setForm(Form $form) {
+ $this->form = $form;
+ return $this;
+ }
+
+ /**
+ * @return Model
+ */
+ protected function makeModel() {
+ $data = array();
+ // рендерим данные
+ foreach ($this->rows as $rowId => $row) {
+ foreach ($this->fields as $fieldId => $fieldName) {
+ $field = isset($row[$fieldId]) ? $row[$fieldId] : null;
+ if ($this->form instanceof Form && $this->form->exists($fieldId)) {
+ if ($this->form->get($fieldId)->isImported())
+ $field = $this->form->get($fieldId)->getValue();
+ else if ($this->form->hasError($fieldId))
+ $field = $this->form->get($fieldId)->getRawValue();
+ }
+
+ // если есть рендерер, прогоним значение через него
+ if (isset($this->renderers[$fieldId])) {
+ $callback = $this->renderers[$fieldId];
+ $object = isset($this->objects[$rowId]) ? $this->objects[$rowId] : $this->rows[$rowId];
+ if ($this->renderers[$fieldId] instanceof Closure) {
+ $field = $callback($field, $object);
+ } else {
+ $field = call_user_func($callback, $field, $object);
+ }
+ }
+ $data[$rowId][$fieldId] = $field;
+ }
+ }
+
+ // отрендерим аттрибуты html
+ $htmlOptions = array();
+ foreach ($this->fields as $fieldId => $fieldName) {
+ if (isset($this->fieldHtmlOptions[$fieldId])) {
+ $htmlOptions[$fieldId] = Html::attributes($this->fieldHtmlOptions[$fieldId]);
+ } else {
+ $htmlOptions[$fieldId] = '';
+ }
+ }
+
+ // отрендерим аттрибуты формы
+ $selfUrl = null;
+ try {
+ $controller = Application::me()->getRunningController();
+ $selfUrl = $controller->getSelfUrl()->__toString();
+ } catch (UnexpectedValueException $e) {
+ $selfUrl = $_SERVER['REQUEST_URI'];
+ $this->hideSorting();
+ }
+ $formOptions = Html::attributes(array_merge(
+ array('action' => $selfUrl, 'method' => 'POST'),
+ $this->formHtmlOptions
+ ));
+
+ $model = parent::makeModel()
+ ->set('fields', $this->fields)
+ ->set('data', $data)
+ ->set('htmlOptions', $htmlOptions)
+ ->set('formOptions', $formOptions)
+ ->set('showHeader', $this->showHeader)
+ ->set('showSorting', $this->showSorting)
+ ->set('sortingFields', $this->sortingFields)
+ ->set('isEditor', $this->isEditor)
+ ->set('form', $this->form);
+ ;
+
+ return $model;
+ }
+}
View
15 main/UI/Widget/IWidget.class.php
@@ -0,0 +1,15 @@
+<?php
+/***************************************************************************
+ * Copyright (C) 2010 by Kutcurua Georgy Tamazievich *
+ * email: g.kutcurua@gmail.com, icq: 723737, jabber: soloweb@jabber.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ ***************************************************************************/
+/* $Id$ */
+
+
+ interface IWidget extends Stringable, View{/**/}
Please sign in to comment.
Something went wrong with that request. Please try again.