diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index d38968ea7..442e021fb 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -56,7 +56,7 @@ jobs: run: composer require --dev roave/security-advisories:dev-latest - name: Install nextcloud/ocp - run: composer require --dev nextcloud/ocp:dev-${{ steps.versions.outputs.branches-max }} --ignore-platform-reqs --with-dependencies + run: composer require --dev nextcloud/ocp:dev-${{ steps.versions.outputs.branches-min }} --ignore-platform-reqs --with-dependencies - name: Run coding standards check run: composer run psalm -- --threads=1 --monochrome --no-progress --output-format=github diff --git a/composer.json b/composer.json index a5a544a34..f2b3fbc18 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "test:integration:dev": "phpunit -c tests/phpunit.integration.xml --no-coverage --order-by=defects --stop-on-defect --fail-on-warning --stop-on-error --stop-on-failure", "test:unit": "phpunit -c tests/phpunit.unit.xml --fail-on-warning", "test:unit:dev": "phpunit -c tests/phpunit.unit.xml --no-coverage --order-by=defects --stop-on-defect --fail-on-warning --stop-on-error --stop-on-failure", + "rector": "rector && composer cs:fix", "post-install-cmd": [ "[ $COMPOSER_DEV_MODE -eq 0 ] || composer bin all install --ansi" ], diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 8e84b47e4..a0e323869 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -1,5 +1,7 @@ , Vinzenz Rosenkranz * @copyright Sander Brand 2014, Vinzenz Rosenkranz 2016, 2017 */ - namespace OCA\Maps\AppInfo; use OCA\DAV\Events\CardCreatedEvent; @@ -23,15 +24,12 @@ use OCA\Maps\Listener\CardUpdatedListener; use OCA\Maps\Listener\LoadAdditionalScriptsListener; use OCA\Maps\Listener\LoadSidebarListener; -use OCA\Maps\Service\PhotofilesService; -use OCA\Maps\Service\TracksService; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Http\EmptyFeaturePolicy; use OCP\EventDispatcher\IEventDispatcher; -use OCP\IServerContainer; use OCP\Security\FeaturePolicy\AddFeaturePolicyEvent; class Application extends App implements IBootstrap { @@ -71,28 +69,18 @@ public function register(IRegistrationContext $context): void { } public function boot(IBootContext $context): void { - // ... boot logic goes here ... - $context->getAppContainer()->registerService('FileHooks', function ($c) { - return new FileHooks( - $c->query(IServerContainer::class)->getRootFolder(), - \OCP\Server::get(PhotofilesService::class), - \OCP\Server::get(TracksService::class), - $c->query('AppName'), - $c->query(IServerContainer::class)->getLockingProvider() - ); - }); - - $context->getAppContainer()->query('FileHooks')->register(); + $context->getAppContainer()->get(FileHooks::class)->register(); $this->registerFeaturePolicy(); } - private function registerFeaturePolicy() { - $dispatcher = $this->getContainer()->getServer()->get(IEventDispatcher::class); + private function registerFeaturePolicy(): void { + $dispatcher = $this->getContainer()->get(IEventDispatcher::class); - $dispatcher->addListener(AddFeaturePolicyEvent::class, function (AddFeaturePolicyEvent $e) { + $dispatcher->addListener(AddFeaturePolicyEvent::class, function (AddFeaturePolicyEvent $e): void { $fp = new EmptyFeaturePolicy(); - $fp->addAllowedGeoLocationDomain('\'self\''); + $fp->addAllowedGeoLocationDomain("'self'"); + $e->addPolicy($fp); }); } diff --git a/lib/BackgroundJob/AddPhotoJob.php b/lib/BackgroundJob/AddPhotoJob.php index aed0cb5c6..e99d71dad 100644 --- a/lib/BackgroundJob/AddPhotoJob.php +++ b/lib/BackgroundJob/AddPhotoJob.php @@ -1,5 +1,7 @@ photofilesService = $photofilesService; $this->root = $root; $this->cacheFactory = $cacheFactory; $this->backgroundJobCache = $this->cacheFactory->createDistributed('maps:background-jobs'); } - public function run($argument) { + public function run($argument): void { $userFolder = $this->root->getUserFolder($argument['userId']); $files = $userFolder->getById($argument['photoId']); if (empty($files)) { return; } + $file = array_shift($files); $this->photofilesService->addPhotoNow($file, $argument['userId']); diff --git a/lib/BackgroundJob/LaunchUsersInstallScanJob.php b/lib/BackgroundJob/LaunchUsersInstallScanJob.php index 2edb7a293..21bb3ebc2 100644 --- a/lib/BackgroundJob/LaunchUsersInstallScanJob.php +++ b/lib/BackgroundJob/LaunchUsersInstallScanJob.php @@ -1,5 +1,7 @@ debug('Launch users install scan jobs cronjob executed'); - $this->userManager->callForSeenUsers(function (IUser $user) { + public function run($argument): void { + Server::get(LoggerInterface::class)->debug('Launch users install scan jobs cronjob executed'); + $this->userManager->callForSeenUsers(function (IUser $user): void { $this->jobList->add(UserInstallScanJob::class, ['userId' => $user->getUID()]); }); } diff --git a/lib/BackgroundJob/LookupMissingGeoJob.php b/lib/BackgroundJob/LookupMissingGeoJob.php index 4c2f171da..93827d1cc 100644 --- a/lib/BackgroundJob/LookupMissingGeoJob.php +++ b/lib/BackgroundJob/LookupMissingGeoJob.php @@ -1,5 +1,7 @@ debug('Maps address lookup cronjob executed'); + public function run($argument): void { + Server::get(LoggerInterface::class)->debug('Maps address lookup cronjob executed'); // lookup at most 200 addresses if (!$this->addressService->lookupMissingGeo(200)) { // if not all addresses where looked up successfully add a new job for next time diff --git a/lib/BackgroundJob/UpdatePhotoByFileJob.php b/lib/BackgroundJob/UpdatePhotoByFileJob.php index c4e72db14..030b793b5 100644 --- a/lib/BackgroundJob/UpdatePhotoByFileJob.php +++ b/lib/BackgroundJob/UpdatePhotoByFileJob.php @@ -1,5 +1,7 @@ photofilesService = $photofilesService; - $this->root = $root; - $this->cacheFactory = $cacheFactory; $this->backgroundJobCache = $this->cacheFactory->createDistributed('maps:background-jobs'); } - public function run($argument) { + public function run($argument): void { $userFolder = $this->root->getUserFolder($argument['userId']); - $files = $userFolder->getById($argument['fileId']); - if (empty($files)) { + $file = $userFolder->getFirstNodeById($argument['fileId']); + if (!$file instanceof File) { return; } - $file = array_shift($files); + $this->photofilesService->updateByFileNow($file); $counter = $this->backgroundJobCache->get('recentlyUpdated:' . $argument['userId']) ?? 0; diff --git a/lib/BackgroundJob/UserInstallScanJob.php b/lib/BackgroundJob/UserInstallScanJob.php index 609aae551..04e698307 100644 --- a/lib/BackgroundJob/UserInstallScanJob.php +++ b/lib/BackgroundJob/UserInstallScanJob.php @@ -1,5 +1,7 @@ config = $config; - $this->jobList = $jobList; - $this->userManager = $userManager; - $this->photofilesService = $photofilesService; - $this->tracksService = $tracksService; } - public function run($argument) { + public function run($argument): void { $userId = $argument['userId']; - \OCP\Server::get(LoggerInterface::class)->debug('Launch user install scan job for ' . $userId . ' cronjob executed'); + Server::get(LoggerInterface::class)->debug('Launch user install scan job for ' . $userId . ' cronjob executed'); // scan photos and tracks for given user $this->rescanUserPhotos($userId); $this->rescanUserTracks($userId); $this->config->setUserValue($userId, 'maps', 'installScanDone', 'yes'); } - private function rescanUserPhotos($userId) { + private function rescanUserPhotos($userId): void { //$this->output->info('======== User '.$userId.' ========'."\n"); $c = 1; foreach ($this->photofilesService->rescan($userId) as $path) { //$this->output->info('['.$c.'] Photo "'.$path.'" added'."\n"); - $c++; + ++$c; } } - private function rescanUserTracks($userId) { + private function rescanUserTracks($userId): void { //$this->output->info('======== User '.$userId.' ========'."\n"); $c = 1; foreach ($this->tracksService->rescan($userId) as $path) { //$this->output->info('['.$c.'] Track "'.$path.'" added'."\n"); - $c++; + ++$c; } } diff --git a/lib/Command/RegisterMimetypes.php b/lib/Command/RegisterMimetypes.php index dcb8434dd..20a5160c1 100644 --- a/lib/Command/RegisterMimetypes.php +++ b/lib/Command/RegisterMimetypes.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\Command; use OCA\Maps\Service\MimetypeService; @@ -19,11 +20,10 @@ class RegisterMimetypes extends Command { - protected MimetypeService $mimetypeService; - - public function __construct(MimetypeService $mimetypeService) { + public function __construct( + protected MimetypeService $mimetypeService, + ) { parent::__construct(); - $this->mimetypeService = $mimetypeService; } /** @@ -34,11 +34,6 @@ protected function configure() { ->setDescription('Registers the maps mimetypes for existing and new files.'); } - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return int - */ protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Register mimetypes for existing files'); $this->mimetypeService->registerForExistingFiles(); diff --git a/lib/Command/RescanPhotos.php b/lib/Command/RescanPhotos.php index 5595fb3a7..35c18408d 100644 --- a/lib/Command/RescanPhotos.php +++ b/lib/Command/RescanPhotos.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\Command; use OCA\Maps\Service\PhotofilesService; @@ -27,20 +28,23 @@ class RescanPhotos extends Command { protected IUserManager $userManager; + protected OutputInterface $output; + protected IManager $encryptionManager; - protected PhotofilesService $photofilesService; + + protected IConfig $config; public function __construct( IUserManager $userManager, IManager $encryptionManager, - PhotofilesService $photofilesService, - IConfig $config) { + protected PhotofilesService $photofilesService, + IConfig $config, + ) { parent::__construct(); $this->userManager = $userManager; $this->encryptionManager = $encryptionManager; - $this->photofilesService = $photofilesService; $this->config = $config; } @@ -58,7 +62,7 @@ protected function configure() { ->addArgument( 'path', InputArgument::OPTIONAL, - 'Scan photos GPS exif data for the given path under user\'s files without wiping the database.' + "Scan photos GPS exif data for the given path under user's files without wiping the database." ) ->addOption( 'now', @@ -68,16 +72,12 @@ protected function configure() { ); } - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return int - */ protected function execute(InputInterface $input, OutputInterface $output): int { if ($this->encryptionManager->isEnabled()) { $output->writeln('Encryption is enabled. Aborted.'); return 1; } + $this->output = $output; $userId = $input->getArgument('user_id'); $pathToScan = $input->getArgument('path'); @@ -85,8 +85,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($inBackground) { echo "Extracting coordinates from photo is performed in a BackgroundJob \n"; } + if ($userId === null) { - $this->userManager->callForSeenUsers(function (IUser $user, ?string $pathToScan = null) use ($inBackground) { + $this->userManager->callForSeenUsers(function (IUser $user, ?string $pathToScan = null) use ($inBackground): void { $this->rescanUserPhotos($user->getUID(), $inBackground, $pathToScan); }); } else { @@ -95,23 +96,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->rescanUserPhotos($userId, $inBackground, $pathToScan); } } + return 0; } /** - * @param string $userId - * @param bool $inBackground * @param string $pathToScan - * @return void * @throws \OCP\PreConditionNotMetException */ - private function rescanUserPhotos(string $userId, bool $inBackground = true, ?string $pathToScan = null) { + private function rescanUserPhotos(string $userId, bool $inBackground = true, ?string $pathToScan = null): void { echo '======== User ' . $userId . ' ========' . "\n"; $c = 1; foreach ($this->photofilesService->rescan($userId, $inBackground, $pathToScan) as $path) { echo '[' . $c . '] Photo "' . $path . '" added' . "\n"; - $c++; + ++$c; } + if ($pathToScan === null) { $this->config->setUserValue($userId, 'maps', 'installScanDone', 'yes'); } diff --git a/lib/Command/RescanTracks.php b/lib/Command/RescanTracks.php index 9435481d5..be208e413 100644 --- a/lib/Command/RescanTracks.php +++ b/lib/Command/RescanTracks.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Command; use OCA\Maps\Service\TracksService; @@ -26,21 +27,26 @@ class RescanTracks extends Command { protected IUserManager $userManager; + protected OutputInterface $output; + protected IManager $encryptionManager; - protected TracksService $tracksService; + + protected IConfig $config; - public function __construct(IUserManager $userManager, + public function __construct( + IUserManager $userManager, IManager $encryptionManager, - TracksService $tracksService, - IConfig $config) { + protected TracksService $tracksService, + IConfig $config, + ) { parent::__construct(); $this->userManager = $userManager; $this->encryptionManager = $encryptionManager; - $this->tracksService = $tracksService; $this->config = $config; } + protected function configure() { $this->setName('maps:scan-tracks') ->setDescription('Rescan track files') @@ -56,10 +62,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln('Encryption is enabled. Aborted.'); return 1; } + $this->output = $output; $userId = $input->getArgument('user_id'); if ($userId === null) { - $this->userManager->callForSeenUsers(function (IUser $user) { + $this->userManager->callForSeenUsers(function (IUser $user): void { $this->rescanUserTracks($user->getUID()); }); } else { @@ -68,16 +75,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->rescanUserTracks($userId); } } + return 0; } - private function rescanUserTracks($userId) { + private function rescanUserTracks(string $userId): void { echo '======== User ' . $userId . ' ========' . "\n"; $c = 1; foreach ($this->tracksService->rescan($userId) as $path) { echo '[' . $c . '] Track "' . $path . '" added' . "\n"; - $c++; + ++$c; } + $this->config->setUserValue($userId, 'maps', 'installScanDone', 'yes'); } } diff --git a/lib/Controller/ContactsController.php b/lib/Controller/ContactsController.php index 4916b79f6..544f9c2c4 100644 --- a/lib/Controller/ContactsController.php +++ b/lib/Controller/ContactsController.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; +use OC\User\NoUserException; use OCA\DAV\CardDAV\CardDavBackend; use OCA\Maps\Service\AddressService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; use OCP\Contacts\IManager; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\File; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; @@ -26,73 +32,48 @@ use OCP\IDBConnection; use OCP\IRequest; use OCP\IURLGenerator; +use Sabre\VObject\Document; use Sabre\VObject\Property\Text; use Sabre\VObject\Reader; class ContactsController extends Controller { - private $userId; - private $contactsManager; - private $addressService; - private $dbconnection; - private $cdBackend; - private $avatarManager; - private $root; - private $urlGenerator; - private $geoDistanceMax; // Max distance in meters to consider that 2 addresses are the same location + private int $geoDistanceMax = 5; // Max distance in meters to consider that 2 addresses are the same location - /** - * @param $AppName - * @param IRequest $request - * @param IDBConnection $dbconnection - * @param IManager $contactsManager - * @param AddressService $addressService - * @param $UserId - * @param CardDavBackend $cdBackend - * @param IAvatarManager $avatarManager - * @param IRootFolder $root - */ public function __construct( - $AppName, + string $appName, IRequest $request, - IDBConnection $dbconnection, - IManager $contactsManager, - AddressService $addressService, - $UserId, - CardDavBackend $cdBackend, - IAvatarManager $avatarManager, - IRootFolder $root, - IURLGenerator $urlGenerator) { - parent::__construct($AppName, $request); - $this->userId = $UserId; - $this->avatarManager = $avatarManager; - $this->contactsManager = $contactsManager; - $this->addressService = $addressService; - $this->dbconnection = $dbconnection; - $this->cdBackend = $cdBackend; - $this->root = $root; - $this->urlGenerator = $urlGenerator; - $this->geoDistanceMax = 5; + private readonly IDBConnection $dbconnection, + private readonly IManager $contactsManager, + private readonly AddressService $addressService, + private readonly string $userId, + private readonly CardDavBackend $cdBackend, + private readonly IAvatarManager $avatarManager, + private readonly IRootFolder $root, + private readonly IURLGenerator $urlGenerator, + ) { + parent::__construct($appName, $request); } + /** * Converts a geo string as a float array - * @param string formatted as "lat;lon" + * @param string $geo formatted as "lat;lon" * @return float[] array containing [lat;lon] */ - private function geoAsFloatArray($geo) { - $res = array_map(function ($value) {return floatval($value);}, explode(';', $geo)); - return $res; + private function geoAsFloatArray(string $geo): array { + return array_map(floatval(...), explode(';', $geo)); } /** * check if geographical address is duplicated - * @param array containing contact's previous different addresses - * @param contact's address to check + * @param list $prevGeo containing contact's previous different addresses + * @param float[] $geo contact's address to check * @return integer : -1 if address is new, index of duplicated address in other cases */ - private function isNewAddress($prevGeo, $geo) { - if (empty($geo)) { // Address not converted to geo coords + private function isNewAddress(array $prevGeo, array $geo): int { + if ($geo === []) { // Address not converted to geo coords return -1; } + $result = -1; $counter = 0; foreach ($prevGeo as $prev) { @@ -100,46 +81,46 @@ private function isNewAddress($prevGeo, $geo) { $result = $counter; break; } - $counter++; + + ++$counter; } + return $result; } /** * Get distance between two geo points. * - * @param array $coordsA GPS coordinates of first point - * @param array $coordsB GPS coordinates of second point + * @param array $coordsA GPS coordinates of first point + * @param array $coordsB GPS coordinates of second point * @return float Distance in meters between these two points */ - private function getDistance(array $coordsA, array $coordsB) { - if (empty($coordsA) || empty($coordsB)) { + private function getDistance(array $coordsA, array $coordsB): float { + if ($coordsA === [] || $coordsB === []) { return 9E999; } + $latA = deg2rad($coordsA[0]); $lonA = deg2rad($coordsA[1]); $latB = deg2rad($coordsB[0]); $lonB = deg2rad($coordsB[1]); $earthRadius = 6378137; // in m - $dlon = ($lonB - $lonA) / 2; - $dlat = ($latB - $latA) / 2; - $a = (sin($dlat) * sin($dlat)) + cos($latA) * cos($latB) * (sin($dlon) * sin($dlon - )); - $d = 2 * atan2(sqrt($a), sqrt(1 - $a)); + $dlon = ($lonB - $lonA) / 2.0; + $dlat = ($latB - $latA) / 2.0; + $a = (sin($dlat) * sin($dlat)) + cos($latA) * cos($latB) * (sin($dlon) * sin($dlon)); + $d = 2.0 * atan2(sqrt($a), sqrt(1.0 - $a)); return $d * $earthRadius; } /** - * get contacts with coordinates + * Get contacts with coordinates * - * @NoAdminRequired - * @param null $myMapId - * @return DataResponse * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ - public function getContacts($myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + #[NoAdminRequired] + public function getContacts(?int $myMapId = null): DataResponse { + if (is_null($myMapId)) { $contacts = $this->contactsManager->search('', ['GEO', 'ADR'], ['types' => false]); $addressBooks = $this->contactsManager->getUserAddressBooks(); $result = []; @@ -147,16 +128,14 @@ public function getContacts($myMapId = null): DataResponse { foreach ($contacts as $c) { $addressBookUri = $addressBooks[$c['addressbook-key']]->getUri(); - $uid = trim($c['UID']); + $uid = trim((string)$c['UID']); $url = $this->directUrlToContact($uid, $addressBookUri); // we don't give users, just contacts - if (strcmp($c['URI'], 'Database:' . $c['UID'] . '.vcf') !== 0 - and strcmp($uid, $userid) !== 0 - ) { + if (strcmp((string)$c['URI'], 'Database:' . $c['UID'] . '.vcf') !== 0 && strcmp($uid, $userid) !== 0) { // if the contact has a geo attibute use it - if (key_exists('GEO', $c)) { + if (array_key_exists('GEO', $c)) { $geo = $c['GEO']; if (is_string($geo) && strlen($geo) > 1) { $result[] = [ @@ -196,6 +175,7 @@ public function getContacts($myMapId = null): DataResponse { } } } + // anyway try to get it from the address $card = $this->cdBackend->getContact($c['addressbook-key'], $c['URI']); if ($card) { @@ -211,10 +191,11 @@ public function getContacts($myMapId = null): DataResponse { if (isset($adr->parameters()['TYPE'])) { $adrtype = $adr->parameters()['TYPE']->getValue(); } - if (is_string($geo) && strlen($geo) > 1) { + + if (strlen($geo) > 1) { if ($duplicatedIndex < 0) { - array_push($prevGeo, $geof); - array_push($prevRes, count($result)); // Add index of new item so that we can update the ADRTYPE in case of duplicate address + $prevGeo[] = $geof; + $prevRes[] = count($result); // Add index of new item so that we can update the ADRTYPE in case of duplicate address $result[] = [ 'FN' => $c['FN'] ?? $this->N2FN($c['N']) ?? '???', 'URI' => $c['URI'], @@ -232,7 +213,7 @@ public function getContacts($myMapId = null): DataResponse { ]; } else { // Concatenate AddressType to the corresponding record - array_push($result[$prevRes[$duplicatedIndex]]['ADRTYPE'], $adrtype); + $result[$prevRes[$duplicatedIndex]]['ADRTYPE'][] = $adrtype; $result[$prevRes[$duplicatedIndex]]['isUpdateable'] = false; $result[$prevRes[$duplicatedIndex]]['isDeletable'] = false; $result[$prevRes[$duplicatedIndex]]['isShareable'] = false; @@ -243,80 +224,80 @@ public function getContacts($myMapId = null): DataResponse { } } } + return new DataResponse($result); - } else { - //Fixme add contacts for my-maps - $result = []; - $userFolder = $this->root->getUserFolder($this->userId); - $folders = $userFolder->getById($myMapId); - if (empty($folders)) { - return new DataResponse($result); - } - $folder = array_shift($folders); - if ($folder === null) { - return new DataResponse($result); + } + + //Fixme add contacts for my-maps + $result = []; + $userFolder = $this->root->getUserFolder($this->userId); + $folder = $userFolder->getFirstNodeById($myMapId); + if (!($folder instanceof Folder)) { + return new DataResponse($result); + } + + $files = $folder->search('.vcf'); + foreach ($files as $file) { + if (!($file instanceof File)) { + continue; } - $files = $folder->search('.vcf'); - foreach ($files as $file) { - // $cards = explode("END:VCARD\r\n", $file->getContent()); - $cards = [$file->getContent()]; - foreach ($cards as $card) { - $vcard = Reader::read($card . "END:VCARD\r\n"); - if (isset($vcard->GEO)) { - $geo = $vcard->GEO; - if (is_string($geo) && strlen($geo->getValue()) > 1) { - $result[] = $this->vCardToArray($file, $vcard, $geo->getValue()); - } elseif (is_countable($geo) && count($geo) > 0 && is_iterable($geo)) { - $prevGeo = ''; - foreach ($geo as $g) { - if (strcmp($prevGeo, $g->getValue()) != 0) { - $prevGeo = $g->getValue(); - if (strlen($g->getValue()) > 1) { - $result[] = $this->vCardToArray($file, $vcard, $g->getValue()); - } + + // $cards = explode("END:VCARD\r\n", $file->getContent()); + $cards = [$file->getContent()]; + foreach ($cards as $card) { + $vcard = Reader::read($card . "END:VCARD\r\n"); + if (isset($vcard->GEO)) { + $geo = $vcard->GEO; + if (is_string($geo) && strlen($geo->getValue()) > 1) { + $result[] = $this->vCardToArray($file, $vcard, $geo->getValue()); + } elseif (is_countable($geo) && count($geo) > 0 && is_iterable($geo)) { + $prevGeo = ''; + foreach ($geo as $g) { + if (strcmp((string)$prevGeo, (string)$g->getValue()) !== 0) { + $prevGeo = $g->getValue(); + if (strlen((string)$g->getValue()) > 1) { + $result[] = $this->vCardToArray($file, $vcard, $g->getValue()); } } } } - if (isset($vcard->ADR) && count($vcard->ADR) > 0) { - $prevGeo = []; - $prevRes = []; - foreach ($vcard->ADR as $adr) { - $geo = $this->addressService->addressToGeo($adr->getValue(), $file->getId()); - $geof = $this->geoAsFloatArray($geo); - $duplicatedIndex = $this->isNewAddress($prevGeo, $geof); - //var_dump($adr->parameters()['TYPE']->getValue()); - $adrtype = ''; - if (isset($adr->parameters()['TYPE'])) { - $adrtype = $adr->parameters()['TYPE']->getValue(); - } - if (is_string($geo) && strlen($geo) > 1) { - if ($duplicatedIndex < 0) { - array_push($prevGeo, $geof); - array_push($prevRes, count($result)); // Add index of new item so that we can update the ADRTYPE in case of duplicate address - $result[] = $this->vCardToArray($file, $vcard, $geo, $adrtype, $adr->getValue(), $file->getId()); - } else { - // Concatenate AddressType to the corresponding record - array_push($result[$prevRes[$duplicatedIndex]]['ADRTYPE'], $adrtype); - $result[$prevRes[$duplicatedIndex]]['isUpdateable'] = false; - $result[$prevRes[$duplicatedIndex]]['isDeletable'] = false; - $result[$prevRes[$duplicatedIndex]]['isShareable'] = false; - } + } + + if (isset($vcard->ADR) && count($vcard->ADR) > 0) { + $prevGeo = []; + $prevRes = []; + foreach ($vcard->ADR as $adr) { + $geo = $this->addressService->addressToGeo($adr->getValue(), $file->getId()); + $geof = $this->geoAsFloatArray($geo); + $duplicatedIndex = $this->isNewAddress($prevGeo, $geof); + //var_dump($adr->parameters()['TYPE']->getValue()); + $adrtype = ''; + if (isset($adr->parameters()['TYPE'])) { + $adrtype = $adr->parameters()['TYPE']->getValue(); + } + + if (strlen($geo) > 1) { + if ($duplicatedIndex < 0) { + $prevGeo[] = $geof; + $prevRes[] = count($result); // Add index of new item so that we can update the ADRTYPE in case of duplicate address + $result[] = $this->vCardToArray($file, $vcard, $geo, $adrtype, $adr->getValue(), $file->getId()); + } else { + // Concatenate AddressType to the corresponding record + $result[$prevRes[$duplicatedIndex]]['ADRTYPE'][] = $adrtype; + $result[$prevRes[$duplicatedIndex]]['isUpdateable'] = false; + $result[$prevRes[$duplicatedIndex]]['isDeletable'] = false; + $result[$prevRes[$duplicatedIndex]]['isShareable'] = false; } } } } } - return new DataResponse($result); } + + return new DataResponse($result); } - /** - * @param $contactUid - * @param $addressBookUri - * @return string - */ - private function directUrlToContact($contactUid, $addressBookUri) { + private function directUrlToContact(string $contactUid, string $addressBookUri): string { return $this->urlGenerator->getAbsoluteURL( $this->urlGenerator->linkToRoute('contacts.contacts.direct', [ 'contact' => $contactUid . '~' . $addressBookUri @@ -325,17 +306,11 @@ private function directUrlToContact($contactUid, $addressBookUri) { } /** - * @param Node $file - * @param \Sabre\VObject\Document $vcard - * @param string $geo - * @param string|null $adrtype - * @param string|null $adr - * @param int|null $fileId - * @return array * @throws NotFoundException * @throws \OCP\Files\InvalidPathException + * @return array */ - private function vCardToArray(Node $file, \Sabre\VObject\Document $vcard, string $geo, ?string $adrtype = null, ?string $adr = null, ?int $fileId = null): array { + private function vCardToArray(Node $file, Document $vcard, string $geo, ?string $adrtype = null, ?string $adr = null, ?int $fileId = null): array { $FNArray = $vcard->FN ? $vcard->FN->getJsonValue() : []; $fn = array_shift($FNArray); $NArray = $vcard->N ? $vcard->N->getJsonValue() : []; @@ -348,15 +323,12 @@ private function vCardToArray(Node $file, \Sabre\VObject\Document $vcard, string } } + $UIDArray = $vcard->UID->getJsonValue(); $uid = array_shift($UIDArray); $groups = $vcard->CATEGORIES; - if (!is_null($groups)) { - $groups = $groups->getValue(); - } else { - $groups = ''; - } - $result = [ + $groups = is_null($groups) ? '' : $groups->getValue(); + return [ 'FN' => $fn ?? $n ?? '???', 'UID' => $uid, 'HAS_PHOTO' => (isset($vcard->PHOTO) && $vcard->PHOTO !== null), @@ -369,33 +341,25 @@ private function vCardToArray(Node $file, \Sabre\VObject\Document $vcard, string 'isDeletable' => $file->isDeletable(), 'isUpdateable' => $file->isUpdateable(), ]; - return $result; } - /** - * @param string $n - * @return string|null - */ private function N2FN(string $n): ?string { - if ($n) { + if ($n !== '' && $n !== '0') { $spl = explode($n, ';'); if (count($spl) >= 4) { return $spl[3] . ' ' . $spl[1] . ' ' . $spl[0]; - } else { - return null; } - } else { + return null; } + + return null; } /** - * get all contacts - * - * @NoAdminRequired - * @param string $query - * @return DataResponse + * Get all contacts */ + #[NoAdminRequired] public function searchContacts(string $query = ''): DataResponse { $contacts = $this->contactsManager->search($query, ['FN'], ['types' => false]); $booksReadOnly = $this->getAddressBooksReadOnly(); @@ -403,11 +367,9 @@ public function searchContacts(string $query = ''): DataResponse { $result = []; $userid = trim($this->userId); foreach ($contacts as $c) { - $uid = trim($c['UID']); + $uid = trim((string)$c['UID']); // we don't give users, just contacts - if (strcmp($c['URI'], 'Database:' . $c['UID'] . '.vcf') !== 0 - and strcmp($uid, $userid) !== 0 - ) { + if (strcmp((string)$c['URI'], 'Database:' . $c['UID'] . '.vcf') !== 0 && strcmp($uid, $userid) !== 0) { $addressBookUri = $addressBooks[$c['addressbook-key']]->getUri(); $result[] = [ 'FN' => $c['FN'] ?? $this->N2FN($c['N']) ?? '???', @@ -421,255 +383,210 @@ public function searchContacts(string $query = ''): DataResponse { ]; } } + return new DataResponse($result); } /** - * @NoAdminRequired - * @param string $bookid - * @param string $uri - * @param string $uid - * @param float|null $lat - * @param float|null $lng - * @param string $attraction - * @param string $house_number - * @param string $road - * @param string $postcode - * @param string $city - * @param string $state - * @param string $country - * @param string $type - * @param string|null $address_string - * @param int|null $fileId - * @param int|null $myMapId - * @return DataResponse * @throws \OCP\DB\Exception * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ - public function placeContact( - string $bookid, - string $uri, - string $uid, - ?float $lat, - ?float $lng, - string $attraction = '', - string $house_number = '', - string $road = '', - string $postcode = '', - string $city = '', - string $state = '', - string $country = '', - string $type = '', - ?string $address_string = null, - ?int $fileId = null, - ?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + #[NoAdminRequired] + public function placeContact(int $bookid, string $uri, string $uid, ?float $lat, ?float $lng, string $attraction = '', string $house_number = '', string $road = '', string $postcode = '', string $city = '', string $state = '', string $country = '', string $type = '', ?string $address_string = null, ?int $fileId = null, ?int $myMapId = null): DataResponse { + if (is_null($myMapId)) { // do not edit 'user' contact even myself - if (strcmp($uri, 'Database:' . $uid . '.vcf') === 0 - or strcmp($uid, $this->userId) === 0 - ) { - return new DataResponse('Can\'t edit users', 400); - } else { - // check addressbook permissions - if (!$this->addressBookIsReadOnly($bookid)) { - if ($lat !== null && $lng !== null) { - // we set the geo tag - if (!$attraction && !$house_number && !$road && !$postcode && !$city && !$state && !$country && !$address_string) { - $result = $this->contactsManager->createOrUpdate(['URI' => $uri, 'GEO' => $lat . ';' . $lng], $bookid); - } - // we set the address - elseif (!$address_string) { - $street = trim($attraction . ' ' . $house_number . ' ' . $road); - $stringAddress = ';;' . $street . ';' . $city . ';' . $state . ';' . $postcode . ';' . $country; - // set the coordinates in the DB - $lat = floatval($lat); - $lng = floatval($lng); - $this->setAddressCoordinates($lat, $lng, $stringAddress, $uri); - // set the address in the vcard - $card = $this->cdBackend->getContact($bookid, $uri); - if ($card) { - $vcard = Reader::read($card['carddata']); - ; - $vcard->add(new Text($vcard, 'ADR', ['', '', $street, $city, $state, $postcode, $country], ['TYPE' => $type])); - $result = $this->cdBackend->updateCard($bookid, $uri, $vcard->serialize()); - } - } else { - $card = $this->cdBackend->getContact($bookid, $uri); - if ($card) { - $vcard = Reader::read($card['carddata']); - ; - $vcard->add(new Text($vcard, 'ADR', explode(';', $address_string), ['TYPE' => $type])); - $result = $this->cdBackend->updateCard($bookid, $uri, $vcard->serialize()); - } - } - } else { - // TODO find out how to remove a property - // following does not work properly - $result = $this->contactsManager->createOrUpdate(['URI' => $uri, 'GEO' => null], $bookid); - } - return new DataResponse('EDITED'); - } else { - return new DataResponse('READONLY', 400); - } - } - } else { - $userFolder = $this->root->getUserFolder($this->userId); - $folders = $userFolder->getById($myMapId); - if (empty($folders)) { - return new DataResponse('MAP NOT FOUND', 404); - } - $mapsFolder = array_shift($folders); - if (is_null($mapsFolder)) { - return new DataResponse('MAP NOT FOUND', 404); + if (strcmp($uri, 'Database:' . $uid . '.vcf') === 0 || strcmp($uid, $this->userId) === 0) { + return new DataResponse("Can't edit users", 400); } - if (is_null($fileId)) { - $card = $this->cdBackend->getContact($bookid, $uri); - try { - $file = $mapsFolder->get($uri); - } catch (NotFoundException $e) { - if (!$mapsFolder->isCreatable()) { - return new DataResponse('CONTACT NOT WRITABLE', 400); - } - $file = $mapsFolder->newFile($uri); - } - } else { - $files = $mapsFolder->getById($fileId); - if (empty($files)) { - return new DataResponse('CONTACT NOT FOUND', 404); - } - $file = array_shift($files); - if (is_null($file)) { - return new DataResponse('CONTACT NOT FOUND', 404); - } - $card = $file->getContent(); - } - if (!$file->isUpdateable()) { - return new DataResponse('CONTACT NOT WRITABLE', 400); - } - if ($card) { - $vcard = Reader::read($card['carddata']); + + // do not edit 'user' contact even myself + if (!$this->addressBookIsReadOnly($bookid)) { if ($lat !== null && $lng !== null) { + // we set the geo tag if (!$attraction && !$house_number && !$road && !$postcode && !$city && !$state && !$country && !$address_string) { - $vcard->add('GEO', $lat . ';' . $lng); - } elseif (!$address_string) { + $result = $this->contactsManager->createOrUpdate(['URI' => $uri, 'GEO' => $lat . ';' . $lng], (string)$bookid); + } + // we set the address + elseif (!$address_string) { $street = trim($attraction . ' ' . $house_number . ' ' . $road); $stringAddress = ';;' . $street . ';' . $city . ';' . $state . ';' . $postcode . ';' . $country; // set the coordinates in the DB $lat = floatval($lat); $lng = floatval($lng); $this->setAddressCoordinates($lat, $lng, $stringAddress, $uri); - $vcard = Reader::read($card['carddata']); - $vcard->add('ADR', ['', '', $street, $city, $state, $postcode, $country], ['TYPE' => $type]); + // set the address in the vcard + $card = $this->cdBackend->getContact($bookid, $uri); + if ($card) { + $vcard = Reader::read($card['carddata']); + ; + $vcard->add(new Text($vcard, 'ADR', ['', '', $street, $city, $state, $postcode, $country], ['TYPE' => $type])); + $result = $this->cdBackend->updateCard($bookid, $uri, $vcard->serialize()); + } } else { - $stringAddress = $address_string; - // set the coordinates in the DB - $lat = floatval($lat); - $lng = floatval($lng); - $this->setAddressCoordinates($lat, $lng, $stringAddress, $uri); - $vcard = Reader::read($card['carddata']); - $vcard->add('ADR', explode(';', $address_string), ['TYPE' => $type]); + $card = $this->cdBackend->getContact($bookid, $uri); + if ($card) { + $vcard = Reader::read($card['carddata']); + ; + $vcard->add(new Text($vcard, 'ADR', explode(';', $address_string), ['TYPE' => $type])); + $result = $this->cdBackend->updateCard($bookid, $uri, $vcard->serialize()); + } } } else { - $vcard->remove('GEO'); + // TODO find out how to remove a property + // following does not work properly + $result = $this->contactsManager->createOrUpdate(['URI' => $uri, 'GEO' => null], (string)$bookid); } - $file->putContent($vcard->serialize()); + + // check addressbook permissions return new DataResponse('EDITED'); } - return new DataResponse('CONTACT NOT FOUND', 404); + + return new DataResponse('READONLY', 400); + } + + $userFolder = $this->root->getUserFolder($this->userId); + $mapsFolder = $userFolder->getFirstNodeById($myMapId); + if (!($mapsFolder instanceof Folder)) { + return new DataResponse('MAP NOT FOUND', 404); + } + + if (is_null($fileId)) { + $card = $this->cdBackend->getContact($bookid, $uri); + try { + /** @var File $file */ + $file = $mapsFolder->get($uri); + } catch (NotFoundException) { + if (!$mapsFolder->isCreatable()) { + return new DataResponse('CONTACT NOT WRITABLE', 400); + } + + $file = $mapsFolder->newFile($uri); + } + } else { + $file = $mapsFolder->getFirstNodeById($fileId); + if (!($file instanceof File)) { + return new DataResponse('CONTACT NOT FOUND', 404); + } + + $card = $file->getContent(); } + if (!$file->isUpdateable()) { + return new DataResponse('CONTACT NOT WRITABLE', 400); + } + + if ($card) { + $vcard = Reader::read($card['carddata']); + if ($lat !== null && $lng !== null) { + if (!$attraction && !$house_number && !$road && !$postcode && !$city && !$state && !$country && !$address_string) { + $vcard->add('GEO', $lat . ';' . $lng); + } elseif (!$address_string) { + $street = trim($attraction . ' ' . $house_number . ' ' . $road); + $stringAddress = ';;' . $street . ';' . $city . ';' . $state . ';' . $postcode . ';' . $country; + // set the coordinates in the DB + $lat = floatval($lat); + $lng = floatval($lng); + $this->setAddressCoordinates($lat, $lng, $stringAddress, $uri); + $vcard = Reader::read($card['carddata']); + $vcard->add('ADR', ['', '', $street, $city, $state, $postcode, $country], ['TYPE' => $type]); + } else { + $stringAddress = $address_string; + // set the coordinates in the DB + $lat = floatval($lat); + $lng = floatval($lng); + $this->setAddressCoordinates($lat, $lng, $stringAddress, $uri); + $vcard = Reader::read($card['carddata']); + $vcard->add('ADR', explode(';', $address_string), ['TYPE' => $type]); + } + } else { + $vcard->remove('GEO'); + } + + $file->putContent($vcard->serialize()); + return new DataResponse('EDITED'); + } + + return new DataResponse('CONTACT NOT FOUND', 404); } /** - * @NoAdminRequired - * @param string $bookid - * @param string $uri - * @param int $myMapId - * @param int|null $fileId - * @return DataResponse|void * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ - public function addContactToMap(string $bookid, string $uri, int $myMapId, ?int $fileId = null): DataResponse { + #[NoAdminRequired] + public function addContactToMap(int $bookid, string $uri, int $myMapId, ?int $fileId = null): DataResponse { $userFolder = $this->root->getUserFolder($this->userId); - $folders = $userFolder->getById($myMapId); - if (empty($folders)) { - return new DataResponse('MAP NOT FOUND', 404); - } - $mapsFolder = array_shift($folders); - if (is_null($mapsFolder)) { + $mapsFolder = $userFolder->getFirstNodeById($myMapId); + if (!($mapsFolder instanceof Folder)) { return new DataResponse('MAP NOT FOUND', 404); } + if (is_null($fileId)) { $card = $this->cdBackend->getContact($bookid, $uri); try { + /** @var File $file */ $file = $mapsFolder->get($uri); - } catch (NotFoundException $e) { + } catch (NotFoundException) { if (!$mapsFolder->isCreatable()) { return new DataResponse('CONTACT NOT WRITABLE', 400); } + $file = $mapsFolder->newFile($uri); } } else { - $files = $mapsFolder->getById($fileId); - if (empty($files)) { - return new DataResponse('CONTACT NOT FOUND', 404); - } - $file = array_shift($files); - if (is_null($file)) { + $file = $mapsFolder->getFirstNodeById($fileId); + if (!($file instanceof File)) { return new DataResponse('CONTACT NOT FOUND', 404); } + $card = $file->getContent(); } + if (!$file->isUpdateable()) { return new DataResponse('CONTACT NOT WRITABLE', 400); } + if ($card) { $vcard = Reader::read($card['carddata']); $file->putContent($vcard->serialize()); return new DataResponse('DONE'); } + + return new DataResponse('CONTACT NOT FOUND', 404); } - /** - * @param string $bookid - * @return bool - */ - private function addressBookIsReadOnly(string $bookid): bool { + private function addressBookIsReadOnly(int $bookid): bool { $userBooks = $this->cdBackend->getAddressBooksForUser('principals/users/' . $this->userId); foreach ($userBooks as $book) { - if ($book['id'] === (int)$bookid) { - return (isset($book['{http://owncloud.org/ns}read-only']) and $book['{http://owncloud.org/ns}read-only']); + if ($book['id'] === $bookid) { + return (isset($book['{http://owncloud.org/ns}read-only']) && $book['{http://owncloud.org/ns}read-only']); } } + return true; } /** - * @return array + * @return array */ private function getAddressBooksReadOnly(): array { $booksReadOnly = []; $userBooks = $this->cdBackend->getAddressBooksForUser('principals/users/' . $this->userId); foreach ($userBooks as $book) { - $ro = (isset($book['{http://owncloud.org/ns}read-only']) and $book['{http://owncloud.org/ns}read-only']); + $ro = (isset($book['{http://owncloud.org/ns}read-only']) && $book['{http://owncloud.org/ns}read-only']); $booksReadOnly[$book['id']] = $ro; } + return $booksReadOnly; } /** - * @param float $lat - * @param float $lng - * @param string $adr - * @param string $uri - * @return void * @throws \OCP\DB\Exception */ private function setAddressCoordinates(float $lat, float $lng, string $adr, string $uri): void { $qb = $this->dbconnection->getQueryBuilder(); - $adr_norm = strtolower(preg_replace('/\s+/', '', $adr)); + $adr_norm = strtolower((string)preg_replace('/\s+/', '', $adr)); $qb->select('id') ->from('maps_address_geo') @@ -679,7 +596,7 @@ private function setAddressCoordinates(float $lat, float $lng, string $adr, stri $result = $req->fetchAll(); $req->closeCursor(); $qb = $this->dbconnection->getQueryBuilder(); - if ($result and count($result) > 0) { + if (count($result) > 0) { $id = $result[0]['id']; $qb->update('maps_address_geo') ->set('lat', $qb->createNamedParameter($lat, IQueryBuilder::PARAM_STR)) @@ -704,15 +621,13 @@ private function setAddressCoordinates(float $lat, float $lng, string $adr, stri /** - * get contacts with coordinates + * Get contacts with coordinates * - * @NoAdminRequired - * @NoCSRFRequired - * @param string $name - * @return DataDisplayResponse * @throws NotFoundException * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] + #[NoCSRFRequired] public function getContactLetterAvatar(string $name): DataDisplayResponse { $av = $this->avatarManager->getGuestAvatar($name); $avatarContent = $av->getFile(64)->getContent(); @@ -720,20 +635,10 @@ public function getContactLetterAvatar(string $name): DataDisplayResponse { } /** - * removes the address from the vcard - * and delete corresponding entry in the DB - * - * @NoAdminRequired - * @param string $bookid - * @param string $uri - * @param string $uid - * @param string $adr - * @param string $geo - * @param ?int $fileId - * @param ?int $myMapId - * @return DataResponse + * Removes the address from the vcard and delete corresponding entry in the DB. */ - public function deleteContactAddress($bookid, $uri, $uid, $adr, $geo, $fileId = null, $myMapId = null): DataResponse { + #[NoAdminRequired] + public function deleteContactAddress(int $bookid, string $uri, string $uid, string $adr, string $geo, ?int $fileId = null, ?int $myMapId = null): DataResponse { // vcard $card = $this->cdBackend->getContact($bookid, $uri); @@ -756,14 +661,15 @@ public function deleteContactAddress($bookid, $uri, $uid, $adr, $geo, $fileId = } } } + $this->cdBackend->updateCard($bookid, $uri, $vcard->serialize()); // no need to cleanup db here, it will be done when catching vcard change hook return new DataResponse('DELETED'); - } else { - return new DataResponse('READONLY', 400); } - } else { - return new DataResponse('FAILED', 400); + + return new DataResponse('READONLY', 400); } + + return new DataResponse('FAILED', 400); } } diff --git a/lib/Controller/DevicesApiController.php b/lib/Controller/DevicesApiController.php index f860c7155..2f61c071d 100644 --- a/lib/Controller/DevicesApiController.php +++ b/lib/Controller/DevicesApiController.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\Service\DevicesService; -use OCP\App\IAppManager; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; -use OCP\IConfig; -use OCP\IGroupManager; use OCP\IL10N; use OCP\IRequest; -use OCP\IServerContainer; -use OCP\IUserManager; -use OCP\Share\IManager; class DevicesApiController extends ApiController { - - private $userId; - private $userfolder; - private $config; - private $appVersion; - private $shareManager; - private $userManager; - private $groupManager; - private $dbtype; - private $dbdblquotes; - private $defaultDeviceId; - private $l; - private $devicesService; - protected $appName; - - public function __construct($AppName, + public function __construct( + string $appName, IRequest $request, - IServerContainer $serverContainer, - IConfig $config, - IManager $shareManager, - IAppManager $appManager, - IUserManager $userManager, - IGroupManager $groupManager, - IL10N $l, - DevicesService $devicesService, - $UserId) { - parent::__construct($AppName, $request, + private readonly IL10N $l, + private readonly DevicesService $devicesService, + private readonly ?string $userId, + ) { + parent::__construct($appName, $request, 'PUT, POST, GET, DELETE, PATCH, OPTIONS', 'Authorization, Content-Type, Accept', 1728000); - $this->devicesService = $devicesService; - $this->appName = $AppName; - $this->appVersion = $config->getAppValue('maps', 'installed_version'); - $this->userId = $UserId; - $this->userManager = $userManager; - $this->groupManager = $groupManager; - $this->l = $l; - $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 - * @NoCSRFRequired - * @CORS * @param $apiversion - * @return DataResponse */ + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] public function getDevices($apiversion): DataResponse { $now = new \DateTime(); @@ -89,28 +52,21 @@ public function getDevices($apiversion): DataResponse { if ($this->request->getHeader('If-None-Match') === '"' . $etag . '"') { return new DataResponse([], Http::STATUS_NOT_MODIFIED); } + return (new DataResponse($devices)) ->setLastModified($now) ->setETag($etag); } - /** - * @NoAdminRequired - * @NoCSRFRequired - * @CORS - * @param $id - * @param int $pruneBefore - * @return DataResponse - */ - public function getDevicePoints($id, int $pruneBefore = 0): DataResponse { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function getDevicePoints(int $id, int $pruneBefore = 0): DataResponse { $points = $this->devicesService->getDevicePointsFromDB($this->userId, $id, $pruneBefore); return new DataResponse($points); } /** - * @NoAdminRequired - * @NoCSRFRequired - * @CORS * @param $apiversion * @param $lat * @param $lng @@ -119,10 +75,12 @@ public function getDevicePoints($id, int $pruneBefore = 0): DataResponse { * @param $altitude * @param $battery * @param $accuracy - * @return DataResponse */ + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] public function addDevicePoint($apiversion, $lat, $lng, $timestamp = null, $user_agent = null, $altitude = null, $battery = null, $accuracy = null): DataResponse { - if (is_numeric($lat) and is_numeric($lng)) { + if (is_numeric($lat) && is_numeric($lng)) { $timestamp = $this->normalizeOptionalNumber($timestamp); $altitude = $this->normalizeOptionalNumber($altitude); $battery = $this->normalizeOptionalNumber($battery); @@ -131,59 +89,56 @@ public function addDevicePoint($apiversion, $lat, $lng, $timestamp = null, $user 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($this->l->t('Invalid values'), 400); } + + return new DataResponse($this->l->t('Invalid values'), 400); } /** - * @NoAdminRequired - * @NoCSRFRequired - * @CORS * @param $id * @param $color - * @return DataResponse */ - public function editDevice($id, $color): DataResponse { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function editDevice(int $id, $color): DataResponse { $device = $this->devicesService->getDeviceFromDB($id, $this->userId); if ($device !== null) { - if (is_string($color) && strlen($color) > 0) { + if (is_string($color) && $color !== '') { $this->devicesService->editDeviceInDB($id, $color, null); $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); + + return new DataResponse($this->l->t('Invalid values'), 400); } + + return new DataResponse($this->l->t('No such device'), 400); } - /** - * @NoAdminRequired - * @NoCSRFRequired - * @CORS - * @param $id - * @return DataResponse - */ - public function deleteDevice($id): DataResponse { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function deleteDevice(int $id): DataResponse { $device = $this->devicesService->getDeviceFromDB($id, $this->userId); if ($device !== null) { $this->devicesService->deleteDeviceFromDB($id); return new DataResponse('DELETED'); - } else { - return new DataResponse($this->l->t('No such device'), 400); } + + return new DataResponse($this->l->t('No such device'), 400); } /** @@ -194,6 +149,7 @@ private function normalizeOptionalNumber($value) { if (!is_numeric($value)) { return null; } + return $value; } diff --git a/lib/Controller/DevicesController.php b/lib/Controller/DevicesController.php index c199d4d6c..1dd379ae5 100644 --- a/lib/Controller/DevicesController.php +++ b/lib/Controller/DevicesController.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; +use OC\User\NoUserException; 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\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; +use OCP\Files\File; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; -use OCP\IConfig; +use OCP\IAppConfig; 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 ?Folder $userFolder = null; + + private readonly string $appVersion; - 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, + public function __construct( + string $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) { + IAppConfig $appConfig, + private readonly IL10N $l, + private readonly DevicesService $devicesService, + private readonly DeviceShareMapper $deviceShareMapper, + private readonly IDateTimeZone $dateTimeZone, + private readonly IRootFolder $root, + private readonly ?string $userId, + ) { + parent::__construct($appName, $request); + $this->appVersion = $appConfig->getValueString('maps', 'installed_version'); + if ($userId !== '' && $userId !== null) { // path of user files folder relative to DATA folder - $this->userfolder = $serverContainer->getUserFolder($UserId); + $this->userFolder = $this->root->getUserFolder($userId); } - $this->shareManager = $shareManager; } /** - * @NoAdminRequired * @param ?string[] $tokens - * @param ?int $myMapId - * @return DataResponse */ - public function getDevices($tokens = null, $myMapId = null): DataResponse { + #[NoAdminRequired] + public function getDevices(?array $tokens = null, ?int $myMapId = null): DataResponse { if (is_null($tokens)) { $tokens = []; } + if (is_null($myMapId)) { $devices = $this->devicesService->getDevicesFromDB($this->userId); $deviceIds = array_column($devices, 'id'); @@ -107,143 +74,125 @@ public function getDevices($tokens = null, $myMapId = null): DataResponse { } else { $devices = []; $userFolder = $this->root->getUserFolder($this->userId); - $folders = $userFolder->getById($myMapId); - $folder = array_shift($folders); - if (is_null($folder)) { + $folder = $userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof 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 */ + #[NoAdminRequired] 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 { + #[NoAdminRequired] + public function getDevicePoints(int $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; + #[NoAdminRequired] + public function addDevicePoint($lat, $lng, ?int $timestamp = null, ?string $user_agent = null, $altitude = null, $battery = null, $accuracy = null): DataResponse { + if (is_numeric($lat) && is_numeric($lng)) { if ($timestamp === null) { - $ts = (new \DateTime())->getTimestamp(); + $timestamp = (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); + $pointId = $this->devicesService->addPointToDB($deviceId, $lat, $lng, $timestamp, $altitude, $battery, $accuracy); return new DataResponse([ 'deviceId' => $deviceId, 'pointId' => $pointId ]); - } else { - return new DataResponse('Invalid values', 400); } + + return new DataResponse('Invalid values', 400); } - /** - * @NoAdminRequired - * @param $id - * @param $color - * @param $name - * @return DataResponse - */ - public function editDevice($id, $color, $name): DataResponse { + #[NoAdminRequired] + public function editDevice(int $id, string $color, string $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) - ) { + if ($color !== '' || $name !== '') { $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); + + return new DataResponse($this->l->t('Invalid values'), 400); } + + return new DataResponse($this->l->t('No such device'), 400); } - /** - * @NoAdminRequired - * @param $id - * @return DataResponse - */ - public function deleteDevice($id): DataResponse { + #[NoAdminRequired] + public function deleteDevice(int $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); } + + return new DataResponse($this->l->t('No such device'), 400); } /** - * @NoAdminRequired * @param ?array $deviceIdList - * @param int $begin - * @param int $end + * @param ?int $begin + * @param ?int $end * @param bool $all=false * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] 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)) { + if ($deviceIdList === null || is_array($deviceIdList) && $deviceIdList === []) { return new DataResponse($this->l->t('No device to export'), 400); } // create /Maps directory if necessary - $userFolder = $this->userfolder; + $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) { + if (!$mapsFolder instanceof Folder) { return new DataResponse($this->l->t('/Maps is not a directory'), 400); - } elseif (!$mapsFolder->isCreatable()) { + } + + if (!$mapsFolder->isCreatable()) { return new DataResponse($this->l->t('/Maps directory is not writeable'), 400); } } else { @@ -265,6 +214,7 @@ public function exportDevices($deviceIdList, $begin, $end, bool $all = false): D if ($mapsFolder->nodeExists($filename)) { $mapsFolder->get($filename)->delete(); } + $file = $mapsFolder->newFile($filename); $handler = $file->fopen('w'); @@ -276,160 +226,134 @@ public function exportDevices($deviceIdList, $begin, $end, bool $all = false): D } /** - * @NoAdminRequired * @param $path - * @return DataResponse * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\NotFoundException */ + #[NoAdminRequired] public function importDevices($path): DataResponse { - $userFolder = $this->userfolder; + $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()) { + if ($file instanceof File && $file->isReadable()) { $lowerFileName = strtolower($file->getName()); - if ($this->endsWith($lowerFileName, '.gpx') or $this->endsWith($lowerFileName, '.kml') or $this->endsWith($lowerFileName, '.kmz')) { + if (str_ends_with($lowerFileName, '.gpx') || str_ends_with($lowerFileName, '.kml') || str_ends_with($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); + + // invalid extension + return new DataResponse($this->l->t('Invalid file extension'), 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; + // directory or not readable + return new DataResponse($this->l->t('Impossible to read the file'), 400); } - return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0; + + // does not exist + return new DataResponse($this->l->t('File does not exist'), 400); } /** - * @NoAdminRequired - * @param int|null $myMapId - * @return DataResponse * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ + #[NoAdminRequired] public function getSharedDevices(?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + if (is_null($myMapId)) { $sharedDevices = []; } else { - $folders = $this->userfolder->getById($myMapId); - $folder = array_shift($folders); - $sharedDevices = $this->devicesService->getSharedDevicesFromFolder($folder); + $folder = $this->userFolder->getFirstNodeById($myMapId); + if ($folder instanceof Folder) { + $sharedDevices = $this->devicesService->getSharedDevicesFromFolder($folder); + } } return new DataResponse($sharedDevices); } - /** - * @NoAdminRequired - * @param int $id - * @param int $timestampFrom - * @param int $timestampTo - * @return DataResponse - */ + #[NoAdminRequired] 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 { + if ($device === null) { return new DataResponse($this->l->t('No such device'), 400); } + $share = $this->deviceShareMapper->create($id, $timestampFrom, $timestampTo); return new DataResponse($share); } /** - * @NoAdminRequired - * @param int $token - * @return DataResponse * @throws NotPermittedException * @throws NotFoundException */ - public function removeDeviceShare(int $token): DataResponse { + #[NoAdminRequired] + public function removeDeviceShare(string $token): DataResponse { try { $share = $this->deviceShareMapper->findByToken($token); - } catch (DoesNotExistException $e) { + } catch (DoesNotExistException) { 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(); } + + throw new NotFoundException(); } /** - * @NoAdminRequired - * @param string $token - * @param $targetMapId - * @return DataResponse * @throws NotFoundException */ - public function addSharedDeviceToMap(string $token, $targetMapId): DataResponse { + #[NoAdminRequired] + public function addSharedDeviceToMap(string $token, int $targetMapId): DataResponse { try { $share = $this->deviceShareMapper->findByToken($token); - } catch (DoesNotExistException $e) { + } catch (DoesNotExistException) { return new DataResponse($this->l->t('Share not Found'), 404); } - $folders = $this->userfolder->getById($targetMapId); - $folder = array_shift($folders); - if (is_null($folder)) { + + $folder = $this->userFolder->getFirstNodeById($targetMapId); + if (!$folder instanceof Folder) { return new DataResponse($this->l->t('Map not Found'), 404); } + try { + /** @var File $file */ $file = $folder->get('.device_shares.json'); - } catch (\OCP\Files\NotFoundException $e) { - $file = $folder->newFile('.device_shares.json', $content = '[]'); + } catch (NotFoundException) { + $file = $folder->newFile('.device_shares.json', '[]'); } - $data = json_decode($file->getContent(), true); + + $data = json_decode((string)$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)) { + $folder = $this->userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { return new DataResponse($this->l->t('Map not Found'), 404); } + try { + /** @var File $file */ $file = $folder->get('.device_shares.json'); - } catch (\OCP\Files\NotFoundException $e) { - $file = $folder->newFile('.device_shares.json', $content = '[]'); + } catch (NotFoundException) { + $file = $folder->newFile('.device_shares.json', '[]'); } - $data = json_decode($file->getContent(), true); + + $data = json_decode((string)$file->getContent(), true); $shares = []; $deleted = null; foreach ($data as $share) { @@ -440,10 +364,12 @@ public function removeSharedDeviceFromMap(string $token, int $myMapId): DataResp $shares[] = $share; } } + $file->putContent(json_encode($shares, JSON_PRETTY_PRINT)); if (is_null($deleted)) { return new DataResponse('Failed', 500); } + return new DataResponse('Done'); } } diff --git a/lib/Controller/FavoritesApiController.php b/lib/Controller/FavoritesApiController.php index d49718958..ff2f117ce 100644 --- a/lib/Controller/FavoritesApiController.php +++ b/lib/Controller/FavoritesApiController.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\Service\FavoritesService; -use OCP\App\IAppManager; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; -use OCP\IConfig; -use OCP\IGroupManager; use OCP\IL10N; use OCP\IRequest; -use OCP\IServerContainer; -use OCP\IUserManager; -use OCP\Share\IManager; class FavoritesApiController extends ApiController { - private $userId; - private $userfolder; - private $config; - private $appVersion; - private $shareManager; - private $userManager; - private $groupManager; - private $dbtype; - private $dbdblquotes; - private $defaultDeviceId; - private $l; - private $favoritesService; - protected $appName; - - public function __construct($AppName, + public function __construct( + string $appName, IRequest $request, - IServerContainer $serverContainer, - IConfig $config, - IManager $shareManager, - IAppManager $appManager, - IUserManager $userManager, - IGroupManager $groupManager, - IL10N $l, - FavoritesService $favoritesService, - $UserId) { - parent::__construct($AppName, $request, + private readonly IL10N $l, + private readonly FavoritesService $favoritesService, + private readonly ?string $userId, + ) { + parent::__construct($appName, $request, 'PUT, POST, GET, DELETE, PATCH, OPTIONS', 'Authorization, Content-Type, Accept', 1728000); - $this->favoritesService = $favoritesService; - $this->appName = $AppName; - $this->appVersion = $config->getAppValue('maps', 'installed_version'); - $this->userId = $UserId; - $this->userManager = $userManager; - $this->groupManager = $groupManager; - $this->l = $l; - $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 - * @NoCSRFRequired - * @CORS * @param $apiversion - * @param int $pruneBefore - * @return DataResponse */ + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] public function getFavorites($apiversion, int $pruneBefore = 0): DataResponse { $now = new \DateTime(); @@ -89,15 +52,13 @@ public function getFavorites($apiversion, int $pruneBefore = 0): DataResponse { if ($this->request->getHeader('If-None-Match') === '"' . $etag . '"') { return new DataResponse([], Http::STATUS_NOT_MODIFIED); } + return (new DataResponse($favorites)) ->setLastModified($now) ->setETag($etag); } /** - * @NoAdminRequired - * @NoCSRFRequired - * @CORS * @param $apiversion * @param $name * @param $lat @@ -105,63 +66,57 @@ public function getFavorites($apiversion, int $pruneBefore = 0): DataResponse { * @param $category * @param $comment * @param $extensions - * @return DataResponse */ + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] public function addFavorite($apiversion, $name, $lat, $lng, $category, $comment, $extensions): DataResponse { - if (is_numeric($lat) && is_numeric($lng)) { + if (is_float($lat) && is_float($lng)) { $favoriteId = $this->favoritesService->addFavoriteToDB($this->userId, $name, $lat, $lng, $category, $comment, $extensions); $favorite = $this->favoritesService->getFavoriteFromDB($favoriteId); return new DataResponse($favorite); - } else { - return new DataResponse($this->l->t('Invalid values'), 400); } + + return new DataResponse($this->l->t('Invalid values'), 400); } /** - * @NoAdminRequired - * @NoCSRFRequired - * @CORS - * @param $id * @param $name * @param $lat * @param $lng * @param $category * @param $comment * @param $extensions - * @return DataResponse */ - public function editFavorite($id, $name, $lat, $lng, $category, $comment, $extensions): DataResponse { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function editFavorite(int $id, $name, $lat, $lng, $category, $comment, $extensions): DataResponse { $favorite = $this->favoritesService->getFavoriteFromDB($id, $this->userId); if ($favorite !== null) { - if (($lat === null || is_numeric($lat)) - && ($lng === null || is_numeric($lng)) - ) { + if (($lat === null || is_float($lat)) && ($lng === null || is_float($lng))) { $this->favoritesService->editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $extensions); $editedFavorite = $this->favoritesService->getFavoriteFromDB($id); return new DataResponse($editedFavorite); - } else { - return new DataResponse($this->l->t('Invalid values'), 400); } - } else { - return new DataResponse($this->l->t('No such favorite'), 400); + + return new DataResponse($this->l->t('Invalid values'), 400); } + + return new DataResponse($this->l->t('No such favorite'), 400); } - /** - * @NoAdminRequired - * @NoCSRFRequired - * @CORS - * @param $id - * @return DataResponse - */ - public function deleteFavorite($id): DataResponse { + #[NoAdminRequired] + #[NoCSRFRequired] + #[CORS] + public function deleteFavorite(int $id): DataResponse { $favorite = $this->favoritesService->getFavoriteFromDB($id, $this->userId); if ($favorite !== null) { $this->favoritesService->deleteFavoriteFromDB($id); return new DataResponse('DELETED'); - } else { - return new DataResponse($this->l->t('No such favorite'), 400); } + + return new DataResponse($this->l->t('No such favorite'), 400); } } diff --git a/lib/Controller/FavoritesController.php b/lib/Controller/FavoritesController.php index 8bada8471..f2c97bc14 100644 --- a/lib/Controller/FavoritesController.php +++ b/lib/Controller/FavoritesController.php @@ -1,5 +1,7 @@ favoritesService = $favoritesService; - $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->dbtype = $config->getSystemValue('dbtype'); + IAppConfig $appConfig, + IRootFolder $rootFolder, + private readonly IL10N $l, + private readonly FavoritesService $favoritesService, + private readonly IDateTimeZone $dateTimeZone, + private readonly FavoriteShareMapper $favoriteShareMapper, + private readonly ?string $userId, + ) { + parent::__construct($appName, $request); + $this->appVersion = $appConfig->getValueString('maps', 'installed_version'); // IConfig object - $this->config = $config; - if ($UserId !== '' and $UserId !== null and $serverContainer !== null) { + if ($userId !== '' && $userId !== null) { // path of user files folder relative to DATA folder - $this->userFolder = $serverContainer->getUserFolder($UserId); + $this->userFolder = $rootFolder->getUserFolder($userId); } - $this->shareManager = $shareManager; - $this->favoriteShareMapper = $favoriteShareMapper; + $this->defaultFavoritsJSON = json_encode([ 'type' => 'FeatureCollection', 'features' => [] @@ -90,255 +69,234 @@ public function __construct($AppName, } /** - * @param \OCP\Files\Folder $folder - * @return mixed * @throws \OCP\Files\NotPermittedException */ - private function getJSONFavoritesFile(\OCP\Files\Folder $folder): \OCP\Files\Node { + private function getJSONFavoritesFile(Folder $folder): Node { try { $file = $folder->get('.favorites.json'); - } catch (NotFoundException $e) { - $file = $folder->newFile('.favorites.json', $content = $this->defaultFavoritsJSON); + } catch (NotFoundException) { + $file = $folder->newFile('.favorites.json', $this->defaultFavoritsJSON); } + return $file; } /** - * @NoAdminRequired - * @param ?int $myMapId - * @return DataResponse * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] public function getFavorites(?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + if (is_null($myMapId)) { $favorites = $this->favoritesService->getFavoritesFromDB($this->userId); } else { - $folders = $this->userFolder->getById($myMapId); - $folder = array_shift($folders); + $folder = $this->userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + throw new NotFoundException(sprintf('Folder with id %d does not exist', $myMapId)); + } + $file = $this->getJSONFavoritesFile($folder); $favorites = $this->favoritesService->getFavoritesFromJSON($file); } + return new DataResponse($favorites); } /** - * @NoAdminRequired - * @param string|null $name * @param float $lat * @param float $lng - * @param string|null $category - * @param string|null $comment - * @param string|null $extensions - * @param int|null $myMapId - * @return DataResponse * @throws NotFoundException * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\NotPermittedException */ - public function addFavorite(?string $name, float $lat, float $lng, ?string $category, ?string $comment, ?string $extensions, ?int $myMapId = null): DataResponse { - if (is_numeric($lat) && is_numeric($lng)) { - if (is_null($myMapId) || $myMapId === '') { - $favoriteId = $this->favoritesService->addFavoriteToDB($this->userId, $name, $lat, $lng, $category, $comment, $extensions); - $favorite = $this->favoritesService->getFavoriteFromDB($favoriteId); - return new DataResponse($favorite); - } else { - $folders = $this->userFolder->getById($myMapId); - if (!empty($folders) && $this->userFolder->getId() === $myMapId) { - $folders[] = $this->userFolder; - } - $folder = array_shift($folders); - if (is_null($folder)) { - return new DataResponse('Map not found', 404); - } - $file = $this->getJSONFavoritesFile($folder); - $favoriteId = $this->favoritesService->addFavoriteToJSON($file, $name, $lat, $lng, $category, $comment, $extensions); - $favorite = $this->favoritesService->getFavoriteFromJSON($file, $favoriteId); - return new DataResponse($favorite); - } - - } else { + #[NoAdminRequired] + public function addFavorite(?string $name, ?float $lat, ?float $lng, ?string $category, ?string $comment, ?string $extensions, ?int $myMapId = null): DataResponse { + if (!is_numeric($lat) || !is_numeric($lng)) { return new DataResponse($this->l->t('Invalid values'), 400); } + + if (is_null($myMapId)) { + $favoriteId = $this->favoritesService->addFavoriteToDB($this->userId, $name, $lat, $lng, $category, $comment, $extensions); + $favorite = $this->favoritesService->getFavoriteFromDB($favoriteId); + return new DataResponse($favorite); + } + + $folders = $this->userFolder->getById($myMapId); + if (!empty($folders) && $this->userFolder->getId() === $myMapId) { + $folders[] = $this->userFolder; + } + + $folder = array_shift($folders); + if (!($folder instanceof Folder)) { + return new DataResponse('Map not found', 404); + } + + $file = $this->getJSONFavoritesFile($folder); + $favoriteId = $this->favoritesService->addFavoriteToJSON($file, $name, $lat, $lng, $category, $comment, $extensions); + $favorite = $this->favoritesService->getFavoriteFromJSON($file, $favoriteId); + return new DataResponse($favorite); } /** - * @NoAdminRequired - * @param array $favorites - * @param int|null $myMapId - * @return DataResponse * @throws NotFoundException * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] public function addFavorites(array $favorites, ?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + if (is_null($myMapId)) { $favoritesAfter = []; foreach ($favorites as $favorite) { - if (is_numeric($favorite->lat) && is_numeric($favorite->lng)) { + if (is_float($favorite->lat) && is_float($favorite->lng)) { $favoriteId = $this->favoritesService->addFavoriteToDB($this->userId, $favorite->name, $favorite->lat, $favorite->lng, $favorite->category, $favorite->comment, $favorite->extensions); $favoritesAfter[] = $this->favoritesService->getFavoriteFromDB($favoriteId); } else { return new DataResponse('invalid values', 400); } } + return new DataResponse($favoritesAfter); - } else { - $folders = $this->userFolder->getById($myMapId); - if (!empty($folders) && $this->userFolder->getId() === $myMapId) { - $folders[] = $this->userFolder; - } - $folder = array_shift($folders); - if (is_null($folder)) { - return new DataResponse('Map not found', 404); + } + + $folders = $this->userFolder->getById($myMapId); + if (!empty($folders) && $this->userFolder->getId() === $myMapId) { + $folders[] = $this->userFolder; + } + + $folder = array_shift($folders); + if (!$folder instanceof Folder) { + return new DataResponse('Map not found', 404); + } + + $file = $this->getJSONFavoritesFile($folder); + $favoriteIds = $this->favoritesService->addFavoritesToJSON($file, $favorites); + $favoritesAfter = []; + foreach ($this->favoritesService->getFavoritesFromJSON($file) as $favorite) { + if (in_array($favorite['id'], $favoriteIds)) { + $favoritesAfter[] = $favorite; } - $file = $this->getJSONFavoritesFile($folder); - $favoriteIds = $this->favoritesService->addFavoritesToJSON($file, $favorites); - $favoritesAfter = []; - foreach ($this->favoritesService->getFavoritesFromJSON($file) as $favorite) { - if (in_array($favorite['id'], $favoriteIds)) { - $favoritesAfter[] = $favorite; - } - }; - return new DataResponse($favoritesAfter); } + + return new DataResponse($favoritesAfter); } /** - * @NoAdminRequired - * @param int $id - * @param string|null $name - * @param float $lat - * @param float $lng - * @param string|null $category - * @param string|null $comment - * @param string|null $extensions - * @param int|null $myMapId - * @return DataResponse * @throws \OCP\Files\NotPermittedException */ - public function editFavorite(int $id, ?string $name, float $lat, float $lng, ?string $category, ?string $comment, ?string $extensions, ?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + #[NoAdminRequired] + public function editFavorite(int $id, ?string $name, ?float $lat, ?float $lng, ?string $category, ?string $comment, ?string $extensions, ?int $myMapId = null): DataResponse { + if (is_null($myMapId)) { $favorite = $this->favoritesService->getFavoriteFromDB($id, $this->userId); if ($favorite !== null) { - if (($lat === null || is_numeric($lat)) - && ($lng === null || is_numeric($lng)) - ) { - $this->favoritesService->editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $extensions); - $editedFavorite = $this->favoritesService->getFavoriteFromDB($id); - return new DataResponse($editedFavorite); - } else { - return new DataResponse($this->l->t('invalid values'), 400); - } - } else { - return new DataResponse($this->l->t('no such favorite'), 400); - } - } else { - $folders = $this->userFolder->getById($myMapId); - $folder = array_shift($folders); - $file = $this->getJSONFavoritesFile($folder); - $favorite = $this->favoritesService->getFavoriteFromJSON($file, $id, $this->userId); - if ($favorite !== null) { - if (($lat === null || is_numeric($lat)) - && ($lng === null || is_numeric($lng)) - ) { - $this->favoritesService->editFavoriteInJSON($file, $id, $name, $lat, $lng, $category, $comment, $extensions); - $editedFavorite = $this->favoritesService->getFavoriteFromJSON($file, $id); - return new DataResponse($editedFavorite); - } else { - return new DataResponse($this->l->t('invalid values'), 400); - } - } else { - return new DataResponse($this->l->t('no such favorite'), 400); + $this->favoritesService->editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $extensions); + $editedFavorite = $this->favoritesService->getFavoriteFromDB($id); + return new DataResponse($editedFavorite); } + + return new DataResponse($this->l->t('no such favorite'), 400); + } + + $folder = $this->userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + return new DataResponse('Map not found', 404); + } + + $file = $this->getJSONFavoritesFile($folder); + $favorite = $this->favoritesService->getFavoriteFromJSON($file, $id); + if ($favorite === null) { + return new DataResponse($this->l->t('no such favorite'), 400); } + + $this->favoritesService->editFavoriteInJSON($file, $id, $name, $lat, $lng, $category, $comment, $extensions); + $editedFavorite = $this->favoritesService->getFavoriteFromJSON($file, $id); + return new DataResponse($editedFavorite); } /** - * @NoAdminRequired - * @param array $categories - * @param string $newName - * @param int|null $myMapId - * @return DataResponse * @throws \OCP\DB\Exception * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] public function renameCategories(array $categories, string $newName, ?int $myMapId = null): DataResponse { - if (is_array($categories)) { - foreach ($categories as $cat) { - if (is_null($myMapId) || $myMapId === '') { - $this->favoritesService->renameCategoryInDB($this->userId, $cat, $newName); - - // Rename share if one exists - try { - $share = $this->favoriteShareMapper->findByOwnerAndCategory($this->userId, $cat); - $share->setCategory($newName); - $this->favoriteShareMapper->update($share); - } catch (DoesNotExistException|MultipleObjectsReturnedException $e) { - } - } else { - $folders = $this->userFolder->getById($myMapId); - $folder = array_shift($folders); - $file = $this->getJSONFavoritesFile($folder); - $this->favoritesService->renameCategoryInJSON($file, $cat, $newName); + foreach ($categories as $cat) { + if (is_null($myMapId)) { + $this->favoritesService->renameCategoryInDB($this->userId, $cat, $newName); + + // Rename share if one exists + try { + $share = $this->favoriteShareMapper->findByOwnerAndCategory($this->userId, $cat); + $share->setCategory($newName); + $this->favoriteShareMapper->update($share); + } catch (DoesNotExistException|MultipleObjectsReturnedException) { + } + } else { + $folder = $this->userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + return new DataResponse('Map not found', 404); } + + $file = $this->getJSONFavoritesFile($folder); + $this->favoritesService->renameCategoryInJSON($file, $cat, $newName); } } + return new DataResponse('RENAMED'); } /** - * @NoAdminRequired - * @param int $id - * @param int|null $myMapId - * @return DataResponse * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] public function deleteFavorite(int $id, ?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + if (is_null($myMapId)) { $favorite = $this->favoritesService->getFavoriteFromDB($id, $this->userId); if ($favorite !== null) { $this->favoritesService->deleteFavoriteFromDB($id); return new DataResponse('DELETED'); } + return new DataResponse($this->l->t('no such favorite'), 400); } - $folders = $this->userFolder->getById($myMapId); - $folder = array_shift($folders); + + $folder = $this->userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + return new DataResponse('Map not found', 404); + } + $file = $this->getJSONFavoritesFile($folder); if ($this->favoritesService->deleteFavoriteFromJSON($file, $id) > 0) { return new DataResponse('DELETED'); } + return new DataResponse($this->l->t('no such favorite'), 400); } /** - * @NoAdminRequired - * @param array $ids - * @param int|null $myMapId - * @return DataResponse * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] public function deleteFavorites(array $ids, ?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + if (is_null($myMapId)) { $this->favoritesService->deleteFavoritesFromDB($ids, $this->userId); } else { - $folders = $this->userFolder->getById($myMapId); - $folder = array_shift($folders); + $folder = $this->userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + return new DataResponse('Map not found', 404); + } + $file = $this->getJSONFavoritesFile($folder); $this->favoritesService->deleteFavoritesFromJSON($file, $ids); } + return new DataResponse('DELETED'); } /** - * @NoAdminRequired - * @param int|null $myMapId - * @return DataResponse * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ + #[NoAdminRequired] public function getSharedCategories(?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + if (is_null($myMapId)) { $categories = $this->favoriteShareMapper->findAllByOwner($this->userId); } else { $categories = $this->favoriteShareMapper->findAllByMapId($this->userId, $myMapId); @@ -347,11 +305,7 @@ public function getSharedCategories(?int $myMapId = null): DataResponse { return new DataResponse($categories); } - /** - * @NoAdminRequired - * @param string $category - * @return DataResponse - */ + #[NoAdminRequired] public function shareCategory(string $category): DataResponse { if ($this->favoritesService->countFavorites($this->userId, [$category], null, null) === 0) { return new DataResponse($this->l->t('Unknown category'), Http::STATUS_BAD_REQUEST); @@ -359,18 +313,14 @@ public function shareCategory(string $category): DataResponse { $share = $this->favoriteShareMapper->findOrCreateByOwnerAndCategory($this->userId, $category); - if ($share === null) { + if (!$share instanceof FavoriteShare) { return new DataResponse($this->l->t('Error sharing favorite'), Http::STATUS_INTERNAL_SERVER_ERROR); } return new DataResponse($share); } - /** - * @NoAdminRequired - * @param string $category - * @return DataResponse - */ + #[NoAdminRequired] public function unShareCategory(string $category): DataResponse { if ($this->favoritesService->countFavorites($this->userId, [$category], null, null) === 0) { return new DataResponse($this->l->t('Unknown category'), Http::STATUS_BAD_REQUEST); @@ -384,74 +334,64 @@ public function unShareCategory(string $category): DataResponse { } /** - * @NoAdminRequired - * @param string $category - * @param int $targetMapId - * @param int|null $myMapId - * @return DataResponse * @throws DoesNotExistException * @throws MultipleObjectsReturnedException * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ + #[NoAdminRequired] public function addShareCategoryToMap(string $category, int $targetMapId, ?int $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + if (is_null($myMapId) || $myMapId === 0) { $share = $this->favoriteShareMapper->findByOwnerAndCategory($this->userId, $category); } else { $share = $this->favoriteShareMapper->findByMapIdAndCategory($this->userId, $myMapId, $category); } + $folders = $this->userFolder->getById($targetMapId); $folder = array_shift($folders); if (!($folder instanceof Folder)) { return new DataResponse($this->l->t('Map not Found'), 404); } + try { $file = $folder->get('.favorite_shares.json'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { $file = $folder->newFile('.favorite_shares.json', $content = '[]'); } - $data = json_decode($file->getContent(), true); + + $data = json_decode((string)$file->getContent(), true); foreach ($data as $s) { if ($s->token === $share->token) { return new DataResponse($this->l->t('Share was already on map')); } } + $share->id = count($data); $data[] = $share; $file->putContent(json_encode($data, JSON_PRETTY_PRINT)); return new DataResponse('Done'); } - /** - * @NoAdminRequired - * @param string $category - * @param int $myMapId - * @return DataResponse - */ + #[NoAdminRequired] public function removeShareCategoryFromMap(string $category, int $myMapId): DataResponse { $d = $this->favoriteShareMapper->removeByMapIdAndCategory($this->userId, $myMapId, $category); if (is_null($d)) { return new DataResponse('Failed', 500); } + return new DataResponse('Done'); } /** - * @NoAdminRequired - * @param array|null $categoryList - * @param int|null $begin - * @param int|null $end - * @param bool $all - * @return DataResponse * @throws NotFoundException * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] public function exportFavorites(?array $categoryList = null, ?int $begin = null, ?int $end = null, bool $all = false): DataResponse { // sorry about ugly categoryList management: // when an empty list is passed in http request, we get null here - if ($categoryList === null or (is_array($categoryList) and count($categoryList) === 0)) { - $response = new DataResponse($this->l->t('Nothing to export'), 400); - return $response; + if ($categoryList === null || is_array($categoryList) && $categoryList === []) { + return new DataResponse($this->l->t('Nothing to export'), 400); } // create /Maps directory if necessary @@ -459,24 +399,23 @@ public function exportFavorites(?array $categoryList = null, ?int $begin = null, if (!$userFolder->nodeExists('/Maps')) { $userFolder->newFolder('Maps'); } + if ($userFolder->nodeExists('/Maps')) { $mapsFolder = $userFolder->get('/Maps'); if (!($mapsFolder instanceof Folder)) { - $response = new DataResponse($this->l->t('/Maps is not a directory'), 400); - return $response; - } elseif (!$mapsFolder->isCreatable()) { - $response = new DataResponse($this->l->t('/Maps directory is not writeable'), 400); - return $response; + return new DataResponse($this->l->t('/Maps is not a directory'), 400); + } + + if (!$mapsFolder->isCreatable()) { + return new DataResponse($this->l->t('/Maps directory is not writeable'), 400); } } else { - $response = new DataResponse($this->l->t('Impossible to create /Maps directory'), 400); - return $response; + return new DataResponse($this->l->t('Impossible to create /Maps directory'), 400); } $nbFavorites = $this->favoritesService->countFavorites($this->userId, $categoryList, $begin, $end); if ($nbFavorites === 0) { - $response = new DataResponse($this->l->t('Nothing to export'), 400); - return $response; + return new DataResponse($this->l->t('Nothing to export'), 400); } // generate export file name @@ -489,6 +428,7 @@ public function exportFavorites(?array $categoryList = null, ?int $begin = null, if ($mapsFolder->nodeExists($filename)) { $mapsFolder->get($filename)->delete(); } + $file = $mapsFolder->newFile($filename); $handler = $file->fopen('w'); @@ -500,50 +440,35 @@ public function exportFavorites(?array $categoryList = null, ?int $begin = null, } /** - * @NoAdminRequired - * @param string $path - * @return DataResponse * @throws NotFoundException * @throws \OCP\Files\InvalidPathException */ + #[NoAdminRequired] public function importFavorites(string $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') or $this->endswith($lowerFileName, '.json') or $this->endswith($lowerFileName, '.geojson')) { - $result = $this->favoritesService->importFavorites($this->userId, $file); - return new DataResponse($result); - } 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 + $cleanPath = str_replace(['../', '..\\'], '', $path); + + if (!$userFolder->nodeExists($cleanPath)) { return new DataResponse($this->l->t('File does not exist'), 400); } - } - /** - * @param string $string - * @param string $test - * @return bool - */ - private function endswith(string $string, string $test): bool { - $strlen = strlen($string); - $testlen = strlen($test); - if ($testlen > $strlen) { - return false; + $file = $userFolder->get($cleanPath); + if (!$file instanceof File || !$file->isReadable()) { + // directory or not readable + return new DataResponse($this->l->t('Impossible to read the file'), 400); } - return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0; - } + $lowerFileName = strtolower($file->getName()); + if (str_ends_with($lowerFileName, '.gpx') + || str_ends_with($lowerFileName, '.kml') + || str_ends_with($lowerFileName, '.kmz') + || str_ends_with($lowerFileName, '.json') + || str_ends_with($lowerFileName, '.geojson')) { + $result = $this->favoritesService->importFavorites($this->userId, $file); + return new DataResponse($result); + } + + // invalid extension + return new DataResponse($this->l->t('Invalid file extension'), 400); + } } diff --git a/lib/Controller/MyMapsController.php b/lib/Controller/MyMapsController.php index 3a4507278..b79247fc4 100644 --- a/lib/Controller/MyMapsController.php +++ b/lib/Controller/MyMapsController.php @@ -1,5 +1,7 @@ myMapsService = $myMapsService; - $this->userId = $userId; } /** @@ -41,6 +42,7 @@ public function addMyMap(array $values): DataResponse { if (is_string($myMap)) { new DataResponse($myMap, 400); } + return new DataResponse($myMap); } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 2fd2ac676..1bafdff69 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -1,5 +1,7 @@ * @copyright Vinzenz Rosenkranz 2017 */ - namespace OCA\Maps\Controller; use OCA\Files\Event\LoadSidebar; use OCA\Maps\Service\MyMapsService; use OCA\Viewer\Event\LoadViewer; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\RedirectResponse; +use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\IEventDispatcher; -use OCP\IConfig; +use OCP\IAppConfig; use OCP\IRequest; use OCP\IURLGenerator; @@ -29,11 +34,11 @@ class PageController extends Controller { public function __construct( string $appName, IRequest $request, - private string $userId, - private IEventDispatcher $eventDispatcher, - private IConfig $config, - private IInitialState $initialState, - private IURLGenerator $urlGenerator, + private readonly ?string $userId, + private readonly IEventDispatcher $eventDispatcher, + private readonly IAppConfig $appConfig, + private readonly IInitialState $initialState, + private readonly IURLGenerator $urlGenerator, ) { parent::__construct($appName, $request); } @@ -44,17 +49,15 @@ public function __construct( * it up in the docs or you might create a security hole. This is * basically the only required method to add this exemption, don't * add it to any other method if you don't exactly know what it does - * - * @NoAdminRequired - * @NoCSRFRequired - * @return TemplateResponse */ + #[NoAdminRequired] + #[NoCSRFRequired] public function index(): TemplateResponse { - $this->eventDispatcher->dispatch(LoadSidebar::class, new LoadSidebar()); - $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer()); + $this->eventDispatcher->dispatchTyped(new LoadSidebar()); + $this->eventDispatcher->dispatchTyped(new LoadViewer()); $params = ['user' => $this->userId]; - $this->initialState->provideInitialState('photos', $this->config->getAppValue('photos', 'enabled', 'no') === 'yes'); + $this->initialState->provideInitialState('photos', $this->appConfig->getValueBool('photos', 'enabled')); $response = new TemplateResponse('maps', 'main', $params); $this->addCsp($response); @@ -62,10 +65,8 @@ public function index(): TemplateResponse { return $response; } - /** - * @NoAdminRequired - * @NoCSRFRequired - */ + #[NoAdminRequired] + #[NoCSRFRequired] public function indexMyMap(int $myMapId, MyMapsService $service): TemplateResponse|RedirectResponse { $map = $service->getMyMap($myMapId, $this->userId); if ($map !== null && $map['id'] !== $myMapId) { @@ -76,11 +77,11 @@ public function indexMyMap(int $myMapId, MyMapsService $service): TemplateRespon ); } - $this->eventDispatcher->dispatch(LoadSidebar::class, new LoadSidebar()); - $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer()); + $this->eventDispatcher->dispatchTyped(new LoadSidebar()); + $this->eventDispatcher->dispatchTyped(new LoadViewer()); $params = ['user' => $this->userId]; - $this->initialState->provideInitialState('photos', $this->config->getAppValue('photos', 'enabled', 'no') === 'yes'); + $this->initialState->provideInitialState('photos', $this->appConfig->getValueBool('photos', 'enabled')); $response = new TemplateResponse('maps', 'main', $params); $this->addCsp($response); @@ -88,71 +89,64 @@ public function indexMyMap(int $myMapId, MyMapsService $service): TemplateRespon return $response; } - /** - * @NoAdminRequired - * @NoCSRFRequired - * @param $url - * @return TemplateResponse - */ - public function openGeoLink($url): TemplateResponse { + #[NoAdminRequired] + #[NoCSRFRequired] + public function openGeoLink(string $url): TemplateResponse { return $this->index(); } - /** - * @param $response - * @return void - */ - private function addCsp($response): void { - if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) { - $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy(); - // map tiles - $csp->addAllowedImageDomain('https://*.tile.openstreetmap.org'); - $csp->addAllowedImageDomain('https://tile.openstreetmap.org'); - $csp->addAllowedImageDomain('https://server.arcgisonline.com'); - $csp->addAllowedImageDomain('https://*.cartocdn.com'); - $csp->addAllowedImageDomain('https://*.opentopomap.org'); - $csp->addAllowedImageDomain('https://*.cartocdn.com'); - $csp->addAllowedImageDomain('https://*.ssl.fastly.net'); - $csp->addAllowedImageDomain('https://*.openstreetmap.se'); - - // default routing engine - $csp->addAllowedConnectDomain('https://*.project-osrm.org'); - $csp->addAllowedConnectDomain('https://api.mapbox.com'); - $csp->addAllowedConnectDomain('https://events.mapbox.com'); - $csp->addAllowedConnectDomain('https://graphhopper.com'); - - $csp->addAllowedChildSrcDomain('blob:'); - $csp->addAllowedWorkerSrcDomain('blob:'); - $csp->addAllowedScriptDomain('https://unpkg.com'); - // allow connections to custom routing engines - $urlKeys = [ - 'osrmBikeURL', - 'osrmCarURL', - 'osrmFootURL', - 'graphhopperURL', - 'maplibreStreetStyleURL', - 'maplibreStreetStyleAuth' - ]; - foreach ($urlKeys as $key) { - $url = $this->config->getAppValue('maps', $key); - if ($url !== '') { - $scheme = parse_url($url, PHP_URL_SCHEME); - $host = parse_url($url, PHP_URL_HOST); - $port = parse_url($url, PHP_URL_PORT); - $cleanUrl = $scheme . '://' . $host; - if ($port && $port !== '') { - $cleanUrl .= ':' . $port; - } - $csp->addAllowedConnectDomain($cleanUrl); + private function addCsp(Response $response): void { + $csp = new ContentSecurityPolicy(); + // map tiles + $csp->addAllowedImageDomain('https://*.tile.openstreetmap.org'); + $csp->addAllowedImageDomain('https://tile.openstreetmap.org'); + $csp->addAllowedImageDomain('https://server.arcgisonline.com'); + $csp->addAllowedImageDomain('https://*.cartocdn.com'); + $csp->addAllowedImageDomain('https://*.opentopomap.org'); + $csp->addAllowedImageDomain('https://*.cartocdn.com'); + $csp->addAllowedImageDomain('https://*.ssl.fastly.net'); + $csp->addAllowedImageDomain('https://*.openstreetmap.se'); + + // default routing engine + $csp->addAllowedConnectDomain('https://*.project-osrm.org'); + $csp->addAllowedConnectDomain('https://api.mapbox.com'); + $csp->addAllowedConnectDomain('https://events.mapbox.com'); + $csp->addAllowedConnectDomain('https://graphhopper.com'); + + $csp->addAllowedChildSrcDomain('blob:'); + $csp->addAllowedWorkerSrcDomain('blob:'); + $csp->addAllowedScriptDomain('https://unpkg.com'); + // allow connections to custom routing engines + $urlKeys = [ + 'osrmBikeURL', + 'osrmCarURL', + 'osrmFootURL', + 'graphhopperURL', + 'maplibreStreetStyleURL', + 'maplibreStreetStyleAuth' + ]; + foreach ($urlKeys as $key) { + $url = $this->appConfig->getValueString('maps', $key); + if ($url !== '') { + $scheme = parse_url($url, PHP_URL_SCHEME); + $host = parse_url($url, PHP_URL_HOST); + $port = parse_url($url, PHP_URL_PORT); + $cleanUrl = $scheme . '://' . $host; + if ($port && $port !== '') { + $cleanUrl .= ':' . $port; } - } - //$csp->addAllowedConnectDomain('http://192.168.0.66:5000'); - // poi images - $csp->addAllowedImageDomain('https://nominatim.openstreetmap.org'); - // search and geocoder - $csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org'); - $response->setContentSecurityPolicy($csp); + $csp->addAllowedConnectDomain($cleanUrl); + } } + + //$csp->addAllowedConnectDomain('http://192.168.0.66:5000'); + + // poi images + $csp->addAllowedImageDomain('https://nominatim.openstreetmap.org'); + // search and geocoder + $csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org'); + + $response->setContentSecurityPolicy($csp); } } diff --git a/lib/Controller/PhotosController.php b/lib/Controller/PhotosController.php index dffc0083a..72f06ed81 100644 --- a/lib/Controller/PhotosController.php +++ b/lib/Controller/PhotosController.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\Controller; use OC\User\NoUserException; @@ -17,6 +18,8 @@ use OCA\Maps\Service\PhotofilesService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; use OCP\DB\Exception; use OCP\Files\Folder; @@ -27,102 +30,83 @@ use OCP\IRequest; class PhotosController extends Controller { - private $userId; - private $geophotoService; - private $photofilesService; - private $root; - - public function __construct($AppName, + public function __construct( + string $appName, IRequest $request, - GeophotoService $GeophotoService, - PhotofilesService $photofilesService, - IRootFolder $root, - $UserId) { - parent::__construct($AppName, $request); - $this->userId = $UserId; - $this->geophotoService = $GeophotoService; - $this->photofilesService = $photofilesService; - $this->root = $root; + private readonly GeophotoService $geophotoService, + private readonly PhotofilesService $photofilesService, + private readonly IRootFolder $root, + private readonly string $userId, + ) { + parent::__construct($appName, $request); } /** - * @NoAdminRequired - * @NoCSRFRequired - * @param null $myMapId - * @param null $respectNoMediaAndNoimage - * @param null $hideImagesOnCustomMaps - * @param null $hideImagesInMapsFolder - * @return DataResponse * @throws Exception * @throws NoUserException * @throws NotFoundException * @throws NotPermittedException */ - public function getPhotos($myMapId = null, $respectNoMediaAndNoimage = null, $hideImagesOnCustomMaps = null, $hideImagesInMapsFolder = null): DataResponse { + #[NoAdminRequired] + #[NoCSRFRequired] + public function getPhotos(?int $myMapId = null, bool $respectNoMediaAndNoimage = true, bool $hideImagesOnCustomMaps = false, bool $hideImagesInMapsFolder = true): DataResponse { $userFolder = $this->root->getUserFolder($this->userId); - if (is_null($myMapId) || $myMapId === '') { - $result = $this->geophotoService->getAll($this->userId, $userFolder, $respectNoMediaAndNoimage ?? true, $hideImagesOnCustomMaps ?? false, $hideImagesInMapsFolder ?? true); + if (is_null($myMapId)) { + $result = $this->geophotoService->getAll($this->userId, $userFolder, $respectNoMediaAndNoimage, $hideImagesOnCustomMaps, $hideImagesInMapsFolder); } else { - $folders = $userFolder->getById($myMapId); - $folder = array_shift($folders); - $result = $this->geophotoService->getAll($this->userId, $folder, $respectNoMediaAndNoimage ?? true, $hideImagesOnCustomMaps ?? false, $hideImagesInMapsFolder ?? false); + $folder = $userFolder->getFirstNodeById($myMapId); + $result = $this->geophotoService->getAll($this->userId, $folder, $respectNoMediaAndNoimage, $hideImagesOnCustomMaps, $hideImagesInMapsFolder); } + return new DataResponse($result); } /** - * @NoAdminRequired - * @NoCSRFRequired - * @param int|null $myMapId - * @param string|null $timezone - * @param int $limit - * @param int $offset - * @param null $respectNoMediaAndNoimage - * @param null $hideImagesOnCustomMaps - * @param null $hideImagesInMapsFolder - * @return DataResponse * @throws Exception * @throws NoUserException * @throws NotFoundException * @throws NotPermittedException */ + #[NoAdminRequired] + #[NoCSRFRequired] public function getNonLocalizedPhotos(?int $myMapId = null, ?string $timezone = null, int $limit = 250, int $offset = 0, $respectNoMediaAndNoimage = null, $hideImagesOnCustomMaps = null, $hideImagesInMapsFolder = null): DataResponse { $userFolder = $this->root->getUserFolder($this->userId); - if (is_null($myMapId) || $myMapId === '') { + if (is_null($myMapId)) { $result = $this->geophotoService->getNonLocalized($this->userId, $userFolder, $respectNoMediaAndNoimage ?? true, $hideImagesOnCustomMaps ?? false, $hideImagesInMapsFolder ?? true, $timezone, $limit, $offset); } else { - $folders = $userFolder->getById($myMapId); - $folder = array_shift($folders); + $folder = $userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + throw new NotFoundException('Could find map with mapid: ' . $myMapId); + } + $result = $this->geophotoService->getNonLocalized($this->userId, $folder, $respectNoMediaAndNoimage ?? true, $hideImagesOnCustomMaps ?? false, $hideImagesInMapsFolder ?? false, $timezone, $limit, $offset); } + return new DataResponse($result); } /** - * @NoAdminRequired - * @param $paths - * @param $lats - * @param $lngs - * @param bool $directory - * @param null $myMapId - * @param bool $relative - * @return DataResponse + * @param list $paths + * @param list $lats + * @param list $lngs + * @return DataResponse|array, array> * @throws NoUserException * @throws NotFoundException * @throws NotPermittedException * @throws InvalidPathException */ - public function placePhotos($paths, $lats, $lngs, bool $directory = false, $myMapId = null, bool $relative = false): DataResponse { + #[NoAdminRequired] + public function placePhotos(array $paths, array $lats, array $lngs, bool $directory = false, ?int $myMapId = null, bool $relative = false): DataResponse { $userFolder = $this->root->getUserFolder($this->userId); - if (!is_null($myMapId) and $myMapId !== '') { - if ($directory === 'true') { + if (!is_null($myMapId)) { + if ($directory) { // forbid folder placement in my-maps throw new NotPermittedException(); } $folder = $userFolder->getFirstNodeById($myMapId); - if (!($folder instanceof Folder)) { + if (!$folder instanceof Folder) { return new DataResponse(statusCode: Http::STATUS_BAD_REQUEST); } @@ -130,66 +114,69 @@ public function placePhotos($paths, $lats, $lngs, bool $directory = false, $myMa if ($relative) { foreach ($paths as $key => $path) { $photoFile = $folder->get($path); - $paths[$key] = $userFolder->getRelativePath($photoFile->getPath()); + $path = $userFolder->getRelativePath($photoFile->getPath()); + if ($path === null) { + return new DataResponse(statusCode: Http::STATUS_BAD_REQUEST); + } + $paths[$key] = $path; } } else { // here the photo path is good, copy it in this map's folder if it's not already there foreach ($paths as $key => $path) { $photoFile = $userFolder->get($path); // is the photo in this map's folder? - if (!$folder->getById($photoFile->getId())) { + if ($folder->getFirstNodeById($photoFile->getId()) !== null) { $copiedFile = $photoFile->copy($folder->getPath() . '/' . $photoFile->getName()); - $paths[$key] = $userFolder->getRelativePath($copiedFile->getPath()); + $path = $userFolder->getRelativePath($copiedFile->getPath()); + if ($path === null) { + return new DataResponse(statusCode: Http::STATUS_BAD_REQUEST); + } + $paths[$key] = $path; } } } } + $result = $this->photofilesService->setPhotosFilesCoords($this->userId, $paths, $lats, $lngs, $directory); return new DataResponse($result); } /** - * @NoAdminRequired * @param $paths - * @return DataResponse */ - public function resetPhotosCoords($paths, $myMapId = null): DataResponse { + #[NoAdminRequired] + public function resetPhotosCoords($paths, ?int $myMapId = null): DataResponse { $userFolder = $this->root->getUserFolder($this->userId); $result = []; - if (sizeof($paths) > 0) { + if (count($paths) > 0) { $result = $this->photofilesService->resetPhotosFilesCoords($this->userId, $paths); } - if (!is_null($myMapId) and $myMapId !== '') { + + if (!is_null($myMapId)) { foreach ($paths as $key => $path) { - $folders = $userFolder->getById($myMapId); - $folder = array_shift($folders); + $folder = $userFolder->getFirstNodeById($myMapId); $photoFile = $userFolder->get($path); - if ($folder->isSubNode($photoFile)) { + if ($folder instanceof Folder && $folder->isSubNode($photoFile)) { $photoFile->delete(); unset($paths[$key]); } } } + return new DataResponse($result); } - /** - * @NoAdminRequired - * @return DataResponse - */ + #[NoAdminRequired] public function clearCache(): DataResponse { $result = $this->geophotoService->clearCache(); if ($result) { return new DataResponse('Cache cleared'); - } else { - return new DataResponse('Failed to clear Cache', 400); } + + return new DataResponse('Failed to clear Cache', 400); } - /** - * @NoAdminRequired - * @return DataResponse - */ + #[NoAdminRequired] public function getBackgroundJobStatus(): DataResponse { return new DataResponse($this->photofilesService->getBackgroundJobStatus($this->userId)); } diff --git a/lib/Controller/PublicContactsController.php b/lib/Controller/PublicContactsController.php index 556a2a624..7cd6234ce 100644 --- a/lib/Controller/PublicContactsController.php +++ b/lib/Controller/PublicContactsController.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; -use OCA\DAV\CardDAV\CardDavBackend; use OCA\Maps\Service\AddressService; use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; -use OCP\Contacts\IManager; use OCP\EventDispatcher\IEventDispatcher; -use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use OCP\IAppConfig; use OCP\IAvatarManager; -use OCP\IConfig; -use OCP\IDBConnection; use OCP\IInitialStateService; use OCP\IRequest; use OCP\ISession; @@ -33,37 +30,24 @@ use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager as ShareManager; use OCP\Share\IShare; +use Sabre\VObject\Document; use Sabre\VObject\Reader; class PublicContactsController extends PublicPageController { - protected IManager $contactsManager; - protected AddressService $addressService; - protected CardDavBackend $cdBackend; - protected IAvatarManager $avatarManager; - protected IRootFolder $root; - public function __construct( string $appName, IRequest $request, ISession $session, IURLGenerator $urlGenerator, IEventDispatcher $eventDispatcher, - IConfig $config, + IAppConfig $appConfig, IInitialStateService $initialStateService, ShareManager $shareManager, IUserManager $userManager, - IManager $contactsManager, - IDBConnection $dbconnection, - AddressService $addressService, - CardDavBackend $cdBackend, - IAvatarManager $avatarManager, - IRootFolder $root) { - parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $config, $initialStateService, $shareManager, $userManager); - $this->avatarManager = $avatarManager; - $this->contactsManager = $contactsManager; - $this->addressService = $addressService; - $this->cdBackend = $cdBackend; - $this->root = $root; + private readonly AddressService $addressService, + private readonly IAvatarManager $avatarManager, + ) { + parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $appConfig, $initialStateService, $shareManager, $userManager); } /** @@ -93,7 +77,7 @@ private function getShare() { // Check whether share exists try { $share = $this->shareManager->getShareByToken($this->getToken()); - } catch (ShareNotFound $e) { + } catch (ShareNotFound) { // The share does not exists, we do not emit an ShareLinkAccessedEvent throw new NotFoundException(); } @@ -101,6 +85,7 @@ private function getShare() { if (!$this->validateShare($share)) { throw new NotFoundException(); } + return $share; } @@ -119,7 +104,6 @@ private function getShareNode() { /** * @PublicPage * - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException @@ -144,12 +128,13 @@ public function getContacts(): DataResponse { $result[] = $this->vCardToArray($permissions, $file, $vcard, $geo->getValue()); } elseif (is_countable($geo) && count($geo) > 0 && is_iterable($geo)) { foreach ($geo as $g) { - if (strlen($g->getValue()) > 1) { + if (strlen((string)$g->getValue()) > 1) { $result[] = $this->vCardToArray($permissions, $file, $vcard, $g->getValue()); } } } } + if (isset($vcard->ADR) && count($vcard->ADR) > 0) { foreach ($vcard->ADR as $adr) { $geo = $this->addressService->addressToGeo($adr->getValue(), $file->getId()); @@ -158,32 +143,27 @@ public function getContacts(): DataResponse { if (isset($adr->parameters()['TYPE'])) { $adrtype = $adr->parameters()['TYPE']->getValue(); } - if (is_string($geo) && strlen($geo) > 1) { + + if (strlen($geo) > 1) { $result[] = $this->vCardToArray($permissions, $file, $vcard, $geo, $adrtype, $adr->getValue(), $file->getId()); } } } } } + return new DataResponse($result); - } else { - throw new NotPermittedException(); } + + throw new NotPermittedException(); } /** - * @param int $sharePermissions - * @param Node $file - * @param \Sabre\VObject\Document $vcard - * @param string $geo - * @param string|null $adrtype - * @param string|null $adr - * @param int|null $fileId - * @return array * @throws NotFoundException * @throws \OCP\Files\InvalidPathException + * @return array */ - private function vCardToArray(int $sharePermissions, Node $file, \Sabre\VObject\Document $vcard, string $geo, ?string $adrtype = null, ?string $adr = null, ?int $fileId = null): array { + private function vCardToArray(int $sharePermissions, Node $file, Document $vcard, string $geo, ?string $adrtype = null, ?string $adr = null, ?int $fileId = null): array { $FNArray = $vcard->FN ? $vcard->FN->getJsonValue() : []; $fn = array_shift($FNArray); $NArray = $vcard->N ? $vcard->N->getJsonValue() : []; @@ -196,15 +176,12 @@ private function vCardToArray(int $sharePermissions, Node $file, \Sabre\VObject\ } } + $UIDArray = $vcard->UID->getJsonValue(); $uid = array_shift($UIDArray); $groups = $vcard->CATEGORIES; - if (!is_null($groups)) { - $groups = $groups->getValue(); - } else { - $groups = ''; - } - $result = [ + $groups = is_null($groups) ? '' : $groups->getValue(); + return [ 'FN' => $fn ?? $n ?? '???', 'UID' => $uid, 'HAS_PHOTO' => (isset($vcard->PHOTO) && $vcard->PHOTO !== null), @@ -218,24 +195,19 @@ private function vCardToArray(int $sharePermissions, Node $file, \Sabre\VObject\ 'isDeletable' => $file->isDeletable() && ($sharePermissions & (1 << 1)), 'isUpdateable' => $file->isUpdateable() && ($sharePermissions & (1 << 3)), ]; - return $result; } - /** - * @param string $n - * @return string|null - */ private function N2FN(string $n): ?string { - if ($n) { + if ($n !== '' && $n !== '0') { $spl = explode($n, ';'); if (count($spl) >= 4) { return $spl[3] . ' ' . $spl[1] . ' ' . $spl[0]; - } else { - return null; } - } else { + return null; } + + return null; } @@ -243,8 +215,6 @@ private function N2FN(string $n): ?string { * @PublicPage * @NoCSRFRequired * - * @param string $name - * @return DataDisplayResponse * @throws NotFoundException * @throws NotPermittedException */ diff --git a/lib/Controller/PublicFavoritePageController.php b/lib/Controller/PublicFavoritePageController.php index eb27fa55a..7cc659be2 100644 --- a/lib/Controller/PublicFavoritePageController.php +++ b/lib/Controller/PublicFavoritePageController.php @@ -1,5 +1,7 @@ * @@ -21,7 +23,6 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\Controller; use OCA\Maps\DB\FavoriteShareMapper; @@ -36,24 +37,21 @@ use OCP\IRequest; use OCP\ISession; use OCP\IUserManager; +use OCP\Server; use OCP\Util; class PublicFavoritePageController extends PublicShareController { private $config; - /* @var FavoriteShareMapper */ - private $favoriteShareMapper; - public function __construct( - $appName, + string $appName, IRequest $request, ISession $session, IConfig $config, - FavoriteShareMapper $favoriteShareMapper, + private readonly FavoriteShareMapper $favoriteShareMapper, ) { parent::__construct($appName, $request, $session); $this->config = $config; - $this->favoriteShareMapper = $favoriteShareMapper; } /** @@ -71,9 +69,9 @@ public function sharedFavoritesCategory($token) { try { $share = $this->favoriteShareMapper->findByToken($token); - } catch (DoesNotExistException $e) { + } catch (DoesNotExistException) { return new DataResponse([], Http::STATUS_NOT_FOUND); - } catch (MultipleObjectsReturnedException $e) { + } catch (MultipleObjectsReturnedException) { return new DataResponse([], Http::STATUS_INTERNAL_SERVER_ERROR); } @@ -82,7 +80,7 @@ public function sharedFavoritesCategory($token) { $response = new PublicTemplateResponse('maps', 'public/favorites_index', []); - $ownerName = \OCP\Server::get(IUserManager::class)->get($share->getOwner())->getDisplayName(); + $ownerName = Server::get(IUserManager::class)->get($share->getOwner())->getDisplayName(); $response->setHeaderTitle($share->getCategory()); $response->setHeaderDetails('shared by ' . $ownerName); @@ -109,13 +107,12 @@ protected function getPasswordHash(): string { * * This function is already called from the middleware directly after setting the token. * - * @return bool * @since 14.0.0 */ public function isValidToken(): bool { try { $this->favoriteShareMapper->findByToken($this->getToken()); - } catch (DoesNotExistException|MultipleObjectsReturnedException $e) { + } catch (DoesNotExistException|MultipleObjectsReturnedException) { return false; } @@ -125,7 +122,6 @@ public function isValidToken(): bool { /** * Is a share with this token password protected * - * @return bool * @since 14.0.0 */ protected function isPasswordProtected(): bool { @@ -134,7 +130,6 @@ protected function isPasswordProtected(): bool { /** * @param $response - * @return void */ private function addCsp($response): void { if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) { @@ -163,16 +158,18 @@ private function addCsp($response): void { foreach ($urlKeys as $key) { $url = $this->config->getAppValue('maps', $key); if ($url !== '') { - $scheme = parse_url($url, PHP_URL_SCHEME); - $host = parse_url($url, PHP_URL_HOST); - $port = parse_url($url, PHP_URL_PORT); + $scheme = parse_url((string)$url, PHP_URL_SCHEME); + $host = parse_url((string)$url, PHP_URL_HOST); + $port = parse_url((string)$url, PHP_URL_PORT); $cleanUrl = $scheme . '://' . $host; if ($port && $port !== '') { $cleanUrl .= ':' . $port; } + $csp->addAllowedConnectDomain($cleanUrl); } } + //$csp->addAllowedConnectDomain('http://192.168.0.66:5000'); // poi images diff --git a/lib/Controller/PublicFavoritesApiController.php b/lib/Controller/PublicFavoritesApiController.php index 8285d1adc..9d81e4c8e 100644 --- a/lib/Controller/PublicFavoritesApiController.php +++ b/lib/Controller/PublicFavoritesApiController.php @@ -1,5 +1,7 @@ * @@ -21,8 +23,6 @@ * along with this program. If not, see . * */ - - namespace OCA\Maps\Controller; use OCA\Maps\DB\FavoriteShareMapper; @@ -30,59 +30,42 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\PublicShareController; use OCP\IRequest; use OCP\ISession; class PublicFavoritesApiController extends PublicShareController { - /* @var FavoritesService */ - private $favoritesService; - - /* @var FavoriteShareMapper */ - private $favoriteShareMapper; - public function __construct( $appName, IRequest $request, ISession $session, - FavoritesService $favoritesService, - FavoriteShareMapper $favoriteShareMapper, + private readonly FavoritesService $favoritesService, + private readonly FavoriteShareMapper $favoriteShareMapper, ) { parent::__construct($appName, $request, $session); - - $this->favoriteShareMapper = $favoriteShareMapper; - $this->favoritesService = $favoritesService; } public function getPasswordHash(): string { return ''; } - /** - * @return bool - */ protected function isPasswordProtected(): bool { return false; } - /** - * @return bool - */ public function isValidToken(): bool { try { $this->favoriteShareMapper->findByToken($this->getToken()); - } catch (DoesNotExistException|MultipleObjectsReturnedException $e) { + } catch (DoesNotExistException|MultipleObjectsReturnedException) { return false; } return true; } - /** - * @return bool - */ - public function canEdit(): bool { + /* public function canEdit(): bool { try { $share = $this->favoriteShareMapper->findByToken($this->getToken()); } catch (DoesNotExistException|MultipleObjectsReturnedException $e) { @@ -90,19 +73,15 @@ public function canEdit(): bool { } return $share->getAllowEdits(); - } + } */ - /** - * @PublicPage - * - * @return DataResponse - */ + #[PublicPage] public function getFavorites(): DataResponse { try { $share = $this->favoriteShareMapper->findByToken($this->getToken()); - } catch (DoesNotExistException $e) { + } catch (DoesNotExistException) { return new DataResponse([], Http::STATUS_NOT_FOUND); - } catch (MultipleObjectsReturnedException $e) { + } catch (MultipleObjectsReturnedException) { return new DataResponse([], Http::STATUS_INTERNAL_SERVER_ERROR); } diff --git a/lib/Controller/PublicFavoritesController.php b/lib/Controller/PublicFavoritesController.php index 2df8f4c12..a9add97a9 100644 --- a/lib/Controller/PublicFavoritesController.php +++ b/lib/Controller/PublicFavoritesController.php @@ -1,5 +1,7 @@ favoritesService = $favoritesService; - $this->dateTimeZone = $dateTimeZone; - $this->appName = $appName; - $this->appVersion = $config->getAppValue('maps', 'installed_version'); + parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $appConfig, $initialStateService, $shareManager, $userManager); $this->userManager = $userManager; - $this->groupManager = $groupManager; - $this->l = $l; - $this->config = $config; $this->shareManager = $shareManager; - $this->favoriteShareMapper = $favoriteShareMapper; $this->defaultFavoritsJSON = json_encode([ 'type' => 'FeatureCollection', 'features' => [] @@ -85,10 +63,8 @@ public function __construct( /** * Validate the permissions of the share - * - * @return bool */ - private function validateShare(\OCP\Share\IShare $share) { + private function validateShare(IShare $share): bool { // If the owner is disabled no access to the link is granted $owner = $this->userManager->get($share->getShareOwner()); if ($owner === null || !$owner->isEnabled()) { @@ -105,14 +81,13 @@ private function validateShare(\OCP\Share\IShare $share) { } /** - * @return \OCP\Share\IShare * @throws NotFoundException */ - private function getShare() { + private function getShare(): IShare { // Check whether share exists try { $share = $this->shareManager->getShareByToken($this->getToken()); - } catch (ShareNotFound $e) { + } catch (ShareNotFound) { // The share does not exists, we do not emit an ShareLinkAccessedEvent throw new NotFoundException(); } @@ -120,11 +95,12 @@ private function getShare() { if (!$this->validateShare($share)) { throw new NotFoundException(); } + return $share; } /** - * @return \OCP\Files\File|\OCP\Files\Folder + * @return \OCP\Files\File|Folder * @throws NotFoundException */ private function getShareNode() { @@ -136,15 +112,12 @@ private function getShareNode() { } /** - * @param Folder $folder - * @param $isCreatable - * @return mixed * @throws NotPermittedException */ - private function getJSONFavoritesFile(\OCP\Files\Folder $folder, $isCreatable): \OCP\Files\Node { + private function getJSONFavoritesFile(Folder $folder, bool $isCreatable): Node { try { $file = $folder->get('.favorites.json'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { if ($isCreatable) { $file = $folder->newFile('.favorites.json', $content = $this->defaultFavoritsJSON); } else { @@ -152,15 +125,15 @@ private function getJSONFavoritesFile(\OCP\Files\Folder $folder, $isCreatable): } } + return $file; } /** - * @PublicPage - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException */ + #[PublicPage] public function getFavorites(): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); @@ -171,7 +144,7 @@ public function getFavorites(): DataResponse { $isReadable = (bool)($permissions & (1 << 0)); if ($isReadable) { $favorites = $this->favoritesService->getFavoritesFromJSON($file); - $favorites = array_map(function ($favorite) use ($permissions) { + $favorites = array_map(function (array $favorite) use ($permissions): array { $favorite['isCreatable'] = ($permissions & (1 << 2)) && $favorite['isCreatable']; $favorite['isUpdateable'] = ($permissions & (1 << 1)) && $favorite['isUpdateable']; $favorite['isDeletable'] = ($permissions & (1 << 3)) && $favorite['isDeletable']; @@ -180,24 +153,18 @@ public function getFavorites(): DataResponse { } else { throw new NotPermittedException(); } + return new DataResponse($favorites); } /** - * @PublicPage - * @param string|null $name - * @param float $lat - * @param float $lng - * @param string|null $category - * @param string|null $comment - * @param string|null $extensions - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException */ + #[PublicPage] public function addFavorite(?string $name, float $lat, float $lng, ?string $category, ?string $comment, ?string $extensions): DataResponse { - if (is_numeric($lat) && is_numeric($lng)) { + if (is_numeric($lng)) { $share = $this->getShare(); $permissions = $share->getPermissions(); $folder = $this->getShareNode(); @@ -211,20 +178,19 @@ public function addFavorite(?string $name, float $lat, float $lng, ?string $cate } else { throw new NotPermittedException(); } + return new DataResponse($favorite); - } else { - return new DataResponse($this->l->t('Invalid values'), 400); } + + return new DataResponse($this->l->t('Invalid values'), 400); } /** - * @PublicPage - * @param array $favorites - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException */ + #[PublicPage] public function addFavorites(array $favorites): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); @@ -240,92 +206,72 @@ public function addFavorites(array $favorites): DataResponse { $favorite['isDeletable'] = ($permissions & (1 << 3)) && $favorite['isDeletable']; $favoritesAfter[] = $favorite; } - }; + } + ; return new DataResponse($favoritesAfter); - } else { - throw new NotPermittedException(); } + + throw new NotPermittedException(); } /** - * @PublicPage - * @param int $id - * @param string|null $name - * @param float $lat - * @param float $lng - * @param string|null $category - * @param string|null $comment - * @param string|null $extensions - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException */ - public function editFavorite(int $id, ?string $name, float $lat, float $lng, ?string $category, ?string $comment, ?string $extensions): DataResponse { + #[PublicPage] + public function editFavorite(int $id, ?string $name, ?float $lat, ?float $lng, ?string $category, ?string $comment, ?string $extensions): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); $folder = $this->getShareNode(); $isCreatable = ($permissions & (1 << 2)) && $folder->isCreatable(); $file = $this->getJSONFavoritesFile($folder, $isCreatable); $isUpdateable = ($permissions & (1 << 1)) && $file->isUpdateable(); - if ($isUpdateable) { - $favorite = $this->favoritesService->getFavoriteFromJSON($file, $id); - if ($favorite !== null) { - if (($lat === null || is_numeric($lat)) - && ($lng === null || is_numeric($lng)) - ) { - $this->favoritesService->editFavoriteInJSON($file, $id, $name, $lat, $lng, $category, $comment, $extensions); - $editedFavorite = $this->favoritesService->getFavoriteFromJSON($file, $id); - $editedFavorite['isDeletable'] = ($permissions & (1 << 3)) && $editedFavorite['isDeletable']; - return new DataResponse($editedFavorite); - } else { - return new DataResponse($this->l->t('invalid values'), 400); - } - } else { - return new DataResponse($this->l->t('no such favorite'), 400); - } - } else { + if (!$isUpdateable) { throw new NotPermittedException(); } + + $favorite = $this->favoritesService->getFavoriteFromJSON($file, $id); + if ($favorite === null) { + return new DataResponse($this->l->t('no such favorite'), 400); + } + + $this->favoritesService->editFavoriteInJSON($file, $id, $name, $lat, $lng, $category, $comment, $extensions); + $editedFavorite = $this->favoritesService->getFavoriteFromJSON($file, $id); + $editedFavorite['isDeletable'] = ($permissions & (1 << 3)) && $editedFavorite['isDeletable']; + return new DataResponse($editedFavorite); } /** - * @PublicPage - * @param array $categories - * @param string $newName - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException */ + #[PublicPage] public function renameCategories(array $categories, string $newName): DataResponse { - if (is_array($categories)) { - $share = $this->getShare(); - $permissions = $share->getPermissions(); - $folder = $this->getShareNode(); - $isCreatable = ($permissions & (1 << 2)) && $folder->isCreatable(); - $file = $this->getJSONFavoritesFile($folder, $isCreatable); - $isUpdateable = ($permissions & (1 << 1)) && $file->isUpdateable(); - if ($isUpdateable) { - foreach ($categories as $cat) { - $this->favoritesService->renameCategoryInJSON($file, $cat, $newName); - } - } else { - throw new NotPermittedException(); + $share = $this->getShare(); + $permissions = $share->getPermissions(); + $folder = $this->getShareNode(); + $isCreatable = ($permissions & (1 << 2)) && $folder->isCreatable(); + $file = $this->getJSONFavoritesFile($folder, $isCreatable); + $isUpdateable = ($permissions & (1 << 1)) && $file->isUpdateable(); + if ($isUpdateable) { + foreach ($categories as $cat) { + $this->favoritesService->renameCategoryInJSON($file, $cat, $newName); } - return new DataResponse('RENAMED'); + } else { + throw new NotPermittedException(); } - throw new NotFoundException(); + + return new DataResponse('RENAMED'); } /** - * @PublicPage - * @param int $id - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException */ + #[PublicPage] public function deleteFavorite(int $id): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); @@ -337,21 +283,20 @@ public function deleteFavorite(int $id): DataResponse { if ($this->favoritesService->deleteFavoriteFromJSON($file, $id) > 0) { return new DataResponse('DELETED'); } + return new DataResponse($this->l->t('no such favorite'), 400); - } else { - throw new NotPermittedException(); } + throw new NotPermittedException(); + } /** - * @PublicPage - * @param array $ids - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException */ + #[PublicPage] public function deleteFavorites(array $ids): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); @@ -364,26 +309,27 @@ public function deleteFavorites(array $ids): DataResponse { } else { throw new NotPermittedException(); } + return new DataResponse('DELETED'); } /** - * @PublicPage - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException */ + #[PublicPage] public function getSharedCategories(): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); $folder = $this->getShareNode(); $isCreatable = ($permissions & (1 << 2)) && $folder->isCreatable(); $isReadable = ($permissions & (1 << 0)); - if ($isReadable) { + if ($isReadable !== 0) { $categories = $this->favoriteShareMapper->findAllByFolder($folder, $isCreatable); } else { throw new NotPermittedException(); } + return new DataResponse($categories); } } diff --git a/lib/Controller/PublicPageController.php b/lib/Controller/PublicPageController.php index 9026d6fa1..8a82502ee 100644 --- a/lib/Controller/PublicPageController.php +++ b/lib/Controller/PublicPageController.php @@ -1,5 +1,7 @@ * @copyright Vinzenz Rosenkranz 2017 */ - namespace OCA\Maps\Controller; use OC\Security\CSP\ContentSecurityPolicy; @@ -17,10 +18,12 @@ use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent; use OCA\Viewer\Event\LoadViewer; use OCP\AppFramework\AuthPublicShareController; +use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Template\PublicTemplateResponse; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Node; use OCP\Files\NotFoundException; -use OCP\IConfig; +use OCP\IAppConfig; use OCP\IInitialStateService; use OCP\IRequest; use OCP\ISession; @@ -39,23 +42,18 @@ public function __construct( ISession $session, IURLGenerator $urlGenerator, protected IEventDispatcher $eventDispatcher, - protected IConfig $config, + protected IAppConfig $appConfig, protected IInitialStateService $initialStateService, protected ShareManager $shareManager, protected IUserManager $userManager, ) { parent::__construct($appName, $request, $session, $urlGenerator); - $this->eventDispatcher = $eventDispatcher; - $this->config = $config; - $this->initialStateService = $initialStateService; - $this->shareManager = $shareManager; - $this->userManager = $userManager; } public function isValidToken(): bool { try { $this->share = $this->shareManager->getShareByToken($this->getToken()); - } catch (ShareNotFound $e) { + } catch (ShareNotFound) { return false; } @@ -76,10 +74,8 @@ protected function isPasswordProtected(): bool { /** * Validate the permissions of the share - * - * @return bool */ - private function validateShare(\OCP\Share\IShare $share) { + private function validateShare(IShare $share): bool { // If the owner is disabled no access to the link is granted $owner = $this->userManager->get($share->getShareOwner()); if ($owner === null || !$owner->isEnabled()) { @@ -96,16 +92,15 @@ private function validateShare(\OCP\Share\IShare $share) { } /** - * @return \OCP\Files\File|\OCP\Files\Folder * @throws NotFoundException */ - private function getShareNode() { + private function getShareNode(): Node { \OC_User::setIncognitoMode(true); // Check whether share exists try { $share = $this->shareManager->getShareByToken($this->getToken()); - } catch (ShareNotFound $e) { + } catch (ShareNotFound) { // The share does not exists, we do not emit an ShareLinkAccessedEvent throw new NotFoundException(); } @@ -122,14 +117,14 @@ private function getShareNode() { * @NoCSRFRequired */ public function showShare(): PublicTemplateResponse { - $shareNode = $this->getShareNode(); + $this->getShareNode(); - $this->eventDispatcher->dispatch(LoadSidebar::class, new LoadSidebar()); - $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer()); + $this->eventDispatcher->dispatchTyped(new LoadSidebar()); + $this->eventDispatcher->dispatchTyped(new LoadViewer()); $params = []; $params['sharingToken'] = $this->getToken(); - $this->initialStateService->provideInitialState($this->appName, 'photos', $this->config->getAppValue('photos', 'enabled', 'no') === 'yes'); + $this->initialStateService->provideInitialState($this->appName, 'photos', $this->appConfig->getValueBool('photos', 'enabled')); $response = new PublicTemplateResponse('maps', 'public/main', $params); $this->addCsp($response); @@ -199,58 +194,54 @@ protected function showIdentificationResult(bool $success = false): PublicTempla } - /** - * @param $response - * @return void - */ - private function addCsp($response): void { - if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) { - $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy(); - // map tiles - $csp->addAllowedImageDomain('https://*.tile.openstreetmap.org'); - $csp->addAllowedImageDomain('https://tile.openstreetmap.org'); - $csp->addAllowedImageDomain('https://server.arcgisonline.com'); - $csp->addAllowedImageDomain('https://*.cartocdn.com'); - $csp->addAllowedImageDomain('https://*.opentopomap.org'); - $csp->addAllowedImageDomain('https://*.cartocdn.com'); - $csp->addAllowedImageDomain('https://*.ssl.fastly.net'); - $csp->addAllowedImageDomain('https://*.openstreetmap.se'); - - // default routing engine - $csp->addAllowedConnectDomain('https://*.project-osrm.org'); - $csp->addAllowedConnectDomain('https://api.mapbox.com'); - $csp->addAllowedConnectDomain('https://events.mapbox.com'); - $csp->addAllowedConnectDomain('https://graphhopper.com'); - - $csp->addAllowedChildSrcDomain('blob:'); - $csp->addAllowedWorkerSrcDomain('blob:'); - $csp->addAllowedScriptDomain('https://unpkg.com'); - // allow connections to custom routing engines - $urlKeys = [ - 'osrmBikeURL', - 'osrmCarURL', - 'osrmFootURL', - 'graphhopperURL' - ]; - foreach ($urlKeys as $key) { - $url = $this->config->getAppValue('maps', $key); - if ($url !== '') { - $scheme = parse_url($url, PHP_URL_SCHEME); - $host = parse_url($url, PHP_URL_HOST); - $port = parse_url($url, PHP_URL_PORT); - $cleanUrl = $scheme . '://' . $host; - if ($port && $port !== '') { - $cleanUrl .= ':' . $port; - } - $csp->addAllowedConnectDomain($cleanUrl); + private function addCsp(Response $response): void { + $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy(); + // map tiles + $csp->addAllowedImageDomain('https://*.tile.openstreetmap.org'); + $csp->addAllowedImageDomain('https://tile.openstreetmap.org'); + $csp->addAllowedImageDomain('https://server.arcgisonline.com'); + $csp->addAllowedImageDomain('https://*.cartocdn.com'); + $csp->addAllowedImageDomain('https://*.opentopomap.org'); + $csp->addAllowedImageDomain('https://*.cartocdn.com'); + $csp->addAllowedImageDomain('https://*.ssl.fastly.net'); + $csp->addAllowedImageDomain('https://*.openstreetmap.se'); + + // default routing engine + $csp->addAllowedConnectDomain('https://*.project-osrm.org'); + $csp->addAllowedConnectDomain('https://api.mapbox.com'); + $csp->addAllowedConnectDomain('https://events.mapbox.com'); + $csp->addAllowedConnectDomain('https://graphhopper.com'); + + $csp->addAllowedChildSrcDomain('blob:'); + $csp->addAllowedWorkerSrcDomain('blob:'); + $csp->addAllowedScriptDomain('https://unpkg.com'); + // allow connections to custom routing engines + $urlKeys = [ + 'osrmBikeURL', + 'osrmCarURL', + 'osrmFootURL', + 'graphhopperURL' + ]; + foreach ($urlKeys as $key) { + $url = $this->appConfig->getValueString('maps', $key); + if ($url !== '') { + $scheme = parse_url($url, PHP_URL_SCHEME); + $host = parse_url($url, PHP_URL_HOST); + $port = parse_url($url, PHP_URL_PORT); + $cleanUrl = $scheme . '://' . $host; + if ($port && $port !== '') { + $cleanUrl .= ':' . $port; } - } - // poi images - $csp->addAllowedImageDomain('https://nominatim.openstreetmap.org'); - // search and geocoder - $csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org'); - $response->setContentSecurityPolicy($csp); + $csp->addAllowedConnectDomain($cleanUrl); + } } + + // poi images + $csp->addAllowedImageDomain('https://nominatim.openstreetmap.org'); + // search and geocoder + $csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org'); + + $response->setContentSecurityPolicy($csp); } } diff --git a/lib/Controller/PublicPhotosController.php b/lib/Controller/PublicPhotosController.php index 8d4477bdf..d1d784f58 100644 --- a/lib/Controller/PublicPhotosController.php +++ b/lib/Controller/PublicPhotosController.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\Controller; +use OC\User\NoUserException; use OCA\Maps\Service\GeophotoService; use OCA\Maps\Service\PhotofilesService; use OCP\AppFramework\Http\DataResponse; @@ -19,8 +21,7 @@ use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; -use OCP\IConfig; - +use OCP\IAppConfig; use OCP\IInitialStateService; use OCP\IRequest; use OCP\ISession; @@ -28,6 +29,7 @@ use OCP\IUserManager; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager as ShareManager; +use OCP\Share\IShare; class PublicPhotosController extends PublicPageController { @@ -37,7 +39,7 @@ public function __construct( ISession $session, IURLGenerator $urlGenerator, IEventDispatcher $eventDispatcher, - IConfig $config, + IAppConfig $appConfig, IInitialStateService $initialStateService, ShareManager $shareManager, IUserManager $userManager, @@ -45,7 +47,7 @@ public function __construct( protected PhotofilesService $photofilesService, protected IRootFolder $root, ) { - parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $config, $initialStateService, $shareManager, $userManager); + parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $appConfig, $initialStateService, $shareManager, $userManager); } /** @@ -53,7 +55,7 @@ public function __construct( * * @return bool */ - private function validateShare(\OCP\Share\IShare $share) { + private function validateShare(IShare $share) { // If the owner is disabled no access to the link is granted $owner = $this->userManager->get($share->getShareOwner()); if ($owner === null || !$owner->isEnabled()) { @@ -77,7 +79,7 @@ private function getShare() { // Check whether share exists try { $share = $this->shareManager->getShareByToken($this->getToken()); - } catch (ShareNotFound $e) { + } catch (ShareNotFound) { // The share does not exists, we do not emit an ShareLinkAccessedEvent throw new NotFoundException(); } @@ -85,6 +87,7 @@ private function getShare() { if (!$this->validateShare($share)) { throw new NotFoundException(); } + return $share; } @@ -102,10 +105,9 @@ private function getShareNode() { /** * @PublicPage - * @return DataResponse * @throws NotFoundException * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ public function getPhotos(): DataResponse { $share = $this->getShare(); @@ -116,7 +118,7 @@ public function getPhotos(): DataResponse { $owner = $share->getShareOwner(); $pre_path = $this->root->getUserFolder($owner)->getPath(); $result = $this->geophotoService->getAll($owner, $folder, true, false, false); - $photos = array_map(function ($photo) use ($folder, $permissions, $pre_path) { + $photos = array_map(function (array $photo) use ($folder, $permissions, $pre_path): \stdClass { $photo_object = (object)$photo; $photo_object->isCreatable = ($permissions & (1 << 2)) && $photo['isCreatable']; $photo_object->isUpdateable = ($permissions & (1 << 1)) && $photo['isUpdateable']; @@ -134,11 +136,10 @@ public function getPhotos(): DataResponse { /** * @PublicPage - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException - * @throws \OC\User\NoUserException + * @throws NoUserException */ public function getNonLocalizedPhotos(?string $timezone = null, int $limit = 250, int $offset = 0): DataResponse { $share = $this->getShare(); @@ -149,7 +150,7 @@ public function getNonLocalizedPhotos(?string $timezone = null, int $limit = 250 $owner = $share->getShareOwner(); $pre_path = $this->root->getUserFolder($owner)->getPath(); $result = $this->geophotoService->getNonLocalized($owner, $folder, true, false, false, $timezone, $limit, $offset); - $photos = array_map(function ($photo) use ($folder, $permissions, $pre_path) { + $photos = array_map(function (array $photo) use ($folder, $permissions, $pre_path): \stdClass { $photo_object = (object)$photo; $photo_object->isCreatable = ($permissions & (1 << 2)) && $photo['isCreatable']; $photo_object->isUpdateable = ($permissions & (1 << 1)) && $photo['isUpdateable']; @@ -167,15 +168,14 @@ public function getNonLocalizedPhotos(?string $timezone = null, int $limit = 250 /** * @PublicPage - * @return DataResponse */ public function clearCache(): DataResponse { $result = $this->geophotoService->clearCache(); if ($result) { return new DataResponse('Cache cleared'); - } else { - return new DataResponse('Failed to clear Cache', 400); } + + return new DataResponse('Failed to clear Cache', 400); } } diff --git a/lib/Controller/PublicTracksController.php b/lib/Controller/PublicTracksController.php index cc7834113..2a25199bc 100644 --- a/lib/Controller/PublicTracksController.php +++ b/lib/Controller/PublicTracksController.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; +use OC\User\NoUserException; use OCA\Maps\Service\TracksService; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\File; +use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; -use OCP\IConfig; +use OCP\IAppConfig; use OCP\IGroupManager; use OCP\IInitialStateService; use OCP\IL10N; use OCP\IRequest; -use OCP\IServerContainer; use OCP\ISession; use OCP\IURLGenerator; use OCP\IUserManager; @@ -31,46 +37,32 @@ use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager as ShareManager; +use OCP\Share\IShare; use function OCA\Maps\Helper\remove_utf8_bom; class PublicTracksController extends PublicPageController { - - protected IConfig $config; - protected ShareManager $shareManager; - protected IUserManager $userManager; - protected IL10N $l; - protected TracksService $tracksService; - protected $appName; - protected IRootFolder $root; - public function __construct( string $appName, IRequest $request, IEventDispatcher $eventDispatcher, - IConfig $config, + IAppConfig $appConfig, IInitialStateService $initialStateService, IURLGenerator $urlGenerator, ShareManager $shareManager, IUserManager $userManager, ISession $session, - IServerContainer $serverContainer, protected IGroupManager $groupManager, - IL10N $l, - TracksService $tracksService, - IRootFolder $root, + protected IL10N $l, + protected TracksService $tracksService, + protected IRootFolder $root, ) { - parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $config, $initialStateService, $shareManager, $userManager); - $this->tracksService = $tracksService; - $this->l = $l; - $this->root = $root; + parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $appConfig, $initialStateService, $shareManager, $userManager); } /** * Validate the permissions of the share - * - * @return bool */ - private function validateShare(\OCP\Share\IShare $share) { + private function validateShare(IShare $share): bool { // If the owner is disabled no access to the link is granted $owner = $this->userManager->get($share->getShareOwner()); if ($owner === null || !$owner->isEnabled()) { @@ -87,14 +79,13 @@ private function validateShare(\OCP\Share\IShare $share) { } /** - * @return \OCP\Share\IShare * @throws NotFoundException */ - private function getShare() { + private function getShare(): IShare { // Check whether share exists try { $share = $this->shareManager->getShareByToken($this->getToken()); - } catch (ShareNotFound $e) { + } catch (ShareNotFound) { // The share does not exists, we do not emit an ShareLinkAccessedEvent throw new NotFoundException(); } @@ -102,14 +93,14 @@ private function getShare() { if (!$this->validateShare($share)) { throw new NotFoundException(); } + return $share; } /** - * @return \OCP\Files\File|\OCP\Files\Folder * @throws NotFoundException */ - private function getShareNode() { + private function getShareNode(): Node { \OC_User::setIncognitoMode(true); $share = $this->getShare(); @@ -118,152 +109,134 @@ private function getShareNode() { } /** - * @PublicPage - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ + #[PublicPage] public function getTracks(): DataResponse { $share = $this->getShare(); $hideDownload = (bool)$share->getHideDownload(); $permissions = $share->getPermissions(); $folder = $this->getShareNode(); $isReadable = (bool)($permissions & (1 << 0)); - if ($isReadable) { - $owner = $share->getShareOwner(); - $pre_path = $this->root->getUserFolder($owner)->getPath(); - $tracks = $this->tracksService->getTracksFromDB($owner, $folder, true, false, false); - $new_tracks = array_map(function ($track) use ($folder, $permissions, $pre_path, $hideDownload) { - $track['isCreatable'] = ($permissions & (1 << 2)) && $track['isCreatable']; - $track['isUpdateable'] = ($permissions & (1 << 1)) && $track['isUpdateable']; - $track['isDeletable'] = ($permissions & (1 << 3)) && $track['isDeletable']; - $track['path'] = $folder->getRelativePath($pre_path . $track['path']); - $track['filename'] = $track['path']; - $track['hideDownload'] = $hideDownload; - return $track; - }, $tracks); - } else { + if (!$isReadable || !($folder instanceof Folder)) { throw new NotPermittedException(); } - return new DataResponse($new_tracks); + + $owner = $share->getShareOwner(); + $pre_path = $this->root->getUserFolder($owner)->getPath(); + $tracks = $this->tracksService->getTracksFromDB($owner, $folder, true, false, false); + $newTracks = array_map(function (array $track) use ($folder, $permissions, $pre_path, $hideDownload): array { + $track['isCreatable'] = ($permissions & (1 << 2)) && $track['isCreatable']; + $track['isUpdateable'] = ($permissions & (1 << 1)) && $track['isUpdateable']; + $track['isDeletable'] = ($permissions & (1 << 3)) && $track['isDeletable']; + $track['path'] = $folder->getRelativePath($pre_path . $track['path']); + $track['filename'] = $track['path']; + $track['hideDownload'] = $hideDownload; + return $track; + }, $tracks); + return new DataResponse($newTracks); } /** - * @PublicPage - * @param $id - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException * @throws \OCP\Files\InvalidPathException */ - public function getTrackContentByFileId($id) { + #[PublicPage] + public function getTrackContentByFileId(int $id): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); $folder = $this->getShareNode(); $isReadable = (bool)($permissions & (1 << 0)); - if (!$isReadable) { + if (!$isReadable || !($folder instanceof Folder)) { throw new NotPermittedException(); } + $owner = $share->getShareOwner(); $track = $this->tracksService->getTrackByFileIDFromDB($id, $owner); - $res = is_null($track) ? null : $folder->getById($track['file_id']); - if (is_array($res) and count($res) > 0) { - $trackFile = array_shift($res); - if ($trackFile->getType() === \OCP\Files\FileInfo::TYPE_FILE) { - $trackContent = remove_utf8_bom($trackFile->getContent()); - // compute metadata if necessary - // first time we get it OR the file changed - if (!$track['metadata'] || $track['etag'] !== $trackFile->getEtag()) { - $metadata = $this->tracksService->generateTrackMetadata($trackFile); - $this->tracksService->editTrackInDB($track['id'], null, $metadata, $trackFile->getEtag()); - } else { - $metadata = $track['metadata']; - } - return new DataResponse([ - 'metadata' => $metadata, - 'content' => $trackContent - ]); - } else { - return new DataResponse($this->l->t('Bad file type'), 400); - } - } else { + $trackFile = is_null($track) ? null : $folder->getFirstNodeById($track['file_id']); + if (!($trackFile instanceof File)) { return new DataResponse($this->l->t('File not found'), 400); } + + $trackContent = remove_utf8_bom($trackFile->getContent()); + // compute metadata if necessary + // first time we get it OR the file changed + if (!$track['metadata'] || $track['etag'] !== $trackFile->getEtag()) { + $metadata = $this->tracksService->generateTrackMetadata($trackFile); + $this->tracksService->editTrackInDB($track['id'], null, $metadata, $trackFile->getEtag()); + } else { + $metadata = $track['metadata']; + } + + return new DataResponse([ + 'metadata' => $metadata, + 'content' => $trackContent + ]); } /** - * @PublicPage - * @param $id - * @return DataResponse * @throws NotFoundException * @throws \OCP\Files\InvalidPathException */ - public function getTrackFileContent($id): DataResponse { + #[PublicPage] + public function getTrackFileContent(int $id): DataResponse { $track = $this->tracksService->getTrackFromDB($id); - $res = is_null($track) ? null : $this->getShareNode()->getById($track['file_id']); - if (is_array($res) and count($res) > 0) { - $trackFile = array_shift($res); - if ($trackFile->getType() === \OCP\Files\FileInfo::TYPE_FILE) { - $trackContent = remove_utf8_bom($trackFile->getContent()); - // compute metadata if necessary - // first time we get it OR the file changed - if (!$track['metadata'] || $track['etag'] !== $trackFile->getEtag()) { - $metadata = $this->tracksService->generateTrackMetadata($trackFile); - $this->tracksService->editTrackInDB($track['id'], null, $metadata, $trackFile->getEtag()); - } else { - $metadata = $track['metadata']; - } - return new DataResponse([ - 'metadata' => $metadata, - 'content' => $trackContent - ]); - } else { - return new DataResponse($this->l->t('Bad file type'), 400); - } - } else { + $shareNode = $this->getShareNode(); + if (!($shareNode instanceof Folder)) { return new DataResponse($this->l->t('File not found'), 400); } + + $trackFile = is_null($track) ? null : $shareNode->getFirstNodeById($track['file_id']); + if (!($trackFile instanceof File)) { + return new DataResponse($this->l->t('File not found'), 400); + } + + $trackContent = remove_utf8_bom($trackFile->getContent()); + // compute metadata if necessary + // first time we get it OR the file changed + if (!$track['metadata'] || $track['etag'] !== $trackFile->getEtag()) { + $metadata = $this->tracksService->generateTrackMetadata($trackFile); + $this->tracksService->editTrackInDB($track['id'], null, $metadata, $trackFile->getEtag()); + } else { + $metadata = $track['metadata']; + } + + return new DataResponse([ + 'metadata' => $metadata, + 'content' => $trackContent + ]); } /** - * @PublicPage - * @param $id - * @param $color - * @param $metadata - * @param $etag - * @return DataResponse * @throws NotFoundException * @throws NotPermittedException */ - public function editTrack($id, $color, $metadata, $etag): DataResponse { + #[PublicPage] + public function editTrack(int $id, string $color, string $metadata, string $etag): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); - $folder = $this->getShareNode(); $isUpdateable = (bool)($permissions & (1 << 1)); - if ($isUpdateable) { - $owner = $share->getShareOwner(); - $track = $this->tracksService->getTrackFromDB($id, $owner); - if ($track !== null) { - $this->tracksService->editTrackInDB($id, $color, $metadata, $etag); - return new DataResponse('EDITED'); - } else { - return new DataResponse($this->l->t('No such track'), 400); - } - } else { + if (!$isUpdateable) { throw new NotPermittedException(); } + + $owner = $share->getShareOwner(); + $track = $this->tracksService->getTrackFromDB($id, $owner); + if ($track !== null) { + $this->tracksService->editTrackInDB($id, $color, $metadata, $etag); + return new DataResponse('EDITED'); + } + + return new DataResponse($this->l->t('No such track'), 400); } - /** - * @NoAdminRequired - * @param $id - * @return DataResponse - */ - public function deleteTrack($id): DataResponse { + #[NoAdminRequired] + public function deleteTrack(int $id): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); - $folder = $this->getShareNode(); $isUpdateable = (bool)($permissions & (1 << 1)); //It's allowed to delete a track from the share, if the share is updateable if ($isUpdateable) { @@ -272,12 +245,11 @@ public function deleteTrack($id): DataResponse { if ($track !== null) { $this->tracksService->deleteTrackFromDB($id); return new DataResponse('DELETED'); - } else { - return new DataResponse($this->l->t('No such track'), 400); } - } else { - throw new NotPermittedException(); + + return new DataResponse($this->l->t('No such track'), 400); } - } + throw new NotPermittedException(); + } } diff --git a/lib/Controller/PublicUtilsController.php b/lib/Controller/PublicUtilsController.php index 16f239fc9..87d38c853 100644 --- a/lib/Controller/PublicUtilsController.php +++ b/lib/Controller/PublicUtilsController.php @@ -1,5 +1,7 @@ 2023 */ - namespace OCA\Maps\Controller; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\GenericFileException; @@ -20,7 +22,7 @@ use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; -use OCP\IConfig; +use OCP\IAppConfig; use OCP\IInitialStateService; use OCP\IRequest; use OCP\ISession; @@ -29,25 +31,22 @@ use OCP\Lock\LockedException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager as ShareManager; +use OCP\Share\IShare; class PublicUtilsController extends PublicPageController { - - protected IRootFolder $root; - public function __construct( string $appName, IRequest $request, ISession $session, IURLGenerator $urlGenerator, - IConfig $config, + IAppConfig $appConfig, IInitialStateService $initialStateService, IUserManager $userManager, ShareManager $shareManager, - IRootFolder $root, + protected IRootFolder $root, IEventDispatcher $eventDispatcher, ) { - parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $config, $initialStateService, $shareManager, $userManager); - $this->root = $root; + parent::__construct($appName, $request, $session, $urlGenerator, $eventDispatcher, $appConfig, $initialStateService, $shareManager, $userManager); } /** @@ -55,7 +54,7 @@ public function __construct( * * @return bool */ - private function validateShare(\OCP\Share\IShare $share) { + private function validateShare(IShare $share) { // If the owner is disabled no access to the link is granted $owner = $this->userManager->get($share->getShareOwner()); if ($owner === null || !$owner->isEnabled()) { @@ -79,7 +78,7 @@ private function getShare() { // Check whether share exists try { $share = $this->shareManager->getShareByToken($this->getToken()); - } catch (ShareNotFound $e) { + } catch (ShareNotFound) { // The share does not exists, we do not emit an ShareLinkAccessedEvent throw new NotFoundException(); } @@ -87,6 +86,7 @@ private function getShare() { if (!$this->validateShare($share)) { throw new NotFoundException(); } + return $share; } @@ -105,15 +105,13 @@ private function getShareNode() { /** * Save options values to the DB for current user * - * @PublicPage * @param $options - * @param null $myMapId - * @return DataResponse * @throws NotFoundException * @throws GenericFileException * @throws InvalidPathException * @throws NotPermittedException */ + #[PublicPage] public function saveOptionValue($options, $myMapId = null): DataResponse { $share = $this->getShare(); $permissions = $share->getPermissions(); @@ -122,40 +120,42 @@ public function saveOptionValue($options, $myMapId = null): DataResponse { try { $file = $folder->get('.index.maps'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { if ($isCreatable) { $file = $folder->newFile('.index.maps', $content = '{}'); } else { throw new NotFoundException(); } } + $isUpdateable = ($permissions & (1 << 1)) && $file->isUpdateable(); if (!$isUpdateable) { throw new NotPermittedException(); } try { - $ov = json_decode($file->getContent(), true, 512); + $ov = json_decode((string)$file->getContent(), true, 512); foreach ($options as $key => $value) { $ov[$key] = $value; } + $file->putContent(json_encode($ov, JSON_PRETTY_PRINT)); - } catch (LockedException $e) { + } catch (LockedException) { return new DataResponse('File is locked', 500); } + return new DataResponse(['done' => 1]); } /** * get options values from the config for current user * - * @PublicPage - * @return DataResponse * @throws InvalidPathException * @throws LockedException * @throws NotFoundException * @throws NotPermittedException */ + #[PublicPage] public function getOptionsValues(): DataResponse { $ov = []; @@ -165,14 +165,15 @@ public function getOptionsValues(): DataResponse { $isCreatable = ($permissions & (1 << 2)) && $folder->isCreatable(); try { $file = $folder->get('.index.maps'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { if ($isCreatable) { $file = $folder->newFile('.index.maps', $content = '{}'); } else { throw new NotFoundException(); } } - $ov = json_decode($file->getContent(), true, 512); + + $ov = json_decode((string)$file->getContent(), true, 512); // Maps content can be read mostly from the folder $ov['isReadable'] = ($permissions & (1 << 0)) && $folder->isReadable(); @@ -198,9 +199,10 @@ public function getOptionsValues(): DataResponse { 'graphhopperURL' ]; foreach ($settingsKeys as $k) { - $v = $this->config->getAppValue('maps', $k); + $v = $this->appConfig->getValueString('maps', $k); $ov[$k] = $v; } + return new DataResponse(['values' => $ov]); } @@ -208,8 +210,6 @@ public function getOptionsValues(): DataResponse { /** * get content of mapbox traffic style * @PublicPage - * - * @return DataResponse */ public function getTrafficStyle(): DataResponse { $style = [ diff --git a/lib/Controller/RoutingController.php b/lib/Controller/RoutingController.php index e4763e544..1b9e61acc 100644 --- a/lib/Controller/RoutingController.php +++ b/lib/Controller/RoutingController.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\Service\TracksService; -use OCP\App\IAppManager; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; -use OCP\IConfig; -use OCP\IDateTimeZone; -use OCP\IGroupManager; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\IAppConfig; use OCP\IL10N; use OCP\IRequest; -use OCP\IServerContainer; -use OCP\IUserManager; -use OCP\Share\IManager; class RoutingController extends Controller { + private ?Folder $userfolder = null; - private $userId; - private $userfolder; - private $config; - private $appVersion; - private $shareManager; - private $userManager; - private $groupManager; - private $dbtype; - private $dbdblquotes; - private $defaultDeviceId; - private $l; - private $dateTimeZone; - private $tracksService; - protected $appName; - - public function __construct($AppName, + private readonly string $appVersion; + + public function __construct( + string $appName, IRequest $request, - IServerContainer $serverContainer, - IConfig $config, - IManager $shareManager, - IAppManager $appManager, - IUserManager $userManager, - IGroupManager $groupManager, - IL10N $l, - IDateTimeZone $dateTimeZone, - TracksService $tracksService, - $UserId) { - parent::__construct($AppName, $request); - $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->dbtype = $config->getSystemValue('dbtype'); + IAppConfig $appConfig, + private readonly IL10N $l, + private readonly TracksService $tracksService, + IRootFolder $rootFolder, + private readonly ?string $userId, + ) { + parent::__construct($appName, $request); + $this->appVersion = $appConfig->getValueString('maps', 'installed_version'); // IConfig object - $this->config = $config; - if ($UserId !== '' and $UserId !== null and $serverContainer !== null) { + if ($this->userId !== '' && $this->userId !== null) { // path of user files folder relative to DATA folder - $this->userfolder = $serverContainer->getUserFolder($UserId); + $this->userfolder = $rootFolder->getUserFolder($userId); } - $this->shareManager = $shareManager; - $this->tracksService = $tracksService; } /** - * @NoAdminRequired * @param $type * @param $coords * @param $name * @param $totDist * @param $totTime - * @return DataResponse * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException */ + #[NoAdminRequired] public function exportRoute($type, $coords, $name, $totDist, $totTime, $myMapId = null): DataResponse { // create /Maps directory if necessary $userFolder = $this->userfolder; @@ -91,29 +63,23 @@ public function exportRoute($type, $coords, $name, $totDist, $totTime, $myMapId if (!$userFolder->nodeExists('/Maps')) { $userFolder->newFolder('Maps'); } + if ($userFolder->nodeExists('/Maps')) { $mapsFolder = $userFolder->get('/Maps'); - if ($mapsFolder->getType() !== \OCP\Files\FileInfo::TYPE_FOLDER) { - $response = new DataResponse($this->l->t('/Maps is not a directory'), 400); - return $response; - } elseif (!$mapsFolder->isCreatable()) { - $response = new DataResponse($this->l->t('/Maps directory is not writeable'), 400); - return $response; + if (!$mapsFolder instanceof Folder) { + return new DataResponse($this->l->t('/Maps is not a directory'), 400); + } + + if (!$mapsFolder->isCreatable()) { + return new DataResponse($this->l->t('/Maps directory is not writeable'), 400); } } else { - $response = new DataResponse($this->l->t('Impossible to create /Maps directory'), 400); - return $response; + return new DataResponse($this->l->t('Impossible to create /Maps directory'), 400); } } else { - $folders = $userFolder->getById($myMapId); - if (!is_array($folders) or count($folders) === 0) { - $response = new DataResponse('myMaps Folder not found', 404); - return $response; - } - $mapsFolder = array_shift($folders); - if (is_null($mapsFolder)) { - $response = new DataResponse('myMaps Folder not found', 404); - return $response; + $folder = $userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + return new DataResponse('myMaps Folder not found', 404); } } @@ -121,9 +87,11 @@ public function exportRoute($type, $coords, $name, $totDist, $totTime, $myMapId if ($mapsFolder->nodeExists($filename)) { $mapsFolder->get($filename)->delete(); } + if ($mapsFolder->nodeExists($filename . '.tmp')) { $mapsFolder->get($filename . '.tmp')->delete(); } + $file = $mapsFolder->newFile($filename . 'tmp'); $fileHandler = $file->fopen('w'); @@ -145,6 +113,7 @@ public function exportRoute($type, $coords, $name, $totDist, $totTime, $myMapId $line = ' ' . "\n"; fwrite($fileHandler, $line); } + fwrite($fileHandler, ' ' . "\n"); } elseif ($type === 'track') { fwrite($fileHandler, ' ' . "\n"); @@ -154,13 +123,16 @@ public function exportRoute($type, $coords, $name, $totDist, $totTime, $myMapId $line = ' ' . "\n"; fwrite($fileHandler, $line); } + fwrite($fileHandler, ' ' . "\n"); fwrite($fileHandler, ' ' . "\n"); } + fwrite($fileHandler, '' . "\n"); fclose($fileHandler); $file->touch(); - $file->move(substr($file->getPath(), 0, -3)); + $file->move(substr((string)$file->getPath(), 0, -3)); + $track = $this->tracksService->getTrackByFileIDFromDB($file->getId(), $this->userId); return new DataResponse($track); } diff --git a/lib/Controller/TracksController.php b/lib/Controller/TracksController.php index 5ce31a115..570b8e6c7 100644 --- a/lib/Controller/TracksController.php +++ b/lib/Controller/TracksController.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\Service\TracksService; -use OCP\App\IAppManager; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; -use OCP\IConfig; -use OCP\IGroupManager; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; use OCP\IL10N; use OCP\IRequest; -use OCP\IServerContainer; -use OCP\IUserManager; -use OCP\Share\IManager; use function OCA\Maps\Helper\remove_utf8_bom; class TracksController extends Controller { + private ?Folder $userfolder = null; - private $userId; - private $userfolder; - private $config; - private $appVersion; - private $shareManager; - private $userManager; - private $groupManager; - private $dbtype; - private $dbdblquotes; - private $l; - private $tracksService; - protected $appName; - - public function __construct($AppName, + public function __construct( + string $appName, IRequest $request, - IServerContainer $serverContainer, - IConfig $config, - IManager $shareManager, - IAppManager $appManager, - IUserManager $userManager, - IGroupManager $groupManager, - IL10N $l, - TracksService $tracksService, - $UserId) { - parent::__construct($AppName, $request); - $this->tracksService = $tracksService; - $this->appName = $AppName; - $this->appVersion = $config->getAppValue('maps', 'installed_version'); - $this->userId = $UserId; - $this->userManager = $userManager; - $this->groupManager = $groupManager; - $this->l = $l; - $this->dbtype = $config->getSystemValue('dbtype'); - $this->config = $config; - if ($UserId !== '' and $UserId !== null and $serverContainer !== null) { - $this->userfolder = $serverContainer->getUserFolder($UserId); + private readonly IL10N $l, + private readonly TracksService $tracksService, + IRootFolder $rootFolder, + private readonly ?string $userId, + ) { + parent::__construct($appName, $request); + if ($this->userId !== '' && $this->userId !== null) { + $this->userfolder = $rootFolder->getUserFolder($this->userId); } - $this->shareManager = $shareManager; } /** - * @NoAdminRequired - * @return DataResponse * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\NotFoundException */ - public function getTracks($myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + #[NoAdminRequired] + public function getTracks(?int $myMapId = null): DataResponse { + if (is_null($myMapId)) { $tracks = $this->tracksService->getTracksFromDB($this->userId, $this->userfolder, true, false, true); } else { $folders = $this->userfolder->getById($myMapId); $folder = array_shift($folders); $tracks = $this->tracksService->getTracksFromDB($this->userId, $folder, true, false, false); } + return new DataResponse($tracks); } - /** - * @NoAdminRequired - */ - public function getTrackContentByFileId($id) { + #[NoAdminRequired] + public function getTrackContentByFileId(int $id): DataResponse { $track = $this->tracksService->getTrackByFileIDFromDB($id, $this->userId); - $res = is_null($track) ? null : $this->userfolder->getById($track['file_id']); - if (is_array($res) and count($res) > 0) { - $trackFile = $res[0]; - if ($trackFile->getType() === \OCP\Files\FileInfo::TYPE_FILE) { - $trackContent = remove_utf8_bom($trackFile->getContent()); - // compute metadata if necessary - // first time we get it OR the file changed - if (!$track['metadata'] || $track['etag'] !== $trackFile->getEtag()) { - $metadata = $this->tracksService->generateTrackMetadata($trackFile); - $this->tracksService->editTrackInDB($track['id'], null, $metadata, $trackFile->getEtag()); - } else { - $metadata = $track['metadata']; - } - return new DataResponse([ - 'metadata' => $metadata, - 'content' => $trackContent - ]); - } else { - return new DataResponse($this->l->t('Bad file type'), 400); - } - } else { + $trackFile = is_null($track) ? null : $this->userfolder->getFirstNodeById($track['file_id']); + if (!$trackFile instanceof File) { return new DataResponse($this->l->t('File not found'), 400); } + + $trackContent = remove_utf8_bom($trackFile->getContent()); + // compute metadata if necessary + // first time we get it OR the file changed + if (!$track['metadata'] || $track['etag'] !== $trackFile->getEtag()) { + $metadata = $this->tracksService->generateTrackMetadata($trackFile); + $this->tracksService->editTrackInDB($track['id'], null, $metadata, $trackFile->getEtag()); + } else { + $metadata = $track['metadata']; + } + + return new DataResponse([ + 'metadata' => $metadata, + 'content' => $trackContent + ]); } /** - * @NoAdminRequired - * @param $id - * @return DataResponse * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\NotFoundException */ - public function getTrackFileContent($id): DataResponse { + #[NoAdminRequired] + public function getTrackFileContent(int $id): DataResponse { $track = $this->tracksService->getTrackFromDB($id); - $res = is_null($track) ? null : $this->userfolder->getById($track['file_id']); - if (is_array($res) and count($res) > 0) { - $trackFile = $res[0]; - if ($trackFile->getType() === \OCP\Files\FileInfo::TYPE_FILE) { - $trackContent = remove_utf8_bom($trackFile->getContent()); - // compute metadata if necessary - // first time we get it OR the file changed - if (!$track['metadata'] || $track['etag'] !== $trackFile->getEtag()) { - $metadata = $this->tracksService->generateTrackMetadata($trackFile); - $this->tracksService->editTrackInDB($track['id'], null, $metadata, $trackFile->getEtag()); - } else { - $metadata = $track['metadata']; - } - return new DataResponse([ - 'metadata' => $metadata, - 'content' => $trackContent - ]); - } else { - return new DataResponse($this->l->t('Bad file type'), 400); - } - } else { + $trackFile = is_null($track) ? null : $this->userfolder->getFirstNodeById($track['file_id']); + if (!$trackFile instanceof File) { return new DataResponse($this->l->t('File not found'), 400); } + + $trackContent = remove_utf8_bom($trackFile->getContent()); + // compute metadata if necessary + // first time we get it OR the file changed + if (!$track['metadata'] || $track['etag'] !== $trackFile->getEtag()) { + $metadata = $this->tracksService->generateTrackMetadata($trackFile); + $this->tracksService->editTrackInDB($track['id'], null, $metadata, $trackFile->getEtag()); + } else { + $metadata = $track['metadata']; + } + + return new DataResponse([ + 'metadata' => $metadata, + 'content' => $trackContent + ]); } - /** - * @NoAdminRequired - * @param $id - * @param $color - * @param $metadata - * @param $etag - * @return DataResponse - */ - public function editTrack($id, $color, $metadata, $etag): DataResponse { + #[NoAdminRequired] + public function editTrack(int $id, ?string $color, ?string $metadata, ?string $etag): DataResponse { $track = $this->tracksService->getTrackFromDB($id, $this->userId); if ($track !== null) { $this->tracksService->editTrackInDB($id, $color, $metadata, $etag); return new DataResponse('EDITED'); - } else { - return new DataResponse($this->l->t('No such track'), 400); } + + return new DataResponse($this->l->t('No such track'), 400); } - /** - * @NoAdminRequired - * @param $id - * @return DataResponse - */ - public function deleteTrack($id): DataResponse { + #[NoAdminRequired] + public function deleteTrack(int $id): DataResponse { $track = $this->tracksService->getTrackFromDB($id, $this->userId); if ($track !== null) { $this->tracksService->deleteTrackFromDB($id); return new DataResponse('DELETED'); - } else { - return new DataResponse($this->l->t('No such track'), 400); } + + return new DataResponse($this->l->t('No such track'), 400); } } diff --git a/lib/Controller/UtilsController.php b/lib/Controller/UtilsController.php index 1d87b94c4..c530f3d74 100644 --- a/lib/Controller/UtilsController.php +++ b/lib/Controller/UtilsController.php @@ -1,5 +1,7 @@ 2023 */ - namespace OCA\Maps\Controller; -use OCP\App\IAppManager; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\DataResponse; +use OCP\Files\File; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IRequest; use OCP\Lock\LockedException; class UtilsController extends Controller { - - - private $userId; - private $config; - private $root; - - public function __construct($AppName, + public function __construct( + string $appName, IRequest $request, - IConfig $config, - IAppManager $appManager, - IRootFolder $root, - $UserId) { - parent::__construct($AppName, $request); - $this->root = $root; - $this->userId = $UserId; - // IConfig object - $this->config = $config; + private readonly IAppConfig $appConfig, + private readonly IConfig $config, + private readonly IRootFolder $root, + private readonly string $userId, + ) { + parent::__construct($appName, $request); } /** * Save options values to the DB for current user * - * @NoAdminRequired * @param $options - * @return DataResponse * @throws \OCP\PreConditionNotMetException */ - public function saveOptionValue($options, $myMapId = null): DataResponse { - if (is_null($myMapId) || $myMapId === '') { + #[NoAdminRequired] + public function saveOptionValue($options, ?int $myMapId = null): DataResponse { + if (is_null($myMapId)) { foreach ($options as $key => $value) { $this->config->setUserValue($this->userId, 'maps', $key, $value); } } else { $userFolder = $this->root->getUserFolder($this->userId); - $folders = $userFolder->getById($myMapId); - $folder = array_shift($folders); + $folder = $userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + throw new NotFoundException('Could find map with mapid: ' . $myMapId); + } + try { + /** @var File $file */ $file = $folder->get('.index.maps'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { $file = $folder->newFile('.index.maps', $content = '{}'); } + try { - $ov = json_decode($file->getContent(), true, 512); + $ov = json_decode((string)$file->getContent(), true, 512); foreach ($options as $key => $value) { $ov[$key] = $value; } + $file->putContent(json_encode($ov, JSON_PRETTY_PRINT)); - } catch (LockedException $e) { + } catch (LockedException) { return new DataResponse('File is locked', 500); } } + return new DataResponse(['done' => 1]); } /** - * get options values from the config for current user - * - * @NoAdminRequired - * @return DataResponse + * Get options values from the config for current user */ + #[NoAdminRequired] public function getOptionsValues($myMapId = null): DataResponse { $ov = []; @@ -98,6 +99,7 @@ public function getOptionsValues($myMapId = null): DataResponse { $value = $this->config->getUserValue($this->userId, 'maps', $key); $ov[$key] = $value; } + $ov['isCreatable'] = true; $ov['isDeletable'] = false; $ov['isReadable'] = true; @@ -105,14 +107,19 @@ public function getOptionsValues($myMapId = null): DataResponse { $ov['isShareable'] = true; } else { $userFolder = $this->root->getUserFolder($this->userId); - $folders = $userFolder->getById($myMapId); - $folder = array_shift($folders); + $folder = $userFolder->getFirstNodeById($myMapId); + if (!$folder instanceof Folder) { + throw new NotFoundException('Could find map with mapid: ' . $myMapId); + } + try { + /** @var File $file */ $file = $folder->get('.index.maps'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { $file = $folder->newFile('.index.maps', $content = '{}'); } - $ov = json_decode($file->getContent(), true, 512); + + $ov = json_decode((string)$file->getContent(), true, 512); $ov['isCreatable'] = $folder->isCreatable(); //We can delete the map by deleting the folder or the .index.maps file $ov['isDeletable'] = $folder->isDeletable() || $file->isDeletable(); @@ -137,9 +144,10 @@ public function getOptionsValues($myMapId = null): DataResponse { 'graphhopperURL' ]; foreach ($settingsKeys as $k) { - $v = $this->config->getAppValue('maps', $k); + $v = $this->appConfig->getValueString('maps', $k); $ov[$k] = $v; } + return new DataResponse(['values' => $ov]); } @@ -147,7 +155,6 @@ public function getOptionsValues($myMapId = null): DataResponse { * set routing settings * * @param $values - * @return DataResponse */ public function setRoutingSettings($values): DataResponse { $acceptedKeys = [ @@ -163,9 +170,10 @@ public function setRoutingSettings($values): DataResponse { ]; foreach ($values as $k => $v) { if (in_array($k, $acceptedKeys)) { - $this->config->setAppValue('maps', $k, $v); + $this->appConfig->setValueString('maps', $k, $v); } } + $response = new DataResponse('DONE'); $csp = new ContentSecurityPolicy(); $csp->addAllowedImageDomain('*') @@ -176,12 +184,10 @@ public function setRoutingSettings($values): DataResponse { } /** - * get content of mapbox traffic style - * @NoAdminRequired - * @NoCSRFRequired - * - * @return DataResponse + * Get content of mapbox traffic style */ + #[NoAdminRequired] + #[NoCSRFRequired] public function getTrafficStyle(): DataResponse { $style = [ 'version' => 8, diff --git a/lib/DB/DeviceShare.php b/lib/DB/DeviceShare.php index 207b370fe..6df1d2967 100644 --- a/lib/DB/DeviceShare.php +++ b/lib/DB/DeviceShare.php @@ -1,5 +1,7 @@ * @@ -21,7 +23,6 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\DB; use OCP\AppFramework\Db\Entity; @@ -29,18 +30,21 @@ /** * @method string getToken() - * @method string getDeviceId() - * @method string getTimestampFrom() - * @method string getTimestampTo() - * @method string setToken(string $token) - * @method string setDeviceId(int $deviceId) - * @method string setTimestampFrom(int $timestampFrom) - * @method string setTimestampTo(int $timestampTo) + * @method int getDeviceId() + * @method int getTimestampFrom() + * @method int getTimestampTo() + * @method void setToken(string $token) + * @method void setDeviceId(int $deviceId) + * @method void setTimestampFrom(int $timestampFrom) + * @method void setTimestampTo(int $timestampTo) */ class DeviceShare extends Entity { public $token; + public $deviceId; + public $timestampFrom; + public $timestampTo; public function __construct() { diff --git a/lib/DB/DeviceShareMapper.php b/lib/DB/DeviceShareMapper.php index 8a2238aea..9bd30aa6a 100644 --- a/lib/DB/DeviceShareMapper.php +++ b/lib/DB/DeviceShareMapper.php @@ -1,5 +1,7 @@ * @@ -21,7 +23,6 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\DB; use OC\Share\Constants; @@ -31,7 +32,6 @@ use OCP\AppFramework\Db\QBMapper; use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\IDBConnection; use OCP\Security\ISecureRandom; @@ -40,22 +40,18 @@ class DeviceShareMapper extends QBMapper { /* @var ISecureRandom */ private $secureRandom; - private $root; - public function __construct(IDBConnection $db, ISecureRandom $secureRandom, IRootFolder $root) { + public function __construct(IDBConnection $db, ISecureRandom $secureRandom) { parent::__construct($db, 'maps_device_shares'); $this->secureRandom = $secureRandom; - $this->root = $root; } /** - * @param string $token - * @return DeviceShare|null * @throws DoesNotExistException * @throws MultipleObjectsReturnedException */ - public function findByToken($token) { + public function findByToken(string $token): DeviceShare { $qb = $this->db->getQueryBuilder(); $qb->select('*') @@ -212,6 +208,7 @@ public function removeById($id) { } catch (DoesNotExistException) { return false; } + return true; } @@ -228,6 +225,7 @@ public function removeAllByDeviceId($deviceId) { } catch (DoesNotExistException) { return false; } + return true; } } diff --git a/lib/DB/FavoriteShare.php b/lib/DB/FavoriteShare.php index b07de4073..a63d376e2 100644 --- a/lib/DB/FavoriteShare.php +++ b/lib/DB/FavoriteShare.php @@ -1,5 +1,7 @@ * @@ -21,7 +23,6 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\DB; use OCP\AppFramework\Db\Entity; @@ -38,8 +39,11 @@ */ class FavoriteShare extends Entity { public $owner; + public $token; + public $category; + public $allowEdits = false; public function __construct() { diff --git a/lib/DB/FavoriteShareMapper.php b/lib/DB/FavoriteShareMapper.php index 105be5fdd..34d20b294 100644 --- a/lib/DB/FavoriteShareMapper.php +++ b/lib/DB/FavoriteShareMapper.php @@ -1,5 +1,7 @@ * @@ -21,15 +23,16 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\DB; use OC\Share\Constants; +use OC\User\NoUserException; use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\File; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\IDBConnection; @@ -37,24 +40,19 @@ /** @template-extends QBMapper */ class FavoriteShareMapper extends QBMapper { - /* @var ISecureRandom */ - private $secureRandom; - private $root; - - public function __construct(IDBConnection $db, ISecureRandom $secureRandom, IRootFolder $root) { + public function __construct( + IDBConnection $db, + private readonly ISecureRandom $secureRandom, + private readonly IRootFolder $root, + ) { parent::__construct($db, 'maps_favorite_shares'); - - $this->secureRandom = $secureRandom; - $this->root = $root; } /** - * @param $token - * @return Entity|null * @throws DoesNotExistException * @throws MultipleObjectsReturnedException */ - public function findByToken($token) { + public function findByToken(string $token): FavoriteShare { $qb = $this->db->getQueryBuilder(); $qb->select('*') @@ -66,12 +64,7 @@ public function findByToken($token) { return $this->findEntity($qb); } - /** - * @param $owner - * @param $category - * @return Entity - */ - public function create($owner, $category) { + public function create(string $owner, string $category): FavoriteShare { $token = $this->secureRandom->generate( Constants::TOKEN_LENGTH, ISecureRandom::CHAR_HUMAN_READABLE @@ -86,10 +79,9 @@ public function create($owner, $category) { } /** - * @param $owner - * @return array|Entity[] + * @return FavoriteShare[] */ - public function findAllByOwner($owner) { + public function findAllByOwner(string $owner): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') @@ -102,53 +94,44 @@ public function findAllByOwner($owner) { } /** - * @param $userId - * @param $mapId - * @return array|mixed * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ - public function findAllByMapId($userId, $mapId) { + public function findAllByMapId(string $userId, int $mapId): array { $userFolder = $this->root->getUserFolder($userId); - $folders = $userFolder->getById($mapId); + $folder = $userFolder->getFirstNodeById($mapId); $shares = []; - if (empty($folders)) { - return $shares; - } - $folder = array_shift($folders); - if ($folder === null) { + if (!$folder instanceof Folder) { return $shares; } + return $this->findAllByFolder($folder); } /** - * @param $folder - * @param $isCreatable - * @return mixed * @throws NotFoundException */ - public function findAllByFolder($folder, $isCreatable = true) { + public function findAllByFolder(Folder $folder, bool $isCreatable = true): array { try { + /** @var File $file */ $file = $folder->get('.favorite_shares.json'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { if ($isCreatable) { - $file = $folder->newFile('.favorite_shares.json', $content = '[]'); - } else { - throw new NotFoundException(); + $folder->newFile('.favorite_shares.json', '[]'); + return []; } + + throw new NotFoundException(); } - return json_decode($file->getContent(), true); + + return json_decode((string)$file->getContent(), true); } /** - * @param $owner - * @param $category - * @return Entity * @throws DoesNotExistException * @throws MultipleObjectsReturnedException */ - public function findByOwnerAndCategory($owner, $category) { + public function findByOwnerAndCategory(string $owner, string $category): FavoriteShare { $qb = $this->db->getQueryBuilder(); $qb->select('*') @@ -163,41 +146,38 @@ public function findByOwnerAndCategory($owner, $category) { } /** - * @param $userId - * @param $mapId * @param $category * @return mixed|null * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ - public function findByMapIdAndCategory($userId, $mapId, $category) { + public function findByMapIdAndCategory(string $userId, int $mapId, $category) { $shares = $this->findAllByMapId($userId, $mapId); foreach ($shares as $share) { if ($share->category === $category) { return $share; } } + return null; } - public function removeByMapIdAndCategory($userId, $mapId, $category) { + public function removeByMapIdAndCategory(string $userId, int $mapId, $category) { $userFolder = $this->root->getUserFolder($userId); - $folders = $userFolder->getById($mapId); + $folder = $userFolder->getFirstNodeById($mapId); $shares = []; $deleted = null; - if (empty($folders)) { - return $deleted; - } - $folder = array_shift($folders); - if ($folder === null) { + if (!$folder instanceof Folder) { return $deleted; } + try { $file = $folder->get('.favorite_shares.json'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { $file = $folder->newFile('.favorite_shares.json', $content = '[]'); } - $data = json_decode($file->getContent(), true); + + $data = json_decode((string)$file->getContent(), true); foreach ($data as $share) { $c = $share['category']; if ($c === $category) { @@ -206,38 +186,29 @@ public function removeByMapIdAndCategory($userId, $mapId, $category) { $shares[] = $share; } } + $file->putContent(json_encode($shares, JSON_PRETTY_PRINT)); return $deleted; } - /** - * @param $owner - * @param $category - * @return Entity|null - */ - public function findOrCreateByOwnerAndCategory($owner, $category) { - /* @var Entity */ + public function findOrCreateByOwnerAndCategory(string $owner, string $category): ?FavoriteShare { + /* @var ?FavoriteShare $entity */ $entity = null; try { $entity = $this->findByOwnerAndCategory($owner, $category); - } catch (DoesNotExistException $e) { + } catch (DoesNotExistException) { $entity = $this->create($owner, $category); - } catch (MultipleObjectsReturnedException $e) { + } catch (MultipleObjectsReturnedException) { } return $entity; } - /** - * @param $owner - * @param $category - * @return bool - */ - public function removeByOwnerAndCategory($owner, $category) { + public function removeByOwnerAndCategory(string $owner, string $category): bool { try { $entity = $this->findByOwnerAndCategory($owner, $category); - } catch (DoesNotExistException|MultipleObjectsReturnedException $e) { + } catch (DoesNotExistException|MultipleObjectsReturnedException) { return false; } diff --git a/lib/DB/Geophoto.php b/lib/DB/Geophoto.php index e2133738a..c240af635 100644 --- a/lib/DB/Geophoto.php +++ b/lib/DB/Geophoto.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\DB; use OCP\AppFramework\Db\Entity; +/** + * @method int getFileId() + * @method float getLat() + * @method float getLng() + * @method string getUserId() + */ class Geophoto extends Entity { protected $fileId; + protected $lat; + protected $lng; + protected $dateTaken; + protected $userId; public function __construct() { diff --git a/lib/DB/GeophotoMapper.php b/lib/DB/GeophotoMapper.php index 372c4eef1..bc8c956c9 100644 --- a/lib/DB/GeophotoMapper.php +++ b/lib/DB/GeophotoMapper.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\DB; use OCP\AppFramework\Db\QBMapper; @@ -43,14 +44,11 @@ public function find($id) { } /** - * @param $fileId - * @param $userId - * @return mixed|\OCP\AppFramework\Db\Entity * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws \OCP\DB\Exception */ - public function findByFileIdUserId($fileId, $userId) { + public function findByFileIdUserId(int $fileId, string $userId): Geophoto { $qb = $this->db->getQueryBuilder(); $qb->select('*') @@ -65,13 +63,11 @@ public function findByFileIdUserId($fileId, $userId) { } /** - * @param $fileId - * @return mixed|\OCP\AppFramework\Db\Entity * @throws \OCP\AppFramework\Db\DoesNotExistException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws \OCP\DB\Exception */ - public function findByFileId($fileId) { + public function findByFileId(int $fileId): Geophoto { $qb = $this->db->getQueryBuilder(); $qb->select('*') @@ -105,9 +101,11 @@ public function findAll($userId, $limit = null, $offset = null) { if (!is_null($offset)) { $qb->setFirstResult($offset); } + if (!is_null($limit)) { $qb->setMaxResults($limit); } + return $this->findEntities($qb); } @@ -134,9 +132,11 @@ public function findAllNonLocalized($userId, $limit = null, $offset = null) { if (!is_null($offset)) { $qb->setFirstResult($offset); } + if (!is_null($limit)) { $qb->setMaxResults($limit); } + return array_reverse($this->findEntities($qb)); } diff --git a/lib/Helper/ExifDataInvalidException.php b/lib/Helper/ExifDataInvalidException.php index 67c12d921..4ee72acca 100644 --- a/lib/Helper/ExifDataInvalidException.php +++ b/lib/Helper/ExifDataInvalidException.php @@ -1,5 +1,7 @@ [0-9]{4})\:(?[0-9]{2})\:(?[0-9]{2}) (?[0-9]{2})\:(?[0-9]{2})\:(?[0-9]{2})'; + private const EXIF_TIME_REGEX = '(?\d{4})\:(?\d{2})\:(?\d{2}) (?\d{2})\:(?\d{2})\:(?\d{2})'; - /** - * @var int|null - */ - private $timestamp = null; + private ?int $timestamp = null; - /** - * @var float|null - */ - private $latitude = null; + private ?float $latitude = null; - /** - * @var float|null - */ - private $longitude = null; + private ?float $longitude = null; - /** - * @var bool|null - */ - private $is_valid = null; - - /** - * @var ?array - */ - protected $exif_data = null; + private ?bool $is_valid = null; /** * @throws PelInvalidArgumentException @@ -103,14 +77,18 @@ protected static function get_exif_data_array(string $path) : array { $data = @exif_read_data($path, null, true); if ($data && isset($data['EXIF']) && is_array($data['EXIF']) && isset($data['EXIF'][self::LATITUDE]) && isset($data['EXIF'][self::LONGITUDE])) { return $data['EXIF']; - } elseif ($data && isset($data['GPS']) && is_array($data['GPS']) && isset($data['GPS'][self::LATITUDE]) && isset($data['GPS'][self::LONGITUDE])) { + } + + if ($data && isset($data['GPS']) && is_array($data['GPS']) && isset($data['GPS'][self::LATITUDE]) && isset($data['GPS'][self::LONGITUDE])) { $d = $data['GPS']; if (!isset($d[self::TIMESTAMP]) && isset($data['EXIF'][self::TIMESTAMP])) { $d[self::TIMESTAMP] = $data['EXIF'][self::TIMESTAMP]; } + return $d; } } + $data = new PelDataWindow(file_get_contents($path)); if (PelJpeg::isValid($data)) { $pelJpeg = new PelJpeg($data); @@ -126,22 +104,27 @@ protected static function get_exif_data_array(string $path) : array { } else { return []; } + if (is_null($pelTiff)) { return []; } + $pelIfd0 = $pelTiff->getIfd(); if (is_null($pelIfd0)) { return []; } + $pelIfdExif = $pelIfd0->getSubIfd(PelIfd::EXIF); if (is_null($pelIfdExif)) { return []; } + $pelDateTimeOriginal = $pelIfdExif->getEntry(PelTag::DATE_TIME_ORIGINAL); if (is_null($pelDateTimeOriginal)) { return []; } + $exif = [ # self::TIMESTAMP => $pelDateTimeOriginal->getValue(PelEntryTime::EXIF_STRING) // for old pel 0.9.6 and above self::TIMESTAMP => (int)$pelDateTimeOriginal->getValue() // for new pel >= 0.9.11 @@ -159,21 +142,21 @@ protected static function get_exif_data_array(string $path) : array { $exif ); } + Pel::clearExceptions(); return $exif; } /** - * @param PelIfd $pelIfdGPS * @param $target * @param $source * @param $ref_target * @param $ref_source - * @param array $exif + * @param array $exif */ - protected static function readPelCoordinate(PelIfd $pelIfdGPS, $target, $source, $ref_target, $ref_source, array &$exif = []) : void { + protected static function readPelCoordinate(PelIfd $pelIfdGPS, $target, $source, string $ref_target, $ref_source, array &$exif = []) : void { $coordinate = $pelIfdGPS->getEntry($source)->getValue(); - if ((int)$coordinate[0][1] != 0 && (int)$coordinate[1][1] != 0 && (int)$coordinate[2][1] != 0) { + if ((int)$coordinate[0][1] !== 0 && (int)$coordinate[1][1] !== 0 && (int)$coordinate[2][1] !== 0) { $exif[$ref_target] = $pelIfdGPS->getEntry($ref_source)->getValue(); $exif[$target] = [ 0 => (int)$coordinate[0][0] / (int)$coordinate[0][1], @@ -184,24 +167,24 @@ protected static function readPelCoordinate(PelIfd $pelIfdGPS, $target, $source, } /** - * @param string $path * @return ExifGeoData */ public static function get(string $path) : ?ExifGeoData { try { $data = static::get_exif_data_array($path); - } catch (\Throwable $e) { + } catch (\Throwable) { $data = []; } + return new static($data); } /** * ExifGeoData constructor. - * @param array $exif_data */ - final private function __construct(array $exif_data) { - $this->exif_data = $exif_data; + final private function __construct( + protected array $exif_data, + ) { $this->parse(); } @@ -210,10 +193,11 @@ final private function __construct(array $exif_data) { * @throws ExifDataInvalidException * @throws ExifDataNoLocationException */ - public function validate($invalidate_zero_iland = false) { + public function validate($invalidate_zero_iland = false): void { if (!$this->exif_data) { throw new ExifDataInvalidException('No exif_data found', 1); } + if (!is_array($this->exif_data)) { throw new ExifDataInvalidException('exif_data is not an array', 2); } @@ -221,6 +205,7 @@ public function validate($invalidate_zero_iland = false) { if (!isset($this->exif_data[self::LATITUDE]) || !isset($this->exif_data[self::LONGITUDE])) { throw new ExifDataNoLocationException('Latitude and/or Longitude are missing from exif data', 1); } + if ($invalidate_zero_iland && $this->isZeroIsland()) { $this->latitude = null; $this->longitude = null; @@ -228,18 +213,16 @@ public function validate($invalidate_zero_iland = false) { } } - /** - * @return bool - */ public function isValid(): bool { if ($this->is_valid === null) { try { $this->validate(); $this->is_valid = true; - } catch (\Throwable $e) { + } catch (\Throwable) { $this->is_valid = false; } } + return $this->is_valid; } @@ -249,11 +232,13 @@ private function parse(): void { if (isset($this->exif_data[self::LONGITUDE_REF]) && $this->exif_data[self::LONGITUDE_REF] === 'W') { $this->longitude *= -1; } + $this->latitude = $this->geo2float($this->exif_data[self::LATITUDE]); if (isset($this->exif_data[self::LATITUDE_REF]) && $this->exif_data[self::LATITUDE_REF] === 'S') { $this->latitude *= -1; } } + // optional if (isset($this->exif_data[self::TIMESTAMP])) { $t = $this->exif_data[self::TIMESTAMP]; @@ -262,27 +247,25 @@ private function parse(): void { } /** - * @param string $timestamp * @return int */ private function string2time(string $timestamp): ?int { - $result = null; if (preg_match('#' . self::EXIF_TIME_REGEX . '#ui', $timestamp, $match)) { - $result - = strtotime("{$match['years']}-{$match['months']}-{$match['days']} {$match['hours']}:{$match['minutes']}:{$match['seconds']}")?:null; + return strtotime(sprintf('%s-%s-%s %s:%s:%s', $match['years'], $match['months'], $match['days'], $match['hours'], $match['minutes'], $match['seconds']))?:null; } - return $result; + + return null; } /** * @param $geo - * @return float|null */ - private function geo2float($geo): ?float { + private function geo2float($geo): float { if (!is_array($geo)) { $geo = [$geo]; } + $result = .0; $d = 1.0; foreach ($geo as $component) { @@ -293,28 +276,22 @@ private function geo2float($geo): ?float { return $result; } - /** - * @param string $value - * @return float|null - */ private function string2float(string $value): ?float { $result = null; $value = trim($value, '/'); if (str_contains($value, '/')) { - $value = array_map('intval', explode('/', $value)); + $value = array_map(intval(...), explode('/', $value)); if ($value[1] != 0) { $result = $value[0] / $value[1]; } } else { $result = floatval($value); } + return $result; } - /** - * @return bool - */ public function isZeroIsland(): bool { return $this->latitude() === .0 && $this->longitude() === .0; } @@ -333,20 +310,17 @@ public function longitude(int $precision = 6): ?float { return $this->longitude === null ? null : round($this->longitude, $precision); } - /** - * @param string|null $format - * @return string|int|null - */ - public function timestamp(?string $format = 'Y-m-d H:i:s') { - $result = $this->timestamp; + public function timestamp(?string $format = 'Y-m-d H:i:s'): int|string|null { if ($this->timestamp !== null && $format) { - $result = date($format, $this->timestamp); + return date($format, $this->timestamp); } - return $result; + + return $this->timestamp; } /** * If someone wants to have it as a json object + * @return array */ public function jsonSerialize(): array { return [ @@ -362,20 +336,15 @@ public function jsonSerialize(): array { * @param $name * @return float|int|string|null */ - public function __get($name) { + public function __get(string $name): mixed { $value = null; - switch ($name) { - case 'lat': - $value = $this->latitude(); - break; - case 'lng': - $value = $this->longitude(); - break; - case 'dateTaken': - $value = $this->timestamp(null); - break; - } - return $value; + + return match ($name) { + 'lat' => $this->latitude(), + 'lng' => $this->longitude(), + 'dateTaken' => $this->timestamp(null), + default => $value, + }; } } diff --git a/lib/Helper/functions.php b/lib/Helper/functions.php index cf04f75f4..bd936974c 100644 --- a/lib/Helper/functions.php +++ b/lib/Helper/functions.php @@ -1,5 +1,7 @@ * @@ -21,22 +23,19 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\Helper; use RuntimeException; /** * function remove_utf8_bom - * - * @param string $text - * @return string */ function remove_utf8_bom(string $text): string { $bom = pack('H*', 'EFBBBF'); - $text = preg_replace("/^$bom/", '', $text); + $text = preg_replace(sprintf('/^%s/', $bom), '', $text); if ($text === null) { throw new RuntimeException(preg_last_error_msg()); } + return $text; } diff --git a/lib/Hooks/FileHooks.php b/lib/Hooks/FileHooks.php index 90d2557fe..b73c54511 100644 --- a/lib/Hooks/FileHooks.php +++ b/lib/Hooks/FileHooks.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\Hooks; use OC\Files\Filesystem; use OCA\Maps\Service\PhotofilesService; use OCA\Maps\Service\TracksService; -use OCP\Files\FileInfo; +use OCP\Files\File; +use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\Node; +use OCP\IUserSession; use OCP\Lock\ILockingProvider; +use OCP\Server; +use OCP\Share; use OCP\Share\IShare; use OCP\Util; use function OCP\Log\logger; @@ -27,28 +33,20 @@ */ class FileHooks { - private $photofilesService; - private $tracksService; - - private $root; - - private ILockingProvider $lockingProvider; - - public function __construct(IRootFolder $root, PhotofilesService $photofilesService, TracksService $tracksService, - $appName, ILockingProvider $lockingProvider) { - $this->photofilesService = $photofilesService; - $this->tracksService = $tracksService; - $this->root = $root; - $this->lockingProvider = $lockingProvider; + public function __construct( + private readonly IRootFolder $root, + private readonly PhotofilesService $photofilesService, + private readonly TracksService $tracksService, + private readonly ILockingProvider $lockingProvider, + ) { } - public function register() { - $fileWriteCallback = function (\OCP\Files\Node $node) { + public function register(): void { + $fileWriteCallback = function (Node $node): void { //logger('maps')->debug("Hook postWrite"); - if ($node->getType() === FileInfo::TYPE_FILE && $this->isUserNode($node) && $node->getSize()) { + if ($node instanceof File && $this->isUserNode($node) && $node->getSize()) { $path = $node->getPath(); - if (!$this->lockingProvider->isLocked($path, ILockingProvider::LOCK_SHARED) - and !$this->lockingProvider->isLocked($path, ILockingProvider::LOCK_EXCLUSIVE) + if (!$this->lockingProvider->isLocked($path, ILockingProvider::LOCK_SHARED) && !$this->lockingProvider->isLocked($path, ILockingProvider::LOCK_EXCLUSIVE) ) { $isPhoto = $this->photofilesService->addByFile($node); if (!$isPhoto) { @@ -59,10 +57,10 @@ public function register() { }; $this->root->listen('\OC\Files', 'postWrite', $fileWriteCallback); - $fileDeletionCallback = function (\OCP\Files\Node $node) { + $fileDeletionCallback = function (Node $node): void { //logger('maps')->debug("Hook preDelete"); if ($this->isUserNode($node)) { - if ($node->getType() === FileInfo::TYPE_FOLDER) { + if ($node instanceof Folder) { $this->photofilesService->deleteByFolder($node); $this->tracksService->deleteByFolder($node); } else { @@ -75,17 +73,17 @@ public function register() { // this one is triggered when restoring a version of a file // and NOT when it's created so we can use it for updating coordinates in DB - $this->root->listen('\OC\Files', 'postTouch', function (\OCP\Files\Node $node) { - if ($this->isUserNode($node) and $node->getType() === FileInfo::TYPE_FILE) { + $this->root->listen('\OC\Files', 'postTouch', function (Node $node): void { + if ($this->isUserNode($node) && $node instanceof File) { $this->photofilesService->updateByFile($node); // nothing to update on tracks, metadata will be regenerated when getting content if etag has changed } }); // move file: delete then add it again in DB to be sure it's there for all users with access to target file - $this->root->listen('\OC\Files', 'postRename', function (\OCP\Files\Node $source, \OCP\Files\Node $target) { + $this->root->listen('\OC\Files', 'postRename', function (Node $source, Node $target): void { if ($this->isUserNode($target)) { - if ($target->getType() === FileInfo::TYPE_FILE) { + if ($target instanceof File) { // if moved (parents are different) => update DB with access list if ($source->getParent()->getId() !== $target->getParent()->getId()) { // we renamed therefore target and source are identical @@ -93,7 +91,7 @@ public function register() { $this->photofilesService->addByFile($target); // tracks: nothing to do here because we use fileID } - } elseif ($target->getType() === FileInfo::TYPE_FOLDER) { + } elseif ($target instanceof Folder) { if ($source->getParent()->getId() !== $target->getParent()->getId()) { // we renamed therefore target and source have the same childs. $this->photofilesService->deleteByFolder($target); @@ -107,85 +105,96 @@ public function register() { Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', $this, 'restore'); // sharing hooks - Util::connectHook(\OCP\Share::class, 'post_shared', $this, 'postShare'); - Util::connectHook(\OCP\Share::class, 'post_unshare', $this, 'postUnShare'); - Util::connectHook(\OCP\Share::class, 'pre_unshare', $this, 'preUnShare'); + Util::connectHook(Share::class, 'post_shared', $this, 'postShare'); + Util::connectHook(Share::class, 'post_unshare', $this, 'postUnShare'); + Util::connectHook(Share::class, 'pre_unshare', $this, 'preUnShare'); } - public function postShare($params) { + /** + * @param array $params + */ + public function postShare(array $params): void { //logger('maps')->debug("Hook postShare"); if ($params['itemType'] === 'file') { //$targetFilePath = $params['itemTarget']; //$sourceUserId = $params['uidOwner']; $fileId = $params['fileSource']; // or itemSource - $files = $this->root->getById($fileId); - if (empty($files)) { + $file = $this->root->getFirstNodeById($fileId); + if (!$file instanceof File) { return; } - $file = array_shift($files); - $this->photofilesService->addByFile($file, ); + + $this->photofilesService->addByFile($file); $this->tracksService->safeAddByFile($file); } elseif ($params['itemType'] === 'folder') { $dirId = $params['fileSource']; // or itemSource - $folders = $this->root->getById($dirId); - if (empty($folders)) { + $folder = $this->root->getFirstNodeById($dirId); + if (!$folder instanceof Folder) { return; } - $folder = array_shift($folders); + $this->photofilesService->addByFolder($folder); $this->tracksService->safeAddByFolder($folder); } } - public function postUnShare($params) { + /** + * @param array $params + */ + public function postUnShare(array $params): void { //logger('maps')->debug("Hook postUnShare"); - if ($params['shareType'] === IShare::TYPE_USER) { - if ($params['itemType'] === 'file') { - $targetUserId = $params['shareWith']; - $fileId = $params['fileSource']; // or itemSource - $this->photofilesService->deleteByFileIdUserId($fileId, $targetUserId); - $this->tracksService->safeDeleteByFileIdUserId($fileId, $targetUserId); - } + if ($params['shareType'] === IShare::TYPE_USER && $params['itemType'] === 'file') { + $targetUserId = $params['shareWith']; + $fileId = $params['fileSource']; + // or itemSource + $this->photofilesService->deleteByFileIdUserId($fileId, $targetUserId); + $this->tracksService->safeDeleteByFileIdUserId($fileId, $targetUserId); } } - public function preUnShare($params) { + /** + * @param array $params + */ + public function preUnShare(array $params): void { //logger('maps')->debug("Hook preUnShare"); - if ($params['shareType'] === IShare::TYPE_USER) { - if ($params['itemType'] === 'folder') { - $targetUserId = $params['shareWith']; - $dirId = $params['fileSource']; // or itemSource - $this->photofilesService->deleteByFolderIdUserId($dirId, $targetUserId); - $this->tracksService->safeDeleteByFolderIdUserId($dirId, $targetUserId); - } + if ($params['shareType'] === IShare::TYPE_USER && $params['itemType'] === 'folder') { + $targetUserId = $params['shareWith']; + $dirId = $params['fileSource']; + // or itemSource + $this->photofilesService->deleteByFolderIdUserId($dirId, $targetUserId); + $this->tracksService->safeDeleteByFolderIdUserId($dirId, $targetUserId); } } - public function restore($params) { + /** + * @param array $params + */ + public function restore(array $params): void { $node = $this->getNodeForPath($params['filePath']); if ($this->isUserNode($node)) { - if ($node->getType() === FileInfo::TYPE_FOLDER) { + if ($node instanceof Folder) { $this->photofilesService->addByFolder($node); $this->tracksService->safeAddByFolder($node); - } else { + } elseif ($node instanceof File) { $this->photofilesService->addByFile($node); $this->tracksService->safeAddByFile($node); } } } - private function getNodeForPath($path) { - $user = \OC::$server->getUserSession()->getUser(); + private function getNodeForPath(string $path): Node { + $user = Server::get(IUserSession::class)->getUser(); $fullPath = Filesystem::normalizePath('/' . $user->getUID() . '/files/' . $path); return $this->root->get($fullPath); } - private function isUserNode(\OCP\Files\Node $node): bool { + private function isUserNode(Node $node): bool { //return $node->getStorage()->instanceOfStorage("\OCP\Files\IHomeStorage") $owner = $node->getStorage()->getOwner(''); if (! $owner) { return false; } + return str_starts_with($node->getPath(), '/' . $owner . '/'); } diff --git a/lib/Listener/CardCreatedListener.php b/lib/Listener/CardCreatedListener.php index 5c2e890b5..8e5a90570 100644 --- a/lib/Listener/CardCreatedListener.php +++ b/lib/Listener/CardCreatedListener.php @@ -31,13 +31,9 @@ /** @template-implements IEventListener */ class CardCreatedListener implements IEventListener { - /** @var AddressService */ - private $addressService; - public function __construct( - AddressService $addressService, + private readonly AddressService $addressService, ) { - $this->addressService = $addressService; } public function handle(Event $event): void { @@ -45,6 +41,7 @@ public function handle(Event $event): void { // Unrelated return; } + $cData = $event->getCardData(); $cUri = $cData['uri']; $this->addressService->scheduleVCardForLookup($cData['carddata'], $cUri); diff --git a/lib/Listener/CardDeletedListener.php b/lib/Listener/CardDeletedListener.php index 1eaefef10..27fc9ec52 100644 --- a/lib/Listener/CardDeletedListener.php +++ b/lib/Listener/CardDeletedListener.php @@ -31,13 +31,9 @@ /** @template-implements IEventListener */ class CardDeletedListener implements IEventListener { - /** @var AddressService */ - private $addressService; - public function __construct( - AddressService $addressService, + private readonly AddressService $addressService, ) { - $this->addressService = $addressService; } public function handle(Event $event): void { @@ -45,6 +41,7 @@ public function handle(Event $event): void { // Unrelated return; } + $cData = $event->getCardData(); $cUri = $cData['uri']; $this->addressService->deleteDBContactAddresses($cUri); diff --git a/lib/Listener/CardUpdatedListener.php b/lib/Listener/CardUpdatedListener.php index 6092b02f5..0f03447c4 100644 --- a/lib/Listener/CardUpdatedListener.php +++ b/lib/Listener/CardUpdatedListener.php @@ -31,13 +31,9 @@ /** @template-implements IEventListener */ class CardUpdatedListener implements IEventListener { - /** @var AddressService */ - private $addressService; - public function __construct( - AddressService $addressService, + private readonly AddressService $addressService, ) { - $this->addressService = $addressService; } public function handle(Event $event): void { @@ -45,6 +41,7 @@ public function handle(Event $event): void { // Unrelated return; } + $cData = $event->getCardData(); $cUri = $cData['uri']; $this->addressService->scheduleVCardForLookup($cData['carddata'], $cUri); diff --git a/lib/Listener/LoadAdditionalScriptsListener.php b/lib/Listener/LoadAdditionalScriptsListener.php index 83ad9a697..7d1dfc803 100644 --- a/lib/Listener/LoadAdditionalScriptsListener.php +++ b/lib/Listener/LoadAdditionalScriptsListener.php @@ -31,9 +31,6 @@ /** @template-implements IEventListener */ class LoadAdditionalScriptsListener implements IEventListener { - public function __construct() { - } - public function handle(Event $event): void { if (!($event instanceof LoadAdditionalScriptsEvent)) { // Unrelated diff --git a/lib/Listener/LoadSidebarListener.php b/lib/Listener/LoadSidebarListener.php index f0761769d..3527e2800 100644 --- a/lib/Listener/LoadSidebarListener.php +++ b/lib/Listener/LoadSidebarListener.php @@ -31,9 +31,6 @@ /** @template-implements IEventListener */ class LoadSidebarListener implements IEventListener { - public function __construct() { - } - public function handle(Event $event): void { if (!($event instanceof LoadSidebar)) { // Unrelated diff --git a/lib/Migration/InstallScan.php b/lib/Migration/InstallScan.php index cd6a3a41d..67019f456 100644 --- a/lib/Migration/InstallScan.php +++ b/lib/Migration/InstallScan.php @@ -1,5 +1,7 @@ * @@ -21,17 +23,18 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\Migration; use OCA\Maps\BackgroundJob\LaunchUsersInstallScanJob; use OCP\BackgroundJob\IJobList; use OCP\Encryption\IManager; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IUser; use OCP\IUserManager; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; +use Override; /** * Class InstallScan @@ -41,33 +44,22 @@ class InstallScan implements IRepairStep { public function __construct( - private IConfig $config, - private IUserManager $userManager, - private IJobList $jobList, - private IManager $encryptionManager, + private readonly IAppConfig $appConfig, + private readonly IConfig $config, + private readonly IUserManager $userManager, + private readonly IJobList $jobList, + private readonly IManager $encryptionManager, ) { - $this->config = $config; - $this->jobList = $jobList; - $this->encryptionManager = $encryptionManager; - $this->userManager = $userManager; } - /** - * Returns the step's name - * - * @return string - * @since 9.1.0 - */ - public function getName() { + #[Override] + public function getName(): string { return 'Scan photos and tracks in users storages'; } - /** - * @param IOutput $output - */ - public function run(IOutput $output) { + public function run(IOutput $output): ?int { if (!$this->shouldRun()) { - return; + return null; } if ($this->encryptionManager->isEnabled()) { @@ -77,15 +69,16 @@ public function run(IOutput $output) { // set the install scan flag for existing users // future users won't have any value and won't be bothered by "media scan" warning - $this->userManager->callForSeenUsers(function (IUser $user) { + $this->userManager->callForSeenUsers(function (IUser $user): void { $this->config->setUserValue($user->getUID(), 'maps', 'installScanDone', 'no'); }); // create the job which will create a job by user $this->jobList->add(LaunchUsersInstallScanJob::class, []); + return null; } - protected function shouldRun() { - $appVersion = $this->config->getAppValue('maps', 'installed_version', '0.0.0'); + protected function shouldRun(): bool { + $appVersion = $this->appConfig->getValueString('maps', 'installed_version', '0.0.0'); return version_compare($appVersion, '0.0.10', '<'); } diff --git a/lib/Migration/RegisterMimeType.php b/lib/Migration/RegisterMimeType.php index 2e6575195..4e2eee022 100644 --- a/lib/Migration/RegisterMimeType.php +++ b/lib/Migration/RegisterMimeType.php @@ -1,5 +1,7 @@ mimeTypeLoader = $mimeTypeLoader; } - public function getName() { + public function getName(): string { return 'Register Maps MIME types'; } - private function registerForExistingFiles() { + private function registerForExistingFiles(): void { $mimeTypeId = $this->mimeTypeLoader->getId('application/x-nextcloud-maps'); $this->mimeTypeLoader->updateFilecache('maps', $mimeTypeId); @@ -36,7 +38,7 @@ private function registerForExistingFiles() { $this->mimeTypeLoader->updateFilecache('notrack', $mimeTypeId); } - private function registerForNewFiles() { + private function registerForNewFiles(): void { $mapping = [ 'maps' => ['application/x-nextcloud-maps'], 'noindex' => ['application/x-nextcloud-noindex'], @@ -56,7 +58,7 @@ private function registerForNewFiles() { file_put_contents($mappingFile, json_encode($mapping, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); } - public function run(IOutput $output) { + public function run(IOutput $output): void { $output->info('Registering the mimetype...'); // Register the mime type for existing files diff --git a/lib/Migration/UnregisterMimeType.php b/lib/Migration/UnregisterMimeType.php index 3e5dd2085..edcec2ee8 100644 --- a/lib/Migration/UnregisterMimeType.php +++ b/lib/Migration/UnregisterMimeType.php @@ -1,5 +1,7 @@ mimeTypeLoader = $mimeTypeLoader; } - public function getName() { + public function getName(): string { return 'Unregister Maps MIME types'; } - private function unregisterForExistingFiles() { + private function unregisterForExistingFiles(): void { $mimeTypeId = $this->mimeTypeLoader->getId('application/octet-stream'); $this->mimeTypeLoader->updateFilecache('maps', $mimeTypeId); $this->mimeTypeLoader->updateFilecache('noindex', $mimeTypeId); @@ -28,7 +30,7 @@ private function unregisterForExistingFiles() { $this->mimeTypeLoader->updateFilecache('notrack', $mimeTypeId); } - private function unregisterForNewFiles() { + private function unregisterForNewFiles(): void { $mappingFile = \OC::$configDir . self::CUSTOM_MIMETYPEMAPPING; if (file_exists($mappingFile)) { @@ -42,11 +44,12 @@ private function unregisterForNewFiles() { } else { $mapping = []; } + file_put_contents($mappingFile, json_encode($mapping, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); } } - public function run(IOutput $output) { + public function run(IOutput $output): void { $output->info('Unregistering the mimetype...'); // Register the mime type for existing files diff --git a/lib/Migration/Version000008Date20190428142257.php b/lib/Migration/Version000008Date20190428142257.php index 69676057a..14441f7e9 100644 --- a/lib/Migration/Version000008Date20190428142257.php +++ b/lib/Migration/Version000008Date20190428142257.php @@ -15,17 +15,13 @@ class Version000008Date20190428142257 extends SimpleMigrationStep { /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -218,13 +214,12 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt ]); $table->setPrimaryKey(['id']); } + return $schema; } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version000009Date20190625000800.php b/lib/Migration/Version000009Date20190625000800.php index a426e698f..c99b40c23 100644 --- a/lib/Migration/Version000009Date20190625000800.php +++ b/lib/Migration/Version000009Date20190625000800.php @@ -15,17 +15,13 @@ class Version000009Date20190625000800 extends SimpleMigrationStep { /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -68,9 +64,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version000012Date20190703155323.php b/lib/Migration/Version000012Date20190703155323.php index 430d1899b..083356d8b 100644 --- a/lib/Migration/Version000012Date20190703155323.php +++ b/lib/Migration/Version000012Date20190703155323.php @@ -15,24 +15,19 @@ */ class Version000012Date20190703155323 extends SimpleMigrationStep { - protected $db; - - public function __construct(IDBConnection $connection) { - $this->db = $connection; + public function __construct( + protected IDBConnection $db, + ) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -46,9 +41,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version000012Date20190722184716.php b/lib/Migration/Version000012Date20190722184716.php index 2f3e0af28..47649c1ed 100644 --- a/lib/Migration/Version000012Date20190722184716.php +++ b/lib/Migration/Version000012Date20190722184716.php @@ -15,24 +15,19 @@ */ class Version000012Date20190722184716 extends SimpleMigrationStep { - protected $db; - - public function __construct(IDBConnection $connection) { - $this->db = $connection; + public function __construct( + protected IDBConnection $db, + ) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -76,9 +71,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version000013Date20190723185417.php b/lib/Migration/Version000013Date20190723185417.php index 19b89a085..9abe66d72 100644 --- a/lib/Migration/Version000013Date20190723185417.php +++ b/lib/Migration/Version000013Date20190723185417.php @@ -15,24 +15,19 @@ */ class Version000013Date20190723185417 extends SimpleMigrationStep { - protected $db; - - public function __construct(IDBConnection $connection) { - $this->db = $connection; + public function __construct( + protected IDBConnection $db, + ) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -43,6 +38,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt if ($table->hasColumn('contact_uid')) { $table->dropColumn('contact_uid'); } + $table->addColumn('object_uri', 'string', [ 'notnull' => true, 'default' => '--', @@ -55,11 +51,9 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ - public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { $query = $this->db->getQueryBuilder(); $query->delete('maps_address_geo'); $query->executeStatement(); diff --git a/lib/Migration/Version000014Date20190817184844.php b/lib/Migration/Version000014Date20190817184844.php index c84713d67..4ef8c559c 100644 --- a/lib/Migration/Version000014Date20190817184844.php +++ b/lib/Migration/Version000014Date20190817184844.php @@ -15,24 +15,19 @@ */ class Version000014Date20190817184844 extends SimpleMigrationStep { - protected $db; - - public function __construct(IDBConnection $connection) { - $this->db = $connection; + public function __construct( + protected IDBConnection $db, + ) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -50,9 +45,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version000102Date20190901152326.php b/lib/Migration/Version000102Date20190901152326.php index 043abb257..b72f01c01 100644 --- a/lib/Migration/Version000102Date20190901152326.php +++ b/lib/Migration/Version000102Date20190901152326.php @@ -15,24 +15,19 @@ */ class Version000102Date20190901152326 extends SimpleMigrationStep { - protected $db; - - public function __construct(IDBConnection $connection) { - $this->db = $connection; + public function __construct( + protected IDBConnection $db, + ) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -51,9 +46,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version000106Date20191118221134.php b/lib/Migration/Version000106Date20191118221134.php index 929531421..32efa7e9c 100644 --- a/lib/Migration/Version000106Date20191118221134.php +++ b/lib/Migration/Version000106Date20191118221134.php @@ -15,17 +15,13 @@ class Version000106Date20191118221134 extends SimpleMigrationStep { /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -59,9 +55,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version000107Date20200608220000.php b/lib/Migration/Version000107Date20200608220000.php index 2e418f998..989f6be21 100644 --- a/lib/Migration/Version000107Date20200608220000.php +++ b/lib/Migration/Version000107Date20200608220000.php @@ -15,24 +15,19 @@ */ class Version000107Date20200608220000 extends SimpleMigrationStep { - protected $db; - - public function __construct(IDBConnection $connection) { - $this->db = $connection; + public function __construct( + protected IDBConnection $db, + ) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -50,9 +45,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version000108Date20230310220000.php b/lib/Migration/Version000108Date20230310220000.php index c8f323548..3fad313f2 100644 --- a/lib/Migration/Version000108Date20230310220000.php +++ b/lib/Migration/Version000108Date20230310220000.php @@ -15,24 +15,19 @@ */ class Version000108Date20230310220000 extends SimpleMigrationStep { - protected $db; - - public function __construct(IDBConnection $connection) { - $this->db = $connection; + public function __construct( + protected IDBConnection $db, + ) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -43,13 +38,12 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addIndex(['user_id']); $table->addIndex(['file_id']); } + return $schema; } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Migration/Version100100Date20230731135102.php b/lib/Migration/Version100100Date20230731135102.php index 85a15fd68..c1474e994 100644 --- a/lib/Migration/Version100100Date20230731135102.php +++ b/lib/Migration/Version100100Date20230731135102.php @@ -15,24 +15,19 @@ */ class Version100100Date20230731135102 extends SimpleMigrationStep { - protected $db; - - public function __construct(IDBConnection $connection) { - $this->db = $connection; + public function __construct( + protected IDBConnection $db, + ) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { @@ -42,6 +37,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt if ($schema->hasTable('maps_device_shares')) { $schema->dropTable('maps_device_shares'); } + if (!$schema->hasTable('maps_device_shares')) { $table = $schema->createTable('maps_device_shares'); $table->addColumn('id', 'bigint', [ @@ -73,9 +69,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } /** - * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { } diff --git a/lib/Service/AddressService.php b/lib/Service/AddressService.php index c6480b208..ff3779321 100644 --- a/lib/Service/AddressService.php +++ b/lib/Service/AddressService.php @@ -1,5 +1,7 @@ dbconnection = $dbconnection; $this->memcache = $cacheFactory->createLocal('maps'); $this->jobList = $jobList; - $this->appData = $appData; } // converts the address to geo lat;lon @@ -73,9 +73,16 @@ public function addressToGeo($adr, $uri): string { * @param $adr * @param $uri ressource identifier (contact URI for example) * @return array($lat,$lng,$lookedUp) + * @return array + * @return array + * @return array + * @return array + * @return array + * @return array + * @return array */ public function lookupAddress($adr, $uri): array { - $adr_norm = strtolower(preg_replace('/\s+/', '', $adr)); + $adr_norm = strtolower((string)preg_replace('/\s+/', '', (string)$adr)); $qb = $this->dbconnection->getQueryBuilder(); $qb->select('id', 'lat', 'lng', 'looked_up') ->from('maps_address_geo') @@ -101,38 +108,39 @@ public function lookupAddress($adr, $uri): array { if (!$geo[2]) { $geo = $this->lookupAddressExternal($adr); } + $lat = $geo[0]; $lng = $geo[1]; $lookedUp = $geo[2]; $inDb = true; } + break; } + $req->closeCursor(); $qb = $this->dbconnection->getQueryBuilder(); // if it's still not in the DB, it means the lookup did not happen yet // so we can schedule it for later if (!$inDb) { - if (strlen($adr) > 255) { + if (strlen((string)$adr) > 255) { $this->logger->notice('lookupAddress: Truncating $adr (entry too long) ' . $adr); - $adr = substr($adr, 0, 255); + $adr = substr((string)$adr, 0, 255); } + $foo = $this->scheduleForLookup($adr, $uri); $id = $foo[0]; $lat = $foo[1]; $lng = $foo[2]; $lookedUp = $foo[3]; - - } else { - if ($lookedUp) { - $qb->update('maps_address_geo') - ->set('lat', $qb->createNamedParameter($lat, IQueryBuilder::PARAM_STR)) - ->set('lng', $qb->createNamedParameter($lng, IQueryBuilder::PARAM_STR)) - ->set('object_uri', $qb->createNamedParameter($uri, IQueryBuilder::PARAM_STR)) - ->set('looked_up', $qb->createNamedParameter($lookedUp, IQueryBuilder::PARAM_BOOL)) - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_STR))); - $qb->executeStatement(); - } + } elseif ($lookedUp) { + $qb->update('maps_address_geo') + ->set('lat', $qb->createNamedParameter($lat, IQueryBuilder::PARAM_STR)) + ->set('lng', $qb->createNamedParameter($lng, IQueryBuilder::PARAM_STR)) + ->set('object_uri', $qb->createNamedParameter($uri, IQueryBuilder::PARAM_STR)) + ->set('looked_up', $qb->createNamedParameter($lookedUp, IQueryBuilder::PARAM_BOOL)) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_STR))); + $qb->executeStatement(); } return [$lat, $lng, $lookedUp]; @@ -149,7 +157,7 @@ private function lookupAddressInternal($adr): array { return $res; } - $adr_norm = strtolower(preg_replace('/\s+/', '', $adr)); + $adr_norm = strtolower((string)preg_replace('/\s+/', '', (string)$adr)); $qb = $this->dbconnection->getQueryBuilder(); $qb->select('lat', 'lng') @@ -162,6 +170,7 @@ private function lookupAddressInternal($adr): array { $res[1] = $row['lng']; $res[2] = true; } + $req->closeCursor(); return $res; @@ -169,7 +178,7 @@ private function lookupAddressInternal($adr): array { // looks up the address on external provider returns lat, lon, lookupstate // do lookup only if last one occured more than one second ago - private function lookupAddressExternal($adr): array { + private function lookupAddressExternal(string $adr): array { if (time() - intval($this->memcache->get('lastAddressLookup')) >= 1) { $opts = [ 'http' => [ @@ -182,9 +191,9 @@ private function lookupAddressExternal($adr): array { // we get rid of "post office box" field $splitted_adr = explode(';', $adr); // remove blank lines (#706) - $splitted_adr = array_filter(array_map('trim', $splitted_adr)); + $splitted_adr = array_filter(array_map(trim(...), $splitted_adr)); // ADR in VCard is mandated to 7 fields - if (sizeof($splitted_adr) == 7) { + if (count($splitted_adr) === 7) { $query_adr_parts = []; // This matches the nominatim query with the fields of 'ADR' in VCard $query_key_part = ['','','street', 'city','state', 'postalcode', 'country']; @@ -208,30 +217,36 @@ private function lookupAddressExternal($adr): array { ); if ($result_json !== false) { $result = \json_decode($result_json, true); - if (!(key_exists('request_failed', $result) and $result['request_failed'])) { + if (!(array_key_exists('request_failed', $result) && $result['request_failed'])) { $this->logger->debug('External looked up address: ' . $adr . ' with result' . print_r($result, true)); $this->memcache->set('lastAddressLookup', time()); $lat = null; $lon = null; foreach ($result as $addr) { - if (key_exists('lat', $addr) and key_exists('lon', $addr)) { - if (is_null($lat) - or (key_exists('category', $addr) and in_array($addr['category'], ['place', 'building', 'amenity']))) { - $lat = $addr['lat']; - $lon = $addr['lon']; - } + if (!(array_key_exists('lat', $addr) && array_key_exists('lon', $addr))) { + continue; + } + + if (!is_null($lat) && !(array_key_exists('category', $addr) && in_array($addr['category'], ['place', 'building', 'amenity']))) { + continue; } + + $lat = $addr['lat']; + $lon = $addr['lon']; } + return [$lat, $lon, true]; } } + $this->logger->debug('Externally looked failed'); } + return [null, null, false]; } // launch lookup for all addresses of the vCard - public function scheduleVCardForLookup($cardData, $cardUri) { + public function scheduleVCardForLookup($cardData, $cardUri): void { $vCard = Reader::read($cardData); $this->cleanUpDBContactAddresses($vCard, $cardUri); @@ -246,16 +261,17 @@ public function scheduleVCardForLookup($cardData, $cardUri) { } } - private function cleanUpDBContactAddresses($vCard, $uri) { + private function cleanUpDBContactAddresses($vCard, $uri): void { $qb = $this->dbconnection->getQueryBuilder(); // get all vcard addresses $vCardAddresses = []; foreach ($vCard->children() as $property) { if ($property->name === 'ADR') { $adr = $property->getValue(); - array_push($vCardAddresses, $adr); + $vCardAddresses[] = $adr; } } + // check which addresses from DB is not in the vCard anymore $adrIdToDelete = []; $qb->select('id', 'adr') @@ -264,9 +280,10 @@ private function cleanUpDBContactAddresses($vCard, $uri) { $req = $qb->executeQuery(); while ($row = $req->fetch()) { if (!in_array($row['adr'], $vCardAddresses)) { - array_push($adrIdToDelete, $row['id']); + $adrIdToDelete[] = $row['id']; } } + $req->closeCursor(); foreach ($adrIdToDelete as $id) { @@ -279,7 +296,7 @@ private function cleanUpDBContactAddresses($vCard, $uri) { } } - public function deleteDBContactAddresses($uri) { + public function deleteDBContactAddresses($uri): void { $qb = $this->dbconnection->getQueryBuilder(); $qb->delete('maps_address_geo') ->where( @@ -289,13 +306,17 @@ public function deleteDBContactAddresses($uri) { } // schedules the address for an external lookup + /** + * @return array + */ private function scheduleForLookup($adr, $uri): array { $geo = $this->lookupAddressInternal($adr); // if not found internally, ask external service if (!$geo[2]) { $geo = $this->lookupAddressExternal($adr); } - $adr_norm = strtolower(preg_replace('/\s+/', '', $adr)); + + $adr_norm = strtolower((string)preg_replace('/\s+/', '', (string)$adr)); $qb = $this->dbconnection->getQueryBuilder(); $qb->insert('maps_address_geo') ->values([ @@ -311,6 +332,7 @@ private function scheduleForLookup($adr, $uri): array { if (!$geo[2]) { $this->jobList->add(LookupMissingGeoJob::class, []); } + return [$id, $geo[0], $geo[1], $geo[2]]; } @@ -329,24 +351,28 @@ public function lookupMissingGeo($max = 200):bool { $req->closeCursor(); $i = 0; foreach ($result as $row) { - $i++; + ++$i; $geo = $this->lookupAddress($row['adr'], $row['object_uri']); // lookup failed if (!$geo[2]) { $lookedUpAll = false; } + \sleep(1); - \usleep(\rand(100, 100000)); + \usleep(random_int(100, 100000)); } + // not all addresses where loaded from database if ($i === $max) { $lookedUpAll = false; } + if ($lookedUpAll) { $this->logger->debug('Successfully looked up all addresses during cron job'); } else { $this->logger->debug('Failed to look up all addresses during cron job'); } + return $lookedUpAll; } } diff --git a/lib/Service/DevicesService.php b/lib/Service/DevicesService.php index 80ea39a67..2e7d33e83 100644 --- a/lib/Service/DevicesService.php +++ b/lib/Service/DevicesService.php @@ -1,5 +1,7 @@ dbconnection->quote($str); - } - /** - * @param string $userId - * @param int $pruneBefore * @return array with devices */ - public function getDevicesFromDB($userId) { + public function getDevicesFromDB(string $userId): array { $devices = []; $qb = $this->dbconnection->getQueryBuilder(); $qb->select('id', 'user_agent', 'color') @@ -70,16 +73,16 @@ public function getDevicesFromDB($userId) { 'shares' => [] ]; } + $req->closeCursor(); return $devices; } /** * @param string[] $tokens - * @return array * @throws Exception */ - public function getDevicesByTokens(array $tokens) { + public function getDevicesByTokens(array $tokens): array { $devices = []; $qb = $this->dbconnection->getquerybuilder(); $qb->select('d.id', 'd.user_agent', 'd.color', 's.token') @@ -107,20 +110,15 @@ public function getDevicesByTokens(array $tokens) { ]; } } + $req->closeCursor(); return $devices; } /** - * @param $userId - * @param $deviceId - * @param int|null $pruneBefore - * @param int|null $limit - * @param int|null $offset - * @return array * @throws \OCP\DB\Exception */ - public function getDevicePointsFromDB($userId, $deviceId, ?int $pruneBefore = 0, ?int $limit = null, ?int $offset = null) { + public function getDevicePointsFromDB(string $userId, int $deviceId, ?int $pruneBefore = 0, ?int $limit = null, ?int $offset = null): array { $qb = $this->dbconnection->getQueryBuilder(); // get coordinates $qb->selectDistinct(['p.id', 'lat', 'lng', 'timestamp', 'altitude', 'accuracy', 'battery']) @@ -137,12 +135,15 @@ public function getDevicePointsFromDB($userId, $deviceId, ?int $pruneBefore = 0, $qb->expr()->gt('timestamp', $qb->createNamedParameter(intval($pruneBefore), IQueryBuilder::PARAM_INT)) ); } + if (!is_null($offset)) { $qb->setFirstResult($offset); } + if (!is_null($limit)) { $qb->setMaxResults($limit); } + $qb->orderBy('timestamp', 'DESC'); $req = $qb->executeQuery(); @@ -158,6 +159,7 @@ public function getDevicePointsFromDB($userId, $deviceId, ?int $pruneBefore = 0, 'battery' => is_numeric($row['battery']) ? floatval($row['battery']) : null ]; } + $req->closeCursor(); return array_reverse($points); @@ -165,13 +167,9 @@ public function getDevicePointsFromDB($userId, $deviceId, ?int $pruneBefore = 0, /** * @param string[] $token - * @param int|null $pruneBefore - * @param int|null $limit - * @param int|null $offset - * @return array * @throws Exception */ - public function getDevicePointsByTokens(array $tokens, ?int $pruneBefore = 0, ?int $limit = 10000, ?int $offset = 0) { + public function getDevicePointsByTokens(array $tokens, ?int $pruneBefore = 0, ?int $limit = 10000, ?int $offset = 0): array { $qb = $this->dbconnection->getQueryBuilder(); // get coordinates $or = []; @@ -182,6 +180,7 @@ public function getDevicePointsByTokens(array $tokens, ?int $pruneBefore = 0, ?i $qb->expr()->gte('p.timestamp', 's.timestamp_from') ); } + $qb->select('p.id', 'lat', 'lng', 'timestamp', 'altitude', 'accuracy', 'battery') ->from('maps_device_points', 'p') ->innerJoin('p', 'maps_device_shares', 's', $qb->expr()->eq('p.device_id', 's.device_id')) @@ -192,12 +191,15 @@ public function getDevicePointsByTokens(array $tokens, ?int $pruneBefore = 0, ?i $qb->expr()->gt('timestamp', $qb->createNamedParameter(intval($pruneBefore), IQueryBuilder::PARAM_INT)) ); } + if (!is_null($offset)) { $qb->setFirstResult($offset); } + if (!is_null($limit)) { $qb->setMaxResults($limit); } + $qb->orderBy('timestamp', 'DESC'); $req = $qb->executeQuery(); @@ -213,6 +215,7 @@ public function getDevicePointsByTokens(array $tokens, ?int $pruneBefore = 0, ?i 'battery' => is_numeric($row['battery']) ? floatval($row['battery']) : null ]; } + $req->closeCursor(); return array_reverse($points); @@ -221,10 +224,9 @@ public function getDevicePointsByTokens(array $tokens, ?int $pruneBefore = 0, ?i /** * @param $userId * @param $deviceId - * @return array * @throws Exception */ - public function getDeviceTimePointsFromDb($userId, $deviceId) { + public function getDeviceTimePointsFromDb(string $userId, int $deviceId): array { $qb = $this->dbconnection->getQueryBuilder(); // get coordinates $qb->select('lat', 'lng', 'timestamp') @@ -243,11 +245,12 @@ public function getDeviceTimePointsFromDb($userId, $deviceId) { while ($row = $req->fetch()) { $points[intval($row['timestamp'])] = [floatval($row['lat']), floatval($row['lng'])]; } + $req->closeCursor(); return $points; } - public function getOrCreateDeviceFromDB($userId, $userAgent) { + public function getOrCreateDeviceFromDB(string $userId, string $userAgent) { $deviceId = null; $qb = $this->dbconnection->getQueryBuilder(); $qb->select('id') @@ -264,6 +267,7 @@ public function getOrCreateDeviceFromDB($userId, $userAgent) { $deviceId = intval($row['id']); break; } + $req->closeCursor(); if ($deviceId === null) { @@ -275,6 +279,7 @@ public function getOrCreateDeviceFromDB($userId, $userAgent) { $qb->executeStatement(); $deviceId = $qb->getLastInsertId(); } + return $deviceId; } @@ -291,35 +296,23 @@ public function addPointToDB($deviceId, $lat, $lng, $ts, $altitude, $battery, $a 'accuracy' => $qb->createNamedParameter(is_numeric($accuracy) ? $accuracy : null, IQueryBuilder::PARAM_STR) ]); $qb->executeStatement(); - $pointId = $qb->getLastInsertId(); - return $pointId; + return $qb->getLastInsertId(); } - public function addPointsToDB($deviceId, $points) { - $values = []; - foreach ($points as $p) { - $value = '(' - . $this->db_quote_escape_string($deviceId) . ', ' - . $this->db_quote_escape_string($p['lat']) . ', ' - . $this->db_quote_escape_string($p['lng']) . ', ' - . $this->db_quote_escape_string($p['date']) . ', ' - . ((isset($p['altitude']) and is_numeric($p['altitude'])) ? $this->db_quote_escape_string(floatval($p['altitude'])) : 'NULL') . ', ' - . ((isset($p['battery']) and is_numeric($p['battery'])) ? $this->db_quote_escape_string(floatval($p['battery'])) : 'NULL') . ', ' - . ((isset($p['accuracy']) and is_numeric($p['accuracy'])) ? $this->db_quote_escape_string(floatval($p['accuracy'])) : 'NULL') . ')'; - array_push($values, $value); - } - $valuesStr = implode(', ', $values); - $sql = ' - INSERT INTO *PREFIX*maps_device_points - (device_id, lat, lng, timestamp, - altitude, battery, accuracy) - VALUES ' . $valuesStr . ' ;'; - $req = $this->dbconnection->prepare($sql); - $req->execute(); - $req->closeCursor(); + public function addPointsToDB($deviceId, array $points): void { + try { + $this->dbconnection->beginTransaction(); + foreach ($points as $p) { + $this->addPointToDB($deviceId, $p['lat'], $p['lng'], $p['date'], $p['altitude'] ?? null, $p['battery'] ?? null, $p['accuracy'] ?? null); + } + + $this->dbconnection->commit(); + } catch (Exception) { + $this->dbconnection->rollBack(); + } } - public function getDeviceFromDB($id, $userId) { + public function getDeviceFromDB(int $id, ?string $userId): ?array { $device = null; $qb = $this->dbconnection->getQueryBuilder(); $qb->select('id', 'user_agent', 'color') @@ -332,6 +325,7 @@ public function getDeviceFromDB($id, $userId) { $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) ); } + $req = $qb->executeQuery(); while ($row = $req->fetch()) { @@ -342,26 +336,29 @@ public function getDeviceFromDB($id, $userId) { ]; break; } + $req->closeCursor(); return $device; } - public function editDeviceInDB($id, $color, $name) { + public function editDeviceInDB(int $id, ?string $color, ?string $name): void { $qb = $this->dbconnection->getQueryBuilder(); $qb->update('maps_devices'); - if (is_string($color) && strlen($color) > 0) { + if (is_string($color) && $color !== '') { $qb->set('color', $qb->createNamedParameter($color, IQueryBuilder::PARAM_STR)); } - if (is_string($name) && strlen($name) > 0) { + + if (is_string($name) && $name !== '') { $qb->set('user_agent', $qb->createNamedParameter($name, IQueryBuilder::PARAM_STR)); } + $qb->where( $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) ); $qb->executeStatement(); } - public function deleteDeviceFromDB($id) { + public function deleteDeviceFromDB(int $id): void { $qb = $this->dbconnection->getQueryBuilder(); $qb->delete('maps_devices') ->where( @@ -376,7 +373,7 @@ public function deleteDeviceFromDB($id) { $qb->executeStatement(); } - public function countPoints($userId, $deviceIdList, $begin, $end) { + public function countPoints(string $userId, array $deviceIdList, ?int $begin, ?int $end): int { $qb = $this->dbconnection->getQueryBuilder(); $qb->select($qb->createFunction('COUNT(*) AS co')) ->from('maps_devices', 'd') @@ -384,25 +381,29 @@ public function countPoints($userId, $deviceIdList, $begin, $end) { ->where( $qb->expr()->eq('d.user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) ); - if (is_array($deviceIdList) and count($deviceIdList) > 0) { + if ($deviceIdList !== []) { $or = $qb->expr()->orx(); foreach ($deviceIdList as $deviceId) { $or->add($qb->expr()->eq('d.id', $qb->createNamedParameter($deviceId, IQueryBuilder::PARAM_INT))); } + $qb->andWhere($or); } else { return 0; } - if ($begin !== null && is_numeric($begin)) { + + if ($begin !== null) { $qb->andWhere( $qb->expr()->gt('p.timestamp', $qb->createNamedParameter(intval($begin), IQueryBuilder::PARAM_INT)) ); } - if ($end !== null && is_numeric($end)) { + + if ($end !== null) { $qb->andWhere( $qb->expr()->lt('p.timestamp', $qb->createNamedParameter(intval($end), IQueryBuilder::PARAM_INT)) ); } + $req = $qb->executeQuery(); $count = 0; while ($row = $req->fetch()) { @@ -413,7 +414,7 @@ public function countPoints($userId, $deviceIdList, $begin, $end) { return $count; } - public function exportDevices($userId, $handler, $deviceIdList, $begin, $end, $appVersion, $filename) { + public function exportDevices(string $userId, $handler, $deviceIdList, $begin, $end, string $appVersion, string $filename): void { $gpxHeader = $this->generateGpxHeader($filename, $appVersion, count($deviceIdList)); fwrite($handler, $gpxHeader); @@ -423,10 +424,11 @@ public function exportDevices($userId, $handler, $deviceIdList, $begin, $end, $a $this->getAndWriteDevicePoints($devid, $begin, $end, $handler, $nbPoints, $userId); } } + fwrite($handler, ''); } - private function generateGpxHeader($name, $appVersion, $nbdev = 0) { + private function generateGpxHeader(string $name, string $appVersion, int $nbdev = 0): string { date_default_timezone_set('UTC'); $dt = new \DateTime(); $date = $dt->format('Y-m-d\TH:i:s\Z'); @@ -451,11 +453,11 @@ private function generateGpxHeader($name, $appVersion, $nbdev = 0) { if ($nbdev > 0) { $gpxText .= ' ' . $nbdev . ' device' . ($nbdev > 1 ? 's' : '') . '' . "\n"; } - $gpxText .= '' . "\n"; - return $gpxText; + + return $gpxText . ('' . "\n"); } - private function getAndWriteDevicePoints($devid, $begin, $end, $fd, $nbPoints, $userId) { + private function getAndWriteDevicePoints(int $devid, $begin, $end, $fd, int $nbPoints, string $userId): void { $device = $this->getDeviceFromDB($devid, $userId); $devname = $device['user_agent']; $qb = $this->dbconnection->getQueryBuilder(); @@ -479,11 +481,13 @@ private function getAndWriteDevicePoints($devid, $begin, $end, $fd, $nbPoints, $ $qb->expr()->gt('timestamp', $qb->createNamedParameter(intval($begin), IQueryBuilder::PARAM_INT)) ); } + if (intval($end) > 0) { $qb->andWhere( $qb->expr()->lt('timestamp', $qb->createNamedParameter(intval($end), IQueryBuilder::PARAM_INT)) ); } + $qb->setFirstResult($pointIndex); $qb->setMaxResults($chunkSize); $qb->orderBy('timestamp', 'ASC'); @@ -497,9 +501,10 @@ private function getAndWriteDevicePoints($devid, $begin, $end, $fd, $nbPoints, $ $date = ''; if (is_numeric($epoch)) { $epoch = intval($epoch); - $dt = new \DateTime("@$epoch"); + $dt = new \DateTime('@' . $epoch); $date = $dt->format('Y-m-d\TH:i:s\Z'); } + $alt = $row['altitude']; $acc = $row['accuracy']; $bat = $row['battery']; @@ -510,43 +515,53 @@ private function getAndWriteDevicePoints($devid, $begin, $end, $fd, $nbPoints, $ if (is_numeric($alt)) { $gpxText .= ' ' . sprintf('%.2f', floatval($alt)) . '' . "\n"; } + if (is_numeric($acc) && intval($acc) >= 0) { $gpxExtension .= ' ' . sprintf('%.2f', floatval($acc)) . '' . "\n"; } + if (is_numeric($bat) && intval($bat) >= 0) { $gpxExtension .= ' ' . sprintf('%.2f', floatval($bat)) . '' . "\n"; } + if ($gpxExtension !== '') { $gpxText .= ' ' . "\n" . $gpxExtension; $gpxText .= ' ' . "\n"; } + $gpxText .= ' ' . "\n"; } + $req->closeCursor(); // write the chunk fwrite($fd, $gpxText); - $pointIndex = $pointIndex + $chunkSize; + $pointIndex += $chunkSize; } + $gpxText = ' ' . "\n"; $gpxText .= '' . "\n"; fwrite($fd, $gpxText); } - public function importDevices($userId, $file) { + public function importDevices(string $userId, File $file) { $lowerFileName = strtolower($file->getName()); if ($this->endswith($lowerFileName, '.gpx')) { return $this->importDevicesFromGpx($userId, $file); - } elseif ($this->endswith($lowerFileName, '.kml')) { + } + + if ($this->endswith($lowerFileName, '.kml')) { $fp = $file->fopen('r'); $name = $file->getName(); return $this->importDevicesFromKml($userId, $fp, $name); - } elseif ($this->endswith($lowerFileName, '.kmz')) { + } + + if ($this->endswith($lowerFileName, '.kmz')) { return $this->importDevicesFromKmz($userId, $file); } } - public function importDevicesFromGpx($userId, $file) { + public function importDevicesFromGpx(string $userId, File $file): int { $this->currentPointList = []; $this->importUserId = $userId; $this->importFileName = $file->getName(); @@ -555,14 +570,14 @@ public function importDevicesFromGpx($userId, $file) { $xml_parser = xml_parser_create(); xml_set_object($xml_parser, $this); - xml_set_element_handler($xml_parser, 'gpxStartElement', 'gpxEndElement'); - xml_set_character_data_handler($xml_parser, 'gpxDataElement'); + xml_set_element_handler($xml_parser, $this->gpxStartElement(...), $this->gpxEndElement(...)); + xml_set_character_data_handler($xml_parser, $this->gpxDataElement(...)); $fp = $file->fopen('r'); // using xml_parse to be able to parse file chunks in case it's too big while ($data = fread($fp, 4096000)) { - if (!xml_parse($xml_parser, $data, feof($fp))) { + if (xml_parse($xml_parser, $data, feof($fp)) === 0) { $this->logger->error( 'Exception in ' . $file->getName() . ' parsing at line ' . xml_get_current_line_number($xml_parser) . ' : ' @@ -572,13 +587,17 @@ public function importDevicesFromGpx($userId, $file) { return 0; } } + fclose($fp); xml_parser_free($xml_parser); return ($this->trackIndex - 1); } - private function gpxStartElement($parser, $name, $attrs) { + /** + * @param array $attrs + */ + private function gpxStartElement($parser, $name, array $attrs): void { //$points, array($lat, $lon, $ele, $timestamp, $acc, $bat, $sat, $ua, $speed, $bearing) $this->currentXmlTag = $name; if ($name === 'TRK') { @@ -591,14 +610,16 @@ private function gpxStartElement($parser, $name, $attrs) { if (isset($attrs['LAT'])) { $this->currentPoint['lat'] = floatval($attrs['LAT']); } + if (isset($attrs['LON'])) { $this->currentPoint['lng'] = floatval($attrs['LON']); } } + //var_dump($attrs); } - private function gpxEndElement($parser, $name) { + private function gpxEndElement($parser, $name): void { if ($name === 'TRK') { $this->insideTrk = false; // log last track points @@ -606,10 +627,12 @@ private function gpxEndElement($parser, $name) { if ($this->importDevName === '') { $this->importDevName = $this->importFileName . ' ' . $this->trackIndex; } + $devid = $this->getOrCreateDeviceFromDB($this->importUserId, $this->importDevName); $this->addPointsToDB($devid, $this->currentPointList); } - $this->trackIndex++; + + ++$this->trackIndex; unset($this->currentPointList); } elseif ($name === 'TRKPT') { // store track point @@ -620,64 +643,65 @@ private function gpxEndElement($parser, $name) { $timestamp = $time->getTimestamp(); $this->currentPoint['date'] = $timestamp; } - array_push($this->currentPointList, $this->currentPoint); + + $this->currentPointList[] = $this->currentPoint; // if we have enough points, we log them and clean the points array if (count($this->currentPointList) >= 500) { if ($this->importDevName === '') { $this->importDevName = 'device' . $this->trackIndex; } + $devid = $this->getOrCreateDeviceFromDB($this->importUserId, $this->importDevName); $this->addPointsToDB($devid, $this->currentPointList); unset($this->currentPointList); $this->currentPointList = []; } - $this->pointIndex++; + + ++$this->pointIndex; } } - private function gpxDataElement($parser, $data) { - $d = trim($data); - if (!empty($d)) { + private function gpxDataElement($parser, $data): void { + $d = trim((string)$data); + if ($d !== '' && $d !== '0') { if ($this->currentXmlTag === 'ELE') { $this->currentPoint['altitude'] = (isset($this->currentPoint['altitude'])) ? $this->currentPoint['altitude'] . $d : $d; } elseif ($this->currentXmlTag === 'BATTERYLEVEL') { $this->currentPoint['battery'] = (isset($this->currentPoint['battery'])) ? $this->currentPoint['battery'] . $d : $d; } elseif ($this->currentXmlTag === 'ACCURACY') { $this->currentPoint['accuracy'] = (isset($this->currentPoint['accuracy'])) ? $this->currentPoint['accuracy'] . $d : $d; - } elseif ($this->insideTrk and $this->currentXmlTag === 'TIME') { + } elseif ($this->insideTrk && $this->currentXmlTag === 'TIME') { $this->currentPoint['date'] = (isset($this->currentPoint['date'])) ? $this->currentPoint['date'] . $d : $d; - } elseif ($this->insideTrk and $this->currentXmlTag === 'NAME') { - $this->importDevName = $this->importDevName . $d; + } elseif ($this->insideTrk && $this->currentXmlTag === 'NAME') { + $this->importDevName .= $d; } } } - public function importDevicesFromKmz($userId, $file) { + public function importDevicesFromKmz(string $userId, File $file): int { $path = $file->getStorage()->getLocalFile($file->getInternalPath()); $name = $file->getName(); $zf = new ZIP($path); - if (count($zf->getFiles()) > 0) { + if ($zf->getFiles() !== []) { $zippedFilePath = $zf->getFiles()[0]; $fstream = $zf->getStream($zippedFilePath, 'r'); - - $nbImported = $this->importDevicesFromKml($userId, $fstream, $name); - } else { - $nbImported = 0; + return $this->importDevicesFromKml($userId, $fstream, $name); } - return $nbImported; + + return 0; } - public function importDevicesFromKml($userId, $fp, $name) { + public function importDevicesFromKml(string $userId, $fp, string $name): int { $this->trackIndex = 1; $this->importUserId = $userId; $this->importFileName = $name; $xml_parser = xml_parser_create(); xml_set_object($xml_parser, $this); - xml_set_element_handler($xml_parser, 'kmlStartElement', 'kmlEndElement'); - xml_set_character_data_handler($xml_parser, 'kmlDataElement'); + xml_set_element_handler($xml_parser, $this->kmlStartElement(...), $this->kmlEndElement(...)); + xml_set_character_data_handler($xml_parser, $this->kmlDataElement(...)); while ($data = fread($fp, 4096000)) { - if (!xml_parse($xml_parser, $data, feof($fp))) { + if (xml_parse($xml_parser, $data, feof($fp)) === 0) { $this->logger->error( 'Exception in ' . $name . ' parsing at line ' . xml_get_current_line_number($xml_parser) . ' : ' @@ -686,35 +710,38 @@ public function importDevicesFromKml($userId, $fp, $name) { return 0; } } + fclose($fp); xml_parser_free($xml_parser); return ($this->trackIndex - 1); } - private function kmlStartElement($parser, $name, $attrs) { + /** + * @param array $attrs + */ + private function kmlStartElement($parser, $name, array $attrs): void { $this->currentXmlTag = $name; if ($name === 'GX:TRACK') { - if (isset($attrs['ID'])) { - $this->importDevName = $attrs['ID']; - } else { - $this->importDevName = $this->importFileName . ' ' . $this->trackIndex; - } + $this->importDevName = $attrs['ID'] ?? $this->importFileName . ' ' . $this->trackIndex; + $this->pointIndex = 1; $this->currentPointList = []; } elseif ($name === 'WHEN') { $this->currentPoint = []; } + //var_dump($attrs); } - private function kmlEndElement($parser, $name) { + private function kmlEndElement($parser, $name): void { if ($name === 'GX:TRACK') { // log last track points if (count($this->currentPointList) > 0) { $devid = $this->getOrCreateDeviceFromDB($this->importUserId, $this->importDevName); $this->addPointsToDB($devid, $this->currentPointList); } - $this->trackIndex++; + + ++$this->trackIndex; unset($this->currentPointList); } elseif ($name === 'GX:COORD') { // convert date @@ -723,6 +750,7 @@ private function kmlEndElement($parser, $name) { $timestamp = $time->getTimestamp(); $this->currentPoint['date'] = $timestamp; } + // get latlng if (isset($this->currentPoint['coords'])) { $spl = explode(' ', $this->currentPoint['coords']); @@ -734,8 +762,9 @@ private function kmlEndElement($parser, $name) { } } } + // store track point - array_push($this->currentPointList, $this->currentPoint); + $this->currentPointList[] = $this->currentPoint; // if we have enough points, we log them and clean the points array if (count($this->currentPointList) >= 500) { $devid = $this->getOrCreateDeviceFromDB($this->importUserId, $this->importDevName); @@ -743,13 +772,14 @@ private function kmlEndElement($parser, $name) { unset($this->currentPointList); $this->currentPointList = []; } - $this->pointIndex++; + + ++$this->pointIndex; } } - private function kmlDataElement($parser, $data) { - $d = trim($data); - if (!empty($d)) { + private function kmlDataElement($parser, $data): void { + $d = trim((string)$data); + if ($d !== '' && $d !== '0') { if ($this->currentXmlTag === 'WHEN') { $this->currentPoint['date'] = (isset($this->currentPoint['date'])) ? $this->currentPoint['date'] . $d : $d; } elseif ($this->currentXmlTag === 'GX:COORD') { @@ -758,32 +788,32 @@ private function kmlDataElement($parser, $data) { } } - private function endswith($string, $test) { - $strlen = strlen($string); + private function endswith($string, string $test) { + $strlen = strlen((string)$string); $testlen = strlen($test); if ($testlen > $strlen) { return false; } - return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0; + + return substr_compare((string)$string, $test, $strlen - $testlen, $testlen) === 0; } /** - * @param $folder - * @param bool $isCreatable - * @return mixed * @throws NotFoundException */ - public function getSharedDevicesFromFolder($folder, bool $isCreatable = true) { + public function getSharedDevicesFromFolder(Folder $folder, bool $isCreatable = true): mixed { try { + /** @var File $file */ $file = $folder->get('.device_shares.json'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { if ($isCreatable) { $file = $folder->newFile('.device_shares.json', $content = '[]'); } else { throw new NotFoundException(); } } - return json_decode($file->getContent(), true); + + return json_decode((string)$file->getContent(), true); } } diff --git a/lib/Service/FavoritesService.php b/lib/Service/FavoritesService.php index 46ea10bb8..54a258aa4 100644 --- a/lib/Service/FavoritesService.php +++ b/lib/Service/FavoritesService.php @@ -1,5 +1,7 @@ l10n = $l10n; - $this->secureRandom = $secureRandom; - $this->dbconnection = $dbconnection; - } - - private function db_quote_escape_string($str) { - return $this->dbconnection->quote($str); } /** - * @param string $userId - * @param int $pruneBefore - * @param string|null $filterCategory * @return array with favorites */ - public function getFavoritesFromDB($userId, $pruneBefore = 0, $filterCategory = null, $isDeletable = true, $isUpdateable = true, $isShareable = true) { + public function getFavoritesFromDB(string $userId, int $pruneBefore = 0, ?string $filterCategory = null, bool $isDeletable = true, bool $isUpdateable = true, bool $isShareable = true): array { $favorites = []; $qb = $this->dbconnection->getQueryBuilder(); $qb->select('id', 'name', 'date_created', 'date_modified', 'lat', 'lng', 'category', 'comment', 'extensions') @@ -72,11 +66,13 @@ public function getFavoritesFromDB($userId, $pruneBefore = 0, $filterCategory = $qb->expr()->gt('date_modified', $qb->createNamedParameter($pruneBefore, IQueryBuilder::PARAM_INT)) ); } + if ($filterCategory !== null) { $qb->andWhere( $qb->expr()->eq('category', $qb->createNamedParameter($filterCategory, IQueryBuilder::PARAM_STR)) ); } + $req = $qb->executeQuery(); while ($row = $req->fetch()) { @@ -105,11 +101,12 @@ public function getFavoritesFromDB($userId, $pruneBefore = 0, $filterCategory = 'isShareable' => $isShareable, ]; } + $req->closeCursor(); return $favorites; } - public function getFavoriteFromDB($id, $userId = null, $category = null, $isDeletable = true, $isUpdateable = true, $isShareable = true) { + public function getFavoriteFromDB(int $id, ?string $userId = null, ?string $category = null, bool $isDeletable = true, bool $isUpdateable = true, bool $isShareable = true): ?array { $favorite = null; $qb = $this->dbconnection->getQueryBuilder(); $qb->select('id', 'name', 'date_modified', 'date_created', 'lat', 'lng', 'category', 'comment', 'extensions') @@ -122,11 +119,13 @@ public function getFavoriteFromDB($id, $userId = null, $category = null, $isDele $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) ); } + if ($category !== null) { $qb->andWhere( $qb->expr()->eq('category', $qb->createNamedParameter($category, IQueryBuilder::PARAM_STR)) ); } + $req = $qb->executeQuery(); while ($row = $req->fetch()) { @@ -156,12 +155,13 @@ public function getFavoriteFromDB($id, $userId = null, $category = null, $isDele ]; break; } + $req->closeCursor(); return $favorite; } - public function addFavoriteToDB($userId, $name, $lat, $lng, $category, $comment, $extensions) { - $nowTimeStamp = (new \DateTime())->getTimestamp(); + public function addFavoriteToDB(string $userId, ?string $name, ?float $lat, ?float $lng, ?string $category, ?string $comment, ?string $extensions): int { + $nowTimeStamp = (new \DateTimeImmutable())->getTimestamp(); $qb = $this->dbconnection->getQueryBuilder(); $qb->insert('maps_favorites') ->values([ @@ -176,12 +176,11 @@ public function addFavoriteToDB($userId, $name, $lat, $lng, $category, $comment, 'extensions' => $qb->createNamedParameter($extensions, IQueryBuilder::PARAM_STR) ]); $qb->executeStatement(); - $favoriteId = $qb->getLastInsertId(); - return $favoriteId; + return $qb->getLastInsertId(); } - public function addMultipleFavoritesToDB($userId, $favoriteList) { - $nowTimeStamp = (new \DateTime())->getTimestamp(); + public function addMultipleFavoritesToDB(string $userId, array $favoriteList): void { + $nowTimeStamp = (new \DateTimeImmutable())->getTimestamp(); $qb = $this->dbconnection->getQueryBuilder(); $qb->insert('maps_favorites'); @@ -189,33 +188,43 @@ public function addMultipleFavoritesToDB($userId, $favoriteList) { try { $this->dbconnection->beginTransaction(); foreach ($favoriteList as $fav) { - if ( - !isset($fav['lat']) or !is_numeric($fav['lat']) - or !isset($fav['lng']) or !is_numeric($fav['lng']) - ) { + if (!isset($fav['lat'])) { continue; - } else { - $lat = floatval($fav['lat']); - $lng = floatval($fav['lng']); } + if (!is_numeric($fav['lat'])) { + continue; + } + if (!isset($fav['lng'])) { + continue; + } + if (!is_numeric($fav['lng'])) { + continue; + } + $lat = floatval($fav['lat']); + $lng = floatval($fav['lng']); + $values = [ 'user_id' => $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR), 'date_modified' => $qb->createNamedParameter($nowTimeStamp, IQueryBuilder::PARAM_INT), 'lat' => $qb->createNamedParameter($lat, IQueryBuilder::PARAM_INT), 'lng' => $qb->createNamedParameter($lng, IQueryBuilder::PARAM_INT), ]; - if (isset($fav['name']) and $fav['name'] !== '') { + if (isset($fav['name']) && $fav['name'] !== '') { $values['name'] = $qb->createNamedParameter($fav['name'], IQueryBuilder::PARAM_STR); } + if (isset($fav['date_created']) && is_numeric($fav['date_created'])) { $values['date_created'] = $qb->createNamedParameter($fav['date_created'], IQueryBuilder::PARAM_STR); } + if (isset($fav['category']) && $fav['category'] !== '') { $values['category'] = $qb->createNamedParameter($fav['category'], IQueryBuilder::PARAM_STR); } + if (isset($fav['comment']) && $fav['comment'] !== '') { $values['comment'] = $qb->createNamedParameter($fav['comment'], IQueryBuilder::PARAM_STR); } + if (isset($fav['extensions']) && $fav['extensions'] !== '') { $values['extensions'] = $qb->createNamedParameter($fav['extensions'], IQueryBuilder::PARAM_STR); } @@ -223,13 +232,14 @@ public function addMultipleFavoritesToDB($userId, $favoriteList) { $qb->values($values); $qb->executeStatement(); } + $this->dbconnection->commit(); } catch (\Throwable) { $this->dbconnection->rollback(); } } - public function renameCategoryInDB($userId, $cat, $newName) { + public function renameCategoryInDB(string $userId, $cat, $newName): void { $qb = $this->dbconnection->getQueryBuilder(); $qb->update('maps_favorites'); $qb->set('category', $qb->createNamedParameter($newName, IQueryBuilder::PARAM_STR)); @@ -242,7 +252,7 @@ public function renameCategoryInDB($userId, $cat, $newName) { $qb->executeStatement(); } - public function editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $extensions) { + public function editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $extensions): void { $nowTimeStamp = (new \DateTime())->getTimestamp(); $qb = $this->dbconnection->getQueryBuilder(); $qb->update('maps_favorites'); @@ -250,28 +260,34 @@ public function editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $e if ($name !== null) { $qb->set('name', $qb->createNamedParameter($name, IQueryBuilder::PARAM_STR)); } + if ($lat !== null) { $qb->set('lat', $qb->createNamedParameter($lat, IQueryBuilder::PARAM_STR)); } + if ($lng !== null) { $qb->set('lng', $qb->createNamedParameter($lng, IQueryBuilder::PARAM_STR)); } + if ($category !== null) { $qb->set('category', $qb->createNamedParameter($category, IQueryBuilder::PARAM_STR)); } + if ($comment !== null) { $qb->set('comment', $qb->createNamedParameter($comment, IQueryBuilder::PARAM_STR)); } + if ($extensions !== null) { $qb->set('extensions', $qb->createNamedParameter($extensions, IQueryBuilder::PARAM_STR)); } + $qb->where( $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) ); $qb->executeStatement(); } - public function deleteFavoriteFromDB($id) { + public function deleteFavoriteFromDB(int $id): void { $qb = $this->dbconnection->getQueryBuilder(); $qb->delete('maps_favorites') ->where( @@ -280,7 +296,7 @@ public function deleteFavoriteFromDB($id) { $qb->executeStatement(); } - public function deleteFavoritesFromDB($ids, $userId) { + public function deleteFavoritesFromDB($ids, $userId): void { $qb = $this->dbconnection->getQueryBuilder(); $qb->delete('maps_favorites') ->where( @@ -291,19 +307,21 @@ public function deleteFavoritesFromDB($ids, $userId) { foreach ($ids as $id) { $or->add($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))); } + $qb->andWhere($or); } else { return; } + $qb->executeStatement(); } - public function countFavorites($userId, $categoryList, $begin, $end) { - if ($categoryList === null - or (is_array($categoryList) and count($categoryList) === 0) + public function countFavorites($userId, $categoryList, $begin, $end): int { + if ($categoryList === null || is_array($categoryList) && count($categoryList) === 0 ) { return 0; } + $qb = $this->dbconnection->getQueryBuilder(); $qb->select($qb->createFunction('COUNT(*) AS co')) ->from('maps_favorites', 'f') @@ -315,39 +333,40 @@ public function countFavorites($userId, $categoryList, $begin, $end) { $qb->expr()->gt('date_created', $qb->createNamedParameter($begin, IQueryBuilder::PARAM_INT)) ); } + if ($end !== null) { $qb->andWhere( $qb->expr()->lt('date_created', $qb->createNamedParameter($end, IQueryBuilder::PARAM_INT)) ); } + // apply category restrictions if it's a non-empty array - if (!is_string($categoryList) - and is_array($categoryList) - and count($categoryList) > 0 + if (!is_string($categoryList) && is_array($categoryList) && count($categoryList) > 0 ) { $or = $qb->expr()->orx(); foreach ($categoryList as $cat) { $or->add($qb->expr()->eq('category', $qb->createNamedParameter($cat, IQueryBuilder::PARAM_STR))); } + $qb->andWhere($or); } + $nbFavorites = 0; $req = $qb->executeQuery(); while ($row = $req->fetch()) { $nbFavorites = intval($row['co']); break; } + $req->closeCursor(); return $nbFavorites; } /** - * @param $file - * @return array * @throws \Exception */ - public function getFavoritesFromJSON($file) { + public function getFavoritesFromJSON(File $file): array { $favorites = []; // Decode file content from JSON @@ -378,6 +397,7 @@ public function getFavoritesFromJSON($file) { } else { $time = $value['properties']['Published']; } + $currentFavorite['date_created'] = $time; } elseif ($key === 'Updated') { if (!is_numeric($value['properties']['Updated'])) { @@ -386,6 +406,7 @@ public function getFavoritesFromJSON($file) { } else { $time = $value['properties']['Updated']; } + $currentFavorite['date_modified'] = $time; } elseif ($key === 'Category') { $currentFavorite['category'] = $v; @@ -398,12 +419,15 @@ public function getFavoritesFromJSON($file) { } + if (!array_key_exists('category', $currentFavorite)) { $currentFavorite['category'] = $this->l10n->t('Personal'); } + if (!array_key_exists('comment', $currentFavorite)) { $currentFavorite['comment'] = ''; } + if ( array_key_exists('Location', $value['properties']) && array_key_exists('Address', $value['properties']['Location']) @@ -413,22 +437,25 @@ public function getFavoritesFromJSON($file) { // Store this favorite $favorites[] = $currentFavorite; - $id++; + ++$id; } return $favorites; } - public function getFavoriteFromJSON($file, $id) { + public function getFavoriteFromJSON(File $file, int $id) { $favorites = $this->getFavoritesFromJSON($file); if (array_key_exists($id, $favorites)) { return $favorites[$id]; - } else { - return null; } + return null; } - private function addFavoriteToJSONData($data, $name, $lat, $lng, $category, $comment, $extensions, $nowTimeStamp) { + /** + * @param array $data + * @return array> + */ + private function addFavoriteToJSONData(array $data, $name, $lat, $lng, $category, $comment, $extensions, $nowTimeStamp): array { $favorite = [ 'type' => 'Feature', 'geometry' => [ @@ -451,6 +478,7 @@ private function addFavoriteToJSONData($data, $name, $lat, $lng, $category, $com $favorite['properties'][$key] = $value; } } + $id = array_push($data['features'], $favorite) - 1; return [ 'id' => $id, @@ -460,7 +488,7 @@ private function addFavoriteToJSONData($data, $name, $lat, $lng, $category, $com public function addFavoriteToJSON($file, $name, $lat, $lng, $category, $comment, $extensions) { $nowTimeStamp = (new \DateTime())->getTimestamp(); - $data = json_decode($file->getContent(), true, 512); + $data = json_decode((string)$file->getContent(), true, 512); $tmp = $this->addFavoriteToJSONData($data, $name, $lat, $lng, $category, $comment, $extensions, $nowTimeStamp); @@ -468,39 +496,45 @@ public function addFavoriteToJSON($file, $name, $lat, $lng, $category, $comment, return $tmp['id']; } - public function addFavoritesToJSON($file, $favorites) { + /** + * @return mixed[] + */ + public function addFavoritesToJSON($file, $favorites): array { $nowTimeStamp = (new \DateTime())->getTimestamp(); - $data = json_decode($file->getContent(), true, 512); + $data = json_decode((string)$file->getContent(), true, 512); $ids = []; foreach ($favorites as $favorite) { $tmp = $this->addFavoriteToJSONData($data, $favorite['name'], $favorite['lat'], $favorite['lng'], $favorite['category'], $favorite['comment'], $favorite['extensions'], $nowTimeStamp); $ids[] = $tmp['id']; $data = $tmp['data']; } + $file->putContent(json_encode($data, JSON_PRETTY_PRINT)); return $ids; } - public function renameCategoryInJSON($file, $cat, $newName) { + public function renameCategoryInJSON($file, $cat, $newName): void { $nowTimeStamp = (new \DateTime())->getTimestamp(); - $data = json_decode($file->getContent(), true, 512); + $data = json_decode((string)$file->getContent(), true, 512); $this->logger->debug($cat); foreach ($data['features'] as $key => $value) { if (!array_key_exists('Category', $value['properties'])) { $value['properties']['Category'] = $this->l10n->t('Personal'); $data['features'][$key]['properties']['Category'] = $this->l10n->t('Personal'); } + if (array_key_exists('Category', $value['properties']) && $value['properties']['Category'] == $cat) { $data['features'][$key]['properties']['Category'] = $newName; $data['features'][$key]['properties']['Updated'] = $nowTimeStamp; } } + $file->putContent(json_encode($data, JSON_PRETTY_PRINT)); } - public function editFavoriteInJSON($file, $id, $name, $lat, $lng, $category, $comment, $extensions) { + public function editFavoriteInJSON($file, $id, $name, $lat, $lng, $category, $comment, $extensions): void { $nowTimeStamp = (new \DateTime())->getTimestamp(); - $data = json_decode($file->getContent(), true, 512); + $data = json_decode((string)$file->getContent(), true, 512); $createdTimeStamp = $data['features'][$id]['properties']['Published']; $favorite = [ 'type' => 'Feature', @@ -531,22 +565,23 @@ public function editFavoriteInJSON($file, $id, $name, $lat, $lng, $category, $co } public function deleteFavoriteFromJSON($file, $id): int { - $data = json_decode($file->getContent(), true, 512); + $data = json_decode((string)$file->getContent(), true, 512); $countBefore = count($data['features']); array_splice($data['features'], $id, 1); $file->putContent(json_encode($data, JSON_PRETTY_PRINT)); return $countBefore - count($data['features']); } - public function deleteFavoritesFromJSON($file, $ids) { - $data = json_decode($file->getContent(), true, 512); + public function deleteFavoritesFromJSON($file, $ids): void { + $data = json_decode((string)$file->getContent(), true, 512); foreach ($ids as $id) { array_splice($data['features'], $id, 1); } + $file->putContent(json_encode($data, JSON_PRETTY_PRINT)); } - public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $end, $appVersion) { + public function exportFavorites(?string $userId, $fileHandler, $categoryList, $begin, $end, string $appVersion): void { $qb = $this->dbconnection->getQueryBuilder(); $nbFavorites = $this->countFavorites($userId, $categoryList, $begin, $end); @@ -573,36 +608,39 @@ public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $e $qb->expr()->gte('date_created', $qb->createNamedParameter($begin, IQueryBuilder::PARAM_INT)) ); } + if ($end !== null) { $qb->andWhere( $qb->expr()->lte('date_created', $qb->createNamedParameter($end, IQueryBuilder::PARAM_INT)) ); } + // apply category restrictions if it's a non-empty array - if (!is_string($categoryList) - and is_array($categoryList) - and count($categoryList) > 0 + if (!is_string($categoryList) && is_array($categoryList) && count($categoryList) > 0 ) { $or = $qb->expr()->orx(); foreach ($categoryList as $cat) { $or->add($qb->expr()->eq('category', $qb->createNamedParameter($cat, IQueryBuilder::PARAM_STR))); } + $qb->andWhere($or); } + $qb->orderBy('date_created', 'ASC') ->setMaxResults($chunkSize) ->setFirstResult($favIndex); $req = $qb->executeQuery(); while ($row = $req->fetch()) { - $name = str_replace('&', '&', $row['name']); + $name = str_replace('&', '&', $row['name'] ?? ''); $epoch = $row['date_created']; $date = ''; if (is_numeric($epoch)) { $epoch = intval($epoch); - $dt = new \DateTime("@$epoch"); + $dt = new \DateTime('@' . $epoch); $date = $dt->format('Y-m-d\TH:i:s\Z'); } + $lat = $row['lat']; $lng = $row['lng']; $category = str_replace('&', '&', $row['category']); @@ -613,66 +651,78 @@ public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $e $gpxText .= ' ' . "\n"; $gpxText .= ' ' . $name . '' . "\n"; $gpxText .= ' ' . "\n"; - if ($category !== null && strlen($category) > 0) { + if ($category !== null && (string)$category !== '') { $gpxText .= ' ' . $category . '' . "\n"; } else { $gpxText .= ' ' . $this->l10n->t('Personal') . '' . "\n"; } - if ($comment !== null && strlen($comment) > 0) { + + if ($comment !== null && (string)$comment !== '') { $gpxText .= ' ' . $comment . '' . "\n"; } - if ($extensions !== null && strlen($extensions) > 0) { + + if ($extensions !== null && (string)$extensions !== '') { $gpxExtension .= ' ' . $extensions . '' . "\n"; } + if ($gpxExtension !== '') { $gpxText .= ' ' . "\n" . $gpxExtension; $gpxText .= ' ' . "\n"; } + $gpxText .= ' ' . "\n"; } + $req->closeCursor(); // write the chunk ! fwrite($fileHandler, $gpxText); - $favIndex = $favIndex + $chunkSize; + $favIndex += $chunkSize; } + $gpxEnd = '' . "\n"; fwrite($fileHandler, $gpxEnd); } - public function importFavorites($userId, $file) { + public function importFavorites(string $userId, File $file): array { $lowerFileName = strtolower($file->getName()); if ($this->endswith($lowerFileName, '.gpx')) { return $this->importFavoritesFromGpx($userId, $file); - } elseif ($this->endswith($lowerFileName, '.kml')) { + } + if ($this->endswith($lowerFileName, '.kml')) { $fp = $file->fopen('r'); $name = $file->getName(); return $this->importFavoritesFromKml($userId, $fp, $name); - } elseif ($this->endswith($lowerFileName, '.kmz')) { + } + if ($this->endswith($lowerFileName, '.kmz')) { return $this->importFavoritesFromKmz($userId, $file); - } elseif ($this->endswith($lowerFileName, '.json') or $this->endswith($lowerFileName, '.geojson')) { + } + if ($this->endswith($lowerFileName, '.json') || $this->endswith($lowerFileName, '.geojson')) { return $this->importFavoritesFromGeoJSON($userId, $file); } + return [ + 'nbImported' => 0, + 'linesFound' => false + ]; } - public function importFavoritesFromKmz($userId, $file) { + public function importFavoritesFromKmz(string $userId, File $file): array { $path = $file->getStorage()->getLocalFile($file->getInternalPath()); $name = $file->getName(); $zf = new ZIP($path); - if (count($zf->getFiles()) > 0) { + if ($zf->getFiles() !== []) { $zippedFilePath = $zf->getFiles()[0]; $fstream = $zf->getStream($zippedFilePath, 'r'); - $result = $this->importFavoritesFromKml($userId, $fstream, $name); - } else { - $result = [ - 'nbImported' => 0, - 'linesFound' => false - ]; + return $this->importFavoritesFromKml($userId, $fstream, $name); } - return $result; + + return [ + 'nbImported' => 0, + 'linesFound' => false + ]; } - public function importFavoritesFromKml($userId, $fp, $name) { + public function importFavoritesFromKml(string $userId, $fp, string $name): array { $this->nbImported = 0; $this->linesFound = false; $this->currentFavoritesList = []; @@ -682,21 +732,25 @@ public function importFavoritesFromKml($userId, $fp, $name) { $xml_parser = xml_parser_create(); xml_set_object($xml_parser, $this); - xml_set_element_handler($xml_parser, 'kmlStartElement', 'kmlEndElement'); - xml_set_character_data_handler($xml_parser, 'kmlDataElement'); + xml_set_element_handler($xml_parser, $this->kmlStartElement(...), $this->kmlEndElement(...)); + xml_set_character_data_handler($xml_parser, $this->kmlDataElement(...)); // using xml_parse to be able to parse file chunks in case it's too big while ($data = fread($fp, 4096000)) { - if (!xml_parse($xml_parser, $data, feof($fp))) { + if (xml_parse($xml_parser, $data, feof($fp)) === 0) { $this->logger->error( 'Exception in ' . $name . ' parsing at line ' . xml_get_current_line_number($xml_parser) . ' : ' . xml_error_string(xml_get_error_code($xml_parser)), ['app' => 'maps'] ); - return 0; + return [ + 'nbImported' => 0, + 'linesFound' => 0, + ]; } } + fclose($fp); xml_parser_free($xml_parser); @@ -706,38 +760,42 @@ public function importFavoritesFromKml($userId, $fp, $name) { ]; } - private function kmlStartElement($parser, $name, $attrs) { + private function kmlStartElement($parser, $name, $attrs): void { $this->currentXmlTag = $name; if ($name === 'PLACEMARK') { $this->currentFavorite = []; $this->kmlInsidePlacemark = true; } + if ($name === 'LINESTRING') { $this->linesFound = true; } } - private function kmlEndElement($parser, $name) { + private function kmlEndElement($parser, $name): void { if ($name === 'KML') { // create last bunch if (count($this->currentFavoritesList) > 0) { $this->addMultipleFavoritesToDB($this->importUserId, $this->currentFavoritesList); } + unset($this->currentFavoritesList); } elseif ($name === 'PLACEMARK') { $this->kmlInsidePlacemark = false; // store favorite - $this->nbImported++; + ++$this->nbImported; $this->currentFavorite['category'] = $this->kmlCurrentCategory; - if (!isset($this->currentFavorite['category']) or $this->currentFavorite['category'] === '') { + if (!isset($this->currentFavorite['category']) || $this->currentFavorite['category'] === '') { $this->currentFavorite['category'] = $this->l10n->t('Personal'); } + // convert date if (isset($this->currentFavorite['date_created'])) { $time = new \DateTime($this->currentFavorite['date_created']); $timestamp = $time->getTimestamp(); $this->currentFavorite['date_created'] = $timestamp; } + if (isset($this->currentFavorite['coordinates'])) { $spl = explode(',', $this->currentFavorite['coordinates']); if (count($spl) > 1) { @@ -745,7 +803,8 @@ private function kmlEndElement($parser, $name) { $this->currentFavorite['lng'] = floatval($spl[0]); } } - array_push($this->currentFavoritesList, $this->currentFavorite); + + $this->currentFavoritesList[] = $this->currentFavorite; // if we have enough favorites, we create them and clean the array if (count($this->currentFavoritesList) >= 500) { $this->addMultipleFavoritesToDB($this->importUserId, $this->currentFavoritesList); @@ -755,28 +814,26 @@ private function kmlEndElement($parser, $name) { } } - private function kmlDataElement($parser, $data) { - $d = trim($data); - if (!empty($d)) { + private function kmlDataElement($parser, $data): void { + $d = trim((string)$data); + if ($d !== '' && $d !== '0') { if (!$this->kmlInsidePlacemark) { if ($this->currentXmlTag === 'NAME') { - $this->kmlCurrentCategory = $this->kmlCurrentCategory . $d; - } - } else { - if ($this->currentXmlTag === 'NAME') { - $this->currentFavorite['name'] = (isset($this->currentFavorite['name'])) ? $this->currentFavorite['name'] . $d : $d; - } elseif ($this->currentXmlTag === 'WHEN') { - $this->currentFavorite['date_created'] = (isset($this->currentFavorite['date_created'])) ? $this->currentFavorite['date_created'] . $d : $d; - } elseif ($this->currentXmlTag === 'COORDINATES') { - $this->currentFavorite['coordinates'] = (isset($this->currentFavorite['coordinates'])) ? $this->currentFavorite['coordinates'] . $d : $d; - } elseif ($this->currentXmlTag === 'DESCRIPTION') { - $this->currentFavorite['comment'] = (isset($this->currentFavorite['comment'])) ? $this->currentFavorite['comment'] . $d : $d; + $this->kmlCurrentCategory .= $d; } + } elseif ($this->currentXmlTag === 'NAME') { + $this->currentFavorite['name'] = (isset($this->currentFavorite['name'])) ? $this->currentFavorite['name'] . $d : $d; + } elseif ($this->currentXmlTag === 'WHEN') { + $this->currentFavorite['date_created'] = (isset($this->currentFavorite['date_created'])) ? $this->currentFavorite['date_created'] . $d : $d; + } elseif ($this->currentXmlTag === 'COORDINATES') { + $this->currentFavorite['coordinates'] = (isset($this->currentFavorite['coordinates'])) ? $this->currentFavorite['coordinates'] . $d : $d; + } elseif ($this->currentXmlTag === 'DESCRIPTION') { + $this->currentFavorite['comment'] = (isset($this->currentFavorite['comment'])) ? $this->currentFavorite['comment'] . $d : $d; } } } - public function importFavoritesFromGpx($userId, $file) { + public function importFavoritesFromGpx(string $userId, File $file): array { $this->nbImported = 0; $this->linesFound = false; $this->currentFavoritesList = []; @@ -785,23 +842,27 @@ public function importFavoritesFromGpx($userId, $file) { $xml_parser = xml_parser_create(); xml_set_object($xml_parser, $this); - xml_set_element_handler($xml_parser, 'gpxStartElement', 'gpxEndElement'); - xml_set_character_data_handler($xml_parser, 'gpxDataElement'); + xml_set_element_handler($xml_parser, $this->gpxStartElement(...), $this->gpxEndElement(...)); + xml_set_character_data_handler($xml_parser, $this->gpxDataElement(...)); $fp = $file->fopen('r'); // using xml_parse to be able to parse file chunks in case it's too big while ($data = fread($fp, 4096000)) { - if (!xml_parse($xml_parser, $data, feof($fp))) { + if (xml_parse($xml_parser, $data, feof($fp)) === 0) { $this->logger->error( 'Exception in ' . $file->getName() . ' parsing at line ' . xml_get_current_line_number($xml_parser) . ' : ' . xml_error_string(xml_get_error_code($xml_parser)), ['app' => 'maps'] ); - return 0; + return [ + 'nbImported' => 0, + 'linesFound' => false + ]; } } + fclose($fp); xml_parser_free($xml_parser); @@ -811,7 +872,10 @@ public function importFavoritesFromGpx($userId, $file) { ]; } - private function gpxStartElement($parser, $name, $attrs) { + /** + * @param array $attrs + */ + private function gpxStartElement($parser, $name, array $attrs): void { $this->currentXmlTag = $name; if ($name === 'WPT') { $this->insideWpt = true; @@ -819,36 +883,41 @@ private function gpxStartElement($parser, $name, $attrs) { if (isset($attrs['LAT'])) { $this->currentFavorite['lat'] = floatval($attrs['LAT']); } + if (isset($attrs['LON'])) { $this->currentFavorite['lng'] = floatval($attrs['LON']); } } - if ($name === 'TRK' or $name === 'RTE') { + + if ($name === 'TRK' || $name === 'RTE') { $this->linesFound = true; } } - private function gpxEndElement($parser, $name) { + private function gpxEndElement($parser, $name): void { if ($name === 'GPX') { // create last bunch if (count($this->currentFavoritesList) > 0) { $this->addMultipleFavoritesToDB($this->importUserId, $this->currentFavoritesList); } + unset($this->currentFavoritesList); } elseif ($name === 'WPT') { $this->insideWpt = false; // store favorite - $this->nbImported++; + ++$this->nbImported; // convert date if (isset($this->currentFavorite['date_created'])) { $time = new \DateTime($this->currentFavorite['date_created']); $timestamp = $time->getTimestamp(); $this->currentFavorite['date_created'] = $timestamp; } - if (!isset($this->currentFavorite['category']) or $this->currentFavorite['category'] === '') { + + if (!isset($this->currentFavorite['category']) || $this->currentFavorite['category'] === '') { $this->currentFavorite['category'] = $this->l10n->t('Personal'); } - array_push($this->currentFavoritesList, $this->currentFavorite); + + $this->currentFavoritesList[] = $this->currentFavorite; // if we have enough favorites, we create them and clean the array if (count($this->currentFavoritesList) >= 500) { $this->addMultipleFavoritesToDB($this->importUserId, $this->currentFavoritesList); @@ -858,24 +927,27 @@ private function gpxEndElement($parser, $name) { } } - private function gpxDataElement($parser, $data) { - $d = trim($data); - if (!empty($d)) { - if ($this->insideWpt and $this->currentXmlTag === 'NAME') { + private function gpxDataElement($parser, $data): void { + $d = trim((string)$data); + if ($d !== '' && $d !== '0') { + if ($this->insideWpt && $this->currentXmlTag === 'NAME') { $this->currentFavorite['name'] = (isset($this->currentFavorite['name'])) ? $this->currentFavorite['name'] . $d : $d; - } elseif ($this->insideWpt and $this->currentXmlTag === 'TIME') { + } elseif ($this->insideWpt && $this->currentXmlTag === 'TIME') { $this->currentFavorite['date_created'] = (isset($this->currentFavorite['date_created'])) ? $this->currentFavorite['date_created'] . $d : $d; - } elseif ($this->insideWpt and $this->currentXmlTag === 'TYPE') { + } elseif ($this->insideWpt && $this->currentXmlTag === 'TYPE') { $this->currentFavorite['category'] = (isset($this->currentFavorite['category'])) ? $this->currentFavorite['category'] . $d : $d; - } elseif ($this->insideWpt and $this->currentXmlTag === 'DESC') { + } elseif ($this->insideWpt && $this->currentXmlTag === 'DESC') { $this->currentFavorite['comment'] = (isset($this->currentFavorite['comment'])) ? $this->currentFavorite['comment'] . $d : $d; - } elseif ($this->insideWpt and $this->currentXmlTag === 'MAPS-EXTENSIONS') { + } elseif ($this->insideWpt && $this->currentXmlTag === 'MAPS-EXTENSIONS') { $this->currentFavorite['extensions'] = (isset($this->currentFavorite['extensions'])) ? $this->currentFavorite['extensions'] . $d : $d; } } } - public function importFavoritesFromGeoJSON($userId, $file) { + /** + * @return array + */ + public function importFavoritesFromGeoJSON($userId, $file): array { $this->nbImported = 0; $this->linesFound = false; $this->currentFavoritesList = []; @@ -883,9 +955,9 @@ public function importFavoritesFromGeoJSON($userId, $file) { // Decode file content from JSON - $data = json_decode($file->getContent(), true, 512); + $data = json_decode((string)$file->getContent(), true, 512); - if ($data == null or !isset($data['features'])) { + if ($data == null || !isset($data['features'])) { $this->logger->error( 'Exception parsing ' . $file->getName() . ': no places found to import', ['app' => 'maps'] @@ -893,7 +965,7 @@ public function importFavoritesFromGeoJSON($userId, $file) { } // Loop over all favorite entries - foreach ($data['features'] as $key => $value) { + foreach ($data['features'] as $value) { $this->currentFavorite = []; // Ensure that we have a valid GeoJSON Point geometry @@ -921,8 +993,8 @@ public function importFavoritesFromGeoJSON($userId, $file) { // Store this favorite - array_push($this->currentFavoritesList, $this->currentFavorite); - $this->nbImported++; + $this->currentFavoritesList[] = $this->currentFavorite; + ++$this->nbImported; // if we have enough favorites, we create them and clean the array if (count($this->currentFavoritesList) >= 500) { @@ -933,9 +1005,10 @@ public function importFavoritesFromGeoJSON($userId, $file) { } // Store last set of favorites - if (count($this->currentFavoritesList) > 0) { + if ($this->currentFavoritesList !== []) { $this->addMultipleFavoritesToDB($this->importUserId, $this->currentFavoritesList); } + unset($this->currentFavoritesList); return [ @@ -944,13 +1017,14 @@ public function importFavoritesFromGeoJSON($userId, $file) { ]; } - private function endswith($string, $test) { - $strlen = strlen($string); + private function endswith($string, string $test) { + $strlen = strlen((string)$string); $testlen = strlen($test); if ($testlen > $strlen) { return false; } - return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0; + + return substr_compare((string)$string, $test, $strlen - $testlen, $testlen) === 0; } } diff --git a/lib/Service/GeophotoService.php b/lib/Service/GeophotoService.php index c5e8f724f..2aa597821 100644 --- a/lib/Service/GeophotoService.php +++ b/lib/Service/GeophotoService.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\Service; use OC\Files\Search\SearchBinaryOperator; @@ -25,56 +26,33 @@ use OCP\Files\NotPermittedException; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; +use OCP\ICache; use OCP\ICacheFactory; -use OCP\IL10N; use OCP\IPreview; -use Psr\Log\LoggerInterface; use RuntimeException; class GeophotoService { + private readonly ICache $photosCache; + + private readonly ICache $timeOrderedPointSetsCache; + + private readonly ICache $backgroundJobCache; - private $l10n; - private $root; - private $photoMapper; - private $preview; - private $tracksService; private $timeorderedPointSets; - private $devicesService; - private $cacheFactory; - private $userId; - private \OCP\ICache $photosCache; - private \OCP\ICache $timeOrderedPointSetsCache; - private \OCP\ICache $backgroundJobCache; public function __construct( - private LoggerInterface $logger, - IRootFolder $root, - IL10N $l10n, - GeophotoMapper $photoMapper, - IPreview $preview, - TracksService $tracksService, - DevicesService $devicesService, - ICacheFactory $cacheFactory, - $userId, + private readonly IRootFolder $root, + private readonly GeophotoMapper $photoMapper, + private readonly IPreview $preview, + private readonly TracksService $tracksService, + private readonly DevicesService $devicesService, + private readonly ICacheFactory $cacheFactory, ) { - $this->root = $root; - $this->l10n = $l10n; - $this->photoMapper = $photoMapper; - $this->preview = $preview; - $this->tracksService = $tracksService; - $this->timeorderedPointSets = null; - $this->userId = $userId; - $this->devicesService = $devicesService; - $this->cacheFactory = $cacheFactory; $this->photosCache = $this->cacheFactory->createDistributed('maps:photos'); $this->timeOrderedPointSetsCache = $this->cacheFactory->createDistributed('maps:time-ordered-point-sets'); $this->backgroundJobCache = $this->cacheFactory->createDistributed('maps:background-jobs'); } - /** - * @param string $userId - * @return bool - */ public function clearCache(string $userId = ''): bool { try { $this->photosCache->clear($userId); @@ -83,35 +61,31 @@ public function clearCache(string $userId = ''): bool { $this->backgroundJobCache->clear('recentlyUpdated:' . $userId); return true; - } catch (\Exception $e) { + } catch (\Exception) { return false; } } /** - * @param string $userId - * @param ?Folder $folder =null - * @param bool $respectNomediaAndNoimage =true - * @param bool $hideImagesOnCustomMaps =true - * @param bool $hideImagesInMapsFolder - * @return array * @throws Exception * @throws NoUserException * @throws NotFoundException * @throws NotPermittedException */ - public function getAll(string $userId, $folder = null, bool $respectNomediaAndNoimage = true, bool $hideImagesOnCustomMaps = false, bool $hideImagesInMapsFolder = true): array { + public function getAll(string $userId, ?Folder $folder = null, bool $respectNomediaAndNoimage = true, bool $hideImagesOnCustomMaps = false, bool $hideImagesInMapsFolder = true): array { $userFolder = $this->getFolderForUser($userId); if (is_null($folder)) { $folder = $userFolder; } - $key = $userId . ':' . $userFolder->getRelativePath($folder->getPath()) . ':' . (string)$respectNomediaAndNoimage . ':' . (string)$hideImagesOnCustomMaps . ':' . (string)$hideImagesInMapsFolder; + + $key = $userId . ':' . $userFolder->getRelativePath($folder->getPath()) . ':' . $respectNomediaAndNoimage . ':' . $hideImagesOnCustomMaps . ':' . $hideImagesInMapsFolder; $filesById = $this->photosCache->get($key); if ($filesById === null) { $ignoredPaths = $respectNomediaAndNoimage ? $this->getIgnoredPaths($userId, $folder, $hideImagesOnCustomMaps) : []; if ($hideImagesInMapsFolder) { $ignoredPaths[] = '/Maps'; } + $photoEntities = $this->photoMapper->findAll($userId); $filesById = []; @@ -120,23 +94,20 @@ public function getAll(string $userId, $folder = null, bool $respectNomediaAndNo // this path is relative to owner's storage //$path = $cacheEntry->getPath(); //but we want it relative to current user's storage - $files = $folder->getById($photoEntity->getFileId()); - if (empty($files)) { - continue; - } - $file = array_shift($files); - + $file = $folder->getFirstNodeById($photoEntity->getFileId()); if ($file === null) { continue; } + $path = $userFolder->getRelativePath($file->getPath()); $isIgnored = false; foreach ($ignoredPaths as $ignoredPath) { - if (str_starts_with($path, $ignoredPath)) { + if (str_starts_with($path, (string)$ignoredPath)) { $isIgnored = true; break; } } + if (!$isIgnored) { $isRoot = $file === $userFolder; @@ -164,27 +135,22 @@ public function getAll(string $userId, $folder = null, bool $respectNomediaAndNo $filesById[] = $file_object; } } + $this->photosCache->set($key, $filesById, 60 * 60 * 24); } + return $filesById; } /** - * @param string $userId - * @param ?Folder $folder =null - * @param bool $respectNomediaAndNoimage - * @param bool $hideImagesOnCustomMaps - * @param bool $hideImagesInMapsFolder * @param string|null $timezone locale time zone used by images - * @param int $limit - * @param int $offset * @return array with geodatas of all nonLocalizedPhotos * @throws Exception * @throws NoUserException * @throws NotFoundException * @throws NotPermittedException */ - public function getNonLocalized(string $userId, $folder = null, bool $respectNomediaAndNoimage = true, bool $hideImagesOnCustomMaps = false, bool $hideImagesInMapsFolder = true, ?string $timezone = null, int $limit = 250, int $offset = 0): array { + public function getNonLocalized(string $userId, ?Folder $folder = null, bool $respectNomediaAndNoimage = true, bool $hideImagesOnCustomMaps = false, bool $hideImagesInMapsFolder = true, ?string $timezone = null, int $limit = 250, int $offset = 0): array { $userFolder = $this->getFolderForUser($userId); if (is_null($folder)) { $folder = $userFolder; @@ -194,36 +160,32 @@ public function getNonLocalized(string $userId, $folder = null, bool $respectNom if ($hideImagesInMapsFolder) { $ignoredPaths[] = '/Maps'; } + $this->loadTimeorderedPointSets($userId, $folder, $respectNomediaAndNoimage, $hideImagesOnCustomMaps, $hideImagesInMapsFolder); $photoEntities = $this->photoMapper->findAllNonLocalized($userId, $limit, $offset); $suggestionsBySource = []; - $cache = $folder->getStorage()->getCache(); + $folder->getStorage()->getCache(); $previewEnableMimetypes = $this->getPreviewEnabledMimetypes(); - if (!is_null($timezone)) { - $tz = new \DateTimeZone($timezone); - } else { - $tz = new \DateTimeZone(\date_default_timezone_get()); - } + $tz = is_null($timezone) ? new \DateTimeZone(\date_default_timezone_get()) : new \DateTimeZone($timezone); + foreach ($photoEntities as $photoEntity) { // this path is relative to owner's storage //$path = $cacheEntry->getPath(); // but we want it relative to current user's storage - $files = $folder->getById($photoEntity->getFileId()); - if (empty($files)) { - continue; - } - $file = array_shift($files); + $file = $folder->getFirstNodeById($photoEntity->getFileId()); if ($file === null) { continue; } + $path = $userFolder->getRelativePath($file->getPath()); $isIgnored = false; foreach ($ignoredPaths as $ignoredPath) { - if (str_starts_with($path, $ignoredPath)) { + if (str_starts_with($path, (string)$ignoredPath)) { $isIgnored = true; break; } } + if (!$isIgnored) { $isRoot = $file === $userFolder; @@ -259,27 +221,27 @@ public function getNonLocalized(string $userId, $folder = null, bool $respectNom if (!array_key_exists($key, $suggestionsBySource)) { $suggestionsBySource[$key] = []; } + $suggestionsBySource[$key][] = $file_object; } } } + return $suggestionsBySource; } /** - * @param $userId - * @param $folder - * @return array * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ - private function getIgnoredPaths($userId, $folder = null, $hideImagesOnCustomMaps = true) { + private function getIgnoredPaths(string $userId, ?Folder $folder = null, bool $hideImagesOnCustomMaps = true): array { $ignoredPaths = []; $userFolder = $this->getFolderForUser($userId); if (is_null($folder)) { $folder = $userFolder; } + $ignoreFileMimetypes = [ 'application/x-nextcloud-noindex', 'application/x-nextcloud-nomedia', @@ -288,9 +250,8 @@ private function getIgnoredPaths($userId, $folder = null, $hideImagesOnCustomMap if ($hideImagesOnCustomMaps) { $ignoreFileMimetypes[] = 'application/x-nextcloud-maps'; } - $func = function (string $i): SearchComparison { - return new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $i); - }; + + $func = (fn (string $i): SearchComparison => new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $i)); $excludedNodes = $folder->search(new SearchQuery( new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, array_map( $func, @@ -303,6 +264,7 @@ private function getIgnoredPaths($userId, $folder = null, $hideImagesOnCustomMap foreach ($excludedNodes as $node) { $ignoredPaths[] = $userFolder->getRelativePath($node->getParent()->getPath()); } + return $ignoredPaths; } @@ -310,7 +272,6 @@ private function getIgnoredPaths($userId, $folder = null, $hideImagesOnCustomMap * returns a array of locations for a given date * * @param $dateTaken int - * @return array */ private function getLocationGuesses(int $dateTaken): array { $locations = []; @@ -320,6 +281,7 @@ private function getLocationGuesses(int $dateTaken): array { $locations[$key] = $location; } } + return $locations; } @@ -329,25 +291,24 @@ private function getLocationGuesses(int $dateTaken): array { * This function loads this Arrays from all Track files of the user. */ private function loadTimeorderedPointSets(string $userId, $folder = null, bool $respectNomediaAndNoimage = true, bool $hideTracksOnCustomMaps = false, bool $hideTracksInMapsFolder = true): void { - $key = $userId . ':' . (string)$respectNomediaAndNoimage . ':' . (string)$hideTracksOnCustomMaps . ':' . (string)$hideTracksInMapsFolder; + $key = $userId . ':' . $respectNomediaAndNoimage . ':' . $hideTracksOnCustomMaps . ':' . $hideTracksInMapsFolder; $this->timeorderedPointSets = $this->timeOrderedPointSetsCache->get($key); if (is_null($this->timeorderedPointSets)) { $userFolder = $this->getFolderForUser($userId); foreach ($this->tracksService->getTracksFromDB($userId, $folder, $respectNomediaAndNoimage, $hideTracksOnCustomMaps, $hideTracksInMapsFolder) as $gpxfile) { - $res = $userFolder->getById($gpxfile['file_id']); - if (is_array($res) and count($res) > 0) { - $file = array_shift($res); - if ($file instanceof File) { - foreach ($this->getTracksFromGPX($file->getContent()) as $i => $track) { - $this->timeorderedPointSets['track:' . $gpxfile['id'] . ':' . $i] = $this->getTimeorderdPointsFromTrack($track); - } + $file = $userFolder->getFirstNodeById($gpxfile['file_id']); + if ($file instanceof File) { + foreach ($this->getTracksFromGPX($file->getContent()) as $i => $track) { + $this->timeorderedPointSets['track:' . $gpxfile['id'] . ':' . $i] = $this->getTimeorderdPointsFromTrack($track); } } } + foreach ($this->devicesService->getDevicesFromDB($userId) as $device) { $device_points = $this->devicesService->getDeviceTimePointsFromDb($userId, $device['id']); $this->timeorderedPointSets['device:' . $device['id']] = $device_points; } + $this->timeOrderedPointSetsCache->set($key, $this->timeorderedPointSets); } } @@ -357,16 +318,21 @@ private function loadTimeorderedPointSets(string $userId, $folder = null, bool $ * @param $content * @return array */ + /** + * @return \SimpleXMLElement[] + */ private function getTracksFromGPX($content): array { $tracks = []; libxml_use_internal_errors(false); - $gpx = simplexml_load_string($content); + $gpx = simplexml_load_string((string)$content); if ($gpx === false) { $this->handleXMLError(); } + foreach ($gpx->trk as $trk) { $tracks[] = $trk; } + return $tracks; } @@ -390,42 +356,32 @@ private function getTimeorderdPointsFromTrack($track): array { $points = []; foreach ($track->trkseg as $seg) { foreach ($seg->trkpt as $pt) { - $points[strtotime($pt->time)] = [(string)$pt['lat'],(string)$pt['lon']]; + $points[strtotime((string)$pt->time)] = [(string)$pt['lat'],(string)$pt['lon']]; } } + foreach ($track->trkpt as $pt) { - $points[strtotime($pt->time)] = [(string)$pt['lat'],(string)$pt['lon']]; + $points[strtotime((string)$pt->time)] = [(string)$pt['lat'],(string)$pt['lon']]; } - $foo = ksort($points); + ksort($points); return $points; } - /** - * @param int $timeUTC - * @param float $lat - * @param float $lng - * @return void - */ - private function getLocalTime(int $timeUTC, float $lat, float $lng) { - - } - /** * @param $dateTaken int timestamp of the picture * @param $points array sorted by keys timestamp => [lat, lng] */ private function getLocationFromSequenceOfPoints(int $dateTaken, array $points): ?array { - $foo = end($points); - $end = key($points); - $foo = reset($points); - $start = key($points); - if ($start > $dateTaken or $end < $dateTaken) { + $end = array_key_last($points); + $start = array_key_first($points); + if ($start > $dateTaken || $end < $dateTaken) { return null; } + $smaller = null; $bigger = null; - foreach ($points as $time => $locations) { + foreach (array_keys($points) as $time) { if ($time < $dateTaken) { $smaller = $time; } else { @@ -433,17 +389,21 @@ private function getLocationFromSequenceOfPoints(int $dateTaken, array $points): break; } } - if (!is_null($smaller) and !is_null($bigger)) { + + if (!is_null($smaller) && !is_null($bigger)) { $d = $bigger - $smaller; $t = ($dateTaken - $smaller) / $d; $latd = $points[$bigger][0] - $points[$smaller][0]; $lngd = $points[$bigger][1] - $points[$smaller][1]; return [$points[$smaller][0] + $t * $latd, $points[$smaller][1] + $t * $lngd]; - } else { - return null; } + + return null; } + /** + * @return 'image/jpeg'[]|'image/tiff'[] + */ private function getPreviewEnabledMimetypes(): array { $enabledMimeTypes = []; foreach (PhotofilesService::PHOTO_MIME_TYPES as $mimeType) { @@ -451,18 +411,15 @@ private function getPreviewEnabledMimetypes(): array { $enabledMimeTypes[] = $mimeType; } } + return $enabledMimeTypes; } - private function normalizePath($path) { + private function normalizePath(string $path): string { return str_replace('files', '', $path); } - /** - * @param string $userId the user id - * @return Folder - */ - private function getFolderForUser($userId) { + private function getFolderForUser(string $userId): Folder { return $this->root->getUserFolder($userId); } diff --git a/lib/Service/MimetypeService.php b/lib/Service/MimetypeService.php index fc42ccfa5..c7fcc0e38 100644 --- a/lib/Service/MimetypeService.php +++ b/lib/Service/MimetypeService.php @@ -1,5 +1,7 @@ mimeTypeLoader = $mimeTypeLoader; } - public function registerForExistingFiles() { + public function registerForExistingFiles(): void { $mimeTypeId = $this->mimeTypeLoader->getId('application/x-nextcloud-maps'); $this->mimeTypeLoader->updateFilecache('maps', $mimeTypeId); @@ -31,7 +33,7 @@ public function registerForExistingFiles() { $this->mimeTypeLoader->updateFilecache('notrack', $mimeTypeId); } - public function registerForNewFiles() { + public function registerForNewFiles(): void { $mapping = [ 'maps' => ['application/x-nextcloud-maps'], 'noindex' => ['application/x-nextcloud-noindex'], diff --git a/lib/Service/MyMapsService.php b/lib/Service/MyMapsService.php index 54286e467..34f054e3b 100644 --- a/lib/Service/MyMapsService.php +++ b/lib/Service/MyMapsService.php @@ -1,5 +1,7 @@ root->getUserFolder($userId); if (!$userFolder->nodeExists('/Maps')) { $userFolder->newFolder('Maps'); } + if ($userFolder->nodeExists('/Maps')) { $mapsFolder = $userFolder->get('/Maps'); if (!($mapsFolder instanceof Folder)) { - $response = '/Maps is not a directory'; - return $response; - } elseif (!$mapsFolder->isCreatable()) { - $response = '/Maps is not writeable'; - return $response; + return '/Maps is not a directory'; + } + + if (!$mapsFolder->isCreatable()) { + return '/Maps is not writeable'; } } else { - $response = 'Impossible to create /Maps'; - return $response; - } - if ($counter > 0) { - $folderName = $newName . ' ' . $counter; - } else { - $folderName = $newName; + return 'Impossible to create /Maps'; } + $folderName = $counter > 0 ? $newName . ' ' . $counter : $newName; + if ($mapsFolder->nodeExists($folderName)) { return $this->addMyMap($newName, $userId, $counter + 1); } + $mapFolder = $mapsFolder->newFolder($folderName); $mapFolder->newFile('.index.maps', '{}'); + $isRoot = $mapFolder->getPath() === $userFolder->getPath(); - $MyMap = [ + return [ 'id' => $mapFolder->getId(), 'name' => $folderName, 'color' => null, @@ -84,23 +83,23 @@ public function addMyMap($newName, $userId, $counter = 0) { 'sharePermissions' => $mapFolder->getPermissions(), ] ]; - return $MyMap; } + /** + * @return array + */ private function node2MyMap($node, $userFolder):array { - $mapData = json_decode($node->getContent(), true); - if (isset($mapData['name'])) { - $name = $mapData['name']; - } else { - $name = $node->getParent()->getName(); - } + $mapData = json_decode((string)$node->getContent(), true); + $name = $mapData['name'] ?? $node->getParent()->getName(); + $color = null; if (isset($mapData['color'])) { $color = $mapData['color']; } + $parentNode = $node->getParent(); $isRoot = $parentNode->getPath() === $userFolder->getPath(); - $MyMap = [ + return [ 'id' => $parentNode->getId(), 'name' => $name, 'color' => $color, @@ -124,16 +123,14 @@ private function node2MyMap($node, $userFolder):array { 'sharePermissions' => $parentNode->getPermissions(), ] ]; - return $MyMap; } /** * @param $userId - * @return array * @throws NoUserException * @throws NotPermittedException */ - public function getAllMyMaps($userId) { + public function getAllMyMaps($userId): array { $userFolder = $this->root->getUserFolder($userId); $MyMaps = []; $MyMapsNodes = $userFolder->search(new SearchQuery( @@ -145,6 +142,7 @@ public function getAllMyMaps($userId) { $MyMaps[] = $this->node2MyMap($node, $userFolder); } } + return $MyMaps; } @@ -155,7 +153,7 @@ public function getAllMyMaps($userId) { * @param string $userId The current user id * @return null|array Either the MyMap or null if not found with that id for the given user */ - public function getMyMap(int $id, string $userId) { + public function getMyMap(int $id, string $userId): ?array { $userFolder = $this->root->getUserFolder($userId); $node = $userFolder->getFirstNodeById($id); if ($node instanceof Folder) { @@ -169,22 +167,25 @@ public function getMyMap(int $id, string $userId) { if ($node->getMimetype() === 'application/x-nextcloud-maps') { return $this->node2MyMap($node, $userFolder); } + return null; } public function updateMyMap($id, $values, $userId) { $userFolder = $this->root->getUserFolder($userId); - $folders = $userFolder->getById($id); - $folder = array_shift($folders); + $folder = $userFolder->getFirstNodeById($id); if (!($folder instanceof Folder)) { return []; } + try { + /** @var File $file */ $file = $folder->get('.index.maps'); - } catch (NotFoundException $e) { + } catch (NotFoundException) { $file = $folder->newFile('.index.maps', '{}'); } - $mapData = json_decode($file->getContent(), true); + + $mapData = json_decode((string)$file->getContent(), true); $renamed = false; foreach ($values as $key => $value) { if ($key === 'newName') { @@ -192,58 +193,61 @@ public function updateMyMap($id, $values, $userId) { $newName = $value; $renamed = true; } + if (is_null($value)) { unset($mapData[$key]); } else { $mapData[$key] = $value; } } + $file->putContent(json_encode($mapData, JSON_PRETTY_PRINT)); - if ($renamed) { - if ($userFolder->nodeExists('/Maps')) { - $mapsFolder = $userFolder->get('/Maps'); - if ($folder->getParent()->getId() === $mapsFolder->getId()) { - try { - $folder->move($mapsFolder->getPath() . '/' . $newName); - } catch (\Exception $e) { - } + if ($renamed && $userFolder->nodeExists('/Maps')) { + $mapsFolder = $userFolder->get('/Maps'); + if ($folder->getParent()->getId() === $mapsFolder->getId()) { + try { + $folder->move($mapsFolder->getPath() . '/' . $newName); + } catch (\Exception) { } } } + return $mapData; } - public function deleteMyMap($id, $userId) { + public function deleteMyMap($id, $userId): int { $userFolder = $this->root->getUserFolder($userId); - $folders = $userFolder->getById($id); - $folder = array_shift($folders); + $folder = $userFolder->getFirstNodeById($id); if (!($folder instanceof Folder)) { return 1; } + if ($userFolder->nodeExists('/Maps')) { $mapsFolder = $userFolder->get('/Maps'); if ($folder->getParent()->getId() === $mapsFolder->getId()) { try { $folder->delete(); - } catch (\Exception $e) { + } catch (\Exception) { return 1; } } else { try { $file = $folder->get('.index.maps'); $file->delete(); - } catch (\Exception $e) { + } catch (\Exception) { return 1; } } } + try { $file = $folder->get('.index.maps'); $file->delete(); - } catch (NotFoundException $e) { + } catch (NotFoundException) { return 1; } + return 0; } diff --git a/lib/Service/PhotofilesService.php b/lib/Service/PhotofilesService.php index a0b979db8..47d495151 100644 --- a/lib/Service/PhotofilesService.php +++ b/lib/Service/PhotofilesService.php @@ -1,5 +1,7 @@ * @copyright Piotr Bator 2017 */ - namespace OCA\Maps\Service; use OCA\Maps\BackgroundJob\AddPhotoJob; @@ -21,10 +22,13 @@ use OCA\Maps\Helper\ExifGeoData; use OCP\AppFramework\Db\DoesNotExistException; use OCP\BackgroundJob\IJobList; -use OCP\Files\FileInfo; +use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; +use OCP\Files\NotFoundException; +use OCP\Files\StorageNotAvailableException; +use OCP\ICache; use OCP\ICacheFactory; use OCP\IL10N; use OCP\Share\IManager; @@ -45,27 +49,29 @@ class PhotofilesService { public const PHOTO_MIME_TYPES = ['image/jpeg', 'image/tiff']; - private $l10n; private $root; - private $photoMapper; + + private $shareManager; + private $jobList; - private ICacheFactory $cacheFactory; - private \OCP\ICache $photosCache; - private \OCP\ICache $backgroundJobCache; + + private readonly ICacheFactory $cacheFactory; + + private readonly ICache $photosCache; + + private readonly ICache $backgroundJobCache; public function __construct( - private LoggerInterface $logger, + private readonly LoggerInterface $logger, ICacheFactory $cacheFactory, IRootFolder $root, IL10N $l10n, - GeophotoMapper $photoMapper, + private readonly GeophotoMapper $photoMapper, IManager $shareManager, IJobList $jobList, ) { $this->root = $root; - $this->l10n = $l10n; - $this->photoMapper = $photoMapper; $this->shareManager = $shareManager; $this->jobList = $jobList; $this->cacheFactory = $cacheFactory; @@ -73,7 +79,10 @@ public function __construct( $this->backgroundJobCache = $this->cacheFactory->createDistributed('maps:background-jobs'); } - public function rescan($userId, $inBackground = true, $pathToScan = null) { + /** + * @psalm-return \Generator + */ + public function rescan($userId, $inBackground = true, $pathToScan = null): \Generator { $this->photosCache->clear($userId); $userFolder = $this->root->getUserFolder($userId); if ($pathToScan === null) { @@ -82,6 +91,7 @@ public function rescan($userId, $inBackground = true, $pathToScan = null) { } else { $folder = $userFolder->get($pathToScan); } + $photos = $this->gatherPhotoFiles($folder, true); foreach ($photos as $photo) { if ($inBackground) { @@ -89,13 +99,14 @@ public function rescan($userId, $inBackground = true, $pathToScan = null) { } else { $this->addPhotoNow($photo, $userId); } + yield $photo->getPath(); } } // add the file for its owner and users that have access // check if it's already in DB before adding - public function addByFile(Node $file) { + public function addByFile(Node $file): bool { if ($this->isPhoto($file)) { $ownerId = $file->getOwner()->getUID(); $this->addPhoto($file, $ownerId); @@ -108,31 +119,24 @@ public function addByFile(Node $file) { } } } + return true; - } else { - return false; } + + return false; } - public function addByFileIdUserId($fileId, $userId) { + public function addByFileIdUserId(int $fileId, string $userId): void { $userFolder = $this->root->getUserFolder($userId); - $files = $userFolder->getById($fileId); - if (empty($files)) { - return; - } - $file = array_shift($files); - if ($file !== null and $this->isPhoto($file)) { + $file = $userFolder->getFirstNodeById($fileId); + if ($file instanceof File && $this->isPhoto($file)) { $this->addPhoto($file, $userId); } } - public function addByFolderIdUserId($folderId, $userId) { - $folders = $this->root->getById($folderId); - if (empty($folders)) { - return; - } - $folder = array_shift($folders); - if ($folder !== null) { + public function addByFolderIdUserId(int $folderId, string $userId): void { + $folder = $this->root->getFirstNodeById($folderId); + if ($folder instanceof Folder) { $photos = $this->gatherPhotoFiles($folder, true); foreach ($photos as $photo) { $this->addPhoto($photo, $userId); @@ -141,18 +145,18 @@ public function addByFolderIdUserId($folderId, $userId) { } // add all photos of a folder taking care of shared accesses - public function addByFolder($folder) { + public function addByFolder(Folder $folder): void { $photos = $this->gatherPhotoFiles($folder, true); foreach ($photos as $photo) { $this->addByFile($photo); } } - public function updateByFile(Node $file) { + public function updateByFile(Node $file): void { $this->jobList->add(UpdatePhotoByFileJob::class, ['fileId' => $file->getId(), 'userId' => $file->getOwner()->getUID()]); } - public function updateByFileNow(Node $file) { + public function updateByFileNow(File $file): void { if ($this->isPhoto($file)) { $exif = $this->getExif($file); if (!is_null($exif)) { @@ -162,30 +166,30 @@ public function updateByFileNow(Node $file) { $this->photoMapper->findByFileIdUserId($file->getId(), $ownerId); $this->updatePhoto($file, $exif); $this->photosCache->clear($ownerId); - } catch (DoesNotExistException $exception) { + } catch (DoesNotExistException) { $this->insertPhoto($file, $ownerId, $exif); } } } } - public function deleteByFile(Node $file) { + public function deleteByFile(Node $file): void { $this->photoMapper->deleteByFileId($file->getId()); } // delete photo only if it's not accessible to user anymore // it might have been shared multiple times by different users - public function deleteByFileIdUserId($fileId, $userId) { + public function deleteByFileIdUserId(int $fileId, string $userId): void { $userFolder = $this->root->getUserFolder($userId); - $files = $userFolder->getById($fileId); - if (!is_array($files) or count($files) === 0) { + $file = $userFolder->getFirstNodeById($fileId); + if ($file !== null) { $this->photoMapper->deleteByFileIdUserId($fileId, $userId); $this->photosCache->clear($userId); } } - public function deleteByFolder(Node $folder) { + public function deleteByFolder(Folder $folder): void { $photos = $this->gatherPhotoFiles($folder, true); foreach ($photos as $photo) { $this->photoMapper->deleteByFileId($photo->getId()); @@ -193,24 +197,24 @@ public function deleteByFolder(Node $folder) { } // delete folder photos only if it's not accessible to user anymore - public function deleteByFolderIdUserId($folderId, $userId) { + public function deleteByFolderIdUserId($folderId, $userId): void { $userFolder = $this->root->getUserFolder($userId); $folders = $userFolder->getById($folderId); - if (is_array($folders) and count($folders) === 1) { + if (is_array($folders) && count($folders) === 1) { $folder = array_shift($folders); $photos = $this->gatherPhotoFiles($folder, true); foreach ($photos as $photo) { $this->photoMapper->deleteByFileIdUserId($photo->getId(), $userId); } + $this->photosCache->clear($userId); } } /** - * @param $userId - * @return array + * @return array */ - public function getBackgroundJobStatus($userId): array { + public function getBackgroundJobStatus(string $userId): array { $add_counter = 0; $addJobsRunning = false; @@ -218,8 +222,10 @@ public function getBackgroundJobStatus($userId): array { if ($job->getArgument()['userId'] === $userId) { $add_counter += 1; } + $addJobsRunning = true; } + $update_counter = 0; $updateJobsRunning = false; @@ -227,8 +233,10 @@ public function getBackgroundJobStatus($userId): array { if ($job->getArgument()['userId'] === $userId) { $update_counter += 1; } + $updateJobsRunning = true; } + $recentlyAdded = $this->backgroundJobCache->get('recentlyAdded:' . $userId) ?? 0; $recentlyUpdated = $this->backgroundJobCache->get('$recentlyUpdated:' . $userId) ?? 0; return [ @@ -241,88 +249,122 @@ public function getBackgroundJobStatus($userId): array { ]; } - public function setPhotosFilesCoords($userId, $paths, $lats, $lngs, $directory) { + /** + * @param list $paths + * @param list $lats + * @param list $lngs + * @return list + */ + public function setPhotosFilesCoords(string $userId, $paths, $lats, $lngs, bool $directory): array { if ($directory) { return $this->setDirectoriesCoords($userId, $paths, $lats, $lngs); - } else { - return $this->setFilesCoords($userId, $paths, $lats, $lngs); } + + return $this->setFilesCoords($userId, $paths, $lats, $lngs); } - private function setDirectoriesCoords($userId, $paths, $lats, $lngs) { + /** + * @param list $paths + * @param list $lats + * @param list $lngs + * @return list + */ + private function setDirectoriesCoords(string $userId, $paths, $lats, $lngs): array { $lat = $lats[0] ?? 0; $lng = $lngs[0] ?? 0; $userFolder = $this->root->getUserFolder($userId); $done = []; foreach ($paths as $dirPath) { $cleanDirPath = str_replace(['../', '..\\'], '', $dirPath); - if ($userFolder->nodeExists($cleanDirPath)) { + try { $dir = $userFolder->get($cleanDirPath); - if ($dir->getType() === FileInfo::TYPE_FOLDER) { - $nodes = $dir->getDirectoryListing(); - foreach ($nodes as $node) { - if ($this->isPhoto($node) && $node->isUpdateable()) { - $photo = $this->photoMapper->findByFileIdUserId($node->getId(), $userId); - $done[] = [ - 'path' => preg_replace('/^files/', '', $node->getInternalPath()), - 'lat' => $lat, - 'lng' => $lng, - 'oldLat' => $photo ? $photo->getLat() : null, - 'oldLng' => $photo ? $photo->getLng() : null, - ]; - $this->setExifCoords($node, $lat, $lng); - $this->updateByFileNow($node); - } + } catch (NotFoundException) { + continue; + } + if (!$dir instanceof Folder) { + continue; + } + $nodes = $dir->getDirectoryListing(); + foreach ($nodes as $node) { + if ($this->isPhoto($node) && $node->isUpdateable()) { + try { + $photo = $this->photoMapper->findByFileIdUserId($node->getId(), $userId); + } catch (DoesNotExistException) { + $photo = null; } + $done[] = [ + 'path' => preg_replace('/^files/', '', (string)$node->getInternalPath()), + 'lat' => $lat, + 'lng' => $lng, + 'oldLat' => $photo?->getLat(), + 'oldLng' => $photo?->getLng(), + ]; + $this->setExifCoords($node, $lat, $lng); + $this->updateByFileNow($node); } } } + return $done; } - private function setFilesCoords($userId, $paths, $lats, $lngs) { + /** + * @param list $paths + * @param list $lats + * @param list $lngs + * @return list + */ + private function setFilesCoords(string $userId, array $paths, array $lats, array $lngs): array { $userFolder = $this->root->getUserFolder($userId); $done = []; foreach ($paths as $i => $path) { - $cleanpath = str_replace(['../', '..\\'], '', $path); - if ($userFolder->nodeExists($cleanpath)) { - $file = $userFolder->get($cleanpath); - if ($this->isPhoto($file) && $file->isUpdateable()) { - $lat = (count($lats) > $i) ? $lats[$i] : $lats[0]; - $lng = (count($lngs) > $i) ? $lngs[$i] : $lngs[0]; - try { - $photo = $this->photoMapper->findByFileIdUserId($file->getId(), $userId); - } catch (DoesNotExistException) { - $photo = null; - } - $done[] = [ - 'path' => preg_replace('/^files/', '', $file->getInternalPath()), - 'lat' => $lat, - 'lng' => $lng, - 'oldLat' => $photo ? $photo->getLat() : null, - 'oldLng' => $photo ? $photo->getLng() : null, - ]; - $this->setExifCoords($file, $lat, $lng); - $this->updateByFileNow($file); + $cleanPath = str_replace(['../', '..\\'], '', $path); + try { + $file = $userFolder->get($cleanPath); + } catch (NotFoundException) { + continue; + } + + if ($file instanceof File && $this->isPhoto($file) && $file->isUpdateable()) { + $lat = (count($lats) > $i) ? $lats[$i] : $lats[0]; + $lng = (count($lngs) > $i) ? $lngs[$i] : $lngs[0]; + try { + $photo = $this->photoMapper->findByFileIdUserId($file->getId(), $userId); + } catch (DoesNotExistException) { + $photo = null; } + + $done[] = [ + 'path' => preg_replace('/^files/', '', $file->getInternalPath()), + 'lat' => $lat, + 'lng' => $lng, + 'oldLat' => $photo?->getLat(), + 'oldLng' => $photo?->getLng(), + ]; + $this->setExifCoords($file, $lat, $lng); + $this->updateByFileNow($file); } } + return $done; } - public function resetPhotosFilesCoords($userId, $paths) { + /** + * @return list + */ + public function resetPhotosFilesCoords($userId, $paths): array { $userFolder = $this->root->getUserFolder($userId); $done = []; - foreach ($paths as $i => $path) { + foreach ($paths as $path) { $cleanpath = str_replace(['../', '..\\'], '', $path); if ($userFolder->nodeExists($cleanpath)) { $file = $userFolder->get($cleanpath); if ($this->isPhoto($file) && $file->isUpdateable()) { $photo = $this->photoMapper->findByFileIdUserId($file->getId(), $userId); $done[] = [ - 'path' => preg_replace('/^files/', '', $file->getInternalPath()), + 'path' => preg_replace('/^files/', '', (string)$file->getInternalPath()), 'lat' => null, 'lng' => null, 'oldLat' => $photo ? $photo->getLat() : null, @@ -333,15 +375,16 @@ public function resetPhotosFilesCoords($userId, $paths) { } } } + return $done; } // avoid adding photo if it already exists in the DB - private function addPhoto($photo, $userId) { + private function addPhoto($photo, string $userId): void { $this->jobList->add(AddPhotoJob::class, ['photoId' => $photo->getId(), 'userId' => $userId]); } - public function addPhotoNow($photo, $userId) { + public function addPhotoNow($photo, $userId): void { $exif = $this->getExif($photo); if (!is_null($exif)) { // filehooks are triggered several times (2 times for file creation) @@ -350,15 +393,16 @@ public function addPhotoNow($photo, $userId) { // OR by using file_id in primary key try { $this->photoMapper->findByFileIdUserId($photo->getId(), $userId); - } catch (DoesNotExistException $exception) { + } catch (DoesNotExistException) { $this->insertPhoto($photo, $userId, $exif); } + $this->photosCache->clear($userId); } } - private function insertPhoto($photo, $userId, $exif) { + private function insertPhoto($photo, $userId, ExifGeoData $exif): void { $photoEntity = new Geophoto(); $photoEntity->setFileId($photo->getId()); $photoEntity->setLat( @@ -370,28 +414,36 @@ private function insertPhoto($photo, $userId, $exif) { $photoEntity->setUserId($userId); // alternative should be file creation date $photoEntity->setDateTaken($exif->dateTaken ?? $photo->getMTime()); + $this->photoMapper->insert($photoEntity); $this->photosCache->clear($userId); } - private function updatePhoto($file, $exif) { + private function updatePhoto(File $file, ExifGeoData $exif): void { $lat = is_numeric($exif->lat) && !is_nan($exif->lat) ? $exif->lat : null; $lng = is_numeric($exif->lng) && !is_nan($exif->lng) ? $exif->lng : null; $this->photoMapper->updateByFileId($file->getId(), $lat, $lng); } - private function normalizePath($node) { + private function normalizePath($node): string|array { return str_replace('files', '', $node->getInternalPath()); } - public function getPhotosByFolder($userId, $path) { + public function getPhotosByFolder(string $userId, string $path): array { $userFolder = $this->root->getUserFolder($userId); $folder = $userFolder->get($path); - return $this->getPhotosListForFolder($folder); + if ($folder instanceof Folder) { + return $this->getPhotosListForFolder($folder); + } + + return []; } - private function getPhotosListForFolder($folder) { + /** + * @return \stdClass[] + */ + private function getPhotosListForFolder(Folder $folder): array { $FilesList = $this->gatherPhotoFiles($folder, false); $notes = []; foreach ($FilesList as $File) { @@ -400,14 +452,15 @@ private function getPhotosListForFolder($folder) { $file_object->path = $this->normalizePath($File); $notes[] = $file_object; } + return $notes; } - private function gatherPhotoFiles($folder, $recursive) { + private function gatherPhotoFiles(Folder $folder, bool $recursive): array { $notes = []; $nodes = $folder->getDirectoryListing(); foreach ($nodes as $node) { - if ($node->getType() === FileInfo::TYPE_FOLDER and $recursive) { + if ($node instanceof Folder && $recursive) { // we don't explore external storages for which previews are disabled if ($node->isMounted()) { $options = $node->getMountPoint()->getOptions(); @@ -415,30 +468,35 @@ private function gatherPhotoFiles($folder, $recursive) { continue; } } + try { $notes = array_merge($notes, $this->gatherPhotoFiles($node, $recursive)); - } catch (\OCP\Files\StorageNotAvailableException|\Exception $e) { + } catch (StorageNotAvailableException|\Exception) { $msg = 'WARNING: Could not access ' . $node->getName(); echo($msg . "\n"); $this->logger->error($msg); } + continue; } + if ($this->isPhoto($node)) { $notes[] = $node; } } + return $notes; } - private function isPhoto($file) { - if ($file->getType() !== \OCP\Files\FileInfo::TYPE_FILE) { - return false; - } - if (!in_array($file->getMimetype(), self::PHOTO_MIME_TYPES)) { + /** + * @psalm-assert $file instanceof File + */ + private function isPhoto(Node $file): bool { + if ($file instanceof File) { return false; } - return true; + + return in_array($file->getMimetype(), self::PHOTO_MIME_TYPES); } /** @@ -446,7 +504,6 @@ private function isPhoto($file) { * returns with null in any validation or Critical errors * * @param $file - * @return ExifGeoData|null */ private function getExif($file) : ?ExifGeoData { $path = $file->getStorage()->getLocalFile($file->getInternalPath()); @@ -462,10 +519,11 @@ private function getExif($file) : ?ExifGeoData { $exif_geo_data = null; $this->logger->error($f->getMessage(), ['code' => $f->getCode(), 'path' => $path]); } + return $exif_geo_data; } - private function resetExifCoords($file) { + private function resetExifCoords($file): void { $data = new PelDataWindow($file->getContent()); $pelJpeg = new PelJpeg($data); @@ -493,7 +551,7 @@ private function resetExifCoords($file) { $file->putContent($pelJpeg->getBytes()); } - private function setExifCoords($file, $lat, $lng) { + private function setExifCoords(File $file, $lat, $lng): void { $data = new PelDataWindow($file->getContent()); $pelJpeg = new PelJpeg($data); @@ -523,7 +581,7 @@ private function setExifCoords($file, $lat, $lng) { $file->putContent($pelJpeg->getBytes()); } - private function setGeolocation($pelSubIfdGps, $latitudeDegreeDecimal, $longitudeDegreeDecimal) { + private function setGeolocation(PelIfd $pelSubIfdGps, $latitudeDegreeDecimal, $longitudeDegreeDecimal): void { $latitudeRef = ($latitudeDegreeDecimal >= 0) ? 'N' : 'S'; $latitudeDegreeMinuteSecond = $this->degreeDecimalToDegreeMinuteSecond(abs($latitudeDegreeDecimal)); @@ -553,7 +611,10 @@ private function setGeolocation($pelSubIfdGps, $latitudeDegreeDecimal, $longitud )); } - private function degreeDecimalToDegreeMinuteSecond($degreeDecimal) { + /** + * @return array{degree: float, minute: float, second: float} + */ + private function degreeDecimalToDegreeMinuteSecond(float|int $degreeDecimal): array { $degree = floor($degreeDecimal); $remainder = $degreeDecimal - $degree; $minute = floor($remainder * 60); diff --git a/lib/Service/TracksService.php b/lib/Service/TracksService.php index 7fd698833..116feead6 100644 --- a/lib/Service/TracksService.php +++ b/lib/Service/TracksService.php @@ -1,5 +1,7 @@ + */ + public function rescan($userId): \Generator { $userFolder = $this->root->getUserFolder($userId); $tracks = $this->gatherTrackFiles($userFolder, true); $this->deleteAllTracksFromDB($userId); @@ -52,8 +54,8 @@ public function rescan($userId) { } } - public function addByFile(Node $file) { - $userFolder = $this->root->getUserFolder($file->getOwner()->getUID()); + public function addByFile(Node $file): void { + $this->root->getUserFolder($file->getOwner()->getUID()); if ($this->isTrack($file)) { $this->addTrackToDB($file->getOwner()->getUID(), $file->getId(), $file); } @@ -61,52 +63,48 @@ public function addByFile(Node $file) { // add the file for its owner and users that have access // check if it's already in DB before adding - public function safeAddByFile(Node $file) { + public function safeAddByFile(File $file): bool { + if (!$this->isTrack($file)) { + return false; + } + $ownerId = $file->getOwner()->getUID(); - $userFolder = $this->root->getUserFolder($ownerId); - if ($this->isTrack($file)) { - $this->safeAddTrack($file, $ownerId); - // is the file accessible to other users ? - $accesses = $this->shareManager->getAccessList($file); - foreach ($accesses['users'] as $uid) { - if ($uid !== $ownerId) { - $this->safeAddTrack($file, $uid); - } + $this->safeAddTrack($file, $ownerId); + // is the file accessible to other users ? + $accesses = $this->shareManager->getAccessList($file); + foreach ($accesses['users'] as $uid) { + if ($uid !== $ownerId) { + $this->safeAddTrack($file, $uid); } - return true; - } else { - return false; } + + return true; } - public function safeAddByFileIdUserId($fileId, $userId) { + public function safeAddByFileIdUserId(int $fileId, string $userId): void { $userFolder = $this->root->getUserFolder($userId); - $files = $userFolder->getById($fileId); - if (empty($files)) { + $file = $userFolder->getFirstNodeById($fileId); + if ($file === null || !$this->isTrack($file)) { return; } - $file = array_shift($files); - if ($file !== null and $this->isTrack($file)) { - $this->safeAddTrack($file, $userId); - } + + $this->safeAddTrack($file, $userId); } - public function safeAddByFolderIdUserId($folderId, $userId) { - $folders = $this->root->getById($folderId); - if (empty($folders)) { + public function safeAddByFolderIdUserId(int $folderId, string $userId): void { + $folder = $this->root->getUserFolder($userId); + if ($folder === null) { return; } - $folder = array_shift($folders); - if ($folder !== null) { - $tracks = $this->gatherTrackFiles($folder, true); - foreach ($tracks as $track) { - $this->safeAddTrack($track, $userId); - } + + $tracks = $this->gatherTrackFiles($folder, true); + foreach ($tracks as $track) { + $this->safeAddTrack($track, $userId); } } // avoid adding track if it already exists in the DB - private function safeAddTrack($track, $userId) { + private function safeAddTrack($track, string $userId): void { // filehooks are triggered several times (2 times for file creation) // so we need to be sure it's not inserted several times // by checking if it already exists in DB @@ -117,14 +115,14 @@ private function safeAddTrack($track, $userId) { } // add all tracks of a folder taking care of shared accesses - public function safeAddByFolder($folder) { + public function safeAddByFolder(Folder $folder): void { $tracks = $this->gatherTrackFiles($folder, true); foreach ($tracks as $track) { $this->safeAddByFile($track); } } - public function addByFolder(Node $folder) { + public function addByFolder(Folder $folder): void { $tracks = $this->gatherTrackFiles($folder, true); foreach ($tracks as $track) { $this->addTrackToDB($folder->getOwner()->getUID(), $track->getId(), $track); @@ -133,19 +131,19 @@ public function addByFolder(Node $folder) { // delete track only if it's not accessible to user anymore // it might have been shared multiple times by different users - public function safeDeleteByFileIdUserId($fileId, $userId) { + public function safeDeleteByFileIdUserId(int $fileId, string $userId): void { $userFolder = $this->root->getUserFolder($userId); - $files = $userFolder->getById($fileId); - if (!is_array($files) or count($files) === 0) { + $file = $userFolder->getFirstNodeById($fileId); + if ($file === null) { $this->deleteByFileIdUserId($fileId, $userId); } } - public function deleteByFile(Node $file) { + public function deleteByFile(Node $file): void { $this->deleteByFileId($file->getId()); } - public function deleteByFolder(Node $folder) { + public function deleteByFolder(Folder $folder): void { $tracks = $this->gatherTrackFiles($folder, true); foreach ($tracks as $track) { $this->deleteByFileId($track->getId()); @@ -153,10 +151,10 @@ public function deleteByFolder(Node $folder) { } // delete folder tracks only if it's not accessible to user anymore - public function safeDeleteByFolderIdUserId($folderId, $userId) { + public function safeDeleteByFolderIdUserId(int $folderId, string $userId): void { $userFolder = $this->root->getUserFolder($userId); $folders = $userFolder->getById($folderId); - if (is_array($folders) and count($folders) === 1) { + if (is_array($folders) && count($folders) === 1) { $folder = array_shift($folders); $tracks = $this->gatherTrackFiles($folder, true); foreach ($tracks as $track) { @@ -165,62 +163,72 @@ public function safeDeleteByFolderIdUserId($folderId, $userId) { } } - private function gatherTrackFiles($folder, $recursive) { + /** + * @return list + */ + private function gatherTrackFiles(Folder $folder, bool $recursive): array { $notes = []; $nodes = $folder->getDirectoryListing(); foreach ($nodes as $node) { - if ($node->getType() === FileInfo::TYPE_FOLDER and $recursive) { + if ($node instanceof Folder && $recursive) { try { $notes = array_merge($notes, $this->gatherTrackFiles($node, $recursive)); - } catch (\OCP\Files\StorageNotAvailableException|\Exception $e) { + } catch (StorageNotAvailableException|\Exception) { $msg = 'WARNING: Could not access ' . $node->getName(); echo($msg . "\n"); $this->logger->error($msg); } + continue; } + if ($this->isTrack($node)) { $notes[] = $node; } } + return $notes; } - private function isTrack($file) { - if ($file->getType() !== \OCP\Files\FileInfo::TYPE_FILE) { - return false; - } - if (!in_array($file->getMimetype(), self::TRACK_MIME_TYPES)) { + private function isTrack(Node $file): bool { + if (!$file instanceof File) { return false; } - return true; + + return in_array($file->getMimetype(), self::TRACK_MIME_TYPES); } - private function dbRowToTrack($row, $folder, $userFolder, $defaultMap, $ignoredPaths) { + /** + * @param array $row + */ + private function dbRowToTrack(array $row, Folder $folder, $userFolder, bool $defaultMap, array $ignoredPaths): ?array { // avoid tracks that are not in "this map's" folder - $files = $folder->getById(intval($row['file_id'])); - if (empty($files)) { + $file = $folder->getFirstNodeById(intval($row['file_id'])); + if ($file === null) { if ($defaultMap) { $this->deleteTrackFromDB($row['id']); } + return null; } - $file = array_shift($files); - if ($file === null || $file->getType() !== \OCP\Files\FileInfo::TYPE_FILE) { + + if (!$file instanceof File) { if ($defaultMap) { $this->deleteTrackFromDB($row['id']); } + return null; } $path = $userFolder->getRelativePath($file->getPath()); $isIgnored = false; foreach ($ignoredPaths as $ignoredPath) { - if (str_starts_with($path, $ignoredPath)) { + if (str_starts_with((string)$path, (string)$ignoredPath)) { $isIgnored = true; break; } } + if ($isIgnored) { return null; } @@ -244,13 +252,14 @@ private function dbRowToTrack($row, $folder, $userFolder, $defaultMap, $ignoredP /** - * @param string $userId + * @return mixed[] */ - public function getTracksFromDB($userId, $folder = null, bool $respectNomediaAndNoimage = true, bool $hideTracksOnCustomMaps = false, bool $hideTracksInMapsFolder = true) { + public function getTracksFromDB(string $userId, $folder = null, bool $respectNomediaAndNoimage = true, bool $hideTracksOnCustomMaps = false, bool $hideTracksInMapsFolder = true): array { $ignoredPaths = $respectNomediaAndNoimage ? $this->getIgnoredPaths($userId, $folder, $hideTracksOnCustomMaps) : []; if ($hideTracksInMapsFolder) { $ignoredPaths[] = '/Maps'; } + $userFolder = $this->root->getUserFolder($userId); $tracks = []; $qb = $this->dbconnection->getQueryBuilder(); @@ -264,6 +273,7 @@ public function getTracksFromDB($userId, $folder = null, bool $respectNomediaAnd if (is_null($folder)) { $folder = $userFolder; } + $defaultMap = $folder->getId() === $userFolder->getId(); // my-maps context @@ -272,8 +282,10 @@ public function getTracksFromDB($userId, $folder = null, bool $respectNomediaAnd if (is_null($track)) { continue; } + $tracks[] = $track; } + $req->closeCursor(); return $tracks; } @@ -281,17 +293,17 @@ public function getTracksFromDB($userId, $folder = null, bool $respectNomediaAnd /** * @param $userId * @param $folder - * @return array * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException - * @throws \OC\User\NoUserException + * @throws NoUserException */ - private function getIgnoredPaths($userId, $folder = null, $hideImagesOnCustomMaps = true) { + private function getIgnoredPaths(string $userId, $folder = null, bool $hideImagesOnCustomMaps = true): array { $ignoredPaths = []; $userFolder = $this->root->getUserFolder($userId); if (is_null($folder)) { $folder = $userFolder; } + $ignoreFileMimetypes = [ 'application/x-nextcloud-noindex', 'application/x-nextcloud-nomedia', @@ -300,9 +312,8 @@ private function getIgnoredPaths($userId, $folder = null, $hideImagesOnCustomMap if ($hideImagesOnCustomMaps) { $ignoreFileMimetypes[] = 'application/x-nextcloud-maps'; } - $func = function (string $i): SearchComparison { - return new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $i); - }; + + $func = (fn (string $i): SearchComparison => new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $i)); $excludedNodes = $folder->search(new SearchQuery( new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, array_map( $func, @@ -315,10 +326,11 @@ private function getIgnoredPaths($userId, $folder = null, $hideImagesOnCustomMap foreach ($excludedNodes as $node) { $ignoredPaths[] = $userFolder->getRelativePath($node->getParent()->getPath()); } + return $ignoredPaths; } - public function getTrackFromDB($id, $userId = null) { + public function getTrackFromDB(int $id, ?string $userId = null): ?array { $track = null; $qb = $this->dbconnection->getQueryBuilder(); $qb->select('id', 'file_id', 'color', 'metadata', 'etag') @@ -331,19 +343,17 @@ public function getTrackFromDB($id, $userId = null) { $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) ); } + $req = $qb->executeQuery(); while ($row = $req->fetch()) { - if ($userId !== '' and $userId !== null) { + if ($userId !== '' && $userId !== null) { $userFolder = $this->root->getUserFolder($userId); - $files = $userFolder->getById(intval($row['file_id'])); - if (empty($files)) { - break; - } - $file = array_shift($files); + $file = $userFolder->getFirstNodeById(intval($row['file_id'])); if ($file === null) { break; } + $track = $this->dbRowToTrack($row, $userFolder, $userFolder, true, []); } else { $track = [ @@ -362,13 +372,15 @@ public function getTrackFromDB($id, $userId = null) { 'file_path' => '', ]; } + break; } + $req->closeCursor(); return $track; } - public function getTrackByFileIDFromDB(int $fileId, ?string $userId = null) { + public function getTrackByFileIDFromDB(int $fileId, ?string $userId = null): ?array { $track = null; $qb = $this->dbconnection->getQueryBuilder(); $qb->select('id', 'file_id', 'color', 'metadata', 'etag') @@ -381,19 +393,17 @@ public function getTrackByFileIDFromDB(int $fileId, ?string $userId = null) { $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) ); } + $req = $qb->executeQuery(); while ($row = $req->fetch()) { - if ($userId !== '' and $userId !== null) { + if ($userId !== '' && $userId !== null) { $userFolder = $this->root->getUserFolder($userId); - $files = $userFolder->getById(intval($row['file_id'])); - if (empty($files)) { - break; - } - $file = array_shift($files); + $file = $userFolder->getFirstNodeById(intval($row['file_id'])); if ($file === null) { break; } + $track = $this->dbRowToTrack($row, $userFolder, $userFolder, true, []); } else { $track = [ @@ -412,13 +422,15 @@ public function getTrackByFileIDFromDB(int $fileId, ?string $userId = null) { 'file_path' => '', ]; } + break; } + $req->closeCursor(); return $track; } - public function addTrackToDB($userId, $fileId, $file) { + public function addTrackToDB($userId, $fileId, $file): int { $metadata = ''; $etag = $file->getEtag(); $qb = $this->dbconnection->getQueryBuilder(); @@ -430,22 +442,24 @@ public function addTrackToDB($userId, $fileId, $file) { 'etag' => $qb->createNamedParameter($etag, IQueryBuilder::PARAM_STR) ]); $qb->executeStatement(); - $trackId = $qb->getLastInsertId(); - return $trackId; + return $qb->getLastInsertId(); } - public function editTrackInDB($id, $color, $metadata, $etag): void { + public function editTrackInDB(int $id, ?string $color, ?string $metadata, ?string $etag): void { $qb = $this->dbconnection->getQueryBuilder(); $qb->update('maps_tracks'); if ($color !== null) { $qb->set('color', $qb->createNamedParameter($color, IQueryBuilder::PARAM_STR)); } + if ($metadata !== null) { $qb->set('metadata', $qb->createNamedParameter($metadata, IQueryBuilder::PARAM_STR)); } + if ($etag !== null) { $qb->set('etag', $qb->createNamedParameter($etag, IQueryBuilder::PARAM_STR)); } + $qb->where( $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) ); @@ -492,7 +506,7 @@ public function deleteAllTracksFromDB(string $userId): void { } public function deleteTracksFromDB(array $ids, string $userId): void { - if (empty($ids)) { + if ($ids === []) { return; } @@ -505,8 +519,7 @@ public function deleteTracksFromDB(array $ids, string $userId): void { $qb->executeStatement(); } - public function generateTrackMetadata($file) { - $DISTANCE_BETWEEN_SHORT_POINTS = 300; + public function generateTrackMetadata($file): ?string { $STOPPED_SPEED_THRESHOLD = 0.9; $name = $file->getName(); @@ -518,8 +531,6 @@ public function generateTrackMetadata($file) { $total_duration = 0; $date_begin = null; $date_end = null; - - $distAccCumulEle = 0; $pos_elevation = 0; $neg_elevation = 0; $min_elevation = null; @@ -529,7 +540,6 @@ public function generateTrackMetadata($file) { $moving_time = 0; $moving_distance = 0; $stopped_distance = 0; - $moving_max_speed = 0; $moving_avg_speed = 0; $stopped_time = 0; $north = null; @@ -545,15 +555,15 @@ public function generateTrackMetadata($file) { try { $gpx = new \SimpleXMLElement($gpx_content); - } catch (\Throwable $e) { + } catch (\Throwable $throwable) { $this->logger->error( - 'Exception in ' . $name . ' gpx parsing : ' . $e->getMessage(), + 'Exception in ' . $name . ' gpx parsing : ' . $throwable->getMessage(), ['app' => 'maps'] ); return null; } - if (count($gpx->trk) === 0 and count($gpx->rte) === 0 and count($gpx->wpt) === 0) { + if (count($gpx->trk) === 0 && count($gpx->rte) === 0 && count($gpx->wpt) === 0) { $this->logger->error( 'Nothing to parse in ' . $name . ' gpx file', ['app' => 'maps'] @@ -562,7 +572,7 @@ public function generateTrackMetadata($file) { } // METADATA - if (!empty($gpx->metadata) and !empty($gpx->metadata->link)) { + if (!empty($gpx->metadata) && !empty($gpx->metadata->link)) { $linkurl = $gpx->metadata->link['href']; if (!empty($gpx->metadata->link->text)) { $linktext = $gpx->metadata->link->text; @@ -571,10 +581,11 @@ public function generateTrackMetadata($file) { // TRACKS foreach ($gpx->trk as $track) { - $trackname = str_replace("\n", '', $track->name); + $trackname = str_replace("\n", '', (string)$track->name); if (empty($trackname)) { $trackname = ''; } + $trackname = str_replace('"', "'", $trackname); $trackNameList[] = sprintf('"%s"', $trackname); foreach ($track->trkseg as $segment) { @@ -583,44 +594,36 @@ public function generateTrackMetadata($file) { $pointIndex = 0; $pointsBySegment[] = $segment->trkpt; foreach ($segment->trkpt as $point) { - if (empty($point['lat']) or empty($point['lon'])) { + if (empty($point['lat'])) { continue; } - if (empty($point->ele)) { - $pointele = null; - } else { - $pointele = floatval($point->ele); - } - if (empty($point->time)) { - $pointtime = null; - } else { - $pointtime = new \DateTime($point->time); - } - if ($lastPoint !== null and (!empty($lastPoint->ele))) { - $lastPointele = floatval($lastPoint->ele); - } else { - $lastPointele = null; - } - if ($lastPoint !== null and (!empty($lastPoint->time))) { - $lastTime = new \DateTime($lastPoint->time); - } else { - $lastTime = null; - } - if ($lastPoint !== null) { - $distToLast = distance($lastPoint, $point); - } else { - $distToLast = null; + + if (empty($point['lon'])) { + continue; } + + $pointele = empty($point->ele) ? null : floatval($point->ele); + + $pointtime = empty($point->time) ? null : new \DateTime((string)$point->time); + + $lastPointele = ($lastPoint !== null && !empty($lastPoint->ele)) ? floatval($lastPoint->ele) : null; + + $lastTime = ($lastPoint !== null && !empty($lastPoint->time)) ? new \DateTime((string)$lastPoint->time) : null; + + $distToLast = $lastPoint !== null ? distance((array)$lastPoint, (array)$point) : null; + $pointlat = floatval($point['lat']); $pointlon = floatval($point['lon']); if ($pointIndex === 0) { - if ($lat === '0' and $lon === '0') { + if ($lat === '0' && $lon === '0') { $lat = $pointlat; $lon = $pointlon; } - if ($pointtime !== null and ($date_begin === null or $pointtime < $date_begin)) { + + if ($pointtime instanceof \DateTime && (!$date_begin instanceof \DateTime || $pointtime < $date_begin)) { $date_begin = $pointtime; } + $downBegin = $pointele; if ($north === null) { $north = $pointlat; @@ -633,29 +636,35 @@ public function generateTrackMetadata($file) { if ($pointlat > $north) { $north = $pointlat; } + if ($pointlat < $south) { $south = $pointlat; } + if ($pointlon > $east) { $east = $pointlon; } + if ($pointlon < $west) { $west = $pointlon; } - if ($pointele !== null and ($min_elevation === null or $pointele < $min_elevation)) { + + if ($pointele !== null && ($min_elevation === null || $pointele < $min_elevation)) { $min_elevation = $pointele; } - if ($pointele !== null and ($max_elevation === null or $pointele > $max_elevation)) { + + if ($pointele !== null && ($max_elevation === null || $pointele > $max_elevation)) { $max_elevation = $pointele; } - if ($lastPoint !== null and $pointtime !== null and $lastTime !== null) { + + if ($lastPoint !== null && $pointtime instanceof \DateTime && $lastTime instanceof \DateTime) { $t = abs($lastTime->getTimestamp() - $pointtime->getTimestamp()); $speed = 0; if ($t > 0) { $speed = $distToLast / $t; - $speed = $speed / 1000; - $speed = $speed * 3600; + $speed /= 1000; + $speed *= 3600; } if ($speed <= $STOPPED_SPEED_THRESHOLD) { @@ -666,6 +675,7 @@ public function generateTrackMetadata($file) { $moving_distance += $distToLast; } } + if ($lastPoint !== null) { $total_distance += $distToLast; } @@ -674,7 +684,7 @@ public function generateTrackMetadata($file) { $pointIndex += 1; } - if ($lastTime !== null and ($date_end === null or $lastTime > $date_end)) { + if ($lastTime instanceof \DateTime && ($date_end === null || $lastTime > $date_end)) { $date_end = $lastTime; } } @@ -687,6 +697,7 @@ public function generateTrackMetadata($file) { if (empty($routename)) { $routename = ''; } + $routename = str_replace('"', "'", $routename); $trackNameList[] = sprintf('"%s"', $routename); @@ -695,44 +706,36 @@ public function generateTrackMetadata($file) { $pointIndex = 0; $pointsBySegment[] = $route->rtept; foreach ($route->rtept as $point) { - if (empty($point['lat']) or empty($point['lon'])) { + if (empty($point['lat'])) { continue; } - if (empty($point->ele)) { - $pointele = null; - } else { - $pointele = floatval($point->ele); - } - if (empty($point->time)) { - $pointtime = null; - } else { - $pointtime = new \DateTime($point->time); - } - if ($lastPoint !== null and (!empty($lastPoint->ele))) { - $lastPointele = floatval($lastPoint->ele); - } else { - $lastPointele = null; - } - if ($lastPoint !== null and (!empty($lastPoint->time))) { - $lastTime = new \DateTime($lastPoint->time); - } else { - $lastTime = null; - } - if ($lastPoint !== null) { - $distToLast = distance($lastPoint, $point); - } else { - $distToLast = null; + + if (empty($point['lon'])) { + continue; } + + $pointele = empty($point->ele) ? null : floatval($point->ele); + + $pointtime = empty($point->time) ? null : new \DateTime((string)$point->time); + + $lastPointele = ($lastPoint !== null && !empty($lastPoint->ele)) ? floatval($lastPoint->ele) : null; + + $lastTime = ($lastPoint !== null && !empty($lastPoint->time)) ? new \DateTime((string)$lastPoint->time) : null; + + $distToLast = $lastPoint !== null ? distance((array)$lastPoint, (array)$point) : null; + $pointlat = floatval($point['lat']); $pointlon = floatval($point['lon']); if ($pointIndex === 0) { - if ($lat === '0' and $lon === '0') { + if ($lat === '0' && $lon === '0') { $lat = $pointlat; $lon = $pointlon; } - if ($pointtime !== null and ($date_begin === null or $pointtime < $date_begin)) { + + if ($pointtime instanceof \DateTime && (!$date_begin instanceof \DateTime || $pointtime < $date_begin)) { $date_begin = $pointtime; } + $downBegin = $pointele; if ($north === null) { $north = $pointlat; @@ -745,29 +748,35 @@ public function generateTrackMetadata($file) { if ($pointlat > $north) { $north = $pointlat; } + if ($pointlat < $south) { $south = $pointlat; } + if ($pointlon > $east) { $east = $pointlon; } + if ($pointlon < $west) { $west = $pointlon; } - if ($pointele !== null and ($min_elevation === null or $pointele < $min_elevation)) { + + if ($pointele !== null && ($min_elevation === null || $pointele < $min_elevation)) { $min_elevation = $pointele; } - if ($pointele !== null and ($max_elevation === null or $pointele > $max_elevation)) { + + if ($pointele !== null && ($max_elevation === null || $pointele > $max_elevation)) { $max_elevation = $pointele; } - if ($lastPoint !== null and $pointtime !== null and $lastTime !== null) { + + if ($lastPoint !== null && $pointtime instanceof \DateTime && $lastTime instanceof \DateTime) { $t = abs($lastTime->getTimestamp() - $pointtime->getTimestamp()); $speed = 0; if ($t > 0) { $speed = $distToLast / $t; - $speed = $speed / 1000; - $speed = $speed * 3600; + $speed /= 1000; + $speed *= 3600; } if ($speed <= $STOPPED_SPEED_THRESHOLD) { @@ -778,6 +787,7 @@ public function generateTrackMetadata($file) { $moving_distance += $distToLast; } } + if ($lastPoint !== null) { $total_distance += $distToLast; } @@ -786,21 +796,21 @@ public function generateTrackMetadata($file) { $pointIndex += 1; } - if ($lastTime !== null and ($date_end === null or $lastTime > $date_end)) { + if ($lastTime instanceof \DateTime && (!$date_end instanceof \DateTime || $lastTime > $date_end)) { $date_end = $lastTime; } } # TOTAL STATS : duration, avg speed, avg_moving_speed - if ($date_end !== null and $date_begin !== null) { + if ($date_end instanceof \DateTime && $date_begin instanceof \DateTime) { $totsec = abs($date_end->getTimestamp() - $date_begin->getTimestamp()); $total_duration = $totsec; if ($totsec === 0) { $avg_speed = 0; } else { $avg_speed = $total_distance / $totsec; - $avg_speed = $avg_speed / 1000; - $avg_speed = $avg_speed * 3600; + $avg_speed /= 1000; + $avg_speed *= 3600; $avg_speed = sprintf('%.2f', $avg_speed); } } @@ -810,13 +820,13 @@ public function generateTrackMetadata($file) { $moving_pace = 0; if ($moving_time > 0) { $moving_avg_speed = $total_distance / $moving_time; - $moving_avg_speed = $moving_avg_speed / 1000; - $moving_avg_speed = $moving_avg_speed * 3600; + $moving_avg_speed /= 1000; + $moving_avg_speed *= 3600; $moving_avg_speed = sprintf('%.2f', $moving_avg_speed); // pace in minutes/km $moving_pace = $moving_time / $total_distance; - $moving_pace = $moving_pace / 60; - $moving_pace = $moving_pace * 1000; + $moving_pace /= 60; + $moving_pace *= 1000; $moving_pace = sprintf('%.2f', $moving_pace); } @@ -825,21 +835,24 @@ public function generateTrackMetadata($file) { $waypointlat = floatval($waypoint['lat']); $waypointlon = floatval($waypoint['lon']); - if ($lat === '0' and $lon === '0') { + if ($lat === '0' && $lon === '0') { $lat = $waypointlat; $lon = $waypointlon; } - if ($north === null or $waypointlat > $north) { + if ($north === null || $waypointlat > $north) { $north = $waypointlat; } - if ($south === null or $waypointlat < $south) { + + if ($south === null || $waypointlat < $south) { $south = $waypointlat; } - if ($east === null or $waypointlon > $east) { + + if ($east === null || $waypointlon > $east) { $east = $waypointlon; } - if ($west === null or $waypointlon < $west) { + + if ($west === null || $waypointlon < $west) { $west = $waypointlon; } } @@ -848,12 +861,15 @@ public function generateTrackMetadata($file) { if ($north === null) { $north = 0; } + if ($south === null) { $south = 0; } + if ($east === null) { $east = 0; } + if ($west === null) { $west = 0; } @@ -863,6 +879,7 @@ public function generateTrackMetadata($file) { foreach ($pointsBySegment as $points) { $distFilteredPointsBySegment[] = $this->getDistanceFilteredPoints($points); } + // and we get points with elevation and time for each segment $pointsWithElevationBySegment = []; $pointsWithTimeBySegment = []; @@ -873,13 +890,16 @@ public function generateTrackMetadata($file) { if (!empty($point->ele)) { $pointsWithElevationOneSegment[] = $point; } + if (!empty($point->time)) { $pointsWithTimeOneSegment[] = $point; } } + $pointsWithElevationBySegment[] = $pointsWithElevationOneSegment; $pointsWithTimeBySegment[] = $pointsWithTimeOneSegment; } + // process elevation gain/loss $pos_elevation = 0; $neg_elevation = 0; @@ -888,6 +908,7 @@ public function generateTrackMetadata($file) { $pos_elevation += $gainLoss[0]; $neg_elevation += $gainLoss[1]; } + $pos_elevation = number_format($pos_elevation, 2, '.', ''); $neg_elevation = number_format($neg_elevation, 2, '.', ''); // process max speed from distance filtered points @@ -899,18 +920,18 @@ public function generateTrackMetadata($file) { } } - $result = sprintf('{"lat":%s, "lng":%s, "name": "%s", "distance": %.3f, "duration": %d, "begin": %d, "end": %d, "posel": %.2f, "negel": %.2f, "minel": %.2f, "maxel": %.2f, "maxspd": %.2f, "avgspd": %.2f, "movtime": %d, "stptime": %d, "movavgspd": %s, "n": %.8f, "s": %.8f, "e": %.8f, "w": %.8f, "trnl": %s, "lnkurl": "%s", "lnktxt": "%s", "movpace": %.2f}', + return sprintf('{"lat":%s, "lng":%s, "name": "%s", "distance": %.3f, "duration": %d, "begin": %d, "end": %d, "posel": %.2f, "negel": %.2f, "minel": %.2f, "maxel": %.2f, "maxspd": %.2f, "avgspd": %.2f, "movtime": %d, "stptime": %d, "movavgspd": %s, "n": %.8f, "s": %.8f, "e": %.8f, "w": %.8f, "trnl": %s, "lnkurl": "%s", "lnktxt": "%s", "movpace": %.2f}', $lat, $lon, str_replace('"', "'", $name), $total_distance, $total_duration, - ($date_begin !== null) ? $date_begin->getTimestamp() : -1, - ($date_end !== null) ? $date_end->getTimestamp() : -1, + ($date_begin instanceof \DateTime) ? $date_begin->getTimestamp() : -1, + ($date_end instanceof \DateTime) ? $date_end->getTimestamp() : -1, $pos_elevation, $neg_elevation, - ($min_elevation !== null) ? $min_elevation : -1000, - ($max_elevation !== null) ? $max_elevation : -1000, + $min_elevation ?? -1000, + $max_elevation ?? -1000, $maxSpeed, $avg_speed, $moving_time, @@ -925,19 +946,21 @@ public function generateTrackMetadata($file) { str_replace('"', "'", $linktext), $moving_pace ); - return $result; } - private function getDistanceFilteredPoints($points) { + /** + * @return object[] + */ + private function getDistanceFilteredPoints($points): array { $DISTANCE_THRESHOLD = 10; $distFilteredPoints = []; if (count($points) > 0) { - array_push($distFilteredPoints, $points[0]); + $distFilteredPoints[] = $points[0]; $lastPoint = $points[0]; foreach ($points as $point) { - if (distance($lastPoint, $point) >= $DISTANCE_THRESHOLD) { - array_push($distFilteredPoints, $point); + if (distance((array)$lastPoint, (array)$point) >= $DISTANCE_THRESHOLD) { + $distFilteredPoints[] = $point; $lastPoint = $point; } } @@ -946,24 +969,28 @@ private function getDistanceFilteredPoints($points) { return $distFilteredPoints; } - private function getMaxSpeed($points) { + /** + * @param object[] $points + */ + private function getMaxSpeed(array $points): float|int { $maxSpeed = 0; - if (count($points) > 0) { + if ($points !== []) { $lastPoint = $points[0]; - $lastTime = new \DateTime($lastPoint->time); + $lastTime = new \DateTime((string)$lastPoint->time); foreach ($points as $point) { - $time = new \DateTime($point->time); + $time = new \DateTime((string)$point->time); $timeDelta = abs($lastTime->getTimestamp() - $time->getTimestamp()); if ($timeDelta > 0) { - $distance = distance($point, $lastPoint); + $distance = distance((array)$point, (array)$lastPoint); $speed = $distance / $timeDelta; - $speed = $speed / 1000; - $speed = $speed * 3600; + $speed /= 1000; + $speed *= 3600; if ($speed > $maxSpeed) { $maxSpeed = $speed; } } + $lastTime = $time; $lastPoint = $point; } @@ -974,14 +1001,16 @@ private function getMaxSpeed($points) { /** * inspired by https://www.gpsvisualizer.com/tutorials/elevation_gain.html + * @param object[] $points + * @return array */ - private function getElevationGainLoss($points) { + private function getElevationGainLoss(array $points): array { $ELEVATION_THRESHOLD = 6; $gain = 0; $loss = 0; // then calculate elevation gain with elevation threshold - if (count($points) > 0) { + if ($points !== []) { $validPoint = $points[0]; foreach ($points as $point) { $deniv = floatval($point->ele) - floatval($validPoint->ele); @@ -1002,20 +1031,24 @@ private function getElevationGainLoss($points) { /* * return distance between these two gpx points in meters */ -function distance($p1, $p2) { +/** + * @param array $p1 + * @param array $p2 + */ +function distance(array $p1, array $p2): int|float { $lat1 = (float)$p1['lat']; $long1 = (float)$p1['lon']; $lat2 = (float)$p2['lat']; $long2 = (float)$p2['lon']; - if ($lat1 === $lat2 and $long1 === $long2) { + if ($lat1 === $lat2 && $long1 === $long2) { return 0; } // Convert latitude and longitude to // spherical coordinates in radians. - $degrees_to_radians = pi() / 180.0; + $degrees_to_radians = M_PI / 180.0; // phi = 90 - latitude $phi1 = (90.0 - $lat1) * $degrees_to_radians; @@ -1039,6 +1072,7 @@ function distance($p1, $p2) { if ($cos > 1.0) { $cos = 1.0; } + $arc = acos($cos); // Remember to multiply arc by the radius of the earth diff --git a/lib/Settings/AdminSettings.php b/lib/Settings/AdminSettings.php index 7baa601b9..b66a66464 100644 --- a/lib/Settings/AdminSettings.php +++ b/lib/Settings/AdminSettings.php @@ -1,24 +1,23 @@ config->getAppValue('maps', $k); + $v = $this->appConfig->getValueString('maps', $k); $parameters[$k] = $v; } return new TemplateResponse('maps', 'adminSettings', $parameters, ''); } - /** - * @return string the section ID, e.g. 'sharing' - */ - public function getSection() { + #[Override] + public function getSection(): string { return 'additional'; } - /** - * @return int whether the form should be rather on the top or bottom of - * the admin section. The forms are arranged in ascending order of the - * priority values. It is required to return a value between 0 and 100. - * - * E.g.: 70 - */ - public function getPriority() { + #[Override] + public function getPriority(): int { return 5; } diff --git a/psalm.xml b/psalm.xml index 8d41d878c..1cf47e261 100644 --- a/psalm.xml +++ b/psalm.xml @@ -29,11 +29,18 @@ + + + + + + + diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..f3f6c10b9 --- /dev/null +++ b/rector.php @@ -0,0 +1,52 @@ +withPaths([ + __DIR__ . '/lib', + __DIR__ . '/tests', + ]) + ->withSkip([ + __DIR__ . '/tests/stub.php', + __DIR__ . '/tests/stubs', + ]) + ->withImportNames( + importShortClasses: false, + ) + ->withPreparedSets( + deadCode: true, + codeQuality: true, + codingStyle: true, + typeDeclarations: true, + typeDeclarationDocblocks: true, + privatization: true, + instanceOf: true, + earlyReturn: true, + rectorPreset: true, + phpunitCodeQuality: true, + doctrineCodeQuality: true, + symfonyCodeQuality: true, + symfonyConfigs: true, + )->withPhpSets( + php81: true, + )->withConfiguredRule(ClassPropertyAssignToConstructorPromotionRector::class, [ + 'inline_public' => true, + 'rename_property' => true, + ]) + ->withSets([ + NextcloudSets::NEXTCLOUD_30, + PHPUnitSetList::PHPUNIT_100, + ]) + ->withRules([ + SafeDeclareStrictTypesRector::class, + ]); diff --git a/tests/Integration/AppTest.php b/tests/Integration/AppTest.php index a5d9d061c..753834fc3 100644 --- a/tests/Integration/AppTest.php +++ b/tests/Integration/AppTest.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Tests\Integration\Controller; +use OCP\App\IAppManager; use OCP\AppFramework\App; +use PHPUnit\Framework\TestCase; /** * This test shows how to make a small Integration Test. Query your class * directly from the container, only pass in mocks if needed and run your tests * against the database */ -class AppTest extends \PHPUnit\Framework\TestCase { +final class AppTest extends TestCase { private $container; @@ -28,8 +31,8 @@ protected function setUp(): void { $this->container = $app->getContainer(); } - public function testAppInstalled() { - $appManager = $this->container->query(\OCP\App\IAppManager::class); + public function testAppInstalled(): void { + $appManager = $this->container->query(IAppManager::class); $this->assertTrue($appManager->isInstalled('maps')); } } diff --git a/tests/Integration/Db/FavoriteShareMapperTest.php b/tests/Integration/Db/FavoriteShareMapperTest.php index 95e75ddff..f7f103211 100644 --- a/tests/Integration/Db/FavoriteShareMapperTest.php +++ b/tests/Integration/Db/FavoriteShareMapperTest.php @@ -1,5 +1,7 @@ * @@ -21,33 +23,35 @@ * along with this program. If not, see . * */ - namespace tests\Integration\Db; use ChristophWurst\Nextcloud\Testing\DatabaseTransaction; use ChristophWurst\Nextcloud\Testing\TestCase; -use OC; use OCA\Maps\DB\FavoriteShare; use OCA\Maps\DB\FavoriteShareMapper; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\Files\IRootFolder; +use OCP\IDBConnection; +use OCP\Security\ISecureRandom; +use OCP\Server; -class FavoriteShareMapperTest extends TestCase { +final class FavoriteShareMapperTest extends TestCase { use DatabaseTransaction; /* @var FavoriteShareMapper */ - private $mapper; + private FavoriteShareMapper $mapper; - public function setUp(): void { + protected function setUp(): void { parent::setUp(); $this->mapper = new FavoriteShareMapper( - OC::$server->query(\OCP\IDBConnection::class), - OC::$server->getSecureRandom(), - OC::$server->getRootFolder() + Server::get(IDBConnection::class), + Server::get(ISecureRandom::class), + Server::get(IRootFolder::class) ); } - public function testCreateByOwnerAndTokenIsSuccessful() { + public function testCreateByOwnerAndTokenIsSuccessful(): void { /* @var FavoriteShare */ $share = $this->mapper->create('testUser', 'testCategory'); @@ -56,7 +60,7 @@ public function testCreateByOwnerAndTokenIsSuccessful() { $this->assertEquals('testCategory', $share->getCategory()); } - public function testFindByTokenIsSuccessful() { + public function testFindByTokenIsSuccessful(): void { /* @var FavoriteShare */ $shareExpected = $this->mapper->create('testUser', 'testCategory'); @@ -68,7 +72,7 @@ public function testFindByTokenIsSuccessful() { $this->assertEquals($shareExpected->getCategory(), $shareActual->getCategory()); } - public function testFindByOwnerAndCategoryIsSuccessful() { + public function testFindByOwnerAndCategoryIsSuccessful(): void { /* @var FavoriteShare */ $shareExpected = $this->mapper->create('testUser', 'testCategory'); @@ -80,7 +84,7 @@ public function testFindByOwnerAndCategoryIsSuccessful() { $this->assertEquals($shareExpected->getCategory(), $shareActual->getCategory()); } - public function testFindAllByOwnerIsSuccessfulAndDoesNotContainOtherShares() { + public function testFindAllByOwnerIsSuccessfulAndDoesNotContainOtherShares(): void { /* @var FavoriteShare */ $share1 = $this->mapper->create('testUser', 'testCategory1'); @@ -92,25 +96,24 @@ public function testFindAllByOwnerIsSuccessfulAndDoesNotContainOtherShares() { /* @var array */ $shares = $this->mapper->findAllByOwner('testUser'); - $shareTokens = array_map(function ($share) { - return $share->getToken(); - }, $shares); + $shareTokens = array_map(fn (FavoriteShare $share) => $share->getToken(), $shares); - $this->assertEquals(2, count($shareTokens)); + $this->assertCount(2, $shareTokens); $this->assertContains($share1->getToken(), $shareTokens); $this->assertContains($share2->getToken(), $shareTokens); } - public function testFindOrCreateByOwnerAndCategoryIsSuccessful() { + public function testFindOrCreateByOwnerAndCategoryIsSuccessful(): void { /* @var FavoriteShare */ $share = $this->mapper->findOrCreateByOwnerAndCategory('testUser', 'testCategory'); + $this->assertInstanceOf(FavoriteShare::class, $share); $this->assertIsString($share->getToken()); $this->assertEquals('testUser', $share->getOwner()); $this->assertEquals('testCategory', $share->getCategory()); } - public function testRemoveByOwnerAndCategoryIsSuccessful() { + public function testRemoveByOwnerAndCategoryIsSuccessful(): void { /* @var FavoriteShare */ $share = $this->mapper->create('testUser', 'testCategory'); diff --git a/tests/Unit/Controller/ContactsControllerTest.php b/tests/Unit/Controller/ContactsControllerTest.php index cd8e6f289..ad1c0ac26 100644 --- a/tests/Unit/Controller/ContactsControllerTest.php +++ b/tests/Unit/Controller/ContactsControllerTest.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\DAV\CardDAV\CardDavBackend; - use OCA\DAV\CardDAV\ContactsManager; -use OCA\DAV\Connector\Sabre\Principal; use OCA\Maps\AppInfo\Application; use OCA\Maps\Service\AddressService; -use OCP\IServerContainer; + +use OCP\BackgroundJob\IJobList; +use OCP\Contacts\IManager as IContactManager; +use OCP\Files\IAppData; +use OCP\Files\IRootFolder; +use OCP\IAvatarManager; +use OCP\ICacheFactory; +use OCP\IDBConnection; +use OCP\IGroupManager; +use OCP\IURLGenerator; +use OCP\IUserManager; +use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; -class ContactsControllerTest extends \PHPUnit\Framework\TestCase { - private $appName; - private $request; - private $contacts; +final class ContactsControllerTest extends TestCase { + + private $mapFolder; - private $container; - private $config; - private $app; - private $contactsController; - private $contactsController2; - private $utilsController; - private $cdBackend; + + private ContactsController $contactsController; + + private $root; public static function setUpBeforeClass(): void { $app = new Application(); $c = $app->getContainer(); - $user = $c->getServer()->getUserManager()->get('test'); - $user2 = $c->getServer()->getUserManager()->get('test2'); - $user3 = $c->getServer()->getUserManager()->get('test3'); - $group = $c->getServer()->getGroupManager()->get('group1test'); - $group2 = $c->getServer()->getGroupManager()->get('group2test'); + $user = $c->get(IUserManager::class)->get('test'); + $user2 = $c->get(IUserManager::class)->get('test2'); + $user3 = $c->get(IUserManager::class)->get('test3'); + $group = $c->get(IGroupManager::class)->get('group1test'); + $group2 = $c->get(IGroupManager::class)->get('group2test'); // CREATE DUMMY USERS if ($user === null) { - $u1 = $c->getServer()->getUserManager()->createUser('test', 'tatotitoTUTU'); - $u1->setEMailAddress('toto@toto.net'); + $user = $c->get(IUserManager::class)->createUser('test', 'tatotitoTUTU'); + $user->setEMailAddress('toto@toto.net'); } + if ($user2 === null) { - $u2 = $c->getServer()->getUserManager()->createUser('test2', 'plopinoulala000'); + $user2 = $c->get(IUserManager::class)->createUser('test2', 'plopinoulala000'); } - if ($user2 === null) { - $u3 = $c->getServer()->getUserManager()->createUser('test3', 'yeyeahPASSPASS'); + + if ($user3 === null) { + $user3 = $c->get(IUserManager::class)->createUser('test3', 'yeyeahPASSPASS'); } + if ($group === null) { - $c->getServer()->getGroupManager()->createGroup('group1test'); - $u1 = $c->getServer()->getUserManager()->get('test'); - $c->getServer()->getGroupManager()->get('group1test')->addUser($u1); + $c->get(IGroupManager::class)->createGroup('group1test'); + $c->get(IGroupManager::class)->get('group1test')->addUser($user); } + if ($group2 === null) { - $c->getServer()->getGroupManager()->createGroup('group2test'); - $u2 = $c->getServer()->getUserManager()->get('test2'); - $c->getServer()->getGroupManager()->get('group2test')->addUser($u2); + $c->get(IGroupManager::class)->createGroup('group2test'); + $c->get(IGroupManager::class)->get('group2test')->addUser($user2); } } protected function setUp(): void { - $this->app = new Application(); - $c = $this->app->getContainer(); - - $this->appName = 'maps'; - $this->request = $this->getMockBuilder('\OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - $this->contacts = $this->getMockBuilder('OCP\Contacts\IManager') - ->disableOriginalConstructor() - ->getMock(); - - $urlGenerator = $c->getServer()->getURLGenerator(); - - $this->contactsManager = $c->query(IServerContainer::class)->getContactsManager(); - $this->cm = $c->query(ContactsManager::class); - $this->cm->setupContactsProvider($this->contactsManager, 'test', $urlGenerator); - - $this->app = new Application(); - $this->container = $this->app->getContainer(); - $c = $this->container; - $this->config = $c->query(IServerContainer::class)->getConfig(); - - $this->appData = $this->getMockBuilder('\OCP\Files\IAppData') - ->disableOriginalConstructor() - ->getMock(); - - $this->addressService = new AddressService( - $c->query(IServerContainer::class)->getMemCacheFactory(), - $c->query(IServerContainer::class)->get(LoggerInterface::class), - $c->query(IServerContainer::class)->getJobList(), - $this->appData, - $c->query(IServerContainer::class)->query(\OCP\IDBConnection::class) + $app = new Application(); + $c = $app->getContainer(); + + $appName = 'maps'; + $request = $this->createMock('\OCP\IRequest'); + + $urlGenerator = $c->get(IURlGenerator::class); + + $contactsManager = $c->get(IContactManager::class); + $cm = $c->get(ContactsManager::class); + $cm->setupContactsProvider($contactsManager, 'test', $urlGenerator); + + $app = new Application(); + $container = $app->getContainer(); + $c = $container; + + $appData = $this->createMock(IAppData::class); + + $addressService = new AddressService( + $c->get(ICacheFactory::class), + $c->get(LoggerInterface::class), + $c->get(IJobList::class), + $appData, + $c->get(IDBConnection::class) ); - //$this->userPrincipalBackend = new Principal( - // $c->getServer()->getUserManager(), - // $c->getServer()->getGroupManager(), - // $c->getServer()->get(\OCP\Share\IManager::class), - // \OC::$server->getUserSession(), - // $c->query(IServerContainer::class)->getConfig(), - // \OC::$server->getAppManager() - //); - $this->userPrincipalBackend = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') - ->disableOriginalConstructor() - ->getMock(); - - $this->cdBackend = $c->query(IServerContainer::class)->query(CardDavBackend::class); - $this->root = $c->query(IServerContainer::class)->getRootFolder(); + $cdBackend = $c->get(CardDavBackend::class); + $this->root = $c->get(IRootFolder::class); $this->mapFolder = $this->createMapFolder(); $this->contactsController = new ContactsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->query(\OCP\IDBConnection::class), - $this->contactsManager, - $this->addressService, + $appName, + $request, + $c->get(IDBConnection::class), + $contactsManager, + $addressService, 'test', - $this->cdBackend, - $c->query(IServerContainer::class)->get(\OCP\IAvatarManager::class), + $cdBackend, + $c->get(IAvatarManager::class), $this->root, $urlGenerator); - //$this->contactsController = $this->getMockBuilder('OCA\Maps\Controller\ContactsController') - // ->disableOriginalConstructor() - // ->getMock(); - - $this->contactsController2 = new ContactsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->query(\OCP\IDBConnection::class), - $this->contactsManager, - $this->addressService, - 'test2', - $this->cdBackend, - $c->query(IServerContainer::class)->get(\OCP\IAvatarManager::class), - $this->root, - $urlGenerator - ); - - $this->utilsController = new UtilsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->getAppManager(), - $this->root, - 'test' - ); } private function createMapFolder() { $userFolder = $this->root->getUserFolder('test'); if ($userFolder->nodeExists('Map')) { return $userFolder->get('Map'); - } else { - return $userFolder->newFolder('Map'); } + + return $userFolder->newFolder('Map'); } public static function tearDownAfterClass(): void { @@ -176,8 +139,7 @@ protected function tearDown(): void { // in case there was a failure and something was not deleted } - public function testAddContact() { - $c = $this->container; + public function testAddContact(): void { //$this->contacts->createOrUpdate() //var_dump($this->contactsManager->isEnabled()); // TODO understand why this only returns system address book @@ -186,12 +148,11 @@ public function testAddContact() { $resp = $this->contactsController->getContacts(); $status = $resp->getStatus(); $this->assertEquals(200, $status); - $data = $resp->getData(); + $resp->getData(); //var_dump($data); } - public function testAddContactMyMap() { - $c = $this->container; + public function testAddContactMyMap(): void { //$this->contacts->createOrUpdate() //var_dump($this->contactsManager->isEnabled()); // TODO understand why this only returns system address book @@ -200,7 +161,7 @@ public function testAddContactMyMap() { $resp = $this->contactsController->getContacts($this->mapFolder->getId()); $status = $resp->getStatus(); $this->assertEquals(200, $status); - $data = $resp->getData(); + $resp->getData(); //var_dump($data); } diff --git a/tests/Unit/Controller/DevicesApiControllerTest.php b/tests/Unit/Controller/DevicesApiControllerTest.php index bac12b4f3..0d86b2763 100644 --- a/tests/Unit/Controller/DevicesApiControllerTest.php +++ b/tests/Unit/Controller/DevicesApiControllerTest.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\AppInfo\Application; use OCA\Maps\Service\DevicesService; +use OCP\IGroupManager; use OCP\IServerContainer; +use OCP\IUserManager; +use OCP\L10N\IFactory; +use PHPUnit\Framework\TestCase; -class DevicesApiControllerTest extends \PHPUnit\Framework\TestCase { - private $appName; - private $request; - private $contacts; - - private $container; - private $config; - private $app; +final class DevicesApiControllerTest extends TestCase { - private $devicesApiController; - private $devicesApiController2; - private $utilsController; - private $root; + private DevicesApiController $devicesApiController; public static function setUpBeforeClass(): void { $app = new Application(); $c = $app->getContainer(); - $user = $c->getServer()->getUserManager()->get('test'); - $user2 = $c->getServer()->getUserManager()->get('test2'); - $user3 = $c->getServer()->getUserManager()->get('test3'); - $group = $c->getServer()->getGroupManager()->get('group1test'); - $group2 = $c->getServer()->getGroupManager()->get('group2test'); + $user = $c->get(IUserManager::class)->get('test'); + $user2 = $c->get(IUserManager::class)->get('test2'); + $user3 = $c->get(IUserManager::class)->get('test3'); + $group = $c->get(IGroupManager::class)->get('group1test'); + $group2 = $c->get(IGroupManager::class)->get('group2test'); // CREATE DUMMY USERS if ($user === null) { - $u1 = $c->getServer()->getUserManager()->createUser('test', 'tatotitoTUTU'); + $u1 = $c->get(IUserManager::class)->createUser('test', 'tatotitoTUTU'); $u1->setEMailAddress('toto@toto.net'); } + if ($user2 === null) { - $u2 = $c->getServer()->getUserManager()->createUser('test2', 'plopinoulala000'); + $user3 = $c->get(IUserManager::class)->createUser('test2', 'plopinoulala000'); } - if ($user2 === null) { - $u3 = $c->getServer()->getUserManager()->createUser('test3', 'yeyeahPASSPASS'); + + if ($user3 === null) { + $user3 = $c->get(IUserManager::class)->createUser('test3', 'yeyeahPASSPASS'); } + if ($group === null) { - $c->getServer()->getGroupManager()->createGroup('group1test'); - $u1 = $c->getServer()->getUserManager()->get('test'); - $c->getServer()->getGroupManager()->get('group1test')->addUser($u1); + $c->get(IGroupManager::class)->createGroup('group1test'); + $c->get(IGroupManager::class)->get('group1test')->addUser($user1); } + if ($group2 === null) { - $c->getServer()->getGroupManager()->createGroup('group2test'); - $u2 = $c->getServer()->getUserManager()->get('test2'); - $c->getServer()->getGroupManager()->get('group2test')->addUser($u2); + $c->get(IGroupManager::class)->createGroup('group2test'); + $c->get(IGroupManager::class)->get('group2test')->addUser($user2); } } protected function setUp(): void { - $this->appName = 'maps'; - $this->request = $this->getMockBuilder('\OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - $this->contacts = $this->getMockBuilder('OCP\Contacts\IManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->app = new Application(); - $this->container = $this->app->getContainer(); - $c = $this->container; - $this->config = $c->query(IServerContainer::class)->getConfig(); - $this->root = $c->query(IServerContainer::class)->getRootFolder(); + $appName = 'maps'; + $request = $this->createMock('\OCP\IRequest'); - $this->devicesApiController = new DevicesApiController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(DevicesService::class), - 'test' - ); - - $this->devicesApiController2 = new DevicesApiController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(DevicesService::class), - 'test2' - ); + $app = new Application(); + $container = $app->getContainer(); + $c = $container; - $this->utilsController = new UtilsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->getAppManager(), - $this->root, + $this->devicesApiController = new DevicesApiController( + $appName, + $request, + $c->get(IFactory::class)->get('maps'), + $c->get(DevicesService::class), 'test' ); @@ -130,7 +91,7 @@ protected function tearDown(): void { // in case there was a failure and something was not deleted } - public function testAddPoints() { + public function testAddPoints(): void { $resp = $this->devicesApiController->getDevices('1.0'); $data = $resp->getData(); foreach ($data as $device) { @@ -149,8 +110,6 @@ public function testAddPoints() { $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $deviceId = $data['deviceId']; - $pointId = $data['pointId']; $_SERVER['HTTP_USER_AGENT'] = 'testBrowser'; $ts = (new \DateTime())->getTimestamp(); @@ -159,7 +118,6 @@ public function testAddPoints() { $this->assertEquals(200, $status); $data = $resp->getData(); $deviceId2 = $data['deviceId']; - $pointId2 = $data['pointId']; // test user agent is correct $resp = $this->devicesApiController->getDevices('1.0'); $data = $resp->getData(); @@ -170,6 +128,7 @@ public function testAddPoints() { $d2Found = true; } } + $this->assertEquals(true, $d2Found); // This happens with a request such as /api/1.0/devices?lat=1.1&lng=2.2×tamp=&user_agent=testDevice&altitude=&battery=&accuracy= @@ -177,8 +136,6 @@ public function testAddPoints() { $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $deviceId3 = $data['deviceId']; - $pointId3 = $data['pointId']; // test point values $resp = $this->devicesApiController->getDevicePoints($deviceId2); @@ -202,7 +159,7 @@ public function testAddPoints() { $this->assertEquals('Invalid values', $data); } - public function testEditDevice() { + public function testEditDevice(): void { $resp = $this->devicesApiController->getDevices('1.0'); $data = $resp->getData(); foreach ($data as $device) { @@ -214,7 +171,6 @@ public function testEditDevice() { $this->assertEquals(200, $status); $data = $resp->getData(); $deviceId = $data['deviceId']; - $pointId = $data['pointId']; $resp = $this->devicesApiController->editDevice($deviceId, '#001122'); $status = $resp->getStatus(); @@ -238,7 +194,7 @@ public function testEditDevice() { // $resp = $this->devicesApiController->deleteDevice($device['id']); // } - // $userfolder = $this->container->query(IServerContainer::class)->getUserFolder('test'); + // $userfolder = $this->container->get(IServerContainer::class)->getUserFolder('test'); // $content1 = file_get_contents('tests/test_files/devicesOk.gpx'); // $userfolder->newFile('devicesOk.gpx')->putContent($content1); @@ -335,8 +291,8 @@ public function testEditDevice() { // $this->assertEquals('Nothing to export', $data); //} - public function testEditDevices() { - $this->assertEquals(true, 1 == 1); + public function testEditDevices(): void { + $this->assertEquals(true, 1 === 1); //// valid edition //$resp = $this->favoritesController->addFavorite('a', 3.1, 4.1, 'cat1', null, null); //$favId = $resp->getData()['id']; diff --git a/tests/Unit/Controller/DevicesControllerTest.php b/tests/Unit/Controller/DevicesControllerTest.php index d61a7b3a8..918530b7b 100644 --- a/tests/Unit/Controller/DevicesControllerTest.php +++ b/tests/Unit/Controller/DevicesControllerTest.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\AppInfo\Application; use OCA\Maps\DB\DeviceShareMapper; use OCA\Maps\Service\DevicesService; use OCP\Files\IRootFolder; -use OCP\IServerContainer; - -class DevicesControllerTest extends \PHPUnit\Framework\TestCase { - private $appName; - private $request; - private $contacts; +use OCP\IAppConfig; +use OCP\IDateTimeZone; +use OCP\IGroupManager; +use OCP\IRequest; +use OCP\IUserManager; +use OCP\L10N\IFactory; +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; - private $container; - private $config; - private $app; +final class DevicesControllerTest extends TestCase { + private ContainerInterface $container; - private $devicesController; - private $devicesController2; - private $utilsController; - private $root; + private DevicesController $devicesController; public static function setUpBeforeClass(): void { $app = new Application(); $c = $app->getContainer(); - $user = $c->getServer()->getUserManager()->get('test'); - $user2 = $c->getServer()->getUserManager()->get('test2'); - $user3 = $c->getServer()->getUserManager()->get('test3'); - $group = $c->getServer()->getGroupManager()->get('group1test'); - $group2 = $c->getServer()->getGroupManager()->get('group2test'); + $user = $c->get(IUserManager::class)->get('test'); + $user2 = $c->get(IUserManager::class)->get('test2'); + $user3 = $c->get(IUserManager::class)->get('test3'); + $group = $c->get(IGroupManager::class)->get('group1test'); + $group2 = $c->get(IGroupManager::class)->get('group2test'); // CREATE DUMMY USERS if ($user === null) { - $u1 = $c->getServer()->getUserManager()->createUser('test', 'tatotitoTUTU'); + $u1 = $c->get(IUserManager::class)->createUser('test', 'tatotitoTUTU'); $u1->setEMailAddress('toto@toto.net'); } + if ($user2 === null) { - $u2 = $c->getServer()->getUserManager()->createUser('test2', 'plopinoulala000'); + $u2 = $c->get(IUserManager::class)->createUser('test2', 'plopinoulala000'); } - if ($user2 === null) { - $u3 = $c->getServer()->getUserManager()->createUser('test3', 'yeyeahPASSPASS'); + + if ($user3 === null) { + $u3 = $c->get(IUserManager::class)->createUser('test3', 'yeyeahPASSPASS'); } + if ($group === null) { - $c->getServer()->getGroupManager()->createGroup('group1test'); - $u1 = $c->getServer()->getUserManager()->get('test'); - $c->getServer()->getGroupManager()->get('group1test')->addUser($u1); + $c->get(IGroupManager::class)->createGroup('group1test'); + $u1 = $c->get(IGroupManager::class)->get('test'); + $c->get(IGroupManager::class)->get('group1test')->addUser($u1); } + if ($group2 === null) { - $c->getServer()->getGroupManager()->createGroup('group2test'); - $u2 = $c->getServer()->getUserManager()->get('test2'); - $c->getServer()->getGroupManager()->get('group2test')->addUser($u2); + $c->get(IGroupManager::class)->createGroup('group2test'); + $u2 = $c->get(IGroupManager::class)->get('test2'); + $c->get(IGroupManager::class)->get('group2test')->addUser($u2); } } protected function setUp(): void { - $this->appName = 'maps'; - $this->request = $this->getMockBuilder('\OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - $this->contacts = $this->getMockBuilder('OCP\Contacts\IManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->app = new Application(); - $this->container = $this->app->getContainer(); + $appName = 'maps'; + $request = $this->createMock(IRequest::class); + + $app = new Application(); + $this->container = $app->getContainer(); $c = $this->container; - $this->config = $c->query(IServerContainer::class)->getConfig(); - $this->root = $c->query(IServerContainer::class)->getRootFolder(); $this->devicesController = new DevicesController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(DevicesService::class), - $c->query(DeviceShareMapper::class), - $c->query(IServerContainer::class)->get(\OCP\IDateTimeZone::class), - $c->query(IRootFolder::class), - 'test' - ); - - $this->devicesController2 = new DevicesController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(DevicesService::class), - $c->query(DeviceShareMapper::class), - $c->query(IServerContainer::class)->get(\OCP\IDateTimeZone::class), - $c->query(IRootFolder::class), - 'test2' - ); - - $this->utilsController = new UtilsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->getAppManager(), - $this->root, + $appName, + $request, + $c->get(IAppConfig::class), + $c->get(IFactory::class)->get('maps'), + $c->get(DevicesService::class), + $c->get(DeviceShareMapper::class), + $c->get(IDateTimeZone::class), + $c->get(IRootFolder::class), 'test' ); @@ -127,7 +92,7 @@ protected function setUp(): void { $resp = $this->devicesController->getDevices(); $data = $resp->getData(); foreach ($data as $device) { - $resp = $this->devicesController->deleteDevice($device['id']); + $this->devicesController->deleteDevice($device['id']); } } @@ -148,7 +113,7 @@ protected function tearDown(): void { // in case there was a failure and something was not deleted } - public function testAddPoints() { + public function testAddPoints(): void { $resp = $this->devicesController->getDevices(); $data = $resp->getData(); foreach ($data as $device) { @@ -168,7 +133,6 @@ public function testAddPoints() { $this->assertEquals(200, $status); $data = $resp->getData(); $deviceId = $data['deviceId']; - $pointId = $data['pointId']; $_SERVER['HTTP_USER_AGENT'] = 'testBrowser'; $ts = (new \DateTime())->getTimestamp(); @@ -177,7 +141,6 @@ public function testAddPoints() { $this->assertEquals(200, $status); $data = $resp->getData(); $deviceId2 = $data['deviceId']; - $pointId2 = $data['pointId']; // test user agent is correct $resp = $this->devicesController->getDevices(); $data = $resp->getData(); @@ -188,6 +151,7 @@ public function testAddPoints() { $d2Found = true; } } + $this->assertEquals(true, $d2Found); // test point values @@ -199,7 +163,7 @@ public function testAddPoints() { $this->assertEquals(true, $data[0]['timestamp'] >= $ts); // test missing values - $resp = $this->devicesController->addDevicePoint(1.1, 2.2, 12346, 'testDevice', null, null, null); + $resp = $this->devicesController->addDevicePoint(1.1, 2.2, 12346, 'testDevice'); $status = $resp->getStatus(); $this->assertEquals(200, $status); @@ -231,7 +195,7 @@ public function testAddPoints() { $this->assertEquals('Invalid values', $data); } - public function testEditDevice() { + public function testEditDevice(): void { $resp = $this->devicesController->getDevices(); $data = $resp->getData(); foreach ($data as $device) { @@ -243,7 +207,6 @@ public function testEditDevice() { $this->assertEquals(200, $status); $data = $resp->getData(); $deviceId = $data['deviceId']; - $pointId = $data['pointId']; $resp = $this->devicesController->editDevice($deviceId, '#001122', 'editedDevice'); $status = $resp->getStatus(); @@ -261,14 +224,14 @@ public function testEditDevice() { $this->assertEquals(400, $status); } - public function testImportExportDevices() { + public function testImportExportDevices(): void { $resp = $this->devicesController->getDevices(); $data = $resp->getData(); foreach ($data as $device) { $resp = $this->devicesController->deleteDevice($device['id']); } - $userfolder = $this->container->query(IServerContainer::class)->getUserFolder('test'); + $userfolder = $this->container->get(IRootFolder::class)->getUserFolder('test'); $content1 = file_get_contents('tests/test_files/devicesOk.gpx'); $userfolder->newFile('devicesOk.gpx')->putContent($content1); @@ -300,6 +263,7 @@ public function testImportExportDevices() { $id = $device['id']; $devices[$id] = $device; } + // get number of points foreach ($devices as $id => $device) { $resp = $this->devicesController->getDevicePoints($id); @@ -319,18 +283,19 @@ public function testImportExportDevices() { // parse xml and compare number of devices and points $xmLData = $userfolder->get($exportPath)->getContent(); - $xml = simplexml_load_string($xmLData); + $xml = simplexml_load_string((string)$xmLData); $trks = $xml->trk; // number of devices - $this->assertEquals(count($ids), count($trks)); + $this->assertCount(count($ids), $trks); $pointCountExport = []; // count exported points per device foreach ($trks as $trk) { $name = (string)$trk->name[0]; $pointCountExport[$name] = count($trk->trkseg[0]->trkpt); } + // check that it matches the data in the DB - foreach ($devices as $id => $device) { + foreach ($devices as $device) { $this->assertEquals($device['nbPoints'], $pointCountExport[$device['user_agent']]); } @@ -365,8 +330,8 @@ public function testImportExportDevices() { $this->assertEquals('Nothing to export', $data); } - public function testEditDevices() { - $this->assertEquals(true, 1 == 1); + public function testEditDevices(): void { + $this->assertEquals(true, 1 === 1); //// valid edition //$resp = $this->favoritesController->addFavorite('a', 3.1, 4.1, 'cat1', null, null); //$favId = $resp->getData()['id']; diff --git a/tests/Unit/Controller/FavoritesApiControllerTest.php b/tests/Unit/Controller/FavoritesApiControllerTest.php index 939563bc8..0c470c1f2 100644 --- a/tests/Unit/Controller/FavoritesApiControllerTest.php +++ b/tests/Unit/Controller/FavoritesApiControllerTest.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\AppInfo\Application; use OCA\Maps\Service\FavoritesService; -use OCP\IServerContainer; +use OCP\IGroupManager; +use OCP\IUserManager; +use OCP\L10N\IFactory; +use PHPUnit\Framework\TestCase; -class FavoritesApiControllerTest extends \PHPUnit\Framework\TestCase { - private $appName; - private $request; - private $contacts; +final class FavoritesApiControllerTest extends TestCase { - private $container; - private $config; - private $app; - private $root; - - private $favoritesApiController; - private $favoritesApiController2; - private $utilsController; + private FavoritesApiController $favoritesApiController; public static function setUpBeforeClass(): void { $app = new Application(); $c = $app->getContainer(); - $user = $c->getServer()->getUserManager()->get('test'); - $user2 = $c->getServer()->getUserManager()->get('test2'); - $user3 = $c->getServer()->getUserManager()->get('test3'); - $group = $c->getServer()->getGroupManager()->get('group1test'); - $group2 = $c->getServer()->getGroupManager()->get('group2test'); + $user = $c->get(IUserManager::class)->get('test'); + $user2 = $c->get(IUserManager::class)->get('test2'); + $c->get(IUserManager::class)->get('test3'); + $group = $c->get(IGroupManager::class)->get('group1test'); + $group2 = $c->get(IGroupManager::class)->get('group2test'); // CREATE DUMMY USERS if ($user === null) { - $u1 = $c->getServer()->getUserManager()->createUser('test', 'tatotitoTUTU'); + $u1 = $c->get(IUserManager::class)->createUser('test', 'tatotitoTUTU'); $u1->setEMailAddress('toto@toto.net'); } + if ($user2 === null) { - $u2 = $c->getServer()->getUserManager()->createUser('test2', 'plopinoulala000'); - } - if ($user2 === null) { - $u3 = $c->getServer()->getUserManager()->createUser('test3', 'yeyeahPASSPASS'); + $u2 = $c->get(IUserManager::class)->createUser('test2', 'plopinoulala000'); + $u3 = $c->get(IUserManager::class)->createUser('test3', 'yeyeahPASSPASS'); } + if ($group === null) { - $c->getServer()->getGroupManager()->createGroup('group1test'); - $u1 = $c->getServer()->getUserManager()->get('test'); - $c->getServer()->getGroupManager()->get('group1test')->addUser($u1); + $c->get(IGroupManager::class)->createGroup('group1test'); + $u1 = $c->get(IUserManager::class)->get('test'); + $c->get(IGroupManager::class)->get('group1test')->addUser($u1); } + if ($group2 === null) { - $c->getServer()->getGroupManager()->createGroup('group2test'); - $u2 = $c->getServer()->getUserManager()->get('test2'); - $c->getServer()->getGroupManager()->get('group2test')->addUser($u2); + $c->get(IGroupManager::class)->createGroup('group2test'); + $u2 = $c->get(IUserManager::class)->get('test2'); + $c->get(IGroupManager::class)->get('group2test')->addUser($u2); } } protected function setUp(): void { - $this->appName = 'maps'; - $this->request = $this->getMockBuilder('\OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - $this->contacts = $this->getMockBuilder('OCP\Contacts\IManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->app = new Application(); - $this->container = $this->app->getContainer(); - $c = $this->container; - $this->config = $c->query(IServerContainer::class)->getConfig(); - $this->root = $c->query(IServerContainer::class)->getRootFolder(); + $appName = 'maps'; + $request = $this->createMock('\OCP\IRequest'); - $this->favoritesApiController = new FavoritesApiController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(FavoritesService::class), - 'test' - ); - - $this->favoritesApiController2 = new FavoritesApiController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(FavoritesService::class), - 'test2' - ); + $app = new Application(); + $container = $app->getContainer(); + $c = $container; - $this->utilsController = new UtilsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->getAppManager(), - $this->root, + $this->favoritesApiController = new FavoritesApiController( + $appName, + $request, + $c->get(IFactory::class)->get('maps'), + $c->get(FavoritesService::class), 'test' ); } @@ -119,14 +78,14 @@ protected function setUp(): void { public static function tearDownAfterClass(): void { //$app = new Application(); //$c = $app->getContainer(); - //$user = $c->getServer()->getUserManager()->get('test'); + //$user = $c->get(IUserManager::class)->get('test'); //$user->delete(); - //$user = $c->getServer()->getUserManager()->get('test2'); + //$user = $c->get(IUserManager::class)->get('test2'); //$user->delete(); - //$user = $c->getServer()->getUserManager()->get('test3'); + //$user = $c->get(IUserManager::class)->get('test3'); //$user->delete(); - //$c->getServer()->getGroupManager()->get('group1test')->delete(); - //$c->getServer()->getGroupManager()->get('group2test')->delete(); + //$c->get(IGroupManager::class)->get('group1test')->delete(); + //$c->get(IGroupManager::class)->get('group2test')->delete(); } protected function tearDown(): void { @@ -138,7 +97,7 @@ protected function tearDown(): void { } } - public function testAddFavorites() { + public function testAddFavorites(): void { // correct values $resp = $this->favoritesApiController->addFavorite('1.0', 'one', 3.1, 4.2, '', null, null); $status = $resp->getStatus(); @@ -168,7 +127,7 @@ public function testAddFavorites() { $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $this->assertEquals(2, count($data)); + $this->assertCount(2, $data); //// get favorites using etag //$etag = $resp->getEtag(); @@ -199,7 +158,7 @@ public function testAddFavorites() { $this->assertEquals(400, $status); } - public function testEditFavorites() { + public function testEditFavorites(): void { // valid edition $resp = $this->favoritesApiController->addFavorite('1.0', 'a', 3.1, 4.1, 'cat1', null, null); $favId = $resp->getData()['id']; @@ -217,13 +176,14 @@ public function testEditFavorites() { if ($fav['id'] === $favId) { $seen = true; $this->assertEquals('aa', $fav['name']); - $this->assertEquals(3.2, $fav['lat']); - $this->assertEquals(4.2, $fav['lng']); + $this->assertEqualsWithDelta(3.2, $fav['lat'], PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(4.2, $fav['lng'], PHP_FLOAT_EPSILON); $this->assertEquals('cat2', $fav['category']); $this->assertEquals('comment', $fav['comment']); $this->assertEquals('ext', $fav['extensions']); } } + $this->assertEquals(true, $seen); // invalid edition diff --git a/tests/Unit/Controller/FavoritesControllerTest.php b/tests/Unit/Controller/FavoritesControllerTest.php index 7194ac275..42bc92b0b 100644 --- a/tests/Unit/Controller/FavoritesControllerTest.php +++ b/tests/Unit/Controller/FavoritesControllerTest.php @@ -1,5 +1,7 @@ getContainer(); - $user = $c->getServer()->getUserManager()->get('test'); - $user2 = $c->getServer()->getUserManager()->get('test2'); - $user3 = $c->getServer()->getUserManager()->get('test3'); - $group = $c->getServer()->getGroupManager()->get('group1test'); - $group2 = $c->getServer()->getGroupManager()->get('group2test'); + $user = $c->get(IUserManager::class)->get('test'); + $user2 = $c->get(IUserManager::class)->get('test2'); + $user3 = $c->get(IUserManager::class)->get('test3'); + $group = $c->get(IGroupManager::class)->get('group1test'); + $group2 = $c->get(IGroupManager::class)->get('group2test'); // CREATE DUMMY USERS if ($user === null) { - $u1 = $c->getServer()->getUserManager()->createUser('test', 'tatotitoTUTU'); + $u1 = $c->get(IUserManager::class)->createUser('test', 'tatotitoTUTU'); $u1->setEMailAddress('toto@toto.net'); } + if ($user2 === null) { - $u2 = $c->getServer()->getUserManager()->createUser('test2', 'plopinoulala000'); + $c->get(IUserManager::class)->createUser('test2', 'plopinoulala000'); } - if ($user2 === null) { - $u3 = $c->getServer()->getUserManager()->createUser('test3', 'yeyeahPASSPASS'); + + if ($user3 === null) { + $c->get(IUserManager::class)->createUser('test3', 'yeyeahPASSPASS'); } + if ($group === null) { - $c->getServer()->getGroupManager()->createGroup('group1test'); - $u1 = $c->getServer()->getUserManager()->get('test'); - $c->getServer()->getGroupManager()->get('group1test')->addUser($u1); + $c->get(IGroupManager::class)->createGroup('group1test'); + $c->get(IGroupManager::class)->get('group1test')->addUser($user1); } + if ($group2 === null) { - $c->getServer()->getGroupManager()->createGroup('group2test'); - $u2 = $c->getServer()->getUserManager()->get('test2'); - $c->getServer()->getGroupManager()->get('group2test')->addUser($u2); + $c->get(IGroupManager::class)->createGroup('group2test'); + $c->get(IGroupManager::class)->get('group2test')->addUser($user2); } } protected function setUp(): void { - $this->appName = 'maps'; - $this->request = $this->getMockBuilder('\OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - $this->contacts = $this->getMockBuilder('OCP\Contacts\IManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->app = new Application(); - $this->container = $this->app->getContainer(); + $appName = 'maps'; + $request = $this->createMock(IRequest::class); + + $app = new Application(); + $this->container = $app->getContainer(); $c = $this->container; - $this->config = $c->query(IServerContainer::class)->getConfig(); - $this->root = $c->query(IServerContainer::class)->getRootFolder(); + + $this->root = $c->get(IRootFolder::class); $this->favoritesController = new FavoritesController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(FavoritesService::class), - $c->query(IServerContainer::class)->get(\OCP\IDateTimeZone::class), - $c->query(FavoriteShareMapper::class), + $appName, + $request, + $c->get(IAppConfig::class), + $this->root, + $c->get(IFactory::class)->get($appName), + $c->get(FavoritesService::class), + $c->get(IDateTimeZone::class), + $c->get(FavoriteShareMapper::class), 'test' ); $this->favoritesController2 = new FavoritesController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(FavoritesService::class), - $c->query(IServerContainer::class)->get(\OCP\IDateTimeZone::class), - $c->query(FavoriteShareMapper::class), + $appName, + $request, + $c->get(IAppConfig::class), + $this->root, + $c->get(IFactory::class)->get($appName), + $c->get(FavoritesService::class), + $c->get(IDateTimeZone::class), + $c->get(FavoriteShareMapper::class), 'test2' ); - $this->utilsController = new UtilsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->getAppManager(), - $this->root, - 'test' - ); $this->mapFolder = $this->createMapFolder(); } - private function createMapFolder() { + private function createMapFolder(): Folder { $userFolder = $this->root->getUserFolder('test'); if ($userFolder->nodeExists('Map')) { - return $userFolder->get('Map')->delete(); + $userFolder->get('Map')->delete(); } + return $userFolder->newFolder('Map'); } @@ -161,7 +144,7 @@ protected function tearDown(): void { } } - public function testAddFavorites() { + public function testAddFavorites(): void { // correct values $resp = $this->favoritesController->addFavorite('one', 3.1, 4.2, '', null, null); $status = $resp->getStatus(); @@ -192,7 +175,7 @@ public function testAddFavorites() { $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $this->assertEquals(2, count($data)); + $this->assertCount(2, $data); // delete created favorites $resp = $this->favoritesController->deleteFavorite($id1); @@ -213,7 +196,8 @@ public function testAddFavorites() { $this->assertEquals(400, $status); } - public function testAddFavoritesMyMap() { + public function testAddFavoritesMyMap(): void { + $this->mapFolder = $this->createMapFolder(); $myMapId = $this->mapFolder->getId(); // correct values $resp = $this->favoritesController->addFavorite('one', 3.1, 4.2, '', null, null, $myMapId); @@ -252,7 +236,7 @@ public function testAddFavoritesMyMap() { $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $this->assertEquals(3, count($data)); + $this->assertCount(3, $data); // delete created favorites0 $resp = $this->favoritesController->deleteFavorite($id3, $myMapId); @@ -273,8 +257,8 @@ public function testAddFavoritesMyMap() { $this->assertEquals(400, $status); } - public function testImportExportFavorites() { - $userfolder = $this->container->query(IServerContainer::class)->getUserFolder('test'); + public function testImportExportFavorites(): void { + $userfolder = $this->container->get(IRootFolder::class)->getUserFolder('test'); $content1 = file_get_contents('tests/test_files/favoritesOk.gpx'); $newFile = $userfolder->newFile('favoritesOk.gpx'); $newFile->putContent($content1); @@ -290,12 +274,13 @@ public function testImportExportFavorites() { $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $this->assertEquals(27, count($data)); + $this->assertCount(27, $data); $nbFavorites = count($data); $categoryCount = []; foreach ($data as $fav) { $categoryCount[$fav['category']] = isset($categoryCount[$fav['category']]) ? ($categoryCount[$fav['category']] + 1) : 1; } + $categories = array_keys($categoryCount); // import errors @@ -322,16 +307,17 @@ public function testImportExportFavorites() { // parse xml and compare number of favorite for each category $xmLData = $userfolder->get($exportPath)->getContent(); - $xml = simplexml_load_string($xmLData); + $xml = simplexml_load_string((string)$xmLData); $wpts = $xml->wpt; - $this->assertEquals($nbFavorites, count($wpts)); + $this->assertCount($nbFavorites, $wpts); $categoryCountExport = []; foreach ($wpts as $wpt) { $cat = (string)$wpt->type[0]; $categoryCountExport[$cat] = isset($categoryCountExport[$cat]) ? ($categoryCountExport[$cat] + 1) : 1; } + foreach ($categoryCount as $cat => $nb) { - $this->assertEquals($categoryCountExport[$cat], $nb); + $this->assertSame($categoryCountExport[$cat], $nb); } // export error @@ -355,12 +341,14 @@ public function testImportExportFavorites() { $data = $resp->getData(); $favIds = []; foreach ($data as $fav) { - array_push($favIds, $fav['id']); + $favIds[] = $fav['id']; } + $resp = $this->favoritesController->deleteFavorites($favIds); // and then try to export $resp = $this->favoritesController->exportFavorites($categories, null, null, true); + $status = $resp->getStatus(); $this->assertEquals(400, $status); $data = $resp->getData(); @@ -369,7 +357,7 @@ public function testImportExportFavorites() { - public function testEditFavorites() { + public function testEditFavorites(): void { // valid edition $resp = $this->favoritesController->addFavorite('a', 3.1, 4.1, 'cat1', null, null); $favId = $resp->getData()['id']; @@ -387,13 +375,14 @@ public function testEditFavorites() { if ($fav['id'] === $favId) { $seen = true; $this->assertEquals('aa', $fav['name']); - $this->assertEquals(3.2, $fav['lat']); - $this->assertEquals(4.2, $fav['lng']); + $this->assertEqualsWithDelta(3.2, $fav['lat'], PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(4.2, $fav['lng'], PHP_FLOAT_EPSILON); $this->assertEquals('cat2', $fav['category']); $this->assertEquals('comment', $fav['comment']); $this->assertEquals('ext', $fav['extensions']); } } + $this->assertEquals(true, $seen); // invalid edition @@ -415,6 +404,7 @@ public function testEditFavorites() { $resp = $this->favoritesController->addFavorite('one', 3.1, 4.2, 'cat2', null, null); $resp = $this->favoritesController->renameCategories(['cat1'], 'cat1RENAMED'); + $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); @@ -429,10 +419,11 @@ public function testEditFavorites() { $this->assertEquals('cat1RENAMED', $fav['category']); } } + $this->assertEquals(true, $seen); } - public function testEditFavoritesMyMap() { + public function testEditFavoritesMyMap(): void { $this->mapFolder = $this->createMapFolder(); $myMapId = $this->mapFolder->getId(); // valid edition @@ -452,12 +443,13 @@ public function testEditFavoritesMyMap() { if ($fav['id'] === $favId) { $seen = true; $this->assertEquals('aa', $fav['name']); - $this->assertEquals(3.2, $fav['lat']); - $this->assertEquals(4.2, $fav['lng']); + $this->assertEqualsWithDelta(3.2, $fav['lat'], PHP_FLOAT_EPSILON); + $this->assertEqualsWithDelta(4.2, $fav['lng'], PHP_FLOAT_EPSILON); $this->assertEquals('cat2', $fav['category']); $this->assertEquals('comment', $fav['comment']); } } + $this->assertEquals(true, $seen); // invalid edition @@ -478,6 +470,7 @@ public function testEditFavoritesMyMap() { $resp = $this->favoritesController->addFavorite('one', 3.1, 4.2, 'cat2', null, null, $myMapId); $resp = $this->favoritesController->renameCategories(['cat1'], 'cat1RENAMED', $myMapId); + $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); @@ -492,10 +485,11 @@ public function testEditFavoritesMyMap() { $this->assertEquals('cat1RENAMED', $fav['category']); } } + $this->assertEquals(true, $seen); } - public function testShareUnShareCategory() { + public function testShareUnShareCategory(): void { $categoryName = 'test3458565'; $id = $this->favoritesController @@ -514,7 +508,7 @@ public function testShareUnShareCategory() { $this->assertTrue($response2->getData()['did_exist']); } - public function testShareUnShareCategoryNotAuthorized() { + public function testShareUnShareCategoryNotAuthorized(): void { $categoryName = 'test3458565'; $id = $this->favoritesController2 @@ -530,7 +524,7 @@ public function testShareUnShareCategoryNotAuthorized() { $this->assertEquals(Http::STATUS_BAD_REQUEST, $response2->getStatus()); } - public function testShareUnShareNonExistentCategory() { + public function testShareUnShareNonExistentCategory(): void { $categoryName = 'non_existent'; $response1 = $this->favoritesController->shareCategory($categoryName); @@ -542,17 +536,14 @@ public function testShareUnShareNonExistentCategory() { $this->assertEquals(Http::STATUS_BAD_REQUEST, $response2->getStatus()); } - public function testGetSharedCategories() { + public function testGetSharedCategories(): void { $categoryNames = ['test345456', 'test2345465', 'test65765']; $ids = []; foreach ($categoryNames as $categoryName) { - array_push( - $ids, - $this->favoritesController - ->addFavorite('Test', 0, 0, $categoryName, '', null) - ->getData()['id'] - ); + $ids[] = $this->favoritesController + ->addFavorite('Test', 0, 0, $categoryName, '', null) + ->getData()['id']; $this->favoritesController->shareCategory($categoryName); } @@ -560,9 +551,7 @@ public function testGetSharedCategories() { $this->assertIsArray($categories->getData()); - $mappedCategories = array_map(function ($el) { - return $el->getCategory(); - }, $categories->getData()); + $mappedCategories = array_map(fn ($el) => $el->getCategory(), $categories->getData()); foreach ($categoryNames as $categoryName) { $this->assertContains($categoryName, $mappedCategories); @@ -577,7 +566,7 @@ public function testGetSharedCategories() { } } - public function testFavoriteShareIsRenamedCorrectly() { + public function testFavoriteShareIsRenamedCorrectly(): void { $categoryName = 'test03059035'; $newCategoryName = 'test097876'; @@ -591,9 +580,7 @@ public function testFavoriteShareIsRenamedCorrectly() { $shares = $this->favoritesController->getSharedCategories()->getData(); - $shareNames = array_map(function ($el) { - return $el->getCategory(); - }, $shares); + $shareNames = array_map(fn ($el) => $el->getCategory(), $shares); $this->favoritesController->deleteFavorite($id); $this->favoritesController->unShareCategory($newCategoryName); diff --git a/tests/Unit/Controller/PageControllerTest.php b/tests/Unit/Controller/PageControllerTest.php index 3a24815c9..d9325f5eb 100644 --- a/tests/Unit/Controller/PageControllerTest.php +++ b/tests/Unit/Controller/PageControllerTest.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\IEventDispatcher; -use OCP\IConfig; +use OCP\IAppConfig; use OCP\IRequest; use OCP\IURLGenerator; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class PageControllerTest extends \PHPUnit\Framework\TestCase { +final class PageControllerTest extends TestCase { private PageController $controller; + private string $userId = 'john'; - private IConfig&MockObject $config; - private IInitialState&MockObject $initialState; - private IEventDispatcher&MockObject $eventDispatcher; - private IURLGenerator&MockObject $urlGenerator; protected function setUp(): void { - /** @var IRequest&MockObject */ $request = $this->createMock(IRequest::class); - $this->config = $this->createMock(IConfig::class); - $this->initialState = $this->createMock(IInitialState::class); - $this->eventDispatcher = $this->createMock(IEventDispatcher::class); - $this->urlGenerator = $this->createMock(IURLGenerator::class); + $appConfig = $this->createMock(IAppConfig::class); + $initialState = $this->createMock(IInitialState::class); + $eventDispatcher = $this->createMock(IEventDispatcher::class); + $urlGenerator = $this->createMock(IURLGenerator::class); $this->controller = new PageController( 'maps', $request, $this->userId, - $this->eventDispatcher, - $this->config, - $this->initialState, - $this->urlGenerator, + $eventDispatcher, + $appConfig, + $initialState, + $urlGenerator, ); } - public function testIndex() { + public function testIndex(): void { $result = $this->controller->index(); $this->assertEquals('main', $result->getTemplateName()); - $this->assertTrue($result instanceof TemplateResponse); + $this->assertInstanceOf(TemplateResponse::class, $result); } - public function testOpenGeoLink() { + public function testOpenGeoLink(): void { $result = $this->controller->openGeoLink('geo:1.1,2.2'); $this->assertEquals('main', $result->getTemplateName()); - $this->assertTrue($result instanceof TemplateResponse); + $this->assertInstanceOf(TemplateResponse::class, $result); } } diff --git a/tests/Unit/Controller/PhotosControllerTest.php b/tests/Unit/Controller/PhotosControllerTest.php index ec491104b..588251e9a 100644 --- a/tests/Unit/Controller/PhotosControllerTest.php +++ b/tests/Unit/Controller/PhotosControllerTest.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\AppInfo\Application; use OCA\Maps\DB\GeophotoMapper; use OCA\Maps\Service\GeophotoService; use OCA\Maps\Service\PhotofilesService; +use OCP\BackgroundJob\IJobList; use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IServerContainer; +use OCP\Files\IRootFolder; +use OCP\ICacheFactory; +use OCP\IDBConnection; +use OCP\IGroupManager; +use OCP\IUserManager; +use OCP\L10N\IFactory; use OCP\Server; +use OCP\Share\IManager; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; -class PhotosControllerTest extends \PHPUnit\Framework\TestCase { - private $appName; - private $request; - private $contacts; +final class PhotosControllerTest extends TestCase { private $container; - private $config; - private $app; - private $photosController; - private $photosController2; - private $utilsController; - private $photoFileService; + private PhotosController $photosController; + + private PhotofilesService $photoFileService; + private $GeoPhotosService; public static function setUpBeforeClass(): void { $app = new Application(); $c = $app->getContainer(); - $user = $c->getServer()->getUserManager()->get('test'); - $user2 = $c->getServer()->getUserManager()->get('test2'); - $user3 = $c->getServer()->getUserManager()->get('test3'); - $group = $c->getServer()->getGroupManager()->get('group1test'); - $group2 = $c->getServer()->getGroupManager()->get('group2test'); + $user = $c->get(IUserManager::class)->get('test'); + $user2 = $c->get(IUserManager::class)->get('test2'); + $user3 = $c->get(IUserManager::class)->get('test3'); + $group = $c->get(IGroupManager::class)->get('group1test'); + $group2 = $c->get(IGroupManager::class)->get('group2test'); // CREATE DUMMY USERS if ($user === null) { - $u1 = $c->getServer()->getUserManager()->createUser('test', 'tatotitoTUTU'); + $u1 = $c->get(IUserManager::class)->createUser('test', 'tatotitoTUTU'); $u1->setEMailAddress('toto@toto.net'); } + if ($user2 === null) { - $u2 = $c->getServer()->getUserManager()->createUser('test2', 'plopinoulala000'); + $c->get(IUserManager::class)->createUser('test2', 'plopinoulala000'); } - if ($user2 === null) { - $u3 = $c->getServer()->getUserManager()->createUser('test3', 'yeyeahPASSPASS'); + + if ($user3 === null) { + $c->get(IUserManager::class)->createUser('test3', 'yeyeahPASSPASS'); } + if ($group === null) { - $c->getServer()->getGroupManager()->createGroup('group1test'); - $u1 = $c->getServer()->getUserManager()->get('test'); - $c->getServer()->getGroupManager()->get('group1test')->addUser($u1); + $c->get(IGroupManager::class)->createGroup('group1test'); + $u1 = $c->get(IUserManager::class)->get('test'); + $c->get(IGroupManager::class)->get('group1test')->addUser($u1); } + if ($group2 === null) { - $c->getServer()->getGroupManager()->createGroup('group2test'); - $u2 = $c->getServer()->getUserManager()->get('test2'); - $c->getServer()->getGroupManager()->get('group2test')->addUser($u2); + $c->get(IGroupManager::class)->createGroup('group2test'); + $u2 = $c->get(IUserManager::class)->get('test2'); + $c->get(IGroupManager::class)->get('group2test')->addUser($u2); } } protected function setUp(): void { - $this->appName = 'maps'; - $this->request = $this->getMockBuilder('\OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - $this->contacts = $this->getMockBuilder('OCP\Contacts\IManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->app = new Application(); - $this->container = $this->app->getContainer(); + $appName = 'maps'; + $request = $this->createMock('\OCP\IRequest'); + + $app = new Application(); + $this->container = $app->getContainer(); $c = $this->container; - $this->config = $c->query(IServerContainer::class)->getConfig(); - $this->rootFolder = $c->query(IServerContainer::class)->getRootFolder(); + $rootFolder = $c->get(IRootFolder::class); - $this->GeoPhotosService = $c->query(GeoPhotoService::class); + $this->GeoPhotosService = $c->get(GeoPhotoService::class); $this->photoFileService = new PhotoFilesService( - $c->query(IServerContainer::class)->get(\Psr\Log\LoggerInterface::class), - $c->query(IServerContainer::class)->getMemCacheFactory(), - $this->rootFolder, - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(GeophotoMapper::class), - $c->query(IServerContainer::class)->get(\OCP\Share\IManager::class), - $c->query(\OCP\BackgroundJob\IJobList::class) + $c->get(LoggerInterface::class), + $c->get(ICacheFactory::class), + $rootFolder, + $c->get(IFactory::class)->get($c->get('AppName')), + $c->get(GeophotoMapper::class), + $c->get(IManager::class), + $c->get(IJobList::class) ); $this->photosController = new PhotosController( - $this->appName, - $this->request, + $appName, + $request, $this->GeoPhotosService, $this->photoFileService, - $this->rootFolder, - 'test' - ); - - $this->photosController2 = new PhotosController( - $this->appName, - $this->request, - $c->query(GeoPhotoService::class), - $this->photoFileService, - $this->rootFolder, - 'test2' - ); - - $this->utilsController = new UtilsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->getAppManager(), - $this->rootFolder, + $rootFolder, 'test' ); - $userfolder = $this->container->query(IServerContainer::class)->getUserFolder('test'); + $userfolder = $this->container->get(IRootFolder::class)->getUserFolder('test'); // delete files if ($userfolder->nodeExists('nc.jpg')) { $file = $userfolder->get('nc.jpg'); $file->delete(); } + if ($userfolder->nodeExists('nut.jpg')) { $file = $userfolder->get('nut.jpg'); $file->delete(); } + // delete db - $qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder(); + $qb = Server::get(IDBConnection::class)->getQueryBuilder(); $qb->delete('maps_photos') ->where( $qb->expr()->eq('user_id', $qb->createNamedParameter('test', IQueryBuilder::PARAM_STR)) ); - $req = $qb->executeStatement(); + $qb->executeStatement(); } public static function tearDownAfterClass(): void { //$app = new Application(); //$c = $app->getContainer(); - //$user = $c->getServer()->getUserManager()->get('test'); + //$user = $c->get(IUserManager::class)->get('test'); //$user->delete(); - //$user = $c->getServer()->getUserManager()->get('test2'); + //$user = $c->get(IUserManager::class)->get('test2'); //$user->delete(); - //$user = $c->getServer()->getUserManager()->get('test3'); + //$user = $c->get(IUserManager::class)->get('test3'); //$user->delete(); - //$c->getServer()->getGroupManager()->get('group1test')->delete(); - //$c->getServer()->getGroupManager()->get('group2test')->delete(); + //$c->get(IGroupManager::class)->get('group1test')->delete(); + //$c->get(IGroupManager::class)->get('group2test')->delete(); } protected function tearDown(): void { // in case there was a failure and something was not deleted } - public function testAddGetPhotos() { - $c = $this->app->getContainer(); - - $userfolder = $this->container->query(IServerContainer::class)->getUserFolder('test'); + public function testAddGetPhotos(): void { + $userfolder = $this->container->get(IRootFolder::class)->getUserFolder('test'); $filename = 'tests/test_files/nc.jpg'; $handle = fopen($filename, 'rb'); @@ -179,6 +164,7 @@ public function testAddGetPhotos() { $file->move($userfolder->getPath() . '/nc.jpg'); $file = $userfolder->get('nc.jpg'); $file->touch(); + $this->photoFileService->addPhotoNow($file, 'test'); $filename = 'tests/test_files/nut.jpg'; @@ -231,11 +217,11 @@ public function testAddGetPhotos() { //Test .maps respected $this->GeoPhotosService->clearCache(); $file = $userfolder->newFile('.index.maps'); - $resp = $this->photosController->getPhotos(null, null, true); + $resp = $this->photosController->getPhotos(null, true, true); $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $this->assertEquals(0, count($data)); + $this->assertCount(0, $data); $file->delete(); // non localized without track @@ -297,7 +283,7 @@ public function testAddGetPhotos() { $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $this->assertEquals(0, count($data)); + $this->assertCount(0, $data); $file->delete(); //Test myMap @@ -307,7 +293,7 @@ public function testAddGetPhotos() { $status = $resp->getStatus(); $this->assertEquals(200, $status); $data = $resp->getData(); - $this->assertEquals(0, count($data)); + $this->assertCount(0, $data); $file->delete(); // place photos diff --git a/tests/Unit/Controller/PublicFavoritePageControllerTest.php b/tests/Unit/Controller/PublicFavoritePageControllerTest.php index dffc93f89..77ecf5bcc 100644 --- a/tests/Unit/Controller/PublicFavoritePageControllerTest.php +++ b/tests/Unit/Controller/PublicFavoritePageControllerTest.php @@ -1,5 +1,7 @@ * @@ -21,85 +23,74 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\Controller; -use OC; -use OC\AppFramework\Http; use OCA\Maps\AppInfo\Application; use OCA\Maps\DB\FavoriteShareMapper; use OCA\Maps\Service\FavoritesService; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\TemplateResponse; -use OCP\AppFramework\IAppContainer; -use OCP\IServerContainer; +use OCP\Files\IRootFolder; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IRequest; +use OCP\ISession; +use OCP\L10N\IFactory; +use OCP\Security\ISecureRandom; +use OCP\Server; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; -class PublicFavoritePageControllerTest extends TestCase { - /* @var PublicFavoritePageController */ - private $publicPageController; - - private $config; +final class PublicFavoritePageControllerTest extends TestCase { + private PublicFavoritePageController $publicPageController; - /* @var Application */ - private $app; + private FavoritesService $favoritesService; - /* @var IAppContainer */ - private $container; - - /* @var FavoritesService */ - private $favoritesService; - - /* @var FavoriteShareMapper */ - private $favoriteShareMapper; + private FavoriteShareMapper $favoriteShareMapper; protected function setUp(): void { // Begin transaction - $db = \OCP\Server::get(\OCP\IDBConnection::class); + $db = Server::get(IDBConnection::class); $db->beginTransaction(); - $this->app = new Application(); + $app = new Application(); - $this->container = $this->app->getContainer(); - $container = $this->container; + $container = $app->getContainer(); - $appName = $container->query('appName'); + $appName = $container->get('appName'); $this->favoritesService = new FavoritesService( - $container->query(IServerContainer::class)->get(LoggerInterface::class), - $container->query(IServerContainer::class)->getL10N($appName), - $container->query(IServerContainer::class)->getSecureRandom(), - $container->query(\OCP\IDBConnection::class) + $container->get(LoggerInterface::class), + $container->get(IFactory::class)->get($appName), + $container->get(IDBConnection::class) ); $this->favoriteShareMapper = new FavoriteShareMapper( - $container->query(\OCP\IDBConnection::class), - $container->query(IServerContainer::class)->getSecureRandom(), - $container->query(IServerContainer::class)->getRootFolder() + $container->get(IDBConnection::class), + $container->get(ISecureRandom::class), + $container->get(IRootFolder::class) ); - $requestMock = $this->getMockBuilder('OCP\IRequest')->getMock(); - $sessionMock = $this->getMockBuilder('OCP\ISession')->getMock(); - - $this->config = $container->query(IServerContainer::class)->getConfig(); + $requestMock = $this->createMock(IRequest::class); + $sessionMock = $this->createMock(ISession::class); $this->publicPageController = new PublicFavoritePageController( $appName, $requestMock, $sessionMock, - $this->config, + $container->get(IConfig::class), $this->favoriteShareMapper ); } protected function tearDown(): void { // Rollback transaction - $db = OC::$server->query(\OCP\IDBConnection::class); + $db = Server::get(IDBConnection::class); $db->rollBack(); } - public function testSharedFavoritesCategory() { + public function testSharedFavoritesCategory(): void { $categoryName = 'test908780'; $testUserName = 'test'; @@ -110,14 +101,14 @@ public function testSharedFavoritesCategory() { $result = $this->publicPageController->sharedFavoritesCategory($share->getToken()); // Assertions - $this->assertTrue($result instanceof TemplateResponse); + $this->assertInstanceOf(TemplateResponse::class, $result); $this->assertEquals('public/favorites_index', $result->getTemplateName()); } - public function testAccessRestrictionsForSharedFavoritesCategory() { + public function testAccessRestrictionsForSharedFavoritesCategory(): void { $result = $this->publicPageController->sharedFavoritesCategory('test8348985'); - $this->assertTrue($result instanceof DataResponse); + $this->assertInstanceOf(DataResponse::class, $result); $this->assertEquals(Http::STATUS_NOT_FOUND, $result->getStatus()); } } diff --git a/tests/Unit/Controller/PublicFavoritesApiControllerTest.php b/tests/Unit/Controller/PublicFavoritesApiControllerTest.php index 682832795..655abc519 100644 --- a/tests/Unit/Controller/PublicFavoritesApiControllerTest.php +++ b/tests/Unit/Controller/PublicFavoritesApiControllerTest.php @@ -1,5 +1,7 @@ * @@ -21,59 +23,53 @@ * along with this program. If not, see . * */ - namespace OCA\Maps\Controller; -use OC; -use OC\AppFramework\Http; use OCA\Maps\AppInfo\Application; -use OCA\Maps\DB\FavoriteShare; use OCA\Maps\DB\FavoriteShareMapper; use OCA\Maps\Service\FavoritesService; -use OCP\IServerContainer; +use OCP\AppFramework\Http; +use OCP\Files\IRootFolder; +use OCP\IDBConnection; +use OCP\IRequest; +use OCP\ISession; +use OCP\L10N\IFactory; +use OCP\Security\ISecureRandom; +use OCP\Server; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; -class PublicFavoritesApiControllerTest extends TestCase { - /* @var PublicFavoritesApiController */ - private $publicFavoritesApiController; - - private $config; +final class PublicFavoritesApiControllerTest extends TestCase { + private PublicFavoritesApiController $publicFavoritesApiController; - /* @var FavoritesService */ - private $favoritesService; + private FavoritesService $favoritesService; - /* @var FavoriteShareMapper */ - private $favoriteShareMapper; + private FavoriteShareMapper $favoriteShareMapper; protected function setUp(): void { // Begin transaction - $db = OC::$server->query(\OCP\IDBConnection::class); + $db = Server::get(IDBConnection::class); $db->beginTransaction(); $container = (new Application())->getContainer(); - $appName = $container->query('AppName'); - - $requestMock = $this->getMockBuilder('OCP\IRequest')->getMock(); - $sessionMock = $this->getMockBuilder('OCP\ISession')->getMock(); - - $this->config = $container->query(IServerContainer::class)->getConfig(); + $requestMock = $this->createMock(IRequest::class); + $sessionMock = $this->createMock(ISession::class); $this->favoritesService = new FavoritesService( - $container->query(IServerContainer::class)->get(\Psr\Log\LoggerInterface::class), - $container->query(IServerContainer::class)->getL10N($appName), - $container->query(IServerContainer::class)->getSecureRandom(), - $container->query(\OCP\IDBConnection::class) + $container->get(LoggerInterface::class), + $container->get(IFactory::class)->get('maps'), + $container->get(IDBConnection::class) ); $this->favoriteShareMapper = new FavoriteShareMapper( - $container->query(\OCP\IDBConnection::class), - $container->query(IServerContainer::class)->getSecureRandom(), - $container->query(IserverContainer::class)->getRootFolder() + $container->get(IDBConnection::class), + $container->get(ISecureRandom::class), + $container->get(IRootFolder::class), ); $this->publicFavoritesApiController = new PublicFavoritesApiController( - $appName, + 'maps', $requestMock, $sessionMock, $this->favoritesService, @@ -83,18 +79,17 @@ protected function setUp(): void { protected function tearDown(): void { // Rollback transaction - $db = OC::$server->query(\OCP\IDBConnection::class); + $db = Server::get(IDBConnection::class); $db->rollBack(); } - public function testGetFavorites() { + public function testGetFavorites(): void { $testUser = 'test099897'; $categoryName = 'test89774590'; $this->favoritesService ->addFavoriteToDB($testUser, 'Test1', 0, 0, $categoryName, '', null); - /* @var FavoriteShare */ $share = $this->favoriteShareMapper->create($testUser, $categoryName); // Mock token sent by request @@ -114,7 +109,7 @@ public function testGetFavorites() { $this->assertEquals($categoryName, $data['share']->getCategory()); $this->assertEquals($share->getToken(), $data['share']->getToken()); - $this->assertEquals(1, count($data['favorites'])); + $this->assertCount(1, $data['favorites']); $el = $data['favorites'][0]; $this->assertEquals('Test1', $el['name']); diff --git a/tests/Unit/Controller/TracksControllerTest.php b/tests/Unit/Controller/TracksControllerTest.php index 1d8bd3fe9..94046bea2 100644 --- a/tests/Unit/Controller/TracksControllerTest.php +++ b/tests/Unit/Controller/TracksControllerTest.php @@ -1,5 +1,7 @@ * @copyright Julien Veyssier 2019 */ - namespace OCA\Maps\Controller; use OCA\Maps\AppInfo\Application; use OCA\Maps\Service\TracksService; use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IServerContainer; +use OCP\Files\IRootFolder; +use OCP\IDBConnection; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IUserManager; use OCP\Server; +use OCP\Share\IManager as IShareManager; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; -class TracksControllerTest extends \PHPUnit\Framework\TestCase { - private $appName; - private $request; - private $contacts; +final class TracksControllerTest extends TestCase { + private IRootFolder $rootFolder; - private $container; - private $config; - private $app; + private Application $app; - private $tracksController; - private $tracksController2; - private $utilsController; + private TracksController $tracksController; - private $tracksService; + private TracksService $tracksService; public static function setUpBeforeClass(): void { $app = new Application(); $c = $app->getContainer(); - $user = $c->getServer()->getUserManager()->get('test'); - $user2 = $c->getServer()->getUserManager()->get('test2'); - $user3 = $c->getServer()->getUserManager()->get('test3'); - $group = $c->getServer()->getGroupManager()->get('group1test'); - $group2 = $c->getServer()->getGroupManager()->get('group2test'); + $user1 = $c->get(IUserManager::class)->get('test'); + $user2 = $c->get(IUserManager::class)->get('test2'); + $user3 = $c->get(IUserManager::class)->get('test3'); + $group = $c->get(IGroupManager::class)->get('group1test'); + $group2 = $c->get(IGroupManager::class)->get('group2test'); // CREATE DUMMY USERS - if ($user === null) { - $u1 = $c->getServer()->getUserManager()->createUser('test', 'tatotitoTUTU'); - $u1->setEMailAddress('toto@toto.net'); + if ($user1 === null) { + $user1 = $c->get(IUserManager::class)->createUser('test', 'tatotitoTUTU'); + $user1->setEMailAddress('toto@toto.net'); } + if ($user2 === null) { - $u2 = $c->getServer()->getUserManager()->createUser('test2', 'plopinoulala000'); + $user2 = $c->get(IUserManager::class)->createUser('test2', 'plopinoulala000'); } - if ($user2 === null) { - $u3 = $c->getServer()->getUserManager()->createUser('test3', 'yeyeahPASSPASS'); + + if ($user3 === null) { + $user3 = $c->get(IUserManager::class)->createUser('test3', 'yeyeahPASSPASS'); } + if ($group === null) { - $c->getServer()->getGroupManager()->createGroup('group1test'); - $u1 = $c->getServer()->getUserManager()->get('test'); - $c->getServer()->getGroupManager()->get('group1test')->addUser($u1); + $c->get(IGroupManager::class)->createGroup('group1test'); + $c->get(IGroupManager::class)->get('group1test')->addUser($user1); } + if ($group2 === null) { - $c->getServer()->getGroupManager()->createGroup('group2test'); - $u2 = $c->getServer()->getUserManager()->get('test2'); - $c->getServer()->getGroupManager()->get('group2test')->addUser($u2); + $c->get(IGroupManager::class)->createGroup('group2test'); + $c->get(IGroupManager::class)->get('group2test')->addUser($user2); } } protected function setUp(): void { - $this->appName = 'maps'; - $this->request = $this->getMockBuilder('\OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - $this->contacts = $this->getMockBuilder('OCP\Contacts\IManager') - ->disableOriginalConstructor() - ->getMock(); + $appName = 'maps'; + $request = $this->createMock(IRequest::class); $this->app = new Application(); - $this->container = $this->app->getContainer(); - $c = $this->container; - $this->config = $c->query(IServerContainer::class)->getConfig(); + $container = $this->app->getContainer(); + $c = $container; - $this->rootFolder = $c->query(IServerContainer::class)->getRootFolder(); + $this->rootFolder = $c->get(IRootFolder::class); $this->tracksService = new TracksService( - $c->query(IServerContainer::class)->get(\Psr\Log\LoggerInterface::class), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), + $c->get(LoggerInterface::class), $this->rootFolder, - $c->query(IServerContainer::class)->get(\OCP\Share\IManager::class), - $c->query(IServerContainer::class)->query(\OCP\IDBConnection::class) + $c->get(IShareManager::class), + $c->get(IDBConnection::class) ); $this->tracksController = new TracksController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->query(IServerContainer::class)->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(TracksService::class), + $appName, + $request, + $c->get(IL10N::class), + $c->get(TracksService::class), + $c->get(IRootFolder::class), 'test', ); - $this->tracksController2 = new TracksController( - $this->appName, - $this->request, - $c->query(IServerContainer::class), - $c->query(IServerContainer::class)->getConfig(), - $c->query(IServerContainer::class)->get(\OCP\Share\IManager::class), - $c->getServer()->getAppManager(), - $c->getServer()->getUserManager(), - $c->getServer()->getGroupManager(), - $c->query(IServerContainer::class)->getL10N($c->query('AppName')), - $c->query(TracksService::class), - 'test2', - ); - - $this->utilsController = new UtilsController( - $this->appName, - $this->request, - $c->query(IServerContainer::class)->getConfig(), - $c->getServer()->getAppManager(), - $this->rootFolder, - 'test' - ); - - $userfolder = $this->container->query(IServerContainer::class)->getUserFolder('test'); + $userfolder = $this->rootFolder->getUserFolder('test'); // delete first if ($userfolder->nodeExists('testFile1.gpx')) { @@ -135,51 +105,51 @@ protected function setUp(): void { $file = $userfolder->get('testFile1.gpx'); $file->delete(); } + // delete db - $qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder(); + $qb = Server::get(IDBConnection::class)->getQueryBuilder(); $qb->delete('maps_tracks') ->where( $qb->expr()->eq('user_id', $qb->createNamedParameter('test', IQueryBuilder::PARAM_STR)) ); - $req = $qb->executeStatement(); + $qb->executeStatement(); } public static function tearDownAfterClass(): void { //$app = new Application(); //$c = $app->getContainer(); - //$user = $c->getServer()->getUserManager()->get('test'); + //$user = $c->get(IUserManager::class)->get('test'); //$user->delete(); - //$user = $c->getServer()->getUserManager()->get('test2'); + //$user = $c->get(IUserManager::class)->get('test2'); //$user->delete(); - //$user = $c->getServer()->getUserManager()->get('test3'); + //$user = $c->get(IUserManager::class)->get('test3'); //$user->delete(); - //$c->getServer()->getGroupManager()->get('group1test')->delete(); - //$c->getServer()->getGroupManager()->get('group2test')->delete(); + //$c->get(IGroupManager::class)->get('group1test')->delete(); + //$c->get(IGroupManager::class)->get('group2test')->delete(); } protected function tearDown(): void { // in case there was a failure and something was not deleted - $c = $this->app->getContainer(); + $this->app->getContainer(); - $userfolder = $this->container->query(IServerContainer::class)->getUserFolder('test'); + $userfolder = $this->rootFolder->getUserFolder('test'); // delete files if ($userfolder->nodeExists('testFile1.gpx')) { $file = $userfolder->get('testFile1.gpx'); $file->delete(); } + // delete db - $qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder(); + $qb = Server::get(IDBConnection::class)->getQueryBuilder(); $qb->delete('maps_tracks') ->where( $qb->expr()->eq('user_id', $qb->createNamedParameter('test', IQueryBuilder::PARAM_STR)) ); - $req = $qb->executeStatement(); + $qb->executeStatement(); } - public function testAddGetTracks() { - $c = $this->app->getContainer(); - - $userfolder = $this->container->query(IServerContainer::class)->getUserFolder('test'); + public function testAddGetTracks(): void { + $userfolder = $this->rootFolder->getUserFolder('test'); $filename = 'tests/test_files/testFile1.gpx'; $content1 = file_get_contents($filename); @@ -198,14 +168,15 @@ public function testAddGetTracks() { $this->assertEquals(200, $status); $data = $resp->getData(); $foundTestFile = false; - foreach ($data as $k => $v) { + foreach ($data as $v) { if ($v['file_path'] === '/testFile1.gpx') { $foundTestFile = true; break; } } - $this->assertEquals(true, count($data) > 0); - $this->assertEquals(true, $foundTestFile); + + $this->assertGreaterThan(0, count($data)); + $this->assertTrue($foundTestFile); foreach ($this->tracksService->rescan('test') as $path) { //echo $path."\n"; @@ -218,7 +189,7 @@ public function testAddGetTracks() { $foundTestFile = false; //var_dump($data); $trackId = null; - foreach ($data as $k => $v) { + foreach ($data as $v) { if ($v['file_path'] === '/testFile1.gpx') { $foundTestFile = true; $trackId = $v['id']; @@ -226,6 +197,7 @@ public function testAddGetTracks() { break; } } + $this->assertEquals(true, count($data) > 0); $this->assertEquals(true, $foundTestFile); @@ -236,7 +208,7 @@ public function testAddGetTracks() { $data = $resp->getData(); $this->assertEquals(true, $content1 === $data['content']); $meta = $data['metadata']; - $this->assertEquals(true, strlen($meta) > 0); + $this->assertEquals(true, (string)$meta !== ''); // to get stored metadata $resp = $this->tracksController->getTrackFileContent($trackId); @@ -266,13 +238,14 @@ public function testAddGetTracks() { $this->assertEquals(200, $status); $data = $resp->getData(); $foundTestFile = false; - foreach ($data as $k => $v) { + foreach ($data as $v) { if ($v['file_path'] === '/testFile1.gpx') { $foundTestFile = true; $this->assertEquals(true, $v['color'] === '#002244'); break; } } + $this->assertEquals(true, count($data) > 0); $this->assertEquals(true, $foundTestFile); @@ -283,5 +256,4 @@ public function testAddGetTracks() { $data = $resp->getData(); $this->assertEquals('No such track', $data); } - } diff --git a/tests/Unit/Helper/ExifGeoDataTest.php b/tests/Unit/Helper/ExifGeoDataTest.php index 29d3c35d0..fbfc5a6d2 100644 --- a/tests/Unit/Helper/ExifGeoDataTest.php +++ b/tests/Unit/Helper/ExifGeoDataTest.php @@ -1,51 +1,55 @@ > + */ + public static function imageWithDateAndLocationProvider(): \Iterator { + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation1.JPG', 1311984000 + 7200, 47.071717, 10.339557]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation2.JPG', 1312156800 + 7200, 46.862350, 10.916452]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation3.JPG', 1312070400 + 7200, 47.069058, 10.329370]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation4.JPG', 1312070400 + 7200, 47.059160, 10.312354]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation5.JPG', 1568101093 + 7200, 47.357735, 11.177585]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation6.JPG', 1577630208 + 3600, 50.083045, 9.986018]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation7.jpg', 1568999599 + 7200, 49.420833, 11.114444]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation8.JPG', 1501431401 + 7200, 45.306983, 10.700902]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation9.JPG', 1302998400 + 7200, 52.363055, 4.903418]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation10.JPG', 1501238375 + 7200, 46.388742, 11.266598]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation11.JPG', 1501567361 + 7200, 44.827830, 10.956387]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation12.JPG', 1501591333 + 7200, 44.528283, 11.262207]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation13.jpg', 1640083235 + 3600, 54.359561, 10.017325]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation14.jpg', 1559327910 + 7200, 52.976844, 12.988281]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation15.jpg', 1559332394 + 7200, 52.983697, 12.935217]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation16.jpeg', 1593458542 + 7200, 62.733947, 6.779617]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation17.jpeg', 1593458620 + 7200, 62.733769, 6.777794]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation18.jpeg', 1596136867 + 7200, 54.350891, 9.903506]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation19.jpeg', 1596136833 + 7200, 54.350894, 9.903505]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation20.jpeg', 1592913150 + 7200, 61.351753, 6.519107]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation21.jpg', 1653565075 + 7200, 48.704331, 8.418475]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation22.jpeg', 1593890841 + 7200, 62.735419, 7.155311]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation23.jpeg', 1592904886 + 7200, 61.217086, 6.558886]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation24.jpeg', 1592677991 + 7200, 60.427481, 6.548446]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation25.jpeg', 1592650395 + 7200, 59.860523, 6.696346]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation26.jpeg', 1592770386 + 7200, 60.594022, 6.581317]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation27.jpeg', 1592654095 + 7200, 60.033561, 6.563068]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation28.jpg', 1595326357 + 7200, 59.852992, 6.714458]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation29.jpg', 1594918175 + 7200, 57.595925, 9.976864]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation30.jpg', 1595418724 + 7200, 60.669492, 6.807386]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation31.jpg', 1594934141 + 7200, 57.801164, 8.314269]; + yield ['tests/test_files/Photos/WithDateAndLocation/imageWithDateAndLocation32.jpeg', 1595629060 + 7200, 59.598981, 9.677297]; } + /** * @dataProvider imageWithDateAndLocationProvider */ - public function testImagesWithDateAndLocation(string $path, int $date, float $lat, float $lng) { + public function testImagesWithDateAndLocation(string $path, int $date, float $lat, float $lng): void { $exif_geo_data = ExifGeoData::get($path); $exif_geo_data->validate(true); $this->assertEquals($date, $exif_geo_data->dateTaken); @@ -54,15 +58,17 @@ public function testImagesWithDateAndLocation(string $path, int $date, float $la $this->assertEqualsWithDelta($lng, $exif_geo_data->lng, 0.000005); } - public static function imageWithZeroIslandProvider(): array { - return [ - ['tests/test_files/Photos/ZeroIsland/imageZeroIsland1.JPG', 1653829180 + 7200], - ]; + /** + * @return \Iterator> + */ + public static function imageWithZeroIslandProvider(): \Iterator { + yield ['tests/test_files/Photos/ZeroIsland/imageZeroIsland1.JPG', 1653829180 + 7200]; } + /** * @dataProvider imageWithZeroIslandProvider */ - public function testImagesWithZeroIslandException(string $path, int $date) { + public function testImagesWithZeroIslandException(string $path, int $date): void { $this->expectException(ExifDataNoLocationException::class); $this->expectExceptionMessage('Zero island is not valid'); $exif_geo_data = ExifGeoData::get($path); @@ -72,12 +78,12 @@ public function testImagesWithZeroIslandException(string $path, int $date) { /** * @dataProvider imageWithZeroIslandProvider */ - public function testImagesWithZeroIslandGeoDataNull(string $path, int $date) { + public function testImagesWithZeroIslandGeoDataNull(string $path, int $date): void { $exif_geo_data = ExifGeoData::get($path); try { $exif_geo_data->validate(true); $this->assertEquals(true, false); - } catch (ExifDataNoLocationException $e) { + } catch (ExifDataNoLocationException) { $this->assertEquals($date, $exif_geo_data->dateTaken); //This is the same upto ~55cm $this->assertEqualsWithDelta(null, $exif_geo_data->lat, 0.000005); @@ -88,7 +94,7 @@ public function testImagesWithZeroIslandGeoDataNull(string $path, int $date) { /** * @dataProvider imageWithZeroIslandProvider */ - public function testImagesWithZeroIsland(string $path, int $date) { + public function testImagesWithZeroIsland(string $path, int $date): void { $exif_geo_data = ExifGeoData::get($path); $exif_geo_data->validate(false); $this->assertEquals($date, $exif_geo_data->dateTaken); diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index a91e4890a..d907e32b3 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -1,5 +1,10 @@ - + + + + + + @@ -11,6 +16,10 @@ + + + + @@ -31,8 +40,23 @@ + + + + + + + + + + + + + + + - + @@ -45,29 +69,24 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + trk]]> + + + + + wpt]]> + + diff --git a/tests/stubs/oc_archive_archive.php b/tests/stubs/oc_archive_archive.php index f481d0545..4bdfaa68b 100644 --- a/tests/stubs/oc_archive_archive.php +++ b/tests/stubs/oc_archive_archive.php @@ -1,5 +1,7 @@ $acl - * @return list - */ - public function applyShareAcl(int $addressBookId, array $acl): array + * For shared address books the sharee is set in the ACL of the address book + * + * @param list $acl + * @return list + */ + public function applyShareAcl(int $addressBookId, array $acl): array { } diff --git a/tests/stubs/oca_dav_events_cardcreatedevent.php b/tests/stubs/oca_dav_events_cardcreatedevent.php index 7238d46b6..c9d65b321 100644 --- a/tests/stubs/oca_dav_events_cardcreatedevent.php +++ b/tests/stubs/oca_dav_events_cardcreatedevent.php @@ -19,47 +19,30 @@ class CardCreatedEvent extends Event { /** - * CardCreatedEvent constructor. - * - * @param int $addressBookId - * @param array $addressBookData - * @param array $shares - * @param array $cardData - * @since 20.0.0 - */ - public function __construct(int $addressBookId, array $addressBookData, array $shares, array $cardData) + * @since 20.0.0 + */ + public function getAddressBookId(): int { } /** - * @return int - * @since 20.0.0 - */ - public function getAddressBookId(): int + * @since 20.0.0 + */ + public function getAddressBookData(): array { } /** - * @return array - * @since 20.0.0 - */ - public function getAddressBookData(): array + * @since 20.0.0 + */ + public function getShares(): array { } /** - * @return array - * @since 20.0.0 - */ - public function getShares(): array - { - } - - /** - * @return array - * @since 20.0.0 - */ - public function getCardData(): array + * @since 20.0.0 + */ + public function getCardData(): array { } } diff --git a/tests/stubs/oca_dav_events_carddeletedevent.php b/tests/stubs/oca_dav_events_carddeletedevent.php index f1f9cd84b..1b3f2e158 100644 --- a/tests/stubs/oca_dav_events_carddeletedevent.php +++ b/tests/stubs/oca_dav_events_carddeletedevent.php @@ -19,47 +19,30 @@ class CardDeletedEvent extends Event { /** - * CardDeletedEvent constructor. - * - * @param int $addressBookId - * @param array $addressBookData - * @param array $shares - * @param array $cardData - * @since 20.0.0 - */ - public function __construct(int $addressBookId, array $addressBookData, array $shares, array $cardData) + * @since 20.0.0 + */ + public function getAddressBookId(): int { } /** - * @return int - * @since 20.0.0 - */ - public function getAddressBookId(): int + * @since 20.0.0 + */ + public function getAddressBookData(): array { } /** - * @return array - * @since 20.0.0 - */ - public function getAddressBookData(): array + * @since 20.0.0 + */ + public function getShares(): array { } /** - * @return array - * @since 20.0.0 - */ - public function getShares(): array - { - } - - /** - * @return array - * @since 20.0.0 - */ - public function getCardData(): array + * @since 20.0.0 + */ + public function getCardData(): array { } } diff --git a/tests/stubs/oca_dav_events_cardupdatedevent.php b/tests/stubs/oca_dav_events_cardupdatedevent.php index 6bdfcaba5..d43c8e4cd 100644 --- a/tests/stubs/oca_dav_events_cardupdatedevent.php +++ b/tests/stubs/oca_dav_events_cardupdatedevent.php @@ -19,47 +19,30 @@ class CardUpdatedEvent extends Event { /** - * CardUpdatedEvent constructor. - * - * @param int $addressBookId - * @param array $addressBookData - * @param array $shares - * @param array $cardData - * @since 20.0.0 - */ - public function __construct(int $addressBookId, array $addressBookData, array $shares, array $cardData) + * @since 20.0.0 + */ + public function getAddressBookId(): int { } /** - * @return int - * @since 20.0.0 - */ - public function getAddressBookId(): int + * @since 20.0.0 + */ + public function getAddressBookData(): array { } /** - * @return array - * @since 20.0.0 - */ - public function getAddressBookData(): array + * @since 20.0.0 + */ + public function getShares(): array { } /** - * @return array - * @since 20.0.0 - */ - public function getShares(): array - { - } - - /** - * @return array - * @since 20.0.0 - */ - public function getCardData(): array + * @since 20.0.0 + */ + public function getCardData(): array { } } diff --git a/tests/stubs/oca_files_sharing_event_beforetemplaterenderedevent.php b/tests/stubs/oca_files_sharing_event_beforetemplaterenderedevent.php index 60b36908b..47d845735 100644 --- a/tests/stubs/oca_files_sharing_event_beforetemplaterenderedevent.php +++ b/tests/stubs/oca_files_sharing_event_beforetemplaterenderedevent.php @@ -23,13 +23,6 @@ class BeforeTemplateRenderedEvent extends Event { */ public const SCOPE_PUBLIC_SHARE_AUTH = 'publicShareAuth'; - /** - * @since 20.0.0 - */ - public function __construct(IShare $share, ?string $scope = null) - { - } - /** * @since 20.0.0 */ diff --git a/vendor-bin/psalm/composer.json b/vendor-bin/psalm/composer.json index d156c448e..dd96fec1d 100644 --- a/vendor-bin/psalm/composer.json +++ b/vendor-bin/psalm/composer.json @@ -6,7 +6,7 @@ }, "require-dev": { "doctrine/dbal": "^3.10.0", - "nextcloud/ocp": "dev-stable31", + "nextcloud/ocp": "dev-stable32", "sabre/dav": "^4.7.0", "symfony/console": "^6.4", "vimeo/psalm": "^6.10" diff --git a/vendor-bin/psalm/composer.lock b/vendor-bin/psalm/composer.lock index 1077c3b1c..69f0011ab 100644 --- a/vendor-bin/psalm/composer.lock +++ b/vendor-bin/psalm/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8ae0df6d06afb578f94b41e46fb6b4e5", + "content-hash": "8dcc41156ad04752614fc55994e6cab8", "packages": [], "packages-dev": [ { @@ -1833,16 +1833,16 @@ }, { "name": "nextcloud/ocp", - "version": "dev-stable31", + "version": "dev-stable32", "source": { "type": "git", "url": "https://github.com/nextcloud-deps/ocp.git", - "reference": "b369e02e33a1b9327eb3c4c5f639135b66e7bc8f" + "reference": "83cf79e2922fe1c969adeeac8dbb10e173a8d781" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/b369e02e33a1b9327eb3c4c5f639135b66e7bc8f", - "reference": "b369e02e33a1b9327eb3c4c5f639135b66e7bc8f", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/83cf79e2922fe1c969adeeac8dbb10e173a8d781", + "reference": "83cf79e2922fe1c969adeeac8dbb10e173a8d781", "shasum": "" }, "require": { @@ -1855,7 +1855,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-stable31": "31.0.0-dev" + "dev-stable32": "32.0.0-dev" } }, "notification-url": "https://packagist.org/downloads/", @@ -1875,7 +1875,7 @@ "description": "Composer package containing Nextcloud's public OCP API and the unstable NCU API", "support": { "issues": "https://github.com/nextcloud-deps/ocp/issues", - "source": "https://github.com/nextcloud-deps/ocp/tree/stable31" + "source": "https://github.com/nextcloud-deps/ocp/tree/stable32" }, "time": "2026-02-06T01:05:41+00:00" }, diff --git a/vendor-bin/rector/composer.json b/vendor-bin/rector/composer.json new file mode 100644 index 000000000..88f1e65e2 --- /dev/null +++ b/vendor-bin/rector/composer.json @@ -0,0 +1,6 @@ +{ + "require-dev": { + "rector/rector": "^2.0", + "nextcloud/rector": "^0.4.1" + } +} diff --git a/vendor-bin/rector/composer.lock b/vendor-bin/rector/composer.lock new file mode 100644 index 000000000..d053cd497 --- /dev/null +++ b/vendor-bin/rector/composer.lock @@ -0,0 +1,504 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "347262bc75027c88fa21b011f732aa31", + "packages": [], + "packages-dev": [ + { + "name": "nextcloud/ocp", + "version": "v32.0.5", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-deps/ocp.git", + "reference": "a79703d9f38e964b003ae1cc805b6531d142fa93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/a79703d9f38e964b003ae1cc805b6531d142fa93", + "reference": "a79703d9f38e964b003ae1cc805b6531d142fa93", + "shasum": "" + }, + "require": { + "php": "~8.1 || ~8.2 || ~8.3 || ~8.4", + "psr/clock": "^1.0", + "psr/container": "^2.0.2", + "psr/event-dispatcher": "^1.0", + "psr/log": "^3.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-stable32": "32.0.0-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + }, + { + "name": "Joas Schilling", + "email": "coding@schilljs.com" + } + ], + "description": "Composer package containing Nextcloud's public OCP API and the unstable NCU API", + "support": { + "issues": "https://github.com/nextcloud-deps/ocp/issues", + "source": "https://github.com/nextcloud-deps/ocp/tree/v32.0.5" + }, + "time": "2026-01-09T00:57:52+00:00" + }, + { + "name": "nextcloud/rector", + "version": "v0.4.1", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-libraries/rector.git", + "reference": "9c5c78cc323537ec6dba5b3cd9c422ff9524d8cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-libraries/rector/zipball/9c5c78cc323537ec6dba5b3cd9c422ff9524d8cf", + "reference": "9c5c78cc323537ec6dba5b3cd9c422ff9524d8cf", + "shasum": "" + }, + "require": { + "nextcloud/ocp": ">=27", + "php": "^8.1", + "rector/rector": "^2.0.4", + "webmozart/assert": "^1.11" + }, + "require-dev": { + "phpunit/phpunit": "^10.5", + "ramsey/devtools": "^2.0" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/devtools": { + "memory-limit": "-1", + "command-prefix": "dev" + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "OCP\\": "vendor/nextcloud/ocp/OCP", + "Nextcloud\\Rector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at", + "homepage": "https://wuc.me" + } + ], + "description": "Rector upgrade rules for Nextcloud", + "keywords": [ + "nextcloud", + "refactoring" + ], + "support": { + "issues": "https://github.com/nextcloud-libraries/rector/issues", + "source": "https://github.com/nextcloud-libraries/rector/tree/v0.4.1" + }, + "time": "2025-03-31T15:27:10+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.38", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629", + "reference": "dfaf1f530e1663aa167bc3e52197adb221582629", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-01-30T17:12:46+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "rector/rector", + "version": "2.3.6", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "ca9ebb81d280cd362ea39474dabd42679e32ca6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/ca9ebb81d280cd362ea39474dabd42679e32ca6b", + "reference": "ca9ebb81d280cd362ea39474dabd42679e32ca6b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.38" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.3.6" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2026-02-06T14:25:06+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^7.2 || ^8.0" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.12.1" + }, + "time": "2025-10-29T15:56:20+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +}