./0000755000015600001650000000000012676763577011127 5ustar jenkinsjenkins./cmake/0000755000015600001650000000000012676763577012207 5ustar jenkinsjenkins./cmake/UseGSettings.cmake0000644000015600001650000000371512676763577015603 0ustar jenkinsjenkins# GSettings.cmake, CMake macros written for Marlin, feel free to re-use them. option (GSETTINGS_LOCALINSTALL "Install GSettings Schemas locally instead of to the GLib prefix" ${LOCAL_INSTALL}) option (GSETTINGS_COMPILE "Compile GSettings Schemas after installation" ${GSETTINGS_LOCALINSTALL}) if(GSETTINGS_LOCALINSTALL) message(STATUS "GSettings schemas will be installed locally.") endif() if(GSETTINGS_COMPILE) message(STATUS "GSettings shemas will be compiled.") endif() macro(add_schema SCHEMA_NAME) set(PKG_CONFIG_EXECUTABLE pkg-config) # Have an option to not install the schema into where GLib is if (GSETTINGS_LOCALINSTALL) SET (GSETTINGS_DIR "${CMAKE_INSTALL_PREFIX}/share/glib-2.0/schemas/") else (GSETTINGS_LOCALINSTALL) execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} glib-2.0 --variable prefix OUTPUT_VARIABLE _glib_prefix OUTPUT_STRIP_TRAILING_WHITESPACE) SET (GSETTINGS_DIR "${_glib_prefix}/share/glib-2.0/schemas/") endif (GSETTINGS_LOCALINSTALL) # Run the validator and error if it fails execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas OUTPUT_VARIABLE _glib_comple_schemas OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process (COMMAND ${_glib_comple_schemas} --dry-run --schema-file=${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME} ERROR_VARIABLE _schemas_invalid OUTPUT_STRIP_TRAILING_WHITESPACE) if (_schemas_invalid) message (SEND_ERROR "Schema validation error: ${_schemas_invalid}") endif (_schemas_invalid) # Actually install and recomple schemas message (STATUS "GSettings schemas will be installed into ${GSETTINGS_DIR}") install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME} DESTINATION ${GSETTINGS_DIR} OPTIONAL) if (GSETTINGS_COMPILE) install (CODE "message (STATUS \"Compiling GSettings schemas\")") install (CODE "execute_process (COMMAND ${_glib_comple_schemas} ${GSETTINGS_DIR})") endif () endmacro() ./scope/0000755000015600001650000000000012676763577012240 5ustar jenkinsjenkins./scope/tests/0000755000015600001650000000000012676763577013402 5ustar jenkinsjenkins./scope/tests/fake_launcher/0000755000015600001650000000000012676763577016171 5ustar jenkinsjenkins./scope/tests/fake_launcher/fake_launcher.h0000644000015600001650000000707412676763577021141 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef _FAKE_LAUNCHER_H_ #define _FAKE_LAUNCHER_H_ #include #include #include #include #include class FakeIcon : public QObject { Q_OBJECT public: explicit FakeIcon(QString title, QString icon_url, QObject *parent=0) : QObject(parent), title(title), icon_url(icon_url) { } void downloadFound(Download& download); void complete(QString app_id); private slots: void handleProgress(qulonglong transferred, qulonglong total); void handleDownloadError(Error* error); private: Download* download; QString title; QString icon_url; void failure(QString message); }; class FakeLauncher : public QObject { Q_OBJECT public: explicit FakeLauncher(QObject *parent=0) : QObject(parent) { manager = Ubuntu::DownloadManager::Manager::createSessionManager(); connect(manager, SIGNAL(downloadsWithMetadataFound(QString,QString,DownloadsList*)), this, SLOT(handleDownloadsFound(QString,QString,DownloadsList*))); } public slots: void startInstallation(QString title, QString icon_url, QString package_name); void completeInstallation(QString package_name, QString app_id); private slots: void handleDownloadsFound(const QString& key, const QString& value, DownloadsList* downloads); signals: private: Ubuntu::DownloadManager::Manager* manager; QMap installations; }; class FakeLauncherAdaptor : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", LAUNCHER_INTERFACE) private: FakeLauncher *_launcher; public: FakeLauncherAdaptor(FakeLauncher *launcher) : QDBusAbstractAdaptor(launcher), _launcher(launcher) { } public slots: Q_NOREPLY void startInstallation(QString title, QString icon_url, QString package_name) { _launcher->startInstallation(title, icon_url, package_name); } Q_NOREPLY void completeInstallation(QString package_name, QString app_id) { _launcher->completeInstallation(package_name, app_id); } }; #endif /* _FAKE_LAUNCHER_H_ */ ./scope/tests/fake_launcher/fake_launcher.cpp0000644000015600001650000000774712676763577021503 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include #include void FakeLauncher::startInstallation(QString title, QString icon_url, QString package_name) { qDebug() << "startInstallation" << title << icon_url << package_name; installations.insert(package_name, new FakeIcon(title, icon_url, this)); manager->getAllDownloadsWithMetadata("package_name", package_name); } void FakeLauncher::handleDownloadsFound(const QString& key, const QString& value, DownloadsList *download_list) { Q_UNUSED(key) Q_UNUSED(value) foreach (const QSharedPointer download, download_list->downloads()) { QString package_name{download->metadata()["package_name"].toString()}; qDebug() << "download found for" << package_name; installations[package_name]->downloadFound(*download); } } void FakeLauncher::completeInstallation(QString package_name, QString app_id) { qDebug() << "completeInstallation" << package_name << app_id; installations[package_name]->complete(app_id); } void FakeIcon::downloadFound(Download& download) { connect(&download, SIGNAL(progress(qulonglong,qulonglong)), this, SLOT(handleProgress(qulonglong,qulonglong))); connect(&download, SIGNAL(error(Error*)), this, SLOT(handleDownloadError(Error*))); qDebug() << title << "starting installation"; // TODO: add icon to the launcher } void FakeIcon::handleProgress(qulonglong transferred, qulonglong total) { qDebug() << title << "download progress" << double(transferred)/total*80; // TODO: update progress bar } void FakeIcon::handleDownloadError(Error *error) { failure(error->errorString()); } void FakeIcon::failure(QString message) { qDebug() << title << "installation failed" << message; // TODO: remove icon from the launcher } void FakeIcon::complete(QString app_id) { if (app_id.isEmpty()) { failure("Failed to install"); } else { qDebug() << title << "installation completed" << app_id; // TODO: update icon with proper app_id } } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); FakeLauncher launcher; new FakeLauncherAdaptor(&launcher); qDebug() << "starting"; auto bus = QDBusConnection::sessionBus(); bus.registerObject(LAUNCHER_OBJECT_PATH, &launcher); bus.registerService(LAUNCHER_BUSNAME); return app.exec(); } ./scope/tests/fake_launcher/CMakeLists.txt0000644000015600001650000000040112676763577020724 0ustar jenkinsjenkinsset(FAKE_LAUNCHER_TARGET fake_launcher) include_directories ( ${CMAKE_SOURCE_DIR}/scope/click ) add_executable (${FAKE_LAUNCHER_TARGET} fake_launcher.cpp fake_launcher.h ) target_link_libraries (${FAKE_LAUNCHER_TARGET} ${STORE_LIB_UNVERSIONED} ) ./scope/tests/test_apps_query.cpp0000644000015600001650000003477212676763577017352 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include "test_helpers.h" using namespace click::test::helpers; using namespace ::testing; class ResultPusherTest : public ::testing::Test { protected: scopes::SearchReplyProxy reply; public: ResultPusherTest() { reply.reset(new scopes::testing::MockSearchReply()); } }; class MockClickInterface : public click::Interface { public: MockClickInterface() = default; MOCK_METHOD4(find_installed_apps, std::vector(const std::string&, const std::vector&, const std::string&, const std::shared_ptr&)); }; class MockAppsQuery : public click::apps::Query { private: std::shared_ptr click_iface; public: MockAppsQuery(unity::scopes::CannedQuery const& query, std::shared_ptr depts_db, scopes::SearchMetadata const& metadata, const std::shared_ptr& click_iface) : click::apps::Query(query, depts_db, metadata), click_iface(click_iface) { } click::Interface& clickInterfaceInstance() override { return *click_iface; } }; MATCHER_P(HasApplicationTitle, n, "") { return arg["title"].get_string() == n; } TEST_F(ResultPusherTest, testPushTopAndLocalResults) { std::string categoryTemplate("{}"); std::vector apps { {"app1", "App1", 0.0f, "icon", "url", "", "sshot", ""}, {"app2", "App2", 0.0f, "icon", "url", "", "sshot", ""}, {"app3", "App3", 0.0f, "icon", "url", "", "sshot", ""}, {"", "App4", 0.0f, "icon", "application:///app4.desktop", "", "sshot", ""} // a non-click app }; click::apps::ResultPusher pusher(reply, {"app2_fooappname", "app4"}); auto mockreply = (scopes::testing::MockSearchReply*)reply.get(); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(*mockreply, register_category(_, _, _, _)).WillRepeatedly(Return(ptrCat)); EXPECT_CALL(*mockreply, push(Matcher(HasApplicationTitle(std::string("App2"))))); EXPECT_CALL(*mockreply, push(Matcher(HasApplicationTitle(std::string("App4"))))); EXPECT_CALL(*mockreply, push(Matcher(HasApplicationTitle(std::string("App1"))))); EXPECT_CALL(*mockreply, push(Matcher(HasApplicationTitle(std::string("App3"))))); pusher.push_top_results(apps, categoryTemplate); pusher.push_local_results(apps, categoryTemplate, true); } MATCHER_P(ResultUriMatchesCannedQuery, q, "") { auto const query = unity::scopes::CannedQuery::from_uri(arg.uri()); return query.scope_id() == q.scope_id() && query.query_string() == q.query_string() && query.department_id() == q.department_id(); } MATCHER_P(CategoryTitleContains, s, "") { return arg.find(s) != std::string::npos; } TEST(Query, testUbuntuStoreFakeResult) { const scopes::SearchMetadata metadata("en_EN", "phone"); const unity::scopes::CannedQuery query("foo.scope", "FooBar", ""); const unity::scopes::CannedQuery query2("foo.scope", "Metallica", ""); click::apps::Query q(query, nullptr, metadata); click::apps::Query q2(query2, nullptr, metadata); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); const unity::scopes::CannedQuery target_query("com.canonical.scopes.clickstore", "FooBar", ""); EXPECT_CALL(mock_reply, register_category("store", CategoryTitleContains("FooBar"), _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, push(Matcher(ResultUriMatchesCannedQuery(target_query)))); scopes::testing::MockSearchReply mock_reply2; scopes::SearchReplyProxy reply2(&mock_reply2, [](unity::scopes::SearchReply*){}); const unity::scopes::CannedQuery target_query2("com.canonical.scopes.clickstore", "Metallica", ""); EXPECT_CALL(mock_reply2, register_category("store", CategoryTitleContains("Metallica"), _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply2, push(Matcher(ResultUriMatchesCannedQuery(target_query2)))); q.add_fake_store_app(reply); q2.add_fake_store_app(reply2); } TEST(Query, testUbuntuStoreFakeResultWithDepartment) { const scopes::SearchMetadata metadata("en_EN", "phone"); const unity::scopes::CannedQuery query("foo.scope", "", "music-department"); click::apps::Query q(query, nullptr, metadata); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); const unity::scopes::CannedQuery target_query("com.canonical.scopes.clickstore", "", "music-department"); EXPECT_CALL(mock_reply, register_category("store", _, _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, push(Matcher(ResultUriMatchesCannedQuery(target_query)))); q.add_fake_store_app(reply); } class DepartmentsTest : public ::testing::Test { protected: virtual void SetUp() override { ASSERT_EQ(setenv("GSETTINGS_SCHEMA_DIR", TEST_SCHEMA_DIR, 1), 0); ASSERT_EQ(setenv("GSETTINGS_BACKEND", "memory", 1), 0); std::cerr << "SCHEMA dir: " << TEST_SCHEMA_DIR << std::endl; } virtual void TearDown() override { ASSERT_EQ(unsetenv("GSETTINGS_BACKEND"), 0); ASSERT_EQ(unsetenv("GSETTINGS_SCHEMA_DIR"), 0); } const std::vector installed_apps = { {"app1", "App1", 0.0f, "icon", "url", "descr", "scrshot", "", "games-rpg"}, {"app2", "App2", 0.0f, "icon", "url", "descr", "scrshot", "", "video"} }; const scopes::SearchMetadata metadata{"en_EN", "phone"}; const scopes::CategoryRenderer renderer{"{}"}; const std::list expected_locales {"en_EN", "en_US"}; }; TEST_F(DepartmentsTest, testRootDepartment) { auto clickif = std::make_shared(); auto ptrCat = std::make_shared("id", "", "", renderer); auto depts_db = std::make_shared(":memory:", true); // query for root of the departments tree { const unity::scopes::CannedQuery query("foo.scope", "", ""); MockAppsQuery q(query, depts_db, metadata, clickif); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); // no apps in 'books' department, thus excluded std::list expected_departments({{"", "games", "video"}}); EXPECT_CALL(*clickif, find_installed_apps(_, _, _, _)).WillOnce(Return(installed_apps)); EXPECT_CALL(mock_reply, register_category("predefined", _, _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, register_category("local", StrNe(""), _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, register_category("store", _, _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, register_departments(MatchesDepartments(expected_departments))); EXPECT_CALL(mock_reply, push(Matcher(_))).Times(3).WillRepeatedly(Return(true)); ON_CALL(*depts_db, is_descendant_of_department(_, _)).WillByDefault(Return(false)); ON_CALL(*depts_db, is_descendant_of_department("games", "")).WillByDefault(Return(true)); ON_CALL(*depts_db, is_descendant_of_department("games-rpg", "games")).WillByDefault(Return(true)); ON_CALL(*depts_db, is_descendant_of_department("books", "")).WillByDefault(Return(true)); ON_CALL(*depts_db, is_descendant_of_department("video", "")).WillByDefault(Return(true)); EXPECT_CALL(*depts_db, get_department_name("games", expected_locales)).WillOnce(Return("Games")); EXPECT_CALL(*depts_db, get_department_name("video", expected_locales)).WillOnce(Return("Video")); EXPECT_CALL(*depts_db, is_empty("games")).WillRepeatedly(Return(false)); EXPECT_CALL(*depts_db, is_empty("video")).WillRepeatedly(Return(false)); EXPECT_CALL(*depts_db, is_empty("books")).WillRepeatedly(Return(false)); EXPECT_CALL(*depts_db, is_descendant_of_department(_, _)).Times(AnyNumber()); EXPECT_CALL(*depts_db, get_children_departments("")).WillOnce(Return( std::list({ {"video", true}, {"books", true}, {"games", true} })) ); q.run(reply); } } TEST_F(DepartmentsTest, testLeafDepartment) { auto clickif = std::make_shared(); auto ptrCat = std::make_shared("id", "", "", renderer); auto depts_db = std::make_shared(":memory:", true); // query for a leaf department { const unity::scopes::CannedQuery query("foo.scope", "", "games"); MockAppsQuery q(query, depts_db, metadata, clickif); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); std::list expected_departments({"", "games"}); EXPECT_CALL(*clickif, find_installed_apps(_, _, _, _)).WillOnce(Return(installed_apps)); EXPECT_CALL(mock_reply, register_category("local", StrEq(""), _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, register_category("store", _, _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, register_departments(MatchesDepartments(expected_departments))); EXPECT_CALL(mock_reply, push(Matcher(_))).Times(3).WillRepeatedly(Return(true)); EXPECT_CALL(*depts_db, get_parent_department_id("games")).WillOnce(Return("")); EXPECT_CALL(*depts_db, get_department_name("games", expected_locales)).WillOnce(Return("Games")); EXPECT_CALL(*depts_db, get_children_departments("games")).WillOnce(Return( std::list({}) )); q.run(reply); } } TEST_F(DepartmentsTest, testNoDepartmentSearch) { auto clickif = std::make_shared(); auto ptrCat = std::make_shared("id", "", "", renderer); auto depts_db = std::make_shared(":memory:", true); // query for department-less search { const unity::scopes::CannedQuery query("foo.scope", "App", ""); MockAppsQuery q(query, depts_db, metadata, clickif); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(*clickif, find_installed_apps(_, _, _, _)).WillOnce(Return(installed_apps)); EXPECT_CALL(mock_reply, register_category("local", StrEq(""), _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, register_category("store", _, _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, push(Matcher(_))).Times(3).WillRepeatedly(Return(true)); q.run(reply); } } TEST_F(DepartmentsTest, testSearchInDepartment) { auto clickif = std::make_shared(); auto ptrCat = std::make_shared("id", "", "", renderer); auto depts_db = std::make_shared(":memory:", true); // query for a leaf department { const unity::scopes::CannedQuery query("foo.scope", "Fooo", "games"); MockAppsQuery q(query, depts_db, metadata, clickif); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); std::list expected_departments({"", "games"}); EXPECT_CALL(*clickif, find_installed_apps("Fooo", _, "games", _)).WillOnce(Return(installed_apps)); EXPECT_CALL(mock_reply, register_category("local", StrEq(""), _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(mock_reply, register_category("store", _, _, _)).WillOnce(Return(ptrCat)); EXPECT_CALL(*depts_db, get_department_name("games", expected_locales)).WillOnce(Return("Games")); EXPECT_CALL(*depts_db, get_children_departments("games")).WillOnce(Return( std::list({}) )); EXPECT_CALL(mock_reply, register_departments(MatchesDepartments(expected_departments))); EXPECT_CALL(mock_reply, push(Matcher(_))).Times(3).WillRepeatedly(Return(true)); q.run(reply); } } ./scope/tests/test_runner.h0000644000015600001650000000644512676763577016134 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef TEST_RUNNER_H #define TEST_RUNNER_H #include #include #include #include #include class TestRunner { public: static TestRunner& Instance() { static TestRunner instance; return instance; } template char RegisterTest(char* name) { if(!_tests.contains(name)) { QSharedPointer test(new T()); _tests.insert(name, QSharedPointer(test)); } return char(1); } int RunAll(int argc, char *argv[]) { // provide command line to run a single test case QCoreApplication* app = QCoreApplication::instance(); QStringList args = app->arguments(); if (args.contains("-testcase")) { int index = args.indexOf("-testcase"); if (args.count() > index + 1) { QString testcase = args[index + 1]; if (_tests.contains(testcase)) { args.removeAt(index + 1); args.removeAt(index); return QTest::qExec(_tests[testcase].data(), args); } else { return -1; } } else { return -1; } } else { int errorCode = 0; foreach (QString const &testName, _tests.keys()) { errorCode |= QTest::qExec(_tests[testName].data(), argc, argv); std::cout << std::endl; } return errorCode; } } private: QMap > _tests; }; // Use this macro after your test declaration #define DECLARE_TEST(className)\ static char test_##className = TestRunner::Instance().RegisterTest(const_cast(#className)); // Use this macro to execute all tests #define RUN_ALL_QTESTS(argc, argv)\ TestRunner::Instance().RunAll(argc, argv); #endif // TEST_RUNNER_H ./scope/tests/test_store_scope.cpp0000644000015600001650000001712512676763577017500 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include using namespace ::testing; class StoreScopeTest : public Test { protected: const std::string FAKE_SHA512 = "FAKE_SHA512"; click::Scope scope; unity::scopes::testing::Result result; unity::scopes::ActionMetadata metadata; unity::scopes::VariantMap metadict; public: StoreScopeTest() : metadata("en_EN", "phone") { metadict["download_url"] = "fake_download_url"; metadict["download_sha512"] = FAKE_SHA512; metadict["rating"] = unity::scopes::Variant(4.0f); metadict["review"] = "This is a review."; metadata.set_scope_data(unity::scopes::Variant(metadict)); } }; TEST_F(StoreScopeTest, testPurchaseCompletedPassesHash) { auto activation = scope.perform_action(result, metadata, "widget_id", "purchaseCompleted"); auto response = activation->activate(); EXPECT_EQ(FAKE_SHA512, response.scope_data().get_dict()["download_sha512"].get_string()); EXPECT_TRUE(response.scope_data().get_dict()["purchased"].get_bool()); } TEST_F(StoreScopeTest, testPurchaseError) { auto activation = scope.perform_action(result, metadata, "widget_id", "purchaseError"); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::DOWNLOAD_FAILED].get_bool()); } TEST_F(StoreScopeTest, testInstallClickPassesHash) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::INSTALL_CLICK); auto response = activation->activate(); EXPECT_EQ(FAKE_SHA512, response.scope_data().get_dict()["download_sha512"].get_string()); } TEST_F(StoreScopeTest, testDownloadFailed) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::DOWNLOAD_FAILED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::DOWNLOAD_FAILED].get_bool()); } TEST_F(StoreScopeTest, testDownloadCompleted) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::DOWNLOAD_COMPLETED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::DOWNLOAD_COMPLETED].get_bool()); EXPECT_TRUE(response.scope_data().get_dict()["installed"].get_bool()); } TEST_F(StoreScopeTest, testCancelPurchaseInstalled) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::CANCEL_PURCHASE_INSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CANCEL_PURCHASE_INSTALLED].get_bool()); } TEST_F(StoreScopeTest, testCancelPurchaseUninstalled) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED].get_bool()); } TEST_F(StoreScopeTest, testUninstallClick) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::UNINSTALL_CLICK); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::UNINSTALL_CLICK].get_bool()); } TEST_F(StoreScopeTest, testShowUninstalled) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::SHOW_UNINSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::SHOW_UNINSTALLED].get_bool()); } TEST_F(StoreScopeTest, testShowInstalled) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::SHOW_INSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::SHOW_INSTALLED].get_bool()); } TEST_F(StoreScopeTest, testConfirmUninstall) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::CONFIRM_UNINSTALL); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CONFIRM_UNINSTALL].get_bool()); } TEST_F(StoreScopeTest, testConfirmCancelPurchaseUninstalled) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED].get_bool()); } TEST_F(StoreScopeTest, testConfirmCancelPurcahseInstalled) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED].get_bool()); } TEST_F(StoreScopeTest, testStoreScopeRatingNew) { auto activation = scope.perform_action(result, metadata, "rating", click::Preview::Actions::RATED); auto response = activation->activate(); EXPECT_EQ("rating", response.scope_data().get_dict()["widget_id"].get_string()); } TEST_F(StoreScopeTest, testStoreScopeRatingEdit) { auto activation = scope.perform_action(result, metadata, "93345", click::Preview::Actions::RATED); auto response = activation->activate(); EXPECT_EQ("93345", response.scope_data().get_dict()["widget_id"].get_string()); } ./scope/tests/test_apps_scope.cpp0000644000015600001650000001242512676763577017305 0ustar jenkinsjenkins/* * Copyright (C) 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include using namespace ::testing; class AppsScopeTest : public Test { protected: click::Scope scope; unity::scopes::testing::Result result; unity::scopes::ActionMetadata metadata; unity::scopes::VariantMap metadict; public: AppsScopeTest() : metadata("en_EN", "phone") { metadict["rating"] = unity::scopes::Variant(4.0f); metadict["review"] = "This is a review."; metadata.set_scope_data(unity::scopes::Variant(metadict)); } }; TEST_F(AppsScopeTest, DISABLED_testConfirmUninstall) { result.set_title("foo"); result[click::apps::Query::ResultKeys::NAME] = "foo.name"; result[click::apps::Query::ResultKeys::VERSION] = "0.1"; auto activation = scope.perform_action(result, metadata, "widget", click::Preview::Actions::CONFIRM_UNINSTALL); auto response = activation->activate(); } TEST_F(AppsScopeTest, testCancelPurchaseInstalled) { auto activation = scope.perform_action(result, metadata, "button", click::Preview::Actions::CANCEL_PURCHASE_INSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CANCEL_PURCHASE_INSTALLED].get_bool()); } TEST_F(AppsScopeTest, testCancelPurchaseUninstalled) { auto activation = scope.perform_action(result, metadata, "button", click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED].get_bool()); } TEST_F(AppsScopeTest, testShowInstalled) { auto activation = scope.perform_action(result, metadata, "button", click::Preview::Actions::SHOW_INSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::SHOW_INSTALLED].get_bool()); } TEST_F(AppsScopeTest, testShowUninstalled) { auto activation = scope.perform_action(result, metadata, "button", click::Preview::Actions::SHOW_UNINSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::SHOW_UNINSTALLED].get_bool()); } TEST_F(AppsScopeTest, testConfirmCancelPurchaseUninstalled) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED].get_bool()); } TEST_F(AppsScopeTest, testConfirmCancelPurcahseInstalled) { auto activation = scope.perform_action(result, metadata, "widget_id", click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED); auto response = activation->activate(); EXPECT_TRUE(response.scope_data().get_dict()[click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED].get_bool()); } TEST_F(AppsScopeTest, testRatingNew) { auto activation = scope.perform_action(result, metadata, "rating", click::Preview::Actions::RATED); auto response = activation->activate(); EXPECT_EQ("rating", response.scope_data().get_dict()["widget_id"].get_string()); } TEST_F(AppsScopeTest, testRatingEdit) { auto activation = scope.perform_action(result, metadata, "93345", click::Preview::Actions::RATED); auto response = activation->activate(); EXPECT_EQ("93345", response.scope_data().get_dict()["widget_id"].get_string()); } ./scope/tests/click_interface_tool/0000755000015600001650000000000012676763577017544 5ustar jenkinsjenkins./scope/tests/click_interface_tool/CMakeLists.txt0000644000015600001650000000046012676763577022304 0ustar jenkinsjenkinsset(CLICK_INTERFACE_TOOL_TARGET click_interface_tool) include_directories ( ${CMAKE_SOURCE_DIR}/libclickscope ${CMAKE_SOURCE_DIR}/scope ) add_executable (${CLICK_INTERFACE_TOOL_TARGET} click_interface_tool.cpp ) target_link_libraries (${CLICK_INTERFACE_TOOL_TARGET} ${STORE_LIB_UNVERSIONED} ) ./scope/tests/click_interface_tool/click_interface_tool.cpp0000644000015600001650000000511312676763577024412 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface ci(keyFileLocator); QTimer timer; timer.setSingleShot(true); QObject::connect(&timer, &QTimer::timeout, [&]() { ci.get_dotdesktop_filename(std::string(argv[1]), [&a] (std::string val, click::InterfaceError error){ if (error == click::InterfaceError::NoError) { std::cout << " Success, got dotdesktop:" << val << std::endl; } else { std::cout << " Error:" << val << std::endl; } a.quit(); }); } ); timer.start(0); qInstallMessageHandler(0); return a.exec(); } ./scope/tests/test_query.cpp0000644000015600001650000006752012676763577016324 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include "clickstore/store-query.h" #include #include #include #include #include "click/qtbridge.h" #include "click/index.h" #include "clickstore/store-query.h" #include "click/application.h" #include "click/departments-db.h" #include "test_helpers.h" #include "click/package.h" #include #include #include #include #include #include #include #include using namespace ::testing; using namespace click; using namespace click::test::helpers; namespace { class MockQueryBase : public click::Query { public: MockQueryBase(const unity::scopes::CannedQuery& query, click::Index& index, click::DepartmentLookup& depts, std::shared_ptr depts_db, click::HighlightList& highlights, scopes::SearchMetadata const& metadata, pay::Package& in_package) : click::Query(query, index, depts, depts_db, highlights, metadata, in_package) { } void run_under_qt(const std::function &task) { // when testing, do not actually run under qt task(); } }; class MockQuery : public MockQueryBase { public: MockQuery(const unity::scopes::CannedQuery& query, click::Index& index, click::DepartmentLookup& depts, std::shared_ptr depts_db, click::HighlightList& highlights, scopes::SearchMetadata const& metadata, pay::Package& in_package) : MockQueryBase(query, index, depts, depts_db, highlights, metadata, in_package) { } void wrap_add_available_apps(const scopes::SearchReplyProxy &searchReply, const PackageSet &installedPackages, const std::string& categoryTemplate) { add_available_apps(searchReply, installedPackages, categoryTemplate); } MOCK_METHOD2(push_result, bool(scopes::SearchReplyProxy const&, scopes::CategorisedResult const&)); MOCK_METHOD0(clickInterfaceInstance, click::Interface&()); MOCK_METHOD1(finished, void(scopes::SearchReplyProxy const&)); MOCK_METHOD5(register_category, scopes::Category::SCPtr(const scopes::SearchReplyProxy &searchReply, const std::string &id, const std::string &title, const std::string &icon, const scopes::CategoryRenderer &renderer_template)); using click::Query::get_installed_packages; // allow tests to access protected method using click::Query::push_package; }; class MockQueryRun : public MockQueryBase { public: MockQueryRun(const unity::scopes::CannedQuery& query, click::Index& index, click::DepartmentLookup& depts, std::shared_ptr depts_db, click::HighlightList& highlights, scopes::SearchMetadata const& metadata, pay::Package& in_package) : MockQueryBase(query, index, depts, depts_db, highlights, metadata, in_package) { } MOCK_METHOD3(add_available_apps, void(scopes::SearchReplyProxy const&searchReply, const PackageSet &locallyInstalledApps, const std::string& categoryTemplate)); MOCK_METHOD3(push_local_results, void(scopes::SearchReplyProxy const &replyProxy, std::vector const &apps, std::string& categoryTemplate)); MOCK_METHOD0(get_installed_packages, PackageSet()); }; } // namespace TEST(QueryTest, testAddAvailableAppsCallsClickIndex) { MockIndex mock_index; click::DepartmentLookup dept_lookup; click::HighlightList highlights; scopes::SearchMetadata metadata("en_EN", "phone"); MockPayPackage pay_pkg; PackageSet no_installed_packages; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)).Times(1); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); } MATCHER_P(CategoryHasNumberOfResults, number, "") { return arg.find(std::to_string(number)) != std::string::npos; } TEST(QueryTest, testAddAvailableAppsPushesResults) { click::Packages packages { {"name", "title", 0.0, "icon", "uri"} }; MockIndex mock_index(packages); click::DepartmentLookup dept_lookup; click::HighlightList highlights; scopes::SearchMetadata metadata("en_EN", "phone"); MockPayPackage pay_pkg; PackageSet no_installed_packages; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); ON_CALL(q, register_category(_, _, _, _, _)).WillByDefault(Return(ptrCat)); EXPECT_CALL(q, register_category(_, "appstore", CategoryHasNumberOfResults(1), _, _)); EXPECT_CALL(q, register_category(_, "recommends", _, _, _)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); auto expected_title = packages.front().title; EXPECT_CALL(q, push_result(_, Property(&scopes::CategorisedResult::title, expected_title))); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); } TEST(QueryTest, testAddAvailableAppsCallsFinished) { click::Packages packages { {"name", "title", 0.0, "icon", "uri"} }; MockIndex mock_index(packages); click::DepartmentLookup dept_lookup; click::HighlightList highlights; scopes::SearchMetadata metadata("en_EN", "phone"); MockPayPackage pay_pkg; PackageSet no_installed_packages; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, push_result(_, _)); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); } TEST(QueryTest, testQueryRunCallsAddAvailableApps) { click::Packages packages { {"name", "title", 0.0, "icon", "uri"} }; MockIndex mock_index(packages); click::DepartmentLookup dept_lookup; click::HighlightList highlights; scopes::SearchMetadata metadata("en_EN", "phone"); MockPayPackage pay_pkg; PackageSet no_installed_packages; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQueryRun q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); auto reply = scopes::SearchReplyProxy(); EXPECT_CALL(q, get_installed_packages()).WillOnce(Return(no_installed_packages)); EXPECT_CALL(q, add_available_apps(reply, no_installed_packages, _)); // No need to test purchases in this testcase ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "0", 1), 0); q.run(reply); } MATCHER_P(HasPackageName, n, "") { return arg[click::Query::ResultKeys::NAME].get_string() == n; } MATCHER_P(IsInstalled, b, "") { return arg[click::Query::ResultKeys::INSTALLED].get_bool() == b; } TEST(QueryTest, testDuplicatesNotFilteredAnymore) { click::Packages packages { {"org.example.app1", "app title1", 0.0, "icon", "uri"}, {"org.example.app2", "app title2", 0.0, "icon", "uri"} }; MockIndex mock_index(packages); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet one_installed_package { {"org.example.app2", "0.2"} }; click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); auto expected_name1 = packages.front().name; EXPECT_CALL(q, push_result(_, HasPackageName(expected_name1))); auto expected_name2 = packages.back().name; EXPECT_CALL(q, push_result(_, HasPackageName(expected_name2))); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE); } TEST(QueryTest, testInstalledPackagesFlaggedAsSuch) { click::Packages packages { {"org.example.app1", "app title1", 0.0, "icon", "uri"}, {"org.example.app2", "app title2", 0.0, "icon", "uri"} }; MockIndex mock_index(packages); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet one_installed_package { {"org.example.app2", "0.2"} }; click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, push_result(_, IsInstalled(true))); EXPECT_CALL(q, push_result(_, IsInstalled(false))); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE); } TEST(QueryTest, testDepartmentsDbIsUpdated) { auto dept1 = std::make_shared("1", "Department one", "http://one.com", true); dept1->set_subdepartments({ std::make_shared("1-1", "Department two", "http://two.com", false), std::make_shared("1-2", "Department three", "http://three.com", false) }); DepartmentList init_departments({dept1}); auto depts_db = std::make_shared(":memory:", true); EXPECT_CALL(*depts_db, store_department_name(_, _, _)).Times(3); EXPECT_CALL(*depts_db, store_department_mapping("1", "")); EXPECT_CALL(*depts_db, store_department_mapping("1-1", "1")); EXPECT_CALL(*depts_db, store_department_mapping("1-2", "1")); MockIndex mock_index(click::Packages(), click::DepartmentList(), init_departments); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet one_installed_package { {"org.example.app2", "0.2"} }; click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, depts_db, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE); } TEST(QueryTest, testSearchInDepartment) { auto dept1 = std::make_shared("1", "Department one", "http://one.com", true); DepartmentList init_departments({dept1}); auto depts_db = std::make_shared(":memory:", true); MockIndex mock_index(click::Packages(), click::DepartmentList(), init_departments); scopes::SearchMetadata metadata("en_EN", "phone"); click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "1"); MockQuery q(query, mock_index, dept_lookup, depts_db, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, "1", _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); std::list expected_departments({"", "1"}); scopes::testing::MockSearchReply mock_reply; EXPECT_CALL(mock_reply, register_departments(MatchesDepartments(expected_departments))); scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, PackageSet(), FAKE_CATEGORY_TEMPLATE); } class FakeInterface : public click::Interface { public: MOCK_METHOD1(get_installed_packages, void(std::function callback)); }; TEST(QueryTest, testGetInstalledPackages) { click::Packages uninstalled_packages { {"name", "title", 0.0, "icon", "uri"} }; MockIndex mock_index(uninstalled_packages); scopes::SearchMetadata metadata("en_EN", "phone"); click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); PackageSet installed_packages{{"package_1", "0.1"}}; FakeInterface fake_interface; EXPECT_CALL(q, clickInterfaceInstance()).WillOnce(ReturnRef(fake_interface)); EXPECT_CALL(fake_interface, get_installed_packages(_)).WillOnce(Invoke( [&](std::function callback){ callback(installed_packages, click::InterfaceError::NoError); })); ASSERT_EQ(q.get_installed_packages(), installed_packages); } typedef std::pair _PurchasedValues; MATCHER_P(PurchasedProperties, b, "") { return arg[click::Query::ResultKeys::PURCHASED].get_bool() == b.first && arg[click::Query::ResultKeys::INSTALLED].get_bool() == b.second; } TEST(QueryTest, testQueryRunPurchased) { ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); click::Packages packages { {"name", "title", 0.99, "icon", "uri"} }; MockIndex mock_index(packages); click::DepartmentLookup dept_lookup; click::HighlightList highlights; scopes::SearchMetadata metadata("en_EN", "phone"); MockPayPackage pay_pkg; PackageSet no_installed_packages; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); q.purchased_apps.insert({"name"}); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); ON_CALL(q, register_category(_, _, _, _, _)).WillByDefault(Return(ptrCat)); EXPECT_CALL(q, register_category(_, "appstore", _, _, _)); EXPECT_CALL(q, register_category(_, "recommends", _, _, _)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, push_result(_, PurchasedProperties(_PurchasedValues{true, false}))).Times(1); EXPECT_CALL(q, finished(_)); q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); } TEST(QueryTest, testQueryRunPurchasedAndInstalled) { ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); click::Packages packages { {"name", "title", 0.99, "icon", "uri"} }; PackageSet one_installed_package { {"name", "0.2"} }; MockIndex mock_index(packages); click::DepartmentLookup dept_lookup; click::HighlightList highlights; scopes::SearchMetadata metadata("en_EN", "phone"); MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); q.purchased_apps.insert({"name"}); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); ON_CALL(q, register_category(_, _, _, _, _)).WillByDefault(Return(ptrCat)); EXPECT_CALL(q, register_category(_, "appstore", _, _, _)); EXPECT_CALL(q, register_category(_, "recommends", _, _, _)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, push_result(_, PurchasedProperties(_PurchasedValues{true, true}))).Times(1); EXPECT_CALL(q, finished(_)); q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE); ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); } TEST(QueryTest, testPushPackageSkipsPricedApps) { ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "0", 1), 0); click::Packages packages { {"org.example.app1", "app title1", 1.99, "icon", "uri"}, {"org.example.app2", "app title2", 0.0, "icon", "uri"} }; MockIndex mock_index(packages); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet no_installed_packages; click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); auto expected_name2 = packages.back().name; EXPECT_CALL(q, push_result(_, HasPackageName(expected_name2))); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); } TEST(QueryTest, testPushPackagePushesPricedApps) { ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); click::Packages packages { {"org.example.app1", "app title1", 1.99, "icon", "uri"}, {"org.example.app2", "app title2", 0.0, "icon", "uri"} }; MockIndex mock_index(packages); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet no_installed_packages; click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); auto expected_name1 = packages.front().name; EXPECT_CALL(q, push_result(_, HasPackageName(expected_name1))); auto expected_name2 = packages.back().name; EXPECT_CALL(q, push_result(_, HasPackageName(expected_name2))); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); } MATCHER_P(HasAttributes, b, "") { return (!arg["attributes"].is_null()) == b; } TEST(QueryTest, testPushPackagePushesAttributes) { click::Packages packages { {"org.example.app1", "app title1", 0.0, "icon", "uri"}, }; MockIndex mock_index(packages); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet no_installed_packages; click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, push_result(_, HasAttributes(true))); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); } MATCHER_P(HasPrice, price, "") { return arg["price"].get_double() == price; } TEST(QueryTest, testPushPackagePushesPriceUSD) { ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); Json::Value root; Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); auto const embedded = root[Package::JsonKeys::embedded]; auto const ci_package = embedded[Package::JsonKeys::ci_package]; Packages packages = package_list_from_json_node(ci_package); MockIndex mock_index(packages); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet no_installed_packages; DepartmentLookup dept_lookup; HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", "", ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, push_result(reply, HasPrice(1.99))); q.push_package(reply, ptrCat, no_installed_packages, packages[0]); ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); } TEST(QueryTest, testPushPackagePushesPriceEUR) { ASSERT_EQ(setenv(Configuration::CURRENCY_ENVVAR, "EUR", 1), 0); ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); Json::Value root; Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); auto const embedded = root[Package::JsonKeys::embedded]; auto const ci_package = embedded[Package::JsonKeys::ci_package]; Packages packages = package_list_from_json_node(ci_package); MockIndex mock_index(packages); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet no_installed_packages; DepartmentLookup dept_lookup; HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", "", ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, push_result(reply, HasPrice(1.69))); q.push_package(reply, ptrCat, no_installed_packages, packages[0]); ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); } MATCHER_P(HasVersion, v, "") { return arg[click::Query::ResultKeys::VERSION].get_string() == v; } TEST(QueryTest, testPushPackagePushesVersion) { auto const fake_version = "0.83b"; click::Packages packages { {"org.example.app1", "app title1", 0.0, "icon", "uri", fake_version, "scope"}, }; MockIndex mock_index(packages); scopes::SearchMetadata metadata("en_EN", "phone"); PackageSet no_installed_packages; click::DepartmentLookup dept_lookup; click::HighlightList highlights; MockPayPackage pay_pkg; const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _, _)); scopes::CategoryRenderer renderer("{}"); auto ptrCat = std::make_shared("id", "", "", renderer); EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); scopes::testing::MockSearchReply mock_reply; scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); EXPECT_CALL(q, push_result(_, HasVersion(fake_version))); EXPECT_CALL(q, finished(_)).Times(1); q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); } ./scope/tests/test_helpers.h0000644000015600001650000000722012676763577016255 0ustar jenkinsjenkins#ifndef TEST_HELPERS_H #define TEST_HELPERS_H #include #include #include "click/index.h" #include #include #include namespace click { namespace test { namespace helpers { static const std::string FAKE_QUERY {"FAKE_QUERY"}; static const std::string FAKE_CATEGORY_TEMPLATE {"{}"}; // this matcher expects a list of department ids in depts: // first on the list is the root, followed by children ids. // the arg of the matcher is unity::scopes::Department ptr. MATCHER_P(MatchesDepartments, depts, "") { auto it = depts.begin(); if (arg->id() != *it) return false; auto const subdeps = arg->subdepartments(); if (subdeps.size() != depts.size() - 1) return false; for (auto const& sub: subdeps) { if (sub->id() != *(++it)) return false; } return true; } class MockIndex : public click::Index { click::Packages packages; click::Packages recommends; click::DepartmentList departments; click::DepartmentList bootstrap_departments; click::HighlightList bootstrap_highlights; public: MockIndex(click::Packages packages = click::Packages(), click::DepartmentList departments = click::DepartmentList(), click::DepartmentList boot_departments = click::DepartmentList()) : Index(QSharedPointer()), packages(packages), departments(departments), bootstrap_departments(boot_departments) { } click::web::Cancellable search(const std::string &query, const std::string &department, std::function callback) override { do_search(query, department, callback); callback(packages, recommends); return click::web::Cancellable(); } click::web::Cancellable bootstrap(std::function callback) override { callback(bootstrap_departments, bootstrap_highlights, click::Index::Error::NoError, 0); return click::web::Cancellable(); } MOCK_METHOD3(do_search, void(const std::string&, const std::string&, std::function)); }; class MockDepartmentsDb : public click::DepartmentsDb { public: MockDepartmentsDb(const std::string& name, bool create) : click::DepartmentsDb(name, create) { } MOCK_METHOD2(get_department_name, std::string(const std::string&, const std::list&)); MOCK_METHOD2(get_packages_for_department, std::unordered_set(const std::string&, bool)); MOCK_METHOD1(get_parent_department_id, std::string(const std::string&)); MOCK_METHOD1(get_children_departments, std::list(const std::string&)); MOCK_METHOD1(is_empty, bool(const std::string&)); MOCK_METHOD2(is_descendant_of_department, bool(const std::string&, const std::string&)); MOCK_METHOD2(store_package_mapping, void(const std::string&, const std::string&)); MOCK_METHOD2(store_department_mapping, void(const std::string&, const std::string&)); MOCK_METHOD3(store_department_name, void(const std::string&, const std::string&, const std::string&)); }; class FakeCategory : public scopes::Category { public: FakeCategory(std::string const& id, std::string const& title, std::string const& icon, scopes::CategoryRenderer const& renderer) : scopes::Category(id, title, icon, renderer) { } }; } // namespace helpers } // namespace test } // namespace click #endif // TEST_HELPERS_H ./scope/tests/CMakeLists.txt0000644000015600001650000000656012676763577016151 0ustar jenkinsjenkinsset (CLICKSCOPE_TESTS_TARGET click-scope-tests) set (APPS_SCOPE_TESTS_TARGET apps-scope-tests) find_package(Threads) # Qt5 bits SET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package(Qt5Core REQUIRED) find_package(Qt5Sql REQUIRED) include_directories ( ${CMAKE_SOURCE_DIR}/libclickscope ${CMAKE_SOURCE_DIR}/scope ${JSON_CPP_INCLUDE_DIRS} ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR} ) add_executable (${CLICKSCOPE_TESTS_TARGET} test_query.cpp test_store_scope.cpp ${CMAKE_SOURCE_DIR}/libclickscope/tests/fake_json.cpp ) add_executable (${APPS_SCOPE_TESTS_TARGET} test_apps_query.cpp test_apps_scope.cpp ) qt5_use_modules(${CLICKSCOPE_TESTS_TARGET} Core DBus Network Test Sql) qt5_use_modules(${APPS_SCOPE_TESTS_TARGET} Core DBus Network Test Sql) target_link_libraries(${CLICKSCOPE_TESTS_TARGET} ${STORE_LIB_UNVERSIONED} ${SCOPE_LIB_NAME} ${UNITY_SCOPES_LDFLAGS} ${UBUNTUONE_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS} ${JSON_CPP_LDFLAGS} gmock gmock_main ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(${APPS_SCOPE_TESTS_TARGET} ${APPS_LIB_UNVERSIONED} ${SCOPE_LIB_NAME} ${UNITY_SCOPES_LDFLAGS} ${UBUNTUONE_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS} ${JSON_CPP_LDFLAGS} gmock gmock_main ${CMAKE_THREAD_LIBS_INIT} ) add_custom_target (test-click-scope COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${CLICKSCOPE_TESTS_TARGET} DEPENDS ${CLICKSCOPE_TESTS_TARGET} ) add_custom_target(test-click-scope-valgrind COMMAND valgrind --tool=memcheck ${CMAKE_CURRENT_BINARY_DIR}/${CLICKSCOPE_TESTS_TARGET} DEPENDS ${CLICKSCOPE_TESTS_TARGET} ) add_custom_target(test-click-scope-leaks COMMAND valgrind --tool=memcheck --track-origins=yes --num-callers=40 --leak-resolution=high --leak-check=full ${CMAKE_CURRENT_BINARY_DIR}/${CLICKSCOPE_TESTS_TARGET} DEPENDS ${CLICKSCOPE_TESTS_TARGET} ) add_custom_target (test-click-scope-disabled COMMAND GTEST_ALSO_RUN_DISABLED_TESTS=1 ${CMAKE_CURRENT_BINARY_DIR}/${CLICKSCOPE_TESTS_TARGET} DEPENDS ${CLICKSCOPE_TESTS_TARGET} ) # --- set(TEST_SCHEMA_DIR ${CMAKE_CURRENT_BINARY_DIR}) add_definitions(-DTEST_SCHEMA_DIR="${TEST_SCHEMA_DIR}") execute_process ( COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE OUTPUT_STRIP_TRAILING_WHITESPACE ) add_custom_target (gschemas.compiled COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${TEST_SCHEMA_DIR} COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${TEST_SCHEMA_DIR} ) add_custom_target (test-apps-scope COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${APPS_SCOPE_TESTS_TARGET} DEPENDS ${APPS_SCOPE_TESTS_TARGET} gschemas.compiled ) add_custom_target(test-apps-scope-valgrind COMMAND valgrind --tool=memcheck ${CMAKE_CURRENT_BINARY_DIR}/${APPS_SCOPE_TESTS_TARGET} DEPENDS ${APPS_SCOPE_TESTS_TARGET} ) add_custom_target(test-apps-scope-leaks COMMAND valgrind --tool=memcheck --track-origins=yes --num-callers=40 --leak-resolution=high --leak-check=full ${CMAKE_CURRENT_BINARY_DIR}/${APPS_SCOPE_TESTS_TARGET} DEPENDS ${APPS_SCOPE_TESTS_TARGET} ) add_custom_target (test-apps-scope-disabled COMMAND GTEST_ALSO_RUN_DISABLED_TESTS=1 ${CMAKE_CURRENT_BINARY_DIR}/${APPS_SCOPE_TESTS_TARGET} DEPENDS ${APPS_SCOPE_TESTS_TARGET} ) add_subdirectory(click_interface_tool) add_subdirectory(fake_launcher) ./scope/clickapps/0000755000015600001650000000000012676763604014200 5ustar jenkinsjenkins./scope/clickapps/clickscope.ini.in.in0000644000015600001650000000037112676763577020044 0ustar jenkinsjenkins[ScopeConfig] _DisplayName=Apps _Description=Scope for searching installed apps Author=Canonical Ltd. Art=@APPS_DATA_DIR@/clickscope-screenshot.jpg Icon=@APPS_DATA_DIR@/apps-scope.png _SearchHint=Search apps [Appearance] LogoOverlayColor=#ffffffff ./scope/clickapps/apps-scope.h0000644000015600001650000000553312676763604016431 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef APPS_SCOPE_H #define APPS_SCOPE_H #include #include #include #include #include #include #include #include namespace scopes = unity::scopes; namespace click { class DepartmentsDb; class Scope : public scopes::ScopeBase { public: Scope(); ~Scope(); virtual void start(std::string const&) override; virtual void run() override; virtual void stop() override; virtual scopes::SearchQueryBase::UPtr search(scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata) override; unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result&, const unity::scopes::ActionMetadata& hints) override; virtual unity::scopes::ActivationQueryBase::UPtr perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& widget_id, std::string const& action_id) override; private: QSharedPointer nam; QSharedPointer client; QSharedPointer index; QSharedPointer pay_package; QSharedPointer dm; std::shared_ptr depts_db; std::string installApplication(unity::scopes::Result const& result); }; } #endif // CLICK_SCOPE_H ./scope/clickapps/apps-query.cpp0000644000015600001650000003526712676763577017040 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "apps-query.h" #include namespace { static const std::string CATEGORY_APPS_DISPLAY = R"( { "schema-version" : 1, "template" : { "category-layout" : "grid", "collapsed-rows": 0, "card-size": "small" }, "components" : { "title" : "title", "art" : { "field": "art", "aspect-ratio": 1.6, "fill-mode": "fit", "fallback": "image://theme/placeholder-app-icon" } } } )"; static const char CATEGORY_STORE[] = R"( { "template": { "category-layout": "grid", "overlay": true, "card-size": "small", "card-background": "color:///#DD4814" }, "components": { "title": "title", "art": { "aspect-ratio": 0.55, "field": "art" }, "overlay-color": "overlay-color" } } )"; } click::apps::ResultPusher::ResultPusher(const scopes::SearchReplyProxy &replyProxy, const std::vector& apps) : replyProxy(replyProxy) { for (auto const& app: apps) { // // click entries in the dconf key are expected to be in the format of // "foo.bar.package_appname"; split on underscore and just use the first part // for now (second part should be honored when we support multiple apps per package). auto i = app.find("_"); if (i != std::string::npos) { const std::string pkg = app.substr(0, i); core_apps.push_back(pkg); top_apps_lookup.insert(pkg); } else { core_apps.push_back(app); top_apps_lookup.insert(app); } } } void click::apps::ResultPusher::push_result(scopes::Category::SCPtr& cat, const click::Application& a) { scopes::CategorisedResult res(cat); res.set_title(a.title); res.set_art(a.icon_url); res.set_uri(a.url); res[click::apps::Query::ResultKeys::NAME] = a.name; res[click::apps::Query::ResultKeys::DESCRIPTION] = a.description; res[click::apps::Query::ResultKeys::MAIN_SCREENSHOT] = a.main_screenshot; res[click::apps::Query::ResultKeys::INSTALLED] = true; res[click::apps::Query::ResultKeys::VERSION] = a.version; replyProxy->push(res); } // // Return an application identifier used to match applications against core-apps dconf key; // For click apps, it just returns application name (e.g. com.canonical.calculator). // For non-click apps, it return the desktop file name (without extension), taken from app uri. std::string click::apps::ResultPusher::get_app_identifier(const click::Application& app) { static const std::string app_prefix("application:///"); if (!app.name.empty()) { return app.name; } if (app.url.size() > app_prefix.size()) { auto i = app.url.rfind('.'); if (i != std::string::npos) { return app.url.substr(app_prefix.size(), i - app_prefix.size()); } } throw std::runtime_error("Cannot determine application identifier for" + app.url); } void click::apps::ResultPusher::push_local_results( const std::vector &apps, const std::string &categoryTemplate, bool show_title) { const scopes::CategoryRenderer rdr(categoryTemplate); auto cat = replyProxy->register_category("local", show_title ? _("Apps") : "", "", rdr); for(const auto & a: apps) { try { if (top_apps_lookup.size() == 0 || top_apps_lookup.find(get_app_identifier(a)) == top_apps_lookup.end()) { push_result(cat, a); } } catch (const std::runtime_error &e) { qWarning() << QString::fromStdString(e.what()); } } } void click::apps::ResultPusher::push_top_results( const std::vector& apps, const std::string& categoryTemplate) { const scopes::CategoryRenderer rdr(categoryTemplate); auto cat = replyProxy->register_category("predefined", "", "", rdr); // // iterate over all apps, insert those matching core apps into top_apps_to_push std::map top_apps_to_push; for (const auto& a: apps) { try { const auto id = get_app_identifier(a); if (top_apps_lookup.find(id) != top_apps_lookup.end()) { top_apps_to_push[id] = a; if (core_apps.size() == top_apps_to_push.size()) { // no need to iterate over remaining apps break; } } } catch (const std::runtime_error &e) { qWarning() << QString::fromStdString(e.what()); } } // // iterate over core apps and insert them based on top_apps_to_push; // this way the order of core apps is preserved. for (const auto &a: core_apps) { auto const it = top_apps_to_push.find(a); if (it != top_apps_to_push.end()) { push_result(cat, it->second); } } } struct click::apps::Query::Private { Private(std::shared_ptr depts_db, const scopes::SearchMetadata& metadata) : depts_db(depts_db), meta(metadata) { } std::shared_ptr depts_db; scopes::SearchMetadata meta; click::Configuration configuration; }; click::apps::Query::Query(unity::scopes::CannedQuery const& query, std::shared_ptr depts_db, scopes::SearchMetadata const& metadata) : unity::scopes::SearchQueryBase(query, metadata), impl(new Private(depts_db, metadata)) { } void click::apps::Query::cancelled() { qDebug() << "cancelling search of" << QString::fromStdString(query().query_string()); } click::apps::Query::~Query() { qDebug() << "destroying search"; } click::Interface& click::apps::Query::clickInterfaceInstance() { static QSharedPointer keyFileLocator(new click::KeyFileLocator()); static click::Interface iface(keyFileLocator); return iface; } void click::apps::Query::add_fake_store_app(scopes::SearchReplyProxy const& searchReply) { static const std::string title = _("Ubuntu Store"); std::string cat_title = _("Get more apps from the store"); const std::string querystr = query().query_string(); if (!querystr.empty()) { char tmp[512]; if (snprintf(tmp, sizeof(tmp), _("Search for '%s' in the store"), querystr.c_str()) > 0) { cat_title = tmp; } } else if (!query().department_id().empty()) { cat_title = _("Get more apps like this from the Store"); } scopes::CategoryRenderer rdr(CATEGORY_STORE); auto cat = searchReply->register_category("store", cat_title, "", rdr); const unity::scopes::CannedQuery store_scope("com.canonical.scopes.clickstore", querystr, querystr.empty() ? query().department_id() : ""); scopes::CategorisedResult res(cat); res.set_title(title); res.set_art(STORE_DATA_DIR "/store-scope-icon.svg"); res.set_uri(store_scope.to_uri()); res[click::apps::Query::ResultKeys::NAME] = title; res[click::apps::Query::ResultKeys::DESCRIPTION] = ""; res[click::apps::Query::ResultKeys::MAIN_SCREENSHOT] = ""; res[click::apps::Query::ResultKeys::INSTALLED] = true; res[click::apps::Query::ResultKeys::VERSION] = ""; res["overlay-color"] = "transparent"; searchReply->push(res); } void click::apps::Query::push_local_departments(scopes::SearchReplyProxy const& replyProxy, const std::vector& apps) { auto const current_dep_id = query().department_id(); const std::list locales = { search_metadata().locale(), "en_US" }; // // create helper lookup of all departments of currently installed apps. // note that apps that are passed here are supposed to be already filterd by current department // that means we only have subdepartments of current department (or subdepartment(s) of subdepartment(s) // and so on of current department, as the hierarchy may be of arbitrary depth. std::unordered_set all_subdepartments; for (auto const app: apps) { all_subdepartments.insert(app.real_department); } unity::scopes::Department::SPtr root; try { static const std::string all_dept_name = _("All"); // create node for current department auto name = current_dep_id == "" ? all_dept_name : impl->depts_db->get_department_name(current_dep_id, locales); unity::scopes::Department::SPtr current = unity::scopes::Department::create(current_dep_id, query(), name); unity::scopes::DepartmentList children; // attach subdepartments to it for (auto const& subdep: impl->depts_db->get_children_departments(current_dep_id)) { // // check if this subdepartment either directly matches a subdepartment of installed app, // or is any of the departments of installed apps is a descendant of current subdepartment. // the latter means we have app somewhere in the subtree of the current subdepartment, so it // needs to be shown. bool show_subdepartment = false; auto it = all_subdepartments.find(subdep.id); if (it != all_subdepartments.end()) { // subdepartment id matches directly one of the ids from all_subdepartments show_subdepartment = true; all_subdepartments.erase(it); } else { // no direct match - we need to check descendants of this subdepartment // by querying the db for (auto it = all_subdepartments.begin(); it != all_subdepartments.end(); it++) { if (impl->depts_db->is_descendant_of_department(*it, subdep.id)) { show_subdepartment = true; all_subdepartments.erase(it); break; } } } if (show_subdepartment) { // if single supdepartment fails, then ignore it and continue with others try { name = impl->depts_db->get_department_name(subdep.id, locales); unity::scopes::Department::SPtr dep = unity::scopes::Department::create(subdep.id, query(), name); dep->set_has_subdepartments(subdep.has_children); children.push_back(dep); } catch (const std::exception &e) { qWarning() << "Failed to create subdeparment:" << QString::fromStdString(e.what()); } } } if (children.size() > 0) { const std::locale loc(""); children.sort([&loc](const unity::scopes::Department::SCPtr &d1, const unity::scopes::Department::SCPtr &d2) -> bool { return loc(d1->label(), d2->label()) > 0; }); current->set_subdepartments(children); } // if current is not the top, then gets its parent if (current_dep_id != "") { auto const parent_dep_id = impl->depts_db->get_parent_department_id(current_dep_id); root = unity::scopes::Department::create(parent_dep_id, query(), parent_dep_id == "" ? all_dept_name : impl->depts_db->get_department_name(parent_dep_id, locales)); root->add_subdepartment(current); } else { root = current; } replyProxy->register_departments(root); } catch (const std::exception& e) { qWarning() << "Failed to push departments: " << QString::fromStdString(e.what()); } } void click::apps::Query::run(scopes::SearchReplyProxy const& searchReply) { const std::string categoryTemplate = CATEGORY_APPS_DISPLAY; auto const current_dept = query().department_id(); auto const querystr = query().query_string(); const bool show_top_apps = querystr.empty() && current_dept.empty(); ResultPusher pusher(searchReply, show_top_apps ? impl->configuration.get_core_apps() : std::vector()); auto const ignoredApps = impl->configuration.get_ignored_apps(); auto const localResults = clickInterfaceInstance().find_installed_apps(querystr, ignoredApps, current_dept, impl->depts_db); if (impl->depts_db) { push_local_departments(searchReply, localResults); } if (show_top_apps) { pusher.push_top_results(localResults, categoryTemplate); } const bool show_cat_title = current_dept.empty() && querystr.empty(); pusher.push_local_results( localResults, categoryTemplate, show_cat_title); add_fake_store_app(searchReply); } ./scope/clickapps/apps-scope.cpp0000644000015600001650000001642312676763604016764 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "apps-scope.h" #include "apps-query.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace click; click::Scope::Scope() { nam.reset(new click::network::AccessManager()); client.reset(new click::web::Client(nam)); index.reset(new click::Index(client)); pay_package.reset(new pay::Package(client)); try { depts_db = click::DepartmentsDb::open(false); } catch (const std::runtime_error& e) { std::cerr << "Failed to open departments db: " << e.what() << std::endl; } } click::Scope::~Scope() { } void click::Scope::start(std::string const&) { setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, GETTEXT_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); click::Date::setup_system_locale(); } void click::Scope::run() { static const int zero = 0; auto emptyCb = [this]() { dm.reset(Ubuntu::DownloadManager::Manager::createSessionManager()); }; qt::core::world::build_and_run(zero, nullptr, emptyCb); } void click::Scope::stop() { qt::core::world::destroy(); } scopes::SearchQueryBase::UPtr click::Scope::search(unity::scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata) { return scopes::SearchQueryBase::UPtr(new click::apps::Query(q, depts_db, metadata)); } unity::scopes::PreviewQueryBase::UPtr click::Scope::preview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata) { qDebug() << "Scope::preview() called."; auto preview = new click::Preview(result, metadata); preview->choose_strategy(client, pay_package, dm, depts_db); return unity::scopes::PreviewQueryBase::UPtr{preview}; } unity::scopes::ActivationQueryBase::UPtr click::Scope::perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& widget_id, std::string const& action_id) { if (action_id == click::Preview::Actions::CONFIRM_UNINSTALL) { auto response = unity::scopes::ActivationResponse(unity::scopes::ActivationResponse::ShowDash); return scopes::ActivationQueryBase::UPtr(new PerformUninstallAction(result, metadata, response)); } auto activation = new ScopeActivation(result, metadata); qDebug() << "perform_action called with action_id" << QString().fromStdString(action_id); if (action_id == click::Preview::Actions::UNINSTALL_CLICK) { activation->setHint(click::Preview::Actions::UNINSTALL_CLICK, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CANCEL_PURCHASE_INSTALLED) { activation->setHint(click::Preview::Actions::CANCEL_PURCHASE_INSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED) { activation->setHint(click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::SHOW_INSTALLED) { activation->setHint(click::Preview::Actions::SHOW_INSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::SHOW_UNINSTALLED) { activation->setHint(click::Preview::Actions::SHOW_UNINSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED) { activation->setHint(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED) { activation->setHint(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::RATED) { scopes::VariantMap rating_info = metadata.scope_data().get_dict(); // Cast to int because widget gives us double, which is wrong. int rating = ((int)rating_info["rating"].get_double()); std::string review_text = rating_info["review"].get_string(); // We have to get the values and then set them as hints here, to be // able to pass them on to the Preview, which actually makes the // call to submit. activation->setHint("rating", scopes::Variant(rating)); activation->setHint("review", scopes::Variant(review_text)); activation->setHint(click::Preview::Actions::RATED, scopes::Variant(true)); activation->setHint("widget_id", scopes::Variant(widget_id)); activation->setStatus(scopes::ActivationResponse::Status::ShowPreview); } return scopes::ActivationQueryBase::UPtr(activation); } #define EXPORT __attribute__ ((visibility ("default"))) extern "C" { EXPORT unity::scopes::ScopeBase* // cppcheck-suppress unusedFunction UNITY_SCOPE_CREATE_FUNCTION() { return new click::Scope(); } EXPORT void // cppcheck-suppress unusedFunction UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base) { delete scope_base; } } ./scope/clickapps/CMakeLists.txt0000644000015600001650000000253712676763577016760 0ustar jenkinsjenkinsSET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package (Qt5Core REQUIRED) find_package (Qt5Sql REQUIRED) pkg_check_modules(JSON_CPP REQUIRED jsoncpp) add_definitions( -DGETTEXT_PACKAGE=\"${PROJECT_NAME}\" -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}\" -DSTORE_DATA_DIR="${STORE_DATA_DIR}" ) add_library(${APPS_LIB_UNVERSIONED} SHARED apps-query.cpp apps-scope.cpp ) set_target_properties(${APPS_LIB_UNVERSIONED} PROPERTIES PREFIX "") include_directories( ${CMAKE_SOURCE_DIR}/libclickscope ${JSON_CPP_INCLUDE_DIRS} ) qt5_use_modules (${APPS_LIB_UNVERSIONED} Network Sql) target_link_libraries (${APPS_LIB_UNVERSIONED} ${SCOPE_LIB_NAME} ${JSON_CPP_LDFLAGS} ${UNITY_SCOPES_LDFLAGS} ${UBUNTUONE_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS} ) install( TARGETS ${APPS_LIB_UNVERSIONED} LIBRARY DESTINATION "${APPS_LIB_DIR}" ) set(APPS_INI_TARGET clickscope.ini) configure_file( ${APPS_INI_TARGET}.in.in ${APPS_INI_TARGET}.in ) add_custom_target(${APPS_INI_TARGET} ALL COMMENT "Merging translations into ${APPS_INI_TARGET}" COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${APPS_INI_TARGET}.in ${APPS_INI_TARGET} >/dev/null ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${APPS_INI_TARGET}" DESTINATION "${APPS_LIB_DIR}" ) ./scope/clickapps/apps-query.h0000644000015600001650000000711512676763577016474 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef APPS_QUERY_H #define APPS_QUERY_H #include namespace scopes = unity::scopes; #include #include #include #include namespace click { class Application; class Configuration; class DepartmentsDb; namespace apps { class Query : public scopes::SearchQueryBase { public: struct ResultKeys { ResultKeys() = delete; constexpr static const char* NAME{"name"}; constexpr static const char* DESCRIPTION{"description"}; constexpr static const char* MAIN_SCREENSHOT{"main_screenshot"}; constexpr static const char* INSTALLED{"installed"}; constexpr static const char* VERSION{"version"}; }; Query(unity::scopes::CannedQuery const& query, std::shared_ptr depts_db, scopes::SearchMetadata const& metadata); virtual ~Query(); virtual void cancelled() override; virtual void run(scopes::SearchReplyProxy const& reply) override; virtual void add_fake_store_app(scopes::SearchReplyProxy const &replyProxy); virtual void push_local_departments(scopes::SearchReplyProxy const& replyProxy, const std::vector& apps); protected: virtual click::Interface& clickInterfaceInstance(); private: struct Private; QSharedPointer impl; }; class ResultPusher { const scopes::SearchReplyProxy &replyProxy; std::vector core_apps; std::unordered_set top_apps_lookup; public: ResultPusher(const scopes::SearchReplyProxy &replyProxy, const std::vector& core_apps); virtual ~ResultPusher() = default; virtual void push_local_results(const std::vector &apps, const std::string& categoryTemplate, bool show_title); virtual void push_top_results( const std::vector& apps, const std::string& categoryTemplate); protected: virtual void push_result(scopes::Category::SCPtr& cat, const click::Application& a); static std::string get_app_identifier(const click::Application& app); }; } // namespace apps } // namespace query #endif // CLICK_QUERY_H ./scope/CMakeLists.txt0000644000015600001650000000012112676763577014772 0ustar jenkinsjenkinsadd_subdirectory(clickstore) add_subdirectory(clickapps) add_subdirectory(tests) ./scope/clickstore/0000755000015600001650000000000012676763604014371 5ustar jenkinsjenkins./scope/clickstore/store-query.h0000644000015600001650000001155712676763577017063 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef STORE_QUERY_H #define STORE_QUERY_H #include #include namespace scopes = unity::scopes; #include #include #include #include #include #include #include namespace click { class Application; class Index; class DepartmentLookup; class DepartmentsDb; class Query : public scopes::SearchQueryBase { public: struct ResultKeys { ResultKeys() = delete; constexpr static const char* NAME{"name"}; constexpr static const char* DESCRIPTION{"description"}; constexpr static const char* MAIN_SCREENSHOT{"main_screenshot"}; constexpr static const char* INSTALLED{"installed"}; constexpr static const char* PURCHASED{"purchased"}; constexpr static const char* REFUNDABLE_UNTIL{"refundable_until"}; constexpr static const char* VERSION{"version"}; }; Query(unity::scopes::CannedQuery const& query, click::Index& index, click::DepartmentLookup& dept_lookup, std::shared_ptr depts_db, click::HighlightList& highlights, scopes::SearchMetadata const& metadata, pay::Package& in_package); virtual ~Query(); virtual void cancelled() override; virtual void run(scopes::SearchReplyProxy const& reply) override; pay::PurchaseSet purchased_apps; protected: virtual unity::scopes::Department::SPtr fromClickDepartment(const click::Department::SCPtr click_dept, const std::string& current_dept_id, const click::DepartmentList& subdepts); virtual unity::scopes::Department::SPtr populate_departments(const click::DepartmentList& depts, const std::string& current_department_id); virtual void store_departments(const click::DepartmentList& depts); virtual void push_departments(const scopes::SearchReplyProxy& searchReply, const scopes::Department::SCPtr& root); virtual void push_departments(scopes::SearchReplyProxy const& searchReply); virtual void add_highlights(scopes::SearchReplyProxy const& searchReply, const PackageSet& installedPackages); virtual void add_available_apps(const scopes::SearchReplyProxy &searchReply, const PackageSet &installedPackages, const std::string &category); virtual click::Interface& clickInterfaceInstance(); virtual PackageSet get_installed_packages(); virtual bool push_result(const scopes::SearchReplyProxy &searchReply, scopes::CategorisedResult const& res); virtual void finished(const scopes::SearchReplyProxy &searchReply); virtual scopes::Category::SCPtr register_category(scopes::SearchReplyProxy const& searchReply, std::string const& id, std::string const& title, std::string const& icon, scopes::CategoryRenderer const& renderer_template); virtual void push_package(const scopes::SearchReplyProxy& searchReply, scopes::Category::SCPtr category, const PackageSet &locallyInstalledApps, const click::Package& pkg); virtual void push_highlights(const scopes::SearchReplyProxy& searchReply, const HighlightList& highlights, const PackageSet &locallyInstalledApps); virtual void run_under_qt(const std::function &task); private: struct Private; QSharedPointer impl; }; } #endif // CLICK_QUERY_H ./scope/clickstore/store-query.cpp0000644000015600001650000005722712676763577017422 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "store-query.h" #include "store-scope.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace click; namespace { static const std::string CATEGORY_APPS_DISPLAY = R"( { "schema-version" : 1, "template" : { "category-layout" : "grid", "card-size": "small" }, "components" : { "title" : "title", "subtitle": "subtitle", "attributes": { "field": "attributes", "max-count": 4 }, "art" : { "field": "art", "aspect-ratio": 1.13, "fallback": "image://theme/placeholder-app-icon" } } } )"; static const std::string CATEGORY_SCOPES_DISPLAY = R"( { "schema-version" : 1, "template" : { "overlay": true, "category-layout" : "grid", "card-size": "small" }, "components" : { "title" : "title", "attributes": { "field": "attributes", "max-count": 1 }, "art" : { "field": "art", "aspect-ratio": 0.55, "fallback": "image://theme/placeholder-app-icon" } } } )"; static const std::string CATEGORY_APP_OF_THE_WEEK = R"( { "schema-version" : 1, "template": { "category-layout": "grid", "card-size": "large" }, "components": { "title": "title", "subtitle": "subtitle", "attributes": { "field": "attributes", "max-count": 4 }, "art": { "aspect-ratio": 2.5, "field": "art", "fallback": "image://theme/placeholder-app-icon" } } })"; static const std::string CATEGORY_APPS_SEARCH = R"( { "schema-version" : 1, "template" : { "category-layout" : "grid", "card-layout" : "horizontal", "collapsed-rows": 0, "card-size": "large" }, "components" : { "title" : "title", "art" : { "field": "art", "aspect-ratio": 1.13, "fallback": "image://theme/placeholder-app-icon" }, "subtitle": "subtitle", "attributes": { "field": "attributes", "max-count": 3 } } } )"; } struct click::Query::Private { Private(click::Index& index, click::DepartmentLookup& depts, std::shared_ptr depts_db, click::HighlightList& highlights, const scopes::SearchMetadata& metadata, pay::Package& in_package) : index(index), department_lookup(depts), depts_db(depts_db), highlights(highlights), meta(metadata), pay_package(in_package) { } click::Index& index; click::DepartmentLookup& department_lookup; std::shared_ptr depts_db; click::HighlightList& highlights; scopes::SearchMetadata meta; click::web::Cancellable search_operation; click::web::Cancellable purchases_operation; pay::Package& pay_package; }; click::Query::Query(unity::scopes::CannedQuery const& query, click::Index& index, click::DepartmentLookup& depts, std::shared_ptr depts_db, click::HighlightList& highlights, scopes::SearchMetadata const& metadata, pay::Package& in_package) : unity::scopes::SearchQueryBase(query, metadata), impl(new Private(index, depts, depts_db, highlights, metadata, in_package)) { } click::Query::~Query() { qDebug() << "destroying search"; } void click::Query::cancelled() { qDebug() << "cancelling search of" << QString::fromStdString(query().query_string()); impl->search_operation.cancel(); } click::Interface& click::Query::clickInterfaceInstance() { static QSharedPointer keyFileLocator(new click::KeyFileLocator()); static click::Interface iface(keyFileLocator); return iface; } bool click::Query::push_result(scopes::SearchReplyProxy const& searchReply, const scopes::CategorisedResult &res) { return searchReply->push(res); } void click::Query::finished(const scopes::SearchReplyProxy &searchReply) { searchReply->finished(); } scopes::Category::SCPtr click::Query::register_category(const scopes::SearchReplyProxy &searchReply, const std::string &id, const std::string &title, const std::string &icon, const scopes::CategoryRenderer &renderer_template) { return searchReply->register_category(id, title, icon, renderer_template); } void click::Query::run_under_qt(const std::function &task) { qt::core::world::enter_with_task([task]() { task(); }); } unity::scopes::Department::SPtr click::Query::fromClickDepartment(const click::Department::SCPtr click_dept, const std::string& current_dept_id, const click::DepartmentList& subdepts) { const std::locale loc(""); unity::scopes::Department::SPtr dep = unity::scopes::Department::create(click_dept->id(), query(), click_dept->name()); if (click_dept->has_children_flag()) { dep->set_has_subdepartments(); } unity::scopes::DepartmentList departments; // subdepts is a list of subdepartments of current department fetched from the Store for current search; if we encountered current department in the tree, // insert the list from the server rather than the one from internal cache. for (auto click_subdep: (click_dept->id() == current_dept_id ? subdepts : click_dept->sub_departments())) { departments.push_back(fromClickDepartment(click_subdep, current_dept_id, subdepts)); } departments.sort([&loc](const unity::scopes::Department::SCPtr &d1, const unity::scopes::Department::SCPtr &d2) -> bool { return loc(d1->label(), d2->label()) > 0; }); dep->set_subdepartments(departments); return dep; } // // creates department menu with narrowed-down list of subdepartments of current department, as // returned by server call unity::scopes::Department::SPtr click::Query::populate_departments(const click::DepartmentList& subdepts, const std::string& current_dep_id) { // Return complete departments tree (starting from 'All') every time, rather than only parent - current - children; this is the only // way we can display siblings corrctly when navigating from Apps scope straight into a subdepartment of Store - see LP #1343242. // For currently visible department include its subdepartments as returned for current search by the server (subdepts) - // all others are constructed from the department lookup. return fromClickDepartment(impl->department_lookup.get_department_info(""), current_dep_id, subdepts); } // recursively store all departments in the departments database void click::Query::store_departments(const click::DepartmentList& depts) { assert(impl->depts_db); try { impl->depts_db->store_departments(depts, search_metadata().locale()); } catch (const std::exception &e) { qWarning() << "Failed to update database: " << QString::fromStdString(e.what()); } } void click::Query::push_package(const scopes::SearchReplyProxy& searchReply, scopes::Category::SCPtr category, const PackageSet &installedPackages, const Package& pkg) { qDebug() << "pushing result" << QString::fromStdString(pkg.name); try { scopes::CategorisedResult res(category); res.set_title(pkg.title); res.set_art(pkg.icon_url); res.set_uri(pkg.url); res[click::Query::ResultKeys::NAME] = pkg.name; res["subtitle"] = pkg.publisher; auto installed = installedPackages.find(pkg); std::string price = _("FREE"); std::stringstream ss; ss << std::fixed << std::setprecision(1); ss << "☆ " << pkg.rating; std::string rating{ss.str()}; bool was_purchased = false; time_t refundable_until = 0; double cur_price{0.00}; auto suggested = impl->index.get_suggested_currency(); std::string currency = Configuration::get_currency(suggested); if (pkg.prices.count(currency) == 1) { cur_price = pkg.prices.at(currency); } else { // NOTE: This is decprecated. Here for compatibility. currency = Configuration::CURRENCY_DEFAULT; cur_price = pkg.price; } res["price"] = scopes::Variant(cur_price); res[click::Query::ResultKeys::VERSION] = pkg.version; qDebug() << "App:" << pkg.name.c_str() << ", price:" << cur_price; std::string formatted_price; if (cur_price > 0.00f) { if (!Configuration::get_purchases_enabled()) { // Don't show priced apps if flag not set return; } // Check if the priced app was already purchased. auto purchased = purchased_apps.find({pkg.name}); was_purchased = purchased != purchased_apps.end(); if (was_purchased) { refundable_until = purchased->refundable_until; } qDebug() << "was purchased?" << was_purchased << ", refundable_until:" << refundable_until; // Get the currency symbol to use. QLocale locale; auto symbol = Configuration::CURRENCY_MAP.at(currency); formatted_price = locale.toCurrencyString(cur_price, symbol.c_str()).toUtf8().data(); res["currency_symbol"] = symbol; } if (installed != installedPackages.end()) { res[click::Query::ResultKeys::INSTALLED] = true; res[click::Query::ResultKeys::PURCHASED] = was_purchased; price = _("✔ INSTALLED"); res[click::Query::ResultKeys::VERSION] = installed->version; } else if (was_purchased) { res[click::Query::ResultKeys::PURCHASED] = true; res[click::Query::ResultKeys::INSTALLED] = false; price = _("✔ PURCHASED"); } else { res[click::Query::ResultKeys::INSTALLED] = false; res[click::Query::ResultKeys::PURCHASED] = false; if (cur_price > 0.00f) { price = formatted_price; } } res[click::Query::ResultKeys::REFUNDABLE_UNTIL] = unity::scopes::Variant((int64_t)refundable_until); res["formatted_price"] = formatted_price; res["price_area"] = price; res["rating"] = rating; // Add the price and rating as attributes. scopes::VariantBuilder builder; builder.add_tuple({ {"value", scopes::Variant(price)}, }); builder.add_tuple({ {"value", scopes::Variant("")}, }); builder.add_tuple({ {"value", scopes::Variant(rating)}, }); builder.add_tuple({ {"value", scopes::Variant("")}, }); res["attributes"] = builder.end(); this->push_result(searchReply, res); } catch(const std::exception& e){ qCritical() << "push_package: Exception: " << e.what() ; } catch(...){ qDebug() << "no reason to catch"; } } void click::Query::push_highlights(const scopes::SearchReplyProxy& searchReply, const HighlightList& highlights, const PackageSet &locallyInstalledApps) { const scopes::CategoryRenderer renderer(CATEGORY_APPS_DISPLAY); const scopes::CategoryRenderer scopes_renderer(CATEGORY_SCOPES_DISPLAY); const scopes::CategoryRenderer aotw_renderer(CATEGORY_APP_OF_THE_WEEK); for (auto const& hl: highlights) { scopes::CategoryRenderer const* rdr = &renderer; if (hl.slug() == "app-of-the-week" || hl.packages().size() == 1) { rdr = &aotw_renderer; } if (hl.contains_scopes()) { rdr = &scopes_renderer; } auto category = register_category(searchReply, hl.slug(), hl.name(), "", *rdr); for (auto const& pkg: hl.packages()) { push_package(searchReply, category, locallyInstalledApps, pkg); } } qDebug() << "Highlights pushed"; } void click::Query::push_departments(const scopes::SearchReplyProxy& searchReply, const scopes::Department::SCPtr& root) { if (root != nullptr) { try { qDebug() << "pushing departments"; searchReply->register_departments(root); } catch (const std::exception& e) { qWarning() << "Failed to register departments for query " << QString::fromStdString(query().query_string()) << ", current department " << QString::fromStdString(query().department_id()) << ": " << e.what(); } } else { qWarning() << "No departments data for query " << QString::fromStdString(query().query_string()) << "', current department " << QString::fromStdString(query().department_id()); } } void click::Query::push_departments(scopes::SearchReplyProxy const& searchReply) { auto rootdep = impl->department_lookup.get_department_info(""); if (!rootdep) { qWarning() << "No department information available"; return; } auto subdepts = rootdep->sub_departments(); auto root = populate_departments(subdepts, ""); push_departments(searchReply, root); } // // push highlights and departments // use cached highlights for root department, otherwise run an async job for highlights of current department. void click::Query::add_highlights(scopes::SearchReplyProxy const& searchReply, const PackageSet& locallyInstalledApps) { auto curdep = impl->department_lookup.get_department_info(query().department_id()); if (!curdep) { qWarning() << "No department information for current department" << QString::fromStdString(query().department_id()); return; } if (query().department_id() == "") // top-level departments { auto subdepts = curdep->sub_departments(); auto root = populate_departments(subdepts, query().department_id()); push_departments(searchReply, root); qDebug() << "pushing cached highlights"; push_highlights(searchReply, impl->highlights, locallyInstalledApps); this->finished(searchReply); //FIXME: this shouldn't be needed } else { qDebug() << "starting departments call for department" << QString::fromStdString(curdep->id()) << ", href" << QString::fromStdString(curdep->href()); impl->search_operation = impl->index.departments(curdep->href(), [this, locallyInstalledApps, searchReply](const DepartmentList& depts, const HighlightList& highlights, Index::Error error, int) { if (error == click::Index::Error::NoError) { qDebug() << "departments call completed"; auto root = populate_departments(depts, query().department_id()); push_departments(searchReply, root); push_highlights(searchReply, highlights, locallyInstalledApps); } else { qWarning() << "departments call failed"; } this->finished(searchReply); //FIXME: this shouldn't be needed }); } } void click::Query::add_available_apps(scopes::SearchReplyProxy const& searchReply, const PackageSet& installedPackages, const std::string& categoryTemplate) { // this assertion is here to ensure unit tests are properly implemented. // this pointer is never null during normal execution. assert(searchReply); run_under_qt([=]() { auto search_cb = [this, searchReply, categoryTemplate, installedPackages](Packages packages, Packages recommends) { qDebug("search callback"); const scopes::CategoryRenderer categoryRenderer(categoryTemplate); std::string cat_title(_("Available")); { char tmp[512]; unsigned int num_results = static_cast(packages.size()); if (snprintf(tmp, sizeof(tmp), dngettext(GETTEXT_PACKAGE, "%u result in Ubuntu Store", "%u results in Ubuntu Store", num_results), num_results) > 0) { cat_title = tmp; } } auto category = register_category(searchReply, "appstore", cat_title, "", categoryRenderer); const scopes::CategoryRenderer recommendsCatRenderer(categoryTemplate); auto recommendsCategory = register_category(searchReply, "recommends", _("Recommended"), "", recommendsCatRenderer); // handle packages data foreach (auto p, packages) { push_package(searchReply, category, installedPackages, p); } foreach (auto r, recommends) { push_package(searchReply, recommendsCategory, installedPackages, r); } qDebug() << "search completed"; this->finished(searchReply); //FIXME: this shouldn't be needed }; // this is the case when we do bootstrap for the first time, or it failed last time if (impl->department_lookup.size() == 0) { qDebug() << "performing bootstrap request"; impl->search_operation = impl->index.bootstrap([this, search_cb, searchReply, installedPackages](const DepartmentList& deps, const HighlightList& highlights, click::Index::Error error, int) { if (error == click::Index::Error::NoError) { qDebug() << "bootstrap request completed"; auto root = std::make_shared("", _("All"), "", true); root->set_subdepartments(deps); DepartmentList rdeps { root }; impl->department_lookup.rebuild(rdeps); impl->highlights = highlights; qDebug() << "Total number of departments:" << impl->department_lookup.size() << ", highlights:" << highlights.size(); if (impl->depts_db) { qDebug() << "Storing departments in the database"; store_departments(deps); } else { qWarning() << "Departments db not available"; } } else { qWarning() << "bootstrap request failed"; } if (error == click::Index::Error::NoError) { if (query().query_string().empty()) { add_highlights(searchReply, installedPackages); } else { qDebug() << "starting search of" << QString::fromStdString(query().query_string()); push_departments(searchReply); impl->search_operation = impl->index.search(query().query_string(), query().department_id(), search_cb); } } }); } else { if (query().query_string().empty()) { add_highlights(searchReply, installedPackages); } else // normal search { qDebug() << "starting search of" << QString::fromStdString(query().query_string()); push_departments(searchReply); impl->search_operation = impl->index.search(query().query_string(), query().department_id(), search_cb); } } }); } PackageSet click::Query::get_installed_packages() { std::promise installed_promise; std::future installed_future = installed_promise.get_future(); run_under_qt([&]() { clickInterfaceInstance().get_installed_packages( [&installed_promise](PackageSet installedPackages, InterfaceError){ installed_promise.set_value(installedPackages); }); }); return installed_future.get(); } void click::Query::run(scopes::SearchReplyProxy const& searchReply) { auto q = query().query_string(); std::string categoryTemplate = CATEGORY_APPS_SEARCH; if (q.empty()) { categoryTemplate = CATEGORY_APPS_DISPLAY; } if (Configuration::get_purchases_enabled()) { std::promise purchased_promise; std::future purchased_future = purchased_promise.get_future(); qDebug() << "Getting list of purchased apps."; run_under_qt([this, &purchased_promise]() { impl->purchases_operation = impl->pay_package.get_purchases([&purchased_promise](const pay::PurchaseSet& purchases) { purchased_promise.set_value(purchases); }); }); purchased_apps = purchased_future.get(); } add_available_apps(searchReply, get_installed_packages(), categoryTemplate); } ./scope/clickstore/store-scope.cpp0000644000015600001650000002520712676763604017346 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include "store-scope.h" #include "store-query.h" #include #include #include #include #include #include #include #include #include click::Scope::Scope() { nam.reset(new click::network::AccessManager()); client.reset(new click::web::Client(nam)); index.reset(new click::Index(client)); depts.reset(new click::DepartmentLookup()); highlights.reset(new click::HighlightList()); pay_package.reset(new pay::Package(client)); try { depts_db = click::DepartmentsDb::open(true); } catch (const std::runtime_error& e) { std::cerr << "Failed to open departments db: " << e.what() << std::endl; } } click::Scope::~Scope() { } void click::Scope::start(std::string const&) { setlocale(LC_ALL, ""); // FIXME: This is wrong, but needed for json-cpp workaround. setlocale(LC_MONETARY, "C"); bindtextdomain(GETTEXT_PACKAGE, GETTEXT_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); click::Date::setup_system_locale(); } void click::Scope::run() { static const int zero = 0; auto emptyCb = [this]() { dm.reset(Ubuntu::DownloadManager::Manager::createSessionManager()); }; qt::core::world::build_and_run(zero, nullptr, emptyCb); } void click::Scope::stop() { qt::core::world::destroy(); } scopes::SearchQueryBase::UPtr click::Scope::search(unity::scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata) { return scopes::SearchQueryBase::UPtr(new click::Query(q, *index, *depts, depts_db, *highlights, metadata, *pay_package)); } unity::scopes::PreviewQueryBase::UPtr click::Scope::preview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata) { qDebug() << "Scope::preview() called."; auto preview = new click::Preview(result, metadata); preview->choose_strategy(client, pay_package, dm, depts_db); return unity::scopes::PreviewQueryBase::UPtr{preview}; } unity::scopes::ActivationQueryBase::UPtr click::Scope::perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& widget_id, std::string const& _action_id) { std::string action_id = _action_id; qDebug() << "perform_action called with widget_id" << QString::fromStdString(widget_id) << "and action_id:" << QString::fromStdString(action_id); auto activation = new ScopeActivation(result, metadata); // if the purchase is completed, do the install if (action_id == "purchaseCompleted") { qDebug() << "Yay, got finished signal"; qDebug() << "about to get the download_url"; std::string download_url = metadata.scope_data().get_dict()["download_url"].get_string(); qDebug() << "the download url is: " << QString::fromStdString(download_url); activation->setHint("download_url", unity::scopes::Variant(download_url)); std::string download_sha512 = metadata.scope_data().get_dict()["download_sha512"].get_string(); activation->setHint("download_sha512", unity::scopes::Variant(download_sha512)); activation->setHint("action_id", unity::scopes::Variant(click::Preview::Actions::INSTALL_CLICK)); activation->setHint("purchased", unity::scopes::Variant(true)); qDebug() << "returning ShowPreview"; activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == "purchaseCancelled") { qDebug() << "Purchase was cancelled, refreshing scope results."; click::DownloadErrorPreview strategy{result}; strategy.invalidateScope(STORE_SCOPE_ID.toUtf8().data()); } else if (action_id == "purchaseError") { activation->setHint(click::Preview::Actions::DOWNLOAD_FAILED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::INSTALL_CLICK) { qDebug() << "about to get the download_url"; std::string download_url = metadata.scope_data().get_dict()["download_url"].get_string(); qDebug() << "the download url is: " << QString::fromStdString(download_url); activation->setHint("download_url", unity::scopes::Variant(download_url)); std::string download_sha512 = metadata.scope_data().get_dict()["download_sha512"].get_string(); activation->setHint("download_sha512", unity::scopes::Variant(download_sha512)); activation->setHint("action_id", unity::scopes::Variant(action_id)); qDebug() << "returning ShowPreview"; activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::DOWNLOAD_FAILED) { activation->setHint(click::Preview::Actions::DOWNLOAD_FAILED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::DOWNLOAD_COMPLETED) { activation->setHint(click::Preview::Actions::DOWNLOAD_COMPLETED, unity::scopes::Variant(true)); activation->setHint("installed", unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CANCEL_PURCHASE_INSTALLED) { activation->setHint(click::Preview::Actions::CANCEL_PURCHASE_INSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED) { activation->setHint(click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::UNINSTALL_CLICK) { activation->setHint(click::Preview::Actions::UNINSTALL_CLICK, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::SHOW_UNINSTALLED) { activation->setHint(click::Preview::Actions::SHOW_UNINSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::SHOW_INSTALLED) { activation->setHint(click::Preview::Actions::SHOW_INSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CONFIRM_UNINSTALL) { activation->setHint(click::Preview::Actions::CONFIRM_UNINSTALL, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED) { activation->setHint(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED) { activation->setHint(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::RATED) { scopes::VariantMap rating_info = metadata.scope_data().get_dict(); // Cast to int because widget gives us double, which is wrong. int rating = ((int)rating_info["rating"].get_double()); std::string review_text = rating_info["review"].get_string(); // We have to get the values and then set them as hints here, to be // able to pass them on to the Preview, which actually makes the // call to submit. activation->setHint("rating", scopes::Variant(rating)); activation->setHint("review", scopes::Variant(review_text)); activation->setHint(click::Preview::Actions::RATED, scopes::Variant(true)); activation->setHint("widget_id", scopes::Variant(widget_id)); activation->setStatus(scopes::ActivationResponse::Status::ShowPreview); } return scopes::ActivationQueryBase::UPtr(activation); } #define EXPORT __attribute__ ((visibility ("default"))) extern "C" { EXPORT unity::scopes::ScopeBase* // cppcheck-suppress unusedFunction UNITY_SCOPE_CREATE_FUNCTION() { // Set up logging UbuntuOne::AuthLogger::setupLogging(); #if ENABLE_DEBUG UbuntuOne::AuthLogger::setLogLevel(QtDebugMsg); #else const char* u1_debug = getenv("U1_DEBUG"); if (u1_debug != NULL && strcmp(u1_debug, "") != 0) { UbuntuOne::AuthLogger::setLogLevel(QtDebugMsg); } #endif return new click::Scope(); } EXPORT void // cppcheck-suppress unusedFunction UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base) { delete scope_base; } } ./scope/clickstore/com.canonical.scopes.clickstore.ini.in.in0000644000015600001650000000042212676763577024253 0ustar jenkinsjenkins[ScopeConfig] _DisplayName=Ubuntu Store _Description=Scope for searching the Ubuntu app store Author=Canonical Ltd. Art=@STORE_DATA_DIR@/clickscope-screenshot.jpg Icon=@STORE_DATA_DIR@/ubuntu-store-scope.png _SearchHint=Search store [Appearance] LogoOverlayColor=#26000000 ./scope/clickstore/CMakeLists.txt0000644000015600001650000000253712676763577017151 0ustar jenkinsjenkinsSET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package (Qt5Core REQUIRED) find_package (Qt5Sql REQUIRED) pkg_check_modules(JSON_CPP REQUIRED jsoncpp) add_definitions( -DGETTEXT_PACKAGE=\"${PROJECT_NAME}\" -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}\" ) add_library(${STORE_LIB_UNVERSIONED} SHARED store-query.cpp store-scope.cpp ) set_target_properties(${STORE_LIB_UNVERSIONED} PROPERTIES PREFIX "") include_directories( ${CMAKE_SOURCE_DIR}/libclickscope ${JSON_CPP_INCLUDE_DIRS} ) qt5_use_modules (${STORE_LIB_UNVERSIONED} Network Sql) target_link_libraries (${STORE_LIB_UNVERSIONED} ${SCOPE_LIB_NAME} ${JSON_CPP_LDFLAGS} ${UNITY_SCOPES_LDFLAGS} ${UBUNTUONE_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS} ) install( TARGETS ${STORE_LIB_UNVERSIONED} LIBRARY DESTINATION "${STORE_LIB_DIR}" ) set(STORE_INI_TARGET com.canonical.scopes.clickstore.ini) configure_file( ${STORE_INI_TARGET}.in.in ${STORE_INI_TARGET}.in ) add_custom_target(${STORE_INI_TARGET} ALL COMMENT "Merging translations into ${STORE_INI_TARGET}" COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${STORE_INI_TARGET}.in ${STORE_INI_TARGET} >/dev/null ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${STORE_INI_TARGET}" DESTINATION "${STORE_LIB_DIR}" ) ./scope/clickstore/store-scope.h0000644000015600001650000000575412676763604017020 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_SCOPE_H #define CLICK_SCOPE_H #include #include #include #include #include #include #include #include #include namespace scopes = unity::scopes; namespace click { class DepartmentLookup; class DepartmentsDb; class Scope : public scopes::ScopeBase { public: Scope(); ~Scope(); virtual void start(std::string const&) override; virtual void run() override; virtual void stop() override; virtual scopes::SearchQueryBase::UPtr search(scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata) override; unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result&, const unity::scopes::ActionMetadata&) override; virtual unity::scopes::ActivationQueryBase::UPtr perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& widget_id, std::string const& action_id) override; private: QSharedPointer nam; QSharedPointer client; QSharedPointer index; QSharedPointer pay_package; QSharedPointer dm; std::shared_ptr depts; std::shared_ptr highlights; std::shared_ptr depts_db; std::string installApplication(unity::scopes::Result const& result); }; } #endif // CLICK_SCOPE_H ./tests/0000755000015600001650000000000012676763577012271 5ustar jenkinsjenkins./tests/scope-harness/0000755000015600001650000000000012676763577015043 5ustar jenkinsjenkins./tests/scope-harness/tests/0000755000015600001650000000000012676763577016205 5ustar jenkinsjenkins./tests/scope-harness/tests/test_apps.py0000644000015600001650000002467612676763577020600 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import testtools from . import ScopeTestBase from scope_harness import ( CategoryListMatcher, CategoryListMatcherMode, CategoryMatcher, CategoryMatcherMode, ChildDepartmentMatcher, DepartmentMatcher, DepartmentMatcherMode, Parameters, PreviewColumnMatcher, PreviewMatcher, PreviewView, PreviewWidgetMatcher, ResultMatcher, ScopeHarness, ) class AppsTest (ScopeTestBase): def setUp(self): super().setUp() scope_dir = os.environ.get('SCOPE_DIR', None) self.harness = self.launch_scope(scope_dir) self.view = self.harness.results_view self.view.active_scope = 'clickscope' def launch_scope(self, scope_dir=None): """Find the scope and launch it.""" if scope_dir is None: return ScopeHarness.new_from_scope_list(Parameters([ 'clickscope.ini' ])) else: scope_path = os.path.join( scope_dir, 'clickapps', 'clickscope.ini') return ScopeHarness.new_from_scope_list(Parameters([ scope_path ])) def test_surfacing_results(self): self.view.browse_department('') self.view.search_query = '' # Check first apps of every category match = ( CategoryListMatcher() .has_exactly(3) .mode(CategoryListMatcherMode.BY_ID) .category( CategoryMatcher("predefined") .has_at_least(1) .mode(CategoryMatcherMode.BY_URI) .result( ResultMatcher("application:///dialer-app.desktop") .title('Phone') .property('installed', True) ) ) .category( CategoryMatcher("local") .has_at_least(1) .mode(CategoryMatcherMode.STARTS_WITH) .result( ResultMatcher('application:///' + 'com.ubuntu.developer.webapps.' + 'webapp-amazon_webapp-amazon.*') .properties({'installed': True, 'version': '1.0.10'}) .title('Amazon') ) ) .category( CategoryMatcher("store") .has_at_least(1) .mode(CategoryMatcherMode.BY_URI) .result( ResultMatcher('scope://com.canonical.scopes.clickstore.*') ) ) .match(self.view.categories) ) self.assertMatchResult(match) def test_surfacing_departments(self): self.view.search_query = '' departments = self.view.browse_department('') self.assertTrue(self.view.has_departments) # TODO: list all expected departments (depending on installed apps) match = DepartmentMatcher() \ .mode(DepartmentMatcherMode.STARTS_WITH) \ .id('') \ .label('All') \ .all_label('') \ .parent_id('') \ .parent_label('') \ .is_root(True) \ .is_hidden(False) \ .child(ChildDepartmentMatcher('communication')) \ .child(ChildDepartmentMatcher('games')) \ .match(departments) self.assertMatchResult(match) def test_department_browsing(self): self.view.search_query = '' departments = self.view.browse_department('games') match = DepartmentMatcher() \ .has_exactly(0) \ .mode(DepartmentMatcherMode.STARTS_WITH) \ .label('Games') \ .all_label('') \ .parent_id('') \ .parent_label('All') \ .is_root(False) \ .is_hidden(False) \ .match(departments) self.assertMatchResult(match) # FIXME: scope harness shouldn't report empty categories, # so should be exactly 2 res_match = ( CategoryListMatcher() .has_at_least(2) .mode(CategoryListMatcherMode.BY_ID) .category( CategoryMatcher("local") .has_at_least(1) .mode(CategoryMatcherMode.STARTS_WITH) .result( ResultMatcher('application:///' + 'com.ubuntu.dropping-letters' + '_dropping-letters.*') .art('/custom/click/.click/users/@all/' 'com.ubuntu.dropping-letters/./dropping-letters.png') .properties({'installed': True, 'version': '0.1.2.2.67'}) .title('Dropping Letters') ) ) .category( CategoryMatcher("store") .has_at_least(1) .mode(CategoryMatcherMode.BY_URI) .result( ResultMatcher('scope://' + 'com.canonical.scopes.clickstore.*dep=games') ) ) .match(self.view.categories) ) self.assertMatchResult(res_match) # browse different department departments = self.view.browse_department('communication') self.view.search_query = '' match = ( DepartmentMatcher() .has_exactly(0) .mode(DepartmentMatcherMode.STARTS_WITH) .label('Communication') .all_label('') .parent_id('') .parent_label('All') .is_root(False) .is_hidden(False) .match(departments) ) self.assertMatchResult(match) # FIXME: scope harness shouldn't report empty categories, # so should be exactly 2 res_match = ( CategoryListMatcher() .has_at_least(2) .mode(CategoryListMatcherMode.BY_ID) .category( CategoryMatcher("local") .has_at_least(1) .mode(CategoryMatcherMode.STARTS_WITH) .result( ResultMatcher('application:///webbrowser-app.desktop') .title('Browser') ) .result( ResultMatcher('application:///dialer-app.desktop') .title('Phone') ) ) .category( CategoryMatcher("store") .has_at_least(1) .mode(CategoryMatcherMode.BY_URI) .result( ResultMatcher( 'scope://' + 'com.canonical.scopes.clickstore.*dep=communication') ) ) .match(self.view.categories) ) self.assertMatchResult(res_match) @testtools.skip('Failing to find the browser') def test_nonremovable_app_preview(self): self.view.browse_department('') self.view.search_query = 'Brow' pview = self.view.categories[0].results[0].long_press() self.assertIsInstance(pview, PreviewView) match = PreviewColumnMatcher().column( PreviewMatcher() .widget(PreviewWidgetMatcher("hdr")) .widget( PreviewWidgetMatcher("buttons") .type("actions") .data( { 'actions': [ { 'id': 'open_click', 'label': 'Open', 'uri': 'application:///' + 'webbrowser-app.desktop' } ] } ) ) .widget(PreviewWidgetMatcher("screenshots")) .widget(PreviewWidgetMatcher("summary")) ).match(pview.widgets) self.assertMatchResult(match) @testtools.skip('Fails only on jenkins.') def test_removable_app_preview(self): self.view.browse_department('') self.view.search_query = 'Amazon' pview = self.view.categories[0].results[0].long_press() self.assertIsInstance(pview, PreviewView) match = PreviewColumnMatcher().column( PreviewMatcher() .widget(PreviewWidgetMatcher("hdr")) .widget( PreviewWidgetMatcher("buttons") .type("actions") .data( { 'actions': [ { 'id': 'open_click', 'label': 'Open', 'uri': 'application:///' + 'com.ubuntu.developer.webapps.webapp-amazon' + '_webapp-amazon_1.0.10.desktop' }, { 'id': 'uninstall_click', 'label': 'Uninstall' } ] } ) ) .widget(PreviewWidgetMatcher("summary") .type('text')) .widget(PreviewWidgetMatcher("other_metadata") .type('table')) .widget(PreviewWidgetMatcher("updates_table") .type('table')) .widget(PreviewWidgetMatcher("whats_new") .type('text')) .widget(PreviewWidgetMatcher("rating") .type('rating-input')) .widget(PreviewWidgetMatcher("reviews_title") .type('text')) .widget(PreviewWidgetMatcher("summary") .type('reviews')) ).match(pview.widgets) self.assertMatchResult(match) ./tests/scope-harness/tests/__init__.py0000644000015600001650000000675112676763577020327 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import fixtures import http.server import logging import os import socketserver from fake_server_fixture import FakeServerFixture from scope_harness.testing import ScopeHarnessTestCase logger = logging.getLogger(__name__) class FakeSearchRequestHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): self.send_content(head_only=False) def do_HEAD(self): self.send_content(head_only=True) def send_content(self, head_only=False): path = self.translate_path(self.server.root_folder + self.path) logger.info("opening this path: %s", path) logger.info("server serves at: %s", self.server.root_folder) if os.path.isdir(path): path = os.path.join(path, "index.json") try: f = open(path, "rb") contents = f.read() new_url = self.server.url replaced_contents = contents.replace(b"[FAKE_SERVER_BASE]", new_url.encode("utf-8")) except OSError: self.send_error(404, "File not found") return try: self.send_response(200) self.send_header("Content-type", "application/json") self.send_header("Content-Length", len(replaced_contents)) self.end_headers() if (head_only): return self.wfile.write(replaced_contents) finally: f.close() class FakeServer(socketserver.ForkingMixIn, http.server.HTTPServer): def __init__(self, server_address, root_folder): super().__init__(server_address, FakeSearchRequestHandler) self.root_folder = root_folder class ScopeTestBase(ScopeHarnessTestCase, fixtures.TestWithFixtures): def setupJsonServer(self, env_var, root_folder, append_slash=False): server_fixture = FakeServerFixture(FakeServer, root_folder) self.useFixture(server_fixture) self.useFixture(fixtures.EnvironmentVariable( env_var, newvalue=server_fixture.url)) def setUp(self): self.useFixture(fixtures.TempHomeDir()) self.useFixture(fixtures.EnvironmentVariable( 'LANGUAGE', newvalue='en_US.utf-8')) if os.environ.get('U1_SEARCH_BASE_URL', 'fake') == 'fake': self.setupJsonServer("U1_SEARCH_BASE_URL", "fake_responses/click-package-index/", append_slash=True) if os.environ.get('U1_REVIEWS_BASE_URL', 'fake') == 'fake': self.setupJsonServer("U1_REVIEWS_BASE_URL", "fake_responses/ratings-and-reviews/") if os.environ.get('PAY_BASE_URL', 'fake') == 'fake': self.setupJsonServer("PAY_BASE_URL", "fake_responses/software-center-agent/") ./tests/scope-harness/tests/test_store.py0000644000015600001650000002224412676763577020756 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import testtools from . import ScopeTestBase from scope_harness import ( CategoryListMatcher, CategoryListMatcherMode, CategoryMatcher, CategoryMatcherMode, ChildDepartmentMatcher, DepartmentMatcher, DepartmentMatcherMode, Parameters, PreviewColumnMatcher, PreviewMatcher, PreviewView, PreviewWidgetMatcher, ResultMatcher, ScopeHarness, ) class StoreTest(ScopeTestBase): def setUp(self): super().setUp() scope_dir = os.environ.get('SCOPE_DIR', None) self.harness = self.launch_scope(scope_dir) self.view = self.harness.results_view self.view.active_scope = 'com.canonical.scopes.clickstore' def launch_scope(self, scope_dir=None): """Find the scope and launch it.""" if scope_dir is None: return ScopeHarness.new_from_scope_list(Parameters([ "com.canonical.scopes.clickstore.ini" ])) else: scope_path = os.path.join( scope_dir, 'clickstore', 'com.canonical.scopes.clickstore.ini') return ScopeHarness.new_from_scope_list(Parameters([ scope_path ])) def test_surfacing_results(self): self.view.browse_department('') self.view.search_query = '' cpi_base = os.environ["U1_SEARCH_BASE_URL"] # Check first apps of every category match = ( CategoryListMatcher() .has_exactly(4) .mode(CategoryListMatcherMode.BY_ID) .category(CategoryMatcher("app-of-the-week") .has_at_least(1)) .category( CategoryMatcher("top-apps") .has_at_least(1) .mode(CategoryMatcherMode.STARTS_WITH) .result( ResultMatcher( cpi_base + '/api/v1/package/' + 'com.ubuntu.developer.bobo1993324.udropcabin') .properties( { 'installed': False, 'version': '0.2.1', 'price': 0.0, 'price_area': 'FREE', 'rating': '☆ 4.2' }) .title('uDropCabin') .subtitle('Zhang Boren') ) ) .category( CategoryMatcher("our-favorite-games") .has_at_least(1) .mode(CategoryMatcherMode.BY_URI) .result(ResultMatcher( cpi_base + '/api/v1/package/' + 'com.ubuntu.developer.andrew-hayzen.volleyball2d')) ) .category(CategoryMatcher("travel-apps") .has_at_least(1)) .match(self.view.categories) ) self.assertMatchResult(match) def test_surfacing_departments(self): self.view.search_query = '' departments = self.view.browse_department('') self.assertTrue(self.view.has_departments) match = DepartmentMatcher() \ .mode(DepartmentMatcherMode.STARTS_WITH) \ .id('') \ .label('All') \ .all_label('') \ .parent_id('') \ .parent_label('') \ .is_root(True) \ .is_hidden(False) \ .child(ChildDepartmentMatcher('books-comics')) \ .child(ChildDepartmentMatcher('business')) \ .child(ChildDepartmentMatcher('communication')) \ .child(ChildDepartmentMatcher('developer-tools')) \ .child(ChildDepartmentMatcher('education')) \ .child(ChildDepartmentMatcher('entertainment')) \ .child(ChildDepartmentMatcher('finance')) \ .child(ChildDepartmentMatcher('food-drink')) \ .child(ChildDepartmentMatcher('games')) \ .child(ChildDepartmentMatcher('graphics')) \ .child(ChildDepartmentMatcher('health-fitness')) \ .child(ChildDepartmentMatcher('lifestyle')) \ .child(ChildDepartmentMatcher('media-video')) \ .child(ChildDepartmentMatcher('medical')) \ .child(ChildDepartmentMatcher('music-audio')) \ .child(ChildDepartmentMatcher('news-magazines')) \ .child(ChildDepartmentMatcher('personalisation')) \ .child(ChildDepartmentMatcher('productivity')) \ .child(ChildDepartmentMatcher('reference')) \ .child(ChildDepartmentMatcher('science-engineering')) \ .child(ChildDepartmentMatcher('shopping')) \ .child(ChildDepartmentMatcher('social-networking')) \ .child(ChildDepartmentMatcher('sports')) \ .child(ChildDepartmentMatcher('travel-local')) \ .child(ChildDepartmentMatcher('universal-accessaccessibility')) \ .child(ChildDepartmentMatcher('accessories')) \ .child(ChildDepartmentMatcher('weather')) \ .match(departments) self.assertMatchResult(match) def test_department_browsing(self): self.view.search_query = '' departments = self.view.browse_department('games') match = DepartmentMatcher() \ .has_exactly(0) \ .mode(DepartmentMatcherMode.STARTS_WITH) \ .label('Games') \ .all_label('') \ .parent_id('') \ .parent_label('All') \ .is_root(False) \ .is_hidden(False) \ .match(departments) self.assertMatchResult(match) res_match = ( CategoryListMatcher() .has_exactly(3) .mode(CategoryListMatcherMode.BY_ID).category( CategoryMatcher("top-games") .has_at_least(1)) .category(CategoryMatcher("__all-scopes__") .has_at_least(1)) .category(CategoryMatcher("__all-apps__") .has_at_least(1)) .match(self.view.categories) ) self.assertMatchResult(res_match) @testtools.skip('Fails only under adt-run.') def test_uninstalled_app_preview(self): self.view.browse_department('') self.view.search_query = 'Calendar' pview = self.view.categories[0].results[0].tap() self.assertIsInstance(pview, PreviewView) match = PreviewColumnMatcher().column( PreviewMatcher() .widget(PreviewWidgetMatcher("hdr")) .widget( PreviewWidgetMatcher("buttons") .type("actions") .data( { "actions": [ { 'download_sha512': '2fa658804e63da1869037cd' + '9bc74b792875404f03b6c6449271ae5244688ff42a' + '4524712ccb748ab9004344cccddd59063f3d3a4af8' + '99a3cc6f64ddc1a27072b', "download_url": 'https://' + 'public.apps.ubuntu.com/download/' + 'com.ubuntu/calendar/' + 'com.ubuntu.calendar_0.4.572_all.click', "id": "install_click", "label": "Install" } ], "online_account_details": { "login_failed_action": 1, "login_passed_action": 3, "provider_name": "ubuntuone", "scope_id": "", "service_name": "ubuntuone", "service_type": "ubuntuone" } } ) ) .widget(PreviewWidgetMatcher("screenshots") .type('gallery')) .widget(PreviewWidgetMatcher("summary") .type('text')) .widget(PreviewWidgetMatcher("other_metadata") .type('table')) .widget(PreviewWidgetMatcher("updates_table") .type('table')) .widget(PreviewWidgetMatcher("whats_new") .type('text')) .widget(PreviewWidgetMatcher("reviews_title") .type('text')) .widget(PreviewWidgetMatcher("summary") .type('reviews')) ).match(pview.widgets) self.assertMatchResult(match) ./tests/scope-harness/run-harness.sh0000755000015600001650000000252712676763577017655 0ustar jenkinsjenkins#!/bin/sh # # Copyright (C) 2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set -x SOURCE_DIR=$1 if [ -z "${SOURCE_DIR}" ]; then echo "No source directory specified." exit 1 fi export PATH=${SOURCE_DIR}/fake_click:${PATH} CWD=`pwd` TEMP_DIR=`mktemp -d --tmpdir=${CWD} test.XXXXXX` # Copy applications test data over mkdir -p ${TEMP_DIR}/applications cp -a ${SOURCE_DIR}/applications/*.desktop ${TEMP_DIR}/applications # Make the cache directory mkdir -p ${TEMP_DIR}/cache # Copy departments db cp -a ${SOURCE_DIR}/../../data/departments.db ${TEMP_DIR}/cache/click-departments.db export XDG_DATA_HOME=${TEMP_DIR} export XDG_CACHE_HOME=${TEMP_DIR}/cache export XDG_CONFIG_HOME=${TEMP_DIR}/config (cd ${SOURCE_DIR} && xvfb-run -a python3 -m testtools.run discover) RESULT=$? rm -rf ${TEMP_DIR} exit $RESULT ./tests/scope-harness/applications/0000755000015600001650000000000012676763577017531 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootroot./tests/scope-harness/applications/com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.10.desktop./tests/scope-harness/applications/com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.10.d0000644000015600001650000000125012676763577033575 0ustar jenkinsjenkins[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.10 -- webapp-container --enable-back-forward --webappUrlPatterns=https?://www.amazon.com/*,https?://s.amazon-adsystem.com/* http://www.amazon.com/gp/aw %u Name=Amazon Icon=/custom/click/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon/./amazon.png X-Ubuntu-Touch=true X-Ubuntu-Single-Instance=true X-Ubuntu-Default-Department-ID=internet Path=/custom/click/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon X-Ubuntu-Old-Icon=./amazon.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.10 ./tests/scope-harness/applications/com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.67.desktop0000644000015600001650000000106312676763577033217 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Icon=/custom/click/.click/users/@all/com.ubuntu.dropping-letters/./dropping-letters.png Terminal=false Name=Dropping Letters Exec=aa-exec-click -p com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.67 -- qmlscene -qt5 ./dropping-letters.qml X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Default-Department-ID=puzzles Path=/custom/click/.click/users/@all/com.ubuntu.dropping-letters X-Ubuntu-Old-Icon=./dropping-letters.png X-Ubuntu-Application-ID=com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.67 ./tests/scope-harness/applications/dialer-app.desktop0000644000015600001650000001674012676763577023152 0ustar jenkinsjenkins[Desktop Entry] Type=Application Name=Phone Name[am]=ስáˆáŠ­ Name[ast]=Teléfonu Name[az]=Telefon Name[br]=Pellgomz Name[bs]=Telefon Name[ca]=Telèfon Name[ca@valencia]=Telèfon Name[da]=Telefon Name[de]=Telefon Name[el]=Τηλέφωνο Name[en_AU]=Phone Name[en_GB]=Phone Name[es]=Teléfono Name[eu]=Telefonoa Name[fa]=تلÙÙ† Name[fi]=Puhelin Name[fr]=Téléphone Name[fr_CA]=Téléphone Name[gd]=Fòn Name[gl]=Teléfono Name[he]=טלפון Name[hr]=Telefon Name[hu]=Telefon Name[id]=Telepon Name[is]=Sími Name[it]=Telefono Name[ja]=電話 Name[km]=ទូរសáŸáž–្ទ​ Name[ko]=ì „í™” Name[lv]=TÄlrunis Name[ms]=Telefon Name[nb]=Telefon Name[nl]=Telefoon Name[oc]=Telefòn Name[pa]=ਫੋਨ Name[pl]=Telefon Name[pt]=Telemóvel Name[pt_BR]=Telefone Name[ro]=Telefon Name[ru]=Телефон Name[sl]=Telefon Name[sr]=Телефон Name[sv]=Telefon Name[tr]=Telefon Name[ug]=تÛÙ„ÛÙون Name[uk]=Телефон Name[zh_TW]=電話 GenericName=Phone App GenericName[am]=የ ስáˆáŠ­ መተáŒá‰ áˆªá‹« GenericName[ast]=Aplicación Teléfonu GenericName[az]=Telefon TÉ™tbiqetmÉ™si GenericName[br]=Arload pellgomz GenericName[bs]=Telefon GenericName[ca]=Trucades GenericName[ca@valencia]=Cridades GenericName[da]=Telefonprogram GenericName[de]=Telefon-Anwendung GenericName[el]=ΕφαÏμογή τηλεφώνου GenericName[en_AU]=Phone App GenericName[en_GB]=Phone App GenericName[es]=Aplicación Teléfono GenericName[eu]=Telefonoa aplikazioa GenericName[fa]=اپ تلÙÙ† GenericName[fi]=Puhelinsovellus GenericName[fr]=Application de téléphonie GenericName[fr_CA]=Appli téléphonique GenericName[gd]=Aplacaid an fhòn GenericName[gl]=Aplicativo de teléfono GenericName[he]=×™×™×©×•× ×˜×œ×¤×•×Ÿ GenericName[hr]=Telefonska aplikacija GenericName[hu]=Telefon alkalmazás GenericName[id]=Aplikasi Telepon GenericName[is]=Símaforrit GenericName[it]=App telefono GenericName[ja]=電話アプリ GenericName[km]=កម្មវិធី​ទូរសáŸáž–្ទ GenericName[ko]=ì „í™” 프로그램 GenericName[lv]=TÄlruņa lietotne GenericName[ms]=Apl Telefon GenericName[nb]=Telefonprogram GenericName[nl]=Telefoonapp GenericName[oc]=Aplicacion de telefonia GenericName[pa]=ਫੋਨ à¨à¨ª GenericName[pt]=App de telemóvel GenericName[pt_BR]=Phone App GenericName[ro]=AplicaÈ›ie pentru telefonie GenericName[ru]=Телефон GenericName[sl]=Mobilni program GenericName[sr]=Програм за телефонирање GenericName[sv]=Telefonapp GenericName[tr]=Telefon Uygulaması GenericName[ug]=تÛÙ„ÛÙون ئەپى GenericName[uk]=Програма-телефон GenericName[zh_TW]=é›»è©±ç¨‹å¼ Comment=Phone application Comment[am]=የ ስáˆáŠ­ መተáŒá‰ áˆªá‹« Comment[ast]=Aplicación Teléfonu Comment[az]=Telefon tÉ™tbiqetmÉ™si Comment[br]=Arload pellgomz Comment[bs]=Telefonska aplikacija Comment[ca]=Aplicació del telèfon Comment[ca@valencia]=Aplicació del telèfon Comment[da]=Telefonprogram Comment[de]=Telefon-Anwendung Comment[el]=ΕφαÏμογή τηλεφώνου Comment[en_AU]=Phone application Comment[en_GB]=Phone application Comment[es]=Aplicación Teléfono Comment[eu]=Telefonoa aplikazioa Comment[fa]=برنامه‌ی تلÙÙ† Comment[fi]=Puhelinsovellus Comment[fr]=Application de téléphonie Comment[fr_CA]=Application téléphonique Comment[gd]=Aplacaid an fhòn Comment[gl]=Aplicativo de teléfono Comment[he]=×™×™×©×•× ×˜×œ×¤×•×Ÿ Comment[hr]=Telefonska aplikacija Comment[hu]=Telefon alkalmazás Comment[id]=Aplikasi telepon Comment[is]=Símahugbúnaður Comment[it]=Applicazione telefono Comment[ja]=電話アプリ Comment[km]=កម្មវិធី​ទូរសáŸáž–្ទ Comment[ko]=ì „í™” 프로그램 Comment[lv]=TÄlruņa lietotne Comment[ms]=Aplikasi telefon Comment[nb]=Telefonprogram Comment[nl]=Telefoonapplicatie Comment[oc]=Aplicacion de telefonia Comment[pa]=ਫੋਨ à¨à¨ªà¨²à©€à¨•ੇਸ਼ਨ Comment[pt]=Aplicação de telemóvel Comment[pt_BR]=Aplicativo de telefone Comment[ro]=AplicaÈ›ie pentru telefonie Comment[ru]=Телефонное приложение Comment[sl]=Mobilni program Comment[sr]=Програм за телефонирање Comment[sv]=Telefonprogram Comment[tr]=Telefon uygulaması Comment[ug]=تÛÙ„ÛÙون پروگراممىسى Comment[uk]=Програма-телефон Comment[zh_TW]=é›»è©±æ‡‰ç”¨ç¨‹å¼ Keywords=Phone;Dialer;Dial;Call;Keypad Keywords[am]=ስáˆáŠ­: መጥሪያ: ጥሪ: መደወያ: Keywords[ast]=Teléfonu;Marcador;Marcar;Tecláu Keywords[br]=Pellgomz;Niverenner;Niverenniñ;Gervel;Klavier niverel Keywords[bs]=Telefon;BiraÄ;Biraj;Zovi;Tastatura Keywords[ca]=Telèfon; marcador; trucades; truca; mòbil Keywords[ca@valencia]=Telèfon; marcador; cridades; crida; mòbil Keywords[da]=Telefon;Opringning;Dialer;Kald;Opkald;Tastatur Keywords[de]=Telefon;Wähler;anrufen;wählen;Anruf;Tastatur Keywords[el]=Τηλέφωνο;Dialer;Dial;Κλήση;ΠληκτÏολόγιο Keywords[en_AU]=Phone;Dialer;Dial;Call;Keypad Keywords[en_GB]=Phone;Dialer;Dial;Call;Keypad Keywords[es]=Teléfono;Marcador;Marcar;Teclado Keywords[eu]=Telefonoa;Markatzailea;Markatu;Deitu;Markatze-teklatua Keywords[fa]=تلÙÙ†;شماره‌گیر;شماره;تماس;ØµÙØ­Ù‡â€ŒÚ©Ù„ید Keywords[fi]=Phone;Dialer;Dial;Call;Keypad;puhelin;puhelu;puhelut;soitto;soita;valintanäppäimistö Keywords[fr]=Téléphone;Composeur;Composer;Appeler;Pavé numérique Keywords[fr_CA]=Téléphone;Composeur;Appel;Clavier Keywords[gd]=Phone;Dialer;Dial;Call;Keypad;Fòn;Daitheal;Gairm;Iuchraichean Keywords[gl]=Teléfono;Marcador;Dial;Chamar;Teclado Keywords[he]=טלפון;חייגן;חוגה;התקשרות;להתקשר;שיחה;לחצני×;לוח Keywords[hr]=Telefon;BiraÄ;Biraj;Poziv;Tipkovnica Keywords[hu]=Telefon;Tárcsázó;Hívás Keywords[id]=Telepon;Panggilan;Keypad Keywords[is]=Sími;Talnaborð;Hringja Keywords[it]=Telefono;Chiamata;Telefonata Keywords[ja]=Phone;Dialer;Dial;Call;Keypad;電話;通話;呼ã³å‡ºã—;キーパッド; Keywords[km]=ទូរសáŸáž–្ទ;បន្ទះលáŸáž;ចុច;ហៅ;បន្ទះ Keywords[ko]=Phone;ì „í™”;Dialer;Dial;다ì´ì–¼;Call;통화;Keypad;키패드 Keywords[lv]=TÄlrunis;CiparnÄ«ca;Zvans;Telefons; Keywords[ms]=Telefon;Pendail;Dail;Panggilan;PadKekunci Keywords[nb]=Telefon;Ringefunksjon;Ring;Samtale;Tastatur Keywords[nl]=Phone;Dialer;Dial;Call;Keypad;Telefoon;Kiezen;Bellen;Toetsenblok Keywords[oc]=Telefòn;Composador;Composer;Sonar;Pavat numeric Keywords[pa]=ਫੋਨ;ਡਾਇਲਰ;ਡਾਇਲ;ਕਾਲ;ਕੀਪੈਡ Keywords[pl]=Telefon;Dzwonić Keywords[pt]=Telefone;Marcadorr;Marcar;Chamar;Teclado Keywords[pt_BR]=Telefone;Discador;Discar;Chamada;Teclado Keywords[ro]=Telefon;Dialer;Formează;Apel;Tastatură Keywords[ru]=Телефон;Ðомеронабиратель;Ðаберите;Вызов;Клавиатура Keywords[sl]=Telefon;Klicalnik;Klic;Tipkovnica Keywords[sr]=Телефон;Бирање;Звање;Позови Keywords[sv]=Telefon;Uppringare;Ring;Knappsats Keywords[tr]=Telefon;Çevirici;Çevirme;Arama;TuÅŸtakımı Keywords[ug]=تÛÙ„ÛÙون ;Dialer;Dial;چاقىرىش;Keypad Keywords[uk]=Phone;Dialer;Dial;Call;Keypad;телефон;набираннÑ;номер;дзвінок;виклик;кнопки Keywords[zh_TW]=Phone;Dialer;Dial;Call;Keypad;手機;通話;撥打; Exec=dialer-app %u Terminal=false Icon=/usr/share/dialer-app/assets/dialer-app.png MimeType=x-scheme-handler/contact;x-scheme-handler/call X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Single-Instance=true X-Ubuntu-Default-Department-ID=accessories X-Ubuntu-Splash-Show-Header=true X-Screenshot=/usr/share/dialer-app/assets/dialer-app-screenshot.png ./tests/scope-harness/applications/webbrowser-app.desktop0000644000015600001650000000111712676763577024063 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Name=Browser GenericName=Web Browser Comment=Browse the World Wide Web Keywords=Internet;WWW;Browser;Web;Explorer Type=Application Icon=/usr/share/webbrowser-app/webbrowser-app.png Exec=webbrowser-app %u Terminal=false Categories=Network;WebBrowser; MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https; X-Ubuntu-Touch=true X-Ubuntu-Gettext-Domain=webbrowser-app X-Ubuntu-Single-Instance=true X-Ubuntu-Default-Department-ID=web-browsers X-Screenshot=/usr/share/webbrowser-app/screenshot.png X-Ubuntu-Splash-Color=#FFFFFF ./tests/scope-harness/fake_click/0000755000015600001650000000000012676763577017116 5ustar jenkinsjenkins./tests/scope-harness/fake_click/info/0000755000015600001650000000000012676763577020051 5ustar jenkinsjenkins./tests/scope-harness/fake_click/info/com.ubuntu.developer.webapps.webapp-amazon0000644000015600001650000000112012676763577030251 0ustar jenkinsjenkins{ "_directory": "/custom/click/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon", "_removable": 1, "architecture": "all", "description": "Amazon (webapp version)", "framework": "ubuntu-sdk-14.10-dev2", "hooks": { "webapp-amazon": { "apparmor": "webapp-amazon.json", "desktop": "webapp-amazon.desktop" } }, "installed-size": "20", "maintainer": "Webapps Team ", "name": "com.ubuntu.developer.webapps.webapp-amazon", "title": "webapp-amazon", "version": "1.0.10" } ./tests/scope-harness/fake_click/info/com.ubuntu.dropping-letters0000644000015600001650000000141512676763577025375 0ustar jenkinsjenkins{ "_directory": "/custom/click/.click/users/@all/com.ubuntu.dropping-letters", "_removable": 1, "description": "Dropping Letters game", "framework": "ubuntu-sdk-14.10-qml-dev3", "hooks": { "dropping-letters": { "apparmor": "apparmor/dropping-letters.json", "desktop": "dropping-letters.desktop" } }, "icon": "dropping-letters.png", "installed-size": "2310", "maintainer": "Ubuntu App Cats ", "name": "com.ubuntu.dropping-letters", "title": "dropping-letters", "version": "0.1.2.2.67", "x-source": { "vcs-bzr": "lp:dropping-letters", "vcs-bzr-revno": "67" }, "x-test": { "autopilot": "dropping_letters_app" } } ./tests/scope-harness/fake_click/click0000755000015600001650000000154012676763577020131 0ustar jenkinsjenkins#!/bin/sh # # Copyright (C) 2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set -ex if [ "${1}" = "info" ]; then if [ -z "${2}" ]; then echo "Usage: click info [options] PATH" echo "" echo "click: error: need file name" fi cat `dirname ${0}`/info/${2} else /usr/bin/click ${@} fi ./tests/scope-harness/fake_responses/0000755000015600001650000000000012676763577020052 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/software-center-agent/0000755000015600001650000000000012676763577024256 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/software-center-agent/api/0000755000015600001650000000000012676763577025027 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/software-center-agent/api/2.0/0000755000015600001650000000000012676763577025326 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/software-center-agent/api/2.0/click/0000755000015600001650000000000012676763577026413 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/software-center-agent/api/2.0/click/purchases/0000755000015600001650000000000012676763577030410 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/software-center-agent/api/2.0/click/purchases/index.json0000644000015600001650000000021112676763577032404 0ustar jenkinsjenkins[ { "state": "Complete", "package_name": "com.example.fake", "open_id": "https://login.ubuntu.com/+openid/fakeuser" } ] ./tests/scope-harness/fake_responses/click-package-index/0000755000015600001650000000000012676763577023635 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/click-package-index/api/0000755000015600001650000000000012676763577024406 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/click-package-index/api/v1/0000755000015600001650000000000012676763577024734 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/click-package-index/api/v1/index.json0000644000015600001650000004532612676763577026750 0ustar jenkinsjenkins{ "_embedded": { "clickindex:department": [ { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/social-networking" } }, "name": "Social Networking", "slug": "social-networking" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/travel-local" } }, "name": "Travel & Local", "slug": "travel-local" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/reference" } }, "name": "Reference", "slug": "reference" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/food-drink" } }, "name": "Food & Drink", "slug": "food-drink" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/communication" } }, "name": "Communication", "slug": "communication" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/accessories" } }, "name": "Utilities", "slug": "accessories" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/science-engineering" } }, "name": "Science & Engineering", "slug": "science-engineering" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/personalisation" } }, "name": "Personalisation", "slug": "personalisation" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/education" } }, "name": "Education", "slug": "education" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/productivity" } }, "name": "Productivity", "slug": "productivity" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/entertainment" } }, "name": "Entertainment", "slug": "entertainment" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/sports" } }, "name": "Sports", "slug": "sports" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/health-fitness" } }, "name": "Health & Fitness", "slug": "health-fitness" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/music-audio" } }, "name": "Music & Audio", "slug": "music-audio" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/weather" } }, "name": "Weather", "slug": "weather" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/shopping" } }, "name": "Shopping", "slug": "shopping" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/finance" } }, "name": "Finance", "slug": "finance" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/business" } }, "name": "Business", "slug": "business" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/media-video" } }, "name": "Media & Video", "slug": "media-video" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/universal-accessaccessibility" } }, "name": "Universal Access/Accessibility", "slug": "universal-accessaccessibility" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/news-magazines" } }, "name": "News & Magazines", "slug": "news-magazines" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/graphics" } }, "name": "Graphics", "slug": "graphics" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/lifestyle" } }, "name": "Lifestyle", "slug": "lifestyle" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/medical" } }, "name": "Medical", "slug": "medical" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/developer-tools" } }, "name": "Developer Tools", "slug": "developer-tools" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/games" } }, "name": "Games", "slug": "games" }, { "has_children": false, "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/books-comics" } }, "name": "Books & Comics", "slug": "books-comics" } ], "clickindex:highlight": [ { "_embedded": { "clickindex:package": [ { "publisher": "Zhang Boren", "name": "com.ubuntu.developer.bobo1993324.udropcabin", "title": "uDropCabin", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/logo.png", "price": 0, "content": "application", "ratings_average": 4.17, "version": "0.2.1", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.bobo1993324.udropcabin" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Ubuntu Core App Developers", "name": "com.ubuntu.docviewer", "title": "Document Viewer", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/docviewer_32.png", "price": 0, "content": "application", "ratings_average": 4, "version": "0.3.92", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.docviewer" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Michael Zanetti", "name": "com.ubuntu.developer.mzanetti.wheretheissat", "title": "WhereTheIssAt", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/wheretheissat.png", "price": 0, "content": "application", "ratings_average": 5, "version": "0.4.0", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mzanetti.wheretheissat" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Michael Zanetti", "name": "com.ubuntu.developer.mzanetti.ubuntu-authenticator", "title": "Authenticator", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/ubuntu-authenticator256.png", "price": 0, "content": "application", "ratings_average": 4.72, "version": "0.10.0", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mzanetti.ubuntu-authenticator" } }, "architecture": [ "amd64", "armhf" ], "prices": {} }, { "publisher": "Michael Zanetti", "name": "com.ubuntu.developer.mzanetti.tagger", "title": "Tagger", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/11/tagger256.png", "price": 0, "content": "application", "ratings_average": 3.64, "version": "0.9.0.0", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mzanetti.tagger" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Dennis O'Flaherty", "name": "com.ubuntu.developer.doflah.realtai", "title": "Réaltaí", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/04/realtai256.png", "price": 0, "content": "application", "ratings_average": 4.6, "version": "0.6.3", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.doflah.realtai" } }, "architecture": [ "armhf" ], "prices": {} } ] }, "slug": "top-apps", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/highlights/top-apps" } }, "description": "", "name": "Top apps" }, { "_embedded": { "clickindex:package": [ { "publisher": "Michael Zanetti", "name": "com.ubuntu.developer.mzanetti.machines-vs-machines", "title": "Machines vs. Machines", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/01/mvm-game-icon256.png", "price": 0, "content": "application", "ratings_average": 4.97, "version": "1.2.0", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mzanetti.machines-vs-machines" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Riccardo Padovani", "name": "com.ubuntu.developer.rpadovani.100balls", "title": "100balls", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/100balls_256.png", "price": 0, "content": "application", "ratings_average": 4.57, "version": "0.4.1", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.rpadovani.100balls" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Robert Ancell", "name": "com.ubuntu.developer.robert-ancell.dotty", "title": "Dotty", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/12/dotty.png", "price": 0, "content": "application", "ratings_average": 4.2, "version": "8", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.andrew-hayzen.volleyball2d" } }, "architecture": [ "all" ], "prices": {} }, { "publisher": "Filippo Scognamiglio", "name": "com.ubuntu.developer.flscogna.ubuntu-netwalk", "title": "Ubuntu Netwalk", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/03/ubuntuNetwalkicon256.png", "price": 0, "content": "application", "ratings_average": 4.92, "version": "0.9.2", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.flscogna.ubuntu-netwalk" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Ken VanDine", "name": "com.ubuntu.developer.ken-vandine.pathwind", "title": "PathWind", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/pathwind.png", "price": 0, "content": "application", "ratings_average": 4.38, "version": "0.2.9", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.ken-vandine.pathwind" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Oliver Grawert", "name": "com.ubuntu.developer.ogra.speed-pool-king", "title": "Speed Billiards", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/icon_e2OQD6l.png", "price": 0, "content": "application", "ratings_average": 5, "version": "0.1", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.ogra.speed-pool-king" } }, "architecture": [ "all" ], "prices": {} } ] }, "slug": "our-favorite-games", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/highlights/our-favorite-games" } }, "description": "", "name": "Our favorite games" }, { "_embedded": { "clickindex:package": [ { "publisher": "Canonical", "name": "com.canonical.scopes.wikinear", "title": "Nearby Articles Scope", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/store-icon_cDUSB9o.png", "price": 0, "content": "scope", "ratings_average": 5, "version": "1.0.9", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.canonical.scopes.wikinear" } }, "architecture": [ "armhf" ], "prices": {} } ] }, "slug": "travel-apps", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/highlights/travel-apps" } }, "description": "", "name": "Travel apps" }, { "_embedded": { "clickindex:package": [ { "publisher": "Ubuntu Core App Developers", "name": "com.ubuntu.telegram", "title": "Telegram", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/icon_Gah5OG4.png", "price": 0, "content": "application", "ratings_average": 4.52, "version": "1.0.6.90", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.telegram" } }, "architecture": [ "armhf" ], "prices": {} } ] }, "slug": "app-of-the-week", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/highlights/app-of-the-week" } }, "description": "", "name": "App of the week" } ] }, "_links": { "search": { "title": "Search", "href": "[FAKE_SERVER_BASE]/api/v1/search{?q}", "templated": true }, "clickindex:departments": { "href": "[FAKE_SERVER_BASE]/api/v1/departments", "title": "Departments" }, "clickindex:highlight": { "title": "Highlight", "href": "[FAKE_SERVER_BASE]/api/v1/highlights/{slug}", "templated": true }, "curies": [ { "href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", "name": "clickindex", "templated": true } ], "self": { "href": "[FAKE_SERVER_BASE]/api/v1" }, "clickindex:highlights": { "href": "[FAKE_SERVER_BASE]/api/v1/highlights", "title": "Highlights" }, "clickindex:package": { "title": "Package", "href": "[FAKE_SERVER_BASE]/api/v1/package/{name}", "templated": true }, "clickindex:department": { "title": "Department", "href": "[FAKE_SERVER_BASE]/api/v1/departments/{slug}", "templated": true } } } ./tests/scope-harness/fake_responses/click-package-index/api/v1/departments/0000755000015600001650000000000012676763577027262 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/click-package-index/api/v1/departments/games/0000755000015600001650000000000012676763577030356 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/click-package-index/api/v1/departments/games/index.json0000644000015600001650000001271712676763577032370 0ustar jenkinsjenkins{ "_embedded": { "clickindex:package": [ { "publisher": "Robert Ancell", "name": "com.ubuntu.developer.robert-ancell.yatzy", "title": "Yatzy", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/yatzy_rqoPQxE.png", "price": 0, "content": "application", "ratings_average": 5, "version": "8", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.robert-ancell.yatzy" } }, "architecture": [ "all" ], "prices": {} }, { "publisher": "Victor Tuson Palau", "name": "com.ubuntu.developer.vtuson.lego", "title": "uBrick scope", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/01/256.png", "price": 0, "content": "scope", "ratings_average": 5, "version": "0.3", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.vtuson.lego" } }, "architecture": [ "armhf" ], "prices": {} } ], "clickindex:highlight": [ { "_embedded": { "clickindex:package": [ { "publisher": "Daniel Beck", "name": "com.ubuntu.developer.danielbeck.greenmahjong", "title": "Green Mahjong", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/greenmahjong.png", "price": 0, "content": "application", "ratings_average": 3.5, "version": "2.2.1", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.danielbeck.greenmahjong" } }, "architecture": [ "all" ], "prices": {} }, { "publisher": "Riccardo Padovani", "name": "com.ubuntu.developer.rpadovani.100balls", "title": "100balls", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/100balls_256.png", "price": 0, "content": "application", "ratings_average": 4.57, "version": "0.4.1", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.rpadovani.100balls" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Robert Ancell", "name": "com.ubuntu.developer.robert-ancell.dotty", "title": "Dotty", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/12/dotty.png", "price": 0, "content": "application", "ratings_average": 4.2, "version": "8", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.robert-ancell.dotty" } }, "architecture": [ "all" ], "prices": {} }, { "publisher": "Filippo Scognamiglio", "name": "com.ubuntu.developer.flscogna.ubuntu-netwalk", "title": "Ubuntu Netwalk", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/03/ubuntuNetwalkicon256.png", "price": 0, "content": "application", "ratings_average": 4.92, "version": "0.9.2", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.flscogna.ubuntu-netwalk" } }, "architecture": [ "armhf" ], "prices": {} }, { "publisher": "Ken VanDine", "name": "com.ubuntu.developer.ken-vandine.pathwind", "title": "PathWind", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/pathwind.png", "price": 0, "content": "application", "ratings_average": 4.38, "version": "0.2.9", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.ken-vandine.pathwind" } }, "architecture": [ "armhf" ], "prices": {} } ] }, "slug": "top-games", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/highlights/top-games" } }, "description": "", "name": "Top Games" } ] }, "has_children": false, "_links": { "curies": [ { "href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", "name": "clickindex", "templated": true } ], "self": { "href": "[FAKE_SERVER_BASE]/api/v1/departments/games" }, "collection": { "href": "[FAKE_SERVER_BASE]/api/v1/departments" } }, "name": "Games", "slug": "games" } ./tests/scope-harness/fake_responses/click-package-index/api/v1/departments/communication/0000755000015600001650000000000012676763577032127 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/click-package-index/api/v1/departments/communication/index.json0000644000015600001650000010671412676763577034142 0ustar jenkinsjenkins{"_embedded": {"clickindex:package": [{"publisher": "Salih EMIN", "name": "ubuntu-gr-irchat.salih-emin", "ratings_average": 5.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/Ubuntu-gr-IRChat_ImpNFEQ.png", "price": 0.0, "title": "Ubuntu Greece IRC", "content": "application", "alias": null, "version": "0.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/ubuntu-gr-irchat.salih-emin"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Cheng Lu", "name": "com.ubuntu.developer.dawndiy.smartqq", "ratings_average": 5.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/01/SmartQQ.png", "price": 0.0, "title": "SmartQQ", "content": "application", "alias": null, "version": "0.1.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.dawndiy.smartqq"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Arnaud Ober", "name": "com.ubuntu.developer.arnaudober.ubuntouch-fr", "ratings_average": 5.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/iconubuntouch.png", "price": 0.0, "title": "UbunTouch-fr", "content": "application", "alias": null, "version": "1.5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.arnaudober.ubuntouch-fr"}}, "architecture": ["all"], "prices": {}}, {"publisher": "JoshStrobl", "name": "com.ubuntu.developer.joshua-strobl.onedrive", "ratings_average": 5.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/onedrive.png", "price": 0.0, "title": "OneDrive", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.joshua-strobl.onedrive"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Oliver Grawert", "name": "com.ubuntu.developer.ogra.freenode-net", "ratings_average": 5.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/freenode-net.png", "price": 0.0, "title": "freenode webchat", "content": "application", "alias": null, "version": "0.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.ogra.freenode-net"}}, "architecture": ["all"], "prices": {}}, {"publisher": "JoshStrobl", "name": "com.ubuntu.developer.joshua-strobl.linkedin", "ratings_average": 4.33, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/linkedin.png", "price": 0.0, "title": "LinkedIn", "content": "application", "alias": null, "version": "1.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.joshua-strobl.linkedin"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Ubuntu Core App Developers", "name": "com.ubuntu.telegram", "ratings_average": 4.23, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/icon_Gah5OG4.png", "price": 0.0, "title": "Telegram", "content": "application", "alias": null, "version": "1.1.2.95", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.telegram"}}, "architecture": ["armhf"], "prices": {}}, {"publisher": "Daniel Chapman", "name": "dekko.dekkoproject", "ratings_average": 4.12, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/dekko256.png", "price": 0.0, "title": "Dekko (Beta)", "content": "application", "alias": null, "version": "0.5.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/dekko.dekkoproject"}}, "architecture": ["armhf"], "prices": {}}, {"publisher": "Caixabank, S.A.", "name": "es.lacaixa.mobile.lacaixa", "ratings_average": 4.01, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/LaCaixa256.png", "price": 0.0, "title": "La Caixa", "content": "application", "alias": null, "version": "1.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/es.lacaixa.mobile.lacaixa"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Mateo Salta", "name": "com.ubuntu.developer.mateosalta.inbox", "ratings_average": 4.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/inbox_GAHwR1u.png", "price": 0.0, "title": "Inbox", "content": "application", "alias": null, "version": "0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.mateosalta.inbox"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Filip Dobrock\u00fd", "name": "com.ubuntu.developer.filip-dobrocky.gagger", "ratings_average": 4.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/Gagger.png", "price": 0.0, "title": "Gagger", "content": "application", "alias": null, "version": "0.3", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.filip-dobrocky.gagger"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Dimitri John Ledkov", "name": "net.launchpad.click-webapps.xkcd", "ratings_average": 3.83, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/09/xkcd-64.png", "price": 0.0, "title": "xkcd", "content": "application", "alias": null, "version": "5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/net.launchpad.click-webapps.xkcd"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Zhang Boren", "name": "com.ubuntu.developer.bobo1993324.udropcabin", "ratings_average": 3.73, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/logo.png", "price": 0.0, "title": "uDropCabin", "content": "application", "alias": null, "version": "0.2.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.bobo1993324.udropcabin"}}, "architecture": ["armhf"], "prices": {}}, {"publisher": "JoshStrobl", "name": "com.ubuntu.developer.joshua-strobl.outlook", "ratings_average": 3.7, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/outlook.png", "price": 0.0, "title": "Outlook", "content": "application", "alias": null, "version": "1.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.joshua-strobl.outlook"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-googlemaps", "ratings_average": 3.5, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/webapp-googlemaps.png", "price": 0.0, "title": "Google Maps", "content": "application", "alias": null, "version": "0.3", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-googlemaps"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-gmail", "ratings_average": 3.5, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/gmail.png", "price": 0.0, "title": "Gmail", "content": "application", "alias": null, "version": "1.0.25", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-gmail"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Penk Chen", "name": "com.ubuntu.developer.penk.slatekit-shell", "ratings_average": 3.33, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/11/3R7EYJQ.png", "price": 0.0, "title": "SlateKit Shell", "content": "application", "alias": null, "version": "0.3", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.penk.slatekit-shell"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-twitter", "ratings_average": 3.33, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/twitter.png", "price": 0.0, "title": "Twitter", "content": "application", "alias": null, "version": "1.0.21", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-twitter"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Robert Schroll", "name": "com.ubuntu.developer.rschroll.gmail", "ratings_average": 3.2, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/icon.svg_ANpfikZ.png", "price": 0.0, "title": "Gmail Scope", "content": "scope", "alias": null, "version": "1.0.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.rschroll.gmail"}}, "architecture": ["armhf"], "prices": {}}, {"publisher": "Mark", "name": "copy.markcortbass", "ratings_average": 3.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/Copy.png", "price": 0.0, "title": "Copy", "content": "application", "alias": null, "version": "0.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/copy.markcortbass"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Corasaaa", "name": "com.ubuntu.developer.corasaaa.webogram", "ratings_average": 3.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/9cdc1009-46b2-4f09-b65c-7a1e8af8d7af.png", "price": 0.0, "title": "Webogram", "content": "application", "alias": null, "version": "0.5.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.corasaaa.webogram"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Chris Wayne", "name": "com.ubuntu.developer.cwayne18.weibo", "ratings_average": 3.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/11/app.png", "price": 0.0, "title": "Weibo", "content": "application", "alias": null, "version": "2.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.cwayne18.weibo"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Dimitri John Ledkov", "name": "net.launchpad.click-webapps.tumblr", "ratings_average": 2.67, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/09/tumblr-small_1.png", "price": 0.0, "title": "tumblr", "content": "application", "alias": null, "version": "5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/net.launchpad.click-webapps.tumblr"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-facebook", "ratings_average": 2.5, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/facebook.png", "price": 0.0, "title": "Facebook", "content": "application", "alias": null, "version": "1.0.26", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-facebook"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Adnane Belmadiaf", "name": "com.ubuntu.developer.daker.airbnb", "ratings_average": 2.33, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/airbnb.jpg.png", "price": 0.0, "title": "Airbnb", "content": "application", "alias": null, "version": "0.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.daker.airbnb"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Mario Kamenjak", "name": "com.ubuntu.developer.mkamenjak77.bluedragonbrowser", "ratings_average": 2.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/12/blue-dragon.png", "price": 0.0, "title": "bluedragonbrowser", "content": "application", "alias": null, "version": "1.27", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.mkamenjak77.bluedragonbrowser"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Jos\u00e9 Antonio Rey Cama", "name": "com.joseeantonior.ircpuzzles", "ratings_average": 2.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/Untitled.png", "price": 0.0, "title": "IRCPuzzles", "content": "application", "alias": null, "version": "0.3", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.joseeantonior.ircpuzzles"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Oliver Grawert", "name": "com.ubuntu.developer.ogra.kiwi-irc", "ratings_average": 2.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/kiwiirc.png", "price": 0.0, "title": "Kiwi IRC", "content": "application", "alias": null, "version": "0.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.ogra.kiwi-irc"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Pablo Seminario", "name": "com.ubuntu.developer.pabluk.eventunity", "ratings_average": 2.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/04/eventunity-256_1.png", "price": 0.0, "title": "Eventunity", "content": "application", "alias": null, "version": "0.3.4", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.pabluk.eventunity"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Fabio Colella", "name": "com.ubuntu.developer.fcole90.feedspot", "ratings_average": 2.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/11/feedspot.png", "price": 0.0, "title": "Feedspot", "content": "application", "alias": null, "version": "0.5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.fcole90.feedspot"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Adnane Belmadiaf", "name": "com.ubuntu.developer.daker.imgur", "ratings_average": 2.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/11/imgur.png", "price": 0.0, "title": "imgur", "content": "application", "alias": null, "version": "0.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.daker.imgur"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-ebay", "ratings_average": 2.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/ebay__1_.png", "price": 0.0, "title": "eBay", "content": "application", "alias": null, "version": "1.0.12", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-ebay"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-amazon", "ratings_average": 2.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/amazon.png", "price": 0.0, "title": "Amazon", "content": "application", "alias": null, "version": "1.0.10", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-amazon"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Ken Crissey", "name": "yahoomail.kcrissey", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/YahooMail_J1fhAxU.png", "price": 0.0, "title": "Yahoo! Mail", "content": "application", "alias": null, "version": "2.0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/yahoomail.kcrissey"}}, "architecture": ["all"], "prices": {}}, {"publisher": "dromas", "name": "com.ubuntu.developer.dromas.dtelegram", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/DTelegram_256.png", "price": 0.0, "title": "DTelegram", "content": "application", "alias": null, "version": "0.1.3", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.dromas.dtelegram"}}, "architecture": ["all"], "prices": {}}, {"publisher": "MetallicaMust", "name": "com.ubuntu.developer.metallicamust.swisslinux", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/swisslinux.png", "price": 0.0, "title": "Swisslinux.org", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.metallicamust.swisslinux"}}, "architecture": ["all"], "prices": {}}, {"publisher": "MetallicaMust", "name": "com.ubuntu.developer.metallicamust.free-fr", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/07/free-fr.png", "price": 0.0, "title": "Free.fr", "content": "application", "alias": null, "version": "1.0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.metallicamust.free-fr"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Michael Vogt", "name": "com.ubuntu.developer.mvo.fastmail", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/app-256.png", "price": 0.0, "title": "Fastmail.fm", "content": "application", "alias": null, "version": "0.1.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.mvo.fastmail"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Adri\u00e1n Arroyo Calle", "name": "com.ubuntu.developer.adrian-arroyocalle.social-share", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/social-share256_1.png", "price": 0.0, "title": "Social Share", "content": "application", "alias": null, "version": "1.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.adrian-arroyocalle.social-share"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Romain", "name": "fr.skimbo.skimbou", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/09/logo_skimbo_64.png", "price": 0.0, "title": "SkimboU", "content": "application", "alias": null, "version": "1.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/fr.skimbo.skimbou"}}, "architecture": ["armhf"], "prices": {}}, {"publisher": "Dimitri John Ledkov", "name": "net.launchpad.click-webapps.ingress", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/09/ingress-64.png", "price": 0.0, "title": "Ingress Intel", "content": "application", "alias": null, "version": "6", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/net.launchpad.click-webapps.ingress"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Dimitri John Ledkov", "name": "net.launchpad.click-webapps.lloyds-bank-uk", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/09/lloyds-bank-uk-64.png", "price": 0.0, "title": "Lloyds Bank", "content": "application", "alias": null, "version": "5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/net.launchpad.click-webapps.lloyds-bank-uk"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Andrew Starr-Bochicchio", "name": "com.ubuntu.developer.andrewsomething.stackbrowser", "ratings_average": 1.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/03/stackbrowser256x256.png", "price": 0.0, "title": "StackBrowser", "content": "application", "alias": null, "version": "0.3.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.andrewsomething.stackbrowser"}}, "architecture": ["all"], "prices": {}}, {"publisher": "xkindenx", "name": "yandexmail.alexander", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/yandex_mail.jpg.png", "price": 0.0, "title": "Yandex Mail", "content": "application", "alias": null, "version": "0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/yandexmail.alexander"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Cerberus", "name": "linuxzasveforum.cerberus", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/LinuxZaSve.png", "price": 0.0, "title": "Linux za sve forum", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/linuxzasveforum.cerberus"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Jesus Gascon", "name": "webpodemosaragon.jesusgascon", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/Podemos_Aragon_NEP0Cjy.png", "price": 0.0, "title": "Web Podemos Aragon", "content": "application", "alias": null, "version": "0.5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/webpodemosaragon.jesusgascon"}}, "architecture": ["all"], "prices": {}}, {"publisher": "xkindenx", "name": "meetme.alexander", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/Meetme.jpg.png", "price": 0.0, "title": "MeetMe", "content": "application", "alias": null, "version": "0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/meetme.alexander"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Rafa\u0142 T", "name": "bramka.elf8dkk", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/icon_wT5jn2t.png", "price": 0.0, "title": "Bramka SMS", "content": "application", "alias": null, "version": "0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/bramka.elf8dkk"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Nikolay", "name": "ble-gateway.devicehive", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/dh_PuOak77.png", "price": 0.0, "title": "DeviceHive BLE Gateway", "content": "application", "alias": null, "version": "1.0.5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/ble-gateway.devicehive"}}, "architecture": ["amd64", "armhf"], "prices": {}}, {"publisher": "Carla Sella", "name": "ubuntuit.carla-sella", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/256x256.png", "price": 0.0, "title": "ubuntu-it", "content": "scope", "alias": null, "version": "0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/ubuntuit.carla-sella"}}, "architecture": ["armhf"], "prices": {}}, {"publisher": "MetallicaMust", "name": "com.ubuntu.developer.metallicamust.swisscows", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/swisscows.png", "price": 0.0, "title": "Swisscows", "content": "application", "alias": null, "version": "2.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.metallicamust.swisscows"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Victor gonzalez", "name": "com.ubuntu.developer.victorbq.canalbq", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/bq_blanco_con_fondo_negro.png", "price": 0.0, "title": "canalbq", "content": "scope", "alias": null, "version": "0.4", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.victorbq.canalbq"}}, "architecture": ["armhf"], "prices": {}}, {"publisher": "Oliver Grawert", "name": "chatroom.ogra", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/icon_VHc7E6S.png", "price": 0.0, "title": "Snappy Chatroom", "content": "application", "alias": null, "version": "0.1-4", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/chatroom.ogra"}}, "architecture": ["amd64", "armhf"], "prices": {}}, {"publisher": "WillCooke", "name": "mosquitto-armhf.willcooke", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/mosquitto-50x35_2CV74KO.png", "price": 0.0, "title": "mosquitto-armhf", "content": "application", "alias": null, "version": "1.3.5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/mosquitto-armhf.willcooke"}}, "architecture": ["armhf"], "prices": {}}, {"publisher": "WillCooke", "name": "mosquitto-v135.willcooke", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/mosquitto-50x35_ul7Xabo.png", "price": 0.0, "title": "mosquitto version 1.3.5", "content": "application", "alias": null, "version": "1.3.5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/mosquitto-v135.willcooke"}}, "architecture": ["amd64"], "prices": {}}, {"publisher": "Michael Vogt", "name": "pastebinit.mvo", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/01/icon.svg_hzXtaYT.png", "price": 0.0, "title": "pastebinit", "content": "application", "alias": null, "version": "1.4.0.0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/pastebinit.mvo"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Christian Dywan", "name": "com.ubuntu.developer.kalikiana.ello", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/ello256.png", "price": 0.0, "title": "ello", "content": "application", "alias": null, "version": "0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.kalikiana.ello"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Christian Dywan", "name": "com.ubuntu.developer.kalikiana.irccloud", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/irccloud256.png", "price": 0.0, "title": "IRCCloud", "content": "application", "alias": null, "version": "0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.kalikiana.irccloud"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Mitchell Reese", "name": "com.ubuntu.developer.mreese.smsglobal", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/SMSGlobal-256.png", "price": 0.0, "title": "SMS Global", "content": "application", "alias": null, "version": "0.11", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.mreese.smsglobal"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Deepak Kumar Dalai", "name": "com.ubuntu.developer.deepakkumardalai.way2sms", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/way2sms256.png", "price": 0.0, "title": "way2sms", "content": "application", "alias": null, "version": "0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.deepakkumardalai.way2sms"}}, "architecture": ["all"], "prices": {}}, {"publisher": "MetallicaMust", "name": "com.ubuntu.developer.metallicamust.swisspost", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/swisspost.png", "price": 0.0, "title": "Swiss Post", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.metallicamust.swisspost"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Bogdan Cuza", "name": "com.ubuntu.developer.boghison.torrentsmd", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/icon.png", "price": 0.0, "title": "TorrentsMD", "content": "application", "alias": null, "version": "1.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.boghison.torrentsmd"}}, "architecture": ["all"], "prices": {}}, {"publisher": "MetallicaMust", "name": "com.ubuntu.developer.metallicamust.medinex", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/07/medinex.png", "price": 0.0, "title": "Medinex", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.metallicamust.medinex"}}, "architecture": ["all"], "prices": {}}, {"publisher": "MetallicaMust", "name": "com.ubuntu.developer.metallicamust.orange", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/07/orange.png", "price": 0.0, "title": "Orange.ch", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.metallicamust.orange"}}, "architecture": ["all"], "prices": {}}, {"publisher": "MetallicaMust", "name": "com.ubuntu.developer.metallicamust.sunrise", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/07/sunrise.png", "price": 0.0, "title": "Sunrise.ch", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.metallicamust.sunrise"}}, "architecture": ["all"], "prices": {}}, {"publisher": "MetallicaMust", "name": "com.ubuntu.developer.metallicamust.swisscom", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/07/swisscom.png", "price": 0.0, "title": "Swisscom", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.metallicamust.swisscom"}}, "architecture": ["all"], "prices": {}}, {"publisher": "JoshStrobl", "name": "com.ubuntu.developer.joshua-strobl.recode", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/recode.png", "price": 0.0, "title": "Re/Code", "content": "application", "alias": null, "version": "1.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.joshua-strobl.recode"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Alaak", "name": "com.ubuntu.developer.alaak.uberbag", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/04/uberpocket_app_icon256x256.png", "price": 0.0, "title": "Uberbag", "content": "application", "alias": null, "version": "5.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.alaak.uberbag"}}, "architecture": ["all"], "prices": {}}, {"publisher": "HostArmor.com", "name": "com.ubuntu.developer.geistschatten.social-groovy", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/02/SG-256.png", "price": 0.0, "title": "Social Groovy", "content": "application", "alias": null, "version": "0.5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.geistschatten.social-groovy"}}, "architecture": ["all"], "prices": {}}, {"publisher": "L\u00e9o-Paul COUTURIER", "name": "com.ubuntu.developer.leopaul3.hashtalk", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/02/HashTalk.png", "price": 0.0, "title": "HashTalk", "content": "application", "alias": null, "version": "0.3", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.leopaul3.hashtalk"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Nathan Haines", "name": "com.ubuntu.developer.nhaines.nintendo-miiverse", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/07/nintendo-miiverse-256.png", "price": 0.0, "title": "Miiverse", "content": "application", "alias": null, "version": "1.6", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.nhaines.nintendo-miiverse"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Chris Wayne", "name": "com.ubuntu.developer.cwayne18.baidu", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/11/app_1.png", "price": 0.0, "title": "Baidu", "content": "application", "alias": null, "version": "2.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.cwayne18.baidu"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Chris Wayne", "name": "com.ubuntu.developer.cwayne18.renren", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/11/app_2.png", "price": 0.0, "title": "Renren", "content": "application", "alias": null, "version": "2.0", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.cwayne18.renren"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Rick Spencer", "name": "com.ubuntu.developer.rick-rickspencer3.reddgur", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/icon_1.png", "price": 0.0, "title": "reddgur", "content": "application", "alias": null, "version": "0.5", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.rick-rickspencer3.reddgur"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Yuuto Tokunaga", "name": "com.ubuntu.developer.yuntan.aztter", "ratings_average": 0.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/09/aztter64.png", "price": 0.0, "title": "Aztter", "content": "application", "alias": null, "version": "0.183", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.yuntan.aztter"}}, "architecture": ["armhf"], "prices": {}}], "clickindex:highlight": [{"_embedded": {"clickindex:package": [{"publisher": "Adri\u00e1n Arroyo Calle", "name": "com.ubuntu.developer.adrian-arroyocalle.wikipedia-mobile", "ratings_average": 3.0, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/04/wikipedia.png", "price": 0.0, "title": "Wikipedia", "content": "application", "alias": null, "version": "2.0.1", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.adrian-arroyocalle.wikipedia-mobile"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-twitter", "ratings_average": 3.33, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/twitter.png", "price": 0.0, "title": "Twitter", "content": "application", "alias": null, "version": "1.0.21", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-twitter"}}, "architecture": ["all"], "prices": {}}, {"publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-facebook", "ratings_average": 2.5, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/facebook.png", "price": 0.0, "title": "Facebook", "content": "application", "alias": null, "version": "1.0.26", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-facebook"}}, "architecture": ["all"], "prices": {}}, {"publisher": "JoshStrobl", "name": "com.ubuntu.developer.joshua-strobl.linkedin", "ratings_average": 4.33, "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/linkedin.png", "price": 0.0, "title": "LinkedIn", "content": "application", "alias": null, "version": "1.2", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.joshua-strobl.linkedin"}}, "architecture": ["all"], "prices": {}}]}, "slug": "top-internet", "_links": {"self": {"href": "https://search.apps.ubuntu.com/api/v1/highlights/top-internet"}}, "description": "", "name": "Top Internet Apps"}]}, "has_children": false, "_links": {"curies": [{"href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", "name": "clickindex", "templated": true}], "self": {"href": "https://search.apps.ubuntu.com/api/v1/departments/communication"}, "collection": {"href": "https://search.apps.ubuntu.com/api/v1/departments"}}, "name": "Communication", "slug": "communication"}./tests/scope-harness/fake_responses/click-package-index/api/v1/package/0000755000015600001650000000000012676763577026327 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000016300000000000011603 Lustar rootroot./tests/scope-harness/fake_responses/click-package-index/api/v1/package/com.ubuntu.developer.webapps.webapp-amazon./tests/scope-harness/fake_responses/click-package-index/api/v1/package/com.ubuntu.developer.webapps0000644000015600001650000000364212676763577034002 0ustar jenkinsjenkins{"whitelist_country_codes": [], "status": "Published", "last_updated": "2014-09-19T16:28:38.679062Z", "video_embedded_html_urls": [], "screenshot_url": null, "video_urls": [], "framework": ["ubuntu-sdk-14.10-dev2"], "terms_of_service": "", "keywords": [], "stores": {"ubuntu": {"status": "Published"}}, "id": 93, "title": "Amazon", "support_url": "mailto:webapps@lists.launchpad.net", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/amazon.png", "binary_filesize": 8528, "download_url": "https://public.apps.ubuntu.com/download/com.ubuntu.developer.webapps/webapp-amazon/com.ubuntu.developer.webapps.webapp-amazon_1.0.10_all.click", "allow_unauthenticated": false, "content": "application", "developer_name": "Ubuntu Webapps", "version": "1.0.10", "_links": {"curies": [{"href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", "name": "clickindex", "templated": true}], "self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.developer.webapps.webapp-amazon"}}, "company_name": "Canonical Group Limited", "department": ["communication"], "screenshot_urls": [], "website": "", "description": "Amazon (webapp version)\nAmazon (webapp version)", "click_framework": ["ubuntu-sdk-14.10-dev2"], "price": 0.0, "translations": {}, "blacklist_country_codes": [], "date_published": "2013-09-17T18:32:23.998044Z", "alias": null, "prices": {}, "icon_urls": {"256": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/09/amazon.png", "64": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/09/amazon.png"}, "download_sha512": "d9d9e361d5a0c132d5858f1e329715ff295e53789486ea187fa3ede5663ef8534d73464e0caee40817666dd13c49d3c76cab40d05fff018e87ad65f46d01640f", "publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-amazon", "license": "GNU GPL v3", "changelog": "", "click_version": "0.1", "ratings_average": 2.0, "architecture": ["all"]}./tests/scope-harness/fake_responses/click-package-index/api/v1/package/com.ubuntu.dropping-letters0000644000015600001650000000420712676763577033655 0ustar jenkinsjenkins{"whitelist_country_codes": [], "status": "Published", "last_updated": "2014-10-10T17:07:31.723999Z", "video_embedded_html_urls": [], "screenshot_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/drop1_screen.png", "video_urls": [], "framework": ["ubuntu-sdk-14.10-qml-dev3"], "terms_of_service": "", "keywords": ["games", "puzzle"], "stores": {"ubuntu": {"status": "Published"}}, "id": 128, "title": "Dropping Letters", "support_url": "mailto:popey@ubuntu.com", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/dl.png", "binary_filesize": 1037014, "download_url": "https://public.apps.ubuntu.com/download/com.ubuntu/dropping-letters/com.ubuntu.dropping-letters_0.1.2.2.67_all.click", "allow_unauthenticated": false, "content": "application", "developer_name": "Ubuntu Core App Developers", "version": "0.1.2.2.67", "_links": {"curies": [{"href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", "name": "clickindex", "templated": true}], "self": {"href": "https://search.apps.ubuntu.com/api/v1/package/com.ubuntu.dropping-letters"}}, "company_name": "", "department": ["games"], "screenshot_urls": ["https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/drop1_screen.png", "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/drop2_screen.png", "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/drop3_screen.png"], "website": "http://launchpad.net/dropping-letters", "description": "Dropping Letters game\nDropping Letters game", "click_framework": ["ubuntu-sdk-14.10-qml-dev3"], "price": 0.0, "translations": {}, "blacklist_country_codes": [], "date_published": "2013-10-11T15:40:44.335571Z", "alias": null, "prices": {}, "icon_urls": {"256": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/dl.png"}, "download_sha512": "76833760e0d38fc9e3d8ee1a4e8d0b7600586757b030278c94259aae2700c9e3cd4d172d2defc7a7094e8c9a702c3de9b504ab52a23f6b61b1112399705b9bba", "publisher": "Ubuntu Core App Developers", "name": "com.ubuntu.dropping-letters", "license": "GNU GPL v3", "changelog": "", "click_version": "0.1", "ratings_average": 4.5, "architecture": ["all"]}./tests/scope-harness/fake_responses/click-package-index/api/v1/package/com.ubuntu.calendar0000644000015600001650000000567212676763577032133 0ustar jenkinsjenkins{ "whitelist_country_codes": [], "status": "Published", "last_updated": "2015-03-05T14:25:13.226958Z", "video_embedded_html_urls": [], "screenshot_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/10/device-2014-10-02-155500.png", "video_urls": [], "framework": [ "ubuntu-sdk-14.10-qml" ], "terms_of_service": "", "keywords": [ "calendar", "time", "task", "plan", "journal", "diary" ], "stores": { "china-mobile": { "status": "Published" }, "ninjablocks": { "status": "Published" }, "ubuntu": { "status": "Published" } }, "id": 156, "title": "Calendar", "support_url": "mailto:ubuntu-touch-coreapps@lists.launchpad.net", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/02/calendar-app_32.png", "binary_filesize": 149943, "download_url": "https://public.apps.ubuntu.com/download/com.ubuntu/calendar/com.ubuntu.calendar_0.4.572_all.click", "allow_unauthenticated": false, "content": "application", "developer_name": "Ubuntu Core App Developers", "version": "0.4.600", "_links": { "curies": [ { "href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", "name": "clickindex", "templated": true } ], "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.calendar" } }, "company_name": "", "department": [ "accessories" ], "screenshot_urls": [ "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/10/device-2014-10-02-155500.png", "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/10/device-2014-10-02-155757.png", "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/10/device-2014-10-02-155855.png" ], "website": "https://launchpad.net/ubuntu-calendar-app", "description": "The Calendar application for Ubuntu devices lets you organise your life your way by month, week or daily diary\nIt’s about the task and the context; use the calendar app as a todo list, a diary, a planner, a journal, a life log; and the calendar will behave how you need it to.", "click_framework": [ "ubuntu-sdk-14.10-qml" ], "price": 0, "translations": {}, "blacklist_country_codes": [], "date_published": "2013-10-16T19:19:29.080512Z", "alias": null, "prices": {}, "icon_urls": { "256": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/02/calendar-app_32.png" }, "download_sha512": "2fa658804e63da1869037cd9bc74b792875404f03b6c6449271ae5244688ff42a4524712ccb748ab9004344cccddd59063f3d3a4af899a3cc6f64ddc1a27072b", "publisher": "Ubuntu Core App Developers", "name": "com.ubuntu.calendar", "license": "GNU GPL v3", "changelog": "* Improved week view\r\n* Events appear faster\r\n* Events can be created by longpress on timeline\r\n* Events can be moved via drag/drop", "click_version": "0.1", "ratings_average": 4, "architecture": [ "all" ] } ./tests/scope-harness/fake_responses/click-package-index/api/v1/search0000644000015600001650000000420712676763577026127 0ustar jenkinsjenkins{ "_embedded": { "clickindex:package": [ { "publisher": "Ubuntu Core App Developers", "name": "com.ubuntu.calendar", "title": "Calendar", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/02/calendar-app_32.png", "price": 0, "content": "application", "ratings_average": 4, "version": "0.4.600", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.calendar" } }, "architecture": [ "all" ], "prices": {} }, { "publisher": "Sonrise Software", "name": "com.ubuntu.developer.mdspencer.project-dashboard", "title": "Project Dashboard", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/03/project-dashboard-256.png", "price": 0, "content": "application", "ratings_average": 3.71, "version": "0.5.3", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mdspencer.project-dashboard" } }, "architecture": [ "all" ], "prices": {} }, { "publisher": "Canonical Group Limited", "name": "com.ubuntu.developer.webapps.webapp-googlecalendar", "title": "Google Calendar", "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/04/icon256_1.png", "price": 0, "content": "application", "ratings_average": 3.5, "version": "1.0.12", "_links": { "self": { "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.webapps.webapp-googlecalendar" } }, "architecture": [ "all" ], "prices": {} } ] }, "_links": { "curies": [ { "href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", "name": "clickindex", "templated": true } ], "self": { "href": "[FAKE_SERVER_BASE]/api/v1/search?q=Calendar" } } } ./tests/scope-harness/fake_responses/ratings-and-reviews/0000755000015600001650000000000012676763577023743 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/ratings-and-reviews/click/0000755000015600001650000000000012676763577025030 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/ratings-and-reviews/click/api/0000755000015600001650000000000012676763577025601 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0/0000755000015600001650000000000012676763577026077 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0/reviews/0000755000015600001650000000000012676763577027563 5ustar jenkinsjenkins./tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0/reviews/index.json0000644000015600001650000001606712676763577031577 0ustar jenkinsjenkins[ { "rating": 2, "hide": false, "package_name": "com.ubuntu.calendar", "language": "es", "reviewer_username": "hnXrswe", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "El diseño es simple pero aceptable, pero no sincroniza con google. Nexus 4.", "date_deleted": null, "summary": "Review", "version": "0.4.585", "date_created": "2015-02-23T16:46:16.314Z", "reviewer_displayname": "javipt", "id": 1201 }, { "rating": 2, "hide": false, "package_name": "com.ubuntu.calendar", "language": "es", "reviewer_username": "oskitar1981", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "No tiene opciones de ningún tipo, la interfaz es sosa y aunque se supone que sincroniza con el calendario de Google, no lo hace. Demasiado básico todavía.", "date_deleted": null, "summary": "Review", "version": "0.4.572", "date_created": "2015-02-09T20:57:47.883Z", "reviewer_displayname": "Oscar Oria Garcia", "id": 1070 }, { "rating": 2, "hide": false, "package_name": "com.ubuntu.calendar", "language": "pt", "reviewer_username": "QYDWhfn", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Looks great, but it crashes and I can't make it sync my calendars", "date_deleted": null, "summary": "Review", "version": "0.4.572", "date_created": "2015-02-07T17:44:12.755Z", "reviewer_displayname": "Tiago Carrondo", "id": 1038 }, { "rating": 5, "hide": false, "package_name": "com.ubuntu.calendar", "language": "pt", "reviewer_username": "ivofernandes12", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Começa a ganhar uma boa interface. Finalmente uma aplicação de calendário para o Ubuntu :)", "date_deleted": null, "summary": "Review", "version": "0.4.565", "date_created": "2015-01-16T01:37:25.293Z", "reviewer_displayname": "Ivo Xavier", "id": 884 }, { "rating": 5, "hide": false, "package_name": "com.ubuntu.calendar", "language": "en", "reviewer_username": "8EycA4w", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Nice", "date_deleted": null, "summary": "Review", "version": "0.4.565", "date_created": "2015-01-15T21:48:30.144Z", "reviewer_displayname": "Julien Robert", "id": 878 }, { "rating": 4, "hide": false, "package_name": "com.ubuntu.calendar", "language": "de", "reviewer_username": "J3AETAH", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Nice calenar With google integration. Unfortunately only first calendar from google integrated, but it's going a good way. I like it aldo visually.", "date_deleted": null, "summary": "Review", "version": "0.4.558", "date_created": "2015-01-02T10:59:22.144Z", "reviewer_displayname": "TuxFux.ch", "id": 810 }, { "rating": 5, "hide": false, "package_name": "com.ubuntu.calendar", "language": "en", "reviewer_username": "dwN4TEP", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Great calendar, but I miss option to show agenda for more than 1 week ahead.", "date_deleted": null, "summary": "Review", "version": "0.4.558", "date_created": "2014-12-19T10:39:12.192Z", "reviewer_displayname": "Jakob Kenda", "id": 777 }, { "rating": 5, "hide": false, "package_name": "com.ubuntu.calendar", "language": "uk", "reviewer_username": "3ERwyc8", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Awesome! Thanks for Google Calendar integration.", "date_deleted": null, "summary": "Review", "version": "0.4.558", "date_created": "2014-12-04T11:26:20.971Z", "reviewer_displayname": "Anton Maminov", "id": 747 }, { "rating": 4, "hide": false, "package_name": "com.ubuntu.calendar", "language": "en", "reviewer_username": "davmor2", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Great app not perfect but it is getting there. ", "date_deleted": null, "summary": "Review", "version": "0.4.531", "date_created": "2014-10-28T19:54:50.176Z", "reviewer_displayname": "Dave Morley", "id": 632 }, { "rating": 3, "hide": false, "package_name": "com.ubuntu.calendar", "language": "en", "reviewer_username": "ausbuscon", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "My work uses office365 to host email calendar etching. Is there anyway I can access the calendar. Should it be in on line accounts? \n\nThis appeared is great start", "date_deleted": null, "summary": "Review", "version": "0.4.502", "date_created": "2014-10-18T22:11:55.915Z", "reviewer_displayname": "Steven Austen", "id": 609 }, { "rating": 5, "hide": false, "package_name": "com.ubuntu.calendar", "language": "en", "reviewer_username": "mJKEXkF", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Pretty good", "date_deleted": null, "summary": "Review", "version": "0.4.489", "date_created": "2014-10-07T12:57:30.387Z", "reviewer_displayname": "Neo", "id": 574 }, { "rating": 5, "hide": false, "package_name": "com.ubuntu.calendar", "language": "zh_CN", "reviewer_username": "pNRKwp7", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "没有农历", "date_deleted": null, "summary": "Review", "version": "0.4.462", "date_created": "2014-09-21T17:57:07.401Z", "reviewer_displayname": "8010252@163.com", "id": 474 }, { "rating": 5, "hide": false, "package_name": "com.ubuntu.calendar", "language": "en", "reviewer_username": "temisaev", "usefulness_total": 0, "usefulness_favorable": 0, "review_text": "Great calendar.", "date_deleted": null, "summary": "Review", "version": "0.4.246", "date_created": "2014-05-13T10:59:23.405Z", "reviewer_displayname": "Arthom Isaev", "id": 50 } ] ./tests/scope-harness/CMakeLists.txt0000644000015600001650000000032712676763577017605 0ustar jenkinsjenkins add_custom_target(test-integration-harness COMMAND PYTHONPATH=$ENV{PYTHONPATH}:../${COMMON_PYTHONPATH} SCOPE_DIR=${CMAKE_BINARY_DIR}/scope ${CMAKE_CURRENT_SOURCE_DIR}/run-harness.sh ${CMAKE_CURRENT_SOURCE_DIR} ) ./tests/autopilot/0000755000015600001650000000000012676763577014311 5ustar jenkinsjenkins./tests/autopilot/unityclickscope/0000755000015600001650000000000012676763577017521 5ustar jenkinsjenkins./tests/autopilot/unityclickscope/__init__.py0000644000015600001650000001232412676763577021634 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013, 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import logging import autopilot.logging from autopilot import exceptions, introspection from autopilot.matchers import Eventually from testtools.matchers import Equals from unity8.shell.emulators import dash logger = logging.getLogger(__name__) class GenericScopeView(dash.GenericScopeView): # XXX workaround for bug http://pad.lv/1350532, which causes the unity8 # GenericScopeView class to match with the ClickScope and StoreScope. # --elopio - 2014-11-28 @classmethod def validate_dbus_object(cls, path, state): return False class ClickScope(GenericScopeView): """Autopilot helper for the click scope.""" @classmethod def validate_dbus_object(cls, path, state): name = introspection.get_classname_from_path(path) if name == b'GenericScopeView': if state['objectName'][1] == 'clickscope': return True return False @autopilot.logging.log_action(logger.info) def go_to_store(self): """Open the applicatmions store. :return: The store Scope View. """ # XXX The click_scope_item in unity should take care of swiping if the # item is not visible. # TODO file a bug. --elopio - 2014-11-28 self._swipe_to_bottom() self.click_scope_item('store', 'Ubuntu Store') store_scope = self.get_root_instance().select_single( 'GenericScopeView', objectName='dashTempScopeItem') store_scope.isCurrent.wait_for(True) # The store scope slides from the right. Wait until it has finished # sliding before trying to press the search button Eventually(Equals(0)).match(lambda: store_scope.globalRect.y) return store_scope def _swipe_to_bottom(self): list_view = self.select_single( 'ListViewWithPageHeader', objectName='categoryListView') list_view.swipe_to_bottom() class StoreScope(GenericScopeView): """Autopilot helper for the generic scope view of the store.""" @classmethod def validate_dbus_object(cls, path, state): name = introspection.get_classname_from_path(path) if name == b'GenericScopeView': # This is probably not a good objectName. It can cause more than # one custom proxy object to match. --elopio - 2014-11-28 if state['objectName'][1] == 'dashTempScopeItem': return True return False @autopilot.logging.log_action(logger.info) def enter_search_query(self, query): # XXX the enter_search_query of the dash provided by unity doesn't # work for the temp store scope. # TODO file a bug. --elopio - 2014-11-28 search_button = self.wait_select_single( objectName='search_action_button') self.pointing_device.click_object(search_button) headerContainer = self.wait_select_single( objectName='headerContainer') headerContainer.contentY.wait_for(0) search_text_field = self.wait_select_single( objectName='searchTextField') search_text_field.write(query) self.get_root_instance().wait_select_single( objectName='processingIndicator').visible.wait_for(False) def open_preview(self, category, app_name): # XXX the open preview method provided by unity doesn't wait for the # preview to be ready. # TODO file a bug. --elopio - 2014-11-28 preview = super(StoreScope, self).open_preview(category, app_name) self.get_root_instance().select_single( objectName='processingIndicator').visible.wait_for(False) return preview class Preview(dash.Preview): """Autopilot helper for the application preview.""" def get_details(self): """Return the details of the application whose preview is open.""" header_widget = self.select_single('PreviewWidget', objectName='hdr') title_label = header_widget.select_single( 'Label', objectName='titleLabel') subtitle_label = header_widget.select_single( 'Label', objectName='subtitleLabel') return dict( title=title_label.text, subtitle=subtitle_label.text) def install(self): install_button = self.select_single( 'PreviewActionButton', objectName='buttoninstall_click') self.pointing_device.click_object(install_button) def is_progress_bar_visible(self): try: self.select_single('ProgressBar', objectName='progressBar') return True except exceptions.StateNotFoundError: return False ./tests/autopilot/unityclickscope/fake_servers.py0000644000015600001650000002205012676763577022551 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013, 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import copy import http.server import json import logging import os import tempfile import urllib.parse import autopilot.logging logger = logging.getLogger(__name__) class BaseFakeHTTPRequestHandler(http.server.BaseHTTPRequestHandler): def send_json_reply(self, code, reply_json): self.send_response(code) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(reply_json.encode()) def send_file(self, file_path, send_body=True, extra_headers={}): with open(file_path) as file_: data = file_.read() self.send_response(200) self.send_header('Content-Length', str(len(data))) for key, value in extra_headers.items(): self.send_header(key, value) self.end_headers() if send_body: self.wfile.write(data) def send_file_headers(self, file_path, extra_headers={}): self.send_file(file_path, send_body=False, extra_headers=extra_headers) class FakeSearchServer(http.server.HTTPServer, object): def __init__(self, server_address): super(FakeSearchServer, self).__init__( server_address, FakeSearchRequestHandler) class FakeSearchRequestHandler(BaseFakeHTTPRequestHandler): _SEARCH_PATH = '/api/v1/search' _FAKE_DELTA_RESULTS = { "publisher": "Rodney Dawes", "_links": { "self": { "href": ( "{U1_SEARCH_BASE_URL}api/v1/" "package/com.ubuntu.developer.dobey.delta-web") } }, "architecture": ["all"], "title": "Delta", "icon_url": "http://TODO/delta-web.png", "price": 0.0, "name": "com.ubuntu.developer.dobey.delta-web" } _FAKE_SEARCH_RESPONSE_DICT = { "_embedded": { "clickindex:package": [_FAKE_DELTA_RESULTS] }, } _FAKE_DELTA_DETAILS_DICT = { "website": "", "description": ( "A simple web app for Delta.\n" "Check in, view flight schedules, and book flights, on the Delta " "mobile web site."), "price": 0.0, "date_published": "2014-05-03T15:30:16.431511Z", "framework": ["ubuntu-sdk-14.04-qml-dev1"], "terms_of_service": "", "prices": {"USD": 0.0}, "screenshot_url": "http://TODO/delta-web-checkin.png", "category": "Utility", "publisher": "Rodney Dawes", "name": "com.ubuntu.developer.dobey.delta-web", "license": "GNU GPL v3", "title": "Delta", "support_url": "https://launchpad.net/~dobey", "icon_url": "http://TODO/delta-web.png", "changelog": "", "binary_filesize": 23728, "download_url": ( '{DOWNLOAD_BASE_URL}download/delta-dummy.click'), "click_version": "0.1", "developer_name": "Rodney Dawes", "version": "1.0.1", "company_name": "", "keywords": [ "delta", "airlines", "flight", "status", "schedules" ], "department": ["Accessories"], "screenshot_urls": [ "http://TODO/delta-web-checkin.png", "https://TODO/delta-web-main.png" ], "architecture": ["all"] } _FAKE_DETAILS = { 'com.ubuntu.developer.dobey.delta-web': _FAKE_DELTA_DETAILS_DICT } _FAKE_INDEX = { "_embedded": { "clickindex:department": [ { "has_children": False, "_links": { "self": { "href": ( "{U1_SEARCH_BASE_URL}api/v1/departments/" "accessories") } }, "name": "Accessories", "slug": "accesories" }, ], "clickindex:highlight": [ { "_embedded": { "clickindex:package": [_FAKE_DELTA_RESULTS], }, "_links": { "self": { "href": ( "{U1_SEARCH_BASE_URL}api/v1/highlights/" "travel-apps") } }, "name": "Travel apps", "slug": "travel-apps" }, ], }, '_links': { 'clickindex:department': { 'href': "{U1_SEARCH_BASE_URL}api/v1/departments/{slug}", 'templated': True, 'title': 'Department' }, 'clickindex:departments': { 'href': "{U1_SEARCH_BASE_URL}api/v1/departments", 'title': 'Departments' }, 'clickindex:highlight': { 'href': '{U1_SEARCH_BASE_URL}api/v1/highlights/{slug}', 'templated': True, 'title': 'Highlight' }, 'clickindex:highlights': { 'href': '{U1_SEARCH_BASE_URL}api/v1/highlights', 'title': 'Highlights' }, 'clickindex:package': { 'href': '{U1_SEARCH_BASE_URL}api/v1/package/{name}', 'templated': True, 'title': 'Package' }, 'curies': [ { 'href': '{U1_SEARCH_BASE_URL}docs/v1/relations.html{#rel}', 'name': 'clickindex', 'templated': True } ], 'search': { 'href': '{U1_SEARCH_BASE_URL}api/v1/search{?q}', 'templated': True, 'title': 'Search' }, 'self': { 'href': '{U1_SEARCH_BASE_URL}api/v1' } } } def do_GET(self): parsed_path = urllib.parse.urlparse(self.path) if parsed_path.path.startswith(self._SEARCH_PATH): self.send_search_results() elif parsed_path.path.startswith('/extra/'): self.send_file(parsed_path.path[1:]) elif parsed_path.path.startswith('/api/v1/package/'): package = parsed_path.path[16:] self.send_package_details(package) elif parsed_path.path.startswith('/api/v1'): self.send_index() else: logger.error( 'Not implemented path in fake server: {}'.format(self.path)) raise NotImplementedError(self.path) @autopilot.logging.log_action(logger.debug) def send_search_results(self): results = json.dumps(self._FAKE_SEARCH_RESPONSE_DICT) self.send_json_reply(200, results) @autopilot.logging.log_action(logger.debug) def send_package_details(self, package): details = copy.deepcopy(self._FAKE_DETAILS.get(package, None)) if details is not None: details['download_url'] = details['download_url'].format( DOWNLOAD_BASE_URL=os.environ.get('DOWNLOAD_BASE_URL')) self.send_json_reply( 200, json.dumps(details)) else: raise NotImplementedError(package) @autopilot.logging.log_action(logger.debug) def send_index(self): self.send_json_reply(200, json.dumps(self._FAKE_INDEX)) class FakeDownloadServer(http.server.HTTPServer, object): def __init__(self, server_address): super(FakeDownloadServer, self).__init__( server_address, FakeDownloadRequestHandler) class FakeDownloadRequestHandler(BaseFakeHTTPRequestHandler): def do_HEAD(self): parsed_path = urllib.parse.urlparse(self.path) if parsed_path.path.startswith('/download/'): self._send_dummy_file_headers(parsed_path.path[10:]) else: raise NotImplementedError(self.path) def _send_dummy_file_headers(self, name): dummy_file_path = self._make_dummy_file(name) self.send_file_headers( dummy_file_path, extra_headers={'X-Click-Token': 'dummy'}) os.remove(dummy_file_path) def _make_dummy_file(self, name): dummy_file = tempfile.NamedTemporaryFile( prefix='dummy', suffix='.click', delete=False) dummy_file.write('Dummy click file.'.encode()) dummy_file.write(name.encode()) dummy_file.close() return dummy_file.name ./tests/autopilot/unityclickscope/fixture_setup.py0000644000015600001650000000232412676763577023002 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013, 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Set up and clean up fixtures for the Unity Click Scope acceptance tests.""" import fake_server_fixture from unityclickscope import fake_servers class FakeSearchServerRunning(fake_server_fixture.FakeServerFixture): def __init__(self): super(FakeSearchServerRunning, self).__init__( fake_servers.FakeSearchServer) class FakeDownloadServerRunning(fake_server_fixture.FakeServerFixture): def __init__(self): super(FakeDownloadServerRunning, self).__init__( fake_servers.FakeDownloadServer) ./tests/autopilot/unityclickscope/test_fixture_setup.py0000644000015600001650000000371312676763577024044 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import http.client import urllib.parse import testscenarios import testtools from unityclickscope import fixture_setup class FakeServerRunningTestCase( testscenarios.TestWithScenarios, testtools.TestCase): scenarios = [ ('fake search server', dict( fixture=fixture_setup.FakeSearchServerRunning, request_method='GET', request_path='/api/v1/search')), ('fake download server', dict( fixture=fixture_setup.FakeDownloadServerRunning, request_method='HEAD', request_path='/download/dummy.click')) ] def test_server_should_start_and_stop(self): fake_server = self.fixture() self.addCleanup(self._assert_server_not_running) self.useFixture(fake_server) self.netloc = urllib.parse.urlparse(fake_server.url).netloc connection = http.client.HTTPConnection(self.netloc) self.addCleanup(connection.close) self._do_request(connection) self.assertEqual(connection.getresponse().status, 200) def _assert_server_not_running(self): connection = http.client.HTTPConnection(self.netloc) self.assertRaises(Exception, self._do_request, connection) def _do_request(self, connection): connection.request(self.request_method, self.request_path) ./tests/autopilot/unityclickscope/test_click_scope.py0000644000015600001650000002275212676763577023420 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013, 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import logging import os import shutil import subprocess import time import dbusmock import fixtures from autopilot.matchers import Eventually from testtools.matchers import Equals from unity8.shell import tests as unity_tests from unityclickscope import credentials, fake_services, fixture_setup logger = logging.getLogger(__name__) class ClickScopeException(Exception): """Exception raised when there's a problem with the scope.""" class BaseClickScopeTestCase( dbusmock.DBusTestCase, unity_tests.DashBaseTestCase): scenarios = [ ('Desktop Nexus 4', dict( app_width=768, app_height=1280, grid_unit_px=18)), ('Desktop Nexus 10', dict(app_width=2560, app_height=1600, grid_unit_px=20)) ] def setUp(self): # We use fake servers by default because the current Jenkins # configuration does not let us override the variables. if os.environ.get('DOWNLOAD_BASE_URL', 'fake') == 'fake': self._use_fake_download_server() self._use_fake_download_service() if os.environ.get('U1_SEARCH_BASE_URL', 'fake') == 'fake': self._use_fake_server() self.useFixture(fixtures.EnvironmentVariable('U1_DEBUG', newvalue='1')) self._restart_scopes() super(BaseClickScopeTestCase, self).setUp() self.scope = self.dash.get_scope('clickscope') def _use_fake_server(self): fake_search_server = fixture_setup.FakeSearchServerRunning() self.useFixture(fake_search_server) self.useFixture(fixtures.EnvironmentVariable( 'U1_SEARCH_BASE_URL', newvalue=fake_search_server.url)) def _use_fake_download_server(self): fake_download_server = fixture_setup.FakeDownloadServerRunning() self.useFixture(fake_download_server) self.useFixture(fixtures.EnvironmentVariable( 'DOWNLOAD_BASE_URL', newvalue=fake_download_server.url)) def _use_fake_download_service(self): self._spawn_fake_downloader() dbus_connection = self.get_dbus(system_bus=False) fake_download_service = fake_services.FakeDownloadService( dbus_connection) fake_download_service.add_method( 'getAllDownloadsWithMetadata', return_value=[]) download_object_path = '/com/canonical/applications/download/test' fake_download_service.add_method( 'createDownload', return_value=download_object_path) fake_download_service.add_download_object(download_object_path) def _spawn_fake_downloader(self): download_manager_mock = self.spawn_server( 'com.canonical.applications.Downloader', '/', 'com.canonical.applications.DownloadManager', system_bus=False, stdout=subprocess.PIPE) self.addCleanup(self._terminate_process, download_manager_mock) def _terminate_process(self, dbus_mock): dbus_mock.terminate() dbus_mock.wait() def _restart_scopes(self): logging.info('Restarting click scope.') scope_runner_path = self._get_scoperunner_path() apps_scope_ini_path, store_scope_ini_path = self._get_scopes_ini_path() os.system('pkill -f -9 clickscope.ini') os.system('pkill -f -9 clickstore.ini') os.system('{scoperunner} "" {appsscope} &'.format( scoperunner=scope_runner_path, appsscope=apps_scope_ini_path)) os.system('{scoperunner} "" {storescope} &'.format( scoperunner=scope_runner_path, storescope=store_scope_ini_path)) def _get_scoperunner_path(self): return os.path.join( self._get_installed_unity_scopes_lib_dir(), 'scoperunner') def _get_installed_unity_scopes_lib_dir(self): arch = subprocess.check_output( ["dpkg-architecture", "-qDEB_HOST_MULTIARCH"], universal_newlines=True).strip() return os.path.join('/usr/lib/{}/'.format(arch), 'unity-scopes') def _get_scopes_ini_path(self): build_dir = os.environ.get('BUILD_DIR', None) if build_dir is not None: return self._get_built_scopes_ini_path(build_dir) else: app_scope_ini_path = os.path.join( self._get_installed_unity_scopes_lib_dir(), 'clickapps', 'clickscope.ini') store_scope_ini_path = os.path.join( self._get_installed_unity_scopes_lib_dir(), 'clickstore', 'com.canonical.scopes.clickstore.ini') return app_scope_ini_path, store_scope_ini_path def _get_built_scopes_ini_path(self, build_dir): # The ini and the so files need to be on the same directory. # We copy them to a temp directory. temp_dir_fixture = fixtures.TempDir() self.useFixture(temp_dir_fixture) built_apps_scope_ini = os.path.join( build_dir, 'data', 'clickscope.ini') temp_apps_scope_dir = os.path.join(temp_dir_fixture.path, 'clickapps') os.mkdir(temp_apps_scope_dir) shutil.copy(built_apps_scope_ini, temp_apps_scope_dir) built_apps_scope = os.path.join( build_dir, 'scope', 'clickapps', 'scope.so') shutil.copy(built_apps_scope, temp_apps_scope_dir) built_store_scope_ini = os.path.join( build_dir, 'data', 'com.canonical.scopes.clickstore.ini') temp_store_scope_dir = os.path.join( temp_dir_fixture.path, 'clickstore') os.mkdir(temp_store_scope_dir) shutil.copy(built_store_scope_ini, temp_store_scope_dir) built_store_scope = os.path.join( build_dir, 'scope', 'clickstore', 'com.canonical.scopes.clickstore.so') shutil.copy(built_store_scope, temp_store_scope_dir) app_scope_ini_path = os.path.join( temp_apps_scope_dir, 'clickscope.ini') store_scope_ini_path = os.path.join( temp_store_scope_dir, 'com.canonical.scopes.clickstore.ini') return app_scope_ini_path, store_scope_ini_path def _unlock_screen(self): self.main_window.get_greeter().swipe() def open_scope(self): scope = self.dash.open_scope('clickscope') scope.isCurrent.wait_for(True) return scope class TestCaseWithHomeScopeOpen(BaseClickScopeTestCase): def test_open_scope_scrolling(self): scope = self.dash.open_scope('clickscope') self.assertThat(scope.isCurrent, Equals(True)) class BaseTestCaseWithStoreScopeOpen(BaseClickScopeTestCase): def setUp(self): super(BaseTestCaseWithStoreScopeOpen, self).setUp() app_scope = self.open_scope() self.scope = app_scope.go_to_store() class TestCaseWithStoreScopeOpen(BaseTestCaseWithStoreScopeOpen): def test_search_available_app(self): self.scope.enter_search_query('Delta') applications = self.scope.get_applications('appstore') self.assertThat(applications[0], Equals('Delta')) def test_open_app_preview(self): expected_details = dict( title='Delta', subtitle='Rodney Dawes') self.scope.enter_search_query('Delta') preview = self.scope.open_preview('appstore', 'Delta') details = preview.get_details() self.assertEqual(details, expected_details) def test_install_without_credentials(self): self.scope.enter_search_query('Delta') preview = self.scope.open_preview('appstore', 'Delta') preview.install() # XXX hacky way to check if the online accounts ui was opened. time.sleep(3) online_accounts_pid = subprocess.check_output( ['pgrep', '-f', 'online-accounts-ui'], universal_newlines=True).strip() subprocess.check_output(['kill', '-9', online_accounts_pid]) class ClickScopeTestCaseWithCredentials(BaseTestCaseWithStoreScopeOpen): def setUp(self): self.skipTest( 'We cannot add credentials yet because the keyring dialog will be ' 'opened prompting for a password. http://pad.lv/1338714') self.add_u1_credentials() super(ClickScopeTestCaseWithCredentials, self).setUp() self.scope.enter_search_query('Delta') self.preview = self.scope.open_preview('appstore', 'Delta') def add_u1_credentials(self): account_manager = credentials.AccountManager() account = account_manager.add_u1_credentials( 'dummy@example.com', 'name=Ubuntu+One+%40+bollo&' 'consumer_secret=*********&' 'token=**************&' 'consumer_key=*******&' 'token_secret=************') self.addCleanup(account_manager.delete_account, account) def test_install_with_credentials_must_start_download(self): self.assertFalse(self.preview.is_progress_bar_visible()) self.preview.install() self.assertThat( self.preview.is_progress_bar_visible, Eventually(Equals(True))) ./tests/autopilot/unityclickscope/credentials.py0000644000015600001650000000711212676763577022371 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import threading from gi.repository import Accounts, GLib, Signon class CredentialsException(Exception): """Exception for credentials problems.""" class AccountManager(object): def __init__(self): self._manager = Accounts.Manager() def _start_main_loop(self): self.error = None self._main_loop = GLib.MainLoop() self._main_loop_thread = threading.Thread( target=self._main_loop.run) self._main_loop_thread.start() def _join_main_loop(self): self._main_loop_thread.join() if self.error is not None: raise CredentialsException(self.error.message) def add_u1_credentials(self, user_name, password): self._start_main_loop() account = self._create_account() info = self._get_identity_info(user_name, password) identity = Signon.Identity.new() identity.store_credentials_with_info( info, self._set_credentials_id_to_account, account) self._join_main_loop() return account def _create_account(self): account = self._manager.create_account('ubuntuone') account.set_enabled(True) account.store(self._on_account_created, None) return account def _on_account_created(self, account, error, _): if error: self.error = error self._main_loop.quit() def _get_identity_info(self, user_name, password): info = Signon.IdentityInfo.new() info.set_username(user_name) info.set_caption(user_name) info.set_secret(password, True) return info def _set_credentials_id_to_account(self, identity, id, error, account): if error: self.error = error self._main_loop.quit() account.set_variant('CredentialsId', GLib.Variant('u', id)) account.store(self._process_session, None) def _process_session(self, account, error, _): if error: self.error = error self._main_loop.quit() account_service = Accounts.AccountService.new(account, None) auth_data = account_service.get_auth_data() identity = auth_data.get_credentials_id() method = auth_data.get_method() mechanism = auth_data.get_mechanism() session_data = auth_data.get_parameters() session = Signon.AuthSession.new(identity, method) session.process( session_data, mechanism, self._on_login_processed, None) def _on_login_processed(self, session, reply, error, userdata): if error: self.error = error self._main_loop.quit() def delete_account(self, account): self._start_main_loop() account.delete() account.store(self._on_account_deleted, None) self._join_main_loop() def _on_account_deleted(self, account, error, userdata): if error: self.error = error self._main_loop.quit() ./tests/autopilot/unityclickscope/fake_services.py0000644000015600001650000000431012676763577022702 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import dbus import dbusmock class FakeDownloadService(object): """Fake Download Service using a dbusmock interface.""" def __init__(self, dbus_connection): super(FakeDownloadService, self).__init__() self.dbus_connection = dbus_connection self.mock = self._get_mock_interface() def _get_mock_interface(self): return dbus.Interface( self.dbus_connection.get_object( 'com.canonical.applications.Downloader', '/'), dbusmock.MOCK_IFACE) def add_method(self, method, return_value): if method == 'getAllDownloadsWithMetadata': self._add_getAllDownloadsWithMetadata(return_value) elif method == 'createDownload': self._add_createDownload(return_value) else: raise NotImplementedError('Unknown method: {}'.format(method)) def _add_getAllDownloadsWithMetadata(self, return_value): self.mock.AddMethod( 'com.canonical.applications.DownloadManager', 'getAllDownloadsWithMetadata', 'ss', 'ao', 'ret = {}'.format(return_value)) def _add_createDownload(self, return_value): self.mock.AddMethod( 'com.canonical.applications.DownloadManager', 'createDownload', '(sssa{sv}a{ss})', 'o', 'ret = "{}"'.format(return_value)) def add_download_object(self, object_path): self.mock.AddObject( object_path, 'com.canonical.applications.Download', {}, [('start', '', '', '')]) ./tests/common/0000755000015600001650000000000012676763577013561 5ustar jenkinsjenkins./tests/common/__init__.py0000644000015600001650000000125412676763577015674 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ./tests/common/fake_server_fixture.py0000644000015600001650000000407512676763577020203 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013, 2014, 2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Base class for fake server fixtures.""" import logging import multiprocessing as mp import fixtures logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) class FakeServerFixture(fixtures.Fixture): def __init__(self, server_class, *server_args): super().__init__() self.server_class = server_class self.server_args = server_args def setUp(self): super().setUp() self._start_fake_server() def _server_process(self, queue, server_class): logger.info('Starting fake server: {}.'.format(server_class)) server_address = ('localhost', 0) fake_server = server_class(server_address, *self.server_args) fake_server.url = 'http://localhost:{}/'.format(fake_server.server_port) logger.info('Serving at port {}.'.format(fake_server.server_port)) queue.put(fake_server.url) fake_server.serve_forever() def _start_fake_server(self): queue = mp.Queue() server_process = mp.Process(target=self._server_process, args=(queue, self.server_class)) server_process.start() self.addCleanup(self._stop_fake_server, server_process) self.url = queue.get() def _stop_fake_server(self, process): logger.info('Stopping fake server: {}.'.format(self.server_class)) process.terminate() process.join() ./tests/CMakeLists.txt0000644000015600001650000000133712676763577015035 0ustar jenkinsjenkinsfind_package (PythonInterp) set (AUTOPILOT_DIR unityclickscope) set (COMMON_PYTHONPATH common) execute_process ( COMMAND python3 -c "from distutils import sysconfig; print(sysconfig.get_python_lib())" COMMAND sed -r -e "s|/usr/(local/)?||g" OUTPUT_VARIABLE PYTHON_PACKAGE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) install ( DIRECTORY autopilot/${AUTOPILOT_DIR} DESTINATION ${PYTHON_PACKAGE_DIR} ) add_custom_target(test-click-scope-autopilot-fake-servers COMMAND PYTHONPATH=$ENV{PYTHONPATH}:../${COMMON_PYTHONPATH} U1_SEARCH_BASE_URL=fake DOWNLOAD_BASE_URL=fake BUILD_DIR=${CMAKE_BINARY_DIR} autopilot3 run -v ${AUTOPILOT_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/autopilot ) add_subdirectory(scope-harness)./README0000644000015600001650000000112712676763577012010 0ustar jenkinsjenkinsReadme ====== unity-scope-click - Click Packages Scope for Unity -------------------------------------------------- This scope shows the list of installed applications, and the list of applications available to download from the appstore webservice. To authenticate downloads, this scope uses Ubuntu Online Accounts to get credentials for the logged in user. Since scopes are short lived, ubuntu-download-manager is used to do the actual download. You can find information on how to compile and run this program in the HACKING file, and you can find licensing information in the COPYING file. ./po/0000755000015600001650000000000012676763577011545 5ustar jenkinsjenkins./po/pl.po0000644000015600001650000001656112676763577012531 0ustar jenkinsjenkins# Polish translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-08-23 12:23+0000\n" "Last-Translator: Bartosz Kosiorek \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Soczewki" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplikacje" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Wydawca/Autor" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Sprzedawca" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Strona www" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakt" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licencja" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Aktualizacje" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Numer wersji" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Ostatnia aktualizacja" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Pierwsze wydanie" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Rozmiar" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Wersja" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "DARMOWA" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ ZAKUPIONA" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informacje" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Co nowego" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recenzje" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Błąd pobierania" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Pobieranie lub instalacja nie powiodÅ‚a siÄ™. Spróbuj ponownie." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Zamknij" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Błąd logowania" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "ProszÄ™ zalogować siÄ™ do konta Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Przejdź do kont użytkowników" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Otwórz" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Szukaj" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Anuluj zakup" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Odinstaluj" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Czy na pewno chcesz anulować zakup '${title}'? Aplikacja zostanie " "odinstalowana." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Wstecz" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Kontynuuj" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Polityka zwrotu i anulowania zakupu" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Po kupieniu aplikacji w Sklepie Ubuntu masz 15 minut, od momentu instalacji, " "na anulowanie zakupu. JeÅ›li przekroczysz ten czas, zalecamy bezpoÅ›redni " "kontakt z twórcÄ… aplikacji w celu zwrotu opÅ‚aty.\n" "Informacje kontaktowe z twórcÄ… znajdziesz na stronie danej aplikacji w " "Sklepie Ubuntu.\n" "PamiÄ™taj, że zakup aplikacji możesz anulować tylko raz." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Potwierdzenie" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Odinstalować ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Anuluj" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Potwiedź" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Zainstaluj" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bajt" msgstr[1] "{1} bajty" msgstr[2] "{1} bajtów" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Soczewka sÅ‚użąca do przeglÄ…dania zainstalowanych programów" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Wyszukaj aplikacje" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Sklep Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Pobierz wiÄ™cej aplikacji ze sklepu" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Szukaj \"%s\" w sklepie" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Pobierz wiÄ™cej aplikacji jak ta ze sklepu" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Wszystko" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Soczewka sÅ‚użąca do przeglÄ…dania Sklepu Ubuntu" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Przeszukaj sklep" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ ZAINSTALOWANA" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Dostepne" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u pozycja w sklepie Ubuntu" msgstr[1] "%u pozycje w sklepie Ubuntu" msgstr[2] "%u pozycji w sklepie Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Polecane" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Soczewka do wyszukiwania zainstalowanych aplikacji" #~ msgid "Scope for searching the click app store" #~ msgstr "Soczewka do przeszukiwania sklepu z aplikacjami" ./po/bs.po0000644000015600001650000001407212676763577012515 0ustar jenkinsjenkins# Bosnian translation for unity-scope-click # Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2016. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2016-02-24 23:32+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Bosnian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Opsezi" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplikacije" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "IzdavaÄ/proizvoÄ‘aÄ" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "ProdavaÄ" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Web stranica" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakt" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licenca" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Ažuriranja" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Broj verzije" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Posljednje ažurirano" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Prvo objavljivanje" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "VeliÄina" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Verzija" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "FREE" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ PURCHASED" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informacije" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Å ta ima novo" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recenzije" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "GreÅ¡ka u preuzimanju" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Preuzimanje ili instalacija neuspjela. Molimo pokuÅ¡ajte ponovo" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/pa.po0000644000015600001650000001662512676763577012517 0ustar jenkinsjenkins# Punjabi translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Punjabi \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "ਸਕੋਪ" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "à¨à¨ªà¨¸" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "ਪà©à¨°à¨•ਾਸ਼ਕ/ਸਿਰਜਣਹਾਰ" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "ਵਿਕਰੇਤਾ" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "ਵੈੱਬਸਾਈਟ" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "ਸੰਪਰਕ" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "ਲਾਇਸੰਸ" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "ਅੱਪਡੇਟਾਂ" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "ਵਰਜਨ ਨੰਬਰ" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "ਆਖਰੀ ਅੱਪਡੇਟ ਹੋਇਆ" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "ਪਹਿਲਾਂ ਰੀਲੀਜ਼ ਹੋਇਆ" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "ਅਕਾਰ" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "ਵਰਜਨ" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ਮà©à©žà¨¤" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ ਖਰੀਦਿਆ ਗਿਆ" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "ਜਾਣਕਾਰੀ" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "ਨਵਾਂ ਕੀ ਹੈ?" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "ਸਮੀਖਿਆ" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "ਡਾਊਨਲੋਡ ਖਾਮੀ" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "ਡਾਊਨਲੋਡ ਜਾਂ ਇੰਸਟਾਲ ਫ਼ੇਲà©à¨¹ ਹੋਇਆ। ਕਿਰਪਾ ਕਰਕੇ ਮà©à©œ ਕੋਸ਼ਿਸ ਕਰੋ।" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "ਬੰਦ" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "ਲਾਗਇਨ ਖਾਮੀ" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਉਬੰਤੂ ਵਨ ਖਾਤੇ ਵਿੱਚ ਲਾਗਇਨ ਕਰੋ।" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "ਖਾਤਿਆਂ ਤੇ ਜਾਓ" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "ਖੋਲà©à¨¹à©‹" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "ਖੋਜ" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "ਅਣਇੰਸਟਾਲ" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "ਪà©à¨¶à¨Ÿà©€" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "ਅਣਇੰਸਟਾਲ ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "ਰੱਦ" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "ਪà©à¨¸à¨¼à¨Ÿà©€" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "ਇੰਸਟਾਲ" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} ਬਾਈਟ" msgstr[1] "{1} ਬਾਈਟਾਂ" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "ਖੋਜੋ à¨à¨ª" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "ਉਬੰਤੂ ਸਟੋਰ" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "ਸਟੋਰ ਤੋਂ ਹੋਰ à¨à¨ª ਲਵੋ" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "ਸਟੋਰ ਵਿੱਚ '%s' ਖੋਜੋ" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "ਸਟੋਰ ਤੋਂ ਇਸ ਤਰà©à¨¹à¨¾à¨‚ ਦੀਆਂ ਹੋਰ à¨à¨ªà¨¾à¨‚ ਲਵੋ" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "ਸਾਰੇ" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "ਖੋਜੋ ਸਟੋਰ" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ ਇੰਸਟਾਲ ਹੋਇਆ" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "ਮੌਜੂਦ" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "ਉਬੰਤੂ ਸਟੋਰ ਵਿੱਚ %u ਨਤੀਜਾ" msgstr[1] "ਉਬੰਤੂ ਸਟੋਰ ਵਿੱਚ %u ਨਤੀਜੇ" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "ਸà©à¨à¨¾à¨ ਗà¨" #~ msgid "Scope for searching the installed click apps" #~ msgstr "ਇੰਸਟਾਲ ਕਲਿੱਕ à¨à¨ªà¨¾à¨‚ ਖੋਜਣ ਲਈ ਸਕੋਪ" #~ msgid "Scope for searching the click app store" #~ msgstr "ਕਲਿੱਕ à¨à¨ª ਸਟੋਰ ਖੋਜਣ ਲਈ ਸਕੋਪ" ./po/genpotfiles.sh0000755000015600001650000000016712676763577014427 0ustar jenkinsjenkins#!/bin/sh sed '/^#/d s/^[[].*] *// /^[ ]*$/d' \ "`dirname ${0}`/POTFILES.in" | sed '$!s/$/ \\/' > POTFILES ./po/az.po0000644000015600001650000001360712676763577012526 0ustar jenkinsjenkins# Azerbaijani translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Azerbaijani \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "ÆhatÉ™ sahÉ™lÉ™ri" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "TÉ™tbiqetmÉ™lÉ™r" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "İnternet sÉ™hifÉ™si" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "ÆlaqÉ™" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Lisenziya" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "YenilÉ™nmÉ™lÉ™r" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Versiya nömrÉ™si" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versiya" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "MÉ™lumat" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "BaÄŸla" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Aç" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Axtar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Sil" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "TÉ™tbiqetmÉ™lÉ™ri axtar" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu MaÄŸazası" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/gl.po0000644000015600001650000001644712676763577012523 0ustar jenkinsjenkins# Galician translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-08 10:45+0000\n" "Last-Translator: Marcos Lans \n" "Language-Team: Galician \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Ãmbitos" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplicativos" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Publicador/Creador" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Vendedor" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Sitio web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contacto" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licenza" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Actualización" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Número da versión" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Última actualización" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Primeira publicación" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Tamaño" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versión" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATIS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ MERCADO" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Información" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Novidades" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Revisións" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Erro de descarga" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Fallou a descarga ou a instalación. Ténteo de novo." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Pechar" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Erro de acceso" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Inicie sesión na conta de Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Ir ás contas" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Abrir" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Buscar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Cancelar compra" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Desinstalar" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Confirma a cancelación da compra de «${title}»? Desinstalarase o aplicativo." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Volver" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Continuar" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Política de cancelacións e devolucións" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Ao mercar un aplicativo na Tenda de Ubuntu, pode cancelar o pago até 15 " "minutos despois da instalación. Se sobrepasou este tempo, recomendámoslle " "contactar directamente co desenvolvedor para solicitar a devolución.\n" "Pode atopar a información de contacto do desenvolvedor na páxina de " "previsualización do aplicativo na Tenda de Ubuntu.\n" "Teña en conta que non poderá cancelar un proceso de compra máis dunha vez." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmación" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Desinstalar ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Cancelar" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirmar" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instalar" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Ãmbito para buscar aplicativos instalados" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Buscar nos aplicativos" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Tenda de Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Obteña máis aplicativos na tenda" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Buscar «%s» na tenda" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Obteña máis aplicativos como este na tenda" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Todos" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Ãmbito para buscar na tenda de aplicativos de Ubuntu" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Buscar na tenda" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALADO" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Dispoñíbel" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultado na Tenda de Ubuntu" msgstr[1] "%u resultados na Tenda de Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomendado" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Ãmbito para buscar nos aplicativos click instalados" #~ msgid "Scope for searching the click app store" #~ msgstr "Ãmbito para buscar na Tenda de aplicativos Click" ./po/nl.po0000644000015600001650000001633512676763577012526 0ustar jenkinsjenkins# Dutch translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-10 15:09+0000\n" "Last-Translator: Hannie Dumoleyn \n" "Language-Team: Dutch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scopes" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Apps" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Uitgever/maker" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Verkoper" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Website" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contact" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licentie" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Updates" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Versienummer" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Laatst bijgewerkt" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Voor het eerst uitgebracht" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Grootte" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versie" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATIS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ GEKOCHT" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Wat is er nieuw" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Beoordelingen" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Downloadfout" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Download of installatie mislukt. Probeer het opnieuw." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Sluiten" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Aanmeldfout" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Meld u aan bij uw Ubuntu One-account." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Ga naar accounts" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Openen" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Zoeken" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Aankoop annuleren" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "De-installeren" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Weet u zeker dat u de aankoop van '${title}' wilt annuleren? De app zal " "worden verwijderd." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Terug" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Doorgaan" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Voorwaarden voor teruggave en annulering" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Wanneer u een app in de Ubuntu Store aanschaft, kunt u de bestelling binnen " "15 minuten na installatie annuleren. Als de annuleringsperiode verstreken " "is, dan raden wij u aan rechtstreeks contact op te nemen met de app-" "ontwikkelaar voor een terugbetaling.\n" "U vindt de contactgegevens van de ontwikkelaar op de voorbeeldpagina van de " "app in de Ubuntu Store.\n" "Houd er rekening mee dat u het aankoopproces van een app niet meerdere keren " "kunt annuleren." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Bevestiging" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "${title} de-installeren?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Annuleren" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Bevestigen" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installeren" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Apps zoeken" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntuwinkel" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Meer apps verkrijgen uit de winkel" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Zoek naar '%s' in de winkel" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Meer apps zoals deze verkrijgen uit de winkel" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Alle" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Winkel doorzoeken" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ GEINSTALLEERD" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Beschikbaar" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultaat in de Ubuntuwinkel" msgstr[1] "%u resultaten in de Ubuntuwinkel" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Aanbevolen" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Scope voor het zoeken naar de geïnstalleerde Click-apps" #~ msgid "Scope for searching the click app store" #~ msgstr "Scope voor het doorzoeken van de Click-appwinkel" ./po/ca.po0000644000015600001650000001647712676763577012507 0ustar jenkinsjenkins# Catalan translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-14 06:16+0000\n" "Last-Translator: David Planella \n" "Language-Team: Catalan \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Lents" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplicacions" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Creador" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Venedor" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Lloc web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contacte" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Llicència" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Actualitzacions" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Número de versió" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Darrera actualització" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Primera publicació" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Mida" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versió" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATUÃTA" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ COMPRADA" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informació" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Novetats" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Ressenyes" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Error de baixada" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Ha fallat la baixada o la instal·lació. Torneu-ho a provar." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Tanca" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Error d'inici de sessió" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Inicieu la sessió amb el vostre compte de l'Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Vés als comptes" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Obre-la" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Cerca" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Cancel·la la compra" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Suprimeix-la" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Segur que voleu cancel·lar la compra de «${title}»? Això farà que es " "desinstal·li l'aplicació.." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Enrere" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Continua" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Política de retorn i cancel·lació" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "En comprar una aplicació a la Botiga de l'Ubuntu, podeu cancel·lar la compra " "durant els 15 minuts següents. En cas que aquest període de cancel·lació " "hagi vençut i vulgueu cancel·lar la compra, és recomanable que contacteu el " "desenvolupador directament.\n" "Podeu trobar les seves dades de contacte a la pàgina de previsualització de " "l'aplicació a la Botiga de l'Ubuntu.\n" "Recordeu que no podeu cancel·lar la compra d'una aplicació més d'una vegada." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmació" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Voleu suprimir ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Cancel·la" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirma" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instal·la-la" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Cerca aplicacions" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Botiga de l'Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Obtén més aplicacions a la botiga" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Cerca «%s» a la botiga" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Obtén aplicacions similars de la botiga de l'Ubuntu" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Tot" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Cerca a la botiga" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTAL·LADA" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponibles" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultat a la botiga de l'Ubuntu" msgstr[1] "%u resultats a la botiga de l'Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomanades" #~ msgid "Scope for searching the click app store" #~ msgstr "Lent per cercar al centre d'aplicacions" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Lent per cercar les aplicacions instal·lades" ./po/ia.po0000644000015600001650000001516512676763577012506 0ustar jenkinsjenkins# Interlingua translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Interlingua \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Ambitos" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Applicationes" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Editor/Creator" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Venditor" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Sito web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contacto" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licentia" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Ajornamentos" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Numero de version" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Ultime ajornamento" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Prime edition" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Dimension" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Version" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "LIBERE" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ EMITE" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informationes" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Qual novas?" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Revisiones" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Error de discarga" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Discarga o installation fallite. Per favor reprobar." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Clauder" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Error de connexion" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Per favor connecte te per tu conto Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Ir al contos" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Aperir" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Cercar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Disinstallar" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmar" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Disinstallar ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Deler" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirmar" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installar" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Cercar le applicationes" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Store" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Procurar plus de applicationes ex le Store" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Cercar pro '%s' in le Store" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Procurar plus de applicationes simile a iste ex le Store" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Toto" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Cercar in le Store" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALLATE" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponibile" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultato in Ubuntu Store" msgstr[1] "%u resultatos in Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recommendate" #~ msgid "Scope for searching the click app store" #~ msgstr "Ambito pro cercar le clic app Store" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Ambito pro cercar le clic app installate" ./po/zh_TW.po0000644000015600001650000001556412676763577013153 0ustar jenkinsjenkins# Chinese (Traditional) translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Steven Liao \n" "Language-Team: Chinese (Traditional) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "觀測é¡" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "App" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "發行人/創作人" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "售賣人" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "網站" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "è¯çµ¡äºº" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "授權" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "æ›´æ–°" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "版本號" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "最近更新" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "首次發佈" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "大å°" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "版本" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "å…è²»" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ 已購" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "資訊" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "æ–°æ±è¥¿" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "è©•è«–" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "下載錯誤" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "下載或安è£å¤±æ•—。請é‡è©¦ä¸€æ¬¡ã€‚" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "關閉" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "登入資訊錯誤" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "請登入您的 Ubuntu One 帳號。" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "å‰å¾€å¸³è™Ÿ" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "開啟" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "æœå°‹" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "å–æ¶ˆè³¼è²·" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "解除安è£" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "你確定è¦å–消購買 '${title}'? 該軟體將會解除安è£." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "返回" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "繼續" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "è¿”å›žå–æ¶ˆè³¼è²·æ–¹é‡" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "在Ubuntu商店購買安è£å¾Œ15分é˜å…§ï¼Œä½ å¯ä»¥å–消購買。如果時間已經éŽäº†ï¼Œæˆ‘們建議你直接è¯çµ¡è»Ÿé«”開發者進行退款。\n" "ä½ å¯ä»¥åœ¨Ubuntu商店內的軟體資訊é çœ‹åˆ°é–‹ç™¼è€…çš„è¯çµ¡è³‡è¨Šã€‚\n" "記ä½ä½ å°ä¸€å€‹è»Ÿé«”åªèƒ½åšä¸€æ¬¡å–消購買的動作。" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "確èª" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "è§£é™¤å®‰è£ ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "å–æ¶ˆ" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "確èª" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "安è£" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} ä½å…ƒçµ„" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "æœå°‹ç¨‹å¼" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu 商城" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "在商城å–得更多程å¼" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "在商城æœå°‹ã€Œ%sã€" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "在商城å–得更多類似程å¼" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "全部" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "æœå°‹å•†åŸŽ" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ 已安è£" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "å¯ç”¨" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "Ubuntu 商城有 %u å€‹çµæžœ" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "推薦" #~ msgid "Scope for searching the installed click apps" #~ msgstr "æœå°‹å·²å®‰è£ click 程å¼çš„觀測é¡" #~ msgid "Scope for searching the click app store" #~ msgstr "æœå°‹ click 程å¼å•†åŸŽçš„觀測é¡" ./po/ca@valencia.po0000644000015600001650000001526412676763577014303 0ustar jenkinsjenkins# Catalan (Valencian) translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Catalan (Valencian) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Lents" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplicacions" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Creador" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Venedor" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Lloc web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contacte" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Llicència" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Actualitzacions" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Número de versió" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Darrera actualització" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Primera publicació" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Mida" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versió" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATUÃTA" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ COMPRADA" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informació" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Novetats" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Ressenyes" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Error de baixada" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Ha fallat la baixada o la instal·lació. Torneu-ho a provar." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Tanca" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Error d'inici de sessió" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Inicieu la sessió amb el vostre compte de l'Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Vés als comptes" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Obri-la" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Cerca" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Suprimeix-la" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmació" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Voleu suprimir ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Cancel·la" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instal·la-la" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Cerca aplicacions" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Botiga de l'Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Obtén més aplicacions a la botiga" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Cerca «%s» a la botiga" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Obtén aplicacions similars de la botiga de l'Ubuntu" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Tot" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Cerca a la botiga" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTAL·LADA" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponibles" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultat a la botiga de l'Ubuntu" msgstr[1] "%u resultats a la botiga de l'Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomanades" #~ msgid "Scope for searching the click app store" #~ msgstr "Lent per cercar al centre d'aplicacions" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Lent per cercar les aplicacions instal·lades" ./po/unity-scope-click.pot0000644000015600001650000001312412676763577015634 0ustar jenkinsjenkins# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/nb.po0000644000015600001650000001617712676763577012520 0ustar jenkinsjenkins# Norwegian Bokmal translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-10 15:26+0000\n" "Last-Translator: Ã…ka Sikrom \n" "Language-Team: Norwegian Bokmal \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Søkefelt" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Programmer" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Utgiver/skaper" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Selger" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Nettsted" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakt" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Lisens" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Oppdateringer" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Versjonsnummer" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Sist oppdatert" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Opprinnelig utgitt" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Størrelse" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versjon" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATIS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ KJØPT" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Nyheter" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Anmeldelser" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Feil ved nedlastning" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Feil ved nedlastning eller installasjon. Prøv igjen." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Lukk" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "PÃ¥loggingsfeil" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Logg pÃ¥ Ubuntu One-kontoen din." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "GÃ¥ til Kontoer" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Ã…pne" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Søk" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Avbryt kjøp" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Avinstaller" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Er du sikker pÃ¥ at du vil avbryte kjøp av «${title}»? Programmet blir " "avinstallert." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "GÃ¥ tilbake" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Fortsett" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Retur- og angrevilkÃ¥r" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "NÃ¥r du kjøper programmer i Ubuntu-butikken kan du angre kjøpet innen 15 " "minutter etter installasjon. Hvis angrefristen er pÃ¥løpt, anbefaler vi Ã¥ " "kontakte programutvikleren direkte for Ã¥ be om refusjon.\n" "Du finner utviklerens kontaktinformasjon pÃ¥ programmets forhÃ¥ndsvisningsside " "i Ubuntu-butikken.\n" "Husk at du ikke kan angre pÃ¥ kjøp av ett og samme program flere enn én gang." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Bekreftelse" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Vil du avinstallere ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Avbryt" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Bekreft" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installer" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} byte" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Søk etter programmer" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu-butikken" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Finn flere programmer i butikken" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Søk etter «%s» i butikken" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Hent lignende programmer fra butikken" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Alt" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Søk i butikken" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALLERT" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Tilgjengelig" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u treff i Ubuntu-butikken" msgstr[1] "%u treff i Ubuntu-butikken" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Anbefalt" #~ msgid "Scope for searching the click app store" #~ msgstr "Ramme som søker gjennom klikk-programvarebutikken" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Virkefelt som søker etter installerte klikk-programmer" ./po/cs.po0000644000015600001650000001552412676763577012521 0ustar jenkinsjenkins# Czech translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Czech \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Rozsahy" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplikace" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Vydavatel/Tvůrce" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Prodávající" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Webová stránka" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakt" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licence" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Aktualizace" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Číslo verze" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Poslední aktualizace" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "První vydání" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Velikost" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Verze" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ZDARMA" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ KOUPENO" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Co je nového" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recenze" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Chyba stahování" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Stažení nebo instalace selhala. Zkuste to, prosím, znovu." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Zavřít" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Chyba pÅ™ihlášení" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "PÅ™ihlaÅ¡te se, prosím, do svého Ubuntu One úÄtu." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "PÅ™ejít do ÚÄtů" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Otevřít" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Hledat" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "ZruÅ¡it nákup" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Odinstalovat" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "Opravdu chcete zruÅ¡it nákup '${title}'? Aplikace bude odinstalována." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Jít zpÄ›t" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "PokraÄovat" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Potvrzení" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Odinstalovat ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "ZruÅ¡it" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Potvrdit" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instalovat" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bajt" msgstr[1] "{1} bajty" msgstr[2] "{1} bajtů" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Hledat aplikace" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Obchod Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Získejte více aplikací z obchodu" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Hledejte '%s' v obchodÄ›" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Získejte více podobných aplikací v obchodÄ›" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "VÅ¡echno" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Prohledat obchod" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ NAINSTALOVÃNO" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Dostupné" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u výsledek v Ubuntu Store" msgstr[1] "%u výsledky v Ubuntu Store" msgstr[2] "%u výsledků v Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "DoporuÄeno" #~ msgid "Scope for searching the click app store" #~ msgstr "Scope pro vyhledávání klikacích aplikací" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Scope pro vyhledávání nainstalovaných klikacích aplikací" ./po/fa.po0000644000015600001650000001754612676763577012510 0ustar jenkinsjenkins# Persian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Danial Behzadi \n" "Language-Team: Persian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "حوزه‌ها" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "اپ‌ها" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "منتشرکننده/سازنده" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "ÙØ±ÙˆØ´Ù†Ø¯Ù‡" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "پایگاه‌وب" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "مخاطب" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "پروانه" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "به‌روز رسانی‌ها" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "شماره‌ی نگارش" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "آخرین به‌روزرسانی" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "نخستین انتشار" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "اندازه" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "نگارش" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "رایگان" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ خریداری شده" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "اطلاعات" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "اپ‌های جدید" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "باربینی‌ها" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "خطای بارگیری" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "بارگیری یا نصب شکست خورد. Ù„Ø·ÙØ§Ù‹ دوباره تلاش کنید." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "بستن" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "خطای ورود" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Ù„Ø·ÙØ§Ù‹ به حساب کاربری اویونتووان خود وارد شوید." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Ø±ÙØªÙ† به حساب‌های کاربری" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "گشودن" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "جست‌وجو" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "لغو خرید" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "حذ٠نصب" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "از لغو خرید «${title}» مطمئنید؟ کاره حذ٠خواهد شد." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "بازگشت" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "ادامه" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "سیاست لغو Ùˆ بازگردانی" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "هنگام خرید یک کاره در ÙØ±ÙˆØ´Ú¯Ø§Ù‡ اوبونتو، می‌توانید تا Û±Ûµ دقیقه پس از نصب، خرید " "را لغو کنید. اگر بازهٔ لغو تمام شد، توصیه می‌کنیم برای برگرداندن هزینه، " "مستقیماً با توسعه‌ٔدهندهٔ کاره تماس بگیرید.\n" "می‌توانید اطّلاعات تماس توسعه‌دهنده را، Ùهرست شده در ØµÙØ­Ù‡Ù” پیش‌نمایش کاره در " "ÙØ±ÙˆØ´Ú¯Ø§Ù‡ اوبونتو بیابید.\n" "به یاد داشته باشید Ú©Ù‡ نمی‌توانید خرید کاره‌ای را، بیش از یک بار لغو کنید." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "تأییدیه" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "حذ٠${title}ØŸ" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "لغو" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "تأیید" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "نصب" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} بایت" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "جست‌وجوی اپ‌ها" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "ÙØ±ÙˆØ´Ú¯Ø§Ù‡ اوبونتو" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Ú¯Ø±ÙØªÙ† اپ‌های بیش‌تر از ÙØ±ÙˆØ´Ú¯Ø§Ù‡" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "جست‌وجو برای «%s» در ÙØ±ÙˆØ´Ú¯Ø§Ù‡" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Ú¯Ø±ÙØªÙ† اپ‌های بیش‌تری شبیه به این از ÙØ±ÙˆØ´Ú¯Ø§Ù‡" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "همه" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "جست‌وجوی ÙØ±ÙˆØ´Ú¯Ø§Ù‡" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ نصب شده" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "موجود" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u ÛŒØ§ÙØªÙ‡ در ÙØ±ÙˆØ´Ú¯Ø§Ù‡ اوبونتو" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "توصيه شده" #~ msgid "Scope for searching the installed click apps" #~ msgstr "حوزه برای جست‌وجوی اپ‌های کلیک نصب شده" #~ msgid "Scope for searching the click app store" #~ msgstr "حوضه برای جست‌وجوی ÙØ±ÙˆØ´Ú¯Ø§Ù‡ اپ کلیک" ./po/pt.po0000644000015600001650000001632212676763577012534 0ustar jenkinsjenkins# Portuguese translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2016-02-02 12:50+0000\n" "Last-Translator: Ivo Xavier \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scopes" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Apps" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Publicador/Criador" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Vendedor" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Página Web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contacto" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licença" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Atualizações" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Número de versão" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Última atualização" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Primeira versão" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Tamanho" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versão" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "Grátis" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ Comprado" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Acerca" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Novidades" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Avaliações" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Erro na transferência" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Falha na transferência ou na instalação. Tente novamente." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Fechar" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Erro na autenticação" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Por favor, entre na sua conta Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Ir para Contas" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Abrir" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Procurar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Cancelar compra" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Desinstalar" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "Quer mesmo cancelar a compra de '${title}'? A app será removida." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Retroceder" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Continuar" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Devoluções e política de cancelamento" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Quando compra uma app na Ubuntu Store, pode cancelar a compra até 15 minutos " "após a instalação. Se o período de cancelamento expirou, aconselhamos a " "contactar o desenvolvedor da app diretamente para a devolução.\n" "Pode encontrar o contacto do desenvolvedor na informação da app na Ubuntu " "Store.\n" "Tenha em mente que não é possível cancelar o processo de compra de uma app " "mais de uma vez ." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmação" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Desinstalar ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Cancelar" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirmar" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instalar" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Scope para procurar apps instaladas" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Procurar apps" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Store" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Obter mais apps da Store" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Procurar por '%s' na Store" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Obter mais apps como esta da Store" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Todas" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Scope para procurar apps na Ubuntu Store" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Procurar na Store" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ Instalado" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponível" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultado na Ubuntu Store" msgstr[1] "%u resultados na Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomendado" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Scope para procurar apps instaladas" #~ msgid "Scope for searching the click app store" #~ msgstr "Scope para procurar na Ubuntu Store" ./po/en_GB.po0000644000015600001650000001624612676763577013070 0ustar jenkinsjenkins# English (United Kingdom) translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Alan Pope ã‹› \n" "Language-Team: English (United Kingdom) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scopes" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Apps" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Publisher/Creator" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Seller" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Website" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contact" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licence" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Updates" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Version number" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Last updated" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "First released" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Size" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Version" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "FREE" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ PURCHASED" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "What's new" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Reviews" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Download Error" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Download or install failed. Please try again." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Close" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Login Error" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Please log in to your Ubuntu One account." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Go to Accounts" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Open" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Search" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Cancel Purchase" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Uninstall" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Go Back" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Continue" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Returns and cancellation policy" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmation" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Uninstall ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Cancel" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirm" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Install" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Scope for searching installed apps" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Search apps" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Store" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Get more apps from the store" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Search for '%s' in the store" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Get more apps like this from the Store" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "All" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Scope for searching the Ubuntu app store" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Search store" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALLED" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Available" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u result in Ubuntu Store" msgstr[1] "%u results in Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recommended" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Scope for searching the installed click apps" #~ msgid "Scope for searching the click app store" #~ msgstr "Scope for searching the click app store" ./po/sq.po0000644000015600001650000001332312676763577012532 0ustar jenkinsjenkins# Albanian translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Albanian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/or.po0000644000015600001650000001331312676763577012526 0ustar jenkinsjenkins# Oriya translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Oriya \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/pt_BR.po0000644000015600001650000001516412676763577013122 0ustar jenkinsjenkins# Brazilian Portuguese translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Rafael Neri \n" "Language-Team: Brazilian Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Plug-in" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Apps" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Editor/Criador" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Vendedor" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Site" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contato" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licença" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Atualizações" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Número de versão" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Última atualização" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Primeiro lançamento" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Tamanho" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versão" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "LIVRE" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ COMPRADO" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informações" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "O que há de novo" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Revisões" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Erro ao baixar" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Falha no download ou instalação. Por favor, tente novamente." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Fechar" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Erro no login" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Por favor, entre em sua conta do Ubuntu One" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Vá para Contas" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Abrir" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Pesquisar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Desinstalar" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmação" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Desinstalar ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Cancelar" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirmar" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instalar" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Pesquisar aplicativos" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Loja Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Obter mais aplicativos da loja" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Procurar por '%s' na loja" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Obter mais aplicativos como este da loja" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Tudo" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Buscar na loja" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALADO" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponível" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultado na Loja Ubuntu" msgstr[1] "%u resultados na Loja Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomendado" #~ msgid "Scope for searching the click app store" #~ msgstr "Plug-in para pesquisar na loja de click apps" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Plug-in para pesquisar click apps instalados" ./po/sk.po0000644000015600001650000001621312676763577012525 0ustar jenkinsjenkins# Slovak translation for unity-scope-click # Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2016. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2016-03-01 09:11+0000\n" "Last-Translator: P_E_T_O \n" "Language-Team: Slovak \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scopes" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplikácie" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Vydavateľ/Vývojár" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Predávajúci" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Webstránka" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakt" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licencia" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Aktualizácie" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Číslo verzie" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Posledná aktualizácia" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Prvýkrát vydané" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "VeľkosÅ¥" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Verzia" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ZADARMO" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ Zakúpené" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informácie" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "ÄŒo je nové" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recenzie" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Chyba pri sÅ¥ahovaní" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "SÅ¥ahovanie alebo inÅ¡talácia zlyhala. Prosím, skúste to znova." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "ZatvoriÅ¥" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Chyba prihlásenia" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Prosím, prihláste sa do úÄtu Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "ChoÄ k úÄtom" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "OtvoriÅ¥" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "HľadaÅ¥" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "ZruÅ¡iÅ¥ nákup" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "OdinÅ¡talovaÅ¥" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Ste si istý, že chcete zruÅ¡iÅ¥ nákup '${title}'? Aplikácia bude odinÅ¡talovaná." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Naspäť" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "PokraÄovaÅ¥" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Podmienky vrátenia a storna" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Pri nákupe aplikácie v obchode Ubuntu, môžete zruÅ¡iÅ¥ platbu do 15 minút od " "inÅ¡talácie. V prípade uplynutia doby, na vrátenie peňazí odporúÄame " "kontaktovaÅ¥ vývojára aplikácie.\n" "Kontaktné informácie vývojára môžete nájsÅ¥ v náhľade aplikácie v obchode " "Ubuntu.\n" "Majte na pamäti, že kúpu nie je možné zruÅ¡iÅ¥ viac ako jedenkrát." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "PotvrdiÅ¥" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "OdinÅ¡talovaÅ¥ ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "ZruÅ¡iÅ¥" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "PotvrdiÅ¥" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "InÅ¡talovaÅ¥" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bajtov" msgstr[1] "{1} bajt" msgstr[2] "{1} bajty" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Scope pre hľadanie nainÅ¡talovaných aplikácií" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "HľadaÅ¥ aplikácie" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Obchod Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Získajte viac aplikácií z obchodu" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "HľadaÅ¥ '%s' v obchode" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Získajte viac aplikácií ako táto v obchode" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "VÅ¡etko" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Scope pre hľadanie aplikácií v obchode Ubuntu" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "HľadaÅ¥ v obchode" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INÅ TALOVANÉ" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "K dispozícii" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u výsledkov v obchode Ubuntu" msgstr[1] "%u výsledok v obchode Ubuntu" msgstr[2] "%u výsledky v obchode Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "OdporúÄané" ./po/oc.po0000644000015600001650000001376612676763577012523 0ustar jenkinsjenkins# Occitan (post 1500) translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Occitan (post 1500) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Motors de recèrca" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplicacions" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Site Internet" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contacte" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licéncia" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Mesas a jorn" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Numèro de version" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Talha" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Version" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informacion" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Revisions" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Error de telecargament" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Tampar" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Error pendent l'autentificacion" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Dobrir" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Recercar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Desinstallar" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmacion" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Abandonar" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirmar" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installar" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Totes" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponible" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomandat" ./po/sv.po0000644000015600001650000001627712676763577012552 0ustar jenkinsjenkins# Swedish translation for unity-scope-click # Copyright © 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # Josef Andersson , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Josef Andersson \n" "Language-Team: Swedish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" "Language: sv\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Vyer" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Appar" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Publicist/Kreatör" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Säljare" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Webbplats" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakt" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licens" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Uppdateringar" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Versionsnummer" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Senast uppdaterad" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Första utgÃ¥van" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Storlek" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Version" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATIS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ KÖPT" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Nyheter" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recensioner" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Hämtningsfel" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Hämtning eller installation misslyckades. Försök igen." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Stäng" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Inloggningsfel" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Logga in pÃ¥ ditt Ubuntu One-konto." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "GÃ¥ till Konton" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Öppna" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Sök" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Avbryt köp" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Avinstallera" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Är du säker pÃ¥ att du vill avbryta ditt köp av '${title}'? Programmet kommer " "avinstalleras." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "GÃ¥ tillbaka" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Fortsätt" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Policy för Ã¥terköp och avbrott" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "När du köper ett program i Ubuntu Store kan du avbryta betalningen inom 15 " "minuter efter installation. Om avbrottsperioden har passerat rekommenderar " "vi att du tar kontakt med programmets utvecklare direkt för ett Ã¥terköp.\n" "Du kan hitta utvecklarens kontaktinformation i programmets sida i Ubuntu " "Store.\n" "Observera att du inte kan avbryta inköpsprocessen för ett program mer än en " "gÃ¥ng." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Bekräftelse" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Avinstallera ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Avbryt" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Bekräfta" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installera" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} byte" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Sök efter program" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu-förrÃ¥det" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Hämta fler appar frÃ¥n förrÃ¥det" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Sök efter '%s' i förrÃ¥det" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Hämta fler program som det här frÃ¥n förrÃ¥det" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Alla" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Sök i förrÃ¥det" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALLERAD" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Tillgängliga" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultat i UbuntuförrÃ¥det" msgstr[1] "%u resultat i UbuntuförrÃ¥det" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Rekommenderade" #~ msgid "Scope for searching the click app store" #~ msgstr "Sök i förrÃ¥det för click-appar" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Sök bland installerade click-appar" ./po/lv.po0000644000015600001650000001667712676763577012547 0ustar jenkinsjenkins# Latvian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # # FIRST AUTHOR , 2014. # RÅ«dolfs Mazurs , 2015. msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-11-22 19:13+0000\n" "Last-Translator: JÄnis Marks Gailis \n" "Language-Team: Latvian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" "Language: lv\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "TvÄ“rumi" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Lietotnes" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "IzdevÄ“js/veidotÄjs" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "PÄrdevÄ“js" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "TÄ«mekļa vietne" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakts" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licence" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "AtjauninÄjumi" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Versijas numurs" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "PÄ“dÄ“jo reizi atjauninÄts" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Pirmo reizi izlaists" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "IzmÄ“rs" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versija" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "BRĪVS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ NOPIRKTS" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "InformÄcija" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Kas jauns" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Atsauksmes" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "LejupielÄdes kļūda" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "LejupielÄde vai instalÄcija neizdevÄs. LÅ«dzu mēģiniet vÄ“lreiz." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "AizvÄ“rt" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "PieteikÅ¡anÄs kļūda" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "LÅ«dzu, ieejiet savÄ Ubuntu One kontÄ." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Iet uz kontiem." #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "AtvÄ“rt" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "MeklÄ“t" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Atcelt pirkumu" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "AtinstalÄ“t" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Vai tieÅ¡Äm vÄ“laties atcelt “${title}†pirkumu? Lietotne tiks atinstalÄ“ta." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Iet atpakaļ" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "TurpinÄt" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "AtgrieÅ¡anas un atcelÅ¡anas politika" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "NopÄ“rkot lietotni Ubuntu veikalÄ, varat atcelt pirkumu 15 minūšu laikÄ pÄ“c " "instalēšanas. Ja ir pagÄjis atcelÅ¡anas periods, iesakÄm sazinÄties ar " "lietotnes izstrÄdÄtÄju, lai saņemtu atlÄ«dzÄ«bu.\n" "IzstrÄdÄtÄja kontaktinformÄciju varat atrast lietotnes priekÅ¡skatÄ«juma lapÄ, " "kas ir Ubuntu veikalÄ.\n" "Atcerieties, ka jÅ«s nevarat atcelt kÄdas lietotnes pirkÅ¡anas procesu vairÄk " "kÄ vienu reizi." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "ApstiprinÄjums" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "AtinstalÄ“t ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Atcelt" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "ApstiprinÄt" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "InstalÄ“t" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} baits" msgstr[1] "{1} baiti" msgstr[2] "{1} baitu" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "MeklÄ“t lietotnes" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Veikals" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Atrast vairÄk lietotnes veikalÄ" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "MeklÄ“t “%s†veikalÄ" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Atrast vairÄk lÄ«dzÄ«gu lietotņu veikalÄ" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Visas" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "MeklÄ“t veikalÄ" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALÄ’TS" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Pieejams" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u rezultÄts Ubuntu veikalÄ" msgstr[1] "%u rezultÄti Ubuntu veikalÄ" msgstr[2] "%u rezultÄtu Ubuntu veikalÄ" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Ieteikts" #~ msgid "Scope for searching the installed click apps" #~ msgstr "TvÄ“rums instalÄ“to klikšķa lietotņu meklēšanai" #~ msgid "Scope for searching the click app store" #~ msgstr "TvÄ“rums klikšķa lietotņu meklēšanai veikalÄ" ./po/id.po0000644000015600001650000001332212676763577012502 0ustar jenkinsjenkins# Indonesian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Indonesian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/ar.po0000644000015600001650000002015012676763577012505 0ustar jenkinsjenkins# Arabic translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Ibrahim Saed \n" "Language-Team: Arabic \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= " "3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "النطاقات" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "التطبيقات" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "الناشر/المبتكر" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "البائع" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "الموقع الإلكتروني" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "شخص" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "الرخصة" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "التحديثات" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "رقم الإصدارة" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "آخر تحديث" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "أول إصدارة" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "الحجم" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "الإصدارة" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "مجاني" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ Ù…ÙØ´ØªØ±Ù‰" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "معلومات" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "ما الجديد" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "تعليقات" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "خطأ ÙÙŠ التنزيل" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "ÙØ´Ù„ ÙÙŠ التنزيل أو التثبيت. يرجى المحاولة مجددًا." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "إغلاق" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "خطأ ÙÙŠ الولوج" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "يرجى الولوج إلى حساب أوبونتو ون خاصتك." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "انتقل إلى الحسابات" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "ÙØªØ­" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "بحث" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "إلغاء عملية الشراء" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "إزالة التثبيت" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "هل أنت متأكد من رغبتك ÙÙŠ إلغاء عملية شراء '${title}' ØŸ التطبيق لن يتم تثبيته." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "عودة للخلÙ" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "متابعة" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "العودة وسياسة الإلغاء" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "عند شرائك لتطبيق ما من متجر أوبونتو، يمكنك إلغاء عملية الشراء خلال 15 دقيقة " "بعد التثبيت. إذا مرّ أكثر من ذلك وأردت إلغاء عملية الشراء Ùننصحك بالتواصل " "مباشرة مع مطوّر التطبيق لاسترداد مالك.\n" "يمكنك الحصول على معلومات الاتصال الخاصة بمطوّر التطبيق من خلال ØµÙØ­Ø© استعراض " "التطبيق ÙÙŠ متجر أوبونتو.\n" "خذ ÙÙŠ اعتبارك أنه لن يمكنك إلغاء عملية الشراء لتطبيق ما أكثر من مرة." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "التأكيد" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "إزالة تثبيت ${title}ØŸ" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "إلغاء" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "تأكيد" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "تثبيت" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} بايت" msgstr[1] "{1} بايت" msgstr[2] "{1} بايت" msgstr[3] "{1} بايت" msgstr[4] "{1} بايت" msgstr[5] "{1} بايت" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "البحث ÙÙŠ التطبيقات" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "متجر أوبونتو" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "احصل على المزيد من التطبيقات من المتجر" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "ابحث عن '%s' ÙÙŠ المتجر" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "احصل على المزيد من التطبيقات الشبيهة بهذه من المتجر" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "الكل" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "البحث ÙÙŠ المتجر" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ Ù…ÙØ«Ø¨Ù‘ت" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Ù…ØªÙˆÙØ±" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u نتيجة ÙÙŠ متجر أوبونتو" msgstr[1] "نتيجة واحدة %u ÙÙŠ متجر أوبونتو" msgstr[2] "نتيجتان %u ÙÙŠ متجر أوبونتو" msgstr[3] "%u نتائج ÙÙŠ متجر أوبونتو" msgstr[4] "%u نتيجة ÙÙŠ متجر أوبونتو" msgstr[5] "%u نتيجة ÙÙŠ متجر أوبونتو" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "موصى به" ./po/ko.po0000644000015600001650000001650712676763577012527 0ustar jenkinsjenkins# Korean translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 19:55+0000\n" "Last-Translator: MinSik CHO \n" "Language-Team: Korean \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "스코프" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "애플리케ì´ì…˜" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "제작ìž" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "íŒë§¤ìž" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "웹 사ì´íЏ" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "ì—°ë½ì²˜" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "ë¼ì´ì„¼ìФ" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "ì—…ë°ì´íЏ" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "버전 번호" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "마지막 ì—…ë°ì´íЏ" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "최초 공개" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "í¬ê¸°" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "버전" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "무료" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ 구매함" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "ì •ë³´" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "새로운 항목" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "í‰ê°€" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "다운로드 오류" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "다운로드나 설치가 실패했습니다. 다시 시ë„해주세요." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "닫기" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "ë¡œê·¸ì¸ ì˜¤ë¥˜" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "우분투 ì› ê³„ì •ìœ¼ë¡œ 로그ì¸í•´ì£¼ì„¸ìš”." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "계정 설정으로 가기" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "열기" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "검색" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "구입 취소" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "제거" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "ì •ë§ '${title}' 구매를 취소하시겠습니까? í”„ë¡œê·¸ëž¨ì„ ì œê±°í•©ë‹ˆë‹¤." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "뒤로 ì´ë™" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "계ì†" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "반환하고 ì •ì±… 취소" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "우분투 ìƒì ì—서 í”„ë¡œê·¸ëž¨ì„ êµ¬ìž…í•˜ë©´ 설치 후 15ë¶„ ì•ˆì— êµ¬ë§¤ë¥¼ 취소할 수 있습니다. 최소 ê¸°í•œì´ ì§€ë‚˜ë©´ 프로그램 개발ìžì—게 ì—°ë½í•˜ì—¬ " "ì§ì ‘ 환불 받기를 권고합니다. ê°œë°œìž ì—°ë½ì²˜ëŠ” 우분투 ìƒì  ì•ˆì˜ í”„ë¡œê·¸ëž¨ 미리 보기 페ì´ì§€ì— 있습니다.\n" "단 프로그램 구입 과정 중 구매 취소는 한 번 ì´ìƒ í•  수 없다는 ì ì„ 기억해주십시오." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "확ì¸" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "${title}ì„ ì œê±°í• ê¹Œìš”?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "취소" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "설치" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} ë°”ì´íЏ" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "설치한 í”„ë¡œê·¸ëž¨ì„ ê²€ìƒ‰í•˜ëŠ” 스코프" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "애플리케ì´ì…˜ 검색" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "우분투 ìƒì " #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "ìƒì ì—서 ë” ë§Žì€ ì• í”Œë¦¬ì¼€ì´ì…˜ì„ 가져오기" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "ìƒì ì—서 '%s' 검색하기" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "장터ì—서 ë” ë§Žì€ ì• í”Œë¦¬ì¼€ì´ì…˜ì„ 가져옵니다" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "모ë‘" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "우분투 앱스토어를 검색하는 스코프" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "장터 검색" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ 설치ë¨" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "사용 가능" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "우분투 ìƒì ì—서 %uê°œì˜ ê²°ê³¼ë¥¼ ì°¾ìŒ" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "추천" #~ msgid "Scope for searching the installed click apps" #~ msgstr "ì„¤ì¹˜ëœ í´ë¦­ 애플리케ì´ì…˜ì„ ì°¾ì„ ìˆ˜ 있는 스코프" #~ msgid "Scope for searching the click app store" #~ msgstr "í´ë¦­ 앱스토어를 ì°¾ì„ ìˆ˜ 있는 스코프" ./po/hu.po0000644000015600001650000001656112676763577012532 0ustar jenkinsjenkins# Hungarian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-09 14:14+0000\n" "Last-Translator: Richard Somlói \n" "Language-Team: Hungarian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Lencsék" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Alkalmazások" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Kiadó/KészítÅ‘" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Eladó" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Weboldal" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kapcsolat" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licenc" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Frissítések" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Verziószám" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Utolsó frissítés" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "ElsÅ‘ kiadás" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Méret" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Verzió" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "Ingyenes" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ Megvásárolva" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Információ" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Újdonságok" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Értékelések" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Letöltési hiba" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "A letöltés vagy a telepítés nem sikerült. Próbálja újra." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Bezárás" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Belépési hiba" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Jelentkezzen be az Ubuntu One fiókjába." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Ugrás a fiókokhoz" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Megnyitás" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Keresés" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Vásárlás lemondása" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Eltávolítás" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Biztos benne, hogy visszaszeretné vonni a(z) ${title} megvásárlását? Az " "alkalmazás ezután eltávolítása kerül." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Vissza" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Folytatás" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Visszatérítési és lemondási feltételek" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Az Ubuntu áruházban minden alkalmazás megvásárlása visszavonható a " "telepítést követÅ‘ 15. percig.\n" "Ezután az alkalmazás készítÅ‘jével kell felvenni a kapcsolatot. Az ehhez " "szükséges adatokat megtalálhatja az Ubuntu áruházban.\n" "Fontos megjegyezni, hogy egy alkalmazás megvásárlása csak egyszer mondható " "vissza." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "MegerÅ‘sítés" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "${title} eltávolítása?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Mégse" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "MegerÅ‘sítés" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Telepítés" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bájt" msgstr[1] "{1} bájt" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Lencse a telepített alkalmazások keresésére" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Alkalmazások keresése" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu áruház" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "További alkalmazások letöltése az áruházból" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "„%s†keresése az áruházban" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Hasonló alkalmazások letöltése az áruházból" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Összes" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Lencse az Ubuntu áruházban való kereséshez" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Keresés az áruházban" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ Telepítve" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "ElérhetÅ‘" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u találat az Ubuntu áruházban" msgstr[1] "%u találat az Ubuntu áruházban" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Ajánlott" #~ msgid "Scope for searching the click app store" #~ msgstr "Lencse a Click alkalmazásboltban való kereséshez" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Lencse a telepített Click alkalmazások kereséséhez" ./po/br.po0000644000015600001650000001634512676763577012521 0ustar jenkinsjenkins# Breton translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-10 13:12+0000\n" "Last-Translator: Fohanno Thierry \n" "Language-Team: Breton \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Kefluskerioù enklask" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Arloadoù" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Embanner/Krouer" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Gwerzher" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Lec'hienn web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Darempred" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Aotre-implijout" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Hizivadennoù" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Niverenn stumm" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Hizivadenn diwezhañ" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Deuet er-maez da gentañ" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Ment" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Stumm" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "DIGOUST" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "PRENET" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Titouroù" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Hag a-nevez" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Burutelladennoù" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Fazi pellgargañ" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "C'hwitet eo ar pellgargañ pe ar staliañ. Esaeit en-dro, mar plij." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Serriñ" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Fazi kevreañ" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Kevreit ouzh ho kont Ubuntu One, mar plij." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Mont d'ar c'hontoù." #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Digeriñ" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Klask" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Nullañ ar brenadenn" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Distaliañ" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Ha sur oc'h e fell deoc'h nullañ prenadenn '${title}' ? Distaliet e vo an " "arload." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Distreiñ" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Kenderc'hel" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Reolennoù adpaeañ ha nullañ" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Pa brenit un arload e stal Ubuntu e c'hallit nullañ ar brenadenn e-pad ur " "c'hard eur goude m'eo bet staliet. Ma vez re ziwezhat e aliomp ac'hanoc'h da " "vont war-eeun e darempred gant diorroer an arload evit bezañ adpaeet.\n" "Gallout a rit kavout daveennoù an diorroer war ar bajenn da rakwelet an " "arload e stal Ubuntu.\n" "Ho pet soñj ne c'hallit ket nullañ prenadenn un arload ouzhpenn ur wech." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Kadarnadur" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Distaliañ ${title} ?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Nullañ" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Kadarnaat" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Staliañ" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} okted" msgstr[1] "{1} a oktedoù" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Klask arloadoù" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Stal Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Tapout muioc'h a arloadoù er stal" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Klask '%s' er stal" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Tapout muioc'h a arloadoù evel hemañ er Stal" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "An holl anezho" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Klask ur stal" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ STALIET" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "A c'haller kaout" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u disoc'h e stal Ubuntu" msgstr[1] "%u results in Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Erbedet" #~ msgid "Scope for searching the click app store" #~ msgstr "Keflusker enklask evit furchal ar stal arloadoù Click" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Keflusker ebnklask evit furchal an arloadoù Click staliet" ./po/ne.po0000644000015600001650000001401112676763577012504 0ustar jenkinsjenkins# Nepali translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-11-09 02:05+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Nepali \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "सà¥à¤•ोपà¥à¤¸" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "अनà¥à¤ªà¥à¤°à¤¯à¥‹à¤—हरू" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "पà¥à¤°à¤•ाशक / सृषà¥à¤Ÿà¤¿à¤•रà¥à¤¤à¤¾" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "विकà¥à¤°à¥‡à¤¤à¤¾" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "वेबसाइट" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "समà¥à¤ªà¤°à¥à¤•" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "अनà¥à¤®à¤¤à¥€à¤ªà¤¤à¥à¤°" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "अपà¥à¤¡à¥‡à¤Ÿà¤¹à¤°à¥" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "संसà¥à¤•रण नमà¥à¤¬à¤°" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "पछिलà¥à¤²à¥‹ अपà¥à¤¡à¥‡à¤Ÿ" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/my.po0000644000015600001650000001415612676763577012541 0ustar jenkinsjenkins# Burmese translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Pyae Sone \n" "Language-Team: Burmese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Apps" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "ပိá€á€º" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "á€á€„်ရောက်မှုမအောင်မြင်ပါ" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "သင်á Ubuntu One အကောင့်ထဲသို့á€á€„်ရောက်ပါ" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "အကောင့်သို့သွားမည်" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "ဖွင့်" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "စက်ထဲမှထုá€á€ºá€›á€”်" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "အá€á€Šá€ºá€•ြုá€á€¼á€„်း" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "စက်ထဲသို့ထည့်သွင်းမည်" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "ရရှိနိုင်သော" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/sl.po0000644000015600001650000001542712676763577012534 0ustar jenkinsjenkins# Slovenian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Dražen MateÅ¡ić \n" "Language-Team: Slovenian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || " "n%100==4 ? 3 : 0);\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Viri" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Programi" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Založnik/Ustvarjalec" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Prodajalec" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Spletna stran" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Stik" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Dovoljenje" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Posodobitve" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Å tevilka razliÄice" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Nazadnje posodobljeno" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "PrviÄ izdano" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Velikost" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "RazliÄica" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ZASTONJ" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ KUPLJENO" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Podrobnosti" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Kaj je novega" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Mnenja" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Napaka med prenosom" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Prejem ali namestitev je spodletela. Poskusite znova." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Zapri" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Napaka med prijavo" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Prijavite se v vaÅ¡ raÄun Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Pojdi v RaÄune" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Odpri" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Iskanje" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Odstrani" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Potrditev" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Odstranitev ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "PrekliÄi" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Potrdi" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Namesti" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bajtov" msgstr[1] "{1} bajt" msgstr[2] "{1} bajta" msgstr[3] "{1} bajti" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Iskanje programov" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Trgovina Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Dobi veÄ programov v trgovini" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "IÅ¡Äi '%s' v trgovini" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Dobi veÄ takÅ¡nih programov v Trgovini" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Vse" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Iskanje po trgovini" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ NAMEÅ ÄŒENO" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Na voljo" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u rezultatov v Trgovini Ubuntu" msgstr[1] "%u rezultat v Trgovini Ubuntu" msgstr[2] "%u rezultata v Trgovini Ubuntu" msgstr[3] "%u rezultati v Trgovini Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "PriporoÄeno" #~ msgid "Scope for searching the click app store" #~ msgstr "Vir za iskanje po trgovini programov click" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Vir za iskanje nameÅ¡Äenih programov click" ./po/ga.po0000644000015600001650000001333612676763577012502 0ustar jenkinsjenkins# Irish translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Irish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "" msgstr[1] "" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/it.po0000644000015600001650000001637212676763577012532 0ustar jenkinsjenkins# Italian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-08-31 06:47+0000\n" "Last-Translator: Claudio Arseni \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scope" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "App" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Editore/Creatore" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Venditore" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Sito web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contatto" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licenza" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Aggiornamenti" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Numero versione" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Ultimo aggiornamento" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Primo rilascio" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Dimensione" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versione" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "Gratuita" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ Acquistata" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informazioni" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Novità" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recensioni" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Errore nello scaricamento" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Scaricamento o installazione non riusciti. Riprovare." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Chiudi" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Errore di accesso" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Accedere al proprio account Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Vai agli account" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Apri" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Cerca" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Annulla acquisto" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Rimuovi" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "Annullare veramente l'acquisto di «${title}»? L'app verrà rimossa." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Indietro" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Continua" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Restituzione e annullamento" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Quando si acquista un'app nell'Ubuntu Store, è possibile annullare " "l'acquisto entro 15 minuti dall'installazione. Se il periodo per annullare " "l'acquisto è passato, è consigliato contattare lo sviluppatore dell'app per " "un rimborso.\n" "Le informazioni sullo sviluppatore possono essere trovate nella pagina di " "anteprima dell'app nell'Ubuntu Store.\n" "Ricordarsi che non è possibile annullare l'acquisto di un'app più di una " "volta." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Conferma" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Rimuovere ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Annulla" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Conferma" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installa" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} byte" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Scope per cercare applicazioni installate" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Cerca app" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Store" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Ottieni altre applicazioni dallo store" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Cerca «%s» nello store" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Ottieni altre applicazioni dallo store simili a questa" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Tutto" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Scoper per eseguire ricerche nell'Ubuntu store" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Cerca nello store" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ Installata" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponibile" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u risultato in Ubuntu Store" msgstr[1] "%u risultati in Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Consigliata" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Scope per la ricerca delle applicazioni installate" #~ msgid "Scope for searching the click app store" #~ msgstr "Scope per la ricerca nello store" ./po/gd.po0000644000015600001650000001754112676763577012507 0ustar jenkinsjenkins# Gaelic; Scottish translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # GunChleoc , 2014, 2015. msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-20 15:03+0000\n" "Last-Translator: GunChleoc \n" "Language-Team: Fòram na Gàidhlig\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : " "(n > 2 && n < 20) ? 2 : 3;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" "Language: gd\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Sgòpaichean" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplacaidean" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Foillsichear/Cruthadair" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Reiceadair" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Làrach-lìn" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Neach-aithne" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Ceadachas" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Ùrachaidhean" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Àireamh an tionndaidh" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Ùrachadh mu dheireadh" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "A' chiad fhoillseachadh" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Meud" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Tionndadh" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "AN-ASGAIDH" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ AIR A CHEANNACH" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Fiosrachadh" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Na tha ùr" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Lèirmheasan" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Mearachd leis an luchdadh a-nuas" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" "Dh'fhàillig leis an luchdadh a-nuas no an stàladh. Am feuch thu ris a-" "rithist?" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Dùin" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Mearachd leis an logadh a-steach" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Logh a-steach dhan chunntas Ubuntu One agad." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Rach dha na cunntasan" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Fosgail" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Lorg" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Sguir dhen cheannach" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Dì-stàlaich" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "A bheil thu cinnteach nach eil thu airson \"${title}\" a cheannach " "tuilleadh? Thèid an aplacaid a dhì-stàladh." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Air ais" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Lean air adhart" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Poileasaidh nan tillidhean agus an sgur dheth" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Nuair a cheannaicheas tu aplacaid ann am Bùth Ubuntu, 's urrainn dhut sgur " "dheth gu ruige 15 mionaidean às dèidh an stàlaidh ach nach iarr sinn airgead " "ort tuilleadh. Ma dh'fhalbh ùine an sguir dheth ort, mholamaid gun cuir thu " "fios gu leasaichear na h-aplacaid gu dìreach airson ath-dhìoladh.\n" "Chì thu fiosrachadh conaltraidh an leasaicheir air duilleag ro-shealladh na " "h-aplacaid ann am Bùth Ubuntu.\n" "Thoir an aire nach urrainn dhut sgur dhe dh'aplacaid a cheannach barrachd " "air aon turas." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Dearbhadh" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "A bheil thu airson ${title} a dhì-stàladh?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Sguir dheth" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Dearbhaich" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Stàlaich" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bhaidht" msgstr[1] "{1} bhaidht" msgstr[2] "{1} baidhtichean" msgstr[3] "{1} baidht" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Sgòp airson lorg sna h-aplacaidean air an stàladh" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Lorg aplacaidean" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Bùth Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Faigh barrachd aplacaidean on bhùth" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Lorg airson \"%s\" sa bhùth" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Faigh barrachd aplacaidean on bhùth seo" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Na h-uile" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Sgòp airson lorg ann am bùth aplacaidean Ubuntu" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Lorg sa bhùth" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ AIR A STÃLADH" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Ri fhaighinn" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u toradh ann am bùth Ubuntu" msgstr[1] "%u thoradh ann am bùth Ubuntu" msgstr[2] "%u toraidhdean ann am bùth Ubuntu" msgstr[3] "%u toradh ann am bùth Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Air a mholadh" #~ msgid "Scope for searching the click app store" #~ msgstr "Sgòp airson lorg sa bhùth click app" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Sgòp gus lorg sna h.-aplacaidean briogaidh a chaidh a stàladh" ./po/km.po0000644000015600001650000001733212676763577012522 0ustar jenkinsjenkins# Khmer translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Sok Sophea \n" "Language-Team: Khmer \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "វិសាល​ភាព" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "កម្មវិធី" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "អ្នក​បោះពុម្ព/អ្នក​បង្កើáž" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "អ្នក​លក់" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "ážáŸ†áž”ន់បណ្ដាញ" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "ទំនាក់ទំនង" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "អាជ្ញាបáŸážŽáŸ’ណ" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "បច្ចុប្បន្នភាព" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "áž›áŸážâ€‹áž€áŸ†ážŽáŸ‚" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "បាន​​ធ្វើ​បច្ចុប្បន្នភាព​ចុងក្រោយ" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "បាន​ចáŸáž‰áž•្សាយ​ដំបូង" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "​ទំហំ" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "កំណែ" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ទំនáŸážš" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ បាន​ទិញ" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "áž–áŸážáŸŒáž˜áž¶áž“" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "អ្វី​ដែល​ážáŸ’មី" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "ពិនិážáŸ’យ​ឡើងវិញ" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "កំហុស​ទាញយក" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "ការ​ទាញ​យក ឬ​ដំឡើង​បាន​បរាជáŸáž™áŸ” សូម​ព្យាយាម​ម្ដង​ទៀážáŸ”" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "បិទ" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "កំហុស​ចូល" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "សូម​ចូល​ក្នុង​គណនី Ubuntu One របស់​អ្នក។" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "ចូល​ទៅ​គណនី" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "បើក" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "ស្វែងរក" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "លុប" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "ការ​បញ្ជាក់" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "លុប ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "បោះ​បង់" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "អះអាង" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "ដំឡើង" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} បៃ" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "ស្វែងរក​កម្មវិធី" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "áž áž¶áž„ Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "យក​កម្មវិធី​ច្រើន​ទៀážâ€‹áž–ី​ហាង" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "ស្វែងរក '%s' ក្នុងហាង" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "យក​កម្មវិធី​ច្រើន​ទៀážâ€‹ážŠáž¼áž…​នáŸáŸ‡â€‹áž–ី​ហាង" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "ទាំងអស់" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "ស្វែងរក​ហាង" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ បាន​ដំឡើង" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "មាន" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "លទ្ធផល %u ក្នុុង​ហាង Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "បាន​ផ្ដល់​យោបល់" #~ msgid "Scope for searching the installed click apps" #~ msgstr "វិសាលភាព​សម្រាប់​ស្វែង​រក​កម្មវិធី​ចុច​ដែល​បាន​ដំឡើង" #~ msgid "Scope for searching the click app store" #~ msgstr "វិសាលភាព​សម្រាប់​ស្វែងរក​ការ​ចុច​កម្មវិធី​ក្នុង​​ហាង" ./po/el.po0000644000015600001650000002106612676763577012512 0ustar jenkinsjenkins# Greek translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-10 13:25+0000\n" "Last-Translator: Simos Xenitellis  \n" "Language-Team: Greek \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Επισκόπια" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "ΕφαÏμογές" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Εκδότης/ΔημιουÏγός" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Πωλητής" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Ιστότοπος" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Επαφή" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Άδεια χÏήσης" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "ΕνημεÏώσεις" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "ΑÏιθμός έκδοσης" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Τελευταία ενημέÏωση" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "ΑÏχική κυκλοφοÏία" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Μέγεθος" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Έκδοση" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ΔΩΡΕΑÎ" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ ΑΓΟΡΑΣΜΕÎΑ" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "ΠληÏοφοÏίες" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Τι νέο υπάÏχει" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "ΚÏιτικές" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Σφάλμα λήψης" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Η λήψη ή η εγκατάσταση απέτυχε. ΠαÏακαλώ Ï€Ïοσπαθήστε ξανά." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Κλείσιμο" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Σφάλμα εισόδου" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "ΠαÏακαλώ συνδεθείτε στο λογαÏιασμό σας στο Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Μετάβαση στους ΛογαÏιασμοÏÏ‚" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Άνοιγμα" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Αναζήτηση" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "ΑκÏÏωση αγοÏάς" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Απεγκατάσταση" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Θέλετε σίγουÏα να ακυÏώσετε την αγοÏά του «${title}»; Η εφαÏμογή θα " "απεγκατασταθεί." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Πίσω" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Συνέχεια" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Πολιτική για επιστÏοφές και ακυÏώσεις" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Όταν αγοÏάζετε μια εφαÏμογή στο Κατάστημα Ubuntu, μποÏείτε να ακυÏώσετε τη " "συναλλαγή μέσα σε 15 λεπτά από την εγκατάσταση. Αν πεÏάσει η Ï€Ïοθεσμία " "ακÏÏωσης, Ï€Ïοτείνουμε να επικοινωνήσετε με τους δημιουÏγοÏÏ‚ της εφαÏμογής " "για επιστÏοφή.\n" "ΜποÏείτε να βÏείτε τα στοιχεία επικοινωνίας με τους δημιουÏγοÏÏ‚ στη σελίδα " "Ï€Ïοεπισκόπησης της εφαÏμογής στο Κατάστημα Ubuntu.\n" "Λάβετε υπόψιν ότι δεν μποÏείτε να ακυÏώσετε τη διαδικασία αγοÏάς μιας " "εφαÏμογής πάνω από μία φοÏά." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Επιβεβαίωση" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Απεγκατάσταση ${title};" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "ΑκυÌÏωση" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Επιβεβαίωση" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Εγκατάσταση" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Αναζήτηση για εφαÏμογές" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Κατάστημα Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Λήψη πεÏισσότεÏων εφαÏμογών από το κατάστημα" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Αναζήτηση για «%s» στο κατάστημα" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Λήψη πεÏισσότεÏων εφαÏμογών σαν και αυτή από το κατάστημα" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Όλα" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Αναζήτηση στο κατάστημα" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ ΕΓΚΑΤΕΣΤΗΜΕÎΑ" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Διαθέσιμα" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u αποτέλεσμα στο κατάστημα του Ubuntu" msgstr[1] "%u αποτελέσματα στο κατάστημα του Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "ΠÏοτείνονται" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Επισκόπιο αναζήτησης ήδη εγκατεστημένων εφαÏμογών click" #~ msgid "Scope for searching the click app store" #~ msgstr "Επισκόπιο αναζήτησης στο κατάστημα εφαÏμογών click" ./po/sr.po0000644000015600001650000002073612676763577012541 0ustar jenkinsjenkins# Serbian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2016-02-10 21:01+0000\n" "Last-Translator: Bojan Bogdanović \n" "Language-Team: Serbian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "ОпÑези" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Програми" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Издавач/творац" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Продавац" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Веб Ñтраница" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Контакт" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Лиценца" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Доградње" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Број издања" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "ПоÑледње оÑвежено" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Прво издање" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Величина" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Издање" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "БЕСПЛÐТÐÐ" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ КУПЉЕÐ" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Инфо" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Шта има ново" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Рецензије" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Грешка при преузимању" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "ÐеуÑпешно преузимање или поÑтављање. Покушајте поново." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Затвори" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Грешка при пријави" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Пријавите Ñе на Ñвој налог Убунтуа Један." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Иди на налоге" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Отвори" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Тражи" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Откажи куповину" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Уклони" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Да ли Ñигурни да желите да откажете куповину „${title}“? Програм ће бити " "уклоњен." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Иди назад" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "ÐаÑтави" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Политика отказа и повраћаја" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Када купујете програм у Убунту продавници, можете отказати плаћање у року од " "15 минута након инÑталације. Ðко отказни рок прође, препоручујемо вам да Ñе " "обратите директно програмеру за повраћај новца.\n" "\n" "Податке о томе како да Ñтупите у везу Ñа програмером можете наћи на Ñтраници " "програма у Убунту продавници.\n" "Имајте на уму да не можете отказати куповину програма више од једном." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Потврда" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Да уклоним „${title}“?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Откажи" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Потврди" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "ИнÑталирај" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} бајт" msgstr[1] "{1} бајта" msgstr[2] "{1} бајтова" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "ОпÑег за претрагу инÑталираних програма" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Потражи програме" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Убунту продавница" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Ðабавите још програма из продавнице" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Потражи „%s“ у продавници" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Ðабавите Ñличне програме из продавнице" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Све" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "ОпÑег за претрагу Убунту продавнице" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Претражи продавницу" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ ИÐСТÐЛИРÐÐ" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "ДоÑтупан" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u резултат у Убунту продавници" msgstr[1] "%u резултата у Убунту продавници" msgstr[2] "%u резултата у Убунту продавници" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Препоручен" #~ msgid "Scope for searching the installed click apps" #~ msgstr "ОпÑег за претрагу инÑталираних клик апликација" #~ msgid "Scope for searching the click app store" #~ msgstr "ОпÑег за претрагу продавнице клик апликација" ./po/ms.po0000644000015600001650000001614012676763577012526 0ustar jenkinsjenkins# Malay translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2016-02-04 14:14+0000\n" "Last-Translator: abuyop \n" "Language-Team: Malay \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Skop" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Apl" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Penerbit/Pencipta" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Penjual" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Laman Sesawang" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Hubungi" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Lesen" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Kemas kini" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Nombor versi" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Terakhir dikemaskini" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Pertama dikeluarkan" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Saiz" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versi" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "BEBAS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ DIBELI" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Maklumat" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Yang terbaharu" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Ulasan" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Ralat Muat turun" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Muat turun atau pemasangan gagal. Sila cuba lagi" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Tutup" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Ralat Daftar Masuk" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Sila daftar masuk ke dalam akaun Ubuntu One anda." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Pergi ke Akaun" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Buka" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Gelintar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Batal Pembelian" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Nyahpasang" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Anda pasti mahu batalkan pembelian '${title}'? Apl akan dinyahpasangkan." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Pergi Balik" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Teruskan" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Polisi pemulangan dan pembatalan" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Bila membeli sesebuah apl dalam Kedai Ubuntu, anda boleh batalkan dalam " "tempoh 15 minit selepas pemasangan. Jika tempoh pembatalan telah dilepasi, " "kami sarankan anda menghubungi pembangun apl secara terus untuk dapatkan " "kembali bayaran.\n" "Anda boleh cari maklumat hubungan pembangun tersenarai pada halaman " "pratonton apl di dalam Kedai Ubuntu.\n" "Pastikan anda tidak membatalkan proses pembelian apl lebih dari sekali." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Pengesahan" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Nyahpasang ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Batal" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Sahkan" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Pasang" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bait" msgstr[1] "{1} bait" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Gelintar apl" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Kedai Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Dapatkan lagi apl dari kedai" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Gelintar '%s' di dalam kedai" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Dapatkan lagi apl seperti ini di dalam Kedai" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Semua" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Gelintar kedai" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ DIPASANG" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Tersedia" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u keputusan dalam Kedai Ubuntu" msgstr[1] "%u keputusan dalam Kedai Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Disarankan" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Skop untuk menggelintar apl klik yang dipasang" #~ msgid "Scope for searching the click app store" #~ msgstr "Skop untuk menggelintar kedai apl klik" ./po/da.po0000644000015600001650000001512312676763577012473 0ustar jenkinsjenkins# Danish translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # # Ask Hjorth Larsen , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Ask Hjorth Larsen \n" "Language-Team: Danish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Apps" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Udgiver/forfatter" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Sælger" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Websted" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontaktperson" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licens" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Opdateringer" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Versionsnummer" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Sidst opdateret" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Først udgivet" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Størrelse" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Version" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATIS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ KØBT" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Nyheder" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Anmeldelser" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Overførselsfejl" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Overførsel eller installation mislykkedes. Prøv venligst igen." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Luk" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Logindfejl" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Log venligst ind med din Ubuntu One-konto." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "GÃ¥ til konti" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Ã…bn" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Søg" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Afinstallér" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Bekræftelse" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Afinstaller ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Annullér" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installér" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} byte" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Søg efter apps" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu-butikken" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Hent flere apps fra butikken" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Søg efter \"%s\" i butikken" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Find flere apps som denne fra butikken" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Søg i butikken" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALLERET" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Tilgængelig" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultat i Ubuntu-butikken" msgstr[1] "%u resultater i Ubuntu-butikken" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Anbefalet" #~ msgid "Scope for searching the click app store" #~ msgstr "Perspektiv til at søge i klik-app-butikken" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Perspektiv til at søge i installerede klik-apps" ./po/eu.po0000644000015600001650000001536212676763577012525 0ustar jenkinsjenkins# Basque translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-22 23:16+0000\n" "Last-Translator: Ibai Oihanguren Sala \n" "Language-Team: Basque \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Eremuak" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplikazioak" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Argitaratzailea/Sortzailea" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Saltzailea" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Webgunea" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontaktua" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Lizentzia" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Eguneraketak" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Bertsio-zenbakia" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Azken eguneratzea" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Aurreneko argitaratzea" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Tamaina" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Bertsioa" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "DOAN" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ EROSITA" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informazioa" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Zer berri?" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Kritikak" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Deskarga-errorea" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Deskarga edo instalazioak huts egin du. Saiatu berriro." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Itxi" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Errorea saioa hastean" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Hasi saioa zure Ubuntu One kontuan." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Joan kontuetara" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Ireki" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Bilatu" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Utzi erosketa" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Desinstalatu" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Ziur '${title}'(r)en erosketa utzi nahi duzula? Aplikazioa desinstalatu " "egingo da." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Itzuli" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Jarraitu" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Itzulketa eta ezeztapenen politika" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Berrespena" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "${title} desinstalatu?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Utzi" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Berretsi" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instalatu" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "byte {1}" msgstr[1] "{1} byte" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Bilatu aplikazioak" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubunturen denda" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Eskuratu dendako aplikazio gehiago" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Bilatu '%s' dendan" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Eskuratu hau moduko aplikazio gehiago dendan" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Guztiak" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Bilatu dendan" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALATUTA" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Erabilgarri" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "Emaitza %u Ubunturen dendan" msgstr[1] "%u emaitza Ubunturen dendan" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Gomendatua" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Instalatutako klik aplikazioak bilatzeko eremua" #~ msgid "Scope for searching the click app store" #~ msgstr "Klik aplikazioen dendan bilatzeko eremua" ./po/tr.po0000644000015600001650000001432512676763577012537 0ustar jenkinsjenkins# Turkish translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Turkish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Uygulamalar" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Dağıtıcı/Yapımcı" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Satıcı" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Web Sitesi" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "İletiÅŸim" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Lisans" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Güncellemeler" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Sürüm Numarası" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Son güncelleme" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Boyut" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Sürüm" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Bilgi" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Neler yeni" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Yorumlar" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "İndirme Hatası" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "İndirme veya yükleme baÅŸarısız oldu. Lütfen tekrar deneyin." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Kapat" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Lütfen Ubuntu One hesabınıza giriÅŸ yapın." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Hesaplara Git" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Aç" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Ara" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Kaldır" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Onay" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "${title}, kaldır?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "İptal" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Kur" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bayt" msgstr[1] "{1} bayt" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Uygulamaları ara" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "MaÄŸazadan daha fazla uygulama edinin" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "'%s' sorgusunu maÄŸazada ara" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "MaÄŸazadan buna benzer daha fazla uygulama edinin" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Hepsi" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "MaÄŸazayu ara" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ YÜKLENDİ" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/hr.po0000644000015600001650000001674212676763577012530 0ustar jenkinsjenkins# Croatian translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Croatian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Djelokruzi" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplikacije" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "ObjavljivaÄ/Autor" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "ProdavaÄ" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Web stranica" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakt" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licenca" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Nadopune" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Broj inaÄice" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Posljedna nadopuna" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Prvi put objavljeno" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "VeliÄina" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "InaÄica" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "BESPLATNO" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ KUPLJENO" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informacije" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Å to je novo" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recenzije" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "GreÅ¡ka preuzimanja" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Neuspjelo preuzimanje ili instalacija. PokuÅ¡ajte ponovno." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Zatvori" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "GreÅ¡ka prijavljivanja" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Prijavite se u vaÅ¡ Ubuntu One raÄun" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Idi na RaÄune" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Otvori" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Pretraži" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Prekini kupovinu" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Deinstaliraj" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Sigurno želite prekinuti kupovinu '${title}'? Aplikacija će biti " "deinstalirana." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Idi natrag" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Nastavi" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Pravila vraćanja i prekidanja" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Kada kupujete aplikaciju u Ubuntu trgovini, možete poniÅ¡titi uplatu 15 " "minuta nakon instalacije. Ako je razdoblje poniÅ¡tenja proteklo, " "preporuÄujemo vam izravno kontaktiranje autora aplikacije za povrat " "uplaćenih sredstava.\n" "Možete pronaći informacije kontakata autora na stranici pregleda aplikacije " "u Ubuntu trgovini.\n" "Imajte na umu da ne možete prekinuti postupak kupovine bilo koje aplikacije " "viÅ¡e od jednog puta." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Potvrda" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Deinstaliraj ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Odustani" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Potvrdi" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instaliraj" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bajt" msgstr[1] "{1} bajta" msgstr[2] "{1} bajtova" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Djelokrug za pretraživanje instaliranih aplikacija" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Pretraži aplikacije" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu trgovina" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Preuzmi viÅ¡e aplikacija iz trgovine" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Pretraži '%s' u trgovini" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Preuzmi viÅ¡e aplikacija poput ove iz trgovine" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Sve" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Djelokrug za pretraživanje aplikacija Ubuntu trgovine" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Pretraži trgovinu" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALIRANO" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Dostupno" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u rezultat u Ubuntu trgovini" msgstr[1] "%u rezultata u Ubuntu trgovini" msgstr[2] "%u rezultata u Ubuntu trgovini" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "PreporuÄeno" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Djelokrug za pretraživanje klikom instaliranih aplikacija" #~ msgid "Scope for searching the click app store" #~ msgstr "Djelokrug pretraživanja trgovine za klik aplikacije" ./po/is.po0000644000015600001650000001543212676763577012525 0ustar jenkinsjenkins# Icelandic translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # # FIRST AUTHOR , 2014. # Sveinn í Felli , 2014. msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-10-16 01:44+0000\n" "Last-Translator: Sveinn í Felli \n" "Language-Team: Icelandic \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" "Language: is\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Leitarsvið" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Forrit" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Útgefandi/Hönnuður" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Söluaðili" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Vefsvæði" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Tengiliður" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Notkunarleyfi" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Uppfærslur" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Útgáfunúmer" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Síðast uppfært" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Útgefið fyrst" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Stærð" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Útgáfa" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "FRJÃLST" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ KEYPT" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Upplýsingar" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Hvað er nýtt" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Umsagnir" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Villa við niðurhal" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Niðurhal eða uppsetning mistókst. Reyndu aftur." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Loka" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Villa við innskráningu" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Skráðu þig inn í Ubuntu One aðganginn þinn." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Fara í aðgangalista" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Opna" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Leita" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Niðurtaka" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Staðfesting" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Fjarlægja ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Hætta við" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Staðfesta" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Setja inn" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bæti" msgstr[1] "{1} bæti" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Leita að forritum" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Store verslunin" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Ná í fleiri forrit í versluninni" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Leita að '%s' í versluninni" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Ná í fleiri forrit í þessum dúr úr safninu" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Allt" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Leita í verslun" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ UPPSETT" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Tiltækt" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u niðurstaða í Ubuntu Store versluninni" msgstr[1] "%u niðurstöður í Ubuntu Store versluninni" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Mælt með þessu" #~ msgid "Scope for searching the click app store" #~ msgstr "Gagnasvið til að leita í forritaverslun" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Gagnasvið til að leita að uppsettum forritum" ./po/en_AU.po0000644000015600001650000001503112676763577013074 0ustar jenkinsjenkins# English (Australia) translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Jared Norris \n" "Language-Team: English (Australia) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scopes" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Apps" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Publisher/Creator" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Seller" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Website" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contact" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licence" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Updates" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Version number" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Last updated" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "First released" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Size" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Version" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "FREE" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ PURCHASED" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "What's new" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Reviews" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Download Error" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Download or install failed. Please try again." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Close" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Login Error" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Please log in to your Ubuntu One account." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Go to Accounts" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Open" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Search" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Uninstall" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmation" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Uninstall ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Cancel" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirm" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Install" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Search apps" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Store" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Get more apps from the store" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Search for '%s' in the store" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Get more apps like this from the Store" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "All" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Search store" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALLED" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Available" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u result in Ubuntu Store" msgstr[1] "%u results in Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recommended" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Scope for searching the installed click apps" #~ msgid "Scope for searching the click app store" #~ msgstr "Scope for searching the click app store" ./po/eo.po0000644000015600001650000001373112676763577012515 0ustar jenkinsjenkins# Esperanto translation for unity-scope-click # Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2016. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2016-01-18 13:45+0000\n" "Last-Translator: Robin van der Vliet \n" "Language-Team: Esperanto \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplikaĵoj" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Retejo" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakto" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Permesilo" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Äœisdatigoj" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Grando" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versio" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "SENPAGA" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ AĈETITA" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informo" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recenzoj" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Fermi" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Malfermi" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Serĉi" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Nuligi aĉeton" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Malinstali" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Reen" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "DaÅ­rigi" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Konfirmo" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Malinstali ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Nuligi" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Konfirmi" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instali" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bitoko" msgstr[1] "{1} bitokoj" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALITA" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponebla" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "" msgstr[1] "" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "" ./po/uk.po0000644000015600001650000002133612676763577012531 0ustar jenkinsjenkins# Ukrainian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-09 16:24+0000\n" "Last-Translator: Yuri Chornoivan \n" "Language-Team: Ukrainian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "ОблаÑті" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Програми" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Ðвтор публікації" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Продавець" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Сайт" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Контакт" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "ЛіцензуваннÑ" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "ОновленнÑ" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Ðомер верÑÑ–Ñ—" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "ОÑтаннє оновленнÑ" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Вперше випущено" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Розмір" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "ВерÑÑ–Ñ" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ВІЛЬÐЕ" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ ПРИДБÐÐО" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "ВідомоÑті" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Ðові надходженнÑ" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Відгуки" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Помилка під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" "Спроба Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… або вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð°ÐºÑƒÐ½ÐºÐ° зазнала невдачі. Будь лаÑка, " "повторіть Ñ—Ñ—." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Закрити" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Помилка входу" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Будь лаÑка, увійдіть до вашого облікового запиÑу Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Перейти до облікових запиÑів" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Відкрити" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Пошук" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "СкаÑувати купівлю" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Вилучити" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Ви Ñправді хочете ÑкаÑувати купівлю «${title}»? ПіÑÐ»Ñ ÑкаÑÐ¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ñƒ " "буде вилучено." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Ðазад" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Далі" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "ПовернутиÑÑ Ð´Ð¾ правил ÑкаÑовуваннÑ" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Придбавши програму в Ubuntu Store ви протÑгом 15 хвилин піÑÐ»Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ " "програми можете розірвати угоду з купівлі. Якщо період ÑкаÑÐ¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ³Ð¾Ð´Ð¸ " "минув, рекомендуємо вам безпоÑередньо зв’ÑзатиÑÑ Ð· розробником програми Ð´Ð»Ñ " "ÑƒÐ·Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ ÑƒÐ¼Ð¾Ð² Ñ€Ð¾Ð·Ñ–Ñ€Ð²Ð°Ð½Ð½Ñ ÑƒÐ³Ð¾Ð´Ð¸ з купівлі.\n" "Контактні дані розробника можна знайти на Ñторінці попереднього переглÑду " "програми в Ubuntu Store.\n" "Зауважте, що ви можете розірвати угоду з купівлі програми лише один раз." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "ПідтверджденнÑ" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Вилучити ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "СкаÑувати" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "ПідтвердженнÑ" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Ð’Ñтановити" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} байт" msgstr[1] "{1} байти" msgstr[2] "{1} байтів" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "МожливоÑті Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ вÑтановлених додатків" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Шукати програми" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Store" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Отримати інші програми з крамниці" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Шукати «%s» у крамниці" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Отримати інші подібні програми з крамниці" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "УÑÑ–" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "МожливоÑті Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ додатків у крамниці Ubuntu" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Шукати у крамниці" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ ВСТÐÐОВЛЕÐО" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "ДоÑтупно" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u відповідник у Ubuntu Store" msgstr[1] "%u відповідники у Ubuntu Store" msgstr[2] "%u відповідників у Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Рекомендоване" #~ msgid "Scope for searching the click app store" #~ msgstr "ОблаÑть Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ у збірці програм click" #~ msgid "Scope for searching the installed click apps" #~ msgstr "ОблаÑть Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ вÑтановлених програм" ./po/fi.po0000644000015600001650000001627212676763577012513 0ustar jenkinsjenkins# Finnish translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-09 13:41+0000\n" "Last-Translator: Jiri Grönroos \n" "Language-Team: Finnish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Näkymät" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Sovellukset" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Julkaisija/tekijä" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Myyjä" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Verkkosivusto" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Yhteys" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Lisenssi" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Päivitykset" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Versionumero" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Viimeksi päivitetty" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Ensijulkaisu" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Koko" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versio" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ILMAINEN" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ OSTETTU" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Tiedot" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Uutuudet" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Arvostelut" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Latausvirhe" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Lataus tai asennus epäonnistui. Yritä uudelleen." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Sulje" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Kirjautumisvirhe" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Kirjaudu Ubuntu One -tilillesi." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Siirry tileihin" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Avaa" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Etsi" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Peru ostos" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Poista" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Haluatko varmasti perua sovellusostoksen'${title}'? Sovellus poistetaan." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Palaa takaisin" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Jatka" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Palautuskäytäntö" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Maksaessasi ostoksen Ubuntu-kaupassa, voit perua kyseisen maksutapahtuman 15 " "minuutin kuluessa sovelluksen asentamisesta. Mikäli ostoksen perumisaika on " "jo kulunut, suosittelemme ottamaan yhteyttä sovelluksen kehittäjään ja " "pyytämään maksun palautusta. Kehittäjien yhteystiedot ovat näkyvillä " "sovellusten esikatselusivulla. Otathan huomioon, ettei maksua yksittäisestä " "sovelluksesta voi perua kuin kerran." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Vahvistus" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Poistetaanko ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Peru" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Vahvista" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Asenna" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} tavu" msgstr[1] "{1} tavua" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Näkymä asennettujen sovellusten etsimiseen" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Etsi sovelluksia" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu-kauppa" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Hanki lisää sovelluksia kaupasta" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Etsi kaupasta hakusanalla \"%s\"" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Hanki lisää samankaltaisia sovelluksia kaupasta" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Kaikki" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Näkymä Ubuntu-sovelluskaupan valikoimaan" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Etsi kaupasta" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ ASENNETTU" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Saatavilla" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u tulos Ubuntu-kaupassa" msgstr[1] "%u tulosta Ubuntu-kaupassa" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Suositeltu" #~ msgid "Scope for searching the click app store" #~ msgstr "Näkymä Click-sovelluskaupasta etsintää varten" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Näkymä asennettujen Click-sovellusten etsimiseen" ./po/ru.po0000644000015600001650000002034712676763577012541 0ustar jenkinsjenkins# Russian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Jay ZDLin \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Линзы" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "ПриложениÑ" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Издатель/Разработчик" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Продавец" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Веб-Ñайт" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Контакты" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "ЛицензиÑ" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "ОбновлениÑ" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Ðомер верÑии" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "ПоÑледнее обновление" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Дата публикации" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Размер" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "ВерÑиÑ" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "БЕСПЛÐТÐО" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ КУПЛЕÐО" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "ИнформациÑ" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "ÐовоÑти" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Обзоры" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Ошибка загрузки" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Загрузка или уÑтановка не удалаÑÑŒ. ПожалуйÑта, попробуйте Ñнова." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Закрыть" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Ошибка входа" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Войдите в учётную запиÑÑŒ Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Перейти к ÑпиÑку учётных запиÑей" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Открыть" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Ðайти" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Отменить покупку" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Удалить" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "ДейÑтвительно отменить покупку '${title}'? Приложение будет удалено Ñ " "уÑтройÑтва." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Ðазад" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Продолжить" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Политика отмены покупки и компенÑации" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "ÐŸÑ€Ð¸Ð¾Ð±Ñ€ÐµÑ‚Ð°Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ в магазине Ubuntu, вы можете отменить покупку в " "течение 15 минут поÑле его уÑтановки. ЕÑли Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° покупки прошло больше " "времени, то рекомендуетÑÑ Ð¾Ð±Ñ€Ð°Ñ‰Ð°Ñ‚ÑŒÑÑ Ð½Ð°Ð¿Ñ€Ñмую к разработчику Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ " "компенÑации.\n" "Контактные данные разработчика можно найти на Ñтранице Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² магазине " "Ubuntu.\n" "Внимание: отменить покупку Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð¶Ð½Ð¾ только один раз." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Подтверждение" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Удалить ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Отмена" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "УÑтановить" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} байт" msgstr[1] "{1} байта" msgstr[2] "{1} байтов" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "ПоиÑк приложений" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Магазин Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Ещё Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð· магазина" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "ИÑкать '%s' в магазине" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Ещё подобные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² Магазине" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Ð’Ñе" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "ПоиÑк в магазине" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ УСТÐÐОВЛЕÐО" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "ДоÑтупно" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u найдено в Магазине Ubuntu" msgstr[1] "%u найдено в Магазине Ubuntu" msgstr[2] "%u найдено в Магазине Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Рекомендованные" #~ msgid "Scope for searching the click app store" #~ msgstr "ПоиÑк в магазине приложений" #~ msgid "Scope for searching the installed click apps" #~ msgstr "ПоиÑк уÑтановленных приложений" ./po/ro.po0000644000015600001650000001551012676763577012527 0ustar jenkinsjenkins# Romanian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Meriuță Cornel \n" "Language-Team: Romanian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 " "== 0) && (n != 0))) ? 2: 1));\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Domenii" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "AplicaÈ›ii" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Editor/Creator" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Comerciant" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Pagină web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contact" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licență" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Actualizări" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Număr versiune" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Ultima actualizare" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Prima lansare" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Dimensiune" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versiune" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATIS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ CUMPÄ‚RAT" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "InformaÈ›ii" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Noutăți" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Recenzii" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Eroare la descărcare" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "EÈ™ec la descărcare sau instalare. ÃŽncercaÈ›i din nou." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "ÃŽnchide" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Eroare la autentificare" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "AutentificaÈ›i-vă în contul dumneavoastră Ubuntu One" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Mergi la Conturi" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Deschide" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Căutare" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Dezinstalare" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmare" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Dezinstalare ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Anulează" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "ConfirmaÈ›i" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instalează" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} bit" msgstr[1] "{1} biÈ›i" msgstr[2] "{1} de biÈ›i" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Caută aplicaÈ›ii" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Magazin Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "ObÈ›ineÈ›i mai multe aplicaÈ›ii din magazin" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Caută '%s' în magazin" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "ObÈ›ineÈ›i mai multe aplicaÈ›ii de acest fel din magazin" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Tot" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Caută în magazin" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALAT" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponibil" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u rezultat în Magazinul Ubuntu" msgstr[1] "%u rezultate în Magazinul Ubuntu" msgstr[2] "%u de rezultate în Magazinul Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomandate" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Domeniu pentru căutarea aplicaÈ›iilor instalate" #~ msgid "Scope for searching the click app store" #~ msgstr "Domeniu pentru căutare aplicaÈ›ii în magazin" ./po/he.po0000644000015600001650000001574612676763577012516 0ustar jenkinsjenkins# Hebrew translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Yaron \n" "Language-Team: Hebrew \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "טווחי×" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "יישומי×" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "יוצר/מפרס×" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "מוכר" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "×תר הבית" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "יצירת קשר" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "רישיון" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "עדכוני×" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "מספר גרסה" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "עדכון ×חרון" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "הפצה לר×שונה" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "גודל" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "גרסה" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "בחינ×" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ נרכש" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "מידע" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "מה חדש" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "סקירות" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "שגי×ת הורדה" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "ההורדה ×ו ההתקנה נכשלו. × × ×œ× ×¡×•×ª שוב." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "סגירה" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "שגי×ת כניסה" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "× × ×œ×”×™×›× ×¡ לחשבונך ב־Ubuntu One" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "מעבר לחשבונות" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "פתיחה" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "חיפוש" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "ביטול הרכישה" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "הסרה" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "×”×× ×œ×‘×˜×œ ×ת הרכישה של '${title}'? ×”×™×™×©×•× ×™×•×¡×¨." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "חזרה" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "המשך" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "×ימות" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "×”×× ×œ×”×¡×™×¨ ×ת ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "ביטול" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "×ישור" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "התקנה" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "בית ×חד" msgstr[1] "{1} בתי×" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "חיפוש ביישומי×" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "החנות של ×ובונטו" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "הורדת ×™×™×©×•×ž×™× × ×•×¡×¤×™× ×ž×—× ×•×ª זו" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "חיפוש ×חר '%s' בחנות" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "ניתן להוריד עוד ×™×™×©×•×ž×™× ×›×לה מהחנות" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "הכול" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "חיפוש בחנות" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ הותקן" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "זמין" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "תוצ××” ×חת מהחנות של ×ובונטו" msgstr[1] "%u תוצ×ות מהחנות של ×ובונטו" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "מומלץ" #~ msgid "Scope for searching the click app store" #~ msgstr "×ž×ª×—× ×œ×—×™×¤×•×© בחנות ×”×™×™×©×•×ž×™× ×‘×œ×—×™×¦×”" #~ msgid "Scope for searching the installed click apps" #~ msgstr "עדשה לחיפוש ×ת יישומי הלחיצה המותקני×" ./po/wo.po0000644000015600001650000001472312676763577012541 0ustar jenkinsjenkins# Wolof translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-10-29 13:26+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Wolof \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Kem" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Jëfekaay" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Siiwalkat/Defarkat" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Jaaykat" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Dalu web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "jotukaay" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Sañal" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Yeesal" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Sumb" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Yeesal gu mujj" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Génne gu mujj" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Dayoo" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Sumb" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "Féex" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ JËDDEES" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Xibaar" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Li bees" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Say gis-gis" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Njuumteg yeb" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Yeb walla samp gu antuwul. Di la ñaan nga Jéemaat." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Tëj" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Njuumteeg dugg" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Di la ñaan nga dugg ci sa sàq mu Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Dem ci Sàq yi" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Ubbi" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Seet" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Sempi" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Dëggal" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Sempi ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Neenal" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Dëggal" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Samp" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} byte" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Seet jëfekaay" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ja bu Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Am yeneeni jëfekaay ca ja ba" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Seet '%s' ca ja ba" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Am yeneeni jëfekaay yu ni mel ca ja ba" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Yépp" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Seet ci ja bi" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ SAMPEES" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Jàppandi" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u njureef ci Ubuntu Store" msgstr[1] "%u njureef ci Ubuntu Store" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Digalees" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Kem ngir seet jëfekaay yees samp" #~ msgid "Scope for searching the click app store" #~ msgstr "Kem ngir seet ci biir ja bu jëfekaay yi" ./po/am.po0000644000015600001650000002012012676763577012475 0ustar jenkinsjenkins# Amharic translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: samson \n" "Language-Team: Amharic \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "ወሰኖች" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "መተáŒá‰ áˆªá‹«á‹Žá‰½" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "አታሚá‹/áˆáŒ£áˆªá‹" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "ሻጩ" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "ድህረ ገጽ" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "áŒáŠ•áŠ™áŠá‰µ" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "áቃድ" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "ማሻሻያዎች" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "የእትሠá‰áŒ¥áˆ­" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "መጨረሻ የተሻሻለá‹" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "መጀመሪያ የተለቀቀá‹" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "መጠን" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "እትáˆ" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "áŠáƒ" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ ተገá‹á‰·áˆ" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "መረጃ" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "áˆáŠ• አዲስ áŠáŒˆáˆ­ አለ" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "áŒáˆáŒˆáˆ›á‹" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "የማá‹áˆ¨á‹µ ስህተት" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "ማá‹áˆ¨á‹µ ወይንሠመáŒáŒ áˆ አáˆá‰°áˆ³áŠ«áˆá¡ እባክዎን እንደገና ይሞክሩ" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "መá‹áŒŠá‹«" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "የመáŒá‰¢á‹« ስህተት" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "እባክዎን ወደ ኡቡንቱ ዋን መáŒáˆˆáŒ«á‹Ž á‹­áŒá‰¡" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "ወደ መáŒáˆˆáŒ« መሄጃ" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "መክáˆá‰»" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "መáˆáˆˆáŒŠá‹«" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "áŒá‹¢á‹áŠ• መሰረዣ" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "ማጥáŠá‹«" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "በ እርáŒáŒ¥ መሰረዠይáˆáˆáŒ‹áˆ‰ áŒá‹¢á‹áŠ• የ '${title}'? መተáŒá‰ áˆªá‹«á‹ ይጠá‹áˆ" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "ወደ ኋላ መሄጃ" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "ይቀጥሉ" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "መመለሻ እና የ መሰረዣ አሰራር" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "ከ ኡቡንቱ ሱቅ መተáŒá‰ áˆªá‹« ከ ገዙ: እና ከገጠሙት በኋላ ካáˆá‹ˆá‹°á‹±á‰µ በ 15 ደቂቃ á‹áˆµáŒ¥ áŒá‹¢á‹áŠ• መሰረዠይችላሉ: መሰረዣ " "ጊዜዠካለሠáŒáŠ• በቀጥታ ከ መተáŒá‰ áˆªá‹«á‹ አዘጋጆች ጋር እንዲገናኙ እና áŒá‹¢á‹áŠ• እንዲሰርዙ እናሳስባለን: የ መተáŒá‰ áˆªá‹«á‹áŠ• " "አዘጋጆች መገናኛ መረጃ በ መተáŒá‰ áˆªá‹« ቅድመ እይታ ገጽ ላይ በ ኡቡንቱ ሱቅ á‹áˆµáŒ¥ ይገኛáˆ: ያስታá‹áˆ± የ መተáŒá‰ áˆªá‹« " "áŒá‹¢á‹áŠ• ከ አንድ ጊዜ በላይ መሰረዠአይቻáˆáˆ" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "ማረጋገጫ" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "ማጥáŠá‹« ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "መሰረዣ" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "ማረጋገጫ" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "መáŒáŒ áˆšá‹«" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} ባይት" msgstr[1] "{1} ባይትስ" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "የ ተገጠመ መተáŒá‰ áˆªá‹« መáˆáˆˆáŒŠá‹« ክáˆáˆ" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "መተáŒá‰ áˆªá‹« መáˆáˆˆáŒŠá‹«" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "የ ኡቡንቱ ሱቅ" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "ተጨማሪ መተáŒá‰ áˆªá‹« በ ሱቅ á‹áˆµáŒ¥ መáˆáˆˆáŒŠá‹«" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "መáˆáˆˆáŒŠá‹« በ '%s' ሱቅ á‹áˆµáŒ¥" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "እንደዚህ ያለ ተጨማሪ መተáŒá‰ áˆªá‹« በ ሱቅ á‹áˆµáŒ¥ መáˆáˆˆáŒŠá‹«" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "áˆáˆ‰áŠ•áˆ" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "የ ኡቡንቱ መተáŒá‰ áˆªá‹« ሱቅ መáˆáˆˆáŒŠá‹« ክáˆáˆ" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "ሱቅ መáˆáˆˆáŒŠá‹«" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ ተገጥሟáˆ" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "á‹áŒáŒ" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u á‹áŒ¤á‰µ ከ ኡቡንቱ ሱቅ á‹áˆµáŒ¥" msgstr[1] "%u á‹áŒ¤á‰¶á‰½ ከ ኡቡንቱ ሱቅ á‹áˆµáŒ¥" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "የተጠቆመá‹" #~ msgid "Scope for searching the click app store" #~ msgstr "የ መተáŒá‰ áˆªá‹« ሱቅ ሲጫኑ የ መáˆáˆˆáŒŠá‹« ክáˆáˆ" #~ msgid "Scope for searching the installed click apps" #~ msgstr "የ ተገጠሙ መተáŒá‰ áˆªá‹« ሲጫኑ የ መáˆáˆˆáŒŠá‹« ክáˆáˆ" ./po/es.po0000644000015600001650000001643112676763577012521 0ustar jenkinsjenkins# Spanish translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-11 19:19+0000\n" "Last-Translator: Paco Molinero \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scopes" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplicaciones" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Autor/editor" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Vendedor" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Sitio web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contacto" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licencia" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Actualizaciones" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Número de versión" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Última actualización" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Primera publicación" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Tamaño" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versión" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATIS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ COMPRADA" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Información" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Novedades" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Reseñas" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Error en la descarga" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "La descarga o la instalación han fallado. Inténtelo nuevamente." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Cerrar" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Error de autenticación" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Acceda a su cuenta de Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Ir a cuentas" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Abrir" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Buscar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Cancelar compra" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Desinstalar" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "¿Confirma que quiere cancelar la compra de «${title}»? La aplicación se " "desinstalará." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Atrás" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Continuar" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Política de cancelación y reembolsos" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Al comprar una aplicación en la Tienda Ubuntu, podrá cancelar el cobro hasta " "15 minutos después de la instalación. Si el periodo de cancelación ha " "expirado, le recomendamos que contacte directamente con el desarrollador de " "la aplicación para solicitar el reembolso.\n" "Tenga en cuenta que no puede cancelar el proceso de compra de una aplicación " "más de una vez." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmación" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "¿Desinstalar ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Cancelar" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirmar" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instalar" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Scope para buscar en las aplicaciones instaladas" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Buscar aplicaciones" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Tienda Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Consiga más aplicaciones en la tienda" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Busque «%s» en la tienda" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Más aplicaciones como esta en la Tienda" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Todo" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Scope para buscar en la tienda de aplicaciones de Ubuntu" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Buscar en la tienda" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALADA" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponible" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultado en la Tienda de Ubuntu" msgstr[1] "%u resultados en la Tienda de Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomendado" #~ msgid "Scope for searching the click app store" #~ msgstr "Lente para buscar en la tienda de aplicaciones" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Scope para buscar las aplicaciones instaladas" ./po/ug.po0000644000015600001650000001651512676763577012530 0ustar jenkinsjenkins# Uyghur translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # Gheyret Kenji , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-10-26 03:48+0000\n" "Last-Translator: Eltikin \n" "Language-Team: Uyghur Computer Science Association \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "دائىرىلەر" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "ئەپلەر" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "نەشر قىلغۇچى/ياسىغۇچى" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "ساتقۇچى" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "تورتۇرا" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "ئالاقەداش" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "ئىجازەتنامە" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "ÙŠÛڭىلانمىلار" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "نەشر نومۇرى" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "ئاخىرقى Ù‚ÛØªÙ‰Ù… ÙŠÛڭىلانغىنى" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "تۇنجى Ù‚ÛØªÙ‰Ù… ئÛلان قىلىنغىنى" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "چوڭلۇقى" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "نەشرى" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ھەقسىز" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ Ø³ÛØªÙ‰Û‹Ø§Ù„غانلىرى" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "ئۇچۇر" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "ÙŠÛڭىلىقلار" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "باھالاش پىكىرلىرى" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "چۈشۈرۈش خاتالىقى" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "چۈشۈرۈش ياكى ئورنىتىش مەغلۇپ بولدى. قايتا سىناڭ." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "ياپ" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "كىرىشتە خاتالىق كۆرۈلدى" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "ئۇبۇنتۇ بىر Ú¾ÛØ³Ø§Ø¨Ø§ØªÙ‰Ú­Ù‰Ø²ØºØ§ كىرىڭ." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Ú¾ÛØ³Ø§Ø¨Ø§ØªÙ„ارغا يۆتكەل" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "ئاچ" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "ئىزدە" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Ø³ÛØªÛ‹Ù‰Ù„ىشنى بىكار قىلىش" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "يوقات" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "قايت" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "داۋاملاشتۇر" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "جەزملەش" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "${title} نى چىقىرىۋەتسۇنمۇ؟" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "ئەمەلدىن قالدۇر" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "جەزملە" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "ئورنات" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} بايت" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "ئەپ ئىزدە" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "ئۇبۇنتۇ دۇكىنى" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "دۇكاندىن ØªÛØ®Ù‰Ù…Û‡ ÙƒÛ†Ù¾ ئەپلەرنى ئÛلىش" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "دۇكاندىن ‹%s› نى ئىزدەش" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "دۇكاندىن بۇنىڭغا ئوخشىشىپ ÙƒÛØªÙ‰Ø¯Ù‰ØºØ§Ù† ئەپلەرگە Ø¦ÛØ±Ù‰Ø´Ù‰Ø´" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "ھەممىسى" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "دۇكاندىن ئىزدە" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ ئورنىتىلغان" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "ئىشلەتكىلى بولىدۇ" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "ئۇبۇنتۇ دۇكىنىدىن %u نەتىجە چىقتى" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "تەۋسىيە قىلىنغان" #~ msgid "Scope for searching the click app store" #~ msgstr "چەكمە ئەپلەرنى ئىزدەيدىغان دۇكاندىكى دائىرە" #~ msgid "Scope for searching the installed click apps" #~ msgstr "ئورنىتىلغان چەكمە ئەپلەرنى ئىزدەش دائىرىسى" ./po/CMakeLists.txt0000644000015600001650000000241612676763577014310 0ustar jenkinsjenkinsinclude(FindGettext) find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) find_program(INTLTOOL_UPDATE intltool-update) set(GETTEXT_PACKAGE ${PROJECT_NAME}) set(POT_FILE ${GETTEXT_PACKAGE}.pot) file(GLOB POFILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.po) # Creates POTFILES add_custom_target(POTFILES ALL COMMENT "Generating POTFILES" COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/genpotfiles.sh ${CMAKE_SOURCE_DIR} ) # Creates the .pot file containing the translations template add_custom_target(${POT_FILE} COMMENT "Generating translation template" COMMAND XGETTEXT="${GETTEXT_XGETTEXT_EXECUTABLE}" srcdir="${CMAKE_CURRENT_SOURCE_DIR}" ${INTLTOOL_UPDATE} --gettext-package ${GETTEXT_PACKAGE} --pot COMMAND cp -f ${POT_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/ DEPENDS POTFILES ) # Builds the binary translations catalog for each language # it finds source translations (*.po) for foreach(POFILE ${POFILES}) string(REPLACE ".po" "" LANG ${POFILE}) list(APPEND PO_FILES "${POFILE}") gettext_process_po_files(${LANG} ALL PO_FILES "${POFILE}") set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/${LANG}/LC_MESSAGES) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo DESTINATION ${INSTALL_DIR} RENAME ${GETTEXT_PACKAGE}.mo ) endforeach(POFILE) ./po/fr.po0000644000015600001650000001714112676763577012520 0ustar jenkinsjenkins# French translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2016-02-04 20:43+0000\n" "Last-Translator: Jean-Marc \n" "Language-Team: French \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Moteurs de recherche" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Applications" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Éditeur/Créateur" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Vendeur" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Site web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contact" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Licence" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Mises à jour" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Numéro de version" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Dernière mise à jour" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Première sortie" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Taille" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Version" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "GRATUIT" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ ACHETÉE" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Informations" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Nouveautés" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Critiques" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Erreur lors du téléchargement" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "Le téléchargement ou l'installation a échoué. Veuillez réessayer." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Fermer" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Erreur lors de l'authentification" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Veuillez vous connecter à votre compte Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Voir les comptes" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Ouvrir" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Rechercher" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Annuler l'achat" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Désinstaller" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Voulez-vous vraiment annuler l'achat de « ${title} » ? L'application sera " "désinstallée." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Retour" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Continuer" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Politique de retour et d'annulation" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "En achetant une application sur le magasin Ubuntu, vous pouvez annuler la " "transaction jusqu'à 15 minutes après l'installation. Si la période " "d'annulation est terminée, nous vous recommandons de contacter le " "développeur de l'application directement pour obtenir un remboursement.\n" "Les informations de contact du développeur sont listées sur la page de " "prévisualisation de l'application dans le magasin.\n" "Gardez en tête que vous ne pouvez pas annuler l'achat d'une même application " "plus d'une fois." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmation" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Désinstaller ${title} ?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Annuler" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirmer" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installer" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} octet" msgstr[1] "{1} octets" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Moteur de recherche pour chercher les applications installées" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Rechercher des applications" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Magasin Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Obtenir plus d'applications à partir de la logithèque" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Recherche de « %s » dans la logithèque" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Obtenir plus d'applications de ce type à partir de la logithèque" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Tous" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Moteur de recherche pour chercher dans la logithèque" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Rechercher un magasin" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALLÉE" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponibles" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u résultat dans la logithèque Ubuntu" msgstr[1] "%u résultats dans la logithèque Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recommandées" #~ msgid "Scope for searching the click app store" #~ msgstr "Moteur de recherche pour parcourir la logithèque Click" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Moteur de recherche pour parcourir les applications Click installées" ./po/POTFILES.in0000644000015600001650000000045112676763577013322 0ustar jenkinsjenkinslibclickscope/click/highlights.cpp libclickscope/click/preview.cpp libclickscope/click/utils.cpp [type: gettext/ini] scope/clickapps/clickscope.ini.in.in scope/clickapps/apps-query.cpp [type: gettext/ini] scope/clickstore/com.canonical.scopes.clickstore.ini.in.in scope/clickstore/store-query.cpp ./po/ja.po0000644000015600001650000001716212676763577012506 0ustar jenkinsjenkins# Japanese translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Kentaro Kazuhama \n" "Language-Team: Japanese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scope" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "アプリ" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "発行者/製作者" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "販売者" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "ウェブサイト" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "連絡先" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "ライセンス" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "æ›´æ–°" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "ãƒãƒ¼ã‚¸ãƒ§ãƒ³ç•ªå·" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "最終更新" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "åˆæœŸãƒªãƒªãƒ¼ã‚¹" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "サイズ" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "ãƒãƒ¼ã‚¸ãƒ§ãƒ³" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "ç„¡æ–™" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ 購入ã—ã¾ã—ãŸ" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "情報" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "æ–°ç€æƒ…å ±" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "レビュー" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "ダウンロードエラー" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "ダウンロードã‹ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚å†åº¦è©¦ã—ã¦ãã ã•ã„。" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "é–‰ã˜ã‚‹" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "ログインエラー" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Ubuntu Oneアカウントã«ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãã ã•ã„。" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "アカウントã«ç§»å‹•" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "é–‹ã" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "検索" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "購入をキャンセル" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "アンインストール" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "'${title}'ã®è³¼å…¥ã‚’本当ã«ã‚­ãƒ£ãƒ³ã‚»ãƒ«ã—ã¾ã™ã‹? アプリケーションã¯ã‚¢ãƒ³ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã™ã€‚" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "戻る" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "ç¶šã‘ã‚‹" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "è¿”å“ã¨ã‚­ãƒ£ãƒ³ã‚»ãƒ«ãƒãƒªã‚·ãƒ¼" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Ubuntu Store " "ã§ã‚¢ãƒ—リケーションを購入ã—ãŸå ´åˆã€ã‚¢ãƒ—リケーションã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«å¾Œ15分以内ã§ã‚れã°è³¼å…¥ã‚’キャンセルã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚15分以上経éŽã—ãŸå ´åˆã«ã¯ã€ç›´æŽ¥ã‚¢" "プリケーションã®é–‹ç™ºè€…ã«é€£çµ¡ã‚’å–りã€è¿”é‡‘ã®æ‰‹ç¶šãを行ã£ã¦ãã ã•ã„。\n" "アプリケーション開発者ã®é€£çµ¡å…ˆã¯ Ubuntu Store 㮠アプリケーションプレビューãºãƒ¼ã‚¸ã«æŽ²è¼‰ã•れã¦ã„ã¾ã™ã€‚\n" "アプリケーション購入ã®ã‚­ãƒ£ãƒ³ã‚»ãƒ«ã¯ä¸€åº¦ã—ã‹ã§ãã¾ã›ã‚“。" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "確èª" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "${title} をアンインストールã—ã¾ã™ã‹ï¼Ÿ" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "キャンセル" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "インストール" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1}ãƒã‚¤ãƒˆ" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "インストールã—ãŸã‚¢ãƒ—リケーションを検索ã™ã‚‹Scope" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "ã‚¢ãƒ—ãƒªã®æ¤œç´¢" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntuストア" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "ストアã‹ã‚‰ã•らã«ã‚¢ãƒ—リを確èª" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "ストアã§ã€Œ%sã€ã‚’検索" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "ストアã‹ã‚‰ã•らã«ã“れã«ä¼¼ãŸã‚¢ãƒ—リを確èª" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "ã™ã¹ã¦" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Ubuntuアプリストアを検索ã™ã‚‹Scope" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "ストアを検索" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ インストールã—ã¾ã—ãŸ" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "利用å¯èƒ½" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "Ubuntu Storeã«%uä»¶" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "ãŠã™ã™ã‚" #~ msgid "Scope for searching the installed click apps" #~ msgstr "インストール済ã¿ã®Clickアプリ検索用Scope" #~ msgid "Scope for searching the click app store" #~ msgstr "Clickアプリストア検索用Scope" ./po/de.po0000644000015600001650000001662312676763577012505 0ustar jenkinsjenkins# German translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-23 21:44+0000\n" "Last-Translator: Torsten Franz \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Filter" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Anwendungen" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Verlag/Autor" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Verkäufer" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Webseite" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Kontakt" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Lizenz" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Aktualisierungen" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Versionsnummer" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Letzte Aktualisierung" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Zuerst erschienen" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Größe" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Version" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "KOSTENLOS" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ GEKAUFT" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Was ist neu?" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Bewertungen" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Fehler beim Herunterladen" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" "Herunterladen oder Installieren ist fehlgeschlagen. Bitte versuchen Sie es " "erneut." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Schließen" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Anmeldefehler" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Bitte melden Sie sich mit Ihrem »Ubuntu One«-Konto an." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Gehen Sie zu Konten" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Öffnen" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Suchen" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Kauf abbrechen" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Entfernen" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Sind Sie sicher, dass Sie den Kauf von »${title}« abbrechen wollen? Die " "Anwendung wird deinstalliert." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Zurück" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Weiter" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Rückgabe- und Stornierungsregel" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Wenn Sie eine Anwendung im Ubuntu Store kaufen, können Sie den Kauf " "innerhalb von 15 Minuten nach der Installation abbrechen. Wenn diese Zeit " "allerdings abgelaufen ist, empfehlen wir Ihnen die Anwendungs-Entwickler " "direkt freundlich nach einer Rückerstattung zu fragen.\n" "\n" "Sie können die Kontakt-Informationen der Entwickler im Ubuntu Store in der " "Vorschau-Seite der Anwendung finden.\n" "Bedenken Sie aber, dass Sie einen Kauf nur ein einziges Mal abbrechen können." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Bestätigung" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Entferne ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Abbrechen" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Bestätigen" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Installieren" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "Scope zur Suche von installierten Anwendungen" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Anwendungen suchen" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu Store" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Mehr Anwendungen aus dem Store beziehen" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Suche nach »%s« im Store" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Erhalten Sie mehr Anwendungen wie diese aus dem Store" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Alle" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "Scope zur Suche im Ubuntu App Store" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Store durchsuchen" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALLIERT" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Verfügbar" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u Ergebnis im »Ubuntu Store«" msgstr[1] "%u Ergebnisse im »Ubuntu Store«" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Empfohlen" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Filter zum Durchsuchen der installierten Click-Anwendungen" #~ msgid "Scope for searching the click app store" #~ msgstr "Suchilter für den Click-App-Store" ./po/be.po0000644000015600001650000002027212676763577012476 0ustar jenkinsjenkins# Belarusian translation for unity-scope-click # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-12-05 22:33+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Belarusian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Лінзы" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Дадаткі" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Выдавецтва / Стваральнік" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Прадаўнік" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Сайт" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Кантакт" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "ЛіцÑнзіÑ" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Ðбнаўленьні" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Ð’ÑÑ€ÑÑ–Ñ" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "ÐпошнÑе абнаўленьне:" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Першы Ñ€Ñліз" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Памер" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "ВерÑÑ–Ñ" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "БЕЗКÐШТОЎÐÐ" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ ÐÐБЫТЫЯ" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "ІнфармацыÑ" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Што цікавага?" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Водгукі" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Памылка пры ÑьцÑгваньні" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "" "Спампаваць ці ÑžÑталÑваць не атрымалаÑÑÑ. Калі лаÑка, паÑпрабуйце ÑÑˆÑ‡Ñ Ñ€Ð°Ð·." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Зачыніць" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Памылка" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Калі лаÑка, ўвайдзіце Ñ–Ð¼Ñ ÐºÐ°Ñ€Ñ‹Ñтальніка Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Да карыÑтальніка" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Ðдчыніць" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Пошук" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "СкаÑаваць набыццё" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Выдаліць" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "Ð’Ñ‹ ўпÑўненыÑ, што хочаце ÑкаÑаваць набыццё '${title}'? Дадатак будзе " "выдалены." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Ð’Ñрнуцца" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "ПрацÑгнуць" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "Зварот Ñ– ÑкаÑаванне палітыкі" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "Пры набыцці дадатку Ñž краме Ubuntu, Ð’Ñ‹ можаце ÑкаÑаваць плату цÑгам 15 " "хвілін паÑÐ»Ñ ÑžÑталёўкі. Калі Ñ‚Ñрмін ÑкаÑÐ°Ð²Ð°Ð½ÑŒÐ½Ñ Ð¿Ñ€Ð°Ð¹ÑˆÐ¾Ñž, мы раім звÑртацца " "да раÑпрацоўніка па зварот грошай.\n" "Ð’Ñ‹ можаце знайÑці ÐºÐ°Ð½Ñ‚Ð°ÐºÑ‚Ð½Ñ‹Ñ Ð´Ð°Ð´Ð·ÐµÐ½Ñ‹Ñ Ñ€Ð°Ñпрацоўніка, Ñны паказаны на " "Ñтаронцы папÑÑ€ÑднÑга праглÑду дадатку Ñž Ubuntu.\n" "Майце на ўвазе, што Ð’Ñ‹ Ð½Ñ Ð·Ð¼Ð¾Ð¶Ð°Ñ†Ðµ ÑкаÑаваць працÑÑ ÐºÑƒÐ¿Ð»Ñ– дадатку больш за " "адзін раз." #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Пацьвердзіць" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Выдаліць ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "СкаÑаваць" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Пацьвердзіць" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "УÑталÑваць" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} байт" msgstr[1] "{1} байты" msgstr[2] "{1} байтаў" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Пошук дадаткаў" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Крама Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Болей дадаткаў у краме" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Шукаць '%s' у краме." #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Больш цікавых дадаткаў у гÑтай краме" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "УÑÑ‘" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Пошук у краме" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ УСТÐЛЯВÐÐÐ" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "ÐÑьць Ñž наÑўнаÑьці" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u знайдзены" msgstr[1] "%u знойдзены" msgstr[2] "%u знойдзена" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "РÑкамендавана" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Лінза Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ Ñ– ÑžÑталёўкі дадаткаў" #~ msgid "Scope for searching the click app store" #~ msgstr "Шукаць у краме" ./po/zh_CN.po0000644000015600001650000001571212676763577013114 0ustar jenkinsjenkins# Chinese (Simplified) translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: Anthony Wong \n" "Language-Team: Chinese (Simplified) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Scopes" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "应用" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "出版商/创作者" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "销售商" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "网站" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "è”系人" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "许å¯è¯" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "æ›´æ–°" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "版本å·" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "æœ€åŽæ›´æ–°" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "首å‘" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "大å°" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "版本" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "å…è´¹" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ 已购买" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "ä¿¡æ¯" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "新增" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "评论" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "下载出错" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "下载或安装失败。请å†è¯•一次。" #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "关闭" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "登录出错" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "请用您的 Ubuntu One è´¦å·ç™»å½•" #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "跳转到å¸å·" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "打开" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "æœç´¢" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "å–æ¶ˆè´­ä¹°" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "å¸è½½" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "您确定è¦å–消购买 '${title}' å—?应用将会被å¸è½½ã€‚" #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "返回" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "ç»§ç»­" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "é€€è´§å’Œå–æ¶ˆæ”¿ç­–" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" "当您在 Ubuntu 商店支付应用时,你å¯ä»¥åœ¨å®‰è£…15åˆ†é’Ÿå†…å–æ¶ˆæ”¶è´¹ã€‚å¦‚æžœå–æ¶ˆçš„æ—¶æœŸå·²ç»è¿‡åŽ»ï¼Œæˆ‘ä»¬å»ºè®®æ‚¨ç›´æŽ¥è”系开å‘者退款。\n" "您å¯ä»¥åœ¨ Ubuntu 商店的应用预览页找到列出的开å‘者è”系信æ¯ã€‚\n" "请记ä½ï¼Œæ‚¨ä¸èƒ½å–消应用的支付过程超过一次。" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "确认" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "å¸è½½ ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "å–æ¶ˆ" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "确定" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "安装" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} 字节" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "用于æœç´¢å·²å®‰è£…Appçš„Scope" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "æœç´¢åº”用" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Ubuntu 商店" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "ä»Žå•†åº—èŽ·å–æ›´å¤šåº”用" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "在商店中æœç´¢â€œ%sâ€" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "ä»Žå•†åº—èŽ·å–æ›´å¤šç±»ä¼¼çš„应用" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "全部" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "用于æœç´¢Ubuntu应用商店的Scope" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "æœç´¢å•†åº—" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ 已安装" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "å¯ç”¨" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "在 Ubuntu 商店中æœç´¢åˆ° %u 个结果" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "推è" #~ msgid "Scope for searching the click app store" #~ msgstr "用以æœç´¢ Click 应用商店的 Scope" #~ msgid "Scope for searching the installed click apps" #~ msgstr "用以æœç´¢å·²å®‰è£… Click 应用的 Scope" ./po/ast.po0000644000015600001650000001533112676763577012677 0ustar jenkinsjenkins# Asturian translation for unity-scope-click # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the unity-scope-click package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: unity-scope-click\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2016-01-20 11:11-0500\n" "PO-Revision-Date: 2015-07-07 15:30+0000\n" "Last-Translator: ivarela \n" "Language-Team: Asturian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2016-03-26 05:44+0000\n" "X-Generator: Launchpad (build 17967)\n" #: ../libclickscope/click/highlights.cpp:127 msgid "Scopes" msgstr "Ãmbitos" #: ../libclickscope/click/highlights.cpp:131 #: ../scope/clickapps/clickscope.ini.in.in.h:1 #: ../scope/clickapps/apps-query.cpp:161 msgid "Apps" msgstr "Aplicaciones" #: ../libclickscope/click/preview.cpp:256 msgid "Publisher/Creator" msgstr "Espublizador/Creador" #: ../libclickscope/click/preview.cpp:257 msgid "Seller" msgstr "Vendedor" #: ../libclickscope/click/preview.cpp:258 msgid "Website" msgstr "Sitiu web" #: ../libclickscope/click/preview.cpp:259 msgid "Contact" msgstr "Contautu" #: ../libclickscope/click/preview.cpp:260 msgid "License" msgstr "Llicencia" #: ../libclickscope/click/preview.cpp:269 msgid "Updates" msgstr "Anovamientos" #: ../libclickscope/click/preview.cpp:271 msgid "Version number" msgstr "Númberu de versión" #: ../libclickscope/click/preview.cpp:272 msgid "Last updated" msgstr "Caberu anovamientu" #: ../libclickscope/click/preview.cpp:273 msgid "First released" msgstr "Primera espublización" #: ../libclickscope/click/preview.cpp:274 msgid "Size" msgstr "Tamañu" #: ../libclickscope/click/preview.cpp:283 msgid "Version" msgstr "Versión" #: ../libclickscope/click/preview.cpp:399 #: ../scope/clickstore/store-query.cpp:294 msgid "FREE" msgstr "LLIBRE" #: ../libclickscope/click/preview.cpp:403 #: ../scope/clickstore/store-query.cpp:346 msgid "✔ PURCHASED" msgstr "✔ MERCADA" #: ../libclickscope/click/preview.cpp:437 msgid "Info" msgstr "Info" #: ../libclickscope/click/preview.cpp:455 msgid "What's new" msgstr "Qué hai nuevo" #: ../libclickscope/click/preview.cpp:471 msgid "Reviews" msgstr "Revisiones" #: ../libclickscope/click/preview.cpp:490 msgid "Download Error" msgstr "Fallu de descarga" #: ../libclickscope/click/preview.cpp:491 msgid "Download or install failed. Please try again." msgstr "La descarga o la instalación fallaron. Inténtalo de nueves." #: ../libclickscope/click/preview.cpp:493 msgid "Close" msgstr "Zarrar" #: ../libclickscope/click/preview.cpp:498 msgid "Login Error" msgstr "Fallu de rexistru" #: ../libclickscope/click/preview.cpp:499 msgid "Please log in to your Ubuntu One account." msgstr "Accedi a la to cuenta d'Ubuntu One." #: ../libclickscope/click/preview.cpp:501 #: ../libclickscope/click/preview.cpp:509 msgid "Go to Accounts" msgstr "Dir a Cuentes" #: ../libclickscope/click/preview.cpp:850 msgid "Open" msgstr "Abrir" #: ../libclickscope/click/preview.cpp:853 #: ../libclickscope/click/preview.cpp:945 msgid "Search" msgstr "Guetar" #: ../libclickscope/click/preview.cpp:873 #: ../libclickscope/click/preview.cpp:1179 msgid "Cancel Purchase" msgstr "Encaboxar merca" #: ../libclickscope/click/preview.cpp:878 msgid "Uninstall" msgstr "Desinstalar" #: ../libclickscope/click/preview.cpp:1002 msgid "" "Are you sure you want to cancel the purchase of '${title}'? The app will be " "uninstalled." msgstr "" "¿De xuru que quies encaboxar la merca de «${title}»? Desinstalaráse " "l'aplicación." #: ../libclickscope/click/preview.cpp:1019 msgid "Go Back" msgstr "Dir p'atrás" #: ../libclickscope/click/preview.cpp:1023 msgid "Continue" msgstr "Siguir" #: ../libclickscope/click/preview.cpp:1030 msgid "Returns and cancellation policy" msgstr "" #: ../libclickscope/click/preview.cpp:1032 msgid "" "When purchasing an app in the Ubuntu Store, you can cancel the charge within " "15 minutes after installation. If the cancel period has passed, we recommend " "contacting the app developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview " "page in the Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more " "than once." msgstr "" #: ../libclickscope/click/preview.cpp:1066 msgid "Confirmation" msgstr "Confirmación" #. TRANSLATORS: Do NOT translate ${title} here. #: ../libclickscope/click/preview.cpp:1069 msgid "Uninstall ${title}?" msgstr "Desinstalar ${title}?" #: ../libclickscope/click/preview.cpp:1078 msgid "Cancel" msgstr "Encaboxar" #: ../libclickscope/click/preview.cpp:1082 msgid "Confirm" msgstr "Confirmar" #: ../libclickscope/click/preview.cpp:1171 msgid "Install" msgstr "Instalar" #: ../libclickscope/click/utils.cpp:54 msgid "{1} byte" msgid_plural "{1} bytes" msgstr[0] "{1} byte" msgstr[1] "{1} bytes" #: ../scope/clickapps/clickscope.ini.in.in.h:2 msgid "Scope for searching installed apps" msgstr "" #: ../scope/clickapps/clickscope.ini.in.in.h:3 msgid "Search apps" msgstr "Guetar apps" #: ../scope/clickapps/apps-query.cpp:263 #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:1 msgid "Ubuntu Store" msgstr "Tienda Ubuntu" #: ../scope/clickapps/apps-query.cpp:264 msgid "Get more apps from the store" msgstr "Consigui más aplicaciones na tienda" #: ../scope/clickapps/apps-query.cpp:270 #, c-format msgid "Search for '%s' in the store" msgstr "Guetar «%s» na tienda" #: ../scope/clickapps/apps-query.cpp:277 msgid "Get more apps like this from the Store" msgstr "Más aplicaciones como esta na Tienda" #: ../scope/clickapps/apps-query.cpp:318 #: ../scope/clickstore/store-query.cpp:531 msgid "All" msgstr "Too" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:2 msgid "Scope for searching the Ubuntu app store" msgstr "" #: ../scope/clickstore/com.canonical.scopes.clickstore.ini.in.in.h:3 msgid "Search store" msgstr "Guetar na tienda" #: ../scope/clickstore/store-query.cpp:341 msgid "✔ INSTALLED" msgstr "✔ INSTALÓSE" #: ../scope/clickstore/store-query.cpp:491 msgid "Available" msgstr "Disponible" #: ../scope/clickstore/store-query.cpp:497 #, c-format msgid "%u result in Ubuntu Store" msgid_plural "%u results in Ubuntu Store" msgstr[0] "%u resultáu na Tienda d'Ubuntu" msgstr[1] "%u resultaos na Tienda d'Ubuntu" #: ../scope/clickstore/store-query.cpp:507 msgid "Recommended" msgstr "Recomendaes" #~ msgid "Scope for searching the installed click apps" #~ msgstr "Lente pa guetar les apps instalaes" #~ msgid "Scope for searching the click app store" #~ msgstr "Ãmbitu pa guetar na tienda d'aplicaciones" ./tools/0000755000015600001650000000000012676763577012267 5ustar jenkinsjenkins./tools/init-departments/0000755000015600001650000000000012676763577015556 5ustar jenkinsjenkins./tools/init-departments/README0000644000015600001650000000070212676763577016435 0ustar jenkinsjenkinsThis tool creates/updates the list of departments and package:department mapping for currently installed apps, and stores it in a sqlite database file. This db should then be put in the unity-scope-click-departmentsdb package or manually copied to the location where click scope expects it to be (~/.cache/click-departments.db). Usage: init-departments DBFILE LOCALE1 [LOCALE2 ...] for example: init-departments click-departments.db "" pl_PL de_DE ./tools/init-departments/init-departments.cpp0000644000015600001650000002525612676763577021563 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include enum { // invalid arguments DEPTS_ERROR_ARG = 1, // network request errors DEPTS_ERROR_NETWORK = 2, // sqlite db errors DEPTS_ERROR_DB = 5, // click errors DEPTS_ERROR_CLICK_PARSE = 10, DEPTS_ERROR_CLICK_CALL = 11, DEPTS_ERROR_CLICK_UNKNOWN = 12 }; std::list> NON_CLICK_APPS = { {"address-book-app.desktop", "accessories"}, {"dialer-app.desktop", "accessories"}, {"mediaplayer-app.desktop", "sound-video"}, {"messaging-app.desktop", "accessories"}, {"ubuntu-system-settings.desktop", "accessories"}, {"webbrowser-app.desktop", "web-browsers"} }; QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); void noDebug(QtMsgType, const QMessageLogContext&, const QString&) {} int main(int argc, char **argv) { if (argc < 3) { std::cerr << "Usage: " << argv[0] << " DBFILE LOCALE1 [LOCALE2 ...]" << std::endl; return DEPTS_ERROR_ARG; } const std::string dbfile(argv[1]); const std::set locales(argv + 2, argv + argc); if (!getenv("INIT_DEPARTMENTS_DEBUG")) qInstallMessageHandler(noDebug); std::unique_ptr db; try { db.reset(new click::DepartmentsDb(dbfile)); } catch (const std::exception &e) { std::cerr << "Failed to open departments database " << dbfile << std::endl; return DEPTS_ERROR_DB; } auto nam = QSharedPointer(new click::network::AccessManager()); auto client = QSharedPointer(new click::web::Client(nam)); click::Index index(client); std::vector cnc; std::promise qt_ready; std::promise bootstrap_ready; std::promise pkgs_ready; auto qt_ready_ft = qt_ready.get_future(); auto bootstrap_ft = bootstrap_ready.get_future(); auto pkgs_ft = pkgs_ready.get_future(); std::atomic return_val(0); std::atomic::size_type> num_of_locales(locales.size()); std::atomic num_of_pkgs(0); // // a thread that does bootstrap request std::thread net_thread([&]() { qt_ready_ft.get(); for (auto const locale: locales) { qt::core::world::enter_with_task([&return_val, &bootstrap_ready, &index, &cnc, &num_of_locales, &db, locale]() { std::cout << "Getting departments for locale '" << locale << "'" << std::endl; cnc.push_back(index.bootstrap([&return_val, &bootstrap_ready, &num_of_locales, &db, locale](const click::DepartmentList& depts, const click::HighlightList&, click::Index::Error error, int) { std::cout << "Bootstrap call for locale '" << locale << "' finished" << std::endl; if (error == click::Index::Error::NoError) { try { db->store_departments(depts, locale); std::cout << "Stored departments for locale '" << locale << "'" << std::endl; } catch (const std::exception& e) { std::cerr << "Failed to update departments database: " << e.what() << std::endl; return_val = DEPTS_ERROR_DB; } if (--num_of_locales == 0) { std::cout << "All locales processed" << std::endl; bootstrap_ready.set_value(); } } else { std::cerr << "Network error" << std::endl; return_val = DEPTS_ERROR_NETWORK; bootstrap_ready.set_value(); qt::core::world::destroy(); return; } })); }); } }); // // a thread that iterates over all packages and performs details requests // it initially blocks on bootstrap_ft and pkgs_ft future // (waits until bootstrap finished and main thread gets all the click packages) std::thread details_thread([&]() { bootstrap_ft.get(); auto const pkgs = pkgs_ft.get(); num_of_pkgs = pkgs.size(); // if click list failed or nothing to do, then end this thread and stop Qt bridge if (return_val != 0 || num_of_pkgs == 0) { std::cerr << "No packages to process or an error occurred, not fetching departments" << std::endl; qt::core::world::destroy(); return; } std::cout << "Getting package details for " << num_of_pkgs << " packages" << std::endl; // // note: this queues requests for all the packages; the number of packages to process // is kept in num_of_pkgs which gets decreased, the last processed package will stop Qt bridge. for (auto const& pkg: pkgs) { auto const pkgname = pkg.name; qt::core::world::enter_with_task([&return_val, &index, &cnc, &num_of_pkgs, pkgname, &db]() { cnc.push_back(index.get_details(pkgname, [&return_val, &num_of_pkgs, pkgname, &db](const click::PackageDetails& details, click::Index::Error error) { std::cout << "Details call for " << pkgname << " finished" << std::endl; if (error == click::Index::Error::NoError) { try { std::cout << "Storing package department for " << pkgname << ", " << details.department << std::endl; db->store_package_mapping(pkgname, details.department); } catch (const std::exception& e) { std::cerr << "Failed to update departments database: " << e.what() << std::endl; return_val = DEPTS_ERROR_DB; } } else { std::cerr << "Network error" << std::endl; return_val = DEPTS_ERROR_NETWORK; } if (--num_of_pkgs == 0) { std::cout << "All packages processed" << std::endl; qt::core::world::destroy(); } })); }); } }); // // enter Qt world; this blocks until qt::core:;world::destroy() gets called qt::core::world::build_and_run(argc, argv, [&pkgs_ready, &qt_ready, &return_val]() { qt::core::world::enter_with_task([&return_val, &pkgs_ready, &qt_ready]() { std::cout << "Querying click for installed packages" << std::endl; iface.get_installed_packages([&return_val, &pkgs_ready, &qt_ready](click::PackageSet pkgs, click::InterfaceError error) { if (error == click::InterfaceError::NoError) { std::cout << "Found: " << pkgs.size() << " click packages" << std::endl; } else { if (error == click::InterfaceError::ParseError) { std::cerr << "Error parsing click output" << std::endl; return_val = DEPTS_ERROR_CLICK_PARSE; } else if (error == click::InterfaceError::CallError) { std::cerr << "Error calling click command" << std::endl; return_val = DEPTS_ERROR_CLICK_CALL; } else { std::cerr << "An unknown click error occured" << std::endl; return_val = DEPTS_ERROR_CLICK_UNKNOWN; } } qt_ready.set_value(); pkgs_ready.set_value(pkgs); // this unblocks net_thread }); }); }); net_thread.join(); details_thread.join(); try { for (auto const& app: NON_CLICK_APPS) { db->store_package_mapping(app.first, app.second); } } catch (const std::exception &e) { std::cerr << "Failed to insert non-click appsint database" << std::endl; return DEPTS_ERROR_DB; } std::cout << std::endl << "Summary:" << std::endl << "Number of department mappings: " << db->department_mapping_count() << std::endl << "Number of department names (all locales): " << db->department_name_count() << std::endl << "Number of applications: " << db->package_count() << std::endl; return return_val; } ./tools/init-departments/CMakeLists.txt0000644000015600001650000000072312676763577020320 0ustar jenkinsjenkinsset (CMAKE_INCLUDE_CURRENT_DIR ON) set (INITDEPTS init-departments) find_package (Qt5Core REQUIRED) find_package (Threads) include_directories ( ${CMAKE_SOURCE_DIR}/libclickscope ${JSON_CPP_INCLUDE_DIRS} ) add_executable (${INITDEPTS} init-departments.cpp ) qt5_use_modules (${INITDEPTS} Network Sql) target_link_libraries(${INITDEPTS} ${SCOPE_LIB_NAME} ${CMAKE_THREAD_LIBS_INIT} ) install(TARGETS ${INITDEPTS} DESTINATION sbin) ./tools/CMakeLists.txt0000644000015600001650000000004312676763577015024 0ustar jenkinsjenkinsadd_subdirectory(init-departments) ./COPYING0000644000015600001650000010437412676763577012173 0ustar jenkinsjenkins GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ./MAINTAINERS0000644000015600001650000000006112676763577012621 0ustar jenkinsjenkinsAlejandro J. Cura ./AUTHORS0000644000015600001650000000014312676763577012175 0ustar jenkinsjenkinsAlejandro J. Cura Manuel de la Pena ./HACKING0000644000015600001650000000365712676763577012131 0ustar jenkinsjenkinsunity-scope-click hacking guide =============================== Getting unity-scope-click ------------------------- To get the main development branch of unity-scope-click: $ bzr branch lp:unity-scope-click/devel Getting dependencies -------------------- To succesfully build the scope there are a few packages required: $ sudo apt-get build-dep unity-scope-click Building the scope ------------------ This scope is built using cmake. Here's an example on how to build it: $ mkdir build $ cd build $ cmake .. $ make -j 8 Running the unit tests ---------------------- $ make check Or, if you want to run the tests under valgrind: $ make check-valgrind Running the autopilot tests --------------------------- $ make test-click-scope-autopilot-fake-servers Running the autopkgtest tests ----------------------------- $ adt-run --unbuilt-tree . -o /tmp/adt-clickscope \ -U --apt-pocket proposed --- qemu ~/adt-vivid-amd64-cloud.img Running the scope ----------------- You will usually run the scope inside a unity8 session, but when developing a good way to test it is with unity-scope-tool. Inside the build directory do: $ mkdir clickscope $ (cd clickscope;ln -s ../scope/click/libclickscope.so .; ln -s ../data/clickscope.ini .) $ U1_DEBUG=true unity-scope-tool clickscope/clickscope.ini Running the coverage tests -------------------------- To run the coverage tests you'll need to install the gcovr and lcov packages. Then you need to compile with coverage enabled, like this: $ cmake .. -DCMAKE_BUILD_TYPE=coverage $ make $ make test $ make coverage If you want the coverage report in pretty HTML, use: $ make coverage-html The html will be left in the coveragereport directory. Updating departments db ----------------------- See tools/init-departments/README for details about how to re-create departments database file found in data/departments.db. ./bin/0000755000015600001650000000000012676763577011677 5ustar jenkinsjenkins./bin/enable-purchases0000755000015600001650000000341612676763577015052 0ustar jenkinsjenkins#!/bin/sh # # Copyright (C) 2014 Canonical Ltd. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # In addition, as a special exception, the copyright holders give # permission to link the code of portions of this program with the # OpenSSL library under certain conditions as described in each # individual source file, and distribute linked combinations # including the two. # You must obey the GNU General Public License in all respects # for all of the code used other than OpenSSL. If you modify # file(s) with this exception, you may extend this exception to your # version of the file(s), but you are not obligated to do so. If you # do not wish to do so, delete this exception statement from your # version. If you delete this exception statement from all source # files in the program, then also delete it here. # set -e CLICK_STORE_ENABLE_PURCHASES=1 /sbin/initctl set-env --global CLICK_STORE_ENABLE_PURCHASES=$CLICK_STORE_ENABLE_PURCHASES gdbus call --session \ --dest org.freedesktop.DBus \ --object-path / \ --method org.freedesktop.DBus.UpdateActivationEnvironment \ "[{'CLICK_STORE_ENABLE_PURCHASES', '$CLICK_STORE_ENABLE_PURCHASES'}]" /sbin/restart scope-registry echo "Purchases enabled and scope-registry restarted." ./bin/install-helper0000755000015600001650000000522512676763577014554 0ustar jenkinsjenkins#!/bin/bash # # Copyright (C) 2014 Canonical Ltd. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # In addition, as a special exception, the copyright holders give # permission to link the code of portions of this program with the # OpenSSL library under certain conditions as described in each # individual source file, and distribute linked combinations # including the two. # You must obey the GNU General Public License in all respects # for all of the code used other than OpenSSL. If you modify # file(s) with this exception, you may extend this exception to your # version of the file(s), but you are not obligated to do so. If you # do not wish to do so, delete this exception statement from your # version. If you delete this exception statement from all source # files in the program, then also delete it here. # FILE_NAME="$1" PACKAGE_NAME="$2" SCOPES_OBJECTPATH=/com/canonical/unity/scopes SCOPES_INTERFACE=com.canonical.unity.scopes SCOPES_METHOD=InvalidateResults LAUNCHER_BUS=com.ubuntu.unity.launcher LAUNCHER_OBJECTPATH=/com/ubuntu/unity/launcher/installations LAUNCHER_INTERFACE=com.ubuntu.unity.launcher.Installations LAUNCHER_METHOD=completeInstallation function invalidate-scope { SCOPE_ID=$1 dbus-send $SCOPES_OBJECTPATH $SCOPES_INTERFACE.$SCOPES_METHOD string:$SCOPE_ID } function install-package { FILE_NAME="$1" pkcon -p install-local "$FILE_NAME" } function app_id-from-package_name { PACKAGE_NAME=$1 IFS=_ TRIPLET=($(ubuntu-app-triplet $PACKAGE_NAME)) unset IFS echo ${TRIPLET[0]}_${TRIPLET[1]}_current-user-version } function complete-installation { PACKAGE_NAME=$1 APP_ID=$2 dbus-send --dest=$LAUNCHER_BUS --type=method_call $LAUNCHER_OBJECTPATH $LAUNCHER_INTERFACE.$LAUNCHER_METHOD string:$PACKAGE_NAME string:$APP_ID } if install-package "$FILE_NAME" ; then invalidate-scope clickscope invalidate-scope com.canonical.scopes.clickstore APP_ID=$(app_id-from-package_name $PACKAGE_NAME) complete-installation $PACKAGE_NAME $APP_ID else PKCON_STATUS=$? NO_APP_ID="" complete-installation $PACKAGE_NAME $NO_APP_ID exit $PKCON_STATUS fi ./bin/CMakeLists.txt0000644000015600001650000000012512676763577014435 0ustar jenkinsjenkinsinstall(PROGRAMS install-helper enable-purchases DESTINATION lib/unity-scope-click/) ./data/0000755000015600001650000000000012676763577012040 5ustar jenkinsjenkins./data/ubuntu-store-scope.png0000644000015600001650000003453412676763577016342 0ustar jenkinsjenkins‰PNG  IHDRôxÔú9#IDATxíÝ{°$×]àsŸsï>$ëeɶ#á‡l=‘l=m=0ØŒ“`N…TB¥  ¨¤’PEþHH¥’@’Jþ©JUœ TR€ $`ã'²,Y¶¬‡-ɃdY]í®´+íãîݽw'çôÌ™;÷îÜÕ>¦{fç|mÏž™žžî>߯uû×ݧOOíùøí` @€Š˜.ª¶*K€T(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Hl @ @ @AWe °  @€$]•  @€€À6@€ tU&@€Û(P@P`ÐU™Ì" @`2Z70L_ò×j¯ÌÑÇî +ß~¨öåXõ Hêõ5wLmÙ¶þô¯†©Åmµ.¯}ôph}ßía߯ýx­Ë1sêÔol jXxÿO‡°°ÚÇŽÖº¬ö+»ÂÔÖmaþÚ÷„#Ü]ë²Ìœz´¨××Ü Ô.Žþàc!îýë]ÖêJh>ÚˇBëöÖ»,s'@ v @íÄ@ ^ÎÑÿ–¸v­¯öÒËùdz ³o|k˜}Ë÷Õ[1s'@ V @­¼fN ~…Û>±պ÷ÿ¡}`_oíC¯„ÅýLý•³jÔFkÆêh½ûÃaúü×Ö¾ öÒ+1Éè»Äpl¥: 0ó=o©}Ù@€@=€z\Í•@#[>üã¥ÿ•¸¬zOÿ‡xÄ¿qíÃûÃÂíîh$ÐB  @ ¨fI  ùëïˆGÿ׿¨ÔøïÈÒñËY9Z7ܦ/¸äøïŒ!@`ì$c"+H`°ÀÂ]ëýþ~XcÛû_ÜtV©aàâfÓï}A€Àø è`|ccÍl*0÷¶w†¹·]Ú«õÞ÷Ÿn-l/ˆë‘.1?¤Žæ¯¼)е—ÒtÎgΖHYO} ïÿÛÍýÇûþ×5þë[‡ÞÛ©ØÑúèyxCà,œ%²š²Àô…¯ó×½'”§Vùõ6þkØüô^ŸöòÁ°ðžÔÞ q^ž’áH†ãh.è´ü÷ý×=¤†±àÉ Çb÷Àßr“šŠ±ŒE¬“HGÿ­[¸sZ¾ÞƒÿÐ>´ÿäN0„©x—Àá°ø¿{r•0c! ‹0X ''Њ½þ5rß{56ê;þÞÿ^F¯ûÇý~ç•V=^ŽH jÝüôÉ@€ÀY 8 ‚d $ê¡?wýT§ÛßšIÚS¿ÿ†©¸×O¯ÞÞ¿û9Žk>æoþà€E€À8 HÆ1*Ö‰ÀÖm?›Û/Äoj>÷çß9úï®Dÿþ¾·^qòø¼>ñ¬Áì›<$¨Gä 1Œy€¬,°;þ©åÕ¼ÿOü +}ÿÚqOŸ_½½~×,½úÆ¥v œC¦$0Ö€±•#ÐHGÿM<ô§ZZê÷?Ýç{u¤ÿ©NýwÖ§ú·w9 oÚx`îïŠÝ¿®oBo G À8FÅ:Ø €N¯ù°»¦2ö,Øë÷¿ÚñoX‘<.•y¨ø!‹mtœq”ÆW@0¾±±f*ªÛß·\ßWçÛkUiWGÿqO^í÷ííóÛ|V •ëÎtW+Žk¯ óWߢc Z#eæÎ\@pæ†æ@ VÆŽþc‚‘Zò¯ 1á˜J¯µ1Õ»*Ø0²×7]ü¼pÇOôð–qŒ[D¬>ªãŸ[>þc·¿5õÏó­æSu/œW îÅ«Æésß¾7.OË Óe„Öïï›À[ÆM@0n±>úRËÿö±šŸø×]^{)öüW Ý}>Åš—¦¶;Ò/@UA`ì$c+D #:þiÝþ¥[òj>ü_YŽ·þé.8)èªKÝKy|>)PMÛ·~Ýñí#‡â-?ž§V 0f€1 ˆÕ!ªŽZ©ãŸú‡êô>âï?ÝŸwò½qqDoº¾õÊãÖ-h‡éKÞfßú}}zK€À¸HÆ%ÖƒÀÅ;²‘ޱcÝ[ÿz{ûµ5é]ÛO£ò¡}|›ø«¡܆ߧi–ijv±ÆJ@0Vá°2:kÿôZ¯é2@{9öü×îíÑׇ ·OOoºCo\Ëê @çsþºúEìhþÊu ÔGå-qŒK$¬>Öm?Ú+M5þë¿õ/®ÄÀÓùi|÷Õ{Gt§Í_U;ý ¿o/Ƕn ì‹®·ÆC@0q°z³ox[˜{óµñó&Gå½)‡ðæhlüwlµ3£j/^í»3Žï«qñcotÿ¸©Þ×Õªi{v~”>ÆŽÒ-S‹ÛªÉüC€ÀxHÆ#Ö‚@O`!>ò·]µÈoàôêø§Úq§Å÷ï¼ÓÇ Hü¾óÿîOºßŸÌïg¦C+õg` @`l$c +B î_Ó­ uüVWC;XËúB÷ê©`ï»îÑ~ÎIúƯu”Þý]ÿïcÇ@n Ì>Jã! 8X •@uô¿švÊyO[_¹´gç†åô¡»=ûÇ]}çÕ¿ÏïM9¨€´î9wè«ÇôösÃüµïéýÔF+ ­¿¥X'PÝúWuü³nt-žúÝO„÷Ýç÷ö±ÜШ/®Ê<]*{;øüfí·i»«Ü× °}ôphi XK,Í”ÀéHNGÍoÔ ÐºõGBh¨ãŸ•Cûþ¯ßžùü·ÓO»ôÞŽ¿ªgÞɧñÝ×Ú› ÓV?Ø0®û£ØØpî²·»%°K¤ 0j À¨#`ùºéôj1_Ý“Ÿî˯ñµã Ÿ GcÀO‡ƒÏ|§Ú¯ Dÿ•‡þ/òø“—¦ïÒY€Åý½¾1Þ 0* À¨ä-—@ŸÀÜÛn³—^ÞÙé÷¯ëísŸú½°m®¶Î†ðÌçâY€jȇ÷±ì;uß[‡uãú§S¤½³'øýêÑ0Ímn ì¡zC`t€ÑÙ[2ž@:ýß^9Üû\ç›}?¦öî¬v×­™vØ}ÿŸ…•C‡:‹¬öëÕÞ|mŽ—vðñëüª¦ì—¾èªéúÆÍ̸%°Ç[£ŒJÞr t¦/x}Ü!ÆÇæ¶Å1ù{}åwÿäwÃö¹5þsçCØqß»Gñi|\vµÓŽo«ývßλ¿á¼~uf ý.'ø}šäh¼%ðŽæ‰•ŒH@0"x‹%:ÝþÆ[ÿêÛç÷æ½´kgØÿà—ÃÜt|R_w¿¾e¶žK«ýzµ‡ïc™_yESÙ×.eýë3‡<]*× ßMo?/>%ðúußø@€@³€f½-Àq ©ãŸxm|ýž´¯:¼÷;ïþtØÚ=ú_˜ 1èäO¿¼;ìûöã}ë—™ïñï;ïÎþ{ýºæqéL@òo×õ&¸ö}»: à)™KI`€Q¨[&®@ºö?õš óØÿmGüihÅ ½Òpî|;<óÙÿ·¶ƒ¯öÕÝ={¯ñ_gÚÞ¿yüZV°öûj¢îï}_=%ðf·ö0½!м€ ysK$ÐhÝòÃ[ÿzcê{³#ýÏé<ùo>îøÓ%€”ÌÄ2½öókaéÅÝqº;îÞ¾oª¯â?ÕÑ~žní')»ã{óü}jô¸pçOöÍÜ[š4©mYúfßðÖ0÷–kâ™ÿfÿíø³OW·þ¥UH§ÿóϤK;ïýboÿ¿î’DÞ§W;õî/óiþ<£ªLv‡AßWIEü>M–žxóòÔJ4 nq²@ç©Í5þ[þ‹GzGûéÚz @¼4ðÂ}_XÛïç#øTæF}ùG©Ìãª+ ½ amŠAß§ió+Í×S×¼¼#а@ߟ†—lq HOýKKßÿ¼G¬¯|ê÷>Ñ»õo1vþÓ?äKiÜll ¸ç‘¯vŽÐû'x4'èí÷óº÷ý(—ʼ×_7.Ž^YŽ·j اæ-Æ$Q[5Ö­ñÚÿ|kmDïV û¼7^ïïÜú—®ÿoòY€í±wÀ_þbü:î©×í¬ó‡ôUú.}îºßçï6~ŸÇ÷Ï´šE;̼þM!]1 Ь€ YoK#P TOýKýþ70ì~àË~ÿã²òm›ÆçÆ€¿ùÕ°ôÒ®î$yÇ?övâý¿Þðýq_Åï{É@µÇŸÓ¼r߉i Ø/ç=F$0[5ùëަϷþÕø°Ÿþy?ó'¿ßkü·eÃéÿµµŠ »ßU¿ü…øUw‡&ªÎðǪuî~ô}5aúAª6·;— Ö®&tçŸ8õ­!]1 М€ 9kK"P ´nûáØïlü×À°ÿé¿ íçÿªÚU§£üÜûß E§ËéûÅô|€{¿¸a’´³î¾ è럮û“ª˜Š¿îüoà™„¹9ÏØ î#º$u ›?>é ^毺%¯Æ±#â:Ëg>õÉê‰iòuþ¾ÕY÷6íºóÝ3/ï {Šóз__[ßøeŸ§KåÚ!þÚ×yºTöêÞw‡˜-Þ¡O€Ì¡$Є€  eË ÐX¼ëoÅ}3GÿÆ_î5þ{µ ­b¾C =*xǽ_ˆ;ó¸Çî]ÃïV"«væÝ={·H{ü|¤ßù:¨ÞuœŠô¹;.Ï+Ž™>÷¼0çù È@  @#ÌB #к9>õou¥ŽÝÜÿ¬–u2;ÿ4aj8ÿ*T=>rXÚ“¦oã—ÕÎ<ïÀóN<}³ö¿ÎÎ=O›~—†þßt߯K ¦ªÇ!WwGt~à_jÔ lö²@µs›‹-íšjü÷©µÆý=ÿåõÙ¬ÌӦƀë.¤¤«½SøíÎn½º’Q}‘¾ŒC5¢[Ïüq“ï«é»¿‰[7¾_cÀž‰7êÔëkîz­ê©ÍÜú×iü÷dµƒÎýþ÷VäUÞäéÓCƒžûìÅ©ó{çíÚ±~÷(¿ï4~gÖ}Gûù»þ£ýAãÒãøÔ8²z>BgFþ%@ F @¸fM TýþïÕ£â<²ÆrÇ—>sÒÿ6®FÚ}çÆ€³±1à¾?´ÊòŽ¿“twþë~ÜÝñWÅ€ïóŽ¿ÿ7ë¦âc‘ïühÿÞ P“€ &X³%Ð/°pglüŸ~×;=žO“×Tî¹ç3Uã¿t-þ4þ+Ï;=~¾ÚçWõI;ìjèžæÏs9èû4®ŸËÞ¸ÞâDi|ì­ðÜóccÀò•Ô$pjZ³%0¡~ÿßÝiü×·¬ißv?pß)7þÛHßßðÅ{?VêL’׿·Ï{òîŽü¸ïãÏŽ§Í Z·àÎøöÑx u•l @ V @­¼fN _÷ÞxNý]ð )þ߯ߧád[ÿZ…Üpû|»ºí¨½w=¿»ÓÏ?N«WßøAãÒDy|þm5¢û!>¹u“Æ€=oÔ$ ¨ Öl dêiw þ_Ú½3|ä¾ê6¾Ü˜/¯Ç©–ù¡A)™xî3¾q¯Ý=´ïî½Ù0iß÷óÔQÐqCw^ÝÓí£‡õ xœ‘†+ ®§¹X'ÿ;îMëÆÕùaσ_ éö½4œÉÑú}Úm§y¤rõù§Âþï>•Fw†Ü ¯:ÐÝñw‹ÎÁ|߇ü¶—@¤¯2Ä%-Þ¡1à«(ùšÀ HÎˆÏ œX =å®}t)N´þ·®ÏÏ}úª¾üO·ñ߯Úä$bk¼‚±ãžÏm8²ïNÝ×õïÚïc}jƀׯÍÒ;†* *§™XHÿZ7þ`±ƒ›&öÿûŸ~2̼üBµùôýÚڜ޻ü˜àV|@О{c0¨ñ^—öï½!å÷?µ}å¥1`ÓµHja5S±ñßµïíÞú׌Æ3Ÿþ?½{ÿs¾a,9?&8u+¼û¡¯tÎT3îžÛϧø«3ûùÖ˜”( ‚¥Y €ba,¤kØ 5þK‡Ì/?|_ìÀ§]õå¢ÇþžjÝr?©1àŽ/ųi¨öóÝÓü1ÝOãPÿ§GRß UŠë–áà CÑ<lè4þ{ㆱõ}ÜÿµŽtü3Ì£ÿ´Æ)™H—R»‚ƒßøJXYê,gpƒ€!×1&‹wüÄgjv$ €í€@ ½ÆÃ= ®®:`Þq÷gCz„o†uý¿Ÿ%7L=î¸;·ˆS4P¿és^ã1ÁýÁðžÀ$C‚4Y êùïê[:ÿØC®:P™§³òù¶½¼.Ã*Óe€t& = è…/6ζ=wÕe=+”æC ' èQxC`8ó×¾'öü×½8³<á\ÒuùÜwGÿyáù,Àq}ä ê*Óc‚ßu—Æ€uùšo±€bC¯âu ´nù`lü·gßÌQr:"OGæÃº÷3—ܶ`{Ìmv|igÒã”  M@04J3"O“_ðº0wùUaêØ±^?8¹?œ:ÊO?UõÒ—ìë<úOóO—fã_Œt§Aê Žúl:Ïø€ =¦( O@0¿ÙdõŒ?z(>&Øe€zp͵D @‰QWçZæ¯{O˜jð±¿éÞÿÙ—wUuÉGæµT¬o¦ùNƒNŸ÷‡•C¹O€¾‰êzÎÇ»+¦¶l«k æK ( @QáVÙ:Z7Ç£Ó#‡â"š9-¾ûÁûC:OC>2¯>ÔüO^VÕ'@z@PCõMË™š™­ºX®¹ŠfO  @aVɺRã¿ù+oŒû¨¸Cn`ÿ¿rð`xé¾ÏW-ÿëº÷3³Ô'@:î<Øùåx úö–Ÿ¬Xu±¼ÙÊO€ÀI HNšÊ„6¨îý¯nýÛ|ša~³ë¡ûÃÖnã¿|D>Ìù¿Ú¼ò2SŸK{:—!^í7Ãú~öâ7„Ùïy˰fg>Šz¦Àâí³ÑÿìŒ ðÒéÿªŸþüWœÛl]?ó™?Œ” žˆO%\Hw[8#üé8£õõcc'0÷Öë«[ÔšZ±tÄ}䯭ȗ{çkjÙy9¹O€ÖL;ì}ø«yt3eÕðÖf–e)&X@0ÁÁUµfªžÿâ-jëÿ© @½¯=±ñ_îúwT @’Íg¦÷í {Ÿx´öz÷»NÍL{Lp3›·¥L°€`‚ƒ«jõ L-n óWåÿÔ¿¼´„g?ûÃb<òN½ò¥Æx£r;€ôÂ÷|¡ÙÕˆí-Z7 ÙeZ LX@U§YùëÞ¦fg[èþï>fº÷þòè?U8å) HIȾGîoÌ ZPûXìrùU×ËÍ.ØÒLŽ@lÂc @`£ÀÜñ–¾“Z·ýhì$?æÑó‹'1õ™O²ÿ…Â…×¼+¤[ñ¶Ä»Fy Õfû±–bLJ®†°÷É'ÃyW\yæ•<Ù9ÄK-[~ôgÃò}|R¿8úDÃmNj­LD`tS{>~Ej¾k P´Àô…—†Åüx˜;þÙ7^Q´Å$W~å»O„•˜,}úáØžç&¹ªêFàU$¯Jd‚IH;þ-ù¹Ðz÷G:Øb?þív<œ§˜«Fg“\ù’ê6¯SLM‡©©xÍb:žøŒŸ—ïùd8ôÉÿ,(i;P×u€u>”$vú[?öOÂÔâ–Ð^=âwü†2¦gb·Âó¡½t(üŸÿªJʨ¸ZX¬YxWÀ¶¿ÿëñ¨ÿÇB;õÞ·z´ š«ê:™¹Øˆ³€?þë?]÷•&]@#ÀI°ú'Píücã½vzpO:Ýo(W žùiÇ3?UcΨ (wS(±æ€£^pÞÿñê}ûH|Œmê°Ç@ Û}Äí!%+O?ÿé'˜(B@PD˜U2 T þ~ìçª?öU#¿,Çv?†ý—pô‰Âj|oxu©Å­¡uÍÍaᦻÂÜ›¯~õŒbŠxÍ?ÄSýíÅsâ†0 ß†˜ ¦$`KÜ>Ž<ø9 G#Ël\@€ÆÉ-pTÛá?…¹«nÚôšÿÁßþwaéOgT«7Ëm]}sØþS¿Vnßú´¶vAkÛ}ôþ°ÿ?üü o#0Q€‰ §Êl&ŽþÏû7Ÿ íåý'yù×>V¾û¿3òÔf/½,œûó¿“€­§öÃ&§Ž;úö¶ .qªµ=ìýår` Ž‘“$ +àIЦºl*:ùioòÀžƒ¿óaåÙoÇSÃñ^q¯36XÙñðÊûõ‹ÔYð˜¾VWÂÔÒ+v Þ”¶“´½Lº€`Ò#¬~•ÀüuïëžúO ÿÖ^+Ïüy8ü¹ÿUu “:‡ñŽÁÑ' Ë_ûüØîÿ«¼$%„Õ- kÛCµmÄqÕöâ¿. à„Xõâ>hËö0}Þ…Ûþ6€,îÇ}~:J5 [`éž? ­ïöl‡:¿©˜´§·7Ï´½¤í¦}hð%£ã~`³P@pÍ*ŸšÀìß;ü‰=ý ¸íïÈ#_êœò>µYšú$Vw|'Û»+&_¯=‰©G4IÚ.æ¿4m/i»ñ¡ÅÅb4Âl!#H]ýÚ‡cÎ Ψս»ÃôùgfµÍåø l²½Ô¶ fL`€ [äýWk‘ýꨒ«17ÞlÛ¨OÅœ Œ…€`,Â`%ê¨ôS5ü;~)®ÿo2Ì1gÃþ¿Ó(ôøZw¶›ãÇC`R$“Iõ8±À€³¼éÓ\޽ô‰ëÛÓ˜yÝåñ·c| >"x“Üð´ëì‡Î ÀÙ)ëyÚû¿õp8g“¿òsWß–¿ôÉÓž·n.0wÙ•ãÝPZõª[àÁÙaÚný…Ü<À¾9ëlÞg}UàdöçÛaû›Þ|ܤ ïù±p$> Ö0|Å»~rìX¶§ãŸÀmö?ý—Ã1Gc& # 1 ˆÕ¾@jç·ãî?3NGzë_Óç]?ü³Uº`í5ƒùî³—§¥ÓÿãùjÇî€;w€¬ß&Ò6’¶íCcè - ˜èðª\HÈ_¼'&éHoÀ«uÛ‡Ãü;ïêL˜&ö:#ƒ™×_¶|ègÆ{㛎ú¦âSli\Ú^$ãBkwæ.œ¹¡9œsËÂ3Ÿúýð†úÈÀµÝòã¿f^yì8> 0õ `8-…Û?nÿ‰ÓúmS?j§ëþ)ؤ]È3ŸúdHÛKˆ' &YÀÓ'9ºêV =ÂÞåv…íáÖßüïavËæO©KýÖýaåñûCz¿ºã©ªD9X`ö²«ª/æÞ~c˜»âÆØëßEƒ'ƒ±íŽúÓåˆÍ‡•Cý¿ôwÂkÃþp^+æ)O0˜P À„VµÖRðJìð¥å©0÷ækÂõÿì_¯}é>ÿå?GÿòáüV;œ3/è£ñvf~åÚ ÿùÖK•ôҵܥ•â¯Ý;v…¥Ý/„‹n¸¥÷½7’Àã¿õáÅï ¯]lWÍ·¦6‚hL°€6\Uë¤?â3ñŸÕØð‚…xGÀ—>[}ñŽðKˆTÿÖoVÛÅE‹~Ú^ìüm“. ˜ô«_%0Û}¥³‹3í˜LUì<ýd¸ú5,^8î«ĺ–ö¼¾ùïÿEØÿÝ'«ä0miHÛ‹À¤ h0éV¿J ýï‹ óppe*¼x¸óéòüT¸äÝw†Å‹$ÙgÒËthç=Ÿ O~ò·«ª¦3C[g;;ÿ4â5±`: ` 0É€IŽ®º­H SƒÀ<,¯N…Ý1 8Öý»Ñõ7‡êuÃÍ'¼S ÿ^yv ,íÙö}ë›a÷ƒ_©^iíSû‹âοÕ=òOãRËÿÔÐ@`Ò$“aõë l< ¾HûþWŽL…ýG×÷þÇÿf^y¶÷»&Þì|ðëáñßÿÝ&5òeÜò~9,žw~ãëqxþüpï¯Ä^ãvüÛc#¿sæ; þúWÆÑ¿†÷“,à.×IŽ®º­H§t·lhõ’Îòžw—nMmB¸ô]·„™vßµ‚us¨ïÃîo=^ßÌÇlÎ;~x$k´¸8.¼ìòNœc¼SÜSüû‡´}8õß/âý$ løs8ÉUU7±`ÜâÓ™€åÕõiG®_üÎ[C8¸7~Ú¸kX?ý0?­^ ¿ýx¸8¶@/aØýÐá²Ûïl¼ªí×Ë>ð‘ðÂ'þíÀe·bÃ?OÿHcä„ H&4°ªµ¹À¶n¯“€™-ÛÂ9×ÝÚ{¾÷ÿÍ%{¾õ­î9�ùÚŸýß,Øìܶ½îõÍVfõhØúöë.3íüóv1p# L €KTUzuôÇ~ã倭×ÞÚK¯tvþ >ðÅ'«nO|õµžŒ)¶ÆÃŽ}½qç”ÔM¯Ûbœû‡´Øù÷‹x_Š€ ”H«çqétojð•û{ßvõ±³€}î˜V–—ÃÁ¿xì¸u›äsÓíðrLzª³, &ZÕò¿ιå*Þ÷§ý'ykS· HN¤ã»‰H ¾Ò-_ç_rqØú¶ô`›t꿹׎¯­ÈÐL¼ °çñ”ø4g]-+>êwë[¯ ç/÷ĸkð7ñÿ‰«à ´8ޝÊXL§ÿÆ£ÿ†‡]±Aܹñôt4ZÊ‘èxËå–Øàr÷ㆠßÑyš`“ìíC{Ãâµ·„å¯~¦ÉÅZ±Œ]H¬Ð(Z׿/„#;¤ ­ÀÒÞ½au×ó±õ_j¸v)¢¡Ål1)ÙI/í­.Œ`5ï ßÿa Àè-r¼\¯xX›Ì\ú½aæüׯ%7{:zçר:£IU.©ïù”ì¤aqu)$ƒ¦ÝÓòf^sA˜>_×ÏU üS¬€ ØÐ«xXx×û«ÆS±AZ“¯=}5žúoW;ÿ”z”2¤3N™:—š4ÏËJ=[7Ƹ, (8øªÞ˜û !¬ÄÞÿl‘¾Çóajêp(ýø_a:ã‘’žƒþhÜG‡í«eÅx/\{åï¥ ø§§ÔP«÷ ¹7_¦âEø†‡gî¹;žþï<…¨¤Óÿ™9_H íyì›yt£åTk.ÌÆË?¥ HJ¼zWÕiàåý¾ŸJ—Nƒ§aI§ÿóf—êž^‹ñˆçb2Ôø€tÆ!Æ}á½=¯’’@q€âB®Âýóo]ÃÆ.b›vÇ#ÞÖÊRµÈþ³õB÷¤™/…ýÏ?—G7WƸÏ_/ÿ* (4𪯽_»„Ziüès÷£ß¬îƒO¤-ñúÞörÝ«®øjãq¨Î:Ä'?VÛA^)%‚$[U× ´núÁx8ÞûßðíûŸèœþO­áKR” Ò/>€†ãP-/ÆþšÛJƒº,PøŸ ‚#_xÕ§·…ù·\B;>8zíˆGºùônWr(²A2ÙýØ7‹C/Þ1þ­kn i{0(M@PZÄÕ·H§}Û‡_ŽïÜûÇeí~ôÕéÿÔ®ô3)¹ DêxÇ×Fs ½|Àe€ê¿ ÿ”& (-âê[ ´ÞŸ—Nÿ7xÿùÊáÃáàߨR޼ã+=)ýJgRB”l’Q“1©–Ÿ8Õ-¥‡Bý ôÒ«<}þ%aî ÍßÿŽþóâNÏÐÈÉP²IF£æßzmìø’Q,Ú2 ŒL@02z •ÀüÕ±ÑW<ê‹ÿ6ú¿—º§ÿgãu©œ¡#îHgÒe€¾ö•Fc’·€P]ÐÐ6Y–€ ¬x«mh½óŽŽÆûð<ý¿´÷¥pà‰GâέsÊ[ Ö ¤³Éfùéo‡dÕdlªeÅ'AVÛÅúÕò‰ÀD H&:¼*·Q æ½(>.ím|íyô‘Þéÿ|ÿûÆu+ùs¾$’.$«&c“—•¶ —JÞ Ë«» ¼˜]ãÅ÷ýxú?vý›ÿê7T¶S··NÿÞüÒ%‘d“Œ’UÓñ©–·‹jû¼ŠÆ˜8 ÀÄ…T…N$0åM'ÿh¢!·ôÒKaõ…g«¹æ#Ý!/b"f—ûhïz6$³Æ‡ø„Àjûh|ÁH`4€Ñ¸[êf¿çÍazK|ò_ƒ×þÓ²ö<úpHÝݦ!·xï|òo¿@N’U2k:NiyiûHÛ‰@ €¢¬Ž•ÀZ׿͂<÷çã½îíj矚 $›” ¥®“ÙH†Ø7DµŒdáJ Y @³Þ–6Bù·¿³ñ'ÿíöÙ0õÊ‹U­5þ{õàg£d–ìÒÓvb P€ÀÔž_Ñ. žªH ¼ôÊp`y%,Çîÿ›Úè—^|1¼æð‹ÕQíù ¦mB±¹@ŠË¾åx;àêTØ·pAX¼à‚Í'ò7é Dº ±­5¶>÷ØçnvÆO {erüV̶À®'GÅ.g{ΛÏ/µlŸÛÒ®v,NÿoIFé ÇÚíptß‹±1`çìIþ¾îrë\‡âò/Û^÷’ÌŸÀè$£5hHà’ÅvXŽ À¡ÙævŹÇ?ÿN>ÈÉ*¥¹(ž19ÖÔ©šîêm™‹Éš £',SžÕ€³:|VþTR'3GޤӼÍîUR¯mŸÊú–:m²Jf©1`ÓCJ óóš^¶åhZ@®Û´¸åL M®Ä3Mû{êâ£2KÛGÓgN]Ç/ G@0Gs9 ŽŒ`çŸXœþ?õc”f£ÚNN]É/œ™€àÌüüú,Hו›ÒSîœþ?uõd–ìF1Œb;E=-“€6¶bÒN¥éñŒû´·¯-±EþêˆÎÚœöJû!³H@p˪ž™€Æ]gæ×ô¯«dMÕ4»å$à?¯‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ H²„’$ ((تJ€²€ K(  @€@A€‚‚­ª @ ü8Ь‚÷õ‡IEND®B`‚./data/update_schema.sh0000755000015600001650000005242012676763577015204 0ustar jenkinsjenkins#!/bin/sh DBFILE=$1 if [ -z "$DBFILE" ] then echo "Usage: $0 " exit 1 fi if [ ! -w "$DBFILE" ] then echo "Cannot open $DBFILE for writing" exit 2 fi SCHEMA_VERSION=$(sqlite3 "$DBFILE" "SELECT value FROM meta WHERE name='version'") ############################################## # Update departments db from version 1 to 2 ############################################## if [ "x$SCHEMA_VERSION" = "x1" ] then # updating deptnames may fail if en_US is already there, in such case just delete entries for empty locale sqlite3 "$DBFILE" "UPDATE deptnames SET locale='en_US' WHERE locale=''" || sqlite3 "$DBFILE" "DELETE FROM deptnames WHERE locale=''" sqlite3 "$DBFILE" << _UPDATE_TO_VER2 INSERT INTO deptnames (deptid,locale,name) VALUES ('universal-access','en_US','Universal Access'); INSERT INTO deptnames (deptid,locale,name) VALUES ('office','en_US','Office'); INSERT INTO deptnames (deptid,locale,name) VALUES ('graphics','en_US','Graphics'); INSERT INTO deptnames (deptid,locale,name) VALUES ('science-engineering','en_US','Science & Engineering'); INSERT INTO deptnames (deptid,locale,name) VALUES ('internet','en_US','Internet'); INSERT INTO deptnames (deptid,locale,name) VALUES ('accessories','en_US','Accessories'); INSERT INTO deptnames (deptid,locale,name) VALUES ('developer-tools','en_US','Developer Tools'); INSERT INTO deptnames (deptid,locale,name) VALUES ('games','en_US','Games'); INSERT INTO deptnames (deptid,locale,name) VALUES ('books-magazines','en_US','Books & Magazines'); INSERT INTO deptnames (deptid,locale,name) VALUES ('education','en_US','Education'); INSERT INTO deptnames (deptid,locale,name) VALUES ('sound-video','en_US','Sound & Video'); BEGIN TRANSACTION; DROP VIEW depts_v; INSERT INTO depts (deptid,parentid) VALUES ('universal-access',''); INSERT INTO depts (deptid,parentid) VALUES ('office',''); INSERT INTO depts (deptid,parentid) VALUES ('graphics',''); INSERT INTO depts (deptid,parentid) VALUES ('science-engineering',''); INSERT INTO depts (deptid,parentid) VALUES ('internet',''); INSERT INTO depts (deptid,parentid) VALUES ('accessories',''); INSERT INTO depts (deptid,parentid) VALUES ('developer-tools',''); INSERT INTO depts (deptid,parentid) VALUES ('games',''); INSERT INTO depts (deptid,parentid) VALUES ('books-magazines',''); INSERT INTO depts (deptid,parentid) VALUES ('education',''); INSERT INTO depts (deptid,parentid) VALUES ('sound-video',''); UPDATE meta SET value='2' WHERE name='version'; END TRANSACTION; _UPDATE_TO_VER2 SCHEMA_VERSION=$(sqlite3 "$DBFILE" "SELECT value FROM meta WHERE name='version'") fi if [ "x$SCHEMA_VERSION" = "x2" ] then sqlite3 "$DBFILE" << _UPDATE_TO_VER3 BEGIN TRANSACTION; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='3d'; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='computing-robotics'; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='debugging'; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='electronics'; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='engineering'; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='ides'; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='profiling'; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='version-control'; UPDATE pkgmap SET deptid='developer-tools' WHERE deptid='web-development'; UPDATE pkgmap SET deptid='science-engineering' WHERE deptid='astronomy'; UPDATE pkgmap SET deptid='science-engineering' WHERE deptid='biology'; UPDATE pkgmap SET deptid='science-engineering' WHERE deptid='chemistry'; UPDATE pkgmap SET deptid='science-engineering' WHERE deptid='geography'; UPDATE pkgmap SET deptid='science-engineering' WHERE deptid='geology'; UPDATE pkgmap SET deptid='science-engineering' WHERE deptid='mathematics'; UPDATE pkgmap SET deptid='science-engineering' WHERE deptid='medicine'; UPDATE pkgmap SET deptid='science-engineering' WHERE deptid='physics'; UPDATE pkgmap SET deptid='games' WHERE deptid='board-games'; UPDATE pkgmap SET deptid='games' WHERE deptid='card-games'; UPDATE pkgmap SET deptid='games' WHERE deptid='puzzles'; UPDATE pkgmap SET deptid='games' WHERE deptid='role-playing'; UPDATE pkgmap SET deptid='books-comics' WHERE deptid='books-magazines'; UPDATE pkgmap SET deptid='communication' WHERE deptid='chat'; UPDATE pkgmap SET deptid='communication' WHERE deptid='file-sharing'; UPDATE pkgmap SET deptid='communication' WHERE deptid='internet'; UPDATE pkgmap SET deptid='communication' WHERE deptid='mail'; UPDATE pkgmap SET deptid='communication' WHERE deptid='web-browsers'; UPDATE pkgmap SET deptid='graphics' WHERE deptid='drawing'; UPDATE pkgmap SET deptid='graphics' WHERE deptid='graphic-interface-design'; UPDATE pkgmap SET deptid='graphics' WHERE deptid='photography'; UPDATE pkgmap SET deptid='graphics' WHERE deptid='publishing'; UPDATE pkgmap SET deptid='graphics' WHERE deptid='scanning-ocr'; UPDATE pkgmap SET deptid='personalisation' WHERE deptid='localization'; UPDATE pkgmap SET deptid='personalisation' WHERE deptid='themes-tweaks'; UPDATE pkgmap SET deptid='productivity' WHERE deptid='office'; UPDATE pkgmap SET deptid='music-audio' WHERE deptid='sound-video'; UPDATE pkgmap SET deptid='universal-accessaccessibility' WHERE deptid='universal-access'; UPDATE pkgmap SET deptid='accessories' WHERE deptid='viewers'; DELETE FROM depts; INSERT INTO depts (deptid,parentid) VALUES ('travel-local', ''); INSERT INTO depts (deptid,parentid) VALUES ('productivity', ''); INSERT INTO depts (deptid,parentid) VALUES ('finance', ''); INSERT INTO depts (deptid,parentid) VALUES ('universal-accessaccessibility', ''); INSERT INTO depts (deptid,parentid) VALUES ('entertainment', ''); INSERT INTO depts (deptid,parentid) VALUES ('communication', ''); INSERT INTO depts (deptid,parentid) VALUES ('graphics', ''); INSERT INTO depts (deptid,parentid) VALUES ('science-engineering', ''); INSERT INTO depts (deptid,parentid) VALUES ('news-magazines', ''); INSERT INTO depts (deptid,parentid) VALUES ('accessories', ''); INSERT INTO depts (deptid,parentid) VALUES ('sports', ''); INSERT INTO depts (deptid,parentid) VALUES ('developer-tools', ''); INSERT INTO depts (deptid,parentid) VALUES ('games', ''); INSERT INTO depts (deptid,parentid) VALUES ('books-comics', ''); INSERT INTO depts (deptid,parentid) VALUES ('music-audio', ''); INSERT INTO depts (deptid,parentid) VALUES ('shopping', ''); INSERT INTO depts (deptid,parentid) VALUES ('education', ''); DELETE FROM deptnames; INSERT INTO deptnames (deptid,locale,name) VALUES ('books-comics','en_US','Books & Comics'); INSERT INTO deptnames (deptid,locale,name) VALUES ('business','en_US','Business'); INSERT INTO deptnames (deptid,locale,name) VALUES ('communication','en_US','Communication'); INSERT INTO deptnames (deptid,locale,name) VALUES ('developer-tools','en_US','Developer Tools'); INSERT INTO deptnames (deptid,locale,name) VALUES ('education','en_US','Education'); INSERT INTO deptnames (deptid,locale,name) VALUES ('entertainment','en_US','Entertainment'); INSERT INTO deptnames (deptid,locale,name) VALUES ('finance','en_US','Finance'); INSERT INTO deptnames (deptid,locale,name) VALUES ('food-drink','en_US','Food & Drink'); INSERT INTO deptnames (deptid,locale,name) VALUES ('games','en_US','Games'); INSERT INTO deptnames (deptid,locale,name) VALUES ('graphics','en_US','Graphics'); INSERT INTO deptnames (deptid,locale,name) VALUES ('health-fitness','en_US','Lifestyle'); INSERT INTO deptnames (deptid,locale,name) VALUES ('lifestyle','en_US','Health & Fitness'); INSERT INTO deptnames (deptid,locale,name) VALUES ('media-video','en_US','Media & Video'); INSERT INTO deptnames (deptid,locale,name) VALUES ('medical','en_US','Medical'); INSERT INTO deptnames (deptid,locale,name) VALUES ('music-audio','en_US','Music & Audio'); INSERT INTO deptnames (deptid,locale,name) VALUES ('news-magazines','en_US','News & Magazines'); INSERT INTO deptnames (deptid,locale,name) VALUES ('personalisation','en_US','Personalisation'); INSERT INTO deptnames (deptid,locale,name) VALUES ('productivity','en_US','Productivity'); INSERT INTO deptnames (deptid,locale,name) VALUES ('reference','en_US','Reference'); INSERT INTO deptnames (deptid,locale,name) VALUES ('science-engineering','en_US','Science & Engineering'); INSERT INTO deptnames (deptid,locale,name) VALUES ('shopping','en_US','Shopping'); INSERT INTO deptnames (deptid,locale,name) VALUES ('social-networking','en_US','Social Networking'); INSERT INTO deptnames (deptid,locale,name) VALUES ('sports','en_US','Sports'); INSERT INTO deptnames (deptid,locale,name) VALUES ('travel-local','en_US','Travel & Local'); INSERT INTO deptnames (deptid,locale,name) VALUES ('universal-accessaccessibility','en_US','Universal Access/Accessibility'); INSERT INTO deptnames (deptid,locale,name) VALUES ('weather','en_US','Weather'); INSERT INTO deptnames (deptid,locale,name) VALUES ('accessories','en_US','Utilities'); UPDATE meta SET value='3' WHERE name='version'; END TRANSACTION; _UPDATE_TO_VER3 SCHEMA_VERSION=$(sqlite3 "$DBFILE" "SELECT value FROM meta WHERE name='version'") fi if [ "x$SCHEMA_VERSION" = "x3" ] then sqlite3 "$DBFILE" << _UPDATE_TO_VER4 BEGIN TRANSACTION; DELETE FROM "deptnames"; INSERT INTO "deptnames" VALUES('social-networking','en_US','Social Networking'); INSERT INTO "deptnames" VALUES('travel-local','en_US','Travel & Local'); INSERT INTO "deptnames" VALUES('reference','en_US','Reference'); INSERT INTO "deptnames" VALUES('food-drink','en_US','Food & Drink'); INSERT INTO "deptnames" VALUES('communication','en_US','Communication'); INSERT INTO "deptnames" VALUES('accessories','en_US','Utilities'); INSERT INTO "deptnames" VALUES('science-engineering','en_US','Science & Engineering'); INSERT INTO "deptnames" VALUES('personalisation','en_US','Personalisation'); INSERT INTO "deptnames" VALUES('education','en_US','Education'); INSERT INTO "deptnames" VALUES('productivity','en_US','Productivity'); INSERT INTO "deptnames" VALUES('universal-accessaccessibility','en_US','Universal Access/Accessibility'); INSERT INTO "deptnames" VALUES('entertainment','en_US','Entertainment'); INSERT INTO "deptnames" VALUES('sports','en_US','Sports'); INSERT INTO "deptnames" VALUES('health-fitness','en_US','Health & Fitness'); INSERT INTO "deptnames" VALUES('weather','en_US','Weather'); INSERT INTO "deptnames" VALUES('shopping','en_US','Shopping'); INSERT INTO "deptnames" VALUES('finance','en_US','Finance'); INSERT INTO "deptnames" VALUES('business','en_US','Business'); INSERT INTO "deptnames" VALUES('media-video','en_US','Media & Video'); INSERT INTO "deptnames" VALUES('music-audio','en_US','Music & Audio'); INSERT INTO "deptnames" VALUES('news-magazines','en_US','News & Magazines'); INSERT INTO "deptnames" VALUES('graphics','en_US','Graphics'); INSERT INTO "deptnames" VALUES('lifestyle','en_US','Lifestyle'); INSERT INTO "deptnames" VALUES('medical','en_US','Medical'); INSERT INTO "deptnames" VALUES('developer-tools','en_US','Developer Tools'); INSERT INTO "deptnames" VALUES('games','en_US','Games'); INSERT INTO "deptnames" VALUES('books-comics','en_US','Books & Comics'); INSERT INTO "deptnames" VALUES('accessories','ca_ES','Utilitats'); INSERT INTO "deptnames" VALUES('accessories','es_ES','Utilidades'); INSERT INTO "deptnames" VALUES('accessories','eu_ES','Tresnak'); INSERT INTO "deptnames" VALUES('accessories','gl_ES','Utilidades'); INSERT INTO "deptnames" VALUES('books-comics','ca_ES','Llibres i còmics'); INSERT INTO "deptnames" VALUES('books-comics','es_ES','Libros y cómics'); INSERT INTO "deptnames" VALUES('books-comics','eu_ES','Liburuak eta komikiak'); INSERT INTO "deptnames" VALUES('books-comics','gl_ES','Libros e cómics'); INSERT INTO "deptnames" VALUES('communication','ca_ES','Comunicació'); INSERT INTO "deptnames" VALUES('communication','es_ES','Comunicación'); INSERT INTO "deptnames" VALUES('communication','eu_ES','Komunikazioa'); INSERT INTO "deptnames" VALUES('communication','gl_ES','Comunicación'); INSERT INTO "deptnames" VALUES('developer-tools','ca_ES','Eines per a desenvolupadors'); INSERT INTO "deptnames" VALUES('developer-tools','es_ES','Herramientas para desarrolladores'); INSERT INTO "deptnames" VALUES('developer-tools','eu_ES','Garatzaileentzako tresnak'); INSERT INTO "deptnames" VALUES('developer-tools','gl_ES','Ferramentas de desenvolvemento'); INSERT INTO "deptnames" VALUES('education','ca_ES','Educació'); INSERT INTO "deptnames" VALUES('education','es_ES','Educación'); INSERT INTO "deptnames" VALUES('education','eu_ES','Hezkuntza'); INSERT INTO "deptnames" VALUES('education','gl_ES','Educación'); INSERT INTO "deptnames" VALUES('entertainment','ca_ES','Entreteniment'); INSERT INTO "deptnames" VALUES('entertainment','es_ES','Entretenimiento'); INSERT INTO "deptnames" VALUES('entertainment','eu_ES','Entretenimendua'); INSERT INTO "deptnames" VALUES('entertainment','gl_ES','Lecer'); INSERT INTO "deptnames" VALUES('finance','ca_ES','Finances'); INSERT INTO "deptnames" VALUES('finance','es_ES','Finanzas'); INSERT INTO "deptnames" VALUES('finance','eu_ES','Finantzak'); INSERT INTO "deptnames" VALUES('finance','gl_ES','Finanzas'); INSERT INTO "deptnames" VALUES('food-drink','ca_ES','Menjar i begudes'); INSERT INTO "deptnames" VALUES('food-drink','es_ES','Comida y bebida'); INSERT INTO "deptnames" VALUES('food-drink','eu_ES','Jana eta edaria'); INSERT INTO "deptnames" VALUES('food-drink','gl_ES','Comida & bebida'); INSERT INTO "deptnames" VALUES('games','ca_ES','Jocs'); INSERT INTO "deptnames" VALUES('games','es_ES','Juegos'); INSERT INTO "deptnames" VALUES('games','eu_ES','Jokoak'); INSERT INTO "deptnames" VALUES('games','gl_ES','Xogos'); INSERT INTO "deptnames" VALUES('graphics','ca_ES','Gràfics'); INSERT INTO "deptnames" VALUES('graphics','es_ES','GraÌficos'); INSERT INTO "deptnames" VALUES('graphics','eu_ES','Grafikoak'); INSERT INTO "deptnames" VALUES('graphics','gl_ES','Gráficos'); INSERT INTO "deptnames" VALUES('lifestyle','ca_ES','Estil de vida'); INSERT INTO "deptnames" VALUES('lifestyle','es_ES','Estilo de vida'); INSERT INTO "deptnames" VALUES('lifestyle','eu_ES','Bizi-estiloa'); INSERT INTO "deptnames" VALUES('lifestyle','gl_ES','Estilo de vida'); INSERT INTO "deptnames" VALUES('media-video','ca_ES','Multimèdia i vídeo'); INSERT INTO "deptnames" VALUES('media-video','es_ES','Multimedia y vídeos'); INSERT INTO "deptnames" VALUES('media-video','eu_ES','Multimedia eta bideoak'); INSERT INTO "deptnames" VALUES('media-video','gl_ES','Multimedia e vídeo'); INSERT INTO "deptnames" VALUES('medical','ca_ES','Medicina'); INSERT INTO "deptnames" VALUES('medical','es_ES','Medicina'); INSERT INTO "deptnames" VALUES('medical','eu_ES','Osasuna'); INSERT INTO "deptnames" VALUES('medical','gl_ES','Medicina'); INSERT INTO "deptnames" VALUES('music-audio','ca_ES','Música i àudio'); INSERT INTO "deptnames" VALUES('music-audio','es_ES','Música y audio'); INSERT INTO "deptnames" VALUES('music-audio','eu_ES','Musika eta audioa'); INSERT INTO "deptnames" VALUES('music-audio','gl_ES','Música e son'); INSERT INTO "deptnames" VALUES('news-magazines','ca_ES','Notícies i revistes'); INSERT INTO "deptnames" VALUES('news-magazines','es_ES','Noticias y revistas'); INSERT INTO "deptnames" VALUES('news-magazines','eu_ES','Albisteak eta aldizkariak'); INSERT INTO "deptnames" VALUES('news-magazines','gl_ES','Novas e revistas'); INSERT INTO "deptnames" VALUES('productivity','ca_ES','Productivitat'); INSERT INTO "deptnames" VALUES('productivity','es_ES','Productividad'); INSERT INTO "deptnames" VALUES('productivity','eu_ES','Produktibitatea'); INSERT INTO "deptnames" VALUES('productivity','gl_ES','Produtividade'); INSERT INTO "deptnames" VALUES('reference','ca_ES','Referència'); INSERT INTO "deptnames" VALUES('reference','es_ES','Referencia'); INSERT INTO "deptnames" VALUES('reference','eu_ES','Erreferentzia'); INSERT INTO "deptnames" VALUES('reference','gl_ES','Referencia'); INSERT INTO "deptnames" VALUES('science-engineering','ca_ES','Ciència i enginyeria'); INSERT INTO "deptnames" VALUES('science-engineering','es_ES','Ciencia e ingeniería'); INSERT INTO "deptnames" VALUES('science-engineering','eu_ES','Zientzia eta ingeniaritza'); INSERT INTO "deptnames" VALUES('science-engineering','gl_ES','Ciencia e enxeñaría'); INSERT INTO "deptnames" VALUES('shopping','ca_ES','Compres'); INSERT INTO "deptnames" VALUES('shopping','es_ES','Tiendas'); INSERT INTO "deptnames" VALUES('shopping','eu_ES','Erosketak'); INSERT INTO "deptnames" VALUES('shopping','gl_ES','Compras'); INSERT INTO "deptnames" VALUES('social-networking','ca_ES','Xarxes socials'); INSERT INTO "deptnames" VALUES('social-networking','es_ES','Redes sociales'); INSERT INTO "deptnames" VALUES('social-networking','eu_ES','Sare sozialak'); INSERT INTO "deptnames" VALUES('social-networking','gl_ES','Redes sociais'); INSERT INTO "deptnames" VALUES('sports','ca_ES','Esports'); INSERT INTO "deptnames" VALUES('sports','es_ES','Deportes'); INSERT INTO "deptnames" VALUES('sports','eu_ES','Kirolak'); INSERT INTO "deptnames" VALUES('sports','gl_ES','Deportes'); INSERT INTO "deptnames" VALUES('travel-local','ca_ES','Viatges i regional'); INSERT INTO "deptnames" VALUES('travel-local','es_ES','Viajes y guías'); INSERT INTO "deptnames" VALUES('travel-local','eu_ES','Bidaiak eta gidak'); INSERT INTO "deptnames" VALUES('travel-local','gl_ES','Viaxes e local'); INSERT INTO "deptnames" VALUES('universal-accessaccessibility','ca_ES','Accés universal i accessibilitat'); INSERT INTO "deptnames" VALUES('universal-accessaccessibility','es_ES','Acceso universal/accesibilidad'); INSERT INTO "deptnames" VALUES('universal-accessaccessibility','eu_ES','Sarbide unibertsala/Erabilerraztasuna'); INSERT INTO "deptnames" VALUES('universal-accessaccessibility','gl_ES','Acceso universal/Accesibilidade'); INSERT INTO "deptnames" VALUES('weather','ca_ES','El temps'); INSERT INTO "deptnames" VALUES('weather','es_ES','Meteorología'); INSERT INTO "deptnames" VALUES('weather','eu_ES','Eguraldia'); INSERT INTO "deptnames" VALUES('weather','gl_ES','O Tempo'); DELETE FROM "pkgmap"; INSERT INTO "pkgmap" VALUES('com.canonical.cincodias','news-magazines'); INSERT INTO "pkgmap" VALUES('com.canonical.elpais','news-magazines'); INSERT INTO "pkgmap" VALUES('com.nokia.heremaps','travel-local'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-amazon','shopping'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-amazon-es','shopping'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-amazon-int','shopping'); INSERT INTO "pkgmap" VALUES('com.ubuntu.dropping-letters','games'); INSERT INTO "pkgmap" VALUES('com.ubuntu.filemanager','accesories'); INSERT INTO "pkgmap" VALUES('com.ubuntu.shorts','news-magazines'); INSERT INTO "pkgmap" VALUES('com.ubuntu.sudoku','games'); INSERT INTO "pkgmap" VALUES('com.ubuntu.telegram','communication'); INSERT INTO "pkgmap" VALUES('com.ubuntu.terminal','accesories'); INSERT INTO "pkgmap" VALUES('com.zeptolab.cuttherope.free','games'); INSERT INTO "pkgmap" VALUES('com.ubuntu.weather','weather'); INSERT INTO "pkgmap" VALUES('com.ubuntu.clock','accessories'); INSERT INTO "pkgmap" VALUES('com.canonical.payui','accessories'); INSERT INTO "pkgmap" VALUES('com.ubuntu.music','music-audio'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-ebay','shopping'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-gmail','communication'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.ken-vandine.pathwind','games'); INSERT INTO "pkgmap" VALUES('com.ubuntu.gallery','accessories'); INSERT INTO "pkgmap" VALUES('com.ubuntu.camera','accessories'); INSERT INTO "pkgmap" VALUES('com.ubuntu.calculator','accessories'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.mzanetti.tagger','accessories'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-facebook','communication'); INSERT INTO "pkgmap" VALUES('com.ubuntu.reminders','accessories'); INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-twitter','communication'); INSERT INTO "pkgmap" VALUES('address-book-app.desktop','accessories'); INSERT INTO "pkgmap" VALUES('dialer-app.desktop','communication'); INSERT INTO "pkgmap" VALUES('mediaplayer-app.desktop','sound-video'); INSERT INTO "pkgmap" VALUES('messaging-app.desktop','communication'); INSERT INTO "pkgmap" VALUES('ubuntu-system-settings.desktop','accessories'); INSERT INTO "pkgmap" VALUES('webbrowser-app.desktop','communication'); UPDATE meta SET value='4' WHERE name='version'; END TRANSACTION; _UPDATE_TO_VER4 SCHEMA_VERSION=$(sqlite3 "$DBFILE" "SELECT value FROM meta WHERE name='version'") fi ./data/ubuntu-logo.png0000644000015600001650000001373712676763577015041 0ustar jenkinsjenkins‰PNG  IHDR¥‡¶ÁF'sBIT|dˆ pHYsWÂJ"tEXtSoftwarewww.inkscape.org›î<\IDATxœíÝy|]eÇñÏ/IÓ¤”B[n¡”Ör TD6µ€8qA”ÅeDeP@EdE‘qPV7Q7P¤ŠÞ"Ì€lR„*›…(Kié-%ÝhößüñÜ6I›&ϹëIò}¿^÷•&}Îy~í¹9¿ûœg3wGDD$ êj€ˆˆÈJJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’JJ""’ µ`´3³zà•Àžý^{û»ûêZÆ&"#SkKÆ€ý€#€YÀŽÀô«XÚﵸ9›Ë?Y›h“1w¯u £Š™L_òÙh¤èdwo«fl"2rÑ[€÷ï$$¢$æ?Ïæò‹Ê^Ù()•™™-ÞQTIID¢´¶dæ_'´ŽJåÀõÀ²¹üSe8_Y))•™’’ˆ”KkKfp!ÐRÓwßÎÏæò/VàüEÑ@‘jmÉü‰Ê$$€qÀ©Àý­-™½*TGbJJ"")ÓÚ’9—ðˆmBª› ÜÛÚ’9² u KIID$%Z[2ÖÚ’¹¸°*V=øUkKæSU¬sPJJ""éñàørŸÔš&`ã‡+V\ÚÚ’yW¹ëOBIID$Z[2>Wîó6ÍÙŸYW-dçùÏ1óª…ÔOž6Tq®«e“’’ˆHµ¶d®¬Ä¹§ézê§îfŒ›‘eÚYW wÈDà×­-™©•ˆg8ZÑAD6cf[s¸É8wSM…Z[2uÀwñE ®žq3²4dv¢aÊöô´-§ëùÐý³Ôm3k8V¢aû™1g œT½IIID0³ã€×Ò—€^ÁæíOT;®1âƒÀÞIj˜¶Ûù1&zÌ äºW<Ïs'L׳‹7s×?ï|&ú2žÜÚ’¹¤Úl•”D££mjÄXÓÚ’OiÍiØaÞÙΤwžˆ5ÞÀjØnG¶:èí,»àÃL;ó2vÌÒþÐ]ä/ùtlUã€/ïK_©Ô§$"R;'TbãÙþ¼±ý篢{ÅRVÝø!ËgN¿„ ûÊÒsßËÓï}Ë/ú½ë×%‰ï¸Ö–LâV\)ÔR©÷Æ´¦ ìpþu4Ï=€Iï8¶ë/eë·~€Þu«iì~:ŸZDCf»ìÍø]æR·Õ$¦žü%¦œx½kÚ¨ÛjKN;œÎ§®–ÐRªÚ®Zû®Ì´öŒDfÖÆðïžp÷WW#ž± µ%“–ùÄjú—¯§ùuó6~ß»nÏ?ïêÄ»»6+_×<‘É>‡mŽ<¬¯ŠÎÖGXòé#ðîÎØPÏæò»Ç.•߉ˆÔÆ;‰¼7íuà€„°æ–ŸÐ»~Ý   wýZ^üî9,9ãmô®ëÛº­1;‡)9'Iœ¯nmÉTíÈ’’ˆHm¼#¶à¶Ç6àûµwÞÄ‹WulÇ¿äô?Ûæ=Ÿ`ÜôÙ±ÕTm]<%%‘ÚØ-¦Pcvö=lã÷Ïu8m7\ÆšÛ~Æóg½Þu«Ùö¸¾õêš“ Pبaû™fíxôOxOwìáÏUiôø.åÌl"¡CtráëË„O-Ëܽ£–±I<34^ __V¸ûꡎ•Qk)°Ë¦?ìZö4+¯î[£u«Z¨kž¸ñûæ×BÛ —'®l«ƒß9àûõ‹îIrøÒÄ©jI©ðK9\[±×Ý“uLGqÿî¯òL…=lÞìK¸ym©üK„õðà&w_^…P±ÐîyÎÜãîƒO9/_,D´úݽ½BõïìAØîaVáë†?g×p‹ñ™Y°¢ðÊ À½îýQ¶ ÌÌšÊ|ÎîáþfVOX±z8]îÝIRŒÈì}ìIJ›Zwߺžý;ãf†©Bͯ›Ç¤#?Êê__QE0nænLùàÙ~–0)=“¤p)ªÙRZÅð7±‡×T8Ž+€“#ʽx¨Â±`f»gbˆ$4ˆÉ…×î„Ùáß1³{€Ÿ¹{%Ÿ¿ x,¢Ü5ÀG*ÀÀþ墠oñf3}6y ¹¿t„q„ÑMF8 |Xmf·7?®ô¶»Å÷²îRàôaÊ| z9ìàà‡¥4Œ˜ÿBà ån%\롹ÓvÃådÎìkmwÊ—éYùëîþͰ‡×5oÅöç\5`³¿õÝIÇD„¸Ñ-I —B}J5df‡˜Ù|àqB¢L’SGhe] ,6³7³˜O˜23;ÇÌrf¶œð‰ñWÀ¹@ ¥'¤¡LÞ\ üÍ̪¶Ô‹TÅüØ‚koÿ%ÝËŸëûÕ1í¬+¨kÞjÈã&ì{;}÷.g÷­§Ú»¶üE§&Y"$jV•B}JµóŠØm2‰À…À fö)wÿ¿ Ö5Úl_ãvæZN§»û#5ŽGJ”ÍåkmÉ,&\Û!ywËþã}LÿÚw™]sëOém™iŸý=«VÒñØŸéxê¯4df0~—½iÚû &ìwøfçÊ_vÝ/&ê"úM6—¯ècÑþ””j§Zgíü¯™}8%e}’ÜáÀ}föwÿC­ƒ‘’ý ølLÁÎgžàùÏÉô¯Ýˆ56±òÚ¯1qÞ1LœwL(pÔIƒ÷òýÿËš×ѵôÔOžÆúnOã/“P =¾;Nn( ‘m"ð;3;lØ’’v—FaFéZòK?s$+.; ïlgÊ çY~õï®aùW?FãìÝÙöèOÒ0uÐ…$†ò —ô R()-G¿6³Rû®¤ö&7›Ù[jˆ/›Ë?\’䘮eO³îž›iœµÛ€}’6åëYyõÉœq)“?øY&z,™3.cʇí¥tv6—¯ê(d=¾{Þ ä̬ÅÝË=’Jª« ¸ÎÌvs÷UµFŠöuà$`»$u<¹ˆçN9„ñ»Î¥i×S?u:õ“3ô´­ {ÙÓt¶>BÝÄm6›ŸÔ¼ßápÍWbª¸5›Ëßš$¦rPRJ§nàÀ“À …׋„ɳ;†ï Ôqþ7ÿ^Ž`eP+Ò7ÿèE`-áQÍzÂHË)…×NÀ„ÖORÓ×ò´á J:esùÕ­-™/ß+æøŽÅÓ±øáAÿÎêðžn¬¾ïV_×µŒ]5º?()¥Gp=aì-îÞ6LyÌl{à8à_‰›§Óßfö w¿/q¤2˜6à,à¯Àâ˜ë×_¡¯ï@àXà£@’¾¿O˜Ù÷Ý}ð;Sùüxw™Ï™/óùF¤l.ekKf?µ/ïé¦íÇ2ùø³ÁêðžîÍv¡Ý‚“²¹ü¢rÆKI)n>ïîO%9ÈÝ_..7³þ¼ù-ƒ«~`f¯ÑrEe±Êݯ.öàÂÄØ;€;Ìì„G:ÇD^\Tº©ÃÝ«2¡|Œúa‚r¢-a‡óÒO¿Éú‡ï¢nÒTºž{’®çžîodsùk‡+T)èP[÷¸ûqIÒ¦Üý'ÀÂò4±^ œ_J½R~îÞêîÇßNpØaf¶ù¾2bdsù.àh²aeÕþèŸyù¾1 éwÀÙê$%¥Úy¿»\ÎÇg…e…Þ$™ˆpº™M-W RV§× [*0â[V’RÙ\~¡ñîT%pT5'ÊFI©v’´h¢Ä>nþ­±Hi Ÿ¬‰<ä¸ †#U’ÍåóÀa„奪¡85›ËŸ\h­Õ”’Ò(TØ áh vÏSÌLï…r÷À7#‹¨Gx£C6—ïÌæò'ú™VV°ªÇó¹ü·*XG"ºRîþ$ûFËÿRÁp¤4—>ÍÇ€×W8©¢l.ÿà•„/åÜze)aè=³¹|ª–«RRÝ. þSÖÇ+ˆÏÝ_bû÷¨d,R}Ù\¾-›ËްpëÅ„¡ùÅz°ÖÞ.Ù\þÊZ÷ FCÂG1wÉÌ®!ôK çP3kЂ­©µ88¢ÜœJ"µ‘ÍåŸ#ì»vfkKf.aPÓÀl¤úM'Òw¶1ÿ;aO®ùÙ\þÙª\$%¥ÑïÄ%¥ „•ˬl8R¤;"Ë))Ù\þa¦¨_hmÉÔ¶W™AèK^RÉ7â()~–Þ¬Ã9%¥´ŠÝçUBR©ðîùÂkDSŸÒ(WV»ô|ÌÎR±Ëñ4™Y1k艤‚’ÒØðÈrJJ)åîkˆâ?±’±ˆT’’ÒØ°$²Ü.ú”j±}JJ2b)) ±I ât•ê‹}„§¤$#–’ÒØ ¤4:¨¥$£ž’ÒØð\‚²“+…”*¶¥”d/&‘TQR 䱋z*)¥×ˆœw"’„’ÒØûOI)½ÖÕ:‘JSR;b×Àk®h""CH[R²Z0ŠmY®ÜËäëšJµè½6 ¤-)mWëF±ØQuåî·Ð5•jÑ{m¨fRòˆ23ӧʈMJ±#¼b®'„E"EJû^›^Ñ(¤*ª™”^Ž(3Í“)»Â* ã#‹Ç¶”b®'()IébxìPÑ(¤*ª™”VG–ÓM¬ü’lg›”b¯ç´u‹ fUd9%¥Q šI)vžŒÞXå»Ðj›»wD–]Y®ÑÌÔúM¿Þˆ2›n"W-JJcˆZJcClRŠÝrwï!þ±Š®iúÅ<Ž­U«WIi IcKI7°ò;0²Üí Ï«k:zÄ\ËIfÖTñH6›”&›Ylß©¤T5“RìÞ𺕑™ ÌŽ,þ O¯k:zÄ>Ž­Ekd)ÐYV­¥®šIéáÈr{T4бçôÈr«€¿$<·®éè‘ÚVo¡Ÿsqdq½×F¸j&¥E‘åÞ¢æÊÃÌ^YüÎB?Q±×ô] Ï+Õ—öH,÷ŽŠF!—Æ¤Ô ¼¥’Œ!ÿEüˆ©›Š8lKi®™Í.âüR=ÿŒ,§¤$Uµ¤äãÿkCÌìXàÑş®+¢šEÄ϶ÿ@ç—êIûã±Øµ³Ì,v`¤Pµ×¾»9²Ü1f¶OE#ÅÌlpe‚C.t÷Τõ¸{pOdñÏh¾Rªý=²ÜÑ5Z ìv`}dÙ¯V2©¬j'¥G–3àÂJ2Z™ÙŽ„ä¿mä!/ÿSB•±×t[à?J¨G*+¶¥4xsãTa£Ê_G£™©s„ªjRr÷û€§"‹Ï3³ÿ®d<£™íüØ+Áaßp÷öª½èŠ,{š™_B]R9O¿ÄÔ…fV‹’›rp÷ÿ$ N‘ÊÝI‰£2š–tøva‘Ö÷?bPFˆš&%w¿ x7ñKçK2n4Uáî_!|ÚNºÀ«¤„»Ÿ \\Å*Kz@a’cŸ•?©•š'%wÿ°ðCâ×R“¡=ì[Ì€R¹ûÅÀ\àÖj×-åáîgõ­Æ#öýŠ9ÈÝ»Üýý„…XŸ(oHR ©HJî¾ÔÝO ¼9ï®A­À „ÑM‡yžO%tÜþ èºxÙµbÝÓÝYéÇuCq÷GÜýÂðïØelÊ¥‡ðÿ pñ»ïJ?î~>0¨DK{-p-ašF’é›q÷ߦG|š0¢®šÖ <ÿÒ`Œy5£0ûí…×”o¤`aI•ú¿Üý¥23Ûšhßì_øZÎÑu]À½ôÍõy°–‰hK sUÞ@ß5[ÆÓw’ÞÀý…¯•ib0fö†å¹ÜÝO*G}CÄqÏ’ІגZ¿Ìl*a‹òû}lMß\‰æ~îòÀòÂ× ^Üáî#®?ÎÌfZ¤3è»–®ë$Â<’þ×rßÛx=Ÿ–¦1f…ë7Ð:Ù05cJáká:mx½HèÛ¼ÏÝŸ¬A¬s€Cû¿Ï¦~¿ÚÙüÞÑNø=ëÿ^{º°¬–TЈHJ""26¤¦OIDDDIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRCIIDDRãÿÍmm^Xú¬±IEND®B`‚./data/clickscope-screenshot.jpg0000644000015600001650000010350712676763577017042 0ustar jenkinsjenkinsÿØÿàJFIFHHÿÛC  % !###&)&")"#"ÿÛC """""""""""""""""""""""""""""""""""""""""""""""""""ÿÂ@@ÿÄÿÄÿÚ úÉ‚L„ €TÈA¬pÏKu¶`,d5Í£” «ƒÎ¬pËJ6ÏDhÆõA ‚@ .#ÅVÁœÊ`*ÁåÏtiFí$I2c2œÓDîtë™ G@R œ2 A$ ±Å€JÀ$€ô ("¤ ˜ŠuÀ €2N/feÄ™$…°HPÈ#NÌRíY¦¶2Æk2- „€€*\B€( ’ €B€X‚@,@ÒŠ$Í[€Aåóljž4%!FhôØeÚÇ óç‹™b™ÌÊYZe¼{œùºÖ‹—€V¹±Ww,~7ÙÏßÏW¦˜nmÕ“NÞn¾CmÐÇ?½ñôfP0Ÿ—rË^mÉŽÛã·$Û|väÇo­ËŸî»¼tPPÃÇ.~Ž®ŽjÅ”Jg²ù2³#êy¾×ÈÝ}Ì;µóúðØãݯͳÆu>ÃÇ×Дaùo¡“·›¾Ÿ®p[µ±ÝÇÇ«±'èÍž (ˆ²d s«Ÿ—§{ÎÏ[Õ,ÑíÓ}ydó·yŽ­ŸRåëÞ”U~eì·ÇgÓtåï5ãEùö]ž1Ù×Åúx"€c9ièé@¶Íy¶é˜Ëfj½ 8=üŒ_¯.gÒòcÏ^Ï>Ü>W_žíÙõ~Íù@åWæK;ÏN~›V]¬_)ÙߪßÖÁú/O‚(‚1Ë­†É³g,0‚L¦l¥À<öÝVðôæ¾³£<šyÛêΞ'W£w×tvoÊ3'æoV^mû$ó›7{®|~]¿·—:zøOМ¾¡˜#9bÕ±cn¼‘ ŠÙÊ\Íôiñ=|VéÕ}¼Ùf8îX9zpùù}~ÕÏ×¶ i×åßw M¾“N|\·ú-9ð³èÉŽîö¬>ÿçøª¡BÄ1BÀ’Ög >ߡŸM= 7Ù†–¶­øøww±Ýô;@æ=×Ãvg‰²ÓeñÙ’mèk¿Kâãõš¹IEÆIbAP @$P Žq¸š«˜’M€¡ŒÈe  3&EX°5JFK2®@@(P/1–³$·cÚ™æœÓ š2Ü¥^Îþ5@X°&4 §FÍõ ׉©,A˜‚à,XÑ‹1®Á¯õ¾šë²Ab…4#V6ÓÄ`=&SbP,1¯®S ™”e޾6™N¬u…HXÃ,º€±`€¡@IX‰$(P‚œ*¹‚3V#z=ôdóžo‘­ÍÉYÇ–VË.Ç_{»Ñ€ <6Ì|þhg®úb…¨xË5ͳLÛ5“`ö’‹—ÁæpüOœêû>ãØôt3ÇÉyúõþg“¥»‡»¯‡Ö}/ØlmÜÏýšý–»ÙÂúŒ//'g玼=Ž«ö|Æ2 Ô<-˜Lƹ˜Äe7s(9œgÅùî×Ó}~¾Í•Ÿì2e˜?>ök¡&Á£]ZxÇÐϧš…H$çÀ `A “ÑŸl €Æl ‚†©AS|øÈ=Ð<1趘<üøtJys oXéŸ~ €Æf2œŽ~}â f©°A$EkTüæ}(øÀ>Î|Ðý l€j|rç\éšç”2Ÿ~ €Æyg;µ¾ã:Zvv~3ßçè×Ù÷¸´qvúò”…pÏsσì&sÝÀÎ>"mžÔùéô‡˜5Ϲ†@b<®ŸK9Îö¾scVÎÏÊûS³Ÿ?»îmêЦÉùTÞ4 ›F©ú”ÆA çŸ ;GläçDðÆá÷" ™CÏ›&BåLfž©—mï€6Ï,~w:e ôIèŒ a>Hvϵ‚†@ D’H*IÓ6±”¡åÉ=9s 0y !쎰*™¡*d 9f±¹fú€…­gޤV]ó‰”ÜŽ²ÀÈÈkÖ@`6c5›j*dAH½u… ¼ëVÆÔcÊR^˜B5ËY ÇΞS<¢†P H @  H¡”‚H$€€ H¡”$€B€ €I…L ƒÆïÝÀÝ»W+Œ©ss}‡6E¯Xiqån|’bÇf†;­VÊmvhèuj!@ó:üýg>JËU1Æ<]\»»yõ H0Ÿ õ=/oNœ±‚¸{¶ÖåÔÕ«ë>,ž^Ï8ÃK)¹†Î>züÏ_›Òçßëðô}©¤†cæù¼œ»0ÍrÏ6Õ/¯<3Lž£§Ô‚¦R ,²ù/§èÖ·´jîéÑã7ïú'7?Ï;ûw°Çì^gœÇƒÎùÝŸ)ÏÓ×'¡›7}OàûÞ&;¯ê9ô]¯Kœ¯Ž.vgËÙÖäìËéëÍÍÉ»§›¾Ÿ¿Ô‚©‘dÐË/’zž•koV¿_ËÍÁÛ³.ù~Þ½ì0û'—ç)ƒåü¿ˆÏÚ¶i]^§gÈñþÄÉÓÏô¿™úi¿ ¶8y;ÈÓÝéú¬ú|ÿÎto}7?Ù¯Óðy+}‡£ëŠA¡–_"õ}*[¹«Ww›G…ëéÙÏ;Øaöo+ÍbÂùï/Ñø·O¡¥6zŸ>}ÉçÕïÕÖöü>ÿ?E½=` \pñžoÆÛÓŸË÷ý‘Õá>«8ÍÙÃäú“O¼ô}˜*d¦Wãž·©CÐrótõëñ]xóÌtuëû7•æ€1cu<~ꯗó»kÏÔJôs} ÓññmOXW>åøy-áîì×ǯ.|ù»Z9­žDô½¡S øÿ«és¶lÄcÛ'®äæú_#CÊíyaÃ.&ü×_¤ìãÚÙ†.Œg¿dùw—ábÃVC%ÊLi‚NßOo¿ïõ…ÿÄ0 2!#345P1@"$0ADÿÚý9x~œ¼pê1Û!Â/á9LfŠŠ†ûM¨:Û´ª‚ƒ!“*Õý²ñÂwûî%ù+œ&-PÝVõŠÌ»Ä¨nŽ!F‹Þ§KòÏûEã_Å7 .N-½1Þܳğ‹Ä›vä5õpšÓŒ«ŒÄaĈÌÊ"?í*f¶ ·O2 ÅjßèÀØ$p~êÄ8ñ•øÌIF!FŒ_Ü/¢UÔVº‰WP*ê]Ak¨•u®¢UÔJº‚×PZê ]@«¨•u® UÔ ºWPZê]D«¨­u® UÔ ºWP*ê]D«¨u®¢µÔº‚×P*ê]Ak¨-$å5ý;W€Ýp®uÀn¸ ׺à7\ë€Ýp®uÀn¸ ׺à7\ë€Ýp®uÀn¸ ׺à7\ë€Ýp®uÀn¸ ׺à7\ë€Ýp®uÀn¸ ׺à7\ ÔŽö`Ûš‘ 7GNèéÝ…sLë?î—†ϱ ˆ¨.Ɉ¸%§A*¶*"©iþéxþœ¼?N^¬„ÏuêÝ~·­ÇëuúÞu(^ÙWرÍ}M_|LUñ1WÄÅCêdÎÎ4îë…â%¶‹ÖmjøÎ¾2¯Œkã kÕ¬Å–ÌÆ°/ ðAçGPŠæ8Þd,{]F¶¸ò„&bÒ=¦·p_·GtdCqŠmÂiÖuœ]=¦$HR³j³k?—_.¾]|º°Jãݰ/ ‹V¹Tª´‚âÖ‡kCµ¥Ê÷ úxúñ±cmÔv¶QÆÐÍlÁÐÐqIJy*TtJƒøìn •¬2C‡$#µÃéOÀnáwº7‹Èä…lö»à^üGÃÂ"ÅŽÚ6*«:j-¸4뛊É葳_x~Öün_ˆÜÜ‹ÓîÆº1mxVÈõy ^cVâN«x`tA¬xƒ\A® ÐÇ@%*ÚË% nm«¬ê÷B÷RÒŽyêʆQí>ïù«… ñøÜ½­ ºLWIY-²ŽïêfeI¼ÀŽì/GYhW"¶þ[ðÀéU9,ÒHeW:Þn·›­Ð\ò哑‚¶TØnºû ù*‹wÞTCpªãñ¸þ+x«x«Ò³æË~o¨îp¾\$Û­Rof7¸µn5[®ჟ¢*m7[MÖ’¤DÈk$¯d¡ðÆô™Ûõêkÿ¨ÉT³öEÿ7œZŒ*ñ”*pÑI§”Ùx~ÕM8­;p¼È¹’‰ ¨•œï† ™¦•¬–½ëÞ²ZÉk%­®6.´í¢CF±DØxIYuIX~¶$ç–å@±š=Ûu°É9n»6¼[ž|[•qnT‘.9µk¹¸¶›RÁļ?N^I'Wt(¥´,†PCMÐ>Ó…ÜûÛ Üœë’ÎÚ: [Íä’ ä5´&†=êîRF@­#퉉v—‡fÙroQl¾±ÝmÓtc›,—tÆÕÖ¸Îm<q]F) «/›sVÿàômé‹ Ñ­ƒV"4ÏaxbëÂȃˆm«€Ý# ®ƒšƒ4ËReý'd‹F."‚»”œÑU×Á¦ÒX¨êJiätsLõ"­†2P²G¬™šÛ*´6"…œÕ誫]ÈßÝa¿™°©k~2¤}(3mÀ‘’Gà´‹Ãþ4 ˆ€ÿt¼?N^½8µ¹.nbìãC“4(]œHRf5.mF”2G±UN{¦»Ò«vUnÊ­ÙTR_ ëbr‘öÜ=@ø™å¯‰^¯‰^¯‰^¯‰^¤õ;ÉV˳w!À¼0|”#¶¢,ºZÈÐÛ嬅[tµ;¡¸D©pìöý·>+©lmp“1hµ3‰.A7ÚàÓ¾½:n 0ëåÓ¦áéÏÊà^Jû0o]:TGR¹âª¶èi!o1ˆŸïöMú]йGaâ8n#tÇÛbNäý)÷µtźúV>˜Ô~~›üžᄟ³¶è‰2É4‹Ú)×PÈБ\ܹvMúT™f³›$'˜*žë gq¼‰Úcí±/ ÿ”ô§Þ›ú/ª¦TK}Š+–Óóôßäð/ $ý Ž¡/à]þ5:lPª'ßvMúU±Ù&#•F–® èÿ¢Ú3L}¶%ápü§¥>öøÿór‚“ãú‰ý›5³ñ'çé¿Éà^Iû1Eˆ´/éGED{J«‰’;•B4Yý“~/µ{j÷x·.?üÌÛÏÛâ^ÿ'éO½õg×¶?ɵú©ýRmŸ‰??MþOðÁÑÖË@‹GM«rA¶"¢êi*‚ Rû%ަQG?ÀÉSJ© ÇUhCS¸éo÷ÿ’-ø…&c󋔸­>û’^nï9¦ëÓIÏðÅÈÈDvçIz{Ù¹ó¡ƒ )m¯- µi¦EìÊŽª¬¤ˆð×Õ£²©V£ ÓQEµíŸebqü+_ ×µð­|+Ié_xVö`7x~œ¼1Ôû ½@²Ñåè3—¤ÖBòѧ÷ìäšCYz(å §”3k˜;l¼Žˆ¯’vnFãÝ“6™rb©r‡&æ*XÜÄVÖhèÌp/ V3›ƒÊ"ªñÜqÕŽá¾q5#±Ðª(9¿Ø±QfE%8Šª±âi" 0ÎÒ#Ðpu=Æ^Wc­nªÃ -µ®rC %U,g®!m2ÞÓ8‡c« 2›2æçd€¢Êm(_sû¥áØÓO·LD&ËŽ÷ØîºmÇPtq%t¼?N^§/ Z´hÐ5¤k@Ö­Z´ hÐ5 k@Ö­Z´ hÐ5 k@Ö­Z´ hÐ5 k@Ö­Z´ hÐ5 k@Ñéý9xàŽ|åt’•ÂJq NªW ·Sok:ÕH¹ÿl¼+ÿ6GB5š£9©u´”¬¢Ö„ÌCN'÷ ÃôåáúrðÅ\"-§–¶­‡kaÚØv´>4j^ÝFE²ò×Úã»\wkŽím>4'šöͽŒwúóÅ]iêëN×ZvºÛÕ×ßE·ÜÂz`^°4:[îu?˱}…±ÒÙ€\}dËçªî¦ã^¬º¶å·Öä•<¯žÜsyuk*ÔU¨«QV²­e¢B¥ÓðÀü¹iÎÒñ¯T^ÊT¨6Ö„I¶‰n.3:Ùè>•¾O9áÙ9r·TXŽKs Ô˜ÇèvÇ%‰Y”šTÉmÞ×\ Ãúi„¼ÔÅhûWÆé'‡h³Ùß»H”ø35÷Ý”üyÅ~Àüû­¦EšTY»_dÿÆêPpØrÜÑ»>øè”†&Xmqˆãª/˃íuÀ¼0?§*b”÷& Eµ6ì¢yÇ­2ˆÓqÇ]Jä:™7ؾ>¡i^ôç¢ xó€–à!c×9Y[ôçdïÆiXÑÊd¹rY´ÇUR/ÆX­We½5¡‰r‡ïsÀ¼0sé8Ë­ÍSšùBfl%r<›Œ–·ämç'?òì_Y™Ežékº+[œ·Nµ[Öá:åu97X_¼]TE¶{&þ:­Ó’åyŒe6kZ…xÙŽ·¨¬ Ž®Aü–á&`'•#•¹[•¹Jå&–é¯ó{±\Âõdjï}¶Uµè÷Ç™‰*ôìˆ-’®OY¬ÍZ#¹üv:ŒÊ=IZ’µ%jJÔ•©*ÐÚÈ»`^»\^#•Åv¸Î×Úâ»I©¦E¤íEʵÑèpôý¥ç°ZYp46é}û§ÙcÏ5ô­|)_ W•ð¥'¥’¡@fX‡éËǵ& 8Ä•Y:_ Bð|”#‰¯&&‡ä¨ «L<@ÂM÷îtÜ9$fÃHþ©A0L›–†© ’’¾k+Áxö·mцY¤=Åg'»Ý Æ–+„ÊBù|B!Ûv¸ßë¬\Ó¹Ö8¦å M4Ô=ºH¹ D!&àí“-í3xþœ¼N^?§/Ó—Ž3.™8Sœ*Y.RÉv¹NÒKq(n.[î +»#z¸W Š8li¬åÄf¸â”Ó†‡Úì‡=€*â³\Vib³\VkkME’jæãƒÅ¡ŠU¦í¦ò­–M¢P‰‰6uo=.Âðño¬7¸ÕðI¹,—.¬:@ÕÍö˜bð`ÜwÆS}Žýo!JJÒµ§:É(…t¿xá/ìp·®Ý×Ô(†Ó”'ní#–ú‰÷ý‡áy¸,nò–£ ™ª¹e•:ïºÒ8H‘Ühd²M›}¸­‚rŽKÒmñÀÅfU)R/ÍÀ¼p™öCüÚ3²A›Iêº\æê'ßö‡ªç5 ÅóÜ"¦á¸PÕU,Àcnìí‘j‰7§m±¶…:„»5Ç•" 2.!¶ óð/&}†ÿ#êCem1‡÷ýŽ})ð†tY0Þˆïù-[mÚÍ’i…ùdøY!ŠˆÙ#íuT¦×6¤H˜$®„dÖÚ¯ ÂM-¶_ìà^8JLáÒ­Ci–%Ü‹.3ƒ¶xA÷¸öÓÿÂ4u€eïçA_—GüvHû]T¾èäUÿn’1»C üF:ç/ñÁ}Ò\r‹"²JTNË$RrWbû§ñ¶Ž WSvÒ*e€`h½×°“P¾Ù°ò֪ܤr”Öµ-['dà^?ÿÄ7 !1AQ"02@q3aBPRb‘#`p€ð¡ÿÚ?ÿd¹þ?À…À."â."â."â kŸ á._ WÁ»ª67§°°Ðú7 ŒµÑˆâuÄ\Óº¡äƒ4Ì P¢]ªï+duf,í¥uX¬ý ú-™Ÿ²’Bó@˜Ü ìª0§7 ¢Š»­Ð<ŠeiÕ ÐÝ“%¯-mBˆ¸´µV”ïGiò&Œ%bªk‰Ô¦¸jSž£‘£B±kK†ÙC…vAßEŒôL.­B‘Ï:9Bd­ œµÎ×a5_úü/Šý þÅ~†ÿ IñŠaÀ´yUUVÉúTÂ|¼UCl­4)¦…(He ®ènŸå;>ëƒ'å(Äñ©QS=£Êªžì5Eä§×a(&šÂPCl¡æ¨<§¦ÆÕôL‰­:*)ÇôÎpH5 â%üÈÍ!'˜U¨§6©ÍT¢ 0‚ekˆ5 ¤ƒ¢¥PQ°3d.´šD}éОHÆî‰Ð;¢à?¢l/蛺 šι¸’ œ„²uBWuBSÕ RãÌ¡ƒš´Z8º ¿çAêÊçO¯½¢ú\9{®Yù.kUÜ®k¨Gcà‚ê¾¾'×Ôœ¼ü´\Š<‘çî:u\ʈò+¢½×ºüÈ.A;tåÌÿqAR¦íJ0#Ú“/´æ_i̾ә7´-ÙÑœ Dh¬Ý Ù޾j¬Kı*¬J¾´^YÍbã·½·È]jiÂj˜q4z>Õù¾P*£²Å¬¡ÿª¤²¸5ÎÜ]Êo¶aqBñèûWä{¬Ññ$츭;¢Ë9Ý|=š¸ƒS-âÄtÿѪíCa45ÅuŸå7ØfƒÑö¯È÷veœ»š6f#fèSØc¯»î­Ï«ðôºÏò›ì3n7GÚ¿#÷MÆE‰ƒ•ö·kE÷½”®Æònƒå7Û0NAÇ£íËà4V'FɃ¤Lš9<¦·Èq=Hì,s®cKœLZ[«pô“v[jÃD{&^D(ì–ØözgÅ5 –œê´ûöRØ'aÓþýì‰9¹Y¬ÀqnsQaXV…aXçEË'LûV·ô[V«Ý}æ: £Ó/%÷QÒ«ï!¹»ßÀçUÉwÑmµÃ–cª+Ùt»}×:ÜuG\¼¨Žµú§#­näWÕ™ šëw*þ ÷«w[ÏùßÊýÿÑXfâ×M¿²Ù@³ñƒ18¨ìÆÑVËoBž7¹Îmh*žçÙž5«JkŸÊµEHÃéB«¥S†Ñàøyt¦Åcˆ]ù 抮¦ë‡$Ò   WE1ÅŠŠIaÀÕMiøE)ýètGB|!ºn¡u¹º•õGLçDÞ‹Ü•5§„uGZýQÕO€ Òñ¢¦”GZø#D4(h†‹šâvE‹R¸ \. ‹‚ÅÀj|e™€T¹›¢5N‹R2šŒÓZMhÕÄrÄV',EÅpPOC˜!w.8Bf AÚé…Yœ¾‹Q¿Uol„Uª;cãJgh(™'*åvÈ´Üа€•„ZM¤ƒ3|Âù‡uD*SãQš:é<§5š+è¥ìè‰NìÖ3u ŽŒé²¶Û %c<Õ’V´÷Ó\ÓFWùJaÄ£ƒMW QKQ*3ß™æËåQ¸4§Í¦Š&TÖé<§(]ŸÜuU&«¥ •Í )ìy.j–F{ÁâV:ˆ;Ùdò•Ê¡Zè‡|P¢ÒÁ-!;CEœfg˜_/•D+o“ÈrÔnÀ™ r.ª*XÜð­fº-SlÑ´Õ²Éä*´Vy‹\¢n:T§AÝî¦8ÜphúF4»ˆ¦S#É*#ýA™»‹ä“¢aÂjšìWËä9[º¢Pº·QZ_…—¯ò›ª¢´–•ñïꤵ¹Æ¨Ú ¹Aóx߉µºŠ™-/Ófº¢àê!=íä/Þçe:©šXê*ª•‰bDª•cas±dÿÄ9!1 2AQa"03@PqBRbp`‘¡±ð$C€ñÿÚ?ÿÒ\¾“ÙÓö 0•±+bVÄ­‰[¶%‘¿UU‰c hÐ kòl7*óDÒ&øKËþ¢Ÿ >ŒÖЃG-QØV…a XBÂ2ß5¦K £¨»5šÍg½Ž0щÊ×kÚœ-ÑY§ÂÚhÖ¹!9Œ”2혵ìêµN×p,+ ©š-XVƒ~NÈ+"¶Û•cPpREnÙǩղ–ÇPB`h`ÍZà2Y¨F•ÄÝ0§k¸ÝVë!ÈTèPj°ï¸TQyÔªòߨÿUå¿Qþ©‘`5©º›Öwa$öG"ÍEæqusÕDK˜ TSÆ(_Zd³3NcX(Ž»Ô"Ê„Y’· QáÙN¡z£J-´SØÚ3ªiç}Uw¢­N…3ÂÛ$!ÕÍY,8Cdp­Q£áíªª´3ijÅø U'TuÜg[&£U¡ŒhÉ6aÌ(˜ãÑlš°æ oEl"ü¨C5Ú²šH™ÎnÇû&’=.¸]l{›£mÇqº¢Æ¹¹§1¤P© lš§08Q64‹°¥ɃCP›ia¤Ûd­59¨í,{kZ-«?2Û3ª•ì{pÕQ£RžöèÝè&Š˜dj­•ÝòÝ¿²"ÏÙgìŽÃ²s¬ã¢•íwú‡*›º]ÛÙå_x.U÷}áÍt7;šæwù®K™TÐ-BèPåìJèºZû|éó#w>Ç5Ò«˜Cš¾Èr¯EÈ}‘×ä产~/²<é¥âmÁvArþ"&Š_¡£WŸ•yùWŸ•yùP¶Îí˜ •±²æÆJØ÷[ëcÝl{­u±îœÜ?'mv·¬î’?SU¢Óµfšhj˜jÐw[‰¢Ä:ÜM!Öé´ù?ø[Ò¹ã*eþÿ”×au]Îè~w[ÂÚ\ÃP¦9Òù´ù?ø_Îë$;iƒ³Õ ?(SØc…ôEÒá-Ô vÑ5õ¥.‡á·íºÝ›EOJ„òNõ9;[¦ÓäüGá;¼1…€È„ïZO0¼RÒ T_€ªCÊè~~Û­Ð)´QŠ´¦» ˆUÉÜFé´ù?øHM‰›6ßâ’UØWý€tN57EðۺݛE p¡PŽiÜFé´ù;c1BU•Ìl¡ÏL•á7ÎìrÕGÍ×4b4Mh­á€uA h‹@Q`mÓiì×Û—ÃÚãVš#áÒr)–{S4rg˜TSÙ Ž«@xt€P!á¯æT6EŸ=æÈZ¶Ý–Û²Ûv[nËmÙm»'8»éÿ†«š¡Ÿ³­)Ík~º/²åT2¥PÌÑ Åwkš§ªˆgD8{£ »íìS*.d¡•Ó5¨ÍVî[£$Þý4\¨‡.ɹ!–èÖ¨eNɹ2§d9!¨û.Èšû\ª¹‹¹Ñ þ‡OM Ô.†îe ?pEüïÓèƒ÷ÆÑÆ™ëôg¿Õ†´EøsiªµÈÆ:•4Llv¶M:)&iíç§@ßþ&Z¦Ú9±I‰£5æ[±Û+ÑÌÇ!×è.iÄÕë=”òÙæÂÒiNki9±:®w>ŠÉªÆÓ‹z¨,Ó‹YšQ¨M€ºs>š×ýÿHŽG\<ÿi¹í;DìŠè‡$ì‚§$3ßÕ´ªÐ-.®UTöFHeNÈd†BžÁG3UÑ}‘Ì*çT2§odçTsçTsÕr(šþÓÇ,Êv-ƒ:-‹:-‹:-ƒ—j’2Íég5 XÜV"±Œ¬e ãÈïnUUU]áší¸è¼ÃP¨tºaVn»MØõ¢ò/vaIi¡På Ý: ôV0‹‘rÄSžó8…ó7Ò¡)ñN£®“€î¿„¦µQñ'°+€2yA¥U¾È%áAŽd ×hUSIä¨1=AâQÍ.ɂ粙ÜÃêÌâÍ¢piÍ>aJdšÝ'Ý M¸¸¦¨¼E¬}$*)ã”U¥6«[ÿäŠn¿„ݵdM¹©žÁÚè¥-°Z‘ç\Ô²fcbvaŠˆo3ˆ_7 ‰ œÖÍ·ËÀw_ÂS\ƒÑ-VËPh£S$qÕYm/ÉÖé^(£®1ºþ‚pÂTÑÇf«£a¨çÉFèÝ.)³MÀáQŸÙ4œÁRJ>!¼Þ!|’b ‡ ªc±_7Ý TUV‡<9 Ýõ×p›XÃ…¶VÈpfLÉM*>1¿ÃÛ[¨¨/µ<áÞ‘¥®¥ÒF×äW•pЯ*O5A‚ë;jêïHÒ×QUTªªª¬Õ¸]ÏÿÄE !1 "2Aq3Qa‘’±á#4BRrs¡Ñ0@P£Áb‚ðCS“Â$cñÿÚ?ýþr¾!ÜáZ'\«æW®Y¥0fŸ­=%y7µÜ ›Œ‡J“"°žiþtåeôgSø(¦ú&—pÿ;ÔR0 ÷&=ð‰s„ɬ§Yk.†áS'þqVhmA HLÁ Mލe†öÎ`«3ßP³8ÍòM‹ecjm÷‘†?›9blj+T›J4Ú#Õºd}V»jKHLiˆ`‘N y(-¦®•<êj]Af äX¦@ g]M7ŒTX 1ùPÍ“’o—æÎId&5Œ5¢C%¤Â„žÒ]CoqþT'DzÃ/¤N¶_ñAÚÑ€h’µ D áÈm²íÈ›<pÉÞÖ -™XTÙÉU6;­¿ó¥rC·à¹!Ûð\íø.Hvü$;~ ’¿Éß‚ä‡oÁrC·à¹!Ûð\íø.Hvü$;~ ’¿Éß‚ä‡oÁrC·à¹!Ûð\íø.Hvü$;~ ’¿Éß‚ä‡oÁrC·à¹!Ûð\íø.Hvü$;~ ’¿Éß‚ä‡oÁrC·à¹!Ûð\íø.Hvü$;~ ’¿Éß‚§4/»kô†ñX¹m9m9m9m9m9m9m9bå´å´å´å´å´å´å´å‹–Ó–Ó–Ó–Ó–Ó–Ó–Ó–Ó–.X¹m9m9m9m9m9m9bå´å´å´å´å´åP.»ôƒ•ÅÛªçPÁ}Ý›îèG·Ioøþ|ådñoÍHmÎåK ‘¸^EÓšsŒ¦epN. ¼JSZÙ/üéý ðý ðГž¥]­ËµÈÚämr¶µ ƒ”¯æ:E‚¨¯¢ºÈ~1<™þï‚ó?Ýð^gû¾ ÌÿwÁkÙ\Cæ¥ Ò¨ìt¥h~¿¨ÛÊÔ²<Ž—Éyîø/1ýßæ_»à¯±~ï‚òÖxŒék9fxsržd=+”›†œÆÓoÏ òËœýY«–´ÇBÈAǤ«¡ÃêRŽÆõMTÁL÷µOiœá6# œÓ0˜ÿ]³Ð|CƒJ|h¢!sÌöüÃû~ aý¯²þׂÙwkÁl»µà¶]ÚPÚÚ©‹ªDòž[Åt×,Î¥S®C™r‹”[aà›ÃAžôw „â’›¶Ê˜™qW8Ïz ¢é‰a,…ððÞ›Ý7»BÖy »¹ „Ç4Ö°˜æZÂc™2×þÛÀ‘¬ß7]òBvÕ ­.sg)ÿ“QáÙ™äêiŸ_Í^&“Þ·¿)ᕼpЇ=Ñ'ò*·rÊìTß:AÜ¢? òS8îF¶º‡oZü&×+<¿Ûov…³Ü¿¹r,ùýW"ÏŸÕ@1m.ÞÑSC°w2³Y-ñsP™ÉßÌ%ü¨¶³¢ÝKžNDZFÏZ"E›Á<ó\›~jɨÞY¼üùO ­âˆ$‰óÊFí®R7mr‘»h9¯ˆzå.u‹ºÑÆôÞ˜7ÄþPw`®Ã&kÐÆ@)oW`¬þí½ÚÏrþä ô9ÐUî³Ålƒóކi–)†–h•Y B#»ùV†À´D!’cpŸd +[ȨÒ/ºÿœ‡iŠÀg"Æý ì…dÙå[èŽ|§†QÅ^\›z•Ìlø,GRÙ d-‰Má¡?U஑óDŽ¥r5c /œÔœCùhFkq,#䮹ÁM¸¨¿wd'ge:ÁÝñMxÈàT'FÛš4u1Š˜ÅY©Üùå<2Ë&`V`V`¯Ñs&×/#(RÌDÛ5æñö ?úÑG0¤«¬ñ{%]f‹Ø*먦üát¬ÛÛ*N<ý:'†€.™™z™ihæxLUp’,ž°U;SŠžå9‰~NŠ^÷ú¬AÇV{r¥‹g5Š.7ËpO“[Dèp‘BwM8á'©Nõq¿!á 3y¾âÁxM¨9Ìký&ÌË‚„K\ö“"áT`šKE˜}œD‡1SæPê†çCcݩܤÝÄ * •6ƒ-žoÉD{˜ç2 ´NK9 ß ²MàT ƒªÍϘ¨BsÐsŽ#ÚaÊ)"z·â£8¹Ñb¹²Á8Ç„çÎTg%[ØK Ý+¶zS+©±Lüž? e/8Ý~CÃðe³T°H ßžñÝ舟Q³^ihÿˆä"7Ä#s5æ–øŽCîÿ‘”ðËØ=Èë΋]¸©ñéÏ5»¹T\ÖŒ/Pº î:-ö´IæAù×6;ùÄÄ䙘sfK]†H~ÈÐwj÷®ïVcùÉh‡¸:c‚ún¤|2;Š»ÿ°Êxeìäh|§¼´1S,F#½\Z‹‰s¦…ø‹ä˜é‰¸“ò:-ö²ka½IÁ§ŠÙhø'ó›‚c¤æ½àÜu…åj½¯åºY!û#Aܫ޻½Z=åA‚p‹õð¨1ǦÚOÁC¯ý8u»½Yžì] ÔÅ?ÝÿØe<2Æö yžÈŸÍ|S‰t¤¿¸w¢ ˆºx/Š58‹§‚…Ä÷h·ÚÈ罕ÎàÚz£Ê„Ðâw§КœÿXä‡ìpV¯zîõhö?•öl]Í&|6F#]ðßòOhÆ) VOtÞäî)þïþÃ)á–7°{‘¦wÿBô±õQ”ïçdÖüG£Ò®«²W¥Ù*ê»%BÇ~îûY!°`ÕÏ2‹¦dÉÁ·';{Êcy†H~ÈÐwj÷®ïVcùV_d«<]å²+_4à‰ik«9¢òæ—{š§‰Ò®f_Xo^wû~+Ï?oÅyßíø¯;ý¿ç·âµ­wtCñTÁœ\q9OÒ Ù¸€fæ–¨R†\ç‰È#¨êdçó'ù7Rͧs'ŒÛ‹Y´à£:nsZÐdŸœk˜X'$Xènc€ú.Ÿ.GŵÎk.|N”é1Îk6œ7(…5­YÍ2lœRKioB1ÅåÔѾhÜZZdAÜ³ÙæÂ‡;…(5Ì4“*¦šÖ4Ⱥz#5¶]%5ƒ¥ZÙ3®‰(ùÜ2ÄçDmO‘&CqsÌ©@†:¢êiÂô <ÙO …‘© úªiºe$ð"JÝQl• UL7žlTpD7Hlâ¢é´ 0’ˆã2ÙI¸§Ä‰T©¬Hèç§ý½)àD”(†nl”@ȔÉ´$œèQh¨³54ú_Ò7Í Dò¡ÕU$êSÞfJt6E”'’Ü[ouW¶þ´"¹í»™²Ñ‡|¨uIÁšµ>¤cPæ´nvòš­KêG\r•ÞÙ…°ÍáäÕMÁÖÒ\fê™9¦²s¤c”ðÑqt®q MŸA•Åg(3à„ë™ÝMé’©Õ‰ŠDÑkj»|®üñá¢æù:âq½6 Òƒª=Ë3©«²~)±e@H¶¢'j€ÖJ@ªõZÝá§kóLJé‡élŽ¥²:–È[#©lŽ¥²:–Èê[!l…²È[!lŽ¥²È[!lŽ¥²:–Èê[!lŽ¥²È[#©lŽ¥²È[!lŽ¥²:–È[#©l…²Èê[#©lŽ¥²ÈêFáúAË:µI¦IÆB–™' [ŠÎ[ä™›3¿ÒN“D™èÈ Z€ºþcùã–™|Sª&DàŸQ2'sˆà¶Œç9£¬ëñéNþ¥´Hæ?ž?¤Ò…0[9bN ø pjóƒÙ Îd/8=¼àöB¹íw©p¥ãv”¡6rÄ•|P85yÁì…粜È\¹ì…sÚî!RñK†í# <ã†&rlKð[úÇÑoëE¿¬}¬}$ÓÄ¢)¢(ŧ)ÊPlwN‰@"çÖ‹É(Ãû>yÃýG\ß\Øí†=VÃʩљz¯†?„ØvæýÞ!ôýôÈ×oF#Ç¢ÒQ—ZÚ=khõ­£Ö¶ZÚ=kh¨äË)Êî€8‘ö+;¥g„dùznM·}¬aÄ2gfÔSôÿ8±ÍÙÿc@~Ë ª,H¿alõµr±þØ=wèÚ}Ó»²pý-üÁ}ÚÈxâyºJ%ÆdâJd[;|¬P&þ)Ö{HÉF†Í‘‚³{Ö÷å9_Á}êÌês hl6Ù›½Ä‡(Ñ ¯|:äx¨f× @ƒ új™(·6Ó4ç¼ Â0òAÕÞ#D§Cx›$G8O‡SÙÔˆÛ¦9ÖvÚçE…¹¸õ™Í¥:5C:ÐÞ=&î(1Ƙ ÖÕjÏÙè,„3ph4ÒÔÈEÏ{žfø†ò: `“[ §Ý;»#¢fë›eŒ‘sþΆçæ_DÈÈ$ÔßÿÝíp³Ð{‚?q²Ró¼€ÞäçÄ3sŒÉVozÞü§)áHéwâš>:A^hŽÍˆŸÂ¢× ·™ÛÁC³Å³ÙmL‡±÷ˆuSÐgežËf†ó7ýÞ5qTYa7¼ì·‰D4×ûq9Ð1ë4„èq.snX…ˆX…ˆX…ŠƒNÌ3QÊt'W9«¬Z±jÅkYãàµzÿ²#Cšqbªu ÿA-%S,l'úÉwz †ÐÖ‹€¿»áÄõš¼ïöüWž~ߊóÏÚñ^yû^+Ï?oÅkZîè‡â©‚/8¸ârŸÒšI¤;¥yVº“¶µ›s ø… |§Ëð"9¸†¦¾UfœK- ¢Ð5¡‘óEÅ´žb„H‚+‹äÝŠÖ„ð*¤›®:y˜N¢M¨ºSB¯*w›‚c9³ ¥°á16ô¡SÀá6“½Yä×5Žž>•ȱì-2ž!UAkwO~S¥0YLç{/ë@:$á‡×M(> C]VÍçâ¢E--e®Ýøf ,ÛãÙ]¨¢à äu[)IEÎDÖ‰+Ãp’E‘œ›Šƒ®Lƒ9bŸ¯µ¼4ÄXO¡ò–ši|F¹Âbö\¡ë첃v*Sa4^ ÍÓÚ[†*âÍçN­èk¶àEÌLdçH”òŸÒéôƒúAÐ,ƒ¹^Ö¯G²#²#²¢´+ƒG ª·¥µL>ëXq+“ “ “ `-Bæð*ˆ·ó;Hˆn¢F%kMÜJØ `-€¶òn{8šŒf}så9b;™¤«òÑe|±¦$ÖܳôS?¡®úªb4µÃqÉ¥ÒÑw:-0Ü$dŸœn°Ü¨l.$b™ ÍOc 1õSY ùb\T8¬»[EþÊÓ„G­”åîÝÝ–†ó/’³Ô="„X‰¹ ÜäçúPï%ŸÞ7¿EÜÍrÑ.oB1LJ˱T¢w(nm¨GlM¶„aÙî†q:ÕuȘÐsŒ•âj K71)hÄöNIÅ{Z9É’0¬ 4³éÚöS­³¿æÝ¿¡Z"ùvÞ~ª¢;˜_º#vìóäg´2œ¶vîì¶mB̉Òo½Uk-l&Þá5÷{9mã†K?¼o~‰à¬Çu%j›¹•@Èô#¼©CÝxޤ뤱¼¦×é>cF/²rF‡hÈbÍuâw'fa² ÷Dco =™î–mùºùå½>Ú'îñ\¤M×|Ùð¬Ñó¢‰.¤é®0¡ûC)Ëh÷nîËÚP*²2Ùýã{ô]ÁfÝs½ÌQlVÓ¹oYË@!ƒΦÊ”V5ÌŠdàªÍÏ ›–Œ_`äY´ÃPA–ˆðsoÚ‡ƒ‡f-ihµ*a¹°G«ËÁ=o}îP½±ß”åŽ9Ø{²¶,ke›S؈1¶¸Í3P*DHq.Ʀ2ÙýàÑw ’x(Ó £€Wd†² ÎOŽŒ_` ¤«†dîp¹hj¨®.w9W© ÛNƒ˜ávãÏ“††|F`yΔŽKÖ¯ÈÉküÌÖ¯^A¢Aޜț°W˜d¹bƒÎË/žR¿ÿÄ*!1AQaq¡ ‘Ñðñ±Á@Pá0ÿÚ?!þŸ±þ£³ÛÉB8»£×‰½Ø¿ÙÞ(Š ÞÄèÛÑ 4Fr‚2ó®Hüƒª¨!¦ÎÂ8+¯òû7o¯&Ýù<Èu‰Þ´ý-0fEmáœyF†óÊuôô¸¢y H@gk~ŸŠ¹ZâUT5YÝ‹.†5g̼ùì;ÐÑÏù}›±@«ApªÂèò<™»•«Îø651ðò¨“x‘ð4\ÞÜf&Ò[|Ô í›ý@ É€ ¯›eFï-×{18X8ÿ/³Ø°Žù£H<›©—ˆxeFÐîS§ˆ¿X|þ©”µ#[{kB”éºB¤ùÁ#î¯#„@¼RAyÿ7±þ †¢Ž`‚8㊢Š(â‚¢Š(¢‚(¡Ž8¢Ž8‚ùnöþ£±Ï·=§ÚžÓíOiö§´ûÚ}©í>ÔöŸj{O·=§ÚžÓíOiö§´ûSÚ}©í>ÔöŸj{O·=§ÚžÓíOiö§´ûÚ}©í>ÔöŸj{Oµ=§ÛžÓíÏiö§´ûSÚ}©í>ÔöŸj{Oµ=§ÛžÓíOiö§´ûSÚ}‰í>Ôö›ÙæZ{QÙí§Tî›ÍÓdc£2¡ä&aKR9L›Ô/šo%s-C¨ƒXƒó{n«âAµÚðY¬@PÅú0íÞ”žÑôbÓUÖ¥^p]ÑÖÌ3ɛٿÔ]ý8Nõà;*Ö˜õ—{ê|´ù?iò~Óàý§9L©y.Ÿc€Caç2çågÊÏ•Ÿ+6ù9~„Qx–…x”ËYaý0´ãr_†òª`¼ª™–\Ë9C”ƒp^ÙÅåV§RÎõµ9Ô¬ "¡ãܤ‡"àln½{ 8@)×üع2yʘÇÂô%!n0ö—¾JÓ©2¾`†9@W©~ÍùÛSH-Ï¢ƒØþ¬ ÿ^ß­oÒö€<-ë Û{öÜŸžŒ,7€.4µdPÇ¡¥9OIÊz@Ü·”2¸w1;ƒFo€'r#ðë,šÜG£ †ŸˆêÍM_‡H†²yË*º”.·=ÜÉ“ü)à' ^x˜òò2½YQ•êÊ€qAÇ]zw@ÿ*42s«T–áÉ·zP<¢ê;{g~Ûòt|7/onð)›¬u€‹¦4{Ã-T²|•ËÙÞó`5à(²Ø p 9eæKúfwÂAW£À©Rÿ8[ûä[ûdÀ“ XÇÕéSã)Ýê˜5MP™¥ªÅ 7Ô,ýþèº z<{{ö߃£Þ©<kÕ‚q$V¬i6L±µÀºNÉàmAÉ0)–ák.QL0b¯™s'¦ÛÁé+Dw£üklønµCXùÁĽÍÇÍ&åh a3XêK@‰\•jŽg¤8XKª®¦=è\<3(º‰†ö{{Öß“£$«º}~ XP1g.éts¯YcuºvOÎê¿)Ëœs†VL!ó Ò^„#E¡—‚£˜îÒµEÓÔ´¼s0¿ãz@ ä¸[{ôšÞ¨!ÔKÙíð†DÑ2 ‹ã|œ§ÔÏ­ë ­< ±çî›-Í#Òáì¶fBe¾9 ¹n̹jP-Ãt!éw gºÚ^hšÐƒ*(xyZÍêªX†«©±äÄój ÕxBxÇ(+冃èmï[.-—9ô¿Á+üè'ÐNcÒ åáá©0¤ŽÞ¥ùŒÈuW Yºn4‚\;Kš.µ¿AþQWË9Æ„¦¥o?£ŠÕÑŒß'7ãÞ¾ oÄô8øœ¥@/]Ñý`ɵQå·½m¿å‡ü»×‚æ.–¹L+·zÃ\«\­é¬ €nÚ¦²r,ñµXš‰O9•k-ñ0ý!Ôã28J—¨jÆ<ߘ »ÑR.$ð†Þ*ìÜ#Ž·ýDrjËoá0&ÜŸzð–Ðd§ÈJèY½¦¤+Ñ%˜¶©Ù×8™v¥ÒQëPáãÇf MÀá»kÊRJÀA/yqÊÐKÒùK”àÅ…·¤z3Ô½%€bÊhßx ïÑÍfåZQ€çû•}DJô%D&»áw¯”ª"Óf„ÑÐð±8ùÁ¥Ô”È…‰Í;¼¼A²ÏàÀòAÆXðçŠÛ780MKÒ*i@ås(Ki`%E Xà7ÑÖ Ì j\ƒ³½x BÉ%xâ>5k)L.ô« lʲ[6Õ–0+‚س…Ì—¥]ÛÔëƒÚ8„h ÷ÚúÅž™Ufå­Ü7LИËà\@qÉ:8į̈&$õ0bÂü ¦½|œ³)+l×sƒÆT4'Uò@sjÍVwìï_ðtDÉpØtÃþáÿ~õÿÿ¸½x.¬tx<9ËGžd`XÕºœ¥Ð"PâÍB¦³xNäF€(+Y p_13õªð ¬aÁ™×Ê-ð^“å?©òŸÔùOêtó†aÒ.¦žSsNOëŧä%ÇqÝ=ïÂ6lÙ•r?†Ò l¼q½ën‹äò&°‡+÷–¡yÆ|)Œbøõ„!ܨ0EëXìLKTcÎ#1KǬ²™ ã‹ýx[S .šþ¼Z­2é™*…«i»Ò½%H5ôð\MA`¤îüÈ|"Ñé>úØIjÔ0òŸ-ýMe…7/Á·½m|f Ñ6·ý~!ët¹ä\>XJ°³|Ǧƒ\FeÑhß/‘Ì•i^ø>'ÂLh.<Í® ¡k “|.QÁgIº|7pŸ?Ç;NÅSz¬™R²·mÓÀozÛºL¸Fi Ìó ßÎgI—‰R«Ö¹%j¡w—\Ut€aO™ …çH `°ìoÏäìÅtsÑ)Äà!Ôt‚R[²´»—IL¨5Ük¤+V¥rWuÙðÜ<ò|¿í1|2'P Tl7Sü=¥«zµhöÛg6;§€Þõ·æxM2zìÜÈßæ;`ÃC}ñN…Ãð%|•…´)JÆJUõ‹„……´ó•Å“õm½Ÿ?“±T‚É®¬ó(×Ëœßq­(íäAÔ?ÞÒð×°lønù>Žv˜Ë5hêµÊ‚ߜ߱—ŠtÕìwŸÁ;§€Þõ·›Lc¦Ûf»ÎS8™Y¿¤¦êò%ª®¶³s”++Q9IN麵·H-Ãe|ˆJõ3Ãòù0µÝ ©†¢™ß/…V·Ç ƒŒy¼wW¤´B¹²÷YhïŽçruÙóÜ<ò|ÿí1ò;‚wO½ëjŸªzˆ8µºÜJÛ†®±= K!žN­Ô†ôâÅ27±a¯û-á©Ý„]>Uû𵻄'4s¥ÍF™jzk;˜PÃhêpãÎ0;§ª¶`ì{”¸ÉpK»tx $ ZŸØ‚áì¦H4R1ˆ7P”Ä;ÝðƒåÕub(0;vëÁ®À®:l›m¶V²™«{L¢È%yÍðªµ?SQ¾ïW´½†¸‚c{Çã2õÑêMÅÎwí  ×¥T¯Gz»·ê_ËØP;A ¸n–Àͯ{»Äù¡¨ß˜ÚÅm™º2*ØïCum!Þ¿îüžõà0•#‹Ö:žÖ«]f}êÊ¥J¸ v@ h ÅòÊÕg„™’¦ÇJ¨£ªymäi®pµ‡sël@¡d%QÑé¼j÷D\FB%£“«]ÒÅR8àÖ"ñ‘”Y¿$8ºÊÌA…ÎZÌ—5½5ð´ÖY½,—j¿3Þs~[˜«ñ,ªäJzËI°Rk20—¢HŽ,enÛÞ¼Å`.˜­nUɆšÜãd¾±M¤Ùšks([U+s‰T¯šÛœ"s`O#`mmj׋œYã<éšÜN{äž3ç+fB©­ËŽc~*뎡>»D¼Èr¼7š e¸¶|ñk9ËyðÞ´ËÒ k¨¯ì7é/jµZ·SL±@Ö ó+N5§ š‘·¨žW‰“ø–ß·½xI!¦ò—ÍÜÊu¦l–*²Öñìd¥Øêp€ÃŒ,Õ‘¿#uìæw¯l`½ µó«þÌ5Ckš¶q,0%_ÍŽVñr§·/&4 7½È?Þ¿âÏí]¢ÔøD)ÿ&},úYô³év ègÐÏ¡ð Jú‚úYô»D/¡ŸC>—h•ô;ô³è|¾‡`„åÕÃÆÐöîÕp0ïÔ£9Š=e™q²Øºk”¾-d#xhÃY(—cªçK–ŽÌ•Ö:@Ô«ü¾ÕØ–‹«˜x+sXº…kTĬ¯Ò©ˆL$¨*žãwÀ4Ê€ÌM8iEسiüÎÕðô«´+ú^ÅØƒCDŸŒ·î|“õ>iúŸýOŠ~£éž¿ÄeÞdzĴn‚'ã%ûŸýO‚~§Á?S៨úE¯ñ~ÇŠðžžã‡8+OGûŽTž\¾\š"J?Bþ tŵ^8Žý½‹b²jáŸ0mJy>hÔ!Ã0ÖŠ…o™ŒBÊ^F»Ü#bô/xK‚>ï= ·Æû9ÁÆærÜ<Ÿ jÁ<ˆûº¶ñ>%jRPvÉ͹¸Àã¼q·µv÷I¤ñ´|YðöpЇrŠü‡¹yIJå:¸î'ÇjQ“¬`¥ôx‘2‘7‚‡Õá5…¾#ŸÇc‹´ú.î}qøŸŸ#“»bãÁi¢ Êã¤7p(¬$Å‹ê‹òJ»‚‡•Û˜®H¤wGg‘Ù·±v÷öZuÎi²åÅÅÇÃÚF¶‰EÖžê”°À¢- ÀÐz”R®K»w”s={ïÎ=ýüy9MÃY9º/\ŽX‹o<ijõ”Ð-Yëáùþ8ðn”ŠÍŠÄËÌ\0s3n¥õª;wˆ;5›¾ððÌ´–õùÆZaÄ,°Òáø;{o~‹\SËíï¸J,ssˆÖ%kŽ•¹UÞ%Õ•ÚÆ/—oÆœuÄy”k2‘^¥\øzIJà/ÃÚM+G—bgÒ•÷Õ$ÑÕ »{¿Eíª‹Š6ß™˜âеoVí|š§çÃòœP-xL3*Úu`BËŸyÀø¸EZ-Y`ʪH»òЈÍîrwT³+Êp°k¼v8þ.ÞÅÛÜþ;>¯M"xÛªp ™hÅ€é½ôÒ¨ |º@c"êK˜²VÖ ¬º‡šGÓ÷x{H Ù~ˆRB Ð…®8Ÿ²&z#«Ty ©-¬m䩸Ðz¶ÆšÎ°piUvúÔ´É•'T¼»Ñ Å@Ð ÞâØÂ¿K#ÔÖã –m{Õ•¦¨ `º^:Ç !9sv‰ÒðogÊpmì]¼ê›‚c`sl<ÛtÓš»¸“·6òð‹G\9BÅuÍÄŠîŠyËã-%zùwÒ ó—¡–HzÒ¬O¦³_°bºqPø~í’XJ•_)öÓí§ÛO¶Ÿm.¾²¨¹M½‹à¡WŽo‰kºÙ(÷ßiö/´Ù}¢þóÚ%Àè¹GÉuZ¾'Ó:e<ý[‚FO›½‚á@>‰%½€G„ŠÖø˜5:†½IdÝ)t¡Ò‡JFKâˆ|½¤;úŽÍñuy)èÖW!¢]àã+²\[Ót¡Ú»\šßü r>Qè²(QmL»UG]2C\«§[¯‰X‹C(‹½ÅÝ…“f6©Ï• Åèt;{=¶.¢ZåZê²b+t±=xJr×Ä~v²¥Ëŵf·Þ<=ò È”( _¤ ð0yÌŠX<® Þ)3ÔE¸•‚ã@#át¦¢üF¼ºŠœ“-3:ÊœJ·_Yº«¤µ»{=¿9űšD±ÖñPäyÒ,(‚n{ô¼óÔžŸ‚  sÃÝ%ò,Éžfd¾}˨-frUÀtŠJÝ#žˆ8 n…Û )\ùÌêV|?=ÂSP…@ÿ)¤§-ïL.ê›ò…VŒ·9n¶jö“V´»=bàçú® {/ŸoÛÙíø)q‹ÐCQJÚ›£†ûÀ¡Çps‰œb«Ý-ÿ<§ËpxU?4z9“ÕY˜å:%®VjÀ÷<ˆhJÁ¸Á`uP¼/Ãó\%d{cÑl«Íš8x&+G27™uøT£ ŠŠ‘zÅC¢uˆ™9O‰ïÛÙíø)q™Óà h?fÏJÙñÜçá3Ë;·²½Ñ1”Ïùš_’Ád´×„j:M€"ø™1RLA¬ÿÁ ŽD8ÚѵéýßÏ’LèwMÝa¦ "åØe@­úŽæÚ 'Kõ¸rL¨Š€u¶ÈÉ%eŒs€b¾T€’€ ‚@$’@` I 'n$’ÆÛ|*ü @û$‚0H àqÁ$’Y~€ µzÄH$d’II$@€’r]<‹mÊAd/’c®·À¶Û'’@w €<€¶ßØ2@L6.’©´À¸©ÍP@É ¦’&Í$ˆ$±à}R@˲¦’m¦É ˜ò@Î2§‚S®®É#o î [#G$’>ýn #Ñ!€K$ A$A I€S<h T ž$?zÎà@L@$Œ0$ \à $I š pò$‚RI$@ €$’I ’I$€A$€I$’I$€ $’A€A$‚$D„žhI$I$E”IC’@ $‚ $I4€I$@ I  I‚A$’I  $ J$H€H$H ‚¤’4 bÌC €I =öCI  €@$A€A ‚@H $I ’AI %€’I ’I$’@ ˆòLáó þÿrJŽ^¤’Iÿ á$ H$I$’@ ’ $I$A$’@ ’ $’’X I%H$\ß—H§·Ž–HÇÞvÞQªÀ°µ’J_$c¨n-V’÷]4%ƒ`d€<ª ˜Yz9Q €TN’I]œ$ï͈ñ±”’Jþâ]$’VüH÷‚FëüÎÿÄ-!1A Qa¡q‘±Ñ0ðÁáPñ@`p€ÿÚ?ÿé*ÈžsðÎDë­õ˜(äo“ÿu©R¼*WáJr”å))ÊS”¤Ap»µpMåûÎDLÊ#OüH]â*-íUè@0«Áé(-YÆX¸6pØ,}ÿ¨¯§ê"ÌE’pËXe„öþ㺫:|hšÙ½GgÞ& ’ù%òKâc;ù†:Àve~¿X÷€Ñqf!ª1 ¬Yq€¸ð¹U8ýþfú#s–e£ ùGåiSv5 Û(ű¼vßHï:Íf³K‚ë2“ÿp,ÿܥÀï÷/NÙZU*ªˆY¯ÄÖM_\D©¨ÖÎOYèKAhÛ™rø©Kç÷2É,fU™á¨&íu[ÄXN—Öe+W(¸@ÁmMc¥à¬÷”à|’µpº›…Ã%“Rö—XãÅ+´Í׬3}!š­á!–ˆgH?ƒyµÚâ>ûËÂò޵ø®¸ÖÌÅ(h×ÕÛçÞV;˰èÅ^TÊÀä¯ÏàýÉË¢}@ÅËêz‹³ã¼ ¡ŸTý÷€hŸ¾’Ï5+¿áM7â ´Ü?¸4º±P)wü5‹‚ÜÐýkWYºr†â%Á²æóˆÊÖö›ÓËŽËð3aÑýÍhÖÏîU ?1æ¼iÿDÂ:|M?ð…¡±]î]ÛbïîP`^Γ4¯^“dç_™ª×gâ;šVw÷”[µç®™ô–VùÇ-ÕüvÕ@­?éWá~/Œ@†Ö:Äið}N¿cêuûS¯Øú–õºäP7$ºÄ(4¸šx<œ$[~-¸ ÐâЫ¤M¯ÃëõFÑÕÜÜ.²®SáW)ð×øÌþ “ááDG:º6瘋@§j¶î¼ù/ƒ…ÖkðÃ¿Ž¾3‚ÿhø|ª‘÷§¬K@Iü zþãP(³Ü.ͺi¦"Qà:n9Íãpðýg#…Ök—˜w†iá¯ò™k‹´|>µè<{üDig¯ÝÏìOõØnoߎò lùðýg#…ÖkŠ’%Ljiá¯òéž!‡“á‚ ×Û1_o«ãe;36ª/†>Kàáušü Èö†žøÂÿ|¸ÎMF}¢AÒó·ÜÓ¿Wñãê¾Ó®˜><5YÑ8]`¦‘WX"-Ëxk㯠ð¯ âA)Ž3jA¿Ê?‰ UÕS¹4çÊQø~%(y倶Õ†8+~§¸üq!ãüz¬æK:Ú{GfX”Ñ–®kU›.jÓqa”9—é/N¼³ `Ϧҧ([+c-_¤QÃrûÊÁ±© ¸Wó3vÖdì<„¿w"¥·úiï=‡{Ò%So¢ÞÓ/ÓP›+³^óAäþaž5Ð×¥`roÞ+´Õ‰³ÈQÈWÌ1]÷=ã©ZÈ?q¤V©…*iUR€¦ÅJ±Ûõ‹DÝ]´™Rý¨¹Sy9•6¸\¯$Ám•2C–½kO™èâÝõ®Ñ÷‚е~  ¢¿éZ)ð¾Ì3.ÿtŽíÇ4»”a³}æ¥rŽ@䯼ͳÿAÃÒ;Ößî&h†pš²7†€Þ/ø§ÁÊþí¸›T×_›Jåÿ¶VXZµ¿ðÁ®F‡B¬´ßOŠ­`Øg³É-¿_œË'uªùíUVŠÎS×÷ù€ÉóAع½®Ó÷r°²[þÖ¥XÏS]ßê­dÏ`ê·é¯£²d[itôG&ùˆš»«»öò„ÃD_ˆ4wß\÷…¹?â£þé‘9k3{¶‘*¹c¼Z-ç_€¨XàNÅÄ¡¾W.íh_ÍL í—ËŽ€7b3EwR€Ý¨e+v½eš9ýÍf ŽŸ‡2ó™©æ™Þ¨Å—öùöü. Q¤+7¢T»±åQØy0£æû¹îöãiÚ ŽÌlß¼ÀM™ˆ­›ù‹f—ph<›‚ŠÿÉÜhpä|·Ìè|Å6ŽÒÆ3“‹u•†° (Åæ`òœVZS+lyK¬æá£eÍþ!hAEM"ZŒè±Nq µ*ZpšÅ¢XÌJ]6²¥Ì¾ e¢+‡ôŠÉ˜‰¬GèA´ÔŒÃŸxJ•Ì*X¼å×òá5”Û cypé” ƒÒ.¨”ú/ öŸÑH‡/h„ªˆé“À÷#‹¸â ]tS· |ßÄ0•ÎUoó;—.\¹rÿCº“©:“©:‘æMwŽ’²¾ $gúµcbaÙ EUªm¢’Ÿ§¤Í(ëê$Y †%5æ&W‚7濫£‰…+ædþ'ó3+šW4®n;è‚h{±Í êâòêžXø½„Ö­+ç:äë4ÓÌÖ5w°i^®6Ûù«À-#ŽšÖ&¦T€•áßú§—cñ]^×/´xÕ½& \Îú漌fwµú}&8W•ÆÛòƈˇ6'ß{‡`Uèïéw»š¾|ÉuvüG^߈U­ ±€0Äu‰ˆ8™mj¾ZÊýßħÝüJ}ßÄ×›r¥8ªý—ñ,ƒ´jfÌ5߯@šÅ&|æZÁQªÑë,.·÷Ç÷zk-mf·²ë ‹¨M.>pYzÌ㜡CB.&(·܋PÏÂÉIN+T±bóÓ:zlų́Pr ‚J A\»ô†#»]^‰¼\Ôà2(oîþb ýßÌ4Œ¯X€ä®³}i+ЯV5ïîÂ4ùx˜ N“Ø•">^J%F` -R…2•µ¼¼£ s§jÚöÎÕ ‰¼VÔãÊå9†&§‚•½%a󊃪É\¢Õ2¡¿»Th$ª$"ã©L¦T©R˜.Z„¥ÜN_ÄÓ«–žÕ/{9.žúÀ€§¹G܉‘ÏR ¡¯xâŽ!ÕyÑÞÓÚ"™$§ãSñ”ü`¤R€<¿oZ!Ðb㆙½·jhÓ7æ€Õñ– Lv#Š½ã‹½£‹¸à¶8«Þ%~†Jr®ðqæ^Ò›sRÿEÒaÄ¥¶¦C¼¥¶¹Fý*Qhî~pË'X¤RîaïÇŠ/Ü0Àcäo.›™]ÿ™BÇO½ÿQm½Z= ?>‘[Mmÿ¾rƒÊ+î~ˆ,yîÁA»/}"Y ]ýó§OѼѬB¦¿M!›é6qÇþAµ ¢SS%é]çYy|J²Îu5 óþƒ£œÝj§úû´ÒÎIø—°ë}ª­ÿX‹ Wcÿ&EÕü¥wâÀ®wÚ%v«ÛOy’ Qîk*‹ÈuÛ#±ƒèû3ZÛž›g¤½w8<µÇHÀ³JªÒ]ä|~qW_ð •*T©R¸€[¤¶ŸÄëö?¯ØüN¿Çâs‡§õ ‹RµÓÄMø_jŸjŸjŸj—‡Yý]âc…V·ÇЀ󀑵çÔþ}"˜X/(™Óx¦tÞ>|n8W‹§Ÿ™ðð…´@œ†´sV‡¸a)¦‡þká—|pö“CÂ…–¤Ñóâ#ž¿¿yxèpèù>(ÂçÈÖV¤¸}‚ˆƒNuÛ/ŠFE¬0nb¯S3*hæaº6S@¬Þ·áÛ>8{i¡ '”°aY[ÁL†“GÏôßýå8t|Ÿ ƒË½&­˜( %+ØÏâÎ?—úï2ÙñÃÛM @—zï!¤Ñóâ8ܕϙðÃZŒv8{i¡42ä˜ï!¤ÑóãZ¼øM<©¶ccAó4uõñ»ë_CHíûrðq:³§ÃÚLhšJ'iQm<4|øïid¤¹rÉ|Uq7ðNÿ‰£~ëòE¤~©ü2Ù òçµv–ø¯7ñ¢{¿ˆŽþn"+iö¹ö¹ö¹ö¹ö¸ÉKo·9{DPûͺuŠƒ¥µ¸ÍzÊA7ºøŽ˜ç^°,^Xàm14é|+XR³â åÁuë)ä×ieÅÍSU—é9dÝXóÛŽ å•òy/•,íó6ߥk”äï§yBÿ,Ü­Ëôš¦«/Ú%qš3Þ_˜Úo3ZC"µëp¿Ê®íüG[£í .­öá×ûW¯¼Â‡ Ùl‹¿çæ*ÝîܺlÁUé6rï1A§æa– ”y75ºý¾CÞzýL‡ž.¯âl5y/¼M‘@h}¹bÿ@Ön\VÃbâPs¯‹ŽOÓy‚ÿc,6ŠŽKÃU-]Ò»4^°i9í1£þÁÌóÏßHf¯ÍC4»þj8ËI¿§òK5tŽ í+_ØÊ‹›†îóû¼ÞyB9)—zÿ»/œ©zWòþÌÔ«äLNz–÷}¡ f×nÞÞRùËAÕ2{üG$ ÍYweµy½ŒVèÕ~ºW¾"ÉaÈÛßâ¿aÕµêEpÆê&Ѝ²€L+Ì^ù½òf ®„¿|ãÒŠ« †”g. µW, löÒõ÷u¨Qû;èÓ«+~ëþi¥Ô²`÷Ox6<Ë}¥fº_¿è$±ÁΠݨ­êj;S”iCî. ¾œa³cå€m_`þc‘u Ž ì_¤[å§©5y Š5ýAÊb¶Ê?1Ôè'Þ°}Úlwý F·s&Í+pÜÊþãcÉø©ï ïž2Å­â­ÊŠÝ@{LÜ©™s*`w%G!Ì©rÿÒ5ÿ™M”@sŸRÇþæ}ËÛ¼_EŠç'.±6<ØŽy¢³©¸²Qƒü ¢‰¦x…‚DõS Å¹²jT¼é©½"Ûà˜‰àš*n'‹& Ó˜”<ˆÊ·–“‹¸ózËPhA­Ï‰Òu!LpÇÆ‚l*T©‡—Â,:1¤¼¤¬6Ü1¤vì»Á=Ó‡´a£3A³“¤ÿ¢œˆ¥˜‹>®ò¥cÊ€%J”ޝ YQRð;v-¬vbˆfUEäN²¢%k2Oý~»GÙ£îÑ‹û£‹ßGÝ£?÷GÝ£ìÑöèû´}Ú>ív»GÝ£ìÑ÷hû´}º>ý~»GÝ£ïÑ÷è9ˆ"œ±u[é¼¾—Ö£ S¬¿ ß[ð_[—×^Ÿ Éâ0"@…¢*ÿê@‹$H @ƒ$HÑ"D‰,H"@‡¬ƒ1x!@‹ÖXª g=[ôÛÃ_ᯀÿ=¦þ/Îþ¦ÝeëÜzyÃ ÔÆƒ™”¨RÅk‚jÔMºÞáoYºb¯1 +†ðb­¸sjÂöc µ */ýެ&þ-úü—1S¹¥—äæüx.UÞ1‡ñ „ ‚±fKæYyZ¡CU.QÕL-ñ.Ûá`nÝÌãµòÛS‰·P¹‘v¦?ŸyEƒaÁ?ö…eصaHÍfˆ†;åÎ#8Y£_yEí».ªã¡žÑ–CФÖÏ1ÇãTPÑņ˜H ±ex"B ˆtu˜d¬«fÖe=êÍGrÔ†Lš»†o›‡3X_qAwÉÉ’‚¢-'È–ÌvbCÐik¾’ƒ–05Ñ, ̳¯ÊqÕÀ#3^Â;@?îÿ’ú¿ä»þù.ob§´­fa{¢´KÎq)ÿ®7P¬q>#JËMX:{ä¼\K¨oX& Óˆ®×Þ è˜+[0½áI.Í׆6O8ÂBÄ5‹;ªßx]P3µk¿ntÄ`T¶¸Á®†q¾sà­W²›)í2€„²²\×t`RÊÉp[Š ÆášWK©,†¶Ê‡£­6ƒäꔎŒ1¥ºKiÀ½°¡jöí +^‘¦=ïÎqÓy‰ö=¢Þgø^ð‘qN€\¢S@«þ•=“éñéF€9˜„.ªtŸ1Ç€Vv5!íõaËÈä*·Alï<ËñNíÌ,ä5U1Ê“h¦Ý’Ø…ŠZÚÃE*Ƙ„5êQ0$‚ (á…-¾qcDÙ²4šÆË´²)ðAD-µƒÉa® 0ã¨èŒ@õ”<³?km¤ùÆ*‰´³^‰`/ÔOìКlGÈéìãx Ö-˜‹‡½‚Ž’áþXD¡h)hÞ1Šž“I¯O'p¬÷Bª h=ñ)«+E»«ÔÇ™ù„d/„‰j¤&³:@ w?°"Ä ع§>Ðe’s´§ç¬¬ƒdú•Ôv±Z»@cÍ–Õ=–§#„óŠ=5jÊp㇔ÐRS«±/pá0Ò9Ô…-È1¸s0b # q¼sMG¶òÓ @Ó"Ëéóœt,@&Ã3äÖgÖõO¸ÏºÏ²Ï²AôõÑ(¶×}/­û¶²ý ŠÁ=é5nµ$D)@á­8ŠtÍ<¥Äu°FNñ’ï¸J?íJ»ÕÀäõ¤@@A’æ*ö.üN Ö×Tðל­¤jݰúf"†ÜÄQ33&½hþAõR<ÖbU@0Þ"Ú¯3I¬øÎ:…޵þ[Íã™§ˆð×—^Ž;u:üg]@$lãUÐZ×Y¤ÞôÐZƒ1è²J…xŽ:‘ 3Z楆4†<»À/2®{ž4ÝIËÙYG‰D¤:1h¤ªˆÔ9c™R¥§QÚ –ð4 ‚6Á¬?1ˆ ‹(¡ãÏ´.­zgo Yz›IxšÁU]æjÊÀo¶º@Ãb½;ÑvÙº5<úg¯ÆqÓ~—V†‘¤ªÄ9± +­ÑÞU…iQV kˆ‚¿ÓÍaUŒ\-ðn¾ÙY ¨7 J\QVò3zÚë5L9ô¿ý=ÄA»Êœóvì(½¦«–{@€Ú¥ÂÊ Zèèq˜“d¼´3ZLî;rϬ£©o‡adc"i°h+`˜¼UÃ×*ÖŠ¨b †Ö½gƒ4ÍE²ha må·ú_J¹^^¤ÂÖi%ëró¥aJïZJ¦P A›/j(·ˆdTDÚ”î¹ÌljÛÚÅ[Fs åD KGÎf¯@†p™mdÅ…C#èC;½mÞ*`5õ¢ÜV³yóœt¨ƒ,Š7è¶H"*ƒAÐÿ*•Ò‚Ý| ÓÂø~3 uÉ,óéã¼zeyfóhuÞoÕ鯃ã8šõh/?=áil:;0ÆÑ›´3 ÑBáµ6¶öŽˆ£*JYamanî,Eí¬Z#ADi£bÝ!m¶t††øyý0ûx^©´`5`&Õh9W¬^ÓÙOò¼íÛÝ TvÉïo{´µ“Þ=¤´;a¼§×Ãró¿½ B£}#Jöu¿/€hÑ´nyÎAÜÁ¸AýÍÃVeö5ëðœJè†Ó/uOÉJÒb¨-ß;ûJß]Ë .ío¼µ4ðÀ.^9ìó.(`€–—}u”Ÿ’€¡NÓxÎÅ'ZË]¢÷k}åQ"¨Ð4ážÐÌÔsh½Þ÷Ç^·»Ö\©E¡êpR²g†!,UdÝ},î9mˆ[õXðš3%¾g!| ¤™èED¨Õ•ÈÒÐ4D‹kè2EF!V¢Ó="€ˆÒ;FTãýÝ.|'º Z÷Bž ” GW‰HÄ4Ñõ˜^ìêEø«È‡C"ìzâèõ&2 ›TLiŒ@&ÑP(Y¡©ZÁ+l…Ñ©„–F3òÞÓ¤%àŒ‚¸Ž)Z®Ó+ö ²Sx.¿™ÝÄù^>–âaø.Ë­æ`V ? “[<Þ+S²ÖK;Oˆæ ì.Ž‘|M¦’ã€"»e¬ZˆXáÑVEUO hì8U"߆.¿!À‰l²ÀY,êNð´@,V³/w€hi˜q„ÍÛBÌ+Y¬ÌË&’çáºfÁ^ØÔõÓÖ䟙è%OÑ*ÀÖ,j“^.F[ªÞfÒ÷0*)XAÊœV@Ó§Êñé¿O㥂á0`+ÈÈ0ÖÆsX½ÚaΉ‡‘û©é;ä;¯»>#™ù¾ªù›uÒÇ÷b^¨V¯*}·úLFF‹ä•d%½Â-dÔŸ–8¨ôî9¨¬~P*$o tÓY¦…Ü ê8šÑ žzÛ ÂØéøn›Y†lh­=¡Íˆ©`›äi/ÓX%íP­À’ŒÐ³Îý¥¦ïƃՕÓÃuéò¼|àmÐÇÅp—M¡Þ~uª±ä*> §lûv ¾#™ù¾¯Æq/£ºîŒêJrl)JjH2R…FCÈr1Rë Ë,ƒIFÔÁ@ò#¢B·dw G_àCáDy¬JOD*ÕÐï5WQ¹ø £$P V0ÒŒ8êmâ—ïUÈÈŠ–S)à=mìp6v·*4*ÕÍ$+þd¢qoì¤ ì1ч¾ èø|W|Ü|?®Lï€{…ú|G3ó]_Œã«µB×eìibì`)Cc»´š-IZ«ÜŽªiH зæjó`4]bñ´zlêEåaJŠº0*(JŠ‚R˜¿.†ÛÁ¬¢~^ÌÚm1 GT8Èþã³aå¶ ÷ô‚âÞµ©¾…¡w¨T¤Ã2¸¦õØßw´÷©C–Áx\ÄbºOÜãpU¦ëö¯^š UÒ3 U|€ZGT?ý‘iîÒ‡·$Ùu‘Þ4mU«Ð8#†©PW#p –€Œh†…VjöG–¼±dv€Ïüésã8޽AƒSþ¨%Â)K&„s‡‰RZV¥Ó[‹}Øb®ÍhR»±REó¨+X)UiîJ¾¯ü™´L«•é] p@(%#9»Ïcˆ«rÔ(TŸ«ôDÆkÇêEF>Ëj¥´¾›J4ÒˆŠx"É¡†˜(ò.2úL¡‡˜×Ï ]à×yß„ûYߌïÿ¶B‚ÝD•W´zªíÝ»Ž—>‰R¥J•+¼ •™Y•WJ¶VνGü/Á]7Kðüg˜1ÆEG”ê=Ëk$o¤eB³ EUÚ Zf=,H7•¦u©_¦U"ÄM¥q üðÁ¥Ýî:´9p,HÛˆ€ZF¦žÐš²²ÝaEa(KF0Ã-i£~‘rÊ‘z­-͈!iKÑnƒšÒ#Zrá ~cõú5ATnÏ33ZÍÞðd­_Cl(ÈÜA€UÈì-m¸é¢Õ𶮥ºØ¢lðá¯0.<9ZžµÙwÅ;[ü ‡W¬)Ïʳå+fgíþRº?Nè1u̪Lj­ÑV¶@®)-§læàB¡W byJŸÄz„(Ç0”Þ¢VzlàÊÞ¸ãx©pÌ« À¡´\wº .ÇVŒV®Óª"V掲T´s~‘…¥Ð³±KW{v—¡Œ+äºÃ:JLëÕÆD4‚"÷¨ êøwëð\F”#â¼ôÛÁ¿J›ôÛ¡Ó~‡M¡Ô|Ï9sä¸mJNýf¬é8ÆVêp£öê¸ó/U¢=æüð__øŽ8‰ÊÜñ#éRé¸åašë²ƒòðN;½yÌ×rª$V¦ÄånMúÝô ~Õ¤ ô‰wÀü“µõÛ7 ÂyÍxUÍ^}F ³Ràô%Æ|—>­Úz„§.åÊëÓ\ œvú ÓnŒ}LSÚÔî\¬6w<¥©p*°¯ÞU¤°Ýœ£sI¹ÛrPáQbÝïXŒÜÑ]¡UÉW€ È¡»(Ø6ô¦ýRÊóÙSõ(ܵr[ù€h^J}Ò}ò}ò}’,4ÿ¢\hªá® ë}~ Ž¿Äü2 ¹}*b¿‰‡‡ ïýCEÚ_ÔõQmMA¢š PŠI¡^7¨‹ce˜¬¯×ÁA%é7F“'¨–Š¿•Öi¨®@ÕMm)àsè¯Ò€T ùK€kíR¢–“V×Vå+í¼u“jÿH´6åYeƒ¨™ "Fj H»Ò°pªÕ¢ ’“ –š3”ƒÒŒ":1HßÜömÓå¸ëðK‚\ãz6Eyf_CÑ__…<'Ý~ p¨CG|➺6ÙPeZUÕ ÜMb‚ B™!‘·Q˜,Z¸6À( iöNÈá4P‰b#àq&V@.òo(Ó ü²•ÑYnÀ»²d¾±Ðy$ôé·ƒÃf@´]Rµ¥a1èöA2¨Œž…Ş啽Ԩëfï0-è™Óá€Ý}ˆ·¦^WäR:@wÓy‚ɽUKK×4æ._Sä¸êé¸ýLÃpT¥ ©«•àŠ/†˜kdm ¦¡Mqü#%)h·ï3‰:"–âÀBUÔc~t°½“)·cu²îËj÷Ø|§š®íìμ—Ûz£½ß•x1ú„¡ø5³h¯“\ûË«¸4a°Øë©A¦¢yä`EØE8Qöx•Êmò æ£LBÝEv +ä{¨o ­EcÔft”ª!ry‚Mªº«¼npE™»°Á¥¢·l#ò¡B 'åvÖo âëI¶[C s‰Z/=O’â=¥ûRƒÞ=€Ø1©¢otißdP@årfŒ›©$4ÀníçK„ ë«ÎAjí«œLwÌCXÐÕ—þ³K»¼š»Ö!!…  "LF0¬ÈÁu ‰Â–t„DŽZÄ‘.4ÏÄ© ­-Iù…1á­µ›Vkh@-hÀ…“&|W0^ÆÐÐF=XòM±Ã©„[[Ôd»Ú‚†S¸É™6¥¸Uš`Ú Á[©¦³L§‚"†ðç1ÐfJ{2ÀXÃzMzó׿¸ð¸†Õ›7Eï\Ub…³ñx77ž¦¸ÇéȤ!m W³·F1ë¼óRË"®·”4ìB^TÅ&÷—z´âY}ôi¸µjY^Ü@Íh ¢–¤¯<Ÿ³1Å^.ùbáÙ>g&{¿£á·•(ÚÝ%›ïqë„©¶’spÁØù쳕ùËe¹£±r±æ0©'\~e†´G Ím*ãÕë”ØÆàÖjmÓæ¸ð²ü Û¦ßã§S£â®£àüï립o¥øoü—.éçÑÿ§ç_á¤ÛÅá¯G¥tz3nŒ¼x_½ý†¾¯œß«6šž=<†ú\üïë­Äñf®§.¯‘^waœäï»'hX¤®ŽXõ¯!~‚W²‹&?Ãåk Å¹YWçGsKfÞ?—G^÷±å5{±#îŵö&!¬íΆðÙ>åÌ®ÞD{]0‰RH¦ºƒŸ†c-Ö*[Øâ¡žV¥÷`…ßèÁÑW¤ ‡µ ¡ù\Fc¸k¦0AZš‡=;úA°[+²‘-Œ¤Z®« ÍáÐaI—K¶®ŸhAqÄ•éx8 õ¹ª}•yçS¾Œ!‰ ªbÖ¤ó޽Z!©G´4FЏ•ÏDw<Ò»;kWsy»ÒbÊÔZ¶×Nf—u j1`µyO㤿¢];îBŠÒA£t×&zWB#jDxÊP=Y*¶ŽÃÉw`–µi,¦é¬àž­ ÈF´‚ÞAkû5éùß×UKÄ…ÄWpí(jª¥<—MrJ&¡{b-PW+<Ž(©³3B*‚-VŠºÝà–ª$x7MnzC¢Ü°Ñí¥N{b¿R¼ˆåÊ!ƒLfΗˆÀL«ÐTT "Ý'+Íi¹âW±J®+jðºN?bh1Û âÕ^?iކ ^¡Ä ¸¢q© S]WXˆ{êVê´%öJ§€ù`‹MeÖ¶¿§×󿮪pÂÖ|“˜_µ ¢Ym¨.²"Å»XjW „¡i`c*ÀÐGÐêÇPKA?àÒÿÊšˆó³µ·…B+f¶ÆÔ‹±Á¯Pi<á [MÒå±ÅŸÃáø~sP„Ò˜”XÉÝ•Í(ãÙ”Vk²÷|÷ûST¢Ò÷˜Qxr­ íjã •p%¡{1zw…ΨÑc1,«ÅéëùßÔ¾™×!Â(äÝì,Z€Ñú° Œ¥Åcáïá”HºÿÁ…Þ8 <ÁÚ7¬®ÎS„xç @2Ô •íL~ùe‘å¼'ÿ•:íÑÒšŸ· -U4€E£5á¾kÜ꜃ƒ ]&$H8]bg‚híaXâ8 ÌÇ£ÝyÍq¶W‚aê~wõ/=]† Ö Ö„h€W- i½q“Æ”0ÁMë]!èƒZ–¡œ]pè jàKë™|©ŽÐânÂÌ­{):•±¤@V&ZÐÖ\ø|‘ ú†ˆUóú^K‘üâÞáüâ è*̨…lväc•{ónƒhâØ&##ã®(TÜeüeëùßÔeAb¬;²×1²•_ v7¯±1«Ø„h”:'|зé­ÁÑÁYZ«ÿzt;è ö5Ëó¶<9ñ{±ÛVdöCMn ý=«Oª_ÊJ—D;Äw”lÐØ9Ž×.såõ¨a£>plE·z%î§.°Á#Íßî=vâ´;O;ÌÚhOÎþ§ÿÙ./data/apps-scope.png0000644000015600001650000015654512676763577014640 0ustar jenkinsjenkins‰PNG  IHDRôxÔú@IDATxì½Y¬,É™g½÷ÜýÞ¾ÝÍ^ÈîæÎá¦!‡³h¬Y<ÚC‚5Ö“í'ÃüªûÉ‚ Øôâ°ùA‚```CàXâœá"®Ý$›½±wvßÛË]Ï~Îõÿý‘_ÕâDV噕UyNVDüñÇ¿|ùGdVfÖÊöööÙÜÊÊŠ³[ŒfëÃ<øó6<r¿ ¯OR[„c—;dç>Op–·¨ð¸ ç¿Eõ7ôkµ,XÔ¤.hH.§G ¬oÓkÊg‰ú™û,íȺ3ó„À²Oþè«Õ”–)Ñl'+Oþíð›·Ö\ Òq8$[æ­O³½Ý"À@·Z†-}=µy5O>©Q­'/ã_¯Eáb¿ѦEéïìG38ñçÅiâ+ͺ#·ÊdR!€ 7Oº©ÐÌr <ùŸìѤ_œKŒÀ,È‹€Y žu<ùŸî¡¼8I¦d漘û.Ì$D Oþq0“ßW“©Œ@ßð«¿®õS_×z²üŒ@8þ‘æí$yp\Ê,vRÎp¡º6;S.*²/ÛhÀ@Ñ&HPÆR!˜-E€ã¡Í˜*.”ž®tLÒ?uÄéêêj'8Qð@>÷Å<ŒŒ~mä)á¸!?¬,kó lWÖ–|eõ1Ù‹HKrÀ$ ‹Röi2Óú~Z}(½.Ø~Z¹kùÓô÷]O‘rOeeCžÍ§’ŸåÌ[á8°e›OéuS&&v»“êËä[–tt ©Ã!€(/ûªª)–óÒ.ìsØ=­ß§ÕSF×ÄlïZç,åãX„ÏôÛöCÛã”2mßµ•9K¬²îúØ1`[ÛqféMò”Åtš ŒAì¸âe·²ö1:hØ}<M%H9$ër:ÿ„ýóˆc`/ë8æØÆÊ³4æÃveü¤³-‡yk똲Žå®ÓÐÞÔú(Ÿ8B~S­ æ›ÊJíg–×=ìóIšÊx@çX±<¤[å[óÁ<ù@ãNÒÏÖÙ|ÈÇ2íµ¼‹_Á5u„à4mŸÛuƒ@ý“Ƀu̇Mª yë–­MeúëÊ$?e§–Kù³HCŸà÷:öPÚ ¿HÕÁaYymÿôŒfña}ÙX Çù)ßʲùò«œù‡í¬ÌX>´/Æ3´Ñ€ºÆ[ë¶Íüó€=m>ôlR]È[· Ù]˜]Ú]×ÏTüô‰˜1¥ü&Ç4eRFN—iã n=øÃñ9 Yðsùbºc´°-ƒ¿Ž=¶íóëö ¯ Šm3dç²mirW³iX) fCîÛÅA{q<és¼L:¶iyr'Úã,çô4'®TŒÀŸ•)Ë„@lð`$–'¬#O,­2Ù®/ÛÔIéCûëÈŸ/}¡o°´YØ“uÎ7vÑ;žÂz[G~ðXºmcéà·u(£ž;ÊÜÂv¤#µ2&ñ± ù‘Vág»ºi×òC{N,ÂJ[&––ó‹@~ŸÄË:<̽°LDÁÃÍòÇÚ°žüà™D£ʲü¶nòÄ )ð‹ahq°õ–žó‹ÇJO'ñ–Õq †õa™ú9^™’^ÆÏúp S/ê§µ¥Œ.ÒY讼èÂá,3=³DÖ \1;XÇ”íPù-Í—µ yÂ2ÚF=a}X¦ò³¼H)ñ€ÌÃ?bÁt‘|ξÌ;®ìñE:Ò=¤ÏÞðÇzʧ ÙrÌ{[oó¶-eÇÚ§¢YÝ©dN“SiЇóÓ ÍõóÀ¤±ÂÊò`ÐÛ2½$/Êa}ì@±ü¶åµr˜Õ—ÙDÛ)¥¯Ä/†Ë"ù›}™=c¡%ád}È1Ë<6 ù­\´ 7Ö“kϺ®ÓЖ.ôÑ?Åðþýû' "sJÃRÊê y“É>êÂî.ewao—2Ãc£K]}Ëf?ÓG ‘`Ù·]Yßpà™¥E—LaË,ìâq’ ‹Ð‡Ôòi§Õ¥W,#§H»’ Û N1MaïÐetçÐýŸgûb}WåÀ·íRñó¸á±CV~Œfñg[K+ËSë­ÒÚ¦ÔÑ…lk[¿m»yÊËYÚŒ~´;l‚]]`B¿8vÃr™ïäg}Xm.¨ŒBÂFC-ÓY¦Cµ³ »lŸ-£ÿ]`:T™è_ôwÕ~®ÊO>;–€õ0-ÃeZ}Y»y§/«ß}ö0¶;t‡ã´O{úÐ5m\¡žÄx-|´›u§–‘y2³±MÁ3©Þòö•¢M}ø>´~èÃçyÕÁc+´¿Œnû–§µ‡ú2_bô­ ³aÒÑîÔ€€…¨Ä6&RKñ’Fù)SÚaeÆl²õa¾kþP_eâöEº²Ì~`߆ZËè!ËäÇau±1cëcj&qyZMà õ¥ Âð§ /xÂ$F£ÜyOC_‡àÏm.Ù†Ó„ÇtX[°ÞŽ1äI·ü1êA·ím›œÏ„`¬Øõec+l›ËåX Oþ^b°e°yˆD™{¨‚¼LY_ÆÏúY§¡½UìiÒ¦ŠÜÌ“v¢æX/;Ž-ï4Û)k_×õÓì˜Vßµ}Ë.ßNüÈ—½eÇ©­ÿ£+©|(',·58·#±c‘siÇVXæ¤Ï4­ö´Ò`;'‘Ôö†¸¤µ|9¥¡ìž1înŒÝ©˜äº‡3oe0xFži’|”1­Mz+¿N»Ì›¨‹€kË–VW^_ü´‘)ôÚ|ÌŽ°eú¶'/ëY¦Ü4ò”¥¡Þ2>KoÒÆ¶ŸU˜p‡ !޳²kQõ.ô I§Ùgó–Ëh“ôB|›ÛPym=ë, yÒcmBÞ\Î4E€ã í—-X[ßcø•ÕÇè¤Cæ‰)RKC¾Êq¶±òòƱTÅÏYûQµ?fmç4ý§î˜Ö ×§A€*Rîló,[lGZX&=§éÈX1å8͘Œ1iš‹aX…Æ>hªwHíæiònmmÑ+±ÁÖVpn_Ž@ÞetJ²õ6ÏúœfúDÀŽApKëÓ–EÒ5 ÃXhÀ?V7¸pì Í[¿ä+ýâ][[ì °z¬¾¶’Ü #ÐŒEî‘ÇcC [6³1¡¥¨Þ›cÌäqÓ;ìªpf÷`ÀæN¯Öéöà¶y´ËÕ$f®Œ@:ìd0·´tš²¤EB€c%ϳëÕ|`vØWÒl©ÍWjœ™2=!€±É½‰Ê<¶› 6ßm¸˜o/æÛú™]˜oØúµ¾np^U÷ÛGYÛÉ«QuÇnÆq±`lZ]]Í1j]è„I&ä: \74òxZ ÃNüXæÉ}+òWÃé‹S–4 ¦<û:%02= €qÛtìö`^V1CòäŸü6ÇZ^¤ï™Kl3 fn|6`!È‹€átã,ãu㤄ûpYNKØ'ð~Õ–Žìuyl”!“éUÀø9>>ÎWª€5pôe›xÀKÿwsÐæ…ø³\çŠ/Û0ÍWÝå³7Žeö–d :Ájü]4ªÆðaçYî÷j#a¾!†a¹Š†°?òM€UP[ržpP6xó !ý_6¿Sô±ƒ,àgË)ägý#`û0<&Â:Ôã{»'lg뙯ÊGþ®ÓзÔú(?µß”Ãd»N#À~e ú×î ‡<äí* õ±Ì4Ô[—¶z¹ì˜Ë €ö\Ù€¨¹•Ì } Ë¡X}ŒÆv±º üetÊê#¥ eg6ô­c}îãYê³?Ç5û|–v-›n‹9û!†Á‰MbŽ hBëKOÛr›Œ@ßT=Xû¶+µ>ë'd÷kRûå•#€þ´;8Ã>/ok,“pÆa=¥ne2òlh+Ù UJ©äQN—6SGN3} ò™(¦ùbí`°˜ÖfZ=åP6[Ò§µ¯ZO¹Uù3_{Їv‡Dös{é‹+¡ F±61Q‹Õ‘6Z)+-mèù˜Í©ËÐ1Èöe,±c"F³mbù&mB9<)‹)ùXÏrÛ´kùmí[¤övâG>Ä~‘|MíK¼€gìø(£×±1º¨#`ȼMbU S€ßvCµ« _³ÌùA ìX$=vÜ¥ôŽzÚÊ´v†2m][=óÒ>s‡Í!&óâÇPí$žHcã‹õ±º*>-ô  !­B/ë”°m_å2ÛûÒŸõdš €q;´c©ÌIÇ}ylp¶u–^¦¯)½=°Ÿ{S;s»Ù"  ;XfkÎüio‚šX;[Ç<ë€h¶LÚü¡—-Îx0¦cãzÞðáñjíŽÑP_F·mü=îC¼šÈ åW-ÃîUÛd¾n°c¢®†| .b øËÔð€¶ªÊÚXžœÏÌ3ãmÚ<û_ÅvbDÞ°Lz—)'þÜO]¢Üìüc@ýà\Y‹= m~’€ª|“d二ÀÀX¶ã9O2Cè•“W+¸†eËi‘¶ÇÇÉ—4/'–sçµít„¹s"œ(A \”°eò À{ýmü™ K¯’1?E?䝿p8qÌ¡éÙäŒ@%ò¯SoL˜lRL8½¼@Šb¸ÇhM\Î €&¨å6Œ@çp*Øunð+@„¿ê·ÀîÆµØØÑšŒã+/š ·$m0@R ¶%,»™»àX$-±ª,.‚0Ç~||©Í¤”gŽ÷:¬ «/ß`‘PÞvÒ,ÍŠ³Äº3³ë`Ï}vV,§f.8-{?té?dw½…:f²èÚé,¿ù ÷¸åñÚlüTmU_ðp¯*7ó5GÇ~xßí§eì ësdË[2Þ‚ÃæË[Ô«‰ÙßëW1ê¹PÎMÙ]W®ux5À¡+ – cúÛU/[ù]õYW¶§ký¯"üU°"/äVᯢy€C]ÿÁÏ}šÏçi¼Uê¡w’L[Ϧ¶ž´!¤´+ô/´| 36º !lÔeyšÁMu‡¾TÕƒvUy›ÚÖU»¦>7µ'Ô×Tμµër|TÁúc|–^–±¶|¬[¨Çò‡y´±ü“äP~˜Ú6a-—é±l–Éoé Ñ&K'-Ö&¬#Oß©µºëÚþºmRùÚʵõÌ3%oX=F+ãŸÄË6Óð eLâG]È–©·,ñ‡rC”{½Pf|[z訕:mëÂ|Þ°mªrÌ—!ؕʿ,Ç#ëçiØ”K/ˇ²-ëb´Xå³ù/i)Sè$~Hël¡½aÙÊŠÕÅhl3©Ž˜’#cK$ª¥À+¼á/l™1 )/×Ū.¹æ“58.Âcã$‡sù @ˆÈœ—»LsKkó3®­!,Ð[8¦¥J–´¢ ¶à©Â·¤žr;ÄŠí¤1HžS aR{ÃV)[UÖLî¬äÑ&¬`óË*êßð3V=3ȸ¦Ç¸ç1ëG_Ûx€`ÊÈØNǘq÷Ü“?Á[ub™,i¾kñf½™†OY=±-«·:Ræ{¿€³‹mR§t¡oˆ2»À6ãê'ê.ú;c›[Nî諌ítlQœºˆ/]W]ÈäØ ñJ5a‡r»ð!&³›Ù8¦© uéè" Ð&«+l × C³´ª+\¡0c[ï.þÒN’ 3L3¶ql1žM“q6ÄwR_,Rõ—þƒfé)ümÒmõöºX[[kkïÄöp N4ª§Ê.±%®©|OдRÓ%®0ŒØ¶2rNw­£ÛÓƒ˜ m²aွ‹ °‰=}¶ézAÙtQÖƒÞ]ƒG¢ ¬[Ô´l¡{Æ5ý(Êcö$¦m'۞؞԰Ø%ø\¶qPV_…ùÔA¬™Vi?O<ð [_ãˆz¨·k¬:» p°q°tí åDܼF ›®x)sH鬰ájq™wŒg…+0 ±w,í¸@¾ ¶lʪRF[lÀØ¢lãeÌ3Îô‘~ÒWúfSÔq·ô¦ùp¼R޵‰´yM‰'Ò>·2l§ÙPÕNö‘Ž‡Û·o'ý "šx°Ms|R=œµû$Þ!Ô?vì±ù!ak1µ6ú6 Wö¹Å·®?Câo‚m8ž £éƶL!‡ POS³jW[Ë×…­v¬.*¦³ò«kl×íQup„mXfZUN|°ÉÚe;²,߇]e:B{Ég} m–iÌNâÉöÙü¬ìaGÓYÙÓ[[´5¾1üHcó± >6•¶…œaq³ø•åÉ;‹4ô›e¦Ól÷i¼MëCùÄ‘)å†eÒûNCìXfÚ·=“ôuíÊýû÷“^˜äL®«‡ÀP˜zVgîŒ@;ÊÆ}›ÒvÎOk^Nž‹‡giÙxž¥õ-/…ë·Í-2Œ@o 7 ÆmÚöæ`bEyòohÓñÖ^s?:» °ó³–Œ@F #P ÌÛ\M¨¦mv\¼Ú±È>¶E׎…¶²æ¹}¾0Ͻ—mÏ,!m‚7Úr_Tè¸XTÿÚúÕfü´Õ=´öy0´Éöd–ªÁ¹*_”‹¸ÀÄŸ/û—õ¸§·7“¥Ï_m^Ì_Ÿe‹3 ‰@Ýà\—?m‘<ëÏ—ýǽöoÛñ2–¼8¹|Àâôeö$#0·4 Îh×fÒ£Þ62f zžüOöû”Ô°LzNåd„Œ@F #0KÚèíÛʘ%~\ÌÒ†¡èžç~œ†y0 ԳΌ@F@H°ÛÊAû¶2fÑ¥ù;ÿY ¾8:óW‹Ó—Ù“ŒÀ\!zÂ…¼6—òiO}uÏúçÁÖ>0aßõ¡k‘tä+‹Ô›Ù—ŒÀœ ÐUÀ†Ü6²Û¶ï ~.úÒ7d=mú{È~õa[¾ÐÊYGF #0B € MÏŽi_Óö#G;Èpâ¢m¸Ûj1×…=‹&3/­G³?#Àɵ›,Bû†6ÑrÐ~³ÖöŬíYDýy°ˆ½š}Ê Yô:‹kŸÍaÀ‰¶ô1´,þ}è[Vù€eíùìwF gb“WŒ–Ú¬¦“ ÚqOmS]y\Ôm7üMûk}µÍù À¬{ ëÏ,± ´®ƒ>äÇt[èYÚÂ2ëm›>òËô¨±î׬ù¼È£ #X ª,D8ÑÛI)¬ë8èâÞ¥ž¡È¶8ŦE·#/½‡³#0/AßÚÙ×" Oþ¸ bZ^,HGf72ó†€Tû²:ëLàÖF›¯#£®oœø»ÔQצ.ù-®]êɲO#°~|||š:…ÂÉt û «íà³ùY½˜;‹¥ÍÏ×EÀ×âióó„í,í¦îØX`RæC\ÑŽ{X—¢ŒïüÃ-fkÈ3Բű,ß§íÄ’iŸºSëj‹çúÎÎNk›x“ RìkkkµVÙ­ ¨ àèèÈa±ÃÀYð*ˆè•¶1È *¶À;ì¶CÇ8¸ÂÎy³ó‚-Ç*Ç+Sйٮ²œ={6V•„†ñZöÕqhWŸ’×RHh?Åu)õ„±€t¦©ñÆì¬°¶'Ê‹&c’ LX)7ŒWœ [?l~’-)íèCV™/©1¥/³Š½/à¨4 mŠï²o]`õìàXüCÿºÀXŰ] áG•ã¼/lÃ>í gøÌÝê °ƒ—ým}ùp²®£ƒ>ØÔN~¤Sf_øR߬Ò6˜N²¹,tkï €So]ÈLmcòRã“Ç`Ö‡?³Ðó/†C[ÛºÙÖ¦Tíc–Éî‡Pf{Êì¬J‡.îhCÝæÜ«ÊjÊG!Uä±mÈ YeueôPƼ”'ùÓÓi~—a;ÉŽi2«Ô÷¾€CXõ§Üº:‹Hic_²Rb»¬¸ÆVÝ)qÅXXdlë­.° ·XŸ†<)ËÀ€;ärâOmÇ4¬ëb[&o‘ÇkÝ~¯‹é4ù³Äv&oåH `Ù…Ì©¸iÙg}ì`M‰mJY}âÒVW׸¾²1ÛÖöY¶áVÅžÔã,†í,bñàäKÿv³õ–æc|¤…¼a¹¶“dV‘cÛÏsܵ~„x¢\‹X»2Ú4y°§+<;]”;@ËÀ©BO-¯ŠÎ!òï2̛؛RVýCi“WøU6f¡««ƒ½K<«Ž•_Mí…¼!áÈý 1Ëä Óª|¶]U,¦É¶}Þictšpéô À$RXuO²g(uÖ›¯c_*l›ê/kgé6ómZ}ئ.ؾJ9®ÐYØ`wl±Ó…?MdVmS•OŽ|tmDM¯qÙ Öß1[›Ò¾°Ø²ŽiUuù«ÊW>‹i[f‰íÉ/¥Úz2¥½uÔæ§4›ZRÖTee°Ø|sSÉicìÛZ l¾­])eµµ¥M{ø‘—2èe1%½¯”ù…=íaÚ—=V_hSU¸`%¿•IÚ"§¡¿a¹ï¡¬°ÜFö´¶½.¬1)´²lÞê›·|?Ú´µ8Y96oy–)Ÿ+Ëæç ϶vÛö6߃”²êÚ³þ²‰¶q¯+{LnŒ6MN¬rÂ-F y¡ú™ Sb.®Hï#íí+ ¢Í·u’a8Ðl¹­Ž¡·O‰§õ5Ä1,[ÞEÌw…+° ± ËCÆ3.)dTÁ¨O\spö?)öÐúO^–«øT—'”]W'Û#e[ÚÀ:–mJÞ‡tËÊf›¯mgóuÛ„:­,ä)/¤O«‹ñ—Ѩ#´…ô:þ—é˜Dïe@g¬!1š­o›m+o¨íCQiMl/“z׃²‰½©Û„þ£ÒšêL%§©þªíú´3•.ÈÁnÇhX®ê>è‹Mþô+´§Žì¦¼Ôk?©.Æß„V¦£½Œw’=uÚÔá…κü“ì¬R×µ¾ÚO” dK¯T*Ç&ÉA]hWÐgÍ3É'Ú6‰gRÛOK)iˆ!ë@·ùP椺wHeÚÚTFù¦•Ëä€b=MVWõe6¦ÐÊF9¤5ÕS&ô”ØZyËv‡v„eð‘–Ò.ê·)õ0µuUò¶ò]Û[ŦYóXLÚØ’JNSj_(3¸ŒV•/l7­ ¹±Ù•¾iöt]_»ô-†¥õ‰õLmó“êÈÓ¥Ô1Ô46fc´6ö/¾UÆ\<öÐÇuuñ¶ü)m·rC›s¹]c ù)Ç@/×ë(Lô¥snÆìè‹Æ¾³x‚ÆÝÒ›ÚDl–IošRžµ•´i2›´L´+ÓaeNÓß´žº™†rêÚ9a›2Ù¡®Y”C{Qæú‘¾.° ½LagSÛÑr˜¦ð¹Kóbg—t%»olk]hs Á±¼uƒƒG(½m…íÃr¨¯M¹‰ì&m`ã¤v¶ÎŽYй·ñm)7v°Cë«ê‰ñÇhUåõÁWæ'èm¶Ó¶òÊl\îe|„ŽÆM1€,îMeLk— Êa:Mï´ú–1Ú49¹~:}áÚË`ÒœtpNª„¨§lËÛxÓ»±âѿ欱.v̆múEë×.ýéêøGŸp·ý•J1Ž62)ÇÚØe¾o}]ú2+Ù±þîW} Ð*µ´2PÈ‹úßÖ•µOIõ¡Ú”RßPd…~wmõ-:¶ô“x†eÒS¦Ð±è¸¯˰œSêkƒ+û2¬k·Í·µ?”euN“mÛÚü´v¹¾‹†©^ˆ9£M‚¨*U¾IºªÖQ æÙ6FC6ð‡ú…ú™RwYjm*Ë—µ­J¯jKUy™oŒÀ,°íå+€±‹³ÉÙƒÄh¨³t›ë(§, ÛVikCù“êêðwVièGX®bWYÒ™V‘U…'µ<ê„\;©X:ó±´ª=ó¼Šù]‡ö*nÔþAÊ|]”Õ4ù“Õ·]1f1¶ùyö»·§BRY)å…¶.{9cÛݘ†-Ç6ù˜V±¨oy]ð ÝFœýs›•­Ð˶شo»úÖg}]ô|ߨŽGwÈöí\. F…ÅÖæcàœb±´ù>Ü õ…å>l˜gmðâYÿ¬ÎüCÜá w[ó1F³mšä»ÙÄŽYµéÒÿ.eOë×À4cÚÖÏȶ¶½}ƶßZ¼‡ê£ü‡h#mbÚïÈôÚf©{þö©³Ol{» O§úì¬EÖ…>ãÐ"û9$ß–õ8ŠßvòÒ¸ -Óp ëÃ2å¤H!{âCˆaXNå¬dÌä À"8«Ž³z3žùÈÛ>Cžû|X??VZœ§Y=ôÉßÚó+F³mÚæ»–ßÖ¾EjßÖ½,úr O]Cp]úߥìeÃrˆþöiS—c©‰ìy™ü­o6ßgßY]Ö›·<È£nZ½mCþImÚðWµ©ª~kK“|LOŒÊO>ÛÎò÷ö€5 ë<\†KSÀÑvf׸Rß2`â–Sc½Lc6ÄnÖØÆ&Ú„:æa·û–NŸêòS¦•ʰ²móHcr¬LÊè"µzl>¦kR}Y]=&´.ùëÊ.³±*½ª¾ª|ÔKþÎTDÅ}¥ÐË£/YOF ᱑ÇlôÒð"Fp§DÛ/6ú°Ì6LÃú°L>›†#0d8ž§ÙX•ÏÊ©ÛfhüÖ—¦ùEœü‰û«ËX]ʧ/˘öí\/8Øë”*ƒ·©ìº¶Ì߃r0©cc8–P&­ÊX¬£+ó–#¬yò·žs|YZ›<äå±ÚÁò¶³Ä¶—÷Ðõ”ƒ’²˜B‡ÍSg,­Êk;TÚ"ú4T¬ëØ5­_¦Õ×Ñ•yO"`±ÅäÅ t[w²Õü—Ý¿YôÐ¢Ž—^¶ãx0ZÚPòóÞÙCÆv(}\Õ;2®UQ«Ï×%¶Í½¾eóÛ‚ eô½ë^ër¼vm»•?³€5b(y0C±'Û‘È4G€Óæ’æ»eqÍ.ŒçúÖwí{×ò­ÇyP Ñ'è¶r~qÈcjrßv…•›'ÿ“;!×.5s¿Ànx›¯Ú³MÚT•=¯|)pWßSØ=iLÛ¼u‡'}¦ÝišÉ“Æãüx‘-M@o ;‘J´v¥·/y¡CÁ¶/ÿûÒS×°?ú²ožõTÅvš”ƒ”ùim½ž84—eí›Ê›7¼c~“¶¾PNLG[ÙUÚ÷¶¡ã!½n¹©>+ÐëúX‡>5ÅÄêI!ÃÊ›Ç|8>ª`¶‰ùÊ©Ò&&g‘h!&M}Ãc~á£~Ë„oÌW‹-êc<“ð¶í'ñ-S]*LÊäÔí£¦ØÏlÐÔàX»ľÀ‹Ù²H´Œë¸71¦B<Ƶ>—Ç]ˆHy9Äj¶å’Æ5a'ÿqÍräBLc^“‡iŒ'FKÑ?1¹C§…8…åöÏÛ^1Ðp ¦Úf `*RÊI…mÆõd¯LÂ#6ÆO¶>YJÕG'¥Îoi¶U¼B{ÊXl1Þ½ '‹0âXeZÖÎÒ‰­¥-sÞbÚ‡Yb›n®‰BJ§SvFM7Éž ÛŒëÉî-õN ¥Ä2Y¬_¦øµkÀ;d0öIX^&|ñfËUq)럪í›àÝDv6Óx'Õ[ ›øfÛÌ[êŸÙ«€×ÖÖhCëµ–³(Ra»Ì¸Æþ®1¾*ã(ÄrR•*6 ‰§,N³˜qg_4•5M×êcc…þÓN–cã–eë—Û›&X0Çð§<è ëCݶ¾JåG_²ÕEûšm,ÝæÃú°L9HYgiV–¥#O,BzÝr*9uõ‘?5©åÕÁ¬ó+1cÖNRŸZ^Ìæy¡¥Ä"¥¬yÁ¯ÌÎ:XL È”“W¥Û/RÃb’˜íònll„¤Ò21gc,« éa™²Êè¬Gò„eËK~ðL㫃í$Yu0 m]”2ñ©ƒißg‰m§ ‚Úa¬¢Êô…ú¥ó7%¶ÄuQðªêG\cmcz—Û àUgÌ7\2tÙ´.¶e}z¬®ŒúfË19¬ëÂ2ùVÕMu°$¿.¦ÖæE˧^ÌÛN±ŽÇ›@è9sæÌ‰Ëc<b6,*­ lkˆeX^T<éWU\ëâl—}«Š-pB Ä^eÛÜܬÂV›gZO«¯­°Eƒ:ØVQCL‡äc»SòÓº‹ª*úg…mg €Ø íìÙ³Uð¨ÍÃೡ¶à6}ë Û®…$‰Y}á c— [øâ[5pòç1Êl»ñ„`Ÿm³hùªØÆüŽáFLcüËBëjÑ>+lO-b?©sC~”CÛã2}Wbeðe¶Ð¦yLc>u…-îÖÖÖ©>Ù0XZ›c>UÅ5ÖÖÊŽåm8f›È‰É"-ô Çl•xNþU|㸭»h<À¹ê¸ä{¬¿ °…u“ä,B1 ×T¾ÙñÚ'¶«Pfw8dËÓò!ÿ$@.\¸0©ºUìDçÄ.ÏXZ)™qcú3£Kl+/QYÝ´éÏ[“1 ,Ð.åF|'®"÷xŸkí ¥©Ç¦íâÈu‹Ži—xZlmžøvíÊ7*@£˜â E©Ý­#]WE.í´öà ?>>íÃ|UäZy©ò´•iˆ-.?±Î꜅½1;– ×®pŸWla7wÆ‚&c2Ðò©q†LnX€±Ë'öéÖ´{䱊üyçÉ‹€ùèÁ¼˜~ÊVÎ!y0î4Nþ±‰g5d²ÞºÌÛäV׿*üe“?èó„âNŽ=Uz|Ì“ñc1¤ûe.ó4†ÔéÙ–Ù °Ìã¾ó;ÿÙ ŸF+fiË%%c7¬þ¶ý1— €aÁ™­ÉdbÌëäiƒdÌ·L«‡@Ƴ^]q‡ý0· €e>«êjpd¹TàøäžJfr&Mö¬ ƒfv,ƒÌŒãl{9†ÿÜ.e^Ìv@eíÓà$2sq8p\â²?ö!o6 ÚümžwÛ2γéÁ2܇}„VÀ*/*€”Y2=!ÀÉèÇe, ‚£÷ÝÒ¨É÷ÛÕ“ðžû z°é·»³¶¡ 0éÀŠ)í˜çÉ9ÆLC¨^ý²õÐIÇ= ç…X®|€¦4YR{¦xí5 KŽ?îòì¤5Mú…±eü;éí°KMúbØ ˺*ø.ÌÐó@V7dk– *Þ"a‰‘ßùõ8lÒ/CõeQÆO“>Yß»ô£*®ë‡‡‡µìàÁδVã˜aWUçaxÉ||܃…å*ˆ)ÓrÎa×O‹í¬,–Øú”¾wéo]\­MÄcš}“Ú°Ž˜2¥LÖ³<„´ªMG{îèðÀ=8:r‡û»2pÄú?Þ f“Ø!¥¢jDÑF•&£²Øø„\Ï´º¾!7R®¹õ3gÝÚú¦[øM•ƵY`Oüc1ÖŽIäÉ{BH¢Ç*ÓDbg&¦ Ûª®ß¹sç„ñhh;äD¥BÁ|ÉÇÚÚšã¾±;œ‹ ° ìGr`c" } }u™ƒ”¸" ¶ÇÝ÷^w7î¼änïüÚÝݽáîï}èöîºÃã}Åv4ŽŠ ªxÚ€gé[h c¡u4&µNþŽOŽShi2 Ø7¢¡Plk[nkó’;æ!wéì£îʹ'Ü#—>é.Ÿûˆp@só-Ýx’ñzï]wðÁ îèî[š?ÞyßïÞ’‰iO lgisËZ*ôk›nõÌ%·¶uÍ­Ä­_|Üm^û”Û¸ò”L^³{ëxØ/ÇGîî·Ü›o¹Ý;ï»íÛ﻽ûwÜáÞ¶NüðQ§öÑô³sdŽÖņ ÆúƒÑ ž¦²t`"Çí哼"™E‡ú± ^ÙŠñLûW7θ3ç.¸­Ë¹­‹×ÜŇwW>ò”;{ñŠçŸá'l´1–qtÚ?ɼQ¬˜ÄÔAôb®BlEº¾¾>˜Kwëb[oÊ^yóÍ7£ãš MR@Ïœ9ãΞ=;qAÑD~•6°aßííí¹ƒƒƒ*Mæ†uVØÞ¼û²ûù[_w¯¾÷=wp¼§“,€³“íʪD3Á_£ ¥~jÕ ‡©3£Ž#v– |øÍ·ð2•ŒE@l+tªîXýé³ èûY|òÑÿÀ}îñ?Ñ|¬õ$}˜Ä3­îH&ùW¾îöÞø–ÄL±»ié$ÉV÷Ÿ/:Ãö‰ïíÊú–;ûäï¸sŸøÛ² ød¯ö±_þô»ßv¯þô{îÉ«+nWÎîi0°”z…Ùд­:b8Ç£wP}A;Áö” ÎÒU®Šó ‘łۆŒ´ƒò,‹]ªeÈØØ«+ÜCÿ’ûØWÿÄm;ßû‰—±ê—˜n}…Ou·¶íëê?mgÛYÏ_°#†-è1|BûÁWeëd`ÃØ­- ²èkÃJôþýûº"íKç,ôô‰íîÁ=÷­þ÷«÷¾+ƒÐD;AÓM3 ÇQKÉÐÈ‚:ù×àXÐ|Á͹U‘!‰n«W´ é|0Iè ^´W²Ó"¯Á+TP :qUamuÓ}é£ÿ‘ûÊS*>V»ŠÕôàóšåóøÈÝþÿrÛ/ý™[9Ú×\!,ü%®´Fò&7’×[ób†ÌjÆ÷¥b‹ž‘ÓܳOþ®»üÕÿÒ­n^d«ÎRöË;o¼æþù?ýÜó?ù¡ûüç?åþø¿&'òÕ§fǯZohÚÆ`s\iYè…‹ã>°ëØ•Œ2ŒÝ#]å¢S1U>Æ4åB´¹¯‹ÖJòX¬Ë Öº\i=:Üwç}Ú=ñÕ¿%ùCwñòUwîü…±ÒŽr8ÿwïžÆØ“¶S˜RVK8vp²Õ÷üe±…U0¡½u}^¯"¼ªÐ˜ mooë@¹p¡û‰UfÌ–ª`Võ·o¾Ð§¾°½³ó®û³ŸþîÞÞM¹”+aP‘îšGÀòÛ*Îü‹Mâ”ÄI0ê¿.¥ŒY´¾{>X‚—×BѼüœ[nðB¦§‚Á"cLDZðúzòúö Á¡ ÐÆ'ªeäû^ÿ×î†\éø[¿ñ$Ðn µ| û¤œ3^ƒKú·ÿòŸºƒ÷~&¶‰ýò¡©8©K3£êw!¶«Eùd¢ž$(MªgÓ ƒ‚ðhŠmQ£y¥ žÒ—«ì>,œ{oþ•»ùÁKî¡?üïÜú|í’~³}ò‚LúÿÛ?þoܶœ?ŒÑµµU™DW>x9Þü˜`ÙËñcËÊÔQV4„DÀ3% ‹GÈóc\h÷àî3òºÈ×2ÁhÿJ½ÉrUqy….\ƒôM¹L½¹¡¸:ñcM}’¯XDÀ½;·ÜáÁ¾»tåv²áŠêÝ»wU6ÇfLѤºHkÛ>”gË'ûÓÖø|Öýù/þ÷w¾ð´eìÃöA¬¾ íî÷ÿWwøÞsnM|R|E?p‚òïwBÙ´ùùñDaÄ¢€á¥ë˜FJ’°„$ÅõНô²ò^\É9Ú¾é>øwÿ½»þ·ÿ‰\ 8OIIRÛ'7ßzÃýïÿø¿u»Û÷ý¢ £Mü^“I÷Xvypc@’‘·°[þˆ¸Ž7– &êAŸùͧhyþX×8è°AyÖ×´)/ÿC–°Ä/O.óÃ:œ¢=îÒþƒxiƒËÕëëJ:–3óý½wÿîmwþâe/+á'ÏNc"½Ï±šáÑ&Ùʾ¥ÕàÅWÉÀ¿Ëù ØNZX…6‡vÒÞ:i£+UÓXË»+ß»mnnêuŒ¬Â =X©Y}´!Ö~R]Œ4ëK¨Ÿö“§+l¿ÿ«¥gþ£‰ Ùq…\í@þ”B(‚Ù¨Îò©bòE[ÔFÞE?•E!$骜ÅA‚ôº˜Ø4hÃ>—^˜„Ôâ̪_ØÀ ëÍby×Ä©£#ðîµ~ _yü{÷ÌõßRùöƒ¸[ZÝüÞÛßwoÏŸÁ‰Ý'ÞÞñ$TWú˜H]ቚ1C\…¶ºNÃÙ¿Â*W]_ÅW.+î¨8Ë=Þ¾áîþì_ºË¿ù_ÔÑ^‹÷_üÏÿ“NþŠ¡Ø‚‰s€±ñu,«N.(a¨R«§äÅÙzÁKÐÿhëYñ)»Òõ㼨áXÃBšöB×*øµƒ¼L! ö>@¤-?á6½ÂRø³ŽãÌbóÊʺ۾w×9»% ƒÉW°TP0Æ¢©=Fkˆ,+ý±Ç8h]ÅXlö„ôTåFW&eAƒ‘ä%}ggÇ]¼˜öû?ÈÆ¥\àF½eeÒ‡œ†>ÀVâH»ÁCZjlïí½ï~þÎ×õ,cEΰE•äe—Œæñác‘ÒtRn`Ñ€V°â"¦÷ËóÀv½ñ bÙ Æ…‚Š›¥­<ì^—Ȇ.•…^QJ4ýŠ`TK.õˇ:àˆ.¯’ŽŽÝw_þ—²øª”Fð. kœìüìÿ”3ùzCvÄtMáŸäå¿ÀÉ‹GÙ˜à‰5>µ} þƬE`òǦ¸êÂMüBÿ óÝ¡€½ûò¿q>ó÷ÝÚ¹ëž9áç ?ù{éÙ)†ÄãÎ/<ÞÞD?Ñæú±`§_A邲ù(HXØÐ/JQ–Ñ¤Ú dØ 9…*)ˈ…NòŠvŇÊÚ<#OWÉÙ¾Ú1 Šÿ€Ð…ÿ×änöíûwå«€‡´mŠi1ÖêˆÅ.[_7ŸZô3n–ÙB–/uŒ¥î[ЩŸä•Γn0*fipÔßåF-;—h¸QÊeöwÞÒ˜?ô75¶/¼ûM™ý ’ŒîïŠî8• A ƒG6”¼­JU¿N@ Ó»›Ñ¢à÷m Ç·Ñ‹òXJQ/ 0ÓðûR!reGìåå~•¡¡I ]ÕYXMSžvÞvïÜ~Ñä“ãmDh˜9”GüŽå?Lü81Ä œæQ™Ècãw¯ÂÐwÂAÛWÅ`õ§ð ¾ª¿‚ùêñ¡Û}õl’4ýο‘*¡8ŠNLæÞ&Ä')ó0øt\0Dçi´ òio ÌzÍ‹áÃ>â•’-ë˜hˆMaÆ‘_øŠqÈC(dÈ§îø€ØB.nø[“÷øÍëórá‹ÿá¥uùJÁ?Ææ¯²&޵e1V­,p„MjWaéÚ¦¡ÍÖŸÔ1–º,¶ Y,[;Ù®mŠc³“ÆÆ„Ä› Æ”i³ùú†$#Ä—¾‡¶¾¬wüKP’xƒ ê£).™úi qÊ—‹¼P@ÃÆ:ÖS?Qc²MéÂì'úÓò°0ú•èôKQÛú •“¿~ç,ú©í` ö±NÔ Ag$¤ž‡Aeœåáå+/É#ØìxSB‹ý7¿£pb‚Ò³RM5Ü{h°¦ò¡>Ãï;0jÒ®AØ©»Ðütñµ üåK²n÷õ¿gÒíXžªxö¯þBqÄq!½ˆ! ãX#‹M ±45]küÇø¹~ÔÁ‘bÓ,Úù²Ê1õ*§(wðªi¢44Å€G";vìC@IDATȨ䥠|bnZÄ÷þZ‡*¡éBBx€«~­!WpuÞÝ”wê}ûã#4m³1¶0ÖPVX&=e:ò=¥ÐYð'ôÉ–‰CIóFd+ÓêŠÙ–§‘¢Q£¯ê(„¡ì<æá,žco³Q&dà’Ê›o£gÞÚ¦ÀV1•güß»ûšœuà Cw©ô± ýªaJ(¸<ïopB[ OHA•ï%ø¼f1K3"‚ù"ñØgÜÞÆ›ö1hBõL!Ôa"+*¡Kë4£Ò„2x÷ä·$å6‰á¯8—Kî‘ ŸÔ³ô·ßrÛû·5Šn¬Ÿš·bhªP}ÞN¼Q?a®FÀž[;7.ä5¿z>&€;ö6ÌÛ66¿ð$YÐY‡šb”³SõÚÝ{'©«7Þ|h» Ù”v(à¾oG}íb –ÕfµÒ-c_Tøv,^ì†qŒ{F°¸<<”*®Jbì­Ë±Àºc}²h+‰ÏùÑF{0q?¾~#‡g,Ž1-À\ð ¢ ^ üè£OŠm«îú#É“QûjϱÜ€¯Úlˆ)8¾¹ÙÄq¶žEKý…­MwåÂÙqÐäöžü€Ðmmrî솻vq‹qMiˆ/÷w܇ww´|þ¬È¹X¼B‘Rêqætw_y|<’+rI}ýÁy÷ÇŸý¯Ý¥­ëòÃDïɽéÞøà9÷»ßqÛ‡·œ_È·¤0çbÓ(øz—Ô/U,~⇟Â'mtAÁÊ–éùžÖ+C[ŽÅ«BOKÑ3kŽaá7É(ÆR÷°ùºbzCXÀ>ôxç¶ôIÇ8äTÙ0þo½wsÌ* ÐçP‡MMSý¤HW‘‰ö‚6Í+ê}Œ¤ò˜Ü··wÝo¾ãÞyç}¹ü~_ÎÆåj”ÔŸ‘G÷Ο;ë®?|Õ=ùÄ£îêÕKB—KüR§”©ºðw\È~᎚(6À&ï‡'¢­B‰ ­—c 'T’Ç„‚+ëò ‚~ ±¶åÀÆXà2ëMû€ÌhKci:dù1å)6Ož®Ò™,à`ƒ}ï´z€guZ€Ã¶¬ݶ e°CBò! e³ yl½ÕkùÈKšmCyXÇÔÖ×ÍïÈK•¤â‹ÆGy9Ž?[G ÷AG/c*r‹?©Ò­ $ËÇ‘äýMX˜°%ÈéY9dû6›¶EСòÔI!ò6ôÊ‘çA;\úüâGþÈ]9ÿ°Û;Üqçäé.n}Ù=õÈ_sŸ~ü÷Ý/ßþ+÷ü;ßrûG»X!\Å@‡FW Zj)É¡)骻¿çÔxPöš:^ö 4`ÍúQSàØÅnø¢˜úŒúw[ÅpÄäë0¨@†ã[I½¸/ßû <Êþ³èS!ë˜&5Wʾ¦h…2d‹@òé$,#¸Yï@Îö_úå¯Ü/~ùš»sû¾Û•«\dÂÇÇ><#—>÷ÅáGüïëaû莻zùQ÷{Ÿú‡îÏöÏÝÚ†6ö~©ÔQø%FÂN Yð ?}œl“ïd!¯Ç…ô-2ÀW¯¥m±U Lä±®ÕËO¹µ‡>íV¶® °x9 Ì`{ìÝ‘½éŽoþB —…ɪ„ï–$â—® Û&6$ÈË¿.µÓa»/㛎[Íù‘JØ,+G à ÂJ[ ûòxœK7¡ó•L‚?$K\òÿö·ä^üÕÛîÊå‹îÊ™uýÞÿ@ÞÛÐæ¡‹çd9÷Î{·Ý·ÿòÇîw~ç‹î#^÷·ˆÇK|°°ÀY¾Ž¡ùq1ñ{[ð -!]²à-ŒÕ²HÐzÔÙ÷xb½O‹G½–ýp{¬¬ž6¼¦QVÛª|u­êu`›æÐ´z+‹ù²Ž‰ÉŠÑ §ŒN©Ò˜žú&Õ‘‡i^¶ ÓCLx˜L5àà* r>iàd;_‹pÎâSø\±c.@;ýCʺBú'BzÙßsx°P‘Âã¡_B€.'ÃîËûi·¦¾Ú#v㉀#¹" BT¾ÒxäÚÇÜå­ÇÜ{oÈ×›/% ôöÁT¨Ã¦¡\dAÏîán¡»¨ô,µ?aÿ±ü tà †·®ÀH¤¡¬§ÌP#ÆÀlà?Ö?ó§nóKÿ™{°†÷håšÁ‡‚%½û·û­âVäÇ£²é¥søMø˜Eë!zÇöO¿µM¶£à%cÚŸ"ˆ8ªLô¹ÙFv‘&õãj£ÐÇÖ?«HGî{ßνöæ ÷ðµËò4DZüXŒ|ç/|'%S Ü”vûò(© òG¥Í­»Ûî»ß{Îýá|Å]ºt^¿£Ç=Q£cD±ñÒÔÍŠØ®°!?¶ W3¼[¾¿ì"V[ÜÇæÔΕÅÖÚ‚:j?û´Qû¤#_B±]êä ŽÃuxC`gUîÚæòðä˜ÒÉÏŸ¨úÀ‚O!k`ñ±ù²§kÞ¤·)¹ó;|­“ ‰›÷pIåí=yÔ&xpœføšážò@Ž|·/ó¬üô©6ëÈzçµðÜÝÙ“ þXnpÚ“;ÿ?á>õøWä»X9ó+‚lß?öDy?äŒÓ¹(ÞX•³°}yEî-”¡¨¢O`*b¬÷ß7“·×I&Õ&Xx»üYý‡}~kpÝF&é±,hŽ/Ò­é?÷ |0 mõñ¯¹ÕÏü·ÿÃ&Øž÷“Ü€ê­7ÖOüJò¿ }¯g¯¾ºõçŽüòŸÝôX(pà £¸ûìø˜Á¨ÀæÇ?è:î¤?à .׿üòî9óÇúø&þPnºS7½(m]öq,÷ҬɕÜxïþ®Üð’ûÝßý’Œss%GÆmàxÔ± [F‚ýèõvËWtX<È•%Œ+L„œ ‘Ž}5®•iÛ¾–²̾¯Æµ5µi*LB9ì7ÖM5ªÃÌ lÍMzD`_&Tœùb’Gà=×Ù±ØP?üsàbÐbâÞýàPʪ,ô>8IÑí Ï;œVàÅAåI ÅSÛûwõþ,&@‡ YVdÈ"âÓû]yîSnþó²$ÀbGÿR™på¾|Wº-?•Šõvðâ{x¤ãÍË÷Ky´JV?ëòîÚÃã„®\òö(ˆO58z»ôÕ Ë~(¯t]{øKnEk|ðŒyìwËÜÁŽ[{ô¯¹ÃÕ³n —£å~nê>F›´$…ƒX¼ÉfÇÓ¨IÓ &@ý‚­¾Þ  .Ëfûqž}Ö>íÁ¼èäyT¡APââ ÎWmp”H3riw4i¼B표ÅÈ•RÏYÇb÷1Mj0ÙX$Wñt€´—æ¾1ôK~¢ØÀs(g;[k×Ügžøš;ÐKêÐïyp·¿Üþ¤²¥¥´÷Wv÷î»nË#bråA¿C»Ðö"…llëb¨<º-gý¸\ï.¬óí>50c#¿>¤r+ä_í!Ä ‰òÃYŒlld;Ò·ÆÍ˜ç®Êà¼Û8¼-g¤¸)ЫõkÑû¨`ÃW™HõñµÀ¢Ñ¸èu‹„‘í —­8c Á_¶bB7`NÓ7ß|×ݺ%¯Û• ïžÀ:fÜöëľ¼'ã/‚~å•7Ý?¬ãí$V0UŽ=H*ìÃ×2hô.Á ÙG?9,W¬t1ÑÐ2w[ª±Ó—㌭Чã³/Å¢'/z{žTñ 4ŠÝ|À3T‰F¦ä•„(\ºÇY;7öî~ð" ¹ì¹·þ!a²á»æE@z–#uZ­Fˆ2ùÇÚ`GΈ¿úäßt—Ï?$/Úö©Xr,ßûãþD½ÙK¸±À é;ï¿ånoßv—äwÒÍúbä~VuCøðŠ^lzµ)lPJ‘‰·ê3æ†þ#MÕ ýøÊc] Åy_BC‰ÒN’û0ô^4L>~¢ '4$êdêÐâ'Þ0™j(Ol ñ ¬'ó^¯®¿Èc FÈC N<ò÷ë_ËKœäk«-ß²ðõ'TV*ÀYCÈBbÍÝÇïܽï®\º c ÊtQ¡Ò‹Wû¼hµKÅx÷¶á8ÃZæöí÷e±|è67·ô•Àx p7Q8N"žØÆ˜«[4ÚÌeà`Ì%S·¦ufhGŒŸ<¬c¹ÌVò¡Þò’)6ÔY^Ò´Ò|Tá1ì§tÚºªyÀà"Âô~ØZr¸œ ¾‹ç6ýK~¤d¾Oïíì»÷äYilçåE@]–ïŠåOõ ïÀ÷ûïëË‚Р_ÿG‚ô=r‰/þùͧÿHoüƒeþR¿¼IðhG&œý㺅Ÿüq3 Âà³/þ@Î8%Jb±g°Ë†I·ÈZD±Ã³ÛX¼ðìŒcÀ·iõ ,E7üÖ[ `œl0G'M=ÒÅ,F÷ €8Ð oÁ“k.zc7Ä#¬a6ÎúWtá'dýÊgÄ5ò*󣊚=þtð“ÁøÓëD?`Œ%f$õ“?Õ{<ö‡‰}?ý1á%’¯zŠ«ZX†Ê˜•¯²nݾ§ »d„¨8Åv*7*|NyQT[e¼Jvgûž;á’¼“à}wí¡(KÊGÖDÅÜlÚ÷bml,±®©3mÛ7Õ›ºÝÌ©)“W·£&ñÇêªÒhø1 ÙŽ)ëcižX»64èÔ éHÎè5ÞŒ(`vC»õ"¸ûDqÊ$:6\ú÷oÄ׈l^(ëšœ±`NРœ|–!%}ñÏg®ÿž»zñ¹#7ûIÑ‹‰þ¡ì¿TòÃóîæ‡7Ý7^ug¯àyh!‰$¼i—úñ›õ°÷H•ò Åíê~ò‡% 7áÿõf8Å@¼<¥Eø0ùèŽ6CÝ0'ɦ_­À ôaAóN M0ÆY?ΠuLýŒÅZW›þ‚Ÿ( zЭPäÕ\ ÀXÐra‘ò‚ÿÞ´‡ÉúÜÙ3â'Þ9ý(˜ë%WøÝŠ=¹oÄ×\¸ e',@L=m (!Spüà¿8^¶ïË».ð{‚³ÃlVϼ…åÖ¾.ñÎb_Â’œlíé[ÿÂ/’÷ÖR ô!ÓækLFˆ9þÂ2‚³ÐE`DYþü™¾LË2Éû¸/TVðmì4¨‰XHð2¡ÃËCýÑáªûâÇ~_(~¢ç;ÿñÊú‡ÿäfÀu „Ï¿òœÛ{°ã.n\Ò³}}¸@ƒ½÷É+5ð’uýÞ>¤Š:{Æ›÷w\žšƒ+ð²äR¹¢Ga 9QP¬´fÚÚ£‘Ýb4[Ÿ0¿ÔàÂMÄCéƒU\ó†)–7 â1“>6Q´õO-”J6øZ‹p- ‹Cü–n^ÅÙÿ–üŽEí~},pîØÙÝx…/¾íRço‰P”‘K3´Ãó¬¸+W¯»÷nþZe¯áMK¾Ÿi0ØþŸÆÖWÑÑF~¨/U9/R!¹`rƒp¹}'"%Þ`Ò’]Ï,Š`¤‘üø“l˜?íA2ÎZðòå¿ìäC½=0&_p¨M5°i´CSyýª<Æ÷æ×ÜÇû¼Øá/Åb¢×Gÿ0ù M—x@®±ßüà†ûÅ+?v×®l¹s›+rõÁÛ=ª@?àÍl8D…ú2²´Ó›õñtmÞ‰>Q°"¿àb*È0H1ò¦ÁèP=Z¯Æ•–:­Žñä˜b‰°ÓdÕ¯§ë(Pª=Û˜7ùᲿÖslpÌw¾Ï{jgŸp·€µ°´èo]Œx8|_ê ›$-ƪo+c^ìÝĔœ]á-7¨À•°-¹) È×ß…Åtâx§_z&Ød”bü (tÀ‰cóÂ…Ëò+€ÛîÊ•ënS~Øÿ(‘mdÚçì6“s³àU·i;2¾E&/Z€·ÈMzpæƒȇù䙫 OBE|Ñ2ÚùFE½dF¸Ôy(;.ÿƒUNñ¡/æÃ£BZ€ŽÆÚVŠ8`pùÏþ™û§¿ê.ž¿(7cíë{þuâÇ‚Ó?Îþåu¹kòÿO~ñ}‘yO~õ²êÝtVÈ£ýz%¡ÐX«¶‰H‹yBrã­ñÁ+u²GÔ–Mï½Ô‡…¶ ’˜S›ûyùÎágÝñWeAÑÁa ›äåCOüu™Ìñ"5Þ ð¨O’0Yhányõ“ýh+ø þ•¸#ÞöÑV`'¦q¾ÿP‹©dýJLiàÑ?Iõý¤3¶å][òÝ‘~Å…¦ 7U{rUá@ ÜÔ÷x;¡lÛ^؇„c–»b£ÍÅKWôÁéU,`l}khë"7[ÖÉ}ÚA¤X䡲<¾aòçøà„ÙÇ?!ÈsÒD0*¨û¼ð2¼È1Lþžã®Ðqƒâžñ?¶øQ üŒ¨mý÷ÈE”ä¢Lƒµ°!ž­ÉûÒoÝrÿ÷·þ…ûOÿÎ%¿°¯¯êÕ‰· É¥T\XY{àÞ~÷u÷ò›?s®Ó ¤Žc¢“M9ü€_Œ®4ÒGm j µS‘&º`‡^m€Ø —Õ*Ø¢~£J<¿Ø‚›æüóÝ¡R¡2UVXݶ ceÓ³P]Ù ]*ä»k½ñRùÄv^òG›È䟔þÈ=É7;|T8X|ñ {µŠ¨ê´Œ«R8û_“—WÉ$ñ¤Cqåi$ÓKªõ pEaSÞ°%ïðËÜ =à`Ž"¥fá±>£j»ðÁT€xÚEÓ acêKù3‚@›É?"n"Iûj"Gÿ• ¿`‡àûàZ(wËòØ:ÓäT6lwŠA1žm'Õ‘'iZà¦qDb#cb"óD°­tä'}åF©yäo,bhÑF‚˜0ïÉ÷¨o}pW‚ÂÞxâ¾" 2~éÄ¢¼#@¸¥þü…ó}×}þEùÅ¿g>ë2Ïüeò—_d—gç÷Ýw~ô ·º)O løŸ†L,Ô^ùPÿ ^ôÂÕ¬tðù?‰®R=ž¤Â±„æu6þ6»ÊXõWRý¨À`ìH0ÉûÕ'6¹)mõÜu}Q÷ÂÔB>Á4äÚYýIYéÌ€#c )ré†*F—ü L=<*|̨g#Ï”îSć°BaEa=zû)4… ¶¡—þ}»³òÃRçå'¬oË {-óñ¨%ru7Œsül0^|þü9±Äc(=ìm÷¦y±Â«cUŒŸ?F¼Ýj'l¡ÃšžÑb@ ­k^æcpgc7ÐÛeqáeT…ò„岎©Âã‰Ñ¨cRyR¦Ð§ñG>ü¤ì¥3&!Õ°R”GHò‡šr¶¯4ßaK/£¢ºú„Œò|E ÒB¡ßk‘zÔÉl)ú0¯È-ü+r%úý»7ä ýR#gþ¸ì/7É“k›«îG?ø®{ûýWÝõÈ¥ýóÏœK/ 1'Ñfõs”ì´¥°yÄÝ,Opsހɴðz, Q-ÀŒ&°CÏôÆ‹‘q#¡¹æVÏ>4&uÃDO­l1ÁñxO^$§¥úì¹Ø©NÀ9´Ñ>-Á'ï]‘(¡¨lŸϘÔƱ’‘]\Z¨½‚3Ë&œýƒ¼)gë׺ìn¼û¡ú{$7ïyÆâªæ ~MTgå«„ËW.èO{»ñészåD¯ŒY©Rã €›sù.‰c[_峌lÓ6Œ°m#d m~0œO˜Áƒôq`† )lðžþ[‡Œ”W'‚C¸KÏõ3˜á{w}̈o”ï®—­ñʪN™ø/TRšÐ÷äñ¿Ïì+î7?û5yy^ï‹»ÿñ½¿ü" \úãíWÝs/þÐ]¼&ïX9H­Nßceã>ŸçT{´ÄòH‚ÚÒôƒþ¬ÉÍdzé\¾¯Õ >6GBQmð’G€÷ï8ˆë¬ŠÉ#^£ŠO±3úkŒ†û7¤ýñýwÝÑ­×%/6â®þ¢¿|ZàFðIˆ×ÈF<Ë5äJYÕfÚ«*m‰…Šd ‹*5‹õ¿Òý¨ÅPŦvè"à{øúe÷–¼¿âƒ÷ïˆùòت^–÷|µ>Å’iVø:ÈäFA ‚ r@?”wé?vñq÷wë?»är¿ÜìçÏþñs´ÇîîýÛîÏ¿ùÿ¸¹Ç`C¾³ÅCÿ¸€€Å ß¼§ª$’"˜êâ@ìÖ¹ )tÁ<ÔK7-úûÐ*Íæec‘»ãåjƱœMúKë"Ÿ˜‰mü0’ü¥_È[o½äÜGáŽû*›È–Ÿ^»þyI'ß!U 0’Wã-Œw^“«=;ÒÚOb ªžõ{µ © 6/6dßZjÙvJzbñ`Tã1TYDùþEâÇcŽX ŠÇöÊ “°T¬ÊÍ0çå»ú‡¹êîÈë€WäfËýõA£`Š]¬^ìpöõ¡KîúµKò4€\Ñ^7¸°Øƒþba Ú¨2KÏ+¸ïå?3-t|Å„<Æ,¾[ßÀM›y+C¸ž/eŒB?Ñøæ¥j €yo‘íô7œù@¨³§„&Ý4Ja*-Š˜Yµ ¡TR\._— ¥—N¥Ì`ŠLóCÀ÷aWùõQW'=/à T¡ªDs³®º÷ÝÕÍËî?üüßw+òˆó¾,p©7ý¯È÷þò\õ7¾ýu·s|G"Ät —?&Y°ÉÛ\øWÞœˆ£ŽxF}oÊö5ªÅ±•°-“‹>ÚP©A\1ò Ô)?ÀbH ¥_È*íX~8éÁ¶üÖAÕ§ \~HiW t–«xÖ¨“’ŒÑõàÞÛb—ø!÷X¬ÈÄŠÉH–Lâ‡L\Ú§âô¨wÞ'|ª¯ðG2hÓå¦Ã E«z5G>ü¿¤È€¨Ëå+ D#åC‚«`ÒO>|Õݼù¡ûðý»òU^ÑÌvલ­Ê“+‡îÌù-÷äÊ£©›£ßPÈÔÀ‡ãal3X©Z€ÝÂ#“ÿüÈ•ŽB=ÆúfFýª¢¨ÈI#4&4j9ìFy0ìþ™™u:éIѳ:b ¢“$˜¥³¦z©ôQKc(b9¹L)zrN¿€ð7San£¸qÆËRºÈ׉Nšñ‡MôÇyd>9–Ëû+Çëî¿üÝ•ËÉ¥ÿû:1Kø}ø­uçþâÛÿŸ{ýݗܵ\Òaœµaƒ^¯ÙËG~|P{->*7l)øPÓm…n ©˜>ð‰—Àà>‰cYÄŒlC•ØgÿHOmràÊY¹ pEéâ¬ñÏ)‚ÈÅY<|–ÉÂ[sŠé$WðÓÄw_—ŸcüPXDHF¼»ŠþSÀVuठ–S¸÷1ÙqÂcu“âb›ïaù”½¤ŠÁéûB‡p‘÷ØÃ(¸#õ²8ÒTx‡Ä%¹ÂõÄ“¸»òÊj<Ç¿¿·OÊDS6¨]—§ 6䫃G¿î®Ë€5ùŠJ¯¤é]…¤€øÃÊ[8’:òv¡ßûk-|+, :X¤ŽyeËQп«ÓÕ~<ø±pºv¾)y0ƒþKèº6!!Æßþ$Ä 5~"êGÀ”Iß÷ëBÑŽh’Ç®b¥'/u4 ^êp¶…WõbÇeøá•uwÖýñþÔ]•É? ‰zÓŸœýã¤÷{?ú®{þÕgÝÕG/È•m>oÃxÒ¯`ô²'r­‡]üó6‘¿u “0ù6 ¨(c7ã9n½¤+•°;Ú°c_Œ%B_ÙzHwC­”ÕÇ qY|â&Æb±°ó¾œõ¿!ŒÇ,åÒ2š mEªØ#-Ù”E>”×óðØ( Æ%’DÄ=‘FÚç°Oþ½.`[ô·Èâ˜ÐN‹qŠgPp_ q}Tn¼%øÛ¯ßÐ1©WqŒè" 2q¥?Ösõ¡«îc•³ÿ3ò:`ùEDè€`ÒMlá)섇ÊB@Ô´G½|°UžŒ\Þ¦ ìÂqgÇÇæ1sU3è.;¨f ¾’JÄLÖˆ%˜œðݹÄGÙðDp‘I/£¡näõõà›£S"–WÐù‹|øX€»›õW×äÌÿ¾ðÜc?)Þ[pÝ?ï¿&“ýOžû¡ûÞO¾í®v÷wÜÖÚE÷'_úOäf©GÝ}œùcòÇã~8ó—Éÿg¿|Î}ëßÃ]¼¾å6ÎÊ;Úev÷gs^©4dt®Qù°ÙSFºF–tð¨_ðm,C«Û|@ä!ÕyR2¢Eo®Ïál—‡õDå÷ç6&Tk+Ö`²ß—;ÞåF?w¸íÏúõ9æ—IGyø0e#Xc’êôÉH®²@¢_e ÏΗ±5Z`a©WeÄWñëýï¸7ÞxÇ=ôÐwíúwiëØ}üéÇÝ/_xÝíÝ–+Tkz ÄÛM´ÈâAÆ·œécl>ó‰'Ý#W/Ë"bC~ pßýâ¹—Ü#>äýÈ5·!OÏË;2`¤ó©>â'2—ÚŽcȬ`u¬K¥ò‹búç=Ÿ‚ƒyËœF`áeA¢.ý4tí(1ý1µLª#OÊÁF¿¡<«@hÑP‚2^£L0a“€yW~4å/ùñ^¡ê_ÿ‹/dB“¿¹;yMîâ{êê§ÝžøªûÕÍçÜË7"7“mÈÙ Kѱw°ë.o>ìþèóÏ=tåa‘YœùËÙè¡LþxŠîù~áþÝ_þ¹;wmÓmÊO #Àª^ù€™Š[a¯DÉë¥ZWØ–0N¢-NoG² Ât—v@6ÊÀO²Þ´q?¢ƒwéûGÁ*F¡ºËM'wу3þ{o‰&Q(gýdA ×'ÔVµYïöÚc™$1y¾)vìƒù`Ҷ™À«*oC`Hªxàÿ4G[U8ú¹hçRxqqcå½÷>p/¿ô¦»ñÞmùÑž}ùM‰[î+¿õYwF&ëk—¶ÜSO?ê^yém·/ \yÿ•ú$È©v؉û` ¾ûò™¸Çå)‚-,EË Ï¿ê~&ûæ‹o¸È¢âãBï/Ø”+ xbAýÄ ñ®xÌÔ:i.DX]•’÷‹¼ï(ð'¦¼e",ü âs&U@@ã B‹äõ.ú"Ô0(!Õ›—Š3AÌKÂ";Îì%x¡€ø#g4 #0È dgÖϺÏ>ü›î÷?õ7ÝGzFÎü·Üo=óî¹7¾ëþíóÿZ®¼'VÜ“W>åþè‹O~ÔdÍm˂ŸùKpà rfüâ+/¸?ÿÖ×ÝÖÕuwæœÜ qS›Æ< êþʉ([a¯ŸpaüòõR‹V¾ j·šø]Mкj ‚;ðQÀ¼P¡s‚þ&@a ~ÞÕÝKá¬]Z,¼¾qÂOœõí9wçW² »­ÿŠ<.迯(°UÝcý ò ~|Àžq}Ô:©VÞi|Acß& ŽŠÀS…T5ø>÷“:,Ã.Œ,±ÒS·ä{~Œ÷?¸ãžÿåkîw>+Qx_¿œÅouïÉ#€ÏÿüWîó_ü„\éÚt]¿*ݶæ~ù‹WÝÖY$‰L|†ïöñ ‚8ÓÇë¤ûè#îé'ÕG 7äÀåÊÁk¯¿+¿Q±%¯^w7ä͘o½û¬{üÑkîÓŸù˜{òñGüøÅU•‘?.›Ž¤ÞŒ^Œõû÷ïºíûzÅá¼üNFÞ2“È €IètT79€u¤´¦X bdõGP¤½µ[§K=ã÷‚ý-~EÐÕI­  7=Ê~EÞR÷¹Ç¾ì¾øäWÝWŸÒÅr~é©ßs½þ)÷íþ_ gGîKÏ|Mn*<’ïüåQ?¹Û™Ïúã;ÿç_ü…û·rÇÿÙ+˜üeËËô4¢V¿º(°Yí¦i£E¤êLEQgÏïÏžŠÉBÛz~°&ÙpÆ ¹Œíª[>Ø¥JïÎǪ 4øGo¿,§’݃óOHÇàk–i7ïÕ±Vôc1·sÓ­àF?y¿‚ÿ®wøã9u½aAt¦“rȤù¿'‹<½/6ÃAøÛÔ'©`ã‰ÐìŒTŸ A5¬(,Ñ:Ó¾d1éƒøblÈÄGô^|é ÷òËo»;÷wå²ü†»*¯ëÕ·6Š€5ymï«oޔߤXwŸûÜÓî¢Làë²@Õ°·Þ–Ç0©PS|…s,_\vÏÈMÏž‘ðº{ówÝÏŸkwMîÀMŸëg7Ý,nÈã…¿þæOÜ'žyÌ}þ7žq—/^ÐÅ3|WûO(À˜¥oðôȽ;·ÝõG“Œ\u“…7Þ/áÛ([þÈœ@`á äaàðg†8BOn–?ä±u'[,…íNÖâ`õQ±ªMh?Mf¨£máQÑ[õ’¹—Bó¶kdÏcp,&4Ù0W¤ƒÀ|Á£s&ì£ 2 {•Ω(†û*_”<Š˜œVðL÷ö¶Ÿå*€Û»å\|ZÞO{Yêe 2ÁÜpÃY¿ÈY¹+ ¹ó@¾†YÑ— ¿ö;­_ íÈU¹gCݘfŠ*œÆ{Z]eJê©þóêM¿‚ “ÿ­»÷ܳ?}Ž.üŠ|uuíòEƒûX„vâqÍ ™¨_㦜½ŸuŸùÌÓѪûèc×ݵ«òë”2™+«|@?¾8'“û9¹ZpF.ý¿{ã}÷㟼( ]Æósð Æ»*_eí¬º—~õŽ»ùþm÷›_þ”{\dC(ÌÔÁ!‚! èB^]0ùû«¸Ê„+qyËÄèuÀ¦pŒ›•ŽjuZ©Uè!OX¶òl~_Y}²'ÕYÝȧÀú\øS­˜FD3E0Ò(„lžßﯯlº§åûýÏ=ñ%÷Ì#ŸpçϜכýpÞ‚WdÒÇŸNÇ{nûðŽÛ9¼«ö!¢A¾÷ögþòc7r–ÿÓŸ=ç¾ù½oº-œùŸ—ÉsÓèYÑ_ÉPéíâ'Ã$Ë'Ó“µ0úýÍ©ß;½b?lö’ªã…ÞxL:Û7^ƒI2£Èíº•éœ{TöǤ:ÈŸ žôjZIô¡íž¼ßgý²ðÒ·Êi¼Ÿ¾€ŠlÙ¨ñJ¹?`GìsòZfùUz©`a‚»l–×Ö7Ì«hæ)Ê«œ4SØ(]™Bûù/^s/þêm¹ç䲌Ór“Þ®¿  ‡q*Îäoïìºçž{E&øU÷©O~T.íË‚ßÝ‹^YÀDŒ <¸ìãÆ‡î‡?|ÁÝÝÞsWäm™˜ äë2•-v GÏÉCáÃÛÛîG?~Á]–×ã-„~n¢C ‡Ûº\Y8sfËÝúà=¹ß`C—3‹òä4 )blKÚIéu@SëLfl“Ó~@|Ĥ§5~_íãµZ¢‘Uè¼]DZ|ÏÿÑ+O»ßýÔ¸G®<¢ÏMƒ¶-7ðIÔÉ—ó1¡ÉEcw Át÷è¾Û9¸#gø¸ƒ ±MÎZD7øðœ?¾ÀWàßÿáå×ý¾ã.=¼åΞ—ïü1ñC/lÕp¯…Va'¶q° òÓ—õ2ÿoÈ×ú,úãWÛ >+n|ó­÷Üw¿÷sY;w]®,àBXxèFÙRÀâP® à‚3²À“3øÕÀ-¹Š€‘­÷Ý¿â ú€«n/]ÕlaÚxÂÇZÞ21z[ð e3&Ó†ƒâ‘žÊÄ?ŠMˆ><’è;õ}Q>}o7“/Ý#®ëÔŽLúkr&¹&—“W`RÁÄ€Àè_†²¼+—ùw$ÀÉwÇ*u¸›À_ºÄ;þÑû}ëÛßwϾø¬»(“ÿæ¹âÌ_ÔéD"gj‹³-¶j ê¼åž®Vú¢ÞPÆYßQU,‘µŸ 7Ø%Z•3Õù…Ôf*µ^8ä¬P.ßüÖ¿r¼üœ;÷ØÓncëœP… •;wé)wå“_U^}‰DF6L¢«›[îÞ;¿r{oÉ“²øÂ{íý+„qI¥ØEþ'mGò ÚÞu;ò¦‹Ç·Ý†œÕzÄ.s/È)ðQþ}žªMDÀh*(ÆôaÎÕyWòz¶.\˜0ä×+ñË—¸ (‹ÖiW}ŽåÇ|Öeav,x>÷³WuÒÿÂçŸÑkZÇ:Ö}7â À+¯ýÚý@Îü“3òžŠ]ùJAÏü'xŠ+_û²ZÀ×^x¥/:,tÍ ~Á',4€c±añŒß¿Àا| ·Œ@ ^ Ä12m˜¬É fVz'ºD„Qž¬Àb†$ÿÆ>\È—x$„7ÞyÝ}áã_–` ˆÂIúg"øÅ>y\J&œÝk@*Ʀ~¤zÉ_®¬È%ÿÝÝmy½ï_ºßxIßð‡çüqÉgA*ò c A¯¤ƒ&mÔÈ‚¯UÓƒòª¶^mò¬À#Õ¶‚—èˆlUŠ`˜NÙ'àŽDxq±o »,?pt÷»›/ÿ@ðà f°J°øÿÛ{·˜[’ëU ·þÅ|83øtänü#>—l¿äÏ}ùzÜ;wn?è#82u€Þbp·wŒ !–F1oÙÝQƒ\6YKåüP¾eR^mË­üfÞBï9 ÚM¼zþôÅá<ððɳÃ/ÿ³¯‡Wêê‹?~ä òÈÓ_—§ýåŸþfXÌ?rÿnxå»bÚ´n‡M6¾6#ðqœ•ò('ú„/¼5 åñSl^uˆ€ý•GÍ™’ Ì2ök2bo&—:aZ`‹ÛÀa>Á,%ø‹ù$âÿ5™ÜñýXü±ˆ£(n7~û­×o½óæáUùì>^Q…ÏæËõ„…]ܮǴ‡IJCÚÍ-ðÉ“þ¿÷Ƈ¿ÿÿÂá{òÀ¾ÛOÿ7+"d›#àˆÊèj¶+O zЇƒ’mœ5øÑÒ¤þ +bÆä{f¹À-Y؃r¬þ®õãÜ©ŸšMÕë²ÐÞ’W×ä‰ô{ò ¸5T´±áêþ‹¿øÚïÊGɾü·¯üá“Oó=ÿeq—€®É«þ§ï¿{øÆßû¿&ßš(ï)_—/µ2- ­yŽjøÓÐP¦†¯!¡¾²YüÂÏÙÞ”dhyYº”lË~¡@7œ—ÿaCt¡3ßh0Ë÷ã­ÌÔ[3¨ÊÑ,¤a¼¾Í8`qÅGõ0Ö·å´ Šä.¿; òí¡< pMêûwï~í7^;<”_½ü’|OÀ-¹eÿÕ_ýW‡¯È[·nÞ<<çÊ[€cP›Ö *°}"wîÈ …;âcHé òò|ð¡M„Ðjzɤ6 Òë‡ s¬'Ó´Tßô]°ÓÜ—TOMœJ)ûc?@iËÑ•J“jÊNŠFc]}äa Þ©¼ðŠ3 y5‚*}™ëÃb+ 5“i[ ñ©<Ð÷Í7¾)ßÜ÷ªü©| ü /Usóž}Óäe•)W^1 M~–ïŸýóß8üâW~åðìʓÃWï6Ÿx“¾ðÊ?Ø˘ ÃT(%ªí&DÔ[Ú€vªôó&s]ÐIuaâ•Öu|ï½Õ!z0.FdJo|‚3ˆ¥q X ¤àIswØ|,²ÈÝ‘/?ü.í›/?è­Ãkç¿:¼÷'îð©?óWeáÇwöËçÚ¿þ•Ãëÿà>œ½ý;áévÜQÀXB-ÜmXí…¶— „>¨4¥~MþàGrBn€FôÈOåºD_8åO!¶#­&®ß¸)¾ib⑆f¸«èÒ@¼È#ì‘pDç÷d‘æÃ| E«ŒëÈeÜÕÂwà»~G>Û…ûþ½Û‡ß”Þ” p~üXnûc3Vxà|;&F¢Éi¹q„œn*h¨1ÃVù.%ÐA¼¤ ÇTù ¦w/º4¶U7]¬UàVzö”i˜à_m`ñ—·E7e’ÇwõcVÁÂx~4uLJøwõ†¼çùÚo~øX&¼æA*ø.þ…äþaáÇ_¼êûïþßøO¿û­oÈÇünîÝ‘÷”±^Š}ê€ &ìpè{þ óPUxLƒ,û¤#ÎÑÐ…Øš0ƒxð5¯f‡Ü†±‡É¼Á 1(à@˜°¥ðGà Èr|gî¼€5,þ(¥ñB¾ï™¼g| ß1/·¤ßý'ÿk¸ð™/ÿ­Ã;_û¥Ãwñ?Ü—õä®| -î&Ü%R„Wš¸³Ò,Œ\¾›I0ÝŽ˜[sHGÓ×ÈÃåàvÛÝà8„B!t6´&~ôˆÝl}­òŽâ–<k´ §ÑQ^:Û îø 9ˆ´º#›<éß÷ @#Øþ•x_îà+°oÉmþoÊ—á<žü¿!ç#Yüñ6òì‚xª¥ü}yð¶|¢&x+6¦XK_Aó¶ž3Àæ1„Þ#r=N|Ðb>IÅ·eš&VzÆbYmp¼àž¡Ï2pèÊÙ³¢'ÂMê ådF;Ñ#xë Q’Wu˜Ää%'^Á`ÖáBıꄉ¦™DñSÐ y%ôí·_?|ûןüøÇëùx„X0áyyZYô?zôðð/¿ú›‡_ý—¿vx$Oµ?øÄyå* ”ôáUxå/üÉãìÖL„Ô ²®Ã"ÚB'ù¼"äV t^:„ÿЇóΗXÆ óê;Rè%X¢šZÓ ”fa׋tcÇÅ7ä=Ú"RnÈ‚ŽÍhDýšÜ xû·ßþßþ yÐïÑáùØ$¾•î†l´n„; xáAÐ*Іgâû(VCÂ߆$ÿRB P†ãX!AÊ–†"€+–nuc]È¥¡«ŠO5àãoxŠ^çž´y‡Îàƒ ~4* ¹Ü8|ðm–ˆMz/•ˆºQ ­È—Éݸþ\>²w+üÐ>ãÿ®ü`í7R—õÑŽÖ† Þ:}¢r¹ Öp[Öú&GåaÁð=Í[ x‹>76bsM!­ÝëS°\¿¦ø1U¶Ê ë‚ev¬VG×ÀÊÆ–ôX`{G>·/ÏÛ…Wþ˜Ôñ@ Æ.L`˜0QoŽ)æ!‡ÅúÅÙ ùžtø·~úgåÈ•ð«vœü ¢xíÑãG‡ß–o]û•ú«‡·ß{[^Þ’ß[¿6Í5È_° ‰"ü=÷ ÍÁE©ñ©áÎÐ|¿› ]M8x°±5zÀþ»Fh½"µkGKÎs¿P?â æö ¯à?þ0“8Fi”SyI/pËÀ5yZ^ÍÊgW®ÜŠÜÉ·W΄›„æ PްÐ\XÀZð <0tvºüi)GÚ±«À$tÊ"võÖKíX\fnâ¾Lï¢Üù#‡·ïÛ-‹ZþÚ8¨lj]È'°IjâÄæ÷Z¸Ûûðj\ÞNy"ok]—÷üñ Æ '>5pS¾1ßZ‰Ïã?– ÅDØüÞÛøˆ›çæë›· ËGûnÉÛå{3 ‹o ĵ‹qúÄ'¢_Ž_=ün†vÏáÇs| #l´o1{RGßÔ;°¿¨»±zzu.ẔDpIl«lpAh°¨.9ÉiÚØú’Žñ¹Ÿ1z)cí=¼J•Å@ž5“SRs«Qša‚­0ÆB@I›¨¿9|7ÂÓO@IDATÿïýþ·å!¾pøÉ?ý§÷ä¶4>“ iü¼ï›òmh¿õ;ß8üöï~ãðÎûïÈSëg‡—?…÷ú›ïa_\;Ñ]Ê)ÌŠín #}Ád…»b®¥‘3¤ÑÊBGk£Q×´¹l€÷oæ©sÍeµ«·Ú/Êi]RªNÛp«ù<=\”g¢Þ>hì5[˜vA_ñž<dïõáUì ÜEVü–ýU¼Ñ¯‡E`¢²PKÍàÃ6”Á”ò’3¼-ö·.5L(=ƒ¢Hü½ór©Tßý—¸h=§-‘f¯¦q4X8› Á»7åVþÙøÊô †7ÂðÈs òPŽëò…?¹ƒ} {ØEGÇÍ YôÈ7þá Œ'¯FoʃP¯ÿÞòéë‡ò=ÿ·îÝ8Üÿøíæ{»?L~¢6è„*lÈámUô…:n}çN‘—ò' )Ô´ G¹¦ÕA¦f)Ææâ£÷>©úÇWËÙ½µ¾55é&u4q´~ËƓ–/t¶DÄ‚–¶è”%,ˆáZ¸† ~‡»(a‡MÈò S^‘¾ïÀ°ÁŽÀ'Ò›’Ï7p8Pñ§ úÒe3!Lùsí¥4¶M~¥5tQ_ùä§ß_‰„S×-wÀ¶…:?Ћ˜‘ëH¾ÿŸ/Ó‘ÏÝã•3î^ýádÕql‚áØ@µ•!(ÄЀ–ˆÅZþ4EO#ÛlÚ0žø á۷壚ذG §ÏÆþ÷åSØüÞºsO6{¸xñB»±ÖØsJøèâX;“³˜c É’Ø.²@ç&u‚2¤ìÛÀV¸è•ÒMuW¯¦ì§ht¤«<(Á‡sêñÒ­WÂi)è Ê›‰æ¢nL]2µs*x?5üºšd×}y‚ÿñû¿ôë_ik¹ *¯òï~LžŒ–¯ô±Hñ}þ ÔæÕ`!L™ÍŸÆƒ0ß’$ðò¥Úè;B! ´Û3t± ·¶ÑNæXD‘;Ÿ¸ÿé†gÂ_ŽÍÕÍ@æéàÞë=¾ ‡ƒtAº›*âGpçÆIoD jÊ.áÕâ ¯ÖQÕBX¼kÃ'Uü€L`9æ âmxbxZ²°‹7¡A ì5g PPH¹˜ÂÊk/&Ç6Šþ‰ïûþ£ü„OÓS^qKF Fòj:©FQÀV˜Û=à š†P䀠ΧÀÕØæõ ?B]X› ´lØD†:¡sžGÀ[8 ¼®GlÔ¯Ê7>=<|ÿ½Ã½/Þyû­Ã½û/‡q¾&ßV8uñî›cƒs'ò‡×±U¸Kb»ÈÀr÷„AÀ{n8ŽThÿáNixu{îúPŸRü)Ÿ-°…­ÞûDx ïªÔÃ&€Æ0¡ÎI3R¨7:V†æ$á Oî˧ä{nàû‚.a—&˜À zUH “ Úæ ¯±ÚðÓ0Š48Poå¡ç…¼O€çVà•£-Žnkë|/¼Uüƒ®»7Ègæ_4‰"F\è(¯Ý}åpUt¿+þÊ„/˜…58B¸t7m ï7ªá`tð°$~¼GÇ­vdyà"x…xåLîÈ&¯ˆÔ-¬à*´V²¡…vësp¼±D¦-‰ê.•PFùýcŸ¿Ô=–€ñúÔ|NÔB7–æö L6Ñ Ñ´š¼è‘͈ŒÇmyÅ~ÝOxð¯Ñþ¶&tŒ#U@+sy¨¼?Nð­ÉËý‘P`h,cC€w²p'¢‰JÎ}ѺP?“g îËà©<¯ðT~Çॼzô-æÒ¶˜W†Ø[3¯5Öú†`·È?¯iyp`¡“²…®œŽš6¬°xE~ÁïÎõ{ò=ï5“¥LBa¡– Â«{¼zÅÑN’˜Ä(s¦ÌãD9lÂÂŒ)LÚNé u¼ÊUÌt¨-Ú`Dl¢LtCŸÌõÍD _Ð…~u°;.4DvZŠðÊP*?øñ ºÇŽ›^ü›…C>)ñ_<<ýÿ+¸ÁX`¼ñ´ŽGSm—¢vF ÿñНð`šÏ}ƒÚ¾PMüÁˆ\“ëß"øBn{_8ˆµ4ø#Gð,ü•0â ½eµeåÂq”—`¯Üz €ÏrÏrolãóü -ŽÍB F›€ð šßÁ–rXde…[æˆ+üÃf*²‘ÇÛønÚp]4dBÜÇ?H|D”4!¡¶ÆSúŽ é°‡ÿAQc£Q$ ]p"'›¼¥ŽŸÆáSwîÞ?ܺ-Ï›Ös¶K‹©°šc€ÆcÈy–ý5KÉÎùŽ&QÏ_±[Y`K☢‡Øêd«/è’ùç_ûäåýÄfþœ…/1in‰7A sdà“ó ä%&-™Ãð‘µðP^¥cNkÛÒê ie¡ëØ&ŠÿÀ‡RNøwôGó§=KÒƒÅ×èýððÇ?ý§‚Í1›ó‡¿ò£ÿŽ<Å´]O„n”b wðÍ+çfÎ;ÎqÒß,þ×¢#oèo¦È°¾ SþàÕΑ†¶ÐéÑ4q"ø&;iËÛáð>4q lø¨/è‘þ ‹2ô71†åöë‡ÿè/üçòó» :ÄLѱÀDÁWþ¹1ýê½W¯þõÿZôÊlÞz ‹SàÃ{ÊáÁ4ð… ¤Ä‚ß.äEßñD@|ž<(Û3Ø ü µzÙºá­|´PX‚\«“~bîX@N˜Â¿à—Šƒ:¥„ÏØ\Ýûsëpë‡~RT¶|0¦Žà›j©B¹ûoüµ¿~øâŸÿ‹AÞÿªô7ñ€&ßË¢<§£;-ùõÜâÀ[/ÂÃþ SÑ. èÁ (¥ß‘·¥I:俌-x€gȇ¦Þåoð  Ëfã#uÈ"ÖpH>â-œO~ús- Lv^À-u»Ú.Šqš°~ =¸ -íµŸÿùŸÿÏ ý͆ÉàY½?Ýå7S?÷ÚecM}¸àïß¿_ [èÿ‘Ïü˜|wüÍï~ë—%tLVRà6&&#TA•а†ƒtt4¼ =û·•ciºÎþ\‰É3LŸ°‹9‘å„û‡6^ùÿ§?ý_~ô3?¾Â¸ô"VÈu”¥ÇW¿ÿpã3äðø×ÿŸÃ‡4œÍ_¨Ãø<øòx¸/w?Â]ík«³oåB²ŠqøÂŸý©Ã[ò›¯ý«¯…·°>ò‘{‡Oê£b»ùÄ>6‡Ÿ„Z›WøB£oRâF†X(I -Oè'áX"úËGÀEqÈ4·ïÛ…×X@Œ6”­¯“›òyÿ—¿ÿY<ú Yü¯Ë€9|/{0Œ‚9º1ÇÉõaVÖÁ\DŒ˜cõƒæ¹¸S¹›ãME87¶W¾õ­o5Y•òfÀaWÃÛF9u)Ðr¼]t ô3ù¾îGMúìv—­5ôáŽNÄm…a4$>Îßzë7ÿÓ?þï_}ý—š‰,5›‰ÌM¡+ÓO/æa6†ÙP+b ´´,hh³Kx(#UüòOÉ{þãÇþ½ðÊÿùÊ\Lr%Øb †.þ”܇ï½uxçÿüoïÿòß‘ÛÏä}x¼ê—òä_¸–B0²hMXòWþc±jð•=á5dœ8V `à¼4ʶÖ„~¾LQše߀ BB©„ŠVVäÑß‚;¨„ÞÃÍÏ}éðàgþãÃõÏüÑ€kj*ÁÚJàÊüýµ_üG‡ÿå¿ÿoן¿wø“â‡Â×Q_—WþÈë`S@„?ÍÒ`‚ Þ4±Ê˜à@ˆëÜ“fœZ" h†jl˜Un„1þðP§ð7z¤£¤s¬µòh>—/ÿyð©Ï>ÿçÿš|ñÏGäsÿò¾¿È!oSøRÅÔ8‹õ+5Ç6yrÅGI¡•âM°Isbk¾€óx¥'%±›að9pŽQWª`G¢âÜú]LfÄøÎ…-'QL–°ù­wþ¿Ã/|ýïþùë¿|øÚÿâðT¾V¶e¾kŠvÖÃ\Žóñç"ÓÎj2$l„ùS$!ßÔ›8Þ:-ð@³šhƒ)L¨aõ¡ ¨0¢À)„æ;þÿÈ'¿pøÂ÷}éð“ŸÿrøÎLœµzƒ8¸P¡þâáw¾úwOëžýîWB;øâlÿ€p)âhx¨UJ€–lv·Ã²XÈÇRä[xõüB®+Œ×ÖÖˆpïÊ™Ü.þ?r¸ùÙ?ÜùùpöêàË“"µJb‹ø~ë·¿vx÷µ_?|絯ÞÿŽ|5não[¬ÆáhJÄ×Ф}ÄìèW´Àþ½ 5ToCr 1ø‚®ó~‚&¼ÐM94õÑÊ£û6^ùú³‡ïûcæðñÏýÑ “¸¢<êÖò†uà‰×ÊSùÑ"^3†&fW…˜†Ì±Ä`Ö”é Ž<µ±½òÆo0s»ü9öÁ1:Ç‹ m,¼ØŽÌme8±ì˜6íÑOê_Õñ"2,)3WI_Y[bÊÅWû3§¯ñÓO<0÷æ{¯^çµÃ[ß<|ïÑ;‡·~çðôÅcù•3¹#O¦?|*_&”:˜yÍ<’âHÓ W síÊ™|f[ž ‘ïÆÇ[÷ä»ûòÅ3äw>~O¾óýÁ§•/<âø£d~07ÒœSÇêœ:¬Z±EùüÝ7Ï¿óÚáƒï~ëðâý·Ã†à…`|øàéáÃg>}$Ïây’#FÇJ똴cR DÀŠýóCÕQ«`‹oyºrãv8¯Þû¨|Ëáˇk>~8ûèÈWü~JºñKvÍõE|Yžë®[#¶'´qâNÆûß}S~2ùMùºêwOÞÿÞáÙ£÷äËtd1“/ÒAÿ³'2Î ® Ô;ÄŒ£´Á2ˆåQ¶áã§=®á{ ä¶þ ù8ßÛwåG¶äÓ#/¿z¸÷Ê'„vÿ¼…œeÞÒÞQmÅ °äÁ±]ã 9ö¬3'ôú¥±ÓuÆ8µÔxõéÒ¼5°½"·É{R·ÏÅt àÒ–.RSv5ˆ¹·ÙJÅ8G$¼xPòÂ!ö·€5ñcÉI“%èìc\©’± ‹ƒút ½]˜võYøÔ¥£ #ö˸ìÒ[£Ocªë°e‰!t1öq@'õ³š·µü‚^K,kú™ÓMLYæøÆÒ§à3E6çïè @-€rŽ–Ð‡úTÐ?Kx†ÆR¢Ó’Øñ„^b—–6§ê¦Ä5Ug_‰Ä©7A"ße#ÆmÈèz—Ž%útü¬çÊ%ü£M`HãRó°¾tI áë¹ri_Kíwƹ-ÖʰΒ¼)=1yã’ºczI»ÔtÑǽCyb?ØfI}h_yøða•;42W9eàæòqÏvLúˆÛºo©:s$.‡úƒØ¸ø•Ë¿FˆÜÒ)" ÄïùwŠYà1ï Üu\º¨—òm·Mlrƒ¶ÝÏm!/þÛòÞ½u¹u¤oQïëOÙÚ mõ€Ü m`÷s]àbÑÇÛþëò|[Þøu»­ñÚ»·¹|ìš 4&}󆿵ªç|¶Ò=«þàX‚麶ƒ/|c¾øÛŒŸÆÔF£kqÆ#À|ä5?^S·$íts ë…Κ~¯vPÌaÐ;÷)  /.å?}ÄýºŽ¡k°C€ù¨¯s;íçšhçœbW£î1¬þ-€Œ5€HÙqÚi €|òÅúXs¢š®É58Ó`>Ö^/hgºÇÝjØYí€)ƒ6E¶{¶Ý«(…‘îßv¤åÞžåRÎé8kF€sYjž³ô›v,uvé‚=˘V»èÁûÆ!`™8ãícÝT¿¶q¢ `‹¸âBà™‹ÕלîR:°}üøñåVŽ’\Z ¶>ØfÕÒsr¯o‘qœ¯%9>ÆN—L ¶©Øékª/¶wå›ßüæä ÆÊц·oßT vHï¿ÿ~xÅŸÓ_NNvNz l»tvÅÖ‡ëš1…o<cà[cÎæÆ¦wÄ[—³Ó5~ºóÅíµck‰Qû”v ÆsbÛ7¤bµÄ6ÆcŒîXGÎgÌ7oÞLuW¡õa;&ÖœŒÉ …B .¼{÷nŠÕ”†]Ó{ï½wa‚ÏojxFeS°eKÝÞ*®{ž]±¦pY2géëšs7…ýŽË˜wil׌kŒ]_{nlSs|œSÆ\Ë&õ³\2_Kb$ýÕ9Ã>Mcý¬«“L¹2eŒ¼Z/øpë ïµ`7UëÀÎI/þÚ‡”;þ”Ì\´¡Ø"–;wîtº×¥³Kp®kÀqÒ”<còë~ä,è}¸va××c ~úÒ'»¥~ÆD|—À–>”â¦ùuNÅò]})^дî˜mâ”ê‹iÔE™šØÖÎWÄÂ8gè¤é:åtiÔ5¶¬‰)}бÕq'.cž¸ÝÇ?é‹€rÆbÐÁÚ£Gx@ðìl’Ù8¦c·ýa'åWŠv\a%ço[¼oŒg.rØÆrCB~øðá¦pvúԱƸƸ°Ÿô>\µî1uæ,di;Ö“£Ç|Ömb½ðA·Ië³™“}il‡àÚÅÛÕãSÊ[·¶}ùZâwŒIÜÎé1§xJi±=êŒéhSçœùJ›ÚŸM÷­Wù8;Ì665ܖ“þ´C)_Ø·Å2cÎaÛ•à}Wœ´Aþ”ì[º¤o±Ï)¿r¼¤C&‡kJßs2±¯´Ó‡èŸÊû·Kôçd×ÜØæü)‰em<¹Xja[’¯µ1bÌVvrs#1¤¶kç+íh»1}ex)N Òb#š'î‹ÛÔA´±H㣠x;ÀòÀ“þ´G½q›ô®2%ÿ5ñ@OŠNšæÓ6Sý åøµ,ë±´k`;WúF_ç(õÁ>Ï)˜ÂoèÁÄW+giƒ-m)éç|Û!ˆwóÆãQ[~šŠ¶à‘®w{hÛ »CroŒuÆF;hטcáÛ˜y6}ŽûCL¿ð @NX õñ¤ AFÓ1¡â¡ ˃ÉI)?S4òw•±\ܦlLÛäc÷Çmò±Ô’M·Æ¶&®ŒÁ²äƲË.›1¦àµÆ:ã‹~ŠÏЩ8bžÒ6}Ó¹YÒµž˜G÷Q†<_¶©8b¿×Ò&v)û­±µÈס83ÆÚ'ß×=Z'ù5¶X‚G÷×ÊWÚCI¿4­‹óÅíœ>ó7ãiHFÇIÃ.Êr}ú ¤ÅmÒ·V2âHÿA'ÍÛ-á °ø# ë¹’Øé~)ê–¸ÂN¶ô%އôÒrª|©ÍÛLá«ù—À6öQû³öºö}.lçÊW=cÓñ²Ÿ}l—–9¹ØḞ~ÒæÎר·ÒXKøÌ74J§ é(ãdÒ}cêxz’íæÚ¤o¹dŒµ±ÍáJûkÂ0µø—úÇxb°nEnž¥=íu½ú ç0ƒÎõ¡§dð†ø8'¯¶kÇcÌÓ ß8otÛ WøJ]Z?èq´5c&ϔ߹øˆGJf(ºr¶†êÛ .^â1%êˆmÄí)6(k•kÔ‡ÒZ'ñÐ6ÆÖ—œggÙè$a=´%€cumMŽxÂoÖ­°µÒSSÄÊÓZ?qŒõZâa=!žZ·s˜LµC½µ±¥©þnIž1[`;g¾Òï)Xku}ŒNíë˜ÒK]ÔYZβH93uP´NèâÀhú©Ö­°q] Æðƒçœcl…+|N]ôkÁ·6¦©8­±MÙ¨×ô§â¶Àv‹ùЏ-bO«¥^èÒã¦ë)Û–´]lâäœ@ËÁ°Ò¥“Sׇêq*_ƒc˳†þ”NæÓ,c½ñE÷ŸZÛ[ÇOÓN©n­…Ž=an‰Ç’óìl€ø"´ÐJÏ–“Sc‹:1a9&¶)²cì•È 6ž%üSx4¦Ôc…‰•úµ‡Ò +={À”1LÅ„ò©k‚6N­$&Sã¶Ò3ÖÙ6ÚAO$†}IeXKìó´GlyKã»$§{mÜka[Kok°»”}ØŒé_dG­(èÑ¢ëcÙƒŒ¶1®Kâ‚1幤¸Â+=Kb1Õv|Zab¥gj|KÊ[cë˜^M+L G—®_¶jOYl`ŠkÜ#¸ x.ßÜæ’±nݶÕÖGÐýŸ ßÌ…tE;z7ªëM΢š ÿRúRvg÷DŒøžÈ@{˜£¨öUÀ)op1îiJÅXƒVŠY)_ ­u"WxZë^ƒ¾­-L{Ê­5Œ¿ûà¬Y7:à=N(k‰i-~èñRç¿¶E²®k‹3«Zqk;sØÐöN©n­ÎQ]ŸG‹XÆúŒ¸iŸåX]k‘ÛÕ[§š˜µ“iN\aKŸµcsýÝÔœèæÌ«î(÷×»Glkæâþ2 ,¢]mÊBv®µ" ~Ôý‚_ëH¹_ŽÀ¼œÂ\°Ä¦m±·æMŸzÖtbv øºú‡z¨íRv«‹&qAÉ“1-]nS ÜR9f¡w.Ì«¹ì¹G`kø`Àˆåƒ’‰¦„g€+a¡ÜÚ­1ˆ}ÇOúòˆûH÷r>| æÃÚ-õ# çÍíyªÑ^?np f :ú5­‹?î£k);ì«QjSúc?ɯé)u±í9KØÖ~Îi;g‹xÄ~‘9Ý:ÿ5Æ“‹ÓŠ®q±Ò9UŸ©º\ÞpÆ#Àù×$ÛÐHšÖ®û5u-Þ3- ëÐeÜ·S¼Ú îŸ£ÞåíçxrtÊzÙ‡O®_çx°ø“W÷:¾KÅïc°òÛ°Ëku oi»$GÁ«ù(K¿uhq?h1ùRtôYÚ']§}¹2–9ÞÈ L¥Ãà€ÅMõ;–Ÿ#†ØæZÛXC_ù#NÇwþÑ.½6KÆ[_Ž_ó0ÚRÈ?µ¤oð…u­Sû¨û‡òkºžÓ¯yt½”_óQ~nli·F©ÇBëÏÑÁ3¶ú÷„cŠËê€ØàÛ©‹k-ql)I#/JýÊ-XîžœàÄñ2ÏÙ¯Ç+æÛ”‰éºÝç—æ­Q×>꺶eE×:u=§_óèz)?ø8~Z~/õ86¶Kñ‹Ã\¸ÖŽ#¿orÈ´t&ZÛn»­Sã/þºo·@Θ+]Ï™ŽqeâvNÏ:tÆv‡È—òÖð½Ô¶óG ÎŽ£Î™¸®eÈ?ÞƒuHÎÇ,=@sÁ;€sÅSj§vÜcôã‚¥ÜÚø¥'”Î%<)¹±4bÕ%¯1ùÖO쟷ë 0wž2 æ+óŽmö£ìêÓ|¨§ä5Oª?EÓ2^O#0Ë mzT&x^¯ËKbhµøSŸÒJ"‡œæ×u­³D—5ö¥KwޝÄÿž.Û[ìËáµÅXÖêsÆ]}%ñ@~©¼]Òv 6SxÙLM†)•ÝsŒÅ¤O,pÓgJfl>Ô£Î9&ÚJa2†6‡ÏcüŠe÷V|}_{{nl­s¸ ß[säU‰]q¬©o‘ @mj ôΑ`CñY³_ðgè~Ø °Æ¼,Ìó ©á|–ØZêI™D—[¹Fºb(C¡œëüë×ÊeFqÖ ª¦î8Ø9mŶ—nü<—ö}¨ý¡±ÕŸâks+[*æ9hcq÷-Û8%\kÆZSw_~ͶèsdKýKØVpF<·âs—Ÿks,ö{Xðׂgט{ß~ØÃ5Sc4v·˜kb™ËNA/Õ96FÈñ,µuê|%XÇ“Ø^6Öc_‚¥µÍ5é;õøã±ˆ¯›¸ÿ”Û»ÛÌ9˜kºÐ´/ºn…G©Nðñ´²ízÎ?FE,|R#^.@é|°„oÚæÖ®“¹qu0wp:jÕ}±;G–XìeœkÆ1D÷ØIlˆóQÜWÍ1Ø×x‰fìu3ÄÆÖygÝh°övaî-=V%uÄϳ„‹<µÆØJ¯ÆŸ:5mm˜ÓGø¥ëkósëþX`k¡c8–[i±Ö°†Î! ûkØuÎìñ‚—óÚ! Ç\×i´5ä&ý‰Ë”Ï1·ËÐãm­µ¾ò¨êröÅÅë|]§Ç¤‘tÒÈ£ûH[¢„_<—°J6㻕ž±ö]Θ8ßSsgÌ3§KÛšý€5ØÖú† HÊvŠ çúR6ÉŸ’a_JΊ–²«uÓ‡>>-³õºU¬Z®oŸ5øïxÚž0c‰Ûöèuk\ÚþYmR;.@bm7§/Gï–²Þ\leÒÓ¹[ʇš1Ãkè×gʇéÑ­OCm\爘1pÌØžÃ6mÐ6Ú°ö~Пµ•Ädª_ÔsÊØƒË“_,Çv,OºuYÛNõ·rÀZÖ¥§«Ïz –Ò—Â×"îœÐñ«~8R¶—ÂaKvsØÎƒ^|ç°7· bËRÛOÑtÿšêc|­5¶Ú]_^¥¾Ä›M®C_ Oð¤è¥öc>Úd©ûS4ÝoQ¯¾€“֠ŧ€JÑb¹)mË$˜âG,kw=Ô27¾V¸ÂÚ‹ýÊÑc>«ö{Cxcÿ4¦ºN>klcS|§[--°~1¦ÄÃÛúbß§Ú°ÀTãûǾÚå"5u40ŒçÏŸI–ºJÛÊRû¡ÛˆWûe?ô¤NØÖöÐÎÙ_®rSº¡3Ž~ZÚÊÙ˜ŠÇåc,ãØÑ¶Ä6žÖˆI-Ÿja›Ÿ½V|cõNñ3Æ>LÑÇç«¥îØVÜžíSqP–;(K]1@[l[áA=;žÀƒ:iñØj̺ú4ßêÄÃÂ×”®=a5£Cu?Æ1n“ïTJ l¡ãÔqd¾‡¦œÉ[Ræt•ÈNå™m;j™LKǵt¸Za{vvúÜ8-m,WÎ>cf¿uÌ×®]£ê]—¥bŒ÷PNÛRŒ,°Maj¡·4†µñ¥b/Íõ8–%ׯE6©dŠAÒÖ¦fˆ®­ój,¦Ä1N§Ž'1¬™³°±Gœ»&Doml9†§RÖÀÖj^ÙêôaÚ•ë}1/‰í"¼²´<¬õYú6—.&¨Ð…óúõës¹¿J;ÄÎYઃ´Ö§u¯¡>dB´ÆBëÓc¸\æöAc1Ŷ•ž)>¬E6ÆbH®§bÐúæÎ×Y7 ÎzǽÖ:SµÚT€%OìLãÝ)Çp XXú8רàcóœJÛ±­7ÒVØÆùzªóFJc:uñ‡>`©u‚6×1ëAaÒÓ;«@çzµj‘ø:R¸MÅ~ñ„~$÷\¸¦âY ˜ÔÈÙ7n¬%DS?†LŠSs6çø^±ÍÅ›¢[cë˜^\¿†äyj|4m©yvö @­$€µV=PºÎÅrh CeȯíÇõ)ØR?J}@gLÓý§P¿yóf•0—ÈÙ*(¥C'Å)9«Ì^ªîÛKAö¬±uLb:4Ï{†*¼ÐZbž­ö[)€°#%€}€ íx˜¨Ÿ€ö†m*/»0˜ÛÜ|Ðå×Ò}È‹¡XÆ>×ÀVç+üCû”b:ulR˜Û¾|µÆÜöi¼Td- Žß¾}»ƒcz6øB >ø`º²k–: §` Yž¹‰ë³gÏr,»¥ß¹s§jlÄvîœÅ˜ãÈMäq~å@Ð|à¡^Íó°oilS¾Ò·¥Ëßr¸Â÷ZØ2_·:×.ìrcLÇÈåôÅtb›š èw,3µ}å7Þøpª’”¼ Î<ì æ8ðj5NP uLsÄ‘³¡ý˜‚-dyæliú£Gv¿¹b¼Sp¥Ž!e*gKäáç‡ÎÁ>û1ïZ°] »>¼Jûc\!7¶cóµ4¶>Ä XR¦d\SØQ>.¡/^ñ Þ¹XOi;…mIL¥ú5_• A‚ÓØÕàœó€]ì¢p;Åò+‚ל±¤lÁ-êC}ƒ Ï” x"A_¼x‘cÙ4˜à½ÎZïûw3[ø»ÄQšošo)la75·¥ð³³%±%¦KÎð°$¦%cª±£\\Bæ¬_±˜×² [©|-‰k¨æ8‰žšÆ9'p:x‚…IwPN]´J’Fû`]Oa Cý‚žc|„¬Æõ-¸3¥svÉXbl‘³]ã þ’ƒ:JùûtRÊ”NöÇØ‚—}}6¬ûa[ç-±MùomÛRñ[¶1¦k˜ 8žÄ‰m=ìÓ4ÔcLÙŸãg¿uIŸã|5·óÖ[o z €Ž¡Ô'€ÃÉ~8:7h)pbpÑó‡<)ÛåsWå§”ôUãŠz [ØãÖ=ÅWÊBüÈáJQ‚w©ƒ¶?s•ØÒÏ¥üë²›Ã2kò›˜clªˆûšüÔ>­[úI¸—ÔÙbbpá·Þ=îeL=GÀ8 ¶8×™y>—W3×݉€/þðx§¾¹4ÒÕTA ¿/þ—¡õ ÀeLvCñÅ7C¹‰@|°‰a:9'}áϹoòØlºÇÿMßf÷MÀf‡n—Žûâß=¬¾èÆg“½¾øorØvã´ov3”›Äÿþáó @?F›âðÅSõ[g}°Û¡ÝD`¾ø— “   §Mpù⿉a:'‘>ŸÌp´4GJ7•¥úF;¼#A¿°“ÁôÅ'¹³0J'í…íá"PºX—æQ©¾B÷vÏæ€C¼ÖäòÅÀ :ëì”NÞ³;æE t>-ÍŸR}‹½2ã¾(µ&—/þ…èl‹"P:‰/ꤟ Òù´4oJõÍàF ù ` Öš\¾ø ž³¬ÒÉ|5»#U(OKó¥T_•`6®Ô7=¸ÖäòÅ¿gà¼{•”Nê«tÞšŒ@é|Zš'¥ú&;¾S¾èص&—/þƒæ]«G tr_} îà ¬çSk}ƒ‚Ù ³o2¹ÖäòÅ?3`Nv] P²A\ëü¼µðïØÐˆùâ¿¡ÁrW³øä…æ¤;J~äùc—&¾°Ã²ª&_ü«ÂëÊg@À'î@Þ¨ _ü—8 `ÜYõÅ\μB|ñ_á ÌèR×øûâ?ã@D¦Î^¼x‘ú›0–ýëå@bÆÉ·çöž¸¢ÔçÜ~L±G YNÑe%K,­ô-¥‡˜²\Êm· Û5ù©}NÕé+ËÏÜ´.lçöeŒ=`IU·•úâa_®Œuäø,èKbJÿ5¶¤Y—W~ÿ÷Ò oP8©bÁšëÀéÉ“'a§4—M ;À’õÆscK\‘˜ôÍ"îµé˜WÄOlQîåHåí–°Õ9ÎX4 ãD:êqhñ¡ùѧeâ¾X¶¯ ]˜gç˜k™¯CæƧcî‹)×O]¹~+úœ˜Ògb;d.‹éä N•zp7àæÍ›)6S‹?m—C^SGF*ƒ¿é¹@ÛgòÌÒP×€À.zì¤p{»ÆñôéÓ ¶/&ökI«áW—Núûú¶?dØ×%O¿ñ¬C-l‰«öƒv5m/u[M\±Õ6‡àX*‡\*å¥ý!2%¹J½(áËZæú£ý[[½_Žq-l™¯Ä‡öØž³¤íRlÆúF;µ0¥_Ä–öH˾þ˜¿«mz åX<8˜Pk¼€ÛRØAÅGÊ'ò /ö}s•ð!>éSÎwökÁ[[àŠ“GÎ'öw•Sd»ôZô¥0…ÞZ¸Bw.gi¥Õ1û12ð7'—¸F·¶9ß ³æ£Ïï9°ÕsAŸ?1–Cùcù\q×ÔÛÝ[¾šnb°Ðæà0A‘D5 zõA»š–ªƒ¾¥úkÓ`_Ÿ°Gßs~±¼š§¶ØõâÐ6AýéêSl«®ê4¦pº®ÐKlQç¡ý m/¥Ž×Âvì|°U¬‰-qEÖØ"_i'…SW_Šß‚›:f ÔÁx´~kLik©|­s/žQ©`Ð8XÅ6ºJÚN©2úUÊoÅG_YÆzstÍûN4Ï”:ôÅ6 ¾¥ú¦Ø[ƒ,cÓ¾Xã ÝúŽUʦ¶¿·ºÎ›ØRç)âZ [bªs‘øj›ºŽzmÛŒ‘±¤p`ߨ’:c[cõ•Ê™>@£zÇDK¨'>Ò§”Л}‰÷Ç~ÆýS|ÊÉÒ&lÅïùçdúèôº-±M颭>ŸöÐX9^),¦ÄÈœ…޵cšò¸h b¾OÌo³ÐOlc´m]×|ôYÓÀ zL#eªôøÈéùÐ&oª¯ o•·)=¥1÷ù:¥ FCíZcJûCòÕó*oh'S”J&‚1¦„>mSëÈÑÁƒ>ú×ŧõM©ÃípñÏù@¿è'íj:i,¡ËÛX}§½S(sŒÅÔØ»rºiwª¹äSþÆ´Tî‚gNlcŸb|rýCé±^¶szدË!¼µ±Çhˆo:¦ZukRxÂw؉±˜S×\`Wìk• €6Â4 º®yÇÖSúhw¬ÎrôI/þ°Cz‰MÍ›Š;E+Ñ›âщ®í¦xkÑ´]Ħ۰™¢Mñ%‡_Ž>ÖVN_ßXý5år¾÷Ùdl±|ÜîÓÓןÒGÛ}²[íg|qìq{l|sÍŒ~ƾ£­ûÁ·A³8´Þ”6¨#Öº¶O¾eõ F@:P$B‹C''ôÍÞPßá—>sò§èŒ3æ·Â–zi§Ë—®¾©òÔÒ“¢‘LI}Œ]ë°Â:㜶Q_ë‘Âe¨¯ˆ3ÖSÛ-à:Ã-l9^¥X–òåâHÑkèLÙ‰iµ0¥x.˜3ÎÙ6VÉ„"VåPð´OV>¤ôÀŽ>S< Á%ŽaŒŽ”?Ðݹ£«/'³:cÓXêúÔ8´.ÚšªsKòŒYã`å¿ÖI;Vº· 1k t}¬ÿБÃ2Gk r>O±ËÖÀ”6t¬5°¤Tió<¥9C« “³†îLƒÈðKŸƒ„ ™kÄ®S»ÁX4m¯õ¸j¬jë×¶¦Ösù0E/㯩{Š[•%®Vþ§Æ6¬íXù[COíXkëOa2û€N¤Š}CKK]Cm÷ñó"aÙÅ?5&ÐT=]>ÒFÏÞú³%®ÐE½[ÀË2öT¼–ú·†m  óË[úEÝl[–5üµòq[ú¸d¾Îú ²=µªüD´u=ö'îc|ÔE™¸ü±«eçVãØ²ßÌ­½…u<Öú¶œ35°¨¡“3ÇÙ^ci¿µ¾!˜Uù€Ø¥u(°9þ˜·ãxÙŸzÈ‘}Ѹèz¬oH[ë"ój¬tÆ6¶Ô¶ÄØöéc¿‡^äC_Ì‹¾zJûÀ§íP~LÛ££K¦O_cb¥Í>Ý𛼨÷ñk^ðã Lª¯á°ÿ«mѾ½•‹ØÔп55±&³Ü@ :‰`™R>­gˆŒoNO ÇMû¿õ:°Ð1æ°§ÖM)û†”9=Ö1ä|še㵉ÎíÅ%ìóŒûºÚcü¦L.‰ºì•öÑF)¿5ì#¾œ]}S|©‰iʯ\|)ÞÚ´9bç¸2–šñ×ÔMÿ×RƸZúe…#õèNúïÚ阭=Fú§ccü¦Ø˜"Ëx—*­}×ã¶TLKÛ%ÖØ2.êg»´¬åO©ý)|ŒÙ:êâÛÙE6ÖÁZë$ló"Wƒwæ°Q›):kÅ뵞T†Ä¼¤í‡!~;o7µ°«wÉ<ëFª¼wlì9 ÖúrvRôE6)G–¤ÈŧŽC÷izÍúØXú|b,}|{í·ÆÕZq3ÁŽ‘¡½5–µ°]c¬Køäø.z›¾€A<À¤Ãݧéº/¦×q½L+ýÕ>uѺ´Æ:ú>ê§uѦ¦éºÖ­ék¨÷ù¾·îƒåøC×ÖÆlkþn=߯ú?eœ,s|¬ÿ{—ãøh¬ICì1ýÂC€šQ5•®RoN'û-JmCש[ÓR>’/.!ÇÅ¿Dtk¾XŸ·O8÷¦äJ¬ ÈÆú4ë1O͚͡6RºkňxJuDZÇrÔó1ÍŸã!/K-CÚ˜r¬žR?Çø´”ÌX,†ø;ÅFN6¦_Ø qn/Ε´7ÔÇ>ÿ¨7µøÃûQ׺4}©£„'%§í°¬.ÊëÒR—Ö»Åz ,ôøYèOé€c£×ØÔq×´5GŒC0ìãe?Ë.lRèí‹1'7Ö§>{cõæä¬ýÏÙ}– @`µÁ¬ të“qé²v|Úê51­‰eÇ6ýÒX“6DO¯ÖÝÅ7¥OÛ°ö?ö ¶`CÛŒyæl¯É—9ã®m«®cósh¾•Ø)á±Ä™ö†ÆbéC-]³njA½(¶-Kèæ ½kJ÷šüŠñ×~Æ}cÚÖú´Ô»· IDAT] O­—¶´ýum³†þ¡:wMŸæÂuhܵùkãšó?¶Ë±ÝË8ÄñåpK_§Ù7µ@¬ tó;¸{“«‰÷š°BœœÈ,ü‚.­ïTpÌag/í8®W:׈eÙ…sWŸ¥sêÚ[¾.ò1À-%|ÕçœÉ¶V[[¿5aOÆŽc3:ŽÃš²´Ü·r¬ÖÊ9û€@X%"ôèImžð[÷Õˆc¨NÆ­ý"m¨®þšºKì/Åc·#Äa¡s)<Ön×±=¡ZXÔÒ{îùzkÖ±[ë‚Ü"w†88oj@Ë=í?‡O}6ès¼°ôÉí§½±ò§,7×2Æû|ø\0Öµ-íâ€%HLn.þ–º­tÑǹÚ³òÿ”ôÌ5F§„©Çº>,‡} Ë~@P'i,þúˆûußœu\|~Ήø4[kÉ›iQ¸´#PŽ€ÏOåX­óÂW×t¨t2ŸN"-§é5|…~ž5ô[êÔ¸XêuÕÆ<¶·—ö”ñ)Á<¾NúpÊ}cdúü Þ>kžR\a7æÍa:-£éÔÓ´œîƒÝ&ßÒ¥Žoi_NÁþxÏö@.©S‰ž¢aÀst«d€)ÐS4+›cõЧš˜ÐÆX×$‡XRXåbLñ–Æ3V6çKÊî^Èå+“ò5¦Á—±źJÚCbÏñZÐs:CÜ·Kâ$O l§øC¿¬Kø„XYR _k`J㲆ÿ± ¶gÛÀ #˜,é̘:pR÷ZŽïùS…oC|Ë ·âëÐ9]rz »øÐW¢:Æâ:f,†øEÿ¶^2æ1x ‰v†ÈlwlîηոPËZ1Ì…ií8b|fÝÄÆ­ÚcA£\¼ø[ù5—ž¹’³4žØâ\*?„oÝ¥‹T)ŸŽ¯¦ÿÚÎZëq®XúyÊØÖÄ5#âÌü§mÒcþ­¶W-ÿ—Àkö “¤ˆCôp}‘]oíäLÅÛ•°]})][§ÉëSèkŒ­±°Ö×åûZû€Á˜¼\kÔiÍÉ7§'71Óv,—ã×öÖ^c²ö·¶~k­ôåâ›39}VþºGÔʳœ^ÐÇ^[±\ì[ñ_û¹ë;© güÊïI«}L¸‘Ý«ŒçÌ^GÖãêB 5¯jþ¾~Íëõe˜ý€u¸}“0“|¨Ç ?üéÓaíóÖôíù]Ó_Ï™šèºîµ"àsÁZGfœ_›½0töÅ\‚ø·¡¹kpLcDìÚŽ­–±&Ç6Fdz{iL7¹à̲dR¯üKäN•géÄ\+îCrn­1¬Ñ/Ï·5ŽJãÓбñkd½c{6ËW§bhRÑñ”.ö¡„^̓6OÍ·çúXlcL¬ôÄz½ÝqG³®%âüÖ}©úP~è+“ò7öIëŽûj·é|`6c¿Ø¯é¤AFÓ©ý):û—(µÏcí[èk{rsà1‡Yž@ E‰Í»ìÇèÜ¢Œ%®9]Cé[ÄQûœ‹Wó ­C§žW†ÊOá_äc€% „úNuñg¶§–9}9úT{k‘Oå^sÜNùž_ŽžÒ³wš5Öú¶ŒM,jê^3æÖq[ë‚Ý*7© ¸+(,ü§¾øà`u¤t-™¬VquéÉåâfßP Rü)Z—_{î³ÆÂZß–±·Â"ž RzS´-c—óÝ:Nk}9¿SôÙÞàä™r‚´ò²xpŒ<õì¥$Vñ@qµÖm壕Æ™Ò75vcJÿ)Ó¦b{ÊØõÅn‰­çpƒ¶%¦}ãW»ßîå¢òi|ªîã{©]<š?WÇ@ðŒw¨9™½Ó­““¸Zë]Û8èÅŸ9¥cfåÿcYb;Fמdb\,bsl-±%¦–:-Æzn5â'¶sÇReЄU „øì²{ }VØj¬ ³FÂkkª§bµÄUë·Ô»& ‡øR ƒZz‡Ä¶4¯5Чówéø–°o)c¨¥—úså¬$E ÐÃñÕ›…îP[ ×Š¿–ÞµcмŠsVßµë?tò8UlŒ-1±*O[`X#þ.œ‡­Æomzö˜¯³n®]»6yL1úÔ ‘œè;ÅÃÛn{ÇUO\ñß5áåð*¡3O÷Žmµ°¥ÍSƶÖ|ÃT_CÄoåóõÊ»ï¾û¡õ@ÅÉ€É.—8ClçþXÇ‹/8Oá°Â¶«çÏŸï¶”ðÇ,ÞĹöP'rz0 Êl­\:éÖ°ëów ¶g/^¼èÓÛÛOPQòìš™àh€t}fW›‹ñ¬ P` &Žk!¸#Ø8q9BÕì"såìØÀbLÙªo¬ÜP;š­Ø ‡®kÿ×X_+¦ÄŠXÆ%ûÇ–&oÀ)H¹©Ð´±N‘ƒ_<§èYZ65èW¯^=âmám”è"¦CdJôÎÍCÿY¾5®CcÚ+¶œ 8G ÅÅ‚ß[7þ•ê ]–[*o­1-ÅÀšX²\SÆ6¶&^ÜÚqÔ æÜÉ9pŒu©’›, l9N}±8®}ïß;¶ŒYäì¤iå^Ëù #Ç´¥qÇv~]Òµ°-½nº|«Ñ7u>(ñ©¦%¶—à™SÆ57¶Wùê%ÉÕé¤.ãÅ?wa€žëÓú¦ÖOiñ'Vc“fÈxœ*®C0âx --±ÅµËC×5-E×ý¬ë²KFó•ÖëV°cŸÃïRS|ðÏ2§b5uǶÖÒÞJ¾"Wã|e›%1EûÒͤëb⃮O*d?Ûq‰Ä‰7 1Ï”¶Ebæb†_ì닳/虪#¶}8écÜ·‡Ø·À5¶?¥ÃOÇÎøR´!¶×³:†ß5?ëqnžÒǾ!2Ðó“–²Ú°…:^´yÎv\²?•k1/Ûäe›:Øfó‘^ZBgN©ž˜oMsbcŒ/3ûã84Ü—k¯!_ul9?AùØfIÙKvô•P¤•PÒØŽõä$æÚ†ÞœMú4T'øS²)ÚPÝ}:t,àÕíœ-ðôé…l‰.Úï~Ê¥ÊßRr)ZŸ®TЦuçâ½OVë)­ç°­akŒÎ¡2)~MKá;7¶í“«=ö;æ‹ÛZg\/å-å‹}Óö¬±…¾.{ÚvW½4¶.ì£.–¤³J§ÊT¬Ö˜Ò^Ûœÿ”›ZÚÀ}ÆÆµ³º®ÔõX~J;¥Wû0E÷²±ïq;oŠû^£eJøcß´ü–ê:·®[Ɠҫ}°´µ]:6ÆÎÒÚ¿X¯¶=ÄV¬gˆì¼:®Ø×¸=ÕŸ}ÚŸ©ö––×±0v–Ö¾Åzµmk[Zß §òàO­Îw9÷ÅÁj]Võئ•ÞµèI|ë¶«/WN†ö÷Š3ã#.9Ø?µŒíMÕ·%ù9°ƒGm¿ÆøÔ%“º-cÈébî¦ìwù»¥>[«x´-+9=Å€®A.q˜òpÄ@è£Nm'ôžè1öÄ!Ž1Gùt;'ÛÔ2{­ç°/ôQç)â©c&c±Œå,°µö)ö±V[ã VqäôÄöjŵ&½9,Æú}Ô97žEoÀ)žcƒœCnnðæˆ©Äâf¥ø»úRü]´SøÛ.¬úúN ËšñOÑmy½Ä1ÏÑ®™³ô ¾Ô±µ²fÌ5uwáÜ»€c<»•öAW l)Kã^Šo ÖZö”ñ=娗ÊÛ¥ìêœ_Ê »ÌY«x´ê¶ðÓu,‹@çÍsY7»­ëäìæÜooꢴÂ%¥{¿HÎ™ÕØÌãí¶¬ŒÅv¬Ü¶Ðqo׆À’y×ù @ÍÅÉ ×––þWž–z]—Ýû©Žåe–ž–¶ æpËØüÅ@3.–˜ÚŒô8-É ™_ØScàI4n°J¥,“ÓǪuçsö€ÏûßK.ø,²å‚²?בÓzÆÂ=Ù~Ýlg¬ÜS;÷žà¢Ïž_v`×ÖäcUa׿7üšÙÛˆz<¥0÷wâÅ¿T‘ó-syOÜG`ø5³qr/íй6¾øÛƒìGÀp5! øuáç€×ä¨ûR†@< eRÎåœ.~ÍœîØŸr䩼?ã«ÿSf©ØãÁXÄ4íúytñ‘ÇKGÀ¸ˆ@×uã××E¬¼µryÜäöÁz"Éa£Óó¾~òyé8È];zÑ×}›qÍëuG`«„ @îâØjPkôÛ1^㨸O§€ÀØkÏ7§û±+ÿ¯vuîÐpNÜ«G æ÷º#°&úÖ÷ãÇ×ä´ûâ8Ž@-°¨Y؇ðÖòÙõ:5ð @ T]§#ଋ|è¦a5Á»#Ž@¾èÇ»G`¿ôÝÝoäÙ© зq½ðUÀ§ŠÇé8§‡€¾ÀÅŸåé¡áŸÈýT®ûà”²ÀcuvŽ&¹ÔD§Ãîë×¼^wö‚@jà€…FW¿Ñ.øä¤Ñðº#PŽ@ßµÓ×_nÉ9m" 7¨û3 Œcnñ_À7é¬_œW1 îÄ €õ‡kߘyÀ üÌfÝœ#°R‹=®‹=vºëú)Õëô¶#pªø€G¾kò‚%àŒîº)G ä¤e^NÕù©:|XG AÀ7+ÉŸÔV2îÆ뜴ԗӅMvßFû WGÀ73&@nÒÊÑgtÍM9U¨‘ã]:}Pu8]ùNð ÀÌOZq{fwÜœ#D@çåšSíg2':Ž@0 M½Ÿ´êaëš§!°—ÜÜKÓFÓ¥nü@7>³÷®ùÕÖì`¸ÁYH-š[ÈGøò}Vðܘ#°A|°¢AãdËrE®¹+'ˆ€çá º‡|Rø`%Ãí“íJâDÝЯ ‘‹[ÌGÉ£‡í BÀ7ƒàªÃ¼Åɶ®u ô¹õ\Ô±,¥Ût¶„€?¸àhm}²]:7m„€^0÷’:&#˜\#° ˜ÛVתßXhX­p!÷Ýìàd‚P<w0 ®Ð׫E Ðç $]‡#°1ôdâ‹ÿÆÏÝu&"Àëßߘd©¸O²¥H9Ÿ#à8Ž€F€ ¶¦­k]¾‹¢Ë9F`Î ©žp6 ™»î,‚€åõëò À"Cš7PžÓ{õ#àù¼þ1r׋€åõ“Ò倂±OW æ,ŽÀ%˜K,Á0ç«ñKU$è+šqÕŽÀ.°¼~rº|Г:9àzļûð\9tÇâ ¯9CÐ×~ é¥:»dNvÐ ïê/Þùkú"/‘Y3_;k÷mÍÄ׎žâ¾’8údNîc€¤”¾þà—äÑI³¤{³m‰«¥®5á<öÚÙ+kÇvú(Äym‰)uÅ6´×q_ÜÖ¼¬—ð,rŽ!èL_I]³ßûû°£~‹§É±–œz Q~éQô¼µSÇ´æ5Šë&uíÐfª#œ£ýY7÷7‡>¼ßØ2ñ5ĉ¨+¦X¦‹×ûG ^sµ¯«ÙÞ¨H7œ§ÓËÄ9ˆç‰´&®k¸6¦ø0E£WÛy²c½V[û±±Ä4uíPªÏ:“ E™;µã PÓ¦Ô­õMñeO²Žk½Ñ\¶¼~ÇD;EvŒ½™5a[âïxSûQâµ£±e}öV/jœüeyQý|-·´óEë–s–Êû¥ìžGîµ>¸ ôñyÿ²pýšûš}ŽŽuÖ“²^²Yck­¯^äu5×ÀÁBçØkp*Z–v-pˆã©¡3¶qjmÇ´ñ=ápé€å…º@j½µ}Oų&Z l×ü{ÔÈ7b>&÷(;6‡ÆØcKÇ yK»S1ÈÅãy›ÏÛf}tÇÔSb¾¶g–3ƒé*k]𰹈]ñÎÙW [ÇõÊ…aÔ׌®_`*hp¼Æè#SàR–¥–=b5<¡ÃóöbÞN€ò(ê˜ÚcJp—Àvô[tzHyõj]sç)5±u\Ï3Êj!džŽÁÖʇ󨖩ÕÌYD4Ûe°·Z [ÇÔ~¬¨q lë®ÈŒLÊZ ©L„ê Æ>ÌÝž[ÇÕîÖ7°ÔÇl}ñ×Èõׇ`Û¯mµçÇ´^Ìí¥g¬C›; ø›<ö2a2]Îí©âj™CÃx,ÙÎÙËÑ)·…rîœ&´ üö€anœg®ß’N[Ž©%ª®9±­²@8X6aÍû—¶Qò¢g9¯'¶Öt\¶šË´iûÄ“e™†ur鸴‡sƦ} Ý¸Ô¾m¥®ãZÒgøs˜Ç¥±uL9å%ó¯Obl¯<|øðÃ>GJú™ˆ%¼Kó”ÀÒ~ÒþV°Ý#®V1MCø`åsªv9%ÞÚ¾ÅúÛ‘éíÚ˜jý¹f¡£5íw ŠÇBõžÍ8 ®¡<ŘçÀ}o¸Z]hSp¡StÌ1ö[¶áØÚ^mLkë·G¤Þ3DS|í!À)Nº¬#07\x§Ú2QYù05—wi¬õZö À´qué"`u±úâ¿ÃäðXÍ'ͱû &gÚ¸ seŽ>4~_ü‡"æüŽÀ6°š3–ˆ¾Ê§–Äm:D ô‚,å£ÞÒrìâ_ËŸR¿ÏpÆ!€k7w݃¾ÖkÛ7ãÆÛ¥6Œ@|¡–^œ±œ%¥>XÚt]Ž€#0’ya­› `úø»† €‹0u±¦hqH%<±Œ·GÀ kœC|ÀÑñr׌½ø 7V¶Põ_Š”ó9ÛF ö\2ß EÌù7‡ÀØ‹n¬Ü€|ñ‚–ó:Ë#€kvÊu;ǼRŠ’oJ‘r¾M"°¦‹-pÊ$ëò¶#àÔGÀêš]˼äÖÏ·°Qj]ìVz7 «»ílËëÖR×0ýÀô\vÕ”î²k_Œµõ¯zÜ9G`X^Öº¦Bëw¦"èò«C tá_Âñ5]üKÄï6·‹€ÎÝ5_cÖ븭u/­Ï7KÀŽíÇÎÔI#Ö—‚nª”N Z‰ïv\‡#PSÍ_븭õMk `*‚. $¹u¢—ê+å»ä´G ‰@êš-EO*Ø(Ñ:>k}°úÀE×qD F’×ÐI‡kê¦ /­"Ðw}ôõï5î¡q­'ß IçÏ"ЕäcoÍwéÌ:RØQSw¡ Îæ¬S½>¬ã¶Ög™0þ €%š'¬«+Éûÿ.ÙZ.a³V,®×°FàT¯!qáµ+}~À ÉÖÓu!øâ‰á¡o®ëy“:}ŠqÿÿQŒ–g>é¸\IEND®B`‚./data/click-scope-departments-db.conf.in0000644000015600001650000000061412676763577020420 0ustar jenkinsjenkinsdescription "Default Click Departments Database" author "Pawel Stolowski " start on starting unity8 script DB_FILE=@APPS_DATA_DIR@/departments.db if [ -f $HOME/.cache/click-departments.db ] then @APPS_DATA_DIR@/update_schema.sh $HOME/.cache/click-departments.db else cp $DB_FILE $HOME/.cache/click-departments.db fi end script ./data/apps-scope.svg0000644000015600001650000005436112676763577014644 0ustar jenkinsjenkins image/svg+xml ./data/com.canonical.unity.clickscope.gschema.xml0000644000015600001650000000160712676763577022165 0ustar jenkinsjenkins [] Applications to display in the top category of the Apps scope List of application IDs that will be displayed in the upper area of the Applications scope for quick access. [] Applications to hide from the Apps scope List of application IDs that will be hidden from the Applications scope. Legacy applications must be the file name of the .desktop file (dialer-app.desktop), and clicks should use the package name (com.ubuntu.calendar). ./data/departments.db0000644000015600001650000005200012676763577014672 0ustar jenkinsjenkinsSQLite format 3@  -æ ü<ZÏs/°@<‡IwtablemetametaCREATE TABLE meta (name TEXT PRIMARY KEY, value TEXT)';indexsqlite_autoindex_meta_1meta itabledeptnamesdeptnamesCREATE TABLE deptnames (deptid TEXT, locale TEXT, name TEXT, CONSTRAINT deptuniq PRIMARY KEY (deptid, locale))1Eindexsqlite_autoindex_deptnames_1deptnames9‚QtabledeptsdeptsCREATE TABLE depts (deptid TEXT, parentid TEXT, CONSTRAINT pkey PRIMARY KEY (deptid, parentid), CONSTRAINT fkey FOREIGN KEY (deptid) REFERENCES deptnames(deptid)))=indexsqlite_autoindex_depts_1deptssAtablepkgmappkgmapCREATE TABLE pkgmap (pkgid TEXT, deptid TEXT, CONSTRAINT pkey PRIMARY KEY (pkgid, deptid))+?indexsqlite_autoindex_pkgmap_1pkgmapû û Û Û -#com.ubuntu.musicmusic-audio BêÙ˼ªš‚n`O-ýñäØË»«˜‹}q]SB% books-comics games+ developer-tools  medical  lifestyle  graphics) news-magazines# music-audio# media-video  business  finance  shopping  weather) health-fitness  sports ' entertainment G universal-accessaccessibility % productivity  education+ personalisation3 science-engineering# accessories' communication ! food-drink  reference% travel-local/ social-networking (ïÝϼ§˜…xh]O;,ýéÔ³šŒvjX5(  weather"G universal-accessaccessibility % travel-local  sports / social-networking  shopping3 science-engineering reference% productivity + personalisation) news-magazines# music-audio  medical# media-video lifestyle) health-fitness  graphics  games! food-drink  finance' entertainment  education + developer-tools' communication  business% books-comics# accessoriesìûöñìkR4ÔìÔreferenceeu_ES^gamesen_US óó version4 õõ  version eÖ¯ŒUöÑ­’mK%çĤo4ß¾™e2U#com.ubuntu.developer.mzanetti.taggeraccessories#7#com.ubuntu.calculatoraccessories/#com.ubuntu.cameraaccessories 1#com.ubuntu.galleryaccessories1_com.ubuntu.developer.ken-vandine.pathwindgames9_'com.ubuntu.developer.webapps.webapp-gmailcommunication3]com.ubuntu.developer.webapps.webapp-ebayshopping-#com.ubuntu.musicmusic-audio!3#com.canonical.payuiaccessories-#com.ubuntu.clockaccessories 1com.ubuntu.weatherweather$ Ecom.zeptolab.cuttherope.freegames 3!com.ubuntu.terminalaccesories# 3'com.ubuntu.telegramcommunication /com.ubuntu.sudokugames"/)com.ubuntu.shortsnews-magazines#9!com.ubuntu.filemanageraccesories#Ccom.ubuntu.dropping-lettersgames8gcom.ubuntu.developer.webapps.webapp-amazon-esshopping5acom.ubuntu.developer.webapps.webapp-amazonshopping!1%com.nokia.heremapstravel-local%5)com.canonical.elpaisnews-magazines(;)com.canonical.cincodiasnews-magazines 6ža9îÇ™q69!icom.ubuntu.developer.webapps.webapp-amazon-intshopping& 9'webbrowser-app.desktopcommunication,I#ubuntu-system-settings.desktopaccessories%7'messaging-app.desktopcommunication%;#mediaplayer-app.desktopsound-video"1'dialer-app.desktopcommunication&=#address-book-app.desktopaccessories;c'com.ubuntu.developer.webapps.webapp-twittercommunication"5#com.ubuntu.remindersaccessories<e'com.ubuntu.developer.webapps.webapp-facebookcommunication ^^‡±Ùý!Giоó+f¢ØS‘·Ý(=#address-book-app.desktopaccessories);) com.canonical.cincodiasnews-magazines'5)com.canonical.elpaisnews-magazines#3#com.canonical.payuiaccessories#1%com.nokia.heremapstravel-local%7#com.ubuntu.calculatoraccessories!/#com.ubuntu.cameraaccessories -#com.ubuntu.clockaccessories3_com.ubuntu.developer.ken-vandine.pathwindgames4U#com.ubuntu.developer.mzanetti.taggeraccessories7acom.ubuntu.developer.webapps.webapp-amazonshopping:gcom.ubuntu.developer.webapps.webapp-amazon-esshopping;icom.ubuntu.developer.webapps.webapp-amazon-intshopping!5]com.ubuntu.developer.webapps.webapp-ebayshopping>e'com.ubuntu.developer.webapps.webapp-facebookcommunication;_'com.ubuntu.developer.webapps.webapp-gmailcommunication=c'com.ubuntu.developer.webapps.webapp-twittercommunication%Ccom.ubuntu.dropping-lettersgames%9!com.ubuntu.filemanageraccesories"1#com.ubuntu.galleryaccessories >Û¶štQ2 æ¾–g>(9'webbrowser-app.desktopcommunication .I#ubuntu-system-settings.desktopaccessories'7'messaging-app.desktopcommunication';#mediaplayer-app.desktopsound-video$1'dialer-app.desktopcommunication&Ecom.zeptolab.cuttherope.freegames 1com.ubuntu.weatherweather "3!com.ubuntu.terminalaccesories %3'com.ubuntu.telegramcommunication /com.ubuntu.sudokugames $/)com.ubuntu.shortsnews-magazines$5#com.ubuntu.remindersaccessories ZÓ®‘pK,ùгJ%å̱˜}Z7óÖ½”Z#%)books-comicsen_USBooks & Comicsgamesen_USGames'++developer-toolsen_USDeveloper Toolsmedicalen_USMedicallifestyleen_USLifestylegraphicsen_USGraphics')-news-magazinesen_USNews & Magazines!#'music-audioen_USMusic & Audio!#'media-videoen_USMedia & Videobusinessen_USBusinessfinanceen_USFinanceshoppingen_USShoppingweatheren_USWeather')-health-fitnessen_USHealth & Fitness sportsen_USSports# ''entertainmenten_USEntertainmentD GIuniversal-accessaccessibilityen_USUniversal Access/Accessibility! %%productivityen_USProductivity educationen_USEducation'++personalisationen_USPersonalisation137science-engineeringen_USScience & Engineering#accessoriesen_USUtilities#''communicationen_USCommunication!%food-drinken_USFood & Drinkreferenceen_USReference#%)travel-localen_USTravel & Local+//social-networkingen_USSocial Networking UáÁ¤„\5 â¾™uPà­uX:ÿÚ³ŒoU4financeca_ESFinances3'entertainmentgl_ESLecer%2'+entertainmenteu_ESEntretenimendua%1'+entertainmentes_ESEntretenimiento#0''entertainmentca_ESEntreteniment/!educationgl_ESEducación.educationeu_ESHezkuntza-!educationes_ESEducación,educationca_ESEducació6++Ideveloper-toolsgl_ESFerramentas de desenvolvemento1*+?developer-toolseu_ESGaratzaileentzako tresnak9)+Odeveloper-toolses_ESHerramientas para desarrolladores3(+Cdeveloper-toolsca_ESEines per a desenvolupadors#'''communicationgl_ESComunicación"&'%communicationeu_ESKomunikazioa#%''communicationes_ESComunicación"$'%communicationca_ESComunicació%#%-books-comicsgl_ESLibros e cómics*"%7books-comicseu_ESLiburuak eta komikiak%!%-books-comicses_ESLibros y cómics& %/books-comicsca_ESLlibres i còmics#!accessoriesgl_ESUtilidades#accessorieseu_ESTresnak#!accessorieses_ESUtilidades#accessoriesca_ESUtilitats TæË±ŒhD  öà˰“w[:øÖ¬‚V-ùàÆ {T%R#/music-audioeu_ESMusika eta audioa#Q#+music-audioes_ESMúsica y audio$P#-music-audioca_ESMúsica i àudioOmedicalgl_ESMedicinaNmedicaleu_ESOsasunaMmedicales_ESMedicinaLmedicalca_ESMedicina'K#3media-videogl_ESMultimedia e vídeo*J#9media-videoeu_ESMultimedia eta bideoak(I#5media-videoes_ESMultimedia y vídeos(H#5media-videoca_ESMultimèdia i vídeo G)lifestylegl_ESEstilo de vidaF%lifestyleeu_ESBizi-estiloa E)lifestylees_ESEstilo de vidaD'lifestyleca_ESEstil de vidaCgraphicsgl_ESGráficosBgraphicseu_ESGrafikoakA!graphicses_ESGraÌficos@graphicsca_ESGràfics?gamesgl_ESXogos>gameseu_ESJokoak=gameses_ESJuegos<gamesca_ESJocs";!+food-drinkgl_ESComida & bebida":!+food-drinkeu_ESJana eta edaria"9!+food-drinkes_ESComida y bebida#8!-food-drinkca_ESMenjar i begudes7financegl_ESFinanzas6financeeu_ESFinantzak5financees_ESFinanzas ;ݰ„R)á»—xZ9èµ~K1ûá·d;'k/'social-networkinggl_ESRedes sociais'j/'social-networkingeu_ESSare sozialak(i/)social-networkinges_ESRedes sociales(h/)social-networkingca_ESXarxes socialsgshoppinggl_ESComprasfshoppingeu_ESErosketakeshoppinges_ESTiendasdshoppingca_ESCompres1c37science-engineeringgl_ESCiencia e enxeñaría5b3?science-engineeringeu_ESZientzia eta ingeniaritza1a37science-engineeringes_ESCiencia e ingeniería1`37science-engineeringca_ESCiència i enginyeria_!referencegl_ESReferencia^'referenceeu_ESErreferentzia]!referencees_ESReferencia\#referenceca_ESReferència"[%'productivitygl_ESProdutividade$Z%+productivityeu_ESProduktibitatea"Y%'productivityes_ESProductividad"X%'productivityca_ESProductivitat'W)-news-magazinesgl_ESNovas e revistas0V)?news-magazineseu_ESAlbisteak eta aldizkariak*U)3news-magazineses_ESNoticias y revistas+T)5news-magazinesca_ESNotícies i revistes!S#'music-audiogl_ESMúsica e son rèÏ·žuO'¹s&ßŦ‹r{weathergl_ESO Tempozweathereu_ESEguraldiay'weatheres_ESMeteorologíaxweatherca_ESEl tempsEwGKuniversal-accessaccessibilitygl_ESAcceso universal/AccesibilidadeKvGWuniversal-accessaccessibilityeu_ESSarbide unibertsala/ErabilerraztasunaDuGIuniversal-accessaccessibilityes_ESAcceso universal/accesibilidadGtGOuniversal-accessaccessibilityca_ESAccés universal i accessibilitat#s%)travel-localgl_ESViaxes e local&r%/travel-localeu_ESBidaiak eta gidak$q%+travel-locales_ESViajes y guías'p%1travel-localca_ESViatges i regionalosportsgl_ESDeportesnsportseu_ESKirolakmsportses_ESDeporteslsportsca_ESEsports *ccy¥»Ñèÿ-DWo‡Ÿ·Ïé7Qey¡µÍåý-?Qcu‡œ±ÆÛð#accessoriesca_ES#accessoriesen_US#accessorieses_ES#accessorieseu_ES#accessoriesgl_ES%books-comicsca_ES %books-comicsen_US%books-comicses_ES!%books-comicseu_ES"%books-comicsgl_ES#businessen_US'communicationca_ES$'communicationen_US'communicationes_ES%'communicationeu_ES&'communicationgl_ES'+developer-toolsca_ES(+developer-toolsen_US+developer-toolses_ES)+developer-toolseu_ES*+developer-toolsgl_ES+educationca_ES,educationen_US educationes_ES-educationeu_ES.educationgl_ES/'entertainmentca_ES0'entertainmenten_US 'entertainmentes_ES1'entertainmenteu_ES2'entertainmentgl_ES3financeca_ES4financeen_USfinancees_ES5financeeu_ES6financegl_ES7!food-drinkca_ES8!food-drinken_US!food-drinkes_ES9!food-drinkeu_ES:!food-drinkgl_ES;gamesca_ES< +xxˆ˜¨»Îáô 4H\p„š°ÆÜò(:LbxޤºÓì7Qh–­ÄØìgameses_ES=gameseu_ES>gamesgl_ES?graphicsca_ES@graphicsen_USgraphicses_ESAgraphicseu_ESBgraphicsgl_ESC)health-fitnessen_USlifestyleca_ESDlifestyleen_USlifestylees_ESElifestyleeu_ESFlifestylegl_ESG#media-videoca_ESH#media-videoen_US#media-videoes_ESI#media-videoeu_ESJ#media-videogl_ESKmedicalca_ESLmedicalen_USmedicales_ESMmedicaleu_ESNmedicalgl_ESO#music-audioca_ESP#music-audioen_US#music-audioes_ESQ#music-audioeu_ESR#music-audiogl_ESS)news-magazinesca_EST)news-magazinesen_US)news-magazineses_ESU)news-magazineseu_ESV)news-magazinesgl_ESW+personalisationen_US%productivityca_ESX%productivityen_US %productivityes_ESY%productivityeu_ESZ%productivitygl_ES[referenceca_ES\referenceen_USreferencees_ES] $‚ìΰ’tVC0 ÷ÛÀ¤ˆl[J9(éÒ»¤|T,Üʸ¦”‚weathergl_ES{weathereu_ESzweatheres_ESyweatheren_USweatherca_ESx'Guniversal-accessaccessibilitygl_ESw'Guniversal-accessaccessibilityeu_ESv'Guniversal-accessaccessibilityes_ESu'Guniversal-accessaccessibilityen_US 'Guniversal-accessaccessibilityca_ESt%travel-localgl_ESs%travel-localeu_ESr%travel-locales_ESq%travel-localen_US%travel-localca_ESpsportsgl_ESosportseu_ESnsportses_ESmsportsen_US sportsca_ESl/social-networkinggl_ESk/social-networkingeu_ESj/social-networkinges_ESi/ social-networkingen_US/social-networkingca_EShshoppinggl_ESgshoppingeu_ESfshoppinges_ESeshoppingen_USshoppingca_ESd3science-engineeringgl_ESc3science-engineeringeu_ESb3science-engineeringes_ESa3science-engineeringen_US3science-engineeringca_ES`referencegl_ES_./data/CMakeLists.txt0000644000015600001650000000134112676763577014577 0ustar jenkinsjenkinsadd_schema(com.canonical.unity.clickscope.gschema.xml) # upstart job for default departments db set(DEPARTMENTS_UPSTART_JOB click-scope-departments-db.conf) configure_file(${DEPARTMENTS_UPSTART_JOB}.in ${DEPARTMENTS_UPSTART_JOB} @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DEPARTMENTS_UPSTART_JOB} DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) install( FILES clickscope-screenshot.jpg ubuntu-store-scope.png store-scope-icon.svg DESTINATION "${STORE_DATA_DIR}" ) install( FILES clickscope-screenshot.jpg apps-scope.png ubuntu-logo.png DESTINATION "${APPS_DATA_DIR}" ) install( FILES departments.db DESTINATION "${APPS_DATA_DIR}" ) install( PROGRAMS update_schema.sh DESTINATION "${APPS_DATA_DIR}" ) ./data/store-scope-icon.svg0000644000015600001650000005300612676763577015756 0ustar jenkinsjenkins image/svg+xml ./libclickscope/0000755000015600001650000000000012676763577013735 5ustar jenkinsjenkins./libclickscope/tests/0000755000015600001650000000000012676763604015066 5ustar jenkinsjenkins./libclickscope/tests/test_data.h0000644000015600001650000000344712676763577017230 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef TEST_DATA_H #define TEST_DATA_H #include namespace testing { const std::string FAKE_SERVER = "http://fake-server/"; const std::string FAKE_PATH = "fake/api/path"; const std::string FAKE_QUERY = "FAKE_QUERY"; const std::string FAKE_PACKAGENAME = "com.example.fakepackage"; const std::string& systemApplicationsDirectoryForTesting(); const std::string& userApplicationsDirectoryForTesting(); } #endif // TEST_DATA_H ./libclickscope/tests/fake_json.h0000644000015600001650000000504312676763577017211 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef FAKE_JSON_H #define FAKE_JSON_H #include extern const std::string FAKE_JSON_REVIEWS_RESULT_ONE; extern const std::string FAKE_JSON_SEARCH_RESULT_ONE; extern const std::string FAKE_JSON_SEARCH_RESULT_ONE_SCOPE; extern const std::string FAKE_JSON_SEARCH_RESULT_MISSING_DATA; extern const std::string FAKE_JSON_SEARCH_RESULT_MANY; extern const std::string FAKE_JSON_SEARCH_RESULT_RECOMMENDS; extern const std::string FAKE_JSON_PACKAGE_DETAILS; extern const std::string FAKE_JSON_PACKAGE_DETAILS_DEB; extern const std::string FAKE_JSON_BOOTSTRAP; extern const std::string FAKE_JSON_BROKEN_BOOTSTRAP; extern const std::string FAKE_JSON_DEPARTMENTS_ONLY; extern const std::string FAKE_JSON_DEPARTMENT_WITH_APPS; extern const std::string FAKE_JSON_STORE_HOME; extern const std::string FAKE_JSON_BROKEN_DEPARTMENTS; extern const std::string FAKE_JSON_MANIFEST_REMOVABLE; extern const std::string FAKE_JSON_MANIFEST_NONREMOVABLE; extern const std::string FAKE_JSON_MANIFEST_ONE_APP; extern const std::string FAKE_JSON_MANIFEST_ONE_SCOPE; extern const std::string FAKE_JSON_MANIFEST_ONE_APP_ONE_SCOPE; extern const std::string FAKE_JSON_MANIFEST_TWO_APPS_TWO_SCOPES; #endif // FAKE_JSON_H ./libclickscope/tests/test_data.cpp.in0000644000015600001650000000333212676763577020161 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "test_data.h" const std::string& testing::systemApplicationsDirectoryForTesting() { static const std::string s{"@CMAKE_CURRENT_SOURCE_DIR@/applications/system"}; return s; } const std::string& testing::userApplicationsDirectoryForTesting() { static const std::string s{"@CMAKE_CURRENT_SOURCE_DIR@/applications/user"}; return s; } ./libclickscope/tests/test_index.cpp0000644000015600001650000005463412676763604017754 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include "fake_json.h" #include #include using namespace ::testing; namespace { const std::string fake_arch{"fake_arch"}; const std::string fake_fwk_1{"fake_fwk_1"}; const std::string fake_fwk_2{"fake_fwk_2"}; std::vector fake_frameworks{fake_fwk_1, fake_fwk_2}; class MockableIndex : public click::Index { public: using click::Index::build_index_query; MockableIndex(const QSharedPointer& client, const QSharedPointer configuration) : click::Index(client, configuration) { } }; class MockConfiguration : public click::Configuration { public: MOCK_METHOD0(get_architecture, std::string()); MOCK_METHOD0(get_available_frameworks, std::vector()); }; class IndexTest : public ::testing::Test { protected: QSharedPointer clientPtr; QSharedPointer namPtr; QSharedPointer configPtr; std::shared_ptr indexPtr; virtual void SetUp() { namPtr.reset(new MockNetworkAccessManager()); clientPtr.reset(new NiceMock(namPtr)); configPtr.reset(new MockConfiguration()); indexPtr.reset(new MockableIndex(clientPtr, configPtr)); // register default value for build_headers() mock DefaultValue>::Set(std::map()); } public: MOCK_METHOD2(search_callback, void(click::Packages, click::Packages)); MOCK_METHOD2(details_callback, void(click::PackageDetails, click::Index::Error)); }; class MockPackageManager : public click::PackageManager, public ::testing::Test { public: MOCK_METHOD2(execute_uninstall_command, void(const std::string&, std::function)); }; } TEST_F(IndexTest, testSearchCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->search("", "", [](click::Packages, click::Packages) {}); } MATCHER_P(QueryContains, query, "") { return arg["q"].find(query) != std::string::npos; } TEST_F(IndexTest, testSearchQueryIsLowercase) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, QueryContains("foobar"))) .Times(1) .WillOnce(Return(response)); indexPtr->search("FooBar", "", [](click::Packages, click::Packages) {}); } TEST_F(IndexTest, testSearchSignsCall) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, true, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->search("", "", [](click::Packages, click::Packages) {}); } TEST_F(IndexTest, testBootstrapSignsCall) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, true, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->bootstrap([](const click::DepartmentList&, const click::HighlightList&, click::Index::Error, int) {}); } TEST_F(IndexTest, testDepartmentsSignsCall) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, true, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->departments("departments", [](const click::DepartmentList&, const click::HighlightList&, click::Index::Error, int) {}); } TEST_F(IndexTest, testSearchSendsRightPath) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(EndsWith(click::SEARCH_PATH), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->search("", "", [](click::Packages, click::Packages) {}); } TEST_F(IndexTest, testSearchCallbackIsCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, search_callback(_, _)).Times(1); indexPtr->search("", "", [this](click::Packages packages, click::Packages recommends){ search_callback(packages, recommends); }); response->replyFinished(); } TEST_F(IndexTest, testSearchEmptyJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::Packages empty_package_list; EXPECT_CALL(*this, search_callback(empty_package_list, _)).Times(1); indexPtr->search("", "", [this](click::Packages packages, click::Packages recommends){ search_callback(packages, recommends); }); response->replyFinished(); } TEST_F(IndexTest, testSearchSingleJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_SEARCH_RESULT_ONE.c_str()); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::Packages single_package_list = { click::Package { "org.example.awesomelauncher", "Awesome Launcher", 1.99, "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }; EXPECT_CALL(*this, search_callback(single_package_list, _)).Times(1); indexPtr->search("", "", [this](click::Packages packages, click::Packages recommends){ search_callback(packages, recommends); }); response->replyFinished(); } TEST_F(IndexTest, testSearchIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); auto search_operation = indexPtr->search("", "", [](click::Packages, click::Packages) {}); EXPECT_CALL(reply.instance, abort()).Times(1); search_operation.cancel(); } TEST_F(IndexTest, DISABLED_testInvalidJsonIsIgnored) { // TODO, in upcoming branch } TEST_F(IndexTest, testSearchNetworkErrorIgnored) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*configPtr, get_architecture()) .Times(1) .WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()) .Times(1) .WillOnce(Return(fake_frameworks)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(reply.instance, errorString()).Times(1).WillOnce(Return("fake error")); indexPtr->search("", "", [this](click::Packages packages, click::Packages recommends){ search_callback(packages, recommends); }); click::Packages empty_package_list; EXPECT_CALL(*this, search_callback(empty_package_list, _)).Times(1); emit reply.instance.error(QNetworkReply::UnknownNetworkError); } TEST_F(IndexTest, testGetDetailsCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details("", [](click::PackageDetails, click::Index::Error) {}); } TEST_F(IndexTest, testGetDetailsSendsPackagename) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(EndsWith(FAKE_PACKAGENAME), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details(FAKE_PACKAGENAME, [](click::PackageDetails, click::Index::Error) {}); } TEST_F(IndexTest, testGetDetailsSendsRightPath) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(StartsWith(click::SEARCH_BASE_URL + click::DETAILS_PATH), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details(FAKE_PACKAGENAME, [](click::PackageDetails, click::Index::Error) {}); } TEST_F(IndexTest, testGetDetailsCallbackIsCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_PACKAGE_DETAILS.c_str()); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details("", [this](click::PackageDetails details, click::Index::Error error){ details_callback(details, error); }); EXPECT_CALL(*this, details_callback(_, _)).Times(1); response->replyFinished(); } TEST_F(IndexTest, testGetDetailsJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_PACKAGE_DETAILS.c_str()); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details("", [this](click::PackageDetails details, click::Index::Error error){ details_callback(details, error); }); click::Date published; published.parse_iso8601("2013-11-04T00:40:24.686298Z"); click::Date updated; updated.parse_iso8601("2014-07-03T08:16:34.532525Z"); click::PackageDetails fake_details { { "ar.com.beuno.wheather-touch", "\u1F4A9 Weather", 1.99, "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png", "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", "0.2", "app" }, "\u1F4A9 Weather\nA weather application.", "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", "fake_sha512", 3.5, "these, are, key, words", "tos", "Proprietary", "Fake Publisher", "Fake Developer", "Fake Company", "http://example.com", "http://example.com/support", "sshot0", {"sshot1", "sshot2"}, 177582, "0.2", published, updated, "This is the changelog", "None", "tools" }; EXPECT_CALL(*this, details_callback(fake_details, _)).Times(1); response->replyFinished(); } TEST_F(IndexTest, testGetDetailsJsonUtf8) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray appname_utf8("\xe5\xb0\x8f\xe6\xb5\xb7"); QByteArray appname_json("\\u5c0f\\u6d77"); qDebug() << "testGetDetailsJsonUtf8, title:" << appname_utf8.toPercentEncoding(" "); QByteArray fake_json = QByteArray(FAKE_JSON_PACKAGE_DETAILS.c_str()).replace("Weather", appname_json); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details("", [this, appname_utf8](click::PackageDetails details, click::Index::Error error){ details_callback(details, error); }); click::Date published; published.parse_iso8601("2013-11-04T00:40:24.686298Z"); click::Date updated; updated.parse_iso8601("2014-07-03T08:16:34.532525Z"); click::PackageDetails fake_details { { "ar.com.beuno.wheather-touch", std::string("\u1F4A9 ") + appname_utf8.constData(), 1.99, "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png", "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", "v0.1", "app" }, (std::string("\u1F4A9 ") + std::string(appname_utf8.constData()) + "\nA weather application.").c_str(), "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", "fake_sha512", 3.5, "these, are, key, words", "tos", "Proprietary", "Fake Publisher", "Fake Developer", "Fake Company", "http://example.com", "http://example.com/support", "sshot0", {"sshot1", "sshot2"}, 177582, "0.2", published, updated, "This is the changelog", "None", "tools" }; EXPECT_CALL(*this, details_callback(fake_details, _)).Times(1); response->replyFinished(); } TEST_F(IndexTest, testGetDetailsNetworkErrorReported) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(reply.instance, errorString()).Times(1).WillOnce(Return("fake error")); indexPtr->get_details("", [this](click::PackageDetails details, click::Index::Error error){ details_callback(details, error); }); EXPECT_CALL(*this, details_callback(_, click::Index::Error::NetworkError)).Times(1); emit reply.instance.error(QNetworkReply::UnknownNetworkError); } TEST_F(IndexTest, testGetDetailsIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); auto get_details_operation = indexPtr->get_details("", [](click::PackageDetails, click::Index::Error) {}); EXPECT_CALL(reply.instance, abort()).Times(1); get_details_operation.cancel(); } TEST_F(IndexTest, testGetBaseUrl) { const char *value = getenv(click::SEARCH_BASE_URL_ENVVAR.c_str()); if (value != NULL) { ASSERT_TRUE(unsetenv(click::SEARCH_BASE_URL_ENVVAR.c_str()) == 0); } ASSERT_TRUE(click::Index::get_base_url() == click::SEARCH_BASE_URL); } TEST_F(IndexTest, testGetBaseUrlFromEnv) { ASSERT_TRUE(setenv(click::SEARCH_BASE_URL_ENVVAR.c_str(), FAKE_SERVER.c_str(), 1) == 0); ASSERT_TRUE(click::Index::get_base_url() == FAKE_SERVER); ASSERT_TRUE(unsetenv(click::SEARCH_BASE_URL_ENVVAR.c_str()) == 0); } TEST_F(IndexTest, testPackageListsFromJsonNodeNoRecommends) { auto lists = indexPtr->package_lists_from_json(FAKE_JSON_SEARCH_RESULT_ONE); EXPECT_EQ(1, lists.first.size()); EXPECT_EQ(0, lists.second.size()); } TEST_F(IndexTest, testPackageListsFromJsonNodeHasRecommends) { auto lists = indexPtr->package_lists_from_json(FAKE_JSON_SEARCH_RESULT_RECOMMENDS); EXPECT_EQ(1, lists.second.size()); } TEST_F(MockPackageManager, testUninstallCommandCorrect) { click::Package package = { "org.example.testapp", "Test App", 0.00, "/tmp/foo.png", "uri", "0.1.5", "app" }; std::string expected = "pkcon -p remove org.example.testapp;0.1.5;all;local:click"; EXPECT_CALL(*this, execute_uninstall_command(expected, _)).Times(1); uninstall(package, [](int, std::string) {}); } class ExhibitionistIndex : public click::Index { public: using click::Index::build_index_query; using click::Index::build_headers; ExhibitionistIndex(const QSharedPointer& client, const QSharedPointer configuration) : click::Index(client, configuration) { } }; class QueryStringTest : public ::testing::Test { protected: QSharedPointer clientPtr; QSharedPointer namPtr; QSharedPointer configPtr; std::shared_ptr indexPtr; virtual void SetUp() { namPtr.reset(new MockNetworkAccessManager()); clientPtr.reset(new NiceMock(namPtr)); configPtr.reset(new MockConfiguration()); indexPtr.reset(new ExhibitionistIndex(clientPtr, configPtr)); } }; TEST_F(QueryStringTest, testBuildHeadersAddsArchitecture) { EXPECT_CALL(*configPtr, get_architecture()).Times(1).WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()).Times(1).WillOnce(Return(fake_frameworks)); auto hdrs = indexPtr->build_headers(); EXPECT_EQ(fake_arch, hdrs["X-Ubuntu-Architecture"]); } TEST_F(QueryStringTest, testBuildHeadersAddsFramework) { EXPECT_CALL(*configPtr, get_architecture()).Times(1).WillOnce(Return(fake_arch)); EXPECT_CALL(*configPtr, get_available_frameworks()).Times(1).WillOnce(Return(fake_frameworks)); auto hdrs = indexPtr->build_headers(); EXPECT_NE(std::string::npos, hdrs["X-Ubuntu-Frameworks"].find(fake_fwk_1)); EXPECT_NE(std::string::npos, hdrs["X-Ubuntu-Frameworks"].find(fake_fwk_2)); } ./libclickscope/tests/mock_network_access_manager.h0000644000015600001650000000640712676763577022774 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef MOCK_NETWORK_ACCESS_MANAGER_H #define MOCK_NETWORK_ACCESS_MANAGER_H #include #include #include #include class MockNetworkReply : public click::network::Reply { public slots: void sendFinished(); void sendError(); public: MockNetworkReply() { // Set a default value for QByteArray-returning mocked methods. ::testing::DefaultValue::Set(QByteArray("")); ON_CALL(*this, attribute(::testing::_)).WillByDefault(::testing::Return(0)); } MOCK_METHOD0(abort, void()); MOCK_METHOD0(readAll, QByteArray()); MOCK_METHOD1(attribute, QVariant(QNetworkRequest::Attribute)); MOCK_METHOD1(hasRawHeader, bool(const QByteArray&)); MOCK_METHOD1(rawHeader, QString(const QByteArray &headerName)); // We have to typedef the result here as the preprocessor is dumb // and would interpret the "," in the template spec as part of the // macro declaration and not part of the signature. typedef QList> ResultType; MOCK_METHOD0(rawHeaderPairs, ResultType()); MOCK_METHOD0(errorString, QString()); }; struct MockNetworkAccessManager : public click::network::AccessManager { MockNetworkAccessManager() { } MOCK_METHOD1(get, QSharedPointer(QNetworkRequest&)); MOCK_METHOD1(head, QSharedPointer(QNetworkRequest&)); MOCK_METHOD2(post, QSharedPointer(QNetworkRequest&, QByteArray&)); MOCK_METHOD3(sendCustomRequest, QSharedPointer(QNetworkRequest&, QByteArray&, QIODevice*)); static QList scripted_responses; static QList performed_get_requests; static QList performed_head_requests; static bool shouldSignalNetworkError; }; #endif // MOCK_NETWORK_ACCESS_MANAGER_H ./libclickscope/tests/test_pay.cpp0000644000015600001650000002146012676763577017436 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "mock_pay.h" #include #include #include #include #include #include #include using namespace ::testing; namespace { class PayTest : public ::testing::Test { protected: QSharedPointer namPtr; QSharedPointer clientPtr; std::shared_ptr package; virtual void SetUp() { namPtr.reset(new MockNetworkAccessManager()); clientPtr.reset(new NiceMock(namPtr)); package.reset(new MockPayPackage(clientPtr)); } public: MOCK_METHOD1(purchases_callback, void(pay::PurchaseSet)); }; } TEST_F(PayTest, testPayPackageRefundCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*package, do_pay_package_refund("foo")).Times(1); EXPECT_EQ(false, package->refund("foo")); } TEST_F(PayTest, testPayPackageRefundNotCalledIfCallbackExists) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); std::string callback_id = std::string{"foo"} + pay::APPENDAGE_REFUND; package->callbacks[callback_id] = [](const std::string&, bool) {}; EXPECT_CALL(*package, do_pay_package_refund("foo")).Times(0); EXPECT_EQ(false, package->refund("foo")); } TEST_F(PayTest, testRefundReturnsTrueForPurchasedItem) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); package->success = true; EXPECT_CALL(*package, do_pay_package_refund("foo")).Times(1); EXPECT_EQ(true, package->refund("foo")); } TEST_F(PayTest, testPayPackageVerifyCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*package, do_pay_package_verify("foo")).Times(1); EXPECT_EQ(false, package->verify("foo")); } TEST_F(PayTest, testPayPackageVerifyNotCalledIfCallbackExists) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); std::string callback_id = std::string{"foo"} + pay::APPENDAGE_VERIFY; package->callbacks[callback_id] = [](const std::string&, bool) {}; EXPECT_CALL(*package, do_pay_package_verify("foo")).Times(0); EXPECT_EQ(false, package->verify("foo")); } TEST_F(PayTest, testVerifyReturnsTrueForPurchasedItem) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); package->success = true; EXPECT_CALL(*package, do_pay_package_verify("foo")).Times(1); EXPECT_EQ(true, package->verify("foo")); } TEST_F(PayTest, testGetPurchasesCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); package->get_purchases([](pay::PurchaseSet) {}); } TEST_F(PayTest, testGetPurchasesSendsCorrectPath) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(EndsWith(pay::PURCHASES_API_PATH), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); package->get_purchases([](pay::PurchaseSet) {}); } TEST_F(PayTest, testGetPurchasesCallbackCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, purchases_callback(_)).Times(1); package->get_purchases([this](pay::PurchaseSet purchases) { purchases_callback(purchases); }); response->replyFinished(); } TEST_F(PayTest, testGetPurchasesEmptyJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); pay::PurchaseSet empty_purchases_list; EXPECT_CALL(*this, purchases_callback(empty_purchases_list)).Times(1); package->get_purchases([this](pay::PurchaseSet purchases) { purchases_callback(purchases); }); response->replyFinished(); } TEST_F(PayTest, testGetPurchasesSingleJsonIsParsedNullTimestamp) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_PURCHASES_LIST_JSON_NULL_TIMESTAMP); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); pay::PurchaseSet single_purchase_list{{"com.example.fake", 0}}; EXPECT_CALL(*this, purchases_callback(single_purchase_list)).Times(1); package->get_purchases([this](pay::PurchaseSet purchases) { purchases_callback(purchases); }); response->replyFinished(); } TEST_F(PayTest, testGetPurchasesTimestampIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_PURCHASES_LIST_JSON); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); const time_t EIGHTYTHREE_SECONDS_INTO_THE_SEVENTIES=83; pay::PurchaseSet single_purchase_list{{"com.example.fake", EIGHTYTHREE_SECONDS_INTO_THE_SEVENTIES}}; EXPECT_CALL(*this, purchases_callback(single_purchase_list)).Times(1); package->get_purchases([this](pay::PurchaseSet purchases) { purchases_callback(purchases); }); response->replyFinished(); } TEST_F(PayTest, testGetPurchasesIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); auto get_purchases_op = package->get_purchases([](pay::PurchaseSet) {}); EXPECT_CALL(reply.instance, abort()).Times(1); get_purchases_op.cancel(); } TEST_F(PayTest, testGetBaseUrl) { const char *value = getenv(pay::BASE_URL_ENVVAR); if (value != NULL) { ASSERT_TRUE(unsetenv(pay::BASE_URL_ENVVAR) == 0); } ASSERT_TRUE(pay::Package::get_base_url() == pay::BASE_URL); } TEST_F(PayTest, testGetBaseUrlFromEnv) { ASSERT_TRUE(setenv(pay::BASE_URL_ENVVAR, FAKE_SERVER.c_str(), 1) == 0); ASSERT_TRUE(pay::Package::get_base_url() == FAKE_SERVER); ASSERT_TRUE(unsetenv(pay::BASE_URL_ENVVAR) == 0); } ./libclickscope/tests/test_bootstrap.cpp0000644000015600001650000002017012676763577020657 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include "fake_json.h" #include #include #include #include #include #include using namespace ::testing; namespace { class BootstrapTest: public ::testing::Test { protected: QSharedPointer clientPtr; QSharedPointer namPtr; std::shared_ptr indexPtr; virtual void SetUp() { namPtr.reset(new MockNetworkAccessManager()); clientPtr.reset(new NiceMock(namPtr)); indexPtr.reset(new click::Index(clientPtr)); } public: MOCK_METHOD4(bootstrap_callback, void(const click::DepartmentList&, const click::HighlightList&, click::Index::Error, int)); }; } TEST_F(BootstrapTest, testBootstrapCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(EndsWith(click::BOOTSTRAP_PATH), "GET", _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->bootstrap([](const click::DepartmentList&, const click::HighlightList&, click::Index::Error, int) {}); } TEST_F(BootstrapTest, testBootstrapJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_BOOTSTRAP.c_str()); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(EndsWith(click::BOOTSTRAP_PATH), "GET", _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, bootstrap_callback(_, _, _, _)).Times(1); indexPtr->bootstrap([this](const click::DepartmentList& depts, const click::HighlightList& highlights, click::Index::Error error, int error_code) { bootstrap_callback(depts, highlights, error, error_code); { EXPECT_EQ(3u, highlights.size()); auto it = highlights.begin(); EXPECT_EQ("top-apps", it->slug()); EXPECT_EQ("Top Apps", it->name()); EXPECT_EQ(2u, it->packages().size()); ++it; EXPECT_EQ("most-purchased", it->slug()); EXPECT_EQ("Most Purchased", it->name()); EXPECT_EQ(2u, it->packages().size()); ++it; EXPECT_EQ("new-releases", it->slug()); EXPECT_EQ("New Releases", it->name()); EXPECT_EQ(2u, it->packages().size()); } { EXPECT_EQ(1u, depts.size()); auto it = depts.begin(); EXPECT_EQ("Fake Subdepartment", (*it)->name()); EXPECT_FALSE((*it)->has_children_flag()); EXPECT_EQ("https://search.apps.staging.ubuntu.com/api/v1/departments/fake-subdepartment", (*it)->href()); } }); response->replyFinished(); } TEST_F(BootstrapTest, testParsing) { Json::Reader reader; Json::Value root; EXPECT_TRUE(reader.parse(FAKE_JSON_BOOTSTRAP, root)); { auto highlights = click::Highlight::from_json_root_node(root); EXPECT_EQ(3u, highlights.size()); auto it = highlights.begin(); EXPECT_EQ("Top Apps", it->name()); EXPECT_EQ(2u, it->packages().size()); ++it; EXPECT_EQ("Most Purchased", it->name()); EXPECT_EQ(2u, it->packages().size()); ++it; EXPECT_EQ("New Releases", it->name()); EXPECT_EQ(2u, it->packages().size()); } { auto depts = click::Department::from_json_root_node(root); EXPECT_EQ(1u, depts.size()); auto it = depts.begin(); EXPECT_EQ("Fake Subdepartment", (*it)->name()); EXPECT_FALSE((*it)->has_children_flag()); EXPECT_EQ("https://search.apps.staging.ubuntu.com/api/v1/departments/fake-subdepartment", (*it)->href()); } } TEST_F(BootstrapTest, testParsingErrors) { Json::Reader reader; Json::Value root; EXPECT_TRUE(reader.parse(FAKE_JSON_BROKEN_BOOTSTRAP, root)); { auto highlights = click::Highlight::from_json_root_node(root); EXPECT_EQ(1u, highlights.size()); auto it = highlights.begin(); EXPECT_EQ("Top Apps", it->name()); EXPECT_EQ(1u, it->packages().size()); } { auto depts = click::Department::from_json_root_node(root); EXPECT_EQ(0u, depts.size()); } } TEST_F(BootstrapTest, testDepartmentAllApps) { Json::Reader reader; Json::Value root; EXPECT_TRUE(reader.parse(FAKE_JSON_DEPARTMENT_WITH_APPS, root)); { auto highlights = click::Highlight::from_json_root_node(root); EXPECT_EQ(5u, highlights.size()); auto it = highlights.begin(); EXPECT_EQ("Editor's Pick", it->name()); EXPECT_EQ(1u, it->packages().size()); EXPECT_EQ(false, it->contains_scopes()); ++it; EXPECT_EQ("Top Apps", it->name()); EXPECT_EQ(2u, it->packages().size()); EXPECT_EQ(false, it->contains_scopes()); ++it; EXPECT_EQ("Most Purchased", it->name()); EXPECT_EQ(2u, it->packages().size()); EXPECT_EQ(false, it->contains_scopes()); ++it; EXPECT_EQ("Scopes", it->name()); EXPECT_EQ(2u, it->packages().size()); EXPECT_EQ(true, it->contains_scopes()); ++it; EXPECT_EQ("Apps", it->name()); EXPECT_EQ(2u, it->packages().size()); EXPECT_EQ(false, it->contains_scopes()); } } TEST_F(BootstrapTest, testStoreHomeAppOfTheWeek) { Json::Reader reader; Json::Value root; EXPECT_TRUE(reader.parse(FAKE_JSON_STORE_HOME, root)); { auto highlights = click::Highlight::from_json_root_node(root); EXPECT_EQ(5u, highlights.size()); auto it = highlights.begin(); EXPECT_EQ("App of the Week", it->name()); EXPECT_EQ(1u, it->packages().size()); EXPECT_EQ(false, it->contains_scopes()); ++it; EXPECT_EQ("Top Apps", it->name()); EXPECT_EQ(2u, it->packages().size()); EXPECT_EQ(false, it->contains_scopes()); ++it; EXPECT_EQ("Most Purchased", it->name()); EXPECT_EQ(2u, it->packages().size()); EXPECT_EQ(false, it->contains_scopes()); ++it; EXPECT_EQ("Scopes", it->name()); EXPECT_EQ(2u, it->packages().size()); EXPECT_EQ(true, it->contains_scopes()); ++it; EXPECT_EQ("Apps", it->name()); EXPECT_EQ(2u, it->packages().size()); EXPECT_EQ(false, it->contains_scopes()); } } ./libclickscope/tests/integration/0000755000015600001650000000000012676763577017422 5ustar jenkinsjenkins./libclickscope/tests/integration/webclient_integration.cpp0000644000015600001650000000767312676763577024522 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include namespace { struct IntegrationTest : public ::testing::Test { IntegrationTest() : app(argc, argv) { QObject::connect( &testTimeout, &QTimer::timeout, [this]() { Quit(); FAIL() << "Operation timed out."; }); } void SetUp() { const int tenSeconds = 10 * 1000; testTimeout.start(tenSeconds); } void TearDown() { testTimeout.stop(); } void Quit() { app.quit(); } int argc = 0; char** argv = nullptr; QCoreApplication app; QTimer testTimeout; }; } TEST_F(IntegrationTest, queryForArmhfPackagesReturnsCorrectResults) { click::web::Client ws(QSharedPointer( new click::network::AccessManager())); click::web::CallParams params; params.add("q", "qr,architecture:armhf"); auto wr = ws.call(click::SEARCH_BASE_URL + click::SEARCH_PATH, params); QString content; QObject::connect( wr.data(), &click::web::Response::finished, [&, this](QString found_content) { content = found_content; Quit(); }); app.exec(); EXPECT_TRUE(content.size() > 0); } TEST_F(IntegrationTest, queryForArmhfPackagesCanBeParsed) { QSharedPointer namPtr( new click::network::AccessManager()); QSharedPointer clientPtr( new click::web::Client(namPtr)); click::Index index(clientPtr); click::Packages packages; index.search("qr", "", [&, this](click::Packages found_packages, click::Packages){ packages = found_packages; Quit(); }); app.exec(); EXPECT_TRUE(packages.size() > 0); } TEST_F(IntegrationTest, detailsCanBeParsed) { const std::string sample_name("com.ubuntu.developer.alecu.qr-code"); QSharedPointer namPtr( new click::network::AccessManager()); QSharedPointer clientPtr( new click::web::Client(namPtr)); click::Index index(clientPtr); index.get_details(sample_name, [&](click::PackageDetails details, click::Index::Error){ EXPECT_EQ(details.package.name, sample_name); Quit(); }); app.exec(); } ./libclickscope/tests/integration/departmentsdb_integration.cpp0000644000015600001650000001065312676763577025372 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include using namespace click; class DepartmentsDbConcurrencyTest: public ::testing::Test { public: const std::string db_path = TEST_DIR "/departments-db-test2.sqlite"; void TearDown() override { unlink(db_path.c_str()); } }; // Keep the numbers below at a reasonable level; it takes around 20 seconds // to run this test on a i7-2620M with the default values. const int NUM_OF_WRITE_OPS = 50; const int NUM_OF_READ_OPS = 100; void populate_departments(DepartmentsDb *db, int repeat_count) { click::DepartmentList depts; // generate departments with one subdepartment for (int i = 0; i < 20; i++) { auto const id = std::to_string(i); auto parent = std::make_shared(id, "Department " + id, "href", true); parent->set_subdepartments({std::make_shared(id + "sub", "Subdepartment of " + id, "href", false)}); depts.push_back(parent); } for (int i = 0; i < repeat_count; i++) { ASSERT_NO_THROW(db->store_departments(depts, "")); // generate apps for (int j = 0; j < 50; j++) { auto const id = std::to_string(j); ASSERT_NO_THROW(db->store_package_mapping("app" + id, id)); } } } TEST_F(DepartmentsDbConcurrencyTest, ConcurrentReadWrite) { // populate the db initially to make sure reader doesn't fail if it's faster than writer { DepartmentsDb db(db_path, true); populate_departments(&db, 1); } pid_t writer_pid = fork(); if (writer_pid < 0) { FAIL(); } else if (writer_pid == 0) // writer child process { SCOPED_TRACE("writer"); std::unique_ptr db; ASSERT_NO_THROW(db.reset(new DepartmentsDb(db_path, true))); populate_departments(db.get(), NUM_OF_WRITE_OPS); exit(0); } else // parent process { pid_t reader_pid = fork(); if (reader_pid < 0) { FAIL(); } else if (reader_pid == 0) // reader child process { SCOPED_TRACE("reader"); std::unique_ptr db; ASSERT_NO_THROW(db.reset(new DepartmentsDb(db_path, false))); for (int i = 0; i < NUM_OF_READ_OPS; i++) { ASSERT_NO_THROW(db->get_department_name("1", {""})); ASSERT_NO_THROW(db->get_parent_department_id("1")); ASSERT_NO_THROW(db->get_packages_for_department("1", false)); ASSERT_NO_THROW(db->get_packages_for_department("1", true)); ASSERT_NO_THROW(db->get_children_departments("")); ASSERT_NO_THROW(db->department_name_count()); ASSERT_NO_THROW(db->department_mapping_count()); ASSERT_NO_THROW(db->package_count()); } exit(0); } else // parent process { wait(nullptr); wait(nullptr); } } } ./libclickscope/tests/integration/CMakeLists.txt0000644000015600001650000000127312676763577022165 0ustar jenkinsjenkinsSET (INTEGRATION_TARGET click_scope_integration_tests) # Qt5 bits SET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package(Qt5Core REQUIRED) include_directories (${CMAKE_SOURCE_DIR}/scope) FILE (GLOB TEST_SOURCES *.cpp) FILE (GLOB TEST_HEADERS *.h) add_executable (${INTEGRATION_TARGET} departmentsdb_integration.cpp webclient_integration.cpp ) qt5_use_modules(${INTEGRATION_TARGET} Core DBus Network Test) target_link_libraries (${INTEGRATION_TARGET} ${STORE_LIB_UNVERSIONED} gmock gmock_main ${CMAKE_THREAD_LIBS_INIT} ) add_custom_target (test-integration-click-scope COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${INTEGRATION_TARGET} DEPENDS ${INTEGRATION_TARGET} ) ./libclickscope/tests/test_interface.cpp0000644000015600001650000007705012676763577020613 0ustar jenkinsjenkins/* * Copyright (C) 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "fake_json.h" #include "test_data.h" #include #include #include #include #include #include #include #include #include #include using namespace click; using namespace ::testing; namespace { // TODO: Get rid of file-based testing and instead make unity::util::IniParser mockable // Maintaining this list here will become tedious over time. static const std::vector non_desktop_applications = { {"com.ubuntu.stock-ticker-mobile", "Stock Ticker", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png", "application:///com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktop", "An awesome Stock Ticker application with all the features you could imagine", "", ""}, {"", "Weather", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.weather/./weather64.png", "application:///com.ubuntu.weather_weather_1.0.168.desktop", "", "", ""}, {"com.ubuntu.developer.webapps.webapp-twitter", "Twitter", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-twitter/./twitter.png", "application:///com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5.desktop", "", "", ""}, {"com.ubuntu.music", "Music", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.music/images/music.png", "application:///com.ubuntu.music_music_1.1.329.desktop", "Ubuntu Touch Music Player", "", ""}, {"com.ubuntu.clock", "Clock", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock/./clock64.png", "application:///com.ubuntu.clock_clock_1.0.300.desktop", "", "", ""}, {"com.ubuntu.dropping-letters", "Dropping Letters", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.dropping-letters/dropping-letters.png", "application:///com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43.desktop", "", "", ""}, {"com.ubuntu.developer.webapps.webapp-gmail", "Gmail", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-gmail/./gmail.png", "application:///com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.8.desktop", "", "", ""}, {"com.ubuntu.terminal", "Terminal", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.terminal/./terminal64.png", "application:///com.ubuntu.terminal_terminal_0.5.29.desktop", "", "", ""}, {"com.ubuntu.calendar", "Calendar", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calendar/./calendar64.png", "application:///com.ubuntu.calendar_calendar_0.4.182.desktop", "", "", ""}, {"com.ubuntu.notes", "Notes", 0.0, "image://theme/notepad", "application:///com.ubuntu.notes_notes_1.4.242.desktop", "", "", ""}, {"com.ubuntu.developer.webapps.webapp-amazon", "Amazon", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon/./amazon.png", "application:///com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.6.desktop", "", "", ""}, {"com.ubuntu.shorts", "Shorts", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.shorts/./rssreader64.png", "application:///com.ubuntu.shorts_shorts_0.2.162.desktop", "", "", ""}, {"com.ubuntu.filemanager", "File Manager", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.filemanager/./filemanager64.png", "application:///com.ubuntu.filemanager_filemanager_0.1.1.97.desktop", "", "", ""}, {"com.ubuntu.calculator", "Calculator", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calculator/./calculator64.png", "application:///com.ubuntu.calculator_calculator_0.1.3.206.desktop", "", "", ""}, {"com.ubuntu.sudoku", "Sudoku", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.sudoku/SudokuGameIcon.png", "application:///com.ubuntu.sudoku_sudoku_1.0.142.desktop", "Sudoku Game for Ubuntu Touch", "", ""}, {"com.ubuntu.developer.webapps.webapp-ebay", "eBay", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ebay/./ebay.png", "application:///com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8.desktop", "", "", ""}, {"com.ubuntu.developer.webapps.webapp-facebook", "Facebook", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-facebook/./facebook.png", "application:///com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook_1.0.5.desktop", "", "", ""}, {"", "Messaging", 0.0, "image://theme/messages-app", "application:///messaging-app.desktop", "Messaging application", "/usr/share/messaging-app/assets/messaging-app-screenshot.png", ""}, {"", "Contacts", 0.0, "image://theme/contacts-app", "application:///address-book-app.desktop", "", "", ""} }; static click::Application desktop_application { "", "Sample Desktop-only non-click app", 0.0, "image://theme/sample-desktop-app", "application:///non-click-app-without-exception.desktop", "multiline description goes here", "", "" }; } namespace { const std::string emptyQuery{}; struct MockKeyFileLocator : public click::KeyFileLocator { typedef click::KeyFileLocator Super; MockKeyFileLocator() { using namespace ::testing; ON_CALL(*this, enumerateKeyFilesForInstalledApplications(_)) .WillByDefault( Invoke( this, &MockKeyFileLocator::doEnumerateKeyFilesForInstalledApplications)); } MOCK_METHOD1(enumerateKeyFilesForInstalledApplications, void(const Super::Enumerator&)); void doEnumerateKeyFilesForInstalledApplications(const Super::Enumerator& enumerator) { Super::enumerateKeyFilesForInstalledApplications(enumerator); } }; class ClickInterfaceTest : public ::testing::Test { public: MOCK_METHOD2(manifest_callback, void(Manifest, InterfaceError)); MOCK_METHOD2(manifests_callback, void(ManifestList, InterfaceError)); MOCK_METHOD2(installed_callback, void(PackageSet, InterfaceError)); std::vector ignoredApps; }; } class FakeClickInterface : public click::Interface { public: FakeClickInterface(const QSharedPointer& keyFileLocator) : Interface(keyFileLocator) {} FakeClickInterface() {} MOCK_METHOD0(show_desktop_apps, bool()); MOCK_METHOD2(run_process, void(const std::string&, std::function)); }; TEST(ClickInterface, testIsNonClickAppFalse) { EXPECT_FALSE(Interface::is_non_click_app("unknown-app.desktop")); } TEST(ClickInterface, testIsNonClickAppNoRegression) { // Loop through and check that all filenames are non-click filenames // If this ever breaks, something is very very wrong. for (const auto& element : nonClickDesktopFiles()) { QString filename = element.c_str(); EXPECT_TRUE(Interface::is_non_click_app(filename)); } } TEST(ClickInterface, testCallsIntoKeyFileLocatorForFindingInstalledApps) { using namespace ::testing; MockKeyFileLocator mockKeyFileLocator; QSharedPointer keyFileLocator( &mockKeyFileLocator, [](click::KeyFileLocator*){}); FakeClickInterface iface(keyFileLocator); EXPECT_CALL(iface, show_desktop_apps()) .Times(1) .WillOnce(Return(false)); EXPECT_CALL(mockKeyFileLocator, enumerateKeyFilesForInstalledApplications(_)).Times(1); iface.find_installed_apps(emptyQuery); } TEST(ClickInterface, testFindAppsInDirEmpty) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto results = iface.find_installed_apps("xyzzygy"); EXPECT_TRUE(results.empty()); } TEST_F(ClickInterfaceTest, testFindAppsInDirIgnoredApps) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); ignoredApps.push_back("messaging-app.desktop"); ignoredApps.push_back("com.ubuntu.calculator"); auto results = iface.find_installed_apps("", ignoredApps); EXPECT_EQ(20, results.size()); } // // test that application with a default department id key in the desktop // file is returned when department matches TEST_F(ClickInterfaceTest, testFindAppsWithAppWithDefaultDepartmentId) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto depts_db = std::make_shared(":memory:"); auto results = iface.find_installed_apps("", ignoredApps, "accessories", depts_db); EXPECT_EQ(1u, results.size()); EXPECT_EQ("Contacts", results.begin()->title); } TEST_F(ClickInterfaceTest, testFindAppsWithAppWithDefaultDepartmentIdOverriden) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto depts_db = std::make_shared(":memory:"); depts_db->store_department_name("utilities", "", "Utilities"); depts_db->store_department_name("accessories", "", "Accessories"); depts_db->store_department_mapping("utilities", ""); depts_db->store_department_mapping("accessories", ""); auto results = iface.find_installed_apps("", ignoredApps, "utilies", depts_db); EXPECT_EQ(0, results.size()); // address book applicaton moved to utilities depts_db->store_package_mapping("address-book-app.desktop", "utilities"); results = iface.find_installed_apps("", ignoredApps, "utilities", depts_db); EXPECT_EQ(1u, results.size()); EXPECT_EQ("Contacts", results.begin()->title); } TEST(ClickInterface, testFindAppsInDirSorted) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto results = iface.find_installed_apps("ock"); const std::vector expected_results = { {"com.ubuntu.clock", "Clock", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock/./clock64.png", "application:///com.ubuntu.clock_clock_1.0.300.desktop", "", "", ""}, {"com.ubuntu.stock-ticker-mobile", "Stock Ticker", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png", "application:///com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktop", "An awesome Stock Ticker application with all the features you could imagine", "", ""}, }; EXPECT_EQ(expected_results, results); } TEST(ClickInterface, testSortApps) { std::vector apps = { {"", "Sudoku", 0.0, "", "", "", "", ""}, {"", "eBay", 0.0, "", "", "", "", ""}, {"", "Facebook", 0.0, "", "", "", "", ""}, {"", "Messaging", 0.0, "", "", "", "", ""}, {"", "Contacts", 0.0, "", "", "", "", ""}, }; std::vector expected = { {"", "Contacts", 0.0, "", "", "", "", ""}, {"", "eBay", 0.0, "", "", "", "", ""}, {"", "Facebook", 0.0, "", "", "", "", ""}, {"", "Messaging", 0.0, "", "", "", "", ""}, {"", "Sudoku", 0.0, "", "", "", "", ""}, }; ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US.UTF-8", 1), 0); EXPECT_EQ(expected, click::Interface::sort_apps(apps)); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(ClickInterface, testSortAppsWithDuplicates) { std::vector apps = { {"com.sudoku.sudoku", "Sudoku", 0.0, "", "", "", "", ""}, {"com.canonical.sudoku", "Sudoku", 0.0, "", "", "", "", ""}, }; std::vector expected = { {"com.canonical.sudoku", "Sudoku", 0.0, "", "", "", "", ""}, {"com.sudoku.sudoku", "Sudoku", 0.0, "", "", "", "", ""}, }; ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US.UTF-8", 1), 0); EXPECT_EQ(expected, click::Interface::sort_apps(apps)); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(ClickInterface, testSortAppsWithAccents) { std::vector apps = { {"", "Robots", 0.0, "", "", "", "", ""}, {"", "Æon", 0.0, "", "", "", "", ""}, {"", "Contacts", 0.0, "", "", "", "", ""}, {"", "Über", 0.0, "", "", "", "", ""}, }; std::vector expected = { {"", "Æon", 0.0, "", "", "", "", ""}, {"", "Contacts", 0.0, "", "", "", "", ""}, {"", "Robots", 0.0, "", "", "", "", ""}, {"", "Über", 0.0, "", "", "", "", ""}, }; ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US.UTF-8", 1), 0); EXPECT_EQ(expected, click::Interface::sort_apps(apps)); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(ClickInterface, testSortAppsMixedCharsets) { std::vector apps = { {"", "Robots", 0.0, "", "", "", "", ""}, {"", "汉字", 0.0, "", "", "", "", ""}, {"", "漢字", 0.0, "", "", "", "", ""}, {"", "Über", 0.0, "", "", "", "", ""}, }; std::vector expected = { {"", "汉字", 0.0, "", "", "", "", ""}, {"", "漢字", 0.0, "", "", "", "", ""}, {"", "Robots", 0.0, "", "", "", "", ""}, {"", "Über", 0.0, "", "", "", "", ""}, }; ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "zh_CN.UTF-8", 1), 0); EXPECT_EQ(expected, click::Interface::sort_apps(apps)); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(ClickInterface, testFindAppByKeyword) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto results = iface.find_installed_apps("rss"); EXPECT_EQ(1, results.size()); } TEST(ClickInterface, testFindAppByKeywordCaseInsensitive) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto results = iface.find_installed_apps("RsS"); EXPECT_EQ(1, results.size()); } TEST(ClickInterface, testFindAppAccented) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto results = iface.find_installed_apps("Cámara"); EXPECT_EQ(1, results.size()); } TEST(ClickInterface, testFindAppAccented2) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto results = iface.find_installed_apps("Camara"); EXPECT_EQ(1, results.size()); } TEST(ClickInterface, testIsIconIdentifier) { EXPECT_TRUE(Interface::is_icon_identifier("contacts-app")); EXPECT_FALSE(Interface::is_icon_identifier( "/usr/share/unity8/graphics/applicationIcons/contacts-app@18.png")); } TEST(ClickInterface, testAddThemeScheme) { EXPECT_EQ("image://theme/contacts-app", Interface::add_theme_scheme("contacts-app")); EXPECT_EQ("/usr/share/unity8/graphics/applicationIcons/contacts-app@18.png", Interface::add_theme_scheme("/usr/share/unity8/graphics/applicationIcons/contacts-app@18.png")); } std::vector find_installed_apps(const std::string& query, bool include_desktop_results) { using namespace ::testing; QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); FakeClickInterface iface(keyFileLocator); EXPECT_CALL(iface, show_desktop_apps()) .Times(1) .WillOnce(Return(include_desktop_results)); return iface.find_installed_apps(query); } TEST(ClickInterface, testFindInstalledAppsOnPhone) { auto result = find_installed_apps(emptyQuery, false); EXPECT_TRUE(result.size() > 0); for (const auto& app : non_desktop_applications) { qDebug() << "comparing" << QString::fromStdString(app.title); EXPECT_NE(result.end(), std::find(result.begin(), result.end(), app)); } EXPECT_EQ(result.end(), std::find(result.begin(), result.end(), desktop_application)); } TEST(ClickInterface, testFindInstalledAppsOnDesktop) { auto result = find_installed_apps(emptyQuery, true); std::vector expected_apps(non_desktop_applications); expected_apps.push_back(desktop_application); EXPECT_TRUE(result.size() > 0); for (const auto& app : expected_apps) { qDebug() << "comparing" << QString::fromStdString(app.title); EXPECT_NE(result.end(), std::find(result.begin(), result.end(), app)); } } TEST(ClickInterface, testInlineTranslationsLoaded) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::systemApplicationsDirectoryForTesting() + "/translated.desktop"; unity::util::IniParser parser(nodisplay.data()); ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "es_ES.UTF-8", 1), 0); auto app = iface.load_app_from_desktop(parser, nodisplay); EXPECT_EQ("Translated App in Spanish", app.title); EXPECT_EQ("Translated application in Spanish", app.description); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(ClickInterface, testIncludeInResultsNoDisplayTrue) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::userApplicationsDirectoryForTesting() + "/non-click-app-nodisplay.desktop"; unity::util::IniParser parser(nodisplay.data()); EXPECT_FALSE(iface.is_visible_app(parser)); } TEST(ClickInterface, testIncludeInResultsOnlyShowInGnome) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::userApplicationsDirectoryForTesting() + "/non-click-app-onlyshowin-gnome.desktop"; unity::util::IniParser parser(nodisplay.data()); EXPECT_FALSE(iface.is_visible_app(parser)); } TEST(ClickInterface, testIncludeInResultsOnlyShowInGnomeUnity) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::userApplicationsDirectoryForTesting() + "/non-click-app-onlyshowin-gnome-unity.desktop"; unity::util::IniParser parser(nodisplay.data()); EXPECT_TRUE(iface.is_visible_app(parser)); } TEST(ClickInterface, testIncludeInResultsOnlyShowInUnity) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::userApplicationsDirectoryForTesting() + "/non-click-app-onlyshowin-unity.desktop"; unity::util::IniParser parser(nodisplay.data()); EXPECT_TRUE(iface.is_visible_app(parser)); } TEST(ClickInterface, testEnableDesktopApps) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); setenv(Interface::ENV_SHOW_DESKTOP_APPS, "YesPlease", true); EXPECT_TRUE(iface.show_desktop_apps()); } TEST(ClickInterface, testDisableDesktopApps) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); unsetenv(Interface::ENV_SHOW_DESKTOP_APPS); EXPECT_FALSE(iface.show_desktop_apps()); } TEST(ClickInterface, testManifestFromJsonOneApp) { Manifest m = manifest_from_json(FAKE_JSON_MANIFEST_ONE_APP); ASSERT_EQ(m.first_app_name, "fake-app"); ASSERT_TRUE(m.has_any_apps()); ASSERT_FALSE(m.has_any_scopes()); } TEST(ClickInterface, testManifestFromJsonOneScope) { Manifest m = manifest_from_json(FAKE_JSON_MANIFEST_ONE_SCOPE); ASSERT_EQ(m.first_scope_id, "com.example.fake-scope_fake-scope-hook"); ASSERT_FALSE(m.has_any_apps()); ASSERT_TRUE(m.has_any_scopes()); } TEST(ClickInterface, testManifestFromJsonOneAppOneScope) { Manifest m = manifest_from_json(FAKE_JSON_MANIFEST_ONE_APP_ONE_SCOPE); ASSERT_EQ(m.first_app_name, "fake-app"); ASSERT_EQ(m.first_scope_id, "com.example.fake-1app-1scope_fake-scope-hook"); ASSERT_TRUE(m.has_any_apps()); ASSERT_TRUE(m.has_any_scopes()); } TEST(ClickInterface, testManifestFromJsonTwoAppsTwoScopes) { Manifest m = manifest_from_json(FAKE_JSON_MANIFEST_TWO_APPS_TWO_SCOPES); ASSERT_EQ(m.first_app_name, "fake-app1"); ASSERT_EQ(m.first_scope_id, "com.example.fake-2apps-2scopes_fake-scope-hook1"); ASSERT_TRUE(m.has_any_apps()); ASSERT_TRUE(m.has_any_scopes()); } TEST(ClickInterface, testGetManifestForAppCorrectCommand) { FakeClickInterface iface; std::string command = "click info " + FAKE_PACKAGENAME; EXPECT_CALL(iface, run_process(command, _)). Times(1); iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest, InterfaceError){}); } TEST_F(ClickInterfaceTest, testGetManifestForAppParseError) { FakeClickInterface iface; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(0, "INVALID JSON", ""); })); EXPECT_CALL(*this, manifest_callback(_, InterfaceError::ParseError)); iface.get_manifest_for_app(FAKE_PACKAGENAME, [this](Manifest manifest, InterfaceError error){ manifest_callback(manifest, error); }); } TEST_F(ClickInterfaceTest, testGetManifestForAppCommandFailed) { FakeClickInterface iface; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(-1, "", "CRITICAL: FAIL"); })); EXPECT_CALL(*this, manifest_callback(_, InterfaceError::CallError)); iface.get_manifest_for_app(FAKE_PACKAGENAME, [this](Manifest manifest, InterfaceError error){ manifest_callback(manifest, error); }); } TEST_F(ClickInterfaceTest, testGetManifestForAppIsRemovable) { FakeClickInterface iface; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(0, FAKE_JSON_MANIFEST_REMOVABLE, ""); })); iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest manifest, InterfaceError error){ ASSERT_TRUE(error == InterfaceError::NoError); ASSERT_TRUE(manifest.removable); }); } TEST_F(ClickInterfaceTest, testGetManifestForAppIsNotRemovable) { FakeClickInterface iface; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(0, FAKE_JSON_MANIFEST_NONREMOVABLE, ""); })); iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest manifest, InterfaceError error){ ASSERT_TRUE(error == InterfaceError::NoError); ASSERT_FALSE(manifest.removable); }); } TEST(ClickInterface, testGetManifestsCorrectCommand) { FakeClickInterface iface; std::string command = "click list --manifest"; EXPECT_CALL(iface, run_process(command, _)). Times(1); iface.get_manifests([](ManifestList, InterfaceError){}); } TEST_F(ClickInterfaceTest, testGetManifestsParseError) { FakeClickInterface iface; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(0, "INVALID JSON", ""); })); EXPECT_CALL(*this, manifests_callback(_, InterfaceError::ParseError)); iface.get_manifests([this](ManifestList manifests, InterfaceError error){ manifests_callback(manifests, error); }); } TEST_F(ClickInterfaceTest, testGetManifestsCommandFailed) { FakeClickInterface iface; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(-1, "", "CRITICAL: FAIL"); })); EXPECT_CALL(*this, manifests_callback(_, InterfaceError::CallError)); iface.get_manifests([this](ManifestList manifests, InterfaceError error){ manifests_callback(manifests, error); }); } TEST_F(ClickInterfaceTest, testGetManifestsParsed) { FakeClickInterface iface; std::string expected_str = "[" + FAKE_JSON_MANIFEST_NONREMOVABLE + "," + FAKE_JSON_MANIFEST_REMOVABLE + "]"; ManifestList expected = manifest_list_from_json(expected_str); EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(0, expected_str, ""); })); iface.get_manifests([expected](ManifestList manifests, InterfaceError error){ ASSERT_TRUE(error == InterfaceError::NoError); ASSERT_TRUE(manifests.size() == expected.size()); }); } TEST(ClickInterface, testGetInstalledPackagesCorrectCommand) { FakeClickInterface iface; std::string command = "click list"; EXPECT_CALL(iface, run_process(command, _)). Times(1); iface.get_installed_packages([](PackageSet, InterfaceError){}); } TEST_F(ClickInterfaceTest, testGetInstalledPackagesParseError) { FakeClickInterface iface; std::string bad_stdout = "INVALID: err\nvalid.package\t1.0\n"; PackageSet expected{{"valid.package", "1.0"}}; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(0, bad_stdout, ""); })); EXPECT_CALL(*this, installed_callback(_, InterfaceError::NoError)); iface.get_installed_packages([this, expected](PackageSet package_names, InterfaceError error){ installed_callback(package_names, error); ASSERT_EQ(package_names, expected); }); } TEST_F(ClickInterfaceTest, testGetInstalledPackagesCommandFailed) { FakeClickInterface iface; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(-1, "", "CRITICAL: FAIL"); })); EXPECT_CALL(*this, installed_callback(_, InterfaceError::CallError)); iface.get_installed_packages([this](PackageSet package_names, InterfaceError error){ installed_callback(package_names, error); }); } TEST_F(ClickInterfaceTest, testGetInstalledPackagesParsed) { FakeClickInterface iface; std::string sample_stdout = "ABC\t0.1\nDEF\t0.2\n"; PackageSet expected{{"ABC", "0.1"}, {"DEF", "0.2"}}; EXPECT_CALL(iface, run_process(_, _)). Times(1). WillOnce(Invoke([&](const std::string&, std::function callback){ callback(0, sample_stdout, ""); })); iface.get_installed_packages([expected](PackageSet package_names, InterfaceError error){ ASSERT_EQ(error, InterfaceError::NoError); ASSERT_EQ(package_names, expected); }); } ./libclickscope/tests/applications/0000755000015600001650000000000012676763577017565 5ustar jenkinsjenkins./libclickscope/tests/applications/system/0000755000015600001650000000000012676763577021111 5ustar jenkinsjenkins./libclickscope/tests/applications/system/translated.desktop0000644000015600001650000000036412676763577024650 0ustar jenkinsjenkins[Desktop Entry] Type=Application Name=Translated App Name[en]=Translated App Name[es]=Translated App in Spanish Comment=Translated application Comment[en]=Translated application Comment[es]=Translated application in Spanish X-Ubuntu-Touch=true ./libclickscope/tests/applications/system/address-book-app.desktop0000644000015600001650000000060512676763577025640 0ustar jenkinsjenkins[Desktop Entry] Encoding=UTF-8 Version=1.0 Terminal=false Type=Application Name=Contacts GenericName=Contacts Exec=/usr/bin/address-book-app %u Icon=contacts-app X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=address-book-app X-Ubuntu-Single-Instance=true X-Ubuntu-Default-Department-ID=accessories # this one has no screenshot nor comment, to test how it breaks ./libclickscope/tests/applications/system/messaging-app.desktop0000644000015600001650000000062212676763577025237 0ustar jenkinsjenkins[Desktop Entry] Type=Application Name=Messaging GenericName=Messaging Comment=Messaging application Exec=messaging-app %u Terminal=false Icon=messages-app MimeType=x-scheme-handler/contact;x-scheme-handler/call X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=messaging-app X-Ubuntu-Single-Instance=true X-Screenshot=/usr/share/messaging-app/assets/messaging-app-screenshot.png ./libclickscope/tests/applications/user/0000755000015600001650000000000012676763577020543 5ustar jenkinsjenkins./libclickscope/tests/applications/user/com.ubuntu.sudoku_sudoku_1.0.142.desktop0000644000015600001650000000103212676763577030017 0ustar jenkinsjenkins[Desktop Entry] Name=Sudoku Version=1.0 Comment=Sudoku Game for Ubuntu Touch Exec=aa-exec-click -p com.ubuntu.sudoku_sudoku_1.0.142 -- qmlscene sudoku-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.sudoku/SudokuGameIcon.png Terminal=false Type=Application X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.sudoku Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.sudoku X-Ubuntu-Old-Icon=SudokuGameIcon.png X-Ubuntu-Application-ID=com.ubuntu.sudoku_sudoku_1.0.142 ./libclickscope/tests/applications/user/com.ubuntu.calculator_calculator_0.1.3.206.desktop0000644000015600001650000000104612676763577031624 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.calculator_calculator_0.1.3.206 -- qmlscene ./ubuntu-calculator-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calculator/./calculator64.png Name=Calculator X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.calculator Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calculator X-Ubuntu-Old-Icon=./calculator64.png X-Ubuntu-Application-ID=com.ubuntu.calculator_calculator_0.1.3.206 ././@LongLink0000644000000000000000000000015700000000000011606 Lustar rootroot./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.6.desktop./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.00000644000015600001650000000120412676763577034145 0ustar jenkinsjenkins[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.6 -- webbrowser-app --enable-back-forward --webapp --webappUrlPatterns=https?://www.amazon.com/*,https?://s.amazon-adsystem.com/* http://www.amazon.com/gp/aw Name=Amazon Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon/./amazon.png X-Ubuntu-Touch=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon X-Ubuntu-Old-Icon=./amazon.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.6 ./libclickscope/tests/applications/user/non-click-app-nodisplay.desktop0000644000015600001650000000040412676763577026567 0ustar jenkinsjenkins[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false MimeType=x-scheme-handler/contact;x-scheme-handler/call NoDisplay=true ./libclickscope/tests/applications/user/non-click-app-onlyshowin-gnome-unity.desktop0000644000015600001650000000050612676763577031252 0ustar jenkinsjenkins[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false MimeType=x-scheme-handler/contact;x-scheme-handler/call OnlyShowIn=GNOME;Unity; Name[en_US]=non-click-app-onlyshowin-gnome-unity.desktop ././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootroot./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.8.desktop./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.80000644000015600001650000000116012676763577033724 0ustar jenkinsjenkins[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.8 -- webbrowser-app --enable-back-forward --webappModelSearchPath=. --webapp='R01haWwNCg==' https://mail.google.com/ Name=Gmail Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-gmail/./gmail.png X-Ubuntu-Touch=true X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-gmail X-Ubuntu-Old-Icon=./gmail.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.8 ./libclickscope/tests/applications/user/non-click-app-without-exception.desktop0000644000015600001650000000041512676763577030266 0ustar jenkinsjenkins[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false Icon=sample-desktop-app MimeType=x-scheme-handler/contact;x-scheme-handler/call ./libclickscope/tests/applications/user/broken.desktop0000644000015600001650000000014412676763577023415 0ustar jenkinsjenkins./libclickscope/tests/applications/user/com.ubuntu.calendar_calendar_0.4.182.desktop0000644000015600001650000000100412676763577030523 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.calendar_calendar_0.4.182 -- qmlscene %u ./calendar.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calendar/./calendar64.png Name=Calendar X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.calendar Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calendar X-Ubuntu-Old-Icon=./calendar64.png X-Ubuntu-Application-ID=com.ubuntu.calendar_calendar_0.4.182 ./libclickscope/tests/applications/user/badd-appid.desktop0000644000015600001650000000034312676763577024123 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=processTest Icon=/home/phablet/animatedDemos/football-ball.png Path=/home/phablet/processTest Name=Football X-Ubuntu-Touch=true X-Ubuntu-Application-ID=football ./libclickscope/tests/applications/user/com.ubuntu.shorts_shorts_0.2.162.desktop0000644000015600001650000000075112676763577030051 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.shorts_shorts_0.2.162 -- qmlscene ./rssreader-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.shorts/./rssreader64.png Name=Shorts Keywords=shorts;rss;reader X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.shorts X-Ubuntu-Old-Icon=./rssreader64.png X-Ubuntu-Application-ID=com.ubuntu.shorts_shorts_0.2.162 ./libclickscope/tests/applications/user/com.ubuntu.filemanager_filemanager_0.1.1.97.desktop0000644000015600001650000000101612676763577032011 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.filemanager_filemanager_0.1.1.97 -- qmlscene ./ubuntu-filemanager-app.qml -I ./plugins Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.filemanager/./filemanager64.png Name=File Manager X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.filemanager X-Ubuntu-Old-Icon=./filemanager64.png X-Ubuntu-Application-ID=com.ubuntu.filemanager_filemanager_0.1.1.97 ./libclickscope/tests/applications/user/com.ubuntu.music_music_1.1.329.desktop0000644000015600001650000000101612676763577027445 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Name=Music Comment=Ubuntu Touch Music Player Exec=aa-exec-click -p com.ubuntu.music_music_1.1.329 -- qmlscene ./music-app.qml --file=%f -I ./plugins Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.music/images/music.png Terminal=false Type=Application StartupNotify=true X-Ubuntu-Single-Instance=true X-Ubuntu-Touch=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.music X-Ubuntu-Old-Icon=images/music.png X-Ubuntu-Application-ID=com.ubuntu.music_music_1.1.329 ./libclickscope/tests/applications/user/non-click-app-onlyshowin-unity.desktop0000644000015600001650000000047212676763577030151 0ustar jenkinsjenkins[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false MimeType=x-scheme-handler/contact;x-scheme-handler/call OnlyShowIn=Unity; Name[en_US]=non-click-app-onlyshowin-unity.desktop ././@LongLink0000644000000000000000000000016100000000000011601 Lustar rootroot./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5.desktop./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_10000644000015600001650000000131712676763577034446 0ustar jenkinsjenkins[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5 -- webbrowser-app --enable-back-forward --webapp --webappUrlPatterns=https?://mobile.twitter.com/* https://mobile.twitter.com/session/new?bypass_interstitial=true Name=Twitter Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-twitter/./twitter.png X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-twitter X-Ubuntu-Old-Icon=./twitter.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5 ./libclickscope/tests/applications/user/com.ubuntu.weather_weather_1.0.168.desktop0000644000015600001650000000105012676763577030301 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.weather_weather_1.0.168 -- qmlscene ./ubuntu-weather-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.weather/./weather64.png Name=Weather X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.weather # Fake an app installed from a .deb #Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.weather #X-Ubuntu-Old-Icon=./weather64.png #X-Ubuntu-Application-ID=com.ubuntu.weather_weather_1.0.168 ./libclickscope/tests/applications/user/com.ubuntu.terminal_terminal_0.5.29.desktop0000644000015600001650000000075312676763577030562 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.terminal_terminal_0.5.29 -- qmlscene ./ubuntu-terminal-app.qml -I ./plugins Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.terminal/./terminal64.png Name=Terminal X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.terminal X-Ubuntu-Old-Icon=./terminal64.png X-Ubuntu-Application-ID=com.ubuntu.terminal_terminal_0.5.29 ./libclickscope/tests/applications/user/non-click-app-onlyshowin-gnome.desktop0000644000015600001650000000047212676763577030106 0ustar jenkinsjenkins[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false MimeType=x-scheme-handler/contact;x-scheme-handler/call OnlyShowIn=GNOME; Name[en_US]=non-click-app-onlyshowin-gnome.desktop ././@LongLink0000644000000000000000000000016300000000000011603 Lustar rootroot./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook_1.0.5.desktop./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook0000644000015600001650000000125412676763577034344 0ustar jenkinsjenkins[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook_1.0.5 -- webbrowser-app --enable-back-forward --webapp --webappUrlPatterns=https?://m.facebook.com/* https://m.facebook.com/ Name=Facebook Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-facebook/./facebook.png X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-facebook X-Ubuntu-Old-Icon=./facebook.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook_1.0.5 ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootroot./libclickscope/tests/applications/user/com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43.desktop./libclickscope/tests/applications/user/com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43.desk0000644000015600001650000000104112676763577033474 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.dropping-letters/dropping-letters.png Terminal=false Name=Dropping Letters Exec=aa-exec-click -p com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43 -- qmlscene dropping-letters.qml X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.dropping-letters X-Ubuntu-Old-Icon=dropping-letters.png X-Ubuntu-Application-ID=com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43 ./libclickscope/tests/applications/user/com.ubuntu.notes_notes_1.4.242.desktop0000644000015600001650000000074112676763577027466 0ustar jenkinsjenkins[Desktop Entry] Type=Application Exec=aa-exec-click -p com.ubuntu.notes_notes_1.4.242 -- qmlscene $@ -I ./usr/lib/arm-linux-gnueabihf/qt5/qml NotesApp.qml Name=Notes GenericName=Notes application for Ubuntu Icon=notepad Terminal=false X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.notes X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.notes X-Ubuntu-Application-ID=com.ubuntu.notes_notes_1.4.242 ./libclickscope/tests/applications/user/com.ubuntu.clock_clock_1.0.300.desktop0000644000015600001650000000075312676763577027366 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.clock_clock_1.0.300 -- qmlscene ./ubuntu-clock-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock/./clock64.png Name=Clock X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.clock Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock X-Ubuntu-Old-Icon=./clock64.png X-Ubuntu-Application-ID=com.ubuntu.clock_clock_1.0.300 ./libclickscope/tests/applications/user/semi-broken.desktop0000644000015600001650000000002112676763577024342 0ustar jenkinsjenkinssemi broken file ././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootroot./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8.desktop./libclickscope/tests/applications/user/com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8.d0000644000015600001650000000114212676763577033624 0ustar jenkinsjenkins[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8 -- webbrowser-app --enable-back-forward --webappUrlPatterns=https?://*.ebay.com/* --webapp http://m.ebay.com/ Name=eBay Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ebay/./ebay.png X-Ubuntu-Touch=true X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ebay X-Ubuntu-Old-Icon=./ebay.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8 ./libclickscope/tests/applications/user/com.ubuntu.accented_accented_0.5.29.desktop0000644000015600001650000000075212676763577030447 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.accented_accented_0.5.29 -- qmlscene ./ubuntu-accented-app.qml -I ./plugins Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.accented/./accented64.png Name=Cámara X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.accented X-Ubuntu-Old-Icon=./accented64.png X-Ubuntu-Application-ID=com.ubuntu.accented_accented_0.5.29 ./libclickscope/tests/applications/user/pre-translated.desktop0000644000015600001650000000077112676763577025070 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.clock_clock_1.0.300 -- qmlscene ./ubuntu-clock-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock/./clock64.png _Name=Untranslated Clock X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.clock Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock X-Ubuntu-Old-Icon=./clock64.png X-Ubuntu-Application-ID=com.ubuntu.clock_clock_1.0.300 ././@LongLink0000644000000000000000000000015400000000000011603 Lustar rootroot./libclickscope/tests/applications/user/com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktop./libclickscope/tests/applications/user/com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.0000644000015600001650000000125612676763577033423 0ustar jenkinsjenkins[Desktop Entry] Version=1.0 Name=Stock Ticker GenericName=Stock Ticker Comment=An awesome Stock Ticker application with all the features you could imagine Exec=aa-exec-click -p com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66 -- qmlscene Stock_Ticker.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png Terminal=false Type=Application X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Categories=Utility; Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile X-Ubuntu-Old-Icon=icons/stock_icon_48.png X-Ubuntu-Application-ID=com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66 ./libclickscope/tests/test_package.cpp0000644000015600001650000000761212676763577020243 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include "fake_json.h" using namespace click; class PackageTest : public ::testing::Test { }; TEST_F(PackageTest, testPackageListFromJsonNodeSingle) { Json::Value root; Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); auto const embedded = root[Package::JsonKeys::embedded]; auto const ci_package = embedded[Package::JsonKeys::ci_package]; Packages pl = package_list_from_json_node(ci_package); ASSERT_EQ(1, pl.size()); } TEST_F(PackageTest, testPackageListFromJsonNodeSingleScope) { Json::Value root; Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE_SCOPE, root); auto const embedded = root[Package::JsonKeys::embedded]; auto const ci_package = embedded[Package::JsonKeys::ci_package]; Packages pl = package_list_from_json_node(ci_package); ASSERT_EQ(1, pl.size()); EXPECT_EQ("scope", pl[0].content); } TEST_F(PackageTest, testPackageListFromJsonNodeMany) { Json::Value root; Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_MANY, root); auto const embedded = root[Package::JsonKeys::embedded]; auto const ci_package = embedded[Package::JsonKeys::ci_package]; Packages pl = package_list_from_json_node(ci_package); ASSERT_GT(pl.size(), 1); } TEST_F(PackageTest, testPackageListFromJsonNodeMissingData) { Json::Value root; Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_MISSING_DATA, root); auto const embedded = root[Package::JsonKeys::embedded]; auto const ci_package = embedded[Package::JsonKeys::ci_package]; Packages pl = package_list_from_json_node(ci_package); ASSERT_EQ(1, pl.size()); } TEST_F(PackageTest, testPackageParsesMultiplePrices) { Json::Value root; Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); auto const embedded = root[Package::JsonKeys::embedded]; auto const ci_package = embedded[Package::JsonKeys::ci_package]; Packages pl = package_list_from_json_node(ci_package); ASSERT_EQ(3, pl[0].prices.size()); } TEST_F(PackageTest, testPackageParsesVersion) { Json::Value root; Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); auto const embedded = root[Package::JsonKeys::embedded]; auto const ci_package = embedded[Package::JsonKeys::ci_package]; Packages pl = package_list_from_json_node(ci_package); ASSERT_EQ("0.83b", pl[0].version); } TEST_F(PackageTest, testPackageDetailsParsesMultiplePrices) { auto details = PackageDetails::from_json(FAKE_JSON_PACKAGE_DETAILS); ASSERT_EQ(3, details.package.prices.size()); } ./libclickscope/tests/test_reviews.cpp0000644000015600001650000003736312676763604020331 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include "fake_json.h" #include #include #include using namespace ::testing; namespace { class ReviewsTest : public ::testing::Test { protected: QSharedPointer clientPtr; QSharedPointer namPtr; std::shared_ptr reviewsPtr; virtual void SetUp() { namPtr.reset(new MockNetworkAccessManager()); clientPtr.reset(new NiceMock(namPtr)); reviewsPtr.reset(new click::Reviews(clientPtr)); } public: MOCK_METHOD2(reviews_callback, void(click::ReviewList, click::Reviews::Error)); }; } TEST_F(ReviewsTest, bringToFrontUserMatches) { click::Review r1; r1.id = 1; r1.reviewer_username = "user1"; click::Review r2; r2.id = 2; r2.reviewer_username = "user2"; click::Review r3; r3.id = 3; r3.reviewer_username = "user3"; click::ReviewList reviews {r1, r2, r3}; auto newReviews = bring_to_front(reviews, "user2"); EXPECT_EQ(newReviews.size(), 3); auto it = newReviews.begin(); EXPECT_EQ(2, (*it).id); ++it; EXPECT_EQ(1, (*it).id); ++it; EXPECT_EQ(3, (*it).id); } TEST_F(ReviewsTest, bringToFrontUserMatchesFirstElement) { click::Review r1; r1.id = 1; r1.reviewer_username = "user1"; click::Review r2; r2.id = 2; r2.reviewer_username = "user2"; click::ReviewList reviews {r1, r2}; auto newReviews = bring_to_front(reviews, "user1"); EXPECT_EQ(newReviews.size(), 2); auto it = newReviews.begin(); EXPECT_EQ(1, (*it).id); ++it; EXPECT_EQ(2, (*it).id); } TEST_F(ReviewsTest, bringToFrontEmptyList) { click::ReviewList reviews; auto newReviews = bring_to_front(reviews, "user1"); EXPECT_EQ(newReviews.size(), 0); } TEST_F(ReviewsTest, bringToFrontUserDoesntMatch) { click::Review r1; r1.id = 1; r1.reviewer_username = "user1"; click::Review r2; r2.id = 2; r2.reviewer_username = "user2"; click::ReviewList reviews {r1, r2}; auto newReviews = bring_to_front(reviews, "user0"); EXPECT_EQ(newReviews.size(), 2); auto it = newReviews.begin(); EXPECT_EQ(1, (*it).id); ++it; EXPECT_EQ(2, (*it).id); } TEST_F(ReviewsTest, testFetchReviewsCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); reviewsPtr->fetch_reviews("", [](click::ReviewList, click::Reviews::Error) {}); } TEST_F(ReviewsTest, testFetchReviewsSendsQueryAsParam) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::web::CallParams params; params.add(click::REVIEWS_QUERY_ARGNAME, FAKE_PACKAGENAME); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, params)) .Times(1) .WillOnce(Return(response)); reviewsPtr->fetch_reviews(FAKE_PACKAGENAME, [](click::ReviewList, click::Reviews::Error) {}); } TEST_F(ReviewsTest, testFetchReviewsSendsCorrectPath) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(EndsWith(click::REVIEWS_API_PATH), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); reviewsPtr->fetch_reviews(FAKE_PACKAGENAME, [](click::ReviewList, click::Reviews::Error) {}); } TEST_F(ReviewsTest, testFetchReviewsCallbackCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, reviews_callback(_, _)).Times(1); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); response->replyFinished(); } TEST_F(ReviewsTest, testFetchReviewsNot200) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(reply.instance, attribute(_)).WillOnce(Return(QVariant(301))); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(FAKE_JSON_REVIEWS_RESULT_ONE.c_str())); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::ReviewList empty_reviews_list; EXPECT_CALL(*this, reviews_callback(empty_reviews_list, _)).Times(1); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); response->replyFinished(); } TEST_F(ReviewsTest, testFetchReviewsEmptyJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, attribute(_)).WillOnce(Return(QVariant(200))); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::ReviewList empty_reviews_list; EXPECT_CALL(*this, reviews_callback(empty_reviews_list, _)).Times(1); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); response->replyFinished(); } TEST_F(ReviewsTest, testFetchReviewsSingleJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_REVIEWS_RESULT_ONE.c_str()); EXPECT_CALL(reply.instance, attribute(_)).WillOnce(Return(QVariant(200))); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::ReviewList single_review_list = { click::Review { 1, 4, 0, 0, false, "2014-01-28T09:09:47.218Z", "null", FAKE_PACKAGENAME, "0.2", "en", "Review Summary", "It is ok.", "Reviewer", "reviewer" } }; EXPECT_CALL(*this, reviews_callback(single_review_list, _)).Times(1); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); response->replyFinished(); } TEST_F(ReviewsTest, testFetchReviewsNetworkErrorReported) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(reply.instance, errorString()) .Times(1) .WillOnce(Return("fake error")); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); click::ReviewList empty_reviews_list; EXPECT_CALL(*this, reviews_callback(empty_reviews_list, click::Reviews::Error::NetworkError)) .Times(1); emit reply.instance.error(QNetworkReply::UnknownNetworkError); } TEST_F(ReviewsTest, testFetchReviewsIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); auto fetch_reviews_op = reviewsPtr->fetch_reviews("", [](click::ReviewList, click::Reviews::Error) {}); EXPECT_CALL(reply.instance, abort()).Times(1); fetch_reviews_op.cancel(); } TEST_F(ReviewsTest, testSubmitReviewIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::Review review; review.rating = 3; review.review_text = "A review"; review.package_name = "com.example.test"; review.package_version = "0.1"; EXPECT_CALL(*clientPtr, callImpl(_, "POST", true, _, _, _)) .Times(1) .WillOnce(Return(response)); auto submit_op = reviewsPtr->submit_review(review, [](click::Reviews::Error){}); EXPECT_CALL(reply.instance, abort()).Times(1); submit_op.cancel(); } TEST_F(ReviewsTest, testSubmitReviewUtf8) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::Review review; review.rating = 3; review.review_text = "'\"å°æµ·åš´é¸"; review.package_name = "com.example.test"; review.package_version = "0.1"; // NOTE: gmock replaces the \" above as \\\" for HasSubstr(), so we have // to manually copy the string into the expected result. std::string expected_review_text = "\"review_text\":\"'\\\"å°æµ·åš´é¸\""; EXPECT_CALL(*clientPtr, callImpl(_, "POST", true, _, HasSubstr(expected_review_text), _)) .Times(1) .WillOnce(Return(response)); auto submit_op = reviewsPtr->submit_review(review, [](click::Reviews::Error){}); } TEST_F(ReviewsTest, testSubmitReviewLanguageCorrect) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::Review review; review.rating = 3; review.review_text = "A review."; review.package_name = "com.example.test"; review.package_version = "0.1"; ASSERT_EQ(setenv(click::Configuration::LANGUAGE_ENVVAR, "es_AR.UTF-8", 1), 0); std::string expected_language = "\"language\":\"es\""; EXPECT_CALL(*clientPtr, callImpl(_, "POST", true, _, HasSubstr(expected_language), _)) .Times(1) .WillOnce(Return(response)); auto submit_op = reviewsPtr->submit_review(review, [](click::Reviews::Error){}); ASSERT_EQ(unsetenv(click::Configuration::LANGUAGE_ENVVAR), 0); } TEST_F(ReviewsTest, testSubmitReviewLanguageCorrectForFullLangCodes) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::Review review; review.rating = 3; review.review_text = "A review."; review.package_name = "com.example.test"; review.package_version = "0.1"; for (std::string lang: click::Configuration::FULL_LANG_CODES) { ASSERT_EQ(setenv(click::Configuration::LANGUAGE_ENVVAR, lang.c_str(), 1), 0); std::string expected_language = "\"language\":\"" + lang + "\""; EXPECT_CALL(*clientPtr, callImpl(_, "POST", true, _, HasSubstr(expected_language), _)) .Times(1) .WillOnce(Return(response)); auto submit_op = reviewsPtr->submit_review(review, [](click::Reviews::Error){}); ASSERT_EQ(unsetenv(click::Configuration::LANGUAGE_ENVVAR), 0); } } TEST_F(ReviewsTest, testEditReviewUrlHasReviewId) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::Review review; review.id = 1234; review.rating = 4; review.review_text = "A review"; review.package_name = "com.example.test"; review.package_version = "0.1"; EXPECT_CALL(*clientPtr, callImpl(HasSubstr("/1234/"), "PUT", true, _, _, _)) .Times(1) .WillOnce(Return(response)); auto submit_op = reviewsPtr->edit_review(review, [](click::Reviews::Error){}); } TEST_F(ReviewsTest, testEditReviewIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::Review review; review.id = 1234; review.rating = 4; review.review_text = "A review"; review.package_name = "com.example.test"; review.package_version = "0.1"; EXPECT_CALL(*clientPtr, callImpl(_, "PUT", true, _, _, _)) .Times(1) .WillOnce(Return(response)); auto submit_op = reviewsPtr->edit_review(review, [](click::Reviews::Error){}); EXPECT_CALL(reply.instance, abort()).Times(1); submit_op.cancel(); } TEST_F(ReviewsTest, testGetBaseUrl) { const char *value = getenv(click::REVIEWS_BASE_URL_ENVVAR.c_str()); if (value != NULL) { ASSERT_TRUE(unsetenv(click::REVIEWS_BASE_URL_ENVVAR.c_str()) == 0); } ASSERT_TRUE(click::Reviews::get_base_url() == click::REVIEWS_BASE_URL); } TEST_F(ReviewsTest, testGetBaseUrlFromEnv) { ASSERT_TRUE(setenv(click::REVIEWS_BASE_URL_ENVVAR.c_str(), FAKE_SERVER.c_str(), 1) == 0); ASSERT_TRUE(click::Reviews::get_base_url() == FAKE_SERVER); ASSERT_TRUE(unsetenv(click::REVIEWS_BASE_URL_ENVVAR.c_str()) == 0); } ./libclickscope/tests/mock_pay.h0000644000015600001650000000666012676763577017062 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include namespace { constexpr static const char* FAKE_PURCHASES_LIST_JSON{R"foo( [ { "state": "Complete", "package_name": "com.example.fake", "refundable_until": "1970-01-01T00:01:23Z", "open_id": "https:\/\/login.ubuntu.com/+openid/fakeuser" } ] )foo"}; constexpr static const char* FAKE_PURCHASES_LIST_JSON_NULL_TIMESTAMP{R"foo( [ { "state": "Complete", "package_name": "com.example.fake", "refundable_until": null, "open_id": "https:\/\/login.ubuntu.com/+openid/fakeuser" } ] )foo"}; class MockPayPackage : public pay::Package { public: MockPayPackage() : Package(QSharedPointer()) { } MockPayPackage(const QSharedPointer& client) : Package(client) { } void pay_package_refund(const std::string& pkg_name) { callbacks[pkg_name + pay::APPENDAGE_REFUND](pkg_name, success); do_pay_package_refund(pkg_name); } void pay_package_verify(const std::string& pkg_name) { callbacks[pkg_name + pay::APPENDAGE_VERIFY](pkg_name, success); do_pay_package_verify(pkg_name); } bool is_refundable(const std::string& pkg_name) { do_is_refundable(pkg_name); return refundable; } MOCK_METHOD0(setup_pay_service, void()); MOCK_METHOD1(do_pay_package_refund, void(const std::string&)); MOCK_METHOD1(do_pay_package_verify, void(const std::string&)); MOCK_METHOD1(do_is_refundable, void(const std::string&)); bool refundable = false; bool success = false; pay::PurchaseSet purchases; }; } // namespace ./libclickscope/tests/test_utils.cpp0000644000015600001650000000717012676763577020007 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include using namespace ::testing; class UtilsTest : public Test { public: void SetUp() { // The tests assume they run always under the same environment setDefaultTimezone(); setDefaultLocale(); } void setDefaultTimezone() { boost::locale::time_zone::global("UTC"); } void setDefaultLocale() { boost::locale::generator gen; std::locale::global(gen("C")); } }; TEST_F(UtilsTest, testHumanReadableZeroBytes) { ASSERT_EQ(click::Formatter::human_readable_filesize(0), "0 bytes"); } TEST_F(UtilsTest, testHumanReadableOneByte) { ASSERT_EQ(click::Formatter::human_readable_filesize(1), "1 byte"); } TEST_F(UtilsTest, testHumanReadableMoreBytes) { ASSERT_EQ(click::Formatter::human_readable_filesize(102), "102 bytes"); } TEST_F(UtilsTest, testHumanReadableKilobytes) { ASSERT_EQ(click::Formatter::human_readable_filesize(102*1024), "102.0 KiB"); } TEST_F(UtilsTest, testHumanReadableFractionalKilobytes) { ASSERT_EQ(click::Formatter::human_readable_filesize(102*1024+512+1), "102.5 KiB"); } TEST_F(UtilsTest, testHumanReadableMegabytes) { ASSERT_EQ(click::Formatter::human_readable_filesize(42*1024*1024), "42.0 MiB"); } TEST_F(UtilsTest, testHumanReadableFractionalMegabytes) { ASSERT_EQ(click::Formatter::human_readable_filesize(42*1024*1024+512*1024+512), "42.5 MiB"); } class TestableDate : public click::Date { public: using click::Date::timestamp; }; TEST_F(UtilsTest, testDateParseIso8601) { TestableDate d; d.parse_iso8601("1975-11-30T00:00:00Z"); ASSERT_EQ(186537600, d.timestamp); } TEST_F(UtilsTest, testDateFormatted) { TestableDate d; d.timestamp = 186537600; ASSERT_EQ("Nov 30, 1975", d.formatted()); } TEST_F(UtilsTest, testAFewDatesFormatted) { TestableDate d; d.parse_iso8601("2008-12-06T00:00:00Z"); ASSERT_EQ("Dec 6, 2008", d.formatted()); d.parse_iso8601("2013-12-16T00:00:00Z"); ASSERT_EQ("Dec 16, 2013", d.formatted()); } TEST_F(UtilsTest, testDateComparesOk) { TestableDate d1; d1.parse_iso8601("1975-11-30T00:00:00Z"); TestableDate d2; d2.timestamp = 186537600; ASSERT_EQ(d1, d2); } ./libclickscope/tests/fake_json.cpp0000644000015600001650000012123512676763577017546 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "fake_json.h" const std::string FAKE_JSON_REVIEWS_RESULT_ONE = R"foo( [ { "date_created": "2014-01-28T09:09:47.218Z", "date_deleted": null, "hide": false, "id": 1, "language": "en", "package_name": "com.example.fakepackage", "rating": 4, "review_text": "It is ok.", "reviewer_displayname": "Reviewer", "reviewer_username": "reviewer", "summary": "Review Summary", "usefulness_favorable": 0, "usefulness_total": 0, "version": "0.2" } ] )foo"; const std::string FAKE_JSON_SEARCH_RESULT_ONE = R"foo({ "_embedded": { "clickindex:package": [ { "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "version": "0.83b", "description": "This is an awesome launcher.", "price": 1.99, "icon_url": "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "prices": { "USD": 1.99, "EUR": 1.69, "GBP": 1.29 }, "_links": { "self": { "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" } } } ] } } )foo"; const std::string FAKE_JSON_SEARCH_RESULT_ONE_SCOPE = R"foo({ "_embedded": { "clickindex:package": [ { "name": "org.example.awesomescope", "title": "Awesome Scope", "description": "This is an awesome scope.", "price": 0.0, "content": "scope", "icon_url": "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "_links": { "self": { "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomescope" } } } ] } } )foo"; const std::string FAKE_JSON_SEARCH_RESULT_MISSING_DATA = R"foo({ "_embedded": { "clickindex:package": [ { "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "description": "This is an awesome launcher.", "_links": { "self": { "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" } } } ] } } )foo"; const std::string FAKE_JSON_SEARCH_RESULT_MANY = R"foo({ "_embedded": { "clickindex:package": [ { "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "description": "This is an awesome launcher.", "price": 1.99, "icon_url": "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "_links": { "self": { "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" } } }, { "name": "org.example.fantastiqueapp", "title": "Fantastic App", "description": "This is a fantasticc app.", "price": 0.0, "icon_url": "http://assets.ubuntu.com/sites/ubuntu/504/u/img/ubuntu/features/icon-find-more-apps-64x64.png", "_links": { "self": { "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.fantasticapp" } } }, { "name": "org.example.awesomewidget", "title": "Awesome Widget", "description": "This is an awesome widget.", "price": 1.99, "icon_url": "http://assets.ubuntu.com/sites/ubuntu/504/u/img/ubuntu/features/icon-photos-and-videos-64x64.png", "_links": { "self": { "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomewidget" } } } ] } } )foo"; const std::string FAKE_JSON_SEARCH_RESULT_RECOMMENDS = R"foo({ "_embedded": { "clickindex:package": [ { "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "description": "This is an awesome launcher.", "price": 1.99, "icon_url": "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "_links": { "self": { "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" } } } ], "clickindex:recommendation": [ { "name": "org.example.awesomelauncher2", "title": "Awesome Launcher 2", "description": "This is an another awesome launcher.", "price": 1.99, "icon_url": "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "_links": { "self": { "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher2" } } } ] } } )foo"; const std::string FAKE_JSON_PACKAGE_DETAILS = R"foo( { "name": "ar.com.beuno.wheather-touch", "icon_url": "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png", "title": "\u1F4A9 Weather", "description": "\u1F4A9 Weather\nA weather application.", "download_url": "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", "download_sha512": "fake_sha512", "rating": 3.5, "keywords": "these, are, key, words", "terms_of_service": "tos", "license": "Proprietary", "publisher": "Fake Publisher", "developer_name": "Fake Developer", "company_name": "Fake Company", "website": "http://example.com", "support_url": "http://example.com/support", "screenshot_url": "sshot0", "screenshot_urls": ["sshot1", "sshot2"], "binary_filesize": 177582, "version": "0.2", "date_published": "2013-11-04T00:40:24.686298Z", "last_updated": "2014-07-03T08:16:34.532525Z", "changelog": "This is the changelog", "framework": "None", "price": 1.99, "prices": { "USD": 1.99, "EUR": 1.69, "GBP": 1.29 }, "license_key_path": "", "click_version": "0.1", "icon_urls": { "64": "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png" }, "requires_license_key": false } )foo"; const std::string FAKE_JSON_PACKAGE_DETAILS_DEB = R"foo( { "name": "weather-app", "icon_url": "theme://weather-app", "title": "\u1F4A9 Weather", "description": "A weather application.", "keywords": "these, are, key, words" } )foo"; const std::string FAKE_JSON_BOOTSTRAP = R"( { "_embedded": { "clickindex:department": [ { "has_children": false, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-subdepartment"} }, "name": "Fake Subdepartment", "slug": "fake-subdepartment"} ], "clickindex:highlight": [ { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher"} }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget" } }, "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png"} ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/top-apps" } }, "name": "Top Apps", "slug": "top-apps" }, { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget" } }, "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png" } ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/most-purchased" } }, "name": "Most Purchased", "slug": "most-purchased" }, { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget" } }, "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png" } ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/new-releases" } }, "name": "New Releases", "slug": "new-releases" } ] }, "has_children": true, "_links": { "curies": [ { "href": "https://search.apps.staging.ubuntu.com/docs/v1/relations.html{#rel}", "name": "clickindex", "templated": true } ], "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-department-with-subdepartments" }, "collection": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments" } }, "name": "Fake Department With Subdepartments", "slug": "fake-department-with-subdepartments" })"; const std::string FAKE_JSON_BROKEN_BOOTSTRAP = R"( { "_embedded": { "clickindex:department": [ { "name": "Broken department" } ], "clickindex:highlight": [ { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher"} }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" } ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/top-apps" } }, "name": "Top Apps", "slug": "top-apps" }, { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" } ] }, "____name": "Broken highlight" } ] } })"; const std::string FAKE_JSON_DEPARTMENTS_ONLY = R"( { "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments" }, "curies": [ { "name": "clickindex", "href": "https://search.apps.ubuntu.com/docs/v1/relations.html{#rel}", "templated": true } ] }, "_embedded": { "clickindex:department": [ { "name": "Games", "slug": "games", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Games" } }, "_embedded": { "clickindex:department": [ { "name": "Board Games", "slug": "board_games", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Games/Board+Games" } } } ] } }, { "name": "Graphics", "slug": "graphics", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Graphics" } }, "_embedded": { "clickindex:department": [ { "name": "Drawing", "slug": "graphics_drawing", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Graphics/Drawing" } } } ] } }, { "name": "Internet", "slug": "internet", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet" } }, "_embedded": { "clickindex:department": [ { "name": "Chat", "slug": "internet_chat", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Chat" } } }, { "name": "Mail", "slug": "internet_mail", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Mail" } } }, { "name": "Web Browsers", "slug": "internet_web", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Web+Browsers" } } } ] } } ] } })"; const std::string FAKE_JSON_BROKEN_DEPARTMENTS = R"( { "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments" }, "curies": [ { "name": "clickindex", "href": "https://search.apps.ubuntu.com/docs/v1/relations.html{#rel}", "templated": true } ] }, "_embedded": { "clickindex:department": [ { "name": "Games", "slug": "games", "_links": { "self": { "href": "https://search.apps.ubuntu.com/api/v1/departments/Games" } }, "_embedded": { "clickindex:department": [ { "name": "Broken department" } ] } } ] } })"; const std::string FAKE_JSON_DEPARTMENT_WITH_APPS = R"( { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomescope", "title": "Awesome Scope", "price": 1.99, "content": "scope", "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomescope" } }, "icon": "http://example.org/media/org.example.awesomescope/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomescope2", "title": "Awesome Scope 2.0", "price": 1.99, "content": "scope", "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomescope2" } }, "icon": "http://example.org/media/org.example.awesomescope2/icons/icon16.png" } ], "clickindex:department": [ { "has_children": false, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-subdepartment"} }, "name": "Fake Subdepartment", "slug": "fake-subdepartment"} ], "clickindex:highlight": [ { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher"} }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget" } }, "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png"} ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/top-apps" } }, "name": "Top Apps", "slug": "top-apps" }, { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget" } }, "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png" } ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/most-purchased" } }, "name": "Most Purchased", "slug": "most-purchased" }, { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" } ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/new-releases" } }, "name": "Editor's Pick", "slug": "editors-pick" } ] }, "has_children": true, "_links": { "curies": [ { "href": "https://search.apps.staging.ubuntu.com/docs/v1/relations.html{#rel}", "name": "clickindex", "templated": true } ], "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-department-with-subdepartments" }, "collection": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments" } }, "name": "Fake Department With Subdepartments", "slug": "fake-department-with-subdepartments" })"; const std::string FAKE_JSON_STORE_HOME = R"( { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomescope", "title": "Awesome Scope", "price": 1.99, "content": "scope", "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomescope" } }, "icon": "http://example.org/media/org.example.awesomescope/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomescope2", "title": "Awesome Scope 2.0", "price": 1.99, "content": "scope", "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomescope2" } }, "icon": "http://example.org/media/org.example.awesomescope2/icons/icon16.png" } ], "clickindex:department": [ { "has_children": false, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-subdepartment"} }, "name": "Fake Subdepartment", "slug": "fake-subdepartment"} ], "clickindex:highlight": [ { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher"} }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget" } }, "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png"} ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/top-apps" } }, "name": "Top Apps", "slug": "top-apps" }, { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" }, { "publisher": "Awesome Widget Company", "name": "org.example.awesomewidget", "title": "Awesome Widget", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget" } }, "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png" } ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/most-purchased" } }, "name": "Most Purchased", "slug": "most-purchased" }, { "_embedded": { "clickindex:package": [ { "publisher": "Awesome Widget Company", "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "price": 1.99, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }, "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" } ] }, "_links": { "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/new-releases" } }, "name": "App of the Week", "slug": "app-of-the-week" } ] }, "has_children": true, "_links": { "curies": [ { "href": "https://search.apps.staging.ubuntu.com/docs/v1/relations.html{#rel}", "name": "clickindex", "templated": true } ], "self": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-department-with-subdepartments" }, "collection": { "href": "https://search.apps.staging.ubuntu.com/api/v1/departments" } }, "name": "Fake Department With Subdepartments", "slug": "fake-department-with-subdepartments" })"; const std::string FAKE_JSON_MANIFEST_REMOVABLE = R"foo( { "_removable": 1, "name": "com.example.fake", "version": "0.1", "hooks": { "fake-app": { "desktop": "fake-app.desktop" } } } )foo"; const std::string FAKE_JSON_MANIFEST_NONREMOVABLE = R"foo( { "_removable": 0, "name": "com.example.fake", "version": "0.1", "hooks": { "fake-app": { "desktop": "fake-app.desktop" } } } )foo"; const std::string FAKE_JSON_MANIFEST_ONE_APP = R"foo( { "_removable": 1, "name": "com.example.fake-app", "version": "0.1", "hooks": { "fake-app": { "apparmor": "fake-app.json", "desktop": "fake-app.desktop" } } } )foo"; const std::string FAKE_JSON_MANIFEST_ONE_SCOPE = R"foo( { "_removable": 1, "name": "com.example.fake-scope", "version": "0.1", "hooks": { "fake-scope-hook": { "apparmor": "scope-security.json", "scope": "fake-scope" } } } )foo"; const std::string FAKE_JSON_MANIFEST_ONE_APP_ONE_SCOPE = R"foo( { "_removable": 1, "name": "com.example.fake-1app-1scope", "version": "0.1", "hooks": { "fake-app": { "apparmor": "fake-app.json", "desktop": "fake-app.desktop" }, "fake-scope-hook": { "apparmor": "scope-security.json", "scope": "fake-scope" } } } )foo"; const std::string FAKE_JSON_MANIFEST_TWO_APPS_TWO_SCOPES = R"foo( { "_removable": 1, "name": "com.example.fake-2apps-2scopes", "version": "0.1", "hooks": { "fake-app1": { "apparmor": "fake-app1.json", "desktop": "fake-app1.desktop" }, "fake-app2": { "apparmor": "fake-app2.json", "desktop": "fake-app2.desktop" }, "fake-scope-hook1": { "apparmor": "scope-security1.json", "scope": "fake-scope1" }, "fake-scope-hook2": { "apparmor": "scope-security1.json", "scope": "fake-scope2" } } } )foo"; ./libclickscope/tests/test_webclient.cpp0000644000015600001650000002702212676763604020610 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include const std::string FAKE_SERVER = "http://fake-server/"; const std::string FAKE_PATH = "fake/api/path"; MATCHER_P(IsCorrectUrl, refUrl, "") { *result_listener << "where the url is " << qPrintable(arg.url().toString()); return arg.url().toString() == refUrl; } MATCHER_P(IsValidOAuthHeader, valid, "") { return (arg.hasRawHeader(click::web::AUTHORIZATION_HEADER.c_str()) && arg.rawHeader(click::web::AUTHORIZATION_HEADER.c_str()).startsWith("OAuth ")) == valid; } MATCHER_P(IsCorrectAcceptLanguageHeader, refAcceptLanguages, "") { return arg.hasRawHeader(click::web::ACCEPT_LANGUAGE_HEADER.c_str()) && arg.rawHeader(click::web::ACCEPT_LANGUAGE_HEADER.c_str()) == refAcceptLanguages; } MATCHER_P(IsCorrectCookieHeader, refCookie, "") { return arg.hasRawHeader("Cookie") && arg.rawHeader("Cookie") == refCookie; } MATCHER_P(IsCorrectBufferData, refData, "") { return dynamic_cast(arg)->data() == refData; } class WebClientTest : public ::testing::Test { public: MockNetworkAccessManager nam; QSharedPointer namPtr; MockCredentialsService sso; QSharedPointer ssoPtr; void SetUp() { namPtr.reset(&nam, [](click::network::AccessManager*) {}); ssoPtr.reset(&sso, [](click::CredentialsService*) {}); } MOCK_METHOD0(replyHandler, void()); MOCK_METHOD1(errorHandler, void(QString description)); }; TEST_F(WebClientTest, testUrlBuiltNoParams) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); EXPECT_CALL(nam, get(IsCorrectUrl(QString("http://fake-server/fake/api/path")))) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH); } TEST_F(WebClientTest, testParamsAppended) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); click::web::CallParams params; params.add("a", "1"); params.add("b", "2"); EXPECT_CALL(nam, get(IsCorrectUrl(QString("http://fake-server/fake/api/path?a=1&b=2")))) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, params); } /* TEST(WebClient, testResultsAreEmmited) { FakeNam::scripted_responses.append("HOLA"); click::web::Client wc( FAKE_SERVER, QSharedPointer(new click::network::AccessManager())); auto wr = wc.call(FAKE_SERVER + FAKE_PATH); connect(wr.data(), &click::web::Response::finished, this, &TestWebClient::gotResults); QTRY_COMPARE(results, QString("HOLA")); using namespace ::testing; MockNetworkAccessManager nam; QSharedPointer namPtr( &nam, [](click::network::AccessManager*) {}); auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); click::web::Client wc(namPtr); auto wr = wc.call(FAKE_SERVER + FAKE_PATH); // TODO: We need to extend the web::Response class to allow for reading the contents of the response // EXPECT_EQ(QByteArray("HOLA"), wr->); } */ TEST_F(WebClientTest, testCookieHeaderSetCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); EXPECT_CALL(nam, get(IsCorrectCookieHeader("CookieCookieCookie"))) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "GET", false, std::map({{"Cookie", "CookieCookieCookie"}})); } TEST_F(WebClientTest, testMethodPassedCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); QByteArray verb("POST", 4); EXPECT_CALL(nam, sendCustomRequest(_, verb, _)) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "POST", false); } TEST_F(WebClientTest, testBufferDataPassedCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA \u5c0f\u6d77")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); EXPECT_CALL(nam, sendCustomRequest(_, _, IsCorrectBufferData("HOLA \u5c0f\u6d77"))) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "POST", false, std::map(), "HOLA \u5c0f\u6d77"); } TEST_F(WebClientTest, testSignedCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); wc.setCredentialsService(ssoPtr); EXPECT_CALL(sso, getCredentials()).WillOnce(Invoke([&](){ UbuntuOne::Token token("token_key", "token_secret", "consumer_key", "consumer_secret"); sso.credentialsFound(token); })); EXPECT_CALL(nam, sendCustomRequest(IsValidOAuthHeader(true), _, _)) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "POST", true); } TEST_F(WebClientTest, testSignedCredentialsServiceUnset) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); EXPECT_CALL(nam, get(_)) .Times(1) .WillOnce(Return(replyPtr)); EXPECT_CALL(*reply, errorString()).Times(1).WillOnce(Return("auth failed")); auto response = wc.call(FAKE_SERVER + FAKE_PATH, "GET", true); QObject::connect(response.data(), &click::web::Response::error, [this](QString desc){ errorHandler(desc); }); EXPECT_CALL(*this, errorHandler(_)); emit reply->error(QNetworkReply::AuthenticationRequiredError); } TEST_F(WebClientTest, testSignTokenNotFound) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); wc.setCredentialsService(ssoPtr); EXPECT_CALL(sso, getCredentials()).WillOnce(Invoke([&]() { sso.credentialsNotFound(); })); EXPECT_CALL(nam, sendCustomRequest(IsValidOAuthHeader(false), _, _)) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "POST", true); } TEST_F(WebClientTest, testResponseFinished) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); EXPECT_CALL(nam, get(_)) .Times(1) .WillOnce(Return(replyPtr)); auto response = wc.call(FAKE_SERVER + FAKE_PATH); QObject::connect(response.data(), &click::web::Response::finished, [this](){replyHandler();}); EXPECT_CALL(*this, replyHandler()); emit reply->finished(); } TEST_F(WebClientTest, testResponseFailed) { using namespace ::testing; auto reply = new NiceMock(); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); EXPECT_CALL(nam, get(_)) .Times(1) .WillOnce(Return(replyPtr)); EXPECT_CALL(*reply, errorString()) .Times(1) .WillOnce(Return("fake error")); EXPECT_CALL(*reply, readAll()) .Times(1) .WillOnce(Return("{'error': 'Invalid request.'}")); auto response = wc.call(FAKE_SERVER + FAKE_PATH); QObject::connect(response.data(), &click::web::Response::error, [this](QString desc){ errorHandler(desc); }); EXPECT_CALL(*this, errorHandler(_)); emit reply->error(QNetworkReply::UnknownNetworkError); } TEST_F(WebClientTest, testResponseAbort) { using namespace ::testing; auto reply = new NiceMock(); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); EXPECT_CALL(nam, get(_)) .Times(1) .WillOnce(Return(replyPtr)); auto response = wc.call(FAKE_SERVER + FAKE_PATH); EXPECT_CALL(*reply, abort()).Times(1); response->abort(); } TEST_F(WebClientTest, testAcceptLanguageSetCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr); ASSERT_EQ(setenv(click::Configuration::LANGUAGE_ENVVAR, "en_US.UTF-8", 1), 0); EXPECT_CALL(nam, get(IsCorrectAcceptLanguageHeader("en-US, en"))) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH); ASSERT_EQ(unsetenv(click::Configuration::LANGUAGE_ENVVAR), 0); } ./libclickscope/tests/test_departments-db.cpp0000644000015600001650000002704112676763577021557 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include "fake_json.h" #include #include #include using namespace click; class DepartmentsDbCheck : public DepartmentsDb { public: DepartmentsDbCheck(const std::string& name, bool create) : DepartmentsDb(name, create) { } void check_sql_queries_finished() { EXPECT_FALSE(delete_pkgmap_query_->isActive()); EXPECT_FALSE(delete_depts_query_->isActive()); EXPECT_FALSE(delete_deptnames_query_->isActive()); EXPECT_FALSE(insert_pkgmap_query_->isActive()); EXPECT_FALSE(insert_dept_id_query_->isActive()); EXPECT_FALSE(insert_dept_name_query_->isActive()); EXPECT_FALSE(select_pkgs_by_dept_->isActive()); EXPECT_FALSE(select_pkgs_by_dept_recursive_->isActive()); EXPECT_FALSE(select_dept_for_pkg_->isActive()); EXPECT_FALSE(select_pkgs_count_in_dept_recursive_->isActive()); EXPECT_FALSE(select_pkg_by_pkgid_->isActive()); EXPECT_FALSE(select_parent_dept_->isActive()); EXPECT_FALSE(select_children_depts_->isActive()); EXPECT_FALSE(select_dept_name_->isActive()); EXPECT_FALSE(select_is_descendant_of_dept_->isActive()); } }; class DepartmentsDbTest: public ::testing::Test { public: const std::string db_path = ":memory:"; void SetUp() override { db.reset(new DepartmentsDbCheck(db_path, true)); db->store_department_mapping("tools", ""); db->store_department_mapping("games", ""); db->store_department_name("tools", "", "Tools"); db->store_department_name("office", "", "Office"); db->store_department_mapping("office", "tools"); db->store_package_mapping("app1", "tools"); db->store_package_mapping("app2", "office"); db->store_department_name("games", "", "Games"); db->store_department_name("games", "pl_PL", "Gry"); db->store_department_name("rpg", "", "RPG"); db->store_department_name("card", "", "Card"); db->store_department_name("fps", "", "First Person Shooter"); db->store_department_mapping("rpg", "games"); db->store_department_mapping("card", "games"); db->store_department_mapping("fps", "games"); db->store_package_mapping("game1", "rpg"); db->store_package_mapping("game2", "fps"); } protected: std::unique_ptr db; }; TEST_F(DepartmentsDbTest, testDepartmentNameLookup) { { EXPECT_EQ("Games", db->get_department_name("games", {"en_EN", ""})); EXPECT_EQ("Gry", db->get_department_name("games", {"pl_PL", ""})); EXPECT_EQ("First Person Shooter", db->get_department_name("fps", {"en_EN", ""})); EXPECT_EQ("Office", db->get_department_name("office", {"en_EN", ""})); EXPECT_EQ("Tools", db->get_department_name("tools", {"en_EN", ""})); EXPECT_THROW(db->get_department_name("xyz", {"en_EN", ""}), std::logic_error); } } TEST_F(DepartmentsDbTest, testDepartmentForPackageLookup) { EXPECT_EQ("rpg", db->get_department_for_package("game1")); EXPECT_EQ("fps", db->get_department_for_package("game2")); EXPECT_EQ("office", db->get_department_for_package("app2")); EXPECT_EQ("tools", db->get_department_for_package("app1")); EXPECT_THROW(db->get_department_for_package("foobar"), std::logic_error); } TEST_F(DepartmentsDbTest, testIsDepartmentEmpty) { EXPECT_TRUE(db->is_empty("card")); EXPECT_FALSE(db->is_empty("")); EXPECT_FALSE(db->is_empty("games")); EXPECT_FALSE(db->is_empty("office")); EXPECT_FALSE(db->is_empty("tools")); } TEST_F(DepartmentsDbTest, testIsDescendantOfDepartment) { EXPECT_TRUE(db->is_descendant_of_department("rpg", "games")); EXPECT_TRUE(db->is_descendant_of_department("office", "tools")); EXPECT_TRUE(db->is_descendant_of_department("office", "")); EXPECT_TRUE(db->is_descendant_of_department("rpg", "")); EXPECT_TRUE(db->is_descendant_of_department("games", "")); EXPECT_FALSE(db->is_descendant_of_department("games", "office")); EXPECT_FALSE(db->is_descendant_of_department("", "games")); } TEST_F(DepartmentsDbTest, testDepartmentNameUpdates) { { EXPECT_EQ(7u, db->department_name_count()); db->store_department_name("tools", "", "Tools!"); EXPECT_EQ("Tools!", db->get_department_name("tools", {"en_EN", ""})); db->store_department_name("games", "pl_PL", "Gry!!!"); EXPECT_EQ("Gry!!!", db->get_department_name("games", {"pl_PL"})); EXPECT_EQ(7u, db->department_name_count()); } } TEST_F(DepartmentsDbTest, testDepartmentParentLookup) { { EXPECT_EQ("games", db->get_parent_department_id("rpg")); EXPECT_EQ("games", db->get_parent_department_id("card")); EXPECT_EQ("", db->get_parent_department_id("games")); EXPECT_EQ("tools", db->get_parent_department_id("office")); EXPECT_EQ("", db->get_parent_department_id("tools")); EXPECT_THROW(db->get_parent_department_id("xyz"), std::logic_error); } } TEST_F(DepartmentsDbTest, testDepartmentChildrenLookup) { { EXPECT_EQ(0, db->get_children_departments("xyz").size()); } { auto depts = db->get_children_departments(""); EXPECT_EQ(2u, depts.size()); EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("tools", true)) != depts.end()); EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("games", true)) != depts.end()); } { auto depts = db->get_children_departments("tools"); EXPECT_EQ(1u, depts.size()); EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("office", false)) != depts.end()); } { auto depts = db->get_children_departments("games"); EXPECT_EQ(3u, depts.size()); EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("rpg", false)) != depts.end()); EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("fps", false)) != depts.end()); EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("card", false)) != depts.end()); } } TEST_F(DepartmentsDbTest, testPackageLookup) { { EXPECT_TRUE(db->has_package("game1")); EXPECT_FALSE(db->has_package("foooo")); } { auto pkgs = db->get_packages_for_department("rpg", false); EXPECT_EQ(1u, pkgs.size()); EXPECT_TRUE(pkgs.find("game1") != pkgs.end()); } { auto pkgs = db->get_packages_for_department("fps", false); EXPECT_EQ(1u, pkgs.size()); EXPECT_TRUE(pkgs.find("game2") != pkgs.end()); } { auto pkgs = db->get_packages_for_department("card", false); EXPECT_EQ(0, pkgs.size()); } } TEST_F(DepartmentsDbTest, testRecursivePackageLookup) { { // get packages from subdepartments of games department auto pkgs = db->get_packages_for_department("games", true); EXPECT_EQ(2u, pkgs.size()); EXPECT_TRUE(pkgs.find("game1") != pkgs.end()); EXPECT_TRUE(pkgs.find("game2") != pkgs.end()); } { // rpg has no subdepartments, so we just get the packages of rpg department auto pkgs = db->get_packages_for_department("rpg", true); EXPECT_EQ(1u, pkgs.size()); EXPECT_TRUE(pkgs.find("game1") != pkgs.end()); } { auto pkgs = db->get_packages_for_department("card", true); EXPECT_EQ(0, pkgs.size()); } } TEST_F(DepartmentsDbTest, testPackageUpdates) { auto pkgs = db->get_packages_for_department("fps"); EXPECT_EQ(1, pkgs.size()); EXPECT_TRUE(pkgs.find("game2") != pkgs.end()); // game2 gets moved to card and removed from fps db->store_package_mapping("game2", "card"); pkgs = db->get_packages_for_department("fps", false); EXPECT_EQ(0, pkgs.size()); EXPECT_TRUE(pkgs.find("game2") == pkgs.end()); pkgs = db->get_packages_for_department("card", false); EXPECT_EQ(1, pkgs.size()); EXPECT_TRUE(pkgs.find("game2") != pkgs.end()); } TEST_F(DepartmentsDbTest, testInvalidDepartments) { EXPECT_THROW(db->get_parent_department_id(""), std::logic_error); EXPECT_THROW(db->get_parent_department_id("foooo"), std::logic_error); } TEST_F(DepartmentsDbTest, testEmptyArguments) { EXPECT_THROW(db->store_department_name("", "", "Foo"), std::logic_error); EXPECT_THROW(db->store_department_name("foo", "", ""), std::logic_error); EXPECT_THROW(db->store_department_mapping("", "foo"), std::logic_error); EXPECT_THROW(db->store_package_mapping("", "foo"), std::logic_error); EXPECT_THROW(db->store_package_mapping("foo", ""), std::logic_error); } TEST_F(DepartmentsDbTest, testNoDuplicates) { db->store_package_mapping("game2", "fps"); db->store_package_mapping("game2", "fps"); db->store_department_name("games", "pl_PL", "Gry"); db->store_department_name("games", "pl_PL", "Gry"); db->store_department_mapping("office", "tools"); db->store_department_mapping("office", "tools"); EXPECT_EQ(7u, db->department_name_count()); EXPECT_EQ(4u, db->package_count()); } TEST_F(DepartmentsDbTest, testSqlQueriesFinished) { db->get_department_name("games", {"en_EN", ""}); db->check_sql_queries_finished(); db->get_parent_department_id("rpg"); db->check_sql_queries_finished(); db->get_packages_for_department("rpg", false); db->check_sql_queries_finished(); db->get_packages_for_department("rpg", true); db->check_sql_queries_finished(); db->get_children_departments("games"); db->check_sql_queries_finished(); db->department_name_count(); db->check_sql_queries_finished(); db->department_mapping_count(); db->check_sql_queries_finished(); db->package_count(); db->check_sql_queries_finished(); db->store_package_mapping("game2", "fps"); db->check_sql_queries_finished(); db->store_department_name("games", "pl_PL", "Gry"); db->check_sql_queries_finished(); db->store_department_mapping("office", "tools"); db->check_sql_queries_finished(); } TEST(DepartmentsDb, testOpenFailsOnUnitializedDb) { ASSERT_THROW( { DepartmentsDb db(":memory:", false); }, std::runtime_error); } ./libclickscope/tests/test_smartconnect.cpp0000644000015600001650000000724412676763577021351 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include using namespace ::testing; namespace { class FakeObject : public QObject { Q_OBJECT public: explicit FakeObject(QObject* parent=0) : QObject(parent) {} signals: void signal0(); void signal1(int x); void signal2(int x, QString y); }; class SmartConnectTest : public Test, QObject { public: MOCK_METHOD0(handler0, void()); MOCK_METHOD1(handler1, void(int x)); MOCK_METHOD2(handler2, void(int x, QString y)); }; } // namespace TEST_F(SmartConnectTest, testSlotNoArgsCalled) { click::utils::SmartConnect sc; FakeObject* o = new FakeObject(&sc); sc.connect(o, &FakeObject::signal0, [=](){ handler0(); }); EXPECT_CALL(*this, handler0()).Times(1); emit o->signal0(); } TEST_F(SmartConnectTest, testSlotOneArgCalled) { click::utils::SmartConnect sc; FakeObject* o = new FakeObject(&sc); sc.connect(o, &FakeObject::signal1, [=](int x){ handler1(x); }); EXPECT_CALL(*this, handler1(42)).Times(1); emit o->signal1(42); } TEST_F(SmartConnectTest, testSlotTwoArgsCalled) { click::utils::SmartConnect sc; FakeObject* o = new FakeObject(&sc); sc.connect(o, &FakeObject::signal2, [=](int x, QString y){ handler2(x, y); }); EXPECT_CALL(*this, handler2(42, QString("Blue"))).Times(1); emit o->signal2(42, "Blue"); } TEST_F(SmartConnectTest, testDisconnectsOnFirstCalled) { click::utils::SmartConnect sc; FakeObject* o = new FakeObject(&sc); sc.connect(o, &FakeObject::signal2, [=](int x, QString y){ handler2(x, y); }); sc.connect(o, &FakeObject::signal1, [=](int x){ handler1(x); }); EXPECT_CALL(*this, handler2(42, QString("Blue"))).Times(1); EXPECT_CALL(*this, handler1(_)).Times(0); emit o->signal2(42, "Blue"); emit o->signal0(); emit o->signal1(83); emit o->signal2(83, "Red"); } class FakeSmartConnect : public click::utils::SmartConnect { Q_OBJECT public: MOCK_METHOD0(cleanup, void()); }; TEST_F(SmartConnectTest, testCleanupCalledOnDisconnect) { FakeSmartConnect fsc; FakeObject* o = new FakeObject(&fsc); fsc.connect(o, &FakeObject::signal0, [](){}); EXPECT_CALL(fsc, cleanup()).Times(1); emit o->signal0(); } #include "test_smartconnect.moc" ./libclickscope/tests/mock_webclient.h0000644000015600001650000000735112676763604020232 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef MOCK_WEBCLIENT_H #define MOCK_WEBCLIENT_H #include "test_data.h" #include #include using namespace ::testing; namespace { template struct LifetimeHelper { LifetimeHelper() : instance() { } template LifetimeHelper(Args&&... args) : instance(std::forward(args...)) { } QSharedPointer asSharedPtr() { return QSharedPointer(&instance, [](Interface*){}); } Mock instance; }; QSharedPointer responseForReply(const QSharedPointer& reply) { auto response = QSharedPointer(new click::web::Response(QSharedPointer(new QNetworkRequest()), QSharedPointer(new QBuffer()))); response->setReply(reply); return response; } class MockClient : public click::web::Client { public: MockClient(const QSharedPointer& networkAccessManager) : Client(networkAccessManager) { } // Mocking default arguments: https://groups.google.com/forum/#!topic/googlemock/XrabW20vV7o MOCK_METHOD6(callImpl, QSharedPointer( const std::string& iri, const std::string& method, bool sign, const std::map& headers, const std::string& data, const click::web::CallParams& params)); QSharedPointer call( const std::string& iri, const click::web::CallParams& params=click::web::CallParams()) override { return callImpl(iri, "GET", false, std::map(), "", params); } QSharedPointer call( const std::string& iri, const std::string& method, bool sign = false, const std::map& headers = std::map(), const std::string& data = "", const click::web::CallParams& params=click::web::CallParams()) override { return callImpl(iri, method, sign, headers, data, params); } MOCK_METHOD1(has_header, bool(const std::string& header)); MOCK_METHOD1(get_header, std::string(const std::string&header)); MOCK_METHOD0(invalidateCredentials, void()); }; } #endif // MOCK_WEBCLIENT_H ./libclickscope/tests/test_download_manager.cpp0000644000015600001650000002050512676763604022134 0ustar jenkinsjenkins/* * Copyright (C) 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include using namespace ::testing; namespace udm = Ubuntu::DownloadManager; #include namespace { const QString TEST_URL("http://test.local/"); const QString TEST_SHA512("fake_hash"); const QString TEST_HEADER_VALUE("test header value"); const QString TEST_APP_ID("test_app_id"); const QString TEST_CLICK_TOKEN_VALUE("test token value"); const QString TEST_DOWNLOAD_ID("/com/ubuntu/download_manager/test"); const QString TEST_DOWNLOADERROR_STRING("test downloadError string"); class DownloadManagerTest : public ::testing::Test { protected: QSharedPointer clientPtr; QSharedPointer namPtr; QSharedPointer sdmPtr; QSharedPointer ssoPtr; std::shared_ptr dmPtr; virtual void SetUp() { namPtr.reset(new MockNetworkAccessManager()); clientPtr.reset(new NiceMock(namPtr)); clientPtr->setCredentialsService(ssoPtr); dmPtr.reset(new click::DownloadManager(clientPtr, sdmPtr)); } MOCK_METHOD2(start_callback, void(std::string, click::DownloadManager::Error)); MOCK_METHOD1(progress_callback, void(std::string)); }; } TEST_F(DownloadManagerTest, testStartCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); dmPtr->start("", "", "", [](std::string, click::DownloadManager::Error) {}); } TEST_F(DownloadManagerTest, testStartCallbackCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(reply.instance, attribute(_)).WillOnce(Return(QVariant(0))); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return("")); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, start_callback(_, _)).Times(1); dmPtr->start("", "", "", [this](std::string msg, click::DownloadManager::Error err) { start_callback(msg, err); }); response->replyFinished(); } TEST_F(DownloadManagerTest, testStartHTTPForbidden) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(reply.instance, attribute(_)).WillOnce(Return(QVariant(403))); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return("")); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, start_callback(StartsWith("Unhandled HTTP response code:"), click::DownloadManager::Error::DownloadInstallError)).Times(1); dmPtr->start("", "", "", [this](std::string msg, click::DownloadManager::Error err) { start_callback(msg, err); }); response->replyFinished(); } TEST_F(DownloadManagerTest, testStartHTTPError) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(reply.instance, errorString()) .WillOnce(Return(QString("ERROR"))); EXPECT_CALL(reply.instance, attribute(_)).WillOnce(Return(QVariant(404))); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return("")); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, start_callback("ERROR (203)", click::DownloadManager::Error::DownloadInstallError)).Times(1); dmPtr->start("", "", "", [this](std::string msg, click::DownloadManager::Error err) { start_callback(msg, err); }); response->errorHandler(QNetworkReply::ContentNotFoundError); } TEST_F(DownloadManagerTest, testStartCredentialsError) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QSharedPointer sso(new MockCredentialsService()); dmPtr->setCredentialsService(sso); EXPECT_CALL(reply.instance, errorString()) .WillOnce(Return(QString("ERROR"))); EXPECT_CALL(reply.instance, attribute(_)).WillOnce(Return(QVariant(401))); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return("")); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*(sso.data()), invalidateCredentials()); EXPECT_CALL(*this, start_callback("ERROR (201)", click::DownloadManager::Error::CredentialsError)).Times(1); dmPtr->start("", "", "test.package", [this](std::string msg, click::DownloadManager::Error err) { start_callback(msg, err); }); response->errorHandler(QNetworkReply::ContentAccessDenied); } // FIXME: createDownload() SEGV under tests TEST_F(DownloadManagerTest, DISABLED_testStartDownloadCreated) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(reply.instance, rawHeader(QByteArray("X-Click-Token"))) .Times(1) .WillOnce(Return(QString("clicktoken"))); EXPECT_CALL(reply.instance, attribute(_)).WillOnce(Return(QVariant(200))); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return("")); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*sdmPtr, createDownload(_, _, _)); dmPtr->start("", "", "test.package", [this](std::string msg, click::DownloadManager::Error err) { start_callback(msg, err); }); response->replyFinished(); } // FIXME: getAllDownloadsWithMetadata() SEGV under tests TEST_F(DownloadManagerTest, DISABLED_testGetProgressNoDownloads) { EXPECT_CALL(*sdmPtr, getAllDownloadsWithMetadata(_, _, _, _)) .Times(1) .WillOnce(InvokeArgument<3>(QStringLiteral(""), QStringLiteral(""), nullptr)); dmPtr->get_progress("com.example.test", [this](std::string object_path) { progress_callback(object_path); }); } ./libclickscope/tests/CMakeLists.txt0000644000015600001650000000364412676763577017646 0ustar jenkinsjenkinsset (LIBCLICKSCOPE_TESTS_TARGET libclick-scope-tests) find_package(Threads) # Qt5 bits SET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package(Qt5Core REQUIRED) find_package(Qt5Sql REQUIRED) include_directories ( ${CMAKE_SOURCE_DIR}/libclickscope ${JSON_CPP_INCLUDE_DIRS} ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR} ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test_data.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/test_data.cpp) add_definitions(-DTEST_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_executable (${LIBCLICKSCOPE_TESTS_TARGET} mock_network_access_manager.h mock_ubuntuone_credentials.h mock_webclient.h fake_json.cpp test_bootstrap.cpp test_configuration.cpp test_departments.cpp test_departments-db.cpp test_download_manager.cpp test_index.cpp test_interface.cpp test_package.cpp test_pay.cpp test_preview.cpp test_reviews.cpp test_smartconnect.cpp test_utils.cpp test_webclient.cpp ${CMAKE_CURRENT_BINARY_DIR}/test_data.cpp ) qt5_use_modules(${LIBCLICKSCOPE_TESTS_TARGET} Core Sql) target_link_libraries(${LIBCLICKSCOPE_TESTS_TARGET} ${SCOPE_LIB_NAME} ${UNITY_SCOPES_LDFLAGS} ${UBUNTUONE_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS} ${JSON_CPP_LDFLAGS} gmock gmock_main ${CMAKE_THREAD_LIBS_INIT} ) add_custom_target (test-libclickscope COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${LIBCLICKSCOPE_TESTS_TARGET} DEPENDS ${LIBCLICKSCOPE_TESTS_TARGET} ) add_custom_target (test-libclickscope-valgrind COMMAND valgrind --tool=memcheck ${CMAKE_CURRENT_BINARY_DIR}/${LIBCLICKSCOPE_TESTS_TARGET} DEPENDS ${LIBCLICKSCOPE_TESTS_TARGET} ) add_custom_target (test-libclickscope-leaks COMMAND valgrind --tool=memcheck --track-origins=yes --num-callers=40 --leak-resolution=high --leak-check=full ${CMAKE_CURRENT_BINARY_DIR}/${LIBCLICKSCOPE_TESTS_TARGET} DEPENDS ${LIBCLICKSCOPE_TESTS_TARGET} ) add_subdirectory(integration) ./libclickscope/tests/mock_ubuntuone_credentials.h0000644000015600001650000000304612676763604022654 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ class MockCredentialsService : public click::CredentialsService { public: MOCK_METHOD0(getCredentials, void()); MOCK_METHOD0(invalidateCredentials, void()); }; ./libclickscope/tests/test_configuration.cpp0000644000015600001650000002471012676763577021515 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include using namespace click; namespace { class FakeConfiguration : public click::Configuration { public: MOCK_METHOD0(deviceIdFromWhoopsie, std::string()); MOCK_METHOD2(list_folder, std::vector( const std::string& folder, const std::string& pattern)); MOCK_CONST_METHOD2(get_dconf_strings, const std::vector(const std::string& schema, const std::string& key)); using Configuration::get_default_core_apps; }; } TEST(Configuration, getCoreAppsFound) { using namespace ::testing; FakeConfiguration c; EXPECT_CALL(c, get_dconf_strings(Configuration::COREAPPS_SCHEMA, Configuration::COREAPPS_KEY)) .WillOnce(Return(std::vector{"package1", "package2"})); auto found_apps = c.get_core_apps(); auto expected_apps = std::vector{"package1", "package2"}; ASSERT_EQ(found_apps, expected_apps); } TEST(Configuration, getCoreAppsEmpty) { using namespace ::testing; FakeConfiguration c; EXPECT_CALL(c, get_dconf_strings(Configuration::COREAPPS_SCHEMA, Configuration::COREAPPS_KEY)) .WillOnce(Return(std::vector{})); auto found_apps = c.get_core_apps(); auto expected_apps = c.get_default_core_apps(); ASSERT_EQ(found_apps, expected_apps); } TEST(Configuration, getIgnoredAppsEmpty) { using namespace ::testing; FakeConfiguration c; auto expected_apps = std::vector{}; EXPECT_CALL(c, get_dconf_strings(Configuration::COREAPPS_SCHEMA, Configuration::IGNORED_KEY)) .WillOnce(Return(expected_apps)); auto ignored_apps = c.get_ignored_apps(); ASSERT_EQ(expected_apps, ignored_apps); } TEST(Configuration, getIgnoredAppsFound) { using namespace ::testing; FakeConfiguration c; auto expected_apps = std::vector{"package1", "package2"}; EXPECT_CALL(c, get_dconf_strings(Configuration::COREAPPS_SCHEMA, Configuration::IGNORED_KEY)) .WillOnce(Return(expected_apps)); auto ignored_apps = c.get_ignored_apps(); ASSERT_EQ(expected_apps, ignored_apps); } TEST(Configuration, getAvailableFrameworksUsesRightFolder) { using namespace ::testing; FakeConfiguration locator; EXPECT_CALL(locator, list_folder(Configuration::FRAMEWORKS_FOLDER, _)) .Times(1).WillOnce(Return(std::vector())); locator.get_available_frameworks(); } TEST(Configuration, getAvailableFrameworksUsesRightPattern) { using namespace ::testing; FakeConfiguration locator; EXPECT_CALL(locator, list_folder(_, Configuration::FRAMEWORKS_PATTERN)) .Times(1).WillOnce(Return(std::vector())); locator.get_available_frameworks(); } TEST(Configuration, getAvailableFrameworksTwoResults) { using namespace ::testing; FakeConfiguration locator; std::vector response = {"abc.framework", "def.framework"}; EXPECT_CALL(locator, list_folder(_, _)) .Times(1) .WillOnce(Return(response)); auto frameworks = locator.get_available_frameworks(); std::vector expected = {"abc", "def"}; EXPECT_EQ(expected, frameworks); } TEST(Configuration, getAvailableFrameworksNoResults) { using namespace ::testing; FakeConfiguration locator; std::vector response = {}; EXPECT_CALL(locator, list_folder(_, _)) .Times(1) .WillOnce(Return(response)); auto frameworks = locator.get_available_frameworks(); EXPECT_EQ(0, frameworks.size()); } TEST(Configuration, getLanguageCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US.UTF-8", 1), 0); EXPECT_EQ(Configuration().get_language(), "en_US"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, getLanguageUnsetFallback) { ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); ASSERT_EQ(Configuration().get_language(), "C"); } TEST(Configuration, getLanguageNoCharsetCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US", 1), 0); EXPECT_EQ(Configuration().get_language(), "en_US"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, getLanguageNoRegionOrCharsetCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en", 1), 0); EXPECT_EQ(Configuration().get_language(), "en"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, getLanguageBaseCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US.UTF-8", 1), 0); EXPECT_EQ(Configuration().get_language_base(), "en"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, getLanguageBaseUnsetFallback) { ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); ASSERT_EQ(Configuration().get_language_base(), "C"); } TEST(Configuration, getLanguageBaseNoCharseteCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US", 1), 0); EXPECT_EQ(Configuration().get_language_base(), "en"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, getLanguageBaseNoRegionOrCharseteCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en", 1), 0); EXPECT_EQ(Configuration().get_language_base(), "en"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, getAcceptLanguagesCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US.UTF-8", 1), 0); EXPECT_EQ(Configuration().get_accept_languages(), "en-US, en"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, getAcceptLanguagesUnsetFallback) { ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); ASSERT_EQ(Configuration().get_accept_languages(), "C"); } TEST(Configuration, getAcceptLanguagesNoCharseteCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en_US", 1), 0); EXPECT_EQ(Configuration().get_accept_languages(), "en-US, en"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, getAcceptLanguagesNoRegionOrCharseteCorrect) { ASSERT_EQ(setenv(Configuration::LANGUAGE_ENVVAR, "en", 1), 0); EXPECT_EQ(Configuration().get_accept_languages(), "en"); ASSERT_EQ(unsetenv(Configuration::LANGUAGE_ENVVAR), 0); } TEST(Configuration, isFullLangCodeTestAllFullLangCodes) { for (auto lang: Configuration::FULL_LANG_CODES) { EXPECT_TRUE(Configuration::is_full_lang_code(lang)); } } TEST(Configuration, isFullLangCodeReturnsFalseForEN) { ASSERT_FALSE(Configuration::is_full_lang_code("en_US")); } TEST(Configuration, getArchitectureOverride) { ASSERT_EQ(setenv(Configuration::ARCH_ENVVAR, "otherarch", 1), 0); EXPECT_EQ("otherarch", Configuration().get_architecture()); ASSERT_EQ(unsetenv(Configuration::ARCH_ENVVAR), 0); } TEST(Configuration, getArchitectureSystem) { ASSERT_EQ(unsetenv(Configuration::ARCH_ENVVAR), 0); ASSERT_NE("otherarch", Configuration().get_architecture()); } TEST(Configuration, getPurchasesEnabledOverrideTrue) { ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); EXPECT_EQ(true, Configuration().get_purchases_enabled()); ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); } TEST(Configuration, getPurchasesEnabledOverrideFalse) { ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "0", 1), 0); EXPECT_EQ(false, Configuration().get_purchases_enabled()); ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); } TEST(Configuration, getPurchasesEnabledDefault) { ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); ASSERT_EQ(true, Configuration().get_purchases_enabled()); } TEST(Configuration, getCurrencyDefault) { ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); EXPECT_EQ("USD", Configuration().get_currency()); } TEST(Configuration, getCurrencyFallback) { ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); EXPECT_EQ("HKD", Configuration().get_currency("HKD")); } TEST(Configuration, getCurrencyFallbackUnknown) { ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); EXPECT_EQ("USD", Configuration().get_currency("not_valid")); } TEST(Configuration, getCurrencyOverride) { ASSERT_EQ(setenv(Configuration::CURRENCY_ENVVAR, "TWD", 1), 0); EXPECT_EQ("TWD", Configuration().get_currency()); ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); } TEST(Configuration, getCurrencyOverrideUnknown) { ASSERT_EQ(setenv(Configuration::CURRENCY_ENVVAR, "not_valid", 1), 0); EXPECT_EQ("USD", Configuration().get_currency()); ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); } TEST(Configuration, getDeviceIdCached) { using namespace ::testing; FakeConfiguration c; FakeConfiguration d; const char* expected = "HAL-9000"; EXPECT_CALL(c, deviceIdFromWhoopsie()).Times(1). WillOnce(Return(expected)); EXPECT_EQ(expected, c.get_device_id()); EXPECT_CALL(d, deviceIdFromWhoopsie()).Times(0); EXPECT_EQ(expected, d.get_device_id()); } ./libclickscope/tests/test_preview.cpp0000644000015600001650000005521212676763577020330 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::testing; using ::testing::Matcher; using namespace unity::scopes; class FakeResult : public Result { public: FakeResult(VariantMap& vm) : Result(vm) { } }; class FakeIndex : public click::Index { public: FakeIndex() { } click::web::Cancellable get_details(const std::string& /*package_name*/, std::function callback) override { callback(click::PackageDetails(), Error::NetworkError); return click::web::Cancellable(); } }; class FakeReviews : public click::Reviews { public: FakeReviews() { } click::web::Cancellable fetch_reviews(const std::string &/*package_name*/, std::function callback) override { callback(click::ReviewList(), Error::NoError); return click::web::Cancellable(); } }; class FakePreview : public click::PreviewStrategy { public: FakePreview(Result& result) : click::PreviewStrategy::PreviewStrategy(result) { index.reset(new FakeIndex()); reviews.reset(new FakeReviews()); } void run(const PreviewReplyProxy &/*reply*/) { } void run_under_qt(const std::function &task) { // when testing, do not actually run under qt task(); } using click::PreviewStrategy::screenshotsWidgets; using click::PreviewStrategy::descriptionWidgets; using click::PreviewStrategy::build_other_metadata; using click::PreviewStrategy::build_updates_table; using click::PreviewStrategy::build_whats_new; using click::PreviewStrategy::populateDetails; using click::PreviewStrategy::isRefundable; }; class PreviewsBaseTest : public Test { public: VariantMap vm; VariantMap internal; VariantMap attrs; PreviewsBaseTest() { attrs["uri"] = "fake://result"; vm["internal"] = internal; vm["attrs"] = attrs; } }; class PreviewStrategyTest : public PreviewsBaseTest { }; TEST_F(PreviewStrategyTest, testScreenshotsWidget) { FakeResult result{vm}; FakePreview preview{result}; click::PackageDetails details; details.main_screenshot_url = "sshot1"; details.more_screenshots_urls = {"sshot2", "sshot3"}; auto widgets = preview.screenshotsWidgets(details); ASSERT_EQ(widgets.size(), 1); auto first_widget = widgets.front(); auto attributes = first_widget.attribute_values(); auto sources = attributes["sources"].get_array(); ASSERT_EQ(sources[0].get_string(), "sshot1"); ASSERT_EQ(sources[1].get_string(), "sshot2"); ASSERT_EQ(sources[2].get_string(), "sshot3"); } TEST_F(PreviewStrategyTest, testEmptyResults) { FakeResult result{vm}; result["name"] = "fake name"; FakePreview preview{result}; preview.populateDetails( [](const click::PackageDetails &){ }, [](const click::ReviewList&, click::Reviews::Error){ }); } MATCHER_P(PreviewWidgetsListMatchers, widgets, "") { if (arg.size() == widgets.size()) { auto it1 = widgets.begin(); auto it2 = arg.begin(); while (*it1 != it2->id()) { *result_listener << "Preview widgets don't match: " << it2->id() << ", expected " << *it1; return false; } return true; } *result_listener << "Preview widgets list don't match: "; for (auto const& widget: arg) { *result_listener << widget.id() << ", "; } *result_listener << "expected: "; for (auto const& widget: widgets) { *result_listener << widget << ", "; } return false; } MATCHER_P(LayoutMatches, layouts, "") { if (arg.size() != layouts.size()) { *result_listener << "Layout lists sizes don't match, " << arg.size() << " vs " << layouts.size(); return false; } auto it1 = layouts.begin(); auto it2 = arg.begin(); while (it1 != layouts.end()) { if (it1->serialize() != it2->serialize()) { *result_listener << "Layouts don't match: " << unity::scopes::Variant(it1->serialize()).serialize_json() << " vs " << unity::scopes::Variant(it2->serialize()).serialize_json(); return false; } it1++; it2++; } return true; } TEST_F(PreviewStrategyTest, testPushCachedWidgets) { unity::scopes::testing::MockPreviewReply reply; std::shared_ptr replyptr{&reply, [](unity::scopes::testing::MockPreviewReply*){}}; FakeResult result{vm}; FakePreview preview{result}; click::PackageDetails details; details.main_screenshot_url = "sshot1"; details.license = "GPL"; details.company_name = "Ubuntu"; details.website = "http://ubuntu.com"; details.changelog = "Foo"; details.version = "0.1"; details.download_url = "http://ubuntu.com/"; details.description = "Foo"; click::CachedPreviewWidgets cache; scopes::PreviewWidget buttons("buttons", "actions"); unity::scopes::ColumnLayout single_column(1); single_column.add_column({"hdr","buttons","screenshots","summary","other_metadata","updates_table","whats_new"}); unity::scopes::ColumnLayout two_columns(2); two_columns.add_column({"hdr","buttons","screenshots","summary"}); two_columns.add_column({"other_metadata","updates_table","whats_new"}); unity::scopes::ColumnLayoutList expected_layout {single_column, two_columns}; std::vector expected_widgets {"hdr", "buttons", "screenshots", "summary", "other_metadata", "updates_table", "whats_new"}; EXPECT_CALL(*replyptr, register_layout(Matcher(LayoutMatches(expected_layout)))); EXPECT_CALL(*replyptr, push(Matcher(PreviewWidgetsListMatchers(expected_widgets)))); preview.pushPackagePreviewWidgets(cache, details, {buttons}); cache.flush(replyptr); } class PreviewStrategyDescriptionTest : public PreviewStrategyTest { public: FakeResult result{vm}; FakePreview preview{result}; click::PackageDetails details; unity::scopes::PreviewWidgetList widgets; PreviewStrategyDescriptionTest() { } void assertWidgetAttribute(int n, std::string attribute_name, std::string expected_value) { ASSERT_GT(widgets.size(), n); auto widget = std::next(widgets.begin(), n); auto attributes = widget->attribute_values(); ASSERT_EQ(expected_value, attributes[attribute_name].get_string()); } void assertWidgetNestedVariantArray(int n, int x, std::string expected_title, std::string expected_value) { auto widget = std::next(widgets.begin(), n); auto attributes = widget->attribute_values(); auto found_title = attributes["values"].get_array()[x].get_array()[0].get_string(); ASSERT_EQ(found_title, expected_title); auto found_value = attributes["values"].get_array()[x].get_array()[1].get_string(); ASSERT_EQ(found_value, expected_value); } }; TEST_F(PreviewStrategyDescriptionTest, testDescriptionWidgetsFull) { boost::locale::time_zone::global("UTC"); details = click::PackageDetails::from_json(FAKE_JSON_PACKAGE_DETAILS); widgets = preview.descriptionWidgets(details); assertWidgetAttribute(0, "title", "Info"); assertWidgetAttribute(0, "text", details.description); assertWidgetNestedVariantArray(1, 0, "Publisher/Creator", "Fake Publisher"); assertWidgetNestedVariantArray(1, 1, "Seller", "Fake Company"); assertWidgetNestedVariantArray(1, 2, "Website", "http://example.com"); assertWidgetNestedVariantArray(1, 3, "Contact", "http://example.com/support"); assertWidgetNestedVariantArray(1, 4, "License", "Proprietary"); assertWidgetAttribute(2, "title", "Updates"); assertWidgetNestedVariantArray(2, 0, "Version number", "0.2"); assertWidgetNestedVariantArray(2, 1, "Last updated", "Jul 3, 2014"); assertWidgetNestedVariantArray(2, 2, "First released", "Nov 4, 2013"); assertWidgetNestedVariantArray(2, 3, "Size", "173.4 KiB"); assertWidgetAttribute(3, "title", "What's new"); assertWidgetAttribute(3, "text", preview.build_whats_new(details)); } TEST_F(PreviewStrategyDescriptionTest, testDescriptionWidgetsMinimal) { details = click::PackageDetails::from_json(FAKE_JSON_PACKAGE_DETAILS_DEB); widgets = preview.descriptionWidgets(details); ASSERT_EQ(1, widgets.size()); assertWidgetAttribute(0, "title", "Info"); assertWidgetAttribute(0, "text", details.description); } TEST_F(PreviewStrategyDescriptionTest, testDescriptionWidgetsNone) { widgets = preview.descriptionWidgets(details); ASSERT_EQ(0, widgets.size()); } class FakePreviewStrategy : public click::PreviewStrategy { public: FakePreviewStrategy(const unity::scopes::Result& result) : PreviewStrategy(result) {} using click::PreviewStrategy::pushPackagePreviewWidgets; std::vector call_order; virtual void run(unity::scopes::PreviewReplyProxy const& /*reply*/) { } virtual unity::scopes::PreviewWidgetList screenshotsWidgets (const click::PackageDetails &/*details*/) { call_order.push_back("screenshots"); return {}; } virtual unity::scopes::PreviewWidgetList headerWidgets (const click::PackageDetails &/*details*/) { call_order.push_back("header"); return {}; } }; TEST_F(PreviewStrategyTest, testScreenshotsPushedAfterHeader) { FakeResult result{vm}; unity::scopes::testing::MockPreviewReply reply; std::shared_ptr replyptr{&reply, [](unity::scopes::testing::MockPreviewReply*){}}; click::PackageDetails details; FakePreviewStrategy preview{result}; preview.pushPackagePreviewWidgets(replyptr, details, {}); std::vector expected_order {"header", "screenshots"}; ASSERT_EQ(expected_order, preview.call_order); } class StrategyChooserTest : public Test { protected: unity::scopes::testing::Result result; unity::scopes::ActionMetadata metadata; unity::scopes::VariantMap metadict; QSharedPointer client; QSharedPointer pay_package; QSharedPointer dm; std::shared_ptr depts; const std::string FAKE_SHA512 = "FAKE_SHA512"; public: StrategyChooserTest() : metadata("en_EN", "phone") { } }; class MockablePreview : public click::Preview { public: MockablePreview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata) : click::Preview(result, metadata) { } MOCK_METHOD6(build_installing, click::PreviewStrategy*(const std::string&, const std::string&, const unity::scopes::Result&, const QSharedPointer&, const QSharedPointer&, std::shared_ptr)); }; TEST_F(StrategyChooserTest, testSha512IsUsed) { metadict["action_id"] = click::Preview::Actions::INSTALL_CLICK; metadict["download_url"] = "fake_download_url"; metadict["download_sha512"] = FAKE_SHA512; metadata.set_scope_data(unity::scopes::Variant(metadict)); MockablePreview preview(result, metadata); EXPECT_CALL(preview, build_installing(_, FAKE_SHA512, _, _, _, _)); preview.choose_strategy(client, pay_package, dm, depts); } class UninstalledPreviewTest : public PreviewsBaseTest { public: FakeResult result{vm}; click::PackageDetails details; unity::scopes::PreviewWidgetList widgets; QSharedPointer client; QSharedPointer pay_package; QSharedPointer sdm; std::shared_ptr depts; unity::scopes::testing::MockPreviewReply reply; std::shared_ptr replyptr{&reply, [](unity::scopes::testing::MockPreviewReply*){}}; }; class FakeBaseUninstalledPreview : public click::UninstalledPreview { std::string object_path; public: FakeBaseUninstalledPreview(const std::string& object_path, const unity::scopes::Result& result, const QSharedPointer& client, const std::shared_ptr& depts, const QSharedPointer& manager, const QSharedPointer pay_package) : click::UninstalledPreview(result, client, depts, manager, pay_package), object_path(object_path) { } void populateDetails(std::function details_callback, std::function reviews_callback) { click::PackageDetails details; details_callback(details); reviews_callback({}, click::Reviews::Error::NoError); } }; class FakeUninstalledPreview : public FakeBaseUninstalledPreview { public: MOCK_METHOD1(uninstalledActionButtonWidgets, scopes::PreviewWidgetList (const click::PackageDetails &details)); MOCK_METHOD1(progressBarWidget, scopes::PreviewWidgetList(const std::string& object_path)); FakeUninstalledPreview(const std::string& object_path, const unity::scopes::Result& result, const QSharedPointer& client, const std::shared_ptr& depts, const QSharedPointer& manager, const QSharedPointer pay_package) : FakeBaseUninstalledPreview(object_path, result, client, depts, manager, pay_package) { } }; // FIXME: Needs Qt main loop TEST_F(UninstalledPreviewTest, DISABLED_testDownloadInProgress) { std::string fake_object_path = "/fake/object/path"; result["name"] = "fake_app_name"; scopes::PreviewWidgetList response; FakeUninstalledPreview preview(fake_object_path, result, client, depts, sdm, pay_package); EXPECT_CALL(preview, progressBarWidget(_)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*replyptr, register_layout(_)); preview.run(replyptr); } // FIXME: Needs Qt main loop TEST_F(UninstalledPreviewTest, DISABLED_testNoDownloadProgress) { std::string fake_object_path = ""; result["name"] = "fake_app_name"; scopes::PreviewWidgetList response; FakeUninstalledPreview preview(fake_object_path, result, client, depts, sdm, pay_package); EXPECT_CALL(preview, uninstalledActionButtonWidgets(_)) .Times(1) .WillOnce(Return(response)); preview.run(replyptr); } class FakeUninstalledRefundablePreview : FakeBaseUninstalledPreview { public: FakeUninstalledRefundablePreview(const unity::scopes::Result& result, const QSharedPointer& client, const std::shared_ptr& depts, const QSharedPointer& manager, const QSharedPointer pay_package) : FakeBaseUninstalledPreview(std::string{""}, result, client, depts, manager, pay_package){ } using click::UninstalledPreview::uninstalledActionButtonWidgets; MOCK_METHOD0(isRefundable, bool()); }; unity::scopes::VariantArray get_actions_from_widgets(const unity::scopes::PreviewWidgetList& widgets, int widget_number) { auto widget = *std::next(widgets.begin(), widget_number); return widget.attribute_values()["actions"].get_array(); } std::string get_action_from_widgets(const unity::scopes::PreviewWidgetList& widgets, int widget_number, int action_number) { auto actions = get_actions_from_widgets(widgets, widget_number); auto selected_action = actions.at(action_number).get_dict(); return selected_action["id"].get_string(); } TEST_F(UninstalledPreviewTest, testIsRefundableButtonShown) { result["name"] = "fake_app_name"; result["price"] = 2.99; result["purchased"] = true; FakeUninstalledRefundablePreview preview(result, client, depts, sdm, pay_package); click::PackageDetails pkgdetails; EXPECT_CALL(preview, isRefundable()).Times(1) .WillOnce(Return(true)); auto widgets = preview.uninstalledActionButtonWidgets(pkgdetails); ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "cancel_purchase_uninstalled"); } TEST_F(UninstalledPreviewTest, testIsRefundableButtonNotShown) { result["name"] = "fake_app_name"; result["price"] = 2.99; result["purchased"] = true; FakeUninstalledRefundablePreview preview(result, client, depts, sdm, pay_package); click::PackageDetails pkgdetails; EXPECT_CALL(preview, isRefundable()).Times(1) .WillOnce(Return(false)); auto widgets = preview.uninstalledActionButtonWidgets(pkgdetails); ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 1); } class InstalledPreviewTest : public Test { protected: unity::scopes::testing::Result result; unity::scopes::ActionMetadata metadata; unity::scopes::VariantMap metadict; QSharedPointer client; QSharedPointer pay_package; std::shared_ptr depts; public: InstalledPreviewTest() : metadata("en_EN", "phone") { } }; class FakeInstalledRefundablePreview : public click::InstalledPreview { public: FakeInstalledRefundablePreview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata, const QSharedPointer client, const QSharedPointer pay_package, const std::shared_ptr depts) : click::InstalledPreview(result, metadata, client, pay_package, depts) { } using click::InstalledPreview::createButtons; MOCK_METHOD0(isRefundable, bool()); }; TEST_F(InstalledPreviewTest, testIsRefundableButtonShownFromStore) { result["price"] = 3.99f; FakeInstalledRefundablePreview preview(result, metadata, client, pay_package, depts); EXPECT_CALL(preview, isRefundable()).Times(1) .WillOnce(Return(true)); click::Manifest manifest; manifest.removable = true; auto widgets = preview.createButtons("fake uri", manifest); ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 2); ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "cancel_purchase_installed"); } TEST_F(InstalledPreviewTest, testIsRefundableButtonNotShownFromApps) { FakeInstalledRefundablePreview preview(result, metadata, client, pay_package, depts); EXPECT_CALL(preview, isRefundable()).Times(0); click::Manifest manifest; manifest.removable = true; auto widgets = preview.createButtons("fake uri", manifest); ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 2); ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "uninstall_click"); } TEST_F(InstalledPreviewTest, testIsRefundableButtonNotShown) { FakeInstalledRefundablePreview preview(result, metadata, client, pay_package, depts); EXPECT_CALL(preview, isRefundable()).Times(0); click::Manifest manifest; manifest.removable = true; click::PackageDetails details; auto widgets = preview.createButtons("fake uri", manifest); ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 2); ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "uninstall_click"); } class FakeCancelPurchasePreview : public click::CancelPurchasePreview { public: FakeCancelPurchasePreview(const unity::scopes::Result& result, bool installed) : click::CancelPurchasePreview(result, installed) { } using click::CancelPurchasePreview::build_widgets; }; class CancelPurchasePreviewTest : public PreviewsBaseTest { }; TEST_F(CancelPurchasePreviewTest, testNoShowsInstalled) { FakeResult result{vm}; result["title"] = "fake app"; FakeCancelPurchasePreview preview(result, true); auto widgets = preview.build_widgets(); auto action = get_action_from_widgets(widgets, 1, 0); ASSERT_EQ(action, "show_installed"); } TEST_F(CancelPurchasePreviewTest, testNoShowsUninstalled) { FakeResult result{vm}; result["title"] = "fake app"; FakeCancelPurchasePreview preview(result, false); auto widgets = preview.build_widgets(); auto action = get_action_from_widgets(widgets, 1, 0); ASSERT_EQ(action, "show_uninstalled"); } TEST_F(CancelPurchasePreviewTest, testYesCancelsPurchase) { FakeResult result{vm}; result["title"] = "fake app"; FakeCancelPurchasePreview preview(result, false); auto widgets = preview.build_widgets(); auto action = get_action_from_widgets(widgets, 1, 1); ASSERT_EQ(action, "confirm_cancel_purchase_uninstalled"); } ./libclickscope/tests/test_departments.cpp0000644000015600001650000001253612676763577021177 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include "fake_json.h" #include #include TEST(DepartmentsTest, testParsing) { const std::string jsonstr(FAKE_JSON_DEPARTMENTS_ONLY); auto depts = click::Department::from_json(jsonstr); EXPECT_EQ(3u, depts.size()); auto it = depts.cbegin(); { auto dep = *it; EXPECT_EQ("games", dep->id()); EXPECT_EQ("Games", dep->name()); EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Games", dep->href()); EXPECT_FALSE(dep->has_children_flag()); auto subdepts = dep->sub_departments(); EXPECT_EQ(1u, subdepts.size()); auto sit = subdepts.cbegin(); EXPECT_EQ("Board Games", (*sit)->name()); } { ++it; auto dep = *it; EXPECT_EQ("graphics", dep->id()); EXPECT_EQ("Graphics", dep->name()); EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Graphics", dep->href()); EXPECT_FALSE(dep->has_children_flag()); auto subdepts = dep->sub_departments(); EXPECT_EQ(1u, subdepts.size()); auto sit = subdepts.cbegin(); EXPECT_EQ("Drawing", (*sit)->name()); } { ++it; auto dep = *it; EXPECT_EQ("internet", dep->id()); EXPECT_EQ("Internet", dep->name()); EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet", dep->href()); EXPECT_FALSE(dep->has_children_flag()); auto subdepts = dep->sub_departments(); EXPECT_EQ(3u, subdepts.size()); auto sit = subdepts.cbegin(); auto subdep = *sit; EXPECT_EQ("Chat", subdep->name()); EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet/Chat", subdep->href()); subdep = *(++sit); EXPECT_EQ("Mail", subdep->name()); EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet/Mail", subdep->href()); subdep = *(++sit); EXPECT_EQ("Web Browsers", subdep->name()); EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet/Web+Browsers", subdep->href()); } } TEST(DepartmentsTest, testParsingErrors) { // invalid json { const std::string jsonstr("{{{"); auto depts = click::Department::from_json(jsonstr); EXPECT_EQ(0, depts.size()); } // one of the departments is invalid { const std::string jsonstr(FAKE_JSON_BROKEN_DEPARTMENTS); auto depts = click::Department::from_json(jsonstr); EXPECT_EQ(1, depts.size()); } } TEST(DepartmentsTest, testLookup) { auto dep_games = std::make_shared("games", "Games", "http://foobar.com/", false); auto dep_rpg = std::make_shared("rpg", "RPG", "http://ubuntu.com/", false); auto dep_strategy = std::make_shared("strategy", "Strategy", "", false); const std::list departments {dep_rpg, dep_strategy}; dep_games->set_subdepartments(departments); const std::list root {dep_games}; click::DepartmentLookup lut; lut.rebuild(root); { EXPECT_EQ(2u, lut.size()); EXPECT_EQ("games", lut.get_parent("strategy")->id()); EXPECT_EQ("games", lut.get_parent("rpg")->id()); EXPECT_EQ(nullptr, lut.get_parent("games")); } { auto info = lut.get_department_info("games"); EXPECT_EQ("games", info->id()); EXPECT_EQ("Games", info->name()); EXPECT_EQ("http://foobar.com/", info->href()); EXPECT_EQ(false, info->has_children_flag()); auto sub = info->sub_departments(); EXPECT_EQ(2u, sub.size()); auto it = sub.begin(); EXPECT_EQ("rpg", (*it)->id()); EXPECT_EQ("RPG", (*it)->name()); EXPECT_EQ("http://ubuntu.com/", (*it)->href()); ++it; EXPECT_EQ("strategy", (*it)->id()); EXPECT_EQ("Strategy", (*it)->name()); } { lut.rebuild(root); EXPECT_EQ(2u, lut.size()); } } ./libclickscope/tests/mock_ubuntu_download_manager.h0000644000015600001650000001253112676763577023166 0ustar jenkinsjenkins/* * Copyright (C) 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef _MOCK_UBUNTU_DOWNLOAD_MANAGER_H_ #define _MOCK_UBUNTU_DOWNLOAD_MANAGER_H_ #include #include #include #include #include #include #include class MockDownload : public Ubuntu::DownloadManager::Download { public: MockDownload() : Ubuntu::DownloadManager::Download(){}; MockDownload(Ubuntu::DownloadManager::Error *err) : Ubuntu::DownloadManager::Download(err) {}; // mock ALL methods so that we do not have an abstract class MOCK_METHOD0(start, void()); MOCK_METHOD0(pause, void()); MOCK_METHOD0(resume, void()); MOCK_METHOD0(cancel, void()); MOCK_METHOD1(allowMobileDownload, void(bool)); MOCK_METHOD0(isMobileDownloadAllowed, bool()); MOCK_METHOD1(setThrottle, void(qulonglong)); MOCK_METHOD0(throttle, qulonglong()); MOCK_CONST_METHOD0(id, QString()); MOCK_METHOD1(setDestinationDir, void(const QString& path)); MOCK_METHOD0(metadata, QVariantMap()); MOCK_METHOD1(setMetadata, void(QVariantMap)); MOCK_METHOD0(progress, qulonglong()); MOCK_METHOD0(totalSize, qulonglong()); MOCK_CONST_METHOD0(clickPackage, QString()); MOCK_CONST_METHOD0(title, QString()); MOCK_CONST_METHOD0(showInIndicator, bool()); MOCK_CONST_METHOD0(isError, bool()); MOCK_CONST_METHOD0(error, Error*()); MOCK_METHOD0(headers, QMap()); MOCK_METHOD1(setHeaders, void(QMap)); MOCK_CONST_METHOD0(destinationApp, QString()); MOCK_METHOD0(collected, void()); MOCK_METHOD0(filePath, QString()); MOCK_METHOD0(state, Download::State()); }; class MockError : public Ubuntu::DownloadManager::Error { public: MockError() : Ubuntu::DownloadManager::Error(Ubuntu::DownloadManager::Error::Type::Process, 0) {}; MOCK_METHOD0(errorString, QString()); }; class MockDownloadsList : public Ubuntu::DownloadManager::DownloadsList { public: MockDownloadsList() : Ubuntu::DownloadManager::DownloadsList() {}; MOCK_CONST_METHOD0(downloads, QList>()); MOCK_CONST_METHOD0(isError, bool()); MOCK_CONST_METHOD0(error, Ubuntu::DownloadManager::Error*()); }; class MockSystemDownloadManager : public Ubuntu::DownloadManager::Manager { public: MockSystemDownloadManager() : Ubuntu::DownloadManager::Manager() {}; MOCK_METHOD1(getDownloadForId, Download*(const QString&)); MOCK_METHOD1(createDownload, void(DownloadStruct downStruct)); MOCK_METHOD3(createDownload, void(DownloadStruct downStruct, DownloadCb cb, DownloadCb errCb)); MOCK_METHOD5(createDownload, void(StructList downs, const QString &algorithm, bool allowed3G, const QVariantMap &metadata, StringMap headers)); MOCK_METHOD7(createDownload, void(StructList downs, const QString &algorithm, bool allowed3G, const QVariantMap &metadata, StringMap headers, GroupCb cb, GroupCb errCb)); MOCK_METHOD2(getAllDownloads, void(const QString &appId, bool uncompleted)); MOCK_METHOD4(getAllDownloads, void(const QString &appId, bool uncompleted, DownloadsListCb cb, DownloadsListCb errCb)); MOCK_METHOD2(getAllDownloadsWithMetadata, void(const QString &name, const QString &value)); MOCK_METHOD4(getAllDownloadsWithMetadata, void(const QString &name, const QString &value, MetadataDownloadsListCb cb, MetadataDownloadsListCb errCb)); MOCK_CONST_METHOD0(isError, bool()); MOCK_CONST_METHOD0(lastError, Error*()); MOCK_METHOD1(allowMobileDataDownload, void(bool)); MOCK_METHOD0(isMobileDataDownload, bool()); MOCK_METHOD0(defaultThrottle, qulonglong()); MOCK_METHOD1(setDefaultThrottle, void(qulonglong)); MOCK_METHOD0(exit, void()); }; #endif /* _MOCK_UBUNTU_DOWNLOAD_MANAGER_H_ */ ./libclickscope/CMakeLists.txt0000644000015600001650000000006012676763577016471 0ustar jenkinsjenkinsadd_subdirectory(click) add_subdirectory(tests) ./libclickscope/click/0000755000015600001650000000000012676763604015011 5ustar jenkinsjenkins./libclickscope/click/webclient.cpp0000644000015600001650000001775212676763604017505 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include "webclient.h" void click::web::CallParams::add(const std::string& key, const std::string& value) { query.addQueryItem(key.c_str(), value.c_str()); } std::string click::web::CallParams::operator[](const std::string& key) const { return query.queryItemValue(key.c_str()).toUtf8().data(); } bool click::web::CallParams::operator==(const CallParams &other) const { return (this->query == other.query); } struct click::web::Client::Private { Private(const QSharedPointer nam) : network_access_manager(nam) { } QSharedPointer network_access_manager; QSharedPointer sso; void setCredentialsService(const QSharedPointer& sso) { this->sso = sso; } }; click::web::Client::Client(const QSharedPointer& network_access_manager) : impl(new Private{network_access_manager}) { } click::web::Client::~Client() { } QSharedPointer click::web::Client::call( const std::string& iri, const click::web::CallParams& params) { return call(iri, "GET", false, std::map(), "", params); } QSharedPointer click::web::Client::call( const std::string& iri, const std::string& method, bool sign, const std::map& headers, const std::string& data, const click::web::CallParams& params) { QUrl url(iri.c_str()); url.setQuery(params.query); QSharedPointer request(new QNetworkRequest(url)); QSharedPointer buffer(new QBuffer()); buffer->setData(data.c_str(), data.length()); // Set the Accept-Language header for all requests. request->setRawHeader(ACCEPT_LANGUAGE_HEADER.c_str(), Configuration().get_accept_languages().c_str()); request->setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); for (const auto& kv : headers) { QByteArray header_name(kv.first.c_str(), kv.first.length()); QByteArray header_value(kv.second.c_str(), kv.second.length()); request->setRawHeader(header_name, header_value); } QSharedPointer responsePtr = QSharedPointer(new click::web::Response(request, buffer)); auto doConnect = [=]() { QByteArray verb(method.c_str(), method.length()); // // for 'get' use get method of access manager explicitly as sendCustomRequest disables the use of cache. auto reply = ((method == "GET" && buffer->size() == 0) ? impl->network_access_manager->get(*request) : (method == "HEAD") ? impl->network_access_manager->head(*request) : impl->network_access_manager->sendCustomRequest(*request, verb, buffer.data())); responsePtr->setReply(reply); }; auto deviceId = Configuration().get_device_id(); request->setRawHeader(DEVICE_ID_HEADER.c_str(), deviceId.data()); if (sign && !impl->sso.isNull()) { click::utils::SmartConnect sc(responsePtr.data()); sc.connect(impl->sso.data(), &click::CredentialsService::credentialsFound, [=](const UbuntuOne::Token& token) { QString auth_header = token.signUrl(url.toString(), method.c_str()); qDebug() << "Signed URL:" << request->url().toString(); request->setRawHeader(AUTHORIZATION_HEADER.c_str(), auth_header.toUtf8()); impl->sso.clear(); doConnect(); }); sc.connect(impl->sso.data(), &click::CredentialsService::credentialsNotFound, [=]() { impl->sso.clear(); qWarning() << "Signing reuested but no credentials found. Using unsigned URL."; doConnect(); }); // TODO: Need to handle error signal once in CredentialsService. impl->sso->getCredentials(); } else { doConnect(); } return responsePtr; } void click::web::Client::setCredentialsService(const QSharedPointer& sso) { impl->setCredentialsService(sso); } void click::web::Client::invalidateCredentials() { if (impl->sso.isNull()) { qCritical() << "Request to delete credentials, but no sso object available."; return; } impl->sso->invalidateCredentials(); } click::web::Response::Response(const QSharedPointer& request, const QSharedPointer& buffer, QObject* parent) : QObject(parent), request(request), buffer(buffer) { } void click::web::Response::setReply(QSharedPointer reply) { this->reply = reply; auto sc = new click::utils::SmartConnect(reply.data()); sc->connect(this->reply.data(), &click::network::Reply::finished, [this](){replyFinished();}); sc->connect(this->reply.data(), &click::network::Reply::error, [this](QNetworkReply::NetworkError err){errorHandler(err);}); } bool click::web::Response::has_header(const std::string& header) const { return reply->hasRawHeader(header.c_str()); } std::string click::web::Response::get_header(const std::string& header) const { return reply->rawHeader(header.c_str()).toUtf8().data(); } int click::web::Response::get_status_code() const { return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); } void click::web::Response::replyFinished() { auto response = reply->readAll(); qDebug() << "Response for: " << request->url(); qDebug() << response.toPercentEncoding(" "); emit finished(response); } void click::web::Response::errorHandler(QNetworkReply::NetworkError network_error) { auto message = reply->errorString() + QString(" (%1)").arg(network_error); qWarning() << "Network error:" << message << "\n" << reply->readAll(); int error_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); emit error(message, error_code); } void click::web::Response::abort() { reply->abort(); } click::web::Response::~Response() { } ./libclickscope/click/webclient.h0000644000015600001650000001020312676763604017132 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_WEBCLIENT_H #define CLICK_WEBCLIENT_H #include #include #include #include #include #include #include namespace click { namespace network { class AccessManager; class Reply; } namespace web { const std::string ACCEPT_LANGUAGE_HEADER ="Accept-Language"; const std::string AUTHORIZATION_HEADER = "Authorization"; const std::string CONTENT_TYPE_HEADER = "Content-Type"; const std::string DEVICE_ID_HEADER = "X-Device-Id"; const std::string CONTENT_TYPE_JSON = "application/json"; class Client; class CallParams { QUrlQuery query; friend class Client; public: void add(const std::string& key, const std::string& value); std::string operator[](const std::string& key) const; bool operator==(const CallParams &other) const; }; class Response : public QObject { Q_OBJECT public: Response(const QSharedPointer& request, const QSharedPointer& buffer, QObject* parent=0); void setReply(QSharedPointer reply); virtual void abort(); virtual ~Response(); virtual bool has_header(const std::string& header) const; virtual std::string get_header(const std::string& header) const; virtual int get_status_code() const; public slots: void replyFinished(); void errorHandler(QNetworkReply::NetworkError network_error); signals: void finished(QByteArray result); void error(QString description, int error_code); private: QSharedPointer reply; QSharedPointer request; QSharedPointer buffer; }; class Cancellable { protected: QSharedPointer response; public: Cancellable() {} Cancellable(QSharedPointer response) : response(response) {} virtual void cancel() { if (response) {response->abort();} } virtual ~Cancellable() {} }; class Client { public: Client(const QSharedPointer& networkAccessManager); virtual ~Client(); virtual QSharedPointer call( const std::string& iri, const CallParams& params = CallParams()); virtual QSharedPointer call( const std::string& iri, const std::string& method, bool sign = false, const std::map& headers = std::map(), const std::string& data = "", const CallParams& params = CallParams()); void setCredentialsService(const QSharedPointer& sso); virtual void invalidateCredentials(); private: struct Private; QScopedPointer impl; }; } } #endif // CLICK_WEBCLIENT_H ./libclickscope/click/departments-db.h0000644000015600001650000001125512676763577020110 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_DEPARTMENTS_DB_H #define CLICK_DEPARTMENTS_DB_H #include #include #include #include #include #include #include #include class QSqlError; namespace click { class DepartmentsDb { public: struct DepartmentInfo { DepartmentInfo(const std::string &id, bool children): id(id), has_children(children) {} std::string id; bool has_children; bool operator==(const DepartmentInfo& other) const { return id == other.id && has_children == other.has_children; } }; DepartmentsDb(const std::string& name, bool create = true); DepartmentsDb(const DepartmentsDb& other) = delete; DepartmentsDb& operator=(const DepartmentsDb&) = delete; virtual ~DepartmentsDb(); virtual std::string get_department_name(const std::string& department_id, const std::list& locales); virtual std::unordered_set get_packages_for_department(const std::string& department_id, bool recursive = true); virtual std::string get_department_for_package(const std::string& package_id); virtual bool is_empty(const std::string& department_id); virtual bool has_package(const std::string& package_id); virtual std::string get_parent_department_id(const std::string& department_id); virtual std::list get_children_departments(const std::string& department_id); virtual bool is_descendant_of_department(const std::string& department_id, const std::string& parent_department_id); virtual void store_package_mapping(const std::string& package_id, const std::string& department_id); virtual void store_department_mapping(const std::string& department_id, const std::string& parent_department_id); virtual void store_department_name(const std::string& department_id, const std::string& locale, const std::string& name); // these methods are mostly for tests virtual int department_mapping_count() const; virtual int package_count() const; virtual int department_name_count() const; virtual void store_departments(const click::DepartmentList& depts, const std::string& locale); static std::unique_ptr open(bool create = true); protected: void init_db(); void store_departments_(const click::DepartmentList& depts, const std::string& locale); static void report_db_error(const QSqlError& error, const std::string& message); QSqlDatabase db_; std::unique_ptr delete_pkgmap_query_; std::unique_ptr delete_depts_query_; std::unique_ptr delete_deptnames_query_; std::unique_ptr insert_pkgmap_query_; std::unique_ptr insert_dept_id_query_; std::unique_ptr insert_dept_name_query_; std::unique_ptr select_pkgs_by_dept_; std::unique_ptr select_dept_for_pkg_; std::unique_ptr select_pkg_by_pkgid_; std::unique_ptr select_pkgs_by_dept_recursive_; std::unique_ptr select_pkgs_count_in_dept_recursive_; std::unique_ptr select_parent_dept_; std::unique_ptr select_children_depts_; std::unique_ptr select_dept_name_; std::unique_ptr select_is_descendant_of_dept_; }; } #endif ./libclickscope/click/departments.cpp0000644000015600001650000001320312676763577020053 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "departments.h" #include namespace click { Department::Department(const std::string& id, const std::string &name, const std::string& href, bool has_children) : id_(id), name_(name), href_(href), has_children_flag_(has_children) { } std::string Department::id() const { return id_; } std::string Department::name() const { return name_; } std::string Department::href() const { return href_; } bool Department::has_children_flag() const { return has_children_flag_; } void Department::set_subdepartments(const std::list& deps) { sub_departments_ = deps; } std::list Department::sub_departments() const { return sub_departments_; } Json::Value Department::check_mandatory_attribute(const Json::Value& item, const std::string& name, Json::ValueType valtype) { if (!item.isMember(name)) { throw std::runtime_error("Missing '" + name + "' node"); } auto const val = item[name]; if (val.type() != valtype) { throw std::runtime_error("Invalid type of '" + name + "' node"); } return val; } std::list Department::from_json_node(const Json::Value& node) { std::list deps; for (uint i = 0; i < node.size(); i++) { try { auto const item = node[i]; auto slug = check_mandatory_attribute(item, Department::JsonKeys::slug, Json::ValueType::stringValue).asString(); auto name = check_mandatory_attribute(item, Department::JsonKeys::name, Json::ValueType::stringValue).asString(); const bool has_children = item.isMember(Department::JsonKeys::has_children) ? item[Department::JsonKeys::has_children].asBool() : false; auto const links = check_mandatory_attribute(item, Department::JsonKeys::links, Json::ValueType::objectValue); auto const self = check_mandatory_attribute(links, Department::JsonKeys::self, Json::ValueType::objectValue); auto const href = check_mandatory_attribute(self, Department::JsonKeys::href, Json::ValueType::stringValue).asString(); auto dep = std::make_shared(slug, name, href, has_children); if (item.isObject() && item.isMember(Department::JsonKeys::embedded)) { auto const emb = item[Department::JsonKeys::embedded]; if (emb.isObject() && emb.isMember(Department::JsonKeys::department)) { auto const ditem = emb[Department::JsonKeys::department]; auto const subdeps = from_json_node(ditem); dep->set_subdepartments(subdeps); } } deps.push_back(dep); } catch (const std::runtime_error& e) { std::cerr << "Invalid department #" << i << std::endl; } } return deps; } std::list Department::from_json_root_node(const Json::Value& root) { if (root.isObject() && root.isMember(Department::JsonKeys::embedded)) { auto const emb = root[Department::JsonKeys::embedded]; if (emb.isObject() && emb.isMember(Department::JsonKeys::department)) { auto const ditem = emb[Department::JsonKeys::department]; return from_json_node(ditem); } } return std::list(); } std::list Department::from_json(const std::string& json) { Json::Reader reader; Json::Value root; try { if (!reader.parse(json, root)) { throw std::runtime_error(reader.getFormattedErrorMessages()); } if (root.isObject() && root.isMember(Department::JsonKeys::embedded)) { auto const emb = root[Department::JsonKeys::embedded]; if (emb.isObject() && emb.isMember(Department::JsonKeys::department)) { auto const ditem = emb[Department::JsonKeys::department]; return from_json_node(ditem); } } } catch (const std::exception& e) { std::cerr << "Error parsing departments: " << e.what() << std::endl; } catch (...) { std::cerr << "Unknown error when parsing departments" << std::endl; } return std::list(); } } ./libclickscope/click/key_file_locator.h0000644000015600001650000000461412676763577020512 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_KEY_FILE_LOCATOR_H #define CLICK_KEY_FILE_LOCATOR_H #include #include namespace unity { namespace util { class IniParser; } } namespace click { class KeyFileLocator { public: static const std::string& systemApplicationsDirectory(); static const std::string& userApplicationsDirectory(); typedef std::function Enumerator; KeyFileLocator(const std::string& systemApplicationsDir = systemApplicationsDirectory(), const std::string& userApplicationsDir = userApplicationsDirectory()); KeyFileLocator(const KeyFileLocator&) = delete; virtual ~KeyFileLocator() = default; KeyFileLocator& operator=(const KeyFileLocator&) = delete; bool operator==(const KeyFileLocator&) const = delete; virtual void enumerateKeyFilesForInstalledApplications(const Enumerator& enumerator); private: std::string systemApplicationsDir; std::string userApplicationsDir; }; } #endif // CLICK_KEY_FILE_LOCATOR_H ./libclickscope/click/utils.cpp0000644000015600001650000000632312676763577016672 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include struct byte_base_unit : boost::units::base_unit { static const char* name() { return("byte"); } static const char* symbol() { return("B"); } }; std::string click::Formatter::human_readable_filesize(long num_bytes) { std::ostringstream s; std::cout.imbue(std::locale()); if (num_bytes > 1023) { s << boost::units::symbol_format << boost::units::binary_prefix; s << boost::locale::format("{1,num=fixed,precision=1}") % (num_bytes * byte_base_unit::unit_type()); } else { std::string tpl(dngettext(GETTEXT_PACKAGE, "{1} byte", "{1} bytes", num_bytes)); s << boost::locale::format(tpl) % num_bytes; } return s.str(); } using namespace boost::posix_time; time_input_facet* build_input_facet(std::stringstream& ss) { time_input_facet* input_facet = new time_input_facet(1); input_facet->set_iso_extended_format(); ss.imbue(std::locale(ss.getloc(), input_facet)); return input_facet; } void click::Date::setup_system_locale() { boost::locale::generator gen; std::locale loc=gen(""); std::locale::global(loc); } void click::Date::parse_iso8601(std::string iso8601) { static std::stringstream ss; static ptime epoch(boost::gregorian::date(1970,1,1)); static time_input_facet* input_facet = NULL; if (input_facet == NULL) { build_input_facet(ss); } ptime time; ss.str(iso8601); ss >> time; ss.clear(); timestamp = (time - epoch).total_seconds(); } std::string click::Date::formatted() const { std::stringstream s; s << boost::locale::as::date << timestamp; return s.str(); } ./libclickscope/click/index.cpp0000644000015600001650000002406312676763577016642 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include "index.h" #include "interface.h" #include "application.h" namespace json = Json; namespace click { void PackageManager::uninstall(const Package& package, std::function callback) { std::string package_id = package.name + ";" + package.version + ";all;local:click"; std::string command = "pkcon -p remove " + package_id; execute_uninstall_command(command, callback); } void PackageManager::execute_uninstall_command(const std::string& command, std::function callback) { QSharedPointer process(new QProcess()); typedef void(QProcess::*QProcessFinished)(int, QProcess::ExitStatus); typedef void(QProcess::*QProcessError)(QProcess::ProcessError); QObject::connect(process.data(), static_cast(&QProcess::finished), [process, callback](int code, QProcess::ExitStatus status) { Q_UNUSED(status); qDebug() << "command finished with exit code:" << code; callback(code, process.data()->readAllStandardError().data()); if (code == 0) { invalidate_results(APPS_SCOPE_ID.toUtf8().data()); invalidate_results(STORE_SCOPE_ID.toUtf8().data()); } } ); QObject::connect(process.data(), static_cast(&QProcess::error), [process, callback](QProcess::ProcessError error) { qCritical() << "error running command:" << error; callback(-255 + error, process.data()->readAllStandardError().data()); } ); qDebug() << "Running command:" << command.c_str(); process.data()->start(command.c_str()); } void PackageManager::invalidate_results(const std::string& scope_id) { QProcess::execute(REFRESH_SCOPE_COMMAND.arg(scope_id.c_str())); } Index::Index(const QSharedPointer& client, const QSharedPointer configuration) : client(client), configuration(configuration) { } std::string Index::build_index_query(const std::string& query, const std::string& department) { std::string lquery{query}; std::transform(lquery.begin(), lquery.end(), lquery.begin(), ::tolower); std::stringstream result; result << lquery; if (!department.empty()) { result << ",department:" << department; } return result.str(); } std::map Index::build_headers() { std::stringstream frameworks; for (auto f: configuration->get_available_frameworks()) { frameworks << "," << f; } return std::map { {"Accept", "application/hal+json,application/json"}, {"X-Ubuntu-Frameworks", frameworks.str()}, {"X-Ubuntu-Architecture", configuration->get_architecture()} }; } std::pair Index::package_lists_from_json(const std::string& json) { Json::Reader reader; Json::Value root; click::Packages pl; click::Packages recommends; if (reader.parse(json, root)) { if (root.isObject() && root.isMember(Package::JsonKeys::embedded)) { auto const emb = root[Package::JsonKeys::embedded]; if (emb.isObject() && emb.isMember(Package::JsonKeys::ci_package)) { auto const pkg = emb[Package::JsonKeys::ci_package]; pl = click::package_list_from_json_node(pkg); if (emb.isMember(Package::JsonKeys::ci_recommends)) { auto const rec = emb[Package::JsonKeys::ci_recommends]; recommends = click::package_list_from_json_node(rec); } } } } return std::pair(pl, recommends); } click::web::Cancellable Index::search (const std::string& query, const std::string& department, std::function callback) { click::web::CallParams params; const std::string built_query(build_index_query(query, department)); params.add(click::QUERY_ARGNAME, built_query.c_str()); QSharedPointer response(client->call( get_base_url() + click::SEARCH_PATH, "GET", true, build_headers(), "", params)); QObject::connect(response.data(), &click::web::Response::finished, [=](QString reply) { std::pair package_lists; package_lists = package_lists_from_json(reply.toUtf8().constData()); callback(package_lists.first, package_lists.second); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString /*description*/) { qDebug() << "No packages found due to network error"; click::Packages pl; click::Packages recommends; qDebug() << "calling callback"; callback(pl, recommends); qDebug() << " ...Done!"; }); return click::web::Cancellable(response); } click::web::Cancellable Index::bootstrap(std::function callback) { return departments(get_base_url() + click::BOOTSTRAP_PATH, callback); } click::web::Cancellable Index::departments(const std::string& department_href, std::function callback) { click::web::CallParams params; QSharedPointer response(client->call( department_href, "GET", true, build_headers(), "", params)); QObject::connect(response.data(), &click::web::Response::finished, [=](QString reply) { qDebug() << "departments request finished"; Json::Reader reader; Json::Value root; // Get the suggested currency from the store. if (response->has_header(CURRENCY_HEADER)) { m_suggested_currency = response->get_header(CURRENCY_HEADER); } else { m_suggested_currency = Configuration::CURRENCY_DEFAULT; } click::DepartmentList depts; click::HighlightList highlights; if (reader.parse(reply.toUtf8().constData(), root)) { depts = Department::from_json_root_node(root); highlights = Highlight::from_json_root_node(root); } callback(depts, highlights, click::Index::Error::NoError, 0); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString /*description*/, int error_code) { qWarning() << "departments call failed due to network error"; const click::DepartmentList depts; const click::HighlightList highlights; qDebug() << "departments: calling callback"; callback(depts, highlights, click::Index::Error::NetworkError, error_code); }); return click::web::Cancellable(response); } click::web::Cancellable Index::get_details (const std::string& package_name, std::function callback) { QSharedPointer response = client->call (get_base_url() + click::DETAILS_PATH + package_name); qDebug() << "getting details for" << package_name.c_str(); QObject::connect(response.data(), &click::web::Response::finished, [=](const QByteArray reply) { qDebug() << "index, response finished:" << reply.toPercentEncoding(" {},=:\n\"'"); click::PackageDetails d = click::PackageDetails::from_json(reply.constData()); qDebug() << "index, details title:" << QByteArray(d.package.title.c_str()).toPercentEncoding(" "); callback(d, click::Index::Error::NoError); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString /*description*/) { qDebug() << "Cannot get package details due to network error"; callback(PackageDetails(), click::Index::Error::NetworkError); }); return click::web::Cancellable(response); } std::string Index::get_suggested_currency() const { return m_suggested_currency; } std::string Index::get_base_url () { const char *env_url = getenv(SEARCH_BASE_URL_ENVVAR.c_str()); if (env_url != NULL) { return env_url; } return click::SEARCH_BASE_URL; } Index::~Index() { } } // namespace click #include "index.moc" ./libclickscope/click/scope_activation.cpp0000644000015600001650000000617212676763577021066 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "scope_activation.h" #include #include #include #include click::ScopeActivation::ScopeActivation(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata) : unity::scopes::ActivationQueryBase(result, metadata) { } unity::scopes::ActivationResponse click::ScopeActivation::activate() { auto response = unity::scopes::ActivationResponse(status_); response.set_scope_data(unity::scopes::Variant(hints_)); return response; } void click::ScopeActivation::setStatus(unity::scopes::ActivationResponse::Status status) { status_ = status; } void click::ScopeActivation::setHint(std::string key, unity::scopes::Variant value) { hints_[key] = value; } click::PerformUninstallAction::PerformUninstallAction(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata, const unity::scopes::ActivationResponse& response) : unity::scopes::ActivationQueryBase(result, metadata), response(response) { } unity::scopes::ActivationResponse click::PerformUninstallAction::activate() { auto const res = result(); click::Package package; package.title = res.title(); package.name = res["name"].get_string(); package.version = res["version"].get_string(); qt::core::world::enter_with_task([this, package] () { click::PackageManager manager; manager.uninstall(package, [&](int code, std::string stderr_content) { if (code != 0) { qDebug() << "Error removing package:" << stderr_content.c_str(); } else { qDebug() << "successfully removed package"; } } ); }); return response; } ./libclickscope/click/department-lookup.h0000644000015600001650000000405312676763577020647 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_DEPARTMENT_LOOKUP_H #define CLICK_DEPARTMENT_LOOKUP_H #include "departments.h" #include #include #include namespace click { class DepartmentLookup { public: DepartmentLookup(); void rebuild(const std::list& root_departments); Department::SPtr get_parent(const std::string& department_id) const; Department::SPtr get_department_info(const std::string& department_id) const; int size() const; private: void rebuild(const Department::SPtr& dept); std::map parent_lut; std::map departments; }; } #endif ./libclickscope/click/qtbridge.h0000644000015600001650000000501512676763577016775 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Jussi Pakkanen * Thomas Voß */ #ifndef QT_CORE_WORLD_BRIDGE_H_ #define QT_CORE_WORLD_BRIDGE_H_ #include #include #include #include namespace qt { namespace core { namespace world { /** * @brief Sets up the Qt core world and executes its event loop. Blocks until destroy() is called. * @param argc Number of arguments in argv. * @param argv Array of command-line arguments. * @param ready Functor be called when the world has been setup and is about to be executed. * @throw std::runtime_error in case of errors. */ void build_and_run(int argc, char** argv, const std::function& ready); /** * @brief Destroys the Qt core world and quits its event loop. */ void destroy(); /** * @brief Enters the Qt core world and schedules the given task for execution. * @param task The task to be executed in the Qt core world. * @return A std::future that can be waited for to synchronize to the world's internal event loop. */ std::future enter_with_task(const std::function& task); /** * @brief Enters the Qt core world and schedules the given task for execution. * @param task The task to be executed in the Qt core world. * @return A std::future that can be waited for to get hold of the result of the task. */ template inline std::future enter_with_task_and_expect_result(const std::function& task) { std::shared_ptr> promise = std::make_shared>(); std::future future = promise->get_future(); auto t = [promise, task]() { try { promise->set_value(task()); } catch(...) { promise->set_exception(std::current_exception()); } }; enter_with_task(t); return future; } } } } #endif // QT_CORE_WORLD_BRIDGE_H_ ./libclickscope/click/dbus_constants.h0000644000015600001650000000320512676763577020224 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef DBUS_CONSTANTS_H #define DBUS_CONSTANTS_H #define LAUNCHER_BUSNAME "com.ubuntu.unity.launcher" #define LAUNCHER_OBJECT_PATH "/com/ubuntu/unity/launcher/installations" #define LAUNCHER_INTERFACE "com.ubuntu.unity.launcher.Installations" #endif // DBUS_CONSTANTS_H ./libclickscope/click/launcher.xml0000644000015600001650000000137112676763577017347 0ustar jenkinsjenkins ./libclickscope/click/network_access_manager.h0000644000015600001650000000612512676763577021703 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_NETWORK_ACCESS_MANAGER_H #define CLICK_NETWORK_ACCESS_MANAGER_H #include #include #include #include #include #include class QByteArray; namespace click { namespace network { // We wrap a QNetworkReply to make sure that we can mock it out // in unit- and integration testing. class Reply : public QObject { Q_OBJECT public: // A Reply instance takes over ownership of the underlying QNetworkReply. explicit Reply(QNetworkReply* reply); Reply(const Reply&) = delete; virtual ~Reply(); Reply& operator=(const Reply&) = delete; virtual void abort(); virtual QByteArray readAll(); virtual QVariant attribute(QNetworkRequest::Attribute code); virtual bool hasRawHeader(const QByteArray &headerName); virtual QString rawHeader(const QByteArray &headerName); virtual QList> rawHeaderPairs(); virtual QString errorString(); signals: void finished(); void error(QNetworkReply::NetworkError); protected: Reply(); private: QScopedPointer reply; }; class AccessManager { public: AccessManager() = default; AccessManager(const AccessManager&) = delete; virtual ~AccessManager() = default; AccessManager& operator=(const AccessManager&) = delete; virtual QSharedPointer get(QNetworkRequest& request); virtual QSharedPointer head(QNetworkRequest& request); virtual QSharedPointer post(QNetworkRequest& request, QByteArray& data); virtual QSharedPointer sendCustomRequest(QNetworkRequest& request, QByteArray& verb, QIODevice *data = 0); }; } } #endif // CLICK_NETWORK_ACCESS_MANAGER_H ./libclickscope/click/configuration.h0000644000015600001650000000753412676763577020053 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CONFIGURATION_H #define CONFIGURATION_H #include #include #include namespace click { class Configuration { public: constexpr static const char* FRAMEWORKS_FOLDER {"/usr/share/click/frameworks/"}; constexpr static const char* FRAMEWORKS_PATTERN {"*.framework"}; constexpr static const int FRAMEWORKS_EXTENSION_LENGTH = 10; // strlen(".framework") constexpr static const char* ARCH_ENVVAR {"U1_SEARCH_ARCH"}; constexpr static const char* LANGUAGE_ENVVAR {"LANGUAGE"}; constexpr static const char* PURCHASES_ENVVAR {"CLICK_STORE_ENABLE_PURCHASES"}; constexpr static const bool PURCHASES_DEFAULT = true; constexpr static const char* CURRENCY_ENVVAR {"U1_SEARCH_CURRENCY"}; constexpr static const char* CURRENCY_DEFAULT {"USD"}; static const std::map CURRENCY_MAP; static const std::vector FULL_LANG_CODES; virtual std::vector get_available_frameworks(); virtual std::string get_architecture(); static bool get_purchases_enabled(); static std::string get_currency(const std::string& fallback = CURRENCY_DEFAULT); virtual std::string get_language_base(); virtual std::string get_language(); virtual std::string get_accept_languages(); static bool is_full_lang_code(const std::string& language); virtual std::string get_device_id(); constexpr static const char* COREAPPS_SCHEMA {"com.canonical.Unity.ClickScope"}; constexpr static const char* COREAPPS_KEY {"coreApps"}; constexpr static const char* IGNORED_KEY {"ignoredApps"}; virtual const std::vector get_core_apps() const; virtual const std::vector get_ignored_apps() const; virtual ~Configuration() {} protected: virtual std::vector list_folder(const std::string &folder, const std::string &pattern); virtual std::string architectureFromDpkg(); virtual std::string deviceIdFromWhoopsie(); virtual const std::vector get_dconf_strings(const std::string& schema, const std::string& key) const; static const std::vector& get_default_core_apps() { static std::vector default_apps { "dialer-app", "messaging-app", "address-book-app", "com.ubuntu.camera_camera", "webbrowser-app", "com.ubuntu.clock_clock" }; return default_apps; } }; } // namespace click #endif // CONFIGURATION_H ./libclickscope/click/click-i18n.h0000644000015600001650000000275512676763577017046 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICKI18N_H #define CLICKI18N_H #include #define _(value) dgettext(GETTEXT_PACKAGE, value) #endif ./libclickscope/click/ubuntuone_credentials.cpp0000644000015600001650000000426712676763604022127 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "ubuntuone_credentials.h" namespace u1 = UbuntuOne; click::CredentialsService::CredentialsService() : ssoService(new u1::SSOService()) { // Forward signals directly: connect(ssoService.data(), &u1::SSOService::credentialsFound, this, &click::CredentialsService::credentialsFound); connect(ssoService.data(), &u1::SSOService::credentialsNotFound, this, &click::CredentialsService::credentialsNotFound); connect(ssoService.data(), &u1::SSOService::credentialsDeleted, this, &click::CredentialsService::credentialsDeleted); } click::CredentialsService::~CredentialsService() { } void click::CredentialsService::getCredentials() { ssoService->getCredentials(); } void click::CredentialsService::invalidateCredentials() { ssoService->invalidateCredentials(); } ./libclickscope/click/index.h0000644000015600001650000000747212676763577016314 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICKINDEX_H #define CLICKINDEX_H #include #include #include #include #include "package.h" #include #include namespace click { class Configuration; const std::string SEARCH_BASE_URL_ENVVAR = "U1_SEARCH_BASE_URL"; const std::string SEARCH_BASE_URL = "https://search.apps.ubuntu.com/"; const std::string SEARCH_PATH = "api/v1/search"; const std::string BOOTSTRAP_PATH = "api/v1"; const std::string SUPPORTED_FRAMEWORKS = "framework:ubuntu-sdk-13.10"; const std::string QUERY_ARGNAME = "q"; const std::string ARCHITECTURE = "architecture:"; const std::string DETAILS_PATH = "api/v1/package/"; const std::string CURRENCY_HEADER = "X-Suggested-Currency"; class PackageManager { public: void uninstall (const Package& package, std::function); virtual void execute_uninstall_command (const std::string& command, std::function); static void invalidate_results(const std::string& scope_id); }; class Index { protected: QSharedPointer client; QSharedPointer configuration; std::string m_suggested_currency; virtual std::string build_index_query(const std::string& query, const std::string& department); virtual std::map build_headers(); public: enum class Error {NoError, CredentialsError, NetworkError}; Index() {} Index(const QSharedPointer& client, const QSharedPointer configuration=QSharedPointer(new Configuration())); virtual std::pair package_lists_from_json(const std::string& json); virtual click::web::Cancellable search (const std::string& query, const std::string& department, std::function callback); virtual click::web::Cancellable get_details(const std::string& package_name, std::function callback); virtual click::web::Cancellable bootstrap(std::function callback); virtual click::web::Cancellable departments(const std::string& department_href, std::function callback); virtual ~Index(); virtual std::string get_suggested_currency() const; static std::string get_base_url(); }; } // namespace click #endif // CLICKINDEX_H ./libclickscope/click/smartconnect.h0000644000015600001650000000423112676763577017673 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef SMARTCONNECT_H #define SMARTCONNECT_H #include #include #include namespace click { namespace utils { class SmartConnect : public QObject { Q_OBJECT QList connections; virtual void cleanup(); private slots: void disconnectAll(); public: explicit SmartConnect(QObject *parent = 0); template void connect(SenderType* sender, SignalType signal, SlotType slot) { connections.append(QObject::connect(sender, signal, slot)); connections.append(QObject::connect(sender, signal, this, &SmartConnect::disconnectAll)); } }; } // namespace utils } // namespace click #endif // SMARTCONNECT_H ./libclickscope/click/launcher.h0000644000015600001650000000334612676763577017002 0ustar jenkinsjenkins/* * This file was generated by qdbusxml2cpp version 0.8 * Command line was: qdbusxml2cpp launcher.xml -p launcher -c Launcher -N * * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). * * This is an auto-generated file. * Do not edit! All changes made to it will be lost. */ #ifndef LAUNCHER_H_1403062747 #define LAUNCHER_H_1403062747 #include #include #include #include #include #include #include #include /* * Proxy class for interface com.ubuntu.unity.launcher.Installations */ class Launcher: public QDBusAbstractInterface { Q_OBJECT public: static inline const char *staticInterfaceName() { return "com.ubuntu.unity.launcher.Installations"; } public: Launcher(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); ~Launcher(); public Q_SLOTS: // METHODS inline Q_NOREPLY void completeInstallation(const QString &package_name, const QString &app_id) { QList argumentList; argumentList << QVariant::fromValue(package_name) << QVariant::fromValue(app_id); callWithArgumentList(QDBus::NoBlock, QLatin1String("completeInstallation"), argumentList); } inline Q_NOREPLY void startInstallation(const QString &title, const QString &icon_url, const QString &package_name) { QList argumentList; argumentList << QVariant::fromValue(title) << QVariant::fromValue(icon_url) << QVariant::fromValue(package_name); callWithArgumentList(QDBus::NoBlock, QLatin1String("startInstallation"), argumentList); } Q_SIGNALS: // SIGNALS }; #endif ./libclickscope/click/interface.cpp0000644000015600001650000006023712676763577017476 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "interface.h" #include #include #include namespace { /* Thanks to * - http://stackoverflow.com/a/14031349 * - http://stackoverflow.com/questions/12278448/removing-accents-from-a-qstring */ QString unaccent(const QString &str) { QString tmp = str.normalized(QString::NormalizationForm_KD, QChar::currentUnicodeVersion()); QString ret; for (int i = 0, j = tmp.length(); i < j; i++) { // strip diacritic marks if (tmp.at(i).category() != QChar::Mark_NonSpacing && tmp.at(i).category() != QChar::Mark_SpacingCombining && tmp.at(i).category() != QChar::Mark_Enclosing) { ret.append(tmp.at(i)); } } return ret; } } namespace click { const std::unordered_set& nonClickDesktopFiles() { static std::unordered_set set = { "address-book-app.desktop", "camera-app.desktop", "click-update-manager.desktop", "dialer-app.desktop", "friends-app.desktop", "gallery-app.desktop", "mediaplayer-app.desktop", "messaging-app.desktop", "music-app.desktop", "ubuntu-filemanager-app.desktop", "ubuntu-system-settings.desktop", "webbrowser-app.desktop", }; return set; } static const std::string DESKTOP_FILE_GROUP("Desktop Entry"); static const std::string DESKTOP_FILE_KEY_NAME("Name"); static const std::string DESKTOP_FILE_KEY_ICON("Icon"); static const std::string DESKTOP_FILE_KEY_KEYWORDS("Keywords"); static const std::string DESKTOP_FILE_KEY_APP_ID("X-Ubuntu-Application-ID"); static const std::string DESKTOP_FILE_KEY_DOMAIN("X-Ubuntu-Gettext-Domain"); static const std::string DESKTOP_FILE_UBUNTU_TOUCH("X-Ubuntu-Touch"); static const std::string DESKTOP_FILE_UBUNTU_DEFAULT_DEPARTMENT("X-Ubuntu-Default-Department-ID"); static const std::string DESKTOP_FILE_COMMENT("Comment"); static const std::string DESKTOP_FILE_SCREENSHOT("X-Screenshot"); static const std::string DESKTOP_FILE_NODISPLAY("NoDisplay"); static const std::string DESKTOP_FILE_ONLYSHOWIN("OnlyShowIn"); static const std::string ONLYSHOWIN_UNITY("Unity"); Interface::Interface(const QSharedPointer& keyFileLocator) : keyFileLocator(keyFileLocator) { } Interface::~Interface() { } bool Interface::show_desktop_apps() { return getenv(ENV_SHOW_DESKTOP_APPS) != nullptr; } bool Interface::is_visible_app(const unity::util::IniParser &keyFile) { if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_NODISPLAY)) { auto val = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_NODISPLAY); if (val == std::string("true")) { return false; } } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_ONLYSHOWIN)) { auto value = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_ONLYSHOWIN); std::stringstream ss(value); std::string item; while (std::getline(ss, item, ';')) { if (item == ONLYSHOWIN_UNITY) { return true; } } return false; } return true; } std::string Interface::get_translated_string(const unity::util::IniParser& keyFile, const std::string& group, const std::string& key, const std::string& domain) { std::string language = Configuration().get_language(); if (!domain.empty()) { return dgettext(domain.c_str(), keyFile.get_string(group, key).c_str()); } else { return keyFile.get_locale_string(group, key, language); } } click::Application Interface::load_app_from_desktop(const unity::util::IniParser& keyFile, const std::string& filename) { Application app; std::string domain; if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_DOMAIN)) { domain = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_DOMAIN); } app.title = get_translated_string(keyFile, DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_NAME, domain); app.url = "application:///" + filename; if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_ICON)) { app.icon_url = add_theme_scheme(keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_ICON)); } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_KEYWORDS)) { app.keywords = keyFile.get_string_array(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_KEYWORDS); } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_UBUNTU_DEFAULT_DEPARTMENT)) { app.default_department = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_UBUNTU_DEFAULT_DEPARTMENT); } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_APP_ID)) { QString app_id = QString::fromStdString(keyFile.get_string( DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_APP_ID)); QStringList id = app_id.split("_", QString::SkipEmptyParts); if (id.length() == 3) { app.name = id[0].toUtf8().data(); app.version = id[2].toUtf8().data(); } else { app.name = "unknown"; app.version = "unknown"; } } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_COMMENT)) { app.description = get_translated_string(keyFile, DESKTOP_FILE_GROUP, DESKTOP_FILE_COMMENT, domain); } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_SCREENSHOT)) { app.main_screenshot = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_SCREENSHOT); } return app; } std::vector Interface::sort_apps(const std::vector& apps) { std::vector result = apps; boost::locale::generator gen; const char* lang = getenv(click::Configuration::LANGUAGE_ENVVAR); if (lang == NULL) { lang = "C.UTF-8"; } std::locale loc = gen(lang); std::locale::global(loc); typedef boost::locale::collator coll_type; // Sort applications alphabetically. std::sort(result.begin(), result.end(), [&loc](const Application& a, const Application& b) { bool lesser = false; int order = std::use_facet(loc) .compare(boost::locale::collator_base::quaternary, a.title, b.title); if (order == 0) { lesser = a.name < b.name; } else { // Because compare returns int, not bool, we have to check // that 0 is greater than the result, which tells us the // first element should be sorted priori lesser = order < 0; } return lesser; }); return result; } /* find_installed_apps() * * Find all of the installed apps matching @search_query in a timeout. */ std::vector Interface::find_installed_apps(const std::string& search_query, const std::vector& ignored_apps, const std::string& current_department, const std::shared_ptr& depts_db) { // // only apply department filtering if not in root of all departments. bool apply_department_filter = !current_department.empty(); // get the set of packages that belong to current deparment; std::unordered_set packages_in_department; if (depts_db && apply_department_filter) { try { packages_in_department = depts_db->get_packages_for_department(current_department); } catch (const std::exception& e) { qWarning() << "Failed to get packages of department" << QString::fromStdString(current_department); apply_department_filter = false; // disable so that we are not loosing any apps if something goes wrong } } std::vector result; bool include_desktop_results = show_desktop_apps(); auto enumerator = [&result, this, search_query, ignored_apps, current_department, packages_in_department, apply_department_filter, include_desktop_results, depts_db] (const unity::util::IniParser& keyFile, const std::string& filename) { if (keyFile.has_group(DESKTOP_FILE_GROUP) == false) { qWarning() << "Broken desktop file:" << QString::fromStdString(filename); return; } if (is_visible_app(keyFile) == false) { return; // from the enumerator lambda } if (include_desktop_results || keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_UBUNTU_TOUCH) || keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_APP_ID) || Interface::is_non_click_app(QString::fromStdString(filename))) { auto app = load_app_from_desktop(keyFile, filename); auto app_id = app.name.empty() ? filename : app.name; if (!ignored_apps.empty() && std::find(ignored_apps.begin(), ignored_apps.end(), app_id) != ignored_apps.end()) { // The app is ignored. Get out of here. return; } // app from click package has non-empty name; for non-click apps use desktop filename const std::string department_key = app.name.empty() ? filename : app.name; // check if apps is present in current department if (apply_department_filter) { if (packages_in_department.find(department_key) == packages_in_department.end()) { if (app.default_department.empty()) { // default department not present in the keyfile, skip this app return; } else { // default department not empty: check if this app is in a different // department in the db (i.e. got moved from the default department); if (depts_db->has_package(department_key)) { // app is now in a different department return; } if (app.default_department != current_department) { return; } // else - this package is in current department } } } // // the packages_in_department set contains packages from // all its subdepartments; we need to find actual department now // to update app.real_department. if (depts_db) { if (depts_db->has_package(department_key)) { try { app.real_department = depts_db->get_department_for_package(department_key); } catch (const std::exception &e) { qWarning() << "Failed to get department of package:" << QString::fromStdString(department_key); } } else { app.real_department = app.default_department; if (app.real_department.empty()) { qWarning() << "No default department set in the .desktop file and no entry in the database for" << QString::fromStdString(department_key); } } } if (search_query.empty()) { result.push_back(app); } else { QString lquery = ::unaccent(QString::fromStdString(search_query)); // Check keywords for the search query as well. for (auto kwd: app.keywords) { QString keyword = ::unaccent(QString::fromStdString(kwd)); if (!keyword.isEmpty() && keyword.contains(lquery, Qt::CaseInsensitive)) { result.push_back(app); return; } } QString search_title = ::unaccent(QString::fromStdString(app.title)); // check the app title for the search query. if (!search_title.isEmpty() && search_title.contains(lquery, Qt::CaseInsensitive)) { result.push_back(app); } } } }; keyFileLocator->enumerateKeyFilesForInstalledApplications(enumerator); return sort_apps(result); } /* is_non_click_app() * * Tests that @filename is one of the special-cased filenames for apps * which are not packaged as clicks, but required on Ubuntu Touch. */ bool Interface::is_non_click_app(const QString& filename) { return click::nonClickDesktopFiles().count(filename.toUtf8().data()) > 0; } /* * is_icon_identifier() * * Checks if @filename has no / in it */ bool Interface::is_icon_identifier(const std::string &icon_id) { return icon_id.find("/") == std::string::npos; } /* * add_theme_scheme() * * Adds the theme prefix if the filename is not an icon identifier */ std::string Interface::add_theme_scheme(const std::string& icon_id) { if (is_icon_identifier(icon_id)) { return "image://theme/" + icon_id; } return icon_id; } ManifestList manifest_list_from_json(const std::string& json) { using namespace boost::property_tree; std::istringstream is(json); ptree pt; read_json(is, pt); ManifestList manifests; BOOST_FOREACH(ptree::value_type &v, pt) { assert(v.first.empty()); // array elements have no names auto node = v.second; Manifest manifest; manifest.name = node.get("name"); manifest.version = node.get("version"); manifest.removable = node.get("_removable"); BOOST_FOREACH(ptree::value_type &sv, node.get_child("hooks")) { // FIXME: "primary app" for a package is not defined, we just // use first one here: manifest.first_app_name = sv.first; break; } qDebug() << "adding manifest: " << manifest.name.c_str() << manifest.version.c_str() << manifest.first_app_name.c_str(); manifests.push_back(manifest); } return manifests; } Manifest manifest_from_json(const std::string& json) { using namespace boost::property_tree; std::istringstream is(json); ptree pt; read_json(is, pt); Manifest manifest; manifest.name = pt.get("name"); manifest.version = pt.get("version"); manifest.removable = pt.get("_removable"); BOOST_FOREACH(ptree::value_type &sv, pt.get_child("hooks")) { // FIXME: "primary app or scope" for a package is not defined, // we just use the first one in the manifest: auto app_name = sv.second.get("desktop", ""); if (manifest.first_app_name.empty() && !app_name.empty()) { manifest.first_app_name = sv.first; } auto scope_id = sv.second.get("scope", ""); if (manifest.first_scope_id.empty() && !scope_id.empty()) { manifest.first_scope_id = manifest.name + "_" + sv.first; } } qDebug() << "adding manifest: " << manifest.name.c_str() << manifest.version.c_str() << manifest.first_app_name.c_str(); return manifest; } void Interface::get_manifests(std::function callback) { std::string command = "click list --manifest"; qDebug() << "Running command:" << command.c_str(); run_process(command, [callback](int code, const std::string& stdout_data, const std::string& stderr_data) { if (code == 0) { try { ManifestList manifests = manifest_list_from_json(stdout_data); callback(manifests, InterfaceError::NoError); } catch (...) { qWarning() << "Can't parse 'click list --manifest' output: " << QString::fromStdString(stdout_data); callback(ManifestList(), InterfaceError::ParseError); } } else { qWarning() << "Error" << code << "running 'click list --manifest': " << QString::fromStdString(stderr_data); callback(ManifestList(), InterfaceError::CallError); } }); } PackageSet package_names_from_stdout(const std::string& stdout_data) { const char TAB='\t', NEWLINE='\n'; std::istringstream iss(stdout_data); PackageSet installed_packages; while (iss.peek() != EOF) { std::string line; std::getline(iss, line, NEWLINE); if (!line.empty()) { // Must initialize linestream after line is filled. std::istringstream linestream(line); Package p; std::getline(linestream, p.name, TAB); std::getline(linestream, p.version); if (iss.eof() || p.name.empty() || p.version.empty()) { qWarning() << "Error encountered parsing 'click list' output:" << QString::fromStdString(line); } else { installed_packages.insert(p); } } } return installed_packages; } void Interface::get_installed_packages(std::function callback) { std::string command = "click list"; qDebug() << "Running command:" << command.c_str(); run_process(command, [callback](int code, const std::string& stdout_data, const std::string& stderr_data) { if (code == 0) { try { PackageSet package_names = package_names_from_stdout(stdout_data); callback(package_names, InterfaceError::NoError); } catch (...) { qWarning() << "Can't parse 'click list' output: " << QString::fromStdString(stdout_data); callback(PackageSet(), InterfaceError::ParseError); } } else { qWarning() << "Error" << code << "running 'click list': " << QString::fromStdString(stderr_data); callback(PackageSet(), InterfaceError::CallError); } }); } void Interface::get_manifest_for_app(const std::string &app_id, std::function callback) { std::string command = "click info " + app_id; qDebug() << "Running command:" << command.c_str(); run_process(command, [callback, app_id](int code, const std::string& stdout_data, const std::string& stderr_data) { if (code == 0) { try { Manifest manifest = manifest_from_json(stdout_data); callback(manifest, InterfaceError::NoError); } catch (...) { qWarning() << "Can't parse 'click info" << QString::fromStdString(app_id) << "' output: " << QString::fromStdString(stdout_data); callback(Manifest(), InterfaceError::ParseError); } } else { qWarning() << "Error" << code << "running 'click info" << QString::fromStdString(app_id) << "': " << QString::fromStdString(stderr_data); callback(Manifest(), InterfaceError::CallError); } }); } void Interface::get_dotdesktop_filename(const std::string &app_id, std::function callback) { get_manifest_for_app(app_id, [app_id, callback] (Manifest manifest, InterfaceError error) { qDebug() << "in get_dotdesktop_filename callback"; if (error != InterfaceError::NoError){ callback(std::string("Internal Error"), error); return; } qDebug() << "in get_dotdesktop_filename callback"; if (!manifest.name.empty()) { std::string ddstr = manifest.name + "_" + manifest.first_app_name + "_" + manifest.version + ".desktop"; callback(ddstr, InterfaceError::NoError); } else { qCritical() << "Warning: no manifest found for " << app_id.c_str(); callback(std::string("Not found"), InterfaceError::CallError); } }); } void Interface::run_process(const std::string& command, std::function callback) { QSharedPointer process(new QProcess()); typedef void(QProcess::*QProcessFinished)(int, QProcess::ExitStatus); typedef void(QProcess::*QProcessError)(QProcess::ProcessError); QObject::connect(process.data(), static_cast(&QProcess::finished), [callback, process](int code, QProcess::ExitStatus /*status*/) { qDebug() << "command finished with exit code:" << code; std::string data{process->readAllStandardOutput().data()}; std::string errors{process->readAllStandardError().data()}; callback(code, data, errors); } ); QObject::connect(process.data(), static_cast(&QProcess::error), [callback, process](QProcess::ProcessError error) { qCritical() << "error running command:" << error; std::string data{process->readAllStandardOutput().data()}; std::string errors{process->readAllStandardError().data()}; callback(process->exitCode(), data, errors); } ); process->start(command.c_str()); } } // namespace click ./libclickscope/click/utils.h0000644000015600001650000000360712676763577016341 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_UTILS_H #define CLICK_UTILS_H #include #include namespace click { class Date { protected: std::time_t timestamp; public: void parse_iso8601(std::string iso8601); std::string formatted() const; static void setup_system_locale(); inline bool operator==(const Date& other) const { return timestamp == other.timestamp; } }; class Formatter { public: static std::string human_readable_filesize(long num_bytes); }; } // namespace click #endif // CLICK_UTILS_H ./libclickscope/click/highlights.cpp0000644000015600001650000001025712676763577017665 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include "highlights.h" #include namespace click { Highlight::Highlight(const std::string& name) : name_(name) { } Highlight::Highlight(const std::string& slug, const std::string& name, const Packages& pkgs, bool contains_scopes) : slug_(slug), name_(name), packages_(pkgs), contains_scopes_(contains_scopes) { } void Highlight::add_package(const Package& pkg) { packages_.push_back(pkg); } bool Highlight::contains_scopes() const { return contains_scopes_; } std::string Highlight::slug() const { return slug_; } std::string Highlight::name() const { return name_; } Packages Highlight::packages() const { return packages_; } std::list Highlight::from_json_node(const Json::Value& node) { std::list highlights; for (uint i = 0; i < node.size(); i++) { auto const item = node[i]; if (item.isObject() && item.isMember(Highlight::JsonKeys::name)) { auto name = item[Highlight::JsonKeys::name].asString(); auto slug = item[Highlight::JsonKeys::slug].asString(); auto pkg_node = item[Package::JsonKeys::embedded][Package::JsonKeys::ci_package]; auto pkgs = package_list_from_json_node(pkg_node); auto hl = Highlight(slug, name, pkgs); if (slug == "app-of-the-week" || slug == "editors-pick") { highlights.push_front(hl); } else { highlights.push_back(hl); } } } return highlights; } std::list Highlight::from_json_root_node(const Json::Value& root) { std::list highlights; if (root.isObject() && root.isMember(Highlight::JsonKeys::embedded)) { auto const emb = root[Highlight::JsonKeys::embedded]; if (emb.isObject() && emb.isMember(Highlight::JsonKeys::highlight)) { auto const hl = emb[Highlight::JsonKeys::highlight]; highlights = from_json_node(hl); } if (emb.isObject() && emb.isMember(Package::JsonKeys::ci_package)) { auto pkg_node = emb[Package::JsonKeys::ci_package]; auto pkgs = package_list_from_json_node(pkg_node); click::Packages apps; click::Packages scopes; for (click::Package& p : pkgs) { if (p.content == "scope") { scopes.push_back(p); } else { apps.push_back(p); } } if (scopes.size() > 0) { highlights.push_back(Highlight("__all-scopes__", _("Scopes"), scopes, true)); } if (apps.size() > 0) { highlights.push_back(Highlight("__all-apps__", _("Apps"), apps)); } } } return highlights; } } ./libclickscope/click/preview.h0000644000015600001650000003560712676763577016667 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICKPREVIEW_H #define CLICKPREVIEW_H #include #include #include #include #include "reviews.h" #include #include #include #include #include #include #include #include #include namespace scopes = unity::scopes; namespace click { class Manifest; class PreviewStrategy; class DepartmentsDb; struct WidgetsInColumns { struct { std::vector column1; } singleColumn; struct { std::vector column1; std::vector column2; } twoColumns; void registerLayouts(unity::scopes::PreviewReplyProxy const& reply); void appendToColumn(std::vector& column, unity::scopes::PreviewWidgetList const& widgets); }; class CachedPreviewWidgets { public: void push(unity::scopes::PreviewWidget const &widget); void push(unity::scopes::PreviewWidgetList const &widgetList); void flush(unity::scopes::PreviewReplyProxy const& reply); bool has(std::string const& widget) const; WidgetsInColumns layout; private: std::list widgets; std::unordered_set widgets_lookup; }; class DepartmentUpdater { protected: DepartmentUpdater() = default; DepartmentUpdater(const std::shared_ptr& depts); virtual ~DepartmentUpdater() = default; void store_department(const PackageDetails& pkg); private: std::shared_ptr depts; }; class Preview : public unity::scopes::PreviewQueryBase { protected: std::unique_ptr strategy; const unity::scopes::Result& result; const unity::scopes::ActionMetadata& metadata; PreviewStrategy* build_strategy(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata, const QSharedPointer &client, const QSharedPointer& ppackage, const QSharedPointer& manager, std::shared_ptr depts); virtual PreviewStrategy* build_installing(const std::string& download_url, const std::string& download_sha512, const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& manager, std::shared_ptr depts); public: UNITY_DEFINES_PTRS(Preview); struct Actions { Actions() = delete; constexpr static const char* INSTALL_CLICK{"install_click"}; constexpr static const char* BUY_CLICK{"buy_click"}; constexpr static const char* DOWNLOAD_COMPLETED{"finished"}; constexpr static const char* DOWNLOAD_FAILED{"failed"}; constexpr static const char* PURCHASE_SUCCEEDED{"purchase_succeeded"}; constexpr static const char* PURCHASE_FAILED{"purchase_failed"}; constexpr static const char* OPEN_CLICK{"open_click"}; constexpr static const char* PIN_TO_LAUNCHER{"pin_to_launcher"}; constexpr static const char* UNINSTALL_CLICK{"uninstall_click"}; constexpr static const char* CONFIRM_UNINSTALL{"confirm_uninstall"}; constexpr static const char* CANCEL_PURCHASE_UNINSTALLED{"cancel_purchase_uninstalled"}; constexpr static const char* CANCEL_PURCHASE_INSTALLED{"cancel_purchase_installed"}; constexpr static const char* SHOW_UNINSTALLED{"show_uninstalled"}; constexpr static const char* SHOW_INSTALLED{"show_installed"}; constexpr static const char* CONFIRM_CANCEL_PURCHASE_UNINSTALLED{"confirm_cancel_purchase_uninstalled"}; constexpr static const char* CONFIRM_CANCEL_PURCHASE_INSTALLED{"confirm_cancel_purchase_installed"}; constexpr static const char* OPEN_ACCOUNTS{"open_accounts"}; constexpr static const char* RATED{"rated"}; }; Preview(const unity::scopes::Result& result); Preview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata); virtual ~Preview(); void choose_strategy(const QSharedPointer &client, const QSharedPointer& ppackage, const QSharedPointer& manager, std::shared_ptr depts); // From unity::scopes::PreviewQuery void cancelled() override; virtual void run(unity::scopes::PreviewReplyProxy const& reply) override; }; class PreviewStrategy { public: PreviewStrategy(const unity::scopes::Result& result); PreviewStrategy(const unity::scopes::Result& result, const QSharedPointer& client); PreviewStrategy(const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& pay_package); virtual ~PreviewStrategy(); virtual void cancelled(); virtual void run(unity::scopes::PreviewReplyProxy const& reply) = 0; virtual void pushPackagePreviewWidgets(CachedPreviewWidgets &reply, const PackageDetails& details, const scopes::PreviewWidgetList& button_area_widgets); virtual void run_under_qt(const std::function &task); virtual void invalidateScope(const std::string& scope_id); protected: virtual void populateDetails(std::function details_callback, std::function reviews_callback); virtual scopes::PreviewWidgetList headerWidgets(const PackageDetails &details); virtual scopes::PreviewWidgetList screenshotsWidgets(const PackageDetails &details); virtual scopes::PreviewWidgetList descriptionWidgets(const PackageDetails &details); virtual scopes::PreviewWidgetList progressBarWidget(const std::string& object_path); virtual scopes::PreviewWidgetList reviewsWidgets(const click::ReviewList &reviewlist); virtual scopes::PreviewWidgetList downloadErrorWidgets(); virtual scopes::PreviewWidgetList loginErrorWidgets(const std::string& download_url, const std::string& download_sha512); virtual scopes::PreviewWidgetList errorWidgets(const scopes::Variant& title, const scopes::Variant& subtitle, const scopes::Variant& action_id, const scopes::Variant& action_label, const scopes::Variant& action_uri = scopes::Variant::null()); virtual void pushPackagePreviewWidgets(const unity::scopes::PreviewReplyProxy &reply, const PackageDetails& details, const scopes::PreviewWidgetList& button_area_widgets); virtual scopes::PreviewWidget build_other_metadata(const PackageDetails& details); virtual scopes::PreviewWidget build_updates_table(const PackageDetails& details); virtual std::string build_whats_new(const PackageDetails& details); virtual bool isRefundable(); scopes::Result result; QSharedPointer client; QSharedPointer index; click::web::Cancellable index_operation; QSharedPointer reviews; click::web::Cancellable reviews_operation; click::web::Cancellable submit_operation; scopes::OnlineAccountClient oa_client; QSharedPointer pay_package; click::web::Cancellable purchase_operation; }; class DownloadErrorPreview : public PreviewStrategy { public: DownloadErrorPreview(const unity::scopes::Result& result); virtual ~DownloadErrorPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; }; class InstallingPreview : public PreviewStrategy, public DepartmentUpdater { public: InstallingPreview(const unity::scopes::Result& result) : PreviewStrategy(result) {} InstallingPreview(const std::string& download_url, const std::string& download_sha512, const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& manager, std::shared_ptr depts); virtual ~InstallingPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: std::string download_url; std::string download_sha512; QSharedPointer dm; std::shared_ptr depts_db; CachedPreviewWidgets cachedWidgets; void startLauncherAnimation(const PackageDetails& details); }; class InstalledPreview : public PreviewStrategy, public DepartmentUpdater { public: InstalledPreview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata, const QSharedPointer& client, const QSharedPointer& ppackage, const std::shared_ptr& depts); virtual ~InstalledPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: void getApplicationUri(const Manifest& manifest, std::function callback); std::string get_consumer_key(); scopes::PreviewWidgetList createButtons(const std::string& uri, const click::Manifest& manifest); private: scopes::ActionMetadata metadata; CachedPreviewWidgets cachedWidgets; PackageDetails cachedDetails; }; class InstalledScopePreview : public PreviewStrategy { public: InstalledScopePreview(const unity::scopes::Result& result); void run(unity::scopes::PreviewReplyProxy const& reply) override; }; class PurchasingPreview : public PreviewStrategy { public: PurchasingPreview(const unity::scopes::Result& result, const QSharedPointer& client); virtual ~PurchasingPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: virtual scopes::PreviewWidgetList purchasingWidgets(const PackageDetails &); }; class CancelPurchasePreview : public PreviewStrategy { public: CancelPurchasePreview(const unity::scopes::Result& result, bool installed); virtual ~CancelPurchasePreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: scopes::PreviewWidgetList build_widgets(); bool installed; }; class UninstallConfirmationPreview : public PreviewStrategy { public: UninstallConfirmationPreview(const unity::scopes::Result& result); virtual ~UninstallConfirmationPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; }; class UninstalledPreview : public PreviewStrategy, public DepartmentUpdater { public: UninstalledPreview(const unity::scopes::Result& result, const QSharedPointer& client, const std::shared_ptr& depts, const QSharedPointer& manager, const QSharedPointer& ppackage); virtual ~UninstalledPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: PackageDetails found_details; CachedPreviewWidgets cachedWidgets; std::string found_object_path; virtual scopes::PreviewWidgetList uninstalledActionButtonWidgets(const PackageDetails &details); QSharedPointer dm; }; // TODO: this is only necessary to perform uninstall. // That should be moved to a separate action, and this class removed. class UninstallingPreview : public UninstalledPreview { public: UninstallingPreview(const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& manager, const QSharedPointer& ppackage); virtual ~UninstallingPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: void uninstall(); }; class CancellingPurchasePreview : public UninstallingPreview { public: CancellingPurchasePreview(const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& ppackage, const QSharedPointer& manager, bool installed); virtual ~CancellingPurchasePreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: void cancel_purchase(); bool installed; }; } // namespace click #endif ./libclickscope/click/reviews.h0000644000015600001650000000631512676763577016664 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_REVIEWS_H #define CLICK_REVIEWS_H #include #include #include #include namespace click { const std::string REVIEWS_BASE_URL_ENVVAR = "U1_REVIEWS_BASE_URL"; const std::string REVIEWS_BASE_URL = "https://reviews.ubuntu.com"; const std::string REVIEWS_API_PATH = "/click/api/1.0/reviews/"; const std::string REVIEWS_QUERY_ARGNAME = "package_name"; struct Review { uint32_t id; int rating; uint32_t usefulness_favorable; uint32_t usefulness_total; bool hide; std::string date_created; std::string date_deleted; std::string package_name; std::string package_version; std::string language; std::string summary; std::string review_text; std::string reviewer_name; std::string reviewer_username; }; typedef std::list ReviewList; ReviewList review_list_from_json (const std::string& json); ReviewList bring_to_front (const ReviewList& reviews, const std::string& userid); class Reviews { protected: QSharedPointer client; public: enum class Error {NoError, CredentialsError, NetworkError}; Reviews() {} Reviews(const QSharedPointer& client); virtual ~Reviews(); virtual click::web::Cancellable fetch_reviews (const std::string& package_name, std::function callback); click::web::Cancellable submit_review (const Review& review, std::function callback); click::web::Cancellable edit_review (const Review& review, std::function callback); static std::string get_base_url (); }; bool operator==(const Review& lhs, const Review& rhs); } // namespace click #endif // CLICK_REVIEWS_H ./libclickscope/click/pay.cpp0000644000015600001650000002421712676763604016314 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "pay.h" #include #include #include #include #include #include #include namespace json = Json; struct pay::Package::Private { Private() { } virtual ~Private() { } PayPackage *pay_package; }; static void pay_verification_observer(PayPackage*, const char* item_id, PayPackageItemStatus status, void* user_data) { pay::Package* p = static_cast(user_data); std::string callback_id = std::string{item_id} + pay::APPENDAGE_VERIFY; if (p->callbacks.count(callback_id) == 0) { qDebug() << "Verify observer called with no callback:" << item_id; return; } switch (status) { case PAY_PACKAGE_ITEM_STATUS_PURCHASED: p->callbacks[callback_id](item_id, true); break; case PAY_PACKAGE_ITEM_STATUS_NOT_PURCHASED: p->callbacks[callback_id](item_id, false); break; default: break; } } static void pay_refund_observer(PayPackage*, const char* item_id, PayPackageRefundStatus status, void* user_data) { pay::Package* p = static_cast(user_data); std::string callback_id = std::string{item_id} + pay::APPENDAGE_REFUND; if (p->callbacks.count(callback_id) == 0) { qDebug() << "Refund observer called with no callback:" << item_id; return; } switch (status) { case PAY_PACKAGE_REFUND_STATUS_NOT_PURCHASED: p->callbacks[callback_id](item_id, true); break; case PAY_PACKAGE_REFUND_STATUS_NOT_REFUNDABLE: p->callbacks[callback_id](item_id, false); default: break; } } namespace pay { bool operator==(const Purchase& lhs, const Purchase& rhs) { return lhs.name == rhs.name; } Package::Package() : impl(new Private()) { } Package::Package(const QSharedPointer& client) : impl(new Private()), client(client) { } Package::~Package() { if (running) { pay_package_item_observer_uninstall(impl->pay_package, pay_verification_observer, this); pay_package_delete(impl->pay_package); } } bool Package::refund(const std::string& pkg_name) { std::promise result_promise; std::future result_future = result_promise.get_future(); bool result; std::string callback_id = pkg_name + pay::APPENDAGE_REFUND; if (callbacks.count(callback_id) == 0) { callbacks[callback_id] = [pkg_name, this, callback_id, &result_promise](const std::string& item_id, bool succeeded) { if (item_id == pkg_name) { try { result_promise.set_value(succeeded); callbacks.erase(callback_id); } catch (std::future_error) { // Just log this to avoid crashing, as it seems that // sometimes this callback may be called more than once. qDebug() << "Refund callback called again for:" << item_id.c_str(); } } }; qDebug() << "Attempting to cancel purchase of " << pkg_name.c_str(); pay_package_refund(pkg_name); result = result_future.get(); return result; } return false; } bool Package::verify(const std::string& pkg_name) { std::promise result_promise; std::future result_future = result_promise.get_future(); bool result; std::string callback_id = pkg_name + pay::APPENDAGE_VERIFY; if (callbacks.count(callback_id) == 0) { callbacks[callback_id] = [pkg_name, &result_promise](const std::string& item_id, bool purchased) { if (item_id == pkg_name) { try { result_promise.set_value(purchased); } catch (std::future_error) { // Just log this to avoid crashing, as it seems that // sometimes this callback may be called more than once. qDebug() << "Callback called again for:" << item_id.c_str(); } } }; qDebug() << "Checking if " << pkg_name.c_str() << " was purchased."; pay_package_verify(pkg_name); result = result_future.get(); callbacks.erase(callback_id); return result; } return false; } bool Package::is_refundable(const std::string& pkg_name) { if (!running) { setup_pay_service(); } if (verify(pkg_name)) { // No Hondas, why racing? Wait for it… usleep(10000); return pay_package_item_is_refundable(impl->pay_package, pkg_name.c_str()) == 0 ? false : true; } // If verify() returned false, then it's not purchased. return false; } time_t parse_timestamp(json::Value v) { if (v.isNull()) { return 0; } QDateTime when = QDateTime::fromString(QString::fromStdString(v.asString()), Qt::ISODate); when.setTimeSpec(Qt::OffsetFromUTC); return when.toTime_t(); } click::web::Cancellable Package::get_purchases(std::function callback) { QSharedPointer sso(new click::CredentialsService()); client->setCredentialsService(sso); QSharedPointer response = client->call (get_base_url() + pay::API_ROOT + pay::PURCHASES_API_PATH, "GET", true); QObject::connect(response.data(), &click::web::Response::finished, [=](QString reply) { PurchaseSet purchases; json::Reader reader; json::Value root; if (reader.parse(reply.toUtf8().constData(), root)) { for (uint i = 0; i < root.size(); i++) { const json::Value item = root[i]; if (item[JsonKeys::state].asString() == PURCHASE_STATE_COMPLETE) { auto package_name = item[JsonKeys::package_name].asString(); qDebug() << "parsing:" << package_name.c_str(); auto refundable_until_value = item[JsonKeys::refundable_until]; qDebug() << "refundable until:" << refundable_until_value.asString().c_str(); auto refundable_parsed = parse_timestamp(refundable_until_value); qDebug() << "parsed:" << refundable_parsed; Purchase p(package_name, refundable_parsed); purchases.insert(p); } } } callback(purchases); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString) { qWarning() << "Network error getting purchases."; callback(PurchaseSet()); }); return click::web::Cancellable(response); } std::string Package::get_base_url() { const char *env_url = getenv(pay::BASE_URL_ENVVAR); if (env_url != NULL) { return env_url; } return pay::BASE_URL; } void Package::setup_pay_service() { PayPackage* newpkg = pay_package_new(Package::NAME); impl->pay_package = newpkg; qDebug() << "installing observers"; pay_package_item_observer_install(impl->pay_package, pay_verification_observer, this); pay_package_refund_observer_install(impl->pay_package, pay_refund_observer, this); running = true; } void Package::pay_package_refund(const std::string& pkg_name) { if (!running) { setup_pay_service(); } if (callbacks.count(pkg_name + pay::APPENDAGE_REFUND) == 0) { return; } pay_package_item_start_refund(impl->pay_package, pkg_name.c_str()); } void Package::pay_package_verify(const std::string& pkg_name) { if (!running) { setup_pay_service(); } if (callbacks.count(pkg_name + pay::APPENDAGE_VERIFY) == 0) { return; } pay_package_item_start_verification(impl->pay_package, pkg_name.c_str()); } } // namespace pay ./libclickscope/click/key_file_locator.cpp0000644000015600001650000000662112676763577021045 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "key_file_locator.h" #include #include #include #include #include #include #include namespace { static const QString NON_CLICK_PATH("/usr/share/applications"); void find_apps_in_dir(const QString& dir_path, const click::KeyFileLocator::Enumerator& enumerator) { QDir dir(dir_path, "*.desktop", QDir::Unsorted, QDir::Readable | QDir::Files); QStringList entries = dir.entryList(); for (int i = 0; i < entries.size(); ++i) { QString filename = entries.at(i); QString full_path = dir.absoluteFilePath(filename); try { enumerator(unity::util::IniParser(full_path.toUtf8().data()), filename.toUtf8().data()); } catch (const unity::FileException& file_exp) { qWarning() << "Error reading file:" << file_exp.to_string().c_str(); } catch (const unity::LogicException& logic_exp) { qCritical() << "Error reading file:" << logic_exp.to_string().c_str(); } } } } const std::string& click::KeyFileLocator::systemApplicationsDirectory() { static const std::string s{"/usr/share/applications"}; return s; } const std::string& click::KeyFileLocator::userApplicationsDirectory() { static const std::string s { qPrintable(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/applications") }; return s; } click::KeyFileLocator::KeyFileLocator( const std::string& systemApplicationsDir, const std::string& userApplicationsDir) : systemApplicationsDir(systemApplicationsDir), userApplicationsDir(userApplicationsDir) { } void click::KeyFileLocator::enumerateKeyFilesForInstalledApplications( const click::KeyFileLocator::Enumerator& enumerator) { find_apps_in_dir(QString::fromStdString(systemApplicationsDir), enumerator); find_apps_in_dir(QString::fromStdString(userApplicationsDir), enumerator); } ./libclickscope/click/departments.h0000644000015600001650000000631612676763577017527 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_DEPARTMENTS_H #define CLICK_DEPARTMENTS_H #include #include #include #include namespace click { class Department { public: typedef std::shared_ptr SPtr; typedef std::shared_ptr SCPtr; struct JsonKeys { JsonKeys() = delete; constexpr static const char* slug {"slug"}; constexpr static const char* name {"name"}; constexpr static const char* embedded {"_embedded"}; constexpr static const char* department {"clickindex:department"}; constexpr static const char* has_children {"has_children"}; constexpr static const char* links {"_links"}; constexpr static const char* self {"self"}; constexpr static const char* href {"href"}; }; Department(const std::string &id, const std::string &name, const std::string& href, bool has_children); std::string id() const; std::string name() const; std::string href() const; bool has_children_flag() const; void set_subdepartments(const std::list& deps); std::list sub_departments() const; static std::list from_json(const std::string& json); static std::list from_json_root_node(const Json::Value& val); private: static std::list from_json_node(const Json::Value& val); static Json::Value check_mandatory_attribute(const Json::Value& item, const std::string& name, Json::ValueType valtype); std::string id_; std::string name_; std::string href_; bool has_children_flag_; std::list sub_departments_; }; typedef std::list DepartmentList; } #endif ./libclickscope/click/scope_activation.h0000644000015600001650000000473312676763577020534 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_SCOPE_ACTIVATION_H #define CLICK_SCOPE_ACTIVATION_H #include #include #include namespace click { class PerformUninstallAction: public unity::scopes::ActivationQueryBase { public: PerformUninstallAction(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata, const unity::scopes::ActivationResponse& response); unity::scopes::ActivationResponse activate() override; private: unity::scopes::ActivationResponse response; }; class ScopeActivation : public unity::scopes::ActivationQueryBase { unity::scopes::ActivationResponse activate() override; public: ScopeActivation(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata); void setStatus(unity::scopes::ActivationResponse::Status status); void setHint(std::string key, unity::scopes::Variant value); private: unity::scopes::ActivationResponse::Status status_ = unity::scopes::ActivationResponse::Status::ShowPreview; unity::scopes::VariantMap hints_; }; } #endif ./libclickscope/click/interface.h0000644000015600001650000001150612676763577017136 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_INTERFACE_H #define CLICK_INTERFACE_H #include #include #include #include #include #include "application.h" #include "package.h" namespace click { class KeyFileLocator; class DepartmentsDb; // Hash map of desktop files that are not yet click packages const std::unordered_set& nonClickDesktopFiles(); struct Manifest { Manifest() = default; Manifest(std::string name, std::string version, std::string first_app_name) : name(name), version(version), first_app_name(first_app_name) { } virtual ~Manifest() = default; std::string name; std::string version; std::string first_app_name; std::string first_scope_id; bool removable = false; bool has_any_apps() const { return !first_app_name.empty(); } bool has_any_scopes() const { return !first_scope_id.empty(); } }; enum class InterfaceError {NoError, CallError, ParseError}; typedef std::list ManifestList; ManifestList manifest_list_from_json(const std::string& json); Manifest manifest_from_json(const std::string& json); PackageSet package_names_from_stdout(const std::string& stdout_data); class Interface { public: Interface(const QSharedPointer& keyFileLocator); Interface() = default; virtual ~Interface(); virtual std::string get_translated_string(const unity::util::IniParser& keyFile, const std::string& group, const std::string& key, const std::string& domain); virtual Application load_app_from_desktop(const unity::util::IniParser& keyFile, const std::string& filename); static std::vector sort_apps(const std::vector& apps); virtual std::vector find_installed_apps(const std::string& search_query, const std::vector& ignored_apps = std::vector{}, const std::string& current_department = "", const std::shared_ptr& depts_db = nullptr); static bool is_non_click_app(const QString& filename); static bool is_icon_identifier(const std::string &icon_id); static std::string add_theme_scheme(const std::string &filename); virtual void get_manifests(std::function callback); virtual void get_installed_packages(std::function callback); virtual void get_manifest_for_app(const std::string &app_id, std::function callback); virtual void get_dotdesktop_filename(const std::string &app_id, std::function callback); constexpr static const char* ENV_SHOW_DESKTOP_APPS {"CLICK_SCOPE_SHOW_DESKTOP_APPS"}; virtual bool is_visible_app(const unity::util::IniParser& keyFile); virtual bool show_desktop_apps(); virtual void run_process(const std::string& command, std::function callback); private: QSharedPointer keyFileLocator; }; } // namespace click #endif // CLICK_INTERFACE_H ./libclickscope/click/preview.cpp0000644000015600001650000016133312676763604017205 0ustar jenkinsjenkins/* * Copyright (C) 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include "preview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace click { void CachedPreviewWidgets::push(unity::scopes::PreviewWidget const &widget) { widgets.push_back(widget); widgets_lookup.insert(widget.id()); } void CachedPreviewWidgets::push(unity::scopes::PreviewWidgetList const &widgetList) { for (auto const& widget: widgetList) { push(widget); } } void CachedPreviewWidgets::flush(unity::scopes::PreviewReplyProxy const& reply) { // A safeguard: if a new widget gets added but missing in the layout, we will get a warning // in the logs and layouts will not be registered (single column with all widgets will be used). if (widgets.size() != layout.singleColumn.column1.size() || widgets.size() != layout.twoColumns.column1.size() + layout.twoColumns.column2.size()) { qWarning() << "Number of column layouts doesn't match the number of widgets"; } else { layout.registerLayouts(reply); } reply->push(widgets); widgets.clear(); widgets_lookup.clear(); } bool CachedPreviewWidgets::has(std::string const& widget) const { return widgets_lookup.find(widget) != widgets_lookup.end(); } void WidgetsInColumns::registerLayouts(unity::scopes::PreviewReplyProxy const& reply) { unity::scopes::ColumnLayout layout1col(1); layout1col.add_column(singleColumn.column1); unity::scopes::ColumnLayout layout2col(2); layout2col.add_column(twoColumns.column1); layout2col.add_column(twoColumns.column2); try { reply->register_layout({layout1col, layout2col}); } catch (unity::LogicException const& e) { qWarning() << "Failed to register layout:" << QString::fromStdString(e.what()); } } void WidgetsInColumns::appendToColumn(std::vector& column, unity::scopes::PreviewWidgetList const& widgets) { for (auto const& widget: widgets) { column.push_back(widget.id()); } } DepartmentUpdater::DepartmentUpdater(const std::shared_ptr& depts) : depts(depts) { } void DepartmentUpdater::store_department(const PackageDetails& details) { // // store package -> department mapping in sqlite db if (depts) { if (!details.department.empty()) { try { depts->store_package_mapping(details.package.name, details.department); qDebug() << "Storing mapping for" << QString::fromStdString(details.package.name) << ":" << QString::fromStdString(details.department); } catch (const std::exception& e) { qWarning() << "Failed to store package mapping for package '" << QString::fromStdString(details.package.name) << "', department '" << QString::fromStdString(details.department) << "':" << QString::fromStdString(e.what()); } } else { qWarning() << "Department is empty for package" << QString::fromStdString(details.package.name); } } } // Preview base class Preview::Preview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata) : PreviewQueryBase(result, metadata), result(result), metadata(metadata) { } Preview::~Preview() { } void Preview::choose_strategy(const QSharedPointer &client, const QSharedPointer& ppackage, const QSharedPointer& manager, std::shared_ptr depts) { strategy.reset(build_strategy(result, metadata, client, ppackage, manager, depts)); } PreviewStrategy* Preview::build_installing(const std::string& download_url, const std::string& download_sha512, const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& manager, std::shared_ptr depts) { return new InstallingPreview(download_url, download_sha512, result, client, manager, depts); } PreviewStrategy* Preview::build_strategy(const unity::scopes::Result &result, const unity::scopes::ActionMetadata &metadata, const QSharedPointer &client, const QSharedPointer& ppackage, const QSharedPointer& manager, std::shared_ptr depts) { if (metadata.scope_data().which() != scopes::Variant::Type::Null) { auto metadict = metadata.scope_data().get_dict(); if (metadict.count(click::Preview::Actions::DOWNLOAD_FAILED) != 0) { return new DownloadErrorPreview(result); } else if (metadict.count(click::Preview::Actions::DOWNLOAD_COMPLETED) != 0 || metadict.count(click::Preview::Actions::SHOW_INSTALLED) != 0) { qDebug() << "in Scope::preview(), metadata has download_completed=" << metadict.count(click::Preview::Actions::DOWNLOAD_COMPLETED) << " and close_preview=" << metadict.count(click::Preview::Actions::SHOW_INSTALLED); return new InstalledPreview(result, metadata, client, ppackage, depts); } else if (metadict.count("action_id") != 0 && metadict.count("download_url") != 0) { std::string action_id = metadict["action_id"].get_string(); std::string download_url = metadict["download_url"].get_string(); std::string download_sha512 = metadict["download_sha512"].get_string(); if (action_id == click::Preview::Actions::INSTALL_CLICK) { return build_installing(download_url, download_sha512, result, client, manager, depts); } else { qWarning() << "unexpected action id " << QString::fromStdString(action_id) << " given with download_url" << QString::fromStdString(download_url); return new UninstalledPreview(result, client, depts, manager, ppackage); } } else if (metadict.count(click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED) != 0) { return new CancelPurchasePreview(result, false); } else if (metadict.count(click::Preview::Actions::CANCEL_PURCHASE_INSTALLED) != 0) { return new CancelPurchasePreview(result, true); } else if (metadict.count(click::Preview::Actions::UNINSTALL_CLICK) != 0) { return new UninstallConfirmationPreview(result); } else if (metadict.count(click::Preview::Actions::CONFIRM_UNINSTALL) != 0) { return new UninstallingPreview(result, client, manager, ppackage); } else if (metadict.count(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED) != 0) { return new CancellingPurchasePreview(result, client, ppackage, manager, false); } else if (metadict.count(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED) != 0) { return new CancellingPurchasePreview(result, client, ppackage, manager, true); } else if (metadict.count(click::Preview::Actions::RATED) != 0) { return new InstalledPreview(result, metadata, client, ppackage, depts); } else if (metadict.count(click::Preview::Actions::SHOW_UNINSTALLED) != 0) { return new UninstalledPreview(result, client, depts, manager, ppackage); } else { qWarning() << "preview() called with unexpected metadata. returning uninstalled preview"; return new UninstalledPreview(result, client, depts, manager, ppackage); } } else { // metadata.scope_data() is Null, so we return an appropriate "default" preview: if (result.uri().find("scope://") == 0) { return new InstalledScopePreview(result); } if (result["installed"].get_bool() == true) { return new InstalledPreview(result, metadata, client, ppackage, depts); } else { return new UninstalledPreview(result, client, depts, manager, ppackage); } } } void Preview::cancelled() { strategy->cancelled(); } void Preview::run(const unity::scopes::PreviewReplyProxy &reply) { strategy->run(reply); } PreviewStrategy::PreviewStrategy(const unity::scopes::Result& result) : result(result), oa_client("ubuntuone", "ubuntuone", "ubuntuone", scopes::OnlineAccountClient::MainLoopSelect::CreateInternalMainLoop) { } PreviewStrategy::PreviewStrategy(const unity::scopes::Result& result, const QSharedPointer& client) : result(result), client(client), index(new click::Index(client)), reviews(new click::Reviews(client)), oa_client("ubuntuone", "ubuntuone", "ubuntuone", scopes::OnlineAccountClient::MainLoopSelect::CreateInternalMainLoop) { } PreviewStrategy::PreviewStrategy(const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& ppackage) : result(result), client(client), index(new click::Index(client)), reviews(new click::Reviews(client)), oa_client("ubuntuone", "ubuntuone", "ubuntuone", scopes::OnlineAccountClient::MainLoopSelect::CreateInternalMainLoop), pay_package(ppackage) { } void PreviewStrategy::pushPackagePreviewWidgets(const unity::scopes::PreviewReplyProxy &reply, const PackageDetails &details, const scopes::PreviewWidgetList& button_area_widgets) { reply->push(headerWidgets(details)); reply->push(button_area_widgets); reply->push(screenshotsWidgets(details)); reply->push(descriptionWidgets(details)); } void PreviewStrategy::pushPackagePreviewWidgets(CachedPreviewWidgets &cache, const PackageDetails& details, const scopes::PreviewWidgetList& button_area_widgets) { cache.push(headerWidgets(details)); cache.layout.singleColumn.column1.push_back("hdr"); cache.layout.twoColumns.column1.push_back("hdr"); cache.push(button_area_widgets); cache.layout.appendToColumn(cache.layout.singleColumn.column1, button_area_widgets); cache.layout.appendToColumn(cache.layout.twoColumns.column1, button_area_widgets); auto const screenshots = screenshotsWidgets(details); cache.push(screenshots); cache.layout.appendToColumn(cache.layout.singleColumn.column1, screenshots); cache.layout.appendToColumn(cache.layout.twoColumns.column1, screenshots); auto descr = descriptionWidgets(details); if (!descr.empty()) { cache.push(descr); cache.layout.appendToColumn(cache.layout.singleColumn.column1, descr); // for two-columns we need to split the widgets, summary goes to 1st column, everything else to 2nd if (descr.front().id() == "summary") { descr.pop_front(); cache.layout.twoColumns.column1.push_back("summary"); } cache.layout.appendToColumn(cache.layout.twoColumns.column2, descr); } } PreviewStrategy::~PreviewStrategy() { } void PreviewStrategy::cancelled() { index_operation.cancel(); reviews_operation.cancel(); submit_operation.cancel(); purchase_operation.cancel(); } scopes::PreviewWidget PreviewStrategy::build_other_metadata(const PackageDetails &details) { scopes::PreviewWidget widget("other_metadata", "table"); scopes::VariantArray values { scopes::Variant{scopes::VariantArray{scopes::Variant{_("Publisher/Creator")}, scopes::Variant{details.publisher}}}, scopes::Variant{scopes::VariantArray{scopes::Variant{_("Seller")}, scopes::Variant{details.company_name}}}, scopes::Variant{scopes::VariantArray{scopes::Variant{_("Website")}, scopes::Variant{details.website}}}, scopes::Variant{scopes::VariantArray{scopes::Variant{_("Contact")}, scopes::Variant{details.support_url}}}, scopes::Variant{scopes::VariantArray{scopes::Variant{_("License")}, scopes::Variant{details.license}}}, }; widget.add_attribute_value("values", scopes::Variant(values)); return widget; } scopes::PreviewWidget PreviewStrategy::build_updates_table(const PackageDetails& details) { scopes::PreviewWidget widget("updates_table", "table"); widget.add_attribute_value("title", scopes::Variant{_("Updates")}); scopes::VariantArray values { scopes::Variant{scopes::VariantArray{scopes::Variant{_("Version number")}, scopes::Variant{details.version}}}, scopes::Variant{scopes::VariantArray{scopes::Variant{_("Last updated")}, scopes::Variant{details.last_updated.formatted()}}}, scopes::Variant{scopes::VariantArray{scopes::Variant{_("First released")}, scopes::Variant{details.date_published.formatted()}}}, scopes::Variant{scopes::VariantArray{scopes::Variant{_("Size")}, scopes::Variant{click::Formatter::human_readable_filesize(details.binary_filesize)}}}, }; widget.add_attribute_value("values", scopes::Variant(values)); return widget; } std::string PreviewStrategy::build_whats_new(const PackageDetails& details) { std::stringstream b; b << _("Version") << ": " << details.version << std::endl; b << details.changelog; return b.str(); } void PreviewStrategy::run_under_qt(const std::function &task) { auto _app = QCoreApplication::instance(); if (_app != nullptr) { qt::core::world::enter_with_task([task]() { task(); }); } else { task(); } } std::string get_string_maybe_null(scopes::Variant variant) { if (variant.is_null()) { return ""; } else { return variant.get_string(); } } // TODO: error handling - once get_details provides errors, we can // return them from populateDetails and check them in the calling code // to decide whether to show error widgets. see bug LP: #1289541 void PreviewStrategy::populateDetails(std::function details_callback, std::function reviews_callback) { std::string app_name = get_string_maybe_null(result["name"]); if (app_name.empty()) { click::PackageDetails details; qDebug() << "in populateDetails(), app_name is empty"; details.package.title = result.title(); details.package.icon_url = result.art(); details.description = get_string_maybe_null(result["description"]); details.main_screenshot_url = get_string_maybe_null(result["main_screenshot"]); details_callback(details); reviews_callback(click::ReviewList(), click::Reviews::Error::NoError); } else { qDebug() << "in populateDetails(), app_name is:" << app_name.c_str(); // I think this should not be required when we switch the click::Index over // to using the Qt bridge. With that, the qt dependency becomes an implementation detail // and code using it does not need to worry about threading/event loop topics. run_under_qt([this, details_callback, reviews_callback, app_name]() { index_operation = index->get_details(app_name, [this, app_name, details_callback, reviews_callback](PackageDetails details, click::Index::Error error){ if(error == click::Index::Error::NoError) { qDebug() << "Got details:" << app_name.c_str(); details_callback(details); } else { qDebug() << "Error getting details for:" << app_name.c_str(); click::PackageDetails details; details.package.title = result.title(); details.package.icon_url = result.art(); details.description = get_string_maybe_null(result["description"]); details.main_screenshot_url = get_string_maybe_null(result["main_screenshot"]); details_callback(details); } reviews_operation = reviews->fetch_reviews(app_name, reviews_callback); }); }); } } scopes::PreviewWidgetList PreviewStrategy::screenshotsWidgets(const click::PackageDetails& details) { scopes::PreviewWidgetList widgets; bool has_screenshots = !details.main_screenshot_url.empty() || !details.more_screenshots_urls.empty(); if (has_screenshots) { scopes::PreviewWidget gallery("screenshots", "gallery"); scopes::VariantArray arr; if (!details.main_screenshot_url.empty()) arr.push_back(scopes::Variant(details.main_screenshot_url)); if (!details.more_screenshots_urls.empty()) { for (auto const& s: details.more_screenshots_urls) { arr.push_back(scopes::Variant(s)); } } gallery.add_attribute_value("sources", scopes::Variant(arr)); widgets.push_back(gallery); } return widgets; } scopes::PreviewWidgetList PreviewStrategy::headerWidgets(const click::PackageDetails& details) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget header("hdr", "header"); header.add_attribute_value("title", scopes::Variant(result.title())); if (!details.publisher.empty()) { header.add_attribute_value("subtitle", scopes::Variant(details.publisher)); } if (!details.package.icon_url.empty()) { header.add_attribute_value("mascot", scopes::Variant(details.package.icon_url)); } if (result.contains("price") && result.contains("rating")) { // Add the price and rating as attributes. bool purchased = result["purchased"].get_bool(); std::string price_area{""}; if (details.package.price == 0.00f) { price_area = _("FREE"); } else if (purchased) { price_area = _("✔ PURCHASED"); } else { price_area = result["formatted_price"].get_string(); } scopes::VariantBuilder builder; builder.add_tuple({ {"value", scopes::Variant(price_area)}, }); builder.add_tuple({ {"value", scopes::Variant("")}, }); builder.add_tuple({ {"value", result["rating"]}, }); builder.add_tuple({ {"value", scopes::Variant("")}, }); header.add_attribute_value("attributes", builder.end()); } widgets.push_back(header); qDebug() << "Pushed widgets for package:" << QString::fromStdString(details.package.name); return widgets; } scopes::PreviewWidgetList PreviewStrategy::descriptionWidgets(const click::PackageDetails& details) { scopes::PreviewWidgetList widgets; if (!details.description.empty()) { scopes::PreviewWidget summary("summary", "text"); summary.add_attribute_value("title", scopes::Variant(_("Info"))); if (result.contains("description") && !result["description"].get_string().empty()) { summary.add_attribute_value("text", scopes::Variant(result["description"].get_string())); } else { summary.add_attribute_value("text", scopes::Variant(details.description)); } widgets.push_back(summary); } if (!details.download_url.empty()) { widgets.push_back(build_other_metadata(details)); widgets.push_back(build_updates_table(details)); scopes::PreviewWidget whats_new("whats_new", "text"); whats_new.add_attribute_value("title", scopes::Variant(_("What's new"))); whats_new.add_attribute_value("text", scopes::Variant(build_whats_new(details))); widgets.push_back(whats_new); } return widgets; } scopes::PreviewWidgetList PreviewStrategy::reviewsWidgets(const click::ReviewList& reviewlist) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget rating("reviews", "reviews"); scopes::VariantBuilder builder; if (reviewlist.size() > 0) { scopes::PreviewWidget title("reviews_title", "text"); title.add_attribute_value("title", scopes::Variant(_("Reviews"))); widgets.push_back(title); for (const auto& kv : reviewlist) { builder.add_tuple({ {"rating", scopes::Variant(kv.rating)}, {"author", scopes::Variant(kv.reviewer_name)}, {"review", scopes::Variant(kv.review_text)} }); } rating.add_attribute_value("reviews", builder.end()); widgets.push_back(rating); } return widgets; } scopes::PreviewWidgetList PreviewStrategy::downloadErrorWidgets() { return errorWidgets(scopes::Variant(_("Download Error")), scopes::Variant(_("Download or install failed. Please try again.")), scopes::Variant(click::Preview::Actions::SHOW_UNINSTALLED), scopes::Variant(_("Close"))); } scopes::PreviewWidgetList PreviewStrategy::loginErrorWidgets(const std::string& download_url, const std::string& download_sha512) { auto widgets = errorWidgets(scopes::Variant(_("Login Error")), scopes::Variant(_("Please log in to your Ubuntu One account.")), scopes::Variant(click::Preview::Actions::INSTALL_CLICK), scopes::Variant(_("Go to Accounts"))); auto buttons = widgets.back(); widgets.pop_back(); scopes::VariantBuilder builder; builder.add_tuple( { {"id", scopes::Variant(click::Preview::Actions::INSTALL_CLICK)}, {"label", scopes::Variant(_("Go to Accounts"))}, {"download_url", scopes::Variant(download_url)}, {"download_sha512", scopes::Variant(download_sha512)}, }); buttons.add_attribute_value("actions", builder.end()); oa_client.register_account_login_item(buttons, scopes::OnlineAccountClient::PostLoginAction::ContinueActivation, scopes::OnlineAccountClient::PostLoginAction::DoNothing); widgets.push_back(buttons); return widgets; } scopes::PreviewWidgetList PreviewStrategy::errorWidgets(const scopes::Variant& title, const scopes::Variant& subtitle, const scopes::Variant& action_id, const scopes::Variant& action_label, const scopes::Variant& uri) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget header("hdr", "header"); header.add_attribute_value("title", title); header.add_attribute_value("subtitle", subtitle); widgets.push_back(header); scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; if (uri.is_null()) { builder.add_tuple({ {"id", action_id}, {"label", action_label} }); } else { builder.add_tuple({ {"id", action_id}, {"label", action_label}, {"uri", uri} }); } buttons.add_attribute_value("actions", builder.end()); widgets.push_back(buttons); return widgets; } bool PreviewStrategy::isRefundable() { if (!result.contains("price")) { return false; } if (pay_package.isNull()) { return false; } std::string pkg_name = get_string_maybe_null(result["name"]); if (pkg_name.empty()) { return false; } return pay_package->is_refundable(pkg_name); } void PreviewStrategy::invalidateScope(const std::string& scope_id) { run_under_qt([scope_id]() { PackageManager::invalidate_results(scope_id); }); } // class DownloadErrorPreview DownloadErrorPreview::DownloadErrorPreview(const unity::scopes::Result &result) : PreviewStrategy(result) { } DownloadErrorPreview::~DownloadErrorPreview() { } void DownloadErrorPreview::run(const unity::scopes::PreviewReplyProxy &reply) { // NOTE: no details used by downloadErrorWidgets(), so no need to // call populateDetails() here. reply->push(downloadErrorWidgets()); } // class InstallingPreview InstallingPreview::InstallingPreview(const std::string &download_url, const std::string &download_sha512, const unity::scopes::Result &result, const QSharedPointer& client, const QSharedPointer& manager, std::shared_ptr depts) : PreviewStrategy(result, client), DepartmentUpdater(depts), download_url(download_url), download_sha512(download_sha512), dm(new DownloadManager(client, manager)), depts_db(depts) { } InstallingPreview::~InstallingPreview() { } void InstallingPreview::startLauncherAnimation(const PackageDetails &details) { Launcher l(LAUNCHER_BUSNAME, LAUNCHER_OBJECT_PATH, QDBusConnection::sessionBus()); l.startInstallation(QString::fromStdString(details.package.title), QString::fromStdString(details.package.icon_url), QString::fromStdString(details.package.name)); } void InstallingPreview::run(const unity::scopes::PreviewReplyProxy &reply) { qDebug() << "Starting installation" << QString(download_url.c_str()) << QString(download_sha512.c_str()); std::promise promise; auto future = promise.get_future(); run_under_qt([this, reply, &promise]() { QSharedPointer sso(new click::CredentialsService()); dm->setCredentialsService(sso); dm->start(download_url, download_sha512, result["name"].get_string(), [this, reply, &promise] (std::string msg, DownloadManager::Error dmerr){ switch (dmerr) { case DownloadManager::Error::DownloadInstallError: qWarning() << "Error received from UDM during startDownload:" << msg.c_str(); reply->push(downloadErrorWidgets()); promise.set_value(false); break; case DownloadManager::Error::CredentialsError: qWarning() << "InstallingPreview got error in getting credentials during startDownload"; reply->push(loginErrorWidgets(download_url, download_sha512)); promise.set_value(false); break; case DownloadManager::Error::NoError: { std::string object_path = msg; qDebug() << "Successfully created UDM Download."; populateDetails([this, reply, object_path](const PackageDetails &details) { store_department(details); pushPackagePreviewWidgets(cachedWidgets, details, progressBarWidget(object_path)); startLauncherAnimation(details); }, [this, reply, &promise](const ReviewList& reviewlist, click::Reviews::Error error) { if (error == click::Reviews::Error::NoError) { auto const revs = reviewsWidgets(reviewlist); cachedWidgets.push(revs); cachedWidgets.layout.appendToColumn(cachedWidgets.layout.singleColumn.column1, revs); cachedWidgets.layout.appendToColumn(cachedWidgets.layout.twoColumns.column1, revs); } else { qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); } cachedWidgets.flush(reply); promise.set_value(true); }); break; } default: qCritical() << "Unknown error occurred downloading."; promise.set_value(false); break; } }); }); future.get(); reply->finished(); } scopes::PreviewWidgetList PreviewStrategy::progressBarWidget(const std::string& object_path) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget progress("download", "progress"); scopes::VariantMap tuple; tuple["dbus-name"] = "com.canonical.applications.Downloader"; tuple["dbus-object"] = object_path; progress.add_attribute_value("source", scopes::Variant(tuple)); widgets.push_back(progress); return widgets; } // class InstalledPreview InstalledPreview::InstalledPreview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata, const QSharedPointer& client, const QSharedPointer& ppackage, const std::shared_ptr& depts) : PreviewStrategy(result, client, ppackage), DepartmentUpdater(depts), metadata(metadata) { } InstalledPreview::~InstalledPreview() { } std::string InstalledPreview::get_consumer_key() { std::promise promise; auto future = promise.get_future(); QSharedPointer sso; qt::core::world::enter_with_task([this, &sso, &promise]() { sso.reset(new click::CredentialsService()); QObject::connect(sso.data(), &click::CredentialsService::credentialsFound, [&promise, &sso](const UbuntuOne::Token& token) { qDebug() << "Credentials found"; sso.clear(); promise.set_value(token.consumerKey().toStdString()); }); QObject::connect(sso.data(), &click::CredentialsService::credentialsNotFound, [&promise, &sso]() { qDebug() << "No credentials found"; sso.clear(); promise.set_value(""); }); sso->getCredentials(); qDebug() << "getCredentials finished"; }); return future.get(); } void InstalledPreview::run(unity::scopes::PreviewReplyProxy const& reply) { // Check if the user is submitting a rating, so we can submit it. Review review; review.rating = 0; std::string widget_id; // We use a try/catch here, as scope_data() can be a dict, but not have // the values we need, which will result in an exception thrown. try { auto metadict = metadata.scope_data().get_dict(); review.rating = metadict["rating"].get_int(); review.review_text = metadict["review"].get_string(); widget_id = metadict["widget_id"].get_string(); } catch(...) { // Do nothing as we are not submitting a review. } auto userid = get_consumer_key(); // // Get the click manifest. Manifest manifest; std::promise manifest_promise; std::future manifest_future = manifest_promise.get_future(); std::string app_name = result["name"].get_string(); if (!app_name.empty()) { qt::core::world::enter_with_task([&]() { click::Interface().get_manifest_for_app(app_name, [&](Manifest found_manifest, InterfaceError error) { qDebug() << "Got manifest for:" << app_name.c_str(); // Fill in required data about the package being reviewed. review.package_name = found_manifest.name; review.package_version = found_manifest.version; if (error != click::InterfaceError::NoError) { qDebug() << "There was an error getting the manifest for:" << app_name.c_str(); } manifest_promise.set_value(found_manifest); }); }); manifest = manifest_future.get(); if (review.rating > 0) { std::promise submit_promise; std::future submit_future = submit_promise.get_future(); qt::core::world::enter_with_task([this, review, &submit_promise, widget_id]() mutable { QSharedPointer sso(new click::CredentialsService()); client->setCredentialsService(sso); if (widget_id == "rating") { submit_operation = reviews->submit_review(review, [&submit_promise](click::Reviews::Error){ // TODO: Need to handle errors properly. submit_promise.set_value(true); }); } else { try { review.id = std::stoul(widget_id); qDebug() << "Updating review" << review.id << "with '" << QString::fromStdString(review.review_text) << "'"; submit_operation = reviews->edit_review(review, [&submit_promise](click::Reviews::Error){ // TODO: Need to handle errors properly. submit_promise.set_value(true); }); } catch (const std::exception &e) { qWarning() << "Failed to update review:" << QString::fromStdString(e.what()) << " review widget:" << QString::fromStdString(widget_id); submit_promise.set_value(false); } } }); submit_future.get(); } } getApplicationUri(manifest, [this, reply, manifest, app_name, &review, userid](const std::string& uri) { populateDetails([this, reply, uri, manifest, app_name](const PackageDetails &details){ cachedDetails = details; store_department(details); pushPackagePreviewWidgets(cachedWidgets, details, createButtons(uri, manifest)); }, [this, reply, &review, manifest, userid](const ReviewList& reviewlist, click::Reviews::Error error) { auto reviews = bring_to_front(reviewlist, userid); if (manifest.removable && !cachedDetails.download_url.empty()) { scopes::PreviewWidgetList review_input; bool has_reviewed = reviews.size() > 0 && reviews.front().reviewer_username == userid; if (has_reviewed) { auto existing_review = reviews.front(); reviews.pop_front(); qDebug() << "Review for current user already exists, review id:" << existing_review.id; scopes::PreviewWidget rating(std::to_string(existing_review.id), "rating-edit"); // pass review id via widget id rating.add_attribute_value("required", scopes::Variant("rating")); rating.add_attribute_value("review", scopes::Variant(existing_review.review_text)); rating.add_attribute_value("rating", scopes::Variant(existing_review.rating)); rating.add_attribute_value("author", scopes::Variant(existing_review.reviewer_name)); review_input.push_back(rating); } else { scopes::PreviewWidget rating("rating", "rating-input"); rating.add_attribute_value("required", scopes::Variant("rating")); review_input.push_back(rating); } cachedWidgets.push(review_input); cachedWidgets.layout.appendToColumn(cachedWidgets.layout.singleColumn.column1, review_input); cachedWidgets.layout.appendToColumn(cachedWidgets.layout.twoColumns.column1, review_input); } if (error == click::Reviews::Error::NoError) { auto const revs = reviewsWidgets(reviews); cachedWidgets.push(revs); cachedWidgets.layout.appendToColumn(cachedWidgets.layout.singleColumn.column1, revs); cachedWidgets.layout.appendToColumn(cachedWidgets.layout.twoColumns.column1, revs); } else { qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); } cachedWidgets.flush(reply); reply->finished(); }); }); } scopes::PreviewWidgetList InstalledPreview::createButtons(const std::string& uri, const Manifest& manifest) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; std::string open_label = _("Open"); if (!manifest.has_any_apps() && manifest.has_any_scopes()) { open_label = _("Search"); } if (!uri.empty()) { builder.add_tuple( { {"id", scopes::Variant(click::Preview::Actions::OPEN_CLICK)}, {"label", scopes::Variant(open_label)}, {"uri", scopes::Variant(uri)} }); qDebug() << "Adding button" << QString::fromStdString(open_label) << "-" << QString::fromStdString(uri); } if (manifest.removable) { auto price = result.contains("price") ? result["price"].get_double() : 0.00f; if (price > 0.00f && isRefundable()) { builder.add_tuple({ {"id", scopes::Variant(click::Preview::Actions::CANCEL_PURCHASE_INSTALLED)}, {"label", scopes::Variant(_("Cancel Purchase"))} }); } else { builder.add_tuple({ {"id", scopes::Variant(click::Preview::Actions::UNINSTALL_CLICK)}, {"label", scopes::Variant(_("Uninstall"))} }); } } if (!uri.empty() || manifest.removable) { buttons.add_attribute_value("actions", builder.end()); widgets.push_back(buttons); } return widgets; } void InstalledPreview::getApplicationUri(const Manifest& manifest, std::function callback) { QString app_url = QString::fromStdString(result.uri()); // asynchronously get application uri based on app name, if the uri is not application://. // this can happen if the app was just installed and we have its http uri from the Result. if (!app_url.startsWith("application:///")) { const std::string name = result["name"].get_string(); if (manifest.has_any_apps()) { qt::core::world::enter_with_task([this, name, callback] () { click::Interface().get_dotdesktop_filename(name, [callback, name] (std::string val, click::InterfaceError error) { std::string uri; if (error == click::InterfaceError::NoError) { uri = "application:///" + val; } else { qWarning() << "Can't get .desktop filename for" << QString::fromStdString(name); } callback(uri); } ); }); } else { if (manifest.has_any_scopes()) { unity::scopes::CannedQuery cq(manifest.first_scope_id); auto scope_uri = cq.to_uri(); qDebug() << "Found uri for scope" << QString::fromStdString(manifest.first_scope_id) << "-" << QString::fromStdString(scope_uri); callback(scope_uri); } } } else { callback(result.uri()); } } // class InstalledScopePreview // this is a temporary fallback preview to get into the Store scope, the proper // requires 'store' category to be treated special (like 'local') in unity8 shell. InstalledScopePreview::InstalledScopePreview(const unity::scopes::Result& result) : PreviewStrategy(result) { } void InstalledScopePreview::run(unity::scopes::PreviewReplyProxy const& reply) { scopes::PreviewWidget actions("actions", "actions"); { scopes::VariantBuilder builder; builder.add_tuple({ {"id", scopes::Variant("search")}, {"uri", scopes::Variant(result.uri())}, {"label", scopes::Variant(_("Search"))} }); actions.add_attribute_value("actions", builder.end()); } reply->push({actions}); } // class PurchasingPreview PurchasingPreview::PurchasingPreview(const unity::scopes::Result& result, const QSharedPointer& client) : PreviewStrategy(result, client) { } PurchasingPreview::~PurchasingPreview() { } void PurchasingPreview::run(unity::scopes::PreviewReplyProxy const& reply) { populateDetails([this, reply](const PackageDetails &details){ reply->push(purchasingWidgets(details)); }, [this, reply](const click::ReviewList&, click::Reviews::Error) { reply->finished(); }); } scopes::PreviewWidgetList PurchasingPreview::purchasingWidgets(const PackageDetails &/*details*/) { scopes::PreviewWidgetList widgets; return widgets; } // class CancelPurchasePreview CancelPurchasePreview::CancelPurchasePreview(const unity::scopes::Result& result, bool installed) : PreviewStrategy(result), installed(installed) { } CancelPurchasePreview::~CancelPurchasePreview() { } scopes::PreviewWidgetList CancelPurchasePreview::build_widgets() { scopes::PreviewWidgetList widgets; scopes::PreviewWidget confirmation("confirmation", "text"); std::string title = result["title"].get_string(); // TRANSLATORS: Do NOT translate ${title} here. std::string message = _("Are you sure you want to cancel the purchase of '${title}'? The app will be uninstalled."); boost::replace_first(message, "${title}", title); confirmation.add_attribute_value("text", scopes::Variant(message)); widgets.push_back(confirmation); scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; auto action_no = installed ? click::Preview::Actions::SHOW_INSTALLED : click::Preview::Actions::SHOW_UNINSTALLED; auto action_yes = installed ? click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_INSTALLED : click::Preview::Actions::CONFIRM_CANCEL_PURCHASE_UNINSTALLED; builder.add_tuple({ {"id", scopes::Variant(action_no)}, {"label", scopes::Variant(_("Go Back"))} }); builder.add_tuple({ {"id", scopes::Variant(action_yes)}, {"label", scopes::Variant(_("Continue"))} }); buttons.add_attribute_value("actions", builder.end()); widgets.push_back(buttons); scopes::PreviewWidget policy("policy", "text"); policy.add_attribute_value("title", scopes::Variant{_("Returns and cancellation policy")}); policy.add_attribute_value("text", scopes::Variant{ _("When purchasing an app in the Ubuntu Store, you can cancel the charge within 15 minutes " "after installation. If the cancel period has passed, we recommend contacting the app " "developer directly for a refund.\n" "You can find the developer’s contact information listed on the app’s preview page in the " "Ubuntu Store.\n" "Keep in mind that you cannot cancel the purchasing process of an app more than once.")}); widgets.push_back(policy); return widgets; } void CancelPurchasePreview::run(unity::scopes::PreviewReplyProxy const& reply) { // NOTE: no need to populateDetails() here. reply->push(build_widgets()); } // class UninstallConfirmationPreview UninstallConfirmationPreview::UninstallConfirmationPreview(const unity::scopes::Result& result) : PreviewStrategy(result) { } UninstallConfirmationPreview::~UninstallConfirmationPreview() { } void UninstallConfirmationPreview::run(unity::scopes::PreviewReplyProxy const& reply) { // NOTE: no need to populateDetails() here. scopes::PreviewWidgetList widgets; scopes::PreviewWidget header("hdr", "header"); header.add_attribute_value("title", scopes::Variant(_("Confirmation"))); std::string title = result["title"].get_string(); // TRANSLATORS: Do NOT translate ${title} here. std::string message = _("Uninstall ${title}?"); boost::replace_first(message, "${title}", title); header.add_attribute_value("subtitle", scopes::Variant(message)); widgets.push_back(header); scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; builder.add_tuple({ {"id", scopes::Variant(click::Preview::Actions::SHOW_INSTALLED)}, {"label", scopes::Variant(_("Cancel"))} }); builder.add_tuple({ {"id", scopes::Variant(click::Preview::Actions::CONFIRM_UNINSTALL)}, {"label", scopes::Variant(_("Confirm"))} }); buttons.add_attribute_value("actions", builder.end()); widgets.push_back(buttons); reply->push(widgets); } // class UninstalledPreview UninstalledPreview::UninstalledPreview(const unity::scopes::Result& result, const QSharedPointer& client, const std::shared_ptr& depts, const QSharedPointer& manager, const QSharedPointer& ppackage) : PreviewStrategy(result, client, ppackage), DepartmentUpdater(depts), dm(new DownloadManager(client, manager)) { qDebug() << "Creating new UninstalledPreview for result" << QString::fromStdString(result["name"].get_string()); } UninstalledPreview::~UninstalledPreview() { } void UninstalledPreview::run(unity::scopes::PreviewReplyProxy const& reply) { qDebug() << "in UninstalledPreview::run, about to populate details"; populateDetails([this, reply](const PackageDetails &details){ store_department(details); found_details = details; }, [this, reply](const ReviewList& reviewlist, click::Reviews::Error reviewserror) { std::string app_name = result["name"].get_string(); dm->get_progress(app_name, [this, reply, reviewlist, reviewserror](std::string object_path){ found_object_path = object_path; scopes::PreviewWidgetList button_widgets; if(found_object_path.empty()) { button_widgets = uninstalledActionButtonWidgets(found_details); } else { button_widgets = progressBarWidget(found_object_path); } qDebug() << "Pushed button action widgets."; pushPackagePreviewWidgets(cachedWidgets, found_details, button_widgets); qDebug() << "Pushed package details widgets."; if (reviewserror == click::Reviews::Error::NoError) { qDebug() << "Pushing reviews widgets."; auto const revs = reviewsWidgets(reviewlist); cachedWidgets.push(revs); cachedWidgets.layout.appendToColumn(cachedWidgets.layout.singleColumn.column1, revs); cachedWidgets.layout.appendToColumn(cachedWidgets.layout.twoColumns.column1, revs); } else { qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); } cachedWidgets.flush(reply); reply->finished(); qDebug() << "---------- Finished reply for:" << result["name"].get_string().c_str(); }); }); } scopes::PreviewWidgetList UninstalledPreview::uninstalledActionButtonWidgets(const PackageDetails &details) { scopes::PreviewWidgetList widgets; auto price = result["price"].get_double(); if (price > double(0.00) && result["purchased"].get_bool() == false) { scopes::PreviewWidget payments("purchase", "payments"); scopes::VariantMap tuple; tuple["currency"] = result["currency_symbol"].get_string(); tuple["price"] = scopes::Variant(price); tuple["store_item_id"] = details.package.name; tuple["download_url"] = details.download_url; tuple["download_sha512"] = details.download_sha512; payments.add_attribute_value("source", scopes::Variant(tuple)); oa_client.register_account_login_item(payments, scopes::OnlineAccountClient::PostLoginAction::ContinueActivation, scopes::OnlineAccountClient::PostLoginAction::DoNothing); widgets.push_back(payments); } else { scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; builder.add_tuple( { {"id", scopes::Variant(click::Preview::Actions::INSTALL_CLICK)}, {"label", scopes::Variant(_("Install"))}, {"download_url", scopes::Variant(details.download_url)}, {"download_sha512", scopes::Variant(details.download_sha512)}, }); if (isRefundable()) { builder.add_tuple( { {"id", scopes::Variant(click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED)}, {"label", scopes::Variant(_("Cancel Purchase"))}, }); } buttons.add_attribute_value("actions", builder.end()); oa_client.register_account_login_item(buttons, scopes::OnlineAccountClient::PostLoginAction::ContinueActivation, scopes::OnlineAccountClient::PostLoginAction::DoNothing); widgets.push_back(buttons); } return widgets; } // class UninstallingPreview : public UninstalledPreview // TODO: this class should be removed once uninstall() is handled elsewhere. UninstallingPreview::UninstallingPreview(const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& manager, const QSharedPointer& ppackage) : UninstalledPreview(result, client, nullptr, manager, ppackage) { } UninstallingPreview::~UninstallingPreview() { } void UninstallingPreview::run(unity::scopes::PreviewReplyProxy const& reply) { qDebug() << "in UninstallingPreview::run, calling uninstall"; uninstall(); qDebug() << "in UninstallingPreview::run, calling UninstalledPreview::run()"; UninstalledPreview::run(reply); } void UninstallingPreview::uninstall() { click::Package package; package.title = result.title(); package.name = result["name"].get_string(); package.version = result["version"].get_string(); qt::core::world::enter_with_task([this, package] () { click::PackageManager manager; manager.uninstall(package, [&](int code, std::string stderr_content) { if (code != 0) { qDebug() << "Error removing package:" << stderr_content.c_str(); } else { qDebug() << "successfully removed package"; } } ); }); } // class CancellingPurchasePreview : public UninstallingPreview CancellingPurchasePreview::CancellingPurchasePreview(const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& ppackage, const QSharedPointer& manager, bool installed) : UninstallingPreview(result, client, manager, ppackage), installed(installed) { } CancellingPurchasePreview::~CancellingPurchasePreview() { } void CancellingPurchasePreview::run(unity::scopes::PreviewReplyProxy const& reply) { qDebug() << "in CancellingPurchasePreview::run, calling cancel_purchase"; cancel_purchase(); qDebug() << "in CancellingPurchasePreview::run, calling next ::run()"; if (installed) { UninstallingPreview::run(reply); } else { UninstalledPreview::run(reply); } } void CancellingPurchasePreview::cancel_purchase() { auto package_name = result["name"].get_string(); qDebug() << "Will cancel the purchase of:" << package_name.c_str(); std::promise refund_promise; std::future refund_future = refund_promise.get_future(); run_under_qt([this, &refund_promise, package_name]() { qDebug() << "Calling refund for:" << package_name.c_str(); auto ret = pay_package->refund(package_name); qDebug() << "Refund returned:" << ret; refund_promise.set_value(ret); }); bool finished = refund_future.get(); qDebug() << "Finished refund:" << finished; if (finished) { // Reset the purchased flag. result["purchased"] = false; invalidateScope(STORE_SCOPE_ID.toUtf8().data()); } } } // namespace click ./libclickscope/click/pay.h0000644000015600001650000001006712676763577015770 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef _PAY_H_ #define _PAY_H_ #include #include #include #include #include namespace pay { constexpr static const char* APPENDAGE_VERIFY{":verify"}; constexpr static const char* APPENDAGE_REFUND{":refund"}; struct Purchase { std::string name; time_t refundable_until; Purchase() = default; Purchase(const std::string &name) : name(name), refundable_until(0) { } Purchase(const std::string& name, time_t refundable_until) : name(name), refundable_until(refundable_until) { } struct hash_name { public : size_t operator()(const Purchase &purchase ) const { return std::hash()(purchase.name); } }; }; bool operator==(const Purchase& lhs, const Purchase& rhs); typedef std::unordered_set PurchaseSet; typedef std::function StatusFunction; constexpr static const char* BASE_URL_ENVVAR{"PAY_BASE_URL"}; constexpr static const char* BASE_URL{"https://software-center.ubuntu.com"}; constexpr static const char* API_ROOT{"/api/2.0/click/"}; constexpr static const char* PURCHASES_API_PATH{"purchases/"}; constexpr static const char* PURCHASE_STATE_COMPLETE{"Complete"}; struct JsonKeys { JsonKeys() = delete; constexpr static const char* package_name{"package_name"}; constexpr static const char* refundable_until{"refundable_until"}; constexpr static const char* state{"state"}; }; class Package { public: constexpr static const char* NAME{"click-scope"}; Package(); Package(const QSharedPointer& client); virtual ~Package(); virtual bool refund(const std::string& pkg_name); virtual bool verify(const std::string& pkg_name); virtual bool is_refundable(const std::string& pkg_name); virtual click::web::Cancellable get_purchases(std::function callback); static std::string get_base_url(); protected: virtual void setup_pay_service(); virtual void pay_package_refund(const std::string& pkg_name); virtual void pay_package_verify(const std::string& pkg_name); struct Private; QScopedPointer impl; bool running = false; QSharedPointer client; public: std::map callbacks; }; } //namespace pay #endif // _PAY_H_ ./libclickscope/click/launcher.cpp0000644000015600001650000000122712676763577017331 0ustar jenkinsjenkins/* * This file was generated by qdbusxml2cpp version 0.8 * Command line was: qdbusxml2cpp launcher.xml -p launcher -c Launcher -N * * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). * * This is an auto-generated file. * This file may have been hand-edited. Look for HAND-EDIT comments * before re-generating it. */ #include "launcher.h" /* * Implementation of interface class Launcher */ Launcher::Launcher(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) { } Launcher::~Launcher() { } ./libclickscope/click/network_access_manager.cpp0000644000015600001650000000777112676763577022246 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "network_access_manager.h" #include #include click::network::Reply::Reply(QNetworkReply* reply) : reply(reply) { connect(this->reply.data(), &QNetworkReply::finished, this, &Reply::finished); typedef void(QNetworkReply::*QNetworkReplyErrorSignal)(QNetworkReply::NetworkError); connect(this->reply.data(), static_cast(&QNetworkReply::error), this, &Reply::error); } click::network::Reply::~Reply() { } void click::network::Reply::abort() { reply->abort(); } QByteArray click::network::Reply::readAll() { return reply->readAll(); } QVariant click::network::Reply::attribute(QNetworkRequest::Attribute code) { return reply->attribute(code); } bool click::network::Reply::hasRawHeader(const QByteArray &headerName) { return reply->hasRawHeader(headerName); } QString click::network::Reply::rawHeader(const QByteArray &headerName) { return reply->rawHeader(headerName); } QList> click::network::Reply::rawHeaderPairs() { return reply->rawHeaderPairs(); } QString click::network::Reply::errorString() { return reply->errorString(); } click::network::Reply::Reply() { } namespace { QNetworkAccessManager& networkAccessManagerInstance() { static QNetworkAccessManager nam; if (!nam.cache()) { QNetworkDiskCache* cache = new QNetworkDiskCache(&nam); cache->setCacheDirectory(QString("%1/unity-scope-click/network").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))); nam.setCache(cache); } return nam; } } QSharedPointer click::network::AccessManager::get(QNetworkRequest& request) { return QSharedPointer(new click::network::Reply(networkAccessManagerInstance().get(request))); } QSharedPointer click::network::AccessManager::head(QNetworkRequest& request) { return QSharedPointer(new click::network::Reply(networkAccessManagerInstance().head(request))); } QSharedPointer click::network::AccessManager::post(QNetworkRequest& request, QByteArray& data) { return QSharedPointer(new click::network::Reply(networkAccessManagerInstance().post(request, data))); } QSharedPointer click::network::AccessManager::sendCustomRequest(QNetworkRequest& request, QByteArray& verb, QIODevice *data) { return QSharedPointer(new click::network::Reply(networkAccessManagerInstance().sendCustomRequest(request, verb, data))); } ./libclickscope/click/qtbridge.cpp0000644000015600001650000000775712676763577017347 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Jussi Pakkanen * Thomas Voß */ #include "qtbridge.h" #include #include #include #include #include #include #include namespace { QCoreApplication* app = nullptr; } namespace qt { namespace core { namespace world { namespace detail { QEvent::Type qt_core_world_task_event_type() { static QEvent::Type event_type = static_cast(QEvent::registerEventType()); return event_type; } class TaskEvent : public QEvent { public: TaskEvent(const std::function& task) : QEvent(qt_core_world_task_event_type()), task(task) { } void run() { try { task(); promise.set_value(); } catch(...) { promise.set_exception(std::current_exception()); } } std::future get_future() { return promise.get_future(); } private: std::function task; std::promise promise; }; class TaskHandler : public QObject { Q_OBJECT public: TaskHandler(QObject* parent) : QObject(parent) { } bool event(QEvent* e); }; void createCoreApplicationInstanceWithArgs(int argc, char** argv) { app = new QCoreApplication(argc, argv); } void destroyCoreApplicationInstace() { delete app; } QCoreApplication* coreApplicationInstance() { return app; } TaskHandler* task_handler() { static TaskHandler* instance = new TaskHandler(coreApplicationInstance()); return instance; } bool TaskHandler::event(QEvent *e) { if (e->type() != qt_core_world_task_event_type()) return QObject::event(e); auto te = dynamic_cast(e); if (te) { te->run(); return true; } return false; } } void build_and_run(int argc, char** argv, const std::function& ready) { QThread::currentThread(); if (QCoreApplication::instance() != nullptr) throw std::runtime_error( "qt::core::world::build_and_run: " "There is already a QCoreApplication running."); detail::createCoreApplicationInstanceWithArgs(argc, argv); detail::task_handler()->moveToThread( detail::coreApplicationInstance()->thread()); // Signal to other worlds that we are good to go. ready(); detail::coreApplicationInstance()->exec(); // Someone has called quit and we clean up on the correct thread here. detail::destroyCoreApplicationInstace(); } void destroy() { enter_with_task([]() { // We make sure that all tasks have completed before quitting the app. QEventLoopLocker locker; }).wait_for(std::chrono::seconds{1}); } std::future enter_with_task(const std::function& task) { QCoreApplication* instance = QCoreApplication::instance(); if (!instance) { throw std::runtime_error("Qt world has not been built before calling this function."); } detail::TaskEvent* te = new detail::TaskEvent(task); auto future = te->get_future(); // We hand over ownership of te here. The event is deleted later after it has // been processed by the event loop. instance->postEvent(detail::task_handler(), te); return future; } } } } #include "qtbridge.moc" ./libclickscope/click/CMakeLists.txt0000644000015600001650000000260112676763577017561 0ustar jenkinsjenkinsSET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package (Qt5Core REQUIRED) find_package (Qt5Sql REQUIRED) pkg_check_modules(JSON_CPP REQUIRED jsoncpp) pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt) pkg_check_modules(LIBPAY REQUIRED pay-2) add_definitions( -DGETTEXT_PACKAGE=\"${PROJECT_NAME}\" -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}\" -DCLICK_INSTALL_HELPER=\"${CMAKE_INSTALL_PREFIX}/lib/unity-scope-click/install-helper\" ${GSETTINGS_QT_CFLAGS} ${GSETTINGS_QT_OTHER} ) add_library(${SCOPE_LIB_NAME} STATIC configuration.cpp download-manager.cpp department-lookup.cpp departments.cpp departments-db.cpp highlights.cpp index.cpp interface.cpp key_file_locator.cpp launcher.cpp network_access_manager.cpp package.cpp pay.cpp preview.cpp reviews.cpp qtbridge.cpp scope_activation.cpp smartconnect.cpp ubuntuone_credentials.cpp utils.cpp webclient.cpp ) qt5_use_modules(${SCOPE_LIB_NAME} Sql) include_directories( ${JSON_CPP_INCLUDE_DIRS} ${LIBPAY_INCLUDE_DIRS} ${GSETTINGS_QT_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/libclickscope ) target_link_libraries (${SCOPE_LIB_NAME} ${JSON_CPP_LDFLAGS} ${LIBPAY_LDFLAGS} ${UNITY_SCOPES_LDFLAGS} ${UBUNTUONE_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS} ${GSETTINGS_QT_LIBRARIES} -lboost_locale ) ./libclickscope/click/configuration.cpp0000644000015600001650000001643312676763577020404 0ustar jenkinsjenkins/* * Copyright (C) 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "configuration.h" namespace click { /* NOTE: The list of languages we need to use the full language code for. * Please keep this list in a-z order. */ const std::vector Configuration::FULL_LANG_CODES = { "pt_BR", "zh_CN", "zh_TW", }; /* NOTE: The list of currencies we need to handle mapping symbols of. * Please keep this list in A-Z order. */ const std::map Configuration::CURRENCY_MAP = { { "CNY", "RMB"}, { "EUR", "€"}, { "GBP", "₤"}, { "HKD", "HK$"}, { "TWD", "TW$"}, { "USD", "US$"}, }; std::vector Configuration::list_folder(const std::string& folder, const std::string& pattern) { std::vector result; QDir dir(QString::fromStdString(folder), QString::fromStdString(pattern), QDir::Unsorted, QDir::Readable | QDir::Files); QStringList entries = dir.entryList(); for (int i = 0; i < entries.size(); ++i) { QString filename = entries.at(i); result.push_back(filename.toStdString()); } return result; } std::vector Configuration::get_available_frameworks() { std::vector result; for (auto f: list_folder(FRAMEWORKS_FOLDER, FRAMEWORKS_PATTERN)) { result.push_back(f.substr(0, f.size()-FRAMEWORKS_EXTENSION_LENGTH)); } return result; } std::string Configuration::architectureFromDpkg() { QString program("dpkg"); QStringList arguments; arguments << "--print-architecture"; QProcess archDetector; archDetector.start(program, arguments); if(!archDetector.waitForFinished()) { throw std::runtime_error("Architecture detection failed."); } auto output = archDetector.readAllStandardOutput(); auto ostr = QString::fromUtf8(output); ostr.remove('\n'); return ostr.toStdString(); } std::string Configuration::get_architecture() { const char* env_arch = getenv(ARCH_ENVVAR); static const std::string deb_arch {architectureFromDpkg()}; if (env_arch == NULL) { return deb_arch; } static const std::string arch {env_arch}; return arch; } bool Configuration::get_purchases_enabled() { const char* env_value = std::getenv(PURCHASES_ENVVAR); if (env_value == NULL) { return PURCHASES_DEFAULT; } return std::string("1") == env_value; } std::string Configuration::get_currency(const std::string& fallback) { const char* env_value = std::getenv(CURRENCY_ENVVAR); if (env_value == NULL) { if (CURRENCY_MAP.count(fallback) == 0) { return CURRENCY_DEFAULT; } return fallback; } if (CURRENCY_MAP.count(env_value) == 0) { return CURRENCY_DEFAULT; } return env_value; } std::string Configuration::get_language_base() { std::string language = get_language(); std::vector lang_parts; boost::split(lang_parts, language, boost::is_any_of("_")); return lang_parts[0]; } std::string Configuration::get_language() { const char* language = getenv(LANGUAGE_ENVVAR); if (language == NULL) { language = "C"; } std::vector lang_parts; boost::split(lang_parts, language, boost::is_any_of(".")); return lang_parts[0]; } std::string Configuration::get_accept_languages() { std::string language = get_language(); std::vector lang_parts; boost::split(lang_parts, language, boost::is_any_of("_")); std::string result; if (lang_parts.size() > 1) { boost::replace_first(language, "_", "-"); result = language + ", " + get_language_base(); } else { result = language; } return result; } bool Configuration::is_full_lang_code(const std::string& language) { return std::find(FULL_LANG_CODES.begin(), FULL_LANG_CODES.end(), language) != FULL_LANG_CODES.end(); } std::string Configuration::deviceIdFromWhoopsie() { QDBusInterface iface("com.ubuntu.WhoopsiePreferences", "/com/ubuntu/WhoopsiePreferences", "com.ubuntu.WhoopsiePreferences", QDBusConnection::systemBus(), 0); QDBusReply reply = iface.call("GetIdentifier"); auto value = reply.value(); return value.toStdString(); } std::string Configuration::get_device_id() { static const std::string whoopsie_id {deviceIdFromWhoopsie()}; return whoopsie_id; } const std::vector Configuration::get_dconf_strings(const std::string& schema, const std::string& key) const { if (!QGSettings::isSchemaInstalled(schema.c_str())) { qWarning() << "Schema" << QString::fromStdString(schema) << "is missing"; return std::vector(); } QGSettings qgs(schema.c_str()); std::vector v; if (qgs.keys().contains(QString::fromStdString(key))) { auto locations = qgs.get(QString::fromStdString(key)).toStringList(); for(const auto& l : locations) { v.push_back(l.toStdString()); } } else { qWarning() << "No" << QString::fromStdString(key) << " key in schema" << QString::fromStdString(schema); } return v; } const std::vector Configuration::get_core_apps() const { auto apps = get_dconf_strings(Configuration::COREAPPS_SCHEMA, Configuration::COREAPPS_KEY); if (apps.empty()) { apps = get_default_core_apps(); } return apps; } const std::vector Configuration::get_ignored_apps() const { auto apps = get_dconf_strings(Configuration::COREAPPS_SCHEMA, Configuration::IGNORED_KEY); return apps; } } // namespace click ./libclickscope/click/package.h0000644000015600001650000001540412676763577016572 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_PACKAGE_H #define CLICK_PACKAGE_H #include #include #include #include #include #include #include namespace json = Json; namespace click { struct Package { struct JsonKeys { JsonKeys() = delete; constexpr static const char* embedded {"_embedded"}; constexpr static const char* links{"_links"}; constexpr static const char* self{"self"}; constexpr static const char* href{"href"}; constexpr static const char* ci_package {"clickindex:package"}; constexpr static const char* ci_recommends {"clickindex:recommendation"}; constexpr static const char* name{"name"}; constexpr static const char* title{"title"}; constexpr static const char* price{"price"}; constexpr static const char* icon_url{"icon_url"}; constexpr static const char* content{"content"}; constexpr static const char* publisher{"publisher"}; constexpr static const char* rating{"ratings_average"}; constexpr static const char* version{"version"}; // NOTE: The "price" field is deprecated in favor of "prices" constexpr static const char* prices{"prices"}; }; Package() = default; Package(const std::string& name, const std::string& title, double price, const std::string& icon_url, const std::string& url) : name(name), title(title), price(price), icon_url(icon_url), url(url) { } Package(const std::string& name, const std::string& title, double price, const std::string& icon_url, const std::string& url, const std::string& version, const std::string& content) : name(name), title(title), price(price), icon_url(icon_url), url(url), version(version), content(content) { } Package(const std::string& name, const std::string& version) : name(name), version(version) { } virtual ~Package() = default; std::string name; // formerly app_id std::string title; double price = 0.0f; std::string icon_url; std::string url; std::string version; std::string publisher; double rating = 0.0f; void matches (std::string query, std::function callback); std::string content; std::map prices; struct hash_name { public : size_t operator()(const Package &package ) const { return std::hash()(package.name); } }; }; typedef std::vector Packages; typedef std::unordered_set PackageSet; Package package_from_json_node(const Json::Value& item); Packages package_list_from_json(const std::string& json); Packages package_list_from_json_node(const Json::Value& root); struct PackageDetails { struct JsonKeys { JsonKeys() = delete; constexpr static const char* name{"name"}; constexpr static const char* title{"title"}; constexpr static const char* icon_url{"icon_url"}; constexpr static const char* description{"description"}; constexpr static const char* download_url{"download_url"}; constexpr static const char* download_sha512{"download_sha512"}; constexpr static const char* rating{"rating"}; constexpr static const char* keywords{"keywords"}; constexpr static const char* terms_of_service{"terms_of_service"}; constexpr static const char* license{"license"}; constexpr static const char* publisher{"publisher"}; constexpr static const char* developer_name{"developer_name"}; constexpr static const char* company_name{"company_name"}; constexpr static const char* website{"website"}; constexpr static const char* support_url{"support_url"}; constexpr static const char* department{"department"}; constexpr static const char* main_screenshot_url{"screenshot_url"}; constexpr static const char* more_screenshot_urls{"screenshot_urls"}; constexpr static const char* binary_filesize{"binary_filesize"}; constexpr static const char* version{"version"}; constexpr static const char* date_published{"date_published"}; constexpr static const char* last_updated{"last_updated"}; constexpr static const char* changelog{"changelog"}; constexpr static const char* framework{"framework"}; }; static PackageDetails from_json(const std::string &json); Package package; std::string description; std::string download_url; std::string download_sha512; double rating; std::string keywords; std::string terms_of_service; std::string license; std::string publisher; std::string developer_name; std::string company_name; std::string website; std::string support_url; std::string main_screenshot_url; std::list more_screenshots_urls; json::Value::UInt64 binary_filesize; std::string version; click::Date date_published; click::Date last_updated; std::string changelog; std::string framework; std::string department; }; std::ostream& operator<<(std::ostream& out, const PackageDetails& details); bool operator==(const Package& lhs, const Package& rhs); bool operator==(const PackageDetails& lhs, const PackageDetails& rhs); } // namespace click #endif // CLICK_PACKAGE_H ./libclickscope/click/reviews.cpp0000644000015600001650000002242512676763604017206 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include "reviews.h" namespace click { bool operator== (const Review& lhs, const Review &rhs) { return lhs.id == rhs.id && lhs.rating == rhs.rating && lhs.usefulness_favorable == rhs.usefulness_favorable && lhs.usefulness_total == rhs.usefulness_total && lhs.hide == rhs.hide && lhs.date_created == rhs.date_created && lhs.date_deleted == rhs.date_deleted && lhs.package_name == rhs.package_name && lhs.package_version == rhs.package_version && lhs.language == rhs.language && lhs.summary == rhs.summary && lhs.review_text == rhs.review_text && lhs.reviewer_name == rhs.reviewer_name && lhs.reviewer_username == rhs.reviewer_username; } ReviewList review_list_from_json (const std::string& json) { std::istringstream stream(json); boost::property_tree::ptree tree; boost::property_tree::read_json(stream, tree); ReviewList reviews; BOOST_FOREACH(boost::property_tree::ptree::value_type &value, tree) { assert(value.first.empty()); // array elements have no names auto node = value.second; Review review; review.id = node.get("id"); review.rating = node.get("rating"); review.usefulness_favorable = node.get("usefulness_favorable"); review.usefulness_total = node.get("usefulness_total"); review.hide = node.get("hide"); review.date_created = node.get("date_created"); review.date_deleted = node.get("date_deleted"); review.package_name = node.get("package_name"); review.package_version = node.get("version"); review.language = node.get("language"); review.summary = node.get("summary"); review.review_text = node.get("review_text"); review.reviewer_username = node.get("reviewer_username"); review.reviewer_name = node.get("reviewer_displayname", review.reviewer_username); reviews.push_back(review); } return reviews; } ReviewList bring_to_front (const ReviewList& reviews, const std::string& userid) { if (userid.size() == 0) { return reviews; } auto new_reviews = reviews; auto it = std::find_if(new_reviews.begin(), new_reviews.end(), [userid](const Review& review) { return review.reviewer_username == userid; }); if (it != new_reviews.end() && it != new_reviews.begin()) { // move own review to the front auto const review = *it; new_reviews.erase(it); new_reviews.push_front(review); } return new_reviews; } Reviews::Reviews (const QSharedPointer& client) : client(client) { } Reviews::~Reviews () { } click::web::Cancellable Reviews::fetch_reviews (const std::string& package_name, std::function callback) { click::web::CallParams params; params.add(click::REVIEWS_QUERY_ARGNAME, package_name.c_str()); QSharedPointer response = client->call (get_base_url() + click::REVIEWS_API_PATH, params); QObject::connect(response.data(), &click::web::Response::finished, [=](QString reply) { auto status = response->get_status_code(); click::ReviewList reviews; if (status == 200) { reviews = review_list_from_json(reply.toUtf8().constData()); callback(reviews, click::Reviews::Error::NoError); } else { callback(reviews, click::Reviews::Error::NetworkError); } }); QObject::connect(response.data(), &click::web::Response::error, [=](QString) { qDebug() << "Network error attempting to fetch reviews for:" << package_name.c_str(); callback(ReviewList(), click::Reviews::Error::NetworkError); }); return click::web::Cancellable(response); } click::web::Cancellable Reviews::submit_review (const Review& review, std::function callback) { std::map headers({ {click::web::CONTENT_TYPE_HEADER, click::web::CONTENT_TYPE_JSON}, }); Json::Value root(Json::ValueType::objectValue); root["package_name"] = review.package_name; root["version"] = review.package_version; root["rating"] = review.rating; root["review_text"] = review.review_text; root["arch_tag"] = click::Configuration().get_architecture(); /* NOTE: We only use the base language code for reviews, except for * codes in the click::Configuration::FULL_LANG_CODES vector. */ auto language = click::Configuration().get_language(); if (click::Configuration::is_full_lang_code(language)) { root["language"] = language; } else { root["language"] = click::Configuration().get_language_base(); } // NOTE: "summary" is a required field, but we don't have one. Use "". root["summary"] = "Review"; qDebug() << "Rating" << review.package_name.c_str() << review.rating; QSharedPointer response = client->call (get_base_url() + click::REVIEWS_API_PATH, "POST", true, headers, Json::FastWriter().write(root), click::web::CallParams()); QObject::connect(response.data(), &click::web::Response::finished, [=](QString) { qDebug() << "Review submitted for:" << review.package_name.c_str(); callback(Error::NoError); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString) { qCritical() << "Network error submitting a reviews for:" << review.package_name.c_str(); callback(Error::NetworkError); }); return click::web::Cancellable(response); } click::web::Cancellable Reviews::edit_review (const Review& review, std::function callback) { std::map headers({ {click::web::CONTENT_TYPE_HEADER, click::web::CONTENT_TYPE_JSON}, }); Json::Value root(Json::ValueType::objectValue); root["rating"] = review.rating; root["review_text"] = review.review_text; // NOTE: "summary" is a required field, but we don't have one. Use "". root["summary"] = "Review"; qDebug() << "Rating" << review.package_name.c_str() << review.rating; QSharedPointer response = client->call (get_base_url() + click::REVIEWS_API_PATH + std::to_string(review.id) + "/", "PUT", true, headers, Json::FastWriter().write(root), click::web::CallParams()); QObject::connect(response.data(), &click::web::Response::finished, [=](QString) { qDebug() << "Review updated for:" << review.package_name.c_str(); callback(Error::NoError); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString) { qCritical() << "Network error updating a review for:" << review.package_name.c_str(); callback(Error::NetworkError); }); return click::web::Cancellable(response); } std::string Reviews::get_base_url () { const char *env_url = getenv(REVIEWS_BASE_URL_ENVVAR.c_str()); if (env_url != NULL) { return env_url; } return click::REVIEWS_BASE_URL; } } //namespace click ./libclickscope/click/departments-db.cpp0000644000015600001650000004363612676763577020453 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "departments-db.h" #include #include #include #include #include #include #include namespace click { std::unique_ptr DepartmentsDb::open(bool create) { auto const path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); if (!path.isEmpty()) { QDir("/").mkpath(path); const std::string dbpath = path.toStdString() + "/click-departments.db"; return std::unique_ptr(new DepartmentsDb(dbpath, create)); } throw std::runtime_error("Cannot determine cache directory"); } DepartmentsDb::DepartmentsDb(const std::string& name, bool create) { db_ = QSqlDatabase::addDatabase("QSQLITE"); db_.setDatabaseName(QString::fromStdString(name)); if (!db_.open()) { throw std::runtime_error("Cannot open departments database"); } if (create) { init_db(); } else { QSqlQuery query; // check for existence of meta table to see if we're dealing with uninitialized database if (!query.exec("SELECT 1 FROM meta")) { throw std::runtime_error("Invalid departments database"); } } delete_pkgmap_query_.reset(new QSqlQuery(db_)); delete_depts_query_.reset(new QSqlQuery(db_)); delete_deptnames_query_.reset(new QSqlQuery(db_)); insert_pkgmap_query_.reset(new QSqlQuery(db_)); insert_dept_id_query_.reset(new QSqlQuery(db_)); insert_dept_name_query_.reset(new QSqlQuery(db_)); select_pkgs_by_dept_.reset(new QSqlQuery(db_)); select_dept_for_pkg_.reset(new QSqlQuery(db_)); select_pkg_by_pkgid_.reset(new QSqlQuery(db_)); select_pkgs_by_dept_recursive_.reset(new QSqlQuery(db_)); select_pkgs_count_in_dept_recursive_.reset(new QSqlQuery(db_)); select_parent_dept_.reset(new QSqlQuery(db_)); select_children_depts_.reset(new QSqlQuery(db_)); select_dept_name_.reset(new QSqlQuery(db_)); select_is_descendant_of_dept_.reset(new QSqlQuery(db_)); delete_pkgmap_query_->prepare("DELETE FROM pkgmap WHERE pkgid=:pkgid"); delete_depts_query_->prepare("DELETE FROM depts"); delete_deptnames_query_->prepare("DELETE FROM deptnames WHERE locale=:locale"); insert_pkgmap_query_->prepare("INSERT OR REPLACE INTO pkgmap (pkgid, deptid) VALUES (:pkgid, :deptid)"); insert_dept_id_query_->prepare("INSERT OR REPLACE INTO depts (deptid, parentid) VALUES (:deptid, :parentid)"); insert_dept_name_query_->prepare("INSERT OR REPLACE INTO deptnames (deptid, locale, name) VALUES (:deptid, :locale, :name)"); select_pkgs_by_dept_->prepare("SELECT pkgid FROM pkgmap WHERE deptid=:deptid"); select_pkgs_by_dept_recursive_->prepare("WITH RECURSIVE recdepts(deptid) AS (SELECT deptid FROM depts WHERE deptid=:deptid OR parentid=:deptid UNION SELECT depts.deptid FROM recdepts,depts WHERE recdepts.deptid=depts.parentid) SELECT pkgid FROM pkgmap NATURAL JOIN recdepts"); select_dept_for_pkg_->prepare("SELECT deptid from pkgmap WHERE pkgid=:pkgid"); select_pkgs_count_in_dept_recursive_->prepare("WITH RECURSIVE recdepts(deptid) AS (SELECT deptid FROM depts WHERE deptid=:deptid OR parentid=:deptid UNION SELECT depts.deptid FROM recdepts,depts WHERE recdepts.deptid=depts.parentid) SELECT COUNT(pkgid) FROM pkgmap NATURAL JOIN recdepts"); select_pkg_by_pkgid_->prepare("SELECT pkgid FROM pkgmap WHERE pkgid=:pkgid"); select_children_depts_->prepare("SELECT deptid,(SELECT COUNT(1) from depts AS inner WHERE inner.parentid=outer.deptid) FROM depts AS outer WHERE parentid=:parentid"); select_parent_dept_->prepare("SELECT parentid FROM depts WHERE deptid=:deptid"); select_dept_name_->prepare("SELECT name FROM deptnames WHERE deptid=:deptid AND locale=:locale"); select_is_descendant_of_dept_->prepare("WITH RECURSIVE recdepts(deptid) AS (SELECT deptid FROM depts WHERE parentid=:parentid UNION SELECT depts.deptid FROM recdepts,depts WHERE recdepts.deptid=depts.parentid) SELECT COUNT(1) FROM recdepts WHERE deptid=:deptid"); } DepartmentsDb::~DepartmentsDb() { } void DepartmentsDb::init_db() { // // CAUTION: // DON'T FORGET TO BUMP SCHEMA VERSION BELOW AND HANDLE SCHEMA UPGRADE IN data/update_schema.sh // WHENEVER YOU CHANGE ANY OF THE TABLES BELOW! QSqlQuery query; // FIXME: for some reason enabling foreign keys gives errors about number of arguments of prepared queries when doing query.exec(); do not enable // them for now. // query.exec("PRAGMA foreign_keys = ON"); // activate write-ahead logging, see http://sqlite.org/wal.html to avoid transaction errors with concurrent reads and writes query.exec("PRAGMA journal_mode=WAL"); db_.transaction(); // package id -> department id mapping table if (!query.exec("CREATE TABLE IF NOT EXISTS pkgmap (pkgid TEXT, deptid TEXT, CONSTRAINT pkey PRIMARY KEY (pkgid, deptid))")) { report_db_error(query.lastError(), "Failed to create pkgmap table"); } // department id -> parent department id mapping table if (!query.exec("CREATE TABLE IF NOT EXISTS depts (deptid TEXT, parentid TEXT, CONSTRAINT pkey PRIMARY KEY (deptid, parentid), CONSTRAINT fkey FOREIGN KEY (deptid) REFERENCES deptnames(deptid))")) { report_db_error(query.lastError(), "Failed to create depts table"); } // department id, locale -> deparment name mapping table if (!query.exec("CREATE TABLE IF NOT EXISTS deptnames (deptid TEXT, locale TEXT, name TEXT, CONSTRAINT deptuniq PRIMARY KEY (deptid, locale))")) { report_db_error(query.lastError(), "Failed to create depts table"); } // name -> value table for storing arbitrary values such as schema version if (!query.exec("CREATE TABLE IF NOT EXISTS meta (name TEXT PRIMARY KEY, value TEXT)")) { report_db_error(query.lastError(), "Failed to create meta table"); } // // note: this will fail due to unique constraint, but that's fine; it's expected to succeed only when new database is created; in other // cases the version needs to be bumped in the update_schema.sh script. query.exec("INSERT INTO meta (name, value) VALUES ('version', 4)"); if (!db_.commit()) { report_db_error(db_.lastError(), "Failed to commit init transaction"); } } void DepartmentsDb::report_db_error(const QSqlError& error, const std::string& message) { throw std::runtime_error(message + ": " + error.text().toStdString()); } std::string DepartmentsDb::get_department_name(const std::string& department_id, const std::list& locales) { for (auto const& locale: locales) { select_dept_name_->bindValue(":deptid", QVariant(QString::fromStdString(department_id))); select_dept_name_->bindValue(":locale", QVariant(QString::fromStdString(locale))); if (!select_dept_name_->exec()) { report_db_error(select_dept_name_->lastError(), "Failed to query for department name of " + department_id + ", locale " + locale); } if (select_dept_name_->next()) { auto const res = select_dept_name_->value(0).toString().toStdString(); select_dept_name_->finish(); return res; } } select_dept_name_->finish(); throw std::logic_error("No name for department " + department_id); } std::string DepartmentsDb::get_parent_department_id(const std::string& department_id) { select_parent_dept_->bindValue(":deptid", QVariant(QString::fromStdString(department_id))); if (!select_parent_dept_->exec()) { report_db_error(select_parent_dept_->lastError(), "Failed to query for parent department " + department_id); } if (!select_parent_dept_->next()) { select_dept_name_->finish(); throw std::logic_error("Unknown department '" + department_id + "'"); } auto const res = select_parent_dept_->value(0).toString().toStdString(); select_parent_dept_->finish(); return res; } std::list DepartmentsDb::get_children_departments(const std::string& department_id) { select_children_depts_->bindValue(":parentid", QVariant(QString::fromStdString(department_id))); if (!select_children_depts_->exec()) { report_db_error(select_children_depts_->lastError(), "Failed to query for children departments of " + department_id); } std::list depts; while (select_children_depts_->next()) { auto const child_id = select_children_depts_->value(0).toString().toStdString(); const DepartmentInfo inf(child_id, select_children_depts_->value(1).toBool()); depts.push_back(inf); } select_children_depts_->finish(); return depts; } bool DepartmentsDb::is_descendant_of_department(const std::string& department_id, const std::string& parent_department_id) { select_is_descendant_of_dept_->bindValue(":deptid", QVariant(QString::fromStdString(department_id))); select_is_descendant_of_dept_->bindValue(":parentid", QVariant(QString::fromStdString(parent_department_id))); if (!select_is_descendant_of_dept_->exec() || !select_is_descendant_of_dept_->next()) { report_db_error(select_is_descendant_of_dept_->lastError(), "Failed to query for package count of department " + department_id); } auto cnt = select_is_descendant_of_dept_->value(0).toInt(); select_is_descendant_of_dept_->finish(); return cnt > 0; } bool DepartmentsDb::is_empty(const std::string& department_id) { select_pkgs_count_in_dept_recursive_->bindValue(":deptid", QVariant(QString::fromStdString(department_id))); if (!select_pkgs_count_in_dept_recursive_->exec() || !select_pkgs_count_in_dept_recursive_->next()) { report_db_error(select_pkgs_count_in_dept_recursive_->lastError(), "Failed to query for package count of department " + department_id); } auto cnt = select_pkgs_count_in_dept_recursive_->value(0).toInt(); select_pkgs_count_in_dept_recursive_->finish(); return cnt == 0; } std::unordered_set DepartmentsDb::get_packages_for_department(const std::string& department_id, bool recursive) { std::unordered_set pkgs; QSqlQuery *query = recursive ? select_pkgs_by_dept_recursive_.get() : select_pkgs_by_dept_.get(); query->bindValue(":deptid", QVariant(QString::fromStdString(department_id))); if (!query->exec()) { report_db_error(query->lastError(), "Failed to query for packages of department " + department_id); } while (query->next()) { pkgs.insert(query->value(0).toString().toStdString()); } query->finish(); return pkgs; } std::string DepartmentsDb::get_department_for_package(const std::string& package_id) { select_dept_for_pkg_->bindValue(":pkgid", QVariant(QString::fromStdString(package_id))); if (!select_dept_for_pkg_->exec()) { report_db_error(select_dept_for_pkg_->lastError(), "Failed to query for department of package " + package_id); } if (!select_dept_for_pkg_->next()) { select_dept_for_pkg_->finish(); throw std::logic_error("Unknown package '" + package_id + "'"); } auto const res = select_dept_for_pkg_->value(0).toString().toStdString(); select_dept_for_pkg_->finish(); return res; } bool DepartmentsDb::has_package(const std::string& package_id) { select_pkg_by_pkgid_->bindValue(":pkgid", QVariant(QString::fromStdString(package_id))); if (!select_pkg_by_pkgid_->exec()) { report_db_error(select_parent_dept_->lastError(), "Failed to query for package " + package_id); } if (!select_pkg_by_pkgid_->next()) { select_pkg_by_pkgid_->finish(); return false; } select_pkg_by_pkgid_->finish(); return true; } void DepartmentsDb::store_package_mapping(const std::string& package_id, const std::string& department_id) { if (package_id.empty()) { throw std::logic_error("Invalid empty package_id"); } if (department_id.empty()) { throw std::logic_error("Invalid empty department id"); } if (!db_.transaction()) { std::cerr << "Failed to start transaction" << std::endl; } // delete package mapping first from any departments delete_pkgmap_query_->bindValue(":pkgid", QVariant(QString::fromStdString(package_id))); delete_pkgmap_query_->exec(); delete_pkgmap_query_->finish(); insert_pkgmap_query_->bindValue(":pkgid", QVariant(QString::fromStdString(package_id))); insert_pkgmap_query_->bindValue(":deptid", QVariant(QString::fromStdString(department_id))); if (!insert_pkgmap_query_->exec()) { if (!db_.rollback()) { std::cerr << "Failed to rollback transaction" << std::endl; } report_db_error(insert_pkgmap_query_->lastError(), "Failed to insert into pkgmap"); } insert_pkgmap_query_->finish(); if (!db_.commit()) { db_.rollback(); report_db_error(db_.lastError(), "Failed to commit transaction in store_package_mapping"); } } void DepartmentsDb::store_department_mapping(const std::string& department_id, const std::string& parent_department_id) { if (department_id.empty()) { throw std::logic_error("Invalid empty department id"); } insert_dept_id_query_->bindValue(":deptid", QVariant(QString::fromStdString(department_id))); insert_dept_id_query_->bindValue(":parentid", QVariant(QString::fromStdString(parent_department_id))); if (!insert_dept_id_query_->exec()) { report_db_error(insert_dept_id_query_->lastError(), "Failed to insert into depts"); } insert_dept_id_query_->finish(); } void DepartmentsDb::store_department_name(const std::string& department_id, const std::string& locale, const std::string& name) { if (department_id.empty()) { throw std::logic_error("Invalid empty department id"); } if (name.empty()) { throw std::logic_error("Invalid empty department name"); } insert_dept_name_query_->bindValue(":deptid", QVariant(QString::fromStdString(department_id))); insert_dept_name_query_->bindValue(":locale", QVariant(QString::fromStdString(locale))); insert_dept_name_query_->bindValue(":name", QVariant(QString::fromStdString(name))); if (!insert_dept_name_query_->exec()) { report_db_error(insert_dept_name_query_->lastError(), "Failed to insert into deptnames"); } insert_dept_name_query_->finish(); } int DepartmentsDb::department_mapping_count() const { QSqlQuery q(db_); if (!q.exec("SELECT COUNT(*) FROM depts") || !q.next()) { report_db_error(q.lastError(), "Failed to query depts table"); } return q.value(0).toInt(); } int DepartmentsDb::package_count() const { QSqlQuery q(db_); if (!q.exec("SELECT COUNT(*) FROM pkgmap") || !q.next()) { report_db_error(q.lastError(), "Failed to query pkgmap table"); } return q.value(0).toInt(); } int DepartmentsDb::department_name_count() const { QSqlQuery q(db_); if (!q.exec("SELECT COUNT(*) FROM deptnames") || !q.next()) { report_db_error(q.lastError(), "Failed to query deptnames table"); } return q.value(0).toInt(); } void DepartmentsDb::store_departments_(const click::DepartmentList& depts, const std::string& locale) { for (auto const& dept: depts) { store_department_name(dept->id(), locale, dept->name()); for (auto const& subdep: dept->sub_departments()) { store_department_mapping(subdep->id(), dept->id()); } store_departments_(dept->sub_departments(), locale); } } void DepartmentsDb::store_departments(const click::DepartmentList& depts, const std::string& locale) { if (!db_.transaction()) { std::cerr << "Failed to start transaction" << std::endl; } // // delete existing departments for given locale first delete_deptnames_query_->bindValue(":locale", QVariant(QString::fromStdString(locale))); if (!delete_deptnames_query_->exec()) { db_.rollback(); report_db_error(delete_deptnames_query_->lastError(), "Failed to delete from deptnames"); } if (!delete_depts_query_->exec()) { db_.rollback(); report_db_error(delete_depts_query_->lastError(), "Failed to delete from depts"); } delete_deptnames_query_->finish(); delete_depts_query_->finish(); // store mapping of top level departments to root "" for (auto const& dept: depts) { store_department_mapping(dept->id(), ""); } store_departments_(depts, locale); if (!db_.commit()) { db_.rollback(); report_db_error(db_.lastError(), "Failed to commit transaction in store_departments"); } } } ./libclickscope/click/department-lookup.cpp0000644000015600001650000000470012676763577021201 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "department-lookup.h" namespace click { DepartmentLookup::DepartmentLookup() { } void DepartmentLookup::rebuild(const Department::SPtr& dept) { departments[dept->id()] = dept; for (auto const& subdep: dept->sub_departments()) { parent_lut[subdep->id()] = dept; rebuild(subdep); } } void DepartmentLookup::rebuild(const std::list& root_departments) { parent_lut.clear(); departments.clear(); for (auto const& dep: root_departments) { rebuild(dep); } } Department::SPtr DepartmentLookup::get_parent(const std::string& department_id) const { auto it = parent_lut.find(department_id); if (it != parent_lut.end()) { return it->second; } return Department::SPtr(nullptr); } Department::SPtr DepartmentLookup::get_department_info(const std::string& department_id) const { auto it = departments.find(department_id); if (it != departments.end()) { return it->second; } return nullptr; } int DepartmentLookup::size() const { return parent_lut.size(); } } ./libclickscope/click/download-manager.cpp0000644000015600001650000001762612676763604020750 0ustar jenkinsjenkins/* * Copyright (C) 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "download-manager.h" #include #include #include #include #include #include namespace u1 = UbuntuOne; #include #include namespace udm = Ubuntu::DownloadManager; #include #include #include #include namespace click { static const QString DOWNLOAD_APP_ID_KEY = "app_id"; static const QString DOWNLOAD_COMMAND_KEY = "post-download-command"; static const QString DOWNLOAD_COMMAND = CLICK_INSTALL_HELPER; static const QString DOWNLOAD_MANAGER_SHA512 = "sha512"; const QByteArray& CLICK_TOKEN_HEADER() { static const QByteArray result("X-Click-Token"); return result; } DownloadManager::DownloadManager(const QSharedPointer& client, const QSharedPointer& manager) : client(client), dm(manager) { } DownloadManager::~DownloadManager() { } void DownloadManager::get_progress(const std::string& package_name, const std::function& callback) { dm->getAllDownloadsWithMetadata(DOWNLOAD_APP_ID_KEY, QString::fromStdString(package_name), [callback, package_name](const QString& /*key*/, const QString& /*value*/, DownloadsList* downloads_list){ // got downloads matching metadata std::string object_path; auto downloads = downloads_list->downloads(); if (downloads.size() > 0) { auto download = downloads.at(0); object_path = download->id().toStdString(); } qDebug() << "Found object path" << QString::fromStdString(object_path) << "for package" << QString::fromStdString(package_name); if (downloads.size() > 1) { qWarning() << "More than one download with the same object path"; } callback(object_path); }, [callback, package_name](const QString& /*key*/, const QString& /*value*/, DownloadsList* /*downloads_list*/){ // no downloads found qDebug() << "No object path found for package" << QString::fromStdString(package_name); callback(""); }); } click::web::Cancellable DownloadManager::start(const std::string& url, const std::string& download_sha512, const std::string& package_name, const std::function& callback) { QSharedPointer response = client->call (url, "HEAD", true); QObject::connect(response.data(), &click::web::Response::finished, [this, callback, url, download_sha512, package_name, response](QString) { auto status = response->get_status_code(); if (status == 200) { auto clickToken = response->get_header(CLICK_TOKEN_HEADER().data()); qDebug() << "Received click token:" << clickToken.c_str(); QVariantMap metadata; QVariant commandline = QVariant(QStringList() << DOWNLOAD_COMMAND << "$file" << package_name.c_str()); metadata[DOWNLOAD_COMMAND_KEY] = commandline; metadata[DOWNLOAD_APP_ID_KEY] = package_name.c_str(); metadata["package_name"] = package_name.c_str(); QMap headers; headers[CLICK_TOKEN_HEADER()] = clickToken.c_str(); udm::DownloadStruct downloadStruct(url.c_str(), download_sha512.c_str(), DOWNLOAD_MANAGER_SHA512, metadata, headers); dm->createDownload(downloadStruct, [callback](Download* download) { if (download->isError()) { auto error = download->error()->errorString().toUtf8().data(); qDebug() << "Received error from ubuntu-download-manager:" << error; callback(error, Error::DownloadInstallError); } else { download->start(); callback(download->id().toUtf8().data(), Error::NoError); } }, [callback](Download* download) { callback(download->error()->errorString().toUtf8().data(), Error::DownloadInstallError); }); } else { std::string error{"Unhandled HTTP response code: "}; error += status; callback(error, Error::DownloadInstallError); } }); QObject::connect(response.data(), &click::web::Response::error, [this, callback, package_name](QString error, int error_code) { qDebug() << QStringLiteral("Network error (%1) fetching click token for:").arg(error_code) << package_name.c_str(); switch(error_code) { case 401: case 403: sso->invalidateCredentials(); callback(error.toUtf8().data(), Error::CredentialsError); break; default: callback(error.toUtf8().data(), Error::DownloadInstallError); } }); return click::web::Cancellable(response); } void DownloadManager::setCredentialsService(const QSharedPointer& credentialsService) { sso = credentialsService; client->setCredentialsService(sso); } } // namespace click ./libclickscope/click/application.h0000644000015600001650000000506712676763577017506 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_APPLICATION_H #define CLICK_APPLICATION_H #include "index.h" namespace click { struct Application : public Package { Application(std::string name, std::string title, double price, std::string icon_url, std::string url, std::string description, std::string main_screenshot, std::string default_department, std::string real_department = "" ) : Package {name, title, price, icon_url, url}, description(description), main_screenshot(main_screenshot), default_department(default_department), real_department(real_department) { } Application() = default; ~Application() {} std::string description; std::vector keywords; std::string main_screenshot; std::string default_department; std::string real_department; time_t installed_time; }; std::ostream& operator<<(std::ostream& out, const Application& app); bool operator==(const Application& lhs, const Application& rhs); } // namespace click #endif // CLICK_APPLICATION_H ./libclickscope/click/smartconnect.cpp0000644000015600001650000000333312676763577020230 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "smartconnect.h" namespace click { namespace utils { SmartConnect::SmartConnect(QObject *parent) : QObject(parent) { } void SmartConnect::cleanup() { deleteLater(); } void SmartConnect::disconnectAll() { foreach (auto c, connections) { QObject::disconnect(c); } cleanup(); } } // namespace utils } // namespace click ./libclickscope/click/highlights.h0000644000015600001650000000473212676763577017333 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_HIGHLIGHTS_H #define CLICK_HIGHLIGHTS_H #include #include #include #include namespace click { class Highlight { public: struct JsonKeys { JsonKeys() = delete; constexpr static const char* name {"name"}; constexpr static const char* slug {"slug"}; constexpr static const char* embedded {"_embedded"}; constexpr static const char* highlight {"clickindex:highlight"}; }; Highlight(const std::string& name); Highlight(const std::string& slug, const std::string& name, const Packages& pkgs, bool contains_scopes=false); void add_package(const Package& pkg); std::string name() const; std::string slug() const; Packages packages() const; bool contains_scopes() const; static std::list from_json_root_node(const Json::Value& val); private: static std::list from_json_node(const Json::Value& val); std::string slug_; std::string name_; Packages packages_; bool contains_scopes_; }; typedef std::list HighlightList; } #endif ./libclickscope/click/package.cpp0000644000015600001650000002752712676763577017136 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include namespace click { QDebug operator<< (QDebug d, const std::string &s) { d << QString::fromStdString(s); return d; } bool operator==(const Package& lhs, const Package& rhs) { // We can't include the version in the comparison here, because this // comparison is used by the sorted_set, that we use to compare a package // installed locally on the device with a (possibly updated) package available in the store. return lhs.name == rhs.name; } bool operator==(const PackageDetails& lhs, const PackageDetails& rhs) { return lhs.package == rhs.package && lhs.description == rhs.description && lhs.download_url == rhs.download_url && lhs.download_sha512 == rhs.download_sha512 && lhs.rating == rhs.rating && // TODO: keywords should be a list of strings lhs.keywords == rhs.keywords && lhs.terms_of_service == rhs.terms_of_service && lhs.license == rhs.license && lhs.publisher == rhs.publisher && lhs.developer_name == rhs.developer_name && lhs.company_name == rhs.company_name && lhs.website == rhs.website && lhs.support_url == rhs.support_url && lhs.main_screenshot_url == rhs.main_screenshot_url && lhs.more_screenshots_urls == rhs.more_screenshots_urls && lhs.binary_filesize == rhs.binary_filesize && lhs.version == rhs.version && lhs.date_published == rhs.date_published && lhs.last_updated == rhs.last_updated && lhs.changelog == rhs.changelog && lhs.framework == rhs.framework; } Package package_from_json_node(const Json::Value& item) { Package p; p.name = item[Package::JsonKeys::name].asString(); p.title = item[Package::JsonKeys::title].asString(); p.price = item[Package::JsonKeys::price].asDouble(); if (item.isMember(Package::JsonKeys::prices)) { auto prices = item[Package::JsonKeys::prices]; auto currencies = prices.getMemberNames(); foreach (auto currency, currencies) { p.prices[currency] = prices[currency].asDouble(); } } p.icon_url = item[Package::JsonKeys::icon_url].asString(); p.url = item[Package::JsonKeys::links][Package::JsonKeys::self][Package::JsonKeys::href].asString(); p.content = item[Package::JsonKeys::content].asString(); p.publisher = item[Package::JsonKeys::publisher].asString(); p.rating = item[Package::JsonKeys::rating].asDouble(); p.version = item[Package::JsonKeys::version].asString(); return p; } Packages package_list_from_json_node(const Json::Value& root) { Packages pl; for (uint i = 0; i < root.size(); i++) { Package p; const json::Value item = root[i]; p = package_from_json_node(item); pl.push_back(p); } return pl; } Packages package_list_from_json(const std::string& json) { std::istringstream is(json); json::Reader reader; json::Value root; if (!reader.parse(json, root)) { throw std::runtime_error(reader.getFormattedErrorMessages()); } return package_list_from_json_node(root); } PackageDetails PackageDetails::from_json(const std::string &json) { PackageDetails details; try { json::Reader reader; json::Value root; if (!reader.parse(json, root)) throw std::runtime_error(reader.getFormattedErrorMessages()); // Mandatory details go here. That is, asString() will throw as we // do not provide a default value if a value with the given key does not exist. details.package.name = root[Package::JsonKeys::name].asString(); details.package.title = root[Package::JsonKeys::title].asString(); details.package.icon_url = root[Package::JsonKeys::icon_url].asString(); details.package.price = root[Package::JsonKeys::price].asDouble(); if (root.isMember(Package::JsonKeys::prices)) { auto prices = root[Package::JsonKeys::prices]; auto currencies = prices.getMemberNames(); foreach (auto currency, currencies) { details.package.prices[currency] = prices[currency].asDouble(); } } details.description = root[JsonKeys::description].asString(); details.download_url = root[JsonKeys::download_url].asString(); details.license = root[JsonKeys::license].asString(); if (root[JsonKeys::department].isArray()) { auto const dept_array = root[JsonKeys::department]; if (dept_array.size() > 0) { details.department = dept_array[dept_array.size() - 1].asString(); } } // Optional details go here. if (root[JsonKeys::download_sha512].isString()) details.download_sha512 = root[JsonKeys::download_sha512].asString(); if (root[JsonKeys::version].isString()) details.version = root[JsonKeys::version].asString(); if (root[JsonKeys::rating].isNumeric()) details.rating = root[JsonKeys::rating].asDouble(); if (root[JsonKeys::keywords].isString()) details.keywords = root[JsonKeys::keywords].asString(); if (root[JsonKeys::terms_of_service].isString()) details.terms_of_service = root[JsonKeys::terms_of_service].asString(); if (root[JsonKeys::publisher].isString()) details.publisher = root[JsonKeys::publisher].asString(); if (root[JsonKeys::main_screenshot_url].isString()) details.main_screenshot_url = root[JsonKeys::main_screenshot_url].asString(); auto screenshots = root[JsonKeys::more_screenshot_urls]; for (uint i = 0; i < screenshots.size(); i++) { auto scr = screenshots[i].asString(); // more_screenshot_urls may contain main_screenshot_url, if so, skip it if (scr != details.main_screenshot_url) { details.more_screenshots_urls.push_back(scr); } } if (root[JsonKeys::binary_filesize].isIntegral()) details.binary_filesize = root[JsonKeys::binary_filesize].asUInt64(); if (root[JsonKeys::framework].isString()) details.framework = root[JsonKeys::framework].asString(); if (root[JsonKeys::developer_name].isString()) details.developer_name = root[JsonKeys::developer_name].asString(); if (root[JsonKeys::company_name].isString()) details.company_name = root[JsonKeys::company_name].asString(); if (root[JsonKeys::website].isString()) details.website = root[JsonKeys::website].asString(); if (root[JsonKeys::support_url].isString()) details.support_url = root[JsonKeys::support_url].asString(); if (root[JsonKeys::date_published].isString()) details.date_published.parse_iso8601(root[JsonKeys::date_published].asString()); if (root[JsonKeys::last_updated].isString()) details.last_updated.parse_iso8601(root[JsonKeys::last_updated].asString()); if (root[JsonKeys::changelog].isString()) details.changelog = root[JsonKeys::changelog].asString(); } catch(const std::exception& e) { std::cerr << "PackageDetails::loadJson: Exception thrown while decoding JSON: " << e.what() << std::endl; } catch(...) { std::cerr << "PackageDetails::loadJson: Exception thrown while decoding JSON." << std::endl; } return details; } std::string print_string_if_not_empty(const std::string& s) { return s.empty() ? "n/a" : s; } std::string print_list_if_not_empty(const std::list& li) { std::stringstream s; s << "["; if (!li.empty()) { auto it = li.begin(); s << print_string_if_not_empty(*it); ++it; while (it != li.end()) { s << ", " << print_string_if_not_empty(*it); ++it; } } s << "]"; return s.str(); } std::ostream& operator<<(std::ostream& out, const click::PackageDetails& details) { out << "(" << print_string_if_not_empty(details.package.name) << ", " << print_string_if_not_empty(details.package.title) << ", " << print_string_if_not_empty(details.package.icon_url) << ", " << print_string_if_not_empty(details.description) << ", " << print_string_if_not_empty(details.download_url) << ", " << print_string_if_not_empty(details.download_sha512) << ", " << details.rating << ", " << print_string_if_not_empty(details.keywords) << ", " << print_string_if_not_empty(details.terms_of_service) << ", " << print_string_if_not_empty(details.license) << ", " << print_string_if_not_empty(details.publisher) << ", " << print_string_if_not_empty(details.developer_name) << ", " << print_string_if_not_empty(details.company_name) << ", " << print_string_if_not_empty(details.website) << ", " << print_string_if_not_empty(details.support_url) << ", " << print_string_if_not_empty(details.main_screenshot_url) << ", " << print_list_if_not_empty(details.more_screenshots_urls) << ", " << details.binary_filesize << ", " << print_string_if_not_empty(details.version) << ", " << print_string_if_not_empty(details.date_published.formatted()) << ", " << print_string_if_not_empty(details.last_updated.formatted()) << ", " << print_string_if_not_empty(details.changelog) << ", " << print_string_if_not_empty(details.framework) << ")"; return out; } bool operator==(const Application& lhs, const Application& rhs) { return lhs.name == rhs.name && lhs.title == rhs.title && lhs.description == rhs.description && lhs.main_screenshot == rhs.main_screenshot && lhs.icon_url == rhs.icon_url; } std::ostream& operator<<(std::ostream& out, const click::Application& app) { out << "(" << print_string_if_not_empty(app.name) << ", " << print_string_if_not_empty(app.title) << ", " << app.price << ", " << print_string_if_not_empty(app.icon_url) << ", " << print_string_if_not_empty(app.url) << ", " << print_string_if_not_empty(app.version) << ", " << print_string_if_not_empty(app.description) << ", " << print_string_if_not_empty(app.main_screenshot) << ")"; return out; } } // namespace click ./libclickscope/click/ubuntuone_credentials.h0000644000015600001650000000416612676763604021572 0ustar jenkinsjenkins/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef _UBUNTUONE_CREDENTIALS_H_ #define _UBUNTUONE_CREDENTIALS_H_ #include #include namespace click { class CredentialsService : public UbuntuOne::SSOService { Q_OBJECT public: CredentialsService(); CredentialsService(const CredentialsService&) = delete; virtual ~CredentialsService(); CredentialsService& operator=(const CredentialsService&) = delete; virtual void getCredentials(); virtual void invalidateCredentials(); signals: void credentialsDeleted(); void credentialsFound(const UbuntuOne::Token& token); void credentialsNotFound(); private: QScopedPointer ssoService; }; // CredentialsService } // namespace click #endif /* _UBUNTUONE_CREDENTIALS_H_ */ ./libclickscope/click/download-manager.h0000644000015600001650000000641212676763577020415 0ustar jenkinsjenkins/* * Copyright (C) 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_DOWNLOAD_MANAGER_H #define CLICK_DOWNLOAD_MANAGER_H #include #include #include #include #include #include #include using Ubuntu::DownloadManager::Download; namespace UbuntuOne { class Token; } namespace click { // The dbus-send command to refresh the search results in the dash. static const QString REFRESH_SCOPE_COMMAND = QStringLiteral("dbus-send /com/canonical/unity/scopes com.canonical.unity.scopes.InvalidateResults string:%1"); static const QString APPS_SCOPE_ID = QStringLiteral("clickscope"); static const QString STORE_SCOPE_ID = QStringLiteral("com.canonical.scopes.clickstore"); const QByteArray& CLICK_TOKEN_HEADER(); class DownloadManager { public: enum class Error {NoError, CredentialsError, DownloadInstallError}; DownloadManager(const QSharedPointer& client, const QSharedPointer& manager); virtual ~DownloadManager(); virtual void get_progress(const std::string& package_name, const std::function& callback); virtual click::web::Cancellable start(const std::string& url, const std::string& download_sha512, const std::string& package_name, const std::function& callback); virtual void setCredentialsService(const QSharedPointer& credentialsService); protected: QSharedPointer client; QSharedPointer dm; QSharedPointer sso; }; } #endif /* CLICK_DOWNLOAD_MANAGER_H */ ./CMakeLists.txt0000644000015600001650000000704512676763577013675 0ustar jenkinsjenkinsproject(unity-scope-click) cmake_minimum_required(VERSION 2.8.10) set(SCOPE_CLICK_VERSION_MAJOR 0) set(SCOPE_CLICK_VERSION_MINOR 0) set(SCOPE_CLICK_VERSION_PATCH 1) # Some default CFLAGS SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -g -Wall -Wextra -Werror -fPIC") SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -g -Wextra -Wall -Werror -fPIC") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(GNUInstallDirs) set(STORE_LIB_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/unity-scopes/clickstore/) set(STORE_DATA_DIR ${CMAKE_INSTALL_FULL_DATADIR}/unity/scopes/clickstore/) set(APPS_LIB_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/unity-scopes/clickapps/) set(APPS_DATA_DIR ${CMAKE_INSTALL_FULL_DATADIR}/unity/scopes/clickapps/) find_program(INTLTOOL_MERGE intltool-merge) include(UseGSettings) find_package (PkgConfig REQUIRED) pkg_check_modules(UNITY_SCOPES REQUIRED libunity-scopes>=0.6.7 libunity-api>=0.1.3) add_definitions(${UNITY_SCOPES_CFLAGS} ${UNITY_SCOPES_CFLAGS_OTHER}) pkg_check_modules(UBUNTUONE REQUIRED ubuntuoneauth-2.0>=15.10) add_definitions(${UBUNTUONE_CFLAGS} ${UBUNTUONE_CFLAGS_OTHER}) pkg_check_modules(UBUNTU_DOWNLOAD_MANAGER_CLIENT REQUIRED ubuntu-download-manager-client) pkg_check_modules(UBUNTU_DOWNLOAD_MANAGER_COMMON REQUIRED ubuntu-download-manager-common) SET (SCOPE_LIB_VERSION 0.2.0) SET (SCOPE_LIB_SOVERSION 0) SET (SCOPE_LIB_API_VERSION 2.0) SET (STORE_LIB_UNVERSIONED com.canonical.scopes.clickstore) SET (SCOPE_LIB_NAME clickscope) SET (STORE_LIB_NAME ${STORE_LIB_UNVERSIONED}-${SCOPE_LIB_API_VERSION}) SET (APPS_LIB_UNVERSIONED scope) SET (APPS_LIB_NAME ${APPS_LIB_UNVERSIONED}-${SCOPE_LIB_API_VERSION}) # Build with system gmock and embedded gtest set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory") set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory") set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory") add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") # Add our own subdirectories. add_subdirectory(tests) add_subdirectory(bin) add_subdirectory(libclickscope) add_subdirectory(scope) add_subdirectory(data) add_subdirectory(po) add_subdirectory(tools) include(EnableCoverageReport) ENABLE_COVERAGE_REPORT(TARGETS ${SCOPE_LIB_NAME} ${STORE_LIB_UNVERSIONED} ${APPS_LIB_UNVERSIONED} TESTS click_scope_integration_tests libclick-scope-tests fake_launcher click-scope-tests apps-scope-tests click_interface_tool init-departments FILTER /usr/include ${CMAKE_BINARY_DIR}/* ) # Custom targets for the tests add_custom_target (test DEPENDS test-click-scope test-apps-scope test-libclickscope ) add_custom_target (test-disabled DEPENDS test-click-scope-disabled ) # Add a custom target for integration tests, as they should not be run # during normal make test. add_custom_target (test-integration DEPENDS test-integration-click-scope ) # Add a custom target for running the tests under valgrind. add_custom_target (test-valgrind DEPENDS test-click-scope-valgrind test-apps-scope-valgrind test-libclickscope-valgrind ) # Add a custom target for running the tests under valgrind with the # full leak checks enabled. add_custom_target (test-leaks DEPENDS test-click-scope-leaks test-apps-scope-leaks test-libclickscope-leaks ) # Also let "make check" and partners work. add_custom_target (check DEPENDS test ) add_custom_target (check-integration DEPENDS test-integration ) add_custom_target (check-valgrind DEPENDS test-valgrind ) add_custom_target (check-leaks DEPENDS test-leaks )