BMLT Root Server
installer.js
Go to the documentation of this file.
1 /*
2  This file is part of the Basic Meeting List Toolbox (BMLT).
3 
4  Find out more at: https://bmlt.app
5 
6  BMLT is free software: you can redistribute it and/or modify
7  it under the terms of the MIT License.
8 
9  BMLT is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  MIT License for more details.
13 
14  You should have received a copy of the MIT License along with this code.
15  If not, see <https://opensource.org/licenses/MIT>.
16 */
17 function BMLTInstaller( in_prefs ///< A JSON object with the initial prefs.
18  )
19 {
21  var m_map_object;
22  var m_map_center;
23  var m_installer_state;
24  var m_ajax_uri;
25  var m_ajax_request_in_progress;
26  var m_google_api_key = "";
27  var m_google_maps_script = null;
28  var m_google_api_key_is_good = false;
29  var m_database_credentials_are_good = false;
30  var m_server_admin_password_is_good = false;
31 
32  // #mark -
33  // #mark Page Selection Handlers
34  // #mark -
35 
36  /************************************************************************************//**
37  * \brief Shows Page 1 of the installer wizard. It does this by setting the wrapper *
38  * className to the first page, which uses CSS to hide the other two pages. *
39  ****************************************************************************************/
40  this.selectPage1 = function () {
41  if ( this.m_installer_wrapper_object.className != 'page_1_wrapper' ) {
42  this.m_installer_wrapper_object.className = 'page_1_wrapper';
43  };
44  };
45 
46  /************************************************************************************//**
47  * \brief Shows Page 2 of the installer wizard. It does this by setting the wrapper *
48  * className to the first page, which uses CSS to hide the other two pages. *
49  ****************************************************************************************/
50  this.selectPage2 = function () {
51  if ( this.m_installer_wrapper_object.className != 'page_2_wrapper' ) {
52  this.m_installer_wrapper_object.className = 'page_2_wrapper';
53  };
54  };
55 
56  /************************************************************************************//**
57  * \brief Shows Page 3 of the installer wizard. It does this by setting the wrapper *
58  * className to the first page, which uses CSS to hide the other two pages. *
59  ****************************************************************************************/
60  this.selectPage3 = function () {
61  if ( this.m_installer_wrapper_object.className != 'page_3_wrapper' ) {
62  this.m_installer_wrapper_object.className = 'page_3_wrapper';
63  this.testForDatabaseSetup();
64  };
65  };
66 
67  /************************************************************************************//**
68  * \brief Shows Page 4 of the installer wizard. It does this by setting the wrapper *
69  * className to the first page, which uses CSS to hide the other two pages. *
70  ****************************************************************************************/
71  this.selectPage4 = function () {
72  if ( this.m_installer_wrapper_object.className != 'page_4_wrapper' ) {
73  this.m_installer_wrapper_object.className = 'page_4_wrapper';
74  document.getElementById('database_install_stuff_div').className = 'item_hidden';
75  document.getElementById('file_text_pre').innerHTML = this.createFileData();
76  var apiKeyWarningDiv = document.getElementById('admin_google_api_key_warning');
78  if (apiKeyWarningDiv) {
79  this.testMapsApiKey(function (message) {
80  if (message) {
81  apiKeyWarningDiv.innerHTML = g_maps_api_key_warning + ' ' + message + '<br><a href="javascript:g_installer_object.selectPage2()">' + g_maps_api_key_click_here + '</a>';
82  this.m_google_api_key_is_good = false;
83  } else {
84  apiKeyWarningDiv.innerHTML = '';
85  this.m_google_api_key_is_good = true;
86  }
87  testForDatabaseSetup.call(g_installer_object);
88  });
89  }
90  }
91  };
92 
93  // #mark -
94  // #mark Text Item Handlers
95  // #mark -
96 
97  /************************************************************************************//**
98  * \brief When a text input (either <input> or <textarea> is initialized, we can set *
99  * up a default text value that is displayed when the item is empty and not in *
100  * focus. If we don't send in a specific value, then the current value of the *
101  * text item is considered to be the default. *
102  ****************************************************************************************/
103  this.handleTextInputLoad = function (
104  in_text_item,
105  in_default_value,
106  in_size
107  ) {
108  if ( in_text_item ) {
109  in_text_item.original_value = in_text_item.value;
110 
111  in_text_item.small = false;
112  in_text_item.med = false;
113  in_text_item.tiny = false;
114 
115  if ( in_size ) {
116  if ( in_size == 'tiny' ) {
117  in_text_item.tiny = true;
118  } else if ( in_size == 'small' ) {
119  in_text_item.small = true;
120  } else if ( in_size == 'med' ) {
121  in_text_item.med = true;
122  };
123  };
124 
125  if ( in_default_value != null ) {
126  in_text_item.defaultValue = in_default_value;
127  } else {
128  in_text_item.defaultValue = in_text_item.value;
129  };
130 
131  in_text_item.value = in_text_item.original_value;
132 
133  if ( !in_text_item.value || (in_text_item.value == in_text_item.defaultValue) ) {
134  in_text_item.value = in_text_item.defaultValue;
135  in_text_item.className = 'bmlt_text_item' + (in_text_item.small ? '_small' : (in_text_item.med ? '_med' : (in_text_item.tiny ? '_tiny' : ''))) + ' bmlt_text_item_dimmed';
136  } else {
137  in_text_item.className = 'bmlt_text_item' + (in_text_item.small ? '_small' : (in_text_item.med ? '_med' : (in_text_item.tiny ? '_tiny' : '')));
138  };
139 
140  in_text_item.onfocus = function () {
141  g_installer_object.handleTextInputFocus(this); };
142  in_text_item.onblur = function () {
143  g_installer_object.handleTextInputBlur(this); };
144  this.setTextItemClass(in_text_item, false);
145  };
146  };
147 
148  /************************************************************************************//**
149  * \brief This just makes sure that the className is correct. *
150  ****************************************************************************************/
151  this.setTextItemClass = function (
152  in_text_item, ///< This is the text item to check.
153  is_focused ///< true, if the item is in focus
154  ) {
155  if ( in_text_item ) {
156  if ( !is_focused && ((in_text_item.value == null) || (in_text_item.value == in_text_item.defaultValue)) ) {
157  in_text_item.className = 'bmlt_text_item' + (in_text_item.small ? '_small' : (in_text_item.med ? '_med' : (in_text_item.tiny ? '_tiny' : ''))) + ' bmlt_text_item_dimmed';
158  } else {
159  in_text_item.className = 'bmlt_text_item' + (in_text_item.small ? '_small' : (in_text_item.med ? '_med' : (in_text_item.tiny ? '_tiny' : '')));
160  };
161  };
162  };
163 
164  /************************************************************************************//**
165  * \brief When a text item receives focus, we clear any default text. *
166  ****************************************************************************************/
167  this.handleTextInputFocus = function ( in_text_item
168  ) {
169  if ( in_text_item ) {
170  if ( in_text_item.value == in_text_item.defaultValue ) {
171  in_text_item.value = '';
172  };
173 
174  this.setTextItemClass(in_text_item, true);
175  };
176  };
177 
178  /************************************************************************************//**
179  * \brief When a text item loses focus, we restore any default text, if the item was *
180  * left empty. *
181  ****************************************************************************************/
182  this.handleTextInputBlur = function ( in_text_item
183  ) {
184  if ( in_text_item ) {
185  if ( !in_text_item.value ) {
186  in_text_item.value = in_text_item.defaultValue;
187  };
188 
189  this.setTextItemClass(in_text_item, false);
190  };
191  };
192 
193  /************************************************************************************//**
194  * \brief This creates the map for the location tab. *
195  ****************************************************************************************/
196  this.createLocationMap = function ( ) {
197  if ( !this.m_map_object ) {
198  var myOptions = {
199  'center': new google.maps.LatLng(this.m_installer_state.search_spec_map_center.latitude, this.m_installer_state.search_spec_map_center.longitude),
200  'zoom': parseInt(this.m_installer_state.search_spec_map_center.zoom),
201  'mapTypeId': google.maps.MapTypeId.ROADMAP,
202  'mapTypeControlOptions': { 'style': google.maps.MapTypeControlStyle.DROPDOWN_MENU },
203  'zoomControl': true,
204  'mapTypeControl': true,
205  'disableDoubleClickZoom' : true,
206  'draggableCursor': "crosshair",
207  'scaleControl' : true,
208  'scrollwheel' : false
209  };
210 
211  myOptions.zoomControlOptions = { 'style': google.maps.ZoomControlStyle.LARGE };
212 
213  parent_div = document.getElementById('installer_map_display_div');
214  parent_div.innerHTML = '';
215  this.m_map_object = new google.maps.Map(parent_div, myOptions);
216 
217  google.maps.event.addListener(this.m_map_object, 'click', g_installer_object.reactToMapClick);
218 
219  this.m_map_object.setOptions(myOptions);
220 
221  m_icon_image = new google.maps.MarkerImage("./local_server/server_admin/style/images/NACenterMarker.png", new google.maps.Size(21, 36), new google.maps.Point(0,0), new google.maps.Point(11, 36));
222  m_icon_shadow = new google.maps.MarkerImage("./local_server/server_admin/style/images/NACenterMarkerS.png", new google.maps.Size(43, 36), new google.maps.Point(0,0), new google.maps.Point(11, 36));
223 
224  this.m_map_object.main_marker = new google.maps.Marker({
225  'position': this.m_map_object.getCenter(),
226  'map': this.m_map_object,
227  'icon': m_icon_image,
228  'shadow': m_icon_shadow,
229  'clickable': false,
230  'cursor': 'pointer',
231  'draggable': true
232  });
233 
234  google.maps.event.addListener(this.m_map_object.main_marker, 'dragend', g_installer_object.reactToMapClick);
235  };
236  };
237 
238  /************************************************************************************//**
239  * \brief This is the callback for a map click or drag end. *
240  ****************************************************************************************/
241  this.reactToMapClick = function ( in_gMap_event ///< The Google Maps event
242  ) {
243  var map_center = in_gMap_event.latLng;
244  g_installer_object.m_map_object.panTo(map_center);
245  g_installer_object.m_map_object.main_marker.setPosition(map_center);
246  };
247 
248  /************************************************************************************//**
249  * \brief
250  ****************************************************************************************/
251  this.buttonTestForDatabaseSetup = function () {
252  var uri = this.m_ajax_uri;
253 
254  this.gatherInstallerState();
255 
256  if ( this.m_ajax_request_in_progress ) {
257  this.m_ajax_request_in_progress.abort();
258  this.m_ajax_request_in_progress = null;
259  };
260 
261  uri += 'test_comprehensive';
262  var formData = new FormData();
263  formData.append('dbType', this.m_installer_state.dbType);
264  formData.append('dbName', this.m_installer_state.dbName);
265  formData.append('dbUser', this.m_installer_state.dbUser);
266  formData.append('dbPassword', this.m_installer_state.dbPassword);
267  formData.append('dbServer', this.m_installer_state.dbServer);
268  formData.append('dbPrefix', this.m_installer_state.dbPrefix);
269 
270  var salt = new Date();
271  uri += '&salt=' + salt.getTime();
272 
273  this.m_ajax_request_in_progress = BMLT_Installer_AjaxPostRequest(uri, function (in_req) {
274  g_installer_object.buttonTestForDatabaseSetupCallback(in_req); }, formData);
275  };
276 
277  /************************************************************************************//**
278  * \brief
279  ****************************************************************************************/
280  this.buttonTestForDatabaseSetupCallback = function ( in_http_request
281  ) {
282  this.m_ajax_request_in_progress = null;
283 
284  if ( in_http_request.responseText ) {
285  eval('var json_object = ' + in_http_request.responseText + ';');
286 
287  if ( json_object ) { // There is an existing database
288  alert(json_object.message);
289  };
290  } else // Nothing to report.
291  {
292  };
293  };
294 
295  /************************************************************************************//**
296  * \brief
297  ****************************************************************************************/
298  this.testForDatabaseSetup = function () {
299  var uri = this.m_ajax_uri;
300 
301  this.gatherInstallerState();
302 
303  if ( this.m_ajax_request_in_progress ) {
304  this.m_ajax_request_in_progress.abort();
305  this.m_ajax_request_in_progress = null;
306  };
307 
308  uri += 'test';
309  var formData = new FormData();
310  formData.append('dbType', this.m_installer_state.dbType);
311  formData.append('dbName', this.m_installer_state.dbName);
312  formData.append('dbUser', this.m_installer_state.dbUser);
313  formData.append('dbPassword', this.m_installer_state.dbPassword);
314  formData.append('dbServer', this.m_installer_state.dbServer);
315  formData.append('dbPrefix', this.m_installer_state.dbPrefix);
316 
317  var salt = new Date();
318  uri += '&salt=' + salt.getTime();
319 
320  this.m_ajax_request_in_progress = BMLT_Installer_AjaxPostRequest(uri, function (in_req) {
321  g_installer_object.testForDatabaseSetupCallback(in_req); }, formData);
322  };
323 
324  /************************************************************************************//**
325  * \brief
326  ****************************************************************************************/
327  this.testForDatabaseSetupCallback = function ( in_http_request
328  ) {
329  this.m_ajax_request_in_progress = null;
330  this.m_database_credentials_are_good = false;
331  if (in_http_request.responseText) {
332  eval('var ret_val = parseInt ( ' + in_http_request.responseText + ', 10 );');
333  document.getElementById('admin_db_items_warning').innerHTML = '';
334  document.getElementById('admin_pw_warning_div_2').className = 'item_hidden';
335 
336  if (ret_val == 0) { // There is an existing database
337  document.getElementById('admin_login_stuff_fieldset').className = 'item_hidden';
338  document.getElementById('admin_pw_warning_div_2').className = 'extra_text_div red_char';
339  document.getElementById('admin_db_items_warning').innerHTML = g_db_init_db_set_warning_text + '<br><a href="javascript:g_installer_object.selectPage1()">' + g_db_init_db_click_here + '</a>';
340  } else if (ret_val == -1) { // No database
341  document.getElementById('admin_login_stuff_fieldset').className = '';
342  document.getElementById('admin_db_items_warning').innerHTML = g_db_init_db_generic_db_error_text + '<br><a href="javascript:g_installer_object.selectPage1()">' + g_db_init_db_click_here + '</a>';
343  } else {
344  document.getElementById('admin_login_stuff_fieldset').className = '';
345  this.m_database_credentials_are_good = true;
346  this.gatherInstallerState();
347  }
348  }
349  };
350 
351  /************************************************************************************//**
352  * \brief
353  ****************************************************************************************/
354  this.initializeRootServer = function () {
355  document.getElementById('bmlt_installer_initialize_ajax_button').className = 'item_hidden';
356  document.getElementById('bmlt_installer_initialize_ajax_button_throbber_span').className = 'bmlt_admin_ajax_button_throbber_span';
357 
358  var uri = this.m_ajax_uri;
359 
360  this.gatherInstallerState();
361 
362  if ( this.m_ajax_request_in_progress ) {
363  this.m_ajax_request_in_progress.abort();
364  this.m_ajax_request_in_progress = null;
365  }
366 
367  uri += 'initialize_server';
368 
369  var formData = new FormData();
370 
371  // db settings
372  formData.append('dbType', this.m_installer_state.dbType);
373  formData.append('dbName', this.m_installer_state.dbName);
374  formData.append('dbUser', this.m_installer_state.dbUser);
375  formData.append('dbPassword', this.m_installer_state.dbPassword);
376  formData.append('dbServer', this.m_installer_state.dbServer);
377  formData.append('dbPrefix', this.m_installer_state.dbPrefix);
378 
379  // admin user settings
380  var admin_login_object = document.getElementById('installer_admin_login_input');
381  var admin_password_object = document.getElementById('installer_admin_password_input');
382  var admin_login = (admin_login_object.value && (admin_login_object.value != admin_login_object.defaultValue)) ? admin_login_object.value : '';
383  var admin_password = (admin_password_object.value && (admin_password_object.value != admin_password_object.defaultValue)) ? admin_password_object.value : '';
384  formData.append('admin_login', admin_login);
385  formData.append('admin_password', admin_password);
386 
387  // auto-config.inc.php
388  formData.append('region_bias', this.m_installer_state.region_bias);
389  formData.append('gkey', this.m_installer_state.api_key);
390  formData.append('search_spec_map_center_longitude', parseFloat(this.m_installer_state.search_spec_map_center.longitude).toString());
391  formData.append('search_spec_map_center_latitude', parseFloat(this.m_installer_state.search_spec_map_center.latitude).toString());
392  formData.append('search_spec_map_center_zoom', parseInt(this.m_installer_state.search_spec_map_center.zoom, 10).toString());
393  formData.append('comdef_distance_units', this.m_installer_state.comdef_distance_units);
394  formData.append('bmlt_title', this.m_installer_state.bmlt_title.replace(/'/g,"\\'"));
395  formData.append('banner_text', this.m_installer_state.banner_text.replace(/'/g,"\\'"));
396  formData.append('comdef_global_language', this.m_installer_state.comdef_global_language);
397  formData.append('min_pw_len', this.m_installer_state.min_pw_len);
398  formData.append('number_of_meetings_for_auto', parseInt(this.m_installer_state.number_of_meetings_for_auto, 10));
399  formData.append('change_depth_for_meetings', parseInt(this.m_installer_state.change_depth_for_meetings, 10));
400  formData.append('default_duration_time', this.m_installer_state.default_duration_time);
401  formData.append('g_enable_language_selector', (this.m_installer_state.enable_language_selector ? 'TRUE' : 'FALSE'));
402  formData.append('g_enable_semantic_admin', (this.m_installer_state.enable_semantic_admin ? 'TRUE' : 'FALSE'));
403  formData.append('g_defaultClosedStatus', (this.m_installer_state.default_closed ? 'TRUE' : 'FALSE'));
404  formData.append('g_enable_email_contact', (this.m_installer_state.enable_email_contact ? 'TRUE' : 'FALSE'));
405  formData.append('include_service_body_admin_on_emails', (this.m_installer_state.send_copy_to_sba ? 'TRUE' : 'FALSE'));
406  formData.append('include_every_admin_on_emails', (this.m_installer_state.send_copy_to_all_admins ? 'TRUE' : 'FALSE'));
407  formData.append('time_format', this.m_installer_state.time_format.replace(/'/g,"\\'"));
408  formData.append('format_lang_names', encodeURIComponent(this.m_installer_state.format_lang_names));
409  formData.append('time_format', this.m_installer_state.time_format.replace(/'/g,"\\'"));
410  formData.append('change_date_format', this.m_installer_state.change_date_format.replace(/'/g,"\\'"));
411  formData.append('admin_session_name', this.m_installer_state.admin_session_name.replace(/'/g,"\\'"));
412 
413  var salt = new Date();
414  uri += '&salt=' + salt.getTime();
415 
416  var file_input = document.getElementById('bmlt_admin_naws_spreadsheet_file_input');
417  if (file_input && file_input.files && file_input.files.length) {
418  formData.append('thefile', file_input.files[0]);
419  formData.append('initialValueForPublished', document.getElementById('bmlt_admin_naws_spreadsheet_publish_checkbox').checked ? 'TRUE' : 'FALSE');
420  }
421 
422  this.m_ajax_request_in_progress = BMLT_Installer_AjaxPostRequest(uri, function (in_req) {
423  g_installer_object.initializeRootServerCallback(in_req); }, formData);
424  };
425 
426  /************************************************************************************//**
427  * \brief
428  ****************************************************************************************/
429  this.initializeRootServerCallback = function ( in_http_request
430  ) {
431  this.m_ajax_request_in_progress = null;
432  if (in_http_request.responseText) {
433  eval('var ret_val = ' + in_http_request.responseText + ';');
434 
435  var file_input = document.getElementById('bmlt_admin_naws_spreadsheet_file_input');
436  var importAttempted = file_input && file_input.files && file_input.files.length;
437 
438  if (ret_val) {
439  if (ret_val.dbStatus === true && ret_val.configStatus === true && (!importAttempted || ret_val.importStatus === true)) { // Hide the initialize button upon success.
440  document.getElementById('database_install_stuff_div').className = 'item_hidden';
441  document.getElementById('admin_db_items_warning').innerHTML = '';
442  location.reload(true);
443  } else if (!ret_val.dbStatus) {
444  if (importAttempted && ret_val.importReport) {
445  alert(ret_val.importReport);
446  } else if (ret_val.dbReport) {
447  alert(ret_val.dbReport);
448  }
449  } else if (!ret_val.configStatus) {
450  document.getElementById('database_install_stuff_div').className = 'item_hidden';
451  document.getElementById('admin_db_items_warning').innerHTML = '';
452  // There was a problem writing the config file, so show the config and allow the user to write it manually
453  if (importAttempted) {
454  alert('The configuration file could not be written.');
455  location.reload(true);
456  }
457  document.getElementById('result_code_div').className = 'item_shown'; // Show settings file.
458  } else {
459  alert(ret_val.importReport);
460  location.reload(true);
461  }
462  }
463  }
464 
465  document.getElementById('bmlt_installer_initialize_ajax_button_throbber_span').className = 'item_hidden';
466  document.getElementById('bmlt_installer_initialize_ajax_button').className = 'bmlt_admin_ajax_button';
467  };
468 
469  /************************************************************************************//**
470  * \brief Returns the minimum password length *
471  * \returns an integer, with the password length. *
472  ****************************************************************************************/
473  this.geMinPasswordLength = function () {
474  var pw_length_object = document.getElementById('installer_pw_length_select');
475 
476  this.m_installer_state.min_pw_len = pw_length_object.options[pw_length_object.selectedIndex].value;
477 
478  return parseInt(this.m_installer_state.min_pw_len, 10);
479  };
480 
481  /************************************************************************************//**
482  * \brief This gathers the installer state. *
483  ****************************************************************************************/
484  this.gatherInstallerState = function () {
485  var db_type_object = document.getElementById('installer_db_type_select');
486  var db_name_object = document.getElementById('installer_db_name_input');
487  var db_user_object = document.getElementById('installer_db_user_input');
488  var db_pw_object = document.getElementById('installer_db_pw_input');
489  var db_host_object = document.getElementById('installer_db_host_input');
490  var db_prefix_object = document.getElementById('installer_db_prefix_input');
491 
492  var admin_login_object = document.getElementById('installer_admin_login_input');
493  var admin_password_object = document.getElementById('installer_admin_password_input');
494 
495  this.m_installer_state.dbType = db_type_object.options[db_type_object.selectedIndex].value;
496  this.m_installer_state.dbName = (db_name_object.value && (db_name_object.value != db_name_object.defaultValue)) ? db_name_object.value : '';
497  this.m_installer_state.dbUser = (db_user_object.value && (db_user_object.value != db_user_object.defaultValue)) ? db_user_object.value : '';
498  this.m_installer_state.dbPassword = (db_pw_object.value && (db_pw_object.value != db_pw_object.defaultValue)) ? db_pw_object.value : '';
499  this.m_installer_state.dbServer = (db_host_object.value && (db_host_object.value != db_host_object.defaultValue)) ? db_host_object.value : '';
500  this.m_installer_state.dbPrefix = (db_prefix_object.value && (db_prefix_object.value != db_prefix_object.defaultValue)) ? db_prefix_object.value : '';
501 
502  var admin_login = (admin_login_object.value && (admin_login_object.value != admin_login_object.defaultValue)) ? admin_login_object.value : '';
503  var admin_password = (admin_password_object.value && (admin_password_object.value != admin_password_object.defaultValue)) ? admin_password_object.value : '';
504 
505  this.m_server_admin_password_is_good = false;
506  document.getElementById('admin_pw_warning_div').innerHTML = '';
507  if (!admin_login || !admin_login.trim() || !admin_password) {
508  document.getElementById('admin_server_admin_user_warning').innerHTML = g_db_init_no_pw_warning_text + '<br><a href="javascript:g_installer_object.selectPage3()">' + g_server_settings_click_here + '</a>';
509  document.getElementById('admin_server_admin_user_warning').className = 'extra_text_div red_char';
510  } else if (admin_password.length < this.geMinPasswordLength()) {
511  document.getElementById('admin_pw_warning_div').innerHTML = sprintf(g_pw_length_warning_text, this.geMinPasswordLength());
512  document.getElementById('admin_server_admin_user_warning').innerHTML = sprintf(g_pw_length_warning_text, this.geMinPasswordLength()) + '<br><a href="javascript:g_installer_object.selectPage3()">' + g_server_settings_click_here + '</a>';
513  document.getElementById('admin_server_admin_user_warning').className = 'extra_text_div red_char';
514  } else {
515  document.getElementById('admin_server_admin_user_warning').innerHTML = '';
516  document.getElementById('admin_server_admin_user_warning').className = 'item_hidden';
517  this.m_server_admin_password_is_good = true;
518  }
519 
520  var search_count_select_object = document.getElementById('search_count_select');
521 
522  this.m_installer_state.number_of_meetings_for_auto = search_count_select_object.options[search_count_select_object.selectedIndex].value;
523 
524  var change_depth_for_meetings_object = document.getElementById('installer_history_select');
525 
526  this.m_installer_state.change_depth_for_meetings = change_depth_for_meetings_object.options[change_depth_for_meetings_object.selectedIndex].value;
527 
528  var language_object = document.getElementById('installer_lang_select');
529 
530  this.m_installer_state.comdef_global_language = language_object.options[language_object.selectedIndex].value;
531 
532  this.geMinPasswordLength();
533 
534  var region_bias_object = document.getElementById('installer_region_bias_select');
535 
536  this.m_installer_state.region_bias = region_bias_object.options[region_bias_object.selectedIndex].value;
537 
538  this.m_installer_state.api_key = document.getElementById('api_text_entry').value;
539 
540  if (this.m_map_object) {
541  var centerPos = this.m_map_object.main_marker.getPosition();
542 
543  if (!this.m_installer_state.search_spec_map_center) {
544  this.m_installer_state.search_spec_map_center = new Object;
545  }
546 
547  this.m_installer_state.search_spec_map_center.longitude = centerPos.lng();
548  this.m_installer_state.search_spec_map_center.latitude = centerPos.lat();
549  this.m_installer_state.search_spec_map_center.zoom = this.m_map_object.getZoom();
550  }
551 
552  this.m_installer_state.bmlt_title = document.getElementById('installer_title_input').value;
553 
554  this.m_installer_state.banner_text = document.getElementById('installer_banner_input').value;
555 
556  var distance_units_object = document.getElementById('distance_units_select');
557 
558  var duration_time_hours_object = document.getElementById('installer_duration_hour_select');
559  var duration_time_minutes_object = document.getElementById('installer_duration_minutes_select');
560 
561  this.m_installer_state.default_duration_time = sprintf("%d:%02d:00", parseInt(duration_time_hours_object.options[duration_time_hours_object.selectedIndex].value, 10), parseInt(duration_time_minutes_object.options[duration_time_minutes_object.selectedIndex].value, 10));
562 
563  this.m_installer_state.comdef_distance_units = distance_units_object.options[distance_units_object.selectedIndex].value;
564 
565  this.m_installer_state.enable_language_selector = document.getElementById('installer_admin_language_selector_checkbox').checked;
566 
567  this.m_installer_state.enable_email_contact = document.getElementById('installer_admin_email_contact_checkbox').checked;
568 
569  this.m_installer_state.enable_semantic_admin = document.getElementById('semantic_admin_checkbox').checked;
570 
571  this.m_installer_state.default_closed = document.getElementById('default_closed_checkbox').checked;
572 
573  this.m_installer_state.send_copy_to_sba = document.getElementById('installer_admin_email_sba_contact_checkbox').checked;
574 
575  this.m_installer_state.send_copy_to_all_admins = document.getElementById('installer_admin_email_all_admins_checkbox').checked;
576 
577  this.m_installer_state.default_duration_text = '';
578 
579  if (this.m_google_api_key_is_good === true && this.m_database_credentials_are_good === true && this.m_server_admin_password_is_good === true) {
580  document.getElementById('database_install_stuff_div').className = '';
581  }
582 
583  this.m_installer_state.format_lang_names = convertFormatLangsToJSON(document.getElementById('format_lang_names').value);
584  };
585 
587  var ret = {};
588  var langs = v.match(/\S+/g);
589  if (!Array.isArray(langs)) {
590  return JSON.stringify(ret);
591  }
592  langs.forEach(function(lang) {
593  var parts = lang.split(':');
594  if (parts.length == 2) {
595  parts[0] = parts[0].trim();
596  parts[1] = parts[1].trim();
597  if (parts[0]==="" || parts[1]==="") {
598  alert("Could not parse format languages");
599  }
600  ret[parts[0].trim()] = parts[1];
601  } else {
602  alert("Could not parse format languages");
603  }
604  });
605  return JSON.stringify(ret);
606  };
607 
608  /************************************************************************************//**
609  * \brief Creates the text for the file *
610  * \returns The PHP code for the auto-config.inc.php file. *
611  ****************************************************************************************/
612  this.createFileData = function () {
613  var ret = "&lt;?php\n";
614 
615  if ( this.m_installer_state && this.m_installer_state.search_spec_map_center ) {
616  ret += "defined( 'BMLT_EXEC' ) or die ( 'Cannot Execute Directly' ); // Makes sure that this file is in the correct context.\n";
617 
618  ret += "\n\t// These are the settings created by the installer wizard.\n";
619 
620  ret += "\n\t\t// Database settings:\n";
621  ret += "\t\t$dbType = '" + this.m_installer_state.dbType.replace(/'/g,"\\'") + "'; // This is the PHP PDO driver name for your database.\n";
622  ret += "\t\t$dbName = '" + this.m_installer_state.dbName.replace(/'/g,"\\'") + "'; // This is the name of the database.\n";
623  ret += "\t\t$dbUser = '" + this.m_installer_state.dbUser.replace(/'/g,"\\'") + "'; // This is the SQL user that is authorized for the above database.\n";
624  ret += "\t\t$dbPassword = '" + this.m_installer_state.dbPassword.replace(/'/g,"\\'") + "'; // This is the password for the above authorized user. Make it a big, ugly hairy one. It is powerful, and there is no need to remember it.\n";
625  ret += "\t\t$dbServer = '" + this.m_installer_state.dbServer.replace(/'/g,"\\'") + "'; // This is the host/server for accessing the database.\n";
626  ret += "\t\t$dbPrefix = '" + this.m_installer_state.dbPrefix.replace(/'/g,"\\'") + "'; // This is a table name prefix that can be used to differentiate tables used by different root server instances that share the same database.\n";
627 
628  ret += "\n\t\t// Location and Map settings:\n";
629  ret += "\t\t$region_bias = '" + this.m_installer_state.region_bias + "'; // This is a 2-letter code for a 'region bias,' which helps Google Maps to figure out ambiguous search queries.\n";
630  ret += "\t\t$gkey = '" + this.m_installer_state.api_key + "'; // This is the Google Maps JavaScript API Key, necessary for using Google Maps.\n";
631  ret += "\t\t$search_spec_map_center = array ( 'longitude' => " + parseFloat(this.m_installer_state.search_spec_map_center.longitude).toString() + ", 'latitude' => " + parseFloat(this.m_installer_state.search_spec_map_center.latitude).toString() + ", 'zoom' => " + parseInt(this.m_installer_state.search_spec_map_center.zoom, 10).toString() + " ); // This is the default map location for new meetings.\n";
632  ret += "\t\t$comdef_distance_units = '" + this.m_installer_state.comdef_distance_units + "';\n";
633 
634  ret += "\n\t\t// Display settings:\n";
635  ret += "\t\t$bmlt_title = '" + this.m_installer_state.bmlt_title.replace(/'/g,"\\'") + "'; // This is the page title and heading for the main administration login page.\n";
636  ret += "\t\t$banner_text = '" + this.m_installer_state.banner_text.replace(/'/g,"\\'") + "'; // This is text that is displayed just above the login box on the main login page.\n";
637 
638  ret += "\n\t\t// Miscellaneous settings:\n";
639  ret += "\t\t$comdef_global_language ='" + this.m_installer_state.comdef_global_language + "'; // This is the 2-letter code for the default root server localization (will default to 'en' -English, if the localization is not available).\n";
640  ret += "\t\t$min_pw_len = " + this.m_installer_state.min_pw_len + "; // The minimum number of characters in a user account password for this root server.\n";
641  ret += "\t\t$number_of_meetings_for_auto = " + parseInt(this.m_installer_state.number_of_meetings_for_auto, 10) + "; // This is an approximation of the number of meetings to search for in the auto-search feature. The higher the number, the wider the radius.\n";
642  ret += "\t\t$change_depth_for_meetings = " + parseInt(this.m_installer_state.change_depth_for_meetings, 10) + ";\t// This is how many changes should be recorded for each meeting.";
643  ret += "\n\t\t\t\t\t\t// The higher the number, the larger the database will grow, as this can become quite substantial.\n";
644  ret += "\t\t$default_duration_time = '" + this.m_installer_state.default_duration_time + "'; // This is the default duration for meetings that have no duration specified.\n";
645  ret += "\t\t$g_enable_language_selector = " + (this.m_installer_state.enable_language_selector ? 'TRUE' : 'FALSE') + "; // Set this to TRUE (or 1) to enable a popup on the login screen that allows the administrator to select their language.\n";
646  ret += "\t\t$g_enable_semantic_admin = " + (this.m_installer_state.enable_semantic_admin ? 'TRUE' : 'FALSE') + "; // If this is TRUE (or 1), then Semantic Administration for this Server is enabled (Administrators can log in using apps).\n";
647  ret += "\t\t$g_defaultClosedStatus = " + (this.m_installer_state.default_closed ? 'TRUE' : 'FALSE') + "; // If this is FALSE (or 0), then the default (unspecified) Open/Closed format for meetings reported to NAWS is OPEN. Otherwise, it is CLOSED.\n";
648  ret += "\n\t\t// These reflect the way that we handle contact emails.\n";
649  ret += "\t\t$g_enable_email_contact = " + (this.m_installer_state.enable_email_contact ? 'TRUE' : 'FALSE') + "; // If this is TRUE (or 1), then this will enable the ability to contact meeting list contacts via a secure email form.\n";
650  ret += "\t\t$include_service_body_admin_on_emails = " + (this.m_installer_state.send_copy_to_sba ? 'TRUE' : 'FALSE') + ";\t// If this is TRUE (or 1), then any emails sent using the meeting contact will include the nearest Service Body Admin\n";
651  ret += "\t\t\t\t\t\t\t\t\t\t\t\t\t\t// contact for the meeting Service body (ignored, if $g_enable_email_contact is FALSE).\n";
652  ret += "\t\t$include_every_admin_on_emails = " + (this.m_installer_state.send_copy_to_all_admins ? 'TRUE' : 'FALSE') + ";\t// If this is TRUE (or 1), then any emails sent using the meeting contact will include all Service Body Admin contacts";
653  ret += "\n\t\t\t\t\t\t\t\t\t\t\t\t// (including the Server Administrator) for the meeting.";
654  ret += "\n\t\t\t\t\t\t\t\t\t\t\t\t// (ignored, if $g_enable_email_contact or $include_service_body_admin_on_emails is FALSE)\n";
655  ret += "\n\t\t\t\t\t\t\t\t\t\t\t\t// The server languages are supported by default, the langs specified here add to them";
656  ret += "\t\t$format_lang_names = " + this.m_installer_state.format_lang_names+";\t//";
657  ret += "\n\t// These are 'hard-coded,' but can be changed later.\n";
658 
659  ret += "\n\t\t$time_format = '" + this.m_installer_state.time_format.replace(/'/g,"\\'") + "'; // The PHP date() format for the times displayed.\n";
660  ret += "\t\t$change_date_format = '" + this.m_installer_state.change_date_format.replace(/'/g,"\\'") + "'; // The PHP date() format for times/dates displayed in the change records.\n";
661  ret += "\t\t$admin_session_name = '" + this.m_installer_state.admin_session_name.replace(/'/g,"\\'") + "'; // This is merely the 'tag' used to identify the BMLT admin session.\n";
662 
663  ret += "?&gt;\n";
664  } else {
665  ret = '';
666  };
667 
668  return ret;
669  };
670 
671  /************************************************************************************//**
672  * \brief Triggered by the TEST KEY button. When no callback is provided, result is *
673  * displayed in an alert box. *
674  ****************************************************************************************/
675  this.testMapsApiKey = function (callback) {
676  this.m_google_api_key = document.getElementById('api_text_entry').value;
677  var showGoogleApiKeyError = function (message) {
678  alert(message);
679  };
680 
681  if (!this.m_google_api_key || !this.m_google_api_key.trim()) {
682  if (callback) {
683  callback.call(g_installer_object, g_maps_api_key_not_set);
684  } else {
685  showGoogleApiKeyError(g_maps_api_key_not_set);
686  }
687  } else {
688  var testKeyXhr = new XMLHttpRequest();
689  testKeyXhr.onreadystatechange = function () {
690  if (testKeyXhr.readyState !== 4) {
691  return;
692  }
693  var response = JSON.parse(testKeyXhr.responseText);
694  if (callback) {
695  if (response.status === 'OK' || (response.status === 'REQUEST_DENIED' && response.error_message.indexOf('referer restrictions') !== -1)) {
696  callback.call(g_installer_object);
697  } else {
698  callback.call(g_installer_object, response.error_message);
699  }
700  } else {
701  if (response.status === 'OK' || (response.status === 'REQUEST_DENIED' && response.error_message.indexOf('referer restrictions') !== -1)) {
702  alert(g_maps_api_key_valid);
703  } else {
704  showGoogleApiKeyError(g_maps_api_key_warning + ' ' + response.error_message);
705  }
706  }
707  };
708  testKeyXhr.open('GET', 'https://maps.googleapis.com/maps/api/geocode/json?key=' + this.m_google_api_key + '&address=27205');
709  testKeyXhr.send();
710  }
711  };
712 
713  // #mark -
714  // #mark Main Context
715  // #mark -
716 
717  if ( !this.m_installer_state ) {
718  this.m_installer_state = in_prefs;
719  };
720 
721  this.m_installer_wrapper_object = document.getElementById('installer_wrapper');
722 };
723 
724 /************************************************************************************//**
725 * \brief A global context callback for the email checkboxes being selected. *
726 ****************************************************************************************/
728 {
729  var enableEmailObject = document.getElementById('installer_admin_email_contact_checkbox');
730  var sbAdminEmailObject = document.getElementById('installer_admin_email_sba_contact_checkbox');
731  var allAdminsEmailCheckbox = document.getElementById('installer_admin_email_all_admins_checkbox');
732 
733  if ( !enableEmailObject.checked ) {
734  sbAdminEmailObject.checked = false;
735  };
736 
737  if ( !sbAdminEmailObject.checked ) {
738  allAdminsEmailCheckbox.checked = false;
739  };
740 
741  sbAdminEmailObject.disabled = !enableEmailObject.checked;
742  allAdminsEmailCheckbox.disabled = !sbAdminEmailObject.checked;
743 };
744 
745 /****************************************************************************************//**
746 * \brief A global context callback for the script load completion. *
747 ********************************************************************************************/
749 {
750  g_installer_object.createLocationMap();
751 };
752 
753 // #mark -
754 // #mark AJAX Handler
755 // #mark -
756 
757 /****************************************************************************************//**
758 * \brief A simple, generic AJAX request function. *
759 * *
760 * \returns a new XMLHTTPRequest object. *
761 ********************************************************************************************/
762 
764  url, ///< The URI to be called
765  callback, ///< The success callback
766  data, ///< FormData object
767  extra_data ///< If supplied, extra data to be delivered to the callback.
768 ) {
769  /************************************************************************************//**
770  * \brief Create a generic XMLHTTPObject. *
771  * *
772  * This will account for the various flavors imposed by different browsers. *
773  * *
774  * \returns a new XMLHTTPRequest object. *
775  ****************************************************************************************/
776 
777  function createXMLHTTPObject() {
778  var XMLHttpArray = [
779  function () {return new XMLHttpRequest()},
780  function () {return new ActiveXObject("Msxml2.XMLHTTP")},
781  function () {return new ActiveXObject("Msxml2.XMLHTTP")},
782  function () {return new ActiveXObject("Microsoft.XMLHTTP")}
783  ];
784 
785  var xmlhttp = false;
786  for (var i=0; i < XMLHttpArray.length; i++) {
787  try {
788  xmlhttp = XMLHttpArray[i]();
789  } catch (e) {
790  continue;
791  }
792  break;
793  }
794 
795  return xmlhttp;
796  }
797 
798  var req = createXMLHTTPObject();
799  req.finalCallback = callback;
800  if (extra_data != null) {
801  req.extra_data = extra_data;
802  }
803  req.open("POST", url, true);
804  req.onreadystatechange = function () {
805  if (req.readyState !== 4) {
806  return;
807  }
808  if (req.status !== 200) {
809  return;
810  }
811  callback(req, req.extra_data);
812  req = null;
813  }
814  req.send(data);
815 
816  return req;
817 }
818 
819 // #mark -
820 // #mark ########## Third-Party Code ##########
821 // #mark -
822 
823 /**
824 sprintf() for JavaScript 0.6
825 
826 Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
827 All rights reserved.
828 
829 Redistribution and use in source and binary forms, with or without
830 modification, are permitted provided that the following conditions are met:
831  * Redistributions of source code must retain the above copyright
832  notice, this list of conditions and the following disclaimer.
833  * Redistributions in binary form must reproduce the above copyright
834  notice, this list of conditions and the following disclaimer in the
835  documentation and/or other materials provided with the distribution.
836  * Neither the name of sprintf() for JavaScript nor the
837  names of its contributors may be used to endorse or promote products
838  derived from this software without specific prior written permission.
839 
840 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
841 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
842 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
843 DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
844 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
845 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
846 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
847 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
848 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
849 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
850 
851 
852 Changelog:
853 2007.04.03 - 0.1:
854  - initial release
855 2007.09.11 - 0.2:
856  - feature: added argument swapping
857 2007.09.17 - 0.3:
858  - bug fix: no longer throws exception on empty paramenters (Hans Pufal)
859 2007.10.21 - 0.4:
860  - unit test and patch (David Baird)
861 2010.05.09 - 0.5:
862  - bug fix: 0 is now preceeded with a + sign
863  - bug fix: the sign was not at the right position on padded results (Kamal Abdali)
864  - switched from GPL to BSD license
865 2010.05.22 - 0.6:
866  - reverted to 0.4 and fixed the bug regarding the sign of the number 0
867  Note:
868  Thanks to Raphael Pigulla <raph (at] n3rd [dot) org> (http://www.n3rd.org/)
869  who warned me about a bug in 0.5, I discovered that the last update was
870  a regress. I appologize for that.
871 **/
872 
873 function sprintf()
874 {
875  function str_repeat(i, m)
876  {
877  for (var o = []; m > 0; o[--m] = i) {
878  }
879  return o.join('');
880  };
881 
882  var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
883 
884  while (f) {
885  if (m = /^[^\x25]+/.exec(f)) {
886  o.push(m[0]);
887  } else if (m = /^\x25{2}/.exec(f)) {
888  o.push('%');
889  } else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
890  if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
891  throw('Too few arguments.');
892  };
893 
894  if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) {
895  throw('Expecting number but found ' + typeof(a));
896  };
897 
898  switch (m[7]) {
899  case 'b': a = a.toString(2); break;
900  case 'c': a = String.fromCharCode(a); break;
901  case 'd': a = parseInt(a,10); break;
902  case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
903  case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
904  case 'o': a = a.toString(8); break;
905  case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
906  case 'u': a = Math.abs(a); break;
907  case 'x': a = a.toString(16); break;
908  case 'X': a = a.toString(16).toUpperCase(); break;
909  };
910 
911  a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a);
912  c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
913  x = m[5] - String(a).length - s.length;
914  p = m[5] ? str_repeat(c, x) : '';
915  o.push(s + (m[4] ? a + p : p + a));
916  } else {
917  throw('Huh ?!');
918  };
919 
920  f = f.substring(m[0].length);
921  };
922 
923  return o.join('');
924 };
function convertFormatLangsToJSON(v)
Definition: installer.js:586
var f
function g
function reactToEmailCheckbox()
A global context callback for the email checkboxes being selected. *.
Definition: installer.js:727
var v
var x
this geMinPasswordLength
Returns the minimum password length *.
Definition: installer.js:473
function sprintf()
Definition: installer.js:873
this testForDatabaseSetupCallback
Definition: installer.js:327
var s
var a
var u
this buttonTestForDatabaseSetupCallback
Definition: installer.js:280
function BMLTInstaller(in_prefs)
Definition: installer.js:17
var o
function gmScriptLoadCompletion()
A global context callback for the script load completion. *.
Definition: installer.js:748
this initializeRootServerCallback
Definition: installer.js:429
function d(e)
var m
this createFileData
Creates the text for the file *.
Definition: installer.js:612
this testMapsApiKey
Triggered by the TEST KEY button. When no callback is provided, result is * displayed in an alert box...
Definition: installer.js:675
this testForDatabaseSetup
Definition: installer.js:298
function e
var i
this reactToMapClick
This is the callback for a map click or drag end. *.
Definition: installer.js:241
this gatherInstallerState
This gathers the installer state. *.
Definition: installer.js:484
function BMLT_Installer_AjaxPostRequest(url, callback, data, extra_data)
A simple, generic AJAX request function. * *.
Definition: installer.js:763
this m_installer_wrapper_object
Definition: installer.js:721
this buttonTestForDatabaseSetup
Definition: installer.js:251
var b
var c
this initializeRootServer
Definition: installer.js:354