[TikiWiki-commits] [Git][tikiwiki/tiki][master] [ENH] Tracker: Implement duplicate item workflow with field-level control
Benoit Grégoire (@benoitg) via TikiWiki-cvs <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <698394ea6b758_3b184570766a2@gitlab-sidekiq-low-urgency-cpu-bound-v2-7d899c995b-g6hwp.mail> |
Benoit Grégoire pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
5a093356 by Moïse Nturubika at 2026-02-04T18:41:14+00:00
[ENH] Tracker: Implement duplicate item workflow with field-level control
---
* [ENH] Tracker: Implement duplicate item workflow with field-level control
See merge request tikiwiki/tiki!9462
- - - - -
5 changed files:
- lib/core/Services/Tracker/Controller.php
- lib/core/Services/Tracker/Utilities.php
- templates/tiki-view_tracker.tpl
- − templates/tracker/clone_item.tpl
- templates/tracker/replace.tpl
Changes:
=====================================
lib/core/Services/Tracker/Controller.php
=====================================
@@ -847,96 +847,6 @@ class Services_Tracker_Controller
return $displayedFields;
}
- public function action_clone_item($input)
- {
- global $prefs;
-
- Services_Exception_Disabled::check('tracker_clone_item');
-
- $trackerId = $input->trackerId->int();
- $definition = Tracker_Definition::get($trackerId);
-
- if (! $definition) {
- throw new Services_Exception_NotFound();
- }
-
- $itemId = $input->itemId->int();
- if (! $itemId) {
- throw new Services_Exception_Denied(tr('No item to clone'));
- }
-
- $itemObject = Tracker_Item::fromId($itemId);
-
- if (! $itemObject->canView()) {
- throw new Services_Exception_Denied(tr("The item to clone isn't visible"));
- }
-
- $newItem = Tracker_Item::newItem($trackerId);
-
- if (! $newItem->canModify()) {
- throw new Services_Exception_Denied(tr("You don't have permission to create new items"));
- }
-
- global $prefs;
- if ($prefs['feature_jquery_validation'] === 'y') {
- $_REQUEST['itemId'] = 0; // let the validation code know this will be a new item
- $validationjs = TikiLib::lib('validators')->generateTrackerValidateJS(
- $definition,
- '',
- '',
- // not custom submit handler that is only needed when called by this service
- 'submitHandler: function(form, event){return process_submit(form, event);}'
- );
- TikiLib::lib('header')->add_jq_onready('$("#cloneItemForm' . $trackerId . '").validate({' . $validationjs . $this->get_validation_options());
- }
-
- $itemObject->asNew();
- $itemData = $itemObject->getData($input);
- $processedFields = [];
-
- $id = 0;
- if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- $itemObject = $this->utilities->cloneItem($definition, $itemData, $itemId);
- $id = $itemObject->getId();
- if ($id === false) {
- throw new Services_Exception_Denied(tr("There were errors cloning the item, please check error messages"));
- }
-
- TikiLib::lib('unifiedsearch')->processUpdateQueue();
- TikiLib::events()->trigger('tiki.process.redirect'); // wait for indexing to complete before loading of next request to ensure updated info shown
-
- $processedItem = $this->utilities->processValues($definition, $itemData);
- $processedFields = $processedItem['fields'];
- }
-
- // sets all fields for the tracker item with their value
- $processedFields = $itemObject->prepareInput($input);
- // fields where the value is forced.
- $forcedFields = $input->forced->none();
-
- // if forced fields are set, remove them from the processedFields since they will not show up visually
- // in the form; they will be set up separately and hidden.
- if (! empty($forcedFields)) {
- foreach ($processedFields as $k => $f) {
- $permName = $f['permName'];
- if (isset($forcedFields[$permName])) {
- unset($processedFields[$k]);
- }
- }
- }
-
- $displayedFields = $this->fieldsToDisplay($input, $processedFields);
-
- return [
- 'title' => tr('Duplicate Item'),
- 'trackerId' => $trackerId,
- 'itemId' => $itemId,
- 'created' => $id,
- 'data' => $itemData['fields'],
- 'fields' => $displayedFields,
- 'forced' => $forcedFields,
- ];
- }
public function action_insert_item($input)
{
@@ -968,6 +878,44 @@ class Services_Tracker_Controller
$fields = $input->fields->none() ?: [];
$forced = $input->forced->none() ?: [];
$editable = $input->editable->none() ?: [];
+
+ $cloneFrom = $input->clone_from->int();
+ if ($cloneFrom && $_SERVER['REQUEST_METHOD'] !== 'POST') {
+ try {
+ $sourceItem = Tracker_Item::fromId($cloneFrom);
+ } catch (Exception $e) {
+ $sourceItem = null;
+ }
+
+ if ($sourceItem && $sourceItem->canView()) {
+ $sourceData = $sourceItem->getData();
+ $duplicateRules = json_decode($definition->getConfiguration('duplicateRules', '[]'), true);
+
+ foreach ($definition->getFields() as $field) {
+ $fid = $field['fieldId'];
+ $permName = $field['permName'];
+ $rule = $duplicateRules[$fid] ?? 'copy';
+
+ if ($rule === 'copy') {
+ if (isset($sourceData['fields'][$permName])) {
+ $value = $sourceData['fields'][$permName];
+ if ($permName) {
+ $input->offsetSet($permName, $value);
+ }
+ $input->offsetSet('ins_' . $fid, $value);
+ }
+ } elseif ($rule === 'clear') {
+ if ($permName) {
+ $input->offsetSet($permName, '');
+ }
+ $input->offsetSet('ins_' . $fid, '');
+ }
+ // default: do nothing, let prepareInput handle defaults
+ }
+
+ Feedback::note(tr('Initializing duplication of item #%0', $cloneFrom));
+ }
+ }
$processedFields = $itemObject->prepareInput($input);
$suppressFeedback = $input->suppressFeedback->bool();
$toRemove = [];
@@ -1165,6 +1113,10 @@ class Services_Tracker_Controller
$util->setTicket();
$item['nextTicket'] = $util->getTicket();
+ if (! $redirect && ! $access->is_xml_http_request()) {
+ $redirect = smarty_modifier_sefurl($trackerId, 'tracker');
+ }
+
if ($redirect) {
//return to page
if ($access->is_xml_http_request()) {
@@ -1210,6 +1162,7 @@ class Services_Tracker_Controller
return [
'title' => $title,
'trackerId' => $trackerId,
+ 'tracker_info' => $definition->getInformation(),
'trackerName' => $trackerName,
'itemId' => $itemId,
'fields' => $displayedFields,
@@ -2217,6 +2170,8 @@ class Services_Tracker_Controller
'name' => $name,
'description' => $input->description->text(),
'descriptionIsParsed' => $input->descriptionIsParsed->int() ? 'y' : 'n',
+ 'duplicateRules' => json_encode($input->asArray('duplicateRules')),
+ 'enableDuplicate' => $input->enableDuplicate->int() ? 'y' : 'n',
], TikiLib::lib('trk')->trackerOptionsFromInput($input));
$trackerId = $this->utilities->updateTracker($trackerId, $data);
@@ -2251,6 +2206,9 @@ class Services_Tracker_Controller
if (! empty($info['descriptionIsParsed']) && $info['descriptionIsParsed'] == 'y') {
$info['description'] = TikiLib::lib('edit')->removeSyntaxPlugin($info['description']);
}
+ if (! empty($info['duplicateRules'])) {
+ $info['val_duplicateRules'] = json_decode($info['duplicateRules'], true);
+ }
return [
'accordion_pos' => $accordion_pos,
@@ -2272,6 +2230,7 @@ class Services_Tracker_Controller
'remoteTabulars' => TikiLib::lib('tabular')->getList(['odbc_config' => new TikiDb_Expr('((odbc_config != ? AND odbc_config IS NOT NULL) OR (api_config != ? AND api_config IS NOT NULL))', ['[]', '[]'])]),
'relationshipBehaviourList' => array_keys(Tiki\Relation\Semantics::BEHAVIOUR_LIST),
'displayTimezone' => TikiLib::lib('tiki')->get_display_timezone(),
+ 'fields' => $trackerId ? $definition->getFields() : [],
];
}
=====================================
lib/core/Services/Tracker/Utilities.php
=====================================
@@ -110,7 +110,7 @@ class Services_Tracker_Utilities
}
if (! $options['validate'] || count($errors) == 0) {
- $newItem = $trklib->replace_item($trackerId, $itemId, ['data' => $fields], $status, 0, $options['bulk_import'], $options['skip_sync'], $options['deleted_files'] ?? [], $options['notify_watchers']);
+ $newItem = $trklib->replace_item($trackerId, $itemId, ['data' => $fields], $status, 0, $options['bulk_import'], $options['skip_sync'], $options['deleted_files'] ?? [], $options['notify_watchers'] ?? null);
return $newItem;
}
=====================================
templates/tiki-view_tracker.tpl
=====================================
@@ -307,9 +307,9 @@
</a>
</action>
{/if}
- {if $tiki_p_create_tracker_items eq 'y' and $prefs.tracker_clone_item eq 'y'}
+ {if $tiki_p_create_tracker_items eq 'y' and ($prefs.tracker_clone_item eq 'y' or $tracker_info.enableDuplicate eq 'y')}
<action>
- <a href="{bootstrap_modal controller=tracker action=clone_item trackerId=$trackerId itemId=$items[user].itemId size='modal-lg'}"
+ <a href="{service controller=tracker action=insert_item trackerId=$trackerId clone_from=$items[user].itemId}"
onclick="$('[data-bs-toggle=popover]').popover('hide');"
>
{icon name="copy" _menu_text='y' _menu_icon='y' alt="{tr}Duplicate{/tr}"}
=====================================
templates/tracker/clone_item.tpl deleted
=====================================
@@ -1,16 +0,0 @@
-{extends $global_extend_layout|default:'layout_view.tpl'}
-
-{block name="title"}
- {title}{$title|escape}{/title}
-{/block}
-
-{block name="content"}
-<form class="simple" method="post" action="{service controller=tracker action=clone_item}" id="cloneItemForm{$trackerId|escape}">
- {trackerfields trackerId=$trackerId fields=$fields itemId=$itemId}
- <div class="submit">
- <input type="hidden" name="itemId" value="{$itemId|escape}">
- <input type="hidden" name="trackerId" value="{$trackerId|escape}">
- <input type="submit" class="btn btn-primary" value="{tr}Duplicate{/tr}">
- </div>
-</form>
-{/block}
=====================================
templates/tracker/replace.tpl
=====================================
@@ -138,6 +138,45 @@
</div>
{/if}
{/accordion_group}
+ {accordion_group title="{tr}Duplication{/tr}" accordion_pos=$accordion_pos}
+ <div class="form-check">
+ <input type="checkbox" class="form-check-input" name="enableDuplicate" id="enableDuplicate" value="1"
+ {if $info.enableDuplicate eq 'y'} checked="checked"{/if}>
+ <label class="form-check-label" for="enableDuplicate">
+ {tr}Enable Duplicate Action{/tr}
+ </label>
+ </div>
+ <div class="mb-3 mx-0 depends" data-on="enableDuplicate">
+ <label class="form-label">{tr}Field Duplication Rules{/tr}</label>
+ <div class="table-responsive">
+ <table class="table table-bordered table-hover">
+ <thead>
+ <tr>
+ <th>{tr}Field{/tr}</th>
+ <th>{tr}Action{/tr}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {foreach from=$fields item=field}
+ <tr>
+ <td>{$field.name|escape}</td>
+ <td>
+ <select name="duplicateRules[{$field.fieldId}]" class="form-select">
+ <option value="copy" {if empty($info.val_duplicateRules[$field.fieldId]) or $info.val_duplicateRules[$field.fieldId] eq 'copy'}selected{/if}>{tr}Copy{/tr}</option>
+ <option value="clear" {if $info.val_duplicateRules[$field.fieldId] eq 'clear'}selected{/if}>{tr}Clear/Empty{/tr}</option>
+ <option value="default" {if $info.val_duplicateRules[$field.fieldId] eq 'default'}selected{/if}>{tr}Set to Default{/tr}</option>
+ </select>
+ </td>
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ </div>
+ <div class="form-text">
+ {tr}Define how each field should be handled when duplicating an item.{/tr}
+ </div>
+ </div>
+ {/accordion_group}
{accordion_group title="{tr}Display{/tr}" accordion_pos=$accordion_pos}
<div class="mb-3 mx-0">
<label class="col-form-label" for="logo">{tr}Logo{/tr}</label>
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/5a09335683c64aa941f1b9838ab62c1d345d12b1
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/5a09335683c64aa941f1b9838ab62c1d345d12b1
You're receiving this email because of your account on gitlab.com.
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs