diff --git a/controller/manifest.php b/controller/manifest.php index 26a42a0..05058f8 100644 --- a/controller/manifest.php +++ b/controller/manifest.php @@ -15,6 +15,7 @@ use phpbb\language\language; use phpbb\path_helper; use phpbb\user; +use phpbb\webpushnotifications\ext; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -63,9 +64,13 @@ public function handle(): JsonResponse $board_path = $this->config['force_server_vars'] ? $this->config['script_path'] : $this->path_helper->get_web_root_path(); $board_url = generate_board_url(); + // Emoji fixer-uppers + $sitename = ext::decode_entities($this->config['sitename'], ENT_QUOTES); + $pwa_short_name = ext::decode_entities($this->config['pwa_short_name'], ENT_QUOTES); + $manifest = [ - 'name' => $this->config['sitename'], - 'short_name' => $this->config['pwa_short_name'] ?: utf8_substr(preg_replace('/[^\x21-\x7E]/', '', html_entity_decode($this->config['sitename'], ENT_QUOTES, 'UTF-8')), 0, 12), + 'name' => $sitename, + 'short_name' => $pwa_short_name ?: utf8_substr($sitename, 0, 12), 'display' => 'standalone', 'orientation' => 'portrait', 'dir' => $this->language->lang('DIRECTION'), diff --git a/event/listener.php b/event/listener.php index 0f42528..43e90a1 100644 --- a/event/listener.php +++ b/event/listener.php @@ -17,6 +17,7 @@ use phpbb\notification\manager; use phpbb\template\template; use phpbb\user; +use phpbb\webpushnotifications\ext; use phpbb\webpushnotifications\form\form_helper; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -85,6 +86,7 @@ public static function getSubscribedEvents() 'core.ucp_display_module_before' => 'load_language', 'core.acp_main_notice' => 'compatibility_notice', 'core.acp_board_config_edit_add' => 'acp_pwa_options', + 'core.acp_board_config_emoji_enabled'=> 'acp_pwa_allow_emoji', 'core.validate_config_variable' => 'validate_pwa_options', 'core.help_manager_add_block_after' => 'wpn_faq', ]; @@ -143,7 +145,7 @@ public function pwa_manifest() $this->template->assign_vars([ 'U_MANIFEST_URL' => $this->controller_helper->route('phpbb_webpushnotifications_manifest_controller'), 'U_TOUCH_ICON' => $this->config['pwa_icon_small'], - 'SHORT_SITE_NAME' => $this->config['pwa_short_name'] ?: $this->get_shortname($this->config['sitename']), + 'SHORT_SITE_NAME' => $this->config['pwa_short_name'] ?: $this->trim_shortname($this->config['sitename']), ]); } @@ -170,6 +172,24 @@ public function acp_pwa_options($event) } } + /** + * Allow PWA short name ACP field to accept emoji characters + * + * @param \phpbb\event\data $event + * @return void + */ + public function acp_pwa_allow_emoji($event) + { + if (in_array('pwa_short_name', $event['config_name_ary'], true)) + { + return; + } + + $config_name_ary = $event['config_name_ary']; + $config_name_ary[] = 'pwa_short_name'; + $event['config_name_ary'] = $config_name_ary; + } + /** * Return HTML for PWA icon name settings * @@ -191,7 +211,7 @@ public function pwa_icon_name($value, $key) */ public function pwa_short_sitename($value, $key) { - $placeholder = $this->get_shortname($this->config['sitename']); + $placeholder = $this->trim_shortname($this->config['sitename']); return ''; } @@ -223,17 +243,10 @@ public function validate_pwa_options($event) return; } - $short_name = $event['cfg_array']['pwa_short_name']; - - // Do not allow multibyte characters or emoji - if (strlen($short_name) !== mb_strlen($short_name, 'UTF-8')) - { - $this->add_error($event, 'PWA_SHORT_NAME_INVALID'); - return; - } + $short_name = ext::decode_entities($event['cfg_array']['pwa_short_name'], ENT_QUOTES); // Do not allow strings longer than 12 characters - if (strlen($short_name) > 12) + if (utf8_strlen($short_name) > 12) { $this->add_error($event, 'PWA_SHORT_NAME_INVALID'); return; @@ -346,13 +359,15 @@ protected function can_use_notifications() } /** - * Get short name from a string (strip out multibyte characters and trim to 12 characters) + * Trim short name from a string to 12 characters * * @param string $name * @return string 12 max characters string */ - protected function get_shortname($name) + protected function trim_shortname($name) { - return utf8_substr(preg_replace('/[^\x20-\x7E]/', '', $name), 0, 12); + $decoded = ext::decode_entities($name, ENT_QUOTES); + $trimmed = utf8_substr($decoded, 0, 12); + return htmlspecialchars($trimmed, ENT_QUOTES, 'UTF-8'); } } diff --git a/ext.php b/ext.php index 2211923..bb19ddc 100644 --- a/ext.php +++ b/ext.php @@ -124,4 +124,17 @@ protected function result() return false; } + + /** + * Decode entities, used primarily to fix emoji for display + * + * @param string $text + * @param int $flags Uses ENT_NOQUOTES to leave single and double quotes encoded by default + * @param string $encoding + * @return string Decoded string + */ + public static function decode_entities($text, $flags = ENT_NOQUOTES, $encoding = 'UTF-8') + { + return html_entity_decode($text, $flags, $encoding); + } } diff --git a/language/en/webpushnotifications_common_acp.php b/language/en/webpushnotifications_common_acp.php index 27aac0c..4e408ef 100644 --- a/language/en/webpushnotifications_common_acp.php +++ b/language/en/webpushnotifications_common_acp.php @@ -41,7 +41,7 @@ 'PWA_SETTINGS' => 'Progressive web application options', 'PWA_SHORT_NAME' => 'Short site name', 'PWA_SHORT_NAME_EXPLAIN' => 'Your site name in 12 characters or fewer, which may be used as a label for an icon on a mobile device’s home screen. (If this field is left empty, the first 12 characters of the Site name will be used.)', - 'PWA_SHORT_NAME_INVALID' => '“Short site name” contains illegal characters or exceeds the 12 character limit.', + 'PWA_SHORT_NAME_INVALID' => '“Short site name” exceeds the 12 character limit.', 'PWA_ICON_SMALL' => 'Small mobile device icon', 'PWA_ICON_SMALL_EXPLAIN' => 'File name of a 192px x 192px PNG image. This file must be uploaded to your board’s icons directory.', 'PWA_ICON_LARGE' => 'Large mobile device icon', diff --git a/tests/event/listener_test.php b/tests/event/listener_test.php index 1bcb393..714d547 100644 --- a/tests/event/listener_test.php +++ b/tests/event/listener_test.php @@ -149,6 +149,7 @@ public function test_getSubscribedEvents() 'core.ucp_display_module_before', 'core.acp_main_notice', 'core.acp_board_config_edit_add', + 'core.acp_board_config_emoji_enabled', 'core.validate_config_variable', 'core.help_manager_add_block_after', ], array_keys(\phpbb\webpushnotifications\event\listener::getSubscribedEvents())); @@ -376,7 +377,6 @@ public function test_acp_pwa_options($mode, $display_vars, $expected_keys) $keys = array_keys($display_vars['vars']); self::assertEquals($expected_keys, $keys); - } public function validate_pwa_options_data() @@ -420,6 +420,21 @@ public function validate_pwa_options_data() [ 'pwa_options:string', ['pwa_short_name' => 'foo❤️'], + [], + ], + [ + 'pwa_options:string', + ['pwa_short_name' => 'Фаны phpBB'], + [], + ], + [ + 'pwa_options:string', + ['pwa_short_name' => 'Фаны phpBB Board'], + ['PWA_SHORT_NAME_INVALID'], + ], + [ + 'pwa_options:string', + ['pwa_short_name' => 'foo❤️bar foo bar'], ['PWA_SHORT_NAME_INVALID'], ], [ @@ -472,6 +487,24 @@ public function test_validate_pwa_options($validate, $cfg_array, $expected_error self::assertEquals($expected_error, $error); } + public function test_acp_pwa_allow_emoji() + { + $config_name_ary = ['foo']; + $expected = ['foo', 'pwa_short_name']; + + $this->set_listener(); + + $dispatcher = new \phpbb\event\dispatcher(); + $dispatcher->addListener('core.acp_board_config_emoji_enabled', [$this->listener, 'acp_pwa_allow_emoji']); + + $event_data = ['config_name_ary']; + $event_data_after = $dispatcher->trigger_event('core.acp_board_config_emoji_enabled', compact($event_data)); + + extract($event_data_after, EXTR_OVERWRITE); + + self::assertEquals($expected, $config_name_ary); + } + public function test_wpn_faq() { $this->language->add_lang('webpushnotifications_faq', 'phpbb/webpushnotifications'); diff --git a/ucp/controller/webpush.php b/ucp/controller/webpush.php index 08b3961..78f7d40 100644 --- a/ucp/controller/webpush.php +++ b/ucp/controller/webpush.php @@ -16,6 +16,7 @@ use phpbb\exception\http_exception; use phpbb\language\language; use phpbb\notification\manager; +use phpbb\webpushnotifications\ext; use phpbb\webpushnotifications\form\form_helper; use phpbb\webpushnotifications\json\sanitizer as json_sanitizer; use phpbb\path_helper; @@ -240,8 +241,8 @@ private function get_notification_data(string $notification_data): string return json_encode([ 'heading' => $this->config['sitename'], - 'title' => strip_tags(html_entity_decode($notification->get_title(), ENT_NOQUOTES, 'UTF-8')), - 'text' => strip_tags(html_entity_decode($notification->get_reference(), ENT_NOQUOTES, 'UTF-8')), + 'title' => strip_tags(ext::decode_entities($notification->get_title())), + 'text' => strip_tags(ext::decode_entities($notification->get_reference())), 'url' => htmlspecialchars_decode($notification->get_url()), 'avatar' => $this->prepare_avatar($notification->get_avatar()), ]);