Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ translations.js

# JS coverage
coverage/

# vim
.*.sw?
4 changes: 4 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
use OCA\Mail\Service\MailManager;
use OCA\Mail\Service\MailTransmission;
use OCA\Mail\Service\UserPreferenceSevice;
use OCA\Mail\Service\Group\IGroupService;
use OCA\Mail\Service\Group\NextcloudGroupService;
use OCP\AppFramework\App;
use OCP\Util;

Expand Down Expand Up @@ -71,6 +73,8 @@ private function initializeAppContainer() {

$container->registerAlias('ErrorMiddleware', ErrorMiddleware::class);
$container->registerMiddleWare('ErrorMiddleware');

$container->registerAlias(IGroupService::class, NextcloudGroupService::class);
}

}
14 changes: 12 additions & 2 deletions lib/Controller/AccountsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* @author Lukas Reschke <lukas@owncloud.com>
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Matthias Rella <mrella@pisys.eu>
*
* Mail
*
Expand Down Expand Up @@ -38,6 +39,7 @@
use OCA\Mail\Service\AliasesService;
use OCA\Mail\Service\Logger;
use OCA\Mail\Service\SetupService;
use OCA\Mail\Service\GroupsIntegration;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\IL10N;
Expand All @@ -49,6 +51,9 @@ class AccountsController extends Controller {
/** @var AccountService */
private $accountService;

/** @var GroupsIntegration */
private $groupsIntegration;

/** @var string */
private $currentUserId;

Expand Down Expand Up @@ -80,10 +85,11 @@ class AccountsController extends Controller {
* @param ICrypto $crypto
* @param SetupService $setup
*/
public function __construct($appName, IRequest $request, AccountService $accountService, $UserId, Logger $logger, IL10N $l10n, ICrypto $crypto, AliasesService $aliasesService, IMailTransmission $mailTransmission, SetupService $setup
public function __construct($appName, IRequest $request, AccountService $accountService, GroupsIntegration $groupsIntegration, $UserId, Logger $logger, IL10N $l10n, ICrypto $crypto, AliasesService $aliasesService, IMailTransmission $mailTransmission, SetupService $setup
) {
parent::__construct($appName, $request);
$this->accountService = $accountService;
$this->groupsIntegration = $groupsIntegration;
$this->currentUserId = $UserId;
$this->logger = $logger;
$this->l10n = $l10n;
Expand Down Expand Up @@ -253,7 +259,11 @@ public function send($accountId, $folderId, $subject, $body, $to, $cc, $bcc, $dr
$account = $this->accountService->find($this->currentUserId, $accountId);
$alias = $aliasId ? $this->aliasesService->find($aliasId, $this->currentUserId) : null;

$messageData = NewMessageData::fromRequest($account, $to, $cc, $bcc, $subject, $body, $attachments);
$expandedTo = $this->groupsIntegration->expand($to);
$expandedCc = $this->groupsIntegration->expand($cc);
$expandedBcc = $this->groupsIntegration->expand($bcc);

$messageData = NewMessageData::fromRequest($account, $expandedTo, $expandedCc, $expandedBcc, $subject, $body, $attachments);
$repliedMessageData = new RepliedMessageData($account, $folderId, $messageId);

try {
Expand Down
11 changes: 9 additions & 2 deletions lib/Service/AutoCompletion/AutoCompleteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

/**
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Matthias Rella <mrella@pisys.eu>
*
* Mail
*
Expand All @@ -23,22 +24,28 @@

use OCA\Mail\Db\CollectedAddress;
use OCA\Mail\Service\ContactsIntegration;
use OCA\Mail\Service\GroupsIntegration;

class AutoCompleteService {

/** @var ContactsIntegration */
private $contactsIntegration;

/** @var GroupsIntegration */
private $groupsIntegration;

/** @var AddressCollector */
private $addressCollector;

public function __construct(ContactsIntegration $ci, AddressCollector $ac) {
public function __construct(ContactsIntegration $ci, GroupsIntegration $gi, AddressCollector $ac) {
$this->contactsIntegration = $ci;
$this->groupsIntegration = $gi;
$this->addressCollector = $ac;
}

public function findMatches($term) {
$recipientsFromContacts = $this->contactsIntegration->getMatchingRecipient($term);
$recipientGroups = $this->groupsIntegration->getMatchingGroups($term);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have this logic moved to a service class, maybe even introduce a facade for that. But that's a minor enhancement - this can be done later on 😉

$fromCollector = $this->addressCollector->searchAddress($term);

// Convert collected addresses into same format as CI creates
Expand All @@ -50,7 +57,7 @@ public function findMatches($term) {
];
}, $fromCollector);

return array_merge($recipientsFromContacts, $recipientsFromCollector);
return array_merge($recipientsFromContacts, $recipientsFromCollector, $recipientGroups);
}

}
50 changes: 50 additions & 0 deletions lib/Service/Group/IGroupService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

/**
* @author Matthias Rella <mrella@pisys.eu>
*
* Mail
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OCA\Mail\Service\Group;

interface IGroupService {

/**
* Search the service's groups.
*
* @param string $term
* @return array of matched groups as associative arrays
*/
public function search($term);

/**
* Get the group's namespace.
*
* @return string
*/
public function getNamespace();

/**
* Get the group's users.
*
* @param string $groupId
* @return array with group's users as associative arrays
*/
public function getUsers($groupId);

}

81 changes: 81 additions & 0 deletions lib/Service/Group/NextcloudGroupService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

/**
* @author Matthias Rella <mrella@pisys.eu>
*
* Mail
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OCA\Mail\Service\Group;

use OCP\IGroupManager;
use OCA\Mail\Exception\ServiceException;

class NextcloudGroupService implements IGroupService {

/**
* Nextcloud's group manager
*
* @var IGroupManager
*/
private $groupManager;

/**
* Group's namespace
*
* @var string
*/
private $namespace = "Nextcloud";

public function __construct(IGroupManager $groupManager) {
$this->groupManager = $groupManager;
}

public function getNamespace() {
return $this->namespace;
}

public function search($term) {
$groups = $this->groupManager->search($term);

return array_map(
function($g) {
return [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a good candidate to implement PHP's JsonSerializable interface for this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ported this pattern from Contacts\IManager::search. It's just returning key/value pairs. Where would JsonSerializable fit in?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm, just realized that $g is some data structure from the Nextcloud server, so nothing we can change. In that case that's fine 👍

'id' => $g->getGID(),
'name' => $g->getDisplayName()
];
},
$groups
);
}

public function getUsers($groupId) {
if(!$this->groupManager->groupExists($groupId)) {
throw new ServiceException("$groupId ({$this->getNamespace()}) does not exist");
}
$users = $this->groupManager->get($groupId)->getUsers();
return array_map(
function($user) {
return [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

'id' => $user->getUID(),
'name' => $user->getDisplayName(),
'email' => $user->getEMailAddress()
];
},
$users
);
}
}
108 changes: 108 additions & 0 deletions lib/Service/GroupsIntegration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

/**
* @author Matthias Rella <mrella@pisys.eu>
*
* Mail
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OCA\Mail\Service;

use OCA\Mail\Service\Group\IGroupService;
use OCA\Mail\Exception\ServiceException;

class GroupsIntegration {

/**
* The services to get groups from
*
* @var IGroupService[]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this interface really exist?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It did :-)
Missed to update the docs here.

But AbstractGroupService should become IGroupService again anyways.

*/
private $groupServices = [];

public function __construct(IGroupService $groupService) {
$this->groupServices[] = $groupService;
}

/**
* Extracts all matching contacts with email address and name
*
* @param string $term
* @return array
*/
public function getMatchingGroups($term) {
$receivers = [];
foreach ($this->groupServices as $gs) {
$result = $gs->search($term);
foreach ($result as $g) {
$gid = $this->servicePrefix($gs) . $g['id'];
$receivers[] = [
'id' => $gid,
'label' => $g['name'] . " (" . $gs->getNamespace() . ")",
'value' => $gid,
'photo' => null,
];
}
}

return $receivers;
}

/**
* Returns the prefix for the group service.
*
* @param IGroupService $gs
* @return string
*/
public function servicePrefix(IGroupService $gs) {
if (empty($gs->getNamespace())) {
throw new ServiceException('GroupService has no namespace');
}
return strtolower($gs->getNamespace()) . ":";
}

/**
* Expands a string of group names to its members email addresses.
*
* @param string $recipients
* @return string
*/
public function expand($recipients) {
return array_reduce($this->groupServices,
function($carry, $service) {
return preg_replace_callback(
'/' . preg_quote($this->servicePrefix($service)) . '([\w\d ]+)(,?)/',
function($matches) use ($service) {
if (empty($matches[1])) {
return '';
}
$members = $service->getUsers($matches[1]);
if (empty($members)) {
throw new ServiceException($matches[1] . " ({$service->getNamespace()}) has no members");
}
$addresses = [];
foreach ($members as $m) {
if (!empty($m['email'])) {
$addresses[] = $m['email'];
}
}
return implode(',', $addresses)
. (!empty($matches[2]) && !empty($addresses) ? ',' : '');
}, $carry);
}, $recipients);
}

}
Loading