template)) { $name = lcfirst($this->getClassName()); return "window/{$name}.tpl"; } return $this->template; } public function createSQLFields($tablename) { $defaults = []; $required = []; $fields = []; $types = []; $conn = $this->getDbalConnection(); $tm = $conn->getSchemaManager(); $columns = $tm->listTableColumns($tablename); foreach ($columns as $column) { $name = $column->getName(); $fields[] = $name; $defaults[$name] = $column->getDefault(); $required[$name] = ($column->getNotNull() && is_null($defaults[$name])); $types[$name] = $column->getType()->getName(); } $this->defaults = array_merge($defaults, $this->defaults); $this->required = array_merge($required, $this->required); $this->fields = array_merge($fields, $this->fields); $this->types = array_merge($types, $this->types); } protected function collectVariables() { $acn = $this->getAction(); if ($acn == 'erased') { return self::get_vars(); } return $this->get_vars(); } public function get_vars() { $vars = parent::get_vars(); $acn = $this->getAction(); $pageVars = [ 'acn' => $acn, ]; $ID = $this->getID(); if (($acn == 'edit' && !empty($ID)) || $acn == 'add') { if ($acn == 'edit' || ($acn == 'add' && $this->isDuplicate() && !empty($ID))) { $pageVars['data'] = $this->getObject(); if (getVal('Submit')) { $data = $this->getProcessedData(); $pageVars['data'] = array_merge($pageVars['data'], $data); } if (!empty($this->isDuplicate())) { $this->getFields(); $this->duplicateObject($pageVars['data']); $pageVars['duplicate'] = true; } } elseif ($acn == 'add') { $pageVars['data'] = $this->createObject(); } } $vars['type'] = $this->getType(); $flap = getVal('flap', null, 1); $vars['header']['flap'] = empty($flap) ? 1 : $flap; $vars['header']['flap_next'] = getVal('flap_next'); $vars['header']['force_resize'] = getVal('force_resize'); $vars['tabs'] = $this->getTabs(); $vars['body'] = $pageVars; $vars['actionsLocator'] = ServiceContainer::getService(ActionsLocator::class); return $vars; } protected function getType() { if (empty($this->type)) { $type = getVal('s'); if (!$type) { $type = lcfirst($this->getClassName()); } else { $type = substr($type, 0, -4); } $this->type = $type; } return $this->type; } public function setType($type) { $this->type = $type; } public function translateType() { // try to translate type to czech/english $type = $this->getType(); // preferred: // e.g. $txt_str['deliveryPriceLists']['navigation'] = 'Ceníky dopravy' if ($typeName = translate('navigation', $type, true)) { return $typeName; } // e.g. $txt_str['navigation']['deliveryDelivery'] = 'Dopravy' if ($typeName = translate($type, 'navigation', true)) { return $typeName; } return $type; } protected function getFields() { if (empty($this->fields)) { $this->createSQLFields($this->getTableName()); } } protected function getUniques($table = null) { if (!is_null($this->uniques)) { return $this->uniques; } if (empty($table)) { $table = $this->getTableName(); } $conn = $this->getDbalConnection(); $tm = $conn->getSchemaManager(); $indexes = $tm->listTableIndexes($table); $this->uniques = [$this->nameField => true]; foreach ($indexes as $index) { if ($index->isUnique() == true && $index->getName() != 'PRIMARY') { if (isset($index->getColumns()[1])) { $this->uniques[$index->getColumns()[1]] = true; } else { $this->uniques[$index->getName()] = true; } } } return $this->uniques; } public function getData() { $data = getVal('data', null, []); if (!empty($data)) { $data['ID'] = getVal('ID'); } return $data; } /** Process all POSTed data from form. Transform data to SQL fields array */ public function processFormData() { return $this->getData(); } final public function getProcessedData() { if (isset($this->processedData)) { return $this->processedData; } return $this->processedData = $this->processFormData(); } public function setCustomData($data) { $this->updateSQL($this->getTableName(), [ 'data' => empty($data) ? null : json_encode($data), ], ['id' => $this->getID()]); // reset custom data cache $this->custom_data = null; } public function getCustomData() { if (empty($this->custom_data)) { $object = $this->getObject(); $this->unserializeCustomData($object); $this->custom_data = $object['data']; } return $this->custom_data; } public function getTableName() { if (empty($this->tableName)) { $this->tableName = strtolower($this->getClassName()); } return $this->tableName; } private function duplicateObject(&$data) { $uniques = $this->getUniques(); foreach ($uniques as $key => $value) { if (isset($data[$key])) { if ($this->required[$key] == false) { $data[$key] = null; } else { $data[$key] = stringCopy(trim($data[$key])); } } } } protected function createObject() { $data = $this->getData(); return array_merge($this->defaults, $data); } protected function getObject() { $object = $this->fetchObject($this->getTableName(), $this->getID()); if (!$object && $this->getAction() !== 'add') { $errStr = sprintf(translate('errorNotFound', 'base'), $this->translateType(), $this->getID()); throw new NotFoundHttpException($errStr); } return $object; } public function getObjectData() { return $this->getObject(); } // Vypisovani aktivit protected function activityMessage($name, array $additionalData = []) { $acn = $this->getAction(); if ($acn == 'add') { addActivityLog(ActivityLog::SEVERITY_NOTICE, ActivityLog::TYPE_CHANGE, sprintf(translate('activityAdded'), $name), [ ...ActivityLog::addObjectData([$this->getID() => $name], $this->getType()), ...$additionalData, ]); } elseif ($acn == 'edit') { addActivityLog(ActivityLog::SEVERITY_NOTICE, ActivityLog::TYPE_CHANGE, sprintf(translate('activityEdited'), $name), [ ...ActivityLog::addObjectData([$this->getID() => $name], $this->getType()), ...$additionalData, ]); } } protected function getAdditionalActivityData(): array { return []; } public function handle() { try { $acn = $this->getAction(); $ID = $this->getID(); if ($acn == 'add') { $this->tryRights('ADD'); } if ($acn == 'edit') { $this->tryRights('EDIT'); } if ((($acn == 'edit' && (strlen($ID) > 0)) || $acn == 'add') && getVal('Submit')) { $data = $this->getProcessedData(); $missing = $this->checkRequired($data); if (!$missing) { $activityAdditionalData = $this->getAdditionalActivityData(); $SQL = $this->handleUpdate(); if ($SQL) { if ((empty($this->getErrors()) && empty($this->getHTMLErrors())) || $acn == 'add') { if (isset($data[$this->nameField])) { $this->activityMessage($data[$this->nameField], $activityAdditionalData); } $this->returnOK(); } } else { $ErrStr = getTextString('status', 'scripterror'); $this->returnError($ErrStr); } } else { $ErrStr = getTextString('base', 'errorNotAllValid'); $ErrStr .= join(',', $missing); $this->addError($ErrStr); } } elseif ($acn == 'erase' && !empty($ID)) { $this->tryRights('ERASE'); $this->handleDelete(); } else { $this->handleTabs(); parent::handle(); } } catch (Doctrine\DBAL\DBALException $e) { $this->handleException($e); } } public function handleException($e) { if (intval($e->getPrevious()->errorInfo[1]) === 1062) { $ErrStr = 'Duplicitní '; $badFields = $this->getBadValues($this->getTableName(), ['id' => $this->ID]); foreach ($badFields as $value) { $ErrStr .= '{'.$value.'} '; } if (!$badFields) { $ErrStr = "Duplicitní záznam: \n".$e->getMessage(); } $this->addError($ErrStr); } elseif (intval($e->getPrevious()->getCode()) === 45000) { $msg = $e->getPrevious()->getMessage(); $this->addError(mb_substr($msg, mb_strpos($msg, 'EAN'))); } elseif (in_array(intval($e->getPrevious()->getErrorCode()), [1205, 1213])) { $this->addError('Chyba při zpracování požadavku. Zkuste to prosím znovu.'); $sentryLogger = ServiceContainer::getService(SentryLogger::class); $data = json_encode($this->getAllSQLProcesses()); $sentryLogger->captureException(new Exception('Deadlock v administraci!', 1205, $e), ['extra' => ['processes' => $data]]); } else { throw $e; } } // Kontroluje, jestli v datech z formulare jsou vsechny povinne polozky public function checkRequired($data) { $this->getFields(); $required = []; foreach ($data as $key => $value) { if (!is_array($value)) { $value = trim($value); } if (isset($this->required[$key]) && $this->required[$key] == true && $value === '' && ((@$this->types[$key] != 'string' && @$this->types[$key] != 'simple_array' && @$this->types[$key] != 'text') || $key == $this->nameField) ) { $required[] = $key; } } return $required; } // Obecna funkce pro update & insert public function handleUpdate() { $acn = $this->getAction(); $SQL = null; if ($acn == 'add') { $sqlFields = $this->getSQLFields(); $this->insertSQL($this->getTableName(), $sqlFields); $SQL = true; if (empty($ID)) { if (isset($sqlFields['id'])) { $this->setID($sqlFields['id']); } else { $this->setID(sqlInsertID()); } } } elseif ($acn == 'edit') { $sqlFields = $this->getSQLFields(); $this->updateSQL($this->getTableName(), $sqlFields, ['id' => $this->getID()]); if (isset($sqlFields['id'])) { $this->setID($sqlFields['id']); } $SQL = true; } // reset custom data cache $this->custom_data = null; $this->handleTabs(true); return $SQL; } public function forceUpdate() { try { // get actual action $action = $this->getAction(); // change action to edit $this->action = 'edit'; // do update $_REQUEST['Submit'] = 'OK'; $this->createSQLFields($this->getTableName()); $result = $this->handleUpdate(); // return action $this->action = $action; return $result; } catch (Doctrine\DBAL\DBALException $e) { $this->handleException($e); return false; } } public function getIdFromDatabase($field) { $id = returnSQLResult('SELECT id FROM '.getTableName($this->getTableName())." WHERE {$field[0]}='{$field[1]}' "); return $id; } public function getSQLFields($data = null, $fields = null, $defaults = null, $types = null) { if ($data == null) { $data = $this->getProcessedData(); } if ($fields == null) { $fields = $this->fields; } if ($defaults == null) { $defaults = $this->defaults; } if ($types == null) { $types = $this->types; } $sqlField = []; foreach ($fields as $row) { if (array_key_exists($row, $data) && !is_array($data[$row])) { if (!is_null($data[$row])) { $data[$row] = trim($data[$row]); } if (array_key_exists($row, $types)) { $type = $types[$row]; if (($type == Type::DECIMAL) || ($type == Type::FLOAT)) { $this->preparePrice($data[$row]); } } if (isset($data[$row]) && ($data[$row] === '') && (@$defaults[$row] === null) && @!$this->required[$row]) { $sqlField[$row] = null; } else { $sqlField[$row] = $data[$row]; } } } return $sqlField; } // Smazani polozky public function handleDelete() { if ($this->nameField) { $name = sqlQueryBuilder()->select($this->nameField)->from($this->getTableName()) ->andWhere(Operator::equals(['id' => $this->getID()])) ->execute()->fetchOne(); if ($logMessage = translate('activityDeleted', $this->getType(), true)) { $logMessage = sprintf($logMessage, $name); } else { $logMessage = translate('activityDeleted', 'status'); // 'Deleted %s: %s' $logMessage = sprintf($logMessage, $this->getType(), $name); } } try { $res = sqlQueryBuilder()->delete($this->getTableName()) ->andWhere(Operator::equals(['id' => $this->getID()])) ->execute(); if ($res && !empty($logMessage)) { addActivityLog(ActivityLog::SEVERITY_WARNING, ActivityLog::TYPE_CHANGE, $logMessage); } } catch (Doctrine\DBAL\DBALException $e) { switch (intval($e->getPrevious()->errorInfo[1])) { case 1451: $ErrStr = 'Tento objekt je použit a nelze ho smazat.'; $this->returnError($ErrStr); break; default: $this->handleException($e); } } throw new \KupShop\KupShopBundle\Exception\RedirectException("launch.php?s={$this->getName()}.php&acn=erased"); } public function hasRights($name = null) { switch ($name) { case self::RIGHT_DUPLICATE: if ($this->getAction() == 'edit' && $this->getID() != null) { return true; } break; case self::RIGHT_SAVE: // Kdyz mam READ a nemam EDIT, tak nezobrazim save button if (UserRights::hasRights($this->getRightsType(), 'READ') && !UserRights::hasRights($this->getRightsType(), 'EDIT')) { return false; } return true; default: return true; } return true; } public function getBadValues($table = null, $rowIdentifier = []) { if (!empty($table)) { $uniques = $this->getUniques($table); } else { $table = $this->getTableName(); $uniques = []; } $where = ''; foreach ($rowIdentifier as $key => $value) { $where .= " AND {$key}!=:{$key}"; } $data = $this->getData(); $badFields = []; foreach ($uniques as $key => $value) { if ($value) { if (isset($data[$key])) { $SQL = returnSQLResult('SELECT COUNT(*) FROM '.getTableName($table)." WHERE {$key}=:{$key} {$where}", array_merge($data, $rowIdentifier)); if ($SQL > 0) { $badFields[] = $key; } } } } return $badFields; } public function redirect($params = []) { parent::redirect(array_merge(['ID' => $this->getID(), 'acn' => 'edit'], $params)); } public function getShowOnWeb() { if ($this->show_on_web == null) { return false; } if ($this->getID() === null) { return null; } return ['type' => $this->show_on_web, 'id' => $this->getID()]; } public function getNameField() { return $this->nameField; } public function handleTabs($update = false) { $tabs = $this->getTabs(); foreach ($tabs as $tab) { $tab->setID($this->getID()); if ($update) { $tab->handleUpdate(); // reset custom data cache $this->custom_data = null; } else { $tab->handle(); } } } /** * Return WindowTabs. */ protected function getTabs() { if (!is_null($this->tabs)) { return $this->tabs; } $windowTabLocator = ServiceContainer::getService(WindowTabLocator::class, 0); $dispatcher = ServiceContainer::getService('event_dispatcher'); $tabsEvent = new WindowTabsEvent(); $dispatcher->dispatch($tabsEvent, WindowTabsEvent::NAME_PREFIX.$this->getType()); $tabs = array_merge( $windowTabLocator ? $windowTabLocator->getTabs($this->getType()) : [], $tabsEvent->getTabs() ); foreach ($tabs as &$tab) { $tab->setWindow($this); } return $this->tabs = $tabs; } public function handleGetActionSnippet() { $actionName = getVal('action'); /** @var $massActionsLocator ActionsLocator */ $actionsLocator = ServiceContainer::getService(ActionsLocator::class); $action = $actionsLocator->getServiceByActionClassName($actionName); $smarty = createSmarty(true, true); $smarty->assign($action->getVars()); $smarty->display($action->getTemplate()); exit; } public function handleExecuteAction() { $actionName = getVal('action'); /** @var $massActionsLocator ActionsLocator */ $actionsLocator = ServiceContainer::getService(ActionsLocator::class); $action = $actionsLocator->getServiceByActionClassName($actionName); $action->setWindow($this); $data = $this->getData(); $result = $action->execute($data, getVal('config', $_POST, []), $this->getType()); if ($result->isSuccessful()) { if ($result->getHTMLMessage()) { $this->action = 'edit'; $this->addHTMLError($result->getHTMLMessage()); return; } $params = ['action' => null]; if ($result->getRedirect()) { $params = array_merge($params, $result->getRedirect()); } $this->returnOK(!empty($result->getMsg()) ? $result->getMsg() : ('Akce ['.$action->getName().'] byla provedena.'), false, $params); } else { $this->redirect(['acn' => 'edit', 'action' => null, 'ErrStr' => !empty($result->getMsg()) ? $result->getMsg() : ('Akce ['.$action->getName().'] se nezdařila.')]); } } protected function getAllSQLProcesses() { return sqlQuery('SHOW FULL PROCESSLIST')->fetchAllAssociative(); } }