Skip to content

Commit 6304f1e

Browse files
authored
Merge pull request #940 from nextcloud/public-page
Public page for MyMaps
2 parents 5c38001 + aefc184 commit 6304f1e

35 files changed

+2141
-226
lines changed

appinfo/routes.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,33 @@
1313
['name' => 'page#indexMyMap', 'url' => '/m/{myMapId}', 'verb' => 'GET'],
1414
['name' => 'page#do_echo', 'url' => '/echo', 'verb' => 'POST'],
1515
['name' => 'page#openGeoLink', 'url' => '/openGeoLink/{url}', 'verb' => 'GET'],
16-
['name' => 'public_page#sharedFavoritesCategory', 'url' => '/s/favorites/{token}', 'verb' => 'GET'],
16+
['name' => 'public_favorite_page#sharedFavoritesCategory', 'url' => '/s/favorites/{token}', 'verb' => 'GET'],
17+
['name' => 'PublicPage#showShare', 'url' => '/s/{token}', 'verb' => 'GET'],
18+
['name' => 'PublicPage#showAuthenticate', 'url' => '/s/{token}/authenticate/{redirect}', 'verb' => 'GET'],
19+
['name' => 'PublicPage#authenticate', 'url' => '/s/{token}/authenticate/{redirect}', 'verb' => 'POST'],
1720

1821

1922
// utils
2023
['name' => 'utils#getOptionsValues', 'url' => '/getOptionsValues', 'verb' => 'GET'],
2124
['name' => 'utils#saveOptionValue', 'url' => '/saveOptionValue', 'verb' => 'POST'],
2225
['name' => 'utils#setRoutingSettings', 'url' => '/setRoutingSettings', 'verb' => 'POST'],
2326
['name' => 'utils#getTrafficStyle', 'url' => '/style/traffic', 'verb' => 'GET'],
27+
['name' => 'PublicUtils#getOptionsValues', 'url' => '/s/{token}/getOptionsValues', 'verb' => 'GET'],
28+
['name' => 'PublicUtils#saveOptionValue', 'url' => '/s/{token}/saveOptionValue', 'verb' => 'POST'],
29+
['name' => 'PublicUtils#setRoutingSettings', 'url' => '/s/{token}/setRoutingSettings', 'verb' => 'POST'],
30+
['name' => 'PublicUtils#getTrafficStyle', 'url' => '/s/{token}/style/traffic', 'verb' => 'GET'],
2431

2532
// photos
26-
['name' => 'photos#getPhotosFromDb', 'url' => '/photos', 'verb' => 'GET'],
27-
['name' => 'photos#getNonLocalizedPhotosFromDb', 'url' => '/photos/nonlocalized', 'verb' => 'GET'],
33+
['name' => 'photos#getPhotos', 'url' => '/photos', 'verb' => 'GET'],
34+
['name' => 'photos#getNonLocalizedPhotos', 'url' => '/photos/nonlocalized', 'verb' => 'GET'],
2835
['name' => 'photos#placePhotos', 'url' => '/photos', 'verb' => 'POST'],
2936
['name' => 'photos#resetPhotosCoords', 'url' => '/photos', 'verb' => 'DELETE'],
3037
['name' => 'photos#clearCache', 'url' => '/photos/clearCache', 'verb' => 'GET'],
3138

39+
['name' => 'PublicPhotos#getPhotos', 'url' => '/s/{token}/photos', 'verb' => 'GET'],
40+
['name' => 'PublicPhotos#getNonLocalizedPhotos', 'url' => '/s/{token}/photos/nonlocalized', 'verb' => 'GET'],
41+
['name' => 'PublicPhotos#clearCache', 'url' => '/s/{token}/photos/clearCache', 'verb' => 'GET'],
42+
3243
// contacts
3344
['name' => 'contacts#getContacts', 'url' => '/contacts', 'verb' => 'GET'],
3445
['name' => 'contacts#searchContacts', 'url' => '/contacts-search', 'verb' => 'GET'],
@@ -37,6 +48,9 @@
3748
['name' => 'contacts#deleteContactAddress', 'url' => '/contacts/{bookid}/{uri}', 'verb' => 'DELETE'],
3849
['name' => 'contacts#getContactLetterAvatar', 'url' => '/contacts-avatar', 'verb' => 'GET'],
3950

51+
['name' => 'PublicContacts#getContacts', 'url' => '/s/{token}/contacts', 'verb' => 'GET'],
52+
['name' => 'PublicContacts#getContactLetterAvatar', 'url' => '/s/{token}/contacts-avatar', 'verb' => 'GET'],
53+
4054
// routing
4155
['name' => 'routing#exportRoute', 'url' => '/exportRoute', 'verb' => 'POST'],
4256

@@ -72,6 +86,13 @@
7286
['name' => 'favorites#deleteFavorite', 'url' => '/favorites/{id}', 'verb' => 'DELETE'],
7387
['name' => 'favorites#deleteFavorites', 'url' => '/favorites', 'verb' => 'DELETE'],
7488

89+
['name' => 'PublicFavorites#getFavorites', 'url' => '/s/{token}/favorites', 'verb' => 'GET'],
90+
['name' => 'PublicFavorites#addFavorite', 'url' => '/s/{token}/favorite', 'verb' => 'POST'],
91+
['name' => 'PublicFavorites#addFavorites', 'url' => '/s/{token}/favorites', 'verb' => 'POST'],
92+
['name' => 'PublicFavorites#editFavorite', 'url' => '/s/{token}/favorites/{id}', 'verb' => 'PUT'],
93+
['name' => 'PublicFavorites#deleteFavorite', 'url' => '/s/{token}/favorites/{id}', 'verb' => 'DELETE'],
94+
['name' => 'PublicFavorites#deleteFavorites', 'url' => '/s/{token}/favorites', 'verb' => 'DELETE'],
95+
7596
// favorite categories
7697
['name' => 'favorites#renameCategories', 'url' => '/favorites-category', 'verb' => 'PUT'],
7798
['name' => 'favorites#getSharedCategories', 'url' => '/favorites-category/shared', 'verb' => 'GET'],
@@ -83,12 +104,20 @@
83104
['name' => 'favorites#exportFavorites', 'url' => '/export/favorites', 'verb' => 'POST'],
84105
['name' => 'favorites#importFavorites', 'url' => '/import/favorites', 'verb' => 'POST'],
85106

107+
['name' => 'PublicFavorites#renameCategories', 'url' => '/s/{token}/favorites-category', 'verb' => 'PUT'],
108+
['name' => 'PublicFavorites#getSharedCategories', 'url' => '/s/{token}/favorites-category/shared', 'verb' => 'GET'],
109+
86110
// tracks
87111
['name' => 'tracks#getTracks', 'url' => '/tracks', 'verb' => 'GET'],
88112
['name' => 'tracks#getTrackFileContent', 'url' => '/tracks/{id}', 'verb' => 'GET'],
89113
['name' => 'tracks#getTrackContentByFileId', 'url' => '/tracks/file/{id}', 'verb' => 'GET'],
90114
['name' => 'tracks#editTrack', 'url' => '/tracks/{id}', 'verb' => 'PUT'],
91115

116+
['name' => 'PublicTracks#getTracks', 'url' => '/s/{token}/tracks', 'verb' => 'GET'],
117+
['name' => 'PublicTracks#getTrackFileContent', 'url' => '/s/{token}/tracks/{id}', 'verb' => 'GET'],
118+
['name' => 'PublicTracks#getTrackContentByFileId', 'url' => '/s/{token}/tracks/file/{id}', 'verb' => 'GET'],
119+
['name' => 'PublicTracks#editTrack', 'url' => '/s/{token}/tracks/{id}', 'verb' => 'PUT'],
120+
92121
// devices API
93122
[
94123
'name' => 'devices_api#preflighted_cors',

lib/Controller/PhotosController.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ public function __construct($AppName,
4949
* @NoCSRFRequired
5050
* @return DataResponse
5151
*/
52-
public function getPhotosFromDb($myMapId=null): DataResponse {
52+
public function getPhotos($myMapId=null): DataResponse {
5353
$userFolder = $this->root->getUserFolder($this->userId);
5454
if (is_null($myMapId) || $myMapId === "") {
55-
$result = $this->geophotoService->getAllFromDB($this->userId, $userFolder);
55+
$result = $this->geophotoService->getAll($this->userId, $userFolder);
5656
} else {
5757
$folders = $userFolder->getById($myMapId);
5858
$folder = array_shift($folders);
59-
$result = $this->geophotoService->getAllFromDB($this->userId, $folder, true, false);
59+
$result = $this->geophotoService->getAll($this->userId, $folder, true, false);
6060
}
6161
return new DataResponse($result);
6262
}
@@ -66,14 +66,14 @@ public function getPhotosFromDb($myMapId=null): DataResponse {
6666
* @NoCSRFRequired
6767
* @return DataResponse
6868
*/
69-
public function getNonLocalizedPhotosFromDb($myMapId=null): DataResponse {
69+
public function getNonLocalizedPhotos($myMapId=null): DataResponse {
7070
$userFolder = $this->root->getUserFolder($this->userId);
7171
if (is_null($myMapId) || $myMapId === "") {
72-
$result = $this->geophotoService->getNonLocalizedFromDB($this->userId, $userFolder);
72+
$result = $this->geophotoService->getNonLocalized($this->userId, $userFolder);
7373
} else {
7474
$folders = $userFolder->getById($myMapId);
7575
$folder = array_shift($folders);
76-
$result = $this->geophotoService->getNonLocalizedFromDB($this->userId, $folder, true, false);
76+
$result = $this->geophotoService->getNonLocalized($this->userId, $folder, true, false);
7777
}
7878
return new DataResponse($result);
7979
}
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
<?php
2+
3+
/**
4+
* Nextcloud - maps
5+
*
6+
* This file is licensed under the Affero General Public License version 3 or
7+
* later. See the COPYING file.
8+
*
9+
* @author Julien Veyssier <eneiluj@posteo.net>
10+
* @copyright Julien Veyssier 2019
11+
*/
12+
13+
namespace OCA\Maps\Controller;
14+
15+
use OC\Files\Node\Node;
16+
use OCP\EventDispatcher\IEventDispatcher;
17+
use OCP\Files\IRootFolder;
18+
use OCP\Files\NotFoundException;
19+
use OCP\Files\NotPermittedException;
20+
use OCP\IConfig;
21+
use OCP\IInitialStateService;
22+
use OCP\IRequest;
23+
use OCP\IAvatarManager;
24+
use OCP\AppFramework\Http\DataDisplayResponse;
25+
use OCP\AppFramework\Http\DataResponse;
26+
use OCP\IDBConnection;
27+
use OCP\AppFramework\Controller;
28+
use OCP\Contacts\IManager;
29+
use OCA\Maps\Service\AddressService;
30+
use \OCP\DB\QueryBuilder\IQueryBuilder;
31+
use \OCA\DAV\CardDAV\CardDavBackend;
32+
use OCP\ISession;
33+
use OCP\IURLGenerator;
34+
use OCP\IUserManager;
35+
use OCP\Share\Exceptions\ShareNotFound;
36+
use OCP\Share\IManager as ShareManager;
37+
use \Sabre\VObject\Property\Text;
38+
use \Sabre\VObject\Reader;
39+
40+
class PublicContactsController extends PublicPageController {
41+
protected IManager $contactsManager;
42+
protected AddressService $addressService;
43+
protected IQueryBuilder $qb;
44+
protected CardDavBackend $cdBackend;
45+
protected IAvatarManager $avatarManager;
46+
protected IRootFolder $root;
47+
48+
/**
49+
* @param $appName
50+
* @param IRequest $request
51+
* @param IEventDispatcher $eventDispatcher
52+
* @param IConfig $config
53+
* @param IInitialStateService $initialStateService
54+
* @param IURLGenerator $urlGenerator
55+
* @param ShareManager $shareManager
56+
* @param IUserManager $userManager
57+
* @param ISession $session
58+
* @param IDBConnection $dbconnection
59+
* @param IManager $contactsManager
60+
* @param AddressService $addressService
61+
* @param CardDavBackend $cdBackend
62+
* @param IAvatarManager $avatarManager
63+
* @param IRootFolder $root
64+
*/
65+
public function __construct($appName,
66+
IRequest $request,
67+
IEventDispatcher $eventDispatcher,
68+
IConfig $config,
69+
IInitialStateService $initialStateService,
70+
IURLGenerator $urlGenerator,
71+
ShareManager $shareManager,
72+
IUserManager $userManager,
73+
ISession $session,
74+
IManager $contactsManager,
75+
IDBConnection $dbconnection,
76+
AddressService $addressService,
77+
CardDavBackend $cdBackend,
78+
IAvatarManager $avatarManager,
79+
IRootFolder $root){
80+
parent::__construct($appName, $request, $eventDispatcher, $config, $initialStateService, $urlGenerator, $shareManager, $userManager, $session);
81+
$this->avatarManager = $avatarManager;
82+
$this->contactsManager = $contactsManager;
83+
$this->addressService = $addressService;
84+
$this->qb = $dbconnection->getQueryBuilder();
85+
$this->cdBackend = $cdBackend;
86+
$this->root = $root;
87+
}
88+
89+
/**
90+
* Validate the permissions of the share
91+
*
92+
* @param Share\IShare $share
93+
* @return bool
94+
*/
95+
private function validateShare(\OCP\Share\IShare $share) {
96+
// If the owner is disabled no access to the link is granted
97+
$owner = $this->userManager->get($share->getShareOwner());
98+
if ($owner === null || !$owner->isEnabled()) {
99+
return false;
100+
}
101+
102+
// If the initiator of the share is disabled no access is granted
103+
$initiator = $this->userManager->get($share->getSharedBy());
104+
if ($initiator === null || !$initiator->isEnabled()) {
105+
return false;
106+
}
107+
108+
return $share->getNode()->isReadable() && $share->getNode()->isShareable();
109+
}
110+
111+
/**
112+
* @return \OCP\Share\IShare
113+
* @throws NotFoundException
114+
*/
115+
private function getShare() {
116+
// Check whether share exists
117+
try {
118+
$share = $this->shareManager->getShareByToken($this->getToken());
119+
} catch (ShareNotFound $e) {
120+
// The share does not exists, we do not emit an ShareLinkAccessedEvent
121+
throw new NotFoundException();
122+
}
123+
124+
if (!$this->validateShare($share)) {
125+
throw new NotFoundException();
126+
}
127+
return $share;
128+
}
129+
130+
/**
131+
* @return \OCP\Files\File|\OCP\Files\Folder
132+
* @throws NotFoundException
133+
*/
134+
private function getShareNode() {
135+
\OC_User::setIncognitoMode(true);
136+
137+
$share = $this->getShare();
138+
139+
return $share->getNode();
140+
}
141+
142+
/**
143+
* @PublicPage
144+
*
145+
* @return DataResponse
146+
* @throws NotFoundException
147+
* @throws NotPermittedException
148+
* @throws \OCP\Files\InvalidPathException
149+
*/
150+
public function getContacts(): DataResponse {
151+
$share = $this->getShare();
152+
$permissions = $share->getPermissions();
153+
$folder = $this->getShareNode();
154+
$isReadable = (bool) ($permissions & (1 << 0));
155+
if ($isReadable) {
156+
//Fixme add contacts for my-maps
157+
$result = [];
158+
$files = $folder->search('.vcf');
159+
foreach ($files as $file) {
160+
// $cards = explode("END:VCARD\r\n", $file->getContent());
161+
$cards = [$file->getContent()];
162+
foreach ($cards as $card) {
163+
$vcard = Reader::read($card."END:VCARD\r\n");
164+
if (isset($vcard->GEO)) {
165+
$geo = $vcard->GEO;
166+
if (is_string($geo->getValue()) && strlen($geo->getValue()) > 1) {
167+
$result[] = $this->vCardToArray($permissions, $file, $vcard, $geo->getValue());
168+
} elseif (is_countable($geo) && count($geo)>0 && is_iterable($geo)) {
169+
foreach ($geo as $g) {
170+
if (strlen($g->getValue()) > 1) {
171+
$result[] = $this->vCardToArray($permissions, $file, $vcard, $g->getValue());
172+
}
173+
}
174+
}
175+
}
176+
if (isset($vcard->ADR) && count($vcard->ADR) > 0) {
177+
foreach ($vcard->ADR as $adr) {
178+
$geo = $this->addressService->addressToGeo($adr->getValue(), $file->getId());
179+
//var_dump($adr->parameters()['TYPE']->getValue());
180+
$adrtype = '';
181+
if (isset($adr->parameters()['TYPE'])) {
182+
$adrtype = $adr->parameters()['TYPE']->getValue();
183+
}
184+
if (is_string($geo) && strlen($geo) > 1) {
185+
$result[] = $this->vCardToArray($permissions, $file, $vcard, $geo, $adrtype, $adr->getValue(), $file->getId());
186+
}
187+
}
188+
}
189+
}
190+
}
191+
return new DataResponse($result);
192+
} else {
193+
throw new NotPermittedException();
194+
}
195+
}
196+
197+
/**
198+
* @param int $sharePermissions
199+
* @param Node $file
200+
* @param \Sabre\VObject\Document $vcard
201+
* @param string $geo
202+
* @param string|null $adrtype
203+
* @param string|null $adr
204+
* @param int|null $fileId
205+
* @return array
206+
* @throws NotFoundException
207+
* @throws \OCP\Files\InvalidPathException
208+
*/
209+
private function vCardToArray(int $sharePermissions, Node $file, \Sabre\VObject\Document $vcard, string $geo, ?string $adrtype=null, ?string $adr=null, ?int $fileId = null): array {
210+
$FNArray = $vcard->FN ? $vcard->FN->getJsonValue() : [];
211+
$fn = array_shift($FNArray);
212+
$NArray = $vcard->N ? $vcard->N->getJsonValue() : [];
213+
$n = array_shift($NArray);
214+
if (!is_null($n)) {
215+
if (is_array($n)) {
216+
$n = $this->N2FN(array_shift($n));
217+
} elseif (is_string($n)) {
218+
$n = $this->N2FN($n);
219+
}
220+
221+
}
222+
$UIDArray = $vcard->UID->getJsonValue();
223+
$uid = array_shift($UIDArray);
224+
$groups = $vcard->CATEGORIES;
225+
if (!is_null($groups)) {
226+
$groups = $groups->getValue();
227+
} else {
228+
$groups = '';
229+
}
230+
$result = [
231+
'FN' => $fn ?? $n ?? '???',
232+
'UID' => $uid,
233+
'HAS_PHOTO' => (isset($vcard->PHOTO) && $vcard->PHOTO !== null),
234+
'FILEID' => $fileId,
235+
'ADR' => $adr ?? '',
236+
'ADRTYPE' => $adrtype ?? '',
237+
'PHOTO' => $vcard->PHOTO ?? '',
238+
'GEO' => $geo,
239+
'GROUPS' => $groups,
240+
'isReadable' => $file->isReadable() && ($sharePermissions & (1 << 0)),
241+
'isDeletable' => $file->isDeletable() && ($sharePermissions & (1 << 1)),
242+
'isUpdateable' => $file->isUpdateable() && ($sharePermissions & (1 << 3)),
243+
];
244+
return $result;
245+
}
246+
247+
/**
248+
* @param string $n
249+
* @return string|null
250+
*/
251+
private function N2FN(string $n): ?string {
252+
if ($n) {
253+
$spl = explode($n, ';');
254+
if (count($spl) >= 4) {
255+
return $spl[3] . ' ' . $spl[1] . ' ' . $spl[0];
256+
}
257+
else {
258+
return null;
259+
}
260+
}
261+
else {
262+
return null;
263+
}
264+
}
265+
266+
267+
/**
268+
* @PublicPage
269+
* @NoCSRFRequired
270+
*
271+
* @param string $name
272+
* @return DataDisplayResponse
273+
* @throws NotFoundException
274+
* @throws NotPermittedException
275+
*/
276+
public function getContactLetterAvatar(string $name): DataDisplayResponse {
277+
$av = $this->avatarManager->getGuestAvatar($name);
278+
$avatarContent = $av->getFile(64)->getContent();
279+
return new DataDisplayResponse($avatarContent);
280+
}
281+
}

0 commit comments

Comments
 (0)