Source for file Grid.class.php
Documentation is available at Grid.class.php
* @copyright Energine 2006
* Количество записей в гриде по умолчанию
* Компонент: менеджер изображений
* Компонент: библиотека изображений
* Имя колонки для определения порядка пользовательскорй сортировки
* @param Document $document
public function __construct($name, $module, Document $document, array $params = null) {
parent::__construct($name, $module, $document, $params);
if (!$this->getParam('recordsPerPage')) {
* Переопределен параметр configFilename
if (!$this->params['configFilename']) {
$params['configFilename'] = $fileConf;
$params['configFilename'] = $coreConf;
$params['active'] = true;
* Метод выводящий форму добавления
protected function add() {
$this->setType(self::COMPONENT_TYPE_FORM_ADD);
//$this->addCrumb('TXT_ADD_ITEM');
* Метод выводящий форму редактирования
protected function edit() {
$this->setType(self::COMPONENT_TYPE_FORM_ALTER);
foreach ($fieldDescriptions as $fieldName => $fieldDescription) {
$fieldData = $fieldData[0];
* Добавлены переводы для фильтра
protected function main() {
$transactionStarted = $this->dbh->beginTransaction();
if ($transactionStarted) {
$JSONResponse = $this->generateError($e->getCode(), $e->getMessage());
$this->response->setHeader('Content-Type', 'text/javascript; charset=utf-8');
* Внутренний метод удаления записи
* @param int идентификаотр записи
//если определен порядок следования перестраиваем индекс сортировки
if ($orderColumn && $ids) {
$request = 'UPDATE '. $this->getTableName(). ' SET '. $orderColumn. ' = '. $orderColumn. ' - 1 '. $this->dbh->buildWhereCondition($this->getFilter());
$this->dbh->modifyRequest($request);
* переписан родительский метод
if (isset ($_POST['languageID']) && $this->getAction()== 'getRawData') {
$langID = $_POST['languageID'];
* Выводит данные в JSON формате для AJAX
protected function getRawData($baseMethod = self::DEFAULT_ACTION_NAME) {
$this->setParam('onlyCurrentLang', true);
$this->config->setCurrentMethod($baseMethod);
if ($data instanceof Data) {
$message['errors'][] = array('message'=> $e->getMessage(). current($e->getCustomMessage()));
$this->response->setHeader('Content-Type', 'text/javascript; charset=utf-8');
* Внешний метод сохранения
* Вызывает внутренний метод сохранения saveData(), который и производит собственно все действия
protected function save() {
$transactionStarted = $this->dbh->beginTransaction();
$transactionStarted = !($this->dbh->commit());
'mode' => (is_int($result))? 'insert': 'update'
//Формируем JS массив ошибок который будет разбираться на клиенте
$errors = $this->saver->getErrors();
foreach ($errors as $errorFieldName) {
$message['errors'][] = array(
$JSONResponse = array_merge(array('result'=> false, 'header'=> $this->translate('TXT_SHIT_HAPPENS')), $message);
if ($transactionStarted) {
$message['errors'][] = array('message'=> $e->getMessage(). current($e->getCustomMessage()));
$JSONResponse = array_merge(array('result'=> false, 'header'=> $this->translate('TXT_SHIT_HAPPENS')), $message);
$this->response->setHeader('Content-Type', 'text/javascript; charset=utf-8');
* Если поле OrderColumn присутствует в списке, убираем его
* @return DataDescription
$this->config->setCurrentMethod(self::DEFAULT_ACTION_NAME);
$this->config->setCurrentMethod($previousAction);
if (in_array($this->getAction(), array('main', 'edit', 'add', 'save', 'getRawData')) && ($col = $this->getOrderColumn()) && ($field = $result->getFieldDescriptionByName($col))) {
$result->removeFieldDescription($field);
* Переписан родительский метод генерации ошибки, поскольку для AJAX такая не подходит
* @param string тип ошибки
* @param string сообщение об ошибке
* @param mixed необязательная дополнительная информация об ошибке
protected function generateError($errorType, $errorMessage, $errorCustomInfo = false) {
$message['errors'][] = array('message'=> $errorMessage);
$response = array_merge(array('result'=> false, 'header'=> $this->translate('TXT_SHIT_HAPPENS')), $message);
* Возвращает объект Saver
* Есть смысл вызывать эту функцию только внутри save/saveSata
* во всех остальных случаях она возвращает false
final protected function setSaver(Saver $saver){
* Внутренний метод сохранения
//если в POST не пустое значение значение первичного ключа - значит мы находимся в режиме редактирования
$mode = self::COMPONENT_TYPE_FORM_ALTER;
$mode = self::COMPONENT_TYPE_FORM_ADD;
//создаем объект описания данных
//получаем описание полей для метода
//если в конфиге есть описание полей для метода - загружаем их
if (isset ($configDataDescription->fields)) {
$dataDescriptionObject->loadXML($configDataDescription->fields);
//Создаем объект описания данных взятых из БД
//Загружаем в него инфу о колонках
//Поле с порядком следования убираем из списка
* @todo Надо бы это как то переделать, потому что разбросано получилось
* часть кода относящаяся к обработке колонки с нумерацией здесь, часть
* @see Grid::createDataDescription
$dataObject = new Data();
//Устанавливаем его режим
if($saver->validate() === true) {
$result = $saver->getResult();
//выдвигается пустой exception который перехватывается в методе save
//Если у нас режим вставки и определена колонка для порядка следования, изменяем порядок следования
if (($orderColumn = $this->getOrderColumn()) && ($mode == self::COMPONENT_TYPE_FORM_ADD)) {
$request = 'UPDATE '. $this->getTableName(). ' SET '. $orderColumn. '='. $orderColumn. '+1 '. $this->dbh->buildWhereCondition($this->getFilter());
$this->dbh->modifyRequest($request);
* Переопределенный метод построения
* Перед построением - добавляется перевод
* После построения добавляется информация о закладках
public function build() {
if ($this->getType() == self::COMPONENT_TYPE_LIST) {
$result = parent::build();
* Для действия main не выводим данные
* Для действия save определяем другой формат данных
if ($this->getAction() == self::DEFAULT_ACTION_NAME) {
//Приводим данные к стандартному виду
foreach ($multidata as $langID => $langValues) {
$result[$idx]['lang_id'] = $langID;
foreach ($langValues as $fieldName => $fieldValue) {
$result[$idx][$fieldName] = $fieldValue;
$uploadPath = 'uploads/protected/';
'var doc = window.parent.document;'. "\n".
'var pb = doc.getElementById(\'progress_bar\'); '. "\n".
'var iframe = doc.getElementById(\'uploader\'); '. "\n".
'var fieldId = iframe.getAttribute(\'field\');'. "\n".
'var preview = doc.getElementById(iframe.getAttribute(\'preview\')); '. "\n".
'var path = doc.getElementById(fieldId);'. "\n";
if (empty($_FILES) || !isset ($_FILES['file'])) {
$uploader->setFile($_FILES['file']);
$uploader->upload($uploadPath);
$fileName = $uploader->getFileObjectName();
"doc.window.insertVideo('". Request::getInstance()->getRootPath(). $fileName. "', fieldId);\n";
'pb.parentNode.removeChild(pb); '.
'iframe.parentNode.removeChild(iframe);',
'pb.parentNode.removeChild(pb); '. "\n".
'alert(\''. $this->translate('TXT_SHIT_HAPPENS'). ': '. $e->getMessage(). '\'); '.
'iframe.parentNode.removeChild(iframe); '. "\n";
$responseText = '<html><head/><body><script type="text/javascript">'. $js. '</script></body></html>';
$response->setHeader('Content-Type', 'text/html; charset=UTF-8');
$response->setHeader('Cache-Control', 'no-cache');
$response->write($responseText);
* Метод для заливки файла
* Вызывается в невидимом фрейме и должен отдать HTML страницу включающаю скрипт
final protected function upload() {
if (isset ($_GET['protected'])) {
$uploadPath = 'uploads/protected/';
$uploadPath = 'uploads/private/';
'var doc = window.parent.document;'. "\n".
'var pb = doc.getElementById(\'progress_bar\'); '. "\n".
'var iframe = doc.getElementById(\'uploader\'); '. "\n".
'var path = doc.getElementById(iframe.getAttribute(\'field\'));'. "\n".
'var link = doc.getElementById(iframe.getAttribute(\'link\')); '. "\n".
'var preview = doc.getElementById(iframe.getAttribute(\'preview\')); '. "\n";
if (empty($_FILES) || !isset ($_FILES['file'])) {
$uploader->setFile($_FILES['file']);
$uploader->upload($uploadPath);
$fileName = $uploader->getFileObjectName();
if (in_array($uploader->getExtension(), array('gif', 'png', 'jpg', 'jpeg'))) {
$js .= "preview.setAttribute('src', '". $fileName. "');\n".
"preview.style.display = 'block';\n";
$js .= "preview.removeAttribute('src');\n".
"preview.style.display = 'none';\n";
$js .= "link.innerHTML = '". $fileName. "';\n".
"link.href = '". $fileName. "';\n";
'pb.parentNode.removeChild(pb); '.
'iframe.parentNode.removeChild(iframe);',
'pb.parentNode.removeChild(pb); '. "\n".
'alert(\''. $this->translate('TXT_SHIT_HAPPENS'). ': '. $e->getMessage(). '\'); '.
'iframe.parentNode.removeChild(iframe); '. "\n";
$responseText = '<html><head/><body><script type="text/javascript">'. $js. '</script></body></html>';
$response->setHeader('Content-Type', 'text/html; charset=UTF-8');
$response->write($responseText);
* Выводит компонент: менеджер изображений
$this->imageManager = $this->document->componentManager->createComponent('imagemanager', 'image', 'ImageManager', null);
//$this->imageManager->getAction();
* Выводит компонент: библиотека изображений
$this->fileLibrary = $this->document->componentManager->createComponent('filelibrary', 'share', 'FileLibrary', null);
//$this->fileLibrary->getAction();
* Метод генерящий thumbnail и сохраняющий его в БД
* @param $sourceFileName string имя исходного файла
* @param $destFieldName string имя поля
* @param $width int ширина
* @param $height int высота
* @param $filter array фильтр
* @param $rewrite boolean переписывать ли если уже существует
* @return имя файла - превьюхи
protected function generateThumbnail($sourceFileName, $destFieldName, $width, $height, $filter, $rewrite = true) {
if (!empty($sourceFileName)) {
$destFileName = $dirname. '/'. '.'. $filename. '.'. $width. '-'. $height. '.'. $extension;
$image->loadFromFile($sourceFileName);
$image->resize($width,$height);
$image->saveToFile($destFileName);
$this->dbh->modify(QAL::UPDATE, $this->getTableName(), array($destFieldName=> $destFileName), $filter);
* Устанавливает имя колонки для пользовательской сортировки
* Возвращает имя колонки для пользовательской сортировки
* Метод для изменения порядка следования - вверх
protected function up() {
$this->response->setHeader('Content-Type', 'text/javascript; charset=utf-8');
* Метод для изменения порядка следования - вниз
protected function down() {
$this->response->setHeader('Content-Type', 'text/javascript; charset=utf-8');
* Изменяет порядок следования
* @param string - направление
//Если не задана колонка для пользовательской сортировки то на выход
list ($currentID) = $currentID;
//Определяем order_num текущей страницы
$this->dbh->selectRequest(
'WHERE '. $this->getPK(). ' = %s',
$orderDirection = ($direction == Grid::DIR_DOWN)? QAL::ASC: QAL::DESC;
//Определяем идентификатор записи которая находится рядом с текущей
'WHERE '. $this->getOrderColumn(). ' '. $direction. ' '. $currentOrderNum. ' '. $baseFilter.
$this->dbh->beginTransaction();
array($this->getPK()=> $currentID)
array($this->getPK()=> $neighborID)
$JSONResponse = $this->generateError($e->getCode(), $e->getMessage());
* Метод применеющий фильтр
//$_POST['filter'][$tableName][$fieldName] = значение фильтра
if (isset ($_POST['filter'])) {
$tableName = key($_POST['filter']);
$fieldName = key($_POST['filter'][$tableName]);
$value = trim($_POST['filter'][$tableName][$fieldName]);
//получили текущий фильтр
$currentFilter = str_replace('WHERE', '',$this->dbh->buildWhereCondition($currentFilter)). ' AND ';
//к текущему фильтру присоединяем пользовательский
$this->setFilter($currentFilter. $tableName. '.'. $fieldName. ' LIKE \'%'. $value. '%\' ');
* Метод выводящий данные для печати
$this->setParam('recordsPerPage', false);
* Выводит список в файл в формате CSV
//Если у нас есть таблица с переводами то експортить не получится
$this->setParam('recordsPerPage', false);
$MIMEType = 'application/csv';
$data[0][] = $fieldInfo->getPropertyValue('title');
for ($i= 0; $i < $this->getData()->getRowCount(); $i++ ){
$data[$i+ 1][]= $this->getData()->getFieldByName($fieldName)->getRowData($i);
$data = array_reduce($data, array($this, 'prepareCSVString'));
* Формирует стоку в формате CSV из массива
* Callback для array_reduce
* @param $result результирующая строка
* @param $nextValue array
$result .= $rowDelimiter;
foreach ($nextValue as $fieldValue) {
$row .= $separator. mb_convert_encoding(str_replace(array($separator, $delimiter),array("''",';'),$fieldValue), 'Windows-1251', 'UTF-8'). $separator. $delimiter;
* Добавляет переводы для WYSIWYG при необходимости
* Строит список дополнительных файлов
* Используется в тех случаях когда необходимо создать дополнительную вкладку с приаттачеными к записи файлами
* Сохранение приаттаченных данных должно происходить в методе saveData на общих основаниях
//Добавляем поле с дополнительными файлами
$field = new Field('attached_files');
//Ссылки на добавление и удаление файла
$field->addRowData($attachedFilesData);
$this->getData()->addField($field);
$dd->addFieldDescription($f);
$f = new FieldDescription('upl_is_main');
$f->setType(FieldDescription::FIELD_TYPE_BOOL);
$dd->addFieldDescription($f);
$dd->addFieldDescription($f);
$dd->addFieldDescription($f);
$pathField = $d->getFieldByName('upl_path');
foreach ($pathField as $i => $path) {
$pathField->setRowProperty($i, 'real_image', $path);
$pathField->setRowData($i, $path);
$pathField->setRowProperty($i, 'is_image', true);
return $builder->getResult();
|