diff --git a/.docker/init.sh b/.docker/init.sh
index 73a9d92..9aa3952 100644
--- a/.docker/init.sh
+++ b/.docker/init.sh
@@ -10,6 +10,11 @@ chown nginx:nginx /run/nginx
sed -i "s/^nobody:.*$/nobody:x:1000:50::nobody:\/:\/sbin\/nologin/" /etc/passwd
sed -i "s/^nobody:.*$/nobody:x:50:/" /etc/group
+# Prepare ulogger filesystem
+grep '^[$]' /var/www/html/config.default.php > /var/www/html/config.php
+chown nobody:nobody /var/www/html/uploads
+chmod 775 /var/www/html/uploads
+
if [ "$ULOGGER_DB_DRIVER" = "sqlite" ]; then
sed -i "s/^\$dbuser = .*$//" /var/www/html/config.php
sed -i "s/^\$dbpass = .*$//" /var/www/html/config.php
@@ -49,12 +54,12 @@ else
mysqld_safe --datadir=/data &
mysqladmin --silent --wait=30 ping
mysqladmin -u root password "${DB_ROOT_PASS}"
- mysql -u root -p${DB_ROOT_PASS} < /var/www/html/scripts/ulogger.sql
- mysql -u root -p${DB_ROOT_PASS} -e "CREATE USER 'ulogger'@'localhost' IDENTIFIED BY '${DB_USER_PASS}'"
- mysql -u root -p${DB_ROOT_PASS} -e "GRANT ALL PRIVILEGES ON ulogger.* TO 'ulogger'@'localhost'"
- mysql -u root -p${DB_ROOT_PASS} -e "CREATE USER 'ulogger'@'%' IDENTIFIED BY '${DB_USER_PASS}'"
- mysql -u root -p${DB_ROOT_PASS} -e "GRANT ALL PRIVILEGES ON ulogger.* TO 'ulogger'@'%'"
- mysql -u root -p${DB_ROOT_PASS} -e "INSERT INTO users (login, password) VALUES ('admin', '\$2y\$10\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq')" ulogger
- mysqladmin -u root -p${DB_ROOT_PASS} shutdown
+ mysql -u root -p"${DB_ROOT_PASS}" < /var/www/html/scripts/ulogger.sql
+ mysql -u root -p"${DB_ROOT_PASS}" -e "CREATE USER 'ulogger'@'localhost' IDENTIFIED BY '${DB_USER_PASS}'"
+ mysql -u root -p"${DB_ROOT_PASS}" -e "GRANT ALL PRIVILEGES ON ulogger.* TO 'ulogger'@'localhost'"
+ mysql -u root -p"${DB_ROOT_PASS}" -e "CREATE USER 'ulogger'@'%' IDENTIFIED BY '${DB_USER_PASS}'"
+ mysql -u root -p"${DB_ROOT_PASS}" -e "GRANT ALL PRIVILEGES ON ulogger.* TO 'ulogger'@'%'"
+ mysql -u root -p"${DB_ROOT_PASS}" -e "INSERT INTO users (login, password) VALUES ('admin', '\$2y\$10\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq')" ulogger
+ mysqladmin -u root -p"${DB_ROOT_PASS}" shutdown
sed -i "s/^\$dbdsn = .*$/\$dbdsn = \"mysql:host=localhost;port=3306;dbname=ulogger;charset=utf8\";/" /var/www/html/config.php
fi
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..61b0213
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,343 @@
+module.exports = {
+ "env": {
+ "browser": true,
+ "es6": true,
+ "jasmine": true
+ },
+ "extends": [
+ "eslint:recommended",
+ "plugin:jasmine/recommended",
+ "plugin:import/errors",
+ "plugin:import/warnings"
+ ],
+ "globals": {
+ "Atomics": "readonly",
+ "SharedArrayBuffer": "readonly",
+ "google": true
+ },
+ "parser": "babel-eslint",
+ "parserOptions": {
+ "ecmaVersion": 2018,
+ "sourceType": "module"
+ },
+ "plugins": [
+ "jasmine",
+ "import"
+ ],
+ "rules": {
+ "accessor-pairs": "error",
+ "array-bracket-newline": "error",
+ "array-bracket-spacing": [
+ "error",
+ "always"
+ ],
+ "array-callback-return": "error",
+ "array-element-newline": "off",
+ "arrow-body-style": "error",
+ "arrow-parens": "error",
+ "arrow-spacing": "error",
+ "block-scoped-var": "off",
+ "block-spacing": "off",
+ "brace-style": [
+ "error",
+ "1tbs",
+ {
+ "allowSingleLine": true
+ }
+ ],
+ "callback-return": "error",
+ "camelcase": "off",
+ "capitalized-comments": "off",
+ "class-methods-use-this": "error",
+ "comma-dangle": "error",
+ "comma-spacing": [
+ "error",
+ {
+ "after": true,
+ "before": false
+ }
+ ],
+ "comma-style": [
+ "error",
+ "last"
+ ],
+ "complexity": "error",
+ "computed-property-spacing": [
+ "error",
+ "never"
+ ],
+ "consistent-return": "error",
+ "consistent-this": "off",
+ "curly": "error",
+ "default-case": "error",
+ "dot-location": [
+ "error",
+ "property"
+ ],
+ "dot-notation": "off",
+ "eol-last": "error",
+ "eqeqeq": "off",
+ "func-call-spacing": "error",
+ "func-name-matching": "error",
+ "func-names": "off",
+ "func-style": [
+ "error",
+ "declaration",
+ {
+ "allowArrowFunctions": true
+ }
+ ],
+ "function-paren-newline": "off",
+ "generator-star-spacing": "error",
+ "global-require": "error",
+ "guard-for-in": "off",
+ "handle-callback-err": "error",
+ "id-blacklist": "error",
+ "id-length": "off",
+ "id-match": "error",
+ "implicit-arrow-linebreak": "error",
+ "indent": "off",
+ "indent-legacy": "off",
+ "init-declarations": "off",
+ "jsx-quotes": "error",
+ "key-spacing": "error",
+ "keyword-spacing": [
+ "error",
+ {
+ "after": true,
+ "before": true
+ }
+ ],
+ "line-comment-position": "off",
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "lines-around-comment": "off",
+ "lines-around-directive": "error",
+ "lines-between-class-members": "error",
+ "max-classes-per-file": "error",
+ "max-depth": "off",
+ "max-len": "off",
+ "max-lines": "off",
+ "max-lines-per-function": "off",
+ "max-nested-callbacks": "error",
+ "max-params": "error",
+ "max-statements": "off",
+ "max-statements-per-line": "off",
+ "multiline-comment-style": "off",
+ "multiline-ternary": [
+ "error",
+ "never"
+ ],
+ "new-cap": [
+ "error", { "newIsCapExceptionPattern": "^u[A-Z]" }
+ ],
+ "new-parens": "error",
+ "newline-after-var": "off",
+ "newline-before-return": "off",
+ "newline-per-chained-call": "off",
+ "no-alert": "off",
+ "no-array-constructor": "error",
+ "no-async-promise-executor": "error",
+ "no-await-in-loop": "error",
+ "no-bitwise": "error",
+ "no-buffer-constructor": "error",
+ "no-caller": "error",
+ "no-catch-shadow": "error",
+ "no-cond-assign": [
+ "error",
+ "except-parens"
+ ],
+ "no-confusing-arrow": "error",
+ "no-continue": "error",
+ "no-div-regex": "error",
+ "no-duplicate-imports": "error",
+ "no-else-return": "error",
+ "no-empty-function": "error",
+ "no-eq-null": "off",
+ "no-eval": "error",
+ "no-extend-native": "off",
+ "no-extra-bind": "error",
+ "no-extra-label": "error",
+ "no-extra-parens": "off",
+ "no-floating-decimal": "error",
+ "no-implicit-globals": "off",
+ "no-implied-eval": "error",
+ "no-inline-comments": "off",
+ "no-inner-declarations": [
+ "error",
+ "functions"
+ ],
+ "no-invalid-this": "off",
+ "no-iterator": "error",
+ "no-label-var": "error",
+ "no-labels": "error",
+ "no-lone-blocks": "error",
+ "no-lonely-if": "error",
+ "no-loop-func": "error",
+ "no-magic-numbers": "off",
+ "no-misleading-character-class": "error",
+ "no-mixed-operators": "off",
+ "no-mixed-requires": "error",
+ "no-multi-assign": "error",
+ "no-multi-spaces": "error",
+ "no-multi-str": "error",
+ "no-multiple-empty-lines": "error",
+ "no-native-reassign": "error",
+ "no-negated-condition": "off",
+ "no-negated-in-lhs": "error",
+ "no-nested-ternary": "error",
+ "no-new": "error",
+ "no-new-func": "error",
+ "no-new-object": "error",
+ "no-new-require": "error",
+ "no-new-wrappers": "error",
+ "no-octal-escape": "error",
+ "no-param-reassign": "off",
+ "no-path-concat": "error",
+ "no-plusplus": "off",
+ "no-process-env": "error",
+ "no-process-exit": "error",
+ "no-proto": "error",
+ "no-prototype-builtins": "off",
+ "no-restricted-globals": "error",
+ "no-restricted-imports": "error",
+ "no-restricted-modules": "error",
+ "no-restricted-properties": "error",
+ "no-restricted-syntax": "error",
+ "no-return-assign": [
+ "error",
+ "except-parens"
+ ],
+ "no-return-await": "error",
+ "no-script-url": "error",
+ "no-self-compare": "error",
+ "no-sequences": "error",
+ "no-shadow": "error",
+ "no-shadow-restricted-names": "error",
+ "no-spaced-func": "error",
+ "no-sync": "error",
+ "no-tabs": "error",
+ "no-template-curly-in-string": "error",
+ "no-ternary": "off",
+ "no-throw-literal": "error",
+ "no-trailing-spaces": "error",
+ "no-undef-init": "error",
+ "no-undefined": "error",
+ "no-underscore-dangle": [
+ "error",
+ {
+ "allowAfterThis": true
+ }
+ ],
+ "no-unmodified-loop-condition": "error",
+ "no-unneeded-ternary": "error",
+ "no-unused-expressions": "error",
+ "no-use-before-define": "off",
+ "no-useless-call": "error",
+ "no-useless-catch": "error",
+ "no-useless-computed-key": "error",
+ "no-useless-concat": "off",
+ "no-useless-constructor": "error",
+ "no-useless-rename": "error",
+ "no-useless-return": "error",
+ "no-var": "error",
+ "no-void": "error",
+ "no-warning-comments": "warn",
+ "no-whitespace-before-property": "error",
+ "no-with": "error",
+ "nonblock-statement-body-position": "error",
+ "object-curly-newline": "error",
+ "object-curly-spacing": [
+ "error",
+ "always"
+ ],
+ "object-shorthand": [
+ "error",
+ "consistent-as-needed"
+ ],
+ "one-var": "off",
+ "one-var-declaration-per-line": [
+ "error",
+ "initializations"
+ ],
+ "operator-assignment": [
+ "error",
+ "always"
+ ],
+ "operator-linebreak": "error",
+ "padded-blocks": "off",
+ "padding-line-between-statements": "error",
+ "prefer-arrow-callback": "error",
+ "prefer-const": "error",
+ "prefer-destructuring": "off",
+ "prefer-named-capture-group": "off",
+ "prefer-numeric-literals": "error",
+ "prefer-object-spread": "error",
+ "prefer-promise-reject-errors": "error",
+ "prefer-reflect": "off",
+ "prefer-rest-params": "off",
+ "prefer-spread": "error",
+ "prefer-template": "off",
+ "quote-props": "off",
+ "quotes": [
+ "error",
+ "single",
+ "avoid-escape"
+ ],
+ "radix": [
+ "error",
+ "as-needed"
+ ],
+ "require-atomic-updates": "error",
+ "require-await": "error",
+ "require-jsdoc": "off",
+ "require-unicode-regexp": "off",
+ "rest-spread-spacing": "error",
+ "semi": "off",
+ "semi-spacing": [
+ "error",
+ {
+ "after": true,
+ "before": false
+ }
+ ],
+ "semi-style": [
+ "error",
+ "last"
+ ],
+ "sort-imports": "error",
+ "sort-keys": "off",
+ "sort-vars": "off",
+ "space-before-blocks": "error",
+ "space-before-function-paren": "off",
+ "space-in-parens": [
+ "error",
+ "never"
+ ],
+ "space-infix-ops": "error",
+ "space-unary-ops": "error",
+ "spaced-comment": [
+ "error",
+ "always"
+ ],
+ "strict": "off",
+ "switch-colon-spacing": "error",
+ "symbol-description": "error",
+ "template-curly-spacing": "error",
+ "template-tag-spacing": "error",
+ "unicode-bom": [
+ "error",
+ "never"
+ ],
+ "valid-jsdoc": "off",
+ "vars-on-top": "off",
+ "wrap-regex": "error",
+ "yield-star-spacing": "error",
+ "yoda": [
+ "error",
+ "never"
+ ]
+ }
+};
diff --git a/.tests/lib/BaseDatabaseTestCase.php b/.tests/lib/BaseDatabaseTestCase.php
index 35dc482..2e71e53 100644
--- a/.tests/lib/BaseDatabaseTestCase.php
+++ b/.tests/lib/BaseDatabaseTestCase.php
@@ -36,7 +36,7 @@ abstract class BaseDatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
protected $testAccuracy = 10;
protected $testProvider = "gps";
protected $testComment = "test comment";
- protected $testImageId = 1;
+ protected $testImage = "1234_1502974402_5d1a1960335cf.jpg";
// Fixes PostgreSQL: "cannot truncate a table referenced in a foreign key constraint"
protected function getSetUpOperation() {
@@ -180,7 +180,11 @@ abstract class BaseDatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
protected function addTestUser($user = NULL, $pass = NULL) {
if (is_null($user)) { $user = $this->testUser; }
if (is_null($pass)) { $pass = $this->testPass; }
- return $this->pdoInsert('users', [ 'login' => $user, 'password' => $pass ]);
+ $id = $this->pdoInsert('users', [ 'login' => $user, 'password' => $pass ]);
+ if ($id !== false) {
+ return (int) $id;
+ }
+ return false;
}
/**
@@ -196,7 +200,11 @@ abstract class BaseDatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
if (is_null($userId)) { $userId = $this->testUserId; }
if (is_null($trackName)) { $trackName = $this->testTrackName; }
if (is_null($comment)) { $comment = $this->testTrackComment; }
- return $this->pdoInsert('tracks', [ 'user_id' => $userId, 'name' => $trackName, 'comment' => $comment ]);
+ $id = $this->pdoInsert('tracks', [ 'user_id' => $userId, 'name' => $trackName, 'comment' => $comment ]);
+ if ($id !== false) {
+ return (int) $id;
+ }
+ return false;
}
/**
diff --git a/.tests/tests/ClientAPITest.php b/.tests/tests/ClientAPITest.php
index 1b99e8d..390682f 100644
--- a/.tests/tests/ClientAPITest.php
+++ b/.tests/tests/ClientAPITest.php
@@ -224,8 +224,7 @@ class ClientAPITest extends UloggerAPITestCase {
'bearing' => $this->testBearing,
'accuracy' => $this->testAccuracy,
'provider' => $this->testProvider,
- 'comment' => $this->testComment,
- 'imageid' => $this->testImageId
+ 'comment' => $this->testComment
],
];
$response = $this->http->post('/client/index.php', $options);
@@ -246,15 +245,115 @@ class ClientAPITest extends UloggerAPITestCase {
"accuracy" => $this->testAccuracy,
"provider" => $this->testProvider,
"comment" => $this->testComment,
- "image_id" => $this->testImageId
+ "image" => null
];
$actual = $this->getConnection()->createQueryTable(
"positions",
- "SELECT id, user_id, track_id, " . $this->unix_timestamp('time') . " AS time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ "SELECT id, user_id, track_id, " . $this->unix_timestamp('time') . " AS time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
}
+ public function testAddPositionWithImage() {
+ $this->assertTrue($this->authenticate(), "Authentication failed");
+
+ $trackId = $this->addTestTrack($this->testUserId);
+ $this->assertEquals(1, $this->getConnection()->getRowCount('tracks'), "Wrong row count");
+ $this->assertEquals(0, $this->getConnection()->getRowCount('positions'), "Wrong row count");
+
+ $options = [
+ 'http_errors' => false,
+ 'multipart' => [
+ [
+ 'name' => 'action',
+ 'contents' => 'addpos',
+ ],
+ [
+ 'name' => 'trackid',
+ 'contents' => $trackId,
+ ],
+ [
+ 'name' => 'time',
+ 'contents' => $this->testTimestamp,
+ ],
+ [
+ 'name' => 'lat',
+ 'contents' => $this->testLat,
+ ],
+ [
+ 'name' => 'lon',
+ 'contents' => $this->testLon,
+ ],
+ [
+ 'name' => 'altitude',
+ 'contents' => $this->testAltitude,
+ ],
+ [
+ 'name' => 'speed',
+ 'contents' => $this->testSpeed,
+ ],
+ [
+ 'name' => 'bearing',
+ 'contents' => $this->testBearing,
+ ],
+ [
+ 'name' => 'accuracy',
+ 'contents' => $this->testAccuracy,
+ ],
+ [
+ 'name' => 'provider',
+ 'contents' => $this->testProvider,
+ ],
+ [
+ 'name' => 'comment',
+ 'contents' => $this->testComment,
+ ],
+ [
+ 'name' => 'image',
+ 'contents' => 'DEADBEEF',
+ 'filename' => 'upload',
+ 'headers' => [ 'Content-Type' => 'image/jpeg', 'Content-Transfer-Encoding' => 'binary' ]
+ ]
+ ]
+ ];
+ $response = $this->http->post('/client/index.php', $options);
+ $this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
+ $json = json_decode((string) $response->getBody());
+ $this->assertFalse($json->{'error'}, "Unexpected error");
+ $this->assertEquals(1, $this->getConnection()->getRowCount('positions'), "Wrong row count");
+ $expected = [
+ "id" => 1,
+ "user_id" => $this->testUserId,
+ "track_id" => $trackId,
+ "time" => $this->testTimestamp,
+ "latitude" => $this->testLat,
+ "longitude" => $this->testLon,
+ "altitude" => $this->testAltitude,
+ "speed" => $this->testSpeed,
+ "bearing" => $this->testBearing,
+ "accuracy" => $this->testAccuracy,
+ "provider" => $this->testProvider,
+ "comment" => $this->testComment
+ ];
+ $actual = $this->getConnection()->createQueryTable(
+ "positions",
+ "SELECT id, user_id, track_id, " . $this->unix_timestamp('time') . " AS time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
+ );
+ $this->assertEquals($expected['id'], $actual->getValue(0, 'id'));
+ $this->assertEquals($expected['user_id'], $actual->getValue(0, 'user_id'));
+ $this->assertEquals($expected['track_id'], $actual->getValue(0, 'track_id'));
+ $this->assertEquals($expected['time'], $actual->getValue(0, 'time'));
+ $this->assertEquals($expected['latitude'], $actual->getValue(0, 'latitude'));
+ $this->assertEquals($expected['longitude'], $actual->getValue(0, 'longitude'));
+ $this->assertEquals($expected['altitude'], $actual->getValue(0, 'altitude'));
+ $this->assertEquals($expected['speed'], $actual->getValue(0, 'speed'));
+ $this->assertEquals($expected['bearing'], $actual->getValue(0, 'bearing'));
+ $this->assertEquals($expected['accuracy'], $actual->getValue(0, 'accuracy'));
+ $this->assertEquals($expected['provider'], $actual->getValue(0, 'provider'));
+ $this->assertEquals($expected['comment'], $actual->getValue(0, 'comment'));
+ $this->assertContains('.jpg', $actual->getValue(0, 'image'));
+ }
+
public function testAddPositionNoexistantTrack() {
$this->assertTrue($this->authenticate(), "Authentication failed");
@@ -275,7 +374,7 @@ class ClientAPITest extends UloggerAPITestCase {
'accuracy' => $this->testAccuracy,
'provider' => $this->testProvider,
'comment' => $this->testComment,
- 'imageid' => $this->testImageId
+ 'imageid' => $this->testImage
],
];
$response = $this->http->post('/client/index.php', $options);
@@ -306,7 +405,7 @@ class ClientAPITest extends UloggerAPITestCase {
'accuracy' => $this->testAccuracy,
'provider' => $this->testProvider,
'comment' => $this->testComment,
- 'imageid' => $this->testImageId
+ 'imageid' => $this->testImage
],
];
@@ -343,7 +442,7 @@ class ClientAPITest extends UloggerAPITestCase {
'accuracy' => $this->testAccuracy,
'provider' => $this->testProvider,
'comment' => $this->testComment,
- 'imageid' => $this->testImageId
+ 'imageid' => $this->testImage
],
];
diff --git a/.tests/tests/ImportTest.php b/.tests/tests/ImportTest.php
index a59a9f1..1b3dba8 100644
--- a/.tests/tests/ImportTest.php
+++ b/.tests/tests/ImportTest.php
@@ -1,4 +1,4 @@
-http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 1, "Wrong count of tracks");
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status");
- $this->assertEquals(1, (int) $xml->trackid, "Wrong error message");
- $this->assertEquals(1, (int) $xml->trackcnt, "Wrong error message");
+ $track = $json[0];
+ $this->assertEquals(1, (int) $track->id, "Wrong track id");
+ $this->assertEquals($this->testTrackName, $track->name, "Wrong track name");
$this->assertEquals(1, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(2, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -88,12 +89,12 @@ class ImportTest extends UloggerAPITestCase {
"accuracy" => null,
"provider" => "gps",
"comment" => null,
- "image_id" => null
+ "image" => null
];
$actual = $this->getConnection()->createQueryTable(
"positions",
"SELECT id, " . $this->unix_timestamp('time') . " AS time, user_id, track_id, latitude, longitude,
- altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
@@ -110,7 +111,7 @@ class ImportTest extends UloggerAPITestCase {
"accuracy" => null,
"provider" => "gps",
"comment" => null,
- "image_id" => null
+ "image" => null
];
$this->assertTableContains($expected, $actual, "Wrong actual table data");
}
@@ -170,12 +171,14 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status");
- $this->assertEquals(1, (int) $xml->trackid, "Wrong error message");
- $this->assertEquals(1, (int) $xml->trackcnt, "Wrong error message");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 1, "Wrong count of tracks");
+
+ $track = $json[0];
+ $this->assertEquals(1, (int) $track->id, "Wrong track id");
+ $this->assertEquals($this->testTrackName, $track->name, "Wrong track name");
$this->assertEquals(1, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(1, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -204,12 +207,12 @@ class ImportTest extends UloggerAPITestCase {
"accuracy" => null,
"provider" => "gps",
"comment" => null,
- "image_id" => null
+ "image" => null
];
$actual = $this->getConnection()->createQueryTable(
"positions",
"SELECT id, " . $this->unix_timestamp('time') . " AS time, user_id, track_id, latitude, longitude,
- altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
}
@@ -241,7 +244,7 @@ class ImportTest extends UloggerAPITestCase {
' . $this->testAltitude . '
1
-
+ testComment . ']]>
' . $this->testSpeed . '
' . $this->testBearing . '
@@ -270,12 +273,14 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status");
- $this->assertEquals(1, (int) $xml->trackid, "Wrong error message");
- $this->assertEquals(1, (int) $xml->trackcnt, "Wrong error message");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 1, "Wrong count of tracks");
+
+ $track = $json[0];
+ $this->assertEquals(1, (int) $track->id, "Wrong track id");
+ $this->assertEquals($this->testTrackName, $track->name, "Wrong track name");
$this->assertEquals(1, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(1, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -303,13 +308,13 @@ class ImportTest extends UloggerAPITestCase {
"bearing" => $this->testBearing,
"accuracy" => $this->testAccuracy,
"provider" => $this->testProvider,
- "comment" => null,
- "image_id" => null
+ "comment" => $this->testComment,
+ "image" => null
];
$actual = $this->getConnection()->createQueryTable(
"positions",
"SELECT id, " . $this->unix_timestamp('time') . " AS time, user_id, track_id, latitude, longitude,
- altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
}
@@ -351,12 +356,14 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status");
- $this->assertEquals(1, (int) $xml->trackid, "Wrong error message");
- $this->assertEquals(1, (int) $xml->trackcnt, "Wrong error message");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 1, "Wrong count of tracks");
+
+ $track = $json[0];
+ $this->assertEquals(1, (int) $track->id, "Wrong track id");
+ $this->assertEquals($this->testTrackName, $track->name, "Wrong track name");
$this->assertEquals(1, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(1, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -385,12 +392,12 @@ class ImportTest extends UloggerAPITestCase {
"accuracy" => null,
"provider" => "gps",
"comment" => null,
- "image_id" => null
+ "image" => null
];
$actual = $this->getConnection()->createQueryTable(
"positions",
"SELECT id, " . $this->unix_timestamp('time') . " AS time, user_id, track_id, latitude, longitude,
- altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
}
@@ -438,12 +445,14 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status: $xml->message");
- $this->assertEquals(1, (int) $xml->trackid, "Wrong error message");
- $this->assertEquals(1, (int) $xml->trackcnt, "Wrong error message");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 1, "Wrong count of tracks");
+
+ $track = $json[0];
+ $this->assertEquals(1, (int) $track->id, "Wrong track id");
+ $this->assertEquals($this->testTrackName, $track->name, "Wrong track name");
$this->assertEquals(1, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(2, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -472,12 +481,12 @@ class ImportTest extends UloggerAPITestCase {
"accuracy" => null,
"provider" => "gps",
"comment" => null,
- "image_id" => null
+ "image" => null
];
$actual = $this->getConnection()->createQueryTable(
"positions",
"SELECT id, " . $this->unix_timestamp('time') . " AS time, user_id, track_id, latitude, longitude,
- altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
$expected = [
@@ -493,7 +502,7 @@ class ImportTest extends UloggerAPITestCase {
"accuracy" => null,
"provider" => "gps",
"comment" => null,
- "image_id" => null
+ "image" => null
];
$this->assertTableContains($expected, $actual, "Wrong actual table data");
}
@@ -543,12 +552,18 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status: $xml->message");
- $this->assertEquals(2, (int) $xml->trackid, "Wrong error message");
- $this->assertEquals(2, (int) $xml->trackcnt, "Wrong error message");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 2, "Wrong count of tracks");
+
+ $track = $json[0];
+ $this->assertEquals(2, (int) $track->id, "Wrong track id");
+ $this->assertEquals($this->testTrackName, $track->name, "Wrong track name");
+
+ $track = $json[1];
+ $this->assertEquals(1, (int) $track->id, "Wrong track id");
+ $this->assertEquals($this->testTrackName, $track->name, "Wrong track name");
$this->assertEquals(2, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(2, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -584,12 +599,12 @@ class ImportTest extends UloggerAPITestCase {
"accuracy" => null,
"provider" => "gps",
"comment" => null,
- "image_id" => null
+ "image" => null
];
$actual = $this->getConnection()->createQueryTable(
"positions",
"SELECT id, " . $this->unix_timestamp('time') . " AS time, user_id, track_id, latitude, longitude,
- altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
$expected = [
@@ -605,7 +620,7 @@ class ImportTest extends UloggerAPITestCase {
"accuracy" => null,
"provider" => "gps",
"comment" => null,
- "image_id" => null
+ "image" => null
];
$this->assertTableContains($expected, $actual, "Wrong actual table data");
}
@@ -647,11 +662,11 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(1, (int) $xml->error, "Wrong error status");
- $this->assertEquals($lang["iparsefailure"], (string) $xml->message, "Wrong error status");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(1, (int) $json->error, "Wrong error status");
+ $this->assertEquals($lang["iparsefailure"], (string) $json->message, "Wrong error status");
$this->assertEquals(0, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(0, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -694,11 +709,11 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(1, (int) $xml->error, "Wrong error status");
- $this->assertEquals($lang["iparsefailure"], (string) $xml->message, "Wrong error status");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(1, (int) $json->error, "Wrong error status");
+ $this->assertEquals($lang["iparsefailure"], (string) $json->message, "Wrong error status");
$this->assertEquals(0, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(0, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -735,11 +750,11 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(1, (int) $xml->error, "Wrong error status");
- $this->assertEquals($lang["iparsefailure"], (string) $xml->message, "Wrong error status");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(1, (int) $json->error, "Wrong error status");
+ $this->assertEquals($lang["iparsefailure"], (string) $json->message, "Wrong error status");
$this->assertEquals(0, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(0, $this->getConnection()->getRowCount("positions"), "Wrong row count");
@@ -780,29 +795,16 @@ class ImportTest extends UloggerAPITestCase {
$response = $this->http->post("/utils/import.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(1, (int) $xml->error, "Wrong error status");
- $this->assertEquals(0, strpos((string) $xml->message, $lang["iparsefailure"]), "Wrong error status");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(1, (int) $json->error, "Wrong error status");
+ $this->assertEquals(0, strpos((string) $json->message, $lang["iparsefailure"]), "Wrong error status");
$this->assertEquals(0, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals(0, $this->getConnection()->getRowCount("positions"), "Wrong row count");
}
- /**
- * @param ResponseInterface $response
- * @return bool|SimpleXMLElement
- */
- private function getXMLfromResponse($response) {
- $xml = false;
- libxml_use_internal_errors(true);
- try {
- $xml = new SimpleXMLElement((string) $response->getBody());
- } catch (Exception $e) { /* ignore */ }
- return $xml;
- }
-
private function getStream($string) {
$stream = tmpfile();
fwrite($stream, $string);
diff --git a/.tests/tests/InternalAPITest.php b/.tests/tests/InternalAPITest.php
index ea8f652..8a68d80 100644
--- a/.tests/tests/InternalAPITest.php
+++ b/.tests/tests/InternalAPITest.php
@@ -28,20 +28,20 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->position->count(), 2, "Wrong count of positions");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 2, "Wrong count of positions");
- $position = $xml->position[0];
- $this->assertEquals((int) $position["id"], 1, "Wrong position id");
+ $position = $json[0];
+ $this->assertEquals((int) $position->id, 1, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp, "Wrong timestamp");
$this->assertEquals((string) $position->username, $this->testAdminUser, "Wrong username");
$this->assertEquals((string) $position->trackname, $this->testTrackName, "Wrong trackname");
- $position = $xml->position[1];
- $this->assertEquals((int) $position["id"], 2, "Wrong position id");
+ $position = $json[1];
+ $this->assertEquals((int) $position->id, 2, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp + 1, "Wrong timestamp");
@@ -67,20 +67,20 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->position->count(), 2, "Wrong count of positions");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 2, "Wrong count of positions");
- $position = $xml->position[0];
- $this->assertEquals((int) $position["id"], 1, "Wrong position id");
+ $position = $json[0];
+ $this->assertEquals((int) $position->id, 1, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp, "Wrong timestamp");
$this->assertEquals((string) $position->username, $this->testUser, "Wrong username");
$this->assertEquals((string) $position->trackname, $this->testTrackName, "Wrong trackname");
- $position = $xml->position[1];
- $this->assertEquals((int) $position["id"], 2, "Wrong position id");
+ $position = $json[1];
+ $this->assertEquals((int) $position->id, 2, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp + 1, "Wrong timestamp");
@@ -107,9 +107,9 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->position->count(), 0, "Wrong count of positions");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 0, "Wrong count of positions");
}
public function testGetPositionsOtherUserByAdmin() {
@@ -131,20 +131,20 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->position->count(), 2, "Wrong count of positions");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 2, "Wrong count of positions");
- $position = $xml->position[0];
- $this->assertEquals((int) $position["id"], 1, "Wrong position id");
+ $position = $json[0];
+ $this->assertEquals((int) $position->id, 1, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp, "Wrong timestamp");
$this->assertEquals((string) $position->username, $this->testUser, "Wrong username");
$this->assertEquals((string) $position->trackname, $this->testTrackName, "Wrong trackname");
- $position = $xml->position[1];
- $this->assertEquals((int) $position["id"], 2, "Wrong position id");
+ $position = $json[1];
+ $this->assertEquals((int) $position->id, 2, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp + 1, "Wrong timestamp");
@@ -175,12 +175,12 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->position->count(), 1, "Wrong count of positions");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 1, "Wrong count of positions");
- $position = $xml->position[0];
- $this->assertEquals((int) $position["id"], 2, "Wrong position id");
+ $position = $json[0];
+ $this->assertEquals((int) $position->id, 2, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp + 3, "Wrong timestamp");
@@ -212,20 +212,20 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->position->count(), 2, "Wrong count of positions");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 2, "Wrong count of positions");
- $position = $xml->position[0];
- $this->assertEquals((int) $position["id"], 2, "Wrong position id");
+ $position = $json[0];
+ $this->assertEquals((int) $position->id, 2, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp + 3, "Wrong timestamp");
$this->assertEquals((string) $position->username, $this->testAdminUser, "Wrong username");
$this->assertEquals((string) $position->trackname, $this->testTrackName, "Wrong trackname");
- $position = $xml->position[1];
- $this->assertEquals((int) $position["id"], 3, "Wrong position id");
+ $position = $json[1];
+ $this->assertEquals((int) $position->id, 3, "Wrong position id");
$this->assertEquals((float) $position->latitude, $this->testLat, "Wrong latitude");
$this->assertEquals((float) $position->longitude, $this->testLon, "Wrong longitude");
$this->assertEquals((int) $position->timestamp, $this->testTimestamp + 2, "Wrong timestamp");
@@ -250,9 +250,9 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, $xml->position->count(), "Wrong count of positions");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertCount(0, $json, "Wrong count of positions");
}
public function testGetPositionsNoUserId() {
@@ -272,10 +272,9 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
-
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, $xml->position->count(), "Wrong count of positions");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertCount(0, $json, "Wrong count of positions");
}
public function testGetPositionsNoAuth() {
@@ -291,10 +290,10 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/getpositions.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
+ $json = json_decode($response->getBody());
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals($xml->position->count(), 0, "Wrong count of positions");
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 0, "Wrong count of positions");
}
@@ -317,17 +316,17 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/gettracks.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->track->count(), 2, "Wrong count of tracks");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 2, "Wrong count of tracks");
- $track = $xml->track[0];
- $this->assertEquals((int) $track->trackid, $this->testTrackId2, "Wrong track id");
- $this->assertEquals((string) $track->trackname, $this->testTrackName . "2", "Wrong track name");
+ $track = $json[0];
+ $this->assertEquals((int) $track->id, $this->testTrackId2, "Wrong track id");
+ $this->assertEquals((string) $track->name, $this->testTrackName . "2", "Wrong track name");
- $track = $xml->track[1];
- $this->assertEquals((int) $track->trackid, $this->testTrackId, "Wrong track id");
- $this->assertEquals((string) $track->trackname, $this->testTrackName, "Wrong track name");
+ $track = $json[1];
+ $this->assertEquals((int) $track->id, $this->testTrackId, "Wrong track id");
+ $this->assertEquals((string) $track->name, $this->testTrackName, "Wrong track name");
}
public function testGetTracksUser() {
@@ -347,18 +346,18 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/gettracks.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->track->count(), 2, "Wrong count of tracks");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 2, "Wrong count of tracks");
- $track = $xml->track[0];
- $this->assertEquals((int) $track->trackid, $this->testTrackId2, "Wrong track id");
- $this->assertEquals((string) $track->trackname, $this->testTrackName . "2", "Wrong track name");
+ $track = $json[0];
+ $this->assertEquals((int) $track->id, $this->testTrackId2, "Wrong track id");
+ $this->assertEquals((string) $track->name, $this->testTrackName . "2", "Wrong track name");
- $track = $xml->track[1];
- $this->assertEquals((int) $track->trackid, $this->testTrackId, "Wrong track id");
- $this->assertEquals((string) $track->trackname, $this->testTrackName, "Wrong track name");
- }
+ $track = $json[1];
+ $this->assertEquals((int) $track->id, $this->testTrackId, "Wrong track id");
+ $this->assertEquals((string) $track->name, $this->testTrackName, "Wrong track name");
+ }
public function testGetTracksOtherUser() {
$this->addTestUser($this->testUser, password_hash($this->testPass, PASSWORD_DEFAULT));
@@ -377,9 +376,9 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/gettracks.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals($xml->track->count(), 0, "Wrong count of tracks");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 0, "Wrong count of tracks");
}
public function testGetTracksNoUserId() {
@@ -397,9 +396,9 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->get("/utils/gettracks.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals($xml->track->count(), 0, "Wrong count of tracks");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 0, "Wrong count of tracks");
}
public function testGetTracksNoAuth() {
@@ -416,9 +415,9 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->get("/utils/gettracks.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals($xml->track->count(), 0, "Wrong count of tracks");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(count($json), 0, "Wrong count of tracks");
}
@@ -428,15 +427,19 @@ class InternalAPITest extends UloggerAPITestCase {
$options = [
"http_errors" => false,
- "form_params" => [ "userid" => $this->testUserId ],
+ "form_params" => [
+ "login" => $this->testUser,
+ "pass" => $this->testPass,
+ "oldpass" => $this->testPass
+ ],
];
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(401, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, "Unauthorized", "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, "Unauthorized", "Wrong error message");
}
public function testChangePassEmpty() {
@@ -449,13 +452,13 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, "Empty password", "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, "Empty password", "Wrong error message");
}
- public function testChangePassNoUser() {
+ public function testChangePassUserUnknown() {
$this->assertTrue($this->authenticate(), "Authentication failed");
$options = [
@@ -468,10 +471,28 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, "User unknown", "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, "User unknown", "Wrong error message");
+ }
+
+ public function testChangePassEmptyLogin() {
+ $this->assertTrue($this->authenticate(), "Authentication failed");
+
+ $options = [
+ "http_errors" => false,
+ "form_params" => [
+ "pass" => $this->testPass,
+ ],
+ ];
+ $response = $this->http->post("/utils/changepass.php", $options);
+ $this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
+
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, "Empty login", "Wrong error message");
}
public function testChangePassWrongOldpass() {
@@ -480,6 +501,7 @@ class InternalAPITest extends UloggerAPITestCase {
$options = [
"http_errors" => false,
"form_params" => [
+ "login" => $this->testAdminUser,
"oldpass" => "badpass",
"pass" => "newpass",
],
@@ -487,10 +509,10 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, "Wrong old password", "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, "Wrong old password", "Wrong error message");
}
public function testChangePassNoOldpass() {
@@ -499,16 +521,17 @@ class InternalAPITest extends UloggerAPITestCase {
$options = [
"http_errors" => false,
"form_params" => [
+ "login" => $this->testAdminUser,
"pass" => "newpass",
],
];
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, "Wrong old password", "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, "Wrong old password", "Wrong error message");
}
public function testChangePassSelfAdmin() {
@@ -519,6 +542,7 @@ class InternalAPITest extends UloggerAPITestCase {
$options = [
"http_errors" => false,
"form_params" => [
+ "login" => $this->testAdminUser,
"oldpass" => $this->testAdminPass,
"pass" => $newPass,
],
@@ -526,9 +550,8 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 0, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertTrue(password_verify($newPass, $this->pdoGetColumn("SELECT password FROM users")), "Wrong actual password hash");
}
@@ -541,6 +564,7 @@ class InternalAPITest extends UloggerAPITestCase {
$options = [
"http_errors" => false,
"form_params" => [
+ "login" => $this->testUser,
"oldpass" => $this->testPass,
"pass" => $newPass,
],
@@ -548,9 +572,8 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 0, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertTrue(password_verify($newPass, $this->pdoGetColumn("SELECT password FROM users WHERE id = $userId")), "Wrong actual password hash");
}
@@ -570,9 +593,8 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 0, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertTrue(password_verify($newPass, $this->pdoGetColumn("SELECT password FROM users WHERE id = $userId")), "Wrong actual password hash");
}
@@ -593,10 +615,10 @@ class InternalAPITest extends UloggerAPITestCase {
$response = $this->http->post("/utils/changepass.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is not false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, "Unauthorized", "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, "Unauthorized", "Wrong error message");
}
/* handletrack.php */
@@ -617,9 +639,8 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handletrack.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 0, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertEquals(1, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals($trackId2, $this->pdoGetColumn("SELECT id FROM tracks WHERE id = $trackId2"), "Wrong actual track id");
}
@@ -640,9 +661,8 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handletrack.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 0, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertEquals(1, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$this->assertEquals($trackId2, $this->pdoGetColumn("SELECT id FROM tracks WHERE id = $trackId2"), "Wrong actual track id");
}
@@ -663,10 +683,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handletrack.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
}
public function testHandleTrackUpdate() {
@@ -686,9 +706,8 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handletrack.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 0, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertEquals(2, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
$row1 = [
"id" => $trackId2,
@@ -727,10 +746,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handletrack.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
$this->assertEquals(2, $this->getConnection()->getRowCount("tracks"), "Wrong row count");
}
@@ -752,10 +771,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handletrack.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
}
public function testHandleTrackMissingAction() {
@@ -767,10 +786,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handletrack.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
}
@@ -785,10 +804,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
}
public function testHandleUserNonAdmin() {
@@ -803,10 +822,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
$this->assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count");
}
@@ -822,10 +841,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
$this->assertEquals(1, $this->getConnection()->getRowCount("users"), "Wrong row count");
}
@@ -840,10 +859,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
$this->assertEquals(1, $this->getConnection()->getRowCount("users"), "Wrong row count");
}
@@ -859,10 +878,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals((int) $xml->error, 1, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals((int) $json->error, 1, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
$this->assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count");
}
@@ -876,9 +895,8 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count");
$expected = [
"login" => $this->testUser,
@@ -903,10 +921,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(1, (int) $xml->error, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["userexists"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(1, (int) $json->error, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["userexists"], "Wrong error message");
$this->assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count");
}
@@ -922,9 +940,8 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count");
$this->assertTrue(password_verify($newPass, $this->pdoGetColumn("SELECT password FROM users WHERE login = '$this->testUser'")), "Wrong actual password hash");
}
@@ -941,10 +958,10 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(1, (int) $xml->error, "Wrong error status");
- $this->assertEquals((string) $xml->message, $lang["servererror"], "Wrong error message");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
+ $this->assertEquals(1, (int) $json->error, "Wrong error status");
+ $this->assertEquals((string) $json->message, $lang["servererror"], "Wrong error message");
$this->assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count");
$this->assertTrue(password_verify($this->testPass, $this->pdoGetColumn("SELECT password FROM users WHERE login = '$this->testUser'")), "Wrong actual password hash");
}
@@ -960,25 +977,11 @@ class InternalAPITest extends UloggerAPITestCase {
];
$response = $this->http->post("/utils/handleuser.php", $options);
$this->assertEquals(200, $response->getStatusCode(), "Unexpected status code");
- $xml = $this->getXMLfromResponse($response);
- $this->assertTrue($xml !== false, "XML object is false");
- $this->assertEquals(0, (int) $xml->error, "Wrong error status");
+ $json = json_decode($response->getBody());
+ $this->assertNotNull($json, "JSON object is null");
$this->assertEquals(1, $this->getConnection()->getRowCount("users"), "Wrong row count");
}
- /**
- * @param ResponseInterface $response
- * @return bool|SimpleXMLElement
- */
- private function getXMLfromResponse($response) {
- $xml = false;
- libxml_use_internal_errors(true);
- try {
- $xml = new SimpleXMLElement((string) $response->getBody());
- } catch (Exception $e) { /* ignore */ }
- return $xml;
- }
-
}
?>
\ No newline at end of file
diff --git a/.tests/tests/PositionTest.php b/.tests/tests/PositionTest.php
index 98a6f0b..3d04d4d 100644
--- a/.tests/tests/PositionTest.php
+++ b/.tests/tests/PositionTest.php
@@ -11,15 +11,15 @@ class PositionTest extends UloggerDatabaseTestCase {
$trackId = $this->addTestTrack($userId);
$this->assertEquals(1, $this->getConnection()->getRowCount('tracks'), "Wrong row count");
- $posId = uPosition::add($userId, $trackId + 1, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImageId);
+ $posId = uPosition::add($userId, $trackId + 1, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImage);
$this->assertEquals(0, $this->getConnection()->getRowCount('positions'), "Wrong row count");
$this->assertFalse($posId, "Adding position with nonexistant track should fail");
- $posId = uPosition::add($userId + 1, $trackId, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImageId);
+ $posId = uPosition::add($userId + 1, $trackId, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImage);
$this->assertEquals(0, $this->getConnection()->getRowCount('positions'), "Wrong row count");
$this->assertFalse($posId, "Adding position with wrong user should fail");
- $posId = uPosition::add($userId, $trackId, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImageId);
+ $posId = uPosition::add($userId, $trackId, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImage);
$this->assertEquals(1, $this->getConnection()->getRowCount('positions'), "Wrong row count");
$expected = [
"id" => $posId,
@@ -34,11 +34,11 @@ class PositionTest extends UloggerDatabaseTestCase {
"accuracy" => $this->testAccuracy,
"provider" => $this->testProvider,
"comment" => $this->testComment,
- "image_id" => $this->testImageId
+ "image" => $this->testImage
];
$actual = $this->getConnection()->createQueryTable(
"positions",
- "SELECT id, user_id, track_id, " . $this->unix_timestamp('time') . " AS time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ "SELECT id, user_id, track_id, " . $this->unix_timestamp('time') . " AS time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
diff --git a/.tests/tests/TrackTest.php b/.tests/tests/TrackTest.php
index 2d4f089..30e8bb6 100644
--- a/.tests/tests/TrackTest.php
+++ b/.tests/tests/TrackTest.php
@@ -41,16 +41,16 @@ class TrackTest extends UloggerDatabaseTestCase {
$this->assertEquals(1, $this->getConnection()->getRowCount('tracks'), "Wrong row count");
$track = new uTrack($trackId + 1);
- $posId = $track->addPosition($userId, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImageId);
+ $posId = $track->addPosition($userId, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImage);
$this->assertEquals(0, $this->getConnection()->getRowCount('positions'), "Wrong row count");
$this->assertFalse($posId, "Adding position with nonexistant track should fail");
$track = new uTrack($trackId);
- $posId = $track->addPosition($userId2, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImageId);
+ $posId = $track->addPosition($userId2, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImage);
$this->assertEquals(0, $this->getConnection()->getRowCount('positions'), "Wrong row count");
$this->assertFalse($posId, "Adding position with wrong user should fail");
- $posId = $track->addPosition($userId, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImageId);
+ $posId = $track->addPosition($userId, $this->testTimestamp, $this->testLat, $this->testLon, $this->testAltitude, $this->testSpeed, $this->testBearing, $this->testAccuracy, $this->testProvider, $this->testComment, $this->testImage);
$this->assertEquals(1, $this->getConnection()->getRowCount('positions'), "Wrong row count");
$expected = [
"id" => $posId,
@@ -65,11 +65,11 @@ class TrackTest extends UloggerDatabaseTestCase {
"accuracy" => $this->testAccuracy,
"provider" => $this->testProvider,
"comment" => $this->testComment,
- "image_id" => $this->testImageId
+ "image" => $this->testImage
];
$actual = $this->getConnection()->createQueryTable(
"positions",
- "SELECT id, user_id, track_id, " . $this->unix_timestamp('time') . " AS time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image_id FROM positions"
+ "SELECT id, user_id, track_id, " . $this->unix_timestamp('time') . " AS time, latitude, longitude, altitude, speed, bearing, accuracy, provider, comment, image FROM positions"
);
$this->assertTableContains($expected, $actual, "Wrong actual table data");
diff --git a/.travis.yml b/.travis.yml
index b237bf1..969cc62 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -43,6 +43,7 @@ before_install:
;;
esac
- composer install
+ - npm install
- until netstat -atn 2>/dev/null | grep '8080.*LISTEN'; do sleep 1; done
after_success:
@@ -56,9 +57,14 @@ after_success:
tx push -s --no-interactive
fi
+after_failure:
+ - docker logs ulogger
script:
- ./vendor/bin/phpunit -c .tests/phpunit.xml
+ - npm test
+ - npm run lint:js
+ - npm run lint:css
addons:
coverity_scan:
diff --git a/Dockerfile b/Dockerfile
index d33ae33..47ee91f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -37,7 +37,6 @@ RUN chown nginx.nginx /etc/nginx/conf.d/default.conf
RUN rm -rf /var/www/html
RUN mkdir -p /var/www/html
COPY . /var/www/html
-RUN grep '^[$]' /var/www/html/config.default.php > /var/www/html/config.php
RUN /init.sh "${DB_ROOT_PASS}" "${DB_USER_PASS}"
diff --git a/README.md b/README.md
index 233d47f..88ac199 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,6 @@ Together with a dedicated [μlogger mobile client](https://github.com/bfabiszews
- user authentication
- Google Maps
- OpenLayers (OpenStreet and other layers)
-- ajax
- user preferences stored in cookies
- simple admin menu
- export tracks to gpx and kml
@@ -30,6 +29,8 @@ Together with a dedicated [μlogger mobile client](https://github.com/bfabiszews
## Install
- Download zipped archive or clone the repository on your computer
- Move it to your web server directory (unzip if needed)
+- Fix folder permissions: `uploads` folder (for uploaded images) should be writeable by PHP scripts
+- In case of development version it is necessary to build javascript bundle from source files. You will need to install `npm` and run `npm install` and `npm run build` in root folder
- Create database and database user (at least SELECT, INSERT, UPDATE, DELETE privileges, CREATE, DROP for setup script, SEQUENCES for postgreSQL)
- Create a copy of `config.default.php` and rename it to `config.php`. Customize it and add database credentials
- Edit `scripts/setup.php` script, enable it by setting [$enabled](https://github.com/bfabiszewski/ulogger-server/blob/master/scripts/setup.php#L21) value to `true`
@@ -51,6 +52,7 @@ Together with a dedicated [μlogger mobile client](https://github.com/bfabiszews
## Tests
- Install tests dependecies.
- `composer install`
+ - `npm install`
- Integration tests may be run against docker image. We need exposed http and optionally database ports (eg. mapped to localhost 8080 and 8081). Below example for MySQL setup.
- `docker build -t ulogger .`
- `docker run -d --name ulogger -p 8080:80 -p 8081:3306 --expose 3306 -e ULOGGER_ENABLE_SETUP=1 ulogger`
@@ -59,13 +61,13 @@ Together with a dedicated [μlogger mobile client](https://github.com/bfabiszews
- `DB_USER=ulogger`
- `DB_PASS=secret2`
- `ULOGGER_URL="http://127.0.0.1:8080"`
-- Run tests
+- PHP tests
- `./vendor/bin/phpunit -c .tests/phpunit.xml`
-
-## Todo
-- improve track editing
-- track display filters (accurracy, provider)
-- improve interface on mobile devices
+- JS tests
+ - `npm test`
+- Other tests
+ - `npm run lint:js`
+ - `npm run lint:css`
## Translations
- translations may be contributed via [Transifex](https://www.transifex.com/bfabiszewski/ulogger/).
diff --git a/client/index.php b/client/index.php
index 43addbd..3de795d 100644
--- a/client/index.php
+++ b/client/index.php
@@ -55,7 +55,7 @@
exitWithError("Unauthorized");
}
- switch ($action) {
+switch ($action) {
// action: authorize
case "auth":
$login = uUtils::postString('user');
@@ -111,16 +111,21 @@
$accuracy = uUtils::postInt('accuracy');
$provider = uUtils::postString('provider');
$comment = uUtils::postString('comment');
- $imageId = uUtils::postInt('imageid');
+ $imageMeta = uUtils::requestFile('image');
$trackId = uUtils::postInt('trackid');
if (!is_float($lat) || !is_float($lon) || !is_int($timestamp) || !is_int($trackId)) {
exitWithError("Missing required parameter");
}
+ $image = null;
+ if (!empty($imageMeta)) {
+ $image = uUpload::add($imageMeta, $trackId);
+ }
+
require_once(ROOT_DIR . "/helpers/position.php");
$positionId = uPosition::add($auth->user->id, $trackId,
- $timestamp, $lat, $lon, $altitude, $speed, $bearing, $accuracy, $provider, $comment, $imageId);
+ $timestamp, $lat, $lon, $altitude, $speed, $bearing, $accuracy, $provider, $comment, $image);
if ($positionId === false) {
exitWithError("Server error");
diff --git a/composer.lock b/composer.lock
index d352563..3258483 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "e0409bcb302c1bef7caa031dafc841a9",
+ "content-hash": "ffb1c6d77d755002ea20d1c1c6338b43",
"packages": [
{
"name": "ulrichsg/getopt-php",
@@ -1656,16 +1656,16 @@
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.10.0",
+ "version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "e3d826245268269cd66f8326bd8bc066687b4a19"
+ "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19",
- "reference": "e3d826245268269cd66f8326bd8bc066687b4a19",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
+ "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"shasum": ""
},
"require": {
@@ -1677,7 +1677,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.9-dev"
+ "dev-master": "1.13-dev"
}
},
"autoload": {
@@ -1693,13 +1693,13 @@
"MIT"
],
"authors": [
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- },
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
@@ -1710,20 +1710,20 @@
"polyfill",
"portable"
],
- "time": "2018-08-06T14:22:27+00:00"
+ "time": "2019-11-27T13:56:44+00:00"
},
{
"name": "symfony/yaml",
- "version": "v3.4.22",
+ "version": "v3.4.36",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d"
+ "reference": "dab657db15207879217fc81df4f875947bf68804"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/ba11776e9e6c15ad5759a07bffb15899bac75c2d",
- "reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/dab657db15207879217fc81df4f875947bf68804",
+ "reference": "dab657db15207879217fc81df4f875947bf68804",
"shasum": ""
},
"require": {
@@ -1769,7 +1769,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2019-01-16T10:59:17+00:00"
+ "time": "2019-10-24T15:33:53+00:00"
},
{
"name": "vlucas/phpdotenv",
@@ -1880,6 +1880,12 @@
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
- "platform": [],
+ "platform": {
+ "ext-json": "*",
+ "ext-pdo": "*",
+ "ext-xmlwriter": "*",
+ "ext-simplexml": "*",
+ "ext-libxml": "*"
+ },
"platform-dev": []
}
diff --git a/css/chartist.min.css b/css/chartist.min.css
new file mode 100644
index 0000000..6a23d47
--- /dev/null
+++ b/css/chartist.min.css
@@ -0,0 +1 @@
+.ct-double-octave:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-grid-background,.ct-line{fill:none}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0}
\ No newline at end of file
diff --git a/css/fonts.css b/css/fonts.css
new file mode 100644
index 0000000..e58f85c
--- /dev/null
+++ b/css/fonts.css
@@ -0,0 +1,224 @@
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/mem6YaGs126MiZpBA-UFUK0Udc1GAK6bt6o.woff2) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/mem6YaGs126MiZpBA-UFUK0ddc1GAK6bt6o.woff2) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/mem6YaGs126MiZpBA-UFUK0Vdc1GAK6bt6o.woff2) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/mem6YaGs126MiZpBA-UFUK0adc1GAK6bt6o.woff2) format('woff2');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/mem6YaGs126MiZpBA-UFUK0Wdc1GAK6bt6o.woff2) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/mem6YaGs126MiZpBA-UFUK0Xdc1GAK6bt6o.woff2) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/mem6YaGs126MiZpBA-UFUK0Zdc1GAK6b.woff2) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 700;
+ src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(../fonts/memnYaGs126MiZpBA-UFUKWiUNhmIqOxjaPXZSk.woff2) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 700;
+ src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(../fonts/memnYaGs126MiZpBA-UFUKWiUNhvIqOxjaPXZSk.woff2) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 700;
+ src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(../fonts/memnYaGs126MiZpBA-UFUKWiUNhnIqOxjaPXZSk.woff2) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 700;
+ src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(../fonts/memnYaGs126MiZpBA-UFUKWiUNhoIqOxjaPXZSk.woff2) format('woff2');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 700;
+ src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(../fonts/memnYaGs126MiZpBA-UFUKWiUNhkIqOxjaPXZSk.woff2) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 700;
+ src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(../fonts/memnYaGs126MiZpBA-UFUKWiUNhlIqOxjaPXZSk.woff2) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-weight: 700;
+ src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(../fonts/memnYaGs126MiZpBA-UFUKWiUNhrIqOxjaPX.woff2) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/mem8YaGs126MiZpBA-UFWJ0bf8pkAp6a.woff2) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/mem8YaGs126MiZpBA-UFUZ0bf8pkAp6a.woff2) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/mem8YaGs126MiZpBA-UFWZ0bf8pkAp6a.woff2) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/mem8YaGs126MiZpBA-UFVp0bf8pkAp6a.woff2) format('woff2');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/mem8YaGs126MiZpBA-UFWp0bf8pkAp6a.woff2) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/mem8YaGs126MiZpBA-UFW50bf8pkAp6a.woff2) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/mem8YaGs126MiZpBA-UFVZ0bf8pkAg.woff2) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url(../fonts/mem5YaGs126MiZpBA-UN7rgOX-hpKKSTj5PW.woff2) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url(../fonts/mem5YaGs126MiZpBA-UN7rgOVuhpKKSTj5PW.woff2) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url(../fonts/mem5YaGs126MiZpBA-UN7rgOXuhpKKSTj5PW.woff2) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url(../fonts/mem5YaGs126MiZpBA-UN7rgOUehpKKSTj5PW.woff2) format('woff2');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url(../fonts/mem5YaGs126MiZpBA-UN7rgOXehpKKSTj5PW.woff2) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url(../fonts/mem5YaGs126MiZpBA-UN7rgOXOhpKKSTj5PW.woff2) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url(../fonts/mem5YaGs126MiZpBA-UN7rgOUuhpKKSTjw.woff2) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
diff --git a/css/main.css b/css/main.css
index 4e5563c..61d38f9 100644
--- a/css/main.css
+++ b/css/main.css
@@ -19,238 +19,360 @@
html {
height: 100%;
}
+
body {
height: 100%;
margin: 0;
padding: 0;
background-color: #666;
}
+
a {
- color: #bce;
+ cursor: pointer;
text-decoration: none;
+ color: #bce;
}
+
:link, :visited {
color: #bce;
}
+
select {
- width: 150px;
font-weight: normal;
+ width: 150px;
padding-top: 0.2em;
}
+
+#container {
+ display: flex;
+ height: 100%;
+}
+
+#main {
+ flex-grow: 1;
+ order: 1;
+ height: 100%;
+}
+
+#map-canvas {
+ height: 100%;
+}
+
+#menu {
+ font-family: "Open Sans", Verdana, sans-serif;
+ font-size: 0.7em;
+ font-weight: bold;
+ float: right;
+ overflow-x: hidden;
+ overflow-y: auto;
+ order: 2;
+ width: 165px;
+ height: 100%;
+ color: white;
+ background-color: #666;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+}
+
+#menu-content {
+ padding: 10px 0 3em 10px;
+}
+
+#footer {
+ line-height: 3em;
+ position: fixed;
+ bottom: 0;
+ width: 165px;
+ padding-left: 10px;
+ color: lightgray;
+ background-color: rgba(102, 102, 102, 0.9);
+}
+
+#menu-button {
+ font-size: 28px;
+ font-weight: normal;
+ line-height: 28px;
+ position: absolute;
+ z-index: 1900;
+ top: 5px;
+ right: 0;
+ width: 30px;
+ height: 35px;
+ cursor: pointer;
+ text-align: center;
+ border-width: 1px 0 1px 1px;
+ border-style: solid;
+ border-color: #bce;
+ border-radius: 11px 0 0 11px;
+ background-color: #666;
+}
+
+#menu-button a {
+ color: white;
+}
+
+#menu-button a::after {
+ content: "»";
+}
+
+#menu.menu-hidden {
+ width: 0;
+}
+
+#menu.menu-hidden #menu-button {
+ font-weight: normal;
+ border-color: white;
+ background-color: rgba(0, 60, 136, 0.3);
+}
+
+#menu.menu-hidden #menu-button a::after {
+ content: "«";
+}
+
#menu input,
#login input {
width: 150px;
text-align: center;
border: 1px solid black;
}
-#menu input[type = "submit"],
-#login input[type = "submit"] {
- background-color: black;
+
+#menu input[type="submit"],
+#login input[type="submit"] {
color: white;
border: 1px solid white;
-}
-#menu input[type = "checkbox"] {
- width: auto;
-}
-.menulink {
- display: block;
- margin-top: .2em;
-}
-#main {
- height: 100%;
- margin-right: 165px;
-}
-#map-canvas {
- height: 100%;
-}
-#menu {
- font-family: 'Open Sans', Verdana, sans-serif;
- -moz-osx-font-smoothing: grayscale;
- -webkit-font-smoothing: antialiased;
- font-size: 0.7em;
- font-weight: bold;
- color: white;
- float: right;
- width: 165px;
- height: 100%;
- background-color: #666;
- overflow-x: hidden;
- overflow-y: auto;
-}
-#menu-content {
- padding: 10px 0 3em 10px;
-}
-#footer {
- position: fixed;
- bottom:0;
- width: 165px;
- line-height: 3em;
- padding-left: 10px;
- background-color:rgba(102, 102, 102, 0.9);
- color: lightgray;
-}
-#menu-close {
- background-color: #666;
- opacity: 0.9;
- position: absolute;
- top: 55px;
- right: 165px;
- z-index: 1900;
- width: 18px;
- height: 20px;
- line-height: 18px;
- text-align: right;
- font-size: 18px;
- font-weight: bolder;
- border-radius: 11px 0 0 11px;
- cursor: pointer;
+ background-color: black;
}
-#user, #track, #summary, #export, #import, #other, #units {
+#menu input[type="checkbox"] {
+ width: auto;
+}
+
+.menu-link {
+ display: block;
+ margin-top: 0.2em;
+}
+
+label[for=user] {
+ display: block;
+ padding-top: 1em;
+}
+
+.section {
+ display: block;
padding-bottom: 10px;
}
-#summary div {
- padding-top: .3em;
+
+.section:first-child {
+ padding-top: 1em;
}
+
+#input-file {
+ display: none;
+}
+
+#summary div {
+ padding-top: 0.3em;
+}
+
#summary div img {
margin-bottom: -2px;
}
+
#login {
- font-family: 'Open Sans', Verdana, sans-serif;
+ font-family: "Open Sans", Verdana, sans-serif;
+ font-size: 0.8em;
position: relative;
top: 10%;
- background-color: #444;
width: 30%;
min-width: 200px;
margin: auto;
padding: 30px;
- font-size: 0.8em;
text-align: center;
color: white;
+ background-color: #444;
}
+
#title {
font-size: 1.3em;
- padding-bottom: 0.5em;
padding-top: 0.6em;
+ padding-bottom: 0.5em;
}
+
#subtitle {
padding-bottom: 2em;
}
+
#error {
padding-top: 1.2em;
color: yellow;
}
+
#popup {
- font-family: 'Open Sans', Verdana, sans-serif;
+ font-family: "Open Sans", Verdana, sans-serif;
+ max-width: 25em;
+ background-color: #666;
}
+
#pheader {
+ font-size: 0.9rem;
float: left;
- font-size: .9rem;
- color: #297b9a;
- padding-bottom: .5rem;
+ padding-bottom: 0.5rem;
+ color: #bce;
}
+
#pheader div {
float: left;
padding-right: 2em;
}
-#pbody {
- clear: both;
- padding-top: .2rem;
- border-top: 1px solid #6cdae7;;
- font-size: .8rem;
- white-space: nowrap;
+
+#pheader div img {
+ background-image: radial-gradient(circle closest-side, #bfbfbc, #666);
}
+
+#pbody {
+ font-size: 0.8rem;
+ line-height: 1.3rem;
+ clear: both;
+ padding-top: 0.2rem;
+ white-space: nowrap;
+ color: #e6e2e2;
+ border-top: 1px solid #bce;
+}
+
#pcomments {
clear: both;
- color: #903;
+ padding: 1em;
+ text-align: center;
+ white-space: normal;
+ color: #e6e6e6;
+ border-radius: 10px;
+ background-color: #777676;
}
+
+#pimage {
+ text-align: center;
+}
+
+#pimage img {
+ max-width: 100%;
+ max-height: 25em;
+ cursor: pointer;
+ border-radius: 10px;
+}
+
+#pimage img:hover {
+ opacity: 0.7;
+}
+
#pleft, #pright {
display: inline-block;
padding-top: 5px;
padding-right: 20px;
}
-#pbody .smaller {
- color: gray;
- font-size: .9em;
-}
-#pfooter {
- font-size: .6rem;
- padding-top: 20px;
-}
-#bottom {
- display: none;
- position: absolute;
- z-index: 10000;
-}
-#chart {
- position: fixed;
- bottom: 0; left:0; right: 0;
- height: 200px;
- margin-right: 165px;
- background-color: white;
- opacity: 0.8;
-}
-#close {
- position: fixed;
- bottom: 175px;
- right: 175px;
- z-index: 10001;
- font-size: 0.8em;
+
+#pleft img {
+ background-image: radial-gradient(circle closest-side, #bfbfbc, #666);
}
-#close a, #close:link, #close:visited {
+#pbody .smaller {
+ font-size: 0.9em;
+ color: #cacaca;
+}
+
+#pfooter {
+ font-size: 0.6rem;
+ padding-top: 20px;
+ color: #f0f8ff;
+}
+
+#pfooter div:first-child {
+ width: 40%;
+ float: left;
+}
+
+#pfooter div:last-child {
+ width: 40%;
+ float: right;
+ text-align: right;
+}
+
+#bottom {
+ position: relative;
+ z-index: 10000;
+ display: none;
+}
+
+#chart {
+ font-family: "Open Sans", Verdana, sans-serif;
+ position: absolute;
+ right: 0;
+ bottom: -15px;
+ left: 0;
+ height: 200px;
+ padding: 0 10px;
+ opacity: 0.8;
+ background-color: white;
+}
+
+#chart-close {
+ font-size: 0.8em;
+ position: absolute;
+ z-index: 10001;
+ right: 15px;
+ bottom: 160px;
+ cursor: pointer;
color: #5070af;
}
.mi {
- color:white;
- padding-right:0.1em;
- font-style:italic;
+ font-style: italic;
+ padding-right: 0.1em;
+ color: white;
}
#modal {
- font-family: 'Open Sans', Verdana, sans-serif;
- display: block;
+ font-family: "Open Sans", Verdana, sans-serif;
position: fixed;
z-index: 10010;
- left: 0;
top: 0;
+ left: 0;
+ display: block;
+ overflow: auto;
width: 100%;
height: 100%;
- overflow: auto;
- background-color: black; /* fallback */
- background-color: rgba(0,0,0,0.4);
padding-top: 10%;
+ background-color: black; /* fallback */
+ background-color: rgba(0, 0, 0, 0.4);
}
#modal-header {
- top: 20px;
position: relative;
- text-align: right;
- margin: 0 auto;
+ top: 20px;
width: 40%;
min-width: 300px;
+ margin: 0 auto;
+ text-align: right;
}
#modal-header button {
- background-color: rgba(0, 0, 0, 0);
border: none;
+ background-color: rgba(0, 0, 0, 0);
}
#modal-body {
font-size: 0.9em;
- -moz-osx-font-smoothing: grayscale;
- -webkit-font-smoothing: antialiased;
- color: white;
- background-color: rgba(102, 102, 102, 0.9);
- margin: 0 auto 15% auto;
- border: 1px solid #888;
width: 40%;
min-width: 300px;
+ margin: 0 auto 15% auto;
padding: 1em;
- border-radius: 10px;
+ color: white;
+ border: 1px solid #888;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
+ border-radius: 10px;
+ background-color: rgba(102, 102, 102, 0.9);
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
}
#modal-body .buttons {
@@ -258,27 +380,53 @@ select {
}
#modal input[type=text], #modal input[type=password] {
- width: 100%;
- padding: 0.4em;
- margin: 0.8em 0;
display: inline-block;
- border: 1px solid #ccc;
box-sizing: border-box;
- border-radius: 5px;
+ width: 100%;
+ margin: 0.8em 0;
+ padding: 0.4em;
+ border: 1px solid #ccc;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+
+#modal.image {
+ overflow: hidden;
+ padding-top: 0;
+ background-color: rgba(45, 45, 45, 0.95);
+}
+
+#modal.image #modal-body img {
+ max-width: 100%;
+ height: auto;
+ max-height: 87vh;
+}
+
+#modal.image #modal-body {
+ width: 90%;
+ text-align: center;
+ background-color: rgb(45, 45, 45);
+}
+
+#modal.image #modal-header {
+ width: 90%;
}
button {
- color: white;
- background-color: #434343;
- cursor: pointer;
- border: 1px solid white;
- border-radius: 5px;
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
font-weight: bold;
margin-right: 5px;
+ cursor: pointer;
+ color: white;
+ border: 1px solid white;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ background-color: #434343;
+}
+
+button > * {
+ pointer-events: none;
}
#cancel {
@@ -286,131 +434,205 @@ button {
}
.red-button {
- color: white;
float: right;
- background-color: red;
- padding: .1em .4em;
- border-radius: 10px;
+ padding: 0.1em 0.4em;
+ color: white;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
+ border-radius: 10px;
+ background-color: red;
}
-.dropdown {
- display: none;
+#user-menu {
position: absolute;
- background-color: gray;
- padding: 1em;
- width: 130px;
- border: 1px solid #888;
-}
-
-.dropdown a {
display: block;
- padding-bottom: .5em;
- padding-top: .5em;
+ width: 130px;
+ padding: 1em;
+ border: 1px solid #888;
+ background-color: gray;
}
-.show { display: block; }
+#user-menu.menu-hidden, a.menu-hidden {
+ display: none;
+}
-.icon { height: 1.4em; }
+#user-menu a {
+ display: block;
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+}
-.u { text-decoration: underline; }
+.icon {
+ height: 1.4em;
+ margin-right: 4px;
+ vertical-align: text-top;
+}
+
+.menu-title {
+ text-decoration: underline;
+}
.loader {
animation: blink 1s linear infinite;
}
@keyframes blink {
- 50% { opacity: 0; }
+ 50% {
+ opacity: 0;
+ }
+}
+
+/* chart */
+.ct-point {
+ transition: 0.3s;
+ stroke-width: 5px !important;
+}
+
+.ct-point:hover {
+ cursor: pointer;
+ stroke-width: 10px !important;
+}
+
+.ct-point-hilight {
+ stroke-width: 10px !important;
+}
+
+.ct-point-selected {
+ stroke-width: 10px !important;
+ stroke: #f4c63d !important;
+}
+
+.ct-line {
+ stroke-width: 2px !important;
+}
+
+.ct-axis-title {
+ font-size: 0.8em;
}
/* openlayers 3 popup */
.ol-popup {
position: absolute;
- background-color: white;
- -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));
- padding: 15px;
- border-radius: 10px;
- border: 1px solid #cccccc;
bottom: 12px;
left: -50px;
min-width: 280px;
+ padding: 15px;
+ 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));
}
-.ol-popup:after, .ol-popup:before {
- top: 100%;
- border: solid transparent;
- content: " ";
- height: 0;
- width: 0;
+
+.ol-popup::after, .ol-popup::before {
position: absolute;
+ top: 100%;
+ width: 0;
+ height: 0;
+ content: " ";
pointer-events: none;
+ border: solid transparent;
}
-.ol-popup:after {
- border-top-color: white;
- border-width: 10px;
+
+.ol-popup::after {
left: 48px;
margin-left: -10px;
+ border-width: 10px;
+ border-top-color: #666;
}
-.ol-popup:before {
- border-top-color: #cccccc;
- border-width: 11px;
+
+.ol-popup::before {
left: 48px;
margin-left: -11px;
+ border-width: 11px;
+ border-top-color: #ccc;
}
+
.ol-popup-closer {
- text-decoration: none;
position: absolute;
- top: 2px;
- right: 8px;
+ top: -5px;
+ right: -10px;
+ width: 30px;
+ height: 30px;
+ background-image: url(../images/close.svg) !important;
+ background-repeat: no-repeat !important;
}
-.ol-popup-closer:after {
- content: "✖";
+
+.ol-overlay-container {
+ background-color: #666;
+}
+
+/* Google Maps InfoWindow */
+.gm-style .gm-style-iw-c {
+ background-color: #666 !important;
+ overflow: visible !important;
+}
+
+.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;
+}
+
+.gm-style-iw button {
+ background-image: url(../images/close.svg) !important;
+ background-repeat: no-repeat !important;
+}
+
+.gm-style-iw button img {
+ visibility: hidden;
}
#switcher {
- display: none;
position: absolute;
bottom: 12px;
left: 10px;
+ display: none;
min-width: 200px;
}
+
.ol-layerswitcher {
+ font-family: sans-serif;
+ font-size: 0.9em;
+ font-weight: bold;
margin: 1px;
+ padding: 0.5em;
color: #fff;
- background-color: rgba(0, 60, 136, .5);
border: none;
border-radius: 2px;
- font-family: sans-serif;
- font-weight: bold;
- font-size: .9em;
- padding: 0.5em;
+ background-color: rgba(0, 60, 136, 0.5);
}
+
.ol-layerswitcher:hover {
- background-color: rgba(0, 60, 136, .7)
+ background-color: rgba(0, 60, 136, 0.7);
}
+
.ol-layerswitcher label {
display: block;
clear: both;
- margin: .5em 0;
+ margin: 0.5em 0;
cursor: pointer;
}
+
.ol-layerswitcher label:hover {
color: #c8dcf2;
}
+
.ol-layerswitcher input {
margin-right: 1em;
}
+
label.ol-datalayer {
margin-top: 1.5em;
}
+
.ol-datalayer ~ .ol-datalayer {
- margin-top: .5em;
+ margin-top: 0.5em;
}
+
.ol-switcher-button {
top: 6.6em;
- left: .5em;
+ left: 0.5em;
}
+
.ol-touch .ol-switcher-button {
top: 10em;
-}
\ No newline at end of file
+}
diff --git a/fonts/OpenSans.LICENSE b/fonts/OpenSans.LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/fonts/OpenSans.LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/fonts/OpenSans.README b/fonts/OpenSans.README
new file mode 100644
index 0000000..e156adc
--- /dev/null
+++ b/fonts/OpenSans.README
@@ -0,0 +1,5 @@
+Open Sans is a humanist sans serif typeface designed by Steve Matteson,
+Type Director of Ascender Corp. This version contains the complete 897 character set,
+which includes the standard ISO Latin 1, Latin CE, Greek and Cyrillic character sets.
+Open Sans was designed with an upright stress, open forms and a neutral, yet friendly appearance.
+It was optimized for print, web, and mobile interfaces, and has excellent legibility characteristics in its letterforms.
\ No newline at end of file
diff --git a/fonts/mem5YaGs126MiZpBA-UN7rgOUehpKKSTj5PW.woff2 b/fonts/mem5YaGs126MiZpBA-UN7rgOUehpKKSTj5PW.woff2
new file mode 100644
index 0000000..b6ceebf
Binary files /dev/null and b/fonts/mem5YaGs126MiZpBA-UN7rgOUehpKKSTj5PW.woff2 differ
diff --git a/fonts/mem5YaGs126MiZpBA-UN7rgOUuhpKKSTjw.woff2 b/fonts/mem5YaGs126MiZpBA-UN7rgOUuhpKKSTjw.woff2
new file mode 100644
index 0000000..90f3939
Binary files /dev/null and b/fonts/mem5YaGs126MiZpBA-UN7rgOUuhpKKSTjw.woff2 differ
diff --git a/fonts/mem5YaGs126MiZpBA-UN7rgOVuhpKKSTj5PW.woff2 b/fonts/mem5YaGs126MiZpBA-UN7rgOVuhpKKSTj5PW.woff2
new file mode 100644
index 0000000..3f39502
Binary files /dev/null and b/fonts/mem5YaGs126MiZpBA-UN7rgOVuhpKKSTj5PW.woff2 differ
diff --git a/fonts/mem5YaGs126MiZpBA-UN7rgOX-hpKKSTj5PW.woff2 b/fonts/mem5YaGs126MiZpBA-UN7rgOX-hpKKSTj5PW.woff2
new file mode 100644
index 0000000..a339675
Binary files /dev/null and b/fonts/mem5YaGs126MiZpBA-UN7rgOX-hpKKSTj5PW.woff2 differ
diff --git a/fonts/mem5YaGs126MiZpBA-UN7rgOXOhpKKSTj5PW.woff2 b/fonts/mem5YaGs126MiZpBA-UN7rgOXOhpKKSTj5PW.woff2
new file mode 100644
index 0000000..a21f919
Binary files /dev/null and b/fonts/mem5YaGs126MiZpBA-UN7rgOXOhpKKSTj5PW.woff2 differ
diff --git a/fonts/mem5YaGs126MiZpBA-UN7rgOXehpKKSTj5PW.woff2 b/fonts/mem5YaGs126MiZpBA-UN7rgOXehpKKSTj5PW.woff2
new file mode 100644
index 0000000..20d5832
Binary files /dev/null and b/fonts/mem5YaGs126MiZpBA-UN7rgOXehpKKSTj5PW.woff2 differ
diff --git a/fonts/mem5YaGs126MiZpBA-UN7rgOXuhpKKSTj5PW.woff2 b/fonts/mem5YaGs126MiZpBA-UN7rgOXuhpKKSTj5PW.woff2
new file mode 100644
index 0000000..efc5c40
Binary files /dev/null and b/fonts/mem5YaGs126MiZpBA-UN7rgOXuhpKKSTj5PW.woff2 differ
diff --git a/fonts/mem6YaGs126MiZpBA-UFUK0Udc1GAK6bt6o.woff2 b/fonts/mem6YaGs126MiZpBA-UFUK0Udc1GAK6bt6o.woff2
new file mode 100644
index 0000000..736c1e7
Binary files /dev/null and b/fonts/mem6YaGs126MiZpBA-UFUK0Udc1GAK6bt6o.woff2 differ
diff --git a/fonts/mem6YaGs126MiZpBA-UFUK0Vdc1GAK6bt6o.woff2 b/fonts/mem6YaGs126MiZpBA-UFUK0Vdc1GAK6bt6o.woff2
new file mode 100644
index 0000000..deabb6d
Binary files /dev/null and b/fonts/mem6YaGs126MiZpBA-UFUK0Vdc1GAK6bt6o.woff2 differ
diff --git a/fonts/mem6YaGs126MiZpBA-UFUK0Wdc1GAK6bt6o.woff2 b/fonts/mem6YaGs126MiZpBA-UFUK0Wdc1GAK6bt6o.woff2
new file mode 100644
index 0000000..45a75c3
Binary files /dev/null and b/fonts/mem6YaGs126MiZpBA-UFUK0Wdc1GAK6bt6o.woff2 differ
diff --git a/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1GAK6bt6o.woff2 b/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1GAK6bt6o.woff2
new file mode 100644
index 0000000..5754164
Binary files /dev/null and b/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1GAK6bt6o.woff2 differ
diff --git a/fonts/mem6YaGs126MiZpBA-UFUK0Zdc1GAK6b.woff2 b/fonts/mem6YaGs126MiZpBA-UFUK0Zdc1GAK6b.woff2
new file mode 100644
index 0000000..3907070
Binary files /dev/null and b/fonts/mem6YaGs126MiZpBA-UFUK0Zdc1GAK6b.woff2 differ
diff --git a/fonts/mem6YaGs126MiZpBA-UFUK0adc1GAK6bt6o.woff2 b/fonts/mem6YaGs126MiZpBA-UFUK0adc1GAK6bt6o.woff2
new file mode 100644
index 0000000..94738e9
Binary files /dev/null and b/fonts/mem6YaGs126MiZpBA-UFUK0adc1GAK6bt6o.woff2 differ
diff --git a/fonts/mem6YaGs126MiZpBA-UFUK0ddc1GAK6bt6o.woff2 b/fonts/mem6YaGs126MiZpBA-UFUK0ddc1GAK6bt6o.woff2
new file mode 100644
index 0000000..41fc44c
Binary files /dev/null and b/fonts/mem6YaGs126MiZpBA-UFUK0ddc1GAK6bt6o.woff2 differ
diff --git a/fonts/mem8YaGs126MiZpBA-UFUZ0bf8pkAp6a.woff2 b/fonts/mem8YaGs126MiZpBA-UFUZ0bf8pkAp6a.woff2
new file mode 100644
index 0000000..cf58322
Binary files /dev/null and b/fonts/mem8YaGs126MiZpBA-UFUZ0bf8pkAp6a.woff2 differ
diff --git a/fonts/mem8YaGs126MiZpBA-UFVZ0bf8pkAg.woff2 b/fonts/mem8YaGs126MiZpBA-UFVZ0bf8pkAg.woff2
new file mode 100644
index 0000000..97338d6
Binary files /dev/null and b/fonts/mem8YaGs126MiZpBA-UFVZ0bf8pkAg.woff2 differ
diff --git a/fonts/mem8YaGs126MiZpBA-UFVp0bf8pkAp6a.woff2 b/fonts/mem8YaGs126MiZpBA-UFVp0bf8pkAp6a.woff2
new file mode 100644
index 0000000..a0c17c7
Binary files /dev/null and b/fonts/mem8YaGs126MiZpBA-UFVp0bf8pkAp6a.woff2 differ
diff --git a/fonts/mem8YaGs126MiZpBA-UFW50bf8pkAp6a.woff2 b/fonts/mem8YaGs126MiZpBA-UFW50bf8pkAp6a.woff2
new file mode 100644
index 0000000..c9470c7
Binary files /dev/null and b/fonts/mem8YaGs126MiZpBA-UFW50bf8pkAp6a.woff2 differ
diff --git a/fonts/mem8YaGs126MiZpBA-UFWJ0bf8pkAp6a.woff2 b/fonts/mem8YaGs126MiZpBA-UFWJ0bf8pkAp6a.woff2
new file mode 100644
index 0000000..458c98d
Binary files /dev/null and b/fonts/mem8YaGs126MiZpBA-UFWJ0bf8pkAp6a.woff2 differ
diff --git a/fonts/mem8YaGs126MiZpBA-UFWZ0bf8pkAp6a.woff2 b/fonts/mem8YaGs126MiZpBA-UFWZ0bf8pkAp6a.woff2
new file mode 100644
index 0000000..9f0e782
Binary files /dev/null and b/fonts/mem8YaGs126MiZpBA-UFWZ0bf8pkAp6a.woff2 differ
diff --git a/fonts/mem8YaGs126MiZpBA-UFWp0bf8pkAp6a.woff2 b/fonts/mem8YaGs126MiZpBA-UFWp0bf8pkAp6a.woff2
new file mode 100644
index 0000000..c01e28d
Binary files /dev/null and b/fonts/mem8YaGs126MiZpBA-UFWp0bf8pkAp6a.woff2 differ
diff --git a/fonts/memnYaGs126MiZpBA-UFUKWiUNhkIqOxjaPXZSk.woff2 b/fonts/memnYaGs126MiZpBA-UFUKWiUNhkIqOxjaPXZSk.woff2
new file mode 100644
index 0000000..99a75fd
Binary files /dev/null and b/fonts/memnYaGs126MiZpBA-UFUKWiUNhkIqOxjaPXZSk.woff2 differ
diff --git a/fonts/memnYaGs126MiZpBA-UFUKWiUNhlIqOxjaPXZSk.woff2 b/fonts/memnYaGs126MiZpBA-UFUKWiUNhlIqOxjaPXZSk.woff2
new file mode 100644
index 0000000..fe8323c
Binary files /dev/null and b/fonts/memnYaGs126MiZpBA-UFUKWiUNhlIqOxjaPXZSk.woff2 differ
diff --git a/fonts/memnYaGs126MiZpBA-UFUKWiUNhmIqOxjaPXZSk.woff2 b/fonts/memnYaGs126MiZpBA-UFUKWiUNhmIqOxjaPXZSk.woff2
new file mode 100644
index 0000000..73c6afa
Binary files /dev/null and b/fonts/memnYaGs126MiZpBA-UFUKWiUNhmIqOxjaPXZSk.woff2 differ
diff --git a/fonts/memnYaGs126MiZpBA-UFUKWiUNhnIqOxjaPXZSk.woff2 b/fonts/memnYaGs126MiZpBA-UFUKWiUNhnIqOxjaPXZSk.woff2
new file mode 100644
index 0000000..4703c88
Binary files /dev/null and b/fonts/memnYaGs126MiZpBA-UFUKWiUNhnIqOxjaPXZSk.woff2 differ
diff --git a/fonts/memnYaGs126MiZpBA-UFUKWiUNhoIqOxjaPXZSk.woff2 b/fonts/memnYaGs126MiZpBA-UFUKWiUNhoIqOxjaPXZSk.woff2
new file mode 100644
index 0000000..5683616
Binary files /dev/null and b/fonts/memnYaGs126MiZpBA-UFUKWiUNhoIqOxjaPXZSk.woff2 differ
diff --git a/fonts/memnYaGs126MiZpBA-UFUKWiUNhrIqOxjaPX.woff2 b/fonts/memnYaGs126MiZpBA-UFUKWiUNhrIqOxjaPX.woff2
new file mode 100644
index 0000000..1c153db
Binary files /dev/null and b/fonts/memnYaGs126MiZpBA-UFUKWiUNhrIqOxjaPX.woff2 differ
diff --git a/fonts/memnYaGs126MiZpBA-UFUKWiUNhvIqOxjaPXZSk.woff2 b/fonts/memnYaGs126MiZpBA-UFUKWiUNhvIqOxjaPXZSk.woff2
new file mode 100644
index 0000000..d2e3337
Binary files /dev/null and b/fonts/memnYaGs126MiZpBA-UFUKWiUNhvIqOxjaPXZSk.woff2 differ
diff --git a/helpers/auth.php b/helpers/auth.php
index 6206bd2..afe9f78 100644
--- a/helpers/auth.php
+++ b/helpers/auth.php
@@ -27,6 +27,7 @@
*/
class uAuth {
+ /** @var bool Is user authenticated */
private $isAuthenticated = false;
/** @var null|uUser */
public $user = null;
@@ -40,6 +41,15 @@
}
}
+ /**
+ * Update user instance stored in session
+ */
+ public function updateSession() {
+ if ($this->isAuthenticated()) {
+ $this->user->storeInSession();
+ }
+ }
+
/**
* Is user authenticated
*
diff --git a/helpers/config.php b/helpers/config.php
index 35169c4..58733d2 100644
--- a/helpers/config.php
+++ b/helpers/config.php
@@ -27,59 +27,108 @@
* Handles config values
*/
class uConfig {
- // version number
- static $version = "0.6";
+ /**
+ * @var string Version number
+ */
+ static $version = "1.0-beta";
- // default map drawing framework
+ /**
+ * @var string Default map drawing framework
+ */
static $mapapi = "openlayers";
- // gmaps key
+ /**
+ * @var string|null Google maps key
+ */
static $gkey = null;
- // openlayers additional map layers
+ /**
+ * @var array Openlayers additional map layers
+ */
static $ol_layers = [];
- // default coordinates for initial map
+ /**
+ * @var float Default latitude for initial map
+ */
static $init_latitude = 52.23;
+ /**
+ * @var float Default longitude for initial map
+ */
static $init_longitude = 21.01;
- // MySQL config
- static $dbdsn = ""; // database dsn
- static $dbuser = ""; // database user
- static $dbpass = ""; // database pass
- static $dbprefix = ""; // optional table names prefix, eg. "ulogger_"
+ /**
+ * @var string Database dsn
+ */
+ static $dbdsn = "";
+ /**
+ * @var string Database user
+ */
+ static $dbuser = "";
+ /**
+ * @var string Database pass
+ */
+ static $dbpass = "";
+ /**
+ * @var string Optional table names prefix, eg. "ulogger_"
+ */
+ static $dbprefix = "";
- // require login/password authentication
+ /**
+ * @var bool Require login/password authentication
+ */
static $require_authentication = true;
- // all users tracks are visible to authenticated user
+ /**
+ * @var bool All users tracks are visible to authenticated user
+ */
static $public_tracks = false;
- // admin user who has access to all users locations
- // none if empty
+ /**
+ * @var string Admin user who has access to all users locations
+ * none if empty
+ */
static $admin_user = "";
- // miniumum required length of user password
+ /**
+ * @var int Miniumum required length of user password
+ */
static $pass_lenmin = 12;
- // required strength of user password
- // 0 = no requirements,
- // 1 = require mixed case letters (lower and upper),
- // 2 = require mixed case and numbers
- // 3 = require mixed case, numbers and non-alphanumeric characters
+ /**
+ * @var int Required strength of user password
+ * 0 = no requirements,
+ * 1 = require mixed case letters (lower and upper),
+ * 2 = require mixed case and numbers
+ * 3 = require mixed case, numbers and non-alphanumeric characters
+ */
static $pass_strength = 2;
- // Default interval in seconds for live auto reload
+ /**
+ * @var int Default interval in seconds for live auto reload
+ */
static $interval = 10;
- // Default language
+ /**
+ * @var string Default language code
+ */
static $lang = "en";
- // units
+ /**
+ * @var string Default units
+ */
static $units = "metric";
+ /**
+ * @var int Stroke weight
+ */
static $strokeWeight = 2;
+ /**
+ * @var string Stroke color
+ */
static $strokeColor = '#ff0000';
+ /**
+ * @var int Stroke opacity
+ */
static $strokeOpacity = 1;
private static $fileLoaded = false;
@@ -109,7 +158,7 @@
include_once($configFile);
if (isset($mapapi)) { self::$mapapi = $mapapi; }
- if (isset($gkey)) { self::$gkey = $gkey; }
+ if (isset($gkey) && !empty($gkey)) { self::$gkey = $gkey; }
if (isset($ol_layers)) { self::$ol_layers = $ol_layers; }
if (isset($init_latitude)) { self::$init_latitude = $init_latitude; }
if (isset($init_longitude)) { self::$init_longitude = $init_longitude; }
diff --git a/helpers/position.php b/helpers/position.php
index 10e6cca..185cf56 100644
--- a/helpers/position.php
+++ b/helpers/position.php
@@ -17,8 +17,9 @@
* along with this program; if not, see .
*/
- require_once(ROOT_DIR . "/helpers/db.php");
- require_once(ROOT_DIR . "/helpers/track.php");
+require_once(ROOT_DIR . "/helpers/db.php");
+require_once(ROOT_DIR . "/helpers/track.php");
+require_once(ROOT_DIR . "/helpers/upload.php");
/**
* Positions handling
@@ -51,9 +52,9 @@
/** @param String Provider */
public $provider;
/** @param String Comment */
- public $comment; // not used yet
- /** @param int Image id */
- public $imageId; // not used yet
+ public $comment;
+ /** @param String Image path */
+ public $image;
public $isValid = false;
@@ -66,11 +67,11 @@
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_id, u.login, t.name
+ 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 id = ? LIMIT 1";
+ WHERE p.id = ? LIMIT 1";
$params = [ $positionId ];
try {
$this->loadWithQuery($query, $params);
@@ -90,6 +91,15 @@
return uDb::getInstance();
}
+ /**
+ * Has image
+ *
+ * @return bool True if has image
+ */
+ public function hasImage() {
+ return !empty($this->image);
+ }
+
/**
* Add position
*
@@ -104,27 +114,27 @@
* @param int $accuracy Optional
* @param string $provider Optional
* @param string $comment Optional
- * @param int $imageId 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, $imageId = 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) {
+ 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_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, $imageId ];
+ $timestamp, $lat, $lon, $altitude, $speed, $bearing, $accuracy, $provider, $comment, $image ];
$stmt->execute($params);
- $positionId = self::db()->lastInsertId("${table}_id_seq");
+ $positionId = (int) self::db()->lastInsertId("${table}_id_seq");
} catch (PDOException $e) {
// TODO: handle error
syslog(LOG_ERR, $e->getMessage());
@@ -134,6 +144,70 @@
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 ]);
+ if ($this->hasImage()) {
+ uUpload::delete($this->image);
+ }
+ $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
*
@@ -151,6 +225,7 @@
$where .= " AND track_id = ?";
$args[] = $trackId;
}
+ self::removeImages($userId, $trackId);
try {
$query = "DELETE FROM " . self::db()->table('positions') . " $where";
$stmt = self::db()->prepare($query);
@@ -181,7 +256,7 @@
}
$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_id, u.login, t.name
+ 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)
@@ -205,7 +280,7 @@
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_id, u.login, t.name
+ 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)
@@ -224,25 +299,30 @@
} 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
- * @return uPosition[]|bool Array of uPosition positions, false on error
- */
- public static function getAll($userId = NULL, $trackId = NULL) {
- $rules = [];
+ /**
+ * 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 {
@@ -250,7 +330,7 @@
}
$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_id, u.login, t.name
+ 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)
@@ -265,10 +345,53 @@
} 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) {
+ /** @var uUpload $position */
+ foreach ($positions as $position) {
+ try {
+ $query = "UPDATE " . self::db()->table('positions') . "
+ SET image = NULL WHERE id = ?";
+ $stmt = self::db()->prepare($query);
+ $stmt->execute([ $position->id ]);
+ // ignore unlink errors
+ uUpload::delete($position->image);
+ } catch (PDOException $e) {
+ // TODO: handle exception
+ syslog(LOG_ERR, $e->getMessage());
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
/**
* Calculate distance to target point using haversine formula
*
@@ -282,7 +405,7 @@
$lon2 = deg2rad($target->longitude);
$latD = $lat2 - $lat1;
$lonD = $lon2 - $lon1;
- $bearing = 2 * asin(sqrt(pow(sin($latD / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($lonD / 2), 2)));
+ $bearing = 2 * asin(sqrt((sin($latD / 2) ** 2) + cos($lat1) * cos($lat2) * (sin($lonD / 2) ** 2)));
return $bearing * 6371000;
}
@@ -318,7 +441,7 @@
$position->accuracy = $row['accuracy'];
$position->provider = $row['provider'];
$position->comment = $row['comment'];
- $position->imageId = $row['image_id'];
+ $position->image = $row['image'];
$position->isValid = true;
return $position;
}
@@ -346,7 +469,7 @@
$stmt->bindColumn('accuracy', $this->accuracy, PDO::PARAM_INT);
$stmt->bindColumn('provider', $this->provider);
$stmt->bindColumn('comment', $this->comment);
- $stmt->bindColumn('image_id', $this->imageId, PDO::PARAM_INT);
+ $stmt->bindColumn('image', $this->image);
$stmt->bindColumn('login', $this->userLogin);
$stmt->bindColumn('name', $this->trackName);
if ($stmt->fetch(PDO::FETCH_BOUND)) {
diff --git a/helpers/track.php b/helpers/track.php
index e2aecf2..aa19ef1 100644
--- a/helpers/track.php
+++ b/helpers/track.php
@@ -84,7 +84,7 @@
$stmt = self::db()->prepare($query);
$params = [ $userId, $name, $comment ];
$stmt->execute($params);
- $trackId = self::db()->lastInsertId("${table}_id_seq");
+ $trackId = (int) self::db()->lastInsertId("${table}_id_seq");
} catch (PDOException $e) {
// TODO: handle exception
syslog(LOG_ERR, $e->getMessage());
@@ -158,7 +158,7 @@
$ret = false;
if (empty($name)) { $name = $this->name; }
if (is_null($comment)) { $comment = $this->comment; }
- if ($comment == "") { $comment = NULL; }
+ if ($comment === "") { $comment = NULL; }
if ($this->isValid) {
try {
$query = "UPDATE " . self::db()->table('tracks') . " SET name = ?, comment = ? WHERE id = ?";
@@ -184,21 +184,17 @@
*/
public static function deleteAll($userId) {
$ret = false;
- if (!empty($userId)) {
- // remove all positions
- if (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());
- }
+ 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;
}
diff --git a/helpers/upload.php b/helpers/upload.php
new file mode 100644
index 0000000..15a4380
--- /dev/null
+++ b/helpers/upload.php
@@ -0,0 +1,166 @@
+.
+ */
+
+require_once(ROOT_DIR . "/helpers/db.php");
+require_once(ROOT_DIR . "/helpers/utils.php");
+
+/**
+ * Uploaded files
+ */
+class uUpload {
+
+ const META_TYPE = "type";
+ const META_NAME = "name";
+ const META_TMP_NAME = "tmp_name";
+ const META_ERROR = "error";
+ const META_SIZE = "size";
+ public static $uploadDir = ROOT_DIR . "/uploads/";
+ private static $filePattern = "[a-z0-9_.]{20,}";
+ private static $mimeMap = [];
+
+ /**
+ * @return string[] Mime to extension mapping
+ */
+ private static function getMimeMap() {
+ if (empty(self::$mimeMap)) {
+ self::$mimeMap["image/jpeg"] = "jpg";
+ self::$mimeMap["image/x-ms-bmp"] = "bmp";
+ self::$mimeMap["image/gif"] = "gif";
+ self::$mimeMap["image/png"] = "png";
+ }
+ return self::$mimeMap;
+ }
+
+ /**
+ * Is mime accepted type
+ * @param string $mime Mime type
+ * @return bool True if known
+ */
+ private static function isKnownMime($mime) {
+ return array_key_exists($mime, self::getMimeMap());
+ }
+
+ /**
+ * Get file extension for given mime
+ * @param $mime
+ * @return string|null Extension or NULL if not found
+ */
+ private static function getExtension($mime) {
+ if (self::isKnownMime($mime)) {
+ return self::getMimeMap()[$mime];
+ }
+ 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
+ */
+ public static function add($uploaded, $trackId) {
+ try {
+ $fileMeta = self::sanitizeUpload($uploaded);
+ } catch (Exception $e) {
+ syslog(LOG_ERR, $e->getMessage());
+ // save exception to txt file as image replacement?
+ return NULL;
+ }
+
+ $extension = self::getExtension($fileMeta[self::META_TYPE]);
+
+ do {
+ $fileName = uniqid("{$trackId}_") . ".$extension";
+ } while (file_exists(self::$uploadDir . $fileName));
+ if (move_uploaded_file($fileMeta[self::META_TMP_NAME], self::$uploadDir . $fileName)) {
+ return $fileName;
+ }
+ return NULL;
+ }
+
+ /**
+ * Delete upload from database and filesystem
+ * @param String $path File relative path
+ * @return bool False if file exists but can't be unlinked
+ */
+ public static function delete($path) {
+ $ret = true;
+ if (preg_match(self::$filePattern, $path)) {
+ $path = self::$uploadDir . $path;
+ if (file_exists($path)) {
+ $ret = unlink($path);
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * @param array $fileMeta File meta array from $_FILES[]
+ * @param boolean $checkMime Check with known mime types
+ * @return array File metadata array
+ * @throws ErrorException Internal server exception
+ * @throws Exception File upload exception
+ */
+ public static function sanitizeUpload($fileMeta, $checkMime = true) {
+ if (!isset($fileMeta) ||
+ !isset($fileMeta[self::META_NAME]) || !isset($fileMeta[self::META_TYPE]) ||
+ !isset($fileMeta[self::META_SIZE]) || !isset($fileMeta[self::META_TMP_NAME])) {
+ $message = "no uploaded file";
+ $lastErr = error_get_last();
+ if (!empty($lastErr)) {
+ $message = $lastErr["message"];
+ }
+ throw new ErrorException($message);
+ }
+
+ $uploadErrors = [];
+ $uploadErrors[UPLOAD_ERR_INI_SIZE] = "Uploaded file exceeds the upload_max_filesize directive in php.ini";
+ $uploadErrors[UPLOAD_ERR_FORM_SIZE] = "Uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form";
+ $uploadErrors[UPLOAD_ERR_PARTIAL] = "File was only partially uploaded";
+ $uploadErrors[UPLOAD_ERR_NO_FILE] = "No file was uploaded";
+ $uploadErrors[UPLOAD_ERR_NO_TMP_DIR] = "Missing a temporary folder";
+ $uploadErrors[UPLOAD_ERR_CANT_WRITE] = "Failed to write file to disk";
+ $uploadErrors[UPLOAD_ERR_EXTENSION] = "A PHP extension stopped file upload";
+
+ $file = NULL;
+ $fileError = isset($fileMeta[self::META_ERROR]) ? $fileMeta[self::META_ERROR] : UPLOAD_ERR_OK;
+ if ($fileMeta[self::META_SIZE] > uUtils::getUploadMaxSize() && $fileError == UPLOAD_ERR_OK) {
+ $fileError = UPLOAD_ERR_FORM_SIZE;
+ }
+ if ($fileError == UPLOAD_ERR_OK) {
+ $file = $fileMeta[self::META_TMP_NAME];
+ } else {
+ $message = "Unknown error";
+ if (isset($uploadErrors[$fileError])) {
+ $message = $uploadErrors[$fileError];
+ }
+ $message .= " ($fileError)";
+ throw new Exception($message);
+ }
+
+ if (!$file || !file_exists($file)) {
+ throw new ErrorException("File not found");
+ }
+ if ($checkMime && !self::isKnownMime($fileMeta[self::META_TYPE])) {
+ throw new Exception("Unsupported mime type");
+ }
+ return $fileMeta;
+ }
+}
\ No newline at end of file
diff --git a/helpers/user.php b/helpers/user.php
index 1decc2a..7d44d71 100644
--- a/helpers/user.php
+++ b/helpers/user.php
@@ -84,7 +84,7 @@
$query = "INSERT INTO $table (login, password) VALUES (?, ?)";
$stmt = self::db()->prepare($query);
$stmt->execute([ $login, $hash ]);
- $userid = self::db()->lastInsertId("${table}_id_seq");
+ $userid = (int) self::db()->lastInsertId("${table}_id_seq");
} catch (PDOException $e) {
// TODO: handle exception
syslog(LOG_ERR, $e->getMessage());
@@ -140,6 +140,7 @@
$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());
@@ -194,7 +195,7 @@
/**
* Get all users
*
- * @return array|bool Array of uUser users, false on error
+ * @return uUser[]|bool Array of uUser users, false on error
*/
public static function getAll() {
try {
diff --git a/helpers/utils.php b/helpers/utils.php
index fc20279..2524e4c 100644
--- a/helpers/utils.php
+++ b/helpers/utils.php
@@ -32,7 +32,7 @@
$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; }
+ 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; }
@@ -45,10 +45,11 @@
*
* @param string $iniParam Ini parameter name
* @return int Bytes
+ * @noinspection PhpMissingBreakStatementInspection
*/
private static function iniGetBytes($iniParam) {
$iniStr = ini_get($iniParam);
- $val = floatval($iniStr);
+ $val = (float) $iniStr;
$suffix = substr(trim($iniStr), -1);
if (ctype_alpha($suffix)) {
switch (strtolower($suffix)) {
@@ -89,22 +90,17 @@
* @param array|null $extra Optional array of extra parameters
*/
private static function exitWithStatus($isError, $extra = NULL) {
- header("Content-type: text/xml");
- $xml = new XMLWriter();
- $xml->openURI("php://output");
- $xml->startDocument("1.0");
- $xml->setIndent(true);
- $xml->startElement("root");
- $xml->writeElement("error", (int) $isError);
+ $output = [];
+ if ($isError) {
+ $output["error"] = true;
+ }
if (!empty($extra)) {
foreach ($extra as $key => $value) {
- $xml->writeElement($key, $value);
+ $output[$key] = $value;
}
}
-
- $xml->endElement();
- $xml->endDocument();
- $xml->flush();
+ header("Content-type: application/json");
+ echo json_encode($output);
exit;
}
@@ -115,9 +111,9 @@
* @return string URL
*/
public static function getBaseUrl() {
- $proto = (!isset($_SERVER["HTTPS"]) || $_SERVER["HTTPS"] == "" || $_SERVER["HTTPS"] == "off") ? "http://" : "https://";
+ $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") {
+ if (isset($_SERVER["HTTP_X_FORWARDED_PROTO"]) && $_SERVER["HTTP_X_FORWARDED_PROTO"] === "https") {
$proto = "https://";
}
$host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : "";
@@ -165,29 +161,47 @@
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;
+ }
+
+ /**
+ * @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);
- } else {
- return $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);
- } else {
- return self::requestValue($name, $default, $type, FILTER_VALIDATE_INT);
}
+ return self::requestValue($name, $default, $type, FILTER_VALIDATE_INT);
}
private static function requestValue($name, $default, $type, $filters = FILTER_DEFAULT, $flags = NULL) {
$input = filter_input($type, $name, $filters, $flags);
- if ($input !== false && !is_null($input)) {
+ if ($input !== false && $input !== null) {
return $input;
- } else {
- return $default;
}
+ return $default;
}
}
diff --git a/images/bearing.svg b/images/bearing.svg
new file mode 100644
index 0000000..f69d466
--- /dev/null
+++ b/images/bearing.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/distance_blue.svg b/images/distance_blue.svg
index 1a18d95..6fe2624 100644
--- a/images/distance_blue.svg
+++ b/images/distance_blue.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
diff --git a/images/marker-gold.png b/images/marker-gold.png
deleted file mode 100644
index 82dd7c4..0000000
Binary files a/images/marker-gold.png and /dev/null differ
diff --git a/images/marker-green.png b/images/marker-green.png
deleted file mode 100644
index 653fbf4..0000000
Binary files a/images/marker-green.png and /dev/null differ
diff --git a/images/marker-red.png b/images/marker-red.png
deleted file mode 100644
index a20eff5..0000000
Binary files a/images/marker-red.png and /dev/null differ
diff --git a/images/marker-white.png b/images/marker-white.png
deleted file mode 100644
index 6e70481..0000000
Binary files a/images/marker-white.png and /dev/null differ
diff --git a/images/position.svg b/images/position.svg
new file mode 100644
index 0000000..249434f
--- /dev/null
+++ b/images/position.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/speed_blue.svg b/images/speed_blue.svg
index 2440465..4f442ef 100644
--- a/images/speed_blue.svg
+++ b/images/speed_blue.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
diff --git a/images/stats_blue.svg b/images/stats_blue.svg
index 69075ad..26b79cd 100644
--- a/images/stats_blue.svg
+++ b/images/stats_blue.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
diff --git a/images/time_blue.svg b/images/time_blue.svg
index 374ae07..576faa0 100644
--- a/images/time_blue.svg
+++ b/images/time_blue.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
diff --git a/index.php b/index.php
index 92f00fb..e4fd8d5 100644
--- a/index.php
+++ b/index.php
@@ -17,12 +17,12 @@
* 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');
@@ -32,202 +32,127 @@
$langsArr = uLang::getLanguages();
$auth = new uAuth();
- if ($action == "auth") {
+ if ($action === 'auth') {
$auth->checkLogin($login, $pass);
}
- if (!$auth->isAuthenticated() && $action == "auth") {
- $auth->exitWithRedirect("login.php?auth_error=1");
+ if ($action === 'auth' && !$auth->isAuthenticated()) {
+ $auth->exitWithRedirect('login.php?auth_error=1');
}
- if (!$auth->isAuthenticated() && uConfig::$require_authentication) {
- $auth->exitWithRedirect("login.php");
- }
-
-
- $displayUserId = NULL;
- $usersArr = [];
- if ($auth->isAdmin() || uConfig::$public_tracks) {
- // public access or admin user
- // get last position user
- $lastPosition = uPosition::getLast();
- if ($lastPosition->isValid) {
- // display track of last position user
- $displayUserId = $lastPosition->userId;
- }
- // populate users array (for