diff --git a/.eslintignore b/.eslintignore index 043716f45..aa1a45c5b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,4 @@ js/ l10n/ src/adminSettings.js -src/filetypes.js src/utils.js diff --git a/css/filetypes.css b/css/filetypes.css deleted file mode 100644 index f76d7b5e8..000000000 --- a/css/filetypes.css +++ /dev/null @@ -1,3 +0,0 @@ -.icon-maps-black { - background-image: url('images/maps_black.svg'); -} diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 4057dd3e9..2fd2ac676 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -13,23 +13,27 @@ 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\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; -use OCP\IInitialStateService; use OCP\IRequest; +use OCP\IURLGenerator; class PageController extends Controller { public function __construct( string $appName, IRequest $request, + private string $userId, private IEventDispatcher $eventDispatcher, private IConfig $config, - private IInitialStateService $initialStateService, - private string $userId, + private IInitialState $initialState, + private IURLGenerator $urlGenerator, ) { parent::__construct($appName, $request); } @@ -50,7 +54,7 @@ public function index(): TemplateResponse { $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer()); $params = ['user' => $this->userId]; - $this->initialStateService->provideInitialState($this->appName, 'photos', $this->config->getAppValue('photos', 'enabled', 'no') === 'yes'); + $this->initialState->provideInitialState('photos', $this->config->getAppValue('photos', 'enabled', 'no') === 'yes'); $response = new TemplateResponse('maps', 'main', $params); $this->addCsp($response); @@ -62,12 +66,21 @@ public function index(): TemplateResponse { * @NoAdminRequired * @NoCSRFRequired */ - public function indexMyMap($myMapId): TemplateResponse { + public function indexMyMap(int $myMapId, MyMapsService $service): TemplateResponse|RedirectResponse { + $map = $service->getMyMap($myMapId, $this->userId); + if ($map !== null && $map['id'] !== $myMapId) { + // Instead of the id of the map containing folder the '.index.maps' file id was passed so redirect + // this happens if coming from the files app integration + return new RedirectResponse( + $this->urlGenerator->linkToRouteAbsolute('maps.page.indexMyMap', ['myMapId' => $map['id']]), + ); + } + $this->eventDispatcher->dispatch(LoadSidebar::class, new LoadSidebar()); $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer()); $params = ['user' => $this->userId]; - $this->initialStateService->provideInitialState($this->appName, 'photos', $this->config->getAppValue('photos', 'enabled', 'no') === 'yes'); + $this->initialState->provideInitialState('photos', $this->config->getAppValue('photos', 'enabled', 'no') === 'yes'); $response = new TemplateResponse('maps', 'main', $params); $this->addCsp($response); diff --git a/lib/Listener/LoadAdditionalScriptsListener.php b/lib/Listener/LoadAdditionalScriptsListener.php index 134a405aa..83ad9a697 100644 --- a/lib/Listener/LoadAdditionalScriptsListener.php +++ b/lib/Listener/LoadAdditionalScriptsListener.php @@ -40,7 +40,6 @@ public function handle(Event $event): void { return; } - Util::addScript('maps', 'maps-filetypes'); - Util::addStyle('maps', 'filetypes'); + Util::addInitScript('maps', 'maps-init-files'); } } diff --git a/lib/Service/MyMapsService.php b/lib/Service/MyMapsService.php index 2f5c1b3ef..54286e467 100644 --- a/lib/Service/MyMapsService.php +++ b/lib/Service/MyMapsService.php @@ -148,6 +148,30 @@ public function getAllMyMaps($userId) { return $MyMaps; } + /** + * Try to lookup a my map by id + * + * @param int $id The map id to lookup + * @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) { + $userFolder = $this->root->getUserFolder($userId); + $node = $userFolder->getFirstNodeById($id); + if ($node instanceof Folder) { + try { + $node = $node->get('.index.maps'); + } catch (NotFoundException) { + return null; + } + } + + 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); @@ -158,7 +182,7 @@ public function updateMyMap($id, $values, $userId) { try { $file = $folder->get('.index.maps'); } catch (NotFoundException $e) { - $file = $folder->newFile('.index.maps', $content = '{}'); + $file = $folder->newFile('.index.maps', '{}'); } $mapData = json_decode($file->getContent(), true); $renamed = false; diff --git a/package-lock.json b/package-lock.json index 3d6a2c60a..3b43a69c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,12 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.7.2", "@maplibre/maplibre-gl-leaflet": "^0.1.2", + "@mdi/svg": "^7.4.47", "@nextcloud/auth": "^2.5.1", "@nextcloud/axios": "^2.5.1", "@nextcloud/dialogs": "^6.3.1", "@nextcloud/event-bus": "^3.3.1", + "@nextcloud/files": "^3.12.0", "@nextcloud/l10n": "^3.1.0", "@nextcloud/moment": "^1.3.4", "@nextcloud/paths": "^2.1.0", @@ -57,7 +59,9 @@ "@nextcloud/eslint-config": "^8.4.1", "@nextcloud/stylelint-config": "^3.1.0", "@nextcloud/webpack-vue-config": "^6.3.0", - "@types/leaflet": "^1.9.20" + "@types/leaflet": "^1.9.20", + "@vue/tsconfig": "^0.5.1", + "typescript": "^5.9.2" }, "engines": { "node": "^22.0.0", @@ -2098,6 +2102,12 @@ "version": "7.4.47", "license": "Apache-2.0" }, + "node_modules/@mdi/svg": { + "version": "7.4.47", + "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-7.4.47.tgz", + "integrity": "sha512-WQ2gDll12T9WD34fdRFgQVgO8bag3gavrAgJ0frN4phlwdJARpE6gO1YvLEMJR0KKgoc+/Ea/A0Pp11I00xBvw==", + "license": "Apache-2.0" + }, "node_modules/@nextcloud/auth": { "version": "2.5.2", "license": "GPL-3.0-or-later", @@ -2277,7 +2287,9 @@ } }, "node_modules/@nextcloud/files": { - "version": "3.11.0", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.12.0.tgz", + "integrity": "sha512-LVZklgooZzBj2jkbPRZO4jnnvW5+RvOn7wN5weyOZltF6i2wVMbg1Y/Czl2pi/UNMjUm5ENqc0j7FgxMBo8bwA==", "license": "AGPL-3.0-or-later", "dependencies": { "@nextcloud/auth": "^2.5.1", @@ -2362,6 +2374,8 @@ }, "node_modules/@nextcloud/sharing": { "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@nextcloud/sharing/-/sharing-0.2.5.tgz", + "integrity": "sha512-B3K5Dq9b5dexDA5n3AAuCF69Huwhrpw0J72fsVXV4KpPdImjhVPlExAv5o70AoXa+OqN4Rwn6gqJw+3ED892zg==", "license": "GPL-3.0-or-later", "dependencies": { "@nextcloud/initial-state": "^2.2.0" @@ -3307,6 +3321,13 @@ } } }, + "node_modules/@vue/tsconfig": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz", + "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@vueuse/components": { "version": "11.3.0", "license": "MIT", @@ -13538,10 +13559,11 @@ } }, "node_modules/typescript": { - "version": "5.8.3", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15993,6 +16015,11 @@ "@mdi/js": { "version": "7.4.47" }, + "@mdi/svg": { + "version": "7.4.47", + "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-7.4.47.tgz", + "integrity": "sha512-WQ2gDll12T9WD34fdRFgQVgO8bag3gavrAgJ0frN4phlwdJARpE6gO1YvLEMJR0KKgoc+/Ea/A0Pp11I00xBvw==" + }, "@nextcloud/auth": { "version": "2.5.2", "requires": { @@ -16087,7 +16114,9 @@ } }, "@nextcloud/files": { - "version": "3.11.0", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.12.0.tgz", + "integrity": "sha512-LVZklgooZzBj2jkbPRZO4jnnvW5+RvOn7wN5weyOZltF6i2wVMbg1Y/Czl2pi/UNMjUm5ENqc0j7FgxMBo8bwA==", "requires": { "@nextcloud/auth": "^2.5.1", "@nextcloud/capabilities": "^1.2.0", @@ -16139,6 +16168,8 @@ }, "@nextcloud/sharing": { "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@nextcloud/sharing/-/sharing-0.2.5.tgz", + "integrity": "sha512-B3K5Dq9b5dexDA5n3AAuCF69Huwhrpw0J72fsVXV4KpPdImjhVPlExAv5o70AoXa+OqN4Rwn6gqJw+3ED892zg==", "requires": { "@nextcloud/initial-state": "^2.2.0" } @@ -16766,6 +16797,12 @@ "vue-eslint-parser": "^9.3.1" } }, + "@vue/tsconfig": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz", + "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", + "dev": true + }, "@vueuse/components": { "version": "11.3.0", "requires": { @@ -23203,9 +23240,10 @@ } }, "typescript": { - "version": "5.8.3", - "devOptional": true, - "peer": true + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "devOptional": true }, "typescript-event-target": { "version": "1.1.1" diff --git a/package.json b/package.json index 880397e0d..bf7331ba0 100644 --- a/package.json +++ b/package.json @@ -38,10 +38,12 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.7.2", "@maplibre/maplibre-gl-leaflet": "^0.1.2", + "@mdi/svg": "^7.4.47", "@nextcloud/auth": "^2.5.1", "@nextcloud/axios": "^2.5.1", "@nextcloud/dialogs": "^6.3.1", "@nextcloud/event-bus": "^3.3.1", + "@nextcloud/files": "^3.12.0", "@nextcloud/l10n": "^3.1.0", "@nextcloud/moment": "^1.3.4", "@nextcloud/paths": "^2.1.0", @@ -84,6 +86,8 @@ "@nextcloud/eslint-config": "^8.4.1", "@nextcloud/stylelint-config": "^3.1.0", "@nextcloud/webpack-vue-config": "^6.3.0", - "@types/leaflet": "^1.9.20" + "@types/leaflet": "^1.9.20", + "@vue/tsconfig": "^0.5.1", + "typescript": "^5.9.2" } } diff --git a/src/files-actions/import-devices.ts b/src/files-actions/import-devices.ts new file mode 100644 index 000000000..140892fcc --- /dev/null +++ b/src/files-actions/import-devices.ts @@ -0,0 +1,49 @@ +import axios from '@nextcloud/axios' +import { showError, showSuccess } from '@nextcloud/dialogs' +import { FileAction, Permission } from '@nextcloud/files' +import { n, t } from '@nextcloud/l10n' +import { generateUrl } from '@nextcloud/router' + +import svgMapMarker from '@mdi/svg/svg/map-marker.svg?raw' + +export default new FileAction({ + id: 'maps:import-device', + + displayName() { + return t('maps', 'Import as devices in Maps') + }, + + enabled(files) { + if (files.length !== 1) { + return false + } + + const [file] = files + if (!(file.permissions & Permission.READ)) { + return false + } + return [ + 'application/gpx+xml', + 'application/vnd.google-earth.kmz', + 'application/vnd.google-earth.kml+xml', + ].includes(file.mime) + }, + + async exec(file) { + const path = file.path + const url = generateUrl('/apps/maps/import/devices') + try { + const { data } = await axios.post(url, { path }) + const number = typeof data === 'number' ? data : data.nbImported + showSuccess(n('maps', 'One device imported', '%n devices imported', number)) + } catch (error) { + showError(t('maps', 'Failed to import devices')) + console.error('Failed to import devices', { error }) + } + return null + }, + + iconSvgInline() { + return svgMapMarker + }, +}) diff --git a/src/files-actions/import-favorite.ts b/src/files-actions/import-favorite.ts new file mode 100644 index 000000000..6e2f03dd1 --- /dev/null +++ b/src/files-actions/import-favorite.ts @@ -0,0 +1,50 @@ +import axios from '@nextcloud/axios' +import { showError, showSuccess } from '@nextcloud/dialogs' +import { FileAction, Permission } from '@nextcloud/files' +import { n, t } from '@nextcloud/l10n' +import { generateUrl } from '@nextcloud/router' + +import svgMapMarker from '@mdi/svg/svg/map-marker.svg?raw' + +export default new FileAction({ + id: 'maps:import-as-favorite', + + displayName() { + return t('maps', 'Import as favorites in Maps') + }, + + enabled(files) { + if (files.length !== 1) { + return false + } + + const [file] = files + if (!(file.permissions & Permission.READ)) { + return false + } + return [ + 'application/geo+json', + 'application/gpx+xml', + 'application/vnd.google-earth.kmz', + 'application/vnd.google-earth.kml+xml', + ].includes(file.mime) + }, + + async exec(file) { + const path = file.path + const url = generateUrl('/apps/maps/import/favorites') + try { + const { data } = await axios.post(url, { path }) + const number = typeof data === 'number' ? data : data.nbImported + showSuccess(n('maps', 'One favorite imported', '%n favorites imported', number)) + } catch (error) { + showError(t('maps', 'Failed to import favorites')) + console.error('Failed to import favorites', { error }) + } + return null + }, + + iconSvgInline() { + return svgMapMarker + }, +}) diff --git a/src/files-actions/view-in-maps.ts b/src/files-actions/view-in-maps.ts new file mode 100644 index 000000000..b3ee74123 --- /dev/null +++ b/src/files-actions/view-in-maps.ts @@ -0,0 +1,45 @@ +import { DefaultType, FileAction, Permission } from '@nextcloud/files' +import { t } from '@nextcloud/l10n' +import { generateUrl } from '@nextcloud/router' + +import svgMapMarker from '@mdi/svg/svg/map-marker.svg?raw' + +export default new FileAction({ + id: 'maps:view-map', + default: DefaultType.DEFAULT, + + displayName() { + return t('maps', 'View in Maps') + }, + + enabled(files) { + if (files.length !== 1) { + return false + } + + const [file] = files + if (!(file.permissions & Permission.READ)) { + return false + } + return [ + 'application/gpx+xml', + 'application/x-nextcloud-maps', + ].includes(file.mime) + }, + + async exec(file) { + if (file.mime === 'application/x-nextcloud-maps') { + const url = generateUrl('apps/maps/m/{mapId}', { mapId: file.fileid }) + window.open(url, '_self') + } else { + const url = generateUrl('apps/maps/?track={path}', { path: file.path }) + window.open(url, '_self') + } + + return null + }, + + iconSvgInline() { + return svgMapMarker + }, +}) diff --git a/src/filetypes.js b/src/filetypes.js deleted file mode 100644 index bacdcaf12..000000000 --- a/src/filetypes.js +++ /dev/null @@ -1,162 +0,0 @@ -import { generateUrl } from '@nextcloud/router'; -import { showSuccess, showError } from "@nextcloud/dialogs"; - -$(document).ready(function() { - - if (OCA.Files && OCA.Files.fileActions) { - - function openMap(file, data) { - var mapId = data.fileList.dirInfo.id; - var url = generateUrl('apps/maps/m/{mapId}',{mapId}); - - window.open(url, '_blank'); - } - - function openTrackFile(file, data) { - var token = $('#sharingToken').val(); - // if we are logged - if (!token) { - var dir = (data.dir === '/') ? '' : data.dir; - var url = generateUrl('apps/maps/?track={dir}%2F{file}',{'dir': dir, 'file': file}); - } - window.open(url, '_blank'); - } - - function importFavoritesFile(file, data) { - $('#content').css('cursor', 'wait'); - var dir = (data.dir === '/') ? '' : data.dir; - var path = dir + '/' + file; - var req = { - path: path - }; - var url = generateUrl('/apps/maps/import/favorites'); - $.ajax({ - type: 'POST', - url: url, - data: req, - async: true - }).done(function (response) { - showSuccess(t('maps', '{nb} favorites imported from {path}', {nb: response, path: path})); - }).always(function (response) { - $('#content').css('cursor', 'default'); - }).fail(function() { - showError(t('maps', 'Failed to import favorites')); - }); - } - - function importDevicesFile(file, data) { - $('#content').css('cursor', 'wait'); - var dir = (data.dir === '/') ? '' : data.dir; - var path = dir + '/' + file; - var req = { - path: path - }; - var url = generateUrl('/apps/maps/import/devices'); - $.ajax({ - type: 'POST', - url: url, - data: req, - async: true - }).done(function (response) { - showSuccess(t('maps', '{nb} devices imported from {path}', {nb: response, path: path})); - }).always(function (response) { - $('#content').css('cursor', 'default'); - }).fail(function(response) { - showError(t('maps', 'Failed to import devices') + ': ' + response.responseText); - }); - } - - // default action is set only for logged in users - if (!$('#sharingToken').val()){ - //Open in Maps - OCA.Files.fileActions.registerAction({ - name: 'viewMap', - displayName: t('maps', 'View in Maps'), - mime: 'application/x-nextcloud-maps', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: openMap - }); - OCA.Files.fileActions.setDefault('application/x-nextcloud-maps', 'viewMap'); - - OCA.Files.fileActions.registerAction({ - name: 'viewTrackMaps', - displayName: t('maps', 'View in Maps'), - mime: 'application/gpx+xml', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: openTrackFile - }); - - OCA.Files.fileActions.register('application/gpx+xml', 'viewTrackMapsDefault', OC.PERMISSION_READ, '', openTrackFile); - OCA.Files.fileActions.setDefault('application/gpx+xml', 'viewTrackMapsDefault'); - - // import gpx files as favorites - OCA.Files.fileActions.registerAction({ - name: 'importGpxFavoritesMaps', - displayName: t('maps', 'Import as favorites in Maps'), - mime: 'application/gpx+xml', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: importFavoritesFile - }); - // import kmz files as favorites - OCA.Files.fileActions.registerAction({ - name: 'importKmzFavoritesMaps', - displayName: t('maps', 'Import as favorites in Maps'), - mime: 'application/vnd.google-earth.kmz', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: importFavoritesFile - }); - // import kml files as favorites - OCA.Files.fileActions.registerAction({ - name: 'importKmlFavoritesMaps', - displayName: t('maps', 'Import as favorites in Maps'), - mime: 'application/vnd.google-earth.kml+xml', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: importFavoritesFile - }); - // import geojson files as favorites - OCA.Files.fileActions.registerAction({ - name: 'importGeoJsonFavoritesMaps', - displayName: t('maps', 'Import as favorites in Maps'), - mime: 'application/geo+json', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: importFavoritesFile - }); - - // import gpx files as devices - OCA.Files.fileActions.registerAction({ - name: 'importGpxDevicesMaps', - displayName: t('maps', 'Import as devices in Maps'), - mime: 'application/gpx+xml', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: importDevicesFile - }); - // import kmz files as devices - OCA.Files.fileActions.registerAction({ - name: 'importKmzDevicesMaps', - displayName: t('maps', 'Import as devices in Maps'), - mime: 'application/vnd.google-earth.kmz', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: importDevicesFile - }); - // import kml files as devicess - OCA.Files.fileActions.registerAction({ - name: 'importKmlDevicesMaps', - displayName: t('maps', 'Import as devices in Maps'), - mime: 'application/vnd.google-earth.kml+xml', - permissions: OC.PERMISSION_READ, - iconClass: 'icon-maps-black', - actionHandler: importDevicesFile - }); - } - } - -}); - diff --git a/src/init-files.ts b/src/init-files.ts new file mode 100644 index 000000000..c935193c2 --- /dev/null +++ b/src/init-files.ts @@ -0,0 +1,11 @@ +import { registerFileAction } from '@nextcloud/files' +import { isPublicShare } from '@nextcloud/sharing/public' +import importDevices from './files-actions/import-devices' +import importFavorite from './files-actions/import-favorite' +import viewInMaps from './files-actions/view-in-maps' + +if (!isPublicShare()) { + registerFileAction(viewInMaps) + registerFileAction(importDevices) + registerFileAction(importFavorite) +} diff --git a/src/modules.d.ts b/src/modules.d.ts new file mode 100644 index 000000000..7766aa6f3 --- /dev/null +++ b/src/modules.d.ts @@ -0,0 +1,4 @@ +declare module '*?raw' { + const content: string + export default content +} diff --git a/tests/Unit/Controller/PageControllerTest.php b/tests/Unit/Controller/PageControllerTest.php index 4c1323240..3a24815c9 100644 --- a/tests/Unit/Controller/PageControllerTest.php +++ b/tests/Unit/Controller/PageControllerTest.php @@ -12,40 +12,41 @@ namespace OCA\Maps\Controller; -use OCA\Maps\AppInfo\Application; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\IEventDispatcher; -use OCP\IServerContainer; +use OCP\IConfig; +use OCP\IRequest; +use OCP\IURLGenerator; +use PHPUnit\Framework\MockObject\MockObject; class PageControllerTest extends \PHPUnit\Framework\TestCase { - private $controller; - private $userId = 'john'; - private $config; - private $eventDispatcher; - private $app; - private $container; + 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 { - $request = $this->getMockBuilder('OCP\IRequest')->getMock(); - $initialStateService = $this->getMockBuilder('OCP\IInitialStateService')->getMock(); - $this->app = new Application(); - $this->container = $this->app->getContainer(); - $c = $this->container; - $this->config = $c->query(IServerContainer::class)->getConfig(); - $this->eventDispatcher = $c->query(IServerContainer::class)->query(IEventDispatcher::class); - - $this->oldGHValue = $this->config->getAppValue('maps', 'graphhopperURL'); - $this->config->setAppValue('maps', 'graphhopperURL', 'https://graphhopper.com:8080'); + /** @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); $this->controller = new PageController( - 'maps', $request, $this->eventDispatcher, $this->config, $initialStateService, $this->userId + 'maps', + $request, + $this->userId, + $this->eventDispatcher, + $this->config, + $this->initialState, + $this->urlGenerator, ); } - protected function tearDown(): void { - $this->config->setAppValue('maps', 'graphhopperURL', $this->oldGHValue); - } - public function testIndex() { $result = $this->controller->index(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..8a72d3d7a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@vue/tsconfig", + "compilerOptions": { + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, + "noEmit": false, + }, + "vueCompilerOptions": { + "target": 2.7 + } +} \ No newline at end of file diff --git a/webpack.js b/webpack.js index 8bd37b01b..087b5db53 100644 --- a/webpack.js +++ b/webpack.js @@ -10,14 +10,19 @@ webpackConfig.stats = { modules: false, } +webpackConfig.module.rules.push({ + resourceQuery: /raw/, + type: 'asset/source', +}) + webpackConfig.entry = { adminSettings: { import: path.join(__dirname, 'src', 'adminSettings.js'), filename: 'maps-adminSettings.js' }, // script: { import: path.join(__dirname, 'src', 'script.js'), filename: 'maps-script.js' }, main: { import: path.join(__dirname, 'src', 'main.js'), filename: 'maps-main.js' }, + 'init-files': { import: path.join(__dirname, 'src', 'init-files.ts'), filename: 'maps-init-files.js' }, 'track-metadata-tab': { import: path.join(__dirname, 'src', 'track-metadata-tab.js'), filename: 'maps-track-metadata-tab.js' }, 'copy-map-link': { import: path.join(__dirname, 'src', 'copy-map-link.js'), filename: 'maps-copy-map-link.js' }, 'report-error-map-action': { import: path.join(__dirname, 'src', 'report-error-map-action.js'), filename: 'maps-report-error-map-action.js' }, - filetypes: { import: path.join(__dirname, 'src', 'filetypes.js'), filename: 'maps-filetypes.js' }, 'public-favorite-share': { import: path.join(__dirname, 'src', 'publicFavoriteShare.js'), filename: 'maps-publicFavoriteShare.js' }, }