Support

Akeeba Ticket System

#20859 Multiple "update_clicks" Ajax call

Posted in ‘Akeeba Ticket System for Joomla! 4 & 5’
This is a public ticket

Everybody will be able to see its contents. Do not include usernames, passwords or any other sensitive information.

Environment Information

Joomla! version
n/a
PHP version
n/a
Akeeba Ticket System version
n/a

Latest post by user83594 on Monday, 08 September 2014 09:19 CDT

user83594
Hi,
I'm testing Akeeba Ticket System on my dev site.

I think I found an issue in the file "instantreply.js".

This piece of code is executed every time the component search for relate results (in public ticket or docs).
jQuery('#ats-instantreply-grid').on('click', 'a', function(){
	var click_type = jQuery(this).data('source');

	jQuery.ajax('index.php?option=com_ats&view=attempts&format=json', {
		dataType : 'json',
		cache    : false,
		data     : {
			task            : 'save',
			ats_attempt_id  : ats_instant_statistics,
			update_clicks   : click_type
		}
	});
})


If you change many time the topic of the ticket and after you click on the "View" button of one related results this call is made to update_clicks:
http://127.0.0.1:8080/index.php/support/index.php?option=com_ats&view=attempts&format=json&task=save&ats_attempt_id=25&update_clicks=docimport&_=1409840809896
The call was repeated for the same number of times the component has searched for related results.

The result is that the row in the table "#__ats_attempts" will be updated with the wrong number of clicks.
In my test the value of the column "docimport_clicks" before the click of the "View" button was 0 and 5 after, I expected it was 1. (called 5 times)

I moved that code out of the "element.keyup()" event and now it seems to work fine and count clicks the correct amount of times.
I have tested the new javascript only on chrome version 37.
See "instantreply.txt" attachment (renamed from instantreply.js).

It's right?

Thanks
Demis

nicholas
Akeeba Staff
Manager
No, you are not correct. If you look at the code you'll see that the first save has ats_instant_statistics=0. When we get a success this now gets a value. Subsequent calls are made with this statistics record ID, replacing the record. This allows us to have a clear picture of the ticket title when the user abandoned the ticket creation page without creating a new ticket. It is supposed to send multiple AJAX requests.

Nicholas K. Dionysopoulos

Lead Developer and Director

πŸ‡¬πŸ‡·Greek: native πŸ‡¬πŸ‡§English: excellent πŸ‡«πŸ‡·French: basic β€’ πŸ• My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

user83594
I checked the code again and I still think that something isn't right.
I try to explain better.

This two ajax calls are involved:

// Instant search statistics
jQuery.ajax('index.php?option=com_ats&view=attempts&format=json', {
	dataType : 'json',
	cache    : false,
	data     : {
		task            : 'save',
		ats_attempt_id  : ats_instant_statistics,
		title           : searchQuery,
		ats_category_id : jQuery('#ticket_catid').val(),
		modified_on     : ''
	},
	success : function(response){
		ats_instant_statistics = response.ats_attempt_id;
		jQuery('input[name="ats_attempt_id"]').val(ats_instant_statistics);
	}
});


jQuery('#ats-instantreply-grid').on('click', 'a', function(){
	var click_type = jQuery(this).data('source');

	jQuery.ajax('index.php?option=com_ats&view=attempts&format=json', {
		dataType : 'json',
		cache    : false,
		data     : {
			task            : 'save',
			ats_attempt_id  : ats_instant_statistics,
			update_clicks   : click_type
		}
	});
})


The first piece of code create or update a row on the table "#__ats_attempts" and return the id and store it in the global variable "ats_instant_statistics" and in the "ats_attempt_id" form input.
Only the first call create a new row, the next calls update the same row updating "title" and "ats_category_id".

The second piece of code register an event that increment the column "ticket_clicks" or "docimport_clicks" when the user press the "View" button on a related result.


----- Now check the case 1: -----
An user press the "New ticket" button.

The user write something into the topic for example "search1" in the category "1".

The first piece of code create the row and set the column "ats_category_id" with 1 and "title" with "search1" and return the id.
The second piece of code register the event but the inner function is not called. (***)

If the user press a "View" button (suppose on a related doc) the inner function of the second piece of code is called and update the column "docimport_clicks" changing is value from 0 to 1.

If the user press a "View" button (suppose on a ticket) the inner function of second piece of code is called and update the column "ticket_clicks" changing is value from 0 to 1.

If the user create the ticket the "onAfterSave" method on "AtsControllerNewtickets" update the row on "#__ats_attempts" setting the column "ats_ticket_id" with the id of the new created ticket.

Everything is fine.
--------------------------------------


----- Now check the case 2: -----
An user press the "New ticket" button.

The user write something into the topic for example "search1" in the category "1".

The first piece of code create the row and set the column "ats_category_id" with 1 and "title" with "search1" and return the id;
The second piece of code register the event but the inner function is not called. (***)

If the user press a "View" button (suppose on a related doc) the inner function of the second piece of code is called and update the column "docimport_clicks" changing is value from 0 to 1.

Now the user change the topic to "search2" in the category "1".

The first piece of code update the row and set the column "ats_category_id" with 1 and "title" with "search2" and return the same id;
The second piece of code register the event but the inner function is not called. (***)

If the user press a "View" button (suppose on a ticket) the inner function of the second piece of code is called (twice) and update the column "ticket_clicks" changing is value from 0 to 1 (first call) and from 1 to 2 (second call).
I think is not correct because the user clicked just one time.

If the user create the ticket the "onAfterSave" method on "AtsControllerNewtickets" update the row on "#__ats_attempts" setting the column "ats_ticket_id" with the id of the new created ticket.

If you check the statistic you see the wrong number of access to the tickets (2 instead 1).
--------------------------------------


With the change that I made to the javascript the step marked with (***) is execute just one time on "akeeba.jQuery(document).ready()" event.
The variable "ats_instant_statistics" is global and when the inner function of the second piece of code is called contains the correct id value (at least on my browser).


With "the inner function of the second piece of code" I mean this:
var click_type = jQuery(this).data('source');

jQuery.ajax('index.php?option=com_ats&view=attempts&format=json', {
	dataType : 'json',
	cache    : false,
	data     : {
		task            : 'save',
		ats_attempt_id  : ats_instant_statistics,
		update_clicks   : click_type
	}
});

nicholas
Akeeba Staff
Manager
We reviewed the code and we found that the way it's being handled right now is inevitable. In any other way we could not record a user doing this:

Type title "foo"
Click on the proposed solution links. Didn't like them
Change the title to "bar"
Click on a few proposed solution links.

Maybe you would say that no, we should record this only udner the "bar" title. I see your use case and raise it by one:

Type title "foo"
Click on the proposed solution links. Didn't like them
Change the title to "bar"
Do NOT click on proposed solution links or none were found. The user creates a ticket.

In this case the instant replies saved a ticket with title "foo" from being created, but didn't save a ticket with title "bar" from being created. Therefore we need to record two different cases, one where the ticket was saved and one where it wasn't.

If we want to be perfectly sure we need to read the user's mind and find out if "foo" and "bar" are about the same issue or whether he had two different issues, he didn't have to ask support for the first but did have to ask about the second.

I'd say that either way (ours and yours) is equally wrong, but with our current implementation we can at least collect all ticket titles typed by the user.

Nicholas K. Dionysopoulos

Lead Developer and Director

πŸ‡¬πŸ‡·Greek: native πŸ‡¬πŸ‡§English: excellent πŸ‡«πŸ‡·French: basic β€’ πŸ• My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

user83594
I like your vision.

If we want to be perfectly sure we need to read the user's mind and find out if "foo" and "bar" are about the same issue or whether he had two different issues, he didn't have to ask support for the first but did have to ask about the second.


I tryed this you use case on my installation, with orginal "instantreply.js".
(I kept the 403 fix)

1: Click "New Ticket"
2: Paste in the topic input the word "filename"
3: Click the "View" button of a proposed result. (only one time)
4: Paste in the topic input the word "performance"
5: Click the "View" button of a proposed result. (only one time)
6: Paste in the topic input the word "postscript"
7: Click the "View" button of a proposed result. (only one time)
8: Click the "Send your Ticket" button.

Now, I'm expect to see 3 row on "#__ats_attempts"
- two with ats_ticket_id = 0 and docimport_clicks = 1 for title = "filename" and "performance".
- one with ats_ticket_id = X and docimport_clicks = 1 for title = "postscript".

But I got only one row with ats_ticket_id = X and docimport_clicks = 6 for title = "postscript".

I attach an image with the log of all Ajax calls and multiple screen of the "#__ats_attempts" table.

You can see what I mean with multiple Ajax call:
- 2 consecutive call to update clicks in about 2 millisecond. (step 5)
- 3 consecutive call to update clicks in about 3 millisecond. (step 7)

The JS that I have proposed revolve this issue, remain the problem that all call are relative to the same row.
To avoid this is necessary to change this piece of code:

From:
// Instant search statistics
jQuery.ajax('index.php?option=com_ats&view=attempts&format=json', {
	dataType : 'json',
	cache    : false,
	data     : {
		task            : 'save',
		ats_attempt_id  : ats_instant_statistics,
		title           : searchQuery,
		ats_category_id : jQuery('#ticket_catid').val(),
		modified_on     : ''
	},
	success : function(response){
		ats_instant_statistics = response.ats_attempt_id;
		jQuery('input[name="ats_attempt_id"]').val(ats_instant_statistics);
	}
});


To:
// Instant search statistics
jQuery.ajax('index.php?option=com_ats&view=attempts&format=json', {
	dataType : 'json',
	cache    : false,
	data     : {
		task            : 'save',
		ats_attempt_id  : 0,
		title           : searchQuery,
		ats_category_id : jQuery('#ticket_catid').val(),
		modified_on     : ''
	},
	success : function(response){
		ats_instant_statistics = response.ats_attempt_id;
		jQuery('input[name="ats_attempt_id"]').val(ats_instant_statistics);
	}
});


Sending "ats_attempt_id" equals to 0 force the creating of a new row, in the success callback will be saved the new id value.

I also attach the new version of "instantreply.js" that contains the change explained above.

nicholas
Akeeba Staff
Manager
I told you, both ways to deal with it are equally correct or wrong depending on how you look at it :) Since you are going to override instantreply.js per the instructions I left you on the other ticket feel free to change that part of the code as well.

Nicholas K. Dionysopoulos

Lead Developer and Director

πŸ‡¬πŸ‡·Greek: native πŸ‡¬πŸ‡§English: excellent πŸ‡«πŸ‡·French: basic β€’ πŸ• My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

user83594
Hi,
Thank you for the answer, i'll close the ticket.

Demis

Support Information

Working hours: We are open Monday to Friday, 9am to 7pm Cyprus timezone (EET / EEST). Support is provided by the same developers writing the software, all of which live in Europe. You can still file tickets outside of our working hours, but we cannot respond to them until we're back at the office.

Support policy: We would like to kindly inform you that when using our support you have already agreed to the Support Policy which is part of our Terms of Service. Thank you for your understanding and for helping us help you!