Skip to content

Commit 07700ac

Browse files
committed
✨ Enhance form collected list with creator filter, chat time search, and field change tracking 🎉
- 🧑‍💼 Add 'Creator operator' multi-dropdown filter to the search panel, allowing filtering of collected forms by the user who submitted them. - 🔍 Introduce 'Search by chat time' checkbox so users can filter collected entries based on associated chat time rather than collection time. - 📅 Rename table column header from 'Time' to 'Created at' for better clarity on the collected listing. - 🕵️ Implement field change tracking for internal forms: when an operator edits a previously submitted form, log field history (old/new values, modifier) and track which operators modified which fields. - 🛡️ Add safety checks when processing collected information, including validation of field definitions to prevent errors with missing or malformed data. - 📋 Skip 'lhc_field_changes' meta-field when formatting collected content for display and downloads, keeping output clean. - ⚙️ Register new search parameters (creator_user_ids, chat_time) in the module's URL handling and search attribute configuration.
1 parent bc9d948 commit 07700ac

8 files changed

Lines changed: 160 additions & 7 deletions

File tree

lhc_web/design/defaulttheme/tpl/lhform/collected.tpl.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<th><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','Chat Operator');?></th>
2727
<th><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','Department');?></th>
2828
<th><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','Intro');?></th>
29-
<th><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','Time');?></th>
29+
<th><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','Created at');?></th>
3030
<?php if ($form->form_type == erLhAbstractModelForm::FORM_TYPE_PUBLIC) : ?>
3131
<th><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','IP');?></th>
3232
<?php endif; ?>

lhc_web/design/defaulttheme/tpl/lhform/search_panel.tpl.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@
3333
)); ?>
3434
</div>
3535
</div>
36+
<div class="col-md-3">
37+
<div class="form-group">
38+
<label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','Creator operator');?></label>
39+
<?php echo erLhcoreClassRenderHelper::renderMultiDropdown(array(
40+
'input_name' => 'creator_user_ids[]',
41+
'optional_field' => erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','Choose operator'),
42+
'selected_id' => $input->creator_user_ids,
43+
'css_class' => 'form-control',
44+
'display_name' => 'name_official',
45+
'ajax' => 'users',
46+
'list_function_params' => array_merge(erLhcoreClassGroupUser::getConditionalUserFilter(), array('sort' => '`name` ASC', 'limit' => 50)),
47+
'list_function' => 'erLhcoreClassModelUser::getUserList'
48+
)); ?>
49+
</div>
50+
</div>
3651
</div>
3752

3853
<div class="row mb-2">
@@ -110,6 +125,9 @@
110125
</div>
111126
</div>
112127
</div>
128+
<div class="col-md-12">
129+
<label class="col-form-label"><input class="form-check-input" type="checkbox" name="chat_time" <?php $input->chat_time == 1 ? print ' checked="checked" ' : ''?> value="on" /> <?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('chat/lists/search_panel','Search by chat time, not create time')?></label>
130+
</div>
113131
<div class="col-md-12 d-flex align-items-end">
114132
<div class="btn-group me-2">
115133
<button type="submit" class="btn btn-primary btn-sm" name="doSearch"><span class="material-icons">search</span><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('form/collected','Search');?></button>

lhc_web/lib/core/lhabstract/fields/erlhabstractmodelform.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@
8989
'validation_definition' => new ezcInputFormDefinitionElement(
9090
ezcInputFormDefinitionElement::OPTIONAL, 'unsafe_raw'
9191
)),
92+
'track_field_changes' => array(
93+
'type' => 'checkbox',
94+
'main_attr' => 'configuration_array',
95+
'trans' => erTranslationClassLhTranslation::getInstance()->getTranslation('abstract/widgettheme','Track field changes, field change values will be saved per operator.'),
96+
'required' => false,
97+
'hidden' => true,
98+
'nginit' => true,
99+
'validation_definition' => new ezcInputFormDefinitionElement(
100+
ezcInputFormDefinitionElement::OPTIONAL, 'unsafe_raw'
101+
)),
92102
'one_fillment_per_chat' => array(
93103
'type' => 'checkbox',
94104
'main_attr' => 'configuration_array',

lhc_web/lib/core/lhform/lhformrenderer.php

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,29 @@ public static function renderForm($form, $asAdmin = false) {
6565
// Support {args.form.*} and {args.form_collected.*} variable substitution
6666
$translationArgs = array('form' => $form);
6767
if (self::$collectedObject !== false) {
68-
$translationArgs['form_collected'] = self::$collectedObject;
68+
$formCollected = clone self::$collectedObject;
69+
$contentArray = $formCollected->content_array;
70+
if (isset($contentArray['lhc_field_changes'])) {
71+
if (isset($contentArray['lhc_field_changes']['field_history']) && is_array($contentArray['lhc_field_changes']['field_history'])) {
72+
foreach ($contentArray['lhc_field_changes']['field_history'] as $field => $history) {
73+
if (isset($history['user_id'])) {
74+
$contentArray['lhc_field_changes']['field_history'][$field]['user'] = erLhcoreClassModelUser::fetch($history['user_id']);
75+
}
76+
}
77+
}
78+
if (isset($contentArray['lhc_field_changes']['modified_operators']) && is_array($contentArray['lhc_field_changes']['modified_operators'])) {
79+
$modifierNames = [];
80+
foreach ($contentArray['lhc_field_changes']['modified_operators'] as $userId => $modifierData) {
81+
$user = erLhcoreClassModelUser::fetch($userId);
82+
if ($user instanceof erLhcoreClassModelUser) {
83+
$modifierNames[] = $user->name_official;
84+
}
85+
}
86+
$contentArray['lhc_field_changes']['modifiers'] = implode(',', $modifierNames);
87+
}
88+
$formCollected->content_array = $contentArray;
89+
}
90+
$translationArgs['form_collected'] = $formCollected;
6991
}
7092
$contentForm = erLhcoreClassGenericBotWorkflow::translateMessage($contentForm, array('args' => $translationArgs));
7193

@@ -168,7 +190,7 @@ public static function setCollectedInformation($information) {
168190
public static function processInput($inputDefinition, $asAdmin = false) {
169191
if (is_array($inputDefinition)) {
170192
$paramsParsed = $inputDefinition;
171-
} else {
193+
} elseif (is_string($inputDefinition)) {
172194
$inputDefinition = str_replace(array('[[',']]'), '', $inputDefinition);
173195
$paramsInput = explode('||', $inputDefinition);
174196
$defaultType = array_shift($paramsInput);
@@ -186,10 +208,16 @@ public static function processInput($inputDefinition, $asAdmin = false) {
186208

187209
$paramsParsed['as_admin'] = $asAdmin;
188210

189-
return call_user_func('erLhcoreClassFormRenderer::renderInputType'.ucfirst($paramsParsed['type']),$paramsParsed);
211+
$method = 'renderInputType'.ucfirst(isset($paramsParsed['type']) ? $paramsParsed['type'] : '');
212+
if (!method_exists('erLhcoreClassFormRenderer', $method)) {
213+
return 'INVALID_FIELD';
214+
}
215+
216+
return call_user_func('erLhcoreClassFormRenderer::'.$method,$paramsParsed);
190217
}
191218

192219
public static function renderInputTypeRange($params) {
220+
193221
$additionalAttributes = self::renderAdditionalAtrributes($params);
194222

195223
if (ezcInputForm::hasPostData()) {
@@ -973,7 +1001,8 @@ public static function storeCollectedInformation($form, $collectedInformation, $
9731001
// Finish collect information
9741002
foreach ($collectedInformation as $fieldName => & $params) {
9751003
// Do not save file again if it was chat file
976-
if (!in_array($fieldName, $chatAttributes) && $params['definition']['type'] == 'file' && !(isset($params['definition']['scope']) && $params['definition']['scope'] == 'chat')) {
1004+
1005+
if (!in_array($fieldName, $chatAttributes) && isset($params['definition']['type']) && $params['definition']['type'] == 'file' && !(isset($params['definition']['scope']) && $params['definition']['scope'] == 'chat')) {
9771006

9781007
$dir = 'var/storageform/'.date('Y').'y/'.date('m').'/'.date('d').'/'.$formCollected->id.'/';
9791008

@@ -998,6 +1027,60 @@ public static function storeCollectedInformation($form, $collectedInformation, $
9981027
}
9991028
}
10001029

1030+
1031+
if (isset($form->configuration_array['track_field_changes']) && $form->configuration_array['track_field_changes'] == true && $form->form_type == erLhAbstractModelForm::FORM_TYPE_INTERNAL) {
1032+
if ($formCollected->user_id != erLhcoreClassUser::instance()->getUserID()) {
1033+
$currentUserId = erLhcoreClassUser::instance()->getUserID();
1034+
$currentTime = time();
1035+
1036+
// Load previous content to compare against
1037+
$previousContent = $formCollected->content_array;
1038+
if (!is_array($previousContent)) {
1039+
$previousContent = [];
1040+
}
1041+
1042+
// Preserve existing field changes history from previous saves
1043+
$fieldChanges = isset($previousContent['lhc_field_changes']) ? $previousContent['lhc_field_changes'] : ['modified_operators' => [], 'field_history' => []];
1044+
$modifiedFields = [];
1045+
1046+
foreach ($collectedInformation as $fieldName => $params) {
1047+
if ($fieldName === 'lhc_field_changes') continue;
1048+
if (!isset($params['definition']['log_changes']) || $params['definition']['log_changes'] != 'true') continue;
1049+
1050+
$newValue = isset($params['value']) ? $params['value'] : null;
1051+
$oldValue = isset($previousContent[$fieldName]['value']) ? $previousContent[$fieldName]['value'] : null;
1052+
1053+
// Compare values; skip comparison for file fields (track by filename instead)
1054+
if ($params['definition']['type'] == 'file') {
1055+
$newValue = isset($params['filename']) ? $params['filename'] : null;
1056+
$oldValue = isset($previousContent[$fieldName]['filename']) ? $previousContent[$fieldName]['filename'] : null;
1057+
}
1058+
1059+
if ($newValue !== $oldValue) {
1060+
$modifiedFields[] = $fieldName;
1061+
1062+
$fieldChanges['field_history'][$fieldName] = [
1063+
'user_id' => $currentUserId,
1064+
'modified_at' => $currentTime,
1065+
'old_value' => $oldValue,
1066+
'new_value' => $newValue
1067+
];
1068+
}
1069+
}
1070+
1071+
if (!empty($modifiedFields)) {
1072+
$existingFields = isset($fieldChanges['modified_operators'][$currentUserId]['fields']) ? $fieldChanges['modified_operators'][$currentUserId]['fields'] : [];
1073+
$fieldChanges['modified_operators'][$currentUserId] = [
1074+
'fields' => array_unique(array_merge($existingFields, $modifiedFields)),
1075+
'modified_at' => $currentTime
1076+
];
1077+
}
1078+
1079+
$collectedInformation['lhc_field_changes'] = $fieldChanges;
1080+
}
1081+
}
1082+
1083+
10011084
$formCollected->content = json_encode($collectedInformation);
10021085
$formCollected->custom_fields = json_encode($customFields);
10031086
$formCollected->saveThis();

lhc_web/lib/core/lhform/searchattr/collected.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@
2626
)
2727
);
2828

29+
$fieldsSearch['creator_user_ids'] = array(
30+
'type' => 'text',
31+
'trans' => 'Creator',
32+
'required' => false,
33+
'valid_if_filled' => false,
34+
'filter_type' => 'filter',
35+
'filter_table_field' => '`lh_abstract_form_collected`.`user_id`',
36+
'validation_definition' => new ezcInputFormDefinitionElement(
37+
ezcInputFormDefinitionElement::OPTIONAL, 'int', array('min_range' => 0), FILTER_REQUIRE_ARRAY
38+
)
39+
);
40+
2941
$fieldsSearch['timefrom'] = array(
3042
'type' => 'text',
3143
'trans' => 'Time from',
@@ -52,6 +64,18 @@
5264
)
5365
);
5466

67+
$fieldsSearch['chat_time'] = array (
68+
'type' => 'checkbox',
69+
'trans' => 'Search in chat time',
70+
'required' => false,
71+
'valid_if_filled' => false,
72+
'filter_type' => 'none',
73+
'filter_table_field' => '',
74+
'validation_definition' => new ezcInputFormDefinitionElement (
75+
ezcInputFormDefinitionElement::OPTIONAL, 'boolean'
76+
)
77+
);
78+
5579
$fieldSortAttr = array(
5680
'field' => false,
5781
'default' => false,

lhc_web/lib/models/lhabstract/erlhabstractmodelformcollected.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ public function getFormattedContent()
9292
{
9393
$dataCollected = array();
9494
foreach ($this->content_array as $nameAttr => $contentArray) {
95+
96+
if ($nameAttr === 'lhc_field_changes') continue;
97+
9598
$nameLiteral = $contentArray['definition']['name_literal'] ?? 'n/a';
9699
if (isset($contentArray['definition']['type']) && $contentArray['definition']['type'] == 'file') {
97100
$dataCollected[] = $nameLiteral . " - " . erLhcoreClassSystem::getHost() . erLhcoreClassDesign::baseurldirect('user/login') . '/(r)/' . rawurlencode(base64_encode('form/download/' . $this->id . '/' . $nameAttr));

lhc_web/modules/lhform/collected.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@
2222

2323
$needsJoin = !empty($departmentIds) || !empty($userIds);
2424

25+
if ($filterParams['input_form']->chat_time === true) {
26+
$needsJoin = true;
27+
28+
if (isset($filter['filtergte']['ctime'])) {
29+
$filter['filtergte']['`lh_chat`.`time`'] = $filter['filtergte']['ctime'];
30+
unset($filter['filtergte']['ctime']);
31+
}
32+
33+
if (isset($filter['filterlte']['ctime'])) {
34+
$filter['filterlte']['`lh_chat`.`time`'] = $filter['filterlte']['ctime'];
35+
unset($filter['filterlte']['ctime']);
36+
}
37+
}
38+
2539
if ($needsJoin) {
2640
$filter['leftjoin']['lh_chat'] = array('lh_chat.id', 'lh_abstract_form_collected.chat_id');
2741
}

lhc_web/modules/lhform/module.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
$ViewList['collected'] = array(
1313
'params' => array('form_id'),
14-
'uparams' => array('action','id','csfr','department_ids','user_ids','timefrom','timeto','timefrom_hours','timefrom_minutes','timefrom_seconds','timeto_hours','timeto_minutes','timeto_seconds'),
14+
'uparams' => array('creator_user_ids','chat_time','action','id','csfr','department_ids','user_ids','timefrom','timeto','timefrom_hours','timefrom_minutes','timefrom_seconds','timeto_hours','timeto_minutes','timeto_seconds'),
1515
'multiple_arguments' => array(
1616
'department_ids',
17-
'user_ids'
17+
'user_ids',
18+
'creator_user_ids'
1819
),
1920
'functions' => array( 'manage_fm' )
2021
);

0 commit comments

Comments
 (0)