diff options
Diffstat (limited to 'Swift')
28 files changed, 323 insertions, 75 deletions
diff --git a/Swift/ChangeLog.md b/Swift/ChangeLog.md index 32b5711..9152b50 100644 --- a/Swift/ChangeLog.md +++ b/Swift/ChangeLog.md @@ -1,3 +1,57 @@ +4.0.3 (2019-01-03) +------------------ +- Fix handling of empty bookmark responses + +4.0.2 (2018-04-05) +------------------ +- Fix versioning issue in Windows Installer process + +4.0.1 (2018-03-28) +------------------ +- Allow setting vCard on servers that do not return an empty vCard on fresh accounts + +4.0 (2018-03-20) +---------------- +- New chat theme including a new font +- Support for message carbons (XEP-0280) +- Enabled trellis mode as a default feature, allowing several tiled chats windows to be shown at once +- Redesigned keyword highlighting +- Improve date formatting +- Fix Last Message Correction in multi client scenarios +- Fix UI layout issue for translations that require right-to-left (RTL) layout +- Fix UX issues in trellis mode +- Improvements to font size handling in the chat theme +- Fix display of default avatar on Windows +- Support for automatic software updates on macOS +- macOS releases are now code-signed +- Support for unicode emojis on macOS +- Add AppImage for Linux 64-bit as a supported platform +- Improved spell checker support on Linux +- And assorted smaller features and usability enhancements + +4.0-rc6 ( 2018-03-07 ) +---------------------- +- Small usability fixes in Carbons ( XEP-0280 ) handling and Windows installer +- Fix crashes related to room invitations, layout changes, and vCard handling +- And smaller bug fixes + +4.0-rc5 ( 2018-01-09 ) +---------------------- +- Fix crash during sleep on macOS + +4.0-rc4 ( 2018-01-04 ) +---------------------- +- Remove conflicting shortcut +- Debian packaging fixes + +4.0-rc3 ( 2017-11-28 ) +---------------------- +- Fix crash in emoticon dialog +- Fix Windows MSI installer when updating from earlier Swift version +- Update translations ( Dutch, German ) +- Add AppImage for Linux 64-bit as a supported platform +- And smaller bug fixes + 4.0-rc2 ( 2017-05-22 ) ---------------------- - Fix regression in chat window titles for chat rooms @@ -10,7 +64,7 @@ - Fix Last Message Correction in multi client scenarios - Fix display of default avatar on Windows - Support for automatic software updates on macOS -- Redesigned keyword highlighing +- Redesigned keyword highlighting - Support for unicode emojis on macOS - Improvements to font size handling in the chat theme - Fix UX issues in trellis mode @@ -117,4 +171,4 @@ Yoann Blein, Catalin Badea, Pavol Babincak, Mateusz Piekos, Alexey Melnikov and 1.0 ( 2011-04-18 ) ------------------ -- Initial release.
\ No newline at end of file +- Initial release. diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index fe8e870..40f6156 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -86,7 +86,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ chatStateNotifier_->setContactIsOnline(theirPresence && theirPresence->getType() == Presence::Available); startMessage += "."; chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection); - chatWindow_->onContinuationsBroken.connect([this, startMessage]() { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection); }); + continuationsBrokenConnection_ = chatWindow_->onContinuationsBroken.connect([this, startMessage]() { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection); }); chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_)); chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_)); chatWindow_->onFileTransferStart.connect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2)); @@ -320,6 +320,7 @@ void ChatController::handleIncomingOwnMessage(std::shared_ptr<Message> message) if (!message->getBody().get_value_or("").empty()) { postSendMessage(message->getBody().get_value_or(""), message); handleStanzaAcked(message); + onActivity(message->getBody().get_value_or("")); } } @@ -585,8 +586,20 @@ JID ChatController::messageCorrectionJID(const JID& fromJID) { } ChatWindow* ChatController::detachChatWindow() { - chatWindow_->onUserTyping.disconnect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_)); + continuationsBrokenConnection_.disconnect(); + chatWindow_->onClosed.disconnect(boost::bind(&ChatController::handleWindowClosed, this)); + chatWindow_->onInviteToChat.disconnect(boost::bind(&ChatController::handleInviteToChat, this, _1)); + chatWindow_->onUnblockUserRequest.disconnect(boost::bind(&ChatController::handleUnblockUserRequest, this)); + chatWindow_->onBlockUserRequest.disconnect(boost::bind(&ChatController::handleBlockUserRequest, this)); + chatWindow_->onWhiteboardWindowShow.disconnect(boost::bind(&ChatController::handleWhiteboardWindowShow, this)); + chatWindow_->onWhiteboardSessionCancel.disconnect(boost::bind(&ChatController::handleWhiteboardSessionCancel, this)); + chatWindow_->onWhiteboardSessionAccept.disconnect(boost::bind(&ChatController::handleWhiteboardSessionAccept, this)); + chatWindow_->onSendFileRequest.disconnect(boost::bind(&ChatController::handleSendFileRequest, this, _1)); + chatWindow_->onFileTransferCancel.disconnect(boost::bind(&ChatController::handleFileTransferCancel, this, _1)); + chatWindow_->onFileTransferAccept.disconnect(boost::bind(&ChatController::handleFileTransferAccept, this, _1, _2)); + chatWindow_->onFileTransferStart.disconnect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2)); chatWindow_->onUserCancelsTyping.disconnect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_)); + chatWindow_->onUserTyping.disconnect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_)); return ChatControllerBase::detachChatWindow(); } diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index d5011e4..a9093d0 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -113,6 +113,7 @@ namespace Swift { boost::signals2::scoped_connection blockingOnStateChangedConnection_; boost::signals2::scoped_connection blockingOnItemAddedConnection_; boost::signals2::scoped_connection blockingOnItemRemovedConnection_; + boost::signals2::scoped_connection continuationsBrokenConnection_; boost::optional<ChatWindow::AlertID> deliveryReceiptAlert_; boost::optional<ChatWindow::AlertID> blockedContactAlert_; diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 0fc735a..19bbf8d 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -58,6 +58,9 @@ void ChatControllerBase::handleContinuationsBroken() { } ChatWindow* ChatControllerBase::detachChatWindow() { + chatWindow_->onContinuationsBroken.disconnect(boost::bind(&ChatControllerBase::handleContinuationsBroken, this)); + chatWindow_->onSendMessageRequest.disconnect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); + chatWindow_->onAllMessagesRead.disconnect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); ChatWindow* chatWindow = chatWindow_; chatWindow_ = nullptr; return chatWindow; diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index a6f7fe0..19400f9 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -331,11 +331,13 @@ void ChatsManager::handleBookmarksReady() { } void ChatsManager::handleMUCBookmarkAdded(const MUCBookmark& bookmark) { - std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark.getRoom()); - if (it == mucControllers_.end() && bookmark.getAutojoin()) { - handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false, false ); + if (bookmark.getRoom().isBare() && !bookmark.getRoom().getNode().empty()) { + std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark.getRoom()); + if (it == mucControllers_.end() && bookmark.getAutojoin()) { + handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false, false ); + } + chatListWindow_->addMUCBookmark(bookmark); } - chatListWindow_->addMUCBookmark(bookmark); } void ChatsManager::handleMUCBookmarkRemoved(const MUCBookmark& bookmark) { diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 1502dc9..ac62942 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -32,9 +32,12 @@ #include <Swiften/Elements/Forwarded.h> #include <Swiften/Elements/MUCInvitationPayload.h> #include <Swiften/Elements/MUCUserPayload.h> +#include <Swiften/Elements/PrivateStorage.h> +#include <Swiften/Elements/Storage.h> #include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h> #include <Swiften/Jingle/JingleSessionManager.h> #include <Swiften/MUC/MUCManager.h> +#include <Swiften/MUC/UnitTest/MockMUC.h> #include <Swiften/Network/DummyTimerFactory.h> #include <Swiften/Presence/DirectedPresenceSender.h> #include <Swiften/Presence/PresenceOracle.h> @@ -71,7 +74,6 @@ #include <SwifTools/Notifier/Notifier.h> #include <Swift/QtUI/QtSwiftUtil.h> -#include <Swiften/MUC/UnitTest/MockMUC.h> using namespace Swift; @@ -155,6 +157,11 @@ class ChatsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST(testImpromptuChatWindowTitle); CPPUNIT_TEST(testStandardMUCChatWindowTitle); + // Bookmark tests + CPPUNIT_TEST(testReceivingBookmarksWithDomainJID); + CPPUNIT_TEST(testReceivingBookmarksWithBareJID); + CPPUNIT_TEST(testReceivingBookmarksWithFullJID); + CPPUNIT_TEST_SUITE_END(); public: @@ -1228,6 +1235,11 @@ public: CPPUNIT_ASSERT_EQUAL(forwardedBody, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(false, window->lastAddedMessageSenderIsSelf_); + + auto recentChats = manager_->getRecentChats(); + CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getFrom().toBare()); + CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some further text.")); } } @@ -1264,6 +1276,11 @@ public: CPPUNIT_ASSERT_EQUAL(true, window->lastAddedMessageSenderIsSelf_); CPPUNIT_ASSERT_EQUAL(size_t(1), window->receiptChanges_.size()); CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptRequested, window->receiptChanges_[0].second); + + auto recentChats = manager_->getRecentChats(); + CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getTo().toBare()); + CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some text my other resource sent.")); } // incoming carbons message for the received delivery receipt to the other resource @@ -1280,6 +1297,12 @@ public: CPPUNIT_ASSERT_EQUAL(size_t(2), window->receiptChanges_.size()); CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptReceived, window->receiptChanges_[1].second); + + //Delivery receipt should not change the latest recent entry. Checking for the original message. + auto recentChats = manager_->getRecentChats(); + CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, messageJID.toBare()); + CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some text my other resource sent.")); } } @@ -1319,6 +1342,11 @@ public: manager_->handleIncomingMessage(messageWrapper); CPPUNIT_ASSERT_EQUAL(std::string(), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(false, window->lastAddedMessageSenderIsSelf_); + + auto recentChats = manager_->getRecentChats(); + CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getFrom().toBare()); + CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some further text.")); } } @@ -1598,6 +1626,78 @@ public: CPPUNIT_ASSERT_EQUAL(std::string("mucroom"), window->name_); } + static std::shared_ptr<Storage> createBookmarkStorageWithJID(const JID& jid) { + auto storage = std::make_shared<Storage>(); + auto room = Storage::Room(); + room.jid = jid; + room.autoJoin = true; + storage->addRoom(room); + return storage; + } + + void testReceivingBookmarksWithDomainJID() { + auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]); + CPPUNIT_ASSERT(bookmarkRequest); + CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); + + auto privateStorage = bookmarkRequest->getPayload<PrivateStorage>(); + CPPUNIT_ASSERT(privateStorage); + + auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload()); + CPPUNIT_ASSERT(storage); + + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("montague.lit")) + ); + stanzaChannel_->onIQReceived(response); + } + + void testReceivingBookmarksWithBareJID() { + auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]); + CPPUNIT_ASSERT(bookmarkRequest); + CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); + + auto privateStorage = bookmarkRequest->getPayload<PrivateStorage>(); + CPPUNIT_ASSERT(privateStorage); + + auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload()); + CPPUNIT_ASSERT(storage); + + MockChatWindow* window = new MockChatWindow(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(window); + + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("example@montague.lit")) + ); + stanzaChannel_->onIQReceived(response); + } + + void testReceivingBookmarksWithFullJID() { + auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]); + CPPUNIT_ASSERT(bookmarkRequest); + CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); + + auto privateStorage = bookmarkRequest->getPayload<PrivateStorage>(); + CPPUNIT_ASSERT(privateStorage); + + auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload()); + CPPUNIT_ASSERT(storage); + + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("example@montague.lit/someresource")) + ); + stanzaChannel_->onIQReceived(response); + } + private: std::shared_ptr<Message> makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) { std::shared_ptr<Message> message = std::make_shared<Message>(); @@ -1664,4 +1764,3 @@ private: }; CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); - diff --git a/Swift/Controllers/Storages/AvatarFileStorage.cpp b/Swift/Controllers/Storages/AvatarFileStorage.cpp index a103920..9d9b9ea 100644 --- a/Swift/Controllers/Storages/AvatarFileStorage.cpp +++ b/Swift/Controllers/Storages/AvatarFileStorage.cpp @@ -1,16 +1,15 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/Controllers/Storages/AvatarFileStorage.h> -#include <iostream> - #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> +#include <Swiften/Base/Log.h> #include <Swiften/Base/String.h> #include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/StringCodecs/Hexify.h> @@ -31,13 +30,13 @@ AvatarFileStorage::AvatarFileStorage(const boost::filesystem::path& avatarsDir, jidAvatars.insert(std::make_pair(jid, r.first)); } else if (!r.first.empty() || !r.second.empty()) { - std::cerr << "Invalid entry in avatars file: " << r.second << std::endl; + SWIFT_LOG(error) << "Invalid entry in avatars file: " << r.second << std::endl; } } } } catch (...) { - std::cerr << "Error reading avatars file" << std::endl; + SWIFT_LOG(error) << "Error reading avatars file" << std::endl; } } } @@ -55,12 +54,17 @@ void AvatarFileStorage::addAvatar(const std::string& hash, const ByteArray& avat boost::filesystem::create_directories(avatarPath.parent_path()); } catch (const boost::filesystem::filesystem_error& e) { - std::cerr << "ERROR: " << e.what() << std::endl; + SWIFT_LOG(error) << "filesystem error: " << e.what() << std::endl; } } - boost::filesystem::ofstream file(avatarPath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); - file.write(reinterpret_cast<const char*>(vecptr(avatar)), static_cast<std::streamsize>(avatar.size())); - file.close(); + + try { + boost::filesystem::ofstream file(avatarPath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); + file.write(reinterpret_cast<const char*>(vecptr(avatar)), static_cast<std::streamsize>(avatar.size())); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << "filesystem error: " << e.what() << std::endl; + } } boost::filesystem::path AvatarFileStorage::getAvatarPath(const std::string& hash) const { @@ -69,7 +73,12 @@ boost::filesystem::path AvatarFileStorage::getAvatarPath(const std::string& hash ByteArray AvatarFileStorage::getAvatar(const std::string& hash) const { ByteArray data; - readByteArrayFromFile(data, getAvatarPath(hash)); + try { + readByteArrayFromFile(data, getAvatarPath(hash)); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << "filesystem error: " << e.what() << std::endl; + } return data; } @@ -98,7 +107,7 @@ void AvatarFileStorage::saveJIDAvatars() { file.close(); } catch (...) { - std::cerr << "Error writing avatars file" << std::endl; + SWIFT_LOG(error) << "Error writing avatars file" << std::endl; } } diff --git a/Swift/Packaging/Debian/Testing/swift-distr-tests.sh b/Swift/Packaging/Debian/Testing/swift-distr-tests.sh index 8d03282..e817974 100755 --- a/Swift/Packaging/Debian/Testing/swift-distr-tests.sh +++ b/Swift/Packaging/Debian/Testing/swift-distr-tests.sh @@ -40,25 +40,31 @@ arr[0,0]="ubuntu" arr[0,1]="http://archive.ubuntu.com/ubuntu" arr[0,2]="xenial" -#Ubuntu yakkety +#Ubuntu artful arr[1,0]="ubuntu" arr[1,1]="http://archive.ubuntu.com/ubuntu" -arr[1,2]="yakkety" +arr[1,2]="artful" #Debian jessie arr[2,0]="debian" arr[2,1]="http://deb.debian.org/debian/" arr[2,2]="jessie" -#Debian sid +#Debian stretch arr[3,0]="debian" arr[3,1]="http://deb.debian.org/debian/" -arr[3,2]="sid" +arr[3,2]="stretch" + +#Debian sid +arr[4,0]="debian" +arr[4,1]="http://deb.debian.org/debian/" +arr[4,2]="sid" + DIST="$1" RETURN_VALUE=0 -for counter in {0..3} +for counter in {0..4} do # for ARCH in "amd64" "i386" for ARCH in "amd64" diff --git a/Swift/Packaging/Debian/changelog.debian-unstable b/Swift/Packaging/Debian/changelog.debian-unstable index ca9ffec..f2bf2c5 100644 --- a/Swift/Packaging/Debian/changelog.debian-unstable +++ b/Swift/Packaging/Debian/changelog.debian-unstable @@ -1,3 +1,21 @@ +swift-im (4.0.3-1) UNRELEASED; urgency=medium + + * New chat theme including a new font + * Support for message carbons (XEP-0280) + * Enabled trellis mode as a default feature, allowing several tiled chats windows to be shown at once + * Redesigned keyword highlighting + * Improve date formatting + * Fix Last Message Correction in multi client scenarios + * Fix UI layout issue for translations that require right-to-left (RTL) layout + * Fix UX issues in trellis mode + * Improvements to font size handling in the chat theme + * Add AppImage for Linux 64-bit as a supported platform + * Improved spell checker support on Linux + * Allow setting vCard on servers that do not return an empty vCard on fresh accounts + * And assorted smaller features and usability enhancements. Closes: 840151, 889062 + + -- Kevin Smith <kevin@kismith.co.uk> Thu, 03 Jan 2019 13:59:07 +0100 + swift-im (3.0.4-1) unstable; urgency=low * New upstream release diff --git a/Swift/Packaging/Debian/debian/control.in b/Swift/Packaging/Debian/debian/control.in index 26544e5..e779f21 100644 --- a/Swift/Packaging/Debian/debian/control.in +++ b/Swift/Packaging/Debian/debian/control.in @@ -3,7 +3,7 @@ Section: net Priority: optional Maintainer: Swift Package Maintainer <packages@swift.im> Uploaders: Remko Tronçon <dev@el-tramo.be>, Kevin Smith <kevin@kismith.co.uk> -Build-Depends: debhelper (>= 9), scons (>= 1.2.0), libssl-dev (>= 0.9.8g), qtbase5-dev (>= 5.0.0), qtchooser, qtbase5-dev-tools (>= 5.0.0), libqt5x11extras5-dev (>= 5.0.0), libqt5webkit5-dev (>= 5.0.0), qtmultimedia5-dev (>=5.0.0), qttools5-dev-tools (>=5.0.0), qt5-image-formats-plugins (>=5.0.0), libqt5svg5-dev (>=5.0.0), libxml2-dev (>= 2.7.6), libxss-dev (>= 1.2.0), libboost-dev (>= 1.34.1), libboost-filesystem-dev (>= 1.34.1), libboost-program-options-dev (>= 1.34.1), libboost-regex-dev (>= 1.34.1), libboost-signals-dev (>= 1.34.1), libboost-system-dev (>= 1.34.1), libboost-thread-dev (>= 1.34.1), libboost-date-time-dev (>= 1.34.1), libidn11-dev (>= 1.10), docbook-xsl (>= 1.75.0), docbook-xml (>= 4.5), xsltproc, libxml2-utils, libnatpmp-dev, libminiupnpc-dev, libsqlite3-dev, libhunspell-dev, zlib1g-dev +Build-Depends: debhelper (>= 9), scons (>= 1.2.0), libssl-dev (>= 0.9.8g), qtbase5-dev (>= 5.0.0), qtchooser, qtbase5-dev-tools (>= 5.0.0), libqt5x11extras5-dev (>= 5.0.0), libqt5webkit5-dev (>= 5.0.0), qtmultimedia5-dev (>=5.0.0), qttools5-dev-tools (>=5.0.0), qt5-image-formats-plugins (>=5.0.0), libqt5svg5-dev (>=5.0.0), libxml2-dev (>= 2.7.6), libxss-dev (>= 1.2.0), libboost-dev (>= 1.34.1), libboost-filesystem-dev (>= 1.34.1), libboost-program-options-dev (>= 1.34.1), libboost-regex-dev (>= 1.34.1), libboost-signals-dev (>= 1.34.1), libboost-system-dev (>= 1.34.1), libboost-thread-dev (>= 1.34.1), libboost-date-time-dev (>= 1.34.1), libidn11-dev (>= 1.10), docbook-xsl (>= 1.75.0), docbook-xml (>= 4.5), xsltproc, libxml2-utils, libnatpmp-dev, libminiupnpc-dev, libsqlite3-dev, libhunspell-dev, zlib1g-dev, help2man Standards-Version: 3.9.8 Vcs-Git: git://swift.im/swift Vcs-Browser: http://swift.im/git/swift diff --git a/Swift/Packaging/Debian/debian/copyright b/Swift/Packaging/Debian/debian/copyright index a0c2c79..9219930 100644 --- a/Swift/Packaging/Debian/debian/copyright +++ b/Swift/Packaging/Debian/debian/copyright @@ -3,7 +3,7 @@ with help from Olly Betts <olly@survex.com>. The upstream sources were obtained from http://swift.im. -Copyright (C) 2010-2016 Isode Limited. +Copyright (C) 2010-2019 Isode Limited. Licensed under the GNU General Public License. See /usr/share/common-licenses/GPL-3 for the full license. diff --git a/Swift/Packaging/Debian/debian/rules b/Swift/Packaging/Debian/debian/rules index 87c551b..ed432bb 100755 --- a/Swift/Packaging/Debian/debian/rules +++ b/Swift/Packaging/Debian/debian/rules @@ -4,7 +4,7 @@ export PYTHONDONTWRITEBYTECODE=1 export QT_SELECT=qt5 -SCONS_FLAGS=V=1 qt5=1 optimize=1 debug=1 allow_warnings=1 swiften_dll=1 docbook_xsl=/usr/share/xml/docbook/stylesheet/docbook-xsl docbook_xml=/usr/share/xml/docbook/schema/dtd/4.5 linkflags="$(shell dpkg-buildflags --get LDFLAGS)" ccflags="$(shell dpkg-buildflags --get CPPFLAGS) $(shell dpkg-buildflags --get CFLAGS)" +SCONS_FLAGS=V=1 qt5=1 optimize=1 debug=1 allow_warnings=1 swiften_dll=1 help2man=1 docbook_xsl=/usr/share/xml/docbook/stylesheet/docbook-xsl docbook_xml=/usr/share/xml/docbook/schema/dtd/4.5 linkflags="$(shell dpkg-buildflags --get LDFLAGS)" ccflags="$(shell dpkg-buildflags --get CPPFLAGS) $(shell dpkg-buildflags --get CFLAGS)" SCONS_INSTALL_BASE=$(CURDIR)/debian/tmp SCONS_INSTALL_FLAGS=SWIFT_INSTALLDIR=$(SCONS_INSTALL_BASE)/usr SWIFTEN_INSTALLDIR=$(SCONS_INSTALL_BASE)/usr SWIFT_INSTALLDIR=$(SCONS_INSTALL_BASE)/usr SWIFTEN_INSTALLDIR=$(SCONS_INSTALL_BASE)/usr @@ -21,7 +21,7 @@ override_dh_clean: override_dh_configure: override_dh_auto_build: - scons $(SCONS_FLAGS) $(SCONS_EXTRA_FLAGS) Swift Swiften + scons $(SCONS_FLAGS) $(SCONS_EXTRA_FLAGS) Swift Swiften debian/swift-im.1 debian/swiften-config.1 override_dh_auto_install: scons $(SCONS_FLAGS) $(SCONS_EXTRA_FLAGS) $(SCONS_INSTALL_FLAGS) $(SCONS_INSTALL_BASE) diff --git a/Swift/Packaging/Debian/package.sh b/Swift/Packaging/Debian/package.sh index d19f5dc..62dfff0 100755 --- a/Swift/Packaging/Debian/package.sh +++ b/Swift/Packaging/Debian/package.sh @@ -95,18 +95,6 @@ cat $DIRNAME/debian/control.in | sed -e "s/%SWIFTEN_SOVERSION%/$SWIFTEN_SOVERSIO rm $DIRNAME/debian/control.in mv $DIRNAME/debian/libswiften.install $DIRNAME/debian/libswiften$SWIFTEN_SOVERSION.install cat ../../../COPYING.thirdparty | tail -n +3 >> $DIRNAME/debian/copyright -# Generate updated man-page if possible -if type "help2man" > /dev/null 2>&1; then - if [ -f ../../QtUI/swift-im ]; then - help2man -m "Swift Manual" -S "Swift" -n "swift-im" -N ../../QtUI/swift-im > $DIRNAME/debian/swift-im.1 - fi - if [ -f ../../../Swiften/Config/swiften-config ]; then - help2man -m "Swift Manual" -S "swiften-config" -n "swiften-config" -N ../../../Swiften/Config/swiften-config > $DIRNAME/debian/swiften-config.1 - fi -else - >2& echo "Unable to generate man pages. Please ensure that help2man is installed" - exit 1; -fi # Build cd $DIRNAME diff --git a/Swift/Packaging/Debian/package_all_platforms.sh b/Swift/Packaging/Debian/package_all_platforms.sh index 097fe38..afd0621 100755 --- a/Swift/Packaging/Debian/package_all_platforms.sh +++ b/Swift/Packaging/Debian/package_all_platforms.sh @@ -24,9 +24,15 @@ export SWIFT_FORCE_LUCID="yep" unset SWIFT_FORCE_LUCID ./package.sh -for distro in xenial yakkety jessie sid; do +if [ -z ${SWIFT_PACKAGE_PLATFORMS+x} ]; +then + platformsarray=( xenial artful jessie stretch sid ) +else + platformsarray=( $SWIFT_PACKAGE_PLATFORMS ) +fi + +for distro in "${platformsarray[@]}"; do for arch in amd64; do pbuilder-dist $distro $arch build *.dsc done done - diff --git a/Swift/Packaging/Debian/update_debian_repo.sh b/Swift/Packaging/Debian/update_debian_repo.sh index d62a376..b057103 100644 --- a/Swift/Packaging/Debian/update_debian_repo.sh +++ b/Swift/Packaging/Debian/update_debian_repo.sh @@ -73,12 +73,23 @@ command -v reprepro >/dev/null 2>&1 || { echo >&2 "This script requires aptly bu mkdir -p $APT_REPO_ROOT +if [ -z ${SWIFT_PACKAGE_PLATFORMS+x} ]; then + platformsarray=( xenial artful jessie stretch sid ) +else + platformsarray=( $SWIFT_PACKAGE_PLATFORMS ) +fi + # distros for full_distribution_path in $INCOMING_FOLDER/{debian,ubuntu}/*; do distro_version=`basename $full_distribution_path` distro_name=$(basename `dirname $full_distribution_path`) distro_reprepro_root=${APT_REPO_ROOT}/${distro_name}/${distro_version} + if ! [[ $SWIFT_PACKAGE_PLATFORMS == *"$distro_version"* ]]; then + echo "$distro_version was not found in SWIFT_PACKAGE_PLATFORMS. Skipping..." + continue + fi + # ensure reprepro diretctory for this distribution version is present if [ ! -d "$distro_preprepro_root" ]; then echo "Create distribution repository for $distro_name/$distro_version" @@ -89,7 +100,7 @@ for full_distribution_path in $INCOMING_FOLDER/{debian,ubuntu}/*; do write_reprepo_conf_incoming_to_file "${distro_reprepro_root}/conf/incoming" "$full_distribution_path" fi - # This is workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=808558 + # This is workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=808558 # and https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=843402 if [ "$distro_name/$distro_version" = "debian/sid" ]; then sed -i '/dbgsym/ d' $full_distribution_path/*.changes diff --git a/Swift/Packaging/SConscript b/Swift/Packaging/SConscript new file mode 100644 index 0000000..3aa791f --- /dev/null +++ b/Swift/Packaging/SConscript @@ -0,0 +1,29 @@ +Import("env") +import os + +################################################################################ +# Build +################################################################################ + +if env["SCONS_STAGE"] == "build" : + help2man = env.WhereIs('help2man', os.environ['PATH']) + if help2man: + env['HELP2MAN'] = help2man + env['HELP2MANSTR'] = "HELP2MAN $TARGET" + + if Dir("#/debian").exists(): + # This is needed for debian packaging using pbuilder which expects + # generated man pages in this location. + env['HELP2MAN_DEBIAN_DIR'] = "#/debian" + else: + env['HELP2MAN_DEBIAN_DIR'] = "Debian/debian" + + swiftenConfigHelp = env.Command( + target='$HELP2MAN_DEBIAN_DIR/swiften-config.1', source='#/Swiften/Config/swiften-config', + action = Action('$HELP2MAN --no-discard-stderr -m "Swift Manual" -S "swiften-config" -n "swiften-config" -N $SOURCE > $TARGET', cmdstr = "$HELP2MANSTR")) + swiftHelp = env.Command( + target='$HELP2MAN_DEBIAN_DIR/swift-im.1', source='#/Swift/QtUI/swift-im', + action = Action('$HELP2MAN --no-discard-stderr -m "Swift Manual" -S "Swift" -n "swift-im" -N $SOURCE > $TARGET', cmdstr = "$HELP2MANSTR")) + else: + print "Enabled help2man but help2man is not in the PATH of the current environment." + Exit(1) diff --git a/Swift/Packaging/WiX/include.xslt b/Swift/Packaging/WiX/include.xslt index ec1ad50..df86446 100644 --- a/Swift/Packaging/WiX/include.xslt +++ b/Swift/Packaging/WiX/include.xslt @@ -1,6 +1,6 @@ <?xml version="1.0"?> -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wix="http://schemas.microsoft.com/wix/2006/wi" xmlns="http://schemas.microsoft.com/wix/2006/wi" exclude-result-prefixes="xsl wix"> <xsl:template match='wix:Directory[@Id="Swift"]/@Id'> <xsl:attribute name='Id'>INSTALLDIR</xsl:attribute> @@ -11,4 +11,14 @@ <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> + + <xsl:template match="wix:Component"> + <xsl:copy> + <xsl:apply-templates select="@*"/> + <xsl:value-of select="concat(preceding-sibling::text(), ' ')" /> + <RemoveFile Id="remove_{@Id}" Name="{@Id}" On="install" /> + <xsl:apply-templates select="node()"/> + </xsl:copy> + </xsl:template> + </xsl:stylesheet> diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp index ad95a07..f4d0d46 100644 --- a/Swift/QtUI/QtChatTabs.cpp +++ b/Swift/QtUI/QtChatTabs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -18,8 +18,8 @@ #include <QMenu> #include <QTabBar> #include <QTabWidget> -#include <QtGlobal> #include <QWindow> +#include <QtGlobal> #include <Swiften/Base/Log.h> @@ -112,8 +112,6 @@ void QtChatTabs::setViewMenu(QMenu* viewMenu) { if (trellisMode_) { viewMenu->addSeparator(); QAction* action = new QAction(tr("Change &layout"), this); - action->setShortcutContext(Qt::ApplicationShortcut); - action->setShortcut(QKeySequence(tr("Ctrl+Alt+L"))); connect(action, SIGNAL(triggered()), this, SLOT(handleOpenLayoutChangeDialog())); viewMenu->addAction(action); diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 7051683..874f710 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -707,9 +707,9 @@ void QtChatWindow::handleEmojiClicked(QString emoji) { if (isVisible()) { input_->textCursor().insertText(emoji); input_->setFocus(); - // The next line also deletes the emojisGrid_, as it was added to the - // layout of the emojisMenu_. - emojisMenu_.reset(); + // We cannot delete the emojisGrid_ + // Grid may not close yet and we should not try to destroy it. + emojisMenu_->setVisible(false); } } diff --git a/Swift/QtUI/QtColorToolButton.cpp b/Swift/QtUI/QtColorToolButton.cpp index b349a47..6452cf4 100644 --- a/Swift/QtUI/QtColorToolButton.cpp +++ b/Swift/QtUI/QtColorToolButton.cpp @@ -36,6 +36,7 @@ void QtColorToolButton::setColor(const QColor& color) void QtColorToolButton::onClicked() { QColor c = QColorDialog::getColor(color_, this); + window()->raise(); if (c.isValid()) { setColor(c); } diff --git a/Swift/QtUI/QtTabWidget.cpp b/Swift/QtUI/QtTabWidget.cpp index 67e3ae9..99ef6ee 100644 --- a/Swift/QtUI/QtTabWidget.cpp +++ b/Swift/QtUI/QtTabWidget.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -57,7 +57,7 @@ void QtTabWidget::paintEvent(QPaintEvent * event) { label.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); label.setGeometry(QRect(QPoint(0,0), size()) - QMargins(10,10,10,10)); label.setWordWrap(true); - label.setText(tr("This empty cell is a placeholder for chat windows. You can move existing chats to this cell by dragging the tab over here. You can change the number of cells via the 'Change layout' dialog under the 'View' menu or by using the %1 shortcut.").arg(QKeySequence(tr("Ctrl+Alt+L")).toString(QKeySequence::NativeText))); + label.setText(tr("This empty cell is a placeholder for chat windows. You can move existing chats to this cell by dragging the tab over here. You can change the number of cells via the 'Change layout' dialog under the 'View' menu.")); QPainter painter(this); painter.drawPixmap(label.geometry().topLeft(), label.grab()); } diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 112a66e..54f0450 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -497,7 +497,7 @@ if env["PLATFORM"] == "win32" : signresult = 0 for x in range (1, 4) : print "Attemping to sign the packages [%s]" % x - signresult = env.Execute('signtool.exe sign /fd SHA256 /f "${SIGNTOOL_KEY_PFX}" /t "${SIGNTOOL_TIMESTAMP_URL}" ' + str(target[0])) + signresult = env.Execute('signtool.exe sign /fd SHA256 /f "${SIGNTOOL_KEY_PFX}" /t "${SIGNTOOL_TIMESTAMP_URL}" /d "Swift Installer" ' + str(target[0])) if signresult != 1 : break #If all 3 attemps to sign the package failed, stop the build. diff --git a/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp b/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp index b753ffa..2509b3f 100644 --- a/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp +++ b/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp @@ -144,7 +144,9 @@ void QtDynamicGridLayout::removeTab(int index) { int tabIndex = -1; QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex); if (tabWidget) { + QWidget* tab = tabWidget->widget(tabIndex); tabWidget->removeTab(tabIndex); + tab->setParent(nullptr); } } diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp index 8d15739..4489bc0 100644 --- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp +++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp @@ -606,6 +606,7 @@ void QtUserSearchWindow::clear() { howText = QString(tr("Who do you want to invite to the chat?")); } firstMultiJIDPage_->howLabel_->setText(howText); + firstMultiJIDPage_->groupBox->setEnabled(true); } clearForm(); resultsPage_->results_->setModel(nullptr); diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.h b/Swift/QtUI/UserSearch/QtUserSearchWindow.h index 0714ac1..fe536ab 100644 --- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h +++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -8,6 +8,7 @@ #include <set> +#include <QAbstractItemModel> #include <QWizard> #include <Swiften/Base/Override.h> diff --git a/Swift/SConscript b/Swift/SConscript index 863597a..b211435 100644 --- a/Swift/SConscript +++ b/Swift/SConscript @@ -19,3 +19,5 @@ if "Swift" in env["PROJECTS"] and env["BOOST_1_64_DETECTED"] and not env.get("al #Version 1.64 has some issues with the serialization of boost::optional, see https://svn.boost.org/trac10/ticket/13050 print "Boost 1.64 has been detected. It is not recommended to use this version due to a regression within the library. Swift has been removed from the current build. You can still use this version by setting allow_boost_1_64 to true, but recent chats and highlighting rules will reset." env["PROJECTS"].remove("Swift") +if env["help2man"]: + SConscript("Packaging/SConscript")
\ No newline at end of file diff --git a/Swift/Translations/swift_de.ts b/Swift/Translations/swift_de.ts index 46c313f..73e0ed8 100644 --- a/Swift/Translations/swift_de.ts +++ b/Swift/Translations/swift_de.ts @@ -2686,9 +2686,8 @@ <translation>&Anordnung ändern</translation> </message> <message> - <location filename="../QtUI/QtChatTabs.cpp" line="116"/> <source>Ctrl+Alt+L</source> - <translation>Ctrl+Alt+L</translation> + <translation type="vanished">Ctrl+Alt+L</translation> </message> <message> <location filename="../QtUI/QtChatTabs.cpp" line="120"/> @@ -3645,14 +3644,17 @@ <context> <name>Swift::QtTabWidget</name> <message> - <location filename="../QtUI/QtTabWidget.cpp" line="60"/> <source>This empty cell is a placeholder for chat windows. You can move existing chats to this cell by dragging the tab over here. You can change the number of cells via the 'Change layout' dialog under the 'View' menu or by using the %1 shortcut.</source> - <translation>Diese leere Zelle ist ein Platzhalter für Chatfenster. Sie können bestehende Chatfenster zu dieser Zelle bewegen indem Sie das Tab hierher ziehen. Sie können die Anzahl von Zellen anhand des "Anordnung ändern" Dialogs im Menü "Ansicht" ändern oder indem Sie die Tastenkombination %1 nutzen.</translation> + <translation type="vanished">Diese leere Zelle ist ein Platzhalter für Chatfenster. Sie können bestehende Chatfenster zu dieser Zelle bewegen indem Sie das Tab hierher ziehen. Sie können die Anzahl von Zellen anhand des "Anordnung ändern" Dialogs im Menü "Ansicht" ändern oder indem Sie die Tastenkombination %1 nutzen.</translation> </message> <message> - <location filename="../QtUI/QtTabWidget.cpp" line="60"/> <source>Ctrl+Alt+L</source> - <translation>Ctrl+Alt+L</translation> + <translation type="vanished">Ctrl+Alt+L</translation> + </message> + <message> + <location filename="../QtUI/QtTabWidget.cpp" line="60"/> + <source>This empty cell is a placeholder for chat windows. You can move existing chats to this cell by dragging the tab over here. You can change the number of cells via the 'Change layout' dialog under the 'View' menu.</source> + <translation>Diese leere Zelle ist ein Platzhalter für Chatfenster. Sie können bestehende Chatfenster zu dieser Zelle bewegen indem Sie das Tab hierher ziehen. Sie können die Anzahl von Zellen anhand des "Anordnung ändern" Dialogs im Menü "Ansicht" ändern.</translation> </message> </context> <context> diff --git a/Swift/Translations/swift_nl.ts b/Swift/Translations/swift_nl.ts index 2aebf93..7b37e72 100644 --- a/Swift/Translations/swift_nl.ts +++ b/Swift/Translations/swift_nl.ts @@ -2137,10 +2137,6 @@ afbeelding</translation> <translation>Wijzig &opmaak</translation> </message> <message> - <source>Ctrl+Alt+L</source> - <translation>Ctrl+Alt+L</translation> - </message> - <message> <source>Move Tab right</source> <translation>Verplaats tab naar rechts</translation> </message> @@ -2874,12 +2870,8 @@ afbeelding</translation> <context> <name>Swift::QtTabWidget</name> <message> - <source>This empty cell is a placeholder for chat windows. You can move existing chats to this cell by dragging the tab over here. You can change the number of cells via the 'Change layout' dialog under the 'View' menu or by using the %1 shortcut.</source> - <translation>Dit lege vak is vulling voor berichtenschermen. U kunt bestaande gesprekken naar dit vak verplaatsen door het tabje hier heen te slepen. U kunt het aantal vakken veranderen door middel van het 'Wijzig opmaak'-scherm in het 'Beeld'-menu of door middel van de %1 snelkoppeling.</translation> - </message> - <message> - <source>Ctrl+Alt+L</source> - <translation>Ctrl+Alt+L</translation> + <source>This empty cell is a placeholder for chat windows. You can move existing chats to this cell by dragging the tab over here. You can change the number of cells via the 'Change layout' dialog under the 'View' menu.</source> + <translation>Dit lege vak is vulling voor berichtenschermen. U kunt bestaande gesprekken naar dit vak verplaatsen door het tabje hier heen te slepen. U kunt het aantal vakken veranderen door middel van het 'Wijzig opmaak'-scherm in het 'Beeld'-menu.</translation> </message> </context> <context> |