From 1166cfc4d70d45e4ef9bff53fc2770bf3d3bd989 Mon Sep 17 00:00:00 2001 From: Bartek Fabiszewski Date: Sat, 19 Mar 2022 17:48:44 +0100 Subject: [PATCH] Minor formatting changes --- .tests/lib/BaseDatabaseTestCase.php | 6 +- .tests/tests/InternalAPITest.php | 174 +++--- .tests/tests/PositionTest.php | 8 +- .tests/tests/TrackTest.php | 8 +- .tests/tests/UserTest.php | 2 +- css/src/main.css | 63 +- helpers/auth.php | 346 +++++------ helpers/db.php | 584 ++++++++--------- helpers/lang.php | 176 +++--- helpers/position.php | 928 ++++++++++++++-------------- helpers/track.php | 420 ++++++------- helpers/upload.php | 12 +- helpers/user.php | 416 ++++++------- helpers/utils.php | 408 ++++++------ index.php | 44 +- js/src/configdialogmodel.js | 2 +- scripts/import_cli.php | 12 +- utils/export.php | 81 +-- utils/handleuser.php | 2 +- utils/import.php | 14 +- 20 files changed, 1854 insertions(+), 1852 deletions(-) diff --git a/.tests/lib/BaseDatabaseTestCase.php b/.tests/lib/BaseDatabaseTestCase.php index fcbb982..92bb68f 100644 --- a/.tests/lib/BaseDatabaseTestCase.php +++ b/.tests/lib/BaseDatabaseTestCase.php @@ -140,10 +140,10 @@ abstract class BaseDatabaseTestCase extends PHPUnit\DbUnit\TestCase { * * @param string $table Table name * @param array $rowsArr Array of rows - * @return int|null Last insert id if available, NULL otherwise + * @return int|null Last insert id if available, null otherwise */ private function pdoInsert(string $table, array $rowsArr = []): ?int { - $ret = NULL; + $ret = null; if (!empty($rowsArr)) { $values = ':' . implode(', :', array_keys($rowsArr)); $columns = implode(', ', array_keys($rowsArr)); @@ -161,7 +161,7 @@ abstract class BaseDatabaseTestCase extends PHPUnit\DbUnit\TestCase { * Execute raw insert query on database * * @param string $query Insert query - * @return int|null Last insert id if available, NULL otherwise + * @return int|null Last insert id if available, null otherwise */ private function pdoInsertRaw(string $query): ?int { $ret = null; diff --git a/.tests/tests/InternalAPITest.php b/.tests/tests/InternalAPITest.php index e6971f1..7456583 100644 --- a/.tests/tests/InternalAPITest.php +++ b/.tests/tests/InternalAPITest.php @@ -37,19 +37,19 @@ class InternalAPITest extends UloggerAPITestCase { $position = $json[0]; self::assertEquals(1, (int) $position->id, "Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testAdminUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testAdminUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); $position = $json[1]; - self::assertEquals(2, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp + 1, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testAdminUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals(2, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp + 1, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testAdminUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); } /** @@ -78,20 +78,20 @@ class InternalAPITest extends UloggerAPITestCase { self::assertCount(2, $json, "Wrong count of positions"); $position = $json[0]; - self::assertEquals(1, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals(1, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); $position = $json[1]; - self::assertEquals(2, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp + 1, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals(2, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp + 1, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); } /** @@ -148,20 +148,20 @@ class InternalAPITest extends UloggerAPITestCase { self::assertCount(2, $json, "Wrong count of positions"); $position = $json[0]; - self::assertEquals(1, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals(1, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); $position = $json[1]; - self::assertEquals(2, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp + 1, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals(2, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp + 1, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); } /** @@ -195,12 +195,12 @@ class InternalAPITest extends UloggerAPITestCase { self::assertCount(1, $json, "Wrong count of positions"); $position = $json[0]; - self::assertEquals(2, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp + 3, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testAdminUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals(2, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp + 3, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testAdminUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); } /** @@ -235,20 +235,20 @@ class InternalAPITest extends UloggerAPITestCase { self::assertCount(2, $json, "Wrong count of positions"); $position = $json[0]; - self::assertEquals(2, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp + 3, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testAdminUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals(2, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp + 3, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testAdminUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); $position = $json[1]; - self::assertEquals(3, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp + 2, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testUser, (string) $position->username,"Wrong username"); - self::assertEquals($trackName, (string) $position->trackname,"Wrong trackname"); + self::assertEquals(3, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp + 2, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testUser, (string) $position->username, "Wrong username"); + self::assertEquals($trackName, (string) $position->trackname, "Wrong trackname"); } /** @@ -349,14 +349,14 @@ class InternalAPITest extends UloggerAPITestCase { self::assertCount(1, $json, "Wrong count of positions"); $position = $json[0]; - self::assertEquals($afterId + 1, (int) $position->id,"Wrong position id"); - self::assertEquals($this->testLat + 1, (float) $position->latitude,"Wrong latitude"); - self::assertEquals($this->testLon, (float) $position->longitude,"Wrong longitude"); - self::assertEquals($this->testTimestamp + 1, (int) $position->timestamp,"Wrong timestamp"); - self::assertEquals($this->testAdminUser, (string) $position->username,"Wrong username"); - self::assertEquals($this->testTrackName, (string) $position->trackname,"Wrong trackname"); - self::assertEquals(111195, (int) $position->meters,"Wrong distance delta"); - self::assertEquals(1, (int) $position->seconds,"Wrong timestamp delta"); + self::assertEquals($afterId + 1, (int) $position->id, "Wrong position id"); + self::assertEquals($this->testLat + 1, (float) $position->latitude, "Wrong latitude"); + self::assertEquals($this->testLon, (float) $position->longitude, "Wrong longitude"); + self::assertEquals($this->testTimestamp + 1, (int) $position->timestamp, "Wrong timestamp"); + self::assertEquals($this->testAdminUser, (string) $position->username, "Wrong username"); + self::assertEquals($this->testTrackName, (string) $position->trackname, "Wrong trackname"); + self::assertEquals(111195, (int) $position->meters, "Wrong distance delta"); + self::assertEquals(1, (int) $position->seconds, "Wrong timestamp delta"); } @@ -387,12 +387,12 @@ class InternalAPITest extends UloggerAPITestCase { self::assertCount(2, $json, "Wrong count of tracks"); $track = $json[0]; - self::assertEquals($this->testTrackId2, (int) $track->id,"Wrong track id"); - self::assertEquals($this->testTrackName . "2", (string) $track->name,"Wrong track name"); + self::assertEquals($this->testTrackId2, (int) $track->id, "Wrong track id"); + self::assertEquals($this->testTrackName . "2", (string) $track->name, "Wrong track name"); $track = $json[1]; - self::assertEquals($this->testTrackId, (int) $track->id,"Wrong track id"); - self::assertEquals($this->testTrackName, (string) $track->name,"Wrong track name"); + self::assertEquals($this->testTrackId, (int) $track->id, "Wrong track id"); + self::assertEquals($this->testTrackName, (string) $track->name, "Wrong track name"); } /** @@ -420,12 +420,12 @@ class InternalAPITest extends UloggerAPITestCase { self::assertCount(2, $json, "Wrong count of tracks"); $track = $json[0]; - self::assertEquals($this->testTrackId2, (int) $track->id,"Wrong track id"); - self::assertEquals($this->testTrackName . "2", (string) $track->name,"Wrong track name"); + self::assertEquals($this->testTrackId2, (int) $track->id, "Wrong track id"); + self::assertEquals($this->testTrackName . "2", (string) $track->name, "Wrong track name"); $track = $json[1]; - self::assertEquals($this->testTrackId, (int) $track->id,"Wrong track id"); - self::assertEquals($this->testTrackName, (string) $track->name,"Wrong track name"); + self::assertEquals($this->testTrackId, (int) $track->id, "Wrong track id"); + self::assertEquals($this->testTrackName, (string) $track->name, "Wrong track name"); } /** @@ -626,8 +626,8 @@ class InternalAPITest extends UloggerAPITestCase { $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); - self::assertEquals(1, (int) $json->error,"Wrong error status"); - self::assertEquals("Wrong old password", (string) $json->message,"Wrong error message"); + self::assertEquals(1, (int) $json->error, "Wrong error status"); + self::assertEquals("Wrong old password", (string) $json->message, "Wrong error message"); } /** @@ -871,8 +871,8 @@ class InternalAPITest extends UloggerAPITestCase { self::assertEquals(200, $response->getStatusCode(), "Unexpected status code"); $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); - self::assertEquals(1, (int) $json->error,"Wrong error status"); - self::assertEquals($lang["servererror"], (string) $json->message,"Wrong error message"); + self::assertEquals(1, (int) $json->error, "Wrong error status"); + self::assertEquals($lang["servererror"], (string) $json->message, "Wrong error message"); self::assertEquals(2, $this->getConnection()->getRowCount("tracks"), "Wrong row count"); } @@ -899,8 +899,8 @@ class InternalAPITest extends UloggerAPITestCase { self::assertEquals(200, $response->getStatusCode(), "Unexpected status code"); $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); - self::assertEquals(1, (int) $json->error,"Wrong error status"); - self::assertEquals($lang["servererror"], (string) $json->message,"Wrong error message"); + self::assertEquals(1, (int) $json->error, "Wrong error status"); + self::assertEquals($lang["servererror"], (string) $json->message, "Wrong error message"); } /** @@ -918,7 +918,7 @@ class InternalAPITest extends UloggerAPITestCase { $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); self::assertEquals(1, (int) $json->error, "Wrong error status"); - self::assertEquals($lang["servererror"], (string) $json->message,"Wrong error message"); + self::assertEquals($lang["servererror"], (string) $json->message, "Wrong error message"); } @@ -938,8 +938,8 @@ class InternalAPITest extends UloggerAPITestCase { self::assertEquals(200, $response->getStatusCode(), "Unexpected status code"); $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); - self::assertEquals(1, (int) $json->error,"Wrong error status"); - self::assertEquals($lang["servererror"], (string) $json->message,"Wrong error message"); + self::assertEquals(1, (int) $json->error, "Wrong error status"); + self::assertEquals($lang["servererror"], (string) $json->message, "Wrong error message"); } /** @@ -959,8 +959,8 @@ class InternalAPITest extends UloggerAPITestCase { self::assertEquals(200, $response->getStatusCode(), "Unexpected status code"); $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); - self::assertEquals(1, (int) $json->error,"Wrong error status"); - self::assertEquals($lang["servererror"], (string) $json->message,"Wrong error message"); + self::assertEquals(1, (int) $json->error, "Wrong error status"); + self::assertEquals($lang["servererror"], (string) $json->message, "Wrong error message"); self::assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count"); } @@ -981,8 +981,8 @@ class InternalAPITest extends UloggerAPITestCase { self::assertEquals(200, $response->getStatusCode(), "Unexpected status code"); $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); - self::assertEquals(1, (int) $json->error,"Wrong error status"); - self::assertEquals($lang["servererror"], (string) $json->message,"Wrong error message"); + self::assertEquals(1, (int) $json->error, "Wrong error status"); + self::assertEquals($lang["servererror"], (string) $json->message, "Wrong error message"); self::assertEquals(1, $this->getConnection()->getRowCount("users"), "Wrong row count"); } @@ -1002,8 +1002,8 @@ class InternalAPITest extends UloggerAPITestCase { self::assertEquals(200, $response->getStatusCode(), "Unexpected status code"); $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); - self::assertEquals(1, (int) $json->error,"Wrong error status"); - self::assertEquals($lang["servererror"], (string) $json->message,"Wrong error message"); + self::assertEquals(1, (int) $json->error, "Wrong error status"); + self::assertEquals($lang["servererror"], (string) $json->message, "Wrong error message"); self::assertEquals(1, $this->getConnection()->getRowCount("users"), "Wrong row count"); } @@ -1024,8 +1024,8 @@ class InternalAPITest extends UloggerAPITestCase { self::assertEquals(200, $response->getStatusCode(), "Unexpected status code"); $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); - self::assertEquals(1, (int) $json->error,"Wrong error status"); - self::assertEquals($lang["servererror"], (string) $json->message,"Wrong error message"); + self::assertEquals(1, (int) $json->error, "Wrong error status"); + self::assertEquals($lang["servererror"], (string) $json->message, "Wrong error message"); self::assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count"); } @@ -1074,7 +1074,7 @@ class InternalAPITest extends UloggerAPITestCase { $json = json_decode($response->getBody()); self::assertNotNull($json, "JSON object is null"); self::assertEquals(1, (int) $json->error, "Wrong error status"); - self::assertEquals($lang["userexists"], (string) $json->message,"Wrong error message"); + self::assertEquals($lang["userexists"], (string) $json->message, "Wrong error message"); self::assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count"); } diff --git a/.tests/tests/PositionTest.php b/.tests/tests/PositionTest.php index c7f7a53..acac7d5 100644 --- a/.tests/tests/PositionTest.php +++ b/.tests/tests/PositionTest.php @@ -41,11 +41,11 @@ class PositionTest extends UloggerDatabaseTestCase { ); $this->assertTableContains($expected, $actual, "Wrong actual table data"); - $posId = uPosition::add($userId, $trackId, NULL, $this->testLat, $this->testLon); + $posId = uPosition::add($userId, $trackId, null, $this->testLat, $this->testLon); self::assertFalse($posId, "Adding position with null time stamp should fail"); - $posId = uPosition::add($userId, $trackId, $this->testTimestamp, NULL, $this->testLon); + $posId = uPosition::add($userId, $trackId, $this->testTimestamp, null, $this->testLon); self::assertFalse($posId, "Adding position with null latitude should fail"); - $posId = uPosition::add($userId, $trackId, $this->testTimestamp, $this->testLat, NULL); + $posId = uPosition::add($userId, $trackId, $this->testTimestamp, $this->testLat, null); self::assertFalse($posId, "Adding position with null longitude should fail"); $posId = uPosition::add($userId, $trackId, "", $this->testLat, $this->testLon); @@ -156,7 +156,7 @@ class PositionTest extends UloggerDatabaseTestCase { self::assertCount(2, $posArr, "Wrong row count"); $posArr = uPosition::getAll($userId, $trackId); self::assertCount(1, $posArr, "Wrong row count"); - $posArr = uPosition::getAll(NULL, $trackId); + $posArr = uPosition::getAll(null, $trackId); self::assertCount(1, $posArr, "Wrong row count"); $posArr = uPosition::getAll($userId3); self::assertCount(0, $posArr, "Wrong row count"); diff --git a/.tests/tests/TrackTest.php b/.tests/tests/TrackTest.php index 7dce897..700228c 100644 --- a/.tests/tests/TrackTest.php +++ b/.tests/tests/TrackTest.php @@ -72,11 +72,11 @@ class TrackTest extends UloggerDatabaseTestCase { ); $this->assertTableContains($expected, $actual, "Wrong actual table data"); - $posId = $track->addPosition($userId, NULL, $this->testLat, $this->testLon); + $posId = $track->addPosition($userId, null, $this->testLat, $this->testLon); self::assertFalse($posId, "Adding position with null time stamp should fail"); - $posId = $track->addPosition($userId, $this->testTimestamp, NULL, $this->testLon); + $posId = $track->addPosition($userId, $this->testTimestamp, null, $this->testLon); self::assertFalse($posId, "Adding position with null latitude should fail"); - $posId = $track->addPosition($userId, $this->testTimestamp, $this->testLat, NULL); + $posId = $track->addPosition($userId, $this->testTimestamp, $this->testLat, null); self::assertFalse($posId, "Adding position with null longitude should fail"); $posId = $track->addPosition($userId, "", $this->testLat, $this->testLon); @@ -113,7 +113,7 @@ class TrackTest extends UloggerDatabaseTestCase { uTrack::deleteAll($userId); self::assertEquals(1, $this->getConnection()->getRowCount('tracks'), "Wrong row count"); self::assertEquals(1, $this->getConnection()->getRowCount('positions'), "Wrong row count"); - self::assertFalse(uTrack::deleteAll(NULL), "User id should not be empty"); + self::assertFalse(uTrack::deleteAll(null), "User id should not be empty"); } public function testUpdate(): void { diff --git a/.tests/tests/UserTest.php b/.tests/tests/UserTest.php index 4bad7cd..0a0c1c7 100644 --- a/.tests/tests/UserTest.php +++ b/.tests/tests/UserTest.php @@ -75,7 +75,7 @@ class UserTest extends UloggerDatabaseTestCase { } public function testIsAdmin(): void { - $this->addTestUser($this->testUser, NULL, true); + $this->addTestUser($this->testUser, null, true); $user = new uUser($this->testUser); self::assertTrue($user->isAdmin, "User should be admin"); } diff --git a/css/src/main.css b/css/src/main.css index 1a704bd..0c65bf3 100644 --- a/css/src/main.css +++ b/css/src/main.css @@ -85,7 +85,7 @@ select { width: 165px; padding-left: 10px; color: lightgray; - background-color: rgba(102, 102, 102, 0.9); + background-color: rgba(102, 102, 102, 90%); } #menu-button { @@ -122,7 +122,7 @@ select { #menu.menu-hidden #menu-button { font-weight: normal; border-color: white; - background-color: rgba(0, 60, 136, 0.3); + background-color: rgba(0, 60, 136, 30%); } #menu.menu-hidden #menu-button a::after { @@ -153,7 +153,7 @@ select { margin-top: 0.2em; } -label[for=user] { +label[for="user"] { display: block; padding-top: 1em; } @@ -340,7 +340,7 @@ label[for=user] { width: 100%; height: 100%; background-color: black; /* fallback */ - background-color: rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 40%); } #modal-header { @@ -353,7 +353,7 @@ label[for=user] { #modal-header button { border: none; - background-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0%); } #modal-body { @@ -363,13 +363,13 @@ label[for=user] { transform: translate(-50%, -50%) !important; font-size: 0.9em; min-width: 300px; - margin: 0 auto 15% auto; + margin: 0 auto 15%; padding: 1em; color: white; border: 1px solid #888; -webkit-border-radius: 10px; border-radius: 10px; - background-color: rgba(102, 102, 102, 0.9); + background-color: rgba(102, 102, 102, 90%); -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; } @@ -378,10 +378,10 @@ label[for=user] { padding-top: 1em; } -#modal input[type=text], -#modal input[type=color], -#modal input[type=number], -#modal input[type=password] { +#modal input[type="text"], +#modal input[type="color"], +#modal input[type="number"], +#modal input[type="password"] { display: inline-block; box-sizing: border-box; width: 100%; @@ -395,7 +395,7 @@ label[for=user] { #modal.image { overflow: hidden; padding-top: 0; - background-color: rgba(45, 45, 45, 0.95); + background-color: rgba(45, 45, 45, 95%); } #modal.image #modal-body img { @@ -466,11 +466,11 @@ button > * { text-decoration: underline; } -#configForm label { +#config-form label { display: block; } -#configForm label b { +#config-form label b { display: inline-block; text-align: right; width: 250px; @@ -479,30 +479,30 @@ button > * { padding-top: 5px; } -#configForm input[type=text], -#configForm input[type=number], -#configForm input[type=color], -#configForm select { +#config-form input[type="text"], +#config-form input[type="number"], +#config-form input[type="color"], +#config-form select { width: 150px; margin: 3px 0; padding: 2px 4px; box-sizing: border-box; } -#configForm input[type=checkbox] { +#config-form input[type="checkbox"] { margin: 0; } -#configForm select { +#config-form select { padding: 2px 0; } -#configForm input[type=color] { +#config-form input[type="color"] { vertical-align: middle; padding: 0; } -#configForm img { +#config-form img { height: 13px; vertical-align: middle; margin: 0 5px; @@ -531,7 +531,7 @@ button > * { padding: 6px 20px; border-radius: 5px; border-top: 1px solid #555; - box-shadow: 10px 10px 10px -8px rgba(0, 0, 0, 0.3); + box-shadow: 10px 10px 10px -8px rgba(0, 0, 0, 30%); z-index: 100000; opacity: 0; transition: all 1s; @@ -596,7 +596,7 @@ button > * { } .alert.spinner > span::before, .alert.spinner > span::after { - content: ''; + content: ""; display: inline-block; position: absolute; top: 0; @@ -661,8 +661,8 @@ button > * { border: 1px solid #ccc; border-radius: 10px; background-color: #666; - -webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); - filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); + -webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 20%)); + filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 20%)); } .ol-popup::after, .ol-popup::before { @@ -695,7 +695,7 @@ button > * { right: -10px; width: 30px; height: 30px; - background-image: url(../../images/close.svg) !important; + background-image: url("../../images/close.svg") !important; background-repeat: no-repeat !important; } @@ -710,11 +710,12 @@ button > * { } .gm-style .gm-style-iw-t::after { - background: linear-gradient(45deg, rgb(102, 102, 102) 50%, rgba(255, 255, 255, 0) 51%, rgba(255, 255, 255, 0) 100%) !important; + /* stylelint-disable-next-line max-line-length */ + background: linear-gradient(45deg, rgb(102, 102, 102) 50%, rgba(255, 255, 255, 0%) 51%, rgba(255, 255, 255, 0%) 100%) !important; } .gm-style-iw button { - background-image: url(../../images/close.svg) !important; + background-image: url("../../images/close.svg") !important; background-repeat: no-repeat !important; } @@ -744,11 +745,11 @@ button > * { color: #fff; border: none; border-radius: 2px; - background-color: rgba(0, 60, 136, 0.5); + background-color: rgba(0, 60, 136, 50%); } .ol-layerswitcher:hover { - background-color: rgba(0, 60, 136, 0.7); + background-color: rgba(0, 60, 136, 70%); } .ol-layerswitcher label { diff --git a/helpers/auth.php b/helpers/auth.php index 9908efa..6762b2f 100644 --- a/helpers/auth.php +++ b/helpers/auth.php @@ -17,185 +17,185 @@ * along with this program; if not, see . */ - if (!defined('ROOT_DIR')) { define('ROOT_DIR', dirname(__DIR__)); } - require_once(ROOT_DIR . "/helpers/user.php"); - require_once(ROOT_DIR . "/helpers/utils.php"); - if (!defined('BASE_URL')) { define('BASE_URL', uUtils::getBaseUrl()); } +if (!defined('ROOT_DIR')) { define('ROOT_DIR', dirname(__DIR__)); } +require_once(ROOT_DIR . "/helpers/user.php"); +require_once(ROOT_DIR . "/helpers/utils.php"); +if (!defined('BASE_URL')) { define('BASE_URL', uUtils::getBaseUrl()); } /** * Authentication */ - class uAuth { +class uAuth { - /** @var bool Is user authenticated */ - private $isAuthenticated = false; - /** @var null|uUser */ - public $user; + /** @var bool Is user authenticated */ + private $isAuthenticated = false; + /** @var null|uUser */ + public $user; - public function __construct() { - $this->sessionStart(); + public function __construct() { + $this->sessionStart(); - $user = uUser::getFromSession(); - if ($user->isValid) { - $this->setAuthenticated($user); - } + $user = uUser::getFromSession(); + if ($user->isValid) { + $this->setAuthenticated($user); } - - /** - * Update user instance stored in session - */ - public function updateSession() { - if ($this->isAuthenticated()) { - $this->user->storeInSession(); - } - } - - /** - * Is user authenticated - * - * @return boolean True if authenticated, false otherwise - */ - public function isAuthenticated() { - return $this->isAuthenticated; - } - - /** - * Is authenticated user admin - * - * @return boolean True if admin, false otherwise - */ - public function isAdmin() { - return ($this->isAuthenticated && $this->user->isAdmin); - } - - /** - * Start php session - * - * @return void - */ - private function sessionStart() { - session_name("ulogger"); - session_start(); - } - - /** - * Terminate php session - * - * @return void - */ - private function sessionEnd() { - $_SESSION = []; - if (ini_get("session.use_cookies") && isset($_COOKIE[session_name()])) { - $params = session_get_cookie_params(); - setcookie(session_name(), '', time() - 42000, - $params["path"], $params["domain"], - $params["secure"], $params["httponly"] - ); - } - session_destroy(); - } - - /** - * Clean session variables - * - * @return void - */ - private function sessionCleanup() { - $_SESSION = []; - } - - /** - * Mark as authenticated, set user - * - * @param uUser $user - * @return void - */ - private function setAuthenticated($user) { - $this->isAuthenticated = true; - $this->user = $user; - } - - /** - * Check valid pass for given login - * - * @param string $login - * @param string $pass - * @return boolean True if valid - */ - public function checkLogin($login, $pass) { - if (!empty($login) && !empty($pass)) { - $user = new uUser($login); - if ($user->isValid && $user->validPassword($pass)) { - $this->setAuthenticated($user); - $this->sessionCleanup(); - $user->storeInSession(); - return true; - } - } - return false; - } - - /** - * Log out with redirect - * - * @param string $path URL path (without leading slash) - * @return void - */ - public function logOutWithRedirect($path = "") { - $this->sessionEnd(); - $this->exitWithRedirect($path); - } - - /** - * Send 401 headers - * - * @return void - */ - public function sendUnauthorizedHeader() { - header('WWW-Authenticate: OAuth realm="users@ulogger"'); - header('HTTP/1.1 401 Unauthorized', true, 401); - } - - /** - * Send 401 headers and exit - * - * @return void - */ - public function exitWithUnauthorized() { - $this->sendUnauthorizedHeader(); - exit(); - } - - /** - * Redirect browser and exit - * - * @param string $path Redirect URL path (without leading slash) - * @return void - */ - public function exitWithRedirect($path = "") { - $location = BASE_URL . $path; - header("Location: $location"); - exit(); - } - - /** - * Check session user has RW access to resource owned by given user - * - * @param int $ownerId - * @return bool True if has access - */ - public function hasReadWriteAccess($ownerId) { - return $this->isAuthenticated() && ($this->isAdmin() || $this->user->id === $ownerId); - } - - /** - * Check session user has RO access to resource owned by given user - * - * @param int $ownerId - * @return bool True if has access - */ - public function hasReadAccess($ownerId) { - return $this->hasReadWriteAccess($ownerId) || uConfig::getInstance()->publicTracks; - } - } + + /** + * Update user instance stored in session + */ + public function updateSession() { + if ($this->isAuthenticated()) { + $this->user->storeInSession(); + } + } + + /** + * Is user authenticated + * + * @return boolean True if authenticated, false otherwise + */ + public function isAuthenticated() { + return $this->isAuthenticated; + } + + /** + * Is authenticated user admin + * + * @return boolean True if admin, false otherwise + */ + public function isAdmin() { + return ($this->isAuthenticated && $this->user->isAdmin); + } + + /** + * Start php session + * + * @return void + */ + private function sessionStart() { + session_name("ulogger"); + session_start(); + } + + /** + * Terminate php session + * + * @return void + */ + private function sessionEnd() { + $_SESSION = []; + if (ini_get("session.use_cookies") && isset($_COOKIE[session_name()])) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, + $params["path"], $params["domain"], + $params["secure"], $params["httponly"] + ); + } + session_destroy(); + } + + /** + * Clean session variables + * + * @return void + */ + private function sessionCleanup() { + $_SESSION = []; + } + + /** + * Mark as authenticated, set user + * + * @param uUser $user + * @return void + */ + private function setAuthenticated($user) { + $this->isAuthenticated = true; + $this->user = $user; + } + + /** + * Check valid pass for given login + * + * @param string $login + * @param string $pass + * @return boolean True if valid + */ + public function checkLogin($login, $pass) { + if (!empty($login) && !empty($pass)) { + $user = new uUser($login); + if ($user->isValid && $user->validPassword($pass)) { + $this->setAuthenticated($user); + $this->sessionCleanup(); + $user->storeInSession(); + return true; + } + } + return false; + } + + /** + * Log out with redirect + * + * @param string $path URL path (without leading slash) + * @return void + */ + public function logOutWithRedirect($path = "") { + $this->sessionEnd(); + $this->exitWithRedirect($path); + } + + /** + * Send 401 headers + * + * @return void + */ + public function sendUnauthorizedHeader() { + header('WWW-Authenticate: OAuth realm="users@ulogger"'); + header('HTTP/1.1 401 Unauthorized', true, 401); + } + + /** + * Send 401 headers and exit + * + * @return void + */ + public function exitWithUnauthorized() { + $this->sendUnauthorizedHeader(); + exit(); + } + + /** + * Redirect browser and exit + * + * @param string $path Redirect URL path (without leading slash) + * @return void + */ + public function exitWithRedirect($path = "") { + $location = BASE_URL . $path; + header("Location: $location"); + exit(); + } + + /** + * Check session user has RW access to resource owned by given user + * + * @param int $ownerId + * @return bool True if has access + */ + public function hasReadWriteAccess($ownerId) { + return $this->isAuthenticated() && ($this->isAdmin() || $this->user->id === $ownerId); + } + + /** + * Check session user has RO access to resource owned by given user + * + * @param int $ownerId + * @return bool True if has access + */ + public function hasReadAccess($ownerId) { + return $this->hasReadWriteAccess($ownerId) || uConfig::getInstance()->publicTracks; + } + +} diff --git a/helpers/db.php b/helpers/db.php index cc1bd63..085087d 100644 --- a/helpers/db.php +++ b/helpers/db.php @@ -19,302 +19,302 @@ require_once(ROOT_DIR . "/helpers/utils.php"); - /** - * PDO wrapper - */ - class uDb extends PDO { - /** - * Singleton instance - * - * @var uDb Object instance - */ - protected static $instance; +/** + * PDO wrapper + */ +class uDb extends PDO { + /** + * Singleton instance + * + * @var uDb Object instance + */ + protected static $instance; - /** - * Table names - * - * @var array Array of names - */ - protected static $tables; + /** + * Table names + * + * @var array Array of names + */ + protected static $tables; - /** - * Database driver name - * - * @var string Driver - */ - protected static $driver; + /** + * Database driver name + * + * @var string Driver + */ + protected static $driver; - /** - * @var string Database DSN - */ - private static $dbdsn = ""; - /** - * @var string Database user - */ - private static $dbuser = ""; - /** - * @var string Database pass - */ - private static $dbpass = ""; - /** - * @var string Optional table names prefix, eg. "ulogger_" - */ - private static $dbprefix = ""; + /** + * @var string Database DSN + */ + private static $dbdsn = ""; + /** + * @var string Database user + */ + private static $dbuser = ""; + /** + * @var string Database pass + */ + private static $dbpass = ""; + /** + * @var string Optional table names prefix, eg. "ulogger_" + */ + private static $dbprefix = ""; - /** - * PDO constuctor - * - * @param string $dsn - * @param string $user - * @param string $pass - */ - public function __construct($dsn, $user, $pass) { - try { - $options = [ - PDO::ATTR_EMULATE_PREPARES => false, // try to use native prepared statements - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // throw exceptions - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // return assoc array by default - ]; - @parent::__construct($dsn, $user, $pass, $options); - self::$driver = $this->getAttribute(PDO::ATTR_DRIVER_NAME); - $this->setCharset("utf8"); - $this->initTables(); - } catch (PDOException $e) { - header("HTTP/1.1 503 Service Unavailable"); - die("Database connection error (" . $e->getMessage() . ")"); - } - } - - /** - * Initialize table names based on config - */ - private function initTables() { - self::$tables = []; - $prefix = preg_replace('/[^a-z0-9_]/i', '', self::$dbprefix); - self::$tables['positions'] = $prefix . "positions"; - self::$tables['tracks'] = $prefix . "tracks"; - self::$tables['users'] = $prefix . "users"; - self::$tables['config'] = $prefix . "config"; - self::$tables['ol_layers'] = $prefix . "ol_layers"; - } - - /** - * Returns singleton instance - * - * @return uDb Singleton instance - */ - public static function getInstance() { - if (!self::$instance) { - self::getConfig(); - self::$instance = new self(self::$dbdsn, self::$dbuser, self::$dbpass); - } - return self::$instance; - } - - /** - * Read database setup from config file - * @noinspection IssetArgumentExistenceInspection - * @noinspection PhpIncludeInspection - */ - private static function getConfig() { - $configFile = dirname(__DIR__) . "/config.php"; - if (!file_exists($configFile)) { - header("HTTP/1.1 503 Service Unavailable"); - die("Missing config.php file!"); - } - include($configFile); - if (isset($dbdsn)) { - self::$dbdsn = self::normalizeDsn($dbdsn); - } - if (isset($dbuser)) { - self::$dbuser = $dbuser; - } - if (isset($dbpass)) { - self::$dbpass = $dbpass; - } - if (isset($dbprefix)) { - self::$dbprefix = $dbprefix; - } - } - - /** - * Get full table name including prefix - * - * @param string $name Name - * @return string Full table name - */ - public function table($name) { - return self::$tables[$name]; - } - - /** - * Returns function name for getting date-time column value as unix timestamp - * @param string $column - * @return string - */ - public function unix_timestamp($column) { - switch (self::$driver) { - default: - case "mysql": - return "UNIX_TIMESTAMP($column)"; - break; - case "pgsql": - return "EXTRACT(EPOCH FROM $column::TIMESTAMP WITH TIME ZONE)"; - break; - case "sqlite": - return "STRFTIME('%s', $column)"; - break; - } - } - - /** - * Returns placeholder for LOB data types - * @return string - */ - public function lobPlaceholder() { - switch (self::$driver) { - default: - case "mysql": - case "sqlite": - return "?"; - break; - case "pgsql": - return "?::bytea"; - break; - } - } - - /** - * Returns construct for getting LOB as string - * @param string $column Column name - * @return string - */ - public function from_lob($column) { - switch (self::$driver) { - default: - case "mysql": - case "sqlite": - return $column; - break; - case "pgsql": - return "encode($column, 'escape') AS $column"; - break; - } - } - - /** - * Returns function name for getting date-time column value as 'YYYY-MM-DD hh:mm:ss' - * @param string $column - * @return string - */ - public function from_unixtime($column) { - switch (self::$driver) { - default: - case "mysql": - return "FROM_UNIXTIME($column)"; - break; - case "pgsql": - return "TO_TIMESTAMP($column)"; - break; - case "sqlite": - return "DATETIME($column, 'unixepoch')"; - break; - } - } - - /** - * Replace into - * Note: requires PostgreSQL >= 9.5 - * @param string $table Table name (without prefix) - * @param string[] $columns Column names - * @param string[][] $values Values [ [ value1, value2 ], ... ] - * @param string $key Unique column - * @param string $update Updated column - * @return string - */ - public function insertOrReplace($table, $columns, $values, $key, $update) { - $cols = implode(", ", $columns); - $rows = []; - foreach ($values as $row) { - $rows[] = "(" . implode(", ", $row) . ")"; - } - $vals = implode(", ", $rows); - switch (self::$driver) { - default: - case "mysql": - return "INSERT INTO {$this->table($table)} ($cols) - VALUES $vals - ON DUPLICATE KEY UPDATE $update = VALUES($update)"; - break; - case "pgsql": - return "INSERT INTO {$this->table($table)} ($cols) - VALUES $vals - ON CONFLICT ($key) DO UPDATE SET $update = EXCLUDED.$update"; - break; - case "sqlite": - return "REPLACE INTO {$this->table($table)} ($cols) - VALUES $vals"; - break; - } - } - - /** - * Set character set - * @param string $charset - */ - private function setCharset($charset) { - if (self::$driver === "pgsql" || self::$driver === "mysql") { - $this->exec("SET NAMES '$charset'"); - } - } - - /** - * Extract database name from DSN - * @param string $dsn - * @return string Empty string if not found - */ - public static function getDbName($dsn) { - $name = ""; - if (strpos($dsn, ":") !== false) { - list($scheme, $dsnWithoutScheme) = explode(":", $dsn, 2); - switch ($scheme) { - case "sqlite": - case "sqlite2": - case "sqlite3": - $pattern = "/(.+)/"; - break; - case "pgsql": - $pattern = "/dbname=([^; ]+)/"; - break; - default: - $pattern = "/dbname=([^;]+)/"; - break; - } - $result = preg_match($pattern, $dsnWithoutScheme, $matches); - if ($result === 1) { - $name = $matches[1]; - } - } - return $name; - } - - /** - * Normalize DSN. - * Make sure sqlite DSN file path is absolute - * @param $dsn string DSN - * @return string Normalized DSN - */ - public static function normalizeDsn($dsn) { - if (stripos($dsn, "sqlite") !== 0) { - return $dsn; - } - $arr = explode(":", $dsn, 2); - if (count($arr) < 2 || empty($arr[1]) || uUtils::isAbsolutePath($arr[1])) { - return $dsn; - } - $scheme = $arr[0]; - $path = dirname(__DIR__) . DIRECTORY_SEPARATOR . $arr[1]; - return $scheme . ":" . realpath(dirname($path)) . DIRECTORY_SEPARATOR . basename(($path)); + /** + * PDO constuctor + * + * @param string $dsn + * @param string $user + * @param string $pass + */ + public function __construct($dsn, $user, $pass) { + try { + $options = [ + PDO::ATTR_EMULATE_PREPARES => false, // try to use native prepared statements + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // throw exceptions + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // return assoc array by default + ]; + @parent::__construct($dsn, $user, $pass, $options); + self::$driver = $this->getAttribute(PDO::ATTR_DRIVER_NAME); + $this->setCharset("utf8"); + $this->initTables(); + } catch (PDOException $e) { + header("HTTP/1.1 503 Service Unavailable"); + die("Database connection error (" . $e->getMessage() . ")"); } } + + /** + * Initialize table names based on config + */ + private function initTables() { + self::$tables = []; + $prefix = preg_replace('/[^a-z0-9_]/i', '', self::$dbprefix); + self::$tables['positions'] = $prefix . "positions"; + self::$tables['tracks'] = $prefix . "tracks"; + self::$tables['users'] = $prefix . "users"; + self::$tables['config'] = $prefix . "config"; + self::$tables['ol_layers'] = $prefix . "ol_layers"; + } + + /** + * Returns singleton instance + * + * @return uDb Singleton instance + */ + public static function getInstance() { + if (!self::$instance) { + self::getConfig(); + self::$instance = new self(self::$dbdsn, self::$dbuser, self::$dbpass); + } + return self::$instance; + } + + /** + * Read database setup from config file + * @noinspection IssetArgumentExistenceInspection + * @noinspection PhpIncludeInspection + */ + private static function getConfig() { + $configFile = dirname(__DIR__) . "/config.php"; + if (!file_exists($configFile)) { + header("HTTP/1.1 503 Service Unavailable"); + die("Missing config.php file!"); + } + include($configFile); + if (isset($dbdsn)) { + self::$dbdsn = self::normalizeDsn($dbdsn); + } + if (isset($dbuser)) { + self::$dbuser = $dbuser; + } + if (isset($dbpass)) { + self::$dbpass = $dbpass; + } + if (isset($dbprefix)) { + self::$dbprefix = $dbprefix; + } + } + + /** + * Get full table name including prefix + * + * @param string $name Name + * @return string Full table name + */ + public function table($name) { + return self::$tables[$name]; + } + + /** + * Returns function name for getting date-time column value as unix timestamp + * @param string $column + * @return string + */ + public function unix_timestamp($column) { + switch (self::$driver) { + default: + case "mysql": + return "UNIX_TIMESTAMP($column)"; + break; + case "pgsql": + return "EXTRACT(EPOCH FROM $column::TIMESTAMP WITH TIME ZONE)"; + break; + case "sqlite": + return "STRFTIME('%s', $column)"; + break; + } + } + + /** + * Returns placeholder for LOB data types + * @return string + */ + public function lobPlaceholder() { + switch (self::$driver) { + default: + case "mysql": + case "sqlite": + return "?"; + break; + case "pgsql": + return "?::bytea"; + break; + } + } + + /** + * Returns construct for getting LOB as string + * @param string $column Column name + * @return string + */ + public function from_lob($column) { + switch (self::$driver) { + default: + case "mysql": + case "sqlite": + return $column; + break; + case "pgsql": + return "encode($column, 'escape') AS $column"; + break; + } + } + + /** + * Returns function name for getting date-time column value as 'YYYY-MM-DD hh:mm:ss' + * @param string $column + * @return string + */ + public function from_unixtime($column) { + switch (self::$driver) { + default: + case "mysql": + return "FROM_UNIXTIME($column)"; + break; + case "pgsql": + return "TO_TIMESTAMP($column)"; + break; + case "sqlite": + return "DATETIME($column, 'unixepoch')"; + break; + } + } + + /** + * Replace into + * Note: requires PostgreSQL >= 9.5 + * @param string $table Table name (without prefix) + * @param string[] $columns Column names + * @param string[][] $values Values [ [ value1, value2 ], ... ] + * @param string $key Unique column + * @param string $update Updated column + * @return string + */ + public function insertOrReplace($table, $columns, $values, $key, $update) { + $cols = implode(", ", $columns); + $rows = []; + foreach ($values as $row) { + $rows[] = "(" . implode(", ", $row) . ")"; + } + $vals = implode(", ", $rows); + switch (self::$driver) { + default: + case "mysql": + return "INSERT INTO {$this->table($table)} ($cols) + VALUES $vals + ON DUPLICATE KEY UPDATE $update = VALUES($update)"; + break; + case "pgsql": + return "INSERT INTO {$this->table($table)} ($cols) + VALUES $vals + ON CONFLICT ($key) DO UPDATE SET $update = EXCLUDED.$update"; + break; + case "sqlite": + return "REPLACE INTO {$this->table($table)} ($cols) + VALUES $vals"; + break; + } + } + + /** + * Set character set + * @param string $charset + */ + private function setCharset($charset) { + if (self::$driver === "pgsql" || self::$driver === "mysql") { + $this->exec("SET NAMES '$charset'"); + } + } + + /** + * Extract database name from DSN + * @param string $dsn + * @return string Empty string if not found + */ + public static function getDbName($dsn) { + $name = ""; + if ($dsn && strpos($dsn, ":") !== false) { + list($scheme, $dsnWithoutScheme) = explode(":", $dsn, 2); + switch ($scheme) { + case "sqlite": + case "sqlite2": + case "sqlite3": + $pattern = "/(.+)/"; + break; + case "pgsql": + $pattern = "/dbname=([^; ]+)/"; + break; + default: + $pattern = "/dbname=([^;]+)/"; + break; + } + $result = preg_match($pattern, $dsnWithoutScheme, $matches); + if ($result === 1) { + $name = $matches[1]; + } + } + return $name; + } + + /** + * Normalize DSN. + * Make sure sqlite DSN file path is absolute + * @param string $dsn DSN + * @return string Normalized DSN + */ + public static function normalizeDsn($dsn) { + if (stripos($dsn, "sqlite") !== 0) { + return $dsn; + } + $arr = explode(":", $dsn, 2); + if (count($arr) < 2 || empty($arr[1]) || uUtils::isAbsolutePath($arr[1])) { + return $dsn; + } + $scheme = $arr[0]; + $path = dirname(__DIR__) . DIRECTORY_SEPARATOR . $arr[1]; + return $scheme . ":" . realpath(dirname($path)) . DIRECTORY_SEPARATOR . basename(($path)); + } +} ?> diff --git a/helpers/lang.php b/helpers/lang.php index d352397..b6e14c1 100644 --- a/helpers/lang.php +++ b/helpers/lang.php @@ -17,100 +17,100 @@ * along with this program; if not, see . */ - require_once(ROOT_DIR . "/helpers/config.php"); +require_once(ROOT_DIR . "/helpers/config.php"); + +/** + * Localization + */ +class uLang { /** - * Localization - */ - class uLang { + * Available languages + * + * @var array + */ + private static $languages = [ + "ca" => "Català", + "cs" => "Čeština", + "de" => "Deutsch", + "el" => "Ελληνικά", + "en" => "English", + "es" => "Español", + "eu" => "Euskera", + "fr" => "Français", + "it" => "Italiano", + "pl" => "Polski", + "ru" => "Русский", + "sk" => "Slovenčina" + ]; - /** - * Available languages - * - * @var array - */ - private static $languages = [ - "ca" => "Català", - "cs" => "Čeština", - "de" => "Deutsch", - "el" => "Ελληνικά", - "en" => "English", - "es" => "Español", - "eu" => "Euskera", - "fr" => "Français", - "it" => "Italiano", - "pl" => "Polski", - "ru" => "Русский", - "sk" => "Slovenčina" - ]; + /** + * Application strings + * Array of key => translation pairs + * + * @var array + */ + private $strings; + /** + * Setup script strings + * Array of key => translation pairs + * + * @var array + */ + private $setupStrings; - /** - * Application strings - * Array of key => translation pairs - * - * @var array - */ - private $strings; - /** - * Setup script strings - * Array of key => translation pairs - * - * @var array - */ - private $setupStrings; + /** + * Constructor + * + * @param uConfig $config Config + */ + public function __construct($config) { + $language = $config->lang; + $lang = []; + $langSetup = []; + // always load en base + require(ROOT_DIR . "/lang/en.php"); - /** - * Constructor - * - * @param uConfig $config Config - */ - public function __construct($config) { - $language = $config->lang; - $lang = []; - $langSetup = []; - // always load en base - require(ROOT_DIR . "/lang/en.php"); - - // override with translated strings if needed - // missing strings will be displayed in English - if ($language !== "en" && array_key_exists($language, self::$languages)) { - require(ROOT_DIR . "/lang/$language.php"); - } - - $this->strings = $lang; - $this->setupStrings = $langSetup; - } - - /** - * Get supported languages array - * Language code => Native language name - * - * @return array - */ - public static function getLanguages() { - return self::$languages; - } - - /** - * Get translated strings array - * Key => translation string - * - * @return array - */ - public function getStrings() { - return $this->strings; - } - - /** - * Get translated strings array for setup script - * Key => translation string - * - * @return array - */ - public function getSetupStrings() { - return $this->setupStrings; + // override with translated strings if needed + // missing strings will be displayed in English + if ($language !== "en" && array_key_exists($language, self::$languages)) { + require(ROOT_DIR . "/lang/$language.php"); } + $this->strings = $lang; + $this->setupStrings = $langSetup; } - ?> + /** + * Get supported languages array + * Language code => Native language name + * + * @return array + */ + public static function getLanguages() { + return self::$languages; + } + + /** + * Get translated strings array + * Key => translation string + * + * @return array + */ + public function getStrings() { + return $this->strings; + } + + /** + * Get translated strings array for setup script + * Key => translation string + * + * @return array + */ + public function getSetupStrings() { + return $this->setupStrings; + } + +} + +?> diff --git a/helpers/position.php b/helpers/position.php index 96764ec..86f8ce9 100644 --- a/helpers/position.php +++ b/helpers/position.php @@ -21,485 +21,485 @@ require_once(ROOT_DIR . "/helpers/db.php"); require_once(ROOT_DIR . "/helpers/track.php"); require_once(ROOT_DIR . "/helpers/upload.php"); - /** - * Positions handling - */ - class uPosition { - /** @param int Position id */ - public $id; - /** @param int Unix time stamp */ - public $timestamp; - /** @param int User id */ - public $userId; - /** @param String User login */ - public $userLogin; - /** @param int Track id */ - public $trackId; - /** @param String Track name */ - public $trackName; - /** @param double Latitude */ - public $latitude; - /** @param double Longitude */ - public $longitude; - /** @param double Altitude */ - public $altitude; - /** @param double Speed */ - public $speed; - /** @param double Bearing */ - public $bearing; - /** @param int Accuracy */ - public $accuracy; - /** @param String Provider */ - public $provider; - /** @param String Comment */ - public $comment; - /** @param String Image path */ - public $image; +/** + * Positions handling + */ +class uPosition { + /** @param int Position id */ + public $id; + /** @param int Unix time stamp */ + public $timestamp; + /** @param int User id */ + public $userId; + /** @param String User login */ + public $userLogin; + /** @param int Track id */ + public $trackId; + /** @param String Track name */ + public $trackName; + /** @param double Latitude */ + public $latitude; + /** @param double Longitude */ + public $longitude; + /** @param double Altitude */ + public $altitude; + /** @param double Speed */ + public $speed; + /** @param double Bearing */ + public $bearing; + /** @param int Accuracy */ + public $accuracy; + /** @param String Provider */ + public $provider; + /** @param String Comment */ + public $comment; + /** @param String Image path */ + public $image; - public $isValid = false; + public $isValid = false; - /** - * Constructor - * @param integer $positionId Position id - */ - public function __construct($positionId = NULL) { + /** + * Constructor + * @param integer $positionId Position id + */ + public function __construct($positionId = null) { - if (!empty($positionId)) { - $query = "SELECT p.id, " . self::db()->unix_timestamp('p.time') . " AS tstamp, p.user_id, p.track_id, - p.latitude, p.longitude, p.altitude, p.speed, p.bearing, p.accuracy, p.provider, - p.comment, p.image, u.login, t.name - FROM " . self::db()->table('positions') . " p - LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id) - LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id) - WHERE p.id = ? LIMIT 1"; - $params = [ $positionId ]; - try { - $this->loadWithQuery($query, $params); - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - } - - /** - * Get db instance - * - * @return uDb instance - */ - private static function db() { - return uDb::getInstance(); - } - - /** - * Has image - * - * @return bool True if has image - */ - public function hasImage() { - return !empty($this->image); - } - - /** - * Add position - * - * @param int $userId - * @param int $trackId - * @param int $timestamp Unix time stamp - * @param double $lat - * @param double $lon - * @param double $altitude Optional - * @param double $speed Optional - * @param double $bearing Optional - * @param int $accuracy Optional - * @param string $provider Optional - * @param string $comment Optional - * @param int $image Optional - * @return int|bool New position id in database, false on error - */ - public static function add($userId, $trackId, $timestamp, $lat, $lon, - $altitude = NULL, $speed = NULL, $bearing = NULL, $accuracy = NULL, - $provider = NULL, $comment = NULL, $image = NULL) { - $positionId = false; - if (is_numeric($lat) && is_numeric($lon) && is_numeric($timestamp) && is_numeric($userId) && is_numeric($trackId)) { - $track = new uTrack($trackId); - if ($track->isValid && $track->userId === $userId) { - try { - $table = self::db()->table('positions'); - $query = "INSERT INTO $table - (user_id, track_id, - time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image) - VALUES (?, ?, " . self::db()->from_unixtime('?') . ", ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - $stmt = self::db()->prepare($query); - $params = [ $userId, $trackId, - $timestamp, $lat, $lon, $altitude, $speed, $bearing, $accuracy, $provider, $comment, $image ]; - $stmt->execute($params); - $positionId = (int) self::db()->lastInsertId("${table}_id_seq"); - } catch (PDOException $e) { - // TODO: handle error - syslog(LOG_ERR, $e->getMessage()); - } - } - } - return $positionId; - } - - /** - * Save position to database - * - * @return bool True if success, false otherwise - */ - public function update() { - $ret = false; - if ($this->isValid) { - try { - $query = "UPDATE " . self::db()->table('positions') . " SET - time = " . self::db()->from_unixtime('?') . ", user_id = ?, track_id = ?, latitude = ?, longitude = ?, altitude = ?, - speed = ?, bearing = ?, accuracy = ?, provider = ?, comment = ?, image = ? WHERE id = ?"; - $stmt = self::db()->prepare($query); - $params = [ - $this->timestamp, - $this->userId, - $this->trackId, - $this->latitude, - $this->longitude, - $this->altitude, - $this->speed, - $this->bearing, - $this->accuracy, - $this->provider, - $this->comment, - $this->image, - $this->id - ]; - $stmt->execute($params); - $ret = true; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $ret; - } - - /** - * Delete positions - * - * @return bool True if success, false otherwise - */ - public function delete() { - $ret = false; - if ($this->isValid) { - try { - $query = "DELETE FROM " . self::db()->table('positions') . " WHERE id = ?"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $this->id ]); - $this->removeImage(); - $ret = true; - $this->id = NULL; - $this->isValid = false; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $ret; - } - - /** - * Delete all user's positions, optionally limit to given track - * - * @param int $userId User id - * @param int $trackId Optional track id - * @return bool True if success, false otherwise - */ - public static function deleteAll($userId, $trackId = NULL) { - $ret = false; - if (!empty($userId)) { - $args = []; - $where = "WHERE user_id = ?"; - $args[] = $userId; - if (!empty($trackId)) { - $where .= " AND track_id = ?"; - $args[] = $trackId; - } - self::removeImages($userId, $trackId); - try { - $query = "DELETE FROM " . self::db()->table('positions') . " $where"; - $stmt = self::db()->prepare($query); - $stmt->execute($args); - $ret = true; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $ret; - } - - /** - * Get last position data from database - * (for given user if specified) - * - * @param int $userId Optional user id - * @return uPosition Position - */ - public static function getLast($userId = NULL) { - if (!empty($userId)) { - $where = "WHERE p.user_id = ?"; - $params = [ $userId ]; - } else { - $where = ""; - $params = NULL; - } + if (!empty($positionId)) { $query = "SELECT p.id, " . self::db()->unix_timestamp('p.time') . " AS tstamp, p.user_id, p.track_id, p.latitude, p.longitude, p.altitude, p.speed, p.bearing, p.accuracy, p.provider, p.comment, p.image, u.login, t.name FROM " . self::db()->table('positions') . " p LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id) LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id) - $where - ORDER BY p.time DESC, p.id DESC LIMIT 1"; - $position = new uPosition(); + WHERE p.id = ? LIMIT 1"; + $params = [ $positionId ]; try { - $position->loadWithQuery($query, $params); + $this->loadWithQuery($query, $params); } catch (PDOException $e) { // TODO: handle exception syslog(LOG_ERR, $e->getMessage()); } - return $position; - } - - /** - * Get last positions for all users - * - * @return array|bool Array of uPosition positions, false on error - */ - public static function getLastAllUsers() { - $query = "SELECT p.id, " . self::db()->unix_timestamp('p.time') . " AS tstamp, p.user_id, p.track_id, - p.latitude, p.longitude, p.altitude, p.speed, p.bearing, p.accuracy, p.provider, - p.comment, p.image, u.login, t.name - FROM " . self::db()->table('positions') . " p - LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id) - LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id) - WHERE p.id = ( - SELECT p2.id FROM " . self::db()->table('positions') . " p2 - WHERE p2.user_id = p.user_id - ORDER BY p2.time DESC, p2.id DESC - LIMIT 1 - )"; - $positionsArr = []; - try { - $result = self::db()->query($query); - while ($row = $result->fetch()) { - $positionsArr[] = self::rowToObject($row); - } - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - $positionsArr = false; - } - return $positionsArr; - } - - /** - * Get array of all positions - * - * @param int $userId Optional limit to given user id - * @param int $trackId Optional limit to given track id - * @param int $afterId Optional limit to positions with id greater then given id - * @param array $rules Optional rules - * @return uPosition[]|bool Array of uPosition positions, false on error - */ - public static function getAll($userId = NULL, $trackId = NULL, $afterId = NULL, $rules = []) { - if (!empty($userId)) { - $rules[] = "p.user_id = " . self::db()->quote($userId); - } - if (!empty($trackId)) { - $rules[] = "p.track_id = " . self::db()->quote($trackId); - } - if (!empty($afterId)) { - $rules[] = "p.id > " . self::db()->quote($afterId); - } - if (!empty($rules)) { - $where = "WHERE " . implode(" AND ", $rules); - } else { - $where = ""; - } - $query = "SELECT p.id, " . self::db()->unix_timestamp('p.time') . " AS tstamp, p.user_id, p.track_id, - p.latitude, p.longitude, p.altitude, p.speed, p.bearing, p.accuracy, p.provider, - p.comment, p.image, u.login, t.name - FROM " . self::db()->table('positions') . " p - LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id) - LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id) - $where - ORDER BY p.time, p.id"; - $positionsArr = []; - try { - $result = self::db()->query($query); - while ($row = $result->fetch()) { - $positionsArr[] = self::rowToObject($row); - } - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - $positionsArr = false; - } - return $positionsArr; - } - - /** - * Get array of all positions with image - * - * @param int $userId Optional limit to given user id - * @param int $trackId Optional limit to given track id - * @param int $afterId Optional limit to positions with id greater then given id - * @param array $rules Optional rules - * @return uPosition[]|bool Array of uPosition positions, false on error - */ - public static function getAllWithImage($userId = NULL, $trackId = NULL, $afterId = NULL, $rules = []) { - $rules[] = "p.image IS NOT NULL"; - return self::getAll($userId, $trackId, $afterId, $rules); - } - - /** - * Delete all user's uploads, optionally limit to given track - * - * @param int $userId User id - * @param int $trackId Optional track id - * @return bool True if success, false otherwise - */ - public static function removeImages($userId, $trackId = NULL) { - if (($positions = self::getAllWithImage($userId, $trackId)) !== false) { - foreach ($positions as $position) { - try { - $position->removeImage(); - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - return false; - } - } - } - return true; - } - - /** - * Add uploaded image - * @param array $imageMeta File metadata array - */ - public function setImage($imageMeta) { - if (!empty($imageMeta)) { - if ($this->hasImage()) { - $this->removeImage(); - } - $this->image = uUpload::add($imageMeta, $this->trackId); - $query = "UPDATE " . self::db()->table('positions') . " - SET image = ? WHERE id = ?"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $this->image, $this->id ]); - } - } - - /** - * Delete image - */ - public function removeImage() { - if ($this->hasImage()) { - $query = "UPDATE " . self::db()->table('positions') . " - SET image = NULL WHERE id = ?"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $this->id ]); - // ignore unlink errors - uUpload::delete($this->image); - $this->image = null; - } - } - - /** - * Calculate distance to target point using haversine formula - * - * @param uPosition $target Target position - * @return int Distance in meters - */ - public function distanceTo($target) { - $lat1 = deg2rad($this->latitude); - $lon1 = deg2rad($this->longitude); - $lat2 = deg2rad($target->latitude); - $lon2 = deg2rad($target->longitude); - $latD = $lat2 - $lat1; - $lonD = $lon2 - $lon1; - $bearing = 2 * asin(sqrt((sin($latD / 2) ** 2) + cos($lat1) * cos($lat2) * (sin($lonD / 2) ** 2))); - return $bearing * 6371000; - } - - /** - * Calculate time elapsed since target point - * - * @param uPosition $target Target position - * @return int Number of seconds - */ - public function secondsTo($target) { - return $this->timestamp - $target->timestamp; - } - - /** - * Convert database row to uPosition - * - * @param array $row Row - * @return uPosition Position - */ - private static function rowToObject($row) { - $position = new uPosition(); - $position->id = (int) $row['id']; - $position->timestamp = (int) $row['tstamp']; - $position->userId = (int) $row['user_id']; - $position->userLogin = $row['login']; - $position->trackId = (int) $row['track_id']; - $position->trackName = $row['name']; - $position->latitude = (double) $row['latitude']; - $position->longitude = (double) $row['longitude']; - $position->altitude = (double) $row['altitude']; - $position->speed = (double) $row['speed']; - $position->bearing = (double) $row['bearing']; - $position->accuracy = (int) $row['accuracy']; - $position->provider = $row['provider']; - $position->comment = $row['comment']; - $position->image = $row['image']; - $position->isValid = true; - return $position; - } - - /** - * Fill class properties with database query result - * - * @param string $query Query - * @param array|null $params Optional array of bind parameters - * @throws PDOException - */ - private function loadWithQuery($query, $params = NULL) { - $stmt = self::db()->prepare($query); - $stmt->execute($params); - - $stmt->bindColumn('id', $this->id, PDO::PARAM_INT); - $stmt->bindColumn('tstamp', $this->timestamp, PDO::PARAM_INT); - $stmt->bindColumn('user_id', $this->userId, PDO::PARAM_INT); - $stmt->bindColumn('track_id', $this->trackId, PDO::PARAM_INT); - $stmt->bindColumn('latitude', $this->latitude); - $stmt->bindColumn('longitude', $this->longitude); - $stmt->bindColumn('altitude', $this->altitude); - $stmt->bindColumn('speed', $this->speed); - $stmt->bindColumn('bearing', $this->bearing); - $stmt->bindColumn('accuracy', $this->accuracy, PDO::PARAM_INT); - $stmt->bindColumn('provider', $this->provider); - $stmt->bindColumn('comment', $this->comment); - $stmt->bindColumn('image', $this->image); - $stmt->bindColumn('login', $this->userLogin); - $stmt->bindColumn('name', $this->trackName); - if ($stmt->fetch(PDO::FETCH_BOUND)) { - $this->isValid = true; - } } } + /** + * Get db instance + * + * @return uDb instance + */ + private static function db() { + return uDb::getInstance(); + } + + /** + * Has image + * + * @return bool True if has image + */ + public function hasImage() { + return !empty($this->image); + } + + /** + * Add position + * + * @param int $userId + * @param int $trackId + * @param int $timestamp Unix time stamp + * @param double $lat + * @param double $lon + * @param double $altitude Optional + * @param double $speed Optional + * @param double $bearing Optional + * @param int $accuracy Optional + * @param string $provider Optional + * @param string $comment Optional + * @param int $image Optional + * @return int|bool New position id in database, false on error + */ + public static function add($userId, $trackId, $timestamp, $lat, $lon, + $altitude = null, $speed = null, $bearing = null, $accuracy = null, + $provider = null, $comment = null, $image = null) { + $positionId = false; + if (is_numeric($lat) && is_numeric($lon) && is_numeric($timestamp) && is_numeric($userId) && is_numeric($trackId)) { + $track = new uTrack($trackId); + if ($track->isValid && $track->userId === $userId) { + try { + $table = self::db()->table('positions'); + $query = "INSERT INTO $table + (user_id, track_id, + time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image) + VALUES (?, ?, " . self::db()->from_unixtime('?') . ", ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + $stmt = self::db()->prepare($query); + $params = [ $userId, $trackId, + $timestamp, $lat, $lon, $altitude, $speed, $bearing, $accuracy, $provider, $comment, $image ]; + $stmt->execute($params); + $positionId = (int) self::db()->lastInsertId("${table}_id_seq"); + } catch (PDOException $e) { + // TODO: handle error + syslog(LOG_ERR, $e->getMessage()); + } + } + } + return $positionId; + } + + /** + * Save position to database + * + * @return bool True if success, false otherwise + */ + public function update() { + $ret = false; + if ($this->isValid) { + try { + $query = "UPDATE " . self::db()->table('positions') . " SET + time = " . self::db()->from_unixtime('?') . ", user_id = ?, track_id = ?, latitude = ?, longitude = ?, altitude = ?, + speed = ?, bearing = ?, accuracy = ?, provider = ?, comment = ?, image = ? WHERE id = ?"; + $stmt = self::db()->prepare($query); + $params = [ + $this->timestamp, + $this->userId, + $this->trackId, + $this->latitude, + $this->longitude, + $this->altitude, + $this->speed, + $this->bearing, + $this->accuracy, + $this->provider, + $this->comment, + $this->image, + $this->id + ]; + $stmt->execute($params); + $ret = true; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $ret; + } + + /** + * Delete positions + * + * @return bool True if success, false otherwise + */ + public function delete() { + $ret = false; + if ($this->isValid) { + try { + $query = "DELETE FROM " . self::db()->table('positions') . " WHERE id = ?"; + $stmt = self::db()->prepare($query); + $stmt->execute([ $this->id ]); + $this->removeImage(); + $ret = true; + $this->id = null; + $this->isValid = false; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $ret; + } + + /** + * Delete all user's positions, optionally limit to given track + * + * @param int $userId User id + * @param int $trackId Optional track id + * @return bool True if success, false otherwise + */ + public static function deleteAll($userId, $trackId = null) { + $ret = false; + if (!empty($userId)) { + $args = []; + $where = "WHERE user_id = ?"; + $args[] = $userId; + if (!empty($trackId)) { + $where .= " AND track_id = ?"; + $args[] = $trackId; + } + self::removeImages($userId, $trackId); + try { + $query = "DELETE FROM " . self::db()->table('positions') . " $where"; + $stmt = self::db()->prepare($query); + $stmt->execute($args); + $ret = true; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $ret; + } + + /** + * Get last position data from database + * (for given user if specified) + * + * @param int $userId Optional user id + * @return uPosition Position + */ + public static function getLast($userId = null) { + if (!empty($userId)) { + $where = "WHERE p.user_id = ?"; + $params = [ $userId ]; + } else { + $where = ""; + $params = null; + } + $query = "SELECT p.id, " . self::db()->unix_timestamp('p.time') . " AS tstamp, p.user_id, p.track_id, + p.latitude, p.longitude, p.altitude, p.speed, p.bearing, p.accuracy, p.provider, + p.comment, p.image, u.login, t.name + FROM " . self::db()->table('positions') . " p + LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id) + LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id) + $where + ORDER BY p.time DESC, p.id DESC LIMIT 1"; + $position = new uPosition(); + try { + $position->loadWithQuery($query, $params); + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + return $position; + } + + /** + * Get last positions for all users + * + * @return array|bool Array of uPosition positions, false on error + */ + public static function getLastAllUsers() { + $query = "SELECT p.id, " . self::db()->unix_timestamp('p.time') . " AS tstamp, p.user_id, p.track_id, + p.latitude, p.longitude, p.altitude, p.speed, p.bearing, p.accuracy, p.provider, + p.comment, p.image, u.login, t.name + FROM " . self::db()->table('positions') . " p + LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id) + LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id) + WHERE p.id = ( + SELECT p2.id FROM " . self::db()->table('positions') . " p2 + WHERE p2.user_id = p.user_id + ORDER BY p2.time DESC, p2.id DESC + LIMIT 1 + )"; + $positionsArr = []; + try { + $result = self::db()->query($query); + while ($row = $result->fetch()) { + $positionsArr[] = self::rowToObject($row); + } + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + $positionsArr = false; + } + return $positionsArr; + } + + /** + * Get array of all positions + * + * @param int $userId Optional limit to given user id + * @param int $trackId Optional limit to given track id + * @param int $afterId Optional limit to positions with id greater then given id + * @param array $rules Optional rules + * @return uPosition[]|bool Array of uPosition positions, false on error + */ + public static function getAll($userId = null, $trackId = null, $afterId = null, $rules = []) { + if (!empty($userId)) { + $rules[] = "p.user_id = " . self::db()->quote($userId); + } + if (!empty($trackId)) { + $rules[] = "p.track_id = " . self::db()->quote($trackId); + } + if (!empty($afterId)) { + $rules[] = "p.id > " . self::db()->quote($afterId); + } + if (!empty($rules)) { + $where = "WHERE " . implode(" AND ", $rules); + } else { + $where = ""; + } + $query = "SELECT p.id, " . self::db()->unix_timestamp('p.time') . " AS tstamp, p.user_id, p.track_id, + p.latitude, p.longitude, p.altitude, p.speed, p.bearing, p.accuracy, p.provider, + p.comment, p.image, u.login, t.name + FROM " . self::db()->table('positions') . " p + LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id) + LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id) + $where + ORDER BY p.time, p.id"; + $positionsArr = []; + try { + $result = self::db()->query($query); + while ($row = $result->fetch()) { + $positionsArr[] = self::rowToObject($row); + } + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + $positionsArr = false; + } + return $positionsArr; + } + + /** + * Get array of all positions with image + * + * @param int $userId Optional limit to given user id + * @param int $trackId Optional limit to given track id + * @param int $afterId Optional limit to positions with id greater then given id + * @param array $rules Optional rules + * @return uPosition[]|bool Array of uPosition positions, false on error + */ + public static function getAllWithImage($userId = null, $trackId = null, $afterId = null, $rules = []) { + $rules[] = "p.image IS NOT NULL"; + return self::getAll($userId, $trackId, $afterId, $rules); + } + + /** + * Delete all user's uploads, optionally limit to given track + * + * @param int $userId User id + * @param int $trackId Optional track id + * @return bool True if success, false otherwise + */ + public static function removeImages($userId, $trackId = null) { + if (($positions = self::getAllWithImage($userId, $trackId)) !== false) { + foreach ($positions as $position) { + try { + $position->removeImage(); + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + return false; + } + } + } + return true; + } + + /** + * Add uploaded image + * @param array $imageMeta File metadata array + */ + public function setImage($imageMeta) { + if (!empty($imageMeta)) { + if ($this->hasImage()) { + $this->removeImage(); + } + $this->image = uUpload::add($imageMeta, $this->trackId); + $query = "UPDATE " . self::db()->table('positions') . " + SET image = ? WHERE id = ?"; + $stmt = self::db()->prepare($query); + $stmt->execute([ $this->image, $this->id ]); + } + } + + /** + * Delete image + */ + public function removeImage() { + if ($this->hasImage()) { + $query = "UPDATE " . self::db()->table('positions') . " + SET image = NULL WHERE id = ?"; + $stmt = self::db()->prepare($query); + $stmt->execute([ $this->id ]); + // ignore unlink errors + uUpload::delete($this->image); + $this->image = null; + } + } + + /** + * Calculate distance to target point using haversine formula + * + * @param uPosition $target Target position + * @return int Distance in meters + */ + public function distanceTo($target) { + $lat1 = deg2rad($this->latitude); + $lon1 = deg2rad($this->longitude); + $lat2 = deg2rad($target->latitude); + $lon2 = deg2rad($target->longitude); + $latD = $lat2 - $lat1; + $lonD = $lon2 - $lon1; + $bearing = 2 * asin(sqrt((sin($latD / 2) ** 2) + cos($lat1) * cos($lat2) * (sin($lonD / 2) ** 2))); + return $bearing * 6371000; + } + + /** + * Calculate time elapsed since target point + * + * @param uPosition $target Target position + * @return int Number of seconds + */ + public function secondsTo($target) { + return $this->timestamp - $target->timestamp; + } + + /** + * Convert database row to uPosition + * + * @param array $row Row + * @return uPosition Position + */ + private static function rowToObject($row) { + $position = new uPosition(); + $position->id = (int) $row['id']; + $position->timestamp = (int) $row['tstamp']; + $position->userId = (int) $row['user_id']; + $position->userLogin = $row['login']; + $position->trackId = (int) $row['track_id']; + $position->trackName = $row['name']; + $position->latitude = (double) $row['latitude']; + $position->longitude = (double) $row['longitude']; + $position->altitude = (double) $row['altitude']; + $position->speed = (double) $row['speed']; + $position->bearing = (double) $row['bearing']; + $position->accuracy = (int) $row['accuracy']; + $position->provider = $row['provider']; + $position->comment = $row['comment']; + $position->image = $row['image']; + $position->isValid = true; + return $position; + } + + /** + * Fill class properties with database query result + * + * @param string $query Query + * @param array|null $params Optional array of bind parameters + * @throws PDOException + */ + private function loadWithQuery($query, $params = null) { + $stmt = self::db()->prepare($query); + $stmt->execute($params); + + $stmt->bindColumn('id', $this->id, PDO::PARAM_INT); + $stmt->bindColumn('tstamp', $this->timestamp, PDO::PARAM_INT); + $stmt->bindColumn('user_id', $this->userId, PDO::PARAM_INT); + $stmt->bindColumn('track_id', $this->trackId, PDO::PARAM_INT); + $stmt->bindColumn('latitude', $this->latitude); + $stmt->bindColumn('longitude', $this->longitude); + $stmt->bindColumn('altitude', $this->altitude); + $stmt->bindColumn('speed', $this->speed); + $stmt->bindColumn('bearing', $this->bearing); + $stmt->bindColumn('accuracy', $this->accuracy, PDO::PARAM_INT); + $stmt->bindColumn('provider', $this->provider); + $stmt->bindColumn('comment', $this->comment); + $stmt->bindColumn('image', $this->image); + $stmt->bindColumn('login', $this->userLogin); + $stmt->bindColumn('name', $this->trackName); + if ($stmt->fetch(PDO::FETCH_BOUND)) { + $this->isValid = true; + } + } +} + ?> diff --git a/helpers/track.php b/helpers/track.php index 6010d41..6fd8a07 100644 --- a/helpers/track.php +++ b/helpers/track.php @@ -20,227 +20,227 @@ require_once(ROOT_DIR . "/helpers/db.php"); require_once(ROOT_DIR . "/helpers/position.php"); - /** - * Track handling - */ - class uTrack { - public $id; - public $userId; - public $name; - public $comment; +/** + * Track handling + */ +class uTrack { + public $id; + public $userId; + public $name; + public $comment; - public $isValid = false; + public $isValid = false; - /** - * Constructor - * - * @param int $trackId Track id - */ - public function __construct($trackId = NULL) { + /** + * Constructor + * + * @param int $trackId Track id + */ + public function __construct($trackId = null) { - if (!empty($trackId)) { - try { - $query = "SELECT id, user_id, name, comment FROM " . self::db()->table('tracks') . " WHERE id = ? LIMIT 1"; - $stmt = self::db()->prepare($query); - $stmt->execute([$trackId]); - $stmt->bindColumn('id', $this->id, PDO::PARAM_INT); - $stmt->bindColumn('user_id', $this->userId, PDO::PARAM_INT); - $stmt->bindColumn('name', $this->name); - $stmt->bindColumn('comment', $this->comment); - if ($stmt->fetch(PDO::FETCH_BOUND)) { - $this->isValid = true; - } - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - - } - } - - /** - * Get db instance - * - * @return uDb instance - */ - private static function db() { - return uDb::getInstance(); - } - - /** - * Add new track - * - * @param string $userId User id - * @param string $name Name - * @param string $comment Optional comment - * @return int|bool New track id, false on error - */ - public static function add($userId, $name, $comment = NULL) { - $trackId = false; - if (!empty($userId) && !empty($name)) { - try { - $table = self::db()->table('tracks'); - $query = "INSERT INTO $table (user_id, name, comment) VALUES (?, ?, ?)"; - $stmt = self::db()->prepare($query); - $params = [ $userId, $name, $comment ]; - $stmt->execute($params); - $trackId = (int) self::db()->lastInsertId("${table}_id_seq"); - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $trackId; - } - - /** - * Add new position to track - * - * @param int $userId - * @param int $timestamp Unix time stamp - * @param double $lat - * @param double $lon - * @param double $altitude Optional - * @param double $speed Optional - * @param double $bearing Optional - * @param int $accuracy Optional - * @param string $provider Optional - * @param string $comment Optional - * @param int $imageId Optional - * @return int|bool New position id in database, false on error - */ - public function addPosition($userId, $timestamp, $lat, $lon, - $altitude = NULL, $speed = NULL, $bearing = NULL, $accuracy = NULL, - $provider = NULL, $comment = NULL, $imageId = NULL) { - return uPosition::add($userId, $this->id, $timestamp, $lat, $lon, - $altitude, $speed, $bearing, $accuracy, $provider, $comment, $imageId); - } - - /** - * Delete track with all positions - * - * @return bool True if success, false otherwise - */ - public function delete() { - $ret = false; - if ($this->isValid) { - // delete positions - if (uPosition::deleteAll($this->userId, $this->id) === false) { - return false; - } - // delete track metadata - try { - $query = "DELETE FROM " . self::db()->table('tracks') . " WHERE id = ?"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $this->id ]); - $ret = true; - $this->id = NULL; - $this->userId = NULL; - $this->name = NULL; - $this->comment = NULL; - $this->isValid = false; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $ret; - } - - /** - * Update track - * - * @param string|null $name New name (not empty string) or NULL if not changed - * @param string|null $comment New comment or NULL if not changed (to remove content use empty string: "") - * @return bool True if success, false otherwise - */ - public function update($name = NULL, $comment = NULL) { - $ret = false; - if (empty($name)) { $name = $this->name; } - if (is_null($comment)) { $comment = $this->comment; } - if ($comment === "") { $comment = NULL; } - if ($this->isValid) { - try { - $query = "UPDATE " . self::db()->table('tracks') . " SET name = ?, comment = ? WHERE id = ?"; - $stmt = self::db()->prepare($query); - $params = [ $name, $comment, $this->id ]; - $stmt->execute($params); - $ret = true; - $this->name = $name; - $this->comment = $comment; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $ret; - } - - /** - * Delete all user's tracks - * - * @param string $userId User id - * @return bool True if success, false otherwise - */ - public static function deleteAll($userId) { - $ret = false; - if (!empty($userId) && uPosition::deleteAll($userId) === true) { - // remove all tracks - try { - $query = "DELETE FROM " . self::db()->table('tracks') . " WHERE user_id = ?"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $userId ]); - $ret = true; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $ret; - } - - /** - * Get all tracks - * - * @param int $userId Optional limit to user id - * @return array|bool Array of uTrack tracks, false on error - */ - public static function getAll($userId = NULL) { - if (!empty($userId)) { - $where = "WHERE user_id=" . self::db()->quote($userId); - } else { - $where = ""; - } - $query = "SELECT id, user_id, name, comment FROM " . self::db()->table('tracks') . " $where ORDER BY id DESC"; + if (!empty($trackId)) { try { - $result = self::db()->query($query); - $trackArr = []; - while ($row = $result->fetch()) { - $trackArr[] = self::rowToObject($row); + $query = "SELECT id, user_id, name, comment FROM " . self::db()->table('tracks') . " WHERE id = ? LIMIT 1"; + $stmt = self::db()->prepare($query); + $stmt->execute([$trackId]); + $stmt->bindColumn('id', $this->id, PDO::PARAM_INT); + $stmt->bindColumn('user_id', $this->userId, PDO::PARAM_INT); + $stmt->bindColumn('name', $this->name); + $stmt->bindColumn('comment', $this->comment); + if ($stmt->fetch(PDO::FETCH_BOUND)) { + $this->isValid = true; } } catch (PDOException $e) { // TODO: handle exception syslog(LOG_ERR, $e->getMessage()); - $trackArr = false; } - return $trackArr; - } - /** - * Convert database row to uTrack - * - * @param array $row Row - * @return uTrack Track - */ - private static function rowToObject($row) { - $track = new uTrack(); - $track->id = (int) $row['id']; - $track->userId = (int) $row['user_id']; - $track->name = $row['name']; - $track->comment = $row['comment']; - $track->isValid = true; - return $track; } } + /** + * Get db instance + * + * @return uDb instance + */ + private static function db() { + return uDb::getInstance(); + } + + /** + * Add new track + * + * @param string $userId User id + * @param string $name Name + * @param string $comment Optional comment + * @return int|bool New track id, false on error + */ + public static function add($userId, $name, $comment = null) { + $trackId = false; + if (!empty($userId) && !empty($name)) { + try { + $table = self::db()->table('tracks'); + $query = "INSERT INTO $table (user_id, name, comment) VALUES (?, ?, ?)"; + $stmt = self::db()->prepare($query); + $params = [ $userId, $name, $comment ]; + $stmt->execute($params); + $trackId = (int) self::db()->lastInsertId("${table}_id_seq"); + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $trackId; + } + + /** + * Add new position to track + * + * @param int $userId + * @param int $timestamp Unix time stamp + * @param double $lat + * @param double $lon + * @param double $altitude Optional + * @param double $speed Optional + * @param double $bearing Optional + * @param int $accuracy Optional + * @param string $provider Optional + * @param string $comment Optional + * @param int $imageId Optional + * @return int|bool New position id in database, false on error + */ + public function addPosition($userId, $timestamp, $lat, $lon, + $altitude = null, $speed = null, $bearing = null, $accuracy = null, + $provider = null, $comment = null, $imageId = null) { + return uPosition::add($userId, $this->id, $timestamp, $lat, $lon, + $altitude, $speed, $bearing, $accuracy, $provider, $comment, $imageId); + } + + /** + * Delete track with all positions + * + * @return bool True if success, false otherwise + */ + public function delete() { + $ret = false; + if ($this->isValid) { + // delete positions + if (uPosition::deleteAll($this->userId, $this->id) === false) { + return false; + } + // delete track metadata + try { + $query = "DELETE FROM " . self::db()->table('tracks') . " WHERE id = ?"; + $stmt = self::db()->prepare($query); + $stmt->execute([ $this->id ]); + $ret = true; + $this->id = null; + $this->userId = null; + $this->name = null; + $this->comment = null; + $this->isValid = false; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $ret; + } + + /** + * Update track + * + * @param string|null $name New name (not empty string) or null if not changed + * @param string|null $comment New comment or null if not changed (to remove content use empty string: "") + * @return bool True if success, false otherwise + */ + public function update($name = null, $comment = null) { + $ret = false; + if (empty($name)) { $name = $this->name; } + if (is_null($comment)) { $comment = $this->comment; } + if ($comment === "") { $comment = null; } + if ($this->isValid) { + try { + $query = "UPDATE " . self::db()->table('tracks') . " SET name = ?, comment = ? WHERE id = ?"; + $stmt = self::db()->prepare($query); + $params = [ $name, $comment, $this->id ]; + $stmt->execute($params); + $ret = true; + $this->name = $name; + $this->comment = $comment; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $ret; + } + + /** + * Delete all user's tracks + * + * @param string $userId User id + * @return bool True if success, false otherwise + */ + public static function deleteAll($userId) { + $ret = false; + if (!empty($userId) && uPosition::deleteAll($userId) === true) { + // remove all tracks + try { + $query = "DELETE FROM " . self::db()->table('tracks') . " WHERE user_id = ?"; + $stmt = self::db()->prepare($query); + $stmt->execute([ $userId ]); + $ret = true; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $ret; + } + + /** + * Get all tracks + * + * @param int $userId Optional limit to user id + * @return array|bool Array of uTrack tracks, false on error + */ + public static function getAll($userId = null) { + if (!empty($userId)) { + $where = "WHERE user_id=" . self::db()->quote($userId); + } else { + $where = ""; + } + $query = "SELECT id, user_id, name, comment FROM " . self::db()->table('tracks') . " $where ORDER BY id DESC"; + try { + $result = self::db()->query($query); + $trackArr = []; + while ($row = $result->fetch()) { + $trackArr[] = self::rowToObject($row); + } + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + $trackArr = false; + } + return $trackArr; + } + + /** + * Convert database row to uTrack + * + * @param array $row Row + * @return uTrack Track + */ + private static function rowToObject($row) { + $track = new uTrack(); + $track->id = (int) $row['id']; + $track->userId = (int) $row['user_id']; + $track->name = $row['name']; + $track->comment = $row['comment']; + $track->isValid = true; + return $track; + } +} + ?> \ No newline at end of file diff --git a/helpers/upload.php b/helpers/upload.php index ddbec56..d01546d 100644 --- a/helpers/upload.php +++ b/helpers/upload.php @@ -61,20 +61,20 @@ class uUpload { /** * Get file extension for given mime * @param $mime - * @return string|null Extension or NULL if not found + * @return string|null Extension or null if not found */ private static function getExtension($mime) { if (self::isKnownMime($mime)) { return self::getMimeMap()[$mime]; } - return NULL; + return null; } /** * Save file to uploads, basic sanitizing * @param array $uploaded File meta array from $_FILES[] * @param int $trackId - * @return string|NULL Unique file name, null on error + * @return string|null Unique file name, null on error */ public static function add($uploaded, $trackId) { try { @@ -82,7 +82,7 @@ class uUpload { } catch (Exception $e) { syslog(LOG_ERR, $e->getMessage()); // save exception to txt file as image replacement? - return NULL; + return null; } $extension = self::getExtension($fileMeta[self::META_TYPE]); @@ -93,7 +93,7 @@ class uUpload { if (move_uploaded_file($fileMeta[self::META_TMP_NAME], self::$uploadDir . $fileName)) { return $fileName; } - return NULL; + return null; } /** @@ -140,7 +140,7 @@ class uUpload { $uploadErrors[UPLOAD_ERR_CANT_WRITE] = "Failed to write file to disk"; $uploadErrors[UPLOAD_ERR_EXTENSION] = "A PHP extension stopped file upload"; - $file = NULL; + $file = null; $fileError = isset($fileMeta[self::META_ERROR]) ? $fileMeta[self::META_ERROR] : UPLOAD_ERR_OK; if ($fileMeta[self::META_SIZE] > uUtils::getSystemUploadLimit() && $fileError == UPLOAD_ERR_OK) { $fileError = UPLOAD_ERR_FORM_SIZE; diff --git a/helpers/user.php b/helpers/user.php index b878343..67c5d87 100644 --- a/helpers/user.php +++ b/helpers/user.php @@ -20,222 +20,222 @@ require_once(ROOT_DIR . "/helpers/track.php"); require_once(ROOT_DIR . "/helpers/position.php"); - /** - * User handling routines - */ - class uUser { - public $id; - public $login; - public $hash; - public $isAdmin = false; - public $isValid = false; +/** + * User handling routines + */ +class uUser { + public $id; + public $login; + public $hash; + public $isAdmin = false; + public $isValid = false; - /** - * Constructor - * - * @param string $login Login - */ - public function __construct($login = NULL) { - if (!empty($login)) { - try { - $query = "SELECT id, login, password, admin FROM " . self::db()->table('users') . " WHERE login = ? LIMIT 1"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $login ]); - $stmt->bindColumn('id', $this->id, PDO::PARAM_INT); - $stmt->bindColumn('login', $this->login); - $stmt->bindColumn('password', $this->hash); - $stmt->bindColumn('admin', $this->isAdmin, PDO::PARAM_BOOL); - if ($stmt->fetch(PDO::FETCH_BOUND)) { - $this->isValid = true; - } - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - } - - /** - * Get db instance - * - * @return uDb instance - */ - private static function db() { - return uDb::getInstance(); - } - - /** - * Add new user - * - * @param string $login Login - * @param string $pass Password - * @param bool $isAdmin Is admin - * @return int|bool New user id, false on error - */ - public static function add($login, $pass, $isAdmin = false) { - $userid = false; - if (!empty($login) && !empty($pass)) { - $hash = password_hash($pass, PASSWORD_DEFAULT); - $table = self::db()->table('users'); - try { - $query = "INSERT INTO $table (login, password, admin) VALUES (?, ?, ?)"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $login, $hash, (int) $isAdmin ]); - $userid = (int) self::db()->lastInsertId("${table}_id_seq"); - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $userid; - } - - /** - * Delete user - * This will also delete all user's positions and tracks - * - * @return bool True if success, false otherwise - */ - public function delete() { - $ret = false; - if ($this->isValid) { - // remove tracks and positions - if (uTrack::deleteAll($this->id) === false) { - return false; - } - // remove user - try { - $query = "DELETE FROM " . self::db()->table('users') . " WHERE id = ?"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $this->id ]); - $ret = true; - $this->id = NULL; - $this->login = NULL; - $this->hash = NULL; - $this->isValid = false; - $this->isAdmin = false; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $ret; - } - - /** - * Set user admin status - * - * @param bool $isAdmin True if is admin - * @return bool True on success, false otherwise - */ - public function setAdmin($isAdmin) { - $ret = false; + /** + * Constructor + * + * @param string $login Login + */ + public function __construct($login = null) { + if (!empty($login)) { try { - $query = "UPDATE " . self::db()->table('users') . " SET admin = ? WHERE login = ?"; + $query = "SELECT id, login, password, admin FROM " . self::db()->table('users') . " WHERE login = ? LIMIT 1"; $stmt = self::db()->prepare($query); - $stmt->execute([ (int) $isAdmin, $this->login ]); - $ret = true; - $this->isAdmin = $isAdmin; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - return $ret; - } - - /** - * Set user password - * - * @param string $pass Password - * @return bool True on success, false otherwise - */ - public function setPass($pass) { - $ret = false; - if (!empty($this->login) && !empty($pass)) { - $hash = password_hash($pass, PASSWORD_DEFAULT); - try { - $query = "UPDATE " . self::db()->table('users') . " SET password = ? WHERE login = ?"; - $stmt = self::db()->prepare($query); - $stmt->execute([ $hash, $this->login ]); - $ret = true; - $this->hash = $hash; - } catch (PDOException $e) { - // TODO: handle exception - syslog(LOG_ERR, $e->getMessage()); - } - } - return $ret; - } - - /** - * Check if given password matches user's one - * - * @param String $password Password - * @return bool True if matches, false otherwise - */ - public function validPassword($password) { - return password_verify($password, $this->hash); - } - - /** - * Store uUser object in session - */ - public function storeInSession() { - $_SESSION['user'] = $this; - } - - /** - * Fill uUser object properties from session data - * @return uUser - */ - public static function getFromSession() { - $user = new uUser(); - if (isset($_SESSION['user'])) { - $sessionUser = $_SESSION['user']; - $user->id = $sessionUser->id; - $user->login = $sessionUser->login; - $user->hash = $sessionUser->hash; - $user->isAdmin = $sessionUser->isAdmin; - $user->isValid = $sessionUser->isValid; - } - return $user; - } - - /** - * Get all users - * - * @return uUser[]|bool Array of uUser users, false on error - */ - public static function getAll() { - try { - $query = "SELECT id, login, password, admin FROM " . self::db()->table('users') . " ORDER BY login"; - $result = self::db()->query($query); - $userArr = []; - while ($row = $result->fetch()) { - $userArr[] = self::rowToObject($row); + $stmt->execute([ $login ]); + $stmt->bindColumn('id', $this->id, PDO::PARAM_INT); + $stmt->bindColumn('login', $this->login); + $stmt->bindColumn('password', $this->hash); + $stmt->bindColumn('admin', $this->isAdmin, PDO::PARAM_BOOL); + if ($stmt->fetch(PDO::FETCH_BOUND)) { + $this->isValid = true; } } catch (PDOException $e) { // TODO: handle exception syslog(LOG_ERR, $e->getMessage()); - $userArr = false; } - return $userArr; - } - - /** - * Convert database row to uUser - * - * @param array $row Row - * @return uUser User - */ - private static function rowToObject($row) { - $user = new uUser(); - $user->id = (int) $row['id']; - $user->login = $row['login']; - $user->hash = $row['password']; - $user->isAdmin = (bool) $row['admin']; - $user->isValid = true; - return $user; } } + + /** + * Get db instance + * + * @return uDb instance + */ + private static function db() { + return uDb::getInstance(); + } + + /** + * Add new user + * + * @param string $login Login + * @param string $pass Password + * @param bool $isAdmin Is admin + * @return int|bool New user id, false on error + */ + public static function add($login, $pass, $isAdmin = false) { + $userid = false; + if (!empty($login) && !empty($pass)) { + $hash = password_hash($pass, PASSWORD_DEFAULT); + $table = self::db()->table('users'); + try { + $query = "INSERT INTO $table (login, password, admin) VALUES (?, ?, ?)"; + $stmt = self::db()->prepare($query); + $stmt->execute([ $login, $hash, (int) $isAdmin ]); + $userid = (int) self::db()->lastInsertId("${table}_id_seq"); + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $userid; + } + + /** + * Delete user + * This will also delete all user's positions and tracks + * + * @return bool True if success, false otherwise + */ + public function delete() { + $ret = false; + if ($this->isValid) { + // remove tracks and positions + if (uTrack::deleteAll($this->id) === false) { + return false; + } + // remove user + try { + $query = "DELETE FROM " . self::db()->table('users') . " WHERE id = ?"; + $stmt = self::db()->prepare($query); + $stmt->execute([ $this->id ]); + $ret = true; + $this->id = null; + $this->login = null; + $this->hash = null; + $this->isValid = false; + $this->isAdmin = false; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $ret; + } + + /** + * Set user admin status + * + * @param bool $isAdmin True if is admin + * @return bool True on success, false otherwise + */ + public function setAdmin($isAdmin) { + $ret = false; + try { + $query = "UPDATE " . self::db()->table('users') . " SET admin = ? WHERE login = ?"; + $stmt = self::db()->prepare($query); + $stmt->execute([ (int) $isAdmin, $this->login ]); + $ret = true; + $this->isAdmin = $isAdmin; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + return $ret; + } + + /** + * Set user password + * + * @param string $pass Password + * @return bool True on success, false otherwise + */ + public function setPass($pass) { + $ret = false; + if (!empty($this->login) && !empty($pass)) { + $hash = password_hash($pass, PASSWORD_DEFAULT); + try { + $query = "UPDATE " . self::db()->table('users') . " SET password = ? WHERE login = ?"; + $stmt = self::db()->prepare($query); + $stmt->execute([ $hash, $this->login ]); + $ret = true; + $this->hash = $hash; + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + } + } + return $ret; + } + + /** + * Check if given password matches user's one + * + * @param String $password Password + * @return bool True if matches, false otherwise + */ + public function validPassword($password) { + return password_verify($password, $this->hash); + } + + /** + * Store uUser object in session + */ + public function storeInSession() { + $_SESSION['user'] = $this; + } + + /** + * Fill uUser object properties from session data + * @return uUser + */ + public static function getFromSession() { + $user = new uUser(); + if (isset($_SESSION['user'])) { + $sessionUser = $_SESSION['user']; + $user->id = $sessionUser->id; + $user->login = $sessionUser->login; + $user->hash = $sessionUser->hash; + $user->isAdmin = $sessionUser->isAdmin; + $user->isValid = $sessionUser->isValid; + } + return $user; + } + + /** + * Get all users + * + * @return uUser[]|bool Array of uUser users, false on error + */ + public static function getAll() { + try { + $query = "SELECT id, login, password, admin FROM " . self::db()->table('users') . " ORDER BY login"; + $result = self::db()->query($query); + $userArr = []; + while ($row = $result->fetch()) { + $userArr[] = self::rowToObject($row); + } + } catch (PDOException $e) { + // TODO: handle exception + syslog(LOG_ERR, $e->getMessage()); + $userArr = false; + } + return $userArr; + } + + /** + * Convert database row to uUser + * + * @param array $row Row + * @return uUser User + */ + private static function rowToObject($row) { + $user = new uUser(); + $user->id = (int) $row['id']; + $user->login = $row['login']; + $user->hash = $row['password']; + $user->isAdmin = (bool) $row['admin']; + $user->isValid = true; + return $user; + } +} ?> diff --git a/helpers/utils.php b/helpers/utils.php index 402e06f..f82ab6a 100644 --- a/helpers/utils.php +++ b/helpers/utils.php @@ -17,211 +17,211 @@ * along with this program; if not, see . */ - /** - * Various util functions - */ - class uUtils { - - /** - * Calculate maximum allowed size of uploaded file - * for current PHP settings - * - * @return int Number of bytes - */ - public static function getSystemUploadLimit() { - $upload_max_filesize = self::iniGetBytes('upload_max_filesize'); - $post_max_size = self::iniGetBytes('post_max_size'); - // post_max_size = 0 means unlimited size - if ($post_max_size === 0) { $post_max_size = $upload_max_filesize; } - $memory_limit = self::iniGetBytes('memory_limit'); - // memory_limit = -1 means no limit - if ($memory_limit < 0) { $memory_limit = $post_max_size; } - return min($upload_max_filesize, $post_max_size, $memory_limit); - } - - /** - * @param $path string Path - * @return bool True if is absolute - */ - public static function isAbsolutePath($path) { - return $path[0] === '/' || $path[0] === '\\' || preg_match('/^[a-zA-Z]:\\\\/', $path); - } - - /** - * Get number of bytes from ini parameter. - * Optionally parses shorthand byte values (G, M, B) - * - * @param string $iniParam Ini parameter name - * @return int Bytes - * @noinspection PhpMissingBreakStatementInspection - */ - private static function iniGetBytes($iniParam) { - $iniStr = ini_get($iniParam); - $val = (float) $iniStr; - $suffix = substr(trim($iniStr), -1); - if (ctype_alpha($suffix)) { - switch (strtolower($suffix)) { - case 'g': - $val *= 1024; - case 'm': - $val *= 1024; - case 'k': - $val *= 1024; - } - } - return (int) $val; - } - - /** - * Exit with error message - * - * @param string $errorMessage Message - * @param array|null $extra Optional array of extra parameters - */ - public static function exitWithError($errorMessage, $extra = NULL) { - $extra['message'] = $errorMessage; - self::exitWithStatus(true, $extra); - } - - /** - * Exit with successful status code - * - * @param array|null $extra Optional array of extra parameters - */ - public static function exitWithSuccess($extra = NULL) { - self::exitWithStatus(false, $extra); - } - - /** - * Exit with xml response - * @param boolean $isError Error if true - * @param array|null $extra Optional array of extra parameters - */ - private static function exitWithStatus($isError, $extra = NULL) { - $output = []; - if ($isError) { - $output["error"] = true; - } - if (!empty($extra)) { - foreach ($extra as $key => $value) { - $output[$key] = $value; - } - } - header("Content-type: application/json"); - echo json_encode($output); - exit; - } - - /** - * Calculate app base URL - * Returned URL has trailing slash. - * - * @return string URL - */ - public static function getBaseUrl() { - $proto = (!isset($_SERVER["HTTPS"]) || $_SERVER["HTTPS"] === "" || $_SERVER["HTTPS"] === "off") ? "http://" : "https://"; - // Check if we are behind an https proxy - if (isset($_SERVER["HTTP_X_FORWARDED_PROTO"]) && $_SERVER["HTTP_X_FORWARDED_PROTO"] === "https") { - $proto = "https://"; - } - $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : ""; - if (realpath($_SERVER["SCRIPT_FILENAME"])) { - $scriptPath = substr(dirname(realpath($_SERVER["SCRIPT_FILENAME"])), strlen(ROOT_DIR)); - } else { - // for phpunit - $scriptPath = substr(dirname($_SERVER["SCRIPT_FILENAME"]), strlen(ROOT_DIR)); - } - $self = dirname($_SERVER["PHP_SELF"]); - $path = str_replace("\\", "/", substr($self, 0, strlen($self) - strlen($scriptPath))); - - return $proto . str_replace("//", "/", $host . $path . "/"); - } - - public static function postFloat($name, $default = NULL) { - return self::requestValue($name, $default, INPUT_POST, FILTER_VALIDATE_FLOAT); - } - - public static function getFloat($name, $default = NULL) { - return self::requestValue($name, $default, INPUT_GET, FILTER_VALIDATE_FLOAT); - } - - public static function postPass($name, $default = NULL) { - return self::requestValue($name, $default, INPUT_POST); - } - - public static function postString($name, $default = NULL) { - return self::requestString($name, $default, INPUT_POST); - } - - public static function getString($name, $default = NULL) { - return self::requestString($name, $default, INPUT_GET); - } - - public static function postBool($name, $default = NULL) { - $input = filter_input(INPUT_POST, $name, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - return $input !== null ? (bool) $input : $default; - } - - public static function getBool($name, $default = NULL) { - $input = filter_input(INPUT_GET, $name, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - return $input !== null ? (bool) $input : $default; - } - - public static function postInt($name, $default = NULL) { - return self::requestInt($name, $default, INPUT_POST); - } - - public static function getInt($name, $default = NULL) { - return self::requestInt($name, $default, INPUT_GET); - } - - public static function requestFile($name, $default = NULL) { - if (isset($_FILES[$name])) { - $files = $_FILES[$name]; - if (isset($files["name"], $files["type"], $files["size"], $files["tmp_name"])) { - return $_FILES[$name]; - } - } - return $default; - } - - public static function postArray($name, $default = NULL) { - return ((isset($_POST[$name]) && is_array($_POST[$name])) ? $_POST[$name] : $default); - } - - /** - * @param string $name Input name - * @param boolean $checkMime Optionally check mime with known types - * @return array File metadata array - * @throws Exception Upload exception - * @throws ErrorException Internal server exception - */ - public static function requireFile($name, $checkMime = false) { - return uUpload::sanitizeUpload($_FILES[$name], $checkMime); - } - - private static function requestString($name, $default, $type) { - if (is_string(($val = self::requestValue($name, $default, $type)))) { - return trim($val); - } - return $val; - } - - private static function requestInt($name, $default, $type) { - if (is_float(($val = self::requestValue($name, $default, $type, FILTER_VALIDATE_FLOAT)))) { - return (int) round($val); - } - return self::requestValue($name, $default, $type, FILTER_VALIDATE_INT); - } - - private static function requestValue($name, $default, $type, $filters = FILTER_DEFAULT, $flags = []) { - $input = filter_input($type, $name, $filters, $flags); - if ($input !== false && $input !== null) { - return $input; - } - return $default; - } +/** + * Various util functions + */ +class uUtils { + /** + * Calculate maximum allowed size of uploaded file + * for current PHP settings + * + * @return int Number of bytes + */ + public static function getSystemUploadLimit() { + $upload_max_filesize = self::iniGetBytes('upload_max_filesize'); + $post_max_size = self::iniGetBytes('post_max_size'); + // post_max_size = 0 means unlimited size + if ($post_max_size === 0) { $post_max_size = $upload_max_filesize; } + $memory_limit = self::iniGetBytes('memory_limit'); + // memory_limit = -1 means no limit + if ($memory_limit < 0) { $memory_limit = $post_max_size; } + return min($upload_max_filesize, $post_max_size, $memory_limit); } + /** + * @param $path string Path + * @return bool True if is absolute + */ + public static function isAbsolutePath($path) { + return $path[0] === '/' || $path[0] === '\\' || preg_match('/^[a-zA-Z]:\\\\/', $path); + } + + /** + * Get number of bytes from ini parameter. + * Optionally parses shorthand byte values (G, M, B) + * + * @param string $iniParam Ini parameter name + * @return int Bytes + * @noinspection PhpMissingBreakStatementInspection + */ + private static function iniGetBytes($iniParam) { + $iniStr = ini_get($iniParam); + $val = (float) $iniStr; + $suffix = substr(trim($iniStr), -1); + if (ctype_alpha($suffix)) { + switch (strtolower($suffix)) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + } + return (int) $val; + } + + /** + * Exit with error message + * + * @param string $errorMessage Message + * @param array|null $extra Optional array of extra parameters + */ + public static function exitWithError($errorMessage, $extra = null) { + $extra['message'] = $errorMessage; + self::exitWithStatus(true, $extra); + } + + /** + * Exit with successful status code + * + * @param array|null $extra Optional array of extra parameters + */ + public static function exitWithSuccess($extra = null) { + self::exitWithStatus(false, $extra); + } + + /** + * Exit with xml response + * @param boolean $isError Error if true + * @param array|null $extra Optional array of extra parameters + */ + private static function exitWithStatus($isError, $extra = null) { + $output = []; + if ($isError) { + $output["error"] = true; + } + if (!empty($extra)) { + foreach ($extra as $key => $value) { + $output[$key] = $value; + } + } + header("Content-type: application/json"); + echo json_encode($output); + exit; + } + + /** + * Calculate app base URL + * Returned URL has trailing slash. + * + * @return string URL + */ + public static function getBaseUrl() { + $proto = (!isset($_SERVER["HTTPS"]) || $_SERVER["HTTPS"] === "" || $_SERVER["HTTPS"] === "off") ? "http://" : "https://"; + // Check if we are behind an https proxy + if (isset($_SERVER["HTTP_X_FORWARDED_PROTO"]) && $_SERVER["HTTP_X_FORWARDED_PROTO"] === "https") { + $proto = "https://"; + } + $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : ""; + if (realpath($_SERVER["SCRIPT_FILENAME"])) { + $scriptPath = substr(dirname(realpath($_SERVER["SCRIPT_FILENAME"])), strlen(ROOT_DIR)); + } else { + // for phpunit + $scriptPath = substr(dirname($_SERVER["SCRIPT_FILENAME"]), strlen(ROOT_DIR)); + } + $self = dirname($_SERVER["PHP_SELF"]); + $path = str_replace("\\", "/", substr($self, 0, strlen($self) - strlen($scriptPath))); + + return $proto . str_replace("//", "/", $host . $path . "/"); + } + + public static function postFloat($name, $default = null) { + return self::requestValue($name, $default, INPUT_POST, FILTER_VALIDATE_FLOAT); + } + + public static function getFloat($name, $default = null) { + return self::requestValue($name, $default, INPUT_GET, FILTER_VALIDATE_FLOAT); + } + + public static function postPass($name, $default = null) { + return self::requestValue($name, $default, INPUT_POST); + } + + public static function postString($name, $default = null) { + return self::requestString($name, $default, INPUT_POST); + } + + public static function getString($name, $default = null) { + return self::requestString($name, $default, INPUT_GET); + } + + public static function postBool($name, $default = null) { + $input = filter_input(INPUT_POST, $name, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + return $input !== null ? (bool) $input : $default; + } + + public static function getBool($name, $default = null) { + $input = filter_input(INPUT_GET, $name, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + return $input !== null ? (bool) $input : $default; + } + + public static function postInt($name, $default = null) { + return self::requestInt($name, $default, INPUT_POST); + } + + public static function getInt($name, $default = null) { + return self::requestInt($name, $default, INPUT_GET); + } + + public static function requestFile($name, $default = null) { + if (isset($_FILES[$name])) { + $files = $_FILES[$name]; + if (isset($files["name"], $files["type"], $files["size"], $files["tmp_name"])) { + return $_FILES[$name]; + } + } + return $default; + } + + public static function postArray($name, $default = null) { + return ((isset($_POST[$name]) && is_array($_POST[$name])) ? $_POST[$name] : $default); + } + + /** + * @param string $name Input name + * @param boolean $checkMime Optionally check mime with known types + * @return array File metadata array + * @throws Exception Upload exception + * @throws ErrorException Internal server exception + */ + public static function requireFile($name, $checkMime = false) { + return uUpload::sanitizeUpload($_FILES[$name], $checkMime); + } + + private static function requestString($name, $default, $type) { + if (is_string(($val = self::requestValue($name, $default, $type)))) { + return trim($val); + } + return $val; + } + + private static function requestInt($name, $default, $type) { + if (is_float(($val = self::requestValue($name, $default, $type, FILTER_VALIDATE_FLOAT)))) { + return (int) round($val); + } + return self::requestValue($name, $default, $type, FILTER_VALIDATE_INT); + } + + private static function requestValue($name, $default, $type, $filters = FILTER_DEFAULT, $flags = []) { + $input = filter_input($type, $name, $filters, $flags); + if ($input !== false && $input !== null) { + return $input; + } + return $default; + } + +} + ?> diff --git a/index.php b/index.php index 4201515..bc72595 100644 --- a/index.php +++ b/index.php @@ -17,32 +17,32 @@ * along with this program; if not, see . */ - require_once(__DIR__ . '/helpers/auth.php'); - require_once(ROOT_DIR . '/helpers/config.php'); - require_once(ROOT_DIR . '/helpers/position.php'); - require_once(ROOT_DIR . '/helpers/track.php'); - require_once(ROOT_DIR . '/helpers/utils.php'); - require_once(ROOT_DIR . '/helpers/lang.php'); +require_once(__DIR__ . '/helpers/auth.php'); +require_once(ROOT_DIR . '/helpers/config.php'); +require_once(ROOT_DIR . '/helpers/position.php'); +require_once(ROOT_DIR . '/helpers/track.php'); +require_once(ROOT_DIR . '/helpers/utils.php'); +require_once(ROOT_DIR . '/helpers/lang.php'); - $login = uUtils::postString('user'); - $pass = uUtils::postPass('pass'); - $action = uUtils::postString('action'); +$login = uUtils::postString('user'); +$pass = uUtils::postPass('pass'); +$action = uUtils::postString('action'); - $config = uConfig::getInstance(); - $lang = (new uLang($config))->getStrings(); - $langsArr = uLang::getLanguages(); +$config = uConfig::getInstance(); +$lang = (new uLang($config))->getStrings(); +$langsArr = uLang::getLanguages(); - $auth = new uAuth(); - if ($action === 'auth') { - $auth->checkLogin($login, $pass); - } +$auth = new uAuth(); +if ($action === 'auth') { + $auth->checkLogin($login, $pass); +} - if ($action === 'auth' && !$auth->isAuthenticated()) { - $auth->exitWithRedirect('login.php?auth_error=1'); - } - if ($config->requireAuthentication && !$auth->isAuthenticated()) { - $auth->exitWithRedirect('login.php'); - } +if ($action === 'auth' && !$auth->isAuthenticated()) { + $auth->exitWithRedirect('login.php?auth_error=1'); +} +if ($config->requireAuthentication && !$auth->isAuthenticated()) { + $auth->exitWithRedirect('login.php'); +} ?> diff --git a/js/src/configdialogmodel.js b/js/src/configdialogmodel.js index 21c516d..b66aa4d 100644 --- a/js/src/configdialogmodel.js +++ b/js/src/configdialogmodel.js @@ -199,7 +199,7 @@ export default class ConfigDialogModel extends ViewModel { } return `
${$._('settings')} ${$._('editingconfig')}
-
+