reports: Don't validate report names in javascript
[ninja.git] / application / views / reports / js / common.js
blob5516653e400527601c97585cc20993c0b8fdfa8f
1 var current_filename;
2 var sla_month_error_color = 'red';
3 var sla_month_disabled_color = '#cdcdcd';
4 var sla_month_enabled_color = '#fafafa';
5 var nr_of_scheduled_instances = 0;
6 var current_obj_type = false; // keep track of what we are viewing
7 $(document).ready(function() {
8 // handle the move-between-lists-button (> + <) and double click events
9 function move_right() {
10 var selects = $(this).parent().parent().find('select');
11 moveAndSort(selects.filter(':first'), selects.filter(':last'));
13 function move_left() {
14 var selects = $(this).parent().parent().find('select');
15 moveAndSort(selects.filter(':last'), selects.filter(':first'));
17 $('.arrow-right').click(move_right);
18 $('.arrow-left').click(move_left);
19 $('#hostgroup_tmp, #servicegroup_tmp, #host_tmp, #service_tmp, #objects_tmp').dblclick(move_right);
20 $('#hostgroup, #servicegroup, #host_name, #service_description, #objects').dblclick(move_left);
22 $("#hide_response").click(function() {
23 $('#response').hide('slow');
24 });
26 $(".fancybox").fancybox({
27 'overlayOpacity' : 0.7,
28 'overlayColor' : '#ffffff',
29 'hideOnContentClick' : false,
30 'autoScale':true,
31 'autoDimensions': true,
32 });
34 init_regexpfilter();
35 $('#filter_field').keyup(function() {
36 if ($(this).attr('value') == '') {
37 MyRegexp.resetFilter($("select[id$=_tmp]").filter(":visible").attr('id'));
38 return;
40 MyRegexp.selectFilter($("select[id$=_tmp]").filter(":visible").attr('id'), this.value);
41 });
43 $('#clear_filter').click(function() {
44 $('#filter_field').attr('value', '');
45 MyRegexp.resetFilter($("select[id$=_tmp]").filter(":visible").attr('id'));
46 $('#filter_field').focus();
47 });
49 var direct_link_visible = false;
50 $('#current_report_params').click(function() {
51 // make sure we always empty the field
52 $('#link_container').html('');
53 // .html('<form><input type="text" size="200" value="' + $('#current_report_params').attr('href') + '"></form>')
54 if (!direct_link_visible) {
55 $('#link_container')
56 .html('<form>'+_label_direct_link+' <input class="wide" type="text" value="'
57 + document.location.protocol + '//'
58 + document.location.host
59 + $('#current_report_params').attr('href')
60 + '"></form>')
61 .css('position', 'absolute')
62 .css('top', this.offsetHeight + this.offsetTop + 5)
63 .css('right', '0')
64 .show();
65 direct_link_visible = true;
66 } else {
67 $('#link_container').hide();
68 direct_link_visible = false;
70 return false;
71 });
73 $('#save_report').click(function() {
74 if (!direct_link_visible) {
75 $('#save_report_form')
76 .css('position', 'absolute')
77 .css('top', this.offsetHeight + this.offsetTop + 5)
78 .css('right', '0')
79 .show()
80 .find('input[name=report_name]')
81 .map(function() {
82 var input = this;
83 if(input.value == "") {
84 input.focus();
86 });
87 direct_link_visible = true;
88 } else {
89 $('#save_report_form').hide();
90 direct_link_visible = false;
92 return false;
93 });
95 $("#report_id").bind('change', function() {
96 $("#saved_report_form").trigger('submit');
97 });
99 $('.save_report_btn').parents('form').submit(function(ev) {
100 ev.preventDefault();
101 loopElements();
102 var form = $(this);
103 if (!(check_form_values(this[0]))) {
104 return;
106 var btn = form.find('.save_report_btn');
107 btn.after(loadimg);
108 $.ajax({
109 url: form[0].action,
110 type: form[0].method,
111 data: form.serialize(),
112 complete: function() {
113 btn.parent().find('img:last').remove();
115 success: function(data, status_msg, xhr) {
116 if (data == null) {
117 $.notify(_reports_error + ": " + xhr.responseText, {'sticky': true});
118 return;
120 jgrowl_message(data.status_msg, _reports_success);
121 if (!btn[0].form.report_id)
122 $('form').append('<input type="hidden" name="report_id" value="'+data.report_id+'"/>');
123 else
124 $('#save_report_form').hide();
126 error: function(data) {
127 $.notify(_reports_error + ": " + data.responseText, {'sticky': true});
128 btn.parent().find('img:last').remove();
130 dataType: 'json'
134 $('select[name=report_type]').on('change', function() {
135 var value = this.value;
136 set_selection(value);
137 get_members(value, function(all_names) {
138 populate_options($('#objects_tmp'), $('#objects'), all_names);
140 }).each(function() {
141 var value = this.value;
142 set_selection(value);
143 get_members(value, function(all_names) {
144 populate_options($('#objects_tmp'), $(), all_names);
145 var tmp = $('#objects_tmp');
146 var mo = new missing_objects();
147 var elems = $('#objects').children();
148 for (var i = 0; i < elems.length; i++) {
149 var prop = elems[i];
150 if (tmp.containsOption(prop.value))
151 tmp.removeOption(prop.value);
152 else
153 mo.add(prop.value);
155 mo.display_if_any();
158 $('#sel_report_type').on('click', function() {
159 var value = this.form.report_type.value;
160 set_selection(value);
161 get_members(value, function(all_names) {
162 populate_options($('#objects_tmp'), $('#objects'), all_names);
166 $('#start_year, #end_year').on('change', function () {
167 var start = 0;
168 var end = 11;
169 // check_custom_months is supposedly initialized by the onload
170 // handler in application/views/reports/js/reports.js or equivalent.
171 if (check_custom_months.start_date == undefined || check_custom_months.end_date == undefined) {
172 return;
174 if (this.value == check_custom_months.start_date.getFullYear()) {
175 start = check_custom_months.start_date.getMonth();
177 if (this.value == check_custom_months.end_date.getFullYear()) {
178 end = check_custom_months.end_date.getMonth();
180 var html = '<option></option>';
181 for (i = start; i <= end; i++) {
182 html += '<option value="' + (i+1) + '">' + Date.monthNames[i] + '</option>';
184 if (this.id == 'start_year')
185 $('#start_month').html(html);
186 else
187 $('#end_month').html(html);
190 $('#start_year, #end_year, #start_month, #end_month').on('change', check_custom_months);
191 $("#delete_report").click(confirm_delete_report);
193 $(".report_form").on('submit', function() {
194 loopElements();
195 return check_form_values();
199 var loadimg = new Image(16,16);
200 loadimg.src = _site_domain + 'application/media/images/loading_small.gif';
202 function init_datepicker()
204 // datePicker Jquery plugin
205 var datepicker_enddate = (new Date()).asString();
206 $('.date-pick').datePicker({clickInput:true, startDate:_start_date, endDate:datepicker_enddate});
207 $('#cal_start').on(
208 'dpClosed',
209 function(e, selectedDates)
211 var d = selectedDates[0];
212 if (d) {
213 d = new Date(d);
214 $('#cal_end').dpSetStartDate(d.asString());
218 $('#cal_end').on(
219 'dpClosed',
220 function(e, selectedDates)
222 var d = selectedDates[0];
223 if (d) {
224 d = new Date(d);
225 $('#cal_start').dpSetEndDate(d.asString());
231 function show_calendar(val, update) {
232 if (val=='custom') {
233 $("#custom_time").show();
235 init_datepicker();
236 init_timepicker();
238 if (update == '') {
239 $('input[name=start_time]').attr('value', '');
240 $('input[name=end_time]').attr('value', '');
242 } else {
243 $("#custom_time").hide();
245 disable_sla_fields(val);
248 function set_selection(val) {
249 if ($.inArray(val, ['servicegroups', 'hostgroups', 'services', 'hosts']) === -1)
250 val = 'hostgroups'; // Why? Because I found it like this
251 $('.object-list-type').text(val);
252 $('*[data-show-for]').hide()
253 $('*[data-show-for~='+val+']').show()
256 function get_members(type, cb) {
257 if (!type)
258 return;
259 var field_name = false;
260 var empty_field = false;
262 show_progress('progress', _wait_str);
263 $.ajax({
264 url: _site_domain + _index_page + '/ajax/group_member',
265 data: {type: type},
266 error: function(data) {
267 $.notify("Unable to fetch objects: " + data.responseText, {'sticky': true});
269 success: function(all_names) {
270 if(typeof cb == 'function')
271 cb(all_names);
272 $('#progress').css('display', 'none');
274 dataType: 'json'
279 * Populate HTML select list with supplied JSON data
281 function populate_options(tmp_field, field, json_data)
283 tmp_field.empty();
284 field.empty();
285 show_progress('progress', _wait_str);
286 var available = document.createDocumentFragment();
287 var selected = document.createDocumentFragment();
288 for (i = 0; i < (json_data ? json_data.length : 0); i++) {
289 var option = document.createElement("option");
290 option.appendChild(document.createTextNode(json_data[i]));
291 available.appendChild(option);
293 tmp_field.append(available);
294 field.append(selected);
298 * Loop through all elements of a form
299 * Verify that all multiselect fields (right hand side)
300 * are set to selected
302 function loopElements(f) {
303 // select all elements that doesn't contain the nosave_suffix
304 $('.multiple:not([id$=_tmp])').each(function() {
305 if ($(this).is(':visible')) {
306 $(this).children('option').attr('selected', 'selected');
307 } else {
308 $(this).children('option').attr('selected', false);
313 function check_form_values(form)
315 if (!form)
316 form = document.documentElement;
317 var errors = 0;
318 var err_str = '';
319 var cur_start = '';
320 var cur_end = '';
322 var rpt_type = $("select[name=report_type]", form).val();
323 if ($("#report_period", form).val() == 'custom') {
324 if ($('input[name=type]', form).val() != 'sla') {
325 // date validation
326 cur_start = Date.fromString($("input[name=cal_start]", form).val());
327 var time = $(".time_start", form).val().split(':');
328 cur_start.addHours(time[0]);
329 cur_start.addMinutes(time[1]);
330 cur_end = Date.fromString($("input[name=cal_end]", form).val());
331 time = $(".time_end", form).val().split(':');
332 cur_end.addHours(time[0]);
333 cur_end.addMinutes(time[1]);
334 var now = new Date();
335 if (!cur_start || !cur_end) {
336 if (!cur_start) {
337 errors++;
338 err_str += "<li>" + _reports_invalid_startdate + ".</li>";
340 if (!cur_end) {
341 errors++;
342 err_str += "<li>" + _reports_invalid_enddate + ".</li>";
344 } else {
345 if (cur_end > now) {
346 if (!confirm(_reports_enddate_infuture)) {
347 return false;
348 } else {
349 cur_end = now;
354 if (cur_end < cur_start) {
355 errors++;
356 err_str += "<li>" + _reports_enddate_lessthan_startdate + ".</li>";
357 $(".datepick-start", form).addClass("time_error");
358 $(".datepick-end", form).addClass("time_error");
359 } else {
360 $(".datepick-start", form).removeClass("time_error");
361 $(".datepick-end", form).removeClass("time_error");
363 } else {
364 // verify that we have years and month fields
365 if ($('#start_year', form).val() == '' || $('#start_month', form).val() == ''
366 || $('#end_year', form).val() == '' || $('#end_month', form).val() == '') {
367 errors++;
368 //@@@Fixme: Add translated string
369 err_str += "<li>Please select year and month for both start and end. ";
370 err_str += "<br />Please note that SLA reports can only be generated for previous months</li>";
372 else {
373 // remember: our months are 1-indexed
374 cur_start = new Date(0);
375 cur_start.setYear($("select[name=start_year]", form).val());
376 cur_start.addMonths(Number($("select[name=start_month]", form).val()) - 1);
378 cur_end = new Date(0);
379 cur_end.setYear($("select[name=end_year]", form).val());
380 cur_end.addMonths(Number($("select[name=end_month]", form).val()));
383 if (cur_end < cur_start) {
384 errors++;
385 err_str += "<li>" + _reports_enddate_lessthan_startdate + ".</li>";
386 $(".datepick-start", form).addClass("time_error");
387 $(".datepick-end", form).addClass("time_error");
388 } else {
389 $(".datepick-start", form).removeClass("time_error");
390 $(".datepick-end", form).removeClass("time_error");
395 if ($('input[name=report_mode]:checked', form).val() != 'standard' && !$('#show_all', form).is(':checked') && $("#objects", form).is('select') && $('#objects option', form).length == 0) {
396 errors++;
397 err_str += "<li>" + _reports_err_str_noobjects + ".</li>";
400 if ($("#enter_sla", form).is(":visible")) {
401 // check for sane SLA values
402 var red_error = false;
403 var max_val = 100;
404 var nr_of_slas = 0;
406 for (i=1;i<=12;i++) {
407 var field_name = 'month_' + i;
408 var input = $('input[id="' + field_name + '"]', form);
409 var value = input.attr('value');
410 value = value.replace(',', '.');
411 if (value > max_val || isNaN(value)) {
412 input.css('background', sla_month_error_color);
413 errors++;
414 red_error = true;
415 } else {
416 if (value != '') {
417 nr_of_slas++;
419 if (input.attr('disabled'))
420 input.css('background', sla_month_disabled_color);
421 else
422 input.css('background', sla_month_enabled_color);
425 if (red_error) {
426 err_str += '<li>' + _reports_sla_err_str + '</li>';
429 if (nr_of_slas == 0 && !red_error) {
430 errors++;
431 err_str += "<li>" + _reports_no_sla_str + "</li>";
435 // create array prototype to sole the lack of in_array() in javascript
436 Array.prototype.has = function(value) {
437 var i;
438 for (var i = 0, loopCnt = this.length; i < loopCnt; i++) {
439 if (this[i] === value) {
440 return true;
443 return false;
446 var report_name = $("input[name=report_name]", form).attr('value');
447 report_name = $.trim(report_name);
448 var saved_report_id = $("input[name=saved_report_id]", form).attr('value');
449 var do_save_report = $('input[name=save_report_settings]', form).is(':checked') ? 1 : 0;
452 * Only perform checks if:
453 * - Saved report exists
454 * - User checked the 'Save Report' checkbox
455 * - We are currently editing a report (i.e. have saved_report_id)
457 if ($('#report_id', form) && do_save_report && saved_report_id) {
458 // Saved reports exists
459 $('#report_id option', form).each(function(i) {
460 if ($(this).val()) {// first item is empty
461 if (saved_report_id != $(this).val()) {
462 // check all the other saved reports
463 // make sure we don't miss the scheduled reports
464 var chk_text = $(this).text();
465 chk_text = chk_text.replace(" ( *" + _scheduled_label + "* )", '');
466 if (report_name == chk_text) {
467 // trying to save an item with an existing name
468 errors++;
469 err_str += "<li>" + _reports_error_name_exists + ".</li>";
470 return false;
475 } else if (do_save_report && report_name == '') {
476 // trying to save a report without a name
477 errors++;
478 err_str += "<li>" + _reports_name_empty + "</li>";
481 // display err_str if any
482 if (!errors) {
483 $('#response', form).html('');
485 $('#response', form).hide();
486 return true;
489 // clear all style info from progress
490 var resp = $('#response', form);
491 if (!resp.length)
492 resp = $('#response');
493 resp.attr("style", "");
494 resp.html("<ul class='alert error'>" + err_str + "</ul>");
495 window.scrollTo(0,0); // make sure user sees the error message
496 return false;
499 function moveAndSort(from, to)
501 from.find('option:selected').remove().appendTo(to);
502 to.sortOptions();
505 // init timepicker once it it is shown
506 function init_timepicker()
508 $("#time_start, #time_end").timePicker();
511 function disable_sla_fields(report_period)
513 if (!$('#month_1').length)
514 return;
515 var now = new Date();
516 var this_month = now.getMonth()+1;
517 switch (report_period) {
518 case 'thisyear':
519 // weird as it seems, the following call actually ENABLES
520 // all months. If not, we could end up with all months being
521 // disabled for 'thisyear'
522 disable_months(0, 12);
523 for (i=this_month + 1;i<=12;i++)
525 $('.report_form #month_' + i).val('').attr('disabled', true).css('background-color', sla_month_disabled_color);
527 break;
528 case 'custom':
529 check_custom_months();
530 break;
531 case 'lastmonth':
532 enable_last_months(1);
533 break;
534 case 'last3months':
535 enable_last_months(3);
536 break;
537 case 'last6months':
538 enable_last_months(6);
539 break;
540 case 'lastyear':
541 case 'last12months':
542 disable_months(0, 12);
543 break;
544 case 'lastquarter':
545 if(this_month <= 3){
546 from = 10;
547 to = 12;
548 } else if (this_month <= 6) {
549 from = 1;
550 to = 3;
551 } else if (this_month <= 9){
552 from = 4;
553 to = 6;
554 } else {
555 from = 7;
556 to = 9;
558 disable_months(from, to);
559 break;
560 default:
561 for (i=1;i<=12;i++)
563 $('#month_' + i).attr('disabled', false).css('bgcolor', sla_month_enabled_color);
569 function disable_months(start, end)
571 var disabled_state = false;
572 var not_disabled_state = false;
573 var col = false;
574 start = Number(start);
575 end = Number(end);
576 for (i=1;i<=12;i++) {
577 var cell = $('.report_form #month_' + i);
578 if (start>end) {
579 if ( i >= start || i <= end) {
580 cell.attr('disabled', false).css('background-color', sla_month_enabled_color);
581 } else {
582 cell.val('').attr('disabled', true).css('background-color', sla_month_disabled_color);
584 } else {
585 if ( i>= start && i <= end) {
586 cell.attr('disabled', false).css('background-color', sla_month_enabled_color);
587 } else {
588 cell.val('').attr('disabled', true).css('background-color', sla_month_disabled_color);
595 function check_custom_months()
597 var f = $('.report_form').get(0);
598 // not SLA?
599 if (!f['start_month'])
600 return;
602 if (check_custom_months.start_date == undefined) {
603 check_custom_months.start_date = new Date(0);
604 check_custom_months.end_date = new Date();
605 $.ajax({
606 url: _site_domain + _index_page + '/sla/custom_start/',
607 type: 'GET',
608 dataType: 'json',
609 success: function(data) {
610 if (!data.timestamp) {
611 $.notify("Unable to fetch oldest report timestamp: " + data.responseText, {'sticky': true});
613 check_custom_months.start_date.setTime(data.timestamp * 1000);
614 var html = '<option></option>';
615 for (i = check_custom_months.start_date.getFullYear(); i <= check_custom_months.end_date.getFullYear(); i++) {
616 html += '<option>' + i + '</option>';
618 $('#start_year').html(html);
619 $('#end_year').html(html);
624 var start_year = f.start_year.value;
625 var start_month = f.start_month.value;
626 var end_year = f.end_year.value;
627 var end_month = f.end_month.value;
628 if (start_year!='' && end_year!='' && start_month!='' && end_month!='') {
629 disable_months(0, 0);
630 } else if (start_year == end_year - 1 || start_year == end_year) {
631 disable_months(start_month, end_month);
632 } else {
633 disable_months(0, 0);
635 $('#progress').hide();
639 * Generic function to enable month_ fields
640 * depending on if selection is last 1, 3 or 6 months.
642 function enable_last_months(mnr)
644 var now = new Date();
645 var this_month = now.getMonth()+1;
646 var from = this_month - mnr;
647 var to = this_month - 1;
648 if (from <= 0)
649 from += 12;
650 if (to <= 0)
651 to += 12;
652 disable_months(from, to);
655 function missing_objects()
657 this.objs = [];
660 missing_objects.prototype.add = function(name)
662 if (name != '*')
663 this.objs.push(name);
666 missing_objects.prototype.display_if_any = function()
668 if (!this.objs.length)
669 return;
671 var info_str = _reports_missing_objects + ": ";
672 info_str += "<ul><li><img src=\"" + _site_domain + "application/views/icons/arrow-right.gif" + "\" /> " + this.objs.join('</li><li><img src="' + _site_domain + 'application/views/icons/arrow-right.gif' + '" /> ') + '</li></ul>';
673 info_str += _reports_missing_objects_pleaseremove;
674 info_str += '<a href="#" id="hide_response" style="position:absolute;top:8px;left:700px;">Close <img src="' + _site_domain + '' + 'application/views/icons/12x12/cross.gif" /></a>';
675 $('#response')
676 .css('background','#f4f4ed url(' + _site_domain + 'application/views/icons/32x32/shield-info.png) 7px 7px no-repeat')
677 .css("position", "relative")
678 .css('top', '0px')
679 .css('width','748px')
680 .css('left', '0px')
681 .css('padding','15px 2px 5px 50px')
682 .css('margin-left','5px')
683 .html(info_str);
686 function confirm_delete_report()
688 var btn = $(this);
689 var id = $("#report_id").attr('value')
691 var is_scheduled = $('#is_scheduled').text()!='' ? true : false;
692 var msg = _reports_confirm_delete + "\n";
693 var type = $('input[name=type]').attr('value');
694 if (!id)
695 return;
696 if (is_scheduled) {
697 msg += _reports_confirm_delete_warning;
699 msg = msg.replace("this saved report", "the saved report '"+$('#report_id option[selected=selected]').text()+"'");
700 if (confirm(msg)) {
701 btn.after(loadimg);
702 $.ajax({
703 url: _site_domain + _index_page + '/' + _controller_name + '/delete/',
704 type: 'POST',
705 data: {'id': id},
706 success: function(data) {
707 var a = document.createElement("a");
708 a.href = window.location.href;
709 if(a.search && a.search.indexOf("report_id="+id) !== -1) {
710 window.location.href = a.search.replace(new RegExp("report_id="+id+"&?"), "");
713 error: function() {
714 $.notify(_reports_error + ": failed to save report.", {'sticky': true});
716 dataType: 'json'
721 jQuery.extend(
722 jQuery.expr[':'], {
723 regex: function(a, i, m, r) {
724 var r = new RegExp(m[3], 'i');
725 return r.test(jQuery(a).text());
731 * Regexp filter that (hopefully) works for all browsers
732 * and not just FF
734 function init_regexpfilter() {
735 MyRegexp = new Object();
736 MyRegexp.selectFilterData = new Object();
737 MyRegexp.selectFilter = function(selectId, filter) {
738 var list = document.getElementById(selectId);
739 if(!MyRegexp.selectFilterData[selectId]) {
740 //if we don't have a list of all the options, cache them now'
741 MyRegexp.selectFilterData[selectId] = new Array();
742 for(var i = 0; i < list.options.length; i++)
743 MyRegexp.selectFilterData[selectId][i] = list.options[i];
745 list.options.length = 0; //remove all elements from the list
746 var r = new RegExp(filter, 'i');
747 for(var i = 0; i < MyRegexp.selectFilterData[selectId].length; i++) {
748 //add elements from cache if they match filter
749 var o = MyRegexp.selectFilterData[selectId][i];
750 //if(o.text.toLowerCase().indexOf(filter.toLowerCase()) >= 0) list.add(o, null);
751 if(!o.parentNode && r.test(o.text)) list.add(o, null);
754 MyRegexp.resetFilter = function(selectId) {
755 if (typeof MyRegexp.selectFilterData[selectId] == 'undefined' || !MyRegexp.selectFilterData[selectId].length)
756 return;
757 var list = document.getElementById(selectId);
758 list.options.length = 0; //remove all elements from the list
759 for(var i = 0; i < MyRegexp.selectFilterData[selectId].length; i++) {
760 //add elements from cache if they match filter
761 var o = MyRegexp.selectFilterData[selectId][i];
762 if (!o.parentNode)
763 list.add(o, null);