<?php const TIME_MAP = array('week' => 604800, 'day' => 86400, 'hour' => 3600, 'minute' => 60, 'off' => 0); class App { public static function rrmdir($dir) { if (is_dir($dir)) { $objects = scandir($dir); foreach ($objects as $object) { if ($object != "." && $object != "..") { if (is_dir($dir. DIRECTORY_SEPARATOR .$object) && !is_link($dir."/".$object)) App::rrmdir($dir. DIRECTORY_SEPARATOR .$object); else unlink($dir. DIRECTORY_SEPARATOR .$object); } } rmdir($dir); } } public static function find_per($cache_seconds){ foreach(TIME_MAP as $per => $val){ if($cache_seconds >= $val){ return $per; } } return 'off'; } public static function parseDatasources($html_dir, $lines = null){ $ds = array(); $ls = array(); $use_dt = false; if($lines == null){ $lines = file($html_dir.'/index.php'); } foreach ($lines as $i => $line) { if(preg_match('/const SHOW_DATATABLES = (.*);/', $line, $matches)){ $use_dt = ($matches[1] == 'true'); }else if(preg_match('/<script src="data_(file|pg|gs)(\d+)\.php" data\-jfn="(.*)"/', $line, $matches)){ $dsi = $matches[2]; $v = array('data_type' => $matches[1], 'line' => $line, 'ln' => $i, 'name' => $matches[3], 'json_filename' => $matches[3], 'pg_host' => '', 'pg_db' => '', 'pg_user' => '', 'pg_pwd' => '', 'pg_port' => 5432, 'gs_host' => '', 'gs_user' => '', 'gs_pwd' => '', 'gs_ws' => '', 'pg_cache_per' => 'minutes', 'pg_cache_val' => '', 'gs_cache_per' => 'minutes', 'gs_cache_val' => ''); $content = null; if($matches[1] == 'pg'){ $content = file_get_contents($html_dir.'/data_pg'.$matches[2].'.php'); if(preg_match('/new Database\("(.*)", "(.*)", "(.*)", "(.*)", (\d+),/', $content, $pg_matches)){ $v['pg_host'] = $pg_matches[1]; $v['pg_db'] = $pg_matches[2]; $v['pg_user'] = $pg_matches[3]; $v['pg_pwd'] = $pg_matches[4]; $v['pg_port'] = $pg_matches[5]; } }else if($matches[1] == 'gs'){ $content = file_get_contents($html_dir.'/data_gs'.$matches[2].'.php'); if(preg_match('/:\/\/(.*):(.*)@(.*)\/geoserver\/.*&typeName=(.*):/', $content, $gs_matches)){ $v['gs_host'] = $matches[1]; $v['gs_user'] = $matches[2]; $v['gs_pwd'] = $matches[3]; $v['gs_ws'] = $matches[4]; } } if($content && preg_match('/\$CACHE_PERIOD = ([0-9]+)/', $content, $matches)) { $cache_seconds = $matches[1]; $per = App::find_per($cache_seconds); $v[$v['data_type'].'_cache_val'] = (TIME_MAP[$per] == 0) ? 0 : $cache_seconds / TIME_MAP[$per]; $v[$v['data_type'].'_cache_per'] = $per; } $ds[$dsi] = $v; }else if(preg_match('/<\?php include\("layer_(wms|gs_geo)(\d+)\.php"\); \?>/', $line, $matches)){ $lyi = $matches[2]; $v = array('layer_type' => $matches[1], 'line' => $line, 'ln' => $i, 'name' => '', 'ly_ws' => '', 'ly_layer' => '', 'ly_user' => '', 'ly_pwd' => '', 'wms_user' => '', 'wms_pwd' => '', 'wms_url' => '', 'gs_geo_host' => '', 'gs_geo_user' => '', 'gs_geo_pwd' => '', 'gs_geo_ws' => '', 'gs_geo_layer' => '', 'gs_geo_color' => '', 'gs_geo_opacity' => '', 'gs_geo_fill_color' => '', 'gs_geo_fill_opacity' => '', 'gs_geo_cache_per' => 'minutes', 'gs_geo_cache_val' => ''); $content = file_get_contents($html_dir.'/layer_wms'.$lyi.'.php'); if(preg_match('/var (.*) = L\.(WMS\.layer|geoJson\()/', $content, $var_matches)){ $v['layer_varname'] = $var_matches[1]; } if(preg_match('/var .* = L\.WMS\.layer\("(.*)", "(.*):(.*)",\s+{/', $content, $matches)){ if(str_starts_with($matches[1], 'proxy_wms')){ // if secured through proxy $content = file_get_contents($html_dir.'/'.$matches[1]); if(preg_match('/const BASE_URL = \'http[s]?:\/\/(.*):(.*)@(.*)\';/', $content, $url_matches)){ $v['wms_user'] = $url_matches[1]; $v['wms_pwd'] = $url_matches[2]; $v['wms_url'] = $url_matches[3]; } }else{ $v['wms_url'] = $matches[1]; } $v['wms_ws'] = $matches[2]; $v['wms_layer'] = $matches[3]; } $v['name'] = $v['wms_ws'].':'.$v['wms_layer']; if(is_file($html_dir.'/layer_gs_geo'.$lyi.'.php')){ $content = file_get_contents($html_dir.'/layer_gs_geo'.$lyi.'.php'); if(preg_match('/:\/\/(.*):(.*)@(.*)\/geoserver\/.*&typeName=(.*):([^&]+)/', $content, $matches)){ $v['gs_geo_host'] = $matches[3]; $v['gs_geo_user'] = $matches[1]; $v['gs_geo_pwd'] = $matches[2]; $v['gs_geo_ws'] = $matches[4]; $v['gs_geo_layer'] = $matches[5]; } if(preg_match('/color: "(.*)",/', $content, $matches)){ $v['gs_geo_color'] = $matches[1]; } if(preg_match('/fillColor: "(.*)",/', $content, $matches)){ $v['gs_geo_fill_color'] = $matches[1]; } if(preg_match('/opacity: ([0-9\.]+),/', $content, $matches)){ $v['gs_geo_opacity'] = $matches[1]; } if(preg_match('/fillOpacity: "([0-9\.]+)",/', $content, $matches)){ $v['gs_geo_fill_opacity'] = $matches[1]; } if(preg_match('/\$CACHE_PERIOD = ([0-9]+)/', $content, $matches)) { $cache_seconds = $matches[1]; $per = App::find_per($cache_seconds); $v['gs_geo_cache_val'] = (TIME_MAP[$per] == 0) ? 0 : $cache_seconds / TIME_MAP[$per]; $v['gs_geo_cache_per'] = $per; } } $ls[$lyi] = $v; } } return [$ds, $ls, $use_dt]; } public static function update_template($src, $dest, $vars){ $lines = file($src); $fp = fopen($dest, 'w'); foreach($lines as $ln => $line){ foreach($vars as $k => $v){ if(str_contains($line, $k)){ $line = str_replace($k, $v, $line); } } fwrite($fp, $line); } fclose($fp); } private static function extract_varname($filename){ // extract varname from first line of data file $js_fp = fopen($filename, 'r'); $line = fread($js_fp, 1024); fclose($js_fp); $eq_pos = strpos($line, '='); $js_varname_decl = substr($line, 0, $eq_pos); // var json_neighborhoods_2 $js_varname = explode(' ', $js_varname_decl)[1]; // json_neighborhoods_2 return $js_varname; } public static function updateDatasources($details, $html_dir, $data_dir, $apps_dir){ $changes = 0; $js_varnames = array(); $lines = file($html_dir.'/index.php'); list($fds,$lys,$use_dt) = App::parseDatasources($html_dir, $lines); // file data sources $newId = $details['id']; foreach($fds as $dsi => $ds) { if( empty($details['data_type'.$dsi])){ // if we have datasource from form continue; } $json_filename = $ds['json_filename']; $js_varname = App::extract_varname($data_dir.'/'.$newId.'/'.$json_filename); if(isset($details['use_datatable'])){ array_push($js_varnames, $js_varname); } // update fds lines if($details['data_type'.$dsi] == 'file'){ $vars = ['MAP_ID_VALUE' => $newId, 'DATA_FILE' => '../../../data/'.$newId.'/'. $json_filename ]; App::update_template('../snippets/data_file.php', $html_dir.'/data_file'.$dsi.'.php', $vars); $lines[$ds['ln']] = '<script src="data_file'.$dsi.'.php" data-jfn="'.$json_filename.'"></script>'."\n"; $changes = $changes + 1; }else{ // extract PG table from filename $pg_tbl = null; if(preg_match('/([a-z]+)/', $json_filename, $matches)){ $pg_tbl = $matches[1]; } if($details['data_type'.$dsi] == 'pg'){ $cache_seconds = TIME_MAP[$details['pg_cache_per'.$dsi]] * intval($details['pg_cache_val'.$dsi]); $vars = [ 'MAP_ID_VALUE' => $newId, 'VARNAME' => $js_varname, 'CACHE_PERIOD_SECONDS' => $cache_seconds, 'PG_HOST' => $details['pg_host'.$dsi], 'PG_DB' => $details['pg_db'.$dsi], 'PG_USER' => $details['pg_user'.$dsi], 'PG_PWD' => $details['pg_pwd'.$dsi], 'PG_PORT' => $details['pg_port'.$dsi], 'PG_TBL' => $pg_tbl ]; App::update_template('../snippets/data_pg.php', $html_dir.'/data_pg'.$dsi.'.php', $vars); $lines[$ds['ln']] = '<script src="data_pg'.$dsi.'.php" data-jfn="'.$json_filename.'"></script>'."\n"; $changes = $changes + 1; }else if($details['data_type'.$dsi] == 'gs'){ $cache_seconds = TIME_MAP[$details['gs_cache_per'.$dsi]] * intval($details['gs_cache_val'.$dsi]); $proto = 'https://'; $url = ''; if(0 === strpos($details['gs_host'.$dsi], 'https://')){ $url = substr($details['gs_host'.$dsi], 8); }else if(0 === strpos($details['gs_host'.$dsi], 'http://')){ $proto = 'http://'; $url = substr($details['gs_host'.$dsi], 7); }else{ $url = $details['gs_host'.$dsi]; //only hostname, no proto } $gs_layer = $pg_tbl; $full_url = $proto.$details['gs_user'.$dsi].":".$details['gs_pwd'.$dsi].'@'.$url. "/geoserver/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=" . $details['gs_ws'.$dsi] . ":" . $gs_layer . "&maxFeatures=3000&outputFormat=application/json"; $vars = ['MAP_ID_VALUE' => $newId, 'VARNAME' => $js_varname, 'CACHE_PERIOD_SECONDS' => $cache_seconds, 'FULL_URL' => $full_url, ]; App::update_template('../snippets/data_gs.php', $html_dir.'/data_gs'.$dsi.'.php', $vars); $lines[$ds['ln']] = '<script src="data_gs'.$dsi.'.php" data-jfn="'.$json_filename.'"></script>'."\n"; $changes = $changes + 1; } } } $sidebar_included = false; foreach($lys as $lyi => $ly) { if( empty($details['layer_type'.$lyi]) ){ // if we have layer from form continue; } // update fds lines if($details['layer_type'.$lyi] == 'wms'){ // update url,ws,layer in WMS file $content = file_get_contents($html_dir.'/layer_wms'.$lyi.'.php'); if(!empty($details['wms_user'.$lyi])){ // if WMS is secured $wms_url = $details['wms_url'.$lyi]; $pos = strpos($wms_url, '://'); $auth_url = substr($wms_url, 0, $pos).'://'.$details['wms_user'.$lyi].':'.$details['wms_pwd'.$lyi].'@'.substr($wms_url, $pos + 3); $vars = [ 'MAP_ID' => $newId, 'BASE_URL_VALUE' => $auth_url ]; App::update_template('../snippets/proxy_wms.php', $html_dir.'/proxy_wms'.$lyi.'.php', $vars); $details['wms_url'.$lyi] = 'proxy_wms'.$lyi.'.php'; } $replacement = ' = L.WMS.layer("'.$details['wms_url'.$lyi].'", "'.$details['wms_ws'.$lyi].':'.$details['wms_layer'.$lyi].'", {'; $content = preg_replace('/ = L\.WMS\.layer\("(.*)", "(.*):(.*)",\s+{/', $replacement, $content); file_put_contents($html_dir.'/layer_wms'.$lyi.'.php', $content); $lines[$ly['ln']] = '<?php include("layer_wms'.$lyi.'.php"); ?>'."\n"; $changes = $changes + 1; }else if($details['layer_type'.$lyi] == 'gs_geo'){ $cache_seconds = TIME_MAP[$details['gs_geo_cache_per'.$lyi]] * intval($details['gs_geo_cache_val'.$lyi]); // add layer varname to show up in datatables array_push($js_varnames, $details['layer_varname'.$lyi].'_data'); $proto = 'https://'; $url = ''; if(0 === strpos($details['gs_geo_host'.$lyi], 'https://')){ $url = substr($details['gs_geo_host'.$lyi], 8); }else if(0 === strpos($details['gs_geo_host'.$lyi], 'http://')){ $proto = 'http://'; $url = substr($details['gs_geo_host'.$lyi], 7); }else{ $url = $details['gs_geo_host'.$lyi]; //only hostname, no proto } $full_url = $proto.$details['gs_geo_user'.$lyi].":".$details['gs_geo_pwd'.$lyi].'@'.$url. "/geoserver/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=" . $details['gs_geo_ws'.$lyi] . ":" . $details['gs_geo_layer'.$lyi] . "&maxFeatures=3000&outputFormat=application/json"; $vars = ['VARNAME' => $details['layer_varname'.$lyi], 'FULL_URL' => $full_url, 'CACHE_PERIOD_SECONDS' => $cache_seconds, 'MAP_ID_VALUE' => $newId, 'STYLE_COLOR' => $details['gs_geo_color'.$lyi], 'STYLE_OPACITY' => $details['gs_geo_opacity'.$lyi], 'STYLE_FILL_COLOR' => $details['gs_geo_fill_color'.$lyi], 'STYLE_FILL_OPACITY' => $details['gs_geo_fill_opacity'.$lyi], ]; App::update_template('../snippets/layer_gs_geo.php', $html_dir.'/layer_gs_geo'.$lyi.'.php', $vars); $line = ''; if(!$sidebar_included){ $line = 'map.addControl(new sidebarControl());'; $sidebar_included = true; // include sidebar once } $line .= '<?php include("layer_gs_geo'.$lyi.'.php"); ?>'."\n"; $lines[$ly['ln']] = $line; $changes = $changes + 1; } } if(isset($details['use_datatable']) != $use_dt){ $changes = $changes + 1; } // update the file if($changes){ $show_dt_updated = false; $fp = fopen($html_dir.'/index.php', 'w'); foreach($lines as $line){ if(!$show_dt_updated){ if(preg_match('/const SHOW_DATATABLES = /', $line, $matches)){ $line = 'const SHOW_DATATABLES = '.(isset($details['use_datatable']) ? 'true' : 'false').';'."\n"; }else if(preg_match('/const JS_VARNAMES = array\(/', $line, $matches)){ $line = 'const JS_VARNAMES = array("'.implode('","', $js_varnames).'");'."\n"; $show_dt_updated = true; } } fwrite($fp, $line); } fclose($fp); } } public static function upload_dir($username){ $ftp_home = shell_exec('grep "^'.$username.':" /etc/passwd | cut -f6 -d:'); $upload_dir = substr($ftp_home, 0, -1); return $upload_dir; } public static function copy_r($source, $target){ if ( is_dir( $source ) ) { @mkdir( $target ); $d = dir( $source ); while ( FALSE !== ( $entry = $d->read() ) ) { if ( $entry == '.' || $entry == '..' ) { continue; } $Entry = $source . '/' . $entry; if ( is_dir( $Entry ) ) { App::copy_r( $Entry, $target . '/' . $entry ); } else { copy( $Entry, $target . '/' . $entry ); } } $d->close(); }else { copy( $source, $target ); } } public static function installApp($newId, $details, $html_dir, $data_dir, $apps_dir){ // move html dir to apps App::copy_r($html_dir, $apps_dir.'/'.$newId); // work in new html dir $html_dir = $apps_dir.'/'.$newId; // index.html -> index.php rename($html_dir.'/index.html', $html_dir.'/index.php'); $auth_lines = file_get_contents('../snippets/index_prefix.php'); $auth_lines = str_replace('MAP_ID', $newId, $auth_lines); if(isset($details['use_datatable'])){ $auth_lines = preg_replace('/const SHOW_DATATABLES = false;/', 'const SHOW_DATATABLES = true;', $auth_lines); } //insert our php auth code above <!doctype html> in index.php $content = file_get_contents($html_dir.'/index.php'); file_put_contents($html_dir.'/index.php', $auth_lines . $content); // data directory outside of /var/www/html to /var/www/data rename($html_dir.'/data', $data_dir.'/'.$newId); // Replace sources to data files $lines = file($html_dir.'/index.php'); $fp = fopen($html_dir.'/index.php', "w"); $no_exec = '<?php if(empty(DB_HOST)){ die("Error: Can\'t execute!"); } ?>'; $di = 0; $li = 0; for($i=0; $i < count($lines); $i++){ $line = $lines[$i]; if(preg_match('/src="data\/(.*)"/', $line, $matches)){ $json_filename = $matches[1]; $vars = [ 'MAP_ID_VALUE' => $newId, 'DATA_FILE' => '../../../data/'.$newId.'/'. $json_filename ]; App::update_template('../snippets/data_file.php', $html_dir.'/data_file'.$di.'.php', $vars); $line = '<script src="data_file'.$di.'.php" data-jfn="'.$json_filename.'"></script>'."\n"; $di = $di + 1; }else if(preg_match('/var (.*) = L\.WMS\.layer\("(.*)", "(.*):(.*)",\s+{/', $line, $matches)){ if(!empty($details['wms_user'.$li])){ // if WMS is secured $wms_url = $matches[2]; $pos = strpos($wms_url, '://'); $auth_url = substr($wms_url, 0, $pos).'://'.$details['wms_user'.$li].':'.$details['wms_pwd'.$li].'@'.substr($wms_url, $pos + 3); $vars = [ 'MAP_ID' => $newId, 'BASE_URL_VALUE' => $auth_url ]; App::update_template('../snippets/proxy_wms.php', $html_dir.'/proxy_wms'.$li.'.php', $vars); $line = str_replace($wms_url, 'proxy_wms'.$li.'.php', $line); } $wms_content = $line; $lb = substr_count($line, '{'); //left brackets $rb = substr_count($line, '}'); //right brackets while($lb > $rb){ $i++; $line = $lines[$i]; $wms_content .= $line; $lb += substr_count($line, '{'); //left brackets $rb += substr_count($line, '}'); //right brackets } file_put_contents($html_dir.'/layer_wms'.$li.'.php', $no_exec."\n".$wms_content); $line = 'var '.$matches[1].' = <?php include("layer_wms'.$li.'.php"); ?>'."\n"; $li = $li + 1; }else if(str_contains($line, 'src="js/leaflet.pattern.js"')){ $line .= '<script src="../../assets/dist/js/sidebar_control.js"></script>'."\n"; $line .= '<script src="../../assets/dist/js/leaflet.browser.print.min.js"></script>'. "\n"; }else if(str_contains($line, 'map.attributionControl.setPrefix')){ $line = file_get_contents('../snippets/datatables_control.php'). "\n".$line; $line = file_get_contents('../snippets/permalink_control.js'). "\n".$line; }else if(str_contains($line, '<body>')){ $line .= file_get_contents('../snippets/index_header.php'); }else if(str_contains($line, '</body>')){ $line = file_get_contents('../snippets/datatables.php')."\n".$line; $line = file_get_contents('../snippets/permalink_modal.html')."\n".$line; }else if(str_contains($line, 'width:')){ if(str_contains($lines[$i-1], '#map {')){ $line = 'width: 100%;'."\n"; } }else if(str_contains($line, 'var measureControl')){ $line = '<?php if($loc) {?>map.flyTo([<?=$loc[1]?>, <?=$loc[2]?>], <?=$loc[0]?>);<?php } ?>'. "\n". $line; $line = file_get_contents('../snippets/print_control.js') . "\n" . $line; } fwrite($fp, $line); } fclose($fp); } public static function uninstallApp($id, $data_dir, $apps_dir){ App::rrmdir($data_dir.'/'.$id); App::rrmdir($apps_dir.'/'.$id); } public static function getApps($apps_dir) { $rv = array(); $entries = scandir($apps_dir); foreach($entries as $e){ if(is_dir($apps_dir.'/'.$e) && !str_starts_with($e, '.')){ array_push($rv, $e); } } return $rv; } }; ?>