Compare commits

..

10 Commits

Author SHA1 Message Date
789838dd07 Fix SNT track color 2025-07-18 00:28:37 +02:00
a5fe6ebf75 Update DNT GPX file 2025-07-18 00:25:07 +02:00
d0dbb85e28 Add manual message upload 2025-05-11 13:19:59 +02:00
6a42494099 Fix SNT track color 2025-04-23 11:12:49 +02:00
1d90b11aef Add function to rebuild GeoJSON 2025-04-23 11:06:43 +02:00
bc75cbc17d Add Build GeoJSON Catch 2025-04-23 11:02:16 +02:00
2e8372d923 Update geo/snt.gpx 2025-04-22 13:40:51 +02:00
383890a9be Add SNT gpx 2025-04-22 13:40:25 +02:00
ae8a27fa64 Fix lightbox addToAlbum external reference 2023-10-19 21:43:02 +02:00
af05650443 Bump lightbox to 2.11.4 2023-10-19 21:33:59 +02:00
123 changed files with 70341 additions and 162 deletions

View File

@@ -1,4 +0,0 @@
#!/bin/bash
wget -qO- https://spot.lutran.fr/index.php?a=update_project > /dev/null
#Crontab job: 0 * * * * . /var/www/spot/spot_cron.sh > /dev/null

View File

@@ -14,7 +14,7 @@
},
"autoload": {
"psr-4": {
"Franzz\\Spot\\": "api/"
"Franzz\\Spot\\": "inc/"
},
"files": [
"settings.php"

69304
geo/snt.gpx Normal file

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 821 B

After

Width:  |  Height:  |  Size: 821 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 261 KiB

After

Width:  |  Height:  |  Size: 261 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -36,18 +36,18 @@ class Converter extends PhpObject {
public static function isGeoJsonValid($sCodeName) {
$bResult = false;
$sGpxFilePath = Gpx::getFilePath($sCodeName);
$sGeoJsonFilePath = GeoJson::getFilePath($sCodeName);
$sGpxFilePath = Geo::getFilePath($sCodeName, Gpx::EXT);
$sGeoJsonFilePath = Geo::getFilePath($sCodeName, GeoJson::EXT);
//No need to generate if gpx is missing
if(!file_exists($sGpxFilePath) || file_exists($sGeoJsonFilePath) && filemtime($sGeoJsonFilePath) > filemtime(Gpx::getFilePath($sCodeName))) $bResult = true;
if(!file_exists($sGpxFilePath) || file_exists($sGeoJsonFilePath) && filemtime($sGeoJsonFilePath) > filemtime(Geo::getFilePath($sCodeName, Gpx::EXT))) $bResult = true;
return $bResult;
}
}
class Geo extends PhpObject {
const GEO_FOLDER = '../geo/';
const GEO_FOLDER = 'geo/';
const OPT_SIMPLE = 'simplification';
protected $asTracks;
@@ -55,16 +55,12 @@ class Geo extends PhpObject {
public function __construct($sCodeName) {
parent::__construct(get_class($this), Settings::DEBUG, PhpObject::MODE_HTML);
$this->sFilePath = self::getFilePath($sCodeName);
$this->sFilePath = self::getFilePath($sCodeName, static::EXT);
$this->asTracks = array();
}
public static function getFilePath($sCodeName) {
return self::GEO_FOLDER.$sCodeName.static::EXT;
}
public static function getDistFilePath($sCodeName) {
return 'geo/'.$sCodeName.static::EXT;
public static function getFilePath($sCodeName, $sExt) {
return self::GEO_FOLDER.$sCodeName.$sExt;
}
public function getLog() {

View File

@@ -168,6 +168,31 @@ class Feed extends PhpObject {
return $bNewMsg;
}
public function addManualPosition($sLat, $sLng, $iTimestamp) {
$sTimeZone = date_default_timezone_get();
$oDateTime = new \DateTime('@'.$iTimestamp);
$oDateTime->setTimezone(new \DateTimeZone($sTimeZone));
$asWeather = $this->getWeather(array($sLat, $sLng), $iTimestamp);
$asMsg = [
'ref_msg_id' => $iTimestamp.'/man',
'id_feed' => $this->getFeedId(),
'type' => 'OK',
'latitude' => $sLat,
'longitude' => $sLng,
'iso_time' => $oDateTime->format("Y-m-d\TH:i:sO"), //Incorrect ISO 8601 format, but compliant with Spot data
'site_time' => $oDateTime->format(Db::TIMESTAMP_FORMAT),
'timezone' => $sTimeZone,
'unix_time' => $iTimestamp,
'content' => '',
'battery_state' => '',
'posted_on' => date(Db::TIMESTAMP_FORMAT),
];
$iMessageId = $this->oDb->insertRow(self::MSG_TABLE, array_merge($asMsg, $asWeather));
return $iMessageId;
}
private function updateFeed() {
$bNewMsg = false;
$asData = $this->retrieveFeed();

View File

@@ -12,7 +12,7 @@ class Media extends PhpObject {
const MEDIA_TABLE = 'medias';
//Media folders
const MEDIA_FOLDER = '../files/';
const MEDIA_FOLDER = 'files/';
const THUMB_FOLDER = self::MEDIA_FOLDER.'thumbs/';
const THUMB_MAX_WIDTH = 400;

View File

@@ -144,8 +144,8 @@ class Project extends PhpObject {
if($sCodeName != '' && !Converter::isGeoJsonValid($sCodeName)) Converter::convertToGeoJson($sCodeName);
$asProject['geofilepath'] = Spot::addTimestampToFilePath(GeoJson::getDistFilePath($sCodeName));
$asProject['gpxfilepath'] = Spot::addTimestampToFilePath(Gpx::getDistFilePath($sCodeName));
$asProject['geofilepath'] = Spot::addTimestampToFilePath(Geo::getFilePath($sCodeName, GeoJson::EXT));
$asProject['gpxfilepath'] = Spot::addTimestampToFilePath(Geo::getFilePath($sCodeName, Gpx::EXT));
$asProject['codename'] = $sCodeName;
}
return $bSpecificProj?$asProject:$asProjects;

View File

@@ -40,8 +40,6 @@ class Spot extends Main
const DEFAULT_LANG = 'en';
const MAIN_PAGE = 'index';
private Project $oProject;
private Media $oMedia;
private User $oUser;
@@ -160,42 +158,42 @@ class Spot extends Main
);
}
public function getAppParams() {
public function getAppMainPage()
{
//Cache Page List
$asPages = array_diff($this->asMasks, array('email_update', 'email_conf'));
if(!$this->oUser->checkUserClearance(User::CLEARANCE_ADMIN)) {
$asPages = array_diff($asPages, array('admin', 'upload'));
}
$asGlobalVars = array(
'vars' => array(
'chunk_size' => self::FEED_CHUNK_SIZE,
'default_project_codename' => $this->oProject->getProjectCodeName(),
'projects' => $this->oProject->getProjects(),
'user' => $this->oUser->getUserInfo()
),
'consts' => array(
'server' => $this->asContext['serv_name'],
'modes' => Project::MODES,
'clearances' => User::CLEARANCES,
'default_timezone' => Settings::TIMEZONE
)
);
return self::getJsonResult(true, '', parent::getParams($asGlobalVars, self::MAIN_PAGE, $asPages));
}
public function getAppMainPage()
{
return parent::getMainPage(
self::MAIN_PAGE,
array(
'language' => $this->oLang->getLanguage(),
'host_url' => $this->asContext['serv_name'],
'filepath_css' => self::addTimestampToFilePath('spot.css'),
'filepath_js' => self::addTimestampToFilePath('../dist/app.js')
)
'vars' => array(
'chunk_size' => self::FEED_CHUNK_SIZE,
'default_project_codename' => $this->oProject->getProjectCodeName(),
'projects' => $this->oProject->getProjects(),
'user' => $this->oUser->getUserInfo()
),
'consts' => array(
'server' => $this->asContext['serv_name'],
'modes' => Project::MODES,
'clearances' => User::CLEARANCES,
'default_timezone' => Settings::TIMEZONE
)
),
'index',
array(
'language' => $this->oLang->getLanguage(),
'host_url' => $this->asContext['serv_name'],
'filepath_css' => self::addTimestampToFilePath('style/spot.css'),
'filepath_js_d3' => self::addTimestampToFilePath('script/d3.min.js'),
'filepath_js_leaflet' => self::addTimestampToFilePath('script/leaflet.min.js'),
'filepath_js_jquery' => self::addTimestampToFilePath('script/jquery.min.js'),
'filepath_js_jquery_mods' => self::addTimestampToFilePath('script/jquery.mods.js'),
'filepath_js_spot' => self::addTimestampToFilePath('script/spot.js'),
'filepath_js_lightbox' => self::addTimestampToFilePath('script/lightbox.js')
),
$asPages
);
}
@@ -632,6 +630,13 @@ class Spot extends Main
return self::getJsonResult($asResult['result'], $asResult['desc'], $asResult['data']);
}
public function addPosition($sLat, $sLng, $iTimestamp) {
$oFeed = new Feed($this->oDb, $this->oProject->getFeedIds()[0]);
$bResult = ($oFeed->addManualPosition($sLat, $sLng, $iTimestamp) > 0);
return self::getJsonResult($bResult, $bResult?'':$this->oDb->getLastError());
}
public function getAdminSettings($sType='') {
$oFeed = new Feed($this->oDb);
$asData = array(
@@ -746,6 +751,10 @@ class Spot extends Main
));
}
public function buildGeoJSON($sCodeName) {
return Converter::convertToGeoJson($sCodeName);
}
public static function decToDms($dValue, $sType) {
if($sType=='lat') $sDirection = ($dValue >= 0)?'N':'S'; //Latitude
else $sDirection = ($dValue >= 0)?'E':'W'; //Longitude

View File

@@ -25,6 +25,9 @@ $oValue = $_REQUEST['value'] ?? '';
$iId = $_REQUEST['id'] ?? 0 ;
$sType = $_REQUEST['type'] ?? '';
$sEmail = $_REQUEST['email'] ?? '';
$sLat = $_REQUEST['latitude'] ?? '';
$sLng = $_REQUEST['longitude'] ?? '';
$iTimestamp = $_REQUEST['timestamp'] ?? 0;
//Initiate class
$oSpot = new Spot(__FILE__, $sTimezone);
@@ -70,6 +73,9 @@ if($sAction!='')
case 'add_comment':
$sResult = $oSpot->addComment($iId, $sContent);
break;
case 'add_position':
$sResult = $oSpot->addPosition($sLat, $sLng, $iTimestamp);
break;
case 'admin_new':
$sResult = $oSpot->createProject();
break;
@@ -88,6 +94,9 @@ if($sAction!='')
case 'sql':
$sResult = $oSpot->getDbBuildScript();
break;
case 'build_geojson':
$sResult = $oSpot->buildGeoJSON($sName);
break;
default:
$sResult = Main::getJsonResult(false, Main::NOT_FOUND);
}

View File

@@ -1,107 +0,0 @@
<?php
/* Requests Handler */
//Start buffering
ob_start();
//Run from /dist/
$oLoader = require __DIR__.'/../vendor/autoload.php';
use Franzz\Objects\ToolBox;
use Franzz\Objects\Main;
use Franzz\Spot\Spot;
use Franzz\Spot\User;
ToolBox::fixGlobalVars($argv ?? array());
//Available variables
$sAction = $_REQUEST['a'] ?? '';
$sTimezone = $_REQUEST['t'] ?? '';
$sName = $_GET['name'] ?? '';
$sContent = $_GET['content'] ?? '';
$iProjectId = $_REQUEST['id_project'] ?? 0 ;
$sField = $_REQUEST['field'] ?? '';
$oValue = $_REQUEST['value'] ?? '';
$iId = $_REQUEST['id'] ?? 0 ;
$sType = $_REQUEST['type'] ?? '';
$sEmail = $_REQUEST['email'] ?? '';
//Initiate class
$oSpot = new Spot(__FILE__, $sTimezone);
$oSpot->setProjectId($iProjectId);
$sResult = '';
if($sAction!='')
{
switch($sAction)
{
case 'params':
$sResult = $oSpot->getAppParams();
break;
case 'markers':
$sResult = $oSpot->getMarkers();
break;
case 'next_feed':
$sResult = $oSpot->getNextFeed($iId);
break;
case 'new_feed':
$sResult = $oSpot->getNewFeed($iId);
break;
case 'add_post':
$sResult = $oSpot->addPost($sName, $sContent);
break;
case 'subscribe':
$sResult = $oSpot->subscribe($sEmail, $sName);
break;
case 'unsubscribe':
$sResult = $oSpot->unsubscribe();
break;
case 'unsubscribe_email':
$sResult = $oSpot->unsubscribeFromEmail($iId);
break;
case 'update_project':
$sResult = $oSpot->updateProject();
break;
default:
if($oSpot->checkUserClearance(User::CLEARANCE_ADMIN))
{
switch($sAction)
{
case 'upload':
$sResult = $oSpot->upload();
break;
case 'add_comment':
$sResult = $oSpot->addComment($iId, $sContent);
break;
case 'admin_new':
$sResult = $oSpot->createProject();
break;
case 'admin_get':
$sResult = $oSpot->getAdminSettings();
break;
case 'admin_set':
$sResult = $oSpot->setAdminSettings($sType, $iId, $sField, $oValue);
break;
case 'admin_del':
$sResult = $oSpot->delAdminSettings($sType, $iId);
break;
case 'generate_cron':
$sResult = $oSpot->genCronFile();
break;
case 'sql':
$sResult = $oSpot->getDbBuildScript();
break;
default:
$sResult = Main::getJsonResult(false, Main::NOT_FOUND);
}
}
else $sResult = Main::getJsonResult(false, Main::NOT_FOUND);
}
}
else $sResult = $oSpot->getAppMainPage();
$sDebug = ob_get_clean();
if(Settings::DEBUG && $sDebug!='') $oSpot->addUncaughtError($sDebug);
echo $sResult;

View File

@@ -6,6 +6,9 @@
<div class="bar" style="width: 0%;"></div>
</div>
<div id="comments"></div>
<div id="location">
<button id="add_loc"><i class="fa fa-message push"></i>New Position</button>
</div>
<div id="status"></div>
</div>
<script type="text/javascript">
@@ -35,6 +38,27 @@ oSpot.pageInit = function(asHash) {
$('#progress .bar').css('width', progress+'%');
}
});
$('#add_loc').click(() => {
if(navigator.geolocation) {
addStatus('Determining position...');
navigator.geolocation.getCurrentPosition(
(position) => {
addStatus('Sending position...');
oSpot.get(
'add_position',
function(asData){addStatus('Position sent');},
{'latitude':position.coords.latitude, 'longitude':position.coords.longitude, 'timestamp':Math.round(position.timestamp / 1000)},
function(sMsgId){addStatus(self.lang(sMsgId));},
);
},
(error) => {
addStatus(error.message);
}
);
}
else addStatus('This browser does not support geolocation');
});
}
else addStatus(self.lang('upload_mode_archived', [asProject.name]), true);
};

View File

@@ -22,6 +22,7 @@
4. Copy settings-sample.php to settings.php and populate
5. Go to #admin and create a new project, feed & maps
6. Add a GPX file named <project_codename>.gpx to /geo/
7. Run composer install
## To Do List
* ECMA import/export
* Add mail frequency slider

2
script/d3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
script/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

20
script/jquery.mods.js Executable file

File diff suppressed because one or more lines are too long

23
script/leaflet.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -9,7 +9,7 @@ $stroke-width-mouse-focus : 1;
$stroke-width-height-focus: 2;
$stroke-width-axis : 2;
@import '../../node_modules/leaflet/dist/leaflet';
@import 'leaflet/leaflet';
@import 'leaflet/leaflet_heightgraph';
/* Leaflet fixes */
@@ -108,7 +108,3 @@ $stroke-width-axis : 2;
}
}
}
.leaflet-default-icon-path {
background-image: none;
}

View File

@@ -1,4 +1,4 @@
@import '../../node_modules/lightbox2/dist/css/lightbox.css';
@import 'lightbox/lightbox';
@mixin lightbox-icon($icon) {
background: none;

Some files were not shown because too many files have changed in this diff Show More