Skip to content

Commit

Permalink
new: Allow to add tags to links
Browse files Browse the repository at this point in the history
  • Loading branch information
marienfressinaud committed Sep 20, 2024
1 parent e002613 commit 6870e57
Show file tree
Hide file tree
Showing 14 changed files with 266 additions and 5 deletions.
Binary file modified locales/fr_FR/LC_MESSAGES/main.mo
Binary file not shown.
10 changes: 5 additions & 5 deletions locales/fr_FR/LC_MESSAGES/main.po
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: Flus\n"
"POT-Creation-Date: 2024-09-19 15:03+0200\n"
"PO-Revision-Date: 2024-09-19 15:03+0200\n"
"POT-Creation-Date: 2024-09-20 15:22+0200\n"
"PO-Revision-Date: 2024-09-20 15:22+0200\n"
"Last-Translator: Marien Fressinaud <dev@marienfressinaud.fr>\n"
"Language-Team: \n"
"Language: fr_FR\n"
Expand Down Expand Up @@ -76,13 +76,13 @@ msgstr "L’une des thématiques associées n’existe pas."
#: controllers/Collections.php:388 controllers/Exportations.php:62
#: controllers/Groups.php:161 controllers/Importations.php:48
#: controllers/Links.php:499 controllers/Mastodon.php:87
#: controllers/Messages.php:93 controllers/Messages.php:141
#: controllers/Messages.php:94 controllers/Messages.php:145
#: controllers/Sessions.php:206 controllers/collections/Read.php:87
#: controllers/collections/Read.php:168 controllers/collections/Read.php:243
#: controllers/importations/Opml.php:79 controllers/importations/Pocket.php:110
#: controllers/importations/Pocket.php:157
#: controllers/importations/Pocket.php:258
#: controllers/links/Collections.php:158 controllers/links/Read.php:46
#: controllers/links/Collections.php:159 controllers/links/Read.php:46
#: controllers/links/Read.php:95 controllers/links/Read.php:144
#: controllers/links/Read.php:192 controllers/my/Avatar.php:44
msgid "A security verification failed."
Expand All @@ -104,7 +104,7 @@ msgstr "Vous avez déjà un groupe portant le même nom."
msgid "The link must be associated to a collection."
msgstr "Le lien doit être associé à une collection."

#: controllers/Links.php:320 controllers/links/Collections.php:153
#: controllers/Links.php:320 controllers/links/Collections.php:154
msgid "One of the associated collection doesn’t exist."
msgstr "L’une des collections associées n’existe pas."

Expand Down
6 changes: 6 additions & 0 deletions src/controllers/Messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Minz\Response;
use App\auth;
use App\models;
use App\services;

/**
* @author Marien Fressinaud <dev@marienfressinaud.fr>
Expand Down Expand Up @@ -104,6 +105,8 @@ public function update(Request $request): Response

$message->save();

services\LinkTags::refresh($message->link());

return Response::found($from);
}

Expand Down Expand Up @@ -133,6 +136,7 @@ public function delete(Request $request): Response
'id' => $message_id,
'user_id' => $user->id,
]);

if (!$message) {
return Response::notFound('not_found.phtml');
}
Expand All @@ -144,6 +148,8 @@ public function delete(Request $request): Response

models\Message::delete($message->id);

services\LinkTags::refresh($message->link());

return Response::found($redirect_to);
}
}
3 changes: 3 additions & 0 deletions src/controllers/links/Collections.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\auth;
use App\jobs;
use App\models;
use App\services;
use App\utils;

/**
Expand Down Expand Up @@ -191,6 +192,8 @@ public function update(Request $request): Response
models\LinkToCollection::markAsRead($user, [$link->id]);
}

services\LinkTags::refresh($link);

$mastodon_configured = models\MastodonAccount::existsBy([
'user_id' => $user->id,
]);
Expand Down
2 changes: 2 additions & 0 deletions src/controllers/links/Messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ public function create(Request $request): Response

$message->save();

services\LinkTags::refresh($link);

if ($mastodon_configured && $share_on_mastodon) {
$share_on_mastodon_job = new jobs\ShareOnMastodon();
$share_on_mastodon_job->performAsap($user->id, $link->id, $message->id);
Expand Down
46 changes: 46 additions & 0 deletions src/migrations/Migration202409200001AddTagsToLinks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace App\migrations;

use App\models;
use App\services;

class Migration202409200001AddTagsToLinks
{
public function migrate(): bool
{
$database = \Minz\Database::get();

$database->exec(<<<'SQL'
ALTER TABLE links
ADD COLUMN tags JSON NOT NULL DEFAULT '[]';
SQL);

$database = \Minz\Database::get();
$statement = $database->query(<<<'SQL'
SELECT l.* FROM links l, messages m
WHERE l.id = m.link_id
AND m.content LIKE '%#%'
SQL);

$links = models\Link::fromDatabaseRows($statement->fetchAll());

foreach ($links as $link) {
services\LinkTags::refresh($link);
}

return true;
}

public function rollback(): bool
{
$database = \Minz\Database::get();

$database->exec(<<<'SQL'
ALTER TABLE links
DROP COLUMN tags;
SQL);

return true;
}
}
4 changes: 4 additions & 0 deletions src/models/Link.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ class Link
#[Database\Column]
public bool $group_by_source = false;

/** @var string[] */
#[Database\Column]
public array $tags = [];

#[Database\Column(computed: true)]
public ?string $source_news_type = null;

Expand Down
14 changes: 14 additions & 0 deletions src/models/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ public function user(): User
return $user;
}

/**
* Return the link of the message
*/
public function link(): Link
{
$link = Link::find($this->link_id);

if (!$link) {
throw new \Exception("Message #{$this->id} has invalid link.");
}

return $link;
}

/**
* Return a tag URI that can be used as Atom id
*
Expand Down
1 change: 1 addition & 0 deletions src/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ CREATE TABLE links (
url_replies TEXT NOT NULL DEFAULT '',
reading_time INTEGER NOT NULL DEFAULT 0,
image_filename TEXT,
tags JSON NOT NULL DEFAULT '[]',

fetched_at TIMESTAMPTZ,
fetched_code INTEGER NOT NULL DEFAULT 0,
Expand Down
46 changes: 46 additions & 0 deletions src/services/LinkTags.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace App\services;

use App\models;

/**
* @author Marien Fressinaud <dev@marienfressinaud.fr>
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL
*/
class LinkTags
{
public const TAG_REGEX = '/(?:^|[^\pL\pN_])#(?P<tag>[\pL\pN_]+)/u';

public static function refresh(models\Link $link): void
{
$messages = $link->messages();

$tags = [];

foreach ($messages as $message) {
$message_tags = self::extractTags($message->content);
$tags = array_merge($tags, $message_tags);
}

$tags = array_unique($tags);

$link->tags = $tags;

$link->save();
}

/**
* @return string[]
*/
public static function extractTags(string $content): array
{
$result = preg_match_all(self::TAG_REGEX, $content, $matches);

if ($result === false) {
return [];
}

return $matches['tag'];
}
}
51 changes: 51 additions & 0 deletions tests/controllers/MessagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\controllers;

use App\models;
use tests\factories\LinkFactory;
use tests\factories\MessageFactory;
use tests\factories\UserFactory;

Expand Down Expand Up @@ -111,6 +112,34 @@ public function testUpdateChangesContentAndRedirect(): void
$this->assertSame($new_content, $message->content);
}

public function testUpdateChangesLinkTags(): void
{
$user = $this->login();
$link = LinkFactory::create([
'tags' => ['foo'],
]);
$old_content = '#foo';
$new_content = '#bar';
$message = MessageFactory::create([
'user_id' => $user->id,
'link_id' => $link->id,
'content' => $old_content,
]);
$from = \Minz\Url::for('home');

$response = $this->appRun('POST', "/messages/{$message->id}/edit", [
'content' => $new_content,
'csrf' => $user->csrf,
'from' => $from,
]);

$this->assertResponseCode($response, 302, $from);
$message = $message->reload();
$this->assertSame($new_content, $message->content);
$link = $link->reload();
$this->assertEquals(['bar'], $link->tags);
}

public function testUpdateRedirectsToLoginIfNotConnected(): void
{
$user = UserFactory::create([
Expand Down Expand Up @@ -252,6 +281,28 @@ public function testDeleteDeletesMessageAndRedirects(): void
$this->assertFalse(models\Message::exists($message->id));
}

public function testDeleteChangesLinkTags(): void
{
$user = $this->login();
$link = LinkFactory::create([
'tags' => ['foo'],
]);
$message = MessageFactory::create([
'user_id' => $user->id,
'link_id' => $link->id,
'content' => '#foo',
]);

$response = $this->appRun('POST', "/messages/{$message->id}/delete", [
'csrf' => $user->csrf,
]);

$this->assertResponseCode($response, 302, '/');
$this->assertFalse(models\Message::exists($message->id));
$link = $link->reload();
$this->assertEquals([], $link->tags);
}

public function testDeleteRedirectsToRedirectToIfGiven(): void
{
$user = $this->login();
Expand Down
24 changes: 24 additions & 0 deletions tests/controllers/links/CollectionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,30 @@ public function testUpdateCreatesComment(): void
$this->assertSame($link->id, $message->link_id);
}

public function testUpdateChangesTags(): void
{
$user = $this->login();
$link = LinkFactory::create([
'user_id' => $user->id,
'tags' => [],
]);
$collection = CollectionFactory::create([
'user_id' => $user->id,
'type' => 'collection',
]);
$comment = '#foo #bar';

$response = $this->appRun('POST', "/links/{$link->id}/collections", [
'csrf' => $user->csrf,
'from' => \Minz\Url::for('bookmarks'),
'collection_ids' => [$collection->id],
'comment' => $comment,
]);

$link = $link->reload();
$this->assertEquals(['foo', 'bar'], $link->tags);
}

public function testUpdateCanMarkAsRead(): void
{
$user = $this->login();
Expand Down
21 changes: 21 additions & 0 deletions tests/controllers/links/MessagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,27 @@ public function testCreateCreatesMessageAndRedirects(): void
$this->assertSame($link->id, $message->link_id);
}

public function testCreateChangesLinkTags(): void
{
$user = $this->login();
$link = LinkFactory::create([
'user_id' => $user->id,
'tags' => [],
]);
$content = '#foo #bar';

$this->assertSame(0, models\Message::count());

$response = $this->appRun('POST', "/links/{$link->id}/messages", [
'csrf' => $user->csrf,
'content' => $content,
]);

$this->assertResponseCode($response, 302, "/links/{$link->id}");
$link = $link->reload();
$this->assertEquals(['foo', 'bar'], $link->tags);
}

public function testCreateWorksIfLinkIsInCollectionSharedWithWriteAccess(): void
{
$user = $this->login();
Expand Down
Loading

0 comments on commit 6870e57

Please sign in to comment.