Skip to content

Commit

Permalink
feat: fiter messages by mention
Browse files Browse the repository at this point in the history
Signed-off-by: Hamza Mahjoubi <[email protected]>
  • Loading branch information
hamza221 committed Oct 14, 2024
1 parent 9ba52fd commit c40ec07
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 13 deletions.
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The rating depends on the installed text processing backend. See [the rating ove
Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).
]]></description>
<version>4.1.0-alpha.0</version>
<version>4.1.1-alpha.0</version>
<licence>agpl</licence>
<author homepage="https://github.com/ChristophWurst">Christoph Wurst</author>
<author homepage="https://github.com/GretaD">GretaD</author>
Expand Down
4 changes: 4 additions & 0 deletions lib/Db/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
* @method void setImipError(bool $imipError)
* @method bool|null isEncrypted()
* @method void setEncrypted(bool|null $encrypted)
* @method bool getMentionsMe()
* @method void setMentionsMe(bool $isMentionned)
*/
class Message extends Entity implements JsonSerializable {
private const MUTABLE_FLAGS = [
Expand Down Expand Up @@ -109,6 +111,7 @@ class Message extends Entity implements JsonSerializable {
protected $imipMessage = false;
protected $imipProcessed = false;
protected $imipError = false;
protected $mentionsMe = false;

/**
* @var bool|null
Expand Down Expand Up @@ -323,6 +326,7 @@ static function (Tag $tag) {
'imipMessage' => $this->isImipMessage(),
'previewText' => $this->getPreviewText(),
'encrypted' => ($this->isEncrypted() === true),
'mentionsMe' => $this->getMentionsMe(),
];
}
}
8 changes: 8 additions & 0 deletions lib/Db/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ public function updatePreviewDataBulk(Message ...$messages): array {
->set('updated_at', $query->createNamedParameter($this->timeFactory->getTime(), IQueryBuilder::PARAM_INT))
->set('imip_message', $query->createParameter('imip_message'))
->set('encrypted', $query->createParameter('encrypted'))
->set('mentions_me', $query->createParameter('mentions_me'))
->where($query->expr()->andX(
$query->expr()->eq('uid', $query->createParameter('uid')),
$query->expr()->eq('mailbox_id', $query->createParameter('mailbox_id'))
Expand Down Expand Up @@ -593,6 +594,7 @@ public function updatePreviewDataBulk(Message ...$messages): array {
);
$query->setParameter('imip_message', $message->isImipMessage(), IQueryBuilder::PARAM_BOOL);
$query->setParameter('encrypted', $message->isEncrypted(), IQueryBuilder::PARAM_BOOL);
$query->setParameter('mentions_me', $message->getMentionsMe(), IQueryBuilder::PARAM_BOOL);

$query->executeStatement();
}
Expand Down Expand Up @@ -955,6 +957,12 @@ public function findIdsByQuery(Mailbox $mailbox, SearchQuery $query, string $sor
);
}

if ($query->getMentionsMe()) {
$select->andWhere(
$qb->expr()->eq('m.mentions_me', $qb->createNamedParameter($query->getMentionsMe(), IQueryBuilder::PARAM_BOOL))
);
}

if ($query->getCursor() !== null && $sortOrder === IMailSearch::ORDER_NEWEST_FIRST) {
$select->andWhere(
$qb->expr()->lt('m.sent_at', $qb->createNamedParameter($query->getCursor(), IQueryBuilder::PARAM_INT))
Expand Down
34 changes: 27 additions & 7 deletions lib/IMAP/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class MessageMapper {

public function __construct(LoggerInterface $logger,
SmimeService $smimeService,
ImapMessageFetcherFactory $imapMessageFactory) {
ImapMessageFetcherFactory $imapMessageFactory, ) {
$this->logger = $logger;
$this->smimeService = $smimeService;
$this->imapMessageFactory = $imapMessageFactory;
Expand Down Expand Up @@ -858,7 +858,8 @@ private function buildAttachmentsPartsQuery(Horde_Mime_Part $structure, array $a
*/
public function getBodyStructureData(Horde_Imap_Client_Socket $client,
string $mailbox,
array $uids): array {
array $uids,
string $emailAddress): array {
$structureQuery = new Horde_Imap_Client_Fetch_Query();
$structureQuery->structure();
$structureQuery->headerText([
Expand All @@ -870,8 +871,7 @@ public function getBodyStructureData(Horde_Imap_Client_Socket $client,
$structures = $client->fetch($mailbox, $structureQuery, [
'ids' => new Horde_Imap_Client_Ids($uids),
]);

return array_map(function (Horde_Imap_Client_Data_Fetch $fetchData) use ($mailbox, $client) {
return array_map(function (Horde_Imap_Client_Data_Fetch $fetchData) use ($mailbox, $client, $emailAddress) {
$hasAttachments = false;
$text = '';
$isImipMessage = false;
Expand Down Expand Up @@ -901,7 +901,7 @@ public function getBodyStructureData(Horde_Imap_Client_Socket $client,
$textBodyId = $structure->findBody() ?? $structure->findBody('text');
$htmlBodyId = $structure->findBody('html');
if ($textBodyId === null && $htmlBodyId === null) {
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted);
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted, false);
}
$partsQuery = new Horde_Imap_Client_Fetch_Query();
if ($htmlBodyId !== null) {
Expand All @@ -927,7 +927,7 @@ public function getBodyStructureData(Horde_Imap_Client_Socket $client,
$part = $parts[$fetchData->getUid()];
// This is sus - why does this even happen? A delete / move in the middle of this processing?
if ($part === null) {
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted);
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted, false);
}


Expand All @@ -939,12 +939,14 @@ public function getBodyStructureData(Horde_Imap_Client_Socket $client,
$structure->setContents($htmlBody);
$htmlBody = $structure->getContents();
}
$mentionsUser = $this->checkLinks($htmlBody, $emailAddress);
$html = new Html2Text($htmlBody, ['do_links' => 'none','alt_image' => 'hide']);
return new MessageStructureData(
$hasAttachments,
trim($html->getText()),
$isImipMessage,
$isEncrypted,
$mentionsUser,
);
}
$textBody = $part->getBodyPart($textBodyId);
Expand All @@ -961,9 +963,27 @@ public function getBodyStructureData(Horde_Imap_Client_Socket $client,
$textBody,
$isImipMessage,
$isEncrypted,
false,
);
}
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted);
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted, false);
}, iterator_to_array($structures->getIterator()));
}
private function checkLinks(string $body, string $mailAddress) : bool {
if (empty($body)) {
return false;
}
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($body);
libxml_use_internal_errors();
$anchors = $dom->getElementsByTagName('a');
foreach ($anchors as $anchor) {
$href = $anchor->getAttribute('href');
if($href === 'mailto:'.$mailAddress) {
return true;
}
}
return false;
}
}
9 changes: 8 additions & 1 deletion lib/IMAP/MessageStructureData.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@ class MessageStructureData {
private $isImipMessage;

private bool $isEncrypted;
private bool $mentionsMe;

public function __construct(bool $hasAttachments,
string $previewText,
bool $isImipMessage,
bool $isEncrypted) {
bool $isEncrypted,
bool $mentionsMe) {
$this->hasAttachments = $hasAttachments;
$this->previewText = $previewText;
$this->isImipMessage = $isImipMessage;
$this->isEncrypted = $isEncrypted;
$this->mentionsMe = $mentionsMe;
}

public function hasAttachments(): bool {
Expand All @@ -46,4 +49,8 @@ public function isImipMessage(): bool {
public function isEncrypted(): bool {
return $this->isEncrypted;
}

public function getMentionsMe(): bool {
return $this->mentionsMe;
}
}
4 changes: 3 additions & 1 deletion lib/IMAP/PreviewEnhancer.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public function process(Account $account, Mailbox $mailbox, array $messages): ar
$data = $this->imapMapper->getBodyStructureData(
$client,
$mailbox->getName(),
$needAnalyze
$needAnalyze,
$account->getEMailAddress()
);
} catch (Horde_Imap_Client_Exception $e) {
// Ignore for now, but log
Expand All @@ -94,6 +95,7 @@ public function process(Account $account, Mailbox $mailbox, array $messages): ar
$message->setStructureAnalyzed(true);
$message->setImipMessage($structureData->isImipMessage());
$message->setEncrypted($structureData->isEncrypted());
$message->setMentionsMe($structureData->getMentionsMe());

return $message;
}, $messages));
Expand Down
43 changes: 43 additions & 0 deletions lib/Migration/Version4001Date20241009140707.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version4001Date20241009140707 extends SimpleMigrationStep {


/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {

/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

$messagesTable = $schema->getTable('mail_messages');
if (!$messagesTable->hasColumn('mentions_me')) {
$messagesTable->addColumn('mentions_me', Types::BOOLEAN, [
'notnull' => false,
'default' => false,
]);
}

return $schema;
}

}
5 changes: 5 additions & 0 deletions lib/Service/Search/FilterStringParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ private function parseFilterToken(SearchQuery $query, string $token): bool {
case 'match':
$query->setMatch($param);
return true;
case 'mentions':
if ($param === 'true') {
$query->setMentionsMe(true);
}
return true;
case 'flags':
$flagArray = explode(',', $param);
foreach ($flagArray as $flagItem) {
Expand Down
17 changes: 17 additions & 0 deletions lib/Service/Search/SearchQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class SearchQuery {
/** @var bool */
private $hasAttachments = false;

/** @var bool */
private $mentionsMe = false;

private string $match = 'allof';

/**
Expand Down Expand Up @@ -230,4 +233,18 @@ public function getHasAttachments(): ?bool {
public function setHasAttachments(bool $hasAttachments): void {
$this->hasAttachments = $hasAttachments;
}

/**
* @return bool
*/
public function getMentionsMe(): bool {
return $this->mentionsMe;
}

/**
* @param bool $hasAttachments
*/
public function setMentionsMe(bool $mentionsMe): void {
$this->mentionsMe = $mentionsMe;
}
}
11 changes: 8 additions & 3 deletions src/components/SearchMessages.vue
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@
{{ t('mail', 'Has attachments') }}
</NcCheckboxRadioSwitch>
</div>
<div class="modal-inner-inline">
<NcCheckboxRadioSwitch :checked.sync="mentionsMe">
{{ t('mail', 'Mentions me') }}
</NcCheckboxRadioSwitch>
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -346,6 +351,7 @@ export default {
searchInSubject: null,
searchInMessageBody: null,
searchFlags: [],
mentionsMe: false,
hasAttachmentActive: false,
hasLast7daysActive: false,
hasFromMeActive: false,
Expand Down Expand Up @@ -406,6 +412,7 @@ export default {
body: this.searchInMessageBody !== null && this.searchInMessageBody.length > 1 ? this.searchInMessageBody : '',
tags: this.selectedTags.length > 0 ? this.selectedTags.map(item => item.id) : '',
flags: this.searchFlags.length > 0 ? this.searchFlags.map(item => item) : '',
mentions: this.mentionsMe,
start: this.prepareStart(),
end: this.prepareEnd(),
}
Expand Down Expand Up @@ -537,6 +544,7 @@ export default {
this.searchFlags = []
this.startDate = null
this.endDate = null
this.mentionsMe = false
// Need if there is only tag filter or recipients filter
if (prevQuery === '') {
this.sendQueryEvent()
Expand Down Expand Up @@ -661,9 +669,6 @@ export default {
display: inline-block;
width: 32%;
&:last-child {
width: 100%;
}
}
.range {
display: flex;
Expand Down

0 comments on commit c40ec07

Please sign in to comment.