mirror of
https://github.com/nextcloud/maps.git
synced 2026-01-12 15:46:09 +00:00
450 lines
13 KiB
PHP
450 lines
13 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Nextcloud - Maps
|
|
*
|
|
* This file is licensed under the Affero General Public License version 3 or
|
|
* later. See the COPYING file.
|
|
*
|
|
* @author Julien Veyssier <eneiluj@posteo.net>
|
|
* @copyright Julien Veyssier 2019
|
|
*/
|
|
|
|
namespace OCA\Maps\Controller;
|
|
|
|
use OCA\Maps\DB\DeviceShareMapper;
|
|
use OCA\Maps\Service\DevicesService;
|
|
use OCP\App\IAppManager;
|
|
use OCP\AppFramework\Controller;
|
|
use OCP\AppFramework\Db\DoesNotExistException;
|
|
use OCP\AppFramework\Http;
|
|
use OCP\AppFramework\Http\DataResponse;
|
|
use OCP\Files\IRootFolder;
|
|
use OCP\Files\NotFoundException;
|
|
use OCP\Files\NotPermittedException;
|
|
use OCP\IConfig;
|
|
use OCP\IDateTimeZone;
|
|
use OCP\IGroupManager;
|
|
use OCP\IL10N;
|
|
use OCP\IRequest;
|
|
use OCP\IServerContainer;
|
|
use OCP\IUserManager;
|
|
use OCP\Share\IManager;
|
|
|
|
//use function \OCA\Maps\Service\endswith;
|
|
|
|
class DevicesController extends Controller {
|
|
|
|
private $userId;
|
|
private $userfolder;
|
|
private $config;
|
|
private $appVersion;
|
|
private $shareManager;
|
|
private $userManager;
|
|
private $groupManager;
|
|
private $dbtype;
|
|
private $dbdblquotes;
|
|
private $defaultDeviceId;
|
|
private $l;
|
|
private $devicesService;
|
|
private $deviceShareMapper;
|
|
private $dateTimeZone;
|
|
private $root;
|
|
protected $appName;
|
|
|
|
public function __construct($AppName,
|
|
IRequest $request,
|
|
IServerContainer $serverContainer,
|
|
IConfig $config,
|
|
IManager $shareManager,
|
|
IAppManager $appManager,
|
|
IUserManager $userManager,
|
|
IGroupManager $groupManager,
|
|
IL10N $l,
|
|
DevicesService $devicesService,
|
|
DeviceShareMapper $deviceShareMapper,
|
|
IDateTimeZone $dateTimeZone,
|
|
IRootFolder $root,
|
|
$UserId) {
|
|
parent::__construct($AppName, $request);
|
|
$this->devicesService = $devicesService;
|
|
$this->deviceShareMapper = $deviceShareMapper;
|
|
$this->dateTimeZone = $dateTimeZone;
|
|
$this->appName = $AppName;
|
|
$this->appVersion = $config->getAppValue('maps', 'installed_version');
|
|
$this->userId = $UserId;
|
|
$this->userManager = $userManager;
|
|
$this->groupManager = $groupManager;
|
|
$this->l = $l;
|
|
$this->root = $root;
|
|
$this->dbtype = $config->getSystemValue('dbtype');
|
|
// IConfig object
|
|
$this->config = $config;
|
|
if ($UserId !== '' and $UserId !== null and $serverContainer !== null) {
|
|
// path of user files folder relative to DATA folder
|
|
$this->userfolder = $serverContainer->getUserFolder($UserId);
|
|
}
|
|
$this->shareManager = $shareManager;
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param ?string[] $tokens
|
|
* @param ?int $myMapId
|
|
* @return DataResponse
|
|
*/
|
|
public function getDevices($tokens = null, $myMapId = null): DataResponse {
|
|
if (is_null($tokens)) {
|
|
$tokens = [];
|
|
}
|
|
if (is_null($myMapId)) {
|
|
$devices = $this->devicesService->getDevicesFromDB($this->userId);
|
|
$deviceIds = array_column($devices, 'id');
|
|
$shares = $this->deviceShareMapper->findByDeviceIds($deviceIds);
|
|
foreach ($shares as $s) {
|
|
$devices[$s->getDeviceId()]['shares'][] = $s;
|
|
}
|
|
} else {
|
|
$devices = [];
|
|
$userFolder = $this->root->getUserFolder($this->userId);
|
|
$folders = $userFolder->getById($myMapId);
|
|
$folder = array_shift($folders);
|
|
if (is_null($folder)) {
|
|
return new DataResponse($this->l->t('Map not Found'), 404);
|
|
}
|
|
$shares = $this->devicesService->getSharedDevicesFromFolder($folder);
|
|
$st = array_column($shares, 'token');
|
|
$tokens = array_merge($tokens, $st);
|
|
}
|
|
$td = $this->devicesService->getDevicesByTokens($tokens);
|
|
$devices = $td + $devices;
|
|
return new DataResponse(array_values($devices));
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param string[] $tokens
|
|
* @return DataResponse
|
|
*/
|
|
public function getDevicesByTokens(array $tokens): DataResponse {
|
|
$devices = $this->devicesService->getDevicesByTokens($tokens);
|
|
return new DataResponse(array_values($devices));
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param $id
|
|
* @param int $pruneBefore
|
|
* @return DataResponse
|
|
*/
|
|
public function getDevicePoints($id, ?int $pruneBefore = 0, ?int $limit = 10000, ?int $offset = 0, ?array $tokens = null): DataResponse {
|
|
if (is_null($tokens)) {
|
|
$points = $this->devicesService->getDevicePointsFromDB($this->userId, $id, $pruneBefore, $limit, $offset);
|
|
} else {
|
|
$points = $this->devicesService->getDevicePointsByTokens($tokens, $pruneBefore, $limit, $offset);
|
|
}
|
|
return new DataResponse($points);
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param $lat
|
|
* @param $lng
|
|
* @param null $timestamp
|
|
* @param null $user_agent
|
|
* @param null $altitude
|
|
* @param null $battery
|
|
* @param null $accuracy
|
|
* @return DataResponse
|
|
*/
|
|
public function addDevicePoint($lat, $lng, $timestamp = null, $user_agent = null, $altitude = null, $battery = null, $accuracy = null): DataResponse {
|
|
if (is_numeric($lat) and is_numeric($lng)) {
|
|
$ts = $timestamp;
|
|
if ($timestamp === null) {
|
|
$ts = (new \DateTime())->getTimestamp();
|
|
}
|
|
$ua = $user_agent;
|
|
if ($user_agent === null) {
|
|
$ua = $_SERVER['HTTP_USER_AGENT'];
|
|
}
|
|
$deviceId = $this->devicesService->getOrCreateDeviceFromDB($this->userId, $ua);
|
|
$pointId = $this->devicesService->addPointToDB($deviceId, $lat, $lng, $ts, $altitude, $battery, $accuracy);
|
|
return new DataResponse([
|
|
'deviceId' => $deviceId,
|
|
'pointId' => $pointId
|
|
]);
|
|
} else {
|
|
return new DataResponse('Invalid values', 400);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param $id
|
|
* @param $color
|
|
* @param $name
|
|
* @return DataResponse
|
|
*/
|
|
public function editDevice($id, $color, $name): DataResponse {
|
|
$device = $this->devicesService->getDeviceFromDB($id, $this->userId);
|
|
if ($device !== null) {
|
|
if ((is_string($color) && strlen($color) > 0) ||
|
|
(is_string($name) && strlen($name) > 0)
|
|
) {
|
|
$this->devicesService->editDeviceInDB($id, $color, $name);
|
|
$editedDevice = $this->devicesService->getDeviceFromDB($id, $this->userId);
|
|
return new DataResponse($editedDevice);
|
|
} else {
|
|
return new DataResponse($this->l->t('Invalid values'), 400);
|
|
}
|
|
} else {
|
|
return new DataResponse($this->l->t('No such device'), 400);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param $id
|
|
* @return DataResponse
|
|
*/
|
|
public function deleteDevice($id): DataResponse {
|
|
$device = $this->devicesService->getDeviceFromDB($id, $this->userId);
|
|
if ($device !== null) {
|
|
$this->devicesService->deleteDeviceFromDB($id);
|
|
$this->deviceShareMapper->removeAllByDeviceId($id);
|
|
return new DataResponse('DELETED');
|
|
} else {
|
|
return new DataResponse($this->l->t('No such device'), 400);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param ?array $deviceIdList
|
|
* @param int $begin
|
|
* @param int $end
|
|
* @param bool $all=false
|
|
* @throws \OCP\Files\NotFoundException
|
|
* @throws \OCP\Files\NotPermittedException
|
|
*/
|
|
public function exportDevices($deviceIdList, $begin, $end, bool $all = false): DataResponse {
|
|
// sorry about ugly deviceIdList management:
|
|
// when an empty list is passed in http request, we get null here
|
|
if ($deviceIdList === null or (is_array($deviceIdList) and count($deviceIdList) === 0)) {
|
|
return new DataResponse($this->l->t('No device to export'), 400);
|
|
}
|
|
|
|
// create /Maps directory if necessary
|
|
$userFolder = $this->userfolder;
|
|
if (!$userFolder->nodeExists('/Maps')) {
|
|
$userFolder->newFolder('Maps');
|
|
}
|
|
if ($userFolder->nodeExists('/Maps')) {
|
|
$mapsFolder = $userFolder->get('/Maps');
|
|
if ($mapsFolder->getType() !== \OCP\Files\FileInfo::TYPE_FOLDER) {
|
|
return new DataResponse($this->l->t('/Maps is not a directory'), 400);
|
|
} elseif (!$mapsFolder->isCreatable()) {
|
|
return new DataResponse($this->l->t('/Maps directory is not writeable'), 400);
|
|
}
|
|
} else {
|
|
return new DataResponse($this->l->t('Impossible to create /Maps directory'), 400);
|
|
}
|
|
|
|
$nbDevices = $this->devicesService->countPoints($this->userId, $deviceIdList, $begin, $end);
|
|
if ($nbDevices === 0) {
|
|
return new DataResponse($this->l->t('Nothing to export'), 400);
|
|
}
|
|
|
|
// generate export file name
|
|
$prefix = $all ? '' : 'filtered-';
|
|
$tz = $this->dateTimeZone->getTimeZone();
|
|
$now = new \DateTime('now', $tz);
|
|
$dateStr = $now->format('Y-m-d H:i:s (P)');
|
|
$filename = $dateStr . ' ' . $prefix . 'devices.gpx';
|
|
|
|
if ($mapsFolder->nodeExists($filename)) {
|
|
$mapsFolder->get($filename)->delete();
|
|
}
|
|
$file = $mapsFolder->newFile($filename);
|
|
$handler = $file->fopen('w');
|
|
|
|
$this->devicesService->exportDevices($this->userId, $handler, $deviceIdList, $begin, $end, $this->appVersion, $filename);
|
|
|
|
fclose($handler);
|
|
$file->touch();
|
|
return new DataResponse('/Maps/' . $filename);
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param $path
|
|
* @return DataResponse
|
|
* @throws \OCP\Files\InvalidPathException
|
|
* @throws \OCP\Files\NotFoundException
|
|
*/
|
|
public function importDevices($path): DataResponse {
|
|
$userFolder = $this->userfolder;
|
|
$cleanpath = str_replace(['../', '..\\'], '', $path);
|
|
|
|
if ($userFolder->nodeExists($cleanpath)) {
|
|
$file = $userFolder->get($cleanpath);
|
|
if ($file->getType() === \OCP\Files\FileInfo::TYPE_FILE and
|
|
$file->isReadable()) {
|
|
$lowerFileName = strtolower($file->getName());
|
|
if ($this->endsWith($lowerFileName, '.gpx') or $this->endsWith($lowerFileName, '.kml') or $this->endsWith($lowerFileName, '.kmz')) {
|
|
$nbImported = $this->devicesService->importDevices($this->userId, $file);
|
|
return new DataResponse($nbImported);
|
|
} else {
|
|
// invalid extension
|
|
return new DataResponse($this->l->t('Invalid file extension'), 400);
|
|
}
|
|
} else {
|
|
// directory or not readable
|
|
return new DataResponse($this->l->t('Impossible to read the file'), 400);
|
|
}
|
|
} else {
|
|
// does not exist
|
|
return new DataResponse($this->l->t('File does not exist'), 400);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $string
|
|
* @param $test
|
|
* @return bool
|
|
*/
|
|
private function endsWith($string, $test): bool {
|
|
$strlen = strlen($string);
|
|
$testlen = strlen($test);
|
|
if ($testlen > $strlen) {
|
|
return false;
|
|
}
|
|
return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0;
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param int|null $myMapId
|
|
* @return DataResponse
|
|
* @throws \OCP\Files\NotPermittedException
|
|
* @throws \OC\User\NoUserException
|
|
*/
|
|
public function getSharedDevices(?int $myMapId = null): DataResponse {
|
|
if (is_null($myMapId) || $myMapId === '') {
|
|
$sharedDevices = [];
|
|
} else {
|
|
$folders = $this->userfolder->getById($myMapId);
|
|
$folder = array_shift($folders);
|
|
$sharedDevices = $this->devicesService->getSharedDevicesFromFolder($folder);
|
|
}
|
|
|
|
return new DataResponse($sharedDevices);
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param int $id
|
|
* @param int $timestampFrom
|
|
* @param int $timestampTo
|
|
* @return DataResponse
|
|
*/
|
|
public function shareDevice(int $id, int $timestampFrom, int $timestampTo): DataResponse {
|
|
$device = $this->devicesService->getDeviceFromDB($id, $this->userId);
|
|
if ($device !== null) {
|
|
$share = $this->deviceShareMapper->create($id, $timestampFrom, $timestampTo);
|
|
|
|
if ($share === null) {
|
|
return new DataResponse($this->l->t('Error sharing device'), Http::STATUS_INTERNAL_SERVER_ERROR);
|
|
}
|
|
} else {
|
|
return new DataResponse($this->l->t('No such device'), 400);
|
|
}
|
|
|
|
return new DataResponse($share);
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param int $token
|
|
* @return DataResponse
|
|
* @throws NotPermittedException
|
|
* @throws NotFoundException
|
|
*/
|
|
public function removeDeviceShare(int $token): DataResponse {
|
|
try {
|
|
$share = $this->deviceShareMapper->findByToken($token);
|
|
} catch (DoesNotExistException $e) {
|
|
throw new NotFoundException();
|
|
}
|
|
$device = $this->devicesService->getDeviceFromDB($share->getDeviceId(), $this->userId);
|
|
if ($device !== null) {
|
|
return new DataResponse($this->deviceShareMapper->removeById($share->getId()));
|
|
} else {
|
|
throw new NotFoundException();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @param string $token
|
|
* @param $targetMapId
|
|
* @return DataResponse
|
|
* @throws NotFoundException
|
|
*/
|
|
public function addSharedDeviceToMap(string $token, $targetMapId): DataResponse {
|
|
try {
|
|
$share = $this->deviceShareMapper->findByToken($token);
|
|
} catch (DoesNotExistException $e) {
|
|
return new DataResponse($this->l->t('Share not Found'), 404);
|
|
}
|
|
$folders = $this->userfolder->getById($targetMapId);
|
|
$folder = array_shift($folders);
|
|
if (is_null($folder)) {
|
|
return new DataResponse($this->l->t('Map not Found'), 404);
|
|
}
|
|
try {
|
|
$file = $folder->get('.device_shares.json');
|
|
} catch (\OCP\Files\NotFoundException $e) {
|
|
$file = $folder->newFile('.device_shares.json', $content = '[]');
|
|
}
|
|
$data = json_decode($file->getContent(), true);
|
|
foreach ($data as $s) {
|
|
if ($s->token == $share->getToken()) {
|
|
return new DataResponse($this->l->t('Share was already on map'));
|
|
}
|
|
}
|
|
$data[] = $share;
|
|
$file->putContent(json_encode($data, JSON_PRETTY_PRINT));
|
|
return new DataResponse('Done');
|
|
}
|
|
|
|
public function removeSharedDeviceFromMap(string $token, int $myMapId): DataResponse {
|
|
$folders = $this->userfolder->getById($myMapId);
|
|
$folder = array_shift($folders);
|
|
if (is_null($folder)) {
|
|
return new DataResponse($this->l->t('Map not Found'), 404);
|
|
}
|
|
try {
|
|
$file = $folder->get('.device_shares.json');
|
|
} catch (\OCP\Files\NotFoundException $e) {
|
|
$file = $folder->newFile('.device_shares.json', $content = '[]');
|
|
}
|
|
$data = json_decode($file->getContent(), true);
|
|
$shares = [];
|
|
$deleted = null;
|
|
foreach ($data as $share) {
|
|
$t = $share['token'];
|
|
if ($t === $token) {
|
|
$deleted = $share;
|
|
} else {
|
|
$shares[] = $share;
|
|
}
|
|
}
|
|
$file->putContent(json_encode($shares, JSON_PRETTY_PRINT));
|
|
if (is_null($deleted)) {
|
|
return new DataResponse('Failed', 500);
|
|
}
|
|
return new DataResponse('Done');
|
|
}
|
|
}
|