QuartzMap/admin/class/app.php

500 lines
18 KiB
PHP

<?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;
}
};
?>