Skip to content

Commit 8e86ad0

Browse files
author
ted
committed
New features:
- Duplicate a tour in another collection - Allow tour with an unique 360° photo - Local login for dev purpose
1 parent 47b6c71 commit 8e86ad0

32 files changed

+2006
-229
lines changed

.gitignore

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1-
composer.phar
2-
/vendor/
3-
4-
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
5-
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
6-
# composer.lock
1+
cache/*
2+
*.bak
3+
*.log
4+
.env
5+
config.php
6+
/vue-app/node_modules
7+
.DS_Store
8+
.DS_Store?
9+
__MACOSX
10+
._*
11+
.Spotlight-V100
12+
.Trashes
13+
ehthumbs.db
14+
Thumbs.db
15+
.ansible/*

CHANGELOG.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Changelog
2+
3+
## [1.1.0] - 2024-11-29
4+
5+
### Added
6+
7+
- **BREAKING** Allowing the copy of a tour between user acounts. You'll need to update the database schema.
8+
- Creation of a tour with one unique 360° photo
9+
- Dev only feature to create and connect a user without OIDC
10+
11+
## [1.0.0] - 2024-10-10
12+
13+
### Added
14+
15+
- Initial release
16+
- Creation, modification, sharing, export, deletion of a tour
17+
- Addition of point of interest with text and image
18+
- OIDC connection

README.md

-5
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,9 @@
2020

2121
Instructions stands in /doc/installation.md. You will find a docker-compose file to quickly setup a development or production environment.
2222

23-
## Issues
24-
25-
Any issue can be reported at bugs@skilltech.dev
26-
2723
## Licences
2824

2925
Unless otherwise stated, source code found in this repository is distributed under the AGPL v3 licence.
3026
The source code of the viewer that resides in www/v is distributed under the MIT Licence (Expat)
3127

3228
See CREDITS.md for more details about licences and contributors.
33-

docs/installation.md

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# 360.skilltech.tools
22

3+
## Read the changelog
4+
5+
We maintain a CHANGELOG.md file at the root of the source code repository. When an update requires an action like updating the database schema or adding new entries into config.php, we will inform you in this file.
6+
37
## Setup
48

59
To redirect the 360.skilltech.tools domain to your computer, add this line to your hosts file:
@@ -9,7 +13,7 @@ To redirect the 360.skilltech.tools domain to your computer, add this line to yo
913
127.0.0.1 360.skilltech.tools
1014
```
1115

12-
A sample config file is located at src/config.sample.php, copy it to src/config.php and adjust its content to your needs. Especially, you need to fill all the OIDC* constants to allow this app to use an autentication server. You should modify the USER_AGENT value with a unique string representing you so that when the app download an image from the web, the remote web server recognizes you.
16+
A sample config file is located at src/config.sample.php, copy it to src/config.php and adjust its content to your needs. Especially, you need to fill all the OIDC* constants to allow this app to use an autentication server.
1317

1418
## Spin containers
1519

@@ -80,10 +84,11 @@ When it's done you can quit the mariadb shell with the ```exit``` command.
8084

8185
This app needs an OIDC server to authenticate users. To configure the identity provider, copy the file /src/config.sample.php to /src/config.php with appropriate values.
8286

87+
If you don't have yet an identity provider, you may enable the dev mode that allows you to log in without a password, creating a new account if needed. Put the DEV variable to true in /src/config.php then browse to the page /dev/login.php. Obviously, you must never activate this feature in production.
8388

8489
### Install PHP dependencies
8590

86-
We use Composer to manage PHP dependencies. If you don't have it installed on your computer you can install it in a container. To do so, ppen a bash shell in the PHP dev container (it must be running):
91+
We use Composer to manage PHP dependencies. If you don't have it installed on your computer you can install it in a container. To do so, open a bash shell in the PHP dev container (it must be running):
8792

8893
docker exec -ti 360skilltechtools-php-dev-1 bash
8994

@@ -111,9 +116,14 @@ chown -R 33:$USER
111116
chmod -R ug+rw www
112117
```
113118

119+
## Demo tour
120+
121+
When a user enters for the first time in their library the app will ask they to launch an onboarding demo. This will copy a demo tour into the user's collection.
122+
123+
For this option to work you will need to create first a demo tour using the app then copy its ID into /src/config.php.
124+
114125
## Files
115126

116-
Files at the root path https://360.skilltech.tools
117127
All PHP files in /www are served by the nginx server at https://360.skilltech.tools. The PHP source files are in the /src folder as not to be directly accessible by the web server.
118128

119129
## Branches

sql_dump/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# Ignore everything in this directory except this file
22
*
33
!.gitignore
4+
!/create_tables.sql

sql_dump/create_tables.sql

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
--USE tour;
2+
3+
CREATE TABLE IF NOT EXISTS `user` (
4+
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
5+
`email` VARCHAR(255) NOT NULL,
6+
`directory` CHAR(12) UNIQUE NOT NULL,
7+
`onboarding` TINYINT DEFAULT 1,
8+
`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
9+
`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
10+
);
11+
12+
CREATE TABLE IF NOT EXISTS `tour` (
13+
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
14+
`title` VARCHAR(65),
15+
`filename` CHAR(10) UNIQUE NOT NULL,
16+
`user_id` INT NOT NULL,
17+
`description` TEXT(1023) DEFAULT NULL,
18+
`author` VARCHAR(255) DEFAULT NULL,
19+
`license` VARCHAR(16) DEFAULT NULL,
20+
`start_id` INT DEFAULT NULL,
21+
`thumb_id` VARCHAR(255) DEFAULT NULL,
22+
`password` VARCHAR(255) DEFAULT NULL,
23+
`share` BOOLEAN DEFAULT FALSE,
24+
`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
25+
`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
26+
FOREIGN KEY (user_id) REFERENCES user(id)
27+
);
28+
29+
CREATE TABLE IF NOT EXISTS `spot` (
30+
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
31+
`title` VARCHAR(65),
32+
`tour_id` INT NOT NULL,
33+
`lat` FLOAT DEFAULT NULL,
34+
`lng` FLOAT DEFAULT NULL,
35+
`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
36+
`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
37+
FOREIGN KEY (tour_id) REFERENCES tour(id)
38+
);
39+
40+
CREATE TABLE IF NOT EXISTS `spot_has_spot` (
41+
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
42+
`spot1` INT,
43+
`spot2` INT,
44+
`spot1x` INT,
45+
`spot1y` INT,
46+
`spot2x` INT,
47+
`spot2y` INT,
48+
`spot1t` TINYINT NOT NULL DEFAULT 0,
49+
`spot2t` TINYINT NOT NULL DEFAULT 0,
50+
FOREIGN KEY (spot1) REFERENCES spot(id),
51+
FOREIGN KEY (spot2) REFERENCES spot(id)
52+
);
53+
54+
CREATE TABLE IF NOT EXISTS `image` (
55+
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
56+
`user_id` INT NOT NULL,
57+
`src_filename` VARCHAR(255) DEFAULT NULL,
58+
`filename` CHAR(10) NOT NULL,
59+
`filetype` CHAR(3) NOT NULL,
60+
`filesize` INT NOT NULL,
61+
`title` VARCHAR(127),
62+
`width` INT NOT NULL,
63+
`height` INT NOT NULL,
64+
`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
65+
`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
66+
FOREIGN KEY (user_id) REFERENCES user(id)
67+
);
68+
69+
CREATE TABLE IF NOT EXISTS `poi` (
70+
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
71+
`icon` VARCHAR(10) DEFAULT NULL,
72+
`title` VARCHAR(65) DEFAULT NULL,
73+
`text` TEXT DEFAULT NULL,
74+
`image_id` INT DEFAULT NULL,
75+
`spot_id` INT NOT NULL,
76+
`x` INT NOT NULL DEFAULT 0,
77+
`y` INT NOT NULL DEFAULT 0,
78+
`layer` INT NOT NULL DEFAULT 0,
79+
`template` INT NOT NULL DEFAULT 0,
80+
`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
81+
`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
82+
FOREIGN KEY (spot_id) REFERENCES spot(id),
83+
FOREIGN KEY (image_id) REFERENCES image(id)
84+
);
85+
86+
CREATE TABLE IF NOT EXISTS `sky` (
87+
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
88+
`image_id` INT DEFAULT NULL,
89+
`spot_id` INT NOT NULL,
90+
`layer` INT NOT NULL DEFAULT 0,
91+
`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
92+
`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
93+
FOREIGN KEY (spot_id) REFERENCES spot(id),
94+
FOREIGN KEY (image_id) REFERENCES image(id)
95+
);
96+
97+
ALTER TABLE `tour` ADD `password` VARCHAR(255) DEFAULT NULL;
98+
ALTER TABLE `tour` ADD `share` BOOLEAN NOT NULL DEFAULT FALSE;

src/Controller/copyTour.php

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
namespace tour\Controller;
3+
/*
4+
* Copies the requested tour into the user's collection
5+
*/
6+
use \tour\Repository\TourRepository;
7+
require_once(__DIR__ . "/../Autoloader.php");
8+
require_once(__DIR__ . "/../Functions/copyTour.php");
9+
require_once(__DIR__ . "/../config.php");
10+
session_start();
11+
header('Content-Type: application/json; charset=utf-8');
12+
13+
$user = isset($_SESSION["user"]) ? $_SESSION["user"] : null;
14+
$filename = isset($_POST["filename"]) ? $_POST["filename"] : null;
15+
$password = isset($_POST["password"]) ? $_POST["password"] : null;
16+
17+
// Verify the CSRF token
18+
if (!isset($_SESSION['csrf']) || !isset($_POST['csrf']) || $_POST['csrf'] != $_SESSION['csrf']) {
19+
exit("{\"error\": \"Invalid CSRF token\"}");
20+
}
21+
// User must be connected
22+
if ( !$user ) {
23+
exit("{\"error\": \"User not connected\"}");
24+
}
25+
26+
// Check tour ID
27+
if ( !$filename ) {
28+
exit("{\"error\": \"Empty filename\"}");
29+
}
30+
31+
$tourRepo = new TourRepository();
32+
$tour = $tourRepo->findOneBy(["filename" => $filename]);
33+
34+
// Check tour
35+
if ( !$tour ) {
36+
exit("{\"error\": \"Bad tour ID\"}");
37+
}
38+
39+
// Check share option
40+
if ( !$tour->getShare() ) {
41+
exit("{\"error\": \"Not allowed\"}");
42+
}
43+
44+
// Check password
45+
if ( !$tour->verifyPassword($password) ) {
46+
exit("{\"error\": \"Not allowed\"}");
47+
}
48+
49+
$result = copyTour($tour->getID(), $user);
50+
51+
exit("{\"success\": \"Tour copied\", \"id\": \"{$result}\"}");

src/Controller/dev/login.php

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
require_once __DIR__ . "/../../Autoloader.php";
3+
require_once __DIR__ . "/../../config.php";
4+
5+
use \tour\Entity\User;
6+
use \tour\Repository\UserRepository;
7+
session_start();
8+
9+
if (DEV !== true){
10+
exit("Prod");
11+
}
12+
?>
13+
<!DOCTYPE html>
14+
<html lang="en">
15+
<head>
16+
<meta charset="utf-8" />
17+
<title>Login as a developer</title>
18+
<script>
19+
async function fetchGet(url){
20+
var response = new Object();
21+
try {
22+
response = await fetch(url,{
23+
method: "GET",
24+
mode: "cors",
25+
credentials: 'include',
26+
headers: {
27+
"Content-Type": "application/json",
28+
},
29+
});
30+
} catch (error) {
31+
console.log("error Get");
32+
}
33+
return response;
34+
}
35+
36+
async function logout(){
37+
const response = await fetchGet("/logout.php");
38+
window.location = "/";
39+
}
40+
</script>
41+
</head>
42+
<body>
43+
44+
<?php
45+
if (
46+
isset($_POST['email']) && strlen($_POST['email']) >= 4 &&
47+
isset($_POST['csrf']) && $_POST['csrf'] == $_SESSION["csrf"]
48+
){
49+
echo "Connection<br>";
50+
$repo = new UserRepository();
51+
$user = $repo->findByEmail($_POST['email']);
52+
if ($user == null) {
53+
$userInfo = new stdClass();
54+
$userInfo->email = $_POST['email'];
55+
$user = $repo->create($userInfo);
56+
}
57+
$_SESSION["user"] = $user;
58+
} elseif (isset($_POST['email']) && strlen($_POST['email']) < 4){
59+
echo "Bad email<br>";
60+
} elseif (isset($_POST['email']) && (!isset($_POST['csrf']) || $_POST['csrf'] != $_SESSION["csrf"])){
61+
echo "Bad CSRF<br>";
62+
}
63+
64+
$_SESSION["csrf"] = random_int(1,999999);
65+
66+
if(array_key_exists("user", $_SESSION)){
67+
echo "<p>Connected as " . htmlspecialchars($_SESSION["user"]->getEmail()) . " </p>";
68+
echo "<a href='/'>Go to home page</a><br>";
69+
echo "<a href='#' onclick='logout()'>Log out</a>";
70+
}
71+
else {
72+
?>
73+
74+
<form method="post">
75+
<label for="email">Login:</label>
76+
<input type=email id="email" value="demo@example.com" name="email">
77+
<input type="hidden" name="csrf" value="<?= $_SESSION["csrf"] ?>">
78+
<button type="submit">OK</button>
79+
</form>
80+
81+
<?php
82+
}
83+
?>
84+
85+
</body>
86+
</html>

src/Controller/getTour.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,18 @@
1414
$tour = $repo->findOneBy(array('id' => $tourId, 'user_id' => $user->getId()));
1515
$tour = $repo->find($tourId, $user->getId());
1616
if ($tour){
17+
$tour_array = $tour->jsonSerialize();
18+
$tour_array["password"] = $tour_array["password"] === null ? 0 : 1;
1719
//search for the thumbnail
1820
if($tour->getThumbID()){
19-
$tour_array = $tour->jsonSerialize();
2021
$imageRepo = new ImageRepository();
2122
$thumb = $imageRepo->find($tour->getThumbID(), $user->getId());
2223
if ($thumb){
2324
$tour_array["thumb_filename"] = $thumb->getFilename() . "." . $thumb->getFiletype();
2425
}
2526
exit(json_encode($tour_array));
2627
}
27-
exit(json_encode($tour));
28+
exit(json_encode($tour_array));
2829
} else {
2930
exit("{\"error\": \"Tour not found\"}");
3031
}

src/Controller/getTours.php

+6
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@
77
$user = isset($_SESSION["user"]) ? $_SESSION["user"] : null;
88
header('Content-Type: application/json; charset=utf-8');
99

10+
function isPassword($tour){
11+
$tour["password"] = $tour["password"] === null ? false : true;
12+
return $tour;
13+
}
14+
1015
if ($user) {
1116
$repo = new TourRepository();
1217
$tour = $repo->getAllByUserID($user->getId());
18+
$tour = array_map('isPassword', $tour);
1319
echo json_encode($tour);
1420
} else {
1521
echo "{\"error\": \"user not connected\"}";

0 commit comments

Comments
 (0)