Improve admin page (add new projects)

This commit is contained in:
2020-02-08 21:13:52 +01:00
parent 7b4b965f15
commit de2e17c430
12 changed files with 312 additions and 79 deletions

View File

@@ -2,10 +2,7 @@
/** /**
* Feed Class * Feed Class
* Also takes care of spot (device) & messages * Also manages spots (devices) & messages
*
* To add a new feed, create a new records in the feed table:
* INSERT INTO feeds (ref_feed_id, id_spot, id_project) VALUES ('<feed_id>', '<related_spot_id>', '<related_project_id>');
*/ */
class Feed extends PhpObject { class Feed extends PhpObject {
@@ -29,24 +26,68 @@ class Feed extends PhpObject {
private $sRefFeedId; private $sRefFeedId;
private $iLastUpdate; private $iLastUpdate;
public function __construct(Db &$oDb, $iFeedId) { public function __construct(Db &$oDb, $iFeedId=0) {
parent::__construct(__CLASS__, Settings::DEBUG); parent::__construct(__CLASS__, Settings::DEBUG);
$this->oDb = &$oDb; $this->oDb = &$oDb;
$this->setFeedId($iFeedId); if($iFeedId > 0) $this->setFeedId($iFeedId);
}
public function getFeedId() {
return $this->iFeedId;
} }
public function setFeedId($iFeedId) { public function setFeedId($iFeedId) {
$this->iFeedId = $iFeedId; $this->iFeedId = $iFeedId;
$asFeed = $this->oDb->selectRow(self::FEED_TABLE, $this->iFeedId); $asFeed = $this->getFeed();
$this->sRefFeedId = $asFeed['ref_feed_id']; $this->sRefFeedId = $asFeed['ref_feed_id'];
$this->iLastUpdate = strtotime($asFeed['last_update']); $this->iLastUpdate = strtotime($asFeed['last_update']);
} }
public function createFeedId($oProjectId) {
$this->setFeedId($this->oDb->insertRow(self::FEED_TABLE, array(
Db::getId(Project::PROJ_TABLE) => $oProjectId,
'status' => 'INACTIVE'
)));
return $this->getFeedId();
}
public function setRefFeedId($sRefFeedId) {
return $this->updateField('ref_feed_id', $sRefFeedId);
}
public function setSpotId($iSpotId) {
return $this->updateField(Db::getId(self::SPOT_TABLE), $iSpotId);
}
public function setProjectId($iProjectId) {
return $this->updateField(Db::getId(Project::PROJ_TABLE), $iProjectId);
}
public function getSpots() {
$asSpots = $this->oDb->selectRows(array('from'=>self::SPOT_TABLE));
foreach($asSpots as &$asSpot) $asSpot['id'] = $asSpot[Db::getId(self::SPOT_TABLE)];
return $asSpots;
}
public function getFeeds($iFeedId=0) {
$asInfo = array('from'=>self::FEED_TABLE);
if($iFeedId > 0) $asInfo['constraint'] = array(Db::getId(self::FEED_TABLE)=>$iFeedId);
$asFeeds = $this->oDb->selectRows($asInfo);
foreach($asFeeds as &$asFeed) $asFeed['id'] = $asFeed[Db::getId(self::FEED_TABLE)];
return $asFeeds;
}
public function getFeed() {
$asFeeds = $this->getFeeds($this->getFeedId());
return array_shift($asFeeds);
}
public function getMessages($asActivePeriod = array()) { public function getMessages($asActivePeriod = array()) {
$asInfo = array( $asInfo = array(
'select' => array('id_message', 'ref_msg_id', 'type', 'latitude', 'longitude', 'site_time', 'unix_time'), 'select' => array('id_message', 'ref_msg_id', 'type', 'latitude', 'longitude', 'site_time', 'unix_time'),
'from' => self::MSG_TABLE, 'from' => self::MSG_TABLE,
'constraint'=> array(Db::getId(self::FEED_TABLE) => $this->iFeedId), 'constraint'=> array(Db::getId(self::FEED_TABLE) => $this->getFeedId()),
'constOpe' => array(Db::getId(self::FEED_TABLE) => "="), 'constOpe' => array(Db::getId(self::FEED_TABLE) => "="),
'orderBy' => array('site_time'=>'ASC') 'orderBy' => array('site_time'=>'ASC')
); );
@@ -114,12 +155,22 @@ class Feed extends PhpObject {
} }
} }
} }
else $this->oDb->updateRow(self::FEED_TABLE, $this->iFeedId, array('last_update'=>$sLastUpdate)); else $this->oDb->updateRow(self::FEED_TABLE, $this->getFeedId(), array('last_update'=>$sLastUpdate));
} }
private function retrieveFeed() { private function retrieveFeed() {
$sUrl = self::FEED_HOOK.$this->sRefFeedId.self::FEED_TYPE_JSON; $sContent = '[]';
$sContent = file_get_contents($sUrl); if($this->sRefFeedId !='') {
$sUrl = self::FEED_HOOK.$this->sRefFeedId.self::FEED_TYPE_JSON;
$sContent = file_get_contents($sUrl);
}
return json_decode($sContent, true); return json_decode($sContent, true);
} }
private function updateField($sField, $oValue) {
$bResult = ($this->oDb->updateRow(self::FEED_TABLE, $this->getFeedId(), array($sField=>$oValue)) > 0);
$this->setFeedId($this->getFeedId());
return $bResult;
}
} }

View File

@@ -24,9 +24,10 @@ class Project extends PhpObject {
private $asActive; private $asActive;
private $asGeo; private $asGeo;
public function __construct(Db &$oDb) { public function __construct(Db &$oDb, $iProjectId=0) {
parent::__construct(__CLASS__, Settings::DEBUG); parent::__construct(__CLASS__, Settings::DEBUG);
$this->oDb = &$oDb; $this->oDb = &$oDb;
if($iProjectId > 0) $this->setProjectId($iProjectId);
} }
public function getProjectId() { public function getProjectId() {
@@ -64,10 +65,23 @@ class Project extends PhpObject {
$this->setProjectInfo(); $this->setProjectInfo();
} }
public function createProjectId() {
$this->setProjectId($this->oDb->insertRow(self::PROJ_TABLE, array('timezone'=>Settings::TIMEZONE)));
return $this->getProjectId();
}
public function getMode() { public function getMode() {
return $this->sMode; return $this->sMode;
} }
public function getProjectName() {
return $this->sName;
}
public function setProjectName($sName) {
return $this->updateField('name', $sName);
}
public function getProjectCodeName() { public function getProjectCodeName() {
return $this->sCodeName; return $this->sCodeName;
} }
@@ -130,7 +144,7 @@ class Project extends PhpObject {
case 2: $asProject['mode'] = self::MODE_HISTO; break; case 2: $asProject['mode'] = self::MODE_HISTO; break;
} }
if(!Converter::isGeoJsonValid($sCodeName)) Converter::convertToGeoJson($sCodeName); if($sCodeName!= '' && !Converter::isGeoJsonValid($sCodeName)) Converter::convertToGeoJson($sCodeName);
$asProject['geofilepath'] = Spot::addTimestampToFilePath(Geo::getFilePath($sCodeName, GeoJson::EXT)); $asProject['geofilepath'] = Spot::addTimestampToFilePath(Geo::getFilePath($sCodeName, GeoJson::EXT));
$asProject['gpxfilepath'] = Spot::addTimestampToFilePath(Geo::getFilePath($sCodeName, Gpx::EXT)); $asProject['gpxfilepath'] = Spot::addTimestampToFilePath(Geo::getFilePath($sCodeName, Gpx::EXT));

View File

@@ -338,30 +338,73 @@ class Spot extends Main
} }
public function getAdminSettings() { public function getAdminSettings() {
return self::getJsonResult(true, '', array('projects'=>$this->oProject->getProjects())); $oFeed = new Feed($this->oDb);
return self::getJsonResult(true, '', array(
'project' => $this->oProject->getProjects(),
'feed' => $oFeed->getFeeds(),
'spot' => $oFeed->getSpots()
));
} }
public function setAdminSettings($sField, $sValue) { public function setAdminSettings($sType, $iId, $sField, $sValue) {
$bSuccess = false; $bSuccess = false;
$sDesc = ''; $sDesc = '';
switch($sField) { switch($sType) {
case 'codename': case 'project':
$bSuccess = $this->oProject->setProjectCodeName($sValue); $oProject = new Project($this->oDb, $iId);
switch($sField) {
case 'name':
$bSuccess = $oProject->setProjectName($sValue);
break;
case 'codename':
$bSuccess = $oProject->setProjectCodeName($sValue);
break;
case 'active_from':
$bSuccess = $oProject->setActivePeriod($sValue.' 00:00:00', 'from');
break;
case 'active_to':
$bSuccess = $oProject->setActivePeriod($sValue.' 23:59:59', 'to');
break;
case 'timezone':
$bSuccess = $oProject->setTimeZone($sValue);
break;
}
$asResult = $oProject->getProject();
break; break;
case 'active_from': case 'feed':
$bSuccess = $this->oProject->setActivePeriod($sValue.' 00:00:00', 'from'); case 'spot':
break; $oFeed = new Feed($this->oDb, $iId);
case 'active_to': switch($sField) {
$bSuccess = $this->oProject->setActivePeriod($sValue.' 23:59:59', 'to'); case 'ref_feed_id':
break; $bSuccess = $oFeed->setRefFeedId($sValue);
case 'timezone': break;
$bSuccess = $this->oProject->setTimeZone($sValue); case 'spot_id':
$bSuccess = $oFeed->setSpotId($sValue);
break;
case 'project_id':
$bSuccess = $oFeed->setProjectId($sValue);
break;
}
$asResult = $oFeed->getFeed();
break; break;
} }
if(!$bSuccess) $sDesc = Mask::LANG_PREFIX.'error_commit_db'; if(!$bSuccess) $sDesc = Mask::LANG_PREFIX.'error_commit_db';
return self::getJsonResult($bSuccess, $sDesc, array('values'=>$this->oProject->getProject())); return self::getJsonResult($bSuccess, $sDesc, array($sType=>array($asResult)));
}
public function createProject() {
$oProject = new Project($this->oDb);
$iNewProjectId = $oProject->createProjectId();
$oFeed = new Feed($this->oDb);
$oFeed->createFeedId($iNewProjectId);
return self::getJsonResult($iNewProjectId>0, '', array(
'project' => array($oProject->getProject()),
'feed' => array($oFeed->getFeed())
));
} }
public function convertGpxToGeojson($sGeoFileName) { public function convertGpxToGeojson($sGeoFileName) {
@@ -385,7 +428,7 @@ class Spot extends Main
//Seconds //Seconds
$fSecond = round($dLeft * 3600, 1); $fSecond = round($dLeft * 3600, 1);
return $iDegree.'°'.$iMinute.'\''.$fSecond.'"'.$sDirection; return $iDegree.'°'.$iMinute."'".$fSecond.'"'.$sDirection;
} }
public function getTimeFormat($iTime) { public function getTimeFormat($iTime) {

View File

@@ -20,7 +20,8 @@ $iChunk = isset($_GET['chunk'])?$_GET['chunk']:0;
$iProjectId = isset($_REQUEST['project_id'])?$_REQUEST['project_id']:0; $iProjectId = isset($_REQUEST['project_id'])?$_REQUEST['project_id']:0;
$sField = isset($_REQUEST['field'])?$_REQUEST['field']:''; $sField = isset($_REQUEST['field'])?$_REQUEST['field']:'';
$oValue = isset($_REQUEST['value'])?$_REQUEST['value']:''; $oValue = isset($_REQUEST['value'])?$_REQUEST['value']:'';
$iId = isset($_REQUEST['id'])?$_REQUEST['id']:''; $iId = isset($_REQUEST['id'])?$_REQUEST['id']:0;
$sType = isset($_REQUEST['type'])?$_REQUEST['type']:'';
//Initiate class //Initiate class
$oSpot = new Spot($oClassManagement, __FILE__, $sTimezone); $oSpot = new Spot($oClassManagement, __FILE__, $sTimezone);
@@ -52,11 +53,14 @@ if($sAction!='')
case 'sync_pics': case 'sync_pics':
$sResult = $oSpot->syncPics(); $sResult = $oSpot->syncPics();
break; break;
case 'get_admin': case 'admin_get':
$sResult = $oSpot->getAdminSettings(); $sResult = $oSpot->getAdminSettings();
break; break;
case 'set_admin': case 'admin_set':
$sResult = $oSpot->setAdminSettings($sField, $oValue); $sResult = $oSpot->setAdminSettings($sType, $iId, $sField, $oValue);
break;
case 'admin_new':
$sResult = $oSpot->createProject();
break; break;
case 'build_geojson': case 'build_geojson':
$sResult = $oSpot->convertGpxToGeojson($sName); $sResult = $oSpot->convertGpxToGeojson($sName);

View File

@@ -41,11 +41,20 @@ media_comment_update= Comment of media "$0" updated
city_time = $0 Time city_time = $0 Time
project_id = Project ID
project = Project project = Project
mode = Mode mode = Mode
code_name = Code name code_name = Code name
start = Start start = Start
end = End end = End
feed_id = Feed ID
ref_feed_id = Ref. Feed ID
spot_id = Spot ID
name = Name
status = Status
last_update = Last Update
ref_spot_id = Ref. Spot ID
model = Model
date_time = $0 at $1 date_time = $0 at $1
time_zone = Time zone time_zone = Time zone

View File

@@ -41,11 +41,20 @@ media_comment_update= Commentaire du media "$0" mis-à-jour
city_time = heure de $0 city_time = heure de $0
project_id = ID projet
project = Projet project = Projet
mode = Mode mode = Mode
code_name = Nom de code code_name = Nom de code
start = Départ start = Départ
end = Arrivée end = Arrivée
feed_id = ID Feed
ref_feed_id = ID Feed ref.
spot_id = ID Spot
name = Description
status = Statut
last_update = Dernière maj
ref_spot_id = ID Spot ref.
model = Modèle
date_time = $0 à $1 date_time = $0 à $1
time_zone = Fuseau horaire time_zone = Fuseau horaire

View File

@@ -1,26 +1,59 @@
<div id="admin"> <div id="admin">
<h1>Projects</h1>
<div id="projects"> <div id="projects">
<table> <table>
<tr> <thead>
<th>[#]lang:project[#]</th> <tr>
<th>[#]lang:mode[#]</th> <th>[#]lang:project_id[#]</th>
<th>[#]lang:code_name[#]</th> <th>[#]lang:project[#]</th>
<th>[#]lang:start[#]</th> <th>[#]lang:mode[#]</th>
<th>[#]lang:end[#]</th> <th>[#]lang:code_name[#]</th>
<th>[#]lang:time_zone[#]</th> <th>[#]lang:start[#]</th>
</tr> <th>[#]lang:end[#]</th>
<th>[#]lang:time_zone[#]</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div id="new"></div>
</div>
<h1>Feeds</h1>
<div id="feeds">
<table>
<thead>
<tr>
<th>[#]lang:feed_id[#]</th>
<th>[#]lang:ref_feed_id[#]</th>
<th>[#]lang:spot_id[#]</th>
<th>[#]lang:project_id[#]</th>
<th>[#]lang:name[#]</th>
<th>[#]lang:status[#]</th>
<th>[#]lang:last_update[#]</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<h1>Spots</h1>
<div id="spots">
<table>
<thead>
<tr>
<th>[#]lang:spot_id[#]</th>
<th>[#]lang:ref_spot_id[#]</th>
<th>[#]lang:name[#]</th>
<th>[#]lang:model[#]</th>
</tr>
</thead>
<tbody></tbody>
</table> </table>
</div> </div>
<div id="feedback"></div> <div id="feedback"></div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
oSpot.pageInit = function(asHash) { oSpot.pageInit = function(asHash) {
self.get( self.get('admin_get', setProjects);
'get_admin', $('#new').addInput('button', 'new', 'New Project', [{on:'click', callback:createProject}]);
function(asData){
setProjects(asData.projects);
}
);
}; };
oSpot.onFeedback = function(sType, sMsg, asContext) { oSpot.onFeedback = function(sType, sMsg, asContext) {
@@ -33,40 +66,71 @@ oSpot.onFeedback = function(sType, sMsg, asContext) {
$('#feedback').append($('<p>', {'class': sType}).text(sMsg)); $('#feedback').append($('<p>', {'class': sType}).text(sMsg));
}; };
function setProjects(asProjects) { function setProjects(asElemTypes) {
$.each(asProjects, function(iKey, oProject) { var aoEvents = [{on:'change', callback:commit}, {on:'keyup', callback:waitAndCommit}];
var sElemId = 'proj_'+oProject.id;
var $Project = ($('#'+sElemId).length==0)?$('<tr>', {'id': sElemId}):$('#'+sElemId).empty();
$Project
.data('project_id', oProject.id)
.append($('<td>', {'class': 'name'}).text(oProject.name))
.append($('<td>', {'class': 'mode'}).text(oProject.mode))
.append($('<td>').addInput('text', 'codename', oProject.codename))
.append($('<td>').addInput('date', 'active_from', oProject.active_from.substr(0, 10)))
.append($('<td>').addInput('date', 'active_to', oProject.active_to.substr(0, 10)))
.append($('<td>').addInput('text', 'timezone', oProject.timezone))
.appendTo($('#projects').find('table'));
});
$('input') $.each(asElemTypes, function(sElemType, aoElems) {
.change(commit) $.each(aoElems, function(iKey, oElem) {
.keyup(waitAndCommit); var sElemId = sElemType+'_'+oElem.id;
var bNew = ($('#'+sElemId).length == 0);
var $Elem = (bNew?$('<tr>', {'id': sElemId}):$('#'+sElemId).empty())
.data('type', sElemType)
.data('id', oElem.id)
.append($('<td>').text(oElem.id || ''));
switch(sElemType) {
case 'project':
$Elem
.append($('<td>').addInput('text', 'name', oElem.name, aoEvents))
.append($('<td>', {'class': 'mode'}).text(oElem.mode))
.append($('<td>').addInput('text', 'codename', oElem.codename, aoEvents))
.append($('<td>').addInput('date', 'active_from', oElem.active_from.substr(0, 10), aoEvents))
.append($('<td>').addInput('date', 'active_to', oElem.active_to.substr(0, 10), aoEvents))
.append($('<td>').addInput('text', 'timezone', oElem.timezone, aoEvents));
break;
case 'feed':
$Elem
.append($('<td>').addInput('text', 'ref_feed_id', oElem.ref_feed_id, aoEvents))
.append($('<td>').addInput('number', 'spot_id', oElem.id_spot, aoEvents))
.append($('<td>').addInput('number', 'project_id', oElem.id_project, aoEvents))
.append($('<td>').text(oElem.name))
.append($('<td>').text(oElem.status))
.append($('<td>').text(oElem.last_update));
break;
case 'spot':
$Elem
.append($('<td>').text(oElem.ref_spot_id))
.append($('<td>').text(oElem.name))
.append($('<td>').text(oElem.model))
break;
}
if(bNew) $Elem.appendTo($('#'+sElemType+'s').find('table tbody'));
});
});
}
function createProject() {
self.get('admin_new', setProjects);
} }
function commit(event, $This) { function commit(event, $This) {
$This = $This || $(this); $This = $This || $(this);
if(typeof self.tmp('wait') != 'undefined') clearTimeout(self.tmp('wait'));
var sOldVal = $This.data('old_value'); var sOldVal = $This.data('old_value');
var sNewVal = $This.val(); var sNewVal = $This.val();
if(sOldVal!=sNewVal) { if(sOldVal!=sNewVal) {
$This.data('old_value', sNewVal); $This.data('old_value', sNewVal);
var asInputs = {project_id: $This.closest('tr').data('project_id'), field: $This.attr('name'), value: sNewVal}; var $Record = $This.closest('tr');
var asInputs = {type: $Record.data('type'), id: $Record.data('id'), field: $This.attr('name'), value: sNewVal};
self.get( self.get(
'set_admin', 'admin_set',
function(asData){ function(asData){
oSpot.onFeedback('success', self.lang('admin_save_success'), asInputs); oSpot.onFeedback('success', self.lang('admin_save_success'), asInputs);
setProjects([asData.values]); setProjects(asData);
}, },
asInputs, asInputs,
function(sError){ function(sError){

View File

@@ -4,8 +4,12 @@
* php-mbstring * php-mbstring
* php-imagick * php-imagick
* ffprobe (ffmpeg) * ffprobe (ffmpeg)
## Getting started
1. Copy files onto web server
2. Copy settings-sample.php to settings.php and populate
3. Go to #admin and create a new project
## To Do List ## To Do List
* import/export * ECMA import/export
* Reset zoom on image closing (lightbox) * Reset zoom on image closing (lightbox)
* Fix fullscreen button on ios * Fix video fullscreen button on ios
* Fix lightbox portrait mode: push text under * Fix lightbox portrait mode: push text under

View File

@@ -255,9 +255,14 @@ function getTimeZoneDesc(sTimeZoneName) {
return oSpot.lang('city_time', sCity); return oSpot.lang('city_time', sCity);
} }
$.prototype.addInput = function(sType, sName, sValue) $.prototype.addInput = function(sType, sName, sValue, aoEvents)
{ {
return $(this).append($('<input>', {type: sType, name: sName, value: sValue}).data('old_value', sValue)); aoEvents = aoEvents || [];
var $Input = $('<input>', {type: sType, name: sName, value: sValue}).data('old_value', sValue);
$.each(aoEvents, function(iIndex, aoEvent) {
$Input.on(aoEvent.on, aoEvent.callback);
});
return $(this).append($Input);
}; };
$.prototype.addIcon = function(sIcon, bMargin, sStyle) $.prototype.addIcon = function(sIcon, bMargin, sStyle)

View File

@@ -1,9 +1,39 @@
#feedback { #admin {
.error { margin: 1em;
color: red;
table {
margin-bottom: 1em;
border-collapse: collapse;
tr {
th {
background: #AAA;
color: white;
padding: 0.2rem 0.5rem;
}
td {
background: #EEE;
text-align: center;
padding: 0.2rem 0.5rem;
input {
&[type="number"] {
width: 50px;
}
&[name="ref_feed_id"] {
width: 300px;
}
}
}
}
} }
.success { #feedback {
color: green; .error {
color: red;
}
.success {
color: green;
}
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long