N°3649 - Activity panel: Add tooltip on compose button

This commit is contained in:
Molkobain
2021-02-06 22:55:59 +01:00
parent 40c112d47a
commit 15c5a22d11
7 changed files with 323 additions and 169 deletions

View File

@@ -36,6 +36,9 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Layout:ActivityPanel:Tab:Toolbar:Info:AuthorsCount:Tooltip' => 'Number of persons interacting in the visible entries',
'UI:Layout:ActivityPanel:Tab:Toolbar:Info:MessagesCount:Tooltip' => 'Number of messages in the visible log(s)',
// Compose button
'UI:Layout:ActivityPanel:ComposeButton:Tooltip' => 'Compose a new case log entry',
// Case log entry
'UI:Layout:ActivityPanel:MutltipleEntriesSaveConfirmation:Title' => 'Multiple case logs save',
'UI:Layout:ActivityPanel:MutltipleEntriesSaveConfirmation:Explanation' => 'By pressing the "save" button, you will submit entries for all the edited case logs at once.',

View File

@@ -23,49 +23,52 @@ $(function()
{
// default options
options:
{
datetime_format: null,
datetimes_reformat_limit: 14, // In days
show_multiple_entries_submit_confirmation: true,
},
{
datetime_format: null,
datetimes_reformat_limit: 14, // In days
transaction_id: null, // Null until the user gets the lock on the object
show_multiple_entries_submit_confirmation: true,
},
css_classes:
{
is_expanded: 'ibo-is-expanded',
is_opened: 'ibo-is-opened',
is_closed: 'ibo-is-closed',
is_active: 'ibo-is-active',
is_visible: 'ibo-is-visible',
is_hidden: 'ibo-is-hidden',
is_draft: 'ibo-is-draft',
},
{
is_expanded: 'ibo-is-expanded',
is_opened: 'ibo-is-opened',
is_closed: 'ibo-is-closed',
is_active: 'ibo-is-active',
is_visible: 'ibo-is-visible',
is_hidden: 'ibo-is-hidden',
is_draft: 'ibo-is-draft',
is_current_user: 'ibo-is-current-user',
},
js_selectors:
{
panel_size_toggler: '[data-role="ibo-activity-panel--size-toggler"]',
tab_toggler: '[data-role="ibo-activity-panel--tab-toggler"]',
tab_title: '[data-role="ibo-activity-panel--tab-title"]',
tabs_toolbars: '[data-role="ibo-activity-panel--tabs-toolbars"]',
tab_toolbar: '[data-role="ibo-activity-panel--tab-toolbar"]',
tab_toolbar_action: '[data-role="ibo-activity-panel--tab-toolbar-action"]',
caselog_tab_open_all: '[data-role="ibo-activity-panel--caselog-open-all"]',
caselog_tab_close_all: '[data-role="ibo-activity-panel--caselog-close-all"]',
activity_filter: '[data-role="ibo-activity-panel--filter"]',
activity_filter_options: '[data-role="ibo-activity-panel--filter-options"]',
activity_filter_options_toggler: '[data-role="ibo-activity-panel--filter-options-toggler"]',
activity_filter_option_input: '[data-role="ibo-activity-panel--filter-option-input"]',
authors_count: '[data-role="ibo-activity-panel--tab-toolbar-info-authors-count"]',
messages_count: '[data-role="ibo-activity-panel--tab-toolbar-info-messages-count"]',
compose_button: '[data-role="ibo-activity-panel--add-caselog-entry-button"]',
caselog_entry_form: '[data-role="ibo-caselog-entry-form"]',
caselog_entry_forms_confirmation_dialog: '[data-role="ibo-activity-panel--entry-forms-confirmation-dialog"]',
caselog_entry_forms_confirmation_preference_input: '[data-role="ibo-activity-panel--entry-forms-confirmation-preference-input"]',
entry_group: '[data-role="ibo-activity-panel--entry-group"]',
entry: '[data-role="ibo-activity-entry"]',
entry_medallion: '[data-role="ibo-activity-entry--medallion"]',
entry_main_information: '[data-role="ibo-activity-entry--main-information"]',
entry_datetime: '[data-role="ibo-activity-entry--datetime"]',
edits_entry_long_description: '[data-role="ibo-edits-entry--long-description"]',
edits_entry_long_description_toggler: '[data-role="ibo-edits-entry--long-description-toggler"]',
},
{
panel_size_toggler: '[data-role="ibo-activity-panel--size-toggler"]',
tab_toggler: '[data-role="ibo-activity-panel--tab-toggler"]',
tab_title: '[data-role="ibo-activity-panel--tab-title"]',
tabs_toolbars: '[data-role="ibo-activity-panel--tabs-toolbars"]',
tab_toolbar: '[data-role="ibo-activity-panel--tab-toolbar"]',
tab_toolbar_action: '[data-role="ibo-activity-panel--tab-toolbar-action"]',
caselog_tab_open_all: '[data-role="ibo-activity-panel--caselog-open-all"]',
caselog_tab_close_all: '[data-role="ibo-activity-panel--caselog-close-all"]',
activity_filter: '[data-role="ibo-activity-panel--filter"]',
activity_filter_options: '[data-role="ibo-activity-panel--filter-options"]',
activity_filter_options_toggler: '[data-role="ibo-activity-panel--filter-options-toggler"]',
activity_filter_option_input: '[data-role="ibo-activity-panel--filter-option-input"]',
authors_count: '[data-role="ibo-activity-panel--tab-toolbar-info-authors-count"]',
messages_count: '[data-role="ibo-activity-panel--tab-toolbar-info-messages-count"]',
compose_button: '[data-role="ibo-activity-panel--add-caselog-entry-button"]',
caselog_entry_form: '[data-role="ibo-caselog-entry-form"]',
caselog_entry_forms_confirmation_dialog: '[data-role="ibo-activity-panel--entry-forms-confirmation-dialog"]',
caselog_entry_forms_confirmation_preference_input: '[data-role="ibo-activity-panel--entry-forms-confirmation-preference-input"]',
body: '[data-role="ibo-activity-panel--body"]',
entry_group: '[data-role="ibo-activity-panel--entry-group"]',
entry: '[data-role="ibo-activity-entry"]',
entry_medallion: '[data-role="ibo-activity-entry--medallion"]',
entry_main_information: '[data-role="ibo-activity-entry--main-information"]',
entry_datetime: '[data-role="ibo-activity-entry--datetime"]',
edits_entry_long_description: '[data-role="ibo-edits-entry--long-description"]',
edits_entry_long_description_toggler: '[data-role="ibo-edits-entry--long-description-toggler"]',
},
enums: {
tab_types: {
caselog: 'caselog',
@@ -79,9 +82,9 @@ $(function()
},
// the constructor
_create: function()
{
_create: function () {
this.element.addClass('ibo-activity-panel');
this._bindEvents();
this._UpdateMessagesCounters();
this._UpdateFiltersCheckboxesFromOptions();
@@ -561,26 +564,36 @@ $(function()
* @return {void}
* @private
*/
_HideCaseLogsEntryForms: function()
{
_HideCaseLogsEntryForms: function () {
this.element.find(this.js_selectors.caselog_entry_form).trigger('hide_form.caselog_entry_form.itop');
this.element.find(this.js_selectors.compose_button).removeClass(this.css_classes.is_hidden);
// TODO 3.0.0: Release lock
},
_FreezeCaseLogsEntryForms: function () {
this.element.find(this.js_selectors.caselog_entry_form).trigger('enter_pending_submission_state.caselog_entry_form.itop');
},
_UnfreezeCaseLogsEntryForms: function () {
this.element.find(this.js_selectors.caselog_entry_form).trigger('leave_pending_submission_state.caselog_entry_form.itop');
},
/**
* @returns {Object} The case logs having a new entry and their values, format is {<ATT_CODE_1>: <HTML_VALUE_1>, <ATT_CODE_2>: <HTML_VALUE_2>}
* @private
*/
_GetEntriesFromAllForms: function()
{
_GetEntriesFromAllForms: function () {
const me = this;
let oEntries = {};
this.element.find(this.js_selectors.caselog_entry_form).each(function(){
this.element.find(this.js_selectors.caselog_entry_form).each(function () {
const oEntryFormElem = $(this);
const sEntryFormValue = oEntryFormElem.triggerHandler('get_entry.caselog_entry_form.itop');
if('' !== sEntryFormValue) {
oEntries[oEntryFormElem.attr('data-attribute-code')] = sEntryFormValue;
if ('' !== sEntryFormValue) {
const sCaseLogAttCode = oEntryFormElem.attr('data-attribute-code');
oEntries[sCaseLogAttCode] = {
value: sEntryFormValue,
rank: me.element.find(me.js_selectors.tab_toggler+'[data-tab-type="caselog"][data-caselog-attribute-code="'+sCaseLogAttCode+'"]').attr('data-caselog-rank'),
};
}
});
@@ -615,6 +628,7 @@ $(function()
if (bDoNotShowAgain) {
me._SaveSubmitConfirmationPref();
}
me._HideEntriesSubmitConfirmation();
me._SendEntriesToServer();
}
},
@@ -650,11 +664,67 @@ $(function()
* Send the edited case logs entries to the server
* @private
*/
_SendEntriesToServer: function()
{
_SendEntriesToServer: function () {
const me = this;
const oEntries = this._GetEntriesFromAllForms();
// Put entries in the feed
// Renew transaction ID for inline images
// Proceed only if entries to send
if (Object.keys(oEntries).length === 0) {
return false;
}
// Prepare parameters
let oParams = {
operation: 'add_caselog_entries',
object_class: this._GetHostObjectClass(),
object_id: this._GetHostObjectID(),
transaction_id: this.options.transaction_id,
entries: oEntries,
};
// Freeze case logs
this._FreezeCaseLogsEntryForms();
// Send request to server
$.post(
GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
oParams,
'json'
)
.fail(function (oXHR, sStatus, sErrorThrown) {
// TODO 3.0.0: Maybe we could have a centralized dialog to display error messages?
alert(sErrorThrown);
})
.done(function (oData) {
if (false === oData.success) {
// TODO 3.0.0: Same comment as the fail() callback
alert(oData.error_message);
return false;
}
// Update the feed
for (let sCaseLogAttCode in oData.entries) {
me._AddEntry(sCaseLogAttCode, oData.entries[sCaseLogAttCode]);
}
me._ApplyEntriesFilters();
// For now, we don't hide the forms as the user may want to add something else
me.element.find(me.js_selectors.caselog_entry_form).trigger('clear_entry.case_entry_form.itop');
// TODO 3.0.0: Redirect to transition page if necessary (buttons need to be added)
// // Redirect to stimulus
// if(sStimulusCode !== null){
// window.location.href = GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=stimulus&class='+sObjClass+'&id='+sObjId+'&stimulus='+sStimulusCode;
// }
// TODO 3.0.0: If no stimulus
// On done, lock was release, remove message
// On done, renew transaction ID
})
.always(function () {
// Always, unfreeze case logs
me._UnfreezeCaseLogsEntryForms();
});
},
// - Helpers on messages
@@ -787,7 +857,7 @@ $(function()
// ... except the selected
for (let sCaseLogAttCode of aOptions) {
this.element.find(sEntrySelector + '[data-entry-caselog-attribute-code="' + sCaseLogAttCode + '"]').removeClass(this.css_classes.is_hidden);
this.element.find(sEntrySelector+'[data-entry-caselog-attribute-code="'+sCaseLogAttCode+'"]').removeClass(this.css_classes.is_hidden);
}
}
// General case
@@ -797,49 +867,63 @@ $(function()
this._UpdateEntryGroupsVisibility();
},
_UpdateEntryGroupsVisibility: function()
{
_UpdateEntryGroupsVisibility: function () {
const me = this;
this.element.find(this.js_selectors.entry_group).each(function(){
if($(this).find(me.js_selectors.entry + ':not(.' + me.css_classes.is_hidden + ')').length === 0)
{
this.element.find(this.js_selectors.entry_group).each(function () {
if ($(this).find(me.js_selectors.entry+':not(.'+me.css_classes.is_hidden+')').length === 0) {
$(this).addClass(me.css_classes.is_hidden);
}
else
{
} else {
$(this).removeClass(me.css_classes.is_hidden);
}
});
},
_GetNewEntryGroup: function()
{
let AjaxNewEntryGroupDeferred = jQuery.Deferred();
const me = this;
var oParams = {
'operation' : 'new_entry_group',
'caselog_new_entry': sData,
'caselog_attcode' : sCaselog,
/**
* Add an entry represented by its oData to the case log with the sCaseLogAttCode
*
* @param sCaseLogAttCode {string}
* @param oData {Object} Structured data of the entry: {html_rendering: <HTML_DATA>}
* @private
*/
_AddEntry: function (sCaseLogAttCode, oData) {
// Info about the new entry
const oNewEntryElem = $(oData.html_rendering);
const sNewEntryAuthorLogin = oNewEntryElem.attr('data-entry-author-login');
const sNewEntryOrigin = oNewEntryElem.attr('data-entry-group-origin');
// Info about the last entry group to see the entry to add should be in this one or a new one
const oLastEntryGroupElem = this.element.find(this.js_selectors.entry_group+':first');
const sLastEntryAuthorLogin = oLastEntryGroupElem.length > 0 ? oLastEntryGroupElem.attr('data-entry-author-login') : null;
const sLastEntryOrigin = oLastEntryGroupElem.length > 0 ? oLastEntryGroupElem.attr('data-entry-group-origin') : null;
let oTargetEntryGroup = null;
if ((sLastEntryAuthorLogin === sNewEntryAuthorLogin) && (sLastEntryOrigin && sNewEntryOrigin)) {
oTargetEntryGroup = oLastEntryGroupElem;
} else {
oTargetEntryGroup = this._CreateEntryGroup(sNewEntryAuthorLogin, sNewEntryOrigin);
}
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data){
AjaxNewEntryGroupDeferred.resolve(data);
});
return AjaxNewEntryGroupDeferred.promise();
oTargetEntryGroup.prepend(oNewEntryElem);
this._ReformatDateTimes();
},
/**
* Create an entry group and add it to the activity panel
*
* @param sAuthorLogin {string}
* @param sOrigin {string}
* @returns {Object} jQuery object representing the created entry group
* @private
*/
_CreateEntryGroup: function (sAuthorLogin, sOrigin) {
// Note: When using the ActivityPanel, there should always be at least one entry group already, the one from the object creation
let oEntryGroupElem = this.element.find(this.js_selectors.entry_group+':first')
.clone()
.attr('data-entry-author-login', sAuthorLogin)
.attr('data-entry-group-origin', sOrigin)
.addClass(this.css_classes.is_current_user)
.html('')
.prependTo(this.element.find(this.js_selectors.body));
return oEntryGroupElem;
},
AddEntry: function(sEntry, sOrigin)
{
let aEntryGroup = this.element.find(this.js_selectors.entry_group)
let sAuthorLogin = $(sEntry).attr('data-entry-author-login');
if (aEntryGroup.length > 0 && $(aEntryGroup[0]).attr('data-entry-group-author-login') === sAuthorLogin && $(aEntryGroup[0]).attr('data-entry-group-origin') === sOrigin)
{
$(aEntryGroup[0]).prepend(sEntry);
this._ReformatDateTimes();
}
else
{
// TODO 3.0.0 Create a new entry group
window.location.reload();
}
}
});
});

View File

@@ -60,8 +60,8 @@ $(function() {
const aMandatoryOptions = ['object_class', 'object_id', 'attribute_code'];
for (let sOption of aMandatoryOptions) {
if (null === this.options[sOption]) {
CombodoGlobalToolbox.Trace('CaseLogEntryForm: Could not initialize widget, make sure that the following options' +
' are passed: ' + aMandatoryOptions.join(' / '), 'error');
CombodoJSConsole.Error('CaseLogEntryForm: Could not initialize widget, make sure that the following options'+
' are passed: '+aMandatoryOptions.join(' / '), 'error');
return false;
}
}
@@ -88,17 +88,16 @@ $(function() {
CKEDITOR.on('instanceReady', function(oEvent){
// Handle only the current CKEditor instance
if(oEvent.editor.name === me.options.text_input_id) {
CKEDITOR.instances[me.options.text_input_id].on('change', function(){
me._GetCKEditorInstance().on('change', function () {
const bWasDraftBefore = me.is_draft;
const bIsDraftNow = !me._IsInputEmpty();
if(bWasDraftBefore !== bIsDraftNow) {
if (bWasDraftBefore !== bIsDraftNow) {
me.is_draft = bIsDraftNow;
me._UpdateEditingVisualHint();
if(me._IsSubmitAutonomous()) {
if (me._IsSubmitAutonomous()) {
me._UpdateSubmitButtonState();
}
else {
} else {
me._UpdateBridgeInput();
}
}
@@ -107,10 +106,10 @@ $(function() {
});
// Form buttons
this.element.find(this.js_selectors.cancel_button).on('click', function(oEvent){
this.element.find(this.js_selectors.cancel_button).on('click', function (oEvent) {
me.element.trigger('cancelled_form.caselog_entry_form.itop');
});
this.element.find(this.js_selectors.save_button).on('click', function(oEvent){
this.element.find(this.js_selectors.save_button).on('click', function (oEvent) {
// Avoid form being submitted
oEvent.preventDefault();
@@ -118,58 +117,39 @@ $(function() {
});
// Form show/hide
this.element.on('show_form.caselog_entry_form.itop', function(){
this.element.on('show_form.caselog_entry_form.itop', function () {
me._ShowEntryForm();
});
this.element.on('hide_form.caselog_entry_form.itop', function(){
this.element.on('hide_form.caselog_entry_form.itop', function () {
me._HideEntryForm();
});
// Form pending submission states
this.element.on('enter_pending_submission_state.caselog_entry_form.itop', function () {
me._EnterPendingSubmissionState();
});
this.element.on('leave_pending_submission_state.caselog_entry_form.itop', function () {
me._LeavePendingSubmissionState();
});
// Get the entry value
this.element.on('get_entry.caselog_entry_form.itop', function(){
this.element.on('get_entry.caselog_entry_form.itop', function () {
return me._GetInputData();
});
// Caselog selection
// TODO 3.0.0: Remove this as it is no longer useful
this.element.on('add_to_caselog.caselog_entry_form.itop', function(oEvent, oData){
const sCaseLogAttCode = oData.caselog_att_code;
const sStimulusCode = oData.stimulus_code !== undefined ? oData.stimulus_code : null;
me._SubmitEntryToCaselog(me._GetInputData(), sCaseLogAttCode, sStimulusCode);
});
},
_SubmitEntryToCaselog: function(sEntryContent, sCaselogAttCode, sStimulusCode = null){
const me = this;
const sObjClass = this.element.closest(this.js_selectors.activity_panel).attr('data-object-class');
const sObjId = this.element.closest(this.js_selectors.activity_panel).attr('data-object-id');
let oParams = {
'operation' : 'add_caselog_entry',
'class' : sObjClass,
'id' : sObjId,
'caselog_new_entry': sEntryContent,
'caselog_attcode' : sCaselogAttCode,
'caselog_rank' : this.element.closest(this.js_selectors.activity_panel).activity_panel('GetCaseLogRank', sCaselogAttCode),
}
//TODO 3.0.0 Handle errors
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(sNewEntry){
me.element.closest(me.js_selectors.activity_panel).activity_panel('AddEntry', sNewEntry, 'caselog:' + sCaselogAttCode)
// Clear the entry value
this.element.on('clear_entry.case_entry_form.itop', function () {
me._EmptyInput();
me._HideEntryForm();
// Redirect to stimulus
if(sStimulusCode !== null){
window.location.href = GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=stimulus&class='+sObjClass+'&id='+sObjId+'&stimulus='+sStimulusCode;
}
});
},
// Helpers
_IsSubmitAutonomous: function() {
_IsSubmitAutonomous: function () {
return this.options.submit_mode === this.enums.submit_mode.autonomous;
},
// - Form
_GetCKEditorInstance: function () {
return CKEDITOR.instances[this.options.text_input_id];
},
_ShowEntryForm: function () {
this.element.closest(this.js_selectors.activity_panel).find(this.js_selectors.form).removeClass(this.css_classes.is_closed);
},
@@ -178,6 +158,16 @@ $(function() {
// TODO 3.0.0: This should also clear the form (input, lock, send button, ...)
},
_EnterPendingSubmissionState: function () {
this._GetCKEditorInstance().setReadOnly(true);
this.element.find(this.js_selectors.cancel_button).prop('disabled', true);
this.element.find(this.js_selectors.save_button).prop('disabled', true);
},
_LeavePendingSubmissionState: function () {
this._GetCKEditorInstance().setReadOnly(false);
this.element.find(this.js_selectors.cancel_button).prop('disabled', false);
this.element.find(this.js_selectors.save_button).prop('disabled', false);
},
// - Bridged form input
/**
* Return the general object form element.
@@ -186,7 +176,7 @@ $(function() {
* @returns {null|jQuery.fn.init|jQuery|HTMLElement}
* @private
*/
_GetGeneralFormElement: function() {
_GetGeneralFormElement: function () {
const oActivityPanelElem = this.element.closest(this.js_selectors.activity_panel);
const sHostObjClass = oActivityPanelElem.attr('data-object-class');
const sHostObjId = oActivityPanelElem.attr('data-object-id');
@@ -194,7 +184,7 @@ $(function() {
const oGeneralFormElem = $('.object-details[data-object-class="'+sHostObjClass+'"][data-object-id="'+sHostObjId+'"] > form');
// Protection in case this is called with non editable general form
if(oGeneralFormElem.length === 0) {
if (oGeneralFormElem.length === 0) {
return null;
}
@@ -212,9 +202,7 @@ $(function() {
const oGeneralFormElem = this._GetGeneralFormElement();
if(oGeneralFormElem === null) {
if(window.console && window.console.error){
console.error('Could not add bridge input as there is no general form');
}
CombodoJSConsole.Error('CaseLogEntryForm: Could not add bridge input as there is no general form');
return false;
}

View File

@@ -17,7 +17,6 @@
* You should have received a copy of the GNU Affero General Public License
*/
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntryFactory;
use Combodo\iTop\Controller\AjaxRenderController;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
@@ -2650,27 +2649,20 @@ EOF
}
$oPage->add(json_encode($aResult));
break;
case 'add_caselog_entry':
// TODO 3.0.0: Handle errors & rights
$sClass = utils::ReadPostedParam('class', '', 'class');
$sClassLabel = MetaModel::GetName($sClass);
$id = utils::ReadPostedParam('id', '');
// TODO 3.0.0 Handle transactions token which is not passed yet
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
$sCaseLogAttCode = utils::ReadPostedParam('caselog_attcode', '');
$sCaseLogNewEntry = utils::ReadPostedParam('caselog_new_entry', '', 'raw');
$iCaseLogRank = utils::ReadPostedParam('caselog_rank', 0, 'integer');
if($id !== 0 && MetaModel::IsValidClass($sClass))
{
$oObj = MetaModel::GetObject($sClass, $id);
$oObj->Set($sCaseLogAttCode, $sCaseLogNewEntry);
$oObj->DBWrite();
// Activity panel
case 'add_caselog_entries':
$oPage->SetContentType('application/json');
try {
$aResult = AjaxRenderController::AddCaseLogsEntries();
}
$oNewEntry = ActivityEntryFactory::MakeFromCaseLogEntryArray($sCaseLogAttCode, $oObj->Get($sCaseLogAttCode)->GetAsArray()[0]);
$oNewEntry->SetCaseLogRank($iCaseLogRank);
$oPage->AddUiBlock($oNewEntry);
break;
case 'new_entry_group':
catch (Exception $oException) {
$aResult = [
'success' => false,
'error_message' => $oException->getMessage(),
];
}
$oPage->add(json_encode($aResult));
break;
case 'get_menus_count':

View File

@@ -13,12 +13,15 @@ use BulkExport;
use BulkExportException;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntryFactory;
use Combodo\iTop\Renderer\BlockRenderer;
use DBObjectSearch;
use DBObjectSet;
use DBSearch;
use Dict;
use Exception;
use ExecutionKPI;
use InlineImage;
use MetaModel;
use utils;
@@ -367,4 +370,85 @@ class AjaxRenderController
return $bRet;
}
/**
* Add new entries to some of the object's (identified by posted parameters) case logs
*
* @return array The status of the update, a renewed transaction ID and the entries as HTML so they can be append to the front.
* [
* 'success' => true,
* 'entries' => [
* '<ATT_CODE_1>' => [
* html_rendering => '<HTML_RENDERING_TO_BE_APPEND_IN_FRONT_END>',
* ],
* '<ATT_CODE_2>' => [
* html_rendering => '<HTML_RENDERING_TO_BE_APPEND_IN_FRONT_END>',
* ],
* ...
* ],
* 'renewed_transaction_id' => '<RENEWED_TRANSACTION_ID>',
* ]
*
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \OQLException
* @throws \ReflectionException
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*/
public static function AddCaseLogsEntries(): array
{
$sObjectClass = utils::ReadPostedParam('object_class', null, utils::ENUM_SANITIZATION_FILTER_CLASS);
$sObjectId = utils::ReadPostedParam('object_id', 0);
$sTransactionId = utils::ReadPostedParam('transaction_id', null, utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID);
// TODO 3.0.0: Remove this line when transaction handled
$sTransactionId = 'foo';
$aEntries = utils::ReadPostedParam('entries', [], utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
// Consistency checks
// - Mandatory parameters
if (empty($sObjectClass) || empty($sObjectId) || empty($sTransactionId) || empty($aEntries)) {
throw new Exception('Missing mandatory parameters object_class / object_id / transaction_id / entries');
}
// - Transaction ID
// TODO 3.0.0: Uncomment when transaction ID is passed (retrieved from the upcoming lock system)
// if (!utils::IsTransactionValid($sTransactionId)) {
// throw new Exception(Dict::S('iTopUpdate:Error:InvalidToken'));
// }
$aResults = [
'success' => true,
'entries' => [],
];
// Note: Will trigger an exception if object does not exists or not accessible to the user
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId);
foreach ($aEntries as $sAttCode => $aData) {
// Add entry to object
$oObject->Set($sAttCode, $aData['value']);
// Make entry rendering to send back to the front
$aEntryAsArray = $oObject->Get($sAttCode)->GetAsArray()[0];
$oEntryBlock = ActivityEntryFactory::MakeFromCaseLogEntryArray($sAttCode, $aEntryAsArray);
$oEntryBlock->SetCaseLogRank((int)$aData['rank']);
$sEntryAsHtml = BlockRenderer::RenderBlockTemplates($oEntryBlock);
$aResults['entries'][$sAttCode] = [
'html_rendering' => $sEntryAsHtml,
];
}
$oObject->DBWrite();
// Finalize inline images
InlineImage::FinalizeInlineImages($oObject);
// Renew transaction ID
utils::RemoveTransaction($sTransactionId);
$aResults['renewed_transaction_id'] = utils::GetNewTransactionId();
return $aResults;
}
}

View File

@@ -1,7 +1,7 @@
<div id="{{ oUIBlock.GetId() }}" class="ibo-activity-panel" data-role="ibo-activity-panel" data-object-class="{{ oUIBlock.GetObjectClass() }}" data-object-id="{{ oUIBlock.GetObjectId() }}" data-object-mode="{{ oUIBlock.GetObjectMode() }}">
<div class="ibo-activity-panel--header">
<div class="ibo-activity-panel--tabs-togglers" data-role="ibo-activity-panel--tabs-togglers">
{% for sCaseLogAttCode, aCaseLogData in oUIBlock.GetCaseLogTabs() %}
<div class="ibo-activity-panel--header" data-role="ibo-activity-panel--header">
<div class="ibo-activity-panel--tabs-togglers" data-role="ibo-activity-panel--tabs-togglers">
{% for sCaseLogAttCode, aCaseLogData in oUIBlock.GetCaseLogTabs() %}
{% set sExtraCSSClass = (loop.index == 1) ? 'ibo-is-active' : '' %}
<div class="ibo-activity-panel--tab-toggler ibo-activity-panel--tab-toggler-for-caselog ibo-activity-panel--tab-toggler-for-caselog-{{ loop.index }} {{ sExtraCSSClass }}"
data-role="ibo-activity-panel--tab-toggler"
@@ -55,10 +55,13 @@
</div>
</div>
{% if oUIBlock.HasAnEditableCaseLogTab() and oUIBlock.IsCaseLogsSubmitAutonomous() %}
<a href="#" class="ibo-activity-panel--add-caselog-entry-button" data-role="ibo-activity-panel--add-caselog-entry-button"><i class="fas fa-feather"></i></a>
{% endif %}
<div class="ibo-activity-panel--body">
{% if oUIBlock.GetGroupedEntries()|length > 0 %}
<a href="#" class="ibo-activity-panel--add-caselog-entry-button" data-role="ibo-activity-panel--add-caselog-entry-button"
data-tooltip-content="{{ 'UI:Layout:ActivityPanel:ComposeButton:Tooltip'|dict_s }}">
<i class="fas fa-feather"></i>
</a>
{% endif %}
<div class="ibo-activity-panel--body" data-role="ibo-activity-panel--body">
{% if oUIBlock.GetGroupedEntries()|length > 0 %}
{% for aEntryGroup in oUIBlock.GetGroupedEntries() %}
{{ include('base/layouts/activity-panel/entry-group.html.twig', {aEntryGroup: aEntryGroup}) }}
{% endfor %}

View File

@@ -1,4 +1,4 @@
$('#{{ oUIBlock.GetId() }}').activity_panel({
datetime_format: {{ oUIBlock.GetDateTimeFormatForJSWidget()|json_encode|raw }},
datetime_format: {{ oUIBlock.GetDateTimeFormatForJSWidget()|json_encode|raw }},
show_multiple_entries_submit_confirmation: {{ oUIBlock.GetShowMultipleEntriesSubmitConfirmation()|var_export }}
});