]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | /* Log the user out if it was logged in. | |
3 | * This is mostly for devs running the install screen and | |
4 | * fater succeeding the first screen suddenly being logged in | |
5 | * with old credentials/uid weirdness. | |
6 | * Code from example #1 at https://www.php.net/manual/en/function.session-destroy.php | |
7 | */ | |
8 | session_start(); | |
9 | $_SESSION = Array(); | |
10 | if (ini_get("session.use_cookies")) { | |
11 | $params = session_get_cookie_params(); | |
12 | setcookie(session_name(), '', time() - 42000, | |
13 | $params["path"], $params["domain"], | |
14 | $params["secure"], $params["httponly"] | |
15 | ); | |
16 | } | |
17 | session_destroy(); | |
18 | ||
19 | require_once "../inc/common.php"; | |
20 | ||
21 | /* Get the base url */ | |
22 | $uri = $_SERVER['REQUEST_URI']; | |
23 | $tok = split($uri, "/"); | |
24 | $base_url = ""; | |
25 | for ($i=0; isset($tok[$i]); $i++) | |
26 | { | |
27 | if ($tok[$i] == "settings" && strstr($tok[$i + 1], "install.php")) | |
28 | { | |
29 | if ($i) | |
30 | { | |
31 | for($j=0; $j < $i; $j++) | |
32 | { | |
33 | strcat($base_url,$tok[$j]); | |
34 | strcat($base_url,"/"); | |
35 | } | |
36 | } | |
37 | } | |
38 | } | |
39 | if (!strlen($base_url)) | |
40 | $base_url = "/"; | |
41 | define('BASE_URL', $base_url); | |
42 | ||
43 | $writable = (is_writable("../config/")) ? true: false; | |
44 | ?> | |
45 | <!DOCTYPE html> | |
46 | <head> | |
47 | <div class="media"> | |
48 | <div class="media-body"> | |
49 | ||
50 | <meta name="viewport" content="width=device-width, initial-scale=1"> | |
51 | <meta name="HandheldFriendly" content="true"> | |
52 | ||
53 | ||
54 | ||
55 | <!-- Latest compiled and minified CSS --> | |
56 | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"> | |
57 | ||
58 | <!-- Font Awesome JS --> | |
59 | <script defer src="https://use.fontawesome.com/releases/v6.2.1/js/solid.js" integrity="sha384-tzzSw1/Vo+0N5UhStP3bvwWPq+uvzCMfrN1fEFe+xBmv1C/AtVX5K0uZtmcHitFZ" crossorigin="anonymous"></script> | |
60 | <script defer src="https://use.fontawesome.com/releases/v6.2.1/js/fontawesome.js" integrity="sha384-6OIrr52G08NpOFSZdxxz1xdNSndlD4vdcf/q2myIUVO0VsqaGHJsB0RaBE01VTOY" crossorigin="anonymous"></script> | |
61 | ||
62 | <!-- Font Awesome icons --> | |
63 | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css"> | |
64 | <script src="../js/unrealircd-admin.js"></script> | |
65 | <title>UnrealIRCd Panel</title> | |
66 | <link rel="icon" type="image/x-icon" href="<?php echo get_config("base_url"); ?>img/favicon.ico"> | |
67 | ||
68 | <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> | |
69 | <!-- Popper.JS --> | |
70 | <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script> | |
71 | <!-- Bootstrap JS --> | |
72 | <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script> | |
73 | </div></div> | |
74 | </head> | |
75 | ||
76 | <body role="document"> | |
77 | ||
78 | <div class="container mt-4"><div class="row justify-content-center"><img src="../img/unreal.jpg" width="35px" height="35px" style="margin-right: 15px"><h3>UnrealIRCd Admin Panel Configuration and Setup</h3></div></div> | |
79 | <?php | |
80 | ||
81 | if (file_exists("../config/config.php")) | |
82 | { | |
83 | ?><br><div class="container"><?php Message::Fail("You're already configured!"); ?> | |
84 | <br> | |
85 | <a class="text-center btn btn-primary" href="<?php echo BASE_URL; ?>">Take me home!</a> | |
86 | </div> | |
87 | <?php | |
88 | return; | |
89 | } | |
90 | elseif (isset($_POST) && !empty($_POST)) | |
91 | { | |
92 | ?><br><div class="container"><?php | |
93 | $opts = (object)$_POST; | |
94 | /* pre-load the appropriate auth plugin */ | |
95 | $auth_method = (isset($opts->auth_method)) ? $opts->auth_method : NULL; | |
96 | $auth_method_name = NULL; | |
97 | switch($auth_method) | |
98 | { | |
99 | case "sql_db": | |
100 | $auth_method_name = "SQLDB"; | |
101 | break; | |
102 | case "file_db": | |
103 | $auth_method_name = "FileDB"; | |
104 | break; | |
105 | } | |
106 | if ($auth_method) | |
107 | $am = new Plugin($auth_method); | |
108 | else | |
109 | { | |
110 | Message::Fail("Invalid parameters"); | |
111 | return; | |
112 | } | |
113 | if ($am->error) | |
114 | { | |
115 | Message::Fail("Couldn't load plugin \"$auth_method\": $am->error"); | |
116 | return; | |
117 | } | |
118 | ||
119 | $config["base_url"] = BASE_URL; | |
120 | $config["plugins"] = Array("$auth_method"); | |
121 | if ($auth_method == "sql_db") | |
122 | { | |
123 | $config["mysql"] = [ | |
124 | "host" => $opts->sql_host, | |
125 | "database" => $opts->sql_db, | |
126 | "username" => $opts->sql_user, | |
127 | "password" => $opts->sql_password, | |
128 | "table_prefix" => $opts->sql_table_prefix, | |
129 | ]; | |
130 | } | |
131 | ||
132 | generate_secrets(); | |
133 | ||
134 | /* First, write only the config file */ | |
135 | write_config_file(); | |
136 | ||
137 | if ($auth_method == "sql_db") | |
138 | { | |
139 | sql_db::delete_tables(); | |
140 | if (!sql_db::create_tables()) | |
141 | Message::Fail("Could not create SQL tables"); | |
142 | } else if ($auth_method == "file_db") | |
143 | { | |
144 | file_db::delete_db(); | |
145 | } | |
146 | ||
147 | $user = [ | |
148 | "user_name" => $opts->account_user, | |
149 | "user_pass" => $opts->account_password, | |
150 | "fname" => $opts->account_fname, | |
151 | "lname" => $opts->account_lname, | |
152 | "user_bio" => $opts->account_bio, | |
153 | "email" => $opts->account_email | |
154 | ]; | |
155 | ||
156 | create_new_user($user); | |
157 | $lkup = new PanelUser($opts->account_user); | |
158 | if (!$lkup->id) | |
159 | { | |
160 | Message::Fail("Could not create user"); | |
161 | return; | |
162 | } | |
163 | $lkup->add_meta('role', 'Super-Admin'); | |
164 | ||
165 | /* Enable lookups on HIBP by default */ | |
166 | $config['hibp'] = true; | |
167 | ||
168 | /* Now, write all the config (config.php + settings in DB) */ | |
169 | write_config(); | |
170 | ?> | |
171 | <br> | |
172 | The configuration file has been written. Now, log in to the panel to proceed with the rest of the installation.<br><br> | |
173 | <a class="text-center btn btn-primary" href="<?php echo BASE_URL; ?>">Let's go!</a></div> | |
174 | <?php | |
175 | return; | |
176 | } | |
177 | ||
178 | ?> | |
179 | <style> | |
180 | table tr td { | |
181 | font-style: italic; | |
182 | } | |
183 | </style> | |
184 | <?php if (!$writable) { ?> | |
185 | <div id="page1" class="container"> | |
186 | <br> | |
187 | The admin panel needs to be able to write the config file.<br> | |
188 | Please run: <code>sudo chown <?php echo get_current_user(); ?> <?php echo UPATH; ?> -R</code><br> | |
189 | And after that, refresh this webpage.<br><br> | |
190 | If you have any questions about this, read <a href="https://www.unrealircd.org/docs/UnrealIRCd_webpanel#Permissions" target="_blank">the installation manual on permissions</a>. | |
191 | </div> | |
192 | <?php | |
193 | die; | |
194 | } ?> | |
195 | ||
196 | <!-- Form start --> | |
197 | <form method="post"> | |
198 | <div id="page3" class="container"> | |
199 | <h5>Database Backend</h5> | |
200 | <br> | |
201 | Which database backend would you like to use? | |
202 | <br><br> | |
203 | Please choose from the available options: | |
204 | <div class="form-group"> | |
205 | <div class="form-check"> | |
206 | <input class="form-check-input" type="radio" name="auth_method" id="file_db_radio" value="file_db"> | |
207 | <label class="form-check-label" for="file_db_radio"> | |
208 | File-based database (Uses local files as a database, no additional setup needed) | |
209 | </label> | |
210 | </div> | |
211 | <div class="form-check"> | |
212 | <input class="form-check-input" type="radio" name="auth_method" id="sql_db_radio" value="sql_db"> | |
213 | <label class="form-check-label" for="sql_db_radio"> | |
214 | SQL Database (Requires an SQL database) | |
215 | </label> | |
216 | </div> | |
217 | </div> | |
218 | <br> | |
219 | <div id="sql_form" style="display:none"> | |
220 | Please enter your SQL information. <div id="sql_instructions" class="ml-4 btn btn-sm btn-info">View instructions</div> | |
221 | <div class="form-group"> | |
222 | <label for="sql_host">Hostname or IP</label> | |
223 | <input name="sql_host" type="text" class="revalidation-needed-sql form-control" id="sql_host" aria-describedby="hostname_help" value="127.0.0.1"> | |
224 | <small id="hostname_help" class="form-text text-muted">The hostname or IP address of your SQL server. You should use <code>127.0.0.1</code> for the same machine.</small> | |
225 | </div> | |
226 | <div class="form-group"> | |
227 | <label for="sql_db">Database name</label> | |
228 | <input name="sql_db" type="text" class="revalidation-needed-sql form-control" id="sql_db" aria-describedby="port_help"> | |
229 | <small id="port_help" class="form-text text-muted">The name of the SQL database to write to and read from.</small> | |
230 | </div> | |
231 | <div class="form-group"> | |
232 | <label for="sql_username">Username</label> | |
233 | <input name="sql_user" type="text" class="revalidation-needed-sql form-control" id="sql_user" aria-describedby="username_help" autocomplete="new-password"> | |
234 | <small id="username_help" class="form-text text-muted">The name of SQL user</small> | |
235 | </div> | |
236 | <div class="form-group"> | |
237 | <label for="sql_password">Password</label> | |
238 | <input name="sql_password" type="password" class="revalidation-needed-sql form-control" id="sql_password" autocomplete="new-password"> | |
239 | </div> | |
240 | <div class="form-group"> | |
241 | <label for="sql_table_prefix">Table prefix</label> | |
242 | <input name="sql_table_prefix" type="text" class="revalidation-needed-sql form-control" id="sql_table_prefix" aria-describedby="sql_table_prefix_help" value="unreal_"> | |
243 | <small id="sql_table_prefix_help" class="form-text text-muted">The prefix for table names (leave blank for none)</small> | |
244 | </div> | |
245 | </div> | |
246 | <div class="text-center"> | |
247 | <div id="page3_next" class="btn btn-primary ml-3">Next</div> | |
248 | <div id="page3_test_connection" class="btn btn-primary ml-3" style="display: none">Test connection</div> | |
249 | </div> | |
250 | </div> | |
251 | <div id="page4" class="container" > | |
252 | <h5>Create your account</h5> | |
253 | <br> | |
254 | You need an account, let's make one.<br><br> | |
255 | <div class="form-group"> | |
256 | <label for="account_user" id="userlabel">Pick a username</label> | |
257 | <input name="account_user" type="text" class="form-control" id="account_user" aria-describedby="username_help"> | |
258 | <small id="username_help" class="form-text text-muted">Pick a username! Please make sure it's at least 3 characters long, contains no spaces, and is made of only letters and numbers.</small> | |
259 | </div> | |
260 | <div class="form-group"> | |
261 | <label for="account_password" id="passlabel">Password</label> | |
262 | <input name="account_password" type="password" class="form-control" id="account_password" aria-describedby="password_help"> | |
263 | <small id="password_help" class="form-text text-muted">Please choose a password that at least 8 characters long, contains at least one uppercase letter, one lowercase letter, one number and one symbol.</small> | |
264 | </div> | |
265 | <div class="form-group"> | |
266 | <label for="account_password_conf" id="passconflabel">Confirm password</label> | |
267 | <input name="account_password_conf" type="password" class="form-control" id="account_password_conf"> | |
268 | </div> | |
269 | <div class="form-group"> | |
270 | <label for="account_email" id="emaillabel">Email address</label> | |
271 | <input name="account_email" type="text" class="form-control" id="account_email" aria-describedby="email_help"> | |
272 | </div> | |
273 | <div class="form-group"> | |
274 | <label for="account_fname" id="fnamelabel">First name</label> | |
275 | <input name="account_fname" type="text" class="form-control" id="account_fname"> | |
276 | </div> | |
277 | <div class="form-group"> | |
278 | <label for="account_lname" id="lnamelabel">Last name</label> | |
279 | <input name="account_lname" type="text" class="form-control" id="account_lname"> | |
280 | </div> | |
281 | <div class="form-group"> | |
282 | <label for="account_bio" id="biolabel">Bio</label> | |
283 | <textarea name="account_bio" type="text" class="form-control" id="account_bio"></textarea> | |
284 | </div> | |
285 | <div class="text-center"> | |
286 | <div id="page4_back" class="btn btn-secondary mr-3">Back</div> | |
287 | <button id="page4_next" type="submit" class="btn btn-primary ml-3">Submit</div> | |
288 | </div> | |
289 | </div> | |
290 | </form> | |
291 | ||
292 | <!-- Database overwrite prompt --> | |
293 | <div class="modal fade" id="db_overwrite_modal" tabindex="-1" role="dialog" aria-labelledby="confirmModalCenterTitle" aria-hidden="true"> | |
294 | <div class="modal-dialog modal-dialog-centered" role="document"> | |
295 | <div class="modal-content"> | |
296 | <div class="modal-header"> | |
297 | <h5 class="modal-title" id="myModalLabel">Database already contains data</h5> | |
298 | <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |
299 | <span aria-hidden="true">×</span> | |
300 | </button> | |
301 | </div> | |
302 | <div class="modal-body"> | |
303 | The database already exists and contains data. | |
304 | If you continue then this existing data will be deleted. | |
305 | </div> | |
306 | <div class="modal-footer"> | |
307 | <button id="CloseButton" type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> | |
308 | <button id="ProceedButton" type="button" class="btn btn-danger" onclick="nextstep();">Continue</button> | |
309 | </form> | |
310 | </div> | |
311 | </div> | |
312 | </div> | |
313 | </div> | |
314 | ||
315 | <!-- Database error dialog --> | |
316 | <div class="modal fade" id="db_error_modal" tabindex="-1" role="dialog" aria-labelledby="confirmModalCenterTitle" aria-hidden="true"> | |
317 | <div class="modal-dialog modal-dialog-centered" role="document"> | |
318 | <div class="modal-content"> | |
319 | <div class="modal-header"> | |
320 | <h5 class="modal-title" id="myModalLabel">Database server error</h5> | |
321 | <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |
322 | <span aria-hidden="true">×</span> | |
323 | </button> | |
324 | </div> | |
325 | <div class="modal-body" id="db_error_text"> | |
326 | Unable to connect to the database. | |
327 | </div> | |
328 | <div class="modal-footer"> | |
329 | <button id="CloseButton" type="button" class="btn btn-primary" data-dismiss="modal">Ok</button> | |
330 | </form> | |
331 | </div> | |
332 | </div> | |
333 | </div> | |
334 | </div> | |
335 | ||
336 | <script> | |
337 | let BASE_URL = '<?php echo BASE_URL; ?>'; | |
338 | let chmod_help = document.getElementById('chmod_help'); | |
339 | ||
340 | if (chmod_help) | |
341 | chmod_help.addEventListener('click', e => { | |
342 | window.open("https://www.unrealircd.org/docs/UnrealIRCd_webpanel#Permissions"); | |
343 | }); | |
344 | ||
345 | let page3 = document.getElementById('page3'); | |
346 | let page4 = document.getElementById('page4'); | |
347 | ||
348 | let file_db_radio = document.getElementById('file_db_radio'); | |
349 | let sql_db_radio = document.getElementById('sql_db_radio'); | |
350 | let sql_form = document.getElementById('sql_form'); | |
351 | let sql_host = document.getElementById('sql_host'); | |
352 | let sql_db = document.getElementById('sql_db'); | |
353 | let sql_user = document.getElementById('sql_user'); | |
354 | let sql_pass = document.getElementById('sql_password'); | |
355 | let sql_test_conn = document.getElementById('page3_test_connection'); | |
356 | let page3_next = document.getElementById('page3_next'); | |
357 | ||
358 | let page4_back = document.getElementById('page4_back'); | |
359 | let page4_next = document.getElementById('page4_next'); | |
360 | ||
361 | page4.style.display = 'none'; | |
362 | ||
363 | revalidate_sql = document.querySelectorAll('.revalidation-needed-sql'); | |
364 | for (let i = 0; i < revalidate_sql.length; i++) | |
365 | { | |
366 | revalidate_sql[i].addEventListener('input', e => { | |
367 | page3_next.style.display = 'none'; | |
368 | sql_test_conn.innerHTML = 'Test connection'; | |
369 | sql_test_conn.style.display = ''; | |
370 | sql_test_conn.classList.remove('disabled'); | |
371 | }); | |
372 | } | |
373 | ||
374 | page3_next.addEventListener('click', e => { | |
375 | <?php if (file_exists(UPATH.'/data/database.php')) { ?> | |
376 | $('#db_overwrite_modal').modal(); | |
377 | e.preventDefault(); | |
378 | return false; | |
379 | <?php } ?> | |
380 | page3.style.display = 'none'; | |
381 | page4.style.display = ''; | |
382 | }); | |
383 | ||
384 | file_db_radio.addEventListener('click', e => { | |
385 | if (file_db_radio.checked){ | |
386 | sql_form.style.display = 'none'; | |
387 | sql_test_conn.style.display = 'none'; | |
388 | page3_next.style.display = ''; | |
389 | } | |
390 | }); | |
391 | ||
392 | sql_db_radio.addEventListener('click', e => { | |
393 | if (!file_db_radio.checked){ | |
394 | sql_form.style.display = ''; | |
395 | sql_test_conn.style.display = ''; | |
396 | page3_next.style.display = 'none'; | |
397 | } | |
398 | }); | |
399 | ||
400 | sql_instructions.addEventListener('click', e => { | |
401 | window.open("https://www.unrealircd.org/docs/UnrealIRCd_webpanel#SQL_Authentication"); | |
402 | }); | |
403 | ||
404 | sql_test_conn.addEventListener('click', e => { | |
405 | sql_test_conn.classList.add('disabled'); | |
406 | sql_test_conn.innerHTML = "Checking..."; | |
407 | fetch(BASE_URL + 'api/installation.php', { | |
408 | method:'POST', | |
409 | headers: {'Content-Type':'application/x-www-form-urlencoded'}, | |
410 | body: 'method=sql&'+ | |
411 | 'host='+encodeURIComponent(sql_host.value)+ | |
412 | '&database='+encodeURIComponent(sql_db.value)+ | |
413 | '&user='+encodeURIComponent(sql_user.value)+ | |
414 | '&password='+encodeURIComponent(sql_pass.value)+ | |
415 | '&table_prefix='+encodeURIComponent(sql_table_prefix.value) | |
416 | }) | |
417 | .then(response => response.json()) | |
418 | .then(data => { | |
419 | if (data.success) | |
420 | { | |
421 | nextstep(); | |
422 | } else | |
423 | if (data.warn) | |
424 | { | |
425 | $('#db_overwrite_modal').modal(); | |
426 | } | |
427 | else | |
428 | { | |
429 | sql_test_conn.innerHTML = "Failed!"; | |
430 | $('#db_error_text').html(data.error ? data.error : 'An error occured while connecting to the DB server'); | |
431 | $('#db_error_modal').modal(); | |
432 | setTimeout(function() { | |
433 | sql_test_conn.innerHTML = "Test connection"; | |
434 | sql_test_conn.classList.remove('disabled'); | |
435 | }, 2000); | |
436 | } | |
437 | }) | |
438 | .catch(error => { | |
439 | sql_test_conn.innerHTML = "Failed!"; | |
440 | setTimeout(function() { | |
441 | sql_test_conn.innerHTML = "Test connection"; | |
442 | sql_test_conn.classList.remove('disabled'); | |
443 | }, 2000); | |
444 | }); | |
445 | }); | |
446 | ||
447 | user_name_label = document.getElementById('userlabel'); | |
448 | user_name = document.getElementById('account_user'); | |
449 | user_pass_label = document.getElementById('passlabel'); | |
450 | user_pass = document.getElementById('account_password'); | |
451 | user_pass2_label = document.getElementById('passconflabel'); | |
452 | user_pass2 = document.getElementById('account_password_conf'); | |
453 | user_email_label = document.getElementById('emaillabel'); | |
454 | user_email = document.getElementById('account_email'); | |
455 | user_fname = document.getElementById('account_fname'); | |
456 | user_lname = document.getElementById('account_lname'); | |
457 | user_bio = document.getElementById('account_bio'); | |
458 | ||
459 | page4_back.addEventListener('click', e => { | |
460 | page4.style.display = 'none'; | |
461 | page3.style.display = ''; | |
462 | }); | |
463 | ||
464 | page4_next.addEventListener('click', e => { | |
465 | ||
466 | /* Form validation */ | |
467 | let req_not_met = ' <small style="color:red">Does not meet requirements</small>'; | |
468 | let errs = 0; | |
469 | const regex_username = /^[a-zA-Z\d]{3,}$/; | |
470 | if (!regex_username.test(user_name.value)) | |
471 | { | |
472 | user_name_label.innerHTML = 'Pick a username!' + req_not_met; | |
473 | errs++; | |
474 | } else | |
475 | user_name_label.innerHTML = 'Pick a username!'; | |
476 | ||
477 | let regex_pass = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,}$/; | |
478 | if (!regex_pass.test(user_pass.value)) | |
479 | { | |
480 | user_pass_label.innerHTML = 'Password' + req_not_met; | |
481 | errs++; | |
482 | } else | |
483 | user_pass_label.innerHTML = 'Password'; | |
484 | ||
485 | if (user_pass2.value !== user_pass.value) | |
486 | { | |
487 | user_pass2_label.innerHTML = 'Confirm password <small style="color:red">Passwords do not match</small>'; | |
488 | errs++; | |
489 | } else | |
490 | user_pass2_label.innerHTML = 'Confirm password'; | |
491 | ||
492 | const regex_email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
493 | if (!regex_email.test(user_email.value)) | |
494 | { | |
495 | user_email_label.innerHTML = 'Email address' + req_not_met; | |
496 | errs++; | |
497 | } | |
498 | else | |
499 | user_email_label.innerHTML = 'Email address'; | |
500 | ||
501 | if (errs) | |
502 | { | |
503 | e.preventDefault(); | |
504 | return false; | |
505 | } | |
506 | ||
507 | page4.style.display = 'none'; | |
508 | }); | |
509 | ||
510 | function nextstep() | |
511 | { | |
512 | $('#db_overwrite_modal').modal('hide'); | |
513 | page3.style.display = 'none'; | |
514 | page4.style.display = ''; | |
515 | window.scrollTo(0,0); | |
516 | } | |
517 | </script> |