aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblizzz <blizzz@arthur-schiwon.de>2022-03-24 17:36:23 +0100
committerGitHub <noreply@github.com>2022-03-24 17:36:23 +0100
commit37106e03b0db3caf4177a8bbb8e5dd942419abd3 (patch)
tree3bda8068ca7d62b4e742c3a4ab629633c1ac35a0
parent04d4666c37a98f969813d0d43ceed007a6a6c391 (diff)
parent3b24a4f217a3915a7a1bb0684c2a6c2a4ac85d40 (diff)
downloadnextcloud-server-37106e03b0db3caf4177a8bbb8e5dd942419abd3.tar.gz
nextcloud-server-37106e03b0db3caf4177a8bbb8e5dd942419abd3.zip
Merge pull request #31700 from nextcloud/bugfix/noid/allow-4-char-emojis
Allow reactions also with other combined emojis 🧑🏾‍💻
-rw-r--r--lib/composer/composer/autoload_classmap.php1
-rw-r--r--lib/composer/composer/autoload_static.php1
-rw-r--r--lib/private/Comments/EmojiHelper.php101
-rw-r--r--lib/private/Comments/Manager.php10
-rw-r--r--tests/lib/Comments/ManagerTest.php14
5 files changed, 118 insertions, 9 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 2ce5d448518..feaa60a4ca6 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -808,6 +808,7 @@ return array(
'OC\\Command\\FileAccess' => $baseDir . '/lib/private/Command/FileAccess.php',
'OC\\Command\\QueueBus' => $baseDir . '/lib/private/Command/QueueBus.php',
'OC\\Comments\\Comment' => $baseDir . '/lib/private/Comments/Comment.php',
+ 'OC\\Comments\\EmojiHelper' => $baseDir . '/lib/private/Comments/EmojiHelper.php',
'OC\\Comments\\Manager' => $baseDir . '/lib/private/Comments/Manager.php',
'OC\\Comments\\ManagerFactory' => $baseDir . '/lib/private/Comments/ManagerFactory.php',
'OC\\Config' => $baseDir . '/lib/private/Config.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 4c35c1d1f85..795742f87f8 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -837,6 +837,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Command\\FileAccess' => __DIR__ . '/../../..' . '/lib/private/Command/FileAccess.php',
'OC\\Command\\QueueBus' => __DIR__ . '/../../..' . '/lib/private/Command/QueueBus.php',
'OC\\Comments\\Comment' => __DIR__ . '/../../..' . '/lib/private/Comments/Comment.php',
+ 'OC\\Comments\\EmojiHelper' => __DIR__ . '/../../..' . '/lib/private/Comments/EmojiHelper.php',
'OC\\Comments\\Manager' => __DIR__ . '/../../..' . '/lib/private/Comments/Manager.php',
'OC\\Comments\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/Comments/ManagerFactory.php',
'OC\\Config' => __DIR__ . '/../../..' . '/lib/private/Config.php',
diff --git a/lib/private/Comments/EmojiHelper.php b/lib/private/Comments/EmojiHelper.php
new file mode 100644
index 00000000000..a75919edbc9
--- /dev/null
+++ b/lib/private/Comments/EmojiHelper.php
@@ -0,0 +1,101 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2020, Georg Ehrke
+ *
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\Comments;
+
+use OCP\IDBConnection;
+
+/**
+ * Copied OCA\UserStatus\Service\EmojiService
+ * Needs to be unified later
+ */
+class EmojiHelper {
+
+ /** @var IDBConnection */
+ private $db;
+
+ /**
+ * EmojiService constructor.
+ *
+ * @param IDBConnection $db
+ */
+ public function __construct(IDBConnection $db) {
+ $this->db = $db;
+ }
+
+ /**
+ * @return bool
+ */
+ public function doesPlatformSupportEmoji(): bool {
+ return $this->db->supports4ByteText() &&
+ \class_exists(\IntlBreakIterator::class);
+ }
+
+ /**
+ * @param string $emoji
+ * @return bool
+ */
+ public function isValidEmoji(string $emoji): bool {
+ $intlBreakIterator = \IntlBreakIterator::createCharacterInstance();
+ $intlBreakIterator->setText($emoji);
+
+ $characterCount = 0;
+ while ($intlBreakIterator->next() !== \IntlBreakIterator::DONE) {
+ $characterCount++;
+ }
+
+ if ($characterCount !== 1) {
+ return false;
+ }
+
+ $codePointIterator = \IntlBreakIterator::createCodePointInstance();
+ $codePointIterator->setText($emoji);
+
+ foreach ($codePointIterator->getPartsIterator() as $codePoint) {
+ $codePointType = \IntlChar::charType($codePoint);
+
+ // If the current code-point is an emoji or a modifier (like a skin-tone)
+ // just continue and check the next character
+ if ($codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_SYMBOL ||
+ $codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_LETTER ||
+ $codePointType === \IntlChar::CHAR_CATEGORY_OTHER_SYMBOL ||
+ $codePointType === \IntlChar::CHAR_CATEGORY_GENERAL_OTHER_TYPES) {
+ continue;
+ }
+
+ // If it's neither a modifier nor an emoji, we only allow
+ // a zero-width-joiner or a variation selector 16
+ $codePointValue = \IntlChar::ord($codePoint);
+ if ($codePointValue === 8205 || $codePointValue === 65039) {
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php
index 384187accf3..123e4f6988d 100644
--- a/lib/private/Comments/Manager.php
+++ b/lib/private/Comments/Manager.php
@@ -59,6 +59,9 @@ class Manager implements ICommentsManager {
/** @var ITimeFactory */
protected $timeFactory;
+ /** @var EmojiHelper */
+ protected $emojiHelper;
+
/** @var IInitialStateService */
protected $initialStateService;
@@ -78,11 +81,13 @@ class Manager implements ICommentsManager {
LoggerInterface $logger,
IConfig $config,
ITimeFactory $timeFactory,
+ EmojiHelper $emojiHelper,
IInitialStateService $initialStateService) {
$this->dbConn = $dbConn;
$this->logger = $logger;
$this->config = $config;
$this->timeFactory = $timeFactory;
+ $this->emojiHelper = $emojiHelper;
$this->initialStateService = $initialStateService;
}
@@ -148,8 +153,9 @@ class Manager implements ICommentsManager {
throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving');
}
- if ($comment->getVerb() === 'reaction' && mb_strlen($comment->getMessage()) > 2) {
- throw new \UnexpectedValueException('Reactions cannot be longer than 2 chars (emoji with skin tone have two chars)');
+ if ($comment->getVerb() === 'reaction' && !$this->emojiHelper->isValidEmoji($comment->getMessage())) {
+ // 4 characters: laptop + person + gender + skin color => "🧑🏽‍💻" is a single emoji from the picker
+ throw new \UnexpectedValueException('Reactions can only be a single emoji');
}
if ($comment->getId() === '') {
diff --git a/tests/lib/Comments/ManagerTest.php b/tests/lib/Comments/ManagerTest.php
index 23a9346909a..961f8dfdd41 100644
--- a/tests/lib/Comments/ManagerTest.php
+++ b/tests/lib/Comments/ManagerTest.php
@@ -3,6 +3,7 @@
namespace Test\Comments;
use OC\Comments\Comment;
+use OC\Comments\EmojiHelper;
use OC\Comments\Manager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\IComment;
@@ -74,6 +75,7 @@ class ManagerTest extends TestCase {
$this->createMock(LoggerInterface::class),
$this->createMock(IConfig::class),
$this->createMock(ITimeFactory::class),
+ new EmojiHelper($this->connection),
$this->createMock(IInitialStateService::class)
);
}
@@ -1181,15 +1183,13 @@ class ManagerTest extends TestCase {
public function providerTestReactionMessageSize(): array {
return [
- ['a', true],
- ['1', true],
- ['12', true],
- ['123', false],
+ ['a', false],
+ ['1', false],
['👍', true],
- ['👍👍', true],
+ ['👍👍', false],
['👍🏽', true],
- ['👍🏽👍', false],
- ['👍🏽👍🏽', false],
+ ['👨🏽‍💻', true],
+ ['👨🏽‍💻👍', false],
];
}