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 @@ |
8 | 8 | */ |
9 | 9 | |
10 | 10 | class SpecialClickTracking extends SpecialPage { |
11 | | - |
| 11 | + |
12 | 12 | private static $minimum_date = '20091009'; // YYYYMMDD (+1 for today) |
13 | 13 | |
14 | 14 | private $user_defs = array(); |
15 | 15 | |
16 | 16 | function __construct() { |
17 | 17 | parent::__construct( 'ClickTracking' , 'clicktrack' ); |
18 | | - wfLoadExtensionMessages( 'ClickTracking' ); |
19 | | - UsabilityInitiativeHooks::initialize(); |
20 | | - UsabilityInitiativeHooks::addStyle( 'ClickTracking/SpecialClickTracking.css' ); |
21 | | - UsabilityInitiativeHooks::addScript( 'ClickTracking/SpecialClickTracking.js' ); |
22 | 18 | } |
23 | | - |
24 | | - |
| 19 | + |
25 | 20 | function setDefaults() { |
26 | 21 | $this->user_defs["basic"] = array( |
27 | 22 | "anonymous" => "1", |
28 | | - "total_contribs" => array( |
29 | | - array( "operation" => "<=", "value" => "10" ), |
30 | | - ), |
| 23 | + "total_contribs" => array( array( "operation" => "<=", "value" => "10" ) ), |
31 | 24 | ); |
32 | | - |
33 | 25 | $this->user_defs["intermediate"] = array( |
34 | 26 | "anonymous" => "0", |
35 | 27 | "total_contribs" => array( |
— | — | @@ -36,86 +28,73 @@ |
37 | 29 | array( "operation" => ">", "value" => "10" ), |
38 | 30 | ), |
39 | 31 | ); |
40 | | - |
41 | 32 | $this->user_defs["expert"] = array( |
42 | 33 | "anonymous" => "0", |
43 | | - "total_contribs" => array( |
44 | | - array( "operation" => ">=", "value" => "400" ), |
45 | | - ), |
| 34 | + "total_contribs" => array( array( "operation" => ">=", "value" => "400" ) ), |
46 | 35 | ); |
47 | | - |
48 | | - |
49 | 36 | } |
50 | | - |
51 | | - |
52 | | - |
| 37 | + |
53 | 38 | function execute( $par ) { |
54 | 39 | global $wgOut, $wgUser; |
| 40 | + |
| 41 | + $wgOut->addModules( 'clickTracking.special' ); |
55 | 42 | |
56 | 43 | // Check permissions |
57 | 44 | if ( !$this->userCanExecute( $wgUser ) ) { |
58 | 45 | $this->displayRestrictionError(); |
59 | 46 | return; |
60 | 47 | } |
61 | | - |
62 | | - |
| 48 | + |
63 | 49 | $this->setHeaders(); |
64 | 50 | $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 ) ); |
68 | 52 | $wgOut->setPageTitle( wfMsg( 'ct-title' ) ); |
69 | | - |
| 53 | + |
70 | 54 | $outputTable = ""; |
71 | | - |
72 | | - |
73 | | - // grab top N |
| 55 | + |
| 56 | + // Grab top N |
74 | 57 | $events = $this->getTopEvents(); |
75 | | - |
76 | | - // open table |
| 58 | + |
| 59 | + // Open table |
77 | 60 | $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 |
80 | 63 | $i = 0; |
81 | 64 | $db_result; |
82 | | - |
83 | | - // build row headers |
| 65 | + |
| 66 | + // Build row headers |
84 | 67 | $header_row = array(); |
85 | | - |
| 68 | + |
86 | 69 | $header_row["event_header"] = wfMsg( 'ct-event-name' ); |
87 | 70 | $header_row["expert_header"] = wfMsg( 'ct-expert-header' ); |
88 | 71 | $header_row["intermediate_header"] = wfMsg( 'ct-intermediate-header' ); |
89 | 72 | $header_row["basic_header"] = wfMsg( 'ct-beginner-header' ); |
90 | 73 | $header_row["total_header"] = wfMsg( 'ct-total-header' ); |
91 | 74 | $outputTable .= Xml::buildTableRow( array( "class" => "table_headers" ), $header_row ); |
92 | | - |
93 | | - // foreach event, build a row |
| 75 | + |
| 76 | + // Build event rows |
94 | 77 | while ( ( $db_result = $events->fetchRow() ) != null ) { |
95 | 78 | ++$i; |
96 | 79 | $outputTable .= $this->buildRow( $db_result, $i, $this->user_defs ); |
97 | 80 | } |
98 | | - |
99 | | - |
100 | | - // close table |
| 81 | + |
| 82 | + // Close table |
101 | 83 | $outputTable .= Xml::closeElement( "table" ); |
| 84 | + |
| 85 | + $wgOut->addHTML( $outputTable ); |
102 | 86 | |
103 | | - $wgOut->addHTML( $outputTable ); |
| 87 | + $wgOut->addHTML( $this->buildDateRange() ); |
104 | 88 | |
105 | | - $wgOut->addHTML( $this->buildDateRange() ); |
106 | | - |
107 | | - // build chart |
| 89 | + // Build chart |
108 | 90 | $wgOut->addHTML( $this->buildChart( "advanced.hide", 10, "20090815", "20090902", 1 ) ); |
109 | | - |
110 | | - // $wgOut->addHTML($this->buildControlBox()); |
111 | | - |
112 | 91 | $wgOut->addHTML( $this->buildChartDialog() ); |
113 | 92 | $wgOut->addHTML( $this->buildUserDefBlankDialog() ); |
114 | | - |
| 93 | + |
115 | 94 | } |
116 | | - |
117 | 95 | |
118 | 96 | /** |
119 | 97 | * Gets the data to build a chart for PHP or JS purposes |
| 98 | + * |
120 | 99 | * @param $event_id event id this chart is for |
121 | 100 | * @param $minTime minimum day |
122 | 101 | * @param $maxTime maximum day |
— | — | @@ -125,41 +104,39 @@ |
126 | 105 | * @return array with chart info |
127 | 106 | */ |
128 | 107 | static function getChartData( $event_id, $minTime, $maxTime, $increment, $userDefs, $isUserDefsJSON = true ) { |
129 | | - // get data |
| 108 | + // Get data |
130 | 109 | date_default_timezone_set( 'UTC' ); |
131 | | - |
| 110 | + |
132 | 111 | if ( $maxTime == 0 ) { |
133 | 112 | $maxTime = gmdate( "Ymd", time() ); // today |
134 | 113 | } |
135 | 114 | if ( $minTime == 0 ) { |
136 | 115 | $minTime = self::$minimum_date; |
137 | 116 | } |
138 | | - |
139 | | - |
| 117 | + |
140 | 118 | // FIXME: On PHP 5.3+, this will be MUCH cleaner |
141 | 119 | $currBeginDate = new DateTime( $minTime ); |
142 | 120 | $currEndDate = new DateTime( $minTime ); |
143 | 121 | $endDate = new DateTime( $maxTime ); |
144 | | - |
145 | | - |
146 | | - // get user definitions |
| 122 | + |
| 123 | + // Get user definitions |
147 | 124 | if ( $isUserDefsJSON ) { |
148 | 125 | $userDefs = json_decode( $userDefs, true ); |
149 | 126 | } |
150 | | - |
| 127 | + |
151 | 128 | $basicUserData = array(); |
152 | 129 | $intermediateUserData = array(); |
153 | 130 | $expertUserData = array(); |
154 | | - |
| 131 | + |
155 | 132 | // PHP 5.3...hurry! |
156 | 133 | $plural = ( $increment == 1 ? "" : "s" ); |
157 | | - |
| 134 | + |
158 | 135 | while ( $currEndDate->format( "U" ) < $endDate->format( "U" ) ) { |
159 | 136 | $currEndDate->modify( "+$increment day$plural" ); |
160 | | - |
| 137 | + |
161 | 138 | $minDate = $currBeginDate->format( "Ymd" ); |
162 | 139 | $maxDate = $currEndDate->format( "Ymd" ); |
163 | | - |
| 140 | + |
164 | 141 | $basicUserData[] = self::getTableValue( $event_id, $userDefs['basic'], $minDate, $maxDate ); |
165 | 142 | $intermediateUserData[] = self::getTableValue( $event_id, $userDefs['intermediate'], $minDate, $maxDate ); |
166 | 143 | $expertUserData[] = self::getTableValue( $event_id, $userDefs['expert'], $minDate, $maxDate ); |
— | — | @@ -170,12 +147,21 @@ |
171 | 148 | |
172 | 149 | function buildChart( $event_name, $event_id, $minTime, $maxTime, $increment ) { |
173 | 150 | $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 | + ); |
175 | 160 | return Xml::element( 'img', array( 'src' => $chartSrc , 'id' => 'chart_img' ) ); |
176 | 161 | } |
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 | + ) { |
180 | 166 | $max = max( max( $basicUserData ), max( $intermediateUserData ), max( $expertUserData ) ); |
181 | 167 | return "http://chart.apis.google.com/chart?" . wfArrayToCGI( |
182 | 168 | array( |
— | — | @@ -186,77 +172,91 @@ |
187 | 173 | 'chdl' => 'Expert|Intermediate|Beginner', |
188 | 174 | 'chxt' => 'x,y', |
189 | 175 | 'chd' => 't:' . implode( "," , $expertUserData ) . "|" . |
190 | | - implode( "," , $intermediateUserData ) . "|" . implode( "," , $basicUserData ), |
| 176 | + implode( "," , $intermediateUserData ) . "|" . implode( "," , $basicUserData ), |
191 | 177 | 'chds' => "0,$max,0,$max,0,$max" |
192 | | - ) ); |
| 178 | + ) ); |
193 | 179 | } |
194 | | - |
195 | | - |
| 180 | + |
196 | 181 | function buildUserDefBlankDialog() { |
197 | 182 | $control = ""; |
198 | 183 | $control .= Xml::openElement( "div", array( "id" => "user_def_dialog", "class" => "dialog" ) ); |
199 | | - |
| 184 | + |
200 | 185 | // currently editing...----| |
201 | 186 | $control .= Xml::openElement( "form", array( "id" => "user_definition_form", "class" => "user_def_form" ) ); |
202 | 187 | $control .= Xml::openElement( "fieldset", array( "id" => "user_def_alter_fieldset" ) ); |
203 | 188 | $control .= Xml::openElement( "legend", array( "id" => "user_def_alter_legend" ) ); |
204 | 189 | $control .= wfMsg( "ct-editing" ); |
205 | 190 | $control .= Xml::closeElement( "legend" ); |
206 | | - |
| 191 | + |
207 | 192 | // [] anonymous users? |
208 | 193 | $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 | + ); |
210 | 197 | $control .= Xml::closeElement( "input" ); |
211 | 198 | $control .= wfMsg( "ct-anon-users" ); |
212 | 199 | $control .= Xml::closeElement( "div" ); |
213 | | - |
| 200 | + |
214 | 201 | // ---------------- |
215 | 202 | $control .= Xml::openElement( "hr" ); |
216 | 203 | $control .= Xml::closeElement( "hr" ); |
217 | 204 | $control .= Xml::openElement( "div", array( "id" => "contrib_opts_container" ) ); |
218 | | - |
| 205 | + |
219 | 206 | // [] 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 | + ); |
222 | 213 | $control .= Xml::closeElement( "input" ); |
223 | 214 | $control .= wfMsg( "ct-user-contribs" ); |
224 | | - |
225 | | - |
| 215 | + |
| 216 | + |
226 | 217 | $control .= Xml::closeElement( "div" ); |
227 | | - |
| 218 | + |
228 | 219 | // [] 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 | + |
231 | 224 | $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 | + ); |
233 | 228 | $control .= Xml::closeElement( "input" ); |
234 | 229 | $control .= wfMsg( "ct-user-span" ) . " 1"; |
235 | 230 | $control .= Xml::closeElement( "div" ); |
236 | 231 | $control .= Xml::closeElement( "div" ); |
237 | | - |
| 232 | + |
238 | 233 | // [] 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 | + |
241 | 238 | $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 | + ); |
243 | 242 | $control .= Xml::closeElement( "input" ); |
244 | 243 | $control .= wfMsg( "ct-user-span" ) . " 2"; |
245 | 244 | $control .= Xml::closeElement( "div" ); |
246 | 245 | $control .= Xml::closeElement( "div" ); |
247 | | - |
| 246 | + |
248 | 247 | // [] 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 | + |
251 | 252 | $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 | + ); |
253 | 256 | $control .= Xml::closeElement( "input" ); |
254 | 257 | $control .= wfMsg( "ct-user-span" ) . " 3"; |
255 | 258 | $control .= Xml::closeElement( "div" ); |
256 | 259 | $control .= Xml::closeElement( "div" ); |
257 | | - |
258 | | - |
259 | | - |
260 | | - |
| 260 | + |
261 | 261 | $control .= Xml::closeElement( "div" );// close contrib opts |
262 | 262 | |
263 | 263 | $control .= Xml::closeElement( "fieldset" ); |
— | — | @@ -264,61 +264,72 @@ |
265 | 265 | $control .= Xml::closeElement( "div" ); |
266 | 266 | return $control; |
267 | 267 | } |
268 | | - |
269 | | - |
270 | | - |
| 268 | + |
271 | 269 | function buildUserDefNumberSelect( $include_checkbox, $include_and, $ids ) { |
272 | 270 | $control = ""; |
273 | 271 | 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 | + ); |
275 | 275 | $control .= Xml::closeElement( "input" ); |
276 | 276 | } |
277 | | - |
| 277 | + |
278 | 278 | if ( $include_and ) { |
279 | 279 | $control .= wfMsg( "ct-and" ); |
280 | 280 | } |
281 | | - |
| 281 | + |
282 | 282 | $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 | + ); |
284 | 286 | $control .= "<="; |
285 | 287 | $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 | + ); |
287 | 291 | $control .= ">="; |
288 | 292 | $control .= Xml::closeElement( "option" ); |
289 | 293 | $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 | + ); |
291 | 297 | $control .= Xml::closeElement( "input" ); |
292 | 298 | return $control; |
293 | 299 | } |
294 | | - |
295 | | - |
| 300 | + |
| 301 | + |
296 | 302 | function buildChartDialog() { |
297 | 303 | $control = ""; |
298 | 304 | $control .= Xml::openElement( "div", array( "id" => "chart_dialog", "class" => "dialog" ) ); |
299 | | - |
| 305 | + |
300 | 306 | $control .= Xml::openElement( "form", array( "id" => "chart_dialog_form", "class" => "chart_form" ) ); |
301 | 307 | $control .= Xml::openElement( "fieldset", array( "id" => "chart_dialog_alter_fieldset" ) ); |
302 | 308 | $control .= Xml::openElement( "legend", array( "id" => "chart_dialog_alter_legend" ) ); |
303 | 309 | $control .= wfMsg( "ct-increment-by" ); |
304 | 310 | $control .= Xml::closeElement( "legend" ); |
305 | | - |
| 311 | + |
306 | 312 | $control .= Xml::openElement( "table", array( "id" => "chart_dialog_increment_table" ) ); |
307 | 313 | $control .= Xml::openElement( "tbody", array( "id" => "chart_dialog_increment_tbody" ) ); |
308 | | - |
| 314 | + |
309 | 315 | $control .= Xml::openElement( "tr", array( "id" => "chart_dialog_increment_row" ) ); |
310 | | - |
| 316 | + |
311 | 317 | $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 | + ); |
313 | 322 | $control .= Xml::closeElement( "input" ); |
314 | 323 | $control .= Xml::closeElement( "td" ); |
315 | | - |
| 324 | + |
316 | 325 | $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 | + ); |
318 | 329 | $control .= Xml::closeElement( "input" ); |
319 | 330 | $control .= Xml::closeElement( "td" ); |
320 | | - |
| 331 | + |
321 | 332 | $control .= Xml::closeElement( "tr" ); |
322 | | - |
| 333 | + |
323 | 334 | $control .= Xml::closeElement( "tbody" ); |
324 | 335 | $control .= Xml::closeElement( "table" ); |
325 | 336 | $control .= Xml::closeElement( "fieldset" ); |
— | — | @@ -326,77 +337,98 @@ |
327 | 338 | $control .= Xml::closeElement( "div" ); |
328 | 339 | return $control; |
329 | 340 | } |
330 | | - |
331 | | - |
| 341 | + |
| 342 | + |
332 | 343 | function buildDateRange() { |
333 | 344 | $control = Xml::openElement( "form", array( "id" => "date_range" ) ); |
334 | | - |
| 345 | + |
335 | 346 | $control .= Xml::openElement( "fieldset", array( "id" => "date_range_fieldset" ) ); |
336 | 347 | $control .= Xml::openElement( "legend", array( "id" => "date_range_legend" ) ); |
337 | 348 | $control .= wfMsg( 'ct-date-range' ); |
338 | 349 | $control .= Xml::closeElement( "legend" ); |
339 | | - |
340 | 350 | |
341 | | - |
| 351 | + |
| 352 | + |
342 | 353 | $control .= Xml::openElement( "table", array( "id" => "date_range_table" ) ); |
343 | 354 | $control .= Xml::openElement( "tbody", array( "id" => "date_range_tbody" ) ); |
344 | | - |
345 | | - |
| 355 | + |
| 356 | + |
346 | 357 | $control .= Xml::openElement( "tr", array( "id" => "start_date_row" ) ); |
347 | | - |
| 358 | + |
348 | 359 | $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 | + ); |
350 | 366 | $control .= Xml::closeElement( "input" ); |
351 | 367 | $control .= wfMsg( "ct-start-date" ); |
352 | 368 | $control .= Xml::closeElement( "td" ); |
353 | | - |
| 369 | + |
354 | 370 | $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 | + ); |
356 | 374 | $control .= Xml::closeElement( "input" ); |
357 | 375 | $control .= Xml::closeElement( "td" ); |
358 | | - |
| 376 | + |
359 | 377 | $control .= Xml::closeElement( "tr" ); |
360 | | - |
| 378 | + |
361 | 379 | $control .= Xml::openElement( "tr", array( "id" => "end_date_row" ) ); |
362 | | - |
| 380 | + |
363 | 381 | $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 | + ); |
365 | 388 | $control .= Xml::closeElement( "input" ); |
366 | 389 | $control .= wfMsg( "ct-end-date" ); |
367 | 390 | $control .= Xml::closeElement( "td" ); |
368 | | - |
| 391 | + |
369 | 392 | $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 | + ); |
371 | 396 | $control .= Xml::closeElement( "input" ); |
372 | 397 | $control .= Xml::closeElement( "td" ); |
373 | | - |
| 398 | + |
374 | 399 | $control .= Xml::closeElement( "tr" ); |
375 | | - |
376 | 400 | |
| 401 | + |
377 | 402 | $control .= Xml::openElement( "tr", array( "id" => "update_row" ) ); |
378 | | - |
| 403 | + |
379 | 404 | $control .= Xml::openElement( "td" ); |
380 | 405 | $control .= Xml::closeElement( "td" ); |
381 | | - |
| 406 | + |
382 | 407 | $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 | + ); |
384 | 417 | $control .= Xml::closeElement( "input" ); |
385 | 418 | $control .= Xml::closeElement( "td" ); |
386 | | - |
| 419 | + |
387 | 420 | $control .= Xml::closeElement( "tr" ); |
388 | | - |
| 421 | + |
389 | 422 | $control .= Xml::closeElement( "tbody" ); |
390 | 423 | $control .= Xml::closeElement( "table" ); |
391 | 424 | $control .= Xml::closeElement( "fieldset" ); |
392 | | - |
| 425 | + |
393 | 426 | $control .= Xml::closeElement( "form" ); |
394 | | - |
| 427 | + |
395 | 428 | return $control; |
396 | 429 | } |
397 | | - |
398 | | - |
| 430 | + |
399 | 431 | /** |
400 | | - * |
| 432 | + * |
401 | 433 | * @param $minTime |
402 | 434 | * @param $maxTime |
403 | 435 | * @param $userDefs |
— | — | @@ -404,23 +436,21 @@ |
405 | 437 | * @return unknown_type |
406 | 438 | */ |
407 | 439 | public static function buildRowArray( $minTime, $maxTime, $userDefs, $is_JSON = true ) { |
408 | | - |
409 | | - |
410 | 440 | if ( $minTime == 0 ) { |
411 | 441 | $minTime = self::$minimum_date; |
412 | 442 | } |
413 | 443 | if ( $maxTime == 0 ) { |
414 | 444 | $maxTime = gmdate( "Ymd", time() ); // today |
415 | 445 | } |
416 | | - |
| 446 | + |
417 | 447 | if ( $is_JSON ) { |
418 | 448 | $userDefs = json_decode( $userDefs, true ); |
419 | 449 | } |
420 | | - |
| 450 | + |
421 | 451 | $events = self::getTopEvents( $minTime, $maxTime ); |
422 | | - |
| 452 | + |
423 | 453 | $returnArray = array(); |
424 | | - |
| 454 | + |
425 | 455 | while ( ( $data_result = $events->fetchRow() ) != null ) { |
426 | 456 | $outputArray = array(); |
427 | 457 | $outputArray['event_name'] = $data_result['event_name']; |
— | — | @@ -431,57 +461,56 @@ |
432 | 462 | $outputArray['total'] = $data_result["totalevtid"]; |
433 | 463 | $returnArray[] = $outputArray; |
434 | 464 | } |
435 | | - |
| 465 | + |
436 | 466 | return $returnArray; |
437 | | - |
438 | 467 | } |
439 | | - |
| 468 | + |
440 | 469 | function buildRow( $data_result, $row_count, $userDefs ) { |
441 | 470 | |
442 | | - $outputRow = Xml::openElement( "tr", array( "class" => "table_data_row" ) ); |
| 471 | + $outputRow = Xml::openElement( "tr", array( "class" => "table_data_row" ) ); |
443 | 472 | |
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" ); |
449 | 478 | |
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", |
454 | 483 | "value" => $cellValue ) ); |
455 | | - $outputRow .= $cellValue; |
456 | | - $outputRow .= Xml::closeElement( "td" ); |
| 484 | + $outputRow .= $cellValue; |
| 485 | + $outputRow .= Xml::closeElement( "td" ); |
457 | 486 | |
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", |
462 | 491 | "value" => $cellValue ) ); |
463 | | - $outputRow .= $cellValue; |
464 | | - $outputRow .= Xml::closeElement( "td" ); |
| 492 | + $outputRow .= $cellValue; |
| 493 | + $outputRow .= Xml::closeElement( "td" ); |
465 | 494 | |
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", |
470 | 499 | "value" => $cellValue ) ); |
471 | | - $outputRow .= $cellValue; |
472 | | - $outputRow .= Xml::closeElement( "td" ); |
| 500 | + $outputRow .= $cellValue; |
| 501 | + $outputRow .= Xml::closeElement( "td" ); |
473 | 502 | |
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", |
478 | 507 | "value" => $cellValue ) ); |
479 | | - $outputRow .= $cellValue; |
480 | | - $outputRow .= Xml::closeElement( "td" ); |
| 508 | + $outputRow .= $cellValue; |
| 509 | + $outputRow .= Xml::closeElement( "td" ); |
481 | 510 | |
482 | 511 | |
483 | | - $outputRow .= Xml::closeElement( "tr" ); |
| 512 | + $outputRow .= Xml::closeElement( "tr" ); |
484 | 513 | |
485 | | - return $outputRow; |
| 514 | + return $outputRow; |
486 | 515 | |
487 | 516 | } |
488 | 517 | |
— | — | @@ -491,95 +520,102 @@ |
492 | 521 | * @return date with spaces |
493 | 522 | */ |
494 | 523 | 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 | + ); |
496 | 529 | } |
497 | | - |
498 | | - |
| 530 | + |
499 | 531 | /* |
500 | 532 | * get time constraints |
501 | 533 | * @param minTime minimum day (YYYYMMDD) |
502 | 534 | * @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 |
504 | 537 | */ |
505 | 538 | static function getTimeConstraints( $minTime, $maxTime ) { |
506 | 539 | 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 | + } |
511 | 544 | 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 |
513 | 546 | $minTime .= "000000"; |
514 | 547 | $maxTime .= "000000"; |
515 | 548 | |
516 | 549 | $dbr = wfGetDB( DB_SLAVE ); |
517 | | - |
| 550 | + |
518 | 551 | // time constraint array |
519 | 552 | return array( |
520 | 553 | "`action_time` >= " . $dbr->addQuotes( $minTime ) , |
521 | 554 | "`action_time` <= " . $dbr->addQuotes( $maxTime ) |
522 | 555 | ); |
523 | | - |
| 556 | + |
524 | 557 | } |
525 | | - |
| 558 | + |
526 | 559 | } |
527 | | - |
528 | | - |
| 560 | + |
| 561 | + |
529 | 562 | /** |
530 | 563 | * Gets the top N events as set in the page pref |
| 564 | + * |
531 | 565 | * @param $time_constraint_statement |
532 | 566 | * @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 |
534 | 569 | */ |
535 | 570 | public static function getTopEvents( $minTime = "", $maxTime = "", $normalize_top_results = false ) { |
536 | | - |
| 571 | + |
537 | 572 | $time_constraint_statement = self::getTimeConstraints( $minTime, $maxTime ); |
538 | 573 | $time_constraint = $time_constraint_statement; |
539 | | - |
| 574 | + |
540 | 575 | $dbr = wfGetDB( DB_SLAVE ); |
541 | | - |
| 576 | + |
542 | 577 | // NOTE: This query is a performance nightmare |
543 | 578 | // Permission to run it is restricted by default |
544 | 579 | $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 | + ) |
553 | 588 | ); |
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" . |
557 | 592 | " LEFT JOIN click_tracking_events ON event_id=click_tracking_events.id". |
558 | 593 | " $time_constraint group by event_id order by totalevtid desc"; |
559 | | - */ |
560 | | - |
| 594 | + */ |
| 595 | + |
561 | 596 | // returns count(event_id),event_id, event_name, top one first |
562 | 597 | return $dbresult; |
563 | 598 | } |
564 | 599 | |
565 | 600 | /** |
566 | 601 | * 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 |
568 | 604 | */ |
569 | 605 | static function getTableValue( $event_id, $userDef, $minTime = '', $maxTime = '', $normalize_results = false ) { |
570 | | - |
| 606 | + |
571 | 607 | $dbr = wfGetDB( DB_SLAVE ); |
572 | 608 | $conds = array_merge( |
573 | 609 | self::getTimeConstraints( $minTime, $maxTime ), |
574 | 610 | SpecialClickTracking::buildUserDefConstraints( $userDef ), |
575 | 611 | array( 'event_id' => $event_id ) |
576 | 612 | ); |
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 | + |
580 | 615 | } |
581 | | - |
| 616 | + |
582 | 617 | /** |
583 | 618 | * Generates a query for a user type definition |
| 619 | + * |
584 | 620 | * @param $include_anon_users boolean, include anon users or not |
585 | 621 | * @param $total_contribs array, nonempty if total contribs to be included |
586 | 622 | * @param $contrib_1 array, nonempty AND conditions for user_contribs_1 |
— | — | @@ -588,46 +624,62 @@ |
589 | 625 | * @return unknown_type query |
590 | 626 | */ |
591 | 627 | public static function buildUserDefConstraints( $def ) { |
592 | | - |
| 628 | + |
593 | 629 | $dbr = wfGetDB( DB_SLAVE ); |
594 | | - |
| 630 | + |
595 | 631 | $include_anon_users = ( empty( $def['anonymous'] ) ? array():$def['anonymous'] ); |
596 | 632 | $total_contribs = ( empty( $def['total_contribs'] ) ? array():$def['total_contribs'] ); |
597 | 633 | $contrib_1 = ( empty( $def['contrib_1'] ) ? array():$def['contrib_1'] ); |
598 | 634 | $contrib_2 = ( empty( $def['contrib_2'] ) ? array():$def['contrib_2'] ); |
599 | 635 | $contrib_3 = ( empty( $def['contrib_3'] ) ? array():$def['contrib_3'] ); |
600 | | - |
| 636 | + |
601 | 637 | $or_conds = array(); |
602 | 638 | $and_conds = array(); |
603 | 639 | $sql = ""; |
604 | | - |
| 640 | + |
605 | 641 | |
606 | 642 | if ( (boolean)$include_anon_users ) { |
607 | 643 | $or_conds[] = array( "field" => "is_logged_in", "operation" => "=", "value" => "0" ); |
608 | 644 | } |
609 | | - |
| 645 | + |
610 | 646 | if ( !empty( $total_contribs ) ) { |
611 | 647 | 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 | + ); |
613 | 653 | } |
614 | 654 | } |
615 | | - |
| 655 | + |
616 | 656 | if ( !empty( $contrib_1 ) ) { |
617 | 657 | 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 | + ); |
619 | 663 | } |
620 | 664 | } |
621 | 665 | if ( !empty( $contrib_2 ) ) { |
622 | 666 | 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 | + ); |
624 | 672 | } |
625 | 673 | } |
626 | 674 | if ( !empty( $contrib_3 ) ) { |
627 | 675 | 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 | + ); |
629 | 681 | } |
630 | 682 | } |
631 | | - |
| 683 | + |
632 | 684 | foreach ( $and_conds as $cond ) { |
633 | 685 | if ( !empty( $sql ) ) { |
634 | 686 | $sql .= " AND "; |
— | — | @@ -640,10 +692,10 @@ |
641 | 693 | } |
642 | 694 | $sql .= $cond["field"] . " " . $cond["operation"] . " " . $dbr->addQuotes( $cond["value"] ); |
643 | 695 | } |
644 | | - |
| 696 | + |
645 | 697 | return array( $sql ); |
646 | 698 | } |
647 | | - |
| 699 | + |
648 | 700 | public static function validate_oper( $operation ) { |
649 | 701 | $o_trim = trim( $operation ); |
650 | 702 | switch( $o_trim ) { // valid operations |
— | — | @@ -657,6 +709,4 @@ |
658 | 710 | return "="; |
659 | 711 | } |
660 | 712 | } |
661 | | - |
662 | | - |
663 | 713 | } |
\ No newline at end of file |
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ClickTracking.hooks.php |
— | — | @@ -1,7 +1,6 @@ |
2 | 2 | <?php |
3 | | - |
4 | 3 | /** |
5 | | - * Hooks for Usability Initiative ClickTracking extension |
| 4 | + * Hooks for ClickTracking extension |
6 | 5 | * |
7 | 6 | * @file |
8 | 7 | * @ingroup Extensions |
— | — | @@ -9,41 +8,31 @@ |
10 | 9 | |
11 | 10 | class ClickTrackingHooks { |
12 | 11 | |
13 | | - /* Static Functions */ |
| 12 | + /* Static Methods */ |
14 | 13 | |
15 | | - /* initializations */ |
16 | | - |
17 | | - /* 3 tables for click tracking */ |
18 | | - public static function schema() { |
| 14 | + /* |
| 15 | + * LoadExtensionSchemaUpdates hook |
| 16 | + */ |
| 17 | + public static function loadExtensionSchemaUpdates() { |
19 | 18 | global $wgExtNewTables, $wgExtNewIndexes, $wgExtNewFields; |
20 | 19 | |
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( |
22 | 23 | 'click_tracking', |
23 | | - dirname( __FILE__ ) . '/ClickTracking.sql' |
| 24 | + 'click_tracking_action_time', |
| 25 | + dirname( __FILE__ ) . '/patch-action_time.sql', |
24 | 26 | ); |
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 | | - |
36 | 27 | $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', |
39 | 31 | ); |
40 | | - |
41 | 32 | return true; |
42 | 33 | } |
43 | 34 | |
44 | 35 | /** |
45 | | - * Make sure the table exists for parser tests |
46 | | - * @param $tables |
47 | | - * @return unknown_type |
| 36 | + * ParserTestTables hook |
48 | 37 | */ |
49 | 38 | public static function parserTestTables( &$tables ) { |
50 | 39 | $tables[] = 'click_tracking'; |
— | — | @@ -52,127 +41,115 @@ |
53 | 42 | } |
54 | 43 | |
55 | 44 | /** |
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 |
57 | 51 | */ |
58 | | - public static function isUserThrottled() { |
| 52 | + public static function beforePageDisplay( $out, $skin ) { |
59 | 53 | 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; |
61 | 59 | } |
62 | 60 | |
63 | 61 | /** |
64 | | - * Adds JavaScript |
| 62 | + * MakeGlobalVariablesScript hook |
65 | 63 | */ |
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() ) ); |
79 | 67 | return true; |
80 | 68 | } |
81 | 69 | |
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 |
85 | 74 | */ |
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 | + ) ); |
89 | 90 | } |
90 | 91 | |
91 | 92 | /** |
92 | 93 | * Get the number of revisions a user has made since a given time |
| 94 | + * |
93 | 95 | * @param $ts beginning timestamp |
94 | 96 | * @return number of revsions this user has made |
95 | 97 | */ |
96 | 98 | public static function getEditCountSince( $ts ) { |
97 | 99 | global $wgUser; |
98 | 100 | |
99 | | - // convert to just the day |
| 101 | + // Convert to just the day |
100 | 102 | $time = gmdate( 'Y-m-d', wfTimestamp( TS_UNIX, $ts ) ); |
101 | | - |
102 | 103 | $dbr = wfGetDB( DB_SLAVE ); |
103 | | - |
104 | 104 | $edits = $dbr->selectField( |
105 | 105 | 'user_daily_contribs', |
106 | 106 | '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 ) ), |
111 | 108 | __METHOD__ |
112 | 109 | ); |
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; |
120 | 112 | } |
121 | 113 | |
122 | 114 | /** |
123 | 115 | * Get event ID from name |
| 116 | + * |
124 | 117 | * @param $event_name String: name of the event to get |
125 | 118 | * @return integer |
126 | 119 | */ |
127 | 120 | 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 |
142 | 126 | // FIXME: Use replace() instead of this selectField --> insert or update logic |
143 | 127 | 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__ ); |
149 | 129 | $id_num = $dbw->insertId(); |
150 | 130 | } |
151 | | - |
152 | | - if ( $id_num === false ) { |
153 | | - return 0; |
154 | | - } |
155 | | - |
156 | | - return $id_num; |
| 131 | + return $id_num === false ? 0 : $id_num; |
157 | 132 | } |
158 | 133 | |
159 | 134 | /** |
160 | 135 | * Track particular event |
| 136 | + * |
161 | 137 | * @param $session_id String: unique session id for this editing sesion |
162 | 138 | * @param $is_logged_in Boolean: whether or not the user is logged in |
163 | 139 | * @param $namespace Integer: namespace the user is editing |
164 | 140 | * @param $event_id Integer: event type |
165 | 141 | * @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) |
169 | 148 | * @return true if the event was stored in the DB |
170 | 149 | */ |
171 | 150 | 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 ) { |
173 | 152 | $dbw = wfGetDB( DB_MASTER ); |
174 | | - |
175 | 153 | $dbw->begin(); |
176 | | - |
177 | 154 | // Builds insert information |
178 | 155 | $data = array( |
179 | 156 | 'action_time' => $dbw->timestamp(), |
— | — | @@ -184,9 +161,8 @@ |
185 | 162 | 'user_contribs_span3' => ( $is_logged_in ? (int) $contribs_in_timespan3 : null ), |
186 | 163 | 'namespace' => (int) $namespace, |
187 | 164 | 'event_id' => (int) $event_id, |
188 | | - 'additional_info' => ( isset($additional) ? (string) $additional : null ) |
| 165 | + 'additional_info' => ( isset($additional) ? (string) $additional : null ) |
189 | 166 | ); |
190 | | - |
191 | 167 | $db_status = $dbw->insert( 'click_tracking', $data, __METHOD__ ); |
192 | 168 | $dbw->commit(); |
193 | 169 | return $db_status; |
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ApiClickTracking.php |
— | — | @@ -1,6 +1,6 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * Extend the API for click tracking |
| 4 | + * Click tracking API extension |
5 | 5 | * |
6 | 6 | * @file |
7 | 7 | * @ingroup API |
— | — | @@ -9,25 +9,29 @@ |
10 | 10 | class ApiClickTracking extends ApiBase { |
11 | 11 | |
12 | 12 | /** |
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 | + * |
14 | 19 | * @see includes/api/ApiBase#execute() |
15 | 20 | */ |
16 | 21 | public function execute() { |
17 | | - global $wgUser, $wgTitle, $wgClickTrackContribGranularity1, $wgClickTrackContribGranularity2, $wgClickTrackContribGranularity3; |
| 22 | + global $wgUser, $wgTitle, $wgClickTrackContribGranularity1, $wgClickTrackContribGranularity2, |
| 23 | + $wgClickTrackContribGranularity3; |
18 | 24 | |
19 | 25 | $params = $this->extractRequestParams(); |
20 | 26 | $this->validateParams( $params ); |
21 | 27 | $eventid_to_lookup = $params['eventid']; |
22 | 28 | $session_id = $params['token']; |
23 | | - |
| 29 | + |
24 | 30 | $additional = null; |
25 | | - |
26 | | - if( isset($params['additional']) && strlen($params['additional']) > 0){ |
| 31 | + |
| 32 | + if ( isset( $params['additional'] ) && strlen( $params['additional'] ) > 0 ){ |
27 | 33 | $additional = $params['additional']; |
28 | | - |
29 | 34 | } |
30 | | - |
31 | | - |
| 35 | + |
32 | 36 | // Event ID lookup table |
33 | 37 | // FIXME: API should already have urldecode()d |
34 | 38 | $event_id = ClickTrackingHooks::getEventIDFromName( urldecode( $eventid_to_lookup ) ); |
— | — | @@ -54,7 +58,7 @@ |
55 | 59 | $granularity3, // contributions made in granularity 3 time frame |
56 | 60 | $additional |
57 | 61 | ); |
58 | | - |
| 62 | + |
59 | 63 | // For links that go off the page, redirect the user |
60 | 64 | // FIXME: The API should have a proper infrastructure for this |
61 | 65 | if ( !is_null( $params['redirectto'] ) ) { |
— | — | @@ -66,7 +70,7 @@ |
67 | 71 | global $wgOut; |
68 | 72 | $wgOut->redirect( $params['redirectto'] ); |
69 | 73 | $wgOut->output(); |
70 | | - |
| 74 | + |
71 | 75 | // Prevent any further output |
72 | 76 | $wgOut->disable(); |
73 | 77 | $this->getMain()->getPrinter()->disable(); |
— | — | @@ -80,7 +84,7 @@ |
81 | 85 | * Required parameter check |
82 | 86 | * @param $params params extracted from the POST |
83 | 87 | */ |
84 | | - protected function validateParams( $params ) { |
| 88 | + protected function validateParams( $params ) { |
85 | 89 | $required = array( 'eventid', 'token' ); |
86 | 90 | foreach ( $required as $arg ) { |
87 | 91 | if ( !isset( $params[$arg] ) ) { |
— | — | @@ -95,19 +99,19 @@ |
96 | 100 | 'token' => 'unique edit ID for this edit session', |
97 | 101 | 'redirectto' => 'URL to redirect to (only used for links that go off the page)', |
98 | 102 | 'additional' => 'additional info for the event, like state information' |
99 | | - ); |
| 103 | + ); |
100 | 104 | } |
101 | 105 | |
102 | 106 | public function getDescription() { |
103 | 107 | return array( |
104 | 108 | 'Track user clicks on JavaScript items.' |
105 | | - ); |
| 109 | + ); |
106 | 110 | } |
107 | | - |
| 111 | + |
108 | 112 | public function getPossibleErrors() { |
109 | 113 | return array_merge( parent::getPossibleErrors(), array( |
110 | | - array( 'missingparam', 'eventid' ), |
111 | | - array( 'missingparam', 'token' ), |
| 114 | + array( 'missingparam', 'eventid' ), |
| 115 | + array( 'missingparam', 'token' ), |
112 | 116 | ) ); |
113 | 117 | } |
114 | 118 | |
— | — | @@ -123,5 +127,4 @@ |
124 | 128 | public function getVersion() { |
125 | 129 | return __CLASS__ . ': $Id$'; |
126 | 130 | } |
127 | | - |
128 | 131 | } |
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 |
1 | 54 | + 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 |
1 | 532 | + 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 |
1 | 22 | + 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 |
1 | 55 | + native |
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ApiSpecialClickTracking.php |
— | — | @@ -1,6 +1,6 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * Extend the API for click tracking visualization in the special:clicktracking page |
| 4 | + * Click Tracking special page API extension |
5 | 5 | * |
6 | 6 | * @file |
7 | 7 | * @ingroup API |
— | — | @@ -9,8 +9,14 @@ |
10 | 10 | class ApiSpecialClickTracking extends ApiBase { |
11 | 11 | |
12 | 12 | /** |
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 | + * |
15 | 21 | * @see includes/api/ApiBase#execute() |
16 | 22 | */ |
17 | 23 | public function execute() { |
— | — | @@ -22,26 +28,30 @@ |
23 | 29 | $increment = $params['increment']; |
24 | 30 | $userDefString = $params['userdefs']; |
25 | 31 | |
26 | | - // this is if it's asking for tableData |
| 32 | + // This is if it's asking for tableData |
27 | 33 | if ( isset( $params['tabledata'] ) ) { |
28 | 34 | $tableData = SpecialClickTracking::buildRowArray( $startdate, $enddate, $userDefString ); |
29 | 35 | $this->getResult()->addValue( array( 'tablevals' ), 'vals', $tableData ); |
30 | | - } else { // chart data |
| 36 | + } else { |
| 37 | + // Chart data |
31 | 38 | $click_data = array(); |
32 | 39 | try { |
33 | 40 | $click_data = SpecialClickTracking::getChartData( $event_id, $startdate, $enddate, $increment, $userDefString ); |
34 | 41 | $this->getResult()->addValue( array( 'datapoints' ), 'expert', $click_data['expert'] ); |
35 | 42 | $this->getResult()->addValue( array( 'datapoints' ), 'basic', $click_data['basic'] ); |
36 | 43 | $this->getResult()->addValue( array( 'datapoints' ), 'intermediate', $click_data['intermediate'] ); |
37 | | - } catch ( Exception $e ) { /* no result */ } |
| 44 | + } catch ( Exception $e ) { |
| 45 | + /* No result */ |
| 46 | + } |
38 | 47 | } |
39 | 48 | } |
40 | 49 | |
41 | 50 | /** |
42 | 51 | * Required parameter check |
| 52 | + * |
43 | 53 | * @param $params params extracted from the POST |
44 | 54 | */ |
45 | | - protected function validateParams( $params ) { |
| 55 | + protected function validateParams( $params ) { |
46 | 56 | $required = array( 'eventid', 'startdate', 'enddate', 'increment', 'userdefs' ); |
47 | 57 | foreach ( $required as $arg ) { |
48 | 58 | if ( !isset( $params[$arg] ) ) { |
— | — | @@ -49,21 +59,21 @@ |
50 | 60 | } |
51 | 61 | } |
52 | 62 | |
53 | | - // check if event id parses to an int greater than zero |
| 63 | + // Check if event id parses to an int greater than zero |
54 | 64 | if ( (int) $params['eventid'] < 0 ) { |
55 | 65 | $this->dieUsage( 'Invalid event ID', 'badeventid' ); |
56 | 66 | } |
57 | 67 | |
58 | | - // check start and end date are of proper format |
| 68 | + // Check start and end date are of proper format |
59 | 69 | if ( $params['startdate'] != 0 && strptime( SpecialClickTracking::space_out_date( $params['startdate'] ), "%Y %m %d" ) === false ) { |
60 | 70 | $this->dieUsage( "startdate not in YYYYMMDD format: <<{$params['startdate']}>>", 'badstartdate' ); |
61 | 71 | } |
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 ) { |
63 | 73 | $this->dieUsage( "enddate not in YYYYMMDD format: <<{$params['enddate']}>>", 'badenddate' ); |
64 | 74 | } |
65 | 75 | |
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 ) { |
68 | 78 | $this->dieUsage( 'Invalid increment', 'badincrement' ); |
69 | 79 | } |
70 | 80 | |
— | — | @@ -80,58 +90,57 @@ |
81 | 91 | 'increment' => 'increment interval (in days) for data points', |
82 | 92 | 'userdefs' => 'JSON object to encode user definitions', |
83 | 93 | 'tabledata' => 'set to 1 for table data instead of chart data' |
84 | | - ); |
| 94 | + ); |
85 | 95 | } |
86 | 96 | |
87 | 97 | public function getDescription() { |
88 | 98 | return array( |
89 | 99 | 'Returns data to the Special:ClickTracking visualization page' |
90 | | - ); |
| 100 | + ); |
91 | 101 | } |
92 | | - |
| 102 | + |
93 | 103 | 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\'>>' ), |
105 | 115 | ) ); |
106 | 116 | } |
107 | 117 | |
108 | 118 | public function getAllowedParams() { |
109 | 119 | return array( |
110 | 120 | 'eventid' => array( |
111 | | - ApiBase::PARAM_TYPE => 'integer', |
112 | | - ApiBase::PARAM_MIN => 1 |
113 | | - ), |
| 121 | + ApiBase::PARAM_TYPE => 'integer', |
| 122 | + ApiBase::PARAM_MIN => 1 |
| 123 | + ), |
114 | 124 | 'startdate' => array( |
115 | | - ApiBase::PARAM_TYPE => 'integer' |
116 | | - ), |
| 125 | + ApiBase::PARAM_TYPE => 'integer' |
| 126 | + ), |
117 | 127 | 'enddate' => array( |
118 | | - ApiBase::PARAM_TYPE => 'integer' |
119 | | - ), |
| 128 | + ApiBase::PARAM_TYPE => 'integer' |
| 129 | + ), |
120 | 130 | '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 | + ), |
125 | 135 | 'userdefs' => array( |
126 | | - ApiBase::PARAM_TYPE => 'string' |
127 | | - ), |
| 136 | + ApiBase::PARAM_TYPE => 'string' |
| 137 | + ), |
128 | 138 | 'tabledata' => array( |
129 | | - ApiBase::PARAM_TYPE => 'integer' |
130 | | - ), |
| 139 | + ApiBase::PARAM_TYPE => 'integer' |
| 140 | + ), |
131 | 141 | ); |
132 | 142 | } |
133 | 143 | |
134 | 144 | public function getVersion() { |
135 | 145 | return __CLASS__ . ': $Id$'; |
136 | 146 | } |
137 | | - |
138 | 147 | } |
\ No newline at end of file |
Index: trunk/extensions/UsabilityInitiative/ClickTracking/ClickTracking.php |
— | — | @@ -1,37 +1,29 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | 4 | * Usability Initiative Click Tracking extension |
5 | | - * |
| 5 | + * |
6 | 6 | * @file |
7 | 7 | * @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 | + * |
15 | 9 | * @author Nimish Gautam <ngautam@wikimedia.org> |
| 10 | + * @author Trevor Parscal <tparscal@wikimedia.org> |
16 | 11 | * @license GPL v2 or later |
17 | 12 | * @version 0.1.1 |
18 | 13 | */ |
19 | 14 | |
20 | 15 | /* Configuration */ |
21 | 16 | |
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 |
28 | 19 | $wgClickTrackThrottle = -1; |
29 | 20 | |
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 |
34 | 25 | |
35 | | -// Credits |
| 26 | +/* Setup */ |
| 27 | + |
36 | 28 | $wgExtensionCredits['other'][] = array( |
37 | 29 | 'path' => __FILE__, |
38 | 30 | 'name' => 'Click Tracking', |
— | — | @@ -40,30 +32,16 @@ |
41 | 33 | 'descriptionmsg' => 'clicktracking-desc', |
42 | 34 | 'url' => 'http://www.mediawiki.org/wiki/Extension:UsabilityInitiative' |
43 | 35 | ); |
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'; |
58 | 42 | $wgHooks['ParserTestTables'][] = 'ClickTrackingHooks::parserTestTables'; |
59 | | - |
60 | | -// Set up the new API module |
61 | 43 | $wgAPIModules['clicktracking'] = 'ApiClickTracking'; |
62 | 44 | $wgAPIModules['specialclicktracking'] = 'ApiSpecialClickTracking'; |
63 | | - |
64 | | -//Special page setup |
65 | 45 | $wgSpecialPages['ClickTracking'] = 'SpecialClickTracking'; |
66 | 46 | $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'; |