| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- <?php
- /**
- * The form class file of ZenTaoPMS.
- *
- * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
- * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
- * @author Lu Fei <lufei@easycorp.ltd>
- * @package form
- * @link https://www.zentao.net
- */
- helper::import(dirname(dirname(__FILE__)) . '/filter/filter.class.php');
- class form extends fixer
- {
- /**
- * 批量处理的数据。
- * The data to be fixed.
- *
- * @var object
- * @access public
- */
- public $dataList;
- /**
- * 原始POST数据。
- * The raw post data.
- *
- * @var object
- * @access public
- */
- public $rawdata;
- /**
- * 类型。single|batch
- * Type. single|batch
- *
- * @var string
- */
- protected $formType = 'single';
- /**
- * 原始配置。
- * The raw cofig.
- *
- * @var array
- */
- protected $rawconfig;
- /**
- * 错误信息列表。
- * Error list.
- *
- * @var array
- */
- public $errors;
- /**
- * 构造方法。
- * The construct function.
- *
- * @return void
- */
- public function __construct()
- {
- $this->rawdata = (object)$_POST;
- $this->data = (object)array();
- $this->errors = array();
- }
- /**
- * 获取表单数据。
- * Get the form data.
- *
- * @param array|null $configObject
- * @param int $objectID
- * @param int $flowGroupID
- * @return form
- */
- public static function data($configObject = null, $objectID = 0, $flowGroupID = 0)
- {
- global $app, $config;
- $form = new form;
- if($configObject === null) $configObject = $config->{$app->moduleName}->form->{$app->methodName};
- $configObject = $form->appendExtendFormConfig($configObject, '', '', $objectID, $flowGroupID);
- return $form->config($configObject);
- }
- /**
- * 获取批量表单数据。
- * Get the batch form data.
- *
- * @param array|null $configObject
- * @return form
- */
- public static function batchData($configObject = null)
- {
- global $app, $config;
- $form = new form;
- if($configObject === null) $configObject = $config->{$app->moduleName}->form->{$app->methodName};
- $configObject = $form->appendExtendFormConfig($configObject);
- return $form->config($configObject, 'batch');
- }
- /**
- * 追加工作流新增的字段到表单提交配置。
- * append workflow form config.
- *
- * @param array $config
- * @param string $moduleName
- * @param string $methodName
- * @param int $objectID
- * @param int $flowGroupID
- * @access public
- * @return array
- * @param mixed[] $configObject
- */
- public function appendExtendFormConfig($configObject, $moduleName = '', $methodName = '', $objectID = 0, $flowGroupID = 0)
- {
- global $app, $config;
- if($config->edition == 'open' || !empty($app->installing)) return $configObject;
- $moduleName = $moduleName ? $moduleName : $app->rawModule;
- $methodName = $methodName ? $moduleName : $app->rawMethod;
- /* 项目发布和项目版本用自己的工作流。 */
- if($moduleName == 'projectrelease') $moduleName = 'release';
- if($moduleName == 'projectbuild') $moduleName = 'build';
- if($moduleName == 'projectplan') $moduleName = 'productplan';
- /* 项目复制用项目创建的工作流。 */
- if($moduleName == 'project' && $methodName == 'copyconfirm') $methodName = 'create';
- if($moduleName == 'project' && $methodName == 'edittemplate') $methodName = 'edit';
- /* 用户需求和业务需求用自己的工作流。*/
- if($moduleName == 'story' && $app->rawModule == 'requirement') $moduleName = 'requirement';
- if($moduleName == 'story' && $app->rawModule == 'epic') $moduleName = 'epic';
- /* 复制项目使用项目创建的工作流。 */
- if($moduleName == 'project' && $methodName == 'copyconfirm') $methodName = 'create';
- if(empty($app->control)) return $configObject;
- $app->control->loadModel('workflowgroup');
- if($flowGroupID)
- {
- $group = $app->control->workflowgroup->fetchByID($flowGroupID);
- $groupID = (empty($group) || $group->main) ? 0 : $flowGroupID;
- }
- $groupID = isset($groupID) ? $groupID : $app->control->workflowgroup->getGroupIDByDataID($moduleName, $objectID);
- $flow = $app->control->loadModel('workflow')->getByModule($moduleName, false, $groupID);
- if(!$flow) return $configObject;
- $action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group);
- if(!$action || $action->extensionType != 'extend') return $configObject;
- $uiID = $app->control->loadModel('workflowlayout')->getUIByDataID($flow->module, $action->action, $objectID);
- $fieldList = $app->control->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $flow->group);
- if($action->open != 'none')
- {
- /* Reset the show property of a field according by the linkages. */
- /* 根据界面联动设置重置字段的是否显示属性。*/
- $linkages = zget($action->linkages, $uiID, array());
- foreach($linkages as $linkage)
- {
- if(empty($linkage)) continue;
- $sources = zget($linkage, 'sources', array());
- $targets = zget($linkage, 'targets', array());
- if(empty($sources) || empty($targets)) continue;
- foreach($sources as $source)
- {
- if(!isset($fieldList[$source->field])) continue;
- if(!isset($this->rawdata->{$source->field})) continue;
- $dataValue = $this->rawdata->{$source->field};
- $condition = false;
- if($source->operator == '==') $condition = $dataValue == $source->value;
- if($source->operator == '!=') $condition = $dataValue != $source->value;
- if($source->operator == '>') $condition = $dataValue > $source->value;
- if($source->operator == '>=') $condition = $dataValue >= $source->value;
- if($source->operator == '<') $condition = $dataValue < $source->value;
- if($source->operator == '<=') $condition = $dataValue <= $source->value;
- foreach($targets as $target)
- {
- if(!isset($fieldList[$target->field])) continue;
- $targetField = $fieldList[$target->field];
- if($condition)
- {
- if($target->status == 'show') $targetField->show = 1;
- if($target->status == 'hide') $targetField->show = 0;
- }
- $fieldList[$target->field] = $targetField;
- }
- }
- }
- }
- $layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID, $flow->group);
- $notEmptyRule = $app->control->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
- if($layouts)
- {
- foreach($fieldList as $key => $field)
- {
- if($field->buildin || !$field->show || !isset($layouts[$field->field])) continue;
- if($field->control == 'file') continue;
- $required = !$field->readonly && $notEmptyRule && strpos(",$field->rules,", ",{$notEmptyRule->id},") !== false;
- if($field->control == 'multi-select' || $field->control == 'checkbox')
- {
- $configObject[$field->field] = array('required' => $required, 'type' => 'array', 'default' => array(''), 'filter' => 'join');
- }
- elseif($field->control == 'date' || $field->control == 'datetime')
- {
- $configObject[$field->field] = array('required' => $required, 'type' => $field->control, 'default' => null);
- }
- else
- {
- $type = 'string';
- if($field->type == 'int') $type = 'int';
- if($field->type == 'decimal') $type = 'float';
- $configObject[$field->field] = array('required' => $required, 'type' => $type, 'default' => $field->type == 'int' || $field->type == 'decimal' ? 0 : '');
- if($field->control == 'richtext') $configObject[$field->field]['control'] = 'editor';
- }
- }
- }
- return $configObject;
- }
- /**
- * 设置表单配置项。
- * Set form configuration.
- *
- * @param array $config
- * @param string $type single|batch
- * @return $this
- * @throws EndResponseException
- */
- public function config($config, $type = 'single')
- {
- $config = $this->processRequiredFields($config);
- $this->rawconfig = $config;
- $this->formType = $type;
- if($type == 'single')
- {
- foreach($this->rawconfig as $field => $fieldConfig)
- {
- if(isset($fieldConfig['control']) && in_array($fieldConfig['control'], array('textarea', 'richtext'))) $this->skipSpecial($field);
- $this->convertField($field, $fieldConfig);
- }
- }
- else
- {
- $this->batchConvertField($config);
- $this->skipRequiredCheck($config);
- }
- if(!empty($this->errors))
- {
- $response = array('result' => 'fail', 'message' => $this->errors);
- helper::end(json_encode($response, JSON_UNESCAPED_UNICODE));
- }
- return $this;
- }
- /**
- * 根据必填字段更新表单配置项中的必填属性。
- * Update the required attribute of the form configuration according to the required fields.
- *
- * @param array $configObject
- * @access private
- * @return array
- */
- private function processRequiredFields($configObject)
- {
- global $app, $config, $lang;
- $module = $app->getModuleName();
- $method = $app->getMethodName();
- if(in_array($app->rawMethod, array('requirement', 'torequirement')) && $module == 'story') $module = 'requirement';
- if(in_array($app->rawMethod, array('epic', 'toepic')) && $module == 'story') $module = 'epic';
- if($app->rawModule == 'requirement' && $module == 'story') $module = 'requirement';
- if($app->rawModule == 'epic' && $module == 'story') $module = 'epic';
- if($app->rawModule == 'feedback' && $app->rawMethod == 'touserstory') $module = 'requirement';
- if($app->rawModule == 'feedback' && $app->rawMethod == 'toepic') $module = 'epic';
- if($app->rawModule == 'projectrelease') $module = 'release';
- if($method == 'batchcreate') $method = 'create';
- if($method == 'batchedit') $method = 'edit';
- if($module == 'project' && $module == 'copyconfirm') $method = 'create';
- if(empty($config->$module->$method->requiredFields)) return $configObject;
- $requiredFields = array_unique(array_filter(explode(',', $config->$module->$method->requiredFields)));
- foreach($requiredFields as $field)
- {
- $field = trim($field);
- if(!isset($configObject[$field])) continue;
- if(!isset($configObject[$field]['skipRequired']))
- {
- $configObject[$field]['required'] = true;
- if(empty($lang->{$app->rawModule}->$field)) $lang->{$app->rawModule}->$field = $lang->$module->$field;
- }
- }
- return $configObject;
- }
- /**
- * 批量转换字段类型。
- * Batch convert the field type.
- *
- * @param array $config
- * @return void
- * @param mixed[] $fieldConfigs
- */
- public function batchConvertField($fieldConfigs)
- {
- global $app;
- $rowDataList = array();
- $baseField = '';
- foreach($fieldConfigs as $field => $config)
- {
- /* 在第一行提示类型未定义。 Display type error in the first row. */
- if(!isset($config['type']))
- {
- if(empty($this->errors)) $this->errors[1] = array();
- if(!isset($this->errors[1][$field])) $this->errors[1][$field] = array();
- $this->errors[1][$field][] = "Field '{$field}' need defined type";
- }
- /* 以该字段为标准,判断某一行是否要构造数据。 If the value of the field in a row is empty, skip that row. */
- if(!empty($config['base'])) $baseField = $field;
- if(isset($config['control']) && in_array($config['control'], array('textarea', 'richtext'))) $this->skipSpecial($field);
- }
- /* 在第一行提示标准字段不能为空。 Display the field error in the first row. */
- if(!isset($this->rawdata->$baseField))
- {
- if(empty($this->errors)) $this->errors[1] = array();
- if(!isset($this->errors[1][$field])) $this->errors[1][$field] = array();
- $this->errors[1][$field][] = "Field '{$field}' not empty";
- }
- /* 构造批量表单数据。Construct batch form data. */
- foreach($this->rawdata->$baseField as $rowIndex => $value)
- {
- if(empty($value)) continue;
- $rowData = new stdclass();
- foreach($fieldConfigs as $field => $config)
- {
- $defaultValue = zget($config, 'default', '');
- $rowData->$field = isset($this->rawdata->$field) ? zget($this->rawdata->$field, $rowIndex, $defaultValue) : $defaultValue;
- $rowData->$field = helper::convertType($rowData->$field, $config['type']);
- if(isset($config['filter'])) $rowData->$field = $this->filter($rowData->$field, $config['filter'], zget($config, 'separator', ','));
- /* 检查必填字段。Check required fields. */
- if(isset($config['required']) && $config['required'] && empty($rowData->$field))
- {
- if($app->moduleName == 'task' && $app->methodName == 'batchcreate' && $field == 'estimate' && $this->rawdata->isParent[$rowIndex] == '1') continue;
- $errorKey = isset($config['type']) && $config['type'] == 'array' ? "{$field}[{$rowIndex}][]" : "{$field}[{$rowIndex}]";
- $fieldName = isset($app->lang->{$app->rawModule}->$field) ? $app->lang->{$app->rawModule}->$field : $field;
- if(!isset($this->errors[$errorKey])) $this->errors[$errorKey] = array();
- $this->errors[$errorKey][] = sprintf($app->lang->error->notempty, $fieldName);
- }
- }
- $rowDataList[$rowIndex] = $rowData;
- }
- $this->dataList = $rowDataList;
- }
- /**
- * 跳过必填项检查。
- * Skip the required check.
- *
- * @param array $fieldConfigs
- * @access public
- * @return void
- */
- public function skipRequiredCheck($fieldConfigs)
- {
- foreach($this->dataList as $rowIndex => $rowData)
- {
- foreach($fieldConfigs as $field => $config)
- {
- if(empty($config['required']) || empty($config['skipRequired']) || !empty($rowData->$field)) continue;
- $skip = true;
- $errorKey = isset($config['type']) && $config['type'] == 'array' ? "{$field}[{$rowIndex}][]" : "{$field}[{$rowIndex}]";
- foreach($config['skipRequired'] as $conditionField => $conditionValue)
- {
- if($rowData->$conditionField != $conditionValue)
- {
- $skip = false;
- break;
- }
- }
- if($skip) unset($this->errors[$errorKey]);
- }
- }
- }
- /**
- * 获取$_POST的数据。
- * Get the data of $_POST.
- *
- * @param bool $isRaw 是否获取原始数据。Whether to get the raw data.
- * @return object
- */
- public function getAll($isRaw = false)
- {
- return $isRaw ? $this->rawdata : $this->data;
- }
- /**
- * 转换字段类型。
- * Convert the field type.
- *
- * @param string $field
- * @param array $config
- * @return void
- */
- public function convertField($field, $config)
- {
- global $app;
- if(!isset($config['type']))
- {
- if(!isset($this->errors[$field])) $this->errors[$field] = array();
- $this->errors[$field][] = "Field '{$field}' need defined type";
- }
- if(isset($this->rawdata->$field)) $data = $this->rawdata->$field;
- /* Assign the default value to the data if the default value exists and the data is not exist or null or empty string. */
- if(isset($config['default']) && (!isset($this->rawdata->$field) || is_null($this->rawdata->$field) || $this->rawdata->$field === '')) $data = $config['default'];
- if(isset($data)) $data = helper::convertType($data, $config['type']);
- if(isset($data) && isset($config['filter'])) $data = $this->filter($data, $config['filter'], zget($config, 'separator', ','));
- $emptyData = empty($data);
- if($config['type'] == 'int' || $config['type'] == 'float' || $config['type'] == 'string')
- {
- $emptyData = (isset($this->rawdata->$field) && $this->rawdata->$field === '') || (isset($data) && $data === '');
- }
- if(isset($config['required']) && $config['required'] && isset($this->rawdata->$field) && $emptyData)
- {
- $rawModule = $app->rawModule == 'feedback' && in_array($app->rawMethod, array('touserstory', 'toepic')) ? 'story' : $app->rawModule;
- $errorKey = isset($config['type']) && $config['type'] == 'array' ? "{$field}[]" : $field;
- if(isset($config['label']))
- {
- $fieldName = $config['label'];
- }
- else
- {
- $fieldName = isset($app->lang->{$rawModule}->$field) ? $app->lang->{$rawModule}->$field : $field;
- }
- if(!isset($this->errors[$errorKey])) $this->errors[$errorKey] = array();
- $this->errors[$errorKey][] = sprintf($app->lang->error->notempty, $fieldName);
- }
- $this->data->$field = isset($data) ? $data : null;
- }
- /**
- * Special array.
- *
- * @param mixed $data
- * @access public
- * @return mixed
- */
- public function specialArray($data)
- {
- if(!is_array($data))
- {
- if(is_string($data)) return htmlspecialchars($data, ENT_QUOTES);
- return $data;
- }
- foreach($data as &$value) $value = $this->specialArray($value);
- return $data;
- }
- /**
- * 过滤表单字段数据。
- * Filter the form field data.
- *
- * @param mixed $value
- * @param mixed $filter
- * @param string $separator
- * @access protected
- * @return string
- */
- protected function filter($value, $filter, $separator = ',')
- {
- switch($filter)
- {
- case 'trim':
- return trim($value);
- case 'join':
- return implode($separator, $value);
- default:
- return $value;
- }
- }
- /**
- * 过滤富文字段数据。
- * Filter the editor fields.
- *
- * @param string $fields
- * @access public
- * @return mixed
- */
- public function get($fields = '')
- {
- global $config;
- if($this->formType == 'single') return parent::get($fields);
- foreach($this->dataList as $rowIndex => $data)
- {
- $this->data = $data;
- $this->dataList[$rowIndex] = parent::get($fields);
- $this->stripedFields = array();
- }
- return $this->dataList;
- }
- }
|