index.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. <?php
  2. /* Copyright (C) 2001-2002 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (C) 2006-2013 Laurent Destailleur <eldy@users.sourceforge.net>
  4. * Copyright (C) 2010 Regis Houssin <regis.houssin@inodbox.com>
  5. * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. */
  20. /**
  21. * \file htdocs/public/demo/index.php
  22. * \ingroup core
  23. * \brief Entry page to access demo
  24. */
  25. define("NOLOGIN", 1); // This means this output page does not require to be logged.
  26. define("NOCSRFCHECK", 1); // We accept to go on this page from external web site.
  27. require '../../main.inc.php';
  28. require_once '../../core/lib/functions2.lib.php';
  29. $langs->loadLangs(array("main", "install", "other"));
  30. $conf->dol_hide_topmenu = GETPOST('dol_hide_topmenu', 'int');
  31. $conf->dol_hide_leftmenu = GETPOST('dol_hide_leftmenu', 'int');
  32. $conf->dol_optimize_smallscreen = GETPOST('dol_optimize_smallscreen', 'int');
  33. $conf->dol_no_mouse_hover = GETPOST('dol_no_mouse_hover', 'int');
  34. $conf->dol_use_jmobile = GETPOST('dol_use_jmobile', 'int');
  35. // Security check
  36. global $dolibarr_main_demo;
  37. if (empty($dolibarr_main_demo)) accessforbidden('Parameter dolibarr_main_demo must be defined in conf file with value "default login,default pass" to enable the demo entry page', 0, 0, 1);
  38. // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
  39. $res = $hookmanager->initHooks(array('demo'));
  40. $demoprofiles = array();
  41. $alwayscheckedmodules = array();
  42. $alwaysuncheckedmodules = array();
  43. $alwayshiddencheckedmodules = array();
  44. $alwayshiddenuncheckedmodules = array();
  45. $url = '';
  46. $url .= ($url ? '&' : '').($conf->dol_hide_topmenu ? 'dol_hide_topmenu='.$conf->dol_hide_topmenu : '');
  47. $url .= ($url ? '&' : '').($conf->dol_hide_leftmenu ? 'dol_hide_leftmenu='.$conf->dol_hide_leftmenu : '');
  48. $url .= ($url ? '&' : '').($conf->dol_optimize_smallscreen ? 'dol_optimize_smallscreen='.$conf->dol_optimize_smallscreen : '');
  49. $url .= ($url ? '&' : '').($conf->dol_no_mouse_hover ? 'dol_no_mouse_hover='.$conf->dol_no_mouse_hover : '');
  50. $url .= ($url ? '&' : '').($conf->dol_use_jmobile ? 'dol_use_jmobile='.$conf->dol_use_jmobile : '');
  51. $url = DOL_URL_ROOT.'/index.php'.($url ? '?'.$url : '');
  52. $tmpaction = 'view';
  53. $parameters = array();
  54. $object = new stdClass();
  55. $reshook = $hookmanager->executeHooks('addDemoProfile', $parameters, $object, $tmpaction); // Note that $action and $object may have been modified by some hooks
  56. $error = $hookmanager->error; $errors = $hookmanager->errors;
  57. if (empty($reshook))
  58. {
  59. $demoprofiles = array(
  60. array('default'=>'1', 'key'=>'profdemoservonly', 'label'=>'DemoCompanyServiceOnly',
  61. 'disablemodules'=>'adherent,barcode,bom,cashdesk,don,expedition,externalsite,ftp,incoterm,mailmanspip,margin,mrp,prelevement,product,productbatch,stock,takepos',
  62. //'icon'=>DOL_URL_ROOT.'/public/demo/dolibarr_screenshot8.png',
  63. 'icon'=>DOL_URL_ROOT.'/public/demo/demo-profile-service.jpg',
  64. 'url'=>$url
  65. ),
  66. array('default'=>'0', 'key'=>'profmanufacture', 'label'=>'DemoCompanyManufacturing',
  67. 'disablemodules'=>'adherent,contrat,don,externalsite,ficheinter,ftp,mailmanspip,prelevement,service',
  68. 'icon'=>DOL_URL_ROOT.'/public/demo/demo-profile-manufacturing.jpg',
  69. 'url'=>$url
  70. ),
  71. array('default'=>'0', 'key'=>'profdemoprodstock', 'label'=>'DemoCompanyProductAndStocks',
  72. 'disablemodules'=>'adherent,bom,contrat,don,externalsite,ficheinter,ftp,mailmanspip,mrp,prelevement,service',
  73. //'icon'=>DOL_URL_ROOT.'/public/demo/dolibarr_screenshot2.png',
  74. 'icon'=>DOL_URL_ROOT.'/public/demo/demo-profile-product.jpg',
  75. 'url'=>$url
  76. ),
  77. array('default'=>'0', 'key'=>'profdemofun2', 'label'=>'DemoFundation2',
  78. 'disablemodules'=>'barcode,cashdesk,bom,commande,commercial,compta,comptabilite,contrat,expedition,externalsite,ficheinter,ftp,incoterm,mailmanspip,margin,mrp,prelevement,product,productbatch,projet,propal,propale,service,societe,stock,tax,takepos',
  79. //'icon'=>DOL_URL_ROOT.'/public/demo/dolibarr_screenshot6.png',
  80. 'icon'=>DOL_URL_ROOT.'/public/demo/demo-profile-foundation.jpg',
  81. 'url'=>$url
  82. ),
  83. // All demo profile
  84. array('default'=>'0', 'key'=>'profdemoall', 'label'=>'ChooseYourDemoProfilMore',
  85. 'disablemodules'=>'adherent,cashdesk,don,externalsite,mailmanspip',
  86. //'icon'=>DOL_URL_ROOT.'/public/demo/dolibarr_screenshot9.png'
  87. 'icon'=>DOL_URL_ROOT.'/public/demo/demo-profile-all.jpg'
  88. )
  89. );
  90. // Visible
  91. $alwayscheckedmodules = array('barcode', 'bookmark', 'categorie', 'externalrss', 'fckeditor', 'geoipmaxmind', 'gravatar', 'memcached', 'syslog', 'user', 'webservices'); // Technical module we always want
  92. $alwaysuncheckedmodules = array('dav', 'dynamicprices', 'incoterm', 'loan', 'multicurrency', 'paybox', 'paypal', 'stripe', 'google', 'printing', 'scanner', 'skype', 'website'); // Module we dont want by default
  93. // Not visible
  94. $alwayshiddencheckedmodules = array('accounting', 'api', 'barcode', 'blockedlog', 'bookmark', 'clicktodial', 'comptabilite', 'cron', 'document', 'domain', 'externalrss', 'externalsite', 'fckeditor', 'geoipmaxmind', 'gravatar', 'label', 'ldap',
  95. 'mailmanspip', 'notification', 'oauth', 'syslog', 'user', 'webservices', 'workflow',
  96. // Extended modules
  97. 'memcached', 'numberwords', 'zipautofillfr');
  98. $alwayshiddenuncheckedmodules = array('dav', 'debugbar', 'emailcollector', 'ftp', 'hrm', 'modulebuilder', 'printing', 'webservicesclient',
  99. // Extended modules
  100. 'awstats', 'bittorrent', 'bootstrap', 'cabinetmed', 'cmcic', 'concatpdf', 'customfield', 'deplacement', 'dolicloud', 'filemanager', 'lightbox', 'mantis', 'monitoring', 'moretemplates', 'multicompany', 'nltechno', 'numberingpack', 'openstreetmap',
  101. 'ovh', 'phenix', 'phpsysinfo', 'pibarcode', 'postnuke', 'selectbank', 'skincoloreditor', 'submiteverywhere', 'survey', 'thomsonphonebook', 'topten', 'tvacerfa', 'voyage', 'webcalendar', 'webmail');
  102. }
  103. // Search modules
  104. $dirlist = $conf->file->dol_document_root;
  105. // Search modules dirs
  106. $modulesdir = dolGetModulesDirs();
  107. $filename = array();
  108. $modules = array();
  109. $orders = array();
  110. $categ = array();
  111. $dirmod = array();
  112. $i = 0; // is a sequencer of modules found
  113. $j = 0; // j is module number. Automatically affected if module number not defined.
  114. foreach ($modulesdir as $dir)
  115. {
  116. // Charge tableaux modules, nom, numero, orders depuis repertoire dir
  117. $handle = @opendir($dir);
  118. if (is_resource($handle))
  119. {
  120. while (($file = readdir($handle)) !== false)
  121. {
  122. //print "$i ".$file."\n<br>";
  123. if (is_readable($dir.$file) && substr($file, 0, 3) == 'mod' && substr($file, dol_strlen($file) - 10) == '.class.php')
  124. {
  125. $modName = substr($file, 0, dol_strlen($file) - 10);
  126. if ($modName)
  127. {
  128. try
  129. {
  130. include_once $dir.$file;
  131. $objMod = new $modName($db);
  132. if ($objMod->numero > 0)
  133. {
  134. $j = $objMod->numero;
  135. }
  136. else
  137. {
  138. $j = 1000 + $i;
  139. }
  140. $modulequalified = 1;
  141. // We discard modules according to features level (PS: if module is activated we always show it)
  142. $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
  143. if ($objMod->version == 'development' && $conf->global->MAIN_FEATURES_LEVEL < 2 && empty($conf->global->$const_name)) $modulequalified = 0;
  144. if ($objMod->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1 && empty($conf->global->$const_name)) $modulequalified = 0;
  145. if ($modulequalified)
  146. {
  147. $modules[$i] = $objMod;
  148. $filename[$i] = $modName;
  149. $orders[$i] = $objMod->family."_".$j; // Tri par famille puis numero module
  150. //print "x".$modName." ".$orders[$i]."\n<br>";
  151. $dirmod[$i] = $dirroot;
  152. $j++;
  153. $i++;
  154. }
  155. }
  156. catch (Exception $e)
  157. {
  158. dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR);
  159. }
  160. }
  161. }
  162. }
  163. }
  164. }
  165. asort($orders);
  166. //var_dump($orders);
  167. /*
  168. * Actions
  169. */
  170. if (GETPOST('action', 'aZ09') == 'gotodemo') // Action run when we click on "Start" after selection modules
  171. {
  172. //print 'ee'.GETPOST("demochoice");
  173. $disablestring = '';
  174. // If we disable modules using a profile choice
  175. if (GETPOST("demochoice"))
  176. {
  177. foreach ($demoprofiles as $profilearray)
  178. {
  179. if ($profilearray['key'] == GETPOST("demochoice"))
  180. {
  181. $disablestring = $profilearray['disablemodules'];
  182. break;
  183. }
  184. }
  185. }
  186. // If we disable modules using personalized list
  187. foreach ($modules as $val)
  188. {
  189. $modulekeyname = strtolower($val->name);
  190. if (empty($_POST[$modulekeyname]) && empty($val->always_enabled) && !in_array($modulekeyname, $alwayscheckedmodules))
  191. {
  192. $disablestring .= $modulekeyname.',';
  193. if ($modulekeyname == 'propale') $disablestring .= 'propal,';
  194. }
  195. }
  196. // Do redirect to login page
  197. if ($disablestring)
  198. {
  199. if (GETPOST('urlfrom')) $url .= (preg_match('/\?/', $url) ? '&amp;' : '?').'urlfrom='.urlencode(GETPOST('urlfrom', 'alpha'));
  200. $url .= (preg_match('/\?/', $url) ? '&amp;' : '?').'disablemodules='.$disablestring;
  201. //var_dump($url);exit;
  202. header("Location: ".$url);
  203. exit;
  204. }
  205. }
  206. /*
  207. * View
  208. */
  209. $head = '';
  210. $head .= '<meta name="keywords" content="demo,online,demonstration,example,test,erp,crm,demos,web">'."\n";
  211. $head .= '<meta name="description" content="Dolibarr ERP and CRM demo. You can test here several profiles for Dolibarr ERP and CRM demonstration.">'."\n";
  212. $head .= '
  213. <script type="text/javascript">
  214. var openedId="";
  215. jQuery(document).ready(function () {
  216. jQuery("tr.moduleline").hide();
  217. // Enable this to allow personalized setup
  218. jQuery(".modulelineshow").attr("href","#a1profdemoall");
  219. jQuery(".cursorpointer").css("cursor","pointer");
  220. jQuery(".modulelineshow").click(function() {
  221. var idstring=$(this).attr("id");
  222. if (typeof idstring != "undefined")
  223. {
  224. var currentId = idstring.substring(2);
  225. jQuery("tr.moduleline").hide();
  226. if (currentId != openedId)
  227. {
  228. openedId=currentId;
  229. jQuery("#tr1"+currentId).show();
  230. jQuery("#tr2"+currentId).show();
  231. }
  232. else openedId = "";
  233. }
  234. });
  235. });
  236. </script>';
  237. llxHeaderVierge($langs->trans("DolibarrDemo"), $head);
  238. print "\n";
  239. print '<div class="demoban demobackground">';
  240. print '<div class="right" style="padding-right: 30px; padding-top: 30px;">';
  241. print '<a alt="Official portal of your ERP CRM application" targe="_blank" href="https://www.dolibarr.org"><img class="demologo" src="'.DOL_URL_ROOT.'/theme/dolibarr_logo.svg" alt="Dolibarr logo"></a>';
  242. print '</div>';
  243. print '</div>';
  244. print '<div class="demobantext" style="max-width: 1024px;">';
  245. print '<div style="font-size: 20px; padding: 40px;">';
  246. print '<div class="hideonsmartphone" style="text-align: justify;">'.$langs->trans("DemoDesc").'</div><br>';
  247. print '<div class="titre"><font style="font-size: 20px">'.$langs->trans("ChooseYourDemoProfil").'</font></div>';
  248. print '</div>';
  249. print '</div>';
  250. print '<div class="clearboth"></div>';
  251. print '<div class="demobanbox">';
  252. $i = 0;
  253. foreach ($demoprofiles as $profilearray)
  254. {
  255. if ($profilearray['default'] >= 0)
  256. {
  257. //print $profilearray['lang'];
  258. if (!empty($profilearray['lang'])) $langs->load($profilearray['lang']);
  259. $url = $_SERVER["PHP_SELF"].'?action=gotodemo';
  260. $urlwithmod = $url.'&amp;demochoice='.$profilearray['key'];
  261. // Should work with DOL_URL_ROOT='' or DOL_URL_ROOT='/dolibarr'
  262. //print "xx".$_SERVER["PHP_SELF"].' '.DOL_URL_ROOT.'<br>';
  263. $urlfrom = preg_replace('/^'.preg_quote(DOL_URL_ROOT, '/').'/i', '', $_SERVER["PHP_SELF"]);
  264. //print $urlfrom;
  265. if (!empty($profilearray['url']))
  266. {
  267. $urlwithmod = $profilearray['url'];
  268. $urlwithmod = $urlwithmod.(preg_match('/\?/', $urlwithmod) ? '&amp;' : '?').'urlfrom='.urlencode($urlfrom);
  269. if (!empty($profilearray['disablemodules']))
  270. {
  271. $urlwithmod = $urlwithmod.(preg_match('/\?/', $urlwithmod) ? '&amp;' : '?').'disablemodules='.$profilearray['disablemodules'];
  272. }
  273. }
  274. if (empty($profilearray['url']))
  275. {
  276. print '<div class="clearboth"></div>';
  277. }
  278. print '<form method="POST" class="valigntop inline-block" name="form'.$profilearray['key'].'" id="form'.$profilearray['key'].'" action="'.$_SERVER["PHP_SELF"].'#a1'.$profilearray['key'].'">'."\n";
  279. print '<input type="hidden" name="action" value="gotodemo">'."\n";
  280. print '<input type="hidden" name="urlfrom" value="'.dol_escape_htmltag($urlfrom).'">'."\n";
  281. print '<input type="hidden" name="token" value="'.newToken().'">'."\n";
  282. print '<input type="hidden" name="username" value="demo">'."\n";
  283. print '<input type="hidden" name="dol_hide_topmenu" value="'.$conf->dol_hide_topmenu.'">'."\n";
  284. print '<input type="hidden" name="dol_hide_leftmenu" value="'.$conf->dol_hide_leftmenu.'">'."\n";
  285. print '<input type="hidden" name="dol_optimize_smallscreen" value="'.$conf->dol_optimize_smallscreen.'">'."\n";
  286. print '<input type="hidden" name="dol_no_mouse_hover" value="'.$conf->dol_no_mouse_hover.'">'."\n";
  287. print '<input type="hidden" name="dol_use_jmobile" value="'.$conf->dol_use_jmobile.'">'."\n";
  288. print '<div id="div'.$profilearray['key'].'" summary="Dolibarr online demonstration for profile '.$profilearray['label'].'" class="center inline-block CTable CTableRow'.($i % 2 == 0 ? '1' : '0').'">'."\n";
  289. print '<div id="a1'.$profilearray['key'].'" class="demobox '.(empty($profilearray['url']) ? 'modulelineshow cursorpointer' : 'nomodulelines').'">';
  290. print '<a href="'.$urlwithmod.'" class="'.(empty($profilearray['url']) ? 'modulelineshow' : 'nomodulelines').'">';
  291. print '<div style="padding: 10px;">';
  292. print '<img class="demothumb" src="'.$profilearray['icon'].'" alt="Demo '.$profilearray['label'].'">';
  293. print '<div class="clearboth"></div>';
  294. print '<div class="demothumbtext">';
  295. print $langs->trans($profilearray['label']);
  296. print '</div>';
  297. print '</div>';
  298. print '</a>';
  299. // Modules (a profile you must choose modules)
  300. if (empty($profilearray['url']))
  301. {
  302. print '<div id="tr1'.$profilearray['key'].'" class="moduleline hidden" style="margin-left: 8px; margin-right: 8px; text-align: justify; font-size:14px; line-height: 130%; padding-bottom: 8px">';
  303. print $langs->trans("ThisIsListOfModules").'<br><br>';
  304. print '<div class="csscolumns">';
  305. $listofdisabledmodules = explode(',', $profilearray['disablemodules']);
  306. $j = 0;
  307. $nbcolsmod = empty($conf->dol_optimize_smallscreen) ? 4 : 3;
  308. //var_dump($modules);
  309. foreach ($orders as $index => $key) // Loop on qualified (enabled) modules
  310. {
  311. //print $index.' '.$key;
  312. $val = $modules[$index];
  313. $modulekeyname = strtolower($val->name);
  314. $modulequalified = 1;
  315. if (!empty($val->always_enabled) || in_array($modulekeyname, $alwayshiddenuncheckedmodules)) $modulequalified = 0;
  316. if ($val->version == 'development' && $conf->global->MAIN_FEATURES_LEVEL < 2 && !$conf->global->$const_name) $modulequalified = 0;
  317. if ($val->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1 && !$conf->global->$const_name) $modulequalified = 0;
  318. if (!$modulequalified) continue;
  319. if (in_array($modulekeyname, $alwayshiddencheckedmodules))
  320. {
  321. print "\n".'<!-- Module '.$modulekeyname.' hidden and always checked -->';
  322. print '<input type="hidden" name="'.$modulekeyname.'" value="1">';
  323. }
  324. else
  325. {
  326. $modulo = ($j % $nbcolsmod);
  327. //if ($modulo == 0) print '<tr>';
  328. print '<!-- id='.$val->numero.' -->';
  329. print '<div class="nowrap">';
  330. print '<input type="checkbox" class="checkbox" id="id'.$modulekeyname.'" name="'.$modulekeyname.'" value="1" title="'.dol_escape_htmltag($val->getName()).'"';
  331. if (in_array($modulekeyname, $alwaysuncheckedmodules)) print ' disabled';
  332. if (!in_array($modulekeyname, $alwaysuncheckedmodules) && (!in_array($modulekeyname, $listofdisabledmodules) || in_array($modulekeyname, $alwayscheckedmodules))) print ' checked';
  333. print '> <label for="id'.$modulekeyname.'" class="inline-block demomaxoveflow" title="'.dol_escape_htmltag($val->getName()).'">'.$val->getName().'</label><br>';
  334. print '</div>';
  335. //if ($modulo == ($nbcolsmod - 1)) print '</tr>';
  336. $j++;
  337. }
  338. }
  339. print '</div>';
  340. print '<br><div class="center">';
  341. print '<input type="submit" value=" &nbsp; &nbsp; '.$langs->trans("Start").' &nbsp; &nbsp; " class="button">';
  342. print '</div>';
  343. print '</div>';
  344. }
  345. print '</div></div>';
  346. print '</form>'."\n";
  347. $i++;
  348. }
  349. }
  350. print '</div>';
  351. // TODO Replace this with a hook
  352. // Google Adsense (need Google module)
  353. if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AD_CLIENT) && !empty($conf->global->MAIN_GOOGLE_AD_SLOT))
  354. {
  355. if (empty($conf->dol_use_jmobile))
  356. {
  357. print '<div align="center">'."\n";
  358. print '<script type="text/javascript"><!--'."\n";
  359. print 'google_ad_client = "'.$conf->global->MAIN_GOOGLE_AD_CLIENT.'";'."\n";
  360. print 'google_ad_slot = "'.$conf->global->MAIN_GOOGLE_AD_SLOT.'";'."\n";
  361. print 'google_ad_width = '.$conf->global->MAIN_GOOGLE_AD_WIDTH.';'."\n";
  362. print 'google_ad_height = '.$conf->global->MAIN_GOOGLE_AD_HEIGHT.';'."\n";
  363. print '//-->'."\n";
  364. print '</script>'."\n";
  365. print '<script type="text/javascript"'."\n";
  366. print 'src="http://pagead2.googlesyndication.com/pagead/show_ads.js">'."\n";
  367. print '</script>'."\n";
  368. print '</div>'."\n";
  369. }
  370. else
  371. {
  372. print '<!-- google js advert tag disabled with jmobile -->'."\n";
  373. }
  374. }
  375. llxFooterVierge();
  376. $db->close();
  377. /**
  378. * Show header for demo
  379. *
  380. * @param string $title Title
  381. * @param string $head Head string
  382. * @return void
  383. */
  384. function llxHeaderVierge($title, $head = "")
  385. {
  386. global $user, $conf, $langs;
  387. top_httphead();
  388. top_htmlhead($head, $title, 0, 0, array(), array('public/demo/demo.css'));
  389. print '<body class="demobody"><div class="demobackgrounddiv">'."\n";
  390. }
  391. /**
  392. * Show footer for demo
  393. *
  394. * @return void
  395. */
  396. function llxFooterVierge()
  397. {
  398. printCommonFooter('public');
  399. print "\n";
  400. print "</div></body>\n";
  401. print "</html>\n";
  402. }