r73245 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r73244‎ | r73245 | r73246 >
Date:19:55, 17 September 2010
Author:tparscal
Status:resolved (Comments)
Tags:
Comment:
Cleaned some things up. Need to test that things are working properly still.
Modified paths:
  • /trunk/extensions/UsabilityInitiative/ClickTracking/ApiClickTracking.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/ApiSpecialClickTracking.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/ClickTracking.hooks.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/ClickTracking.js (deleted) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/ClickTracking.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/SpecialClickTracking.css (deleted) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/SpecialClickTracking.js (deleted) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/SpecialClickTracking.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/modules (added) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.js (added) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.special.css (added) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.special.js (added) (history)
  • /trunk/extensions/UsabilityInitiative/ClickTracking/modules/jquery.clickTracking.js (added) (history)

Diff [purge]

Index: trunk/extensions/UsabilityInitiative/ClickTracking/SpecialClickTracking.css
@@ -1,63 +0,0 @@
2 -@CHARSET "UTF-8";
3 -
4 -.table_headers{
5 - font-weight: bold;
6 - border: bottom;
7 -}
8 -
9 -.table_data_row{
10 - text-align: center;
11 -}
12 -
13 -.table_data_row .event_name{
14 - text-align: left;
15 - font-weight: bold;
16 -}
17 -
18 -#clicktrack_data_table {
19 - float: left;
20 -}
21 -
22 -#chart_img {
23 - float: left;
24 - margin-left: 90px;
25 -}
26 -
27 -#change_graph_cell {
28 - text-align: right;
29 -}
30 -
31 -.disabled_option {
32 - color: #999999;
33 -}
34 -
35 -.hidden {
36 - display: none;
37 -}
38 -
39 -.control_div {
40 - margin: 10px;
41 - clear: both;
42 -}
43 -
44 -.sub_option_div{
45 - margin-top: 4px;
46 - margin-left: 15px;
47 -}
48 -
49 -.sub_option_div input[type="text"]{
50 - width: 20px;
51 - margin-left: 10px;
52 -}
53 -
54 -#date_range{
55 - display:inline;
56 - float:left;
57 - width:200px;
58 -}
59 -
60 -.add_condition_button{
61 - float: right;
62 - color: blue;
63 - cursor: pointer;
64 -}
\ No newline at end of file
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ClickTracking.js
@@ -1,31 +0,0 @@
2 -( function( $ ) {
3 - if ( !wgClickTrackingIsThrottled ) {
4 - // Create 'track action' function to call the clicktracking API and send the ID
5 - $.trackAction = function( id ) {
6 - $j.post(
7 - wgScriptPath + '/api.php', { 'action': 'clicktracking', 'eventid': id, 'token': wgTrackingToken }
8 - );
9 - };
10 -
11 - $.trackActionWithInfo = function( id, info ) {
12 - $j.post(
13 - wgScriptPath + '/api.php', { 'action': 'clicktracking', 'eventid': id, 'token': wgTrackingToken, 'additional': info }
14 - );
15 - };
16 -
17 -
18 - // Add click tracking hooks to the sidebar
19 - $j(document).ready( function() {
20 - $( '#p-logo a, #p-navigation a, #p-interaction a, #p-tb a' ).each( function() {
21 - var href = $(this).attr( 'href' );
22 - // Only modify local URLs
23 - if ( href.length > 0 && href[0] == '/' && ( href.length == 1 || href[1] != '/' ) ) {
24 - var id = 'leftnav-' + skin + '-' + ( $(this).attr( 'id' ) || $(this).parent().attr( 'id' ) );
25 - href = wgScriptPath + '/api.php?action=clicktracking' +
26 - '&eventid=' + id + '&token=' + wgTrackingToken + '&redirectto=' + escape( href );
27 - $(this).attr( 'href', href );
28 - }
29 - } );
30 - } );
31 - }
32 -} )( jQuery );
Index: trunk/extensions/UsabilityInitiative/ClickTracking/SpecialClickTracking.js
@@ -1,517 +0,0 @@
2 -(function($) {
3 - /* Very limited JSON encoder */
4 - $.json_encode = function( js_obj ) {
5 - var returnstr = "{ ";
6 -
7 - // trailing commas and json don't mix
8 - var propertynum = 0;
9 - for( property in js_obj ) {
10 - if( propertynum > 0 ) {
11 - returnstr +=", ";
12 - }
13 - returnstr += "\"" + property + "\"" + " : ";
14 - if( typeof js_obj[property] == 'object' ) {
15 - returnstr += $.json_encode( js_obj[property] );
16 - } else {
17 - returnstr += "\"" + js_obj[property] + "\" ";
18 - }
19 - propertynum++;
20 - }
21 -
22 - returnstr += " }";
23 - return returnstr;
24 - };
25 -
26 - $.getUserDefsFromDialog = function() {
27 - var currUserDefs = new Array();
28 - if( $("#anon_users_checkbox").is( ":checked" ) ) {
29 - currUserDefs['anonymous'] = 1;
30 - } else {
31 - currUserDefs['anonymous'] = 0;
32 - }
33 -
34 - var getCheckBoxData = function( contribName ) {
35 - if( $("#"+ contribName + "_checkbox").is( ":checked" ) ) {
36 - currUserDefs[contribName] = new Array();
37 - } else {
38 - return;
39 - }
40 - var totalConds = $("#" + contribName + "_div").data("totalConditions");
41 - var i;
42 -
43 - for( i = 1; i <= totalConds; i++ ) {
44 - if( $("#" + contribName + "_" + i + "_checkbox").is( ":checked" ) ) {
45 - $("#" + contribName + "_" + i + "_ltgt").children().each(function() {
46 - if( $(this).is( ":selected" ) ) {
47 - var currentCond = new Array();
48 - switch( $(this).attr("value") ) {
49 - case 'lt':
50 - currentCond['operation'] = '<';
51 - break;
52 - case 'lteq':
53 - currentCond['operation'] = '<=';
54 - break;
55 - case 'gt':
56 - currentCond['operation'] = '>';
57 - break;
58 - case 'gteq':
59 - currentCond['operation'] = '>=';
60 - break;
61 - default:
62 - currentCond['operation'] = '<';
63 - break;
64 - }
65 - currentCond['value'] = $("#" + contribName + "_" + i + "_text").val();
66 - currUserDefs[contribName].push(currentCond);
67 - }
68 - });
69 - } // ifchecked
70 - } // forloop
71 - };
72 -
73 - getCheckBoxData("total_contribs");
74 - getCheckBoxData("contribs_span_1");
75 - getCheckBoxData("contribs_span_2");
76 - getCheckBoxData("contribs_span_3");
77 - wgClickTrackUserDefs[$("#user_def_alter_legend").data("currentlyEditing")] = currUserDefs;
78 - };
79 -
80 - $.renderUserDefDialogWith = function( userDef, defName ) {
81 - // change name
82 - $("#user_def_alter_legend").text($("#user_def_alter_legend").data("defaultChangeText") + " " + defName);
83 - $("#user_def_alter_legend").data("currentlyEditing", defName);
84 -
85 -
86 - var setContribs = function( conditionArray, contribName ) {
87 - initialDiv = $("<div></div>").attr('id', contribName + '_div');
88 - initialDiv.addClass('checkbox_div');
89 - initialDiv.addClass('control_div');
90 -
91 - textDiv = $("<div></div>").attr('id', contribName + '_text_div');
92 - mainCheckbox = $("<input>").attr('id', contribName + '_checkbox');
93 - mainCheckbox.attr('type', 'checkbox');
94 - mainCheckbox.addClass('user_def_checkbox');
95 -
96 - if( conditionArray.length > 0 ) {
97 - mainCheckbox.attr( 'checked', true );
98 - }
99 -
100 - textDiv.append( mainCheckbox );
101 - textDiv.text( contribName ); // i18n txt here
102 - textDiv.css( 'display', 'inline' );
103 - initialDiv.append( mainCheckbox );
104 - initialDiv.append( textDiv );
105 -
106 - var buildConditionDiv = function( condition, counter, isChecked ) {
107 - conditionDiv = $("<div></div>").attr('id', contribName + '_range_' + counter + '_div');
108 - conditionDiv.addClass( 'checkbox_div' );
109 - conditionDiv.addClass( 'sub_option_div' );
110 -
111 -
112 - //initialDiv.append(conditionDiv);
113 - cCheckbox = $("<input type=\"checkbox\"></input>").attr('id', contribName + '_' + counter + '_checkbox');
114 - //cCheckbox.attr('type', 'checkbox');
115 - if( isChecked ) {
116 - cCheckbox.attr( 'checked', true );
117 - }
118 -
119 - cCheckbox.addClass( 'number_select_checkbox' );
120 - conditionDiv.append( cCheckbox );
121 -
122 -
123 - cSelect = $("<select></select>").attr('id', contribName + '_' + counter + '_ltgt');
124 - cSelect.addClass( 'number_select_ltgt' );
125 -
126 - cOpt1 = $("<option></option>").attr('id', contribName + '_' + counter + '_lt');
127 - cOpt1.addClass( 'number_select_ltgt_opt' );
128 - cOpt1.attr( 'value', 'lt' );
129 - cOpt1.text( '<' );
130 - if( condition['operation'] == '<' ) {
131 - cOpt1.attr( 'selected', true );
132 - }
133 -
134 -
135 - cOpt2 = $("<option></option>").attr('id', contribName + '_' + counter + '_gt');
136 - cOpt2.addClass( 'number_select_ltgt_opt' );
137 - cOpt2.attr( 'value', 'gt' );
138 - cOpt2.text( '>' );
139 - if( condition['operation'] == '>' ) {
140 - cOpt2.attr( 'selected', true );
141 - }
142 -
143 - cOpt3 = $("<option></option>").attr('id', contribName + '_' + counter + '_lteq');
144 - cOpt3.addClass( 'number_select_ltgt_opt' );
145 - cOpt3.attr( 'value', 'lteq' );
146 - cOpt3.text( '<=' );
147 - if( condition['operation'] == '<=' ) {
148 - cOpt3.attr( 'selected', true );
149 - }
150 -
151 - cOpt4 = $("<option></option>").attr('id', contribName + '_' + counter + '_gteq');
152 - cOpt4.addClass( 'number_select_ltgt_opt' );
153 - cOpt4.attr( 'value', 'gteq' );
154 - cOpt4.text( '>=' );
155 - if( condition['operation'] == '>=' ) {
156 - cOpt4.attr( 'selected', true );
157 - }
158 -
159 - cSelect.append( cOpt1 );
160 - cSelect.append( cOpt2 );
161 - cSelect.append( cOpt3 );
162 - cSelect.append( cOpt4 );
163 - conditionDiv.append( cSelect );
164 -
165 - cTextInput = $("<input></input>").attr('id', contribName + '_' + counter + '_text');
166 - cTextInput.addClass( 'number_select_text' );
167 - cTextInput.attr( 'value', condition['value'] );
168 - conditionDiv.append( cTextInput );
169 -
170 - return conditionDiv;
171 - };
172 -
173 - var i = 0;
174 - for( var condition in conditionArray ) {
175 - i++;
176 - var conditionDiv = buildConditionDiv( conditionArray[condition], i, true );
177 - initialDiv.append( conditionDiv );
178 - } // forloop
179 -
180 - initialDiv.data( 'totalConditions', i );
181 - addConditions = $("<div></div>").attr('id', contribName + '_addbutton');
182 - addConditions.data( 'contribName', contribName );
183 - addConditions.addClass( 'add_condition_button' );
184 - addConditions.text( '+' );
185 - initialDiv.append( addConditions );
186 - addConditions.click( function() {
187 - var initDiv = $("#" + $(this).data('contribName') + '_div');
188 - var totalConds = initDiv.data( 'totalConditions' );
189 - totalConds++;
190 - initDiv.data( 'totalConditions', totalConds );
191 - var tmpCond = new Array();
192 - tmpCond['operation'] = ' ';
193 - tmpCond['value'] = ' ';
194 -
195 - buildConditionDiv(tmpCond, totalConds).insertBefore($(this));
196 - initDiv.data( 'totalConditions', totalConds, false );
197 - });
198 -
199 - return initialDiv;
200 - }; // setcontribs
201 -
202 - // check anonymous
203 - var anon = false;
204 - if( parseInt( userDef['anonymous'] ) == 1 ) {
205 - anon = true;
206 - }
207 - $("#anon_users_checkbox").attr('checked', anon);
208 -
209 - // clear out old contents
210 - $("#contrib_opts_container").empty();
211 -
212 - var setup_set_contribs = function( contribName ) {
213 - var current_contribs = userDef[contribName];
214 - if( current_contribs == undefined ) {
215 - current_contribs = new Array();
216 - }
217 - $("#contrib_opts_container").append( setContribs( current_contribs, contribName ) );
218 - };
219 -
220 - // total contribs
221 - setup_set_contribs( 'total_contribs' );
222 - setup_set_contribs( 'contribs_span_1' );
223 - setup_set_contribs( 'contribs_span_2' );
224 - setup_set_contribs( 'contribs_span_3' );
225 -
226 - // OK button
227 - var okButton = $("<input>").attr('id', 'ok_button');
228 - okButton.attr( 'type', 'button' );
229 - okButton.attr( 'value', 'ok' );
230 - okButton.click(function() {
231 - $.getUserDefsFromDialog();
232 - $("#user_def_dialog").dialog('close');
233 - });
234 - $("#contrib_opts_container").append(okButton);
235 - }; // renderUserDefDialogWith
236 -
237 - // functions
238 - $.updateChart = function() {
239 - event_name = $("#chart_img").data('event_name');
240 -
241 - var processChartJSON = function( data, status ) {
242 -
243 - var getMax = function( findMax ) {
244 - var retval = Number.MIN_VALUE;
245 - for( var i in findMax ) {
246 - if( findMax[i] > retval ) {
247 - retval = findMax[i];
248 - }
249 - }
250 - return retval;
251 - };
252 -
253 - max1 = getMax( data['datapoints']['expert'] );
254 - max2 = getMax( data['datapoints']['intermediate'] );
255 - max3 = getMax( data['datapoints']['basic'] );
256 - max = Math.max( max3, Math.max( max1, max2 ) );
257 - chartURL = 'http://chart.apis.google.com/chart?' +
258 - 'chs=400x400&' +
259 - 'cht=lc&' +
260 - 'chco=FF0000,0000FF,00FF00&' +
261 - 'chtt=' + event_name + ' from ' + $("#start_date").val() +' to ' +$("#end_date").val() + "&" +
262 - 'chdl=' + 'Expert|Intermediate|Beginner' + "&" +
263 - 'chxt=x,y&' +
264 - 'chd=t:' + data['datapoints']['expert'].join(',') + "|" +
265 - data['datapoints']['intermediate'].join(',') + "|" + data['datapoints']['basic'].join(',') + "&" +
266 - 'chds=0,'+ max +',0,'+ max +',0,'+ max
267 - ;
268 - $("#chart_img").attr( 'src', chartURL );
269 - };
270 -
271 - start_date = $("#start_date").val();
272 - if( $("#start_date").hasClass( 'hidden' ) ) {
273 - start_date = '0';
274 - }
275 -
276 - end_date = $("#end_date").val();
277 - if( $("#end_date").hasClass( 'hidden' ) ) {
278 - end_date = '0';
279 - }
280 -
281 - // post relevant info
282 - $j.post( wgScriptPath + '/api.php',
283 - { 'action': 'specialclicktracking', 'format': 'json',
284 - 'eventid': $("#chart_img").data( 'eventid' ), 'increment': $("#chart_increment").val(),
285 - 'startdate': start_date, 'enddate': end_date, 'userdefs': $.json_encode( wgClickTrackUserDefs ) },
286 - processChartJSON, 'json'
287 - );
288 - };
289 -
290 - // pretty colors for the table
291 - $.colorizeTable = function() {
292 - // expert
293 -
294 - // get totals
295 - var expert_total = 0;
296 -
297 - $(".expert_data").each(function() {
298 - expert_total += parseInt( $(this).attr( 'value' ) );
299 - });
300 -
301 - // set proper red shade
302 - $(".expert_data").each(function() {
303 - var rval = 255;
304 - var gval = ( expert_total == 0 ? 255 : 255 - ( 255 * $(this).attr( 'value' ) / expert_total ) );
305 - var bval = gval;
306 - rgbString = "rgb(" + parseInt( rval ) + "," + parseInt( gval ) + "," + parseInt( bval ) + ")";
307 - $(this).data('rgb', rgbString);
308 - $(this).css('color', rgbString);
309 - $(this).css('background-color', rgbString);
310 - });
311 -
312 - // intermediate
313 -
314 - // total
315 - var intermediate_total = 0;
316 - $(".intermediate_data").each(function() {
317 - intermediate_total += parseInt( $(this).attr( 'value' ) );
318 - });
319 -
320 - // blue shade
321 - $(".intermediate_data").each(function() {
322 - var rval = ( intermediate_total == 0 ? 255 : 255 - ( 255 * $(this).attr( 'value' ) / intermediate_total ) );
323 - var gval = rval;
324 - var bval = 255;
325 - rgbString = "rgb(" + parseInt( rval ) + "," + parseInt( gval ) + "," + parseInt( bval ) + ")";
326 - $(this).data('rgb', rgbString);
327 - $(this).css('color', rgbString);
328 - $(this).css('background-color', rgbString);
329 - });
330 -
331 - // total
332 - var basic_total = 0;
333 - $(".basic_data").each(function() {
334 - basic_total += parseInt( $(this).attr( 'value' ) );
335 - });
336 -
337 - // green shade
338 - $(".basic_data").each(function() {
339 - var rval = ( basic_total == 0 ? 255 : 255 - ( 255 * $(this).attr( 'value' ) / basic_total ) );
340 - var gval = 255;
341 - var bval = rval;
342 - rgbString = "rgb(" + parseInt( rval ) + "," + parseInt( gval ) + "," + parseInt( bval ) + ")";
343 - $(this).data('rgb', rgbString);
344 - $(this).css('color', rgbString);
345 - $(this).css('background-color', rgbString);
346 - });
347 -
348 - // I wanted to do this with classes, but the element's style rule wins over class rule
349 - // and each element has its own alternative color
350 - $(".event_data").mouseover(function() {
351 - $(this).css('color', '#000000');
352 - $(this).css('background-color', '#FFFFFF');
353 - });
354 -
355 - $(".event_data").mouseout(function() {
356 - rgbString = $(this).data("rgb");
357 - $(this).css('color', rgbString);
358 - $(this).css('background-color', rgbString);
359 - });
360 -
361 - }; // colorize
362 -
363 - $.updateTable = function() {
364 - var processTableJSON = function( data, status ) {
365 - // clear
366 - $(".table_data_row").each( function() { $(this).remove(); } );
367 -
368 - var row_count = 0;
369 - for( var row_iter in data['tablevals']['vals'] ) {
370 - var row = data['tablevals']['vals'][row_iter]; // really, JS?
371 - row_count++;
372 -
373 - var outputRow = $("<tr></tr>");
374 - outputRow.addClass( 'table_data_row' );
375 -
376 - var cell =$("<td></td>").attr('id', 'event_name_' + row_count);
377 - cell.addClass( 'event_name' );
378 - cell.attr( 'value', row['event_id'] );
379 - cell.text( row['event_name']);
380 - outputRow.append( cell );
381 -
382 - var createClassCell = function( userclass ) {
383 - var newcell = $("<td></td>").attr('id', 'event_' + userclass + '_' + row_count);
384 - newcell.addClass( 'event_data' );
385 - newcell.addClass( userclass + '_data' );
386 - newcell.text( row[userclass] );
387 - newcell.attr( 'value', row[userclass] );
388 - outputRow.append( newcell );
389 - };
390 -
391 - createClassCell( 'expert' );
392 - createClassCell( 'intermediate' );
393 - createClassCell( 'basic' );
394 - createClassCell( 'total' );
395 - $("#clicktrack_data_table").append( outputRow );
396 - }
397 -
398 - $.colorizeTable();
399 - $.changeDataLinks();
400 - };
401 -
402 - start_date = $("#start_date").val();
403 - if( $("#start_date").hasClass( 'hidden' ) ) {
404 - start_date = '0';
405 - }
406 -
407 - end_date = $("#end_date").val();
408 - if( $("#end_date").hasClass( 'hidden' ) ) {
409 - end_date = '0';
410 - }
411 -
412 - // post relevant info
413 - $j.post( wgScriptPath + '/api.php',
414 - { 'action': 'specialclicktracking', 'format': 'json',
415 - 'eventid': 1, 'increment': $("#chart_increment").val(),
416 - 'startdate': start_date, 'enddate': end_date, 'userdefs': $.json_encode( wgClickTrackUserDefs ), 'tabledata': 1 },
417 - processTableJSON, 'json'
418 - );
419 -
420 - }; // updateTable
421 -
422 - $.setUIControls = function() {
423 - // SET UP DATE RANGES
424 -
425 - // date-pickers for start and end dates
426 - $('.date_range_input').each(function() {
427 - $(this).datepicker();
428 - $(this).datepicker( 'option', 'dateFormat', 'yymmdd' );
429 - });
430 - var startDate = new Date();
431 - $('#start_date').val("20091009"); // click_tracking start date as default
432 -
433 - var toggleDateInput = function( tableRow ) {
434 - var checked = false;
435 - tableRow.children().each( function() {
436 - if( checked == false ) {
437 - checked = $(this).children("input:checkbox").eq(0).is(":checked");
438 - }
439 - });
440 -
441 - if( checked ) {
442 - tableRow.removeClass( 'disabled_option' );
443 - tableRow.children("td").each(function() {
444 - $(this).children(".date_range_input").removeClass( 'hidden' );
445 - });
446 - } else {
447 - tableRow.children("td").each(function() {
448 - $(this).children(".date_range_input").addClass( 'hidden' );
449 - });
450 - tableRow.addClass( 'disabled_option' );
451 - }
452 - };
453 -
454 - $('.date_range_checkbox').click(function() {
455 - toggleDateInput( $(this).closest( 'tr' ) );
456 - });
457 -
458 - // update table
459 - $('#update_table_button').click($.updateTable);
460 -
461 - // CHART DIALOG
462 - $("#chart_dialog").dialog({ autoOpen: false, width: 400 });
463 - $("#chart_img").css('cursor', 'pointer');
464 - $("#chart_img").click(function() {
465 - $("#chart_dialog").dialog('open');
466 - });
467 -
468 - $("#chart_increment").data( 'value', $("#chart_increment").val() );
469 -
470 - $("#change_graph").click(function() {
471 - $("#chart_dialog").dialog('close');
472 -
473 - // check if the value actually changed, if so, update and increment things accordingly
474 - if( $("#chart_increment").data( 'value' ) != $("#chart_increment").val() ) {
475 - $("#chart_increment").data( 'value', $("#chart_increment").val() );
476 - $.updateChart();
477 - }
478 -
479 - });
480 -
481 - // CHANGE USER INFO DIALOG
482 - $("#user_def_dialog").dialog({ autoOpen: false, width: 400 });
483 - $("#user_def_alter_legend").data( 'defaultChangeText', $("#user_def_alter_legend").text() );
484 -
485 - // CHANGE USER/INTERMEDIATE/EXPERT DIALOGS
486 - var loadHeaderInfo = function( headerName ) {
487 - $("#" + headerName + "_header").css('cursor', 'pointer');
488 - $("#" + headerName + "_header").click(function() {
489 - $.renderUserDefDialogWith( wgClickTrackUserDefs[headerName], headerName );
490 - $("#user_def_dialog").dialog('open');
491 - });
492 - }; // headername
493 -
494 - loadHeaderInfo( 'basic' );
495 - loadHeaderInfo( 'intermediate' );
496 - loadHeaderInfo( 'expert' );
497 -
498 - };
499 -
500 - $.changeDataLinks = function() {
501 - $(".event_name").each(function() {
502 - $(this).css('cursor', 'pointer');
503 -
504 - $(this).click(function() {
505 - $("#chart_img").data( 'eventid', $(this).attr( 'value' ) );
506 - $("#chart_img").data( 'event_name', $(this).text() );
507 - $.updateChart();
508 - }); // click
509 - }); // each
510 - }; // addlink
511 -
512 -return $(this);
513 -})(jQuery);
514 -
515 -// colorize the table on document.ready
516 -$j(document).ready( $j.colorizeTable );
517 -$j(document).ready( $j.changeDataLinks );
518 -$j(document).ready( $j.setUIControls );
Index: trunk/extensions/UsabilityInitiative/ClickTracking/SpecialClickTracking.php
@@ -7,28 +7,20 @@
88 */
99
1010 class SpecialClickTracking extends SpecialPage {
11 -
 11+
1212 private static $minimum_date = '20091009'; // YYYYMMDD (+1 for today)
1313
1414 private $user_defs = array();
1515
1616 function __construct() {
1717 parent::__construct( 'ClickTracking' , 'clicktrack' );
18 - wfLoadExtensionMessages( 'ClickTracking' );
19 - UsabilityInitiativeHooks::initialize();
20 - UsabilityInitiativeHooks::addStyle( 'ClickTracking/SpecialClickTracking.css' );
21 - UsabilityInitiativeHooks::addScript( 'ClickTracking/SpecialClickTracking.js' );
2218 }
23 -
24 -
 19+
2520 function setDefaults() {
2621 $this->user_defs["basic"] = array(
2722 "anonymous" => "1",
28 - "total_contribs" => array(
29 - array( "operation" => "<=", "value" => "10" ),
30 - ),
 23+ "total_contribs" => array( array( "operation" => "<=", "value" => "10" ) ),
3124 );
32 -
3325 $this->user_defs["intermediate"] = array(
3426 "anonymous" => "0",
3527 "total_contribs" => array(
@@ -36,86 +28,73 @@
3729 array( "operation" => ">", "value" => "10" ),
3830 ),
3931 );
40 -
4132 $this->user_defs["expert"] = array(
4233 "anonymous" => "0",
43 - "total_contribs" => array(
44 - array( "operation" => ">=", "value" => "400" ),
45 - ),
 34+ "total_contribs" => array( array( "operation" => ">=", "value" => "400" ) ),
4635 );
47 -
48 -
4936 }
50 -
51 -
52 -
 37+
5338 function execute( $par ) {
5439 global $wgOut, $wgUser;
 40+
 41+ $wgOut->addModules( 'clickTracking.special' );
5542
5643 // Check permissions
5744 if ( !$this->userCanExecute( $wgUser ) ) {
5845 $this->displayRestrictionError();
5946 return;
6047 }
61 -
62 -
 48+
6349 $this->setHeaders();
6450 $this->setDefaults();
65 -
66 - $wgOut->addScript( '<script type="text/javascript">' . "var wgClickTrackUserDefs = " . json_encode( $this->user_defs ) . '</script>' );
67 -
 51+ $wgOut->addInlineScript( 'var wgClickTrackUserDefs = ' . json_encode( $this->user_defs ) );
6852 $wgOut->setPageTitle( wfMsg( 'ct-title' ) );
69 -
 53+
7054 $outputTable = "";
71 -
72 -
73 - // grab top N
 55+
 56+ // Grab top N
7457 $events = $this->getTopEvents();
75 -
76 - // open table
 58+
 59+ // Open table
7760 $outputTable .= Xml::openElement( "table", array( "class" => "sortable click-data", "id" => "clicktrack_data_table" ) );
78 -
79 - // create a row for every event
 61+
 62+ // Create a row for every event
8063 $i = 0;
8164 $db_result;
82 -
83 - // build row headers
 65+
 66+ // Build row headers
8467 $header_row = array();
85 -
 68+
8669 $header_row["event_header"] = wfMsg( 'ct-event-name' );
8770 $header_row["expert_header"] = wfMsg( 'ct-expert-header' );
8871 $header_row["intermediate_header"] = wfMsg( 'ct-intermediate-header' );
8972 $header_row["basic_header"] = wfMsg( 'ct-beginner-header' );
9073 $header_row["total_header"] = wfMsg( 'ct-total-header' );
9174 $outputTable .= Xml::buildTableRow( array( "class" => "table_headers" ), $header_row );
92 -
93 - // foreach event, build a row
 75+
 76+ // Build event rows
9477 while ( ( $db_result = $events->fetchRow() ) != null ) {
9578 ++$i;
9679 $outputTable .= $this->buildRow( $db_result, $i, $this->user_defs );
9780 }
98 -
99 -
100 - // close table
 81+
 82+ // Close table
10183 $outputTable .= Xml::closeElement( "table" );
 84+
 85+ $wgOut->addHTML( $outputTable );
10286
103 - $wgOut->addHTML( $outputTable );
 87+ $wgOut->addHTML( $this->buildDateRange() );
10488
105 - $wgOut->addHTML( $this->buildDateRange() );
106 -
107 - // build chart
 89+ // Build chart
10890 $wgOut->addHTML( $this->buildChart( "advanced.hide", 10, "20090815", "20090902", 1 ) );
109 -
110 - // $wgOut->addHTML($this->buildControlBox());
111 -
11291 $wgOut->addHTML( $this->buildChartDialog() );
11392 $wgOut->addHTML( $this->buildUserDefBlankDialog() );
114 -
 93+
11594 }
116 -
11795
11896 /**
11997 * Gets the data to build a chart for PHP or JS purposes
 98+ *
12099 * @param $event_id event id this chart is for
121100 * @param $minTime minimum day
122101 * @param $maxTime maximum day
@@ -125,41 +104,39 @@
126105 * @return array with chart info
127106 */
128107 static function getChartData( $event_id, $minTime, $maxTime, $increment, $userDefs, $isUserDefsJSON = true ) {
129 - // get data
 108+ // Get data
130109 date_default_timezone_set( 'UTC' );
131 -
 110+
132111 if ( $maxTime == 0 ) {
133112 $maxTime = gmdate( "Ymd", time() ); // today
134113 }
135114 if ( $minTime == 0 ) {
136115 $minTime = self::$minimum_date;
137116 }
138 -
139 -
 117+
140118 // FIXME: On PHP 5.3+, this will be MUCH cleaner
141119 $currBeginDate = new DateTime( $minTime );
142120 $currEndDate = new DateTime( $minTime );
143121 $endDate = new DateTime( $maxTime );
144 -
145 -
146 - // get user definitions
 122+
 123+ // Get user definitions
147124 if ( $isUserDefsJSON ) {
148125 $userDefs = json_decode( $userDefs, true );
149126 }
150 -
 127+
151128 $basicUserData = array();
152129 $intermediateUserData = array();
153130 $expertUserData = array();
154 -
 131+
155132 // PHP 5.3...hurry!
156133 $plural = ( $increment == 1 ? "" : "s" );
157 -
 134+
158135 while ( $currEndDate->format( "U" ) < $endDate->format( "U" ) ) {
159136 $currEndDate->modify( "+$increment day$plural" );
160 -
 137+
161138 $minDate = $currBeginDate->format( "Ymd" );
162139 $maxDate = $currEndDate->format( "Ymd" );
163 -
 140+
164141 $basicUserData[] = self::getTableValue( $event_id, $userDefs['basic'], $minDate, $maxDate );
165142 $intermediateUserData[] = self::getTableValue( $event_id, $userDefs['intermediate'], $minDate, $maxDate );
166143 $expertUserData[] = self::getTableValue( $event_id, $userDefs['expert'], $minDate, $maxDate );
@@ -170,12 +147,21 @@
171148
172149 function buildChart( $event_name, $event_id, $minTime, $maxTime, $increment ) {
173150 $chartData = self::getChartData( $event_id, $minTime, $maxTime, $increment, $this->user_defs, false );
174 - $chartSrc = $this->getGoogleChartParams( $event_id, $event_name, $minTime, $maxTime, $chartData["basic"], $chartData["intermediate"], $chartData["expert"] );
 151+ $chartSrc = $this->getGoogleChartParams(
 152+ $event_id,
 153+ $event_name,
 154+ $minTime,
 155+ $maxTime,
 156+ $chartData["basic"],
 157+ $chartData["intermediate"],
 158+ $chartData["expert"]
 159+ );
175160 return Xml::element( 'img', array( 'src' => $chartSrc , 'id' => 'chart_img' ) );
176161 }
177 -
178 -
179 - function getGoogleChartParams( $event_id, $event_name, $minDate, $maxDate, $basicUserData, $intermediateUserData, $expertUserData ) {
 162+
 163+ function getGoogleChartParams(
 164+ $event_id, $event_name, $minDate, $maxDate, $basicUserData, $intermediateUserData, $expertUserData
 165+ ) {
180166 $max = max( max( $basicUserData ), max( $intermediateUserData ), max( $expertUserData ) );
181167 return "http://chart.apis.google.com/chart?" . wfArrayToCGI(
182168 array(
@@ -186,77 +172,91 @@
187173 'chdl' => 'Expert|Intermediate|Beginner',
188174 'chxt' => 'x,y',
189175 'chd' => 't:' . implode( "," , $expertUserData ) . "|" .
190 - implode( "," , $intermediateUserData ) . "|" . implode( "," , $basicUserData ),
 176+ implode( "," , $intermediateUserData ) . "|" . implode( "," , $basicUserData ),
191177 'chds' => "0,$max,0,$max,0,$max"
192 - ) );
 178+ ) );
193179 }
194 -
195 -
 180+
196181 function buildUserDefBlankDialog() {
197182 $control = "";
198183 $control .= Xml::openElement( "div", array( "id" => "user_def_dialog", "class" => "dialog" ) );
199 -
 184+
200185 // currently editing...----|
201186 $control .= Xml::openElement( "form", array( "id" => "user_definition_form", "class" => "user_def_form" ) );
202187 $control .= Xml::openElement( "fieldset", array( "id" => "user_def_alter_fieldset" ) );
203188 $control .= Xml::openElement( "legend", array( "id" => "user_def_alter_legend" ) );
204189 $control .= wfMsg( "ct-editing" );
205190 $control .= Xml::closeElement( "legend" );
206 -
 191+
207192 // [] anonymous users?
208193 $control .= Xml::openElement( "div", array( "id" => "anon_users_div", "class" => "checkbox_div control_div" ) );
209 - $control .= Xml::openElement( "input", array( "type" => "checkbox", "id" => "anon_users_checkbox", "class" => "user_def_checkbox" ) );
 194+ $control .= Xml::openElement(
 195+ "input", array( "type" => "checkbox", "id" => "anon_users_checkbox", "class" => "user_def_checkbox" )
 196+ );
210197 $control .= Xml::closeElement( "input" );
211198 $control .= wfMsg( "ct-anon-users" );
212199 $control .= Xml::closeElement( "div" );
213 -
 200+
214201 // ----------------
215202 $control .= Xml::openElement( "hr" );
216203 $control .= Xml::closeElement( "hr" );
217204 $control .= Xml::openElement( "div", array( "id" => "contrib_opts_container" ) );
218 -
 205+
219206 // [] users with contributions [>=V] [n ]
220 - $control .= Xml::openElement( "div", array( "id" => "total_users_contrib_div", "class" => "checkbox_div control_div" ) );
221 - $control .= Xml::openElement( "input", array( "type" => "checkbox", "id" => "contrib_checkbox", "class" => "user_def_checkbox" ) );
 207+ $control .= Xml::openElement(
 208+ "div", array( "id" => "total_users_contrib_div", "class" => "checkbox_div control_div" )
 209+ );
 210+ $control .= Xml::openElement(
 211+ "input", array( "type" => "checkbox", "id" => "contrib_checkbox", "class" => "user_def_checkbox" )
 212+ );
222213 $control .= Xml::closeElement( "input" );
223214 $control .= wfMsg( "ct-user-contribs" );
224 -
225 -
 215+
 216+
226217 $control .= Xml::closeElement( "div" );
227 -
 218+
228219 // [] contributions in timespan 1
229 - $control .= Xml::openElement( "div", array( "id" => "contrib_span_1_div", "class" => "checkbox_div control_div" ) );
230 -
 220+ $control .= Xml::openElement(
 221+ "div", array( "id" => "contrib_span_1_div", "class" => "checkbox_div control_div" )
 222+ );
 223+
231224 $control .= Xml::openElement( "div", array( "id" => "contrib_span_1_text_div", "class" => "checkbox_div" ) );
232 - $control .= Xml::openElement( "input", array( "type" => "checkbox", "id" => "contrib_span_1_checkbox", "class" => "user_def_checkbox" ) );
 225+ $control .= Xml::openElement(
 226+ "input", array( "type" => "checkbox", "id" => "contrib_span_1_checkbox", "class" => "user_def_checkbox" )
 227+ );
233228 $control .= Xml::closeElement( "input" );
234229 $control .= wfMsg( "ct-user-span" ) . " 1";
235230 $control .= Xml::closeElement( "div" );
236231 $control .= Xml::closeElement( "div" );
237 -
 232+
238233 // [] contributions in timespan 2
239 - $control .= Xml::openElement( "div", array( "id" => "contrib_span_2_div", "class" => "checkbox_div control_div" ) );
240 -
 234+ $control .= Xml::openElement(
 235+ "div", array( "id" => "contrib_span_2_div", "class" => "checkbox_div control_div" )
 236+ );
 237+
241238 $control .= Xml::openElement( "div", array( "id" => "contrib_span_2_text_div", "class" => "checkbox_div" ) );
242 - $control .= Xml::openElement( "input", array( "type" => "checkbox", "id" => "contrib_span_2_checkbox", "class" => "user_def_checkbox" ) );
 239+ $control .= Xml::openElement(
 240+ "input", array( "type" => "checkbox", "id" => "contrib_span_2_checkbox", "class" => "user_def_checkbox" )
 241+ );
243242 $control .= Xml::closeElement( "input" );
244243 $control .= wfMsg( "ct-user-span" ) . " 2";
245244 $control .= Xml::closeElement( "div" );
246245 $control .= Xml::closeElement( "div" );
247 -
 246+
248247 // [] contributions in timespan 3
249 - $control .= Xml::openElement( "div", array( "id" => "contrib_span_3_div", "class" => "checkbox_div control_div" ) );
250 -
 248+ $control .= Xml::openElement(
 249+ "div", array( "id" => "contrib_span_3_div", "class" => "checkbox_div control_div" )
 250+ );
 251+
251252 $control .= Xml::openElement( "div", array( "id" => "contrib_span_3_text_div", "class" => "checkbox_div" ) );
252 - $control .= Xml::openElement( "input", array( "type" => "checkbox", "id" => "contrib_span_3_checkbox", "class" => "user_def_checkbox" ) );
 253+ $control .= Xml::openElement(
 254+ "input", array( "type" => "checkbox", "id" => "contrib_span_3_checkbox", "class" => "user_def_checkbox" )
 255+ );
253256 $control .= Xml::closeElement( "input" );
254257 $control .= wfMsg( "ct-user-span" ) . " 3";
255258 $control .= Xml::closeElement( "div" );
256259 $control .= Xml::closeElement( "div" );
257 -
258 -
259 -
260 -
 260+
261261 $control .= Xml::closeElement( "div" );// close contrib opts
262262
263263 $control .= Xml::closeElement( "fieldset" );
@@ -264,61 +264,72 @@
265265 $control .= Xml::closeElement( "div" );
266266 return $control;
267267 }
268 -
269 -
270 -
 268+
271269 function buildUserDefNumberSelect( $include_checkbox, $include_and, $ids ) {
272270 $control = "";
273271 if ( $include_checkbox ) {
274 - $control .= Xml::openElement( "input", array( "type" => "checkbox", "id" => "{$ids}_checkbox", "class" => "number_select_checkbox" ) );
 272+ $control .= Xml::openElement(
 273+ "input", array( "type" => "checkbox", "id" => "{$ids}_checkbox", "class" => "number_select_checkbox" )
 274+ );
275275 $control .= Xml::closeElement( "input" );
276276 }
277 -
 277+
278278 if ( $include_and ) {
279279 $control .= wfMsg( "ct-and" );
280280 }
281 -
 281+
282282 $control .= Xml::openElement( "select", array( "id" => "{$ids}_ltgt", "class" => "number_select_ltgt" ) );
283 - $control .= Xml::openElement( "option", array( "id" => "{$ids}_lt", "class" => "number_select_ltgt_opt", "value" => "lt" ) );
 283+ $control .= Xml::openElement(
 284+ "option", array( "id" => "{$ids}_lt", "class" => "number_select_ltgt_opt", "value" => "lt" )
 285+ );
284286 $control .= "&lt;=";
285287 $control .= Xml::closeElement( "option" );
286 - $control .= Xml::openElement( "option", array( "id" => "{$ids}_gt", "class" => "number_select_ltgt_opt", "value" => "gt" ) );
 288+ $control .= Xml::openElement(
 289+ "option", array( "id" => "{$ids}_gt", "class" => "number_select_ltgt_opt", "value" => "gt" )
 290+ );
287291 $control .= "&gt;=";
288292 $control .= Xml::closeElement( "option" );
289293 $control .= Xml::closeElement( "select" );
290 - $control .= Xml::openElement( "input", array( "type" => "text", "id" => "{$ids}_text", "class" => "number_select_text" ) );
 294+ $control .= Xml::openElement(
 295+ "input", array( "type" => "text", "id" => "{$ids}_text", "class" => "number_select_text" )
 296+ );
291297 $control .= Xml::closeElement( "input" );
292298 return $control;
293299 }
294 -
295 -
 300+
 301+
296302 function buildChartDialog() {
297303 $control = "";
298304 $control .= Xml::openElement( "div", array( "id" => "chart_dialog", "class" => "dialog" ) );
299 -
 305+
300306 $control .= Xml::openElement( "form", array( "id" => "chart_dialog_form", "class" => "chart_form" ) );
301307 $control .= Xml::openElement( "fieldset", array( "id" => "chart_dialog_alter_fieldset" ) );
302308 $control .= Xml::openElement( "legend", array( "id" => "chart_dialog_alter_legend" ) );
303309 $control .= wfMsg( "ct-increment-by" );
304310 $control .= Xml::closeElement( "legend" );
305 -
 311+
306312 $control .= Xml::openElement( "table", array( "id" => "chart_dialog_increment_table" ) );
307313 $control .= Xml::openElement( "tbody", array( "id" => "chart_dialog_increment_tbody" ) );
308 -
 314+
309315 $control .= Xml::openElement( "tr", array( "id" => "chart_dialog_increment_row" ) );
310 -
 316+
311317 $control .= Xml::openElement( "td", array( "id" => "chart_dialog_increment_cell" ) );
312 - $control .= Xml::openElement( "input", array( "type" => "text", "id" => "chart_increment", "class" => "chart_dialog_area", "value" => '1' ) );
 318+ $control .= Xml::openElement(
 319+ "input",
 320+ array( "type" => "text", "id" => "chart_increment", "class" => "chart_dialog_area", "value" => '1' )
 321+ );
313322 $control .= Xml::closeElement( "input" );
314323 $control .= Xml::closeElement( "td" );
315 -
 324+
316325 $control .= Xml::openElement( "td", array( "id" => "chart_dialog_button_cell" ) );
317 - $control .= Xml::openElement( "input", array( "type" => "button", "id" => "change_graph", "value" => wfMsg( "ct-change-graph" ) ) );
 326+ $control .= Xml::openElement(
 327+ "input", array( "type" => "button", "id" => "change_graph", "value" => wfMsg( "ct-change-graph" ) )
 328+ );
318329 $control .= Xml::closeElement( "input" );
319330 $control .= Xml::closeElement( "td" );
320 -
 331+
321332 $control .= Xml::closeElement( "tr" );
322 -
 333+
323334 $control .= Xml::closeElement( "tbody" );
324335 $control .= Xml::closeElement( "table" );
325336 $control .= Xml::closeElement( "fieldset" );
@@ -326,77 +337,98 @@
327338 $control .= Xml::closeElement( "div" );
328339 return $control;
329340 }
330 -
331 -
 341+
 342+
332343 function buildDateRange() {
333344 $control = Xml::openElement( "form", array( "id" => "date_range" ) );
334 -
 345+
335346 $control .= Xml::openElement( "fieldset", array( "id" => "date_range_fieldset" ) );
336347 $control .= Xml::openElement( "legend", array( "id" => "date_range_legend" ) );
337348 $control .= wfMsg( 'ct-date-range' );
338349 $control .= Xml::closeElement( "legend" );
339 -
340350
341 -
 351+
 352+
342353 $control .= Xml::openElement( "table", array( "id" => "date_range_table" ) );
343354 $control .= Xml::openElement( "tbody", array( "id" => "date_range_tbody" ) );
344 -
345 -
 355+
 356+
346357 $control .= Xml::openElement( "tr", array( "id" => "start_date_row" ) );
347 -
 358+
348359 $control .= Xml::openElement( "td", array( "id" => "start_date_label", "class" => "date_range_label" ) );
349 - $control .= Xml::openElement( "input", array( "type" => "checkbox", "id" => "start_date_checkbox", "class" => "date_range_checkbox", "checked" => "" ) );
 360+ $control .= Xml::openElement(
 361+ "input",
 362+ array(
 363+ "type" => "checkbox", "id" => "start_date_checkbox", "class" => "date_range_checkbox", "checked" => ""
 364+ )
 365+ );
350366 $control .= Xml::closeElement( "input" );
351367 $control .= wfMsg( "ct-start-date" );
352368 $control .= Xml::closeElement( "td" );
353 -
 369+
354370 $control .= Xml::openElement( "td", array( "id" => "start_date_textarea" ) );
355 - $control .= Xml::openElement( "input", array( "type" => "text", "id" => "start_date", "class" => "date_range_input" ) );
 371+ $control .= Xml::openElement(
 372+ "input", array( "type" => "text", "id" => "start_date", "class" => "date_range_input" )
 373+ );
356374 $control .= Xml::closeElement( "input" );
357375 $control .= Xml::closeElement( "td" );
358 -
 376+
359377 $control .= Xml::closeElement( "tr" );
360 -
 378+
361379 $control .= Xml::openElement( "tr", array( "id" => "end_date_row" ) );
362 -
 380+
363381 $control .= Xml::openElement( "td", array( "id" => "end_date_label", "class" => "date_range_label" ) );
364 - $control .= Xml::openElement( "input", array( "type" => "checkbox", "id" => "end_date_checkbox", "class" => "date_range_checkbox", "checked" => "" ) );
 382+ $control .= Xml::openElement(
 383+ "input",
 384+ array(
 385+ "type" => "checkbox", "id" => "end_date_checkbox", "class" => "date_range_checkbox", "checked" => ""
 386+ )
 387+ );
365388 $control .= Xml::closeElement( "input" );
366389 $control .= wfMsg( "ct-end-date" );
367390 $control .= Xml::closeElement( "td" );
368 -
 391+
369392 $control .= Xml::openElement( "td", array( "id" => "end_date_textarea" ) );
370 - $control .= Xml::openElement( "input", array( "type" => "text", "id" => "end_date", "class" => "date_range_input" ) );
 393+ $control .= Xml::openElement(
 394+ "input", array( "type" => "text", "id" => "end_date", "class" => "date_range_input" )
 395+ );
371396 $control .= Xml::closeElement( "input" );
372397 $control .= Xml::closeElement( "td" );
373 -
 398+
374399 $control .= Xml::closeElement( "tr" );
375 -
376400
 401+
377402 $control .= Xml::openElement( "tr", array( "id" => "update_row" ) );
378 -
 403+
379404 $control .= Xml::openElement( "td" );
380405 $control .= Xml::closeElement( "td" );
381 -
 406+
382407 $control .= Xml::openElement( "td", array( "id" => "update_table_button_td" ) );
383 - $control .= Xml::openElement( "input", array( "type" => "button", "id" => "update_table_button", "class" => "update_button", "value" => wfMsg( "ct-update-table" ) ) );
 408+ $control .= Xml::openElement(
 409+ "input",
 410+ array(
 411+ "type" => "button",
 412+ "id" => "update_table_button",
 413+ "class" => "update_button",
 414+ "value" => wfMsg( "ct-update-table" )
 415+ )
 416+ );
384417 $control .= Xml::closeElement( "input" );
385418 $control .= Xml::closeElement( "td" );
386 -
 419+
387420 $control .= Xml::closeElement( "tr" );
388 -
 421+
389422 $control .= Xml::closeElement( "tbody" );
390423 $control .= Xml::closeElement( "table" );
391424 $control .= Xml::closeElement( "fieldset" );
392 -
 425+
393426 $control .= Xml::closeElement( "form" );
394 -
 427+
395428 return $control;
396429 }
397 -
398 -
 430+
399431 /**
400 - *
 432+ *
401433 * @param $minTime
402434 * @param $maxTime
403435 * @param $userDefs
@@ -404,23 +436,21 @@
405437 * @return unknown_type
406438 */
407439 public static function buildRowArray( $minTime, $maxTime, $userDefs, $is_JSON = true ) {
408 -
409 -
410440 if ( $minTime == 0 ) {
411441 $minTime = self::$minimum_date;
412442 }
413443 if ( $maxTime == 0 ) {
414444 $maxTime = gmdate( "Ymd", time() ); // today
415445 }
416 -
 446+
417447 if ( $is_JSON ) {
418448 $userDefs = json_decode( $userDefs, true );
419449 }
420 -
 450+
421451 $events = self::getTopEvents( $minTime, $maxTime );
422 -
 452+
423453 $returnArray = array();
424 -
 454+
425455 while ( ( $data_result = $events->fetchRow() ) != null ) {
426456 $outputArray = array();
427457 $outputArray['event_name'] = $data_result['event_name'];
@@ -431,57 +461,56 @@
432462 $outputArray['total'] = $data_result["totalevtid"];
433463 $returnArray[] = $outputArray;
434464 }
435 -
 465+
436466 return $returnArray;
437 -
438467 }
439 -
 468+
440469 function buildRow( $data_result, $row_count, $userDefs ) {
441470
442 - $outputRow = Xml::openElement( "tr", array( "class" => "table_data_row" ) );
 471+ $outputRow = Xml::openElement( "tr", array( "class" => "table_data_row" ) );
443472
444 - // event name
445 - $outputRow .= Xml::openElement( "td",
446 - array( "class" => "event_name", "id" => "event_name_$row_count", "value" => $data_result['event_id'] ) );
447 - $outputRow .= $data_result['event_name'];
448 - $outputRow .= Xml::closeElement( "td" );
 473+ // event name
 474+ $outputRow .= Xml::openElement( "td",
 475+ array( "class" => "event_name", "id" => "event_name_$row_count", "value" => $data_result['event_id'] ) );
 476+ $outputRow .= $data_result['event_name'];
 477+ $outputRow .= Xml::closeElement( "td" );
449478
450 - // advanced users
451 - $cellValue = self::getTableValue( $data_result['event_id'], $userDefs["expert"] );
452 - $outputRow .= Xml::openElement( "td",
453 - array( "class" => "event_data expert_data", "id" => "event_expert_$row_count",
 479+ // advanced users
 480+ $cellValue = self::getTableValue( $data_result['event_id'], $userDefs["expert"] );
 481+ $outputRow .= Xml::openElement( "td",
 482+ array( "class" => "event_data expert_data", "id" => "event_expert_$row_count",
454483 "value" => $cellValue ) );
455 - $outputRow .= $cellValue;
456 - $outputRow .= Xml::closeElement( "td" );
 484+ $outputRow .= $cellValue;
 485+ $outputRow .= Xml::closeElement( "td" );
457486
458 - // intermediate users
459 - $cellValue = self::getTableValue( $data_result['event_id'], $userDefs["intermediate"] );
460 - $outputRow .= Xml::openElement( "td",
461 - array( "class" => "event_data intermediate_data", "id" => "event_intermediate_$row_count",
 487+ // intermediate users
 488+ $cellValue = self::getTableValue( $data_result['event_id'], $userDefs["intermediate"] );
 489+ $outputRow .= Xml::openElement( "td",
 490+ array( "class" => "event_data intermediate_data", "id" => "event_intermediate_$row_count",
462491 "value" => $cellValue ) );
463 - $outputRow .= $cellValue;
464 - $outputRow .= Xml::closeElement( "td" );
 492+ $outputRow .= $cellValue;
 493+ $outputRow .= Xml::closeElement( "td" );
465494
466 - // basic users
467 - $cellValue = self::getTableValue( $data_result['event_id'], $userDefs["basic"] );
468 - $outputRow .= Xml::openElement( "td",
469 - array( "class" => "event_data basic_data", "id" => "event_basic_$row_count",
 495+ // basic users
 496+ $cellValue = self::getTableValue( $data_result['event_id'], $userDefs["basic"] );
 497+ $outputRow .= Xml::openElement( "td",
 498+ array( "class" => "event_data basic_data", "id" => "event_basic_$row_count",
470499 "value" => $cellValue ) );
471 - $outputRow .= $cellValue;
472 - $outputRow .= Xml::closeElement( "td" );
 500+ $outputRow .= $cellValue;
 501+ $outputRow .= Xml::closeElement( "td" );
473502
474 - // totals
475 - $cellValue = $data_result["totalevtid"];
476 - $outputRow .= Xml::openElement( "td",
477 - array( "class" => "event_data total_data", "id" => "total_$row_count",
 503+ // totals
 504+ $cellValue = $data_result["totalevtid"];
 505+ $outputRow .= Xml::openElement( "td",
 506+ array( "class" => "event_data total_data", "id" => "total_$row_count",
478507 "value" => $cellValue ) );
479 - $outputRow .= $cellValue;
480 - $outputRow .= Xml::closeElement( "td" );
 508+ $outputRow .= $cellValue;
 509+ $outputRow .= Xml::closeElement( "td" );
481510
482511
483 - $outputRow .= Xml::closeElement( "tr" );
 512+ $outputRow .= Xml::closeElement( "tr" );
484513
485 - return $outputRow;
 514+ return $outputRow;
486515
487516 }
488517
@@ -491,95 +520,102 @@
492521 * @return date with spaces
493522 */
494523 public static function space_out_date( $datewithnospaces ) {
495 - return ( substr( $datewithnospaces, 0, 4 ) . ' ' . substr( $datewithnospaces, 4, 2 ) . ' ' . substr( $datewithnospaces, 6, 2 ) );
 524+ return (
 525+ substr( $datewithnospaces, 0, 4 ) . ' ' .
 526+ substr( $datewithnospaces, 4, 2 ) . ' ' .
 527+ substr( $datewithnospaces, 6, 2 )
 528+ );
496529 }
497 -
498 -
 530+
499531 /*
500532 * get time constraints
501533 * @param minTime minimum day (YYYYMMDD)
502534 * @param maxTime max day (YYYYMMDD)
503 - * NOTE: once some of the constraints have been finalized, this will use more of the Database functions and not raw SQL
 535+ * NOTE: once some of the constraints have been finalized, this will use more of the Database functions and not raw
 536+ * SQL
504537 */
505538 static function getTimeConstraints( $minTime, $maxTime ) {
506539 if ( $minTime == 0 || $maxTime == 0 ||
507 - ( strptime( SpecialClickTracking::space_out_date( $minTime ), "%Y %m %d" ) === false ) ||
508 - ( strptime( SpecialClickTracking::space_out_date( $minTime ), "%Y %m %d" ) === false ) ) {
509 - return array();
510 - }
 540+ ( strptime( SpecialClickTracking::space_out_date( $minTime ), "%Y %m %d" ) === false ) ||
 541+ ( strptime( SpecialClickTracking::space_out_date( $minTime ), "%Y %m %d" ) === false ) ) {
 542+ return array();
 543+ }
511544 else {
512 - // the dates are stored in the DB as MW_TIMESTAMP formats, add the zeroes to fix that
 545+ // the dates are stored in the DB as MW_TIMESTAMP formats, add the zeroes to fix that
513546 $minTime .= "000000";
514547 $maxTime .= "000000";
515548
516549 $dbr = wfGetDB( DB_SLAVE );
517 -
 550+
518551 // time constraint array
519552 return array(
520553 "`action_time` >= " . $dbr->addQuotes( $minTime ) ,
521554 "`action_time` <= " . $dbr->addQuotes( $maxTime )
522555 );
523 -
 556+
524557 }
525 -
 558+
526559 }
527 -
528 -
 560+
 561+
529562 /**
530563 * Gets the top N events as set in the page pref
 564+ *
531565 * @param $time_constraint_statement
532566 * @return unknown_type
533 - * NOTE: once some of the constraints have been finalized, this will use more of the Database functions and not raw SQL
 567+ * NOTE: once some of the constraints have been finalized, this will use more of the Database functions and not raw
 568+ * SQL
534569 */
535570 public static function getTopEvents( $minTime = "", $maxTime = "", $normalize_top_results = false ) {
536 -
 571+
537572 $time_constraint_statement = self::getTimeConstraints( $minTime, $maxTime );
538573 $time_constraint = $time_constraint_statement;
539 -
 574+
540575 $dbr = wfGetDB( DB_SLAVE );
541 -
 576+
542577 // NOTE: This query is a performance nightmare
543578 // Permission to run it is restricted by default
544579 $dbresult = $dbr->select(
545 - array( 'click_tracking', 'click_tracking_events' ),
546 - array( 'count(event_id) as totalevtid', 'event_id', 'event_name' ),
547 - $time_constraint,
548 - __METHOD__,
549 - array( 'GROUP BY' => 'event_id', 'ORDER BY' => 'totalevtid DESC' ),
550 - array( 'click_tracking_events' =>
551 - array( 'LEFT JOIN', 'event_id=id' )
552 - )
 580+ array( 'click_tracking', 'click_tracking_events' ),
 581+ array( 'count(event_id) as totalevtid', 'event_id', 'event_name' ),
 582+ $time_constraint,
 583+ __METHOD__,
 584+ array( 'GROUP BY' => 'event_id', 'ORDER BY' => 'totalevtid DESC' ),
 585+ array( 'click_tracking_events' =>
 586+ array( 'LEFT JOIN', 'event_id=id' )
 587+ )
553588 );
554 -
555 - /*
556 - $sql = "select count(event_id) as totalevtid, event_id,event_name from click_tracking" .
 589+
 590+ /*
 591+ $sql = "select count(event_id) as totalevtid, event_id,event_name from click_tracking" .
557592 " LEFT JOIN click_tracking_events ON event_id=click_tracking_events.id".
558593 " $time_constraint group by event_id order by totalevtid desc";
559 - */
560 -
 594+ */
 595+
561596 // returns count(event_id),event_id, event_name, top one first
562597 return $dbresult;
563598 }
564599
565600 /**
566601 * Gets a table value for a given User ID
567 - * NOTE: once some of the constraints have been finalized, this will use more of the Database functions and not raw SQL
 602+ * NOTE: once some of the constraints have been finalized, this will use more of the Database functions and not raw
 603+ * SQL
568604 */
569605 static function getTableValue( $event_id, $userDef, $minTime = '', $maxTime = '', $normalize_results = false ) {
570 -
 606+
571607 $dbr = wfGetDB( DB_SLAVE );
572608 $conds = array_merge(
573609 self::getTimeConstraints( $minTime, $maxTime ),
574610 SpecialClickTracking::buildUserDefConstraints( $userDef ),
575611 array( 'event_id' => $event_id )
576612 );
577 - return wfGetDB( DB_SLAVE )->selectField(
578 - 'click_tracking', 'count(*)', $conds, __METHOD__ );
579 -
 613+ return wfGetDB( DB_SLAVE )->selectField( 'click_tracking', 'count(*)', $conds, __METHOD__ );
 614+
580615 }
581 -
 616+
582617 /**
583618 * Generates a query for a user type definition
 619+ *
584620 * @param $include_anon_users boolean, include anon users or not
585621 * @param $total_contribs array, nonempty if total contribs to be included
586622 * @param $contrib_1 array, nonempty AND conditions for user_contribs_1
@@ -588,46 +624,62 @@
589625 * @return unknown_type query
590626 */
591627 public static function buildUserDefConstraints( $def ) {
592 -
 628+
593629 $dbr = wfGetDB( DB_SLAVE );
594 -
 630+
595631 $include_anon_users = ( empty( $def['anonymous'] ) ? array():$def['anonymous'] );
596632 $total_contribs = ( empty( $def['total_contribs'] ) ? array():$def['total_contribs'] );
597633 $contrib_1 = ( empty( $def['contrib_1'] ) ? array():$def['contrib_1'] );
598634 $contrib_2 = ( empty( $def['contrib_2'] ) ? array():$def['contrib_2'] );
599635 $contrib_3 = ( empty( $def['contrib_3'] ) ? array():$def['contrib_3'] );
600 -
 636+
601637 $or_conds = array();
602638 $and_conds = array();
603639 $sql = "";
604 -
 640+
605641
606642 if ( (boolean)$include_anon_users ) {
607643 $or_conds[] = array( "field" => "is_logged_in", "operation" => "=", "value" => "0" );
608644 }
609 -
 645+
610646 if ( !empty( $total_contribs ) ) {
611647 foreach ( $total_contribs as $contribs ) {
612 - $and_conds[] = array( "field" => "user_total_contribs", "operation" => SpecialClickTracking::validate_oper( $contribs["operation"] ), "value" => intval( $contribs["value"] ) );
 648+ $and_conds[] = array(
 649+ "field" => "user_total_contribs",
 650+ "operation" => SpecialClickTracking::validate_oper( $contribs["operation"] ),
 651+ "value" => intval( $contribs["value"] )
 652+ );
613653 }
614654 }
615 -
 655+
616656 if ( !empty( $contrib_1 ) ) {
617657 foreach ( $contrib_1 as $contribs ) {
618 - $and_conds[] = array( "field" => "user_contribs_span1", "operation" => SpecialClickTracking::validate_oper( $contribs["operation"] ), "value" => intval( $contribs["value"] ) );
 658+ $and_conds[] = array(
 659+ "field" => "user_contribs_span1",
 660+ "operation" => SpecialClickTracking::validate_oper( $contribs["operation"] ),
 661+ "value" => intval( $contribs["value"] )
 662+ );
619663 }
620664 }
621665 if ( !empty( $contrib_2 ) ) {
622666 foreach ( $contrib_2 as $contribs ) {
623 - $and_conds[] = array( "field" => "user_contribs_span2", "operation" => SpecialClickTracking::validate_oper( $contribs["operation"] ), "value" => intval( $contribs["value"] ) );
 667+ $and_conds[] = array(
 668+ "field" => "user_contribs_span2",
 669+ "operation" => SpecialClickTracking::validate_oper( $contribs["operation"] ),
 670+ "value" => intval( $contribs["value"] )
 671+ );
624672 }
625673 }
626674 if ( !empty( $contrib_3 ) ) {
627675 foreach ( $contrib_3 as $contribs ) {
628 - $and_conds[] = array( "field" => "user_contribs_span3", "operation" => SpecialClickTracking::validate_oper( $contribs["operation"] ), "value" => intval( $contribs["value"] ) );
 676+ $and_conds[] = array(
 677+ "field" => "user_contribs_span3",
 678+ "operation" => SpecialClickTracking::validate_oper( $contribs["operation"] ),
 679+ "value" => intval( $contribs["value"] )
 680+ );
629681 }
630682 }
631 -
 683+
632684 foreach ( $and_conds as $cond ) {
633685 if ( !empty( $sql ) ) {
634686 $sql .= " AND ";
@@ -640,10 +692,10 @@
641693 }
642694 $sql .= $cond["field"] . " " . $cond["operation"] . " " . $dbr->addQuotes( $cond["value"] );
643695 }
644 -
 696+
645697 return array( $sql );
646698 }
647 -
 699+
648700 public static function validate_oper( $operation ) {
649701 $o_trim = trim( $operation );
650702 switch( $o_trim ) { // valid operations
@@ -657,6 +709,4 @@
658710 return "=";
659711 }
660712 }
661 -
662 -
663713 }
\ No newline at end of file
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ClickTracking.hooks.php
@@ -1,7 +1,6 @@
22 <?php
3 -
43 /**
5 - * Hooks for Usability Initiative ClickTracking extension
 4+ * Hooks for ClickTracking extension
65 *
76 * @file
87 * @ingroup Extensions
@@ -9,41 +8,31 @@
109
1110 class ClickTrackingHooks {
1211
13 - /* Static Functions */
 12+ /* Static Methods */
1413
15 - /* initializations */
16 -
17 - /* 3 tables for click tracking */
18 - public static function schema() {
 14+ /*
 15+ * LoadExtensionSchemaUpdates hook
 16+ */
 17+ public static function loadExtensionSchemaUpdates() {
1918 global $wgExtNewTables, $wgExtNewIndexes, $wgExtNewFields;
2019
21 - $wgExtNewTables[] = array(
 20+ $wgExtNewTables[] = array( 'click_tracking', dirname( __FILE__ ) . '/ClickTracking.sql' );
 21+ $wgExtNewTables[] = array( 'click_tracking_events', dirname( __FILE__ ) . '/ClickTrackingEvents.sql' );
 22+ $wgExtNewIndexes[] = array(
2223 'click_tracking',
23 - dirname( __FILE__ ) . '/ClickTracking.sql'
 24+ 'click_tracking_action_time',
 25+ dirname( __FILE__ ) . '/patch-action_time.sql',
2426 );
25 -
26 - $wgExtNewTables[] = array(
27 - 'click_tracking_events',
28 - dirname( __FILE__ ) . '/ClickTrackingEvents.sql'
29 - );
30 -
31 - $wgExtNewIndexes[] = array(
32 - 'click_tracking', 'click_tracking_action_time',
33 - dirname( __FILE__ ) . '/patch-action_time.sql'
34 - );
35 -
3627 $wgExtNewFields[] = array(
37 - 'click_tracking', 'additional_info',
38 - dirname( __FILE__ ) . '/patch-additional_info.sql'
 28+ 'click_tracking',
 29+ 'additional_info',
 30+ dirname( __FILE__ ) . '/patch-additional_info.sql',
3931 );
40 -
4132 return true;
4233 }
4334
4435 /**
45 - * Make sure the table exists for parser tests
46 - * @param $tables
47 - * @return unknown_type
 36+ * ParserTestTables hook
4837 */
4938 public static function parserTestTables( &$tables ) {
5039 $tables[] = 'click_tracking';
@@ -52,127 +41,115 @@
5342 }
5443
5544 /**
56 - * Check to see if user is throttled
 45+ * BeforePageDisplay hook
 46+ *
 47+ * Adds the modules to the page
 48+ *
 49+ * @param $out OutputPage output page
 50+ * @param $skin Skin current skin
5751 */
58 - public static function isUserThrottled() {
 52+ public static function beforePageDisplay( $out, $skin ) {
5953 global $wgClickTrackThrottle;
60 - return !( $wgClickTrackThrottle >= 0 && rand() % $wgClickTrackThrottle == 0 );
 54+
 55+ if ( !( $wgClickTrackThrottle >= 0 && rand() % $wgClickTrackThrottle == 0 ) ) {
 56+ $out->addModules( 'clickTracking' );
 57+ }
 58+ return true;
6159 }
6260
6361 /**
64 - * Adds JavaScript
 62+ * MakeGlobalVariablesScript hook
6563 */
66 - public static function addJS() {
67 - global $wgClickTrackingStyleVersion;
68 -
69 - // HACK: Only add scripts when they're really needed by not initializing UIH here
70 - //UsabilityInitiativeHooks::initialize();
71 - UsabilityInitiativeHooks::addScript( 'ClickTracking/ClickTracking.js', $wgClickTrackingStyleVersion );
72 - UsabilityInitiativeHooks::addVariables(
73 - array(
74 - 'wgTrackingToken' => ClickTrackingHooks::get_session_id(),
75 - 'wgClickTrackingIsThrottled' => ClickTrackingHooks::isUserThrottled()
76 - )
77 - );
78 -
 64+ public static function makeGlobalVariablesScript( &$vars ) {
 65+ global $wgUser;
 66+ $vars['wgTrackingToken'] = wfGenerateToken( array( $wgUser->getName(), time() ) );
7967 return true;
8068 }
8169
82 - /**
83 - * Gets the session ID...we just want a unique random ID for the page load
84 - * @return session ID
 70+ /*
 71+ * ResourceLoaderRegisterModules hook
 72+ *
 73+ * Adds modules to ResourceLoader
8574 */
86 - public static function get_session_id() {
87 - global $wgUser;
88 - return wfGenerateToken( array( $wgUser->getName(), time() ) );
 75+ public static function resourceLoaderRegisterModules() {
 76+ ResourceLoader::register( array(
 77+ 'jquery.clickTracking' => new ResourceLoaderFileModule( array(
 78+ 'scripts' => 'extensions/UsabilityInitiative/ClickTracking/modules/jquery.clickTracking.js',
 79+ 'dependencies' => 'jquery.cookie',
 80+ ) ),
 81+ 'clickTracking' => new ResourceLoaderFileModule( array(
 82+ 'scripts' => 'extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.js',
 83+ 'dependencies' => 'jquery.clickTracking',
 84+ ) ),
 85+ 'clickTracking.special' => new ResourceLoaderFileModule( array(
 86+ 'scripts' => 'extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.special.js',
 87+ 'styles' => 'extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.special.css',
 88+ ) ),
 89+ ) );
8990 }
9091
9192 /**
9293 * Get the number of revisions a user has made since a given time
 94+ *
9395 * @param $ts beginning timestamp
9496 * @return number of revsions this user has made
9597 */
9698 public static function getEditCountSince( $ts ) {
9799 global $wgUser;
98100
99 - // convert to just the day
 101+ // Convert to just the day
100102 $time = gmdate( 'Y-m-d', wfTimestamp( TS_UNIX, $ts ) );
101 -
102103 $dbr = wfGetDB( DB_SLAVE );
103 -
104104 $edits = $dbr->selectField(
105105 'user_daily_contribs',
106106 'SUM(contribs)',
107 - array(
108 - 'user_id' => $wgUser->getId(),
109 - "day >= " . $dbr->addQuotes( $time )
110 - ),
 107+ array( 'user_id' => $wgUser->getId(), 'day >= ' . $dbr->addQuotes( $time ) ),
111108 __METHOD__
112109 );
113 -
114 - // user hasn't made any edits in whatever amount of time
115 - if ( $edits == null ) {
116 - $edits = 0;
117 - }
118 -
119 - return $edits;
 110+ // User hasn't made any edits in whatever amount of time
 111+ return $edits == null ? 0 : $edits;
120112 }
121113
122114 /**
123115 * Get event ID from name
 116+ *
124117 * @param $event_name String: name of the event to get
125118 * @return integer
126119 */
127120 public static function getEventIDFromName( $event_name ) {
128 - $dbw = wfGetDB( DB_MASTER ); // replication lag means sometimes a new event will not exist in the table yet
129 -
130 - $id_num = $dbw->selectField(
131 - 'click_tracking_events',
132 - 'id',
133 - array(
134 - 'event_name' => $event_name
135 - ),
136 - __METHOD__
137 - );
138 -
139 - // if this entry doesn't exist...
140 - // this will be incredibly rare as the whole database will only have a few hundred entries in it at most
141 - // and getting DB_MASTER up top would be wasteful
 121+ // Replication lag means sometimes a new event will not exist in the table yet
 122+ $dbw = wfGetDB( DB_MASTER );
 123+ $id_num = $dbw->selectField( 'click_tracking_events', 'id', array( 'event_name' => $event_name ), __METHOD__ );
 124+ // If this entry doesn't exist, which will be incredibly rare as the whole database will only have a few hundred
 125+ // entries in it at most and getting DB_MASTER up top would be wasteful
142126 // FIXME: Use replace() instead of this selectField --> insert or update logic
143127 if ( $id_num === false ) {
144 - $dbw->insert(
145 - 'click_tracking_events',
146 - array( 'event_name' => (string) $event_name ),
147 - __METHOD__
148 - );
 128+ $dbw->insert( 'click_tracking_events', array( 'event_name' => (string) $event_name ), __METHOD__ );
149129 $id_num = $dbw->insertId();
150130 }
151 -
152 - if ( $id_num === false ) {
153 - return 0;
154 - }
155 -
156 - return $id_num;
 131+ return $id_num === false ? 0 : $id_num;
157132 }
158133
159134 /**
160135 * Track particular event
 136+ *
161137 * @param $session_id String: unique session id for this editing sesion
162138 * @param $is_logged_in Boolean: whether or not the user is logged in
163139 * @param $namespace Integer: namespace the user is editing
164140 * @param $event_id Integer: event type
165141 * @param $contribs Integer: contributions the user has made (or NULL if user not logged in)
166 - * @param $contribs_in_timespan1 Integer: number of contributions user has made in timespan of granularity 1 (defined by ClickTracking/$wgClickTrackContribGranularity1)
167 - * @param $contribs_in_timespan2 Integer: number of contributions user has made in timespan of granularity 2 (defined by ClickTracking/$wgClickTrackContribGranularity2)
168 - * @param $contribs_in_timespan3 Integer: number of contributions user has made in timespan of granularity 3 (defined by ClickTracking/$wgClickTrackContribGranularity3)
 142+ * @param $contribs_in_timespan1 Integer: number of contributions user has made in timespan of granularity 1
 143+ * (defined by ClickTracking/$wgClickTrackContribGranularity1)
 144+ * @param $contribs_in_timespan2 Integer: number of contributions user has made in timespan of granularity 2
 145+ * (defined by ClickTracking/$wgClickTrackContribGranularity2)
 146+ * @param $contribs_in_timespan3 Integer: number of contributions user has made in timespan of granularity 3
 147+ * (defined by ClickTracking/$wgClickTrackContribGranularity3)
169148 * @return true if the event was stored in the DB
170149 */
171150 public static function trackEvent( $session_id, $is_logged_in, $namespace, $event_id, $contribs = 0,
172 - $contribs_in_timespan1 = 0, $contribs_in_timespan2 = 0, $contribs_in_timespan3 = 0, $additional= null ) {
 151+ $contribs_in_timespan1 = 0, $contribs_in_timespan2 = 0, $contribs_in_timespan3 = 0, $additional= null ) {
173152 $dbw = wfGetDB( DB_MASTER );
174 -
175153 $dbw->begin();
176 -
177154 // Builds insert information
178155 $data = array(
179156 'action_time' => $dbw->timestamp(),
@@ -184,9 +161,8 @@
185162 'user_contribs_span3' => ( $is_logged_in ? (int) $contribs_in_timespan3 : null ),
186163 'namespace' => (int) $namespace,
187164 'event_id' => (int) $event_id,
188 - 'additional_info' => ( isset($additional) ? (string) $additional : null )
 165+ 'additional_info' => ( isset($additional) ? (string) $additional : null )
189166 );
190 -
191167 $db_status = $dbw->insert( 'click_tracking', $data, __METHOD__ );
192168 $dbw->commit();
193169 return $db_status;
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ApiClickTracking.php
@@ -1,6 +1,6 @@
22 <?php
33 /**
4 - * Extend the API for click tracking
 4+ * Click tracking API extension
55 *
66 * @file
77 * @ingroup API
@@ -9,25 +9,29 @@
1010 class ApiClickTracking extends ApiBase {
1111
1212 /**
13 - * runs when the API is called with "clicktracking", takes in "eventid" and an edit token given to the user, "token"
 13+ * API clicktracking action
 14+ *
 15+ * Parameters:
 16+ * eventid: event name
 17+ * token: unique identifier for a user session
 18+ *
1419 * @see includes/api/ApiBase#execute()
1520 */
1621 public function execute() {
17 - global $wgUser, $wgTitle, $wgClickTrackContribGranularity1, $wgClickTrackContribGranularity2, $wgClickTrackContribGranularity3;
 22+ global $wgUser, $wgTitle, $wgClickTrackContribGranularity1, $wgClickTrackContribGranularity2,
 23+ $wgClickTrackContribGranularity3;
1824
1925 $params = $this->extractRequestParams();
2026 $this->validateParams( $params );
2127 $eventid_to_lookup = $params['eventid'];
2228 $session_id = $params['token'];
23 -
 29+
2430 $additional = null;
25 -
26 - if( isset($params['additional']) && strlen($params['additional']) > 0){
 31+
 32+ if ( isset( $params['additional'] ) && strlen( $params['additional'] ) > 0 ){
2733 $additional = $params['additional'];
28 -
2934 }
30 -
31 -
 35+
3236 // Event ID lookup table
3337 // FIXME: API should already have urldecode()d
3438 $event_id = ClickTrackingHooks::getEventIDFromName( urldecode( $eventid_to_lookup ) );
@@ -54,7 +58,7 @@
5559 $granularity3, // contributions made in granularity 3 time frame
5660 $additional
5761 );
58 -
 62+
5963 // For links that go off the page, redirect the user
6064 // FIXME: The API should have a proper infrastructure for this
6165 if ( !is_null( $params['redirectto'] ) ) {
@@ -66,7 +70,7 @@
6771 global $wgOut;
6872 $wgOut->redirect( $params['redirectto'] );
6973 $wgOut->output();
70 -
 74+
7175 // Prevent any further output
7276 $wgOut->disable();
7377 $this->getMain()->getPrinter()->disable();
@@ -80,7 +84,7 @@
8185 * Required parameter check
8286 * @param $params params extracted from the POST
8387 */
84 - protected function validateParams( $params ) {
 88+ protected function validateParams( $params ) {
8589 $required = array( 'eventid', 'token' );
8690 foreach ( $required as $arg ) {
8791 if ( !isset( $params[$arg] ) ) {
@@ -95,19 +99,19 @@
96100 'token' => 'unique edit ID for this edit session',
97101 'redirectto' => 'URL to redirect to (only used for links that go off the page)',
98102 'additional' => 'additional info for the event, like state information'
99 - );
 103+ );
100104 }
101105
102106 public function getDescription() {
103107 return array(
104108 'Track user clicks on JavaScript items.'
105 - );
 109+ );
106110 }
107 -
 111+
108112 public function getPossibleErrors() {
109113 return array_merge( parent::getPossibleErrors(), array(
110 - array( 'missingparam', 'eventid' ),
111 - array( 'missingparam', 'token' ),
 114+ array( 'missingparam', 'eventid' ),
 115+ array( 'missingparam', 'token' ),
112116 ) );
113117 }
114118
@@ -123,5 +127,4 @@
124128 public function getVersion() {
125129 return __CLASS__ . ': $Id$';
126130 }
127 -
128131 }
Index: trunk/extensions/UsabilityInitiative/ClickTracking/modules/jquery.clickTracking.js
@@ -0,0 +1,52 @@
 2+/*
 3+ * JavaScript for Click Tracking jQuery plugin
 4+ */
 5+
 6+( function( $ ) {
 7+ if ( !$.cookie( 'clicktracking-session' ) ) {
 8+ /*
 9+ * Very simple hashing of date, why simple?
 10+ * 1. This is based on the date, not the user, so security is not an issue.
 11+ * 2. This is for statistics gathering a large scales, in the very unlikley event that two users end up with the
 12+ * same token, it will only introduce a tiny and acceptable amount of noise.
 13+ * 3. Because it's much more problematic to sent tons of JavaScript to the client than to cope with 1 and 2.
 14+ */
 15+ var token = '',
 16+ dict = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
 17+ date = new Date().getTime();
 18+ while ( token.length <= 32 ) {
 19+ token += dict.charAt( ( ( Math.random() * date ) + token.length + date ) % dict.length );
 20+ }
 21+ $.cookie( 'clicktracking-session', token );
 22+ }
 23+ /**
 24+ * Performs click tracking API call
 25+ *
 26+ * @param {string} id event identifier
 27+ */
 28+ $.trackAction = function( id ) {
 29+ $j.post(
 30+ mediaWiki.config.get( 'wgScriptPath' ) + '/api.php', {
 31+ 'action': 'clicktracking',
 32+ 'eventid': id,
 33+ 'token': $.cookie( 'clicktracking-session' )
 34+ }
 35+ );
 36+ };
 37+ /**
 38+ * Performs click tracking API call
 39+ *
 40+ * @param {string} id event identifier
 41+ * @param {string} info additional information to be stored with the click
 42+ */
 43+ $.trackActionWithInfo = function( id, info ) {
 44+ $j.post(
 45+ mediaWiki.config.get( 'wgScriptPath' ) + '/api.php', {
 46+ 'action': 'clicktracking',
 47+ 'eventid': id,
 48+ 'token': $.cookie( 'clicktracking-session' ),
 49+ 'additional': info
 50+ }
 51+ );
 52+ };
 53+} )( jQuery );
Property changes on: trunk/extensions/UsabilityInitiative/ClickTracking/modules/jquery.clickTracking.js
___________________________________________________________________
Added: svn:eol-style
154 + native
Index: trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.special.js
@@ -0,0 +1,530 @@
 2+/*
 3+ * JavaScript for Click Tracking special page
 4+ */
 5+
 6+( function( $ ) {
 7+ /* Very limited JSON encoder */
 8+ $.json_encode = function( js_obj ) {
 9+ var returnstr = "{ ";
 10+
 11+ // trailing commas and json don't mix
 12+ var propertynum = 0;
 13+ for ( property in js_obj ) {
 14+ if ( propertynum > 0 ) {
 15+ returnstr += ", ";
 16+ }
 17+ returnstr += "\"" + property + "\"" + " : ";
 18+ if ( typeof js_obj[property] == 'object' ) {
 19+ returnstr += $.json_encode( js_obj[property] );
 20+ } else {
 21+ returnstr += "\"" + js_obj[property] + "\" ";
 22+ }
 23+ propertynum++;
 24+ }
 25+
 26+ returnstr += " }";
 27+ return returnstr;
 28+ };
 29+
 30+ $.getUserDefsFromDialog = function() {
 31+ var currUserDefs = new Array();
 32+ if ( $( "#anon_users_checkbox" ).is( ":checked" ) ) {
 33+ currUserDefs['anonymous'] = 1;
 34+ } else {
 35+ currUserDefs['anonymous'] = 0;
 36+ }
 37+
 38+ var getCheckBoxData = function( contribName ) {
 39+ if ( $( "#" + contribName + "_checkbox" ).is( ":checked" ) ) {
 40+ currUserDefs[contribName] = new Array();
 41+ } else {
 42+ return;
 43+ }
 44+ var totalConds = $( "#" + contribName + "_div" ).data( "totalConditions" );
 45+ var i;
 46+
 47+ for ( i = 1; i <= totalConds; i++ ) {
 48+ if ( $( "#" + contribName + "_" + i + "_checkbox" ).is( ":checked" ) ) {
 49+ $( "#" + contribName + "_" + i + "_ltgt" ).children().each( function() {
 50+ if ( $( this ).is( ":selected" ) ) {
 51+ var currentCond = new Array();
 52+ switch ( $( this ).attr( "value" ) ) {
 53+ case 'lt':
 54+ currentCond['operation'] = '<';
 55+ break;
 56+ case 'lteq':
 57+ currentCond['operation'] = '<=';
 58+ break;
 59+ case 'gt':
 60+ currentCond['operation'] = '>';
 61+ break;
 62+ case 'gteq':
 63+ currentCond['operation'] = '>=';
 64+ break;
 65+ default:
 66+ currentCond['operation'] = '<';
 67+ break;
 68+ }
 69+ currentCond['value'] = $( "#" + contribName + "_" + i + "_text" ).val();
 70+ currUserDefs[contribName].push( currentCond );
 71+ }
 72+ } );
 73+ } // ifchecked
 74+ } // forloop
 75+ };
 76+
 77+ getCheckBoxData( "total_contribs" );
 78+ getCheckBoxData( "contribs_span_1" );
 79+ getCheckBoxData( "contribs_span_2" );
 80+ getCheckBoxData( "contribs_span_3" );
 81+ wgClickTrackUserDefs[$( "#user_def_alter_legend" ).data( "currentlyEditing" )] = currUserDefs;
 82+ };
 83+
 84+ $.renderUserDefDialogWith = function( userDef, defName ) {
 85+ // change name
 86+ $( "#user_def_alter_legend" ).text( $( "#user_def_alter_legend" ).data( "defaultChangeText" ) + " " + defName );
 87+ $( "#user_def_alter_legend" ).data( "currentlyEditing", defName );
 88+
 89+ var setContribs = function( conditionArray, contribName ) {
 90+ initialDiv = $( "<div></div>" ).attr( 'id', contribName + '_div' );
 91+ initialDiv.addClass( 'checkbox_div' );
 92+ initialDiv.addClass( 'control_div' );
 93+
 94+ textDiv = $( "<div></div>" ).attr( 'id', contribName + '_text_div' );
 95+ mainCheckbox = $( "<input>" ).attr( 'id', contribName + '_checkbox' );
 96+ mainCheckbox.attr( 'type', 'checkbox' );
 97+ mainCheckbox.addClass( 'user_def_checkbox' );
 98+
 99+ if ( conditionArray.length > 0 ) {
 100+ mainCheckbox.attr( 'checked', true );
 101+ }
 102+
 103+ textDiv.append( mainCheckbox );
 104+ textDiv.text( contribName ); // i18n txt here
 105+ textDiv.css( 'display', 'inline' );
 106+ initialDiv.append( mainCheckbox );
 107+ initialDiv.append( textDiv );
 108+
 109+ var buildConditionDiv = function( condition, counter, isChecked ) {
 110+ conditionDiv = $( "<div></div>" ).attr( 'id', contribName + '_range_' + counter + '_div' );
 111+ conditionDiv.addClass( 'checkbox_div' );
 112+ conditionDiv.addClass( 'sub_option_div' );
 113+
 114+ // initialDiv.append(conditionDiv);
 115+ cCheckbox = $( "<input type=\"checkbox\"></input>" ).attr( 'id',
 116+ contribName + '_' + counter + '_checkbox' );
 117+ // cCheckbox.attr('type', 'checkbox');
 118+ if ( isChecked ) {
 119+ cCheckbox.attr( 'checked', true );
 120+ }
 121+
 122+ cCheckbox.addClass( 'number_select_checkbox' );
 123+ conditionDiv.append( cCheckbox );
 124+
 125+ cSelect = $( "<select></select>" ).attr( 'id', contribName + '_' + counter + '_ltgt' );
 126+ cSelect.addClass( 'number_select_ltgt' );
 127+
 128+ cOpt1 = $( "<option></option>" ).attr( 'id', contribName + '_' + counter + '_lt' );
 129+ cOpt1.addClass( 'number_select_ltgt_opt' );
 130+ cOpt1.attr( 'value', 'lt' );
 131+ cOpt1.text( '<' );
 132+ if ( condition['operation'] == '<' ) {
 133+ cOpt1.attr( 'selected', true );
 134+ }
 135+
 136+ cOpt2 = $( "<option></option>" ).attr( 'id', contribName + '_' + counter + '_gt' );
 137+ cOpt2.addClass( 'number_select_ltgt_opt' );
 138+ cOpt2.attr( 'value', 'gt' );
 139+ cOpt2.text( '>' );
 140+ if ( condition['operation'] == '>' ) {
 141+ cOpt2.attr( 'selected', true );
 142+ }
 143+
 144+ cOpt3 = $( "<option></option>" ).attr( 'id', contribName + '_' + counter + '_lteq' );
 145+ cOpt3.addClass( 'number_select_ltgt_opt' );
 146+ cOpt3.attr( 'value', 'lteq' );
 147+ cOpt3.text( '<=' );
 148+ if ( condition['operation'] == '<=' ) {
 149+ cOpt3.attr( 'selected', true );
 150+ }
 151+
 152+ cOpt4 = $( "<option></option>" ).attr( 'id', contribName + '_' + counter + '_gteq' );
 153+ cOpt4.addClass( 'number_select_ltgt_opt' );
 154+ cOpt4.attr( 'value', 'gteq' );
 155+ cOpt4.text( '>=' );
 156+ if ( condition['operation'] == '>=' ) {
 157+ cOpt4.attr( 'selected', true );
 158+ }
 159+
 160+ cSelect.append( cOpt1 );
 161+ cSelect.append( cOpt2 );
 162+ cSelect.append( cOpt3 );
 163+ cSelect.append( cOpt4 );
 164+ conditionDiv.append( cSelect );
 165+
 166+ cTextInput = $( "<input></input>" ).attr( 'id', contribName + '_' + counter + '_text' );
 167+ cTextInput.addClass( 'number_select_text' );
 168+ cTextInput.attr( 'value', condition['value'] );
 169+ conditionDiv.append( cTextInput );
 170+
 171+ return conditionDiv;
 172+ };
 173+
 174+ var i = 0;
 175+ for ( var condition in conditionArray ) {
 176+ i++;
 177+ var conditionDiv = buildConditionDiv( conditionArray[condition], i, true );
 178+ initialDiv.append( conditionDiv );
 179+ } // forloop
 180+
 181+ initialDiv.data( 'totalConditions', i );
 182+ addConditions = $( "<div></div>" ).attr( 'id', contribName + '_addbutton' );
 183+ addConditions.data( 'contribName', contribName );
 184+ addConditions.addClass( 'add_condition_button' );
 185+ addConditions.text( '+' );
 186+ initialDiv.append( addConditions );
 187+ addConditions.click( function() {
 188+ var initDiv = $( "#" + $( this ).data( 'contribName' ) + '_div' );
 189+ var totalConds = initDiv.data( 'totalConditions' );
 190+ totalConds++;
 191+ initDiv.data( 'totalConditions', totalConds );
 192+ var tmpCond = new Array();
 193+ tmpCond['operation'] = ' ';
 194+ tmpCond['value'] = ' ';
 195+
 196+ buildConditionDiv( tmpCond, totalConds ).insertBefore( $( this ) );
 197+ initDiv.data( 'totalConditions', totalConds, false );
 198+ } );
 199+
 200+ return initialDiv;
 201+ }; // setcontribs
 202+
 203+ // check anonymous
 204+ var anon = false;
 205+ if ( parseInt( userDef['anonymous'] ) == 1 ) {
 206+ anon = true;
 207+ }
 208+ $( "#anon_users_checkbox" ).attr( 'checked', anon );
 209+
 210+ // clear out old contents
 211+ $( "#contrib_opts_container" ).empty();
 212+
 213+ var setup_set_contribs = function( contribName ) {
 214+ var current_contribs = userDef[contribName];
 215+ if ( current_contribs == undefined ) {
 216+ current_contribs = new Array();
 217+ }
 218+ $( "#contrib_opts_container" ).append( setContribs( current_contribs, contribName ) );
 219+ };
 220+
 221+ // total contribs
 222+ setup_set_contribs( 'total_contribs' );
 223+ setup_set_contribs( 'contribs_span_1' );
 224+ setup_set_contribs( 'contribs_span_2' );
 225+ setup_set_contribs( 'contribs_span_3' );
 226+
 227+ // OK button
 228+ var okButton = $( "<input>" ).attr( 'id', 'ok_button' );
 229+ okButton.attr( 'type', 'button' );
 230+ okButton.attr( 'value', 'ok' );
 231+ okButton.click( function() {
 232+ $.getUserDefsFromDialog();
 233+ $( "#user_def_dialog" ).dialog( 'close' );
 234+ } );
 235+ $( "#contrib_opts_container" ).append( okButton );
 236+ }; // renderUserDefDialogWith
 237+
 238+ // functions
 239+ $.updateChart = function() {
 240+ event_name = $( "#chart_img" ).data( 'event_name' );
 241+
 242+ var processChartJSON = function( data, status ) {
 243+
 244+ var getMax = function( findMax ) {
 245+ var retval = Number.MIN_VALUE;
 246+ for ( var i in findMax ) {
 247+ if ( findMax[i] > retval ) {
 248+ retval = findMax[i];
 249+ }
 250+ }
 251+ return retval;
 252+ };
 253+
 254+ max1 = getMax( data['datapoints']['expert'] );
 255+ max2 = getMax( data['datapoints']['intermediate'] );
 256+ max3 = getMax( data['datapoints']['basic'] );
 257+ max = Math.max( max3, Math.max( max1, max2 ) );
 258+ chartURL = 'http://chart.apis.google.com/chart?' + 'chs=400x400&' + 'cht=lc&'
 259+ + 'chco=FF0000,0000FF,00FF00&' + 'chtt=' + event_name + ' from ' + $( "#start_date" ).val()
 260+ + ' to ' + $( "#end_date" ).val() + "&" + 'chdl=' + 'Expert|Intermediate|Beginner' + "&"
 261+ + 'chxt=x,y&' + 'chd=t:' + data['datapoints']['expert'].join( ',' ) + "|"
 262+ + data['datapoints']['intermediate'].join( ',' ) + "|" + data['datapoints']['basic'].join( ',' )
 263+ + "&" + 'chds=0,' + max + ',0,' + max + ',0,' + max;
 264+ $( "#chart_img" ).attr( 'src', chartURL );
 265+ };
 266+
 267+ start_date = $( "#start_date" ).val();
 268+ if ( $( "#start_date" ).hasClass( 'hidden' ) ) {
 269+ start_date = '0';
 270+ }
 271+
 272+ end_date = $( "#end_date" ).val();
 273+ if ( $( "#end_date" ).hasClass( 'hidden' ) ) {
 274+ end_date = '0';
 275+ }
 276+
 277+ // post relevant info
 278+ $.post( wgScriptPath + '/api.php', {
 279+ 'action' : 'specialclicktracking',
 280+ 'format' : 'json',
 281+ 'eventid' : $( "#chart_img" ).data( 'eventid' ),
 282+ 'increment' : $( "#chart_increment" ).val(),
 283+ 'startdate' : start_date,
 284+ 'enddate' : end_date,
 285+ 'userdefs' : $.json_encode( wgClickTrackUserDefs )
 286+ }, processChartJSON, 'json' );
 287+ };
 288+
 289+ // pretty colors for the table
 290+ $.colorizeTable = function() {
 291+ // expert
 292+
 293+ // get totals
 294+ var expert_total = 0;
 295+
 296+ $( ".expert_data" ).each( function() {
 297+ expert_total += parseInt( $( this ).attr( 'value' ) );
 298+ } );
 299+
 300+ // set proper red shade
 301+ $( ".expert_data" ).each( function() {
 302+ var rval = 255;
 303+ var gval = ( expert_total == 0 ? 255 : 255 - ( 255 * $( this ).attr( 'value' ) / expert_total ) );
 304+ var bval = gval;
 305+ rgbString = "rgb(" + parseInt( rval ) + "," + parseInt( gval ) + "," + parseInt( bval ) + ")";
 306+ $( this ).data( 'rgb', rgbString );
 307+ $( this ).css( 'color', rgbString );
 308+ $( this ).css( 'background-color', rgbString );
 309+ } );
 310+
 311+ // intermediate
 312+
 313+ // total
 314+ var intermediate_total = 0;
 315+ $( ".intermediate_data" ).each( function() {
 316+ intermediate_total += parseInt( $( this ).attr( 'value' ) );
 317+ } );
 318+
 319+ // blue shade
 320+ $( ".intermediate_data" ).each(
 321+ function() {
 322+ var rval = ( intermediate_total == 0 ? 255
 323+ : 255 - ( 255 * $( this ).attr( 'value' ) / intermediate_total ) );
 324+ var gval = rval;
 325+ var bval = 255;
 326+ rgbString = "rgb(" + parseInt( rval ) + "," + parseInt( gval ) + "," + parseInt( bval ) + ")";
 327+ $( this ).data( 'rgb', rgbString );
 328+ $( this ).css( 'color', rgbString );
 329+ $( this ).css( 'background-color', rgbString );
 330+ } );
 331+
 332+ // total
 333+ var basic_total = 0;
 334+ $( ".basic_data" ).each( function() {
 335+ basic_total += parseInt( $( this ).attr( 'value' ) );
 336+ } );
 337+
 338+ // green shade
 339+ $( ".basic_data" ).each( function() {
 340+ var rval = ( basic_total == 0 ? 255 : 255 - ( 255 * $( this ).attr( 'value' ) / basic_total ) );
 341+ var gval = 255;
 342+ var bval = rval;
 343+ rgbString = "rgb(" + parseInt( rval ) + "," + parseInt( gval ) + "," + parseInt( bval ) + ")";
 344+ $( this ).data( 'rgb', rgbString );
 345+ $( this ).css( 'color', rgbString );
 346+ $( this ).css( 'background-color', rgbString );
 347+ } );
 348+
 349+ // I wanted to do this with classes, but the element's style rule wins over class rule
 350+ // and each element has its own alternative color
 351+ $( ".event_data" ).mouseover( function() {
 352+ $( this ).css( 'color', '#000000' );
 353+ $( this ).css( 'background-color', '#FFFFFF' );
 354+ } );
 355+
 356+ $( ".event_data" ).mouseout( function() {
 357+ rgbString = $( this ).data( "rgb" );
 358+ $( this ).css( 'color', rgbString );
 359+ $( this ).css( 'background-color', rgbString );
 360+ } );
 361+
 362+ }; // colorize
 363+
 364+ $.updateTable = function() {
 365+ var processTableJSON = function( data, status ) {
 366+ // clear
 367+ $( ".table_data_row" ).each( function() {
 368+ $( this ).remove();
 369+ } );
 370+
 371+ var row_count = 0;
 372+ for ( var row_iter in data['tablevals']['vals'] ) {
 373+ var row = data['tablevals']['vals'][row_iter]; // really, JS?
 374+ row_count++;
 375+
 376+ var outputRow = $( "<tr></tr>" );
 377+ outputRow.addClass( 'table_data_row' );
 378+
 379+ var cell = $( "<td></td>" ).attr( 'id', 'event_name_' + row_count );
 380+ cell.addClass( 'event_name' );
 381+ cell.attr( 'value', row['event_id'] );
 382+ cell.text( row['event_name'] );
 383+ outputRow.append( cell );
 384+
 385+ var createClassCell = function( userclass ) {
 386+ var newcell = $( "<td></td>" ).attr( 'id', 'event_' + userclass + '_' + row_count );
 387+ newcell.addClass( 'event_data' );
 388+ newcell.addClass( userclass + '_data' );
 389+ newcell.text( row[userclass] );
 390+ newcell.attr( 'value', row[userclass] );
 391+ outputRow.append( newcell );
 392+ };
 393+
 394+ createClassCell( 'expert' );
 395+ createClassCell( 'intermediate' );
 396+ createClassCell( 'basic' );
 397+ createClassCell( 'total' );
 398+ $( "#clicktrack_data_table" ).append( outputRow );
 399+ }
 400+
 401+ $.colorizeTable();
 402+ $.changeDataLinks();
 403+ };
 404+
 405+ start_date = $( "#start_date" ).val();
 406+ if ( $( "#start_date" ).hasClass( 'hidden' ) ) {
 407+ start_date = '0';
 408+ }
 409+
 410+ end_date = $( "#end_date" ).val();
 411+ if ( $( "#end_date" ).hasClass( 'hidden' ) ) {
 412+ end_date = '0';
 413+ }
 414+
 415+ // post relevant info
 416+ $.post( wgScriptPath + '/api.php', {
 417+ 'action' : 'specialclicktracking',
 418+ 'format' : 'json',
 419+ 'eventid' : 1,
 420+ 'increment' : $( "#chart_increment" ).val(),
 421+ 'startdate' : start_date,
 422+ 'enddate' : end_date,
 423+ 'userdefs' : $.json_encode( wgClickTrackUserDefs ),
 424+ 'tabledata' : 1
 425+ }, processTableJSON, 'json' );
 426+
 427+ }; // updateTable
 428+
 429+ $.setUIControls = function() {
 430+ // SET UP DATE RANGES
 431+
 432+ // date-pickers for start and end dates
 433+ $( '.date_range_input' ).each( function() {
 434+ $( this ).datepicker();
 435+ $( this ).datepicker( 'option', 'dateFormat', 'yymmdd' );
 436+ } );
 437+ var startDate = new Date();
 438+ $( '#start_date' ).val( "20091009" ); // click_tracking start date as default
 439+
 440+ var toggleDateInput = function( tableRow ) {
 441+ var checked = false;
 442+ tableRow.children().each( function() {
 443+ if ( checked == false ) {
 444+ checked = $( this ).children( "input:checkbox" ).eq( 0 ).is( ":checked" );
 445+ }
 446+ } );
 447+
 448+ if ( checked ) {
 449+ tableRow.removeClass( 'disabled_option' );
 450+ tableRow.children( "td" ).each( function() {
 451+ $( this ).children( ".date_range_input" ).removeClass( 'hidden' );
 452+ } );
 453+ } else {
 454+ tableRow.children( "td" ).each( function() {
 455+ $( this ).children( ".date_range_input" ).addClass( 'hidden' );
 456+ } );
 457+ tableRow.addClass( 'disabled_option' );
 458+ }
 459+ };
 460+
 461+ $( '.date_range_checkbox' ).click( function() {
 462+ toggleDateInput( $( this ).closest( 'tr' ) );
 463+ } );
 464+
 465+ // update table
 466+ $( '#update_table_button' ).click( $.updateTable );
 467+
 468+ // CHART DIALOG
 469+ $( "#chart_dialog" ).dialog( {
 470+ autoOpen : false,
 471+ width : 400
 472+ } );
 473+ $( "#chart_img" ).css( 'cursor', 'pointer' );
 474+ $( "#chart_img" ).click( function() {
 475+ $( "#chart_dialog" ).dialog( 'open' );
 476+ } );
 477+
 478+ $( "#chart_increment" ).data( 'value', $( "#chart_increment" ).val() );
 479+
 480+ $( "#change_graph" ).click( function() {
 481+ $( "#chart_dialog" ).dialog( 'close' );
 482+
 483+ // check if the value actually changed, if so, update and increment things accordingly
 484+ if ( $( "#chart_increment" ).data( 'value' ) != $( "#chart_increment" ).val() ) {
 485+ $( "#chart_increment" ).data( 'value', $( "#chart_increment" ).val() );
 486+ $.updateChart();
 487+ }
 488+
 489+ } );
 490+
 491+ // CHANGE USER INFO DIALOG
 492+ $( "#user_def_dialog" ).dialog( {
 493+ autoOpen : false,
 494+ width : 400
 495+ } );
 496+ $( "#user_def_alter_legend" ).data( 'defaultChangeText', $( "#user_def_alter_legend" ).text() );
 497+
 498+ // CHANGE USER/INTERMEDIATE/EXPERT DIALOGS
 499+ var loadHeaderInfo = function( headerName ) {
 500+ $( "#" + headerName + "_header" ).css( 'cursor', 'pointer' );
 501+ $( "#" + headerName + "_header" ).click( function() {
 502+ $.renderUserDefDialogWith( wgClickTrackUserDefs[headerName], headerName );
 503+ $( "#user_def_dialog" ).dialog( 'open' );
 504+ } );
 505+ }; // headername
 506+
 507+ loadHeaderInfo( 'basic' );
 508+ loadHeaderInfo( 'intermediate' );
 509+ loadHeaderInfo( 'expert' );
 510+
 511+ };
 512+
 513+ $.changeDataLinks = function() {
 514+ $( ".event_name" ).each( function() {
 515+ $( this ).css( 'cursor', 'pointer' );
 516+
 517+ $( this ).click( function() {
 518+ $( "#chart_img" ).data( 'eventid', $( this ).attr( 'value' ) );
 519+ $( "#chart_img" ).data( 'event_name', $( this ).text() );
 520+ $.updateChart();
 521+ } ); // click
 522+ } ); // each
 523+ }; // addlink
 524+
 525+ return $( this );
 526+} )( jQuery );
 527+
 528+// colorize the table on document.ready
 529+$( document ).ready( $.colorizeTable );
 530+$( document ).ready( $.changeDataLinks );
 531+$( document ).ready( $.setUIControls );
Property changes on: trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.special.js
___________________________________________________________________
Added: svn:eol-style
1532 + native
Index: trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.js
@@ -0,0 +1,20 @@
 2+/*
 3+ * JavaScript for Click Tracking
 4+ */
 5+
 6+( function( $ ) {
 7+ // Add click tracking hooks to the sidebar
 8+ $(document).ready( function() {
 9+ $( '#p-logo a, #p-navigation a, #p-interaction a, #p-tb a' ).each( function() {
 10+ var href = $(this).attr( 'href' );
 11+ var token = mediaWiki.config.get( 'wgTrackingToken' );
 12+ // Only modify local URLs
 13+ if ( href.length > 0 && href[0] == '/' && ( href.length == 1 || href[1] != '/' ) ) {
 14+ var id = 'leftnav-' + skin + '-' + ( $(this).attr( 'id' ) || $(this).parent().attr( 'id' ) );
 15+ href = wgScriptPath + '/api.php?action=clicktracking' +
 16+ '&eventid=' + id + '&token=' + token + '&redirectto=' + escape( href );
 17+ $(this).attr( 'href', href );
 18+ }
 19+ } );
 20+ } );
 21+} )( jQuery );
Property changes on: trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.js
___________________________________________________________________
Added: svn:eol-style
122 + native
Index: trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.special.css
@@ -0,0 +1,53 @@
 2+/*
 3+ * CSS for Click Tracking special page
 4+ */
 5+
 6+.table_headers {
 7+ font-weight: bold;
 8+ border: bottom;
 9+}
 10+.table_data_row {
 11+ text-align: center;
 12+}
 13+.table_data_row .event_name {
 14+ text-align: left;
 15+ font-weight: bold;
 16+}
 17+#clicktrack_data_table {
 18+ float: left;
 19+}
 20+#chart_img {
 21+ float: left;
 22+ margin-left: 90px;
 23+}
 24+#change_graph_cell {
 25+ text-align: right;
 26+}
 27+.disabled_option {
 28+ color: #999999;
 29+}
 30+.hidden {
 31+ display: none;
 32+}
 33+.control_div {
 34+ margin: 10px;
 35+ clear: both;
 36+}
 37+.sub_option_div {
 38+ margin-top: 4px;
 39+ margin-left: 15px;
 40+}
 41+.sub_option_div input[type="text"] {
 42+ width: 20px;
 43+ margin-left: 10px;
 44+}
 45+#date_range {
 46+ display: inline;
 47+ float: left;
 48+ width: 200px;
 49+}
 50+.add_condition_button {
 51+ float: right;
 52+ color: blue;
 53+ cursor: pointer;
 54+}
\ No newline at end of file
Property changes on: trunk/extensions/UsabilityInitiative/ClickTracking/modules/clickTracking.special.css
___________________________________________________________________
Added: svn:eol-style
155 + native
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ApiSpecialClickTracking.php
@@ -1,6 +1,6 @@
22 <?php
33 /**
4 - * Extend the API for click tracking visualization in the special:clicktracking page
 4+ * Click Tracking special page API extension
55 *
66 * @file
77 * @ingroup API
@@ -9,8 +9,14 @@
1010 class ApiSpecialClickTracking extends ApiBase {
1111
1212 /**
13 - * runs when the API is called with "specialclicktracking", takes in "startdate" and "enddate" as YYYYMMDD , "eventid" as the event ID,
14 - * and "increment" as how many days to increment
 13+ * API specialclicktracking action
 14+ *
 15+ * Parameters:
 16+ * startdate: begining of results
 17+ * enddate: ending of results
 18+ * eventid: identier of event being queried
 19+ * increment: how many days to increment
 20+ *
1521 * @see includes/api/ApiBase#execute()
1622 */
1723 public function execute() {
@@ -22,26 +28,30 @@
2329 $increment = $params['increment'];
2430 $userDefString = $params['userdefs'];
2531
26 - // this is if it's asking for tableData
 32+ // This is if it's asking for tableData
2733 if ( isset( $params['tabledata'] ) ) {
2834 $tableData = SpecialClickTracking::buildRowArray( $startdate, $enddate, $userDefString );
2935 $this->getResult()->addValue( array( 'tablevals' ), 'vals', $tableData );
30 - } else { // chart data
 36+ } else {
 37+ // Chart data
3138 $click_data = array();
3239 try {
3340 $click_data = SpecialClickTracking::getChartData( $event_id, $startdate, $enddate, $increment, $userDefString );
3441 $this->getResult()->addValue( array( 'datapoints' ), 'expert', $click_data['expert'] );
3542 $this->getResult()->addValue( array( 'datapoints' ), 'basic', $click_data['basic'] );
3643 $this->getResult()->addValue( array( 'datapoints' ), 'intermediate', $click_data['intermediate'] );
37 - } catch ( Exception $e ) { /* no result */ }
 44+ } catch ( Exception $e ) {
 45+ /* No result */
 46+ }
3847 }
3948 }
4049
4150 /**
4251 * Required parameter check
 52+ *
4353 * @param $params params extracted from the POST
4454 */
45 - protected function validateParams( $params ) {
 55+ protected function validateParams( $params ) {
4656 $required = array( 'eventid', 'startdate', 'enddate', 'increment', 'userdefs' );
4757 foreach ( $required as $arg ) {
4858 if ( !isset( $params[$arg] ) ) {
@@ -49,21 +59,21 @@
5060 }
5161 }
5262
53 - // check if event id parses to an int greater than zero
 63+ // Check if event id parses to an int greater than zero
5464 if ( (int) $params['eventid'] < 0 ) {
5565 $this->dieUsage( 'Invalid event ID', 'badeventid' );
5666 }
5767
58 - // check start and end date are of proper format
 68+ // Check start and end date are of proper format
5969 if ( $params['startdate'] != 0 && strptime( SpecialClickTracking::space_out_date( $params['startdate'] ), "%Y %m %d" ) === false ) {
6070 $this->dieUsage( "startdate not in YYYYMMDD format: <<{$params['startdate']}>>", 'badstartdate' );
6171 }
62 - if ( $params['enddate'] != 0 && strptime( SpecialClickTracking::space_out_date( $params['enddate'] ), "%Y %m %d" ) === false ) {
 72+ if ( $params['enddate'] != 0 && strptime( SpecialClickTracking::space_out_date( $params['enddate'] ), "%Y %m %d" ) === false ) {
6373 $this->dieUsage( "enddate not in YYYYMMDD format: <<{$params['enddate']}>>", 'badenddate' );
6474 }
6575
66 - // check if increment is a positive int
67 - if ( (int) $params['increment'] <= 0 ) {
 76+ // Check if increment is a positive int
 77+ if ( (int) $params['increment'] <= 0 ) {
6878 $this->dieUsage( 'Invalid increment', 'badincrement' );
6979 }
7080
@@ -80,58 +90,57 @@
8191 'increment' => 'increment interval (in days) for data points',
8292 'userdefs' => 'JSON object to encode user definitions',
8393 'tabledata' => 'set to 1 for table data instead of chart data'
84 - );
 94+ );
8595 }
8696
8797 public function getDescription() {
8898 return array(
8999 'Returns data to the Special:ClickTracking visualization page'
90 - );
 100+ );
91101 }
92 -
 102+
93103 public function getPossibleErrors() {
94 - return array_merge( parent::getPossibleErrors(), array(
95 - array( 'missingparam', 'eventid' ),
96 - array( 'missingparam', 'startdate' ),
97 - array( 'missingparam', 'enddate' ),
98 - array( 'missingparam', 'increment' ),
99 - array( 'missingparam', 'userdefs' ),
100 - array( 'code' => 'badeventid', 'info' => 'Invalid event ID' ),
101 - array( 'code' => 'badstartdate', 'info' => 'startdate not in YYYYMMDD format: <<\'startdate\'>>' ),
102 - array( 'code' => 'badenddate', 'info' => 'enddate not in YYYYMMDD format: <<\'enddate\'>>' ),
103 - array( 'code' => 'badincrement', 'info' => 'Invalid increment' ),
104 - array( 'code' => 'badjson', 'info' => 'Invalid JSON encoding <<\'userdefs\'>>' ),
 104+ return array_merge( parent::getPossibleErrors(), array(
 105+ array( 'missingparam', 'eventid' ),
 106+ array( 'missingparam', 'startdate' ),
 107+ array( 'missingparam', 'enddate' ),
 108+ array( 'missingparam', 'increment' ),
 109+ array( 'missingparam', 'userdefs' ),
 110+ array( 'code' => 'badeventid', 'info' => 'Invalid event ID' ),
 111+ array( 'code' => 'badstartdate', 'info' => 'startdate not in YYYYMMDD format: <<\'startdate\'>>' ),
 112+ array( 'code' => 'badenddate', 'info' => 'enddate not in YYYYMMDD format: <<\'enddate\'>>' ),
 113+ array( 'code' => 'badincrement', 'info' => 'Invalid increment' ),
 114+ array( 'code' => 'badjson', 'info' => 'Invalid JSON encoding <<\'userdefs\'>>' ),
105115 ) );
106116 }
107117
108118 public function getAllowedParams() {
109119 return array(
110120 'eventid' => array(
111 - ApiBase::PARAM_TYPE => 'integer',
112 - ApiBase::PARAM_MIN => 1
113 - ),
 121+ ApiBase::PARAM_TYPE => 'integer',
 122+ ApiBase::PARAM_MIN => 1
 123+ ),
114124 'startdate' => array(
115 - ApiBase::PARAM_TYPE => 'integer'
116 - ),
 125+ ApiBase::PARAM_TYPE => 'integer'
 126+ ),
117127 'enddate' => array(
118 - ApiBase::PARAM_TYPE => 'integer'
119 - ),
 128+ ApiBase::PARAM_TYPE => 'integer'
 129+ ),
120130 'increment' => array(
121 - ApiBase::PARAM_TYPE => 'integer',
122 - ApiBase::PARAM_MIN => 1,
123 - ApiBase::PARAM_MAX => 365 // 1 year
124 - ),
 131+ ApiBase::PARAM_TYPE => 'integer',
 132+ ApiBase::PARAM_MIN => 1,
 133+ ApiBase::PARAM_MAX => 365 // 1 year
 134+ ),
125135 'userdefs' => array(
126 - ApiBase::PARAM_TYPE => 'string'
127 - ),
 136+ ApiBase::PARAM_TYPE => 'string'
 137+ ),
128138 'tabledata' => array(
129 - ApiBase::PARAM_TYPE => 'integer'
130 - ),
 139+ ApiBase::PARAM_TYPE => 'integer'
 140+ ),
131141 );
132142 }
133143
134144 public function getVersion() {
135145 return __CLASS__ . ': $Id$';
136146 }
137 -
138147 }
\ No newline at end of file
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ClickTracking.php
@@ -1,37 +1,29 @@
22 <?php
33 /**
44 * Usability Initiative Click Tracking extension
5 - *
 5+ *
66 * @file
77 * @ingroup Extensions
8 - *
9 - * This file contains the include file for the Click Tracking portion of the
10 - * UsabilityInitiative extension of MediaWiki.
11 - *
12 - * Usage: Include the following line in your LocalSettings.php
13 - * require_once( "$IP/extensions/UsabilityInitiative/ClickTracking/ClickTracking.php" );
14 - *
 8+ *
159 * @author Nimish Gautam <ngautam@wikimedia.org>
 10+ * @author Trevor Parscal <tparscal@wikimedia.org>
1611 * @license GPL v2 or later
1712 * @version 0.1.1
1813 */
1914
2015 /* Configuration */
2116
22 -// Increment this value when you change ClickTracking.js
23 -$wgClickTrackingStyleVersion = 5;
24 -
25 -// click throttle, should be seen as "1 out of every $wgClickTrackThrottle users will have it enabled"
26 -// setting this to 1 means all users will have it enabled
27 -// setting to a negative number will disable it for all users
 17+// Click tracking throttle, should be seen as "1 out of every $wgClickTrackThrottle users will have it enabled"
 18+// Setting this to 1 means all users will have it enabled, setting to a negative number will disable it for all users
2819 $wgClickTrackThrottle = -1;
2920
30 -// set the time window for what we consider 'recent' contributions, in days
31 -$wgClickTrackContribGranularity1 = 60 * 60 * 24 * 365 / 2; // half a year
32 -$wgClickTrackContribGranularity2 = 60 * 60 * 24 * 365 / 4; // 1/4 a year (3 months approx)
33 -$wgClickTrackContribGranularity3 = 60 * 60 * 24 * 30; //30 days (1 month approx)
 21+// Set the time window for what we consider 'recent' contributions, in days
 22+$wgClickTrackContribGranularity1 = 60 * 60 * 24 * 365 / 2; // 6 months
 23+$wgClickTrackContribGranularity2 = 60 * 60 * 24 * 365 / 4; // 3 months
 24+$wgClickTrackContribGranularity3 = 60 * 60 * 24 * 30; // 1 month
3425
35 -// Credits
 26+/* Setup */
 27+
3628 $wgExtensionCredits['other'][] = array(
3729 'path' => __FILE__,
3830 'name' => 'Click Tracking',
@@ -40,30 +32,16 @@
4133 'descriptionmsg' => 'clicktracking-desc',
4234 'url' => 'http://www.mediawiki.org/wiki/Extension:UsabilityInitiative'
4335 );
44 -
45 -// Includes parent extension
46 -require_once( dirname( dirname( __FILE__ ) ) . "/UsabilityInitiative.php" );
47 -
48 -// Adds Autoload Classes
49 -$dir = dirname( __FILE__ ) . '/';
50 -$wgAutoloadClasses['ClickTrackingHooks'] = $dir . 'ClickTracking.hooks.php';
51 -$wgAutoloadClasses['ApiClickTracking'] = $dir . 'ApiClickTracking.php';
52 -$wgAutoloadClasses['SpecialClickTracking'] = $dir . 'SpecialClickTracking.php';
53 -$wgAutoloadClasses['ApiSpecialClickTracking'] = $dir .'ApiSpecialClickTracking.php';
54 -
55 -// Hooked functions
56 -$wgHooks['LoadExtensionSchemaUpdates'][] = 'ClickTrackingHooks::schema';
57 -$wgHooks['AjaxAddScript'][] = 'ClickTrackingHooks::addJS';
 36+$wgAutoloadClasses['ClickTrackingHooks'] = dirname( __FILE__ ) . '/ClickTracking.hooks.php';
 37+$wgAutoloadClasses['ApiClickTracking'] = dirname( __FILE__ ) . '/ApiClickTracking.php';
 38+$wgAutoloadClasses['SpecialClickTracking'] = dirname( __FILE__ ) . '/SpecialClickTracking.php';
 39+$wgAutoloadClasses['ApiSpecialClickTracking'] = dirname( __FILE__ ) . '/ApiSpecialClickTracking.php';
 40+$wgHooks['LoadExtensionSchemaUpdates'][] = 'ClickTrackingHooks::loadExtensionSchemaUpdates';
 41+$wgHooks['BeforePageDisplay'][] = 'ClickTrackingHooks::beforePageDisplay';
5842 $wgHooks['ParserTestTables'][] = 'ClickTrackingHooks::parserTestTables';
59 -
60 -// Set up the new API module
6143 $wgAPIModules['clicktracking'] = 'ApiClickTracking';
6244 $wgAPIModules['specialclicktracking'] = 'ApiSpecialClickTracking';
63 -
64 -//Special page setup
6545 $wgSpecialPages['ClickTracking'] = 'SpecialClickTracking';
6646 $wgGroupPermissions['sysop']['clicktrack'] = true;
67 -
68 -// Adds Internationalized Messages
69 -$wgExtensionMessagesFiles['ClickTracking'] = $dir . 'ClickTracking.i18n.php';
70 -$wgExtensionAliasesFiles['ClickTracking'] = $dir . 'ClickTracking.alias.php';
 47+$wgExtensionMessagesFiles['ClickTracking'] = dirname( __FILE__ ) . '/ClickTracking.i18n.php';
 48+$wgExtensionAliasesFiles['ClickTracking'] = dirname( __FILE__ ) . '/ClickTracking.alias.php';

Comments

#Comment by Catrope (talk | contribs)   19:06, 1 October 2010
		$dbresult = $dbr->select(
-			array( 'click_tracking', 'click_tracking_events' ),
-			array( 'count(event_id) as totalevtid', 'event_id', 'event_name' ),
-			$time_constraint,
-			__METHOD__,
-			array( 'GROUP BY' => 'event_id', 'ORDER BY' => 'totalevtid DESC' ),
-			array( 'click_tracking_events' =>
-				array( 'LEFT JOIN', 'event_id=id' )
-			)
+		array( 'click_tracking', 'click_tracking_events' ),
+		array( 'count(event_id) as totalevtid', 'event_id', 'event_name' ),
+		$time_constraint,
+		__METHOD__,
+		array( 'GROUP BY' => 'event_id', 'ORDER BY' => 'totalevtid DESC' ),
+		array( 'click_tracking_events' =>
+		array( 'LEFT JOIN', 'event_id=id' )
+		)
 		);

That doesn't look right indentation-wise. There's lots of such cases in this rev.

+		$j.post(

You missed a $j --> $ conversion here (and elsewhere).

+			for ( var condition in conditionArray ) {

If conditionArray is really an array, this'll break. (Wasn't introduced in this rev, I know, but I happened to notice this.)

#Comment by Trevor Parscal (WMF) (talk | contribs)   19:40, 1 October 2010

$j stuff is fixed in r74096.

As for the array thing... I'm notifying Nimish of his misdeeds.

#Comment by Nimish Gautam (talk | contribs)   19:50, 1 October 2010

It was an array once upon a time but it's an object now

#Comment by Nimish Gautam (talk | contribs)   21:07, 1 October 2010

Actually, nevermind. It is an array and works fine. What you smokin, R oan?

#Comment by Catrope (talk | contribs)   21:10, 1 October 2010

for(var foo in someArray) works fine in most cases, but if something adds additional functions to the Array type with, say Array.prototype.indexOf = function( .... (you would do this in old browsers like IE7 that don't have that function natively), the for loop will turn up 'indexOf' as one of the keys, in addition to 0 through length-1 . As you can imagine, that breaks code in very interesting ways.

The convention is to use for(var i = 0; i < someArray.length; i++) for iterating over arrays because it is safe that way. for..in should only be used for objects.

Status & tagging log