initial commit

This commit is contained in:
AcuGIS 2024-03-17 01:03:30 +02:00
parent 79efa2b06b
commit ab0389da57
94 changed files with 37964 additions and 0 deletions

330
admin/access_groups.php Normal file
View File

@ -0,0 +1,330 @@
<?php
session_start();
require('incl/const.php');
require('class/database.php');
require('class/user.php');
require('class/access_groups.php');
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$dbconn = $database->getConn();
$acc_obj = new access_group_Class($dbconn, $_SESSION['user']->id);
$rows = $acc_obj->getRows();
$obj = new user_Class($dbconn, $_SESSION['user']->id);
$users = $obj->getRowsArr();
// admin is owned by super admin, so we have to add him to list
$users[$_SESSION['user']->id] = $_SESSION['user']->name;
if(!isset($_SESSION['user']) || $_SESSION['user']->accesslevel != 'Admin') {
header('Location: ../login.php');
exit;
}
?>
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<?php include("incl/meta.php"); ?>
<link href="dist/css/table.css" rel="stylesheet">
<script type="text/javascript">
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
var actions = `
<a class="add" title="Add" data-toggle="tooltip">
<i class="material-icons">&#xE03B;</i>
</a>
<a class="edit" title="Edit" data-toggle="tooltip">
<i class="material-icons">&#xE254;</i>
</a>
<a class="delete" title="Delete" data-toggle="tooltip">
<i class="material-icons">&#xE872;</i>
</a>
`;
//$("table td:last-child").html();
// Append table with add row form on add new button click
$(".add-new").click(function() {
//var actions = $("table td:last-child").html();
$(this).attr("disabled", "disabled");
var index = $("table tbody tr:last-child").index();
var row = '<tr>';
$("table thead tr th").each(function(k, v) {
if($(this).attr('data-editable') == 'false') {
if($(this).attr('data-action') == 'true') { // last child or actions cell
row += '<td>'+actions+'</td>';
}
else {
row += '<td></td>';
}
}
else {
if($(this).attr('data-type') == 'select') {
if($(this).attr('data-name') == 'userids') {
row += `
<td data-type="select" data-value="0">
<select name="`+$(this).attr('data-name')+`" multiple>
<?PHP foreach($users as $k => $v) { ?>
`+
`<option value="<?=$k?>"><?='('.$k.')'.$v?></option>`
+`
<?PHP } ?>
</select>
</td>
`;
}
}
else {
row += ' <td> <input type = "text" class = "form-control" name="'+$(this).attr('data-name')+'"> </td>';
}
}
});
row += '</tr>';
$("table").append(row);
$("table tbody tr").eq(index + 1).find(".add, .edit").toggle();
$('[data-toggle="tooltip"]').tooltip();
});
// Add row on add button click
$(document).on("click", ".add", function() {
var obj = $(this);
var empty = false;
var input = $(this).parents("tr").find('input[type="text"], select');
input.each(function() {
if (!$(this).val()) {
$(this).addClass("error");
empty = true;
} else {
$(this).removeClass("error");
}
});
$(this).parents("tr").find(".error").first().focus();
if (!empty) {
var data = {};
data['save'] = 1;
data['id'] = $(this).closest('tr').attr('data-id');
input.each(function() {
if($(this).closest('td').attr('data-type') == 'select') {
var val = $(this).find('option:selected').text();
$(this).parent("td").attr('data-value', $(this).val());
$(this).parent("td").html(val);
}
else {
$(this).parent("td").html($(this).val());
}
data[$(this).attr('name')] = $(this).val();
});
$.ajax({
type: "POST",
url: 'action/access_groups.php',
data: data,
dataType:"json",
success: function(response){
if(response.id) { // means, new record is added
obj.closest('table').find('tr:last-child').attr('data-id', response.id);
obj.closest('table').find('tr:last-child td:first-child').text(response.id)
}
alert(response.message)
}
});
$(this).parents("tr").find(".add, .edit").toggle();
$(".add-new").removeAttr("disabled");
}
});
// Edit row on edit button click
$(document).on("click", ".edit", function() {
$(this).parents("tr").find("td:not([data-editable=false])").each(function(k, v) {
if($(this).closest('table').find('thead tr th').eq(k).attr('data-editable') != 'false') {
var name = $(this).closest('table').find('thead tr th').eq(k).attr('data-name');
var id = $(this).closest('tr').attr('data-id');
if($(this).closest('table').find('thead tr th').eq(k).attr('data-type') == 'select') {
if(name == 'userids') {
$(this).html(`
<select name="`+name+`" multiple>
<?PHP foreach($users as $k => $v) { ?>
<option value="<?=$k?>"><?='('.$k.')'.$v?></option>
<?PHP } ?>
</select>
`);
}
var val = $(this).attr('data-value').split(',');
$(this).find('[name='+name+']').val(val);
}
else {
$(this).html(' <input type = "text" name="'+ name +'" class = "form-control" value = "' + $(this).text() + '" > ');
}
}
});
$(this).parents("tr").find(".add, .edit").toggle(); $(".add-new").attr("disabled", "disabled");
});
// Delete row on delete button click
$(document).on("click", ".delete", function() {
var obj = $(this);
var data = {'delete': true, 'id': obj.parents("tr").attr('data-id')}
$.ajax({
type: "POST",
url: 'action/access_groups.php',
data: data,
dataType:"json",
success: function(response){
if(response.success) { // means, new record is added
obj.parents("tr").remove();
}
$(".add-new").removeAttr("disabled");
alert(response.message);
}
});
});
});
</script>
</head>
<body>
<div id="main-wrapper" data-layout="vertical" data-navbarbg="skin5" data-sidebartype="full"
data-sidebar-position="absolute" data-header-position="absolute" data-boxed-layout="full">
<?php const MENU_SEL = 'access_groups.php';
include("incl/topbar.php");
include("incl/sidebar.php");
?>
<div class="page-wrapper">
<div class="page-breadcrumb" style="padding-left:30px; padding-right: 30px; padding-top:0px; padding-bottom: 0px">
<div class="row align-items-center">
<div class="col-6">
<nav aria-label="breadcrumb">
</nav>
<h1 class="mb-0 fw-bold">Access Groups</h1>
</div>
<div class="col-6">
<div class="text-end upgrade-btn">
<!--<a href="https://www.wrappixel.com/templates/flexy-bootstrap-admin-template/" class="btn btn-primary text-white"
target="_blank">Add New</a>-->
<button type="button" class="btn btn-primary text-white add-new">
<i class="fa fa-plus"></i> Add New </button><br>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<table class="table table-bordered">
<thead>
<tr>
<th data-name="id" data-editable='false'>ID</th>
<th data-name="name">Name</th>
<th data-name="userids" data-type="select">Users</th>
<th data-editable='false' data-action='true'>Actions</th>
</tr>
</thead>
<tbody> <?php while($row = pg_fetch_object($rows)): ?> <tr data-id="<?=$row->id?>" align="left">
<td><?=$row->id?></td>
<td><?= $row->name?></td>
<?php
$grp_usrs = $acc_obj->getGroupUsers(array($row->id));
$grp_id_usrs = array();
foreach($grp_usrs as $id => $name){
array_push($grp_id_usrs, '('.$id.')'.$name);
}
?>
<td data-type="select" data-value="<?=implode(',',array_keys($grp_usrs))?>"><?=implode(', ',$grp_id_usrs)?></td>
<td>
<a class="add" title="Add" data-toggle="tooltip"> <i class="material-icons">&#xE03B;</i></a>
<a class="edit" title="Edit" data-toggle="tooltip"> <i class="material-icons">&#xE254;</i></a>
<a class="delete" title="Delete" data-toggle="tooltip"> <i class="material-icons">&#xE872;</i></a>
</td>
</tr> <?php endwhile; ?> </tr>
</tbody>
</table>
<div class="row">
<div class="col-12">
<div class="col-6">
<p>&nbsp;</p>
<div id = "repThumbnail" class = "alert alert-warning">
<a href = "#" class = "close" data-dismiss = "alert">&times;</a>
<strong>Note:</strong> Username is prefixed with its ID.
</div>
<script type = "text/javascript">
$(function(){
$(".close").click(function(){
$("#repThumbnail").alert();
});
});
</script>
</div>
<div class="row">
</div>
</div>
</div>
</div>
<footer class="footer text-center">
</footer>
</div>
</div>
<!--Menu sidebar -->
<script src="dist/js/sidebarmenu.js"></script>
<!--Custom JavaScript -->
<script src="dist/js/custom.js"></script>
</body>
</html>

View File

@ -0,0 +1,40 @@
<?php
session_start();
require('../incl/const.php');
require('../class/database.php');
require('../class/access_groups.php');
$result = ['success' => false, 'message' => 'Error while processing your request!'];
if(isset($_SESSION['user']) && $_SESSION['user']->accesslevel == 'Admin') {
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$obj = new access_group_Class($database->getConn(), $_SESSION['user']->id);
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
if(($id > 0) && !$obj->isOwnedByUs($id)){
$result = ['success' => false, 'message' => 'Action not allowed!'];
}else if(isset($_POST['save'])) {
$newId = 0;
if($id) { // update
$obj->update($_POST);
}
else { // insert
$newId = $obj->create($_POST);
}
$result = ['success' => true, 'message' => 'Data Successfully Saved!', 'id' => $newId];
} else if(isset($_POST['delete']) && ($id != 1)) {
if($obj->delete($id)){
$result = ['success' => true, 'message' => 'Group Successfully Deleted!'];
}else{
$result = ['success' => false,'message' => 'Failed to delete group!'];
}
}
}
echo json_encode($result);
?>

24
admin/action/login.php Normal file
View File

@ -0,0 +1,24 @@
<?php
require('../incl/const.php');
require('../class/database.php');
require('../class/user.php');
session_start();
if(isset($_SESSION['user'])) {
header("Location: ../../index.php");
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$user_obj = new user_Class($database->getConn(), 0);
if(isset($_POST['submit'])&&!empty($_POST['submit'])){
$row = $user_obj->loginCheck($_POST['pwd'], $_POST['email']);
if($row){
$_SESSION['user'] = $row;
header("Location: ../../index.php");
}else{
header("Location: ../../login.php?err=".urlencode('Error: Failed to login!'));
}
}
?>

165
admin/action/map.php Normal file
View File

@ -0,0 +1,165 @@
<?php
session_start();
require('../incl/const.php');
require('../class/database.php');
require('../class/map.php');
require('../class/app.php');
function unzip_me($zipname){
$ext_dir = '/tmp/uploads';
if(!is_dir($ext_dir)){
mkdir($ext_dir);
}
$zip = new ZipArchive;
$res = $zip->open($zipname);
if ($res === TRUE) {
$zip->extractTo($ext_dir);
$zip->close();
} else {
echo 'Error: Failed to open'.$zipname;
}
return $ext_dir;
}
function zip2html_dir($upload, $upload_dir){
$unzip_dir = unzip_me($upload["tmp_name"]);
$name = basename($upload["name"]);
$name = explode('.', $name)[0];
if(is_file($unzip_dir.'/index.html')){
$html_dir = $upload_dir.'/'.$name;
rename($unzip_dir, $html_dir);
}else if(is_file($unzip_dir.'/'.$name.'/index.html')){
$html_dir = $unzip_dir.'/'.$name;
}else{
echo 'Error: index.html not found';
$html_dir = null;
}
return $html_dir;
}
$result = ['success' => false, 'message' => 'Error while processing your request!'];
if(isset($_SESSION['user']) && $_SESSION['user']->accesslevel == 'Admin') {
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$obj = new map_Class($database->getConn(), $_SESSION['user']->id);
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
if(($id > 0) && !$obj->isOwnedByUs($id)){
$result = ['success' => false, 'message' => 'Action not allowed!'];
}else if(isset($_POST['save'])) {
$newId = 0;
if($id) { // update
$newId = $obj->update($_POST) ? $id : 0;
if($newId > 0){
$html_dir = APPS_DIR.'/'.$newId;
App::updateDatasources($_POST, $html_dir, DATA_DIR, APPS_DIR);
}
} else if(!empty($_POST['app']) || !empty($_FILES['archive'])){ // insert
$newId = $obj->create($_POST);
if(!is_dir(CACHE_DIR.'/'.$newId)){
mkdir(CACHE_DIR.'/'.$newId, 0770);
}
if($newId > 0){
$upload_dir = App::upload_dir($_SESSION['user']->ftp_user);
$html_dir = null;
$unzip_dir = null;
// html dir can be in /var/www/upload or in /tmp, if its an upload
if(isset($_POST['app'])){
$html_dir = $upload_dir.'/'.$_POST['app'];
}else if(!empty($_FILES["archive"]["tmp_name"])){ // if we have uploaded file
$html_dir = zip2html_dir($_FILES["archive"], $upload_dir);
}
if($html_dir){
App::installApp($newId, $_POST, $html_dir, DATA_DIR, APPS_DIR); // process map data files
if($unzip_dir){
App::rrmdir($unzip_dir);
}
}else{
$obj->delete($newId);
$newId = 0;
}
}
}
if($newId > 0){
if(isset($_FILES["image"]) && ($_FILES['image']['size'] < 10485760)){ // if image file and is less than 10 MB
$image = null;
// scale image to 200x150
if($_FILES["image"]["type"] == 'image/png'){
$image = imagecreatefrompng($_FILES["image"]["tmp_name"]);
}else if($_FILES["image"]["type"] == 'image/jpeg'){
$image = imagecreatefromjpeg($_FILES["image"]["tmp_name"]);
}
if($image){
$imgResized = imagescale($image , 200, 150);
imagepng($imgResized, "../../assets/maps/".$newId.'.png');
}
}
$result = ['success' => true, 'message' => 'Map successfully created!', 'id' => $newId];
}else{
$result = ['success' => false, 'message' => 'Failed to save Map!'];
}
} else if(isset($_POST['delete'])) {
$result = $obj->getById($_POST['id']);
$row = pg_fetch_assoc($result);
pg_free_result($result);
if($obj->delete(intval($_POST['id']))){
App::uninstallApp($row['name'], DATA_DIR, APPS_DIR);
$result = ['success' => true, 'message' => 'Data Successfully Deleted!'];
}else{
$result = ['success' => false, 'message' => 'Error: Data Not Deleted!'];
}
} else if(isset($_POST['clear'])) {
$map_cache_dir = CACHE_DIR.'/'.$_POST['id'];
if(is_dir($map_cache_dir)){
$dir_size = 0;
$files = scandir($map_cache_dir);
foreach($files as $f){
if(is_file($map_cache_dir.'/'.$f)){
$dir_size += filesize($map_cache_dir.'/'.$f);
unlink($map_cache_dir.'/'.$f);
}
}
rmdir($map_cache_dir);
$unit = 'bytes';
if($dir_size > (1024*1024)){
$dir_size = $dir_size / (1024*1024);
$unit = 'Mbytes';
} else if($dir_size > 1024){
$dir_size = $dir_size / 1024;
$unit = 'kbytes';
}
$result = ['success' => true, 'message' => 'Successfully removed '.sprintf("%.2f %s", $dir_size, $unit)];
}else{
$result = ['success' => false, 'message' => 'Error: No cache!'];
}
}
}
echo json_encode($result);
?>

View File

@ -0,0 +1,79 @@
<?php
session_start();
require('../incl/const.php');
require('../class/database.php');
require('../class/map.php');
require('../class/permalink.php');
$result = ['success' => false, 'message' => 'Error while processing your request!'];
if(isset($_SESSION['user']) && $_SESSION['user']->accesslevel == 'Admin') {
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$obj = new permalink_Class($database->getConn(), $_SESSION['user']->id);
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
if($_SERVER['REQUEST_METHOD'] == 'GET'){ // if called from map directly
// make a 1 visit permalink, expiring in 1 hour
if(!empty($_GET['permalink'])){
$row = $obj->getMap($_GET['permalink']);
$_POST['map_id'] = $row['map_id'];
$_POST['query'] = str_replace('permalink='.$_GET['permalink'].'&', '', $_SERVER['QUERY_STRING']);
}else{
$_POST['map_id'] = $_GET['id'];
$_POST['query'] = $_GET['loc'];
}
$_POST['description'] = 'Permalink for map '.$_POST['map_id'];
$_POST['visits_limit'] = 1;
$_POST['expires'] = "1 hour";
$_POST['save'] = true;
}
if(($id > 0) && !$obj->isOwnedByUs($id)){
$result = ['success' => false, 'message' => 'Action not allowed!'];
}else if(isset($_POST['save'])) {
$newId = 0;
if(isset($_POST['id'])) { // update
if($obj->update($_POST)){
$result = ['success' => true, 'message' => 'Permalink Successfully Updated!'];
}else{
$result = ['success' => false, 'message' => 'Permalink Not Updated!'];
}
} else { // insert
$map_obj = new map_Class($database->getConn(), $_SESSION['user']->id);
$res = $map_obj->getById($_POST['map_id']);
$map_row = pg_fetch_assoc($res);
$hash_data = $_SERVER['HTTP_USER_AGENT'].date('m/d/Y h:i:s a', time());
$hash_data .= $_POST['map_id'].$_POST['description'].$_POST['expires'].$_POST['visits_limit'];
$_POST['hash'] = hash('md5', $hash_data);
list($newId,$created,$expires) = $obj->create($_POST);
if($newId > 0){
$perma_url = 'apps/'. $_POST['map_id'].'/index.php?permalink='.$_POST['hash'];
$result = ['success' => true, 'message' => 'Data Successfully Saved!',
'id' => $newId, 'hash' => $_POST['hash'], 'url'=> $perma_url,'created'=>$created, 'expires'=>$expires ];
}else{
$result = ['success' => false, 'message' => 'Data Not Saved!'];
}
}
} else if(isset($_POST['delete'])) {
if($obj->delete(intval($_POST['id']))){
$result = ['success' => true, 'message' => 'Data Successfully Deleted!'];
}else{
$result = ['success' => false, 'message' => 'Error: Data Not Deleted!'];
}
}
}
echo json_encode($result);
?>

114
admin/action/signup.php Normal file
View File

@ -0,0 +1,114 @@
<?php
session_start();
require_once('../incl/const.php');
require_once('../class/database.php');
require_once('../class/user.php');
require_once('../class/signup.php');
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require '../class/PHPMailer/Exception.php';
require '../class/PHPMailer/PHPMailer.php';
require '../class/PHPMailer/SMTP.php';
function send_verify_email_smtp($name, $email, $url){
try {
//Create an instance; passing `true` enables exceptions
$mail = new PHPMailer(true);
//TODO: move this to setup.php
$mail->SMTPDebug = SMTP::DEBUG_OFF; //Enable verbose debug output
//$mail->SMTPDebug = SMTP::DEBUG_SERVER; //Enable verbose debug output
$mail->isSMTP(); //Send using SMTP
$mail->Host = SMTP_HOST; //Set the SMTP server to send through
$mail->SMTPAuth = true; //Enable SMTP authentication
$mail->Username = SMTP_USER; //SMTP username
$mail->Password = SMTP_PASS; //SMTP password
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; //Enable implicit TLS encryption
$mail->Port = SMTP_PORT; //TCP port to connect to; use 587 if you have set `SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS`
//Recipients
$mail->setFrom(SMTP_USER, 'Signup Mailer');
$mail->addAddress($email, $name); //Add a recipient
//$mail->addAddress('ellen@example.com'); //Name is optional
$mail->addReplyTo(SMTP_USER, 'Information');
//$mail->addCC('cc@example.com');
//$mail->addBCC('bcc@example.com');
//Attachments
//$mail->addAttachment('/var/tmp/file.tar.gz'); //Add attachments
//$mail->addAttachment('/tmp/image.jpg', 'new.jpg'); //Optional name
//Content
$mail->isHTML(true); //Set email format to HTML
$mail->Subject = 'QatMaps Verification Email ';
$email_html = file_get_contents('../snippets/verify_email.html');
$email_html = str_replace('USER_NAME', $name, $email_html);
$email_html = str_replace('VERIFY_URL', $url, $email_html);
$mail->Body = $email_html;
$mail->AltBody = 'Click to verify your email '.$url;
$mail->send();
return true;
} catch (Exception $e) {
//die("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
return false;
}
}
function send_verify_email($name, $email, $url){
$to = $email;
$subject = 'Verification Email for PostGIS Sync';
$message = 'Hello '.$name.' here is your verification link for PostGIS Sync <a href="'.$url.'">'.$url.'</a>.';
$headers = 'From: noreply@'.$_SERVER['SERVER_NAME'] . "\r\n" .
'Reply-To: noreply@'.$_SERVER['SERVER_NAME'] . "\r\n" .
'X-Mailer: PHP/' . phpversion();
mail($to, $subject, $message, $headers);
}
if(isset($_SESSION['user'])) {
header("Location: ../../index.php");
}
$loc = '../../signup.php?err='.urlencode('Error: Bad request!');
if(!empty($_POST['name']) && !empty($_POST['email']) && !empty($_POST['password']) ){
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$obj = new signup_Class($database->getConn());
$uobj = new user_Class($database->getConn(), SUPER_ADMIN_ID);
$urow = $uobj->getByEmail($_POST['email']);
if($urow){
$loc = '../../signup.php?err='.urlencode('Error: Email is already registered!');
}else{
$_POST['verify'] = hash('sha256', $_POST['name'].$_POST['email'].$_POST['password'].date("D M j G:i:s T Y"));
$_POST['password'] = password_hash($_POST['password'], PASSWORD_DEFAULT);
$newId = $obj->create($_POST);
if($newId > 0){
$verify_url = 'https://'.$_SERVER['SERVER_NAME'].str_replace('signup.php', 'verify.php', $_SERVER['REQUEST_URI'])
. '?id='.$newId.'&verify='.urlencode($_POST['verify']);
if(!send_verify_email_smtp($_POST['name'], $_POST['email'], $verify_url)){
$obj->delete($newId);
$loc = '../../signup.php?err='.urlencode('Error: Failed to signup!');
}else{
$loc = '../../login.php?msg='.urlencode('Your verification email has been sent!');
}
}else{ // error
$loc = '../../signup.php?err='.urlencode('Error: Failed to signup!');
}
}
}
header('Location: '.$loc);
?>

65
admin/action/user.php Normal file
View File

@ -0,0 +1,65 @@
<?php
session_start();
require('../incl/const.php');
require('../class/database.php');
require('../class/user.php');
$result = ['success' => false, 'message' => 'Error while processing your request!'];
if(isset($_SESSION['user']) && $_SESSION['user']->accesslevel == 'Admin') {
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$obj = new user_Class($database->getConn(), $_SESSION['user']->id);
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
if(($id > 0) && !$obj->isOwnedByUs($id)){
$result = ['success' => false, 'message' => 'Action not allowed!'];
}else if(isset($_POST['save'])) {
$newId = 0;
if($id) { // update
$obj->update($_POST);
}
else { // insert
$newId = $obj->create($_POST);
}
$result = ['success' => true, 'message' => 'Data Successfully Saved!', 'id' => $newId];
} else if(isset($_POST['delete']) && ($id != 1)) {
$ref_ids = array();
$ref_name = null;
$tbls = array('map', 'access_groups', 'permalink');
foreach($tbl as $k){
$rows = $database->getAll('public.'.$k, 'owner_id = '.$id);
foreach($rows as $row){
$ref_ids[] = $row[$k.'_id'];
}
if(count($ref_ids) > 0){
$ref_name = $k;
break;
}
}
if(count($ref_ids) > 0){
$result = ['success' => false, 'message' => 'Error: Can\'t delete because user owns '.$ref_name.'(s) ' . implode(',', $ref_ids) . '!' ];
}else {
$result = $obj->getById($id);
$row = pg_fetch_assoc($result);
pg_free_result($result);
$ret_val = $obj->delete($id);
if($ret_val){
shell_exec('sudo /usr/local/bin/delete_ftp_user.sh '.$row['ftp_user']);
}
$result = ['success' => $ret_val, 'message' => 'Data Successfully Deleted!'];
}
}
}
echo json_encode($result);
?>

60
admin/action/verify.php Normal file
View File

@ -0,0 +1,60 @@
<?php
session_start();
require_once('../incl/const.php');
require_once('../class/database.php');
require_once('../class/signup.php');
require_once('../class/user.php');
require_once('../class/access_groups.php');
$loc = '../../signup.php?err='.urlencode('Error: Bad verify request!');
if( !empty($_GET['id']) && !empty($_GET['verify']) ){
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$obj = new signup_Class($database->getConn());
$result = $obj->verify($_GET['id'], $_GET['verify']);
if(!$result || (pg_num_rows($result) > 0)){
$row = pg_fetch_assoc($result);
pg_free_result($result);
$row['accesslevel'] = 'Admin';
$row['groups'] = [1]; // Default group
$email_user = explode('@', $row['email'])[0];
$row['ftp_user'] = $email_user.$row['id'];
// create a new user
$uobj = new user_Class($database->getConn(), SUPER_ADMIN_ID);
$newId = $uobj->create($row, true);
if($newId){
user_Class::create_ftp_user($row['ftp_user'], $row['email'], $row['password']);
// create def access group for new admin
$def_grp = array('name' => $row['ftp_user'], 'userids' => array($newId));
$acc_obj = new access_group_Class($database->getConn(), $newId);
$grp_id = $acc_obj->create($def_grp);
if($grp_id > 0){
$row['id'] = $newId;
$row['groups'] = array($grp_id);
$uobj->update($row);
$obj->delete($_GET['id']); // remove signup request
$loc = '../../login.php?msg='.urlencode('Congratulations '.$row['name'].'. Your verification is successfull !');
}else{
$loc = '../../signup.php?err='.urlencode('Error: Failed to create group!');
}
}else{
$loc = '../../signup.php?err='.urlencode('Error: Failed to create user!');
}
}else{ // error
$loc = '../../signup.php?err='.urlencode('Error: Invalid verify request!');
}
}
header('Location: '.$loc);
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

View File

@ -0,0 +1,40 @@
<?php
/**
* PHPMailer Exception class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace PHPMailer\PHPMailer;
/**
* PHPMailer exception handler.
*
* @author Marcus Bointon <phpmailer@synchromedia.co.uk>
*/
class Exception extends \Exception
{
/**
* Prettify error message output.
*
* @return string
*/
public function errorMessage()
{
return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n";
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
<?php
class access_group_Class
{
private $table_name = 'access_groups';
private $dbconn = null;
private $owner_id = null;
function __construct($dbconn, $owner_id) {
$this->dbconn = $dbconn;
$this->owner_id = $owner_id;
}
function create($data)
{
$sql = "INSERT INTO PUBLIC." .$this->table_name." (name,owner_id) ".
"VALUES('".$this->cleanData($data['name'])."',".$this->owner_id.") RETURNING id";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return 0;
}
$row = pg_fetch_object($result);
pg_free_result($result);
if($row) {
# insert report access
$values = array();
foreach($data['userids'] as $user_id){
array_push($values, "(".$user_id.",".$row->id.")");
}
$sql = "insert into public.user_access (user_id,access_group_id) values ".implode(',', $values);
$result = pg_query($this->dbconn, $sql);
if(!$result){
return 0;
}
pg_free_result($result);
return $row->id;
}
return 0;
}
function getRows()
{
$sql ="select * from public." .$this->table_name;
if($this->owner_id != SUPER_ADMIN_ID){
$sql .= " WHERE owner_id = ".$this->owner_id;
}
$sql .= " ORDER BY id DESC";
return pg_query($this->dbconn, $sql);
}
function getRowsArr(){
$rv = array();
$sql = "select id,name from public.".$this->table_name;
if($this->owner_id != SUPER_ADMIN_ID){
$sql .= " WHERE owner_id = ".$this->owner_id;
}
$result = pg_query($this->dbconn, $sql);
while ($row = pg_fetch_assoc($result)) {
$rv[$row['id']] = $row['name'];
}
return $rv;
}
function getGroupUsers($gids){
$rv = array();
$sql = "select id,name from public.user WHERE id in (select user_id from public.user_access where access_group_id in (".implode(',', $gids)."))";
$result = pg_query($this->dbconn, $sql);
while ($row = pg_fetch_assoc($result)) {
$rv[$row['id']] = $row['name'];
}
return $rv;
}
function getGroupMapGroups($gids){
$rv = array();
$sql = "select id,name from public.map WHERE id in (SELECT map_id from public.map_access where access_group_id IN (".implode(',', $gids)."))";
$result = pg_query($this->dbconn, $sql);
while ($row = pg_fetch_assoc($result)) {
$rv[$row['id']] = $row['name'];
}
return $rv;
}
function getByUserId($user_id){
$rv = array();
$sql ="select id,name from public.access_groups WHERE id in (SELECT access_group_id from public.user_access where user_id='".intval($user_id)."')";
$result = pg_query($this->dbconn, $sql);
while ($row = pg_fetch_assoc($result)) {
$rv[$row['id']] = $row['name'];
}
return $rv;
}
function getGroupById($id){
$sql ="select * from public." .$this->table_name . " where id='".intval($id)."'";
return pg_query($this->dbconn, $sql);
}
function getGroupByName($name){
$sql ="select * from public." .$this->table_name . " where name='".$name."'";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
$row = pg_fetch_assoc($result);
pg_free_result($result);
return $row;
}
function delete($id){
$sql ="delete from public.user_access where access_group_id='".intval($id)."'";
if($this->owner_id != SUPER_ADMIN_ID){
$sql .= " AND owner_id = ".$this->owner_id;
}
$result = pg_query($this->dbconn, $sql);
if(pg_affected_rows($result) >= 0){
pg_free_result($result);
$sql ="delete from public.map_access where access_group_id='".intval($id)."'";
if($this->owner_id != SUPER_ADMIN_ID){
$sql .= " AND owner_id = ".$this->owner_id;
}
$result = pg_query($this->dbconn, $sql);
if(pg_affected_rows($result) >= 0){
pg_free_result($result);
$sql ="delete from public." .$this->table_name . " where id='".intval($id)."'";
$result = pg_query($this->dbconn, $sql);
$rv = (pg_affected_rows($result) >= 0);
pg_free_result($result);
return $rv;
}
}
return false;
}
function update($data=array()) {
$sql = "update public.access_groups set name='".$this->cleanData($data['name'])."' where id = '".intval($data['id'])."' ";
$rv = pg_affected_rows(pg_query($this->dbconn, $sql));
if($rv > 0){
$sql ="delete from public.user_access where access_group_id='".intval($data['id'])."'";
$rv = pg_query($this->dbconn, $sql);
# insert report access
$values = array();
foreach($data['userids'] as $user_id){
array_push($values, "(".$user_id.",".$data['id'].")");
}
$sql = "insert into public.user_access (user_id,access_group_id) values ".implode(',', $values);
$ret = pg_query($this->dbconn, $sql);
}
}
function isOwnedByUs($id){
if($this->owner_id == SUPER_ADMIN_ID){ // if Super Admin
return true;
}
$sql = "select * from public.".$this->table_name." where id=".$id." and owner_id=".$this->owner_id;
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
$rv = (pg_num_rows($result) > 0);
pg_free_result($result);
return $rv;
}
function cleanData($val)
{
return pg_escape_string($this->dbconn, $val);
}
}

500
admin/class/app.php Normal file
View File

@ -0,0 +1,500 @@
<?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;
}
};
?>

138
admin/class/database.php Normal file
View File

@ -0,0 +1,138 @@
<?PHP
class Database {
private $connection;
function __construct($db_host, $db_name, $db_user, $db_pass, $db_port, $db_schema) {
$this->connection = pg_connect("dbname={$db_name} user={$db_user} password={$db_pass} host={$db_host} port={$db_port}");
}
function __destruct() {
pg_close($this->connection);
}
function modify($str) {
return ucwords(str_replace("_", " ", $str));
}
function getConn() {
return $this->connection;
}
function getAll($table, $where = '', $orderby = '') {
$orderby = $orderby ? 'ORDER BY '.$orderby : '';
$where = $where ? 'WHERE '.$where : '';
$query = "SELECT * FROM {$table} {$where} {$orderby}";
$result = pg_query($this->connection, $query);
if (!$result) {
echo "An error occurred executing the query.\n";
exit;
}
// Fetch all rows
$rows = array();
while ($row = pg_fetch_assoc($result)) {
$rows[] = $row;
}
return $rows;
}
function get($table, $where = '') {
if(is_numeric($where)) {
$where = "id = ".intval($where);
}
else if (empty($where)) {
$where = "1";
}
$query = "SELECT * FROM {$table} WHERE $where";
$result = pg_query($this->connection, $query);
if (!$result) {
echo "An error occurred executing the query.\n";
exit;
}
// Fetch one rows
$row = pg_fetch_assoc($result);
return $row;
}
/* Select Query */
function select($query) {
$result = pg_query($this->connection, $query);
if (!$result) {
echo "An error occurred executing the query.\n";
exit;
}
// Fetch all rows
$rows = array();
while ($row = pg_fetch_assoc($result)) {
$rows[] = $row;
}
return $rows;
}
function buildGeoJSON($query){
$qc_id = 0; # NOTE: counter needed by qgis2web ?
echo '{"type": "FeatureCollection",
"features": [';
$feature = array('type' => 'Feature');
$result = pg_query($this->connection, $query);
if ($result) {
$sep = '';
while ($row = pg_fetch_assoc($result)) {
$feature['geometry'] = json_decode($row['geojson'], true);
# Remove geojson and geometry fields from properties
unset($row['geojson']);
unset($row['geom']);
$row['qc_id'] = $qc_id; $qc_id = $qc_id + 1;
$feature['properties'] = $row;
echo $sep."\n".json_encode($feature, JSON_NUMERIC_CHECK);
$sep = ',';
}
pg_free_result($result);
}
echo ']}';
return 0;
}
function find_srid($schema, $tbl, $geom){
/*$query = "SELECT Find_SRID('".$schema."','".$tbl."','".$geom."')";
$result = pg_query($this->connection, $query);
if (!$result) {
echo "An error occurred executing the query.\n";
exit;
}
$row = pg_fetch_assoc($result);
pg_free_result($result);
return $row['find_srid'];*/
return 4326;
}
function getGeoJSON($schema, $tbl, $geom){
if(!empty($where)){
$where = 'WHERE '.$where;
}
$srid = $this->find_srid($schema, $tbl, $geom);
$query = 'SELECT *, public.ST_AsGeoJSON(public.ST_Transform(('.$tbl.'.'.$geom.'),'.$srid.')) AS geojson FROM "'.$schema.'"."'.$tbl.'"';
return $this->buildGeoJSON($query);
}
}
?>

139
admin/class/map.php Normal file
View File

@ -0,0 +1,139 @@
<?php
class map_Class
{
private $table_name = 'map';
private $dbconn = null;
private $owner_id = null;
function __construct($dbconn, $owner_id) {
$this->dbconn = $dbconn;
$this->owner_id = $owner_id;
}
function create($data)
{
$sql = "INSERT INTO PUBLIC." .$this->table_name."
(name,description,owner_id) "."VALUES('".
$this->cleanData($data['name'])."','".
$this->cleanData($data['description'])."',".$this->owner_id.") RETURNING id";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return 0;
}
$row = pg_fetch_object($result);
pg_free_result($result);
if($row) {
# insert into access groups
$values = array();
foreach($data['accgrps'] as $group_id){
array_push($values, "(".$group_id.",".$row->id.")");
}
$sql = "insert into public.map_access (access_group_id,map_id) values ".implode(',', $values);
$ret = pg_query($this->dbconn, $sql);
return $row->id;
}
return 0;
//return pg_affected_rows(pg_query($this->dbconn, $sql));
}
function getRows(){
$sql ="select * from public." .$this->table_name;
if($this->owner_id != SUPER_ADMIN_ID){
$sql .= " WHERE owner_id = ".$this->owner_id;
}
$sql .= " ORDER BY id DESC";
return pg_query($this->dbconn, $sql);
}
function getById($id){
$sql ="select * from public." .$this->table_name . " where id='".intval($id)."'";
return pg_query($this->dbconn, $sql);
}
function getBy($k, $v){
$sql ="select * from public." .$this->table_name . " where ".$k."='".$v."'";
return pg_query($this->dbconn, $sql);
}
function getAccessGroups($id){
$rv = array();
$sql ="select id,name from public.access_groups WHERE id in (SELECT access_group_id from public.map_access where map_id='".intval($id)."')";
$result = pg_query($this->dbconn, $sql);
while ($row = pg_fetch_assoc($result)) {
$rv[$row['id']] = $row['name'];
}
return $rv;
}
function chainExec($sqls){
for($i=0; $i < count($sqls); $i++){
$result = pg_query($this->dbconn, $sqls[$i]);
if(!$result) {
return false;
}
$success = (pg_affected_rows($result) >= 0);
pg_free_result($result);
if(!$success){
return false;
}
}
return true;
}
function delete($id)
{
$sqls = array(
"delete from public.map_access where map_id='".intval($id)."'",
"delete from public." .$this->table_name . " where id='".intval($id)."'");
return $this->chainExec($sqls);
}
function update($data)
{
# insert access groups
$values = array();
foreach($data['accgrps'] as $group_id){
array_push($values, "(".$group_id.",".$data['id'].")");
}
$sqls = array(
"update public.".$this->table_name." set name='".
$this->cleanData($data['name'])."', description='".
$this->cleanData($data['description'])."' where id = '".intval($data['id'])."' ",
"delete from public.map_access where map_id=".$data['id'],
"insert into public.map_access (access_group_id,map_id) values ".implode(',', $values)
);
return $this->chainExec($sqls);
}
function isOwnedByUs($id){
if($this->owner_id == SUPER_ADMIN_ID){ // if Super Admin
return true;
}
$sql = "select * from public.".$this->table_name." where id=".$id." and owner_id=".$this->owner_id;
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
$rv = (pg_num_rows($result) > 0);
pg_free_result($result);
return $rv;
}
function cleanData($val)
{
return pg_escape_string($this->dbconn, $val);
}
}

204
admin/class/permalink.php Normal file
View File

@ -0,0 +1,204 @@
<?php
class permalink_Class
{
private $table_name = 'permalink';
private $dbconn = null;
private $owner_id = null;
function __construct($dbconn, $owner_id) {
$this->dbconn = $dbconn;
$this->owner_id = $owner_id;
}
function getExpires($interval){
if(!preg_match("/[a-z]/i", $interval)){ // if its just numbers (ex. 2024-02-20 18:35:07.616773)
return ['expires' => $interval]; // it comes from update
}
$sql = "SELECT CURRENT_TIMESTAMP + '".$interval."' as expires";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
$row = pg_fetch_assoc($result);
return $row;
}
function create($data)
{
$row = $this->getExpires($data['expires']);
if($row === false){
return [0,0,0];
}
$sql = "INSERT INTO PUBLIC." .$this->table_name."
(map_id,description,query,expires,visits_limit,hash,owner_id) "."VALUES('".
$this->cleanData($data['map_id'])."','".
$this->cleanData($data['description'])."','".
$this->cleanData($data['query'])."','".
$row['expires']."','".
$this->cleanData($data['visits_limit'])."','".
$this->cleanData($data['hash'])."',".$this->owner_id.") RETURNING id,created,expires";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return [0,0,0];
}
$row = pg_fetch_object($result);
if($row) {
return [$row->id,$row->created,$row->expires];
}
return [0,0,0];
}
function getRows()
{
$sql ="select * from public." .$this->table_name;
if($this->owner_id != SUPER_ADMIN_ID){
$sql .= " WHERE owner_id = ".$this->owner_id;
}
$sql .= " ORDER BY id DESC";
return pg_query($this->dbconn, $sql);
}
function getRowsArr(){
$rv = array();
$sql = "select id,name from public.".$this->table_name;
$result = pg_query($this->dbconn, $sql);
while ($row = pg_fetch_assoc($result)) {
$rv[$row['id']] = $row['name'];
}
return $rv;
}
function getById($id){
$sql ="select * from public." .$this->table_name . " where id='".intval($id)."'";
return pg_query($this->dbconn, $sql);
}
function getByHash($hash){
$sql ="select * from public." .$this->table_name . " where hash='".$hash."'";
return pg_query($this->dbconn, $sql);
}
function deleteByMap($map_id){
$sql ="delete from public." .$this->table_name . " where map_id='".intval($map_id)."'";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return 0;
}
$rv = (pg_affected_rows($result) > 0);
pg_free_result($result);
return $rv;
}
function delete($id){
$sql ="delete from public." .$this->table_name . " where id='".intval($id)."'";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return 0;
}
$rv = (pg_affected_rows($result) > 0);
pg_free_result($result);
return $rv;
}
function update($data) {
$row = $this->getExpires($data['expires']);
if($row === false){
return 0;
}
$sql = "update public.".$this->table_name." set description='".
$this->cleanData($data['description'])."', query='".
$this->cleanData($data['query'])."', expires='".
$this->cleanData($data['expires'])."', visits_limit='".
$this->cleanData($data['visits_limit'])."' where id = '".intval($data['id'])."' ";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return 0;
}
return pg_affected_rows($result);
}
function updateVisits($data) {
$sql = "update public.".$this->table_name." set visits='".
$this->cleanData($data['visits'])."' where id = '".intval($data['id'])."' ";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return 0;
}
return pg_affected_rows($result);
}
function getMap($hash, $countme){
$sql = "select * FROM ".$this->table_name." where hash='".$hash."'";
$result = pg_query($this->dbconn, $sql);
if(!$result || (pg_num_rows($result) == 0)){
return null;
}
$row = pg_fetch_assoc($result);
pg_free_result($result);
if($countme){
// update the visited permalink counter
$visits = intval($row['visits']) + 1;
$limit = intval($row['visits_limit']);
if($limit != 0){
if($visits > $limit){
$this->delete($row['id']);
return null; //expired by visits
}/* NOTE: can't delete, because after last visit, we use permalink in data_{file,pg,gs}.php!
else if($visits == $limit){ //last visit
$this->delete($row['id']); // delete permalink
return $row;
}*/
}
$row['visits'] = $visits;
$this->updateVisits($row);
}
// check time
if (time() >= strtotime($row['expires'])) {
$this->delete($row['id']);
return null; //expired by time
}
return $row;
}
function isOwnedByUs($id){
if($this->owner_id == SUPER_ADMIN_ID){ // if Super Admin
return true;
}
$sql = "select * from public.".$this->table_name." where id=".$id." and owner_id=".$this->owner_id;
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
$rv = (pg_num_rows($result) > 0);
pg_free_result($result);
return $rv;
}
function cleanData($val)
{
return pg_escape_string($this->dbconn, $val);
}
}

65
admin/class/signup.php Normal file
View File

@ -0,0 +1,65 @@
<?php
class signup_Class
{
private $table_name = 'signup';
private $dbconn = null;
function __construct($dbconn) {
$this->dbconn = $dbconn;
}
function create($data)
{
$sql = "INSERT INTO PUBLIC." .$this->table_name."
(name,email,password,verify) "."VALUES('".
$this->cleanData($data['name'])."','".
$this->cleanData($data['email'])."','".
$data['password']."','".
$this->cleanData($data['verify'])."'".
") RETURNING id";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return 0;
}
$row = pg_fetch_object($result);
pg_free_result($result);
if($row) {
return $row->id;
}
return 0;
}
function getRows(){
$sql ="select * from public." .$this->table_name." ORDER BY id DESC";
return pg_query($this->dbconn, $sql);
}
function getById($id){
$sql ="select * from public." .$this->table_name . "
where id='".intval($id)."'";
return pg_query($this->dbconn, $sql);
}
function delete($id){
$sql ="delete from public." .$this->table_name . " where id='".intval($id)."'";
$result = pg_query($this->dbconn, $sql);
if($result){
$rv = (pg_affected_rows($result) > 0);
pg_free_result($result);
return $rv;
}else{
return false;
}
}
function verify($id, $verify){
$sql ="select * from public.".$this->table_name." where id='".intval($id)."' AND verify='".$verify."'";
return pg_query($this->dbconn, $sql);
}
function cleanData($val){
return pg_escape_string($this->dbconn, $val);
}
}

203
admin/class/user.php Normal file
View File

@ -0,0 +1,203 @@
<?php
class user_Class
{
private $table_name = 'user';
private $dbconn = null;
private $owner_id = null;
function __construct($dbconn, $owner_id) {
$this->dbconn = $dbconn;
$this->owner_id = $owner_id;
}
function create($data, $isHashed = false)
{
if(!$isHashed){
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
}
$sql = "INSERT INTO PUBLIC." .$this->table_name."
(name,email,password,ftp_user,accesslevel,owner_id) "."VALUES('".
$this->cleanData($data['name'])."','".
$this->cleanData($data['email'])."','".
$data['password']."','".
$this->cleanData($data['ftp_user'])."','".
$this->cleanData($data['accesslevel'])."',".
$this->owner_id.") RETURNING id";
$row = pg_fetch_object(pg_query($this->dbconn, $sql));
if($row) {
# insert user groups
$values = array();
foreach($data['groups'] as $group_id){
array_push($values, "(".$row->id.",".$group_id.")");
}
$sql = "insert into public.user_access (user_id,access_group_id) values ".implode(',', $values);
$ret = pg_query($this->dbconn, $sql);
return $row->id;
}
return 0;
//return pg_affected_rows(pg_query($this->dbconn, $sql));
}
function getRows()
{
$sql ="select * from public." .$this->table_name;
if($this->owner_id != SUPER_ADMIN_ID){
$sql .= " WHERE owner_id = ".$this->owner_id;
}
$sql .= " ORDER BY id DESC";
return pg_query($this->dbconn, $sql);
}
function getRowsArr(){
$rv = array();
$result = $this->getRows();
while ($row = pg_fetch_assoc($result)) {
$rv[$row['id']] = $row['name'];
}
return $rv;
}
function getById($id){
$sql ="select * from public." .$this->table_name . "
where id='".intval($id)."'";
return pg_query($this->dbconn, $sql);
}
function loginCheck($pwd, $email){
$sql ="select * from public.user where email = '".$this->cleanData($email)."'";
$result = pg_query($this->dbconn,$sql);
$row = pg_fetch_object($result);
pg_free_result($result);
if (password_verify($pwd, $row->password)) {
return $row;
}
return null;
}
function getByEmail($email){
$sql ="select * from public.".$this->table_name." where email='".$email."'";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
$row = pg_fetch_object($result);
pg_free_result($result);
return $row;
}
function delete($id)
{
$sql ="delete from public.user_access where user_id='".intval($id)."'";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
pg_free_result($result);
$sql ="delete from public." .$this->table_name . " where id='".intval($id)."'";
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
pg_free_result($result);
return true;
}
function update($data=array())
{
$id = intval($data['id']);
$row = pg_fetch_object($this->getById($id));
$sql = "update public.user set name='".
$this->cleanData($data['name'])."'";
if($row->password != $data['password']){ # if password is changed
$hashpassword = password_hash($data['password'], PASSWORD_DEFAULT);
$sql .= ", password='".$hashpassword."'";
}
if(isset($data['ftp_user'])){
$sql .= ", ftp_user = '".$this->cleanData($data['ftp_user'])."'";
}
$sql .= ", accesslevel='".$this->cleanData($data['accesslevel']).
"' where id = '".$id."'";
$rv = pg_affected_rows(pg_query($this->dbconn, $sql));
if($rv > 0){
# drop old groups
$sql = "delete from public.user_access where user_id=".$data['id'];
$ret = pg_query($this->dbconn, $sql);
# insert user groups
$values = array();
foreach($data['groups'] as $group_id){
array_push($values, "(".$data['id'].",".$group_id.")");
}
$sql = "insert into public.user_access (user_id,access_group_id) values ".implode(',', $values);
$ret = pg_query($this->dbconn, $sql);
}
return $rv;
}
function isOwnedByUs($id){
if($this->owner_id == SUPER_ADMIN_ID){ // if Super Admin
return true;
}
$sql = "select * from public.".$this->table_name." where id=".$id." and owner_id=".$this->owner_id;
$result = pg_query($this->dbconn, $sql);
if(!$result){
return false;
}
$rv = (pg_num_rows($result) > 0);
pg_free_result($result);
return $rv;
}
function cleanData($val)
{
return pg_escape_string($this->dbconn, $val);
}
static public function create_ftp_user($ftp_user, $email, $hashed_pwd){
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$process = proc_open('sudo /usr/local/bin/create_ftp_user.sh', $descriptorspec, $pipes, null, null);
if (is_resource($process)) {
fwrite($pipes[0], $ftp_user."\n".$hashed_pwd."\n");
fclose($pipes[0]);
//echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
// It is important that you close any pipes before calling proc_close in order to avoid a deadlock
$return_value = proc_close($process);
}
}
}

30
admin/dist/css/accordion.css vendored Normal file
View File

@ -0,0 +1,30 @@
/* plus glyph for showing collapsible panels */
.panel-heading .accordion-plus-toggle:before {
font-family: FontAwesome;
content: "\f068";
float: right;
color: silver;
}
.panel-heading .accordion-plus-toggle.collapsed:before {
content: "\f067";
color: silver;
}
/* arrow glyph for showing collapsible panels */
.panel-heading .accordion-arrow-toggle:before {
font-family: FontAwesome;
content: "\f078";
float: right;
color: silver;
}
.panel-heading .accordion-arrow-toggle.collapsed:before {
content: "\f054";
color: silver;
}
/* sets the link to the width of the entire panel title */
.panel-title > a {
display: block;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.9 MiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

96
admin/dist/css/meta.css vendored Normal file
View File

@ -0,0 +1,96 @@
.table>thead {
background-color: #fff!important;
color: #fff;
border-style : hidden!important;
}
.table thead th {
color: #black!important;
font-weight: 900;
}
.table {
width: 80%;
max-width: 100%;
margin-bottom: 20px;
border-collapse:collapse!important;
border: none;
}
td {
border-style : hidden!important;
}
.text-end {
text-align: center!important;
}
.nav-tabs {
border-bottom: 1px solid #fff;
max-width: 90%;
}
.secrer {
display: block;
padding-left: 50px;
}
#main-wrapper[data-layout=horizontal] .left-sidebar[data-sidebarbg=skin6] .sidebar-nav ul, #main-wrapper[data-layout=vertical] .left-sidebar[data-sidebarbg=skin6] .sidebar-nav ul {
background: #fff;
border-radius:40px;
}
#main-wrapper[data-layout=horizontal] .left-sidebar[data-sidebarbg=skin6], #main-wrapper[data-layout=vertical] .left-sidebar[data-sidebarbg=skin6] {
background: #fff;
}
.sidebar-nav ul .sidebar-item.selected>.sidebar-link {
background-color: #666;
color: #fff!important;
opacity: 1;
}
.page-wrapper {
background: #fff;
position: relative;
}
div.dataTables_wrapper div.dataTables_filter {
text-align: right;
display: none!important;
}
.bg-success {
background-color: #41afa5!important;
}
.bg-warning2 {
background-color: cornflowerblue!important;
}
.bg-info {
background-color: #50667f!important;
}
.card {
position: relative;
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
border: none!important;
border-radius: inherit;
}
.navbar {
position: relative;
min-height: 50px;
margin-bottom: 0px!important;
border: 1px solid transparent;
}

14832
admin/dist/css/style.css vendored Normal file

File diff suppressed because it is too large Load Diff

7
admin/dist/css/style.min.css vendored Normal file

File diff suppressed because one or more lines are too long

102
admin/dist/css/table.css vendored Normal file
View File

@ -0,0 +1,102 @@
body {
color: #404E67;
background: #fff; /*#F5F7FA;*/
font-family: 'Open Sans', sans-serif;
}
.table-wrapper {
width: 100%;
margin: 20px auto;
background: #fff;
padding: 5px;
box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
}
.table-title {
padding-bottom: 10px;
margin: 0 0 10px;
}
.table-title h2 {
margin: 6px 0 0;
font-size: 16px;
}
.table-title .add-new {
float: right;
height: 30px;
font-weight: bold;
font-size: 10px;
text-shadow: none;
min-width: 100px;
border-radius: 50px;
line-height: 13px;
}
.table-title .add-new i {
margin-right: 4px;
}
table.table {
/*table-layout: fixed;*/
}
table.table tr th,
table.table tr td {
border-color: #e9e9e9;
}
table.table th i {
font-size: 10px;
margin: 0 5px;
cursor: pointer;
}
table.table th:last-child {
width: 100px;
}
table.table td a {
cursor: pointer;
display: inline-block;
margin: 0 5px;
min-width: 24px;
}
table.table td a.add {
color: #27C46B;
}
table.table td a.edit {
color: #FFC107;
}
table.table td a.delete {
color: #E34724;
}
table.table td i {
font-size: 19px;
}
table.table td a.add i {
font-size: 24px;
margin-right: -1px;
position: relative;
top: 3px;
}
table.table .form-control {
height: 32px;
line-height: 32px;
box-shadow: none;
border-radius: 2px;
}
table.table .form-control.error {
border-color: #f50000;
}
table.table td .add {
display: none;
}

33
admin/dist/js/custom.js vendored Normal file
View File

@ -0,0 +1,33 @@
$(function () {
"use strict";
$(".preloader").fadeOut();
// this is for close icon when navigation open in mobile view
$(".nav-toggler").on("click", function () {
$("#main-wrapper").toggleClass("show-sidebar");
});
$(".search-box a, .search-box .app-search .srh-btn").on("click", function () {
$(".app-search").toggle(200);
$(".app-search input").focus();
});
// ==============================================================
// Resize all elements
// ==============================================================
$("body, .page-wrapper").trigger("resize");
$(".page-wrapper").delay(20).show();
//****************************
/* This is for the mini-sidebar if width is less then 1170*/
//****************************
var setsidebartype = function () {
var width = window.innerWidth > 0 ? window.innerWidth : this.screen.width;
if (width < 1170) {
$("#main-wrapper").attr("data-sidebartype", "mini-sidebar");
} else {
$("#main-wrapper").attr("data-sidebartype", "full");
}
};
$(window).ready(setsidebartype);
$(window).on("resize", setsidebartype);
});

60
admin/dist/js/sidebarmenu.js vendored Normal file
View File

@ -0,0 +1,60 @@
/*
Template Name: Admin Template
Author: Wrappixel
File: js
*/
// ==============================================================
// Auto select left navbar
// ==============================================================
$(function() {
"use strict";
var url = window.location + "";
var path = url.replace(window.location.protocol + "//" + window.location.host + "/", "");
var element = $('ul#sidebarnav a').filter(function() {
return this.href === url || this.href === path;// || url.href.indexOf(this.href) === 0;
});
element.parentsUntil(".sidebar-nav").each(function (index)
{
if($(this).is("li") && $(this).children("a").length !== 0)
{
$(this).children("a").addClass("active");
$(this).parent("ul#sidebarnav").length === 0
? $(this).addClass("active")
: $(this).addClass("selected");
}
else if(!$(this).is("ul") && $(this).children("a").length === 0)
{
$(this).addClass("selected");
}
else if($(this).is("ul")){
$(this).addClass('in');
}
});
element.addClass("active");
$('#sidebarnav a').on('click', function (e) {
if (!$(this).hasClass("active")) {
// hide any open menus and remove all other classes
$("ul", $(this).parents("ul:first")).removeClass("in");
$("a", $(this).parents("ul:first")).removeClass("active");
// open our new menu and add the open class
$(this).next("ul").addClass("in");
$(this).addClass("active");
}
else if ($(this).hasClass("active")) {
$(this).removeClass("active");
$(this).parents("ul:first").removeClass("active");
$(this).next("ul").removeClass("in");
}
})
$('#sidebarnav >li >a.has-arrow').on('click', function (e) {
e.preventDefault();
});
});

429
admin/edit_map.php Normal file
View File

@ -0,0 +1,429 @@
<?php
session_start();
require('incl/const.php');
require('class/database.php');
require('class/map.php');
require('class/app.php');
require('class/access_groups.php');
if(!isset($_SESSION['user']) || $_SESSION['user']->accesslevel != 'Admin'){
header('Location: ../login.php');
exit;
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$map = ['name' => '','description' => ''];
$map_acc_ids = array();
$use_dt = false;
if(!empty($_GET['id'])){
$map_obj = new map_Class($database->getConn(), $_SESSION['user']->id);
$result = $map_obj->getById($_GET['id']);
if(!$result){
$_GET['error'] = 'Error: No such map!';
exit;
}
$map = pg_fetch_assoc($result);
pg_free_result($result);
$map_acc_grps = $map_obj->getAccessGroups($_GET['id']);
$map_acc_ids = array_keys($map_acc_grps);
list($dss,$lys,$use_dt) = App::parseDatasources(APPS_DIR.'/'.$map['id']);
}else{
$upload_dir = App::upload_dir($_SESSION['user']->ftp_user);
$app_names = App::getApps($upload_dir);
}
$acc_obj = new access_group_Class($database->getConn(), $_SESSION['user']->id);
$acc_grps = $acc_obj->getRowsArr();
?>
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<?php include("incl/meta.php"); ?>
<link href="dist/css/table.css" rel="stylesheet">
<?php if(!empty($_GET['id'])){ ?>
<link href="dist/css/accordion.css" rel="stylesheet">
<?php } ?>
<script type="text/javascript">
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
$('#map_form').submit(false);
<?php
if(isset($_GET['id'])){
foreach($dss as $dsi => $ds) {
if($ds['data_type'] == 'gs'){ ?>
$('#pg_details<?=$dsi?>').hide();
<?php } else if($ds['data_type'] == 'pg'){ ?>
$('#gs_details<?=$dsi?>').hide();
<?php }else { ?>
$('#pg_details<?=$dsi?>').hide();
$('#gs_details<?=$dsi?>').hide();
<?php }
}
foreach($lys as $lyi => $ly) {
if($ly['layer_type'] == 'gs_geo'){ ?>
$('#wms_details<?=$lyi?>').hide();
<?php }else if($ly['layer_type'] == 'wms'){ ?>
$('#gs_geo_details<?=$lyi?>').hide();
<?php }
}
} ?>
$('#archive').hide();
$(document).on("click", 'input[name="from_type"]', function() {
if($(this).attr('id') == 'from_uploaded'){
$('#app').show(); $('#app').prop('disabled', false); $('#app').prop('required',true);
$('#archive').hide();$('#archive').prop('disabled', true); $('#archive').prop('required',false);
}else{
$('#app').hide(); $('#app').prop('disabled', true); $('#app').prop('required',false);
$('#archive').show(); $('#archive').prop('disabled', false); $('#archive').prop('required',true);
}
});
//// data
$(document).on("click", 'input[class="data_files"]', function() {
const id = $(this).attr('data-id');
let pg = $('#pg_details' + id);
let gs = $('#gs_details' + id);
pg.hide(); pg.children('input').prop('required',false);
gs.hide(); gs.children('input').prop('required',false);
});
$(document).on("click", 'input[class="data_pg"]', function() {
const id = $(this).attr('data-id');
let pg = $('#pg_details' + id);
let gs = $('#gs_details' + id);
pg.show(); pg.children('input').prop('required',true);
gs.hide(); gs.children('input').prop('required',false);
});
$(document).on("click", 'input[class="data_gs"]', function() {
const id = $(this).attr('data-id');
let pg = $('#pg_details' + id);
let gs = $('#gs_details' + id);
pg.hide(); pg.children('input').prop('required',false);
gs.show(); gs.children('input').prop('required',true);
});
//// layers
$(document).on("click", 'input[class="layer_wms"]', function() {
const id = $(this).attr('data-id');
let wms = $('#wms_details' + id);
let gs = $('#gs_geo_details' + id);
wms.show(); wms.children('input').prop('required',true);
wms.children('input[name^="wms_user"], input[name^="wms_pwd"]').prop('required',false);
gs.hide(); gs.children('input').prop('required',false);
});
$(document).on("click", 'input[class="layer_gs_geo"]', function() {
const id = $(this).attr('data-id');
let wms = $('#wms_details' + id);
let gs = $('#gs_geo_details' + id);
wms.hide(); wms.children('input').prop('required',false);
gs.show(); gs.children('input').prop('required',true);
});
// Update/Create connection on submit button click
$(document).on("click", "#btn_submit", function() {
let obj = $(this);
let input = $('#map_form').find('input[type="text"], input[type="password"], select');
let empty = false;
input.each(function() {
if (!$(this).prop('disabled') && $(this).prop('required') && !$(this).val()) {
$(this).addClass("error");
empty = true;
} else {
$(this).removeClass("error");
}
});
if(empty){
$('#map_form').find(".error").first().focus();
}else{
$.ajax({
type: "POST",
url: 'action/map.php',
data: new FormData($('#map_form')[0]),
processData: false,
contentType: false,
dataType:"json",
success: function(response){
alert(response.message);
if(response.success) {
<?php if(isset($_GET['id'])){ ?>
window.location.href = 'maps.php'; // redirect to maps.php
<?php } else { ?>
window.location.href = 'edit_map.php?id=' + response.id; // redirect to step 2 for datasources
<?php } ?>
}
}
});
}
});
});
</script>
</head>
<body>
<div id="main-wrapper" data-layout="vertical" data-navbarbg="skin5" data-sidebartype="full"
data-sidebar-position="absolute" data-header-position="absolute" data-boxed-layout="full">
<?php const MENU_SEL = 'maps.php';
include("incl/topbar.php");
include("incl/sidebar.php");
?>
<div class="page-wrapper">
<div class="page-breadcrumb" style="padding-left:30px; padding-right: 30px; padding-top:0px; padding-bottom: 0px">
<div class="row align-items-center">
<div class="col-6">
<nav aria-label="breadcrumb">
</nav>
<?php if(isset($_GET['id'])){ ?>
<h1 class="mb-0 fw-bold">Update Map <?=$_GET['id']?></h1>
<?php } else { ?>
<h1 class="mb-0 fw-bold">Add new map</h1>
<?php } ?>
</div>
<div class="col-6">
<div class="text-end upgrade-btn">
</div>
</div>
</div>
</div>
<div class="container-fluid">
<form action="" method="post" enctype="multipart/form-data" id="map_form" style="width: 50%;">
<?php if (isset($_GET['error'])) { ?>
<div class="alert alert-danger" role="alert">
<?=$_GET['error']?>
</div>
<?php } ?>
<?php if(isset($_GET['id'])){ ?>
<input type="hidden" class="form-control" name="id" id="id" value="<?=$_GET['id']?>" />
<?php } ?>
<input type="hidden" class="form-control" name="save" id="save" value="1" />
<div class="form-group">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" name="name" id="name" value="<?=$map['name']?>" required/>
<label for="description" class="form-label">Description</label>
<input type="text" class="form-control" name="description" id="description" value="<?=$map['description']?>" required/>
</div>
<?php if(!isset($_GET['id'])){ ?>
<fieldset>
<legend>Source</legend>
<div class="form-group">
<input type="radio" id="from_uploaded" name="from_type" value="uploaded" checked>
<label for="from_uploaded">Uploads</label>
<input type="radio" id="from_zip" name="from_type" value="archive">
<label for="from_zip">Archive</label>
<select class="form-control" name="app" id="app" aria-label="Select app">
<?php foreach($app_names as $k){ ?>
<option <?php if($k == $map['name']){ ?> selected <?php }?> value="<?=$k?>"><?=$k?></option>
<?php } ?>
</select>
<input type="file" class="form-control" name="archive" id="archive" value="" accept=".zip" disabled/>
<p>NOTE: Archive can contains files, or project directory.</p>
</div>
</fieldset>
<?php }else {
$aci = 0;
?>
<fieldset>
<legend>Data</legend>
<?php
foreach($dss as $dsi => $ds) { ?>
<div class="panel-group" id="accordion<?=$aci?>" role="tablist" aria-multiselectable="false">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="heading<?=$aci?>">
<h5 class="panel-title">
<a role="button" data-toggle="collapse" class="accordion-plus-toggle collapsed" data-parent="#accordion<?=$aci?>" href="#collapse<?=$aci?>" aria-expanded="false" aria-controls="collapse<?=$aci?>"><?=$ds['name']?></a>
</h5>
</div>
<div id="collapse<?=$aci?>" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading<?=$aci?>">
<div class="form-group col-sm">
<input type="radio" class="data_files" data-id="<?=$dsi?>" name="data_type<?=$dsi?>" value="file" <?php if($ds['data_type'] == 'file'){ ?> checked<?php } ?>> <label for="data_files<?=$dsi?>">Files</label>
<input type="radio" class="data_pg" data-id="<?=$dsi?>" name="data_type<?=$dsi?>" value="pg" <?php if($ds['data_type'] == 'pg'){ ?> checked<?php } ?>> <label for="data_pg<?=$dsi?>">Postgres</label>
<input type="radio" class="data_gs" data-id="<?=$dsi?>" name="data_type<?=$dsi?>" value="gs" <?php if($ds['data_type'] == 'gs'){ ?> checked<?php } ?>> <label for="data_gs<?=$dsi?>">GeoServer</label>
</div>
<div class="pg_details" id="pg_details<?=$dsi?>">
<label for="pg_host<?=$dsi?>" class="form-label">Host</label> <input type="text" class="form-control" name="pg_host<?=$dsi?>" id="pg_host<?=$dsi?>" value="<?=$ds['pg_host']?>"/>
<label for="pg_port<?=$dsi?>" class="form-label">Port</label> <input type="text" class="form-control" name="pg_port<?=$dsi?>" id="pg_port<?=$dsi?>" value="<?=$ds['pg_port']?>"/>
<label for="pg_db<?=$dsi?>" class="form-label">Database</label> <input type="text" class="form-control" name="pg_db<?=$dsi?>" id="pg_db<?=$dsi?>" value="<?=$ds['pg_db']?>"/>
<label for="pg_user<?=$dsi?>" class="form-label">User</label> <input type="text" class="form-control" name="pg_user<?=$dsi?>" id="pg_user<?=$dsi?>" value="<?=$ds['pg_user']?>"/>
<label for="pg_pwd<?=$dsi?>" class="form-label">Password</label> <input type="password" class="form-control" name="pg_pwd<?=$dsi?>" id="pg_pwd<?=$dsi?>" value="<?=$ds['pg_pwd']?>"/>
<div class="form-group">
<label for="pg_cache<?=$dsi?>" class="form-label">Cache</label>
<input type="number" name="pg_cache_val<?=$dsi?>" id="pg_cache_val<?=$dsi?>" value="<?=$ds['pg_cache_val']?>"/>
<select name="pg_cache_per<?=$dsi?>" id="pg_cache_per<?=$dsi?>">
<?php foreach(TIME_MAP as $per => $val) { ?>
<option value="<?=$per?>" <?php if($ds['pg_cache_per'] == $per) { ?> selected <?php } ?>><?=$per?></option>
<?php } ?>
</select>
</div>
</div>
<div class="gs_details" id="gs_details<?=$dsi?>">
<label for="gs_host<?=$dsi?>" class="form-label">Host</label> <input type="text" class="form-control" name="gs_host<?=$dsi?>" id="gs_host<?=$dsi?>" value="<?=$ds['gs_host']?>"/>
<label for="gs_user<?=$dsi?>" class="form-label">User</label> <input type="text" class="form-control" name="gs_user<?=$dsi?>" id="gs_user<?=$dsi?>" value="<?=$ds['gs_user']?>"/>
<label for="gs_pwd<?=$dsi?>" class="form-label">Password</label> <input type="password" class="form-control" name="gs_pwd<?=$dsi?>" id="gs_pwd<?=$dsi?>" value="<?=$ds['gs_pwd']?>"/>
<label for="gs_ws<?=$dsi?>" class="form-label">Workspace</label><input type="text" class="form-control" name="gs_ws<?=$dsi?>" id="gs_ws<?=$dsi?>" value="<?=$ds['gs_ws']?>"/>
<div class="form-group">
<label for="gs_cache<?=$dsi?>" class="form-label">Cache</label>
<input type="number" name="gs_cache_val<?=$dsi?>" id="gs_cache_val<?=$dsi?>" value="<?=$ds['gs_cache_val']?>"/>
<select name="gs_cache_per<?=$dsi?>" id="gs_cache_per<?=$dsi?>">
<?php foreach(TIME_MAP as $per => $val) { ?>
<option value="<?=$per?>" <?php if($ds['gs_cache_per'] == $per) { ?> selected <?php } ?>><?=$per?></option>
<?php } ?>
</select>
</div>
</div>
</div>
</div>
</div>
<?php $aci = $aci + 1;
} ?>
</fieldset>
<fieldset>
<legend>Layers</legend>
<?php
foreach($lys as $lyi => $ly) { ?>
<div class="panel-group" id="accordion<?=$aci?>" role="tablist" aria-multiselectable="false">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="heading<?=$aci?>">
<h5 class="panel-title">
<a role="button" data-toggle="collapse" class="accordion-plus-toggle collapsed" data-parent="#accordion<?=$aci?>" href="#collapse<?=$aci?>" aria-expanded="false" aria-controls="collapse<?=$aci?>"><?=$ly['name']?></a>
</h5>
</div>
<div id="collapse<?=$aci?>" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading<?=$aci?>">
<div class="form-group col-sm">
<input type="radio" class="layer_wms" data-id="<?=$lyi?>" name="layer_type<?=$lyi?>" value="wms" <?php if($ly['layer_type'] == 'wms'){ ?> checked<?php } ?>> <label for="layer_wms<?=$lyi?>">WMS</label>
<input type="radio" class="layer_gs_geo" data-id="<?=$lyi?>" name="layer_type<?=$lyi?>" value="gs_geo" <?php if($ly['layer_type'] == 'gs_geo'){ ?> checked<?php } ?>> <label for="layer_gs_geo<?=$lyi?>">GeoJSON(GS)</label>
</div>
<input type="hidden" name="layer_varname<?=$lyi?>" id="layer_varname<?=$lyi?>" value="<?=$ly['layer_varname']?>"/>
<div class="wms_details" id="wms_details<?=$lyi?>">
<label for="wms_url<?=$lyi?>" class="form-label">URL</label> <input type="text" class="form-control" name="wms_url<?=$lyi?>" id="wms_url<?=$lyi?>" value="<?=$ly['wms_url']?>"/>
<label for="wms_ws<?=$lyi?>" class="form-label">Workspace</label> <input type="text" class="form-control" name="wms_ws<?=$lyi?>" id="wms_ws<?=$lyi?>" value="<?=$ly['wms_ws']?>"/>
<label for="wms_layer<?=$lyi?>" class="form-label">Layer</label> <input type="text" class="form-control" name="wms_layer<?=$lyi?>" id="wms_layer<?=$lyi?>" value="<?=$ly['wms_layer']?>"/>
<label for="wms_user<?=$lyi?>" class="form-label">User (only for secured connections)</label> <input type="text" class="form-control" name="wms_user<?=$lyi?>" id="wms_user<?=$lyi?>" value="<?=$ly['wms_user']?>"/>
<label for="wms_pwd<?=$lyi?>" class="form-label">Password</label> <input type="password" class="form-control" name="wms_pwd<?=$lyi?>" id="wms_pwd<?=$lyi?>" value="<?=$ly['wms_pwd']?>"/>
</div>
<div class="gs_geo_details" id="gs_geo_details<?=$lyi?>">
<label for="gs_geo_host<?=$lyi?>" class="form-label">Host</label> <input type="text" class="form-control" name="gs_geo_host<?=$lyi?>" id="gs_geo_host<?=$lyi?>" value="<?=$ly['gs_geo_host']?>"/>
<label for="gs_geo_user<?=$lyi?>" class="form-label">User</label> <input type="text" class="form-control" name="gs_geo_user<?=$lyi?>" id="gs_geo_user<?=$lyi?>" value="<?=$ly['gs_geo_user']?>"/>
<label for="gs_geo_pwd<?=$lyi?>" class="form-label">Password</label> <input type="password" class="form-control" name="gs_geo_pwd<?=$lyi?>" id="gs_geo_pwd<?=$lyi?>" value="<?=$ly['gs_geo_pwd']?>"/>
<label for="gs_geo_ws<?=$lyi?>" class="form-label">Workspace</label> <input type="text" class="form-control" name="gs_geo_ws<?=$lyi?>" id="gs_geo_ws<?=$lyi?>" value="<?=$ly['gs_geo_ws']?>"/>
<label for="gs_geo_layer<?=$lyi?>" class="form-label">Layer</label> <input type="text" class="form-control" name="gs_geo_layer<?=$lyi?>" id="gs_geo_layer<?=$lyi?>" value="<?=$ly['gs_geo_layer']?>"/>
<div class="form-group">
<label for="gs_geo_color<?=$lyi?>" class="form-label">Color</label>
<input type="color" class="form-control" name="gs_geo_color<?=$lyi?>" id="gs_geo_color<?=$lyi?>" value="<?=$ly['gs_geo_color']?>"/>
<input type="range" class="form-control" name="gs_geo_opacity<?=$lyi?>" id="gs_geo_opacity<?=$lyi?>" value="<?=$ly['gs_geo_opacity']?>" min="0" max="100"/>
</div>
<div class="form-group">
<label for="gs_geo_fillcolor<?=$lyi?>" class="form-label">Fill Color</label>
<input type="color" class="form-control" name="gs_geo_fill_color<?=$lyi?>" id="gs_geo_fill_color<?=$lyi?>" value="<?=$ly['gs_geo_fill_color']?>"/>
<input type="range" class="form-control" name="gs_geo_fill_opacity<?=$lyi?>" id="gs_geo_fill_opacity<?=$lyi?>" value="<?=$ly['gs_geo_fill_opacity']?>" min="0" max="100"/>
</div>
<div class="form-group">
<label for="gs_geo_cache<?=$lyi?>" class="form-label">Cache</label>
<input type="number" name="gs_geo_cache_val<?=$lyi?>" id="gs_geo_cache_val<?=$lyi?>" value="<?=$ly['gs_geo_cache_val']?>"/>
<select name="gs_geo_cache_per<?=$lyi?>" id="gs_geo_cache_per<?=$lyi?>">
<?php foreach(TIME_MAP as $per => $val) { ?>
<option value="<?=$per?>" <?php if($ly['gs_geo_cache_per'] == $per) { ?> selected <?php } ?>><?=$per?></option>
<?php } ?>
</select>
</div>
</div>
</div>
</div>
</div>
<?php $aci = $aci + 1;
} ?>
</fieldset>
<?php } ?>
<fieldset>
<legend>View</legend>
<div class="form-group">
<input type="checkbox" class="form-checkbox" name="use_datatable" id="use_datatable" value="1" <?php if($use_dt) {?> checked <?php } ?>/>
<label for="use_datatable" class="form-label">Show DataTable below map</label>
</div>
<div class="form-group">
<label for="image" class="form-label">Preview image (.png, .jpeg formats)</label>
<?php if(isset($_GET['id']) && is_file("../assets/maps/".$_GET['id'].".png")){ ?>
<img src="../assets/maps/<?=$_GET['id']?>.png" alt="Map Preview" width="200" height="150">
<?php } else { ?>
<img src="../assets/maps/default.png" alt="Map Preview" width="200" height="150">
<?php } ?>
<input type="file" class="form-control" name="image" id="image" value="" accept=".png,.jpg,.jpeg"/>
</div>
<div class="form-group">
<label for="accgrps" class="form-label">Access Groups</label>
<select class="form-control" name="accgrps[]" id="accgrps" aria-label="Select access groups" multiple required>
<?php foreach($acc_grps as $k => $v){?>
<option <?php if(in_array($k, $map_acc_ids)){?> selected <?php }?> value="<?=$k?>"><?=$v?></option>
<?php } ?>
</select>
</div>
</fieldset>
<button type="submit" class="btn btn-primary" id="btn_submit"><?php if(isset($_GET['id'])){ ?>Update<?php } else { ?>Create<?php } ?></button>
</form>
</div>
</div>
<footer class="footer text-center">
</footer>
</div>
<script src="dist/js/sidebarmenu.js"></script>
<script src="dist/js/custom.js"></script>
</body>
</html>

17
admin/incl/meta.php Normal file
View File

@ -0,0 +1,17 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="robots" content="noindex,nofollow">
<title>QGIS2Map App</title>
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon.ico">
<link href="dist/css/style.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto|Varela+Round|Open+Sans">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="dist/css/meta.css" rel="stylesheet">

28
admin/incl/sidebar.php Normal file
View File

@ -0,0 +1,28 @@
<?php function sidebar_row($page, $icon, $label){ ?>
<li class="sidebar-item" <?php if(MENU_SEL == $page){ ?> selected <?php } ?> >
<a class="sidebar-link waves-effect waves-dark sidebar-link" href="<?=$page?>" aria-expanded="false">
<i class="mdi mdi-<?=$icon?>"></i><span class="hide-menu"><?=$label?></span>
</a>
</li>
<?php } ?>
<aside class="left-sidebar toggled" data-sidebarbg="skin6">
<div class="scroll-sidebar toggled" style="padding-left:10px">
<nav class="sidebar-nav toggled"><br>
<ul id="sidebarnav">
<?php
sidebar_row('index.php', 'view-dashboard', 'Dashboard');
if($_SESSION['user']->accesslevel == 'Admin') { // only admins have acess to user/group creation
sidebar_row('users.php', 'account-settings-variant', 'Users');
sidebar_row('access_groups.php', 'account-multiple', 'User Groups');
sidebar_row('maps.php', 'map', 'Maps');
sidebar_row('permalinks.php', 'map', 'Permalinks');
}
sidebar_row('../index.php', 'exit-to-app', 'Front End');
sidebar_row('../logout.php', 'logout', 'Log Out');
?>
</ul>
</nav>
</div>
</aside>

21
admin/incl/topbar.php Normal file
View File

@ -0,0 +1,21 @@
<header class="topbar" data-navbarbg="skin6">
<nav class="navbar top-navbar navbar-expand-md navbar-light" style="background-color:#50667f!important">
<div class="navbar-header" data-logobg="skin6">
<a class="navbar-brand" href="index.php" style="background-color:#50667f!important">
<b class="logo-icon">
<img src="assets/images/cited-logo.png" alt="homepage" class="dark-logo" />
<img src="assets/images/cited-logo.png" alt="homepage" class="light-logo" />
</b>
<span class="logo-text">
</span>
</a>
<a class="nav-toggler waves-effect waves-light d-block d-md-none" href="javascript:void(0)"><i
class="ti-menu ti-close"></i></a>
</div>
</nav>
</header>
<!--<p>&nbsp;</p>-->

315
admin/index.php Normal file
View File

@ -0,0 +1,315 @@
<?php
session_start();
require('incl/const.php');
require('class/database.php');
if(!isset($_SESSION['user']) || $_SESSION['user']->accesslevel != 'Admin') {
header('Location: ../login.php');
exit;
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$dbconn = $database->getConn();
?>
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<?php include("incl/meta.php"); ?>
<link href="dist/css/table.css" rel="stylesheet">
<style type="text/css">
a {
text-decoration:none!important;
}
.card:hover {
box-shadow: 0 4px 10px rgba(0,0,0,0.16), 0 4px 10px rgba(0,0,0,0.23);
}
</style>
</head>
<body>
<div id="main-wrapper" data-layout="vertical" data-navbarbg="skin5" data-sidebartype="full"
data-sidebar-position="absolute" data-header-position="absolute" data-boxed-layout="full">
<?php const MENU_SEL = 'index.php';
include("incl/topbar.php");
include("incl/sidebar.php");
?>
<div class="page-wrapper">
<div class="page-breadcrumb" style="padding-left:30px; padding-right: 30px; padding-top:0px; padding-bottom: 0px">
<div class="row align-items-center">
<div class="col-6">
<h1 class="mb-0 fw-bold">Dashboard</h1>
</div>
<div class="col-6">
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="d-flex border-bottom title-part-padding px-0 mb-3 align-items-center">
</div>
<div class="row" style="width:65%">
<div class="col-md-4 col-xl-2 d-flex align-items-stretch">
<a
href="maps.php"
class="card bg-info text-white w-100 card-hover"
>
<div class="card-header pt-5">
<!--begin::Title-->
<div class="card-title d-flex flex-column">
<!--begin::Amount-->
<span class="fs-2hx fw-bold text-white me-2 lh-1 ls-n2"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-map-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.598-.49L10.5.99 5.598.01a.5.5 0 0 0-.196 0l-5 1A.5.5 0 0 0 0 1.5v14a.5.5 0 0 0 .598.49l4.902-.98 4.902.98a.5.5 0 0 0 .196 0l5-1A.5.5 0 0 0 16 14.5zM5 14.09V1.11l.5-.1.5.1v12.98l-.402-.08a.5.5 0 0 0-.196 0zm5 .8V1.91l.402.08a.5.5 0 0 0 .196 0L11 1.91v12.98l-.5.1z"/>
</svg>
Maps</span>
<!--end::Amount-->
<!--begin::Subtitle-->
<span class="text-white opacity-75 pt-1 fw-semibold fs-6"></span>
<!--end::Subtitle-->
</div>
<!--end::Title-->
</div>
<div class="card-body" style="box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);">
<div class="d-flex align-items-center">
<span class="ri-apple-fill display-6"></span>
<div class="ms-auto">
<i data-feather="arrow-right" class="fill-white"></i>
</div>
</div>
<div class="mt-4">
<h4 class="card-title mb-1 text-white">
Maps
</h4>
<h6 class="card-text fw-normal text-white-50">
Add and Edit Maps
</h6>
</div>
</div>
</a>
</div>
<div class="col-md-4 col-xl-2 d-flex align-items-stretch">
<a
href="users.php"
class="card bg-success text-white w-100 card-hover"
>
<div class="card-header pt-5">
<!--begin::Title-->
<div class="card-title d-flex flex-column">
<!--begin::Amount-->
<span class="fs-2hx fw-bold text-white me-2 lh-1 ls-n2"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-fill" viewBox="0 0 16 16">
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6"/>
</svg> Users</span>
<!--end::Amount-->
<!--begin::Subtitle-->
<span class="text-white opacity-75 pt-1 fw-semibold fs-6"></span>
<!--end::Subtitle-->
</div>
<!--end::Title-->
</div>
<div class="card-body" style="box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);">
<div class="d-flex align-items-center">
<span class="ri-folders-line display-6"></span>
<div class="ms-auto">
<i data-feather="arrow-right" class="fill-white"></i>
</div>
</div>
<div class="mt-4">
<h4 class="card-title mb-1 text-white">Manage Users</h4>
<h6 class="card-text fw-normal text-white-50">
Add and Edit Users
</h6>
</div>
</div>
</a>
</div>
<div class="col-md-4 col-xl-2 d-flex align-items-stretch">
<a
href="access_groups.php"
class="card bg-warning text-white w-100 card-hover"
>
<div class="card-header pt-5">
<!--begin::Title-->
<div class="card-title d-flex flex-column">
<!--begin::Amount-->
<span class="fs-2hx fw-bold text-white me-2 lh-1 ls-n2"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-people-fill" viewBox="0 0 16 16">
<path d="M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6m-5.784 6A2.24 2.24 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.3 6.3 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5"/>
</svg> User Groups</span>
<!--end::Amount-->
<!--begin::Subtitle-->
<span class="text-white opacity-75 pt-1 fw-semibold fs-6"></span>
<!--end::Subtitle-->
</div>
<!--end::Title-->
</div>
<div class="card-body" style="box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);">
<div class="d-flex align-items-center">
<span class="ri-spam-2-line display-6"></span>
<div class="ms-auto">
<i data-feather="arrow-right" class="fill-white"></i>
</div>
</div>
<div class="mt-4">
<h4 class="card-title mb-1 text-white">
User Groups
</h4>
<h6 class="card-text fw-normal text-white-50">
Manage User Groups
</h6>
</div>
</div>
</a>
</div>
</div>
</div>
<div class="row">
<div class="d-flex border-bottom title-part-padding px-0 mb-3 align-items-center">
</div>
<div class="row" style="width:65%">
<div class="col-md-4 col-xl-2 d-flex align-items-stretch">
<a
href="permalinks.php"
class="card bg-purple text-white w-100 card-hover"
>
<div class="card-body" style="box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);">
<div class="d-flex align-items-center">
<span class="ri-apple-fill display-6"></span>
<div class="ms-auto">
<i data-feather="arrow-right" class="fill-white"></i>
</div>
</div>
<div class="mt-4">
<h4 class="card-title mb-1 text-white">
Permalinks
</h4>
<h6 class="card-text fw-normal text-white-50">
Manage Permalinks
</h6>
</div>
</div>
</a>
</div>
<div class="col-md-4 col-xl-2 d-flex align-items-stretch">
<a
href="../index.php"
class="card bg-warning2 text-white w-100 card-hover"
>
<div class="card-body" style="box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);">
<div class="d-flex align-items-center">
<span class="ri-folders-line display-6"></span>
<div class="ms-auto">
<i data-feather="arrow-right" class="fill-white"></i>
</div>
</div>
<div class="mt-4">
<h4 class="card-title mb-1 text-white">Front End</h4>
<h6 class="card-text fw-normal text-white-50">
Client View
</h6>
</div>
</div>
</a>
</div>
<div class="col-md-4 col-xl-2 d-flex align-items-stretch">
<a
href="../logout.php"
class="card bg-danger text-white w-100 card-hover"
>
<div class="card-body" style="box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);">
<div class="d-flex align-items-center">
<span class="ri-spam-2-line display-6"></span>
<div class="ms-auto">
<i data-feather="arrow-right" class="fill-white"></i>
</div>
</div>
<div class="mt-4">
<h4 class="card-title mb-1 text-white">
Log Out
</h4>
<h6 class="card-text fw-normal text-white-50">
Add and Edit Styles
</h6>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
<footer class="footer text-center">
</footer>
</div>
</div>
<script src="dist/js/sidebarmenu.js"></script>
<script src="dist/js/custom.js"></script>
</body>
</html>

179
admin/maps.php Normal file
View File

@ -0,0 +1,179 @@
<?php
session_start();
require('incl/const.php');
require('class/database.php');
require('class/map.php');
if(!isset($_SESSION['user']) || $_SESSION['user']->accesslevel != 'Admin') {
header('Location: ../login.php');
exit;
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$dbconn = $database->getConn();
$maps_obj = new map_Class($dbconn, $_SESSION['user']->id);
$rows = $maps_obj->getRows();
//TODO: get directory names with new maps
?>
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<?php include("incl/meta.php"); ?>
<link href="dist/css/table.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap4.min.css">
<script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap4.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
// Delete row on delete button click
$(document).on("click", ".delete", function() {
var obj = $(this);
var data = {'delete': true, 'id': obj.parents("tr").attr('data-id')}
if(confirm('Map will be deleted ?')){
$.ajax({
type: "POST",
url: 'action/map.php',
data: data,
dataType:"json",
success: function(response){
if(response.success) { // means, new record is added
obj.parents("tr").remove();
}
$(".add-new").removeAttr("disabled");
alert(response.message);
}
});
}
});
$(document).on("click", ".clear", function() {
var obj = $(this);
var data = {'clear': true, 'id': obj.parents("tr").attr('data-id')}
if(confirm('Map cache will be deleted ?')){
$.ajax({
type: "POST",
url: 'action/map.php',
data: data,
dataType:"json",
success: function(response){
if(response.success){
obj.hide();
}
alert(response.message);
}
});
}
});
});
</script>
<style>
.card {
position: relative;
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
border: none!important;
border-radius: inherit;
text-decoration: none!important;
}
.bg-warning {
background-color: #50667f!important;
}
</style>
</head>
<body>
<div id="main-wrapper" data-layout="vertical" data-navbarbg="skin5" data-sidebartype="full"
data-sidebar-position="absolute" data-header-position="absolute" data-boxed-layout="full">
<?php define('MENU_SEL', 'maps.php');
include("incl/topbar.php");
include("incl/sidebar.php");
?>
<div class="page-wrapper">
<div class="page-breadcrumb" style="padding-left:30px; padding-right: 30px; padding-top:0px; padding-bottom: 0px">
<div class="row align-items-center">
<div class="col-6">
<nav aria-label="breadcrumb">
</nav>
<h1 class="mb-0 fw-bold">Maps</h1>
</div>
<div class="col-6">
<div class="text-end upgrade-btn">
<a href="edit_map.php" class="btn btn-primary text-white add-new" role="button" aria-pressed="true">
<i class="fa fa-plus"></i> Add New
</a>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<table class="table table-bordered" id="sortTable">
<thead>
<tr>
<th data-name="id" data-editable='false'>ID</th>
<th data-name="name">Name</th>
<th data-name="description">Description</th>
<th data-editable='false' data-action='true'>Actions</th>
</tr>
</thead>
<tbody> <?php while($row = pg_fetch_object($rows)): ?> <tr data-id="<?=$row->id?>" align="left">
<td><?=$row->id?></td>
<td><?=$row->name?></td>
<td><?=$row->description?></td>
<td>
<a href="edit_map.php?id=<?=$row->id?>" class="edit" title="Edit" data-toggle="tooltip">
<i class="material-icons">&#xE254;</i>
</a>
<?php if(is_dir(CACHE_DIR.'/'.$row->id)) { ?>
<a class="clear" title="Clear cache" data-toggle="tooltip">
<i class="material-icons">&#xf0ff;</i>
</a>
<?php } ?>
<a class="delete" title="Delete" data-toggle="tooltip">
<i class="material-icons">&#xE872;</i>
</a>
</td>
</tr> <?php endwhile; ?>
</tbody>
</table>
</div>
</div>
<footer class="footer text-center">
</footer>
</div>
</div>
<script src="dist/js/sidebarmenu.js"></script>
<script src="dist/js/custom.js"></script>
<script>new DataTable('#sortTable', { paging: false });</script>
</body>
</html>

357
admin/permalinks.php Normal file
View File

@ -0,0 +1,357 @@
<?php
session_start();
require('incl/const.php');
require('class/database.php');
require('class/map.php');
require('class/permalink.php');
require('class/access_groups.php');
if(!isset($_SESSION['user']) || $_SESSION['user']->accesslevel != 'Admin') {
header('Location: ../login.php');
exit;
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$dbconn = $database->getConn();
$tab = 'permalink'; $action = 'permalink';
$conn_obj = new permalink_Class($dbconn, $_SESSION['user']->id);
$conn_rows = $conn_obj->getRows();
$maps_obj = new map_Class($dbconn, $_SESSION['user']->id);
$map_rows = $maps_obj->getRows();
$maps = array();
while($row = pg_fetch_assoc($map_rows)){
$maps[$row['id']] = $row;
}
?>
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<?php include("incl/meta.php"); ?>
<link href="dist/css/table.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap4.min.css">
<script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap4.min.js"></script>
<style type="text/css">
td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active {
color: #fff;
background-color: #666;
border-color: #f1f9ff #f1f9ff #fff;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
var actions = `
<a class="add" title="Add" data-toggle="tooltip">
<i class="material-icons">&#xE03B;</i>
</a>
<a class="edit" title="Edit" data-toggle="tooltip">
<i class="material-icons">&#xE254;</i>
</a>
<a class="delete" title="Delete" data-toggle="tooltip">
<i class="material-icons">&#xE872;</i>
</a>
`;
//$("table td:last-child").html();
// Append table with add row form on add new button click
$(".add-new").click(function() {
//var actions = $("table td:last-child").html();
$(this).attr("disabled", "disabled");
var index = $("table tbody tr:last-child").index();
var row = '<tr>';
$("table thead tr th").each(function(k, v) {
if($(this).attr('data-editable') == 'false') {
if($(this).attr('data-action') == 'true') { // last child or actions cell
row += '<td>'+actions+'</td>';
} else {
row += '<td></td>';
}
}
else {
if($(this).attr('data-type') == 'select') {
if($(this).attr('data-name') == 'map_id') {
row += `
<td data-type="select" data-value="0">
<select name="`+$(this).attr('data-name')+`">
<?PHP foreach($maps as $k => $v) { ?>
<option value="<?=$k?>"><?=$v['name']?></option>
<?PHP } ?>
</select>
</td>
`;
}
}else{
row += ' <td> <input type = "text" class = "form-control" name="'+$(this).attr('data-name')+'"> </td>';
}
}
});
row += '</tr>';
$("table").append(row);
$("table tbody tr").eq(index + 1).find(".add, .edit").toggle();
$('[data-toggle="tooltip"]').tooltip();
});
// Add row on add button click
$(document).on("click", ".add", function() {
var obj = $(this);
var empty = false;
var input = $(this).parents("tr").find('input[type="text"], select');
input.each(function() {
if (!$(this).val() && ($(this).attr('name') != 'query')) {
$(this).addClass("error");
empty = true;
} else {
$(this).removeClass("error");
}
});
$(this).parents("tr").find(".error").first().focus();
if (!empty) {
var data = {};
data['save'] = 1;
data['id'] = $(this).closest('tr').attr('data-id');
input.each(function() {
if($(this).closest('td').attr('data-type') == 'select') {
var val = $(this).find('option:selected').text();
$(this).parent("td").attr('data-value', $(this).val());
$(this).parent("td").html(val);
}else {
$(this).parent("td").html($(this).val());
}
data[$(this).attr('name')] = $(this).val();
});
$.ajax({
type: "POST",
url: 'action/permalink.php',
data: data,
dataType:"json",
success: function(response){
if(response.id) { // means, new record is added
obj.closest('table').find('tr:last-child').attr('data-id', response.id);
obj.closest('table').find('tr:last-child td:first-child').text(response.id)
obj.closest('table').find('tr:last-child td').eq(3).text(response.created);
obj.closest('table').find('tr:last-child td').eq(4).text(response.expires);
obj.closest('table').find('tr:last-child td').eq(5).text(0);
var perma_link = '<a href="../' + response.url + '">' + response.hash + '</a>';
obj.closest('table').find('tr:last-child td').eq(7).html(perma_link);
}
alert(response.message)
}
});
$(this).parents("tr").find(".add, .edit").toggle();
$(".add-new").removeAttr("disabled");
}
});
// Edit row on edit button click
$(document).on("click", ".edit", function() {
$(this).parents("tr").find("td:not([data-editable=false])").each(function(k, v) {
if($(this).closest('table').find('thead tr th').eq(k).attr('data-editable') != 'false') {
var name = $(this).closest('table').find('thead tr th').eq(k).attr('data-name');
var id = $(this).closest('tr').attr('data-id');
if($(this).closest('table').find('thead tr th').eq(k).attr('data-type') == 'select') {
if(name == 'map_id') {
$(this).html(`
<select name="`+name+`" multiple>
<?PHP foreach($maps as $k => $v) { ?>
<option value="<?=$k?>"><?=$v['name']?></option>
<?PHP } ?>
</select>
`);
}
var val = $(this).attr('data-value').split(',');
$(this).find('[name='+name+']').val(val);
} else {
var val = $(this).html().replace('<br>', '&');
$(this).html(' <input type = "text" name="'+ name +'" class = "form-control" value = "' + val + '" > ');
}
}
});
$(this).parents("tr").find(".add, .edit").toggle(); $(".add-new").attr("disabled", "disabled");
});
// Delete row on delete button click
$(document).on("click", ".delete", function() {
var obj = $(this);
var data = {'delete': true, 'id': obj.parents("tr").attr('data-id')}
if(confirm('Connection will be deleted ?')){
$.ajax({
type: "POST",
url: 'action/permalink.php',
data: data,
dataType:"json",
success: function(response){
if(response.success) { // means, new record is added
obj.parents("tr").remove();
}
alert(response.message);
}
});
}
});
});
</script>
<style>
.card {
position: relative;
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
border: none!important;
border-radius: inherit;
text-decoration: none!important;
}
.bg-warning {
background-color: #50667f!important;
}
</style>
</head>
<body>
<div id="main-wrapper" data-layout="vertical" data-navbarbg="skin5" data-sidebartype="full"
data-sidebar-position="absolute" data-header-position="absolute" data-boxed-layout="full">
<?php define('MENU_SEL', 'permalinks.php');
include("incl/topbar.php");
include("incl/sidebar.php");
?>
<div class="page-wrapper">
<div class="page-breadcrumb" style="padding-left:30px; padding-right: 30px; padding-top:0px; padding-bottom: 0px">
<div class="row align-items-center">
<div class="col-6">
<nav aria-label="breadcrumb">
</nav>
<h1 class="mb-0 fw-bold">Permalinks</h1>
</div>
<div class="col-6">
<div class="text-end upgrade-btn">
<div class="text-end upgrade-btn">
<a class="btn btn-primary text-white add-new" role="button" aria-pressed="true"><i class="fa fa-plus"></i> Add New</a>
</div>
</div>
</div>
</div>
<div class="container-fluid"><p>&nbsp;</p>
<div class="table-responsive">
<table class="table table-bordered" id="sortTable">
<thead>
<tr>
<th data-name="id" data-editable='false'>ID</th>
<th data-name="map_id" data-type="select">Map</th>
<th data-name="description">Description</th>
<th data-name="query">Query</th>
<th data-name="created" data-editable='false'>Created</th>
<th data-name="expires">Expires</th>
<th data-name="visits" data-editable='false'>Visits</th>
<th data-name="visits_limit" >Limit</th>
<th data-name="hash" data-editable='false'>Hash</th>
<th data-editable='false' data-action='true'>Actions</th>
</tr>
</thead>
<tbody> <?php while($row = pg_fetch_object($conn_rows)): ?> <tr data-id="<?=$row->id?>" align="left">
<td><?=$row->id?></td>
<td data-type="select" data-value="<?=$row->map_id?>"><?=$maps[$row->map_id]['name']?></td>
<td><?=$row->description?></td>
<td><?=str_replace('&', '</br>', urldecode($row->query))?></td>
<td><?=$row->created?></td>
<td><?=$row->expires?></td>
<td><?=$row->visits?></td>
<td><?=$row->visits_limit?></td>
<td>
<a href="../apps/<?=$row->map_id?>/index.php?permalink=<?=$row->hash?>"><?=$row->hash?></a>
</td>
<td>
<a class="add" title="Add" data-toggle="tooltip"><i class="material-icons">&#xE03B;</i></a>
<a class="edit" title="Edit" data-toggle="tooltip"><i class="material-icons">&#xE254;</i></a>
<a class="delete" title="Delete" data-toggle="tooltip"><i class="material-icons">&#xE872;</i></a>
</td>
</tr> <?php endwhile; ?>
</tbody>
</table>
</div>
<div class="row">
<p>&nbsp;</p>
<div class="col-6" style="width: 50%!important">
<p>&nbsp;</p>
<div class = "alert alert-success">
<a href = "#" class = "close" data-dismiss = "alert">&times;</a>
<strong>Note:</strong> Permalinks will be auto-deleted on when they expire by time or run out of visits. Limit of 0, disables visits limitation.
</div>
<div class = "alert alert-warning">
<a href = "#" class = "close" data-dismiss="alert">&times;</a>
<strong>Note:</strong> In expires you can write PostgreSQL TIMESTAMP values, like '1 hour', '1 day'.
For more <a href="https://www.postgresql.org/docs/current/functions-datetime.html" target="_blank">see here.</a>
</div>
</div>
</div>
</div>
<script>new DataTable('#sortTable', { paging: false });</script>
<footer class="footer text-center">
</footer>
</div>
</div>
<script src="dist/js/sidebarmenu.js"></script>
<script src="dist/js/custom.js"></script>
</body>
</html>

216
admin/registration.php Normal file
View File

@ -0,0 +1,216 @@
<?php
session_start();
require('incl/const.php');
require('class/user.php');
require('class/access_groups.php');
require('class/database.php');
if(!isset($_SESSION['user']) || $_SESSION['user']->accesslevel != 'Admin') {
header('Location: ../login.php');
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$dbconn = $database->getConn();
$acc_obj = new access_group_Class($dbconn, $_SESSION['user']->id);
$acc_grp = $acc_obj->getRowsArr();
if(isset($_POST['submit'])&&!empty($_POST['submit'])){
$usr_obj = new user_Class($dbconn, $_SESSION['user']->id);
$_POST['ftp_user'] = '';
if(empty($_POST['groups'])){
$_POST['groups'] = array(1); // give new admin Default group for now
}
$newId = $usr_obj->create($_POST);
if($newId > 0){
if($_POST['accesslevel'] == 'Admin'){
$myuser_result = $usr_obj->getById($newId);
$myuser = pg_fetch_assoc($myuser_result);
pg_free_result($myuser_result);
$email_user = explode('@', $_POST['email'])[0];
$_POST['ftp_user'] = $email_user.$newId;
user_Class::create_ftp_user($_POST['ftp_user'], $_POST['email'], $myuser['password']);
// create def access group for new admin
$def_grp = array('name' => $_POST['ftp_user'], 'userids' => array($newId));
$acc_obj = new access_group_Class($dbconn, $newId);
$grp_id = $acc_obj->create($def_grp);
if($grp_id > 0){
$_POST['id'] = $newId;
$_POST['groups'] = array($grp_id);
$usr_obj->update($_POST);
}
}
header("Location: users.php");
}else{
echo "Something Went Wrong";
}
}
?>
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<?php include("incl/meta.php"); ?>
<link href="dist/css/table.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$(document).on("change", "#accesslevel", function() {
var obj = $(this);
const acc_level = obj.find('option:selected').text();
if(acc_level == 'User'){
$('#acc_grp_div').show(); $('#acc_grp_div').attr('required', true);
}else{
$('#acc_grp_div').hide(); $('#acc_grp_div').attr('required', false);
}
});
});
</script>
</head>
<body>
<div id="main-wrapper" data-layout="vertical" data-navbarbg="skin5" data-sidebartype="full"
data-sidebar-position="absolute" data-header-position="absolute" data-boxed-layout="full">
<?php define('MENU_SEL', 'registration.php');
include("incl/topbar.php");
include("incl/sidebar.php");
?>
<div class="page-wrapper">
<div class="page-breadcrumb" style="padding-left:30px; padding-right: 30px; padding-top:0px; padding-bottom: 0px">
<div class="row align-items-center">
<div class="col-6">
<nav aria-label="breadcrumb">
</nav>
<h1 class="mb-0 fw-bold">Register New User</h1>
</div>
<div class="col-6">
<div class="text-end upgrade-btn">
</div>
</div>
</div>
</div>
<div class="container-fluid">
<table class="table table-bordered">
<tbody>
<form method="post">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control" id="name" placeholder="Enter name" name="name" required>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" class="form-control" id="email" placeholder="Enter email" name="email" required>
</div>
<div class="form-group">
<label for="accesslevel">Access Level:</label>
<select name="accesslevel" id="accesslevel">
<option value="User">User</option>
<?php if($_SESSION['user']->id == SUPER_ADMIN_ID) { // only super admin can create admins ?>
<option value="Admin">Admin</option>
<?php } ?>
</select>
</div>
<div class="form-group" id="acc_grp_div">
<fieldset>
<legend>Access Groups:</legend>
<?php
foreach($acc_grp as $group_id => $name){ ?>
<p>
<input type="checkbox" name="groups[]" id="group_<?=$group_id?>" value="<?=$group_id?>"/>
<label for="group_<?=$group_id?>" class="form-label"><?=$name?></label>
</p>
<?php } ?>
</fieldset>
</div>
<div class="form-group">
<label for="pwd">Password:</label>
<input type="password" class="form-control" id="password" placeholder="Enter password" name="password">
</div>
<input type="submit" name="submit" class="btn btn-primary" value="Submit">
</form>
</tbody>
</table>
<div class="row">
<div class="col-6">
<p>&nbsp;</p>
<div id = "repThumbnail" class = "alert alert-danger">
<a href = "#" class = "close" data-dismiss = "alert">&times;</a>
<strong>Note:</strong> Be sure to set the Access Level for the user.
</div>
<script type = "text/javascript">
$(function(){
$(".close").click(function(){
$("#repThumbnail").alert();
});
});
</script>
</div>
</div>
</div>
<footer class="footer text-center">
</footer>
</div>
</div>
<script src="dist/js/sidebarmenu.js"></script>
<script src="dist/js/custom.js"></script>
</body>
</html>

253
admin/setup.php Normal file
View File

@ -0,0 +1,253 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
require('class/user.php');
if(file_exists('incl/const.php')){
require('incl/const.php');
}
$msg="";
$smtp_keys = ['host', 'user', 'pass', 'port'];
$host=empty(DB_HOST) ? '' : DB_HOST;
$port=empty(DB_PORT) ? '' : DB_PORT;
$dbuname=empty(DB_USER) ? '' : DB_USER;
$dbpwd=empty(DB_PASS) ? '' : DB_PASS;
$dbname=empty(DB_NAME) ? '' : DB_NAME;
$apps_dir=empty(APPS_DIR) ? '' : APPS_DIR;
$data_dir=empty(DATA_DIR) ? '' : DATA_DIR;
$cache_dir=empty(CACHE_DIR) ? '' : CACHE_DIR;
if(isset($_POST['submit'])){
$host=$_POST['host'];
$port=$_POST['port'];
$dbuname=$_POST['dbuname'];
$dbpwd=$_POST['dbpwd'];
$dbname=$_POST['dbname'];
$con = pg_connect("dbname=$dbname user=$dbuname password=$dbpwd host=$host port=$port");
if(!$con){
$msg= pg_last_error($con);
}else{
$file_data = "<?php\n";
$file_data .= "const DB_HOST = '$host';\n";
$file_data .= "const DB_NAME = '$dbname';\n";
$file_data .= "const DB_SCMA = 'public';\n";
$file_data .= "const DB_USER = '$dbuname';\n";
$file_data .= "const DB_PASS = '$dbpwd';\n";
$file_data .= "const DB_PORT = '$port';\n";
$file_data .= "const SUPER_ADMIN_ID = 1;\n";
$file_data .= "const ACCESS_LEVELS = array('User', 'Admin', 'Devel');\n";
$file_data .= "const ADMINISTRATION_ACCESS_LEVELS = array('Admin', 'Devel');\n";
$file_data .= "const APPS_DIR = '$apps_dir';\n";
$file_data .= "const DATA_DIR = '$data_dir';\n";
$file_data .= "const CACHE_DIR = '$cache_dir';\n";
foreach($smtp_keys as $k){
$file_data .= "const SMTP_".strtoupper($k)." = '".$_POST['smtp_'.$k]."';\n";
}
file_put_contents('incl/const.php', $file_data);
$sql = file_get_contents('setup.sql');
$res = pg_query($con, $sql);
if(!$res){
echo pg_last_error($con);
die();
}
$def_user = array('name' => 'John Smith', 'email' => 'admin@admin.com', 'password' => '1234',
'accesslevel' => 'Admin', 'ftp_user' => 'admin1', 'owner_id' => 1);
$def_grp = array('name' => 'Default', 'owner_id' => 1);
$def_usr_grps = array('user_id' => 1, 'access_group_id' => 1);
$def_user['password'] = password_hash($def_user['password'], PASSWORD_DEFAULT);
// insert manually
if(!pg_insert($con, 'public.user', $def_user) ||
!pg_insert($con, 'public.access_groups', $def_grp) ||
!pg_insert($con, 'public.user_access', $def_usr_grps) ){
die(pg_last_error($con));
}
user_Class::create_ftp_user($def_user['ftp_user'], $def_user['email'], $def_user['password']);
if(!isset($_POST['allow_signup'])){
$result = pg_query($con, 'DROP TABLE signup');
unlink('../signup.php');
unlink('class/signup.php');
unlink('action/signup.php');
unlink('action/verify.php');
}
unlink('setup.sql');
unlink('setup.php');
header('location:index.php');
}
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>QGIS2Map App Installer</title>
<link href="../assets/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
table{width:30% !important; text-align:center; margin:auto; margin-top:70px;}
.success{color:green;}
.error{color:red;}
.frm{width:70% !important; margin:auto; margin-top:100px;}
</style>
</head>
<body>
<main role="main" class="container">
<?php
if((isset($_GET['step'])) && $_GET['step']==2){
?>
<div align="center"><p>&nbsp;</p><img src="img/jri-admin-logo.png"></div>
<form class="frm" method="post">
<span class="error"><?=$msg?></span>
<div>
<fieldset>
<legend>Database</legend>
<div class="form-group">
<input type="text" class="form-control" placeholder="Host" required name="host" value="<?=$host?>">
</div>
<div class="form-group">
<input type="number" class="form-control" placeholder="Port Number" required name="port" value="<?=$port?>">
</div>
<div class="form-group">
<input type="text" class="form-control" placeholder="Database User Name" required name="dbuname" value="<?=$dbuname?>">
</div>
<div class="form-group">
<input type="text" class="form-control" placeholder="Database Password" name="dbpwd" value="<?=$dbpwd?>">
</div>
<div class="form-group">
<input type="text" class="form-control" placeholder="Database Name" required name="dbname" value="<?=$dbname?>">
</div>
</fieldset>
</div>
<div>
<fieldset>
<legend>SMTP details:</legend>
<div class="form-group">
<?php foreach($smtp_keys as $k){ ?>
<input type="text" class="form-control" placeholder="<?=$k?>" name="smtp_<?=$k?>" value="" required>
<?php } ?>
</div>
</fieldset>
</div>
<div>
<fieldset>
<legend>Options</legend>
<div class="form-group">
<input type="checkbox" class="form-checkbox" placeholder="signup allowed" name="allow_signup" value="1"/>
<label for="allow_signup">Allow Sign-Up for Admin accounts</label>
</div>
</fieldset>
</div>
<div align="right">
<button type="submit" name="submit" class="btn btn-primary">Submit</button>
</div>
</form>
<?php
}else{
?>
<div align="center"><p>&nbsp;</p>QGIS2Map App Installer</div>
<table class="table">
<thead>
<tr>
<th scope="col">Requirement</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">PHP Version</th>
<td>
<?php
$is_error="";
$php_version=phpversion();
if($php_version>5){
echo "<span class='success'>".$php_version."</span>";
}else{
echo "<span class='error'>".$php_version."</span>";
$is_error='yes';
}
?>
</td>
</tr>
<tr>
<th scope="row">Session Working</th>
<td>
<?php
$_SESSION['IS_WORKING']=1;
if(!empty($_SESSION['IS_WORKING'])){
echo "<span class='success'>Yes</span>";
}else{
echo "<span class='error'>No</span>";
$is_error='yes';
}
?>
</td>
</tr>
<tr>
<th scope="row"><?=$apps_dir?></th>
<td>
<?php
if(is_writeable($apps_dir)){
echo "<span class='success'>Writeable</span>";
}else{
echo "<span class='error'>Not writeable</span>";
$is_error='yes';
}
?>
</td>
</tr>
<tr>
<td colspan="2">
<?php
if($is_error==''){
?>
<a href="?step=2"><button type="button" class="btn btn-success">Next</button></a>
<?php
}else{
?><button type="button" class="btn btn-danger">Errors</button><br><br>Please fix above error(s) and try again<?php
}
?>
</td>
</tr>
</tbody>
</table>
<?php }?>
</main>
<script src="https://getbootstrap.com/docs/4.0/dist/js/bootstrap.min.js"></script>
</body>
</html>

54
admin/setup.sql Normal file
View File

@ -0,0 +1,54 @@
CREATE TYPE public.userlevel AS ENUM ('Admin', 'User', 'Devel');
CREATE TABLE public.user ( id SERIAL PRIMARY KEY,
name character varying(250),
email character varying(250),
password character varying(255),
ftp_user character varying(250),
accesslevel public.userlevel,
owner_id integer NOT NULL REFERENCES public.user(id),
UNIQUE(email)
);
CREATE TABLE public.access_groups ( id SERIAL PRIMARY KEY,
name character varying(255) NOT NULL,
owner_id integer NOT NULL REFERENCES public.user(id)
);
CREATE TABLE public.user_access ( id SERIAL PRIMARY KEY,
user_id integer NOT NULL REFERENCES public.user(id),
access_group_id integer NOT NULL REFERENCES public.access_groups(id),
UNIQUE(user_id, access_group_id)
);
CREATE TABLE public.map ( id SERIAL PRIMARY KEY,
name character varying(50) NOT NULL,
description character varying(50) NOT NULL,
owner_id integer NOT NULL REFERENCES public.user(id)
);
CREATE TABLE public.map_access ( id SERIAL PRIMARY KEY,
map_id integer NOT NULL REFERENCES public.map(id),
access_group_id integer NOT NULL REFERENCES public.access_groups(id),
UNIQUE(map_id, access_group_id)
);
CREATE TABLE public.permalink ( id SERIAL PRIMARY KEY,
description character varying(255),
query character varying(255),
map_id integer NOT NULL REFERENCES public.map(id),
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + interval '1 hour',
visits integer NOT NULL DEFAULT 0,
visits_limit integer NOT NULL DEFAULT 1,
hash character varying(36) NOT NULL,
owner_id integer NOT NULL REFERENCES public.user(id)
);
CREATE TABLE public.signup ( id SERIAL PRIMARY KEY,
name character varying(250),
email character varying(250),
password character varying(250),
verify character varying(250),
UNIQUE(email)
);

View File

@ -0,0 +1,5 @@
<?php
const MAP_ID = MAP_ID_VALUE;
include('../../admin/snippets/index_prefix.php');
readfile('DATA_FILE');
?>

View File

@ -0,0 +1,40 @@
var VARNAME =
<?php
const MAP_ID = MAP_ID_VALUE;
include('../../admin/snippets/index_prefix.php');
const CACHE_PERIOD = CACHE_PERIOD_SECONDS;
const GS_URL = 'FULL_URL';
if(CACHE_PERIOD == 0){
readfile(GS_URL);
}else {
$js_file = CACHE_DIR.'/MAP_ID_VALUE/VARNAME_data.js';
if(!is_file($js_file) || (time() - filemtime($js_file)) > CACHE_PERIOD){
if(!is_dir(CACHE_DIR.'/MAP_ID_VALUE')){
mkdir(CACHE_DIR.'/MAP_ID_VALUE');
}
$fin = fopen(GS_URL, 'r');
$fout = fopen($js_file, 'w');
while(($contents = fread($fin, 4096))){
fwrite($fout, $contents);
}
fclose($fin);
fclose($fout);
}
readfile($js_file);
}
?>
;
{
let qc_id = 0;
VARNAME.features.forEach(function (feat) { feat.properties["qc_id"] = qc_id; qc_id = qc_id + 1;});
if(VARNAME.features[0].properties["gid"] === undefined){
let gid = 0;
VARNAME.features.forEach(function (feat) { feat.properties["gid"] = gid; gid = gid + 1;});
}
}

View File

@ -0,0 +1,33 @@
var VARNAME =
<?php
const MAP_ID = MAP_ID_VALUE;
include('../../admin/snippets/index_prefix.php');
const CACHE_PERIOD = CACHE_PERIOD_SECONDS;
if(CACHE_PERIOD == 0){
$proj_db = new Database("PG_HOST", "PG_DB", "PG_USER", "PG_PWD", PG_PORT, "public");
$proj_db->getGeoJSON("public", "PG_TBL", "geom");
}else {
$js_file = CACHE_DIR.'/MAP_ID_VALUE/VARNAME_data.js';
if(!is_file($js_file) || (time() - filemtime($js_file)) > CACHE_PERIOD){
if(!is_dir(CACHE_DIR.'/MAP_ID_VALUE')){
mkdir(CACHE_DIR.'/MAP_ID_VALUE');
}
$fout = fopen($js_file, 'w');
ob_start(function($buffer) use($fout){
fwrite($fout, $buffer);
}, 1024); //notice the use of chunk_size == 1
$proj_db = new Database("PG_HOST", "PG_DB", "PG_USER", "PG_PWD", PG_PORT, "public");
$proj_db->getGeoJSON("public", "PG_TBL", "geom");
ob_end_clean();
fclose($fout);
}
readfile($js_file);
}
?>
;

View File

@ -0,0 +1,63 @@
<?php
if(SHOW_DATATABLES){ ?>
<script>
$(document).ready(function() {
let lg_ids_i = 0;
<?php for($i=0; $i < count(JS_VARNAMES); $i++){
$varname = JS_VARNAMES[$i]; ?>
var columns_<?=$i?> = Object.keys(<?=$varname?>.features[0].properties).map(function(k){
return {'title' : k};
});
if (typeof <?=$varname?>_lg_ids !== 'undefined') {
columns_<?=$i?>.unshift({'title' : '#'});
}
lg_ids_i = 0;
const dataSet_<?=$i?> = <?=$varname?>.features.map(function(e) {
var props = Object.keys(e.properties).map(function(k){
return e.properties[k];
});
if (typeof <?=$varname?>_lg_ids !== 'undefined') {
var t = '<a href="javascript:void(0);" class="fa fa-search" onclick="focusLayer(\'<?=$varname?>_lg\',' + <?=$varname?>_lg_ids[lg_ids_i] + ')"></a>';
props.unshift(t);
lg_ids_i = lg_ids_i + 1;
}
return props;
});
$('#dataTable<?=$i?>').DataTable({
columns: columns_<?=$i?>,
deferRender: true,
data: dataSet_<?=$i?>
});
<?php } ?>
});
</script>
<div id='dataTables'>
<ul class="nav nav-tabs" role="tablist">
<?php $first = ' active'; $li_first = 'class="nav-item" role="presentation"';
for($i=0; $i < count(JS_VARNAMES); $i++){
$varname = JS_VARNAMES[$i]; ?>
<li <?=$li_first?>>
<button class="nav-link<?=$first?>" href="#tab-table<?=$i?>" data-bs-toggle="tab" data-bs-target="#tab-table<?=$i?>"><?=$varname?></button>
</li>
<?php $first = ''; $li_first = ''; } ?>
</ul>
<div class="tab-content pt-2">
<?php $first = ' show active';
for($i=0; $i < count(JS_VARNAMES); $i++){
$varname = JS_VARNAMES[$i]; ?>
<div class="tab-pane<?=$first?>" id="tab-table<?=$i?>">
<table id="dataTable<?=$i?>" class="table table-striped table-bordered" cellspacing="0" width="100%"></table>
<p class="mb-0"> <a href="javascript:void(0);" onclick="dataToCSV('<?=$varname?>', <?=$varname?>)">Export to CSV</a> </p>
</div>
<?php $first = ''; } ?>
</div>
</div>
<?php } ?>

View File

@ -0,0 +1,15 @@
<?php if(SHOW_DATATABLES) { ?>
var DtControl = L.Control.extend({
options: { position: 'topleft' },
onAdd: function(map) {
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
container.title = 'Hide/Show DataTables';
container.innerHTML = `<a href="#" id="dt-link" class="fa fa-table"></a>`;
L.DomEvent.disableClickPropagation(container); /* Prevent click events propagation to map */
return container;
}
});
map.addControl(new DtControl());
map.on('click', unfocusLayer);
<?php } ?>

View File

@ -0,0 +1,136 @@
<header>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<link href="../../assets/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="../../assets/dist/js/bootstrap.bundle.min.js"></script>
<div class="navbar navbar-dark bg-dark shadow-sm" style="background-color:#50667f!important">
<div class="container">
<a href="#" class="navbar-brand d-flex align-items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-text" viewBox="0 0 16 16">
<path d="M5.5 7a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1h-5zM5 9.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0 2a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5z"/>
<path d="M9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.5L9.5 0zm0 1v2A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
</svg> <strong> &nbsp;QatMaps</strong>
</a>
<?php if((isset($_SESSION['user'])) && ($_SESSION['user']->accesslevel == 'Admin')) { ?>
<a href="../../admin/index.php" style="text-decoration:none; color: #fff!important; font-size: 1.25rem; font-weight: 300;">Administration</a>
<a href="../../logout.php" style="text-decoration:none; color: #fff!important; font-size: 1.25rem; font-weight: 300;">Log Out</a>
<?php } else { ?>
<a href="../../index.php" style="text-decoration:none; color: #fff!important; font-size: 1.25rem; font-weight: 300;">Back to Dashboard</a>
<?php } ?>
</div>
</div>
</header>
<style>
.sidebar {
max-width: 300px;
background: white;
max-height: 400px;
overflow-x: hidden;
overflow-y: auto;
display: none;
}
.sidebar .close {
position: absolute;
right: 0;
}
</style>
<script>
var table_visible = false;
$(document).ready(function() {
$(document).on("click", ".copy", function() {
var obj = $(".modal-body");
var temp = $("<input>");
$("body").append(temp);
temp.val(obj.text()).select();
temp.focus();
document.execCommand("copy");
temp.remove();
});
$(document).on("click", "#fg-permalink", function() {
//href = /apps/57/index.php#11/41.8036/-87.6407
let path = window.location.href.split('/apps/')[1]; // 57/index.php#11/41.8036/-87.6407
let loc = path.split('#');
let map_id = loc[0].split('/');
$.get('../../admin/action/permalink.php?id=' + map_id[0] + '&loc=' + loc[1], function (data){
const response = $.parseJSON(data);
if(response.success){
var url = window.location.origin + '/' + response.url;
$('.modal-body').html( '<a href="' + url + '" target="_blank">' + url +'</a>');
$('#conn_modal').modal('show');
}
});
});
$(document).on("click", "#dt-link", function() {
const d = (table_visible) ? 1 : 2;
$("#map").height($(window).height() / d);
map.invalidateSize();
table_visible = !table_visible;
});
});
</script>
<?php if(SHOW_DATATABLES) { ?>
<script>
var saved_lg_name = null;
var saved_layer_id = null;
const on_focus_style = {
color: "#efefef",
fillColor: "#efefef",
opacity: 1.0,
fillOpacity: 1.0,
weight: 1
};
function unfocusLayer(){
if(saved_lg_name != null){
var lg_name = saved_lg_name;
var l = window[lg_name].getLayer(saved_layer_id);
var lg_style = {
color: window[lg_name].options.color,
fillColor: window[lg_name].options.fillColor,
opacity: window[lg_name].options.opacity,
fillOpacity: window[lg_name].options.fillOpacity,
weight: window[lg_name].options.weight
};
l.setStyle(lg_style);
saved_lg_name = null;
saved_layer_id = null;
}
}
function focusLayer(lg_name, layer_id){
var l = window[lg_name].getLayer(layer_id);
unfocusLayer();
saved_lg_name = lg_name;
saved_layer_id = layer_id;
if(l.getLatLng){
map.flyTo(l.getLatLng());
}else{
var bounds = l.getBounds();
map.fitBounds(bounds); // Fit the map to the polygon bounds
map.panTo(bounds.getCenter()); // Or center on the polygon
}
l.setStyle(on_focus_style);
}
</script>
<script src="../../assets/dist/js/tbl2CSV.js"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap4.min.css">
<script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap4.min.js"></script>
<?php } ?>

View File

@ -0,0 +1,54 @@
<?php
session_start();
require('../../admin/incl/const.php');
require('../../admin/class/database.php');
require('../../admin/class/permalink.php');
require('../../admin/class/access_groups.php');
$loc = null;
const SHOW_DATATABLES = false;
const JS_VARNAMES = array();
if(isset($_GET['permalink']) || isset($_SESSION['permalink'])){
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$obj = new permalink_Class($database->getConn(), $_SESSION['user']->id);
if(isset($_GET['permalink'])){ // called from index.php
$_SESSION['permalink'] = $_GET['permalink']; // save permalink for data_{file,gs,pg}.php
$row = $obj->getMap($_GET['permalink'], 1);
}else{
$row = $obj->getMap($_SESSION['permalink'], 0);
}
if($row == null){
die('Sorry permalink is invalid or expired!');
}
$map_id = $row['map_id'];
if($map_id != MAP_ID){
die('Sorry permalink is not for this map!');
}
$loc = explode('/', $row['query']); // 11/41.8036/-87.6407
}else{
if(!isset($_SESSION['user'])) {
header('Location: ../../login.php');
exit;
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$map_id = MAP_ID;
$acc_obj = new access_group_Class($database->getConn(), $_SESSION['user']->id);
$usr_grps = $acc_obj->getByUserId($_SESSION['user']->id);
if(!count($usr_grps)){
die('Sorry, no access group!');
}
$usr_grps = $acc_obj->getGroupMapGroups(array_keys($usr_grps));
if(!count($usr_grps) || !isset($usr_grps[$map_id])){
die('Sorry, access not allowed!');
}
}
?>

View File

@ -0,0 +1,80 @@
<?php if(empty(DB_HOST)){ die("Error: Can\'t execute!"); } ?>
var VARNAME_data_lg = L.layerGroup(null, {
color: "STYLE_COLOR",
fillColor: "STYLE_FILL_COLOR",
opacity: STYLE_OPACITY,
fillOpacity: STYLE_FILL_OPACITY,
weight: 2
});
var VARNAME_data_lg_ids = [];
const VARNAME_data = <?php
function fn_VARNAME(){
$CACHE_PERIOD = CACHE_PERIOD_SECONDS;
$GS_URL = 'FULL_URL';
if($CACHE_PERIOD == 0){
readfile($GS_URL);
}else {
$js_file = CACHE_DIR.'/MAP_ID_VALUE/VARNAME_data.js';
if(!is_file($js_file) || (time() - filemtime($js_file)) > $CACHE_PERIOD){
if(!is_dir(CACHE_DIR.'/MAP_ID_VALUE')){
mkdir(CACHE_DIR.'/MAP_ID_VALUE');
}
$fin = fopen($GS_URL, 'r');
$fout = fopen($js_file, 'w');
while(($contents = fread($fin, 4096))){
fwrite($fout, $contents);
}
fclose($fin);
fclose($fout);
}
readfile($js_file);
}
}
fn_VARNAME();
?>;
var VARNAME = L.geoJson(VARNAME_data, {
style: {
color: "STYLE_COLOR",
fillColor: "STYLE_FILL_COLOR",
opacity: STYLE_OPACITY,
fillOpacity: STYLE_FILL_OPACITY,
weight: 2
},
onEachFeature(feature, layer) {
VARNAME_data_lg.addLayer(layer);
VARNAME_data_lg_ids.push(layer._leaflet_id);
var on_event = 'click';
layer.on(on_event, function(e) {
var properties = feature.properties;
var html = '<table style="text-align: left; font-size: 100%;">';
for (const key in properties) {
html += '<tr><th>'+ key +"</th> <td>"+ properties[key] + "</td></tr>";
}
html += '</table>';
$('.sidebar .table-container').html(html);
$('.sidebar').show();
});
},
pointToLayer(feature, latlng) {
return L.circleMarker(latlng, {
radius: 6,
color: "STYLE_COLOR",
fillColor: "STYLE_FILL_COLOR",
opacity: STYLE_OPACITY,
fillOpacity: STYLE_FILL_OPACITY,
weight: 2
});
}
});

View File

@ -0,0 +1,10 @@
var CustomControlPermalink = L.Control.extend({
options: { position: 'topleft'},
onAdd: function(map) {
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
container.innerHTML = `<a href="#" id="fg-permalink" class="fa fa-share-alt"></a>`;
L.DomEvent.disableClickPropagation(container); // Prevent click events propagation to map
return container;
}
});
map.addControl(new CustomControlPermalink());

View File

@ -0,0 +1,15 @@
<div id="conn_modal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<p>This permalink will be available for 1h and deleted after 1 visit.</p>
</div>
<div class="modal-body" id="modal-body"><p>Permalink URL.</p></div>
<div class="modal-footer">
<button type="button" class="btn btn-primary copy">Copy</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,19 @@
L.control.browserPrint({
title: 'Just print me!',
documentTitle: 'Map printed using leaflet.browser.print plugin',
printLayer: L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
subdomains: 'abcd',
minZoom: 1,
maxZoom: 16,
ext: 'png'
}),
closePopupsOnPrint: false,
printModes: [
L.BrowserPrint.Mode.Landscape(),
"Portrait",
L.BrowserPrint.Mode.Auto("B4",{title: "Auto"}),
L.BrowserPrint.Mode.Custom("B5",{title:"Select area"})
],
manualMode: false
}).addTo(map);

View File

@ -0,0 +1,41 @@
<?php
session_start();
require('../../admin/incl/const.php');
require('../../admin/class/database.php');
require('../../admin/class/access_groups.php');
if(!isset($_SESSION['user'])) {
header('Location: ../../login.php');
exit;
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$map_id = MAP_ID;
$acc_obj = new access_group_Class($database->getConn(), $_SESSION['user']->id);
$usr_grps = $acc_obj->getByUserId($_SESSION['user']->id);
if(!count($usr_grps)){
die('Sorry, no access group!');
}
$usr_grps = $acc_obj->getGroupMapGroups(array_keys($usr_grps));
if(!count($usr_grps) || !isset($usr_grps[$map_id])){
die('Sorry, access not allowed!');
}
// https://shop.chicagotvguides.com/geoserver/wms?service=WMS&request=GetFeatureInfo&version=1.1.1&layers=topp%3Astates&styles=&format=image%2Fpng&transparent=true&continuousWorld=true&tiled=true&info_format=text%2Fhtml&width=1374&height=852&srs=EPSG%3A3857&bbox=-12983287.876406899%2C1633917.916623927%2C-6261721.357121639%2C5801876.194958019&query_layers=topp%3Astates&X=653&Y=285
const BASE_URL = 'BASE_URL_VALUE';
readfile(BASE_URL.'?'.$_SERVER['QUERY_STRING']);
/*define("COOKIE_FILE", "/tmp/cookie.txt");
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, BASE_URL.'?'.$_SERVER['QUERY_STRING']);
curl_setopt($curl, CURLOPT_COOKIEJAR, COOKIE_FILE);
curl_setopt($curl, CURLOPT_USERPWD, "admin:geoserver");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 0);
curl_setopt($curl, CURLOPT_FORBID_REUSE, TRUE);
//curl_setopt($curlHandle, CURLOPT_HTTPHEADER, array('Connection: close'));
$result = curl_exec($curl);
curl_close($curl);*/
?>

View File

@ -0,0 +1,406 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Simple Transactional Email</title>
<style>
/* -------------------------------------
GLOBAL RESETS
------------------------------------- */
/*All the styling goes here*/
img {
border: none;
-ms-interpolation-mode: bicubic;
max-width: 100%;
}
body {
background-color: #f6f6f6;
font-family: sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
line-height: 1.4;
margin: 0;
padding: 0;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
table {
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 100%; }
table td {
font-family: sans-serif;
font-size: 14px;
vertical-align: top;
}
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
.body {
background-color: #f6f6f6;
width: 100%;
}
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
.container {
display: block;
margin: 0 auto !important;
/* makes it centered */
max-width: 580px;
padding: 10px;
width: 580px;
}
/* This should also be a block element, so that it will fill 100% of the .container */
.content {
box-sizing: border-box;
display: block;
margin: 0 auto;
max-width: 580px;
padding: 10px;
}
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
background: #ffffff;
border-radius: 3px;
width: 100%;
}
.wrapper {
box-sizing: border-box;
padding: 20px;
}
.content-block {
padding-bottom: 10px;
padding-top: 10px;
}
.footer {
clear: both;
margin-top: 10px;
text-align: center;
width: 100%;
}
.footer td,
.footer p,
.footer span,
.footer a {
color: #999999;
font-size: 12px;
text-align: center;
}
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3,
h4 {
color: #000000;
font-family: sans-serif;
font-weight: 400;
line-height: 1.4;
margin: 0;
margin-bottom: 30px;
}
h1 {
font-size: 35px;
font-weight: 300;
text-align: center;
text-transform: capitalize;
}
p,
ul,
ol {
font-family: sans-serif;
font-size: 14px;
font-weight: normal;
margin: 0;
margin-bottom: 15px;
}
p li,
ul li,
ol li {
list-style-position: inside;
margin-left: 5px;
}
a {
color: #3498db;
text-decoration: underline;
}
/* -------------------------------------
BUTTONS
------------------------------------- */
.btn {
box-sizing: border-box;
width: 100%; }
.btn > tbody > tr > td {
padding-bottom: 15px; }
.btn table {
width: auto;
}
.btn table td {
background-color: #ffffff;
border-radius: 5px;
text-align: center;
}
.btn a {
background-color: #ffffff;
border: solid 1px #3498db;
border-radius: 5px;
box-sizing: border-box;
color: #3498db;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-weight: bold;
margin: 0;
padding: 12px 25px;
text-decoration: none;
text-transform: capitalize;
}
.btn-primary table td {
background-color: #3498db;
}
.btn-primary a {
background-color: #3498db;
border-color: #3498db;
color: #ffffff;
}
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.align-left {
text-align: left;
}
.clear {
clear: both;
}
.mt0 {
margin-top: 0;
}
.mb0 {
margin-bottom: 0;
}
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0;
}
.powered-by a {
text-decoration: none;
}
hr {
border: 0;
border-bottom: 1px solid #f6f6f6;
margin: 20px 0;
}
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
/* -------------------------------------
PRESERVE THESE STYLES IN THE HEAD
------------------------------------- */
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
</style>
</head>
<body class="">
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
<tr>
<td>&nbsp;</td>
<td class="container">
<div class="content">
<!-- START CENTERED WHITE CONTAINER -->
<table role="presentation" class="main">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<p>Hi USER_NAME,</p>
<p>Click below to verify your email address for QatMaps.</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
<tbody>
<tr>
<td align="left">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td> <a href="VERIFY_URL" target="_blank">Verify Email</a> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p>Questions? Be sure to read the <a href="https://qatmaps.docs.acugis.com">Docs</a></p>
<p>Happy Mapping!.</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="content-block">
<span class="apple-link">AcuGIS</span><br>
<span class="apple-link">Cited, Inc. Wilmington, Delaware 19802</span>
<br> Don't like these emails? <a href="http://i.imgur.com/CScmqnj.gif">Unsubscribe</a>.
</td>
</tr>
<tr>
<td class="content-block powered-by">
Powered by <a href="http://htmlemail.io">HTMLemail</a>.
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
</div>
</td>
<td>&nbsp;</td>
</tr>
</table>
</body>
</html>

318
admin/users.php Normal file
View File

@ -0,0 +1,318 @@
<?php
session_start();
require('incl/const.php');
require('class/database.php');
require('class/user.php');
require('class/access_groups.php');
if(!isset($_SESSION['user']) || $_SESSION['user']->accesslevel != 'Admin') {
header('Location: ../login.php');
exit;
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$dbconn = $database->getConn();
$obj = new user_Class($dbconn, $_SESSION['user']->id);
$users = $obj->getRows();
$myuser_result = $obj->getById($_SESSION['user']->id);
$myuser = pg_fetch_assoc($myuser_result);
pg_free_result($myuser_result);
$acc_obj = new access_group_Class($dbconn, $_SESSION['user']->id);
$acc_grps = $acc_obj->getRowsArr();
?>
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<?php include("incl/meta.php"); ?>
<link href="dist/css/table.css" rel="stylesheet">
<script type="text/javascript">
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
// Append table with add row form on add new button click
$(".add-new").click(function() {
//var actions = $("table td:last-child").html();
$(this).attr("disabled", "disabled");
var index = $("table tbody tr:last-child").index();
var row = '<tr>';
$("table thead tr th").each(function(k, v) {
if($(this).attr('data-editable') == 'false') {
if($(this).attr('data-action') == 'true') { // last child or actions cell
row += '<td>'+actions+'</td>';
}
else {
row += '<td></td>';
}
}
else {
if($(this).attr('data-type') == 'select') {
if($(this).attr('data-name') == 'groups') {
row += `
<td data-type="select" data-value="0">
<select name="`+$(this).attr('data-name')+`" multiple>
<?PHP foreach($acc_grps as $k => $v) { ?>
<option value="<?=$k?>"><?=$v?></option>
<?PHP } ?>
</select>
</td>
`;
}
else if($(this).attr('data-name') == 'accesslevel') {
row += `
<td data-type="select" data-value="0">
<select name="`+$(this).attr('data-name')+`">
<?PHP foreach(ACCESS_LEVELS as $k) { ?>
<option value="<?=$k?>"><?=$k?></option>
<?PHP } ?>
</select>
</td>
`;
}
}
else {
row += ' <td> <input type = "text" class = "form-control" name="'+$(this).attr('data-name')+'"> </td>';
}
}
});
row += '</tr>';
$("table").append(row);
$("table tbody tr").eq(index + 1).find(".add, .edit").toggle();
$('[data-toggle="tooltip"]').tooltip();
});
// Add row on add button click
$(document).on("click", ".add", function() {
var obj = $(this);
var empty = false;
var input = $(this).parents("tr").find('input[type="text"], select');
input.each(function() {
if (!$(this).val()) {
$(this).addClass("error");
empty = true;
} else {
$(this).removeClass("error");
}
});
$(this).parents("tr").find(".error").first().focus();
if (!empty) {
var data = {};
data['save'] = 1;
data['id'] = $(this).closest('tr').attr('data-id');
input.each(function() {
if($(this).closest('td').attr('data-type') == 'select') {
var val = $(this).find('option:selected').text();
$(this).parent("td").attr('data-value', $(this).val());
$(this).parent("td").html(val);
}else {
$(this).parent("td").html($(this).val());
}
data[$(this).attr('name')] = $(this).val();
});
$.ajax({
type: "POST",
url: 'action/user.php',
data: data,
dataType:"json",
success: function(response){
if(response.id) { // means, new record is added
obj.closest('table').find('tr:last-child').attr('data-id', response.id);
obj.closest('table').find('tr:last-child td:first-child').text(response.id)
}
alert(response.message)
}
});
$(this).parents("tr").find(".add, .edit").toggle();
$(".add-new").removeAttr("disabled");
}
});
// Edit row on edit button click
$(document).on("click", ".edit", function() {
$(this).parents("tr").find("td:not([data-editable=false])").each(function(k, v) {
if($(this).closest('table').find('thead tr th').eq(k).attr('data-editable') != 'false') {
var name = $(this).closest('table').find('thead tr th').eq(k).attr('data-name');
var id = $(this).closest('tr').attr('data-id');
if($(this).closest('table').find('thead tr th').eq(k).attr('data-type') == 'select') {
if(name == 'accesslevel') {
$(this).html(`
<select name="`+name+`">
<?PHP foreach(ACCESS_LEVELS as $k) { ?>
<option value="<?=$k?>"><?=$k?></option>
<?PHP } ?>
</select>
`);
var val = $(this).attr('data-value');
$(this).find('[name='+name+']').val(val);
} else if(name == 'groups') {
$(this).html(`
<select name="`+name+`" multiple>
<?PHP foreach($acc_grps as $k => $v) { ?>
<option value="<?=$k?>"><?=$v?></option>
<?PHP } ?>
</select>
`);
}
var val = $(this).attr('data-value').split(',');
$(this).find('[name='+name+']').val(val);
} else {
$(this).html(' <input type = "text" name="'+ name +'" class = "form-control" value = "' + $(this).text() + '" > ');
}
}
});
$(this).parents("tr").find(".add, .edit").toggle();
$(".add-new").attr("disabled", "disabled");
});
// Delete row on delete button click
$(document).on("click", ".delete", function() {
var obj = $(this);
var data = {'delete': true, 'id': obj.parents("tr").attr('data-id')}
$.ajax({
type: "POST",
url: 'action/user.php',
data: data,
dataType:"json",
success: function(response){
if(response.success) { // means, new record is added
obj.parents("tr").remove();
}
$(".add-new").removeAttr("disabled");
alert(response.message);
}
});
});
});
</script>
</head>
<body>
<div id="main-wrapper" data-layout="vertical" data-navbarbg="skin5" data-sidebartype="full"
data-sidebar-position="absolute" data-header-position="absolute" data-boxed-layout="full">
<?php const MENU_SEL = 'users.php';
include("incl/topbar.php");
include("incl/sidebar.php");
?>
<div class="page-wrapper">
<div class="page-breadcrumb" style="padding-left:30px; padding-right: 30px; padding-top:0px; padding-bottom: 0px">
<div class="row align-items-center">
<div class="col-6">
<nav aria-label="breadcrumb">
</nav>
<h1 class="mb-0 fw-bold">Users</h1>
</div>
<div class="col-6">
<div class="text-end upgrade-btn">
<a href="registration.php" class="btn btn-info btn-md active" role="button" aria-pressed="true">Add User</a>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<table class="table table-bordered">
<thead>
<tr>
<th data-name="id" data-editable='false'>ID</th>
<th data-name="name">name</th>
<th data-name="email" data-editable='false'>Email</th>
<th data-name="password">Password</th>
<th data-name="ftp_user" data-editable='false'>FTP User</th>
<th data-name="accesslevel" data-type="select">Access Level</th>
<th data-name="groups" data-type="select">Access Groups</th>
<th data-editable='false' data-action='true'>Actions</th>
</tr>
</thead>
<tbody> <?php while($user = pg_fetch_object($users)): ?> <tr data-id="<?=$user->id?>" align="left">
<td><?=$user->id?> </td>
<td><?= $user->name?></td>
<td><?= $user->email?></td>
<td><?= $user->password?></td>
<td><?= $user->ftp_user?></td>
<td data-type="select" data-value="<?=$user->accesslevel?>"><?=$user->accesslevel?></td>
<?php
$usr_acc_grps = $acc_obj->getByUserId($user->id);
$grp_ids = implode(',',array_keys($usr_acc_grps));
$grp_names = implode(',',array_values($usr_acc_grps));
?>
<td data-type="select" data-value="<?=$grp_ids?>"><?=$grp_names?></td>
<td>
<a class="add" title="Add" data-toggle="tooltip">
<i class="material-icons">&#xE03B;</i>
</a>
<a class="edit" title="Edit" data-toggle="tooltip">
<i class="material-icons">&#xE254;</i>
</a>
<a class="delete" title="Delete" data-toggle="tooltip">
<i class="material-icons">&#xE872;</i>
</a>
</td>
</tr> <?php endwhile; ?>
</tbody>
</table>
<div class="row">
<div class="col-6" style="width: 50%!important">
<div class = "alert alert-success">
<a href = "#" class = "close" data-dismiss = "alert">&times;</a>
<strong>Note:</strong> Your personal FTP login username is <b><?=$myuser['ftp_user']?></b>. For password use your login password.
</div>
</div>
</div>
</div>
</div>
</div>
<!--Menu sidebar -->
<script src="dist/js/sidebarmenu.js"></script>
<!--Custom JavaScript -->
<script src="dist/js/custom.js"></script>
</body>
</html>

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="408" fill="#fff"><path d="M106.342 0c-29.214 0-50.827 25.58-49.86 53.32.927 26.647-.278 61.165-8.966 89.31C38.802 170.862 24.07 188.707 0 191v26c24.069 2.293 38.802 20.138 47.516 48.37 8.688 28.145 9.893 62.663 8.965 89.311C55.515 382.42 77.128 408 106.342 408h299.353c29.214 0 50.827-25.58 49.861-53.319-.928-26.648.277-61.166 8.964-89.311 8.715-28.232 23.411-46.077 47.48-48.37v-26c-24.069-2.293-38.765-20.138-47.48-48.37-8.687-28.145-9.892-62.663-8.964-89.31C456.522 25.58 434.909 0 405.695 0H106.342zm236.559 251.102c0 38.197-28.501 61.355-75.798 61.355h-87.202a2 2 0 01-2-2v-213a2 2 0 012-2h86.74c39.439 0 65.322 21.354 65.322 54.138 0 23.008-17.409 43.61-39.594 47.219v1.203c30.196 3.309 50.532 24.212 50.532 53.085zm-84.58-128.125h-45.91v64.814h38.669c29.888 0 46.373-12.03 46.373-33.535 0-20.151-14.174-31.279-39.132-31.279zm-45.91 90.53v71.431h47.605c31.12 0 47.605-12.482 47.605-35.941 0-23.46-16.947-35.49-49.608-35.49h-45.602z"/></svg>

After

Width:  |  Height:  |  Size: 1007 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="408"><defs><linearGradient id="bs-logo-a" x1="76.079" x2="523.48" y1="10.798" y2="365.945" gradientUnits="userSpaceOnUse"><stop stop-color="#9013fe"/><stop offset="1" stop-color="#6610f2"/></linearGradient><linearGradient id="bs-logo-b" x1="193.508" x2="293.514" y1="109.74" y2="278.872" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#f1e5fc"/></linearGradient><filter xmlns="http://www.w3.org/2000/svg" id="bs-logo-c" width="197" height="249" x="161.901" y="83.457" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4"/><feGaussianBlur stdDeviation="8"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow"/><feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape"/></filter></defs><path fill="url(#bs-logo-a)" d="M56.481 53.32C55.515 25.58 77.128 0 106.342 0h299.353c29.214 0 50.827 25.58 49.861 53.32-.928 26.647.277 61.165 8.964 89.31 8.715 28.232 23.411 46.077 47.48 48.37v26c-24.069 2.293-38.765 20.138-47.48 48.37-8.687 28.145-9.892 62.663-8.964 89.311.966 27.739-20.647 53.319-49.861 53.319H106.342c-29.214 0-50.827-25.58-49.86-53.319.927-26.648-.278-61.166-8.966-89.311C38.802 237.138 24.07 219.293 0 217v-26c24.069-2.293 38.802-20.138 47.516-48.37 8.688-28.145 9.893-62.663 8.965-89.31z"/><path fill="url(#bs-logo-b)" filter="url(#bs-logo-c)" stroke="#fff" d="M267.103 312.457c47.297 0 75.798-23.158 75.798-61.355 0-28.873-20.336-49.776-50.532-53.085v-1.203c22.185-3.609 39.594-24.211 39.594-47.219 0-32.783-25.882-54.138-65.322-54.138h-88.74v217h89.202zm-54.692-189.48h45.911c24.958 0 39.131 11.128 39.131 31.279 0 21.505-16.484 33.535-46.372 33.535h-38.67v-64.814zm0 161.961v-71.431h45.602c32.661 0 49.608 12.03 49.608 35.49 0 23.459-16.484 35.941-47.605 35.941h-47.605z"/></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

7
assets/dist/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

7
assets/dist/css/bootstrap.rtl.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11
assets/dist/js/sidebar_control.js vendored Normal file
View File

@ -0,0 +1,11 @@
var sidebarControl = L.Control.extend({
options: { position: 'bottomleft'},
onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
container.innerHTML = `<div class="sidebar">
<a href="#" class="btn btn-sm mt-1 mx-3 close" id="fg-close-it" onclick="$(this).closest('.sidebar').hide()">X</a>
<div class="table-container px-3 py-4"></div>
</div>`;
return container;
}
});

78
assets/dist/js/tbl2CSV.js vendored Normal file
View File

@ -0,0 +1,78 @@
function tableToCSV(tbl_id, csv_name) {
// Variable to store the final csv data
var csv_data = [];
//gets table
var tbl = document.getElementById(tbl_id);
//gets rows of table
const rows_len = tbl.rows.length;
// Get each row data
for (var i = 0; i < rows_len; i++) {
//gets cells of current row
var cols = tbl.rows.item(i).cells;
//gets amount of cells of current row
var cols_len = cols.length;
//loops through each cell in current row
var csvrow = [];
for(var j = 0; j < cols_len; j++){
// Get the text data of each cell
// of a row and push it to csvrow
csvrow.push(cols[j].innerHTML);
}
// Combine each column value with comma
csv_data.push(csvrow.join(","));
}
// Combine each row data with new line character
csv_data = csv_data.join('\n');
// Call this function to download csv file
downloadCSVFile(csv_name, csv_data);
}
function dataToCSV(csv_name, project_data){
var csv_hdr = JSON.stringify(Object.keys(project_data.features[0].properties)).replace(/(^\[)|(\]$)/mg, '');
var csv_data = project_data.features.map(function (feat){
return JSON.stringify(Object.values(feat.properties));
}).join('\n')
.replace(/(^\[)|(\]$)/mg, '');
// Call this function to download csv file
downloadCSVFile(csv_name, csv_hdr + '\n' + csv_data);
}
function downloadCSVFile(csv_name, csv_data) {
// Create CSV file object and feed
// our csv_data into it
CSVFile = new Blob([csv_data], {
type: "text/csv"
});
// Create to temporary link to initiate
// download process
var temp_link = document.createElement('a');
// Download csv file
temp_link.download = csv_name + '.csv';
var url = window.URL.createObjectURL(CSVFile);
temp_link.href = url;
// This link should not be displayed
temp_link.style.display = "none";
document.body.appendChild(temp_link);
// Automatically click the link to
// trigger download
temp_link.click();
document.body.removeChild(temp_link);
}

BIN
assets/images/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
assets/images/csv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/images/docx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/images/html.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/images/html2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
assets/images/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
assets/images/qat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
assets/links/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
assets/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
assets/maps/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

161
index.php Normal file
View File

@ -0,0 +1,161 @@
<?php
session_start();
require('admin/incl/const.php');
require('admin/class/database.php');
require('admin/class/access_groups.php');
if(!isset($_SESSION['user'])) {
header('Location: login.php');
exit;
}
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT, DB_SCMA);
$acc_obj = new access_group_Class($database->getConn(), $_SESSION['user']->id);
$usr_grps = ($_SESSION['user']->id == SUPER_ADMIN_ID) ? $acc_obj->getRowsArr()
: $acc_obj->getByUserId($_SESSION['user']->id);
$rows = array();
if(count($usr_grps)){
$usr_grps_keys = array_keys($usr_grps);
$usr_map_grps = $acc_obj->getGroupMapGroups($usr_grps_keys);
if(count($usr_map_grps)){
$usr_map_grps_ids = implode(',', array_keys($usr_map_grps));
$rows = $database->getAll('map', "id IN (".$usr_map_grps_ids.")", 'id');
}
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<title>QGIS2Map App</title>
<link href="assets/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
.card {
box-shadow: 0 0.15rem 0.55rem rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease-in-out;
}
.card:hover {
box-shadow: 0 0.35rem 0.85rem rgba(0, 0, 0, 0.3);
}
.col {
padding-right: calc(var(--bs-gutter-x) * .75);
padding-left: calc(var(--bs-gutter-x) * .75);
}
.navbar {
position: relative;
min-height: 50px;
margin-bottom: 0px!important;
border: 1px solid transparent;
}
</style>
</head>
<body>
<header>
<div class="navbar navbar-dark bg-dark shadow-sm" style="background-color:#50667f!important">
<div class="container">
<a href="#" class="navbar-brand d-flex align-items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-text" viewBox="0 0 16 16">
<path d="M5.5 7a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1h-5zM5 9.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0 2a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5z"/>
<path d="M9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.5L9.5 0zm0 1v2A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
</svg> <strong> &nbsp;QatMaps</strong>
</a>
<?php
if(in_array($_SESSION['user']->accesslevel, ADMINISTRATION_ACCESS_LEVELS)){
echo '<a href="admin/index.php" style="text-decoration:none; color: #fff!important; font-size: 1.25rem; font-weight: 300;">Administration</a>';
}
?>
<a href="logout.php" style="text-decoration:none; color: #fff!important; font-size: 1.25rem; font-weight: 300;">Log Out</a>
</div>
</div>
</header>
<main style="background-color:#edf0f2">
<section class="py-5 text-left container" style="padding-bottom: 0rem!important;">
<div class="row py-lg-5">
<div class="col-lg-6 col-md-8 mx-auto" style="margin-left: 5px!important;">
<h1 class="fw-light"><?php echo $_SESSION['user']->name;?> Maps
</h1>
<p class="lead text-muted">Maps</p>
</div>
</div>
</section>
<div class="album py-5 bg-light">
<div class="container">
<div class="row row-cols-1 row-cols-md-4 g-4">
<?php foreach($rows as $row) {
$image = file_exists("assets/maps/{$row['id']}.png") ? "assets/maps/{$row['id']}.png" : "assets/maps/default.png"; ?>
<div class="col">
<a href="apps/<?=$row['id']?>/index.php" style="text-decoration:none; color: #6c757d!important; font-size: 1.25rem; font-weight: 300;">
<div class="card">
<div class="card-body">
<h5 class="card-title" style="font-size: 15px; font-weight: 800;"><?=$row['name']?></h5>
</div>
<div class="px-3">
<div style="height: 150px; width: 100%; background: url('<?=$image?>') no-repeat; background-size: cover; background-position: center center;"></div>
</div>
<?PHP if($row['description']) { ?>
<div class="card-body">
<p class="card-text" style="color: #6c757d!important; font-size: 15px; font-weight: 600;"> <?=$row['description']?> </p>
</div>
<?PHP } ?>
</div>
</a>
</div>
<?php } ?>
</div>
</div>
</div>
</main>
<footer class="text-muted py-5">
<div class="container">
<p class="float-end mb-1">
<a href="#" style="text-decoration:none; color: #6c757d!important; font-size: 1.25rem; font-weight: 300;">Back to top</a> </p>
</div>
</footer>
<script src="assets/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

68
installer/apache2.conf Normal file
View File

@ -0,0 +1,68 @@
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
#ServerName example.com
#ServerAlias www.example.com
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
#SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt
#SSLCACertificatePath /etc/ssl/certs/
#SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt
#SSLCARevocationPath /etc/apache2/ssl.crl/
#SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl
# Client Authentication (Type):
# Client certificate verification type and depth. Types are
# none, optional, require and optional_no_ca. Depth is a
# number which specifies how deeply to verify the certificate
# issuer chain before deciding the certificate is not valid.
#SSLVerifyClient require
#SSLVerifyDepth 10
#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
<FilesMatch "\.php$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/">
Require all denied
</Directory>
<Directory "/var/www/html/">
Require all granted
Options -Indexes
<LimitExcept GET POST>
Deny from all
</LimitExcept>
</Directory>
<Directory "/var/www/html/class">
Require all denied
</Directory>
<Directory "/var/www/html/admin/class">
Require all denied
</Directory>
<Directory "/var/www/html/admin/snippets">
Require all denied
</Directory>
<FilesMatch "\.(jpe?g|png|gif|js|css|ico)$">
ExpiresActive On
ExpiresDefault "access plus 1 day"
</FilesMatch>
</VirtualHost>
</IfModule>

80
installer/app-install.sh Normal file
View File

@ -0,0 +1,80 @@
#!/bin/bash -e
APP_DB='q2w'
APP_DB_PASS=$(< /dev/urandom tr -dc _A-Za-z0-9 | head -c32);
DATA_DIR='/var/www/data'
CACHE_DIR='/var/www/cache'
APPS_DIR='/var/www/html/apps'
if [ ! -d installer ]; then
echo "Usage: ./installer/app-installer.sh"
exit 1
fi
# 1. Install packages (assume PG is preinstalled)
apt-get -y install apache2 libapache2-mod-php php-{pgsql,zip,gd} proftpd
# setup apache
a2enmod ssl headers expires
cp installer/apache2.conf /etc/apache2/sites-available/default-ssl.conf
a2ensite default-ssl
a2dissite 000-default
systemctl reload apache2
sed -i.save '
s/#DefaultRoot~/DefaultRoot ~/
s/# RequireValidShelloff/RequireValidShell off/' /etc/proftpd/proftpd.conf
systemctl enable proftpd
systemctl restart proftpd
# 2. Create db
su postgres <<CMD_EOF
createdb ${APP_DB}
createuser -sd ${APP_DB}
psql -c "alter user ${APP_DB} with password '${APP_DB_PASS}'"
psql -c "ALTER DATABASE ${APP_DB} OWNER TO ${APP_DB}"
CMD_EOF
echo "${APP_DB} pass: ${APP_DB_PASS}" >> /root/auth.txt
mkdir -p "${APPS_DIR}"
mkdir -p "${CACHE_DIR}"
mkdir -p "${DATA_DIR}"
chown -R www-data:www-data "${APPS_DIR}"
chown -R www-data:www-data "${CACHE_DIR}"
chown -R www-data:www-data "${DATA_DIR}"
cat >admin/incl/const.php <<CAT_EOF
<?php
define("DB_HOST", "localhost");
define("DB_NAME", "${APP_DB}");
define("DB_USER", "${APP_DB}");
define("DB_PASS", "${APP_DB_PASS}");
define("DB_PORT", 5432);
define("DB_SCMA", 'public');
define("APPS_DIR", "${APPS_DIR}");
define("CACHE_DIR", "${APPS_DIR}");
define("DATA_DIR", "${DATA_DIR}");
?>
CAT_EOF
cp -r . /var/www/html/
chown -R www-data:www-data /var/www/html
rm -rf /var/www/html/installer
systemctl restart apache2
# create group for all FTP users
groupadd qatusers
# install ftp user creation script
for f in create delete; do
cp installer/${f}_ftp_user.sh /usr/local/bin/
chown www-data:www-data /usr/local/bin/${f}_ftp_user.sh
chmod 0550 /usr/local/bin/${f}_ftp_user.sh
done
cat >/etc/sudoers.d/q2w <<CAT_EOF
www-data ALL = NOPASSWD: /usr/local/bin/create_ftp_user.sh, /usr/local/bin/delete_ftp_user.sh
CAT_EOF

View File

@ -0,0 +1,14 @@
#!/bin/bash -e
read FTP_USERNAME
read FTP_PASSWORD
useradd -G qatusers -m -s /usr/sbin/nologin ${FTP_USERNAME}
echo -n "${FTP_USERNAME}:${FTP_PASSWORD}" | chpasswd -e
FTP_HOME=$(grep "^${FTP_USERNAME}:" /etc/passwd | cut -f6 -d:)
# allow Apache access
chown ${FTP_USERNAME}:www-data "${FTP_HOME}"
chmod 0755 "${FTP_HOME}"

View File

@ -0,0 +1,7 @@
#!/bin/bash -e
FTP_USERNAME="${1}"
if [ $(groups "${FTP_USERNAME}" | grep -m 1 -c 'qatusers') -gt 0 ]; then
userdel -r ${FTP_USERNAME}
fi

309
installer/gui-installer.sh Normal file
View File

@ -0,0 +1,309 @@
#!/bin/bash -e
APP_DB='q2w'
APP_DB_PASS=$(< /dev/urandom tr -dc _A-Za-z0-9 | head -c32);
DATA_DIR='/var/www/data'
CACHE_DIR='/var/www/cache'
APPS_DIR='/var/www/html/apps'
PG_VER='16'
PG_PASS=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32);
HNAME=$(hostname -I | sed -n 1p | cut -f1 -d' ' | tr -d '\n')
USE_SSL='no'
declare -x STEPS=('Initializing')
declare -x CMDS=('init_installer')
function install_postgresql(){
RELEASE=$(lsb_release -cs)
#3. Install PostgreSQL
echo "deb http://apt.postgresql.org/pub/repos/apt/ ${RELEASE}-pgdg main" > /etc/apt/sources.list.d/pgdg.list
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
apt-get update -y || true
apt-get install -y postgresql-${PG_VER} postgresql-client-${PG_VER} postgresql-contrib-${PG_VER} \
python3-postgresql postgresql-plperl-${PG_VER} \
postgresql-pltcl-${PG_VER} postgresql-${PG_VER}-postgis-3 \
odbc-postgresql libpostgresql-jdbc-java
if [ ! -f /usr/lib/postgresql/${PG_VER}/bin/postgres ]; then
echo "Error: Get PostgreSQL version"; exit 1;
fi
ln -sf /usr/lib/postgresql/${PG_VER}/bin/pg_config /usr/bin
ln -sf /var/lib/postgresql/${PG_VER}/main/ /var/lib/postgresql
ln -sf /var/lib/postgresql/${PG_VER}/backups /var/lib/postgresql
systemctl start postgresql
#5. Set postgres Password
if [ $(grep -m 1 -c 'pg pass' /root/auth.txt) -eq 0 ]; then
sudo -u postgres psql 2>/dev/null -c "alter user postgres with password '${PG_PASS}'"
echo "pg pass: ${PG_PASS}" > /root/auth.txt
fi
#4. Add Postgre variables to environment
if [ $(grep -m 1 -c 'PGDATA' /etc/environment) -eq 0 ]; then
cat >>/etc/environment <<CMD_EOF
PGDATA=/var/lib/postgresql/${PG_VER}/main
CMD_EOF
fi
#6. Configure ph_hba.conf
cat >/etc/postgresql/${PG_VER}/main/pg_hba.conf <<CMD_EOF
local all all trust
host all all 127.0.0.1 255.255.255.255 trust
host all all 0.0.0.0/0 scram-sha-256
host all all ::1/128 scram-sha-256
hostssl all all 127.0.0.1 255.255.255.255 scram-sha-256
hostssl all all 0.0.0.0/0 scram-sha-256
hostssl all all ::1/128 scram-sha-256
CMD_EOF
sed -i.save "s/.*listen_addresses.*/listen_addresses = '*'/" /etc/postgresql/${PG_VER}/main/postgresql.conf
sed -i.save "s/.*ssl =.*/ssl = on/" /etc/postgresql/${PG_VER}/main/postgresql.conf
#10. Create Symlinks for Backward Compatibility from PostgreSQL 9 to PostgreSQL 8
#ln -sf /usr/pgsql-9.4/bin/pg_config /usr/bin
mkdir -p /var/lib/pgsql
ln -sf /var/lib/postgresql/${PG_VER}/main /var/lib/pgsql
ln -sf /var/lib/postgresql/${PG_VER}/backups /var/lib/pgsql
#create SSL certificates
if [ ! -f /var/lib/postgresql/${PG_VER}/main/server.key -o ! -f /var/lib/postgresql/${PG_VER}/main/server.crt ]; then
SSL_PASS=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32);
if [ $(grep -m 1 -c 'ssl pass' /root/auth.txt) -eq 0 ]; then
echo "ssl pass: ${SSL_PASS}" >> /root/auth.txt
else
sed -i.save "s/ssl pass:.*/ssl pass: ${SSL_PASS}/" /root/auth.txt
fi
openssl genrsa -des3 -passout pass:${SSL_PASS} -out server.key 2048
openssl rsa -in server.key -passin pass:${SSL_PASS} -out server.key
chmod 400 server.key
openssl req -new -key server.key -days 3650 -out server.crt -passin pass:${SSL_PASS} -x509 -subj '/C=CA/ST=Frankfurt/L=Frankfurt/O=acuciva-de.com/CN=acuciva-de.com/emailAddress=info@acugis.com'
chown postgres.postgres server.key server.crt
mv server.key server.crt /var/lib/postgresql/${PG_VER}/main
fi
systemctl restart postgresql
}
function install_webmin(){
if [ -f "/etc/letsencrypt/live/${HNAME}/cert.pem" ]; then
cat /etc/letsencrypt/live/${HNAME}/cert.pem > /etc/webmin/miniserv.pem
cat /etc/letsencrypt/live/${HNAME}/privkey.pem >> /etc/webmin/miniserv.pem
echo "extracas=/etc/letsencrypt/live/${HNAME}/fullchain.pem" >> /etc/webmin/miniserv.conf
fi
systemctl restart webmin
echo "deb http://download.webmin.com/download/repository sarge contrib" > /etc/apt/sources.list.d/webmin.list
wget --quiet -qO - http://www.webmin.com/jcameron-key.asc | apt-key add -
apt-get -y update
apt-get -y install webmin
}
function install_proftpd(){
apt-get -y install proftpd
sed -i.save '
s/#DefaultRoot~/DefaultRoot ~/
s/# RequireValidShelloff/RequireValidShell off/' /etc/proftpd/proftpd.conf
systemctl enable proftpd
systemctl restart proftpd
cat >/etc/sudoers.d/q2w <<CAT_EOF
www-data ALL = NOPASSWD: /usr/local/bin/create_ftp_user.sh, /usr/local/bin/delete_ftp_user.sh
CAT_EOF
}
function install_qat_application(){
# 1. Install packages (assume PG is preinstalled)
apt-get -y install apache2 libapache2-mod-php php-{pgsql,zip,gd}
# setup apache
a2enmod ssl headers expires
cp installer/apache2.conf /etc/apache2/sites-available/default-ssl.conf
sed -i.save "s/#ServerName example.com/#ServerName ${HNAME}/" /etc/apache2/sites-available/default-ssl.conf
a2ensite default-ssl
a2dissite 000-default
systemctl reload apache2
# 2. Create db
su postgres <<CMD_EOF
createdb ${APP_DB}
createuser -sd ${APP_DB}
psql -c "alter user ${APP_DB} with password '${APP_DB_PASS}'"
psql -c "ALTER DATABASE ${APP_DB} OWNER TO ${APP_DB}"
CMD_EOF
echo "${APP_DB} pass: ${APP_DB_PASS}" >> /root/auth.txt
mkdir -p "${APPS_DIR}"
mkdir -p "${CACHE_DIR}"
mkdir -p "${DATA_DIR}"
chown -R www-data:www-data "${APPS_DIR}"
chown -R www-data:www-data "${CACHE_DIR}"
chown -R www-data:www-data "${DATA_DIR}"
cat >admin/incl/const.php <<CAT_EOF
<?php
define("DB_HOST", "localhost");
define("DB_NAME", "${APP_DB}");
define("DB_USER", "${APP_DB}");
define("DB_PASS", "${APP_DB_PASS}");
define("DB_PORT", 5432);
define("DB_SCMA", 'public');
define("APPS_DIR", "${APPS_DIR}");
define("CACHE_DIR", "${APPS_DIR}");
define("DATA_DIR", "${DATA_DIR}");
?>
CAT_EOF
cp -r . /var/www/html/
chown -R www-data:www-data /var/www/html
rm -rf /var/www/html/installer
systemctl restart apache2
# create group for all FTP users
groupadd qatusers
# install ftp user creation script
for f in create delete; do
cp installer/${f}_ftp_user.sh /usr/local/bin/
chown www-data:www-data /usr/local/bin/${f}_ftp_user.sh
chmod 0550 /usr/local/bin/${f}_ftp_user.sh
done
}
function install_certbot(){
apt-get -y install apache2 python3-certbot-apache
service apache2 restart
certbot --apache --agree-tos --email hostmaster@${HNAME} --no-eff-email -d ${HNAME}
}
function install_postfix(){
apt-get -y install postgix
}
function init_installer(){
add-apt-repository -y universe
apt-get -y update || true
apt-get -y install wget unzip whiptail
}
function info_for_user(){
#End message for user
echo -e "Installation is now completed."
echo -e "Complete the QAT Application installer at http://${HNAME}/admin/setup.php"
echo -e "postgres and other passwords are saved in /root/auth.txt file"
}
function menu(){
CHOICES=$(whiptail --separate-output --checklist "Choose options" 20 55 7 \
"1." "Change Hostname" OFF \
"2." "Install Let's Encrypt SSL" OFF \
"3." "Install PostgreSQL" ON \
"4." "Install Postfix" OFF \
"5." "Install ProFTPD" ON \
"6." "Install Webmin" ON \
"7." "QAP Application" ON 3>&1 1>&2 2>&3)
if [ -z "${CHOICES}" ]; then
echo "No option was selected (user hit Cancel or unselected all options)"
else
for CHOICE in ${CHOICES}; do
case "${CHOICE}" in
"1.")
HNAME=$(whiptail --inputbox "Please enter hostname" 10 100 3>&1 1>&2 2>&3)
hostname -s "${HNAME}"
;;
"2.")
USE_SSL='yes'
;;
"3.")
STEPS+=("PostgreSQL")
CMDS+=("install_postgresql")
;;
"4.")
STEPS+=("Postfix")
CMDS+=("install_postfix")
;;
"5.")
STEPS+=("ProFTPd")
CMDS+=("install_proftpd")
;;
"6.")
STEPS+=("Webmin")
CMDS+=("install_webmin")
;;
"7.")
STEPS+=("QAT Application")
CMDS+=("install_qat_application")
if [ "${USE_SSL}" == "yes" ]; then
STEPS+=("Let's Encrypt SSL")
CMDS+=("install_certbot")
fi
;;
*)
echo "Unsupported item ${CHOICE}!" >&2
exit 1
;;
esac
done
fi
}
function progress_bar(){
local MAX_STEPS=${#STEPS[@]}
local BAR_SIZE="##########"
local MAX_BAR_SIZE="${#BAR_SIZE}"
local CLEAR_LINE="\\033[K"
#tput civis -- invisible
for step in "${!STEPS[@]}"; do
perc=$((step * 100 / MAX_STEPS))
percBar=$((perc * MAX_BAR_SIZE / 100))
echo -ne "\\r- ${STEPS[step]} [ ]$CLEAR_LINE\\n"
echo -ne "\\r[${BAR_SIZE:0:percBar}] $perc %$CLEAR_LINE"
${CMDS[$step]} 1>"/tmp/${CMDS[$step]}.log" 2>&1
perc=$(((step + 1) * 100 / MAX_STEPS))
percBar=$((perc * MAX_BAR_SIZE / 100))
echo -ne "\\r\\033[1A- ${STEPS[step]} [✔]$CLEAR_LINE\\n"
echo -ne "\\r[${BAR_SIZE:0:percBar}] $perc %$CLEAR_LINE"
done
echo ""
#tput cnorm -- normal
}
################################################################################
touch /root/auth.txt
export DEBIAN_FRONTEND=noninteractive
if [ ! -d installer ]; then
echo "Usage: ./installer/gui-installer.sh"
exit 1
fi
progress_bar;
declare -x STEPS=()
declare -x CMDS=()
menu;
progress_bar;
info_for_user

100
installer/postgres.sh Normal file
View File

@ -0,0 +1,100 @@
#!/bin/bash -e
PG_VER='16'
PG_PASS=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32);
function install_postgresql(){
RELEASE=$(lsb_release -cs)
#3. Install PostgreSQL
echo "deb http://apt.postgresql.org/pub/repos/apt/ ${RELEASE}-pgdg main" > /etc/apt/sources.list.d/pgdg.list
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
apt-get update -y || true
apt-get install -y postgresql-${PG_VER} postgresql-client-${PG_VER} postgresql-contrib-${PG_VER} \
python3-postgresql postgresql-plperl-${PG_VER} \
postgresql-pltcl-${PG_VER} postgresql-${PG_VER}-postgis-3 \
odbc-postgresql libpostgresql-jdbc-java
if [ ! -f /usr/lib/postgresql/${PG_VER}/bin/postgres ]; then
echo "Error: Get PostgreSQL version"; exit 1;
fi
ln -sf /usr/lib/postgresql/${PG_VER}/bin/pg_config /usr/bin
ln -sf /var/lib/postgresql/${PG_VER}/main/ /var/lib/postgresql
ln -sf /var/lib/postgresql/${PG_VER}/backups /var/lib/postgresql
systemctl start postgresql
#5. Set postgres Password
if [ $(grep -m 1 -c 'pg pass' /root/auth.txt) -eq 0 ]; then
sudo -u postgres psql 2>/dev/null -c "alter user postgres with password '${PG_PASS}'"
echo "pg pass: ${PG_PASS}" > /root/auth.txt
fi
#4. Add Postgre variables to environment
if [ $(grep -m 1 -c 'PGDATA' /etc/environment) -eq 0 ]; then
cat >>/etc/environment <<CMD_EOF
PGDATA=/var/lib/postgresql/${PG_VER}/main
CMD_EOF
fi
#6. Configure ph_hba.conf
cat >/etc/postgresql/${PG_VER}/main/pg_hba.conf <<CMD_EOF
local all all trust
host all all 127.0.0.1 255.255.255.255 trust
host all all 0.0.0.0/0 scram-sha-256
host all all ::1/128 scram-sha-256
hostssl all all 127.0.0.1 255.255.255.255 scram-sha-256
hostssl all all 0.0.0.0/0 scram-sha-256
hostssl all all ::1/128 scram-sha-256
CMD_EOF
sed -i.save "s/.*listen_addresses.*/listen_addresses = '*'/" /etc/postgresql/${PG_VER}/main/postgresql.conf
sed -i.save "s/.*ssl =.*/ssl = on/" /etc/postgresql/${PG_VER}/main/postgresql.conf
#10. Create Symlinks for Backward Compatibility from PostgreSQL 9 to PostgreSQL 8
#ln -sf /usr/pgsql-9.4/bin/pg_config /usr/bin
mkdir -p /var/lib/pgsql
ln -sf /var/lib/postgresql/${PG_VER}/main /var/lib/pgsql
ln -sf /var/lib/postgresql/${PG_VER}/backups /var/lib/pgsql
#create SSL certificates
if [ ! -f /var/lib/postgresql/${PG_VER}/main/server.key -o ! -f /var/lib/postgresql/${PG_VER}/main/server.crt ]; then
SSL_PASS=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32);
if [ $(grep -m 1 -c 'ssl pass' /root/auth.txt) -eq 0 ]; then
echo "ssl pass: ${SSL_PASS}" >> /root/auth.txt
else
sed -i.save "s/ssl pass:.*/ssl pass: ${SSL_PASS}/" /root/auth.txt
fi
openssl genrsa -des3 -passout pass:${SSL_PASS} -out server.key 2048
openssl rsa -in server.key -passin pass:${SSL_PASS} -out server.key
chmod 400 server.key
openssl req -new -key server.key -days 3650 -out server.crt -passin pass:${SSL_PASS} -x509 -subj '/C=CA/ST=Frankfurt/L=Frankfurt/O=acuciva-de.com/CN=acuciva-de.com/emailAddress=info@acugis.com'
chown postgres.postgres server.key server.crt
mv server.key server.crt /var/lib/postgresql/${PG_VER}/main
fi
systemctl restart postgresql
}
function install_webmin(){
echo "deb http://download.webmin.com/download/repository sarge contrib" > /etc/apt/sources.list.d/webmin.list
wget --quiet -qO - http://www.webmin.com/jcameron-key.asc | apt-key add -
apt-get -y update
apt-get -y install webmin
}
touch /root/auth.txt
export DEBIAN_FRONTEND=noninteractive
add-apt-repository -y universe
apt-get -y update || true
apt-get -y install wget unzip
install_postgresql;
install_webmin;

120
login.php Normal file
View File

@ -0,0 +1,120 @@
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>AcuGIS | Layer Viewer</title>
<meta content="" name="description">
<meta content="" name="keywords">
<!-- Favicons -->
<link href="assets/images/favicon.ico" rel="icon">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,600;1,700&family=Montserrat:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Raleway:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
<link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="assets/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
<link href="assets/vendor/aos/aos.css" rel="stylesheet">
<link href="assets/vendor/glightbox/css/glightbox.min.css" rel="stylesheet">
<link href="assets/vendor/swiper/swiper-bundle.min.css" rel="stylesheet">
<link href="assets/css/style.css" rel="stylesheet">
</head>
<body>
<!-- Login 8 - Bootstrap Brain Component -->
<section class="bg-light p-3 p-md-4 p-xl-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-xxl-11">
<div class="card border-light-subtle shadow-sm">
<div class="row g-0">
<div class="col-12 col-md-6">
<img class="img-fluid rounded-start w-100 h-100 object-fit-cover" loading="lazy" src="assets/images/login-page.png" alt="Welcome back you've been missed!">
</div>
<div class="col-12 col-md-6 d-flex align-items-center justify-content-center">
<div class="col-12 col-lg-11 col-xl-10">
<div class="card-body p-3 p-md-4 p-xl-5">
<div class="row">
<div class="col-12">
<div class="mb-5">
<div class="text-center mb-4">
<a href="#!">
<img src="assets/images/qat.png" alt="BootstrapBrain Logo" width="125" height="125">
</a>
</div>
<h4 class="text-center">QatMaps</h4>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="d-flex gap-3 flex-column">
</div>
</div>
</div>
<form method="post" action="admin/action/login.php">
<?php if(!empty($_GET['err'])){ ?>
<div class="alert alert-danger" role="alert" style="width: 80%"><?=$_GET['err']?></div>
<?php } else if(!empty($_GET['msg'])){ ?>
<div class="alert alert-success" role="alert" style="width: 80%"><?=$_GET['msg']?></div>
<?php } ?>
<div class="row gy-3 overflow-hidden">
<div class="col-12">
<div class="form-floating mb-3">
<input type="email" class="form-control" name="email" id="email" placeholder="name@example.com" required>
<label for="email" class="form-label">Email</label>
</div>
</div>
<div class="col-12">
<div class="form-floating mb-3">
<input type="password" class="form-control" name="pwd" id="pwd" value="" placeholder="Password" required>
<label for="password" class="form-label">Password</label>
</div>
</div>
<div class="col-12">
</div>
<div class="col-12">
<div class="d-grid">
<button class="btn btn-dark btn-lg" type="submit" value="Login" name="submit">Log in</button>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-12">
<div class="d-flex gap-2 gap-md-4 flex-column flex-md-row justify-content-md-center mt-5">
<a href="https://www.acugis.com" class="link-secondary text-decoration-none">From AcuGIS</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="assets/vendor/aos/aos.js"></script>
<script src="assets/vendor/glightbox/js/glightbox.min.js"></script>
<script src="assets/vendor/purecounter/purecounter_vanilla.js"></script>
<script src="assets/vendor/swiper/swiper-bundle.min.js"></script>
<script src="assets/js/web.js"></script>
</body>
</html>

9
logout.php Normal file
View File

@ -0,0 +1,9 @@
<?php
session_start();
foreach($_SESSION as $sk=>$sv){
unset($_SESSION[$sk]);
}
header("location: /");
?>

129
signup.php Normal file
View File

@ -0,0 +1,129 @@
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>AcuGIS | Layer Viewer</title>
<meta content="" name="description">
<meta content="" name="keywords">
<!-- Favicons -->
<link href="assets/images/favicon.ico" rel="icon">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,600;1,700&family=Montserrat:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Raleway:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
<link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="assets/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
<link href="assets/vendor/aos/aos.css" rel="stylesheet">
<link href="assets/vendor/glightbox/css/glightbox.min.css" rel="stylesheet">
<link href="assets/vendor/swiper/swiper-bundle.min.css" rel="stylesheet">
<link href="assets/css/style.css" rel="stylesheet">
</head>
<body>
<!-- Login 8 - Bootstrap Brain Component -->
<section class="bg-light p-3 p-md-4 p-xl-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-xxl-11">
<div class="card border-light-subtle shadow-sm">
<div class="row g-0">
<div class="col-12 col-md-6">
<img class="img-fluid rounded-start w-100 h-100 object-fit-cover" loading="lazy" src="assets/images/login-page.png" alt="Welcome back you've been missed!">
</div>
<div class="col-12 col-md-6 d-flex align-items-center justify-content-center">
<div class="col-12 col-lg-11 col-xl-10">
<div class="card-body p-3 p-md-4 p-xl-5">
<div class="row">
<div class="col-12">
<div class="mb-5">
<div class="text-center mb-4">
<a href="#!">
<img src="assets/images/qat.png" alt="BootstrapBrain Logo" width="125" height="125">
</a>
</div>
<h4 class="text-center">QatMaps</h4>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="d-flex gap-3 flex-column">
</div>
</div>
</div>
<p>You can sign-up below for an Admin account. A verification email will be sent to you shortly ater that.</p>
<form method="post" action="admin/action/signup.php">
<?php if(!empty($_GET['err'])){ ?>
<div class="alert alert-danger" role="alert" style="width: 80%"><?=$_GET['err']?></div>
<?php } else if(!empty($_GET['msg'])){ ?>
<div class="alert alert-success" role="alert" style="width: 80%"><?=$_GET['msg']?></div>
<?php } ?>
<div class="row gy-3 overflow-hidden">
<div class="col-12">
<div class="form-floating mb-3">
<input type="text" class="form-control" name="name" id="name" placeholder="John Doe" required>
<label for="name" class="form-label">Name</label>
</div>
</div>
<div class="col-12">
<div class="form-floating mb-3">
<input type="email" class="form-control" name="email" id="email" placeholder="name@example.com" required>
<label for="email" class="form-label">Email</label>
</div>
</div>
<div class="col-12">
<div class="form-floating mb-3">
<input type="password" class="form-control" name="password" id="password" value="" placeholder="Password" required>
<label for="password" class="form-label">Password</label>
</div>
</div>
<div class="col-12">
</div>
<div class="col-12">
<div class="d-grid">
<button class="btn btn-dark btn-lg" type="submit" value="Sign Up" name="submit">Sign up</button>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-12">
<div class="d-flex gap-2 gap-md-4 flex-column flex-md-row justify-content-md-center mt-5">
<a href="https://www.acugis.com" class="link-secondary text-decoration-none">From AcuGIS</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="assets/vendor/aos/aos.js"></script>
<script src="assets/vendor/glightbox/js/glightbox.min.js"></script>
<script src="assets/vendor/purecounter/purecounter_vanilla.js"></script>
<script src="assets/vendor/swiper/swiper-bundle.min.js"></script>
<script src="assets/js/web.js"></script>
</body>
</html>