diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ab8c853cc9..6b86318c16 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -90,5 +90,4 @@ After adding a new module to core, you need to do the following: 1. Ensure you have a clean copy of the source code without leftover files from testing. For example, clone the Nameless repository into a new directory 2. Run ./dev/scripts/release.sh. Release zip files are produced and placed in `./release`. -3. TODO: Add instructions for producing a zip only containing files changed since the last release -4. TODO: Add instructions for publishing a release +3. TODO: Add instructions for publishing a release diff --git a/CHANGELOG.md b/CHANGELOG.md index af6fc732ef..f367e0c19e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,29 @@ # NamelessMC v2 Changelog -## [Unreleased](https://github.com/NamelessMC/Nameless/compare/v2.1.1...v2) -> [Milestone](https://github.com/NamelessMC/Nameless/milestone/21) +## [Unreleased](https://github.com/NamelessMC/Nameless/compare/v2.1.2...develop) +> [Milestone](https://github.com/NamelessMC/Nameless/milestone/22) + +## [2.1.2](https://github.com/NamelessMC/Nameless/compare/v2.1.1...v2.1.2) - 2023-09-30 +### Added +- No additions this release + +### Changed +- Small misc improvements [#3389](https://github.com/NamelessMC/Nameless/pull/3389) +- Add PHP_SAPI checks on scripts [#3403](https://github.com/NamelessMC/Nameless/pull/3403) +- Rewrite release script to fix checksums in upgrade package [#3414](https://github.com/NamelessMC/Nameless/pull/3414) +- Ignore group sync request instead of returning error [#3433](https://github.com/NamelessMC/Nameless/pull/3433) +- Limit logs & support group sync from modules [#3426](https://github.com/NamelessMC/Nameless/pull/3426) +- Ignore adding group if it's invalid [#3436](https://github.com/NamelessMC/Nameless/pull/3436) +- Updated translations + +### Fixed +- Rework user group cache issue [#3398](https://github.com/NamelessMC/Nameless/pull/3398) +- Re-add deleted term + fix Discord OAuth link success message [#3403](https://github.com/NamelessMC/Nameless/pull/3403) +- Fix typo in en_US translation [#3412](https://github.com/NamelessMC/Nameless/pull/3412) +- Fix auto verify OAuth email [#3413](https://github.com/NamelessMC/Nameless/pull/3413) +- Fix forum index showing topics without view other topics permission [#3410](https://github.com/NamelessMC/Nameless/pull/3410) +- Fix phpdoc build, pin version [#3438](https://github.com/NamelessMC/Nameless/pull/3438) +- Fix single/double quote not working within member list username CSS [#3427](https://github.com/NamelessMC/Nameless/pull/3427) ## [2.1.1](https://github.com/NamelessMC/Nameless/compare/v2.1.0...v2.1.1) - 2023-06-18 ### Added diff --git a/Dockerfile.phpdoc b/Dockerfile.phpdoc index 777ca827f1..2fb04ece4a 100644 --- a/Dockerfile.phpdoc +++ b/Dockerfile.phpdoc @@ -1,11 +1,11 @@ -FROM phpdoc/phpdoc as doc_builder +FROM phpdoc/phpdoc:3.4 as doc_builder COPY . /source WORKDIR /source RUN mkdir /target && \ - phpdoc \ + phpdoc run \ -d 'core/classes' \ -d 'modules/Core/classes' \ -d 'modules/Discord Integration/classes' \ diff --git a/core/classes/Core/URL.php b/core/classes/Core/URL.php index c7b3da98df..b3e51323a2 100644 --- a/core/classes/Core/URL.php +++ b/core/classes/Core/URL.php @@ -85,6 +85,16 @@ private static function buildNonFriendly(string $url, string $params): string { return (defined('CONFIG_PATH') ? CONFIG_PATH : '') . '/index.php?route=' . $url . ((substr($url, -1) == '/') ? '' : '/'); } + /** + * Build an asset path + * + * @param string $path Contains the asset path relative to the root Nameless directory + * @return string + */ + public static function buildAssetPath(string $path): string { + return (defined('CONFIG_PATH') ? CONFIG_PATH : '') . '/' . ltrim($path, '/'); + } + /** * Get the server name. * diff --git a/core/classes/Core/User.php b/core/classes/Core/User.php index b689b32dc5..ff262f62ea 100644 --- a/core/classes/Core/User.php +++ b/core/classes/Core/User.php @@ -6,7 +6,7 @@ * @author Samerton * @author Partydragen * @author Aberdeener - * @version 2.1.1 + * @version 2.1.2 * @license MIT */ class User { @@ -134,6 +134,12 @@ public function addGroup(int $group_id, int $expire = 0): bool { return false; } + $group = Group::find($group_id); + if (!$group) { + ErrorHandler::logWarning('Could not add invalid group ' . $group_id . ' to user ' . $this->data()->id); + return false; + } + $this->_db->query('INSERT INTO `nl2_users_groups` (`user_id`, `group_id`, `received`, `expire`) VALUES (?, ?, ?, ?)', [ $this->data()->id, $group_id, @@ -141,10 +147,8 @@ public function addGroup(int $group_id, int $expire = 0): bool { $expire ]); - $group = Group::find($group_id); - if ($group) { - $this->_groups[$group_id] = $group; - } + $this->_groups[$group_id] = $group; + self::$_group_cache[$this->data()->id][$group_id] = $group; EventHandler::executeEvent(new UserGroupAddedEvent( $this, @@ -524,28 +528,26 @@ public function getGroups(): array { } if (isset(self::$_group_cache[$this->data()->id])) { - $groups_query = self::$_group_cache[$this->data()->id]; + $this->_groups = self::$_group_cache[$this->data()->id]; } else { $groups_query = $this->_db->query('SELECT nl2_groups.* FROM nl2_users_groups INNER JOIN nl2_groups ON group_id = nl2_groups.id WHERE user_id = ? AND deleted = 0 ORDER BY `order`', [$this->data()->id]); if ($groups_query->count()) { - $groups_query = $groups_query->results(); + foreach ($groups_query->results() as $item) { + $this->_groups[$item->id] = new Group($item); + } } else { - $groups_query = []; + $this->_groups = []; } - self::$_group_cache[$this->data()->id] = $groups_query; + + self::$_group_cache[$this->data()->id] = $this->_groups; } - if ($groups_query) { - foreach ($groups_query as $item) { - $this->_groups[$item->id] = new Group($item); - } - } else { + if (!count($this->_groups)) { // Get default group // TODO: Use PRE_VALIDATED_DEFAULT ? $default_group = Group::find(1, 'default_group'); $default_group_id = $default_group->id ?? 1; - $this->_groups = []; $this->addGroup($default_group_id); } @@ -659,6 +661,12 @@ public function getMainGroup(): Group { * @return false|void */ public function setGroup(int $group_id, int $expire = 0) { + $group = Group::find($group_id); + if (!$group) { + ErrorHandler::logWarning('Could not set invalid group ' . $group_id . ' to user ' . $this->data()->id); + return false; + } + if ($this->data()->id == 1) { return false; } @@ -673,10 +681,9 @@ public function setGroup(int $group_id, int $expire = 0) { ]); $this->_groups = []; - $group = Group::find($group_id); - if ($group) { - $this->_groups[$group_id] = $group; - } + $this->_groups[$group_id] = $group; + self::$_group_cache[$this->data()->id] = $this->_groups; + } /** @@ -706,6 +713,7 @@ public function removeGroup(?int $group_id): bool { )); unset($this->_groups[$group_id]); + unset(self::$_group_cache[$this->data()->id][$group_id]); return true; } diff --git a/core/classes/Database/DB.php b/core/classes/Database/DB.php index 727297fe91..a1c5d88301 100644 --- a/core/classes/Database/DB.php +++ b/core/classes/Database/DB.php @@ -474,9 +474,9 @@ public function addColumn(string $table, string $column, string $attributes): bo * column, operator (default =), value, and glue (default AND). * @return array The where clause string, and parameters to bind. */ - private function makeWhere(array $clauses): array { + public static function makeWhere(array $clauses): array { if (count($clauses) === count($clauses, COUNT_RECURSIVE)) { - return $this->makeWhere([$clauses]); + return self::makeWhere([$clauses]); } $where_clauses = []; @@ -486,7 +486,7 @@ private function makeWhere(array $clauses): array { } if (count($clause) !== count($clause, COUNT_RECURSIVE)) { - $this->makeWhere(...$clause); + self::makeWhere(...$clause); continue; } diff --git a/core/classes/Database/DatabaseInitialiser.php b/core/classes/Database/DatabaseInitialiser.php index 568b0130f1..fff95658fc 100644 --- a/core/classes/Database/DatabaseInitialiser.php +++ b/core/classes/Database/DatabaseInitialiser.php @@ -183,7 +183,7 @@ private function initialiseSettings(): void { Util::setSetting('recaptcha_type', 'Recaptcha3'); Util::setSetting('recaptcha_login', '0'); Util::setSetting('email_verification', '1'); - Util::setSetting('nameless_version', '2.1.1'); + Util::setSetting('nameless_version', '2.1.2'); Util::setSetting('version_checked', date('U')); Util::setSetting('phpmailer', '0'); Util::setSetting('user_avatars', '0'); diff --git a/core/includes/updates/211.php b/core/includes/updates/211.php new file mode 100644 index 0000000000..e176b1c176 --- /dev/null +++ b/core/includes/updates/211.php @@ -0,0 +1,8 @@ +runMigrations(); + + $this->setVersion('2.1.2'); + } +}; diff --git a/custom/languages/cs_CZ.json b/custom/languages/cs_CZ.json index db22d1e7c0..3ef1538fc1 100644 --- a/custom/languages/cs_CZ.json +++ b/custom/languages/cs_CZ.json @@ -158,7 +158,7 @@ "admin/email_message_message": "Zpráva", "admin/email_message_options": "Možnosti", "admin/email_message_subject": "Předmět", - "admin/email_message_thanks": "Poděkování", + "admin/email_message_thanks": "Děkujeme", "admin/email_password_hidden": "Heslo není z bezpečnostních důvodů zobrazeno.", "admin/email_port_invalid": "Zadejte platný e-mailový port.", "admin/email_preview_popup": "Náhled", @@ -1320,5 +1320,17 @@ "admin/discord_hook": "Discord", "admin/event_supports_discord": "Tato událost podporuje Discord webhooky a má vlastní vložení.", "admin/event_supports_normal": "Tato událost podporuje normální webhooky.", - "general/registration_disabled_message_fallback": "Registrace jsou momentálně zakázány." + "general/registration_disabled_message_fallback": "Registrace jsou momentálně zakázány.", + "user/integration_linked": "Úspěšně jste připojili svůj účet {{integration}}.", + "user/profile_posts_score": "Skóre profilových příspěvků", + "admin/custom_score": "Vlastní skóre", + "admin/profile_widgets": "Profilové widgety", + "admin/reaction_added_event_info": "Reakce přidána", + "admin/reaction_deleted_event_info": "Reakce odstraněna", + "admin/site_widgets": "Webové widgety", + "general/on": "zapnuto", + "user/given": "Uděleno", + "user/minecraft_account": "Účet Minecraft", + "user/reaction_score": "Reakční skóre", + "user/received": "Obdrženo" } diff --git a/custom/languages/de_DE.json b/custom/languages/de_DE.json index f16ea8ec9a..0c74eff0f1 100644 --- a/custom/languages/de_DE.json +++ b/custom/languages/de_DE.json @@ -175,7 +175,7 @@ "admin/enable_player_list": "Spielerliste aktivieren?", "admin/enable_registration": "Registrierung aktivieren?", "admin/enable_status_query": "Aktiviere Status Abfrage?", - "admin/enable_username_sync": "Benutzername Synchronisation aktivieren?", + "admin/enable_username_sync": "Benutzernamen-Synchronisation aktivieren?", "admin/enable_username_sync_info": "Wenn diese Option aktiviert ist, werden die Benutzernamen der Website aktualisiert, damit sie mit den Benutzernamen im Spiel übereinstimmen. Dies geschieht, wenn das In-Game-Plugin eine Liste von UUIDs und Benutzernamen für Online-Spieler an die Website sendet.", "admin/enabled": "Aktiviert", "admin/enter_authme_db_details": "Bitte gebe gültige Datenbank Informationen ein.", @@ -200,7 +200,7 @@ "admin/force_tfa_alert": "Für Deine Gruppe muss die Zwei-Faktor-Authentifizierung aktiviert sein.", "admin/force_tfa_warning": "Bitte vergewissere dich, dass du weißt, was das tut, sonst riskierst du, dich und alle Gruppenmitglieder auszusperren.", "admin/force_www": "www erzwingen?", - "admin/forgot_password_email": "Password vergessen E-Mail", + "admin/forgot_password_email": "E-Mail Password vergessen", "admin/forum_posts": "Anzeige im Forum", "admin/forum_topic_reply_email": "Forum Thema Antwort", "admin/general_settings": "Allgemeine Einstellungen", @@ -436,7 +436,7 @@ "admin/select_default_avatar": "Wähle ein neues Standard Avatar:", "admin/select_user_group": "Du musst eine Benutzergruppe auswählen.", "admin/send": "Senden", - "admin/send_test_email": "Sende Test E-Mail", + "admin/send_test_email": "Test E-Mail senden", "admin/send_test_email_info": "Die folgende Schaltfläche versucht, eine E-Mail an Deine E-Mail-Adresse zu senden, {{email}}. Falls Irgendwelche Fehler, die beim Senden der E-Mail enstehen, werden angezeigt.", "admin/sending_mass_message": "Massennachricht senden", "admin/seo": "SEO", @@ -1320,5 +1320,17 @@ "admin/discord_hook": "Discord", "admin/event_supports_discord": "Dieses Ereignis unterstützt Discord-Webhooks und hat eine eigene Einbettung.", "admin/event_supports_normal": "Dieses Ereignis unterstützt normale Webhooks.", - "general/registration_disabled_message_fallback": "Die Registrierung ist derzeit deaktiviert." + "general/registration_disabled_message_fallback": "Die Registrierung ist derzeit deaktiviert.", + "admin/custom_score": "Benutzerdefinierte Punktzahl", + "admin/profile_widgets": "Profil Widgets", + "admin/reaction_added_event_info": "Reaktion hinzugefügt", + "admin/reaction_deleted_event_info": "Reaktion gelöscht", + "admin/site_widgets": "Website-Widgets", + "general/on": "an", + "user/given": "Gegeben", + "user/minecraft_account": "Minecraft Account", + "user/profile_posts_score": "Bewertung der Profilbeiträge", + "user/reaction_score": "Bewertung der Reaktion", + "user/received": "Empfangen", + "user/integration_linked": "Du hast dein {{integration}}-Konto erfolgreich verknüpft." } diff --git a/custom/languages/en_UK.json b/custom/languages/en_UK.json index f5fb9939d4..b54e40c4a0 100644 --- a/custom/languages/en_UK.json +++ b/custom/languages/en_UK.json @@ -753,6 +753,7 @@ "api/finish_registration_link": "Please click on the following link to complete registration:", "api/group_updated": "Group updated successfully", "api/groups_updates_successfully": "Groups updated successfully", + "api/groups_updates_ignored": "Group sync request ignored, because the Minecraft integration is disabled or the server is not configured as the group sync server in StaffCP.", "api/integration_identifier_already_linked": "{{integration}} identifier is already linked to another user.", "api/integration_username_already_linked": "{{integration}} username is already linked to another user.", "api/report_created": "Report created successfully", @@ -1174,6 +1175,7 @@ "user/incorrect_password": "Your password is incorrect.", "user/integration_identifier_already_linked": "{{integration}} identifier is already linked to another user.", "user/integration_required_to_continue": "Please connect and verify the required connections before continuing to use this website.", + "user/integration_linked": "You have successfully linked your {{integration}} account.", "user/integration_unlinked": "You have successfully unlinked your {{integration}} account.", "user/integration_username_already_linked": "{{integration}} username is already linked to another user.", "user/invalid_email": "Your email is invalid.", diff --git a/custom/languages/ru_RU.json b/custom/languages/ru_RU.json index 6832278db4..ece3d94587 100644 --- a/custom/languages/ru_RU.json +++ b/custom/languages/ru_RU.json @@ -83,7 +83,7 @@ "admin/confirm_query_error_deletion": "Вы уверены, что хотите удалить эту ошибку запроса?", "admin/confirm_user_deletion": "Вы уверены, что хотите удалить пользователя {{user}}?", "admin/contact_email": "Контактная электронная Почта", - "admin/contact_email_address": "Контактный Адрес электронной Почты", + "admin/contact_email_address": "Контактный адрес электронной почты", "admin/content": "Содержание", "admin/copy": "Скопировать", "admin/core": "Главное", @@ -111,11 +111,11 @@ "admin/default": "По умолчанию", "admin/default_avatar": "Аватар по умолчанию", "admin/default_group": "Является ли эта группа группой по умолчанию (для новых пользователей)?", - "admin/default_language": "Язык по Умолчанию", + "admin/default_language": "Язык по умолчанию", "admin/default_language_help": "Пользователи смогут выбрать любой установленный язык. {{docLinkStart}}Прочитать больше »{{docLinkEnd}}", "admin/default_server": "Сервер по умолчанию", "admin/default_template_set": "Шаблон по умолчанию установлен на {{template}} успешно.", - "admin/default_timezone": "Часовой Пояс по умолчанию", + "admin/default_timezone": "Часовой пояс по умолчанию", "admin/delete": "Удалить", "admin/delete_email_error": "Удалить ошибку", "admin/delete_group": "Удалить группу", @@ -215,7 +215,7 @@ "admin/forgot_password_email": "Восстановление Пароль Почты", "admin/forum_posts": "Отображать на форуме", "admin/forum_topic_reply_email": "Ответ в теме на форуме", - "admin/general_settings": "Общие Настройки", + "admin/general_settings": "Общие настройки", "admin/generate_sitemap": "Сгенерировать карту сайта", "admin/google_analytics": "Google Аналитика", "admin/google_analytics_help": "Добавьте Google Аналитики к вашему сайту, что бы отслеживать посетителей и статистику. Ваш нужно создать Google Аналитики аккаунт для использования этой функции. Введите ваш Google Аналитики ID Принадлежности Сайта. ID должен выглядит как UA-XXXXA-X, и вы можете найти его в информации вашего аккаунта или в коде отслежки от Google.", @@ -245,7 +245,7 @@ "admin/head": "Голова", "admin/header": "Заголовок", "admin/header_required": "Придумайте заголовок.", - "admin/homepage_type": "Тип домашней Страницы", + "admin/homepage_type": "Тип домашней страницы", "admin/hook_created": "Хук создан.", "admin/hook_deleted": "Хук удалён.", "admin/hook_edited": "Хук обновлён", @@ -387,7 +387,7 @@ "admin/page_url_minimum_2": "Путь страницы должен состоять не менее чем из 2 символов.", "admin/page_url_required": "Требуется указать путь странице.", "admin/pages": "Страницы", - "admin/panel_templates": "Темы Панели", + "admin/panel_templates": "Темы панели", "admin/parent_server": "Родительский сервер", "admin/parent_server_help": "Родительский сервер обычно является Bungee, к которому подключен сервер, если таковой имеется.", "admin/permissions": "Права", @@ -422,7 +422,7 @@ "admin/pre_1_7": "Minecraft версия старше 1.7?", "admin/privacy_and_terms": "Правила", "admin/privacy_policy_error": "Политика конфиденциальности не должна содержать более 100 000 символов.", - "admin/private_profiles": "Приватные Профили", + "admin/private_profiles": "Приватные профили", "admin/profile_field_created_successfully": "Поле профиля было успешно создано.", "admin/profile_field_deleted_successfully": "Поле профиля было успешно удалено.", "admin/profile_field_editable_help": "Если этот параметр включен, пользователи будут иметь разрешение на редактирование поля в настройках своего профиля.", @@ -511,7 +511,7 @@ "admin/sitemap_link": "Ссылка на карту сайта:", "admin/sitemap_not_generated_yet": "Карта сайта ещё не была сгенерирована!", "admin/sitemap_not_writable": "Каталог cache/sitemaps не доступен для записи.", - "admin/sitename": "Название Сайта", + "admin/sitename": "Название сайта", "admin/social_media": "Социальные Cети", "admin/social_media_settings_updated": "Настройки социальных сетей успешно обновлены.", "admin/source": "Исходный код", @@ -574,7 +574,7 @@ "admin/updated_user_languages": "Обновлены языки пользователей.", "admin/updated_x": "Обновлено: {{updatedAt}}", "admin/upload_new_image": "Загрузить новое изображение", - "admin/use_friendly_urls": "Дружеские Ссылки", + "admin/use_friendly_urls": "Дружеские ссылки", "admin/use_friendly_urls_help": "Если включено, чистые веб ссылки будут использованы. Вы должны разрешить использование mod_rewrite и .htaccess чтобы это заработало. {{docLinkStart}}Прочитать больше »{{docLinkEnd}}", "admin/user": "Пользователь", "admin/user_deleted": "Пользователь успешно удалён.", @@ -673,7 +673,7 @@ "general/invalid_timezone": "Часовая зона недействительна", "general/invalid_token": "Неверный токен, пожалуйста, попробуйте снова.", "general/ip": "IP", - "general/latest_member": "Последний Пользователь", + "general/latest_member": "Последний пользователь", "general/leaderboards": "Список лидеров", "general/link": "Связать", "general/links": "Ссылки", @@ -712,7 +712,7 @@ "general/server_offline": "Сервер выключен.", "general/server_status": "Статус сервера", "general/sign_in": "Вход", - "general/social": "Социальных сети", + "general/social": "Социальные сети", "general/spoiler": "Спойлер", "general/statistics": "Статистика", "general/status": "Сервера", @@ -988,7 +988,7 @@ "user/no_about_fields": "Этот пользователь еще не добавил никакой информации о себе.", "user/no_alerts": "Нет новых уведомлений", "user/no_alerts_usercp": "У вас нет никаких уведомлений.", - "user/no_messages": "Нет новых сообщение", + "user/no_messages": "Нет новых сообщений", "user/no_messages_full": "У вас нет никаких сообщений.", "user/no_placeholders": "Плейсхолдеров нету", "user/no_profile_posts": "Нету записей в профиле.", @@ -1061,7 +1061,7 @@ "user/username_minimum_3": "Ваше имя пользователя должно содержать не менее 3-х символов.", "user/username_required": "Требуется указать имя пользователя.", "user/users_to_required": "Пожалуйста, введите получателей сообщений", - "user/validate_account_command": "Для завершения регистрации, пожалуйста, введите команду /verify {{command}} на сервере.", + "user/validate_account_command": "Для завершения регистрации, пожалуйста, введите команду {{command}} на сервере.", "user/validation_complete": "Ваша учетная запись была подтверждена, теперь вы можете войти в систему.", "user/validation_error": "Неверный код проверки, пользователь уже прошел проверку?", "user/view_alerts": "Посмотреть уведомления", @@ -1251,7 +1251,7 @@ "admin/emoji_native": "Обычный", "admin/emoji_twemoji": "Twemoji", "admin/emoji_joypixels": "JoyPixels", - "admin/emoji_style": "Стиль Эмодзи", + "admin/emoji_style": "Стиль эмодзи", "admin/emoji_style_help": "Изменить какой сет эмодзи используется:
• Обычный {{nativeExample}}
• Twemoji {{twemojiExample}}
• JoyPixels {{joypixelsExample}}", "admin/sent_mass_message": "Успешно отправлено рассылка.", "admin/unable_to_load_outdated_module": "Не возможно загрузить устаревший модуль {{module}}.", @@ -1320,5 +1320,12 @@ "admin/queue_task_output": "Вывод задачи", "admin/queue_task_scheduled_for": "Запланировано для", "admin/queue_task_status": "Состояние", - "admin/queue_task_fragment_total": "Информация о фрагменте задачи" + "admin/queue_task_fragment_total": "Информация о фрагменте задачи", + "admin/reaction_added_event_info": "Реакция добавлена", + "admin/reaction_deleted_event_info": "Реакция удалена", + "admin/profile_widgets": "Виджеты профиля", + "admin/site_widgets": "Виджеты сайта", + "general/on": "вкл", + "user/integration_linked": "Вы успешно привязали ваш {{integration}} аккаунт.", + "user/minecraft_account": "Minecraft Аккаунт" } diff --git a/custom/languages/uk_UA.json b/custom/languages/uk_UA.json index 65e6afdf8e..9977fbca0f 100644 --- a/custom/languages/uk_UA.json +++ b/custom/languages/uk_UA.json @@ -1021,7 +1021,7 @@ "user/users_to_required": "Будь ласка, введіть одержувачів повідомлень", "user/validate_account_command": "Щоб завершити реєстрацію, будь ласка, введіть {{command}} на сервері.", "user/validation_complete": "Ваш обліковий запис було підтверджено, тепер ви можете увійти до системи.", - "user/validation_error": "Відбулася невідома помилка під час перевірки вашого облікового запису, будь ласка, звʼяжіться з адміністратором сайту.", + "user/validation_error": "Недійсний код перевірки, користувач вже пройшов перевірку?", "user/view_alerts": "Переглянути повідомлення", "user/view_messages": "Перегляд повідомлень", "user/views": "Просмотров профиля:", @@ -1215,5 +1215,28 @@ "general/problem_loading_widget": "Виникла проблема при завантаженні віджета {{widget}}", "user/username_already_exists": "Вибране вами ім'я користувача вже використовується.", "admin/unable_to_load_module": "Помилка {{message}} в модулі{{module}}.", - "admin/unable_to_load_outdated_module": "Не вдалося завантажити застарілий модуль {{module}}." + "admin/unable_to_load_outdated_module": "Не вдалося завантажити застарілий модуль {{module}}.", + "admin/discord_hook": "Discord", + "admin/emoji_style": "Стиль Емодзі", + "admin/image": "Зображення", + "admin/profile_widgets": "Віджети профілю", + "admin/queue_cancel_task_confirm": "Ви впевнені, що бажаєте скасувати цю задачу?", + "admin/enable_auto_language": "Увімкнути автоматичний підбір мови?", + "admin/user_warned_webhook": "{{punished}} був попереджений користувачем {{punisher}}.", + "admin/authme_db_test_connection": "Перевірити підключення", + "admin/authme_db_connection_success": "Успішне підключення", + "admin/authme_db_connection_failed": "Помилка підключення", + "admin/clone_group": "Клонувати групу", + "admin/query_type": "Тип запиту", + "admin/internal": "Внутрішній", + "admin/external": "Зовнішній", + "admin/plugin": "Плагін", + "admin/queue": "Черга", + "admin/confirm_delete_placeholder": "Ви певні, що бажаєте видалити цей плейсхолдер?", + "admin/emoji_native": "Звичайний", + "admin/emoji_twemoji": "Twemoji", + "admin/emoji_joypixels": "JoyPixels", + "admin/queue_cancel_task": "Скасувати задачу", + "admin/queue_interval": "Інтервал черги (хвилини)", + "admin/queue_interval_required": "Будь ласка, введіть інтервал черги" } diff --git a/custom/panel_templates/Default/template.php b/custom/panel_templates/Default/template.php index 7c880f0054..c558b3c9da 100644 --- a/custom/panel_templates/Default/template.php +++ b/custom/panel_templates/Default/template.php @@ -5,7 +5,7 @@ * * For NamelessMC * https://github.com/NamelessMC/Nameless/ - * NamelessMC version 2.1.1 + * NamelessMC version 2.1.2 * * License: MIT * @@ -24,8 +24,8 @@ public function __construct(Smarty $smarty, Language $language) { parent::__construct( 'Default', // Template name - '2.1.1', // Template version - '2.1.1', // Nameless version template is made for + '2.1.2', // Template version + '2.1.2', // Nameless version template is made for 'Coldfire' // Author, you can use HTML here ); diff --git a/custom/templates/DefaultRevamp/members/members.tpl b/custom/templates/DefaultRevamp/members/members.tpl index ec3f0baf69..f52dcd28cc 100644 --- a/custom/templates/DefaultRevamp/members/members.tpl +++ b/custom/templates/DefaultRevamp/members/members.tpl @@ -167,7 +167,7 @@ {/if} const nameDiv = document.createElement('span'); - nameDiv.style = member.group_style; + nameDiv.style = member.group_style?.replace(''', "'")?.replace('"', '"'); {if $VIEWING_LIST != "overview"} nameDiv.innerHTML = member.username + ' ' + member.group_html.join(''); {else} diff --git a/custom/templates/DefaultRevamp/template.php b/custom/templates/DefaultRevamp/template.php index 138079d8e8..8db2862b1d 100755 --- a/custom/templates/DefaultRevamp/template.php +++ b/custom/templates/DefaultRevamp/template.php @@ -2,7 +2,7 @@ /* * Made by Samerton | Revamped by Xemah * https://github.com/NamelessMC/Nameless/ - * NamelessMC version 2.1.1 + * NamelessMC version 2.1.2 * * License: MIT * @@ -25,8 +25,8 @@ class DefaultRevamp_Template extends TemplateBase { public function __construct($cache, $smarty, $language, $user, $pages) { $template = [ 'name' => 'DefaultRevamp', - 'version' => '2.1.1', - 'nl_version' => '2.1.1', + 'version' => '2.1.2', + 'nl_version' => '2.1.2', 'author' => 'Xemah', ]; diff --git a/dev/scripts/delete_empty_language_strings.php b/dev/scripts/delete_empty_language_strings.php index c707b1485c..dcac194bd7 100644 --- a/dev/scripts/delete_empty_language_strings.php +++ b/dev/scripts/delete_empty_language_strings.php @@ -1,5 +1,9 @@ str: + # Copy all files that should be included to a temporary directory + archive_temp = tempfile.mkdtemp(prefix='nameless') for include_file in INCLUDE_FILES: include_path = Path(archive_source, include_file) @@ -43,11 +47,11 @@ def create_archives(archive_name: str, archive_source: str = '.'): if include_path.exists(): shutil.copytree(include_path, Path(archive_temp, include_dir)) - if Path(archive_source, 'vendor').exists(): - print('Archive: Generate checksums') - subprocess.check_call(['php', 'dev/scripts/generate_checksums.php'], - shell=False, - cwd=archive_temp) + return archive_temp + + +def create_archives(archive_name, cwd): + archive_path: str = Path('release', archive_name).absolute().as_posix() print('Archive: Creating .zip file') zip_command = [ @@ -57,7 +61,7 @@ def create_archives(archive_name: str, archive_source: str = '.'): f'{archive_path}.zip', '.', ] - subprocess.check_call(zip_command, shell=False, cwd=archive_temp) + subprocess.check_call(zip_command, shell=False, cwd=cwd) print('Archive: Creating .tar.xz file') @@ -71,35 +75,40 @@ def create_archives(archive_name: str, archive_source: str = '.'): '.', ] - subprocess.check_call(tar_command, shell=False, cwd=archive_temp, + subprocess.check_call(tar_command, shell=False, cwd=cwd, env={'XZ_DEFAULTS': '-T 0'}) # Enable multithreading - shutil.rmtree(archive_temp) +def regenerate_vendor_files(): + print('Re-generating vendor files') + shutil.rmtree('core/assets/vendor', ignore_errors=True) + subprocess.check_call(['composer', 'update']) + subprocess.check_call(['composer', 'install', '--no-dev', '--no-interaction']) -if __name__ == '__main__': - if not Path('.git').exists(): - print('.git does not exist') - sys.exit(1) - print('Deleting vendor files') - shutil.rmtree('core/assets/vendor', ignore_errors=True) - shutil.rmtree('node_modules', ignore_errors=True) - shutil.rmtree('vendor', ignore_errors=True) +def create_deps_dist_archive(): + print('Creating nameless-deps-dist archive') + # Copy the required files to a new temporary directory + deps_dist_temp = create_archive_dir() + # Generate checksums + generate_checksums(deps_dist_temp) + # Create .zip and .tar.xz archives + create_archives('nameless-deps-dist', deps_dist_temp) + # Temporary directory can now be deleted + shutil.rmtree(deps_dist_temp) - # Create base archive - create_archives('nameless-base') - # Run npm and composer (production dependencies only) - subprocess.check_call(['npm', 'ci', '-q', '--cache', '.node_cache']) - subprocess.check_call(['composer', 'update']) - subprocess.check_call(['composer', 'install', '--no-dev', '--no-interaction']) +def always_in_update_package(relative_path: str) -> bool: + return relative_path == 'checksums.json' or relative_path.startswith('vendor/') or relative_path.startswith('core/assets/vendor') - create_archives('nameless-deps-dist') - # Create archive with files changed since last update +def create_upgrade_archive(): + print('Creating update archive') - upgrade_temp = Path('release', 'upgrade_temp') + # Copy the required files to a new temporary directory + upgrade_temp = create_archive_dir() + # Generate checksums + generate_checksums(upgrade_temp) # Find previous tag previous_tag_command = ['git', 'describe', '--abbrev=0', '--tags'] @@ -109,19 +118,53 @@ def create_archives(archive_name: str, archive_source: str = '.'): # Find all files changed between previous tag and HEAD (current state) changed_command = ['git', 'diff', previous_tag, 'HEAD', '--name-only', '--diff-filter=d'] - changed_files = subprocess.check_output(changed_command, shell=False)[:-1].decode() + changed_files_output = subprocess.check_output(changed_command, shell=False)[:-1] + changed_files = set(changed_files_output.decode().split('\n')) + + # Delete any files that have not been changed + for path in Path(upgrade_temp).rglob("*"): + relative_path = path.as_posix()[len(upgrade_temp)+1:] + if ( + not always_in_update_package(relative_path) and + relative_path not in changed_files and + path.is_file() + ): + path.unlink() + + # Delete empty directoryes + subprocess.check_call(['find', '.', '-type', 'd', '-empty', '-delete'], cwd=upgrade_temp) + + # Create .zip and .tar.xz archives + create_archives('upgrade-from-' + previous_tag, upgrade_temp) + + +def create_deps_dev_archive(): + print('Creating nameless-deps-dev archive') + # Copy the required files to a new temporary directory + deps_dev_temp = create_archive_dir() + # Generate checksums + generate_checksums(deps_dev_temp) + # Create .zip and .tar.xz archives + create_archives('nameless-deps-dev', deps_dev_temp) + # Temporary directory can now be deleted + shutil.rmtree(deps_dev_temp) + + +if __name__ == '__main__': + if not Path('.git').exists(): + print('.git does not exist') + sys.exit(1) - for changed_file in changed_files.split('\n'): - changed_file_target = Path(upgrade_temp, changed_file) - changed_file_target.parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(changed_file, changed_file_target) + # Re-generate vendor files (without development dependencies) + regenerate_vendor_files() - # Vendor files are always included - for vendor_dir in ['vendor', 'core/assets/vendor']: - shutil.copytree(vendor_dir, Path(upgrade_temp, vendor_dir)) + # Create nameless-deps-dist archive with production dependencies only + create_deps_dist_archive() - create_archives('upgrade-from-' + previous_tag, archive_source=upgrade_temp.as_posix()) + # Create update archive (files changed since last tag) + create_upgrade_archive() # Run composer again, to install development dependencies subprocess.check_call(['composer', 'install', '--no-interaction']) - create_archives('nameless-deps-dev') + # Create nameless-deps-dev archive with development dependencies + create_deps_dev_archive() diff --git a/dev/scripts/verify_checksums.php b/dev/scripts/verify_checksums.php index 1c09fb8812..066a629f0f 100644 --- a/dev/scripts/verify_checksums.php +++ b/dev/scripts/verify_checksums.php @@ -1,5 +1,9 @@ Samerton'; - $module_version = '2.1.1'; - $nameless_version = '2.1.1'; + $module_version = '2.1.2'; + $nameless_version = '2.1.2'; parent::__construct($this, $name, $author, $module_version, $nameless_version); diff --git a/modules/Core/includes/endpoints/UpdateGroupsEndpoint.php b/modules/Core/includes/endpoints/UpdateGroupsEndpoint.php index 4a8fe0f396..d74a3101fb 100644 --- a/modules/Core/includes/endpoints/UpdateGroupsEndpoint.php +++ b/modules/Core/includes/endpoints/UpdateGroupsEndpoint.php @@ -13,25 +13,28 @@ public function execute(Nameless2API $api): void { $api->validateParams($_POST, ['server_id', 'player_groups']); $server_id = $_POST['server_id']; - $group_sync_log = []; - if (Util::getSetting('mc_integration') && $server_id == Util::getSetting('group_sync_mc_server')) { - $integration = Integrations::getInstance()->getIntegration('Minecraft'); + if (!Settings::get('mc_integration') || $server_id != Settings::get('group_sync_mc_server')) { + $api->returnArray(['message' => $api->getLanguage()->get('api', 'groups_updates_ignored')]); + } - foreach ($_POST['player_groups'] as $uuid => $groups) { - $integrationUser = new IntegrationUser($integration, str_replace('-', '', $uuid), 'identifier'); - if ($integrationUser->exists()) { - $log = $this->updateGroups($integrationUser, $groups['groups']); - if (count($log)) { - $group_sync_log[] = $log; - } + $group_sync_log = []; + $integration = Integrations::getInstance()->getIntegration('Minecraft'); + + foreach ($_POST['player_groups'] as $uuid => $groups) { + $integrationUser = new IntegrationUser($integration, str_replace('-', '', $uuid), 'identifier'); + if ($integrationUser->exists()) { + $log = $this->updateGroups($integrationUser, $groups['groups']); + if (count($log)) { + $group_sync_log[] = $log; } } - - $api->returnArray(array_merge(['message' => $api->getLanguage()->get('api', 'groups_updates_successfully')], ['log' => $group_sync_log])); } - $api->throwError(CoreApiErrors::ERROR_INVALID_SERVER_ID, $server_id); + $api->returnArray([ + 'message' => $api->getLanguage()->get('api', 'groups_updates_successfully'), + 'log' => $group_sync_log, + ]); } private function updateGroups(IntegrationUser $integrationUser, array $groups): array { diff --git a/modules/Core/module.php b/modules/Core/module.php index f3814fe54c..6421fceccd 100644 --- a/modules/Core/module.php +++ b/modules/Core/module.php @@ -2,7 +2,7 @@ /* * Made by Samerton * https://github.com/NamelessMC/Nameless/ - * NamelessMC version 2.1.1 + * NamelessMC version 2.1.2 * * License: MIT * @@ -21,8 +21,8 @@ public function __construct(Language $language, Pages $pages, User $user, Naviga $name = 'Core'; $author = 'Samerton'; - $module_version = '2.1.1'; - $nameless_version = '2.1.1'; + $module_version = '2.1.2'; + $nameless_version = '2.1.2'; parent::__construct($this, $name, $author, $module_version, $nameless_version); diff --git a/modules/Core/pages/panel/security.php b/modules/Core/pages/panel/security.php index b04f0a5bb9..838bb91a12 100644 --- a/modules/Core/pages/panel/security.php +++ b/modules/Core/pages/panel/security.php @@ -2,7 +2,7 @@ /* * Made by Samerton * https://github.com/NamelessMC/Nameless/ - * NamelessMC version 2.0.0-pr9 + * NamelessMC version 2.1.2 * * License: MIT * @@ -74,7 +74,7 @@ } $log_title = $language->get('admin', 'acp_logins'); - $logs = DB::getInstance()->orderWhere('logs', 'action = \'acp_login\'', 'time', 'DESC')->results(); + $logs = DB::getInstance()->orderWhere('logs', 'action = \'acp_login\'', 'time', 'DESC LIMIT 500')->results(); $cols = 3; $col_titles = [ @@ -111,7 +111,7 @@ } $log_title = $language->get('admin', 'template_changes'); - $logs = DB::getInstance()->orderWhere('logs', 'action = \'acp_template_update\'', 'time', 'DESC')->results(); + $logs = DB::getInstance()->orderWhere('logs', 'action = \'acp_template_update\'', 'time', 'DESC LIMIT 500')->results(); $cols = 4; $col_titles = [ @@ -152,7 +152,7 @@ } $log_title = $language->get('admin', 'email_logs'); - $logs = DB::getInstance()->orderWhere('logs', 'action = \'acp_email_mass_message\'', 'time', 'DESC')->results(); + $logs = DB::getInstance()->orderWhere('logs', 'action = \'acp_email_mass_message\'', 'time', 'DESC LIMIT 500')->results(); $cols = 3; $col_titles = [ @@ -188,7 +188,7 @@ } $log_title = $language->get('admin', 'group_sync_logs'); - $logs_set = DB::getInstance()->orderWhere('logs', 'action = \'discord_role_set\' OR action = \'mc_group_sync_set\' ', 'time', 'DESC')->results(); + $logs_set = DB::getInstance()->orderWhere('logs', 'action LIKE \'%_role_set\' OR action LIKE \'%_group_set\'OR action = \'mc_group_sync_set\' ', 'time', 'DESC LIMIT 500')->results(); $cols = 5; $col_titles = [ @@ -246,7 +246,7 @@ } $log_title = $language->get('admin', 'all_logs'); - $logs = DB::getInstance()->orderWhere('logs', 'id <> 0', 'time', 'DESC')->results(); + $logs = DB::getInstance()->orderWhere('logs', 'id <> 0', 'time', 'DESC LIMIT 500')->results(); $cols = 5; $col_titles = [ diff --git a/modules/Core/pages/register.php b/modules/Core/pages/register.php index 547b39170c..2b88ecd9d7 100644 --- a/modules/Core/pages/register.php +++ b/modules/Core/pages/register.php @@ -2,7 +2,7 @@ /* * Made by Samerton * https://github.com/NamelessMC/Nameless/ - * NamelessMC version 2.0.0-pr13 + * NamelessMC version 2.1.2 * * License: MIT * @@ -278,8 +278,7 @@ $data['id'], ); $auto_verify_oauth_email = $data['email'] === Input::get('email') - && NamelessOAuth::getInstance()->hasVerifiedEmail($data['provider'], $data['data']) - && DB::getInstance()->get('users', ['email', $data['email']])->count() === 0; + && NamelessOAuth::getInstance()->hasVerifiedEmail($data['provider'], $data['data']); Session::delete('oauth_register_data'); } diff --git a/modules/Discord Integration/classes/DiscordIntegration.php b/modules/Discord Integration/classes/DiscordIntegration.php index b191d876b6..f34b26c4f3 100644 --- a/modules/Discord Integration/classes/DiscordIntegration.php +++ b/modules/Discord Integration/classes/DiscordIntegration.php @@ -4,7 +4,7 @@ * * @package Modules\Core\Integrations * @author Partydragen - * @version 2.1.1 + * @version 2.1.2 * @license MIT */ class DiscordIntegration extends IntegrationBase { @@ -75,6 +75,7 @@ public function onSuccessfulVerification(IntegrationUser $integrationUser) { }, $user->getAllGroupIds())); Discord::updateDiscordRoles($user, $roles, []); + Session::flash('connections_success', $this->_language->get('user', 'integration_linked', ['integration' => Output::getClean($this->_name)])); } public function validateUsername(string $username, int $integration_user_id = 0): bool { diff --git a/modules/Discord Integration/module.php b/modules/Discord Integration/module.php index 03ad2dc7f4..a752617b0f 100644 --- a/modules/Discord Integration/module.php +++ b/modules/Discord Integration/module.php @@ -9,8 +9,8 @@ public function __construct(Language $language, Pages $pages, Endpoints $endpoin $name = 'Discord Integration'; $author = 'Aberdeener'; - $module_version = '2.1.1'; - $nameless_version = '2.1.1'; + $module_version = '2.1.2'; + $nameless_version = '2.1.2'; parent::__construct($this, $name, $author, $module_version, $nameless_version); diff --git a/modules/Forum/classes/Forum.php b/modules/Forum/classes/Forum.php index 1c8b862457..7cdf4a57ef 100644 --- a/modules/Forum/classes/Forum.php +++ b/modules/Forum/classes/Forum.php @@ -62,7 +62,15 @@ public function listAllForums(array $groups = [0], int $user_id = 0): array { // Get discussion forums $forums = $this->_db->query( <<getAnySubforums($item->id, $groups, 0, true, $user_id); - $latest_post = [$item->last_post_date, $item->last_user_posted, $item->last_topic_posted]; + if ($item->view_other_topics) { + $latest_post = [$item->last_post_date, $item->last_user_posted, $item->last_topic_posted]; + } else { + $latest_post = $this->getLatestPostInOwnTopicForum($item->id, $user_id); + } if (count($subforums)) { $return[$forum->id]['subforums'][$item->id]->subforums = []; @@ -733,41 +745,18 @@ public function getAnySubforums( foreach ($subforums_query->results() as $result) { $to_add = new stdClass(); - $to_add->id = Output::getClean($result->id); + $to_add->id = $result->id; $to_add->forum_title = Output::getClean($result->forum_title); $to_add->icon = Output::getPurified($result->icon); $to_add->category = false; // Latest post - if ($onlyOwnTopics && $result->view_other_topics != '1') { - // Get the latest topic that the user can view - $latest_post = $this->_db->query( - <<id, $user_id] - ); - - if ($latest_post->count() && $latest_post = $latest_post->first()) { - $to_add->last_post_date = $latest_post->created ?? strtotime($latest_post->post_date); - $to_add->last_user_posted = $latest_post->post_creator; - $to_add->last_topic_posted = $latest_post->topic_id; - } + if ($onlyOwnTopics && $result->view_other_topics !== 1) { + [ + $to_add->last_post_date, + $to_add->last_user_posted, + $to_add->last_topic_posted, + ] = $this->getLatestPostInOwnTopicForum($result->id, $user_id); } else { $to_add->last_post_date = $result->last_post_date; $to_add->last_user_posted = $result->last_user_posted; @@ -776,7 +765,7 @@ public function getAnySubforums( $ret[] = $to_add; - $subforums = $this->getAnySubforums($result->id, $groups, ++$depth); + $subforums = $this->getAnySubforums($result->id, $groups, ++$depth, $onlyOwnTopics, $user_id); if (count($subforums)) { foreach ($subforums as $subforum) { @@ -808,4 +797,47 @@ public static function getAccessibleLabels(array $labels, array $user_groups): a return $prev; }, []); } + + /** + * Get the latest post in a "View own topic" forum + * This could be a topic created by the user, or a sticky topic + * + * @param int $forumId + * @param int $userId + * @return array|null Time of latest post, post creator ID, topic ID + */ + private function getLatestPostInOwnTopicForum(int $forumId, int $userId): ?array { + $latest_post = $this->_db->query( + <<count() && $latest_post = $latest_post->first()) { + return [ + $latest_post->created ?? strtotime($latest_post->post_date), + $latest_post->post_creator, + $latest_post->topic_id, + ]; + } + + return null; + } } diff --git a/modules/Forum/language/cs_CZ.json b/modules/Forum/language/cs_CZ.json index fcf278bec4..aeccee1863 100644 --- a/modules/Forum/language/cs_CZ.json +++ b/modules/Forum/language/cs_CZ.json @@ -177,11 +177,12 @@ "forum/label_type_in_use": "Typ štítku je používán, nelze jej odstranit.", "forum/latest_posts_limit": "Limit nejnovějších příspěvků", "forum/latest_posts_widget_cached": "Widget nejnovějších příspěvků je uložen v mezipaměti po dobu jedné minuty, vaše změny se nemusí projevit okamžitě.", - "forum/highest_reaction_scores": "Nejvyšší reakční skóre", + "forum/highest_reaction_scores": "Nejvyšší reakční skóre fóra", "forum/reaction_score": "Reakční skóre", "forum/most_posts": "Nejvíce příspěvků", "forum/no_posts_found": "Nenalezeny žádné příspěvky.", "forum/news_items_max": "Počet novinek by měl být maximálně {{max}}", "forum/news_items_min": "Počet novinek by měl být alespoň {{min}}", - "forum/news_items_front_page_limit": "Počet novinek, které mají být zobrazeny na hlavní stránce" + "forum/news_items_front_page_limit": "Počet novinek, které mají být zobrazeny na hlavní stránce", + "forum/forum_score": "Skóre fóra" } diff --git a/modules/Forum/language/de_DE.json b/modules/Forum/language/de_DE.json index 2b26b1a24b..daf8a95262 100644 --- a/modules/Forum/language/de_DE.json +++ b/modules/Forum/language/de_DE.json @@ -177,11 +177,12 @@ "forum/label_type_in_use": "Der Etikettentyp wird verwendet und kann nicht gelöscht werden.", "forum/latest_posts_widget_cached": "Das Widget für die neuesten Beiträge wird eine Minute lang zwischengespeichert, so dass deine Änderungen möglicherweise nicht sofort wirksam werden.", "forum/latest_posts_limit": "Letzte Beiträge begrenzen", - "forum/highest_reaction_scores": "Höchster Reaktionsanzahl", + "forum/highest_reaction_scores": "Höchste Reaktionswerte im Forum", "forum/most_posts": "Die meisten Beiträge", "forum/reaction_score": "Reaktionsanzahl", "forum/no_posts_found": "Keine Beiträge gefunden.", "forum/news_items_front_page_limit": "Anzahl der News, die auf der Titelseite angezeigt werden sollen", "forum/news_items_max": "Die Anzahl der News sollte mindestens {{max}} sein", - "forum/news_items_min": "Die Anzahl der News sollte mindestens {{min}} sein" + "forum/news_items_min": "Die Anzahl der News sollte mindestens {{min}} sein", + "forum/forum_score": "Forum Punkte" } diff --git a/modules/Forum/language/ru_RU.json b/modules/Forum/language/ru_RU.json index a3269f793a..348a5499b1 100644 --- a/modules/Forum/language/ru_RU.json +++ b/modules/Forum/language/ru_RU.json @@ -78,7 +78,7 @@ "forum/label_type_name": "Название типа метки", "forum/label_types": "Типы меток", "forum/labels": "Метки", - "forum/last_7_days_posts": "Сообщения на форума (последние 7 дней)", + "forum/last_7_days_posts": "Сообщения на форуме (последние 7 дней)", "forum/last_edited": "Последнее редактирование: {{lastEditedAt}}", "forum/last_reply": "Последний ответ", "forum/latest_announcements": "Последние новости", diff --git a/modules/Forum/module.php b/modules/Forum/module.php index 6d2ae791b1..7d0bfad6bd 100644 --- a/modules/Forum/module.php +++ b/modules/Forum/module.php @@ -2,7 +2,7 @@ /* * Made by Samerton * https://github.com/NamelessMC/Nameless/ - * NamelessMC version 2.1.1 + * NamelessMC version 2.1.2 * * License: MIT * @@ -20,8 +20,8 @@ public function __construct(Language $language, Language $forum_language, Pages $name = 'Forum'; $author = 'Samerton'; - $module_version = '2.1.1'; - $nameless_version = '2.1.1'; + $module_version = '2.1.2'; + $nameless_version = '2.1.2'; parent::__construct($this, $name, $author, $module_version, $nameless_version); diff --git a/modules/Forum/pages/forum/view_forum.php b/modules/Forum/pages/forum/view_forum.php index e8040fca3f..742a7abbf4 100644 --- a/modules/Forum/pages/forum/view_forum.php +++ b/modules/Forum/pages/forum/view_forum.php @@ -192,7 +192,7 @@ if ($forum->canViewOtherTopics($subforum->id, $user_groups)) { $latest_post = DB::getInstance()->query('SELECT * FROM nl2_topics WHERE forum_id = ? AND deleted = 0 ORDER BY topic_reply_date DESC', [$subforum->id])->results(); } else { - $latest_post = DB::getInstance()->query('SELECT * FROM nl2_topics WHERE forum_id = ? AND deleted = 0 AND topic_creator = ? ORDER BY topic_reply_date DESC', [$subforum->id, $user_id])->results(); + $latest_post = DB::getInstance()->query('SELECT * FROM nl2_topics WHERE forum_id = ? AND deleted = 0 AND (topic_creator = ? OR sticky = 1) ORDER BY topic_reply_date DESC', [$subforum->id, $user_id])->results(); } $subforum_topics = count($latest_post); diff --git a/modules/Members/language/en_US.json b/modules/Members/language/en_US.json index 056346e2da..50a622c328 100644 --- a/modules/Members/language/en_US.json +++ b/modules/Members/language/en_US.json @@ -1,5 +1,5 @@ { - "members/members": "Menbers", + "members/members": "Members", "members/new_members": "New members", "members/no_members": "No members found.", "members/member_lists": "Member Lists", diff --git a/modules/Members/module.php b/modules/Members/module.php index 6a2cd24b8b..4ccfbe36a7 100644 --- a/modules/Members/module.php +++ b/modules/Members/module.php @@ -2,7 +2,7 @@ /* * Made by Aberdeener * https://github.com/NamelessMC/Nameless/ - * NamelessMC version 2.1.1 + * NamelessMC version 2.1.2 * * License: MIT * @@ -20,8 +20,8 @@ public function __construct(Language $language, Language $members_language, Page $name = 'Members'; $author = 'Aberdeener'; - $module_version = '2.1.1'; - $nameless_version = '2.1.1'; + $module_version = '2.1.2'; + $nameless_version = '2.1.2'; parent::__construct($this, $name, $author, $module_version, $nameless_version); diff --git a/package.json b/package.json index d155a04f36..4e854444b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nameless", - "version": "2.1.1", + "version": "2.1.2", "repository": "https://github.com/NamelessMC/Nameless", "license": "MIT", "private": true,