diff --git a/discover/autotests/DiscoverTest.qml b/discover/autotests/DiscoverTest.qml index 5e79931a..64589c3e 100644 --- a/discover/autotests/DiscoverTest.qml +++ b/discover/autotests/DiscoverTest.qml @@ -1,113 +1,114 @@ import QtQuick 2.1 import QtTest 1.1 import org.kde.discover.app 1.0 Item { id: testRoot signal reset() property QtObject appRoot function verify(condition, msg) { if (!condition) { console.trace(); var e = new Error(condition + (msg ? (": " + msg) : "")) e.object = testRoot; throw e; } } function compare(valA, valB, msg) { if (valA !== valB) { console.trace(); var e = new Error(valA + " !== " + valB + (msg ? (": " + msg) : "")) e.object = testRoot; throw e; } } function typeName(obj) { var name = obj.toString(); var idx = name.indexOf("_QMLTYPE_"); return name.substring(0, idx); } function isType(obj, typename) { return obj && obj.toString().indexOf(typename+"_QMLTYPE_") === 0 } function chooseChild(obj, validator) { if (validator(obj)) return true; var children = obj.data ? obj.data : obj.contentData for(var v in children) { var stop = chooseChild(children[v], validator) if (stop) return true } return false } function findChild(obj, typename) { var ret = null; chooseChild(obj, function(o) { var found = isType(o, typename); if (found) { ret = o; } return found }) return ret; } SignalSpy { id: spy } function waitForSignal(object, name, timeout) { if (!timeout) timeout = 5000; spy.clear(); spy.signalName = "" spy.target = object; spy.signalName = name; verify(spy); verify(spy.valid); verify(spy.count == 0); try { spy.wait(timeout); } catch (e) { console.warn("wait for signal '"+name+"' failed") return false; } return spy.count>0; } function waitForRendering() { return waitForSignal(appRoot, "frameSwapped") } property string currentTest: "" onCurrentTestChanged: console.log("changed to test", currentTest) Connections { target: ResourcesModel property bool done: false onIsFetchingChanged: { if (ResourcesModel.isFetching || done) return; done = true; for(var v in testRoot) { if (v.indexOf("test_") === 0) { testRoot.currentTest = v; testRoot.reset(); testRoot[v](); } } - Qt.quit(); + console.log("done") + appRoot.close() } } } diff --git a/discover/autotests/toplevels.qml b/discover/autotests/toplevels.qml index e176d70b..e18c7095 100644 --- a/discover/autotests/toplevels.qml +++ b/discover/autotests/toplevels.qml @@ -1,127 +1,127 @@ import QtQuick 2.0 import org.kde.discover.app 1.0 import QtTest 1.1 DiscoverTest { onReset: { appRoot.currentTopLevel = appRoot.topBrowsingComp } function test_openCategory() { var categoryName = "dummy 3"; app.openCategory(categoryName); verify(appRoot.stack.currentItem, "has a page"); compare(appRoot.stack.currentItem.title, categoryName, "same title"); verify(waitForRendering()) categoryName = "dummy 4"; app.openCategory(categoryName); verify(appRoot.stack.currentItem, "has a page"); compare(appRoot.stack.currentItem.title, categoryName, "same title"); verify(waitForRendering()) } function test_openHome() { var drawer = appRoot.globalDrawer; var firstitem; chooseChild(drawer, function(object) { if (object.hasOwnProperty("label") && object.label.indexOf("ummy")>0) { firstitem = object; return true } return false; }); var categoryName = "dummy 3"; firstitem.clicked() drawer.bannerClicked() compare(appRoot.stack.currentItem.title, "Featured", "same title"); compare(drawer.currentSubMenu, null) } function test_navigateThenUpdate() { var drawer = appRoot.globalDrawer; var firstitem; chooseChild(drawer, function(object) { if (object.hasOwnProperty("label") && object.label.indexOf("ummy")>0) { firstitem = object; return true } return false; }); var updateButton; chooseChild(drawer, function(object) { if (object.objectName === "updateButton") { updateButton = object; return true } return false; }); firstitem.clicked() verify(updateButton.enabled) updateButton.clicked() compare(appRoot.currentTopLevel, appRoot.topUpdateComp, "correct component, updates"); } function test_update() { app.openMode("Update"); var updatePage = appRoot.stack.currentItem; compare(typeName(updatePage), "UpdatesPage") compare(updatePage.state, "has-updates", "to update") - var action = updatePage.currentAction + var action = updatePage.actions.main verify(action); action.triggered(updatePage); - compare(updatePage.state, "has-updates", "updating") + compare(updatePage.state, "progressing", "updating") //make sure the window doesn't close while updating verify(appRoot.visible); verify(waitForRendering()) appRoot.close() verify(appRoot.visible); while(updatePage.state !== "now-uptodate") waitForSignal(updatePage, "stateChanged") compare(ResourcesModel.updatesCount, 0, "should be up to date") } function test_search() { app.openMode("Browsing"); var searchField = findChild(appRoot.globalDrawer.topContent[0], "SearchField"); verify(searchField); searchField.text = "cocacola" searchField.accepted() while(!isType(appRoot.stack.currentItem, "ApplicationsListPage")) verify(waitForSignal(appRoot.stack, "currentItemChanged")) var listPage = appRoot.stack.currentItem while(listPage.count>0) verify(waitForSignal(listPage, "countChanged")) compare(listPage.count, 0) compare(listPage.search, "cocacola") searchField.text = "dummy" searchField.accepted() compare(listPage.search, searchField.text) // compare(listPage.count, ResourcesModel.rowCount()/2) } function test_modes() { app.openMode("Browsing"); compare(appRoot.currentTopLevel, appRoot.topBrowsingComp, "correct component, browsing"); verify(waitForRendering()) app.openMode("Installed"); compare(appRoot.currentTopLevel, appRoot.topInstalledComp, "correct component, installed"); verify(waitForRendering()) app.openMode("Update"); compare(appRoot.currentTopLevel, appRoot.topUpdateComp, "correct component, updates"); verify(waitForRendering()) app.openMode("Sources"); compare(appRoot.currentTopLevel, appRoot.topSourcesComp, "correct component, sources"); verify(waitForRendering()) } } diff --git a/discover/autotests/updateandinstall.qml b/discover/autotests/updateandinstall.qml index bc8decc0..fc007810 100644 --- a/discover/autotests/updateandinstall.qml +++ b/discover/autotests/updateandinstall.qml @@ -1,45 +1,41 @@ import QtQuick 2.0 import org.kde.discover.app 1.0 import QtTest 1.1 DiscoverTest { function test_openResource() { app.openMode("Update"); {// we start an upate var updatePage = appRoot.stack.currentItem; compare(typeName(updatePage), "UpdatesPage") compare(updatePage.state, "has-updates", "to update") - var action = updatePage.currentAction + var action = updatePage.actions.main verify(action); action.triggered(null); - compare(updatePage.state, "has-updates", "updating") + compare(updatePage.state, "progressing", "updating") } {//we start installing a resource app.openApplication("dummy://dummy.1"); verify(waitForSignal(appRoot.stack, "currentItemChanged")) var button = findChild(appRoot.stack.currentItem, "InstallApplicationButton") console.log("button", appRoot.stack.currentItem, button) verify(button) verify(!button.isActive) button.click() } app.openMode("Update"); { var updatePage = appRoot.stack.currentItem; compare(typeName(updatePage), "UpdatesPage") - while(updatePage.state === "fetching") + while(updatePage.state === "fetching" || updatePage.state === "progressing") { waitForSignal(updatePage, "stateChanged") + } compare(updatePage.state, "now-uptodate", "to update") - var action = updatePage.currentAction - verify(!action.visible) } - - while(updatePage.state !== "now-uptodate") - waitForSignal(updatePage, "stateChanged") } } diff --git a/discover/org.kde.discover.appdata.xml b/discover/org.kde.discover.appdata.xml index 8663201f..8a66804b 100644 --- a/discover/org.kde.discover.appdata.xml +++ b/discover/org.kde.discover.appdata.xml @@ -1,213 +1,213 @@ org.kde.discover.desktop CC0-1.0 GPL-2.0+ Discover استكشف Discover Discover Discover Discover Discover Discover Discover Discover Avastusretk Discover Discover Discover Discover Discoperi Discover Discover 살펴보기 Discover Ontdekken Discover ਖੋਜੋ Odkrywca Discover Discover Центр приложений Discover Discover Discover Oткривач Otkrivač Oткривач Otkrivač Discover Кашфиёт Keşfet Discover xxDiscoverxx 发现 探尋 Discover استكشف Discover Discover Discover Discover Discover Discover Discover Discover Avastusretk Discover Discover Discover Discover Discoperi Discover Discover 살펴보기 Discover Ontdekken Discover ਖੋਜੋ Odkrywca Discover Discover Центр приложений Discover Discover Discover Oткривач Otkrivač Oткривач Otkrivač Discover Кашфиёт Keşfet Discover xxDiscoverxx 发现 探尋

Discover helps you find and install applications, games, and tools. You can search or browse by category, and look at screenshots and read reviews to help you pick the perfect app.

Discover ayúdate a alcontrar ya instalar aplicaciones y ferramientes. Pues guetar o restolar por estayes, mirar captures de pantalla y lleer reseñes p'ayudate a escoyer l'aplicación perfeuta.

El Discover us ajuda a instal·lar aplicacions, jocs i eines. Podeu cercar o explorar per categoria, i veure les captures de pantalla i llegir els comentaris per ajudar-vos a triar l'aplicació perfecta.

Discover us ajuda a instal·lar aplicacions, jocs i eines. Podeu buscar o explorar per categoria, i veure les captures de pantalla i llegir els comentaris per a ajudar-vos a triar l'aplicació perfecta.

Discover helps you find and install applications, games, and tools. You can search or browse by category, and look at screenshots and read reviews to help you pick the perfect app.

Discover le ayuda a encontrar e instalar aplicaciones, juegos y herramientas. Puede buscar o explorar por categorías, ver capturas de pantalla y leer comentarios que le ayudarán a escoger la aplicación perfecta.

Discover ehk Avastusretk aitab leida ja paigaldada rakendusi, mänge ja tööriistu. Otsida või sirvida saab kategooriaid pidi, ekraanipiltide uurimine ja ülevaadete lugemine lubab leida just selle õige rakenduse.

Discover-rek aplikazioak, jokoak eta tresnak bilatzen eta instalatzen laguntzen dizu. Kategoria bidez bilatu eta arakatu dezakezu, eta pantaila-argazkiak ikusi eta iritziak irakur ditzakezu aplikazio egokia aukeratzen laguntzako.

Discover auttaa sinua etsimään ja asentamaan sovelluksia, pelejä ja apuohjelmia. Voit etsiä tai selata luokittain, katsella ruutukaappauskuvia ja lukea arvosteluja.

Discover vous aide à trouver et à installer des applications, des jeux et des outils. Vous pouvez chercher ou naviguer par catégorie, ou consulter les captures d'écran et les avis d'utilisateurs pour vous aider à trouver l'appli parfaite.

Discover axúdalle a atopar e instalar aplicacións, xogos e ferramentas. Pode buscar ou explorar por categoría, e ollar capturas de pantalla e ler recensións como axuda para escoller a aplicación perfecta.

Discover membantu kamu untuk menemukan dan menginstal aplikasi, permainan, dan peralatan. Kamu bisa mencari atau menelusuri berdasarkan kategori, dan melihat cuplikan dan membaca ulasan untuk membantu kamu memilih aplikasi yang sempurna.

Discover ti aiuta a trovare e installare applicazioni, giochi e strumenti. Puoi cercare o sfogliare per categoria, vedere schermate e leggere recensioni per aiutarti a scegliere l'applicazione perfetta.

살펴보기를 사용하여 프로그램, 게임, 도구를 찾고 설치할 수 있습니다. 분류별로 검색 및 탐색하고, 스크린샷과 리뷰를 참조하여 꼭 필요한 앱을 찾으십시오.

Discover padeda jums rasti ir įdiegti programas, žaidimus bei įrankius. Galite ieškoti ar naršyti pagal kategoriją ir žiūrėti ekrano kopijas bei skaityti apžvalgas, kurios padeda jums išsirinkti tobulą programą.

Discover helpt om toepassingen, spellen en hulpmiddelen te zoeken en te installeren. U kunt zoeken of bladeren op categorie, kijken naar schermafdrukken en reviews lezen om u te helpen de perfecte toepassing te kiezen.

Discover hjelper deg å finna og installera program, spel og verktøy. Du kan søkja etter program eller bla gjennom program­kategoriar, sjå skjermbilete og lesa brukaromtalar for å finna det perfekte programmet.

Odkrywca pomaga znaleźć i wgrać aplikacje, gry oraz narzędzia. Można wyszukiwać lub przeglądać po kategorii. Można oglądać zrzuty ekranu i czytać oceny, aby wybrać najlepszą aplikację.

O Discover ajuda-o a instalar aplicações, jogos e ferramentas. Poderá pesquisar ou navegar por categoria, assim como ver as imagens e ler as revisões e comentários que o possam ajudar a escolher a aplicação perfeita.

O Discover ajuda-o a encontrar e instalar aplicativos, jogos e ferramentas. Você pode pesquisar ou navegar por categoria, assim como ver as capturas de tela e ler as avaliações e comentários que o possam ajudá-lo a escolher o aplicativo perfeito.

Центр программ Discover предназначен для поиска, в числе по категориям и установки приложений и игр. Описания приложений содержат снимки экрана и отзывы других пользователей.

Discover vám pomôže nájsť a nainštalovať aplikácie, hry a nástroje. Môžete vyhľadávať alebo prehliadať podľa kategórií, prezerať si snímky obrazovky a čítať recenzie, ktoré vám pomôžu vybrať si perfektnú aplikáciu.

Discover hjälper till att hitta och installera program, spel och verktyg. Man kan söka eller bläddra enligt kategori, och titta på skärmbilder och läsa recensioner för att hjälpa till att välja det perfekta programmet.

Discover допоможе вам знайти і встановити програми, ігри та інструменти. Ви можете виконувати пошук або навігацію за категоріями, переглядати знімки вікон і читати рецензії, щоб вибрати ту програму, яка пасує вам найкраще.

xxDiscover helps you find and install applications, games, and tools. You can search or browse by category, and look at screenshots and read reviews to help you pick the perfect app.xx

Discover 帮助您发现和安装应用,游戏和工具。您可以搜索关键字或浏览分类,通过屏幕截图和用户评论来选择最佳应用。

《探尋》協助您尋找及安裝應用程式、遊戲及工具。您可以搜尋,或者是瀏覽分類,看看螢幕截圖和閱讀評論,以協助你找到最好的 App。

With Discover, you can manage software from multiple sources, including your operating system's software repository, Flatpak repos, the Snap store, or even AppImages from store.kde.org.

Amb el Discover podeu gestionar programari de diverses fonts, incloent-hi el repositori de programari del sistema operatiu, repositoris del Flatpak, la botiga de l'Snap, o també AppImages des de «store.kde.org».

Amb Discover podeu gestionar programari de diverses fonts, incloent-hi el repositori de programari del sistema operatiu, repositoris de Flatpak, la botiga d'Snap, o també AppImages des de «store.kde.org».

With Discover, you can manage software from multiple sources, including your operating system's software repository, Flatpak repos, the Snap store, or even AppImages from store.kde.org.

Con Discover puede gestionar software de múltiples fuentes, incluyendo los repositorios de software de su sistema operativo, repositorios de Flatpak, la tienda de Snap, e incluso AppImages de store.kde.org.

Avastusretkega saab hallata tarkvara, mis on pärit mitmest allikast, kaasa arvatud su enda operatsioonisüsteemi tarkvarahoidla, Flatpaki hoidlad, Snapi hoidla või isegi hoidlas store.kde.org pakutavad AppImage'id.

Discover-rekin, sorburu askotako softwarea kudeatu dezakezu, tartean zure sistema eragilearen software gordetegia, Flatpak gordetegiak, Snap biltegia, edo baita store.kde.org-eko AppImages-etik ere.

Discoverilla voit hallita ohjelmia eri lähteistä: käyttöjärjestelmän tai Flatpakin asennuslähteistä, Snap-kaupasta tai jopa store.kde.orgin AppImageista.

Grâce à Discover, vous pouvez gérer des logiciels provenant de plusieurs sources, dont le dépôt de logiciels de votre système d'exploitation, les dépôts Flatpak, la boutique Snap ou même des AppImages de store.kde.org.

Con Discover pode xestionar software de varias fontes, entre elas o repositorio de software do seu sistema operativo, os repositorios de Flatpak, a tenda de Snap, ou mesmo as AppImage de store.kde.org.

Dengan Discover, kamu bisa mengelola perangkat lunak dari banyak sumber, termasuk repositori perangkat lunak operasi sistemmu, repo Flatpak, Snap store, atau bahkan AppCitras dari store.kde.org.

Con Discover, puoi gestire i programmi da diverse fonti, inclusi i depositi del software del tuo sistema operativo, depositi Flatpak, lo Snap Store o anche AppImages da store.kde.org.

살펴보기를 사용하면 여러 원본에서 제공되는 소프트웨어를 관리할 수 있습니다. 운영 체제 소프트웨어 저장소, Flatpak 저장소, Snap 스토어, store.kde.org의 AppImage를 한 곳에서 관리할 수 있습니다.

Naudodami Discover, galite tvarkyti programinę įrangą iš daugelio šaltinių, įskaitant jūsų operacinės sistemos programinės įrangos saugyklas, Flatpak saugyklas, Snap parduotuvę ar netgi AppImage paketus iš store.kde.org.

Met Discover kunt u software uit meerdere bronnen beheren, inclusief de opslagruimten van uw besturingssysteem, Flatpak-repo's, de Snap store of zelfs AppImages uit store.kde.org.

Men Discover kan du installera program frå ulike kjelder, blant anna standard pakkebrønnar for operativsystemet, Flatpak-depot, Snap-butikken og AppImages frå store.kde.org.

Dzięki Odkrywcy, można zarządzać oprogramowaniem z wielu źródeł, uwzględniając w tym źródło oprogramowania systemu operacyjnego, repozytoria Flatpak, sklep Snap lub nawet AppImage z store.kde.org.

Com o Discover, poderá gerir aplicações de várias fontes, incluindo os repositórios de aplicações do seu sistema operativo, os repositórios do Flatpak, a loja do Snap ou mesmo AppImages do store.kde.org.

Com o Discover você poderá gerenciar aplicativos de várias origens, incluindo os repositórios de aplicativos do seu sistema operacional, os repositórios do Flatpak, a loja do Snap ou mesmo AppImages do store.kde.org.

Discover позволяет управлять приложениями, получаемыми из различных источников: репозиториев установленной системы, репозиториев Flatpack, магазина приложений Snap, и даже приложений AppImages, расположенных по адресу store.kde.org.

S Discover môžete spravovať softvér z viacerých zdrojov vrátane úložiska softvéru vášho operačného systému, úložiska Flatpak, úložiska Snap alebo dokonca AppImages z obchodu store.kde.org.

Man kan hantera programvara från flera olika källor med Discover, inklusive operativsystemets programvaruarkiv, Flatpak-arkiv, Snap-butiken eller till och med programavbilder från store.kde.org.

За допомогою Discover ви можете керувати програмним забезпеченням із багатьох джерел, зокрема зі сховищ програмного забезпечення вашої операційної системи, зі сховищ Flatpak, із крамниці Snap або навіть із образів AppImage з store.kde.org.

xxWith Discover, you can manage software from multiple sources, including your operating system's software repository, Flatpak repos, the Snap store, or even AppImages from store.kde.org.xx

使用 Discover,您可以管理来自不同来源的软件,包括系统软件源,Flatpak 仓库,Snap 商店,或者来自 store.kde.org 的 AppImage。

使用《探尋》,您可以管理來自多個來源的軟體,包含您作業系統的軟體庫、Flatpak 軟體庫、Snap 商店,或甚至是來自 store.kde.org 的 AppImage。

Finally, Discover also allows you to find, install, and manage add-ons for Plasma and all your favorite KDE apps!

P'acabar, Discover tamién te permite alcontrar, instalar y xestionar complementos de Plasma y toles aplicaciones de KDE.

Finalment, el Discover també permet cercar, instal·lar i gestionar complements per al Plasma i totes les aplicacions preferides del KDE!

Finalment, Discover també permet buscar, instal·lar i gestionar complements per a Plasma i totes les aplicacions preferides de KDE.

Finally, Discover also allows you to find, install, and manage add-ons for Plasma and all your favourite KDE apps!

Por último, Discover también le permite encontrar, instalar y gestionar complementos para Plasma y para todas sus aplicaciones favoritas de KDE.

Ja lõpuks lubab Avastusretk leida, paigaldada ja hallata lisasid nii Plasmale kui ka kõigile KDE parimatele rakendustele!

Azkenik, Discover-rek uzten dizu Plasma eta zure KDEko aplikazio gogoko guztietarako gehigarriak bilatu, instalatu eta kudeatzen.

Discoverilla voit myös etsiä, asentaa ja hallita Plasman laajennusosia ja kaikkia suosikki-KDE-sovelluksiasi!

Enfin, Discover vous permet de trouver, d'installer et de gérer des modules complémentaires pour Plasma et toutes vos applis KDE préférées !

Por último, Discover tamén lle permite atopar, instalar e xestionar complementos de Plasma e das súas aplicacións favoritas de KDE!

Terakhir, Discover juga memungkinkan kamu untuk menemukan, menginstal, dan mengelola pernik untuk Plasma dan semua aplikasi KDE favoritmu.

Infine, Discover ti consente di trovare, installare e gestire componenti aggiuntivi di Plasma e tutte le tue applicazioni preferite di KDE!

또한 살펴보기를 사용하면 Plasma와 KDE 프로그램의 추가 기능을 찾고, 설치하고, 관리할 수 있습니다!

Pagaliau, Discover taip pat leidžia jums rasti, įdiegti ir tvarkyti Plasma papildinius ir visas jūsų mėgstamas KDE programas!

Tenslotte biedt Discover ook om add-ons voor Plasma en al uw favoriete KDE toepassingen te vinden, te installeren en te beheren!

Discover lèt deg òg finna, installera og handsama programtillegg for Plasma og andre KDE-program.

Odkrywca umożliwia także znalezienie, wgranie i zrządzanie dodatkami do Plazmy i innych ulubionych aplikacji KDE!

Finalmente, o Discover também lhe permite procurar, instalar e gerir as extensões do Plasma e de todas as suas aplicações favoritas do KDE!

Finalmente, o Discover também lhe permite procurar, instalar e gerenciar as extensões do Plasma e de todos os seus aplicativos favoritos do KDE!

Кроме того, при помощи Discover возможно искать, устанавливать и управлять дополнениями рабочей среды Plasma и приложений KDE.

Tak isto, Discover tiež umožňuje nájsť, nainštalovať a spravovať doplnky pre Plasma a všetky vaše obľúbené aplikácie KDE!

Slutligen låter Discovery dig söka efter, installera och hantera tilläggsprogram för Plasma och alla favoritprogram i KDE.

Нарешті, Discover надає вам змогу шукати, встановлювати додатки до Плазми та усі ваші улюблені програми KDE та керувати ними!

xxFinally, Discover also allows you to find, install, and manage add-ons for Plasma and all your favorite KDE apps!xx

最后,Discover 还可以帮您找到,安装和管理 Plasma 附加组件,以及您喜爱的 KDE 应用!

最後,《探尋》也讓你能尋找、安裝和管理 Plasma 的外掛程式及你所有喜歡的 KDE 應用程式!

https://bugs.kde.org/enter_bug.cgi?format=guided&product=Discover Plasma Discover Plasma Discover Discover del Plasma Discover de Plasma Plasma Discover Plasma Discover Plasma Discover Plasma Avastusretk Plasma Discover Plasma Discover Plasma Discover Plasma Discover Discover Plasma Plasma Discover Plasma 살펴보기 Plasma Discover Plasma Discover Plasma Discover Odkrywca Plazmy Plasma Discover Plasma Discover Центр приложений Discover Plasma Discover Plasma Discover Кашфиёти Плазма Discover у Плазмі xxPlasma Discoverxx Plasma Discover Plasma 探詢 https://cdn.kde.org/screenshots/plasma-discover/plasma-discover.png KDE plasma-discover + -
diff --git a/discover/qml/InstallApplicationButton.qml b/discover/qml/InstallApplicationButton.qml index fb2c2da9..117a79e3 100644 --- a/discover/qml/InstallApplicationButton.qml +++ b/discover/qml/InstallApplicationButton.qml @@ -1,70 +1,73 @@ import QtQuick 2.1 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.1 import org.kde.discover 2.0 import org.kde.kirigami 2.0 as Kirigami ConditionalLoader { id: root property alias application: listener.resource readonly property alias isActive: listener.isActive readonly property alias progress: listener.progress readonly property alias listener: listener readonly property string text: !application.isInstalled ? i18n("Install") : i18n("Remove") property Component additionalItem: null TransactionListener { id: listener } readonly property Kirigami.Action action: Kirigami.Action { text: root.text icon { name: application.isInstalled ? "trash-empty" : "cloud-download" color: !enabled ? Kirigami.Theme.backgroundColor : !listener.isActive ? (application.isInstalled ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.positiveTextColor) : Kirigami.Theme.backgroundColor } enabled: !listener.isActive && application.state !== AbstractResource.Broken onTriggered: root.click() } readonly property Kirigami.Action cancelAction: Kirigami.Action { text: i18n("Cancel") icon.name: "dialog-cancel" enabled: listener.isCancellable onTriggered: listener.cancel() visible: listener.isActive } function click() { if (!isActive) { if(application.isInstalled) ResourcesModel.removeApplication(application); else ResourcesModel.installApplication(application); } else { console.warn("trying to un/install but resource still active", application.name) } } condition: listener.isActive componentTrue: RowLayout { ToolButton { Layout.fillHeight: true action: root.cancelAction + text: "" + ToolTip.visible: hovered + ToolTip.text: root.cancelAction.text } LabelBackground { Layout.fillWidth: true text: listener.statusText progress: listener.progress/100 } } componentFalse: Button { enabled: application.state !== AbstractResource.Broken text: root.text focus: true onClicked: root.click() } } diff --git a/discover/qml/LabelBackground.qml b/discover/qml/LabelBackground.qml index 7005ea13..fd13887c 100644 --- a/discover/qml/LabelBackground.qml +++ b/discover/qml/LabelBackground.qml @@ -1,55 +1,58 @@ /* * Copyright (C) 2012 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, 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 Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.1 import org.kde.discover.app 1.0 import org.kde.kirigami 2.0 as Kirigami Control { id: root property alias text: theLabel.text property real progress: 1. padding: Kirigami.Units.smallSpacing * 1.5 background: Item { Rectangle { color: Kirigami.Theme.disabledTextColor anchors.fill: parent radius: root.padding } Rectangle { anchors { fill: parent rightMargin: (1-root.progress) * parent.width } color: Kirigami.Theme.highlightColor radius: root.padding } } contentItem: Label { id: theLabel horizontalAlignment: Text.AlignHCenter color: Kirigami.Theme.highlightedTextColor } + + ToolTip.visible: hovered + ToolTip.text: theLabel.text } diff --git a/libdiscover/ReviewsBackend/Rating.cpp b/libdiscover/ReviewsBackend/Rating.cpp index 87590e12..241c2ca2 100644 --- a/libdiscover/ReviewsBackend/Rating.cpp +++ b/libdiscover/ReviewsBackend/Rating.cpp @@ -1,150 +1,160 @@ /*************************************************************************** * Copyright © 2011 Jonathan Thomas * * * * 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 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 . * ***************************************************************************/ #include "Rating.h" #include #include "libdiscover_debug.h" #include inline double fastPow(double a, double b) { union { double d; int x[2]; } u = { a }; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN u.x[1] = (int)(b * (u.x[1] - 1072632447) + 1072632447); u.x[0] = 0; #else u.x[1] = 0; u.x[0] = (int)(b * (u.x[1] - 1072632447) + 1072632447); #endif return u.d; } // Converted from a Ruby example, returns an inverse normal distribution double pnormaldist(double qn) { double b[] = {1.570796288, 0.03706987906, -0.8364353589e-3, -0.2250947176e-3, 0.6841218299e-5, 0.5824238515e-5, -0.104527497e-5, 0.8360937017e-7, -0.3231081277e-8, 0.3657763036e-10, 0.6936233982e-12}; if(qn < 0.0 || 1.0 < qn) return 0.0; if(qn == 0.5) return 0.0; double w1 = qn; if(qn > 0.5) w1 = 1.0 - w1; double w3 = -qLn(4.0 * w1 * (1.0 - w1)); w1 = b[0]; for(int i = 1; i < 11; i++) w1 += b[i] * fastPow(w3,i); if(qn > 0.5) return qSqrt(w1*w3); return -qSqrt(w1*w3); } double wilson_score(int pos, int n, double power = 0.2) { if (n == 0) return 0; double z = pnormaldist(1 - power / 2); double phat = 1.0 * pos / n; return (phat + z * z / (2 * n) - z * qSqrt( (phat * (1 - phat) + z * z / (4 * n)) / n)) / (1 + z * z / n); } double dampenedRating(const QVector &ratings, double power = 0.1) { if (ratings.count() != 5) return 0; int tot_ratings = 0; Q_FOREACH (const int rating, ratings) tot_ratings = rating + tot_ratings; double sum_scores = 0.0; for (int i = 0; i < ratings.count(); i++) { const int rating = ratings.at(i); double ws = wilson_score(rating, tot_ratings, power); sum_scores = sum_scores + float((i + 1) - 3) * ws; } return sum_scores + 3; } Rating::Rating(const QString &packageName, quint64 ratingCount, const QVariantMap &data) : QObject() , m_packageName(packageName) , m_ratingCount(ratingCount) // TODO consider storing this and present in UI , m_rating(((data.value(QStringLiteral("star1")).toInt() + (data.value(QStringLiteral("star2")).toInt() * 2) + (data.value(QStringLiteral("star3")).toInt() * 3) + (data.value(QStringLiteral("star4")).toInt() * 4) + (data.value(QStringLiteral("star5")).toInt() * 5)) * 2) / qMax(1, ratingCount)) , m_ratingPoints(0) , m_sortableRating(0) { const QVector histo = { data.value(QStringLiteral("star1")).toInt(), data.value(QStringLiteral("star2")).toInt(), data.value(QStringLiteral("star3")).toInt(), data.value(QStringLiteral("star4")).toInt(), data.value(QStringLiteral("star5")).toInt() }; QVector spread; spread.reserve(histo.size()); for(int i=0; i * * * * 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 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 . * ***************************************************************************/ #ifndef RATING_H #define RATING_H #include #include #include "discovercommon_export.h" class DISCOVERCOMMON_EXPORT Rating : public QObject { Q_OBJECT Q_PROPERTY(double sortableRating READ sortableRating CONSTANT) Q_PROPERTY(float rating READ rating CONSTANT) Q_PROPERTY(int ratingPoints READ ratingPoints CONSTANT) Q_PROPERTY(quint64 ratingCount READ ratingCount CONSTANT) public: + explicit Rating(const QString &packageName, quint64 ratingCount, int rating); explicit Rating(const QString &packageName, quint64 ratingCount, const QVariantMap &data); ~Rating() override; QString packageName() const; quint64 ratingCount() const; // 0.0 - 10.0 ranged rating float rating() const; int ratingPoints() const; // Returns a dampened rating calculated with the Wilson Score Interval algorithm double sortableRating() const; private: const QString m_packageName; const quint64 m_ratingCount; const float m_rating; int m_ratingPoints; double m_sortableRating; }; #endif diff --git a/libdiscover/backends/CMakeLists.txt b/libdiscover/backends/CMakeLists.txt index 99e6dd89..5f87f639 100644 --- a/libdiscover/backends/CMakeLists.txt +++ b/libdiscover/backends/CMakeLists.txt @@ -1,48 +1,48 @@ function(add_unit_test name) add_executable(${name} ${ARGN}) - add_test(${name} ${name}) + add_test(${name} ${CMAKE_BINARY_DIR}/bin/${name}) ecm_mark_as_test(${name}) target_link_libraries(${name} Discover::Common Qt5::Test Qt5::Core ${EXTRA_LIBS}) endfunction() if(KF5Attica_FOUND AND KF5NewStuff_FOUND) add_subdirectory(KNSBackend) endif() if(packagekitqt5_FOUND AND AppStreamQt_FOUND) add_subdirectory(PackageKitBackend) endif() option(BUILD_DummyBackend "Build the DummyBackend" "OFF") if(BUILD_DummyBackend) add_subdirectory(DummyBackend) endif() option(BUILD_FlatpakBackend "Build Flatpak support" "ON") if(Flatpak_FOUND AND AppStreamQt_FOUND AND BUILD_FlatpakBackend) add_subdirectory(FlatpakBackend) elseif(BUILD_FlatpakBackend) message(WARNING "BUILD_FlatpakBackend enabled but Flatpak=${Flatpak_FOUND} or AppStreamQt=${AppStreamQt_FOUND} not found") endif() find_package(Snapd) set_package_properties(Snapd PROPERTIES DESCRIPTION "Library that exposes Snapd" URL "https://www.snapcraft.io" PURPOSE "Required to build the Snap backend" TYPE OPTIONAL) option(BUILD_SnapBackend "Build Snap support." "ON") if(BUILD_SnapBackend AND Snapd_FOUND) add_subdirectory(SnapBackend) endif() option(BUILD_FwupdBackend "Build Fwupd support." "ON") if(BUILD_FwupdBackend AND TARGET PkgConfig::Fwupd) add_subdirectory(FwupdBackend) endif() diff --git a/libdiscover/backends/DummyBackend/tests/DummyTest.cpp b/libdiscover/backends/DummyBackend/tests/DummyTest.cpp index 28d266f1..96024d30 100644 --- a/libdiscover/backends/DummyBackend/tests/DummyTest.cpp +++ b/libdiscover/backends/DummyBackend/tests/DummyTest.cpp @@ -1,298 +1,298 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * 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 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 . * ***************************************************************************/ #include "DummyTest.h" #include "DiscoverBackendsFactory.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include QTEST_MAIN(DummyTest) AbstractResourcesBackend* backendByName(ResourcesModel* m, const QString& name) { QVector backends = m->backends(); foreach(AbstractResourcesBackend* backend, backends) { if(QString::fromLatin1(backend->metaObject()->className()) == name) { return backend; } } return nullptr; } DummyTest::DummyTest(QObject* parent): QObject(parent) { DiscoverBackendsFactory::setRequestedBackends({ QStringLiteral("dummy-backend") }); m_model = new ResourcesModel(QStringLiteral("dummy-backend"), this); m_appBackend = backendByName(m_model, QStringLiteral("DummyBackend")); CategoryModel::global()->populateCategories(); } void DummyTest::initTestCase() { QVERIFY(m_appBackend); while(m_appBackend->isFetching()) { QSignalSpy spy(m_appBackend, &AbstractResourcesBackend::fetchingChanged); QVERIFY(spy.wait()); } } QVector fetchResources(ResultsStream* stream) { QVector ret; QObject::connect(stream, &ResultsStream::resourcesFound, stream, [&ret](const QVector& res) { ret += res; }); QSignalSpy spy(stream, &ResultsStream::destroyed); Q_ASSERT(spy.wait()); return ret; } void DummyTest::testReadData() { const auto resources = fetchResources(m_appBackend->search({})); - QCOMPARE(m_appBackend->property("startElements").toInt(), resources.size()); + QCOMPARE(m_appBackend->property("startElements").toInt() * 2, resources.size()); QBENCHMARK { for(AbstractResource* res: resources) { QVERIFY(!res->name().isEmpty()); } } } void DummyTest::testProxy() { ResourcesProxyModel pm; QSignalSpy spy(&pm, &ResourcesProxyModel::busyChanged); // QVERIFY(spy.wait()); QVERIFY(!pm.isBusy()); pm.setFiltersFromCategory(CategoryModel::global()->rootCategories().first()); pm.componentComplete(); QVERIFY(pm.isBusy()); QVERIFY(spy.wait()); QVERIFY(!pm.isBusy()); - QCOMPARE(m_appBackend->property("startElements").toInt(), pm.rowCount()); + QCOMPARE(pm.rowCount(), m_appBackend->property("startElements").toInt() * 2); pm.setSearch(QStringLiteral("techie")); QVERIFY(pm.isBusy()); QVERIFY(spy.wait()); QVERIFY(!pm.isBusy()); QCOMPARE(0, pm.rowCount()); QCOMPARE(pm.subcategories().count(), 7); pm.setSearch(QString()); QVERIFY(pm.isBusy()); QVERIFY(spy.wait()); QVERIFY(!pm.isBusy()); - QCOMPARE(m_appBackend->property("startElements").toInt(), pm.rowCount()); + QCOMPARE(pm.rowCount(), m_appBackend->property("startElements").toInt() * 2); } void DummyTest::testProxySorting() { ResourcesProxyModel pm; QSignalSpy spy(&pm, &ResourcesProxyModel::busyChanged); // QVERIFY(spy.wait()); QVERIFY(!pm.isBusy()); pm.setFiltersFromCategory(CategoryModel::global()->rootCategories().first()); pm.setSortOrder(Qt::DescendingOrder); pm.setSortRole(ResourcesProxyModel::RatingCountRole); pm.componentComplete(); QVERIFY(pm.isBusy()); QVERIFY(spy.wait()); QVERIFY(!pm.isBusy()); - QCOMPARE(m_appBackend->property("startElements").toInt(), pm.rowCount()); + QCOMPARE(m_appBackend->property("startElements").toInt() * 2, pm.rowCount()); QVariant lastRatingCount; for(int i=0, rc=pm.rowCount(); isearch({})); - QCOMPARE(m_appBackend->property("startElements").toInt(), resources.count()); + QCOMPARE(m_appBackend->property("startElements").toInt()*2, resources.count()); //fetches updates, adds new things m_appBackend->checkForUpdates(); QSignalSpy spy(m_model, &ResourcesModel::allInitialized); QVERIFY(spy.wait(80000)); auto resources2 = fetchResources(m_appBackend->search({})); - QCOMPARE(m_appBackend->property("startElements").toInt()*2, resources2.count()); + QCOMPARE(m_appBackend->property("startElements").toInt()*4, resources2.count()); } void DummyTest::testSort() { ResourcesProxyModel pm; QCollator c; QBENCHMARK_ONCE { pm.setSortRole(ResourcesProxyModel::NameRole); pm.sort(0); QCOMPARE(pm.sortOrder(), Qt::AscendingOrder); QString last; for(int i = 0, count = pm.rowCount(); isearch(filter)); QCOMPARE(resources.count(), 1); AbstractResource* res = resources.first(); QVERIFY(res); ApplicationAddonsModel m; new QAbstractItemModelTester(&m, &m); m.setApplication(res); QCOMPARE(m.rowCount(), res->addonsInformation().count()); QCOMPARE(res->addonsInformation().at(0).isInstalled(), false); QString firstAddonName = m.data(m.index(0,0)).toString(); m.changeState(firstAddonName, true); QVERIFY(m.hasChanges()); m.applyChanges(); QSignalSpy sR(TransactionModel::global(), &TransactionModel::transactionRemoved); QVERIFY(sR.wait()); QVERIFY(!m.hasChanges()); QCOMPARE(m.data(m.index(0,0)).toString(), firstAddonName); QCOMPARE(res->addonsInformation().at(0).name(), firstAddonName); QCOMPARE(res->addonsInformation().at(0).isInstalled(), true); m.changeState(m.data(m.index(1,0)).toString(), true); QVERIFY(m.hasChanges()); for(int i=0, c=m.rowCount(); isearch(filter)); QCOMPARE(resources.count(), 1); AbstractResource* res = resources.first(); QVERIFY(res); ReviewsModel m; new QAbstractItemModelTester(&m, &m); m.setResource(res); m.fetchMore(); QVERIFY(m.rowCount()>0); QCOMPARE(ReviewsModel::UserChoice(m.data(m.index(0,0), ReviewsModel::UsefulChoice).toInt()), ReviewsModel::None); m.markUseful(0, true); QCOMPARE(ReviewsModel::UserChoice(m.data(m.index(0,0), ReviewsModel::UsefulChoice).toInt()), ReviewsModel::Yes); m.markUseful(0, false); QCOMPARE(ReviewsModel::UserChoice(m.data(m.index(0,0), ReviewsModel::UsefulChoice).toInt()), ReviewsModel::No); const auto resources2 = fetchResources(m_appBackend->search(filter)); QCOMPARE(resources2.count(), 1); res = resources2.first(); m.setResource(res); m.fetchMore(); QSignalSpy spy(&m, &ReviewsModel::rowsChanged); QVERIFY(m.rowCount()>0); } void DummyTest::testUpdateModel() { const auto backend = m_model->backends().first(); ResourcesUpdatesModel ruModel; new QAbstractItemModelTester(&ruModel, &ruModel); UpdateModel model; new QAbstractItemModelTester(&model, &model); model.setBackend(&ruModel); - QCOMPARE(model.rowCount(), 4*backend->property("startElements").toInt()/3); + QCOMPARE(model.rowCount(), backend->property("startElements").toInt()*2); QCOMPARE(model.hasUpdates(), true); } void DummyTest::testScreenshotsModel() { AbstractResourcesBackend::Filters filter; filter.resourceUrl = QUrl(QStringLiteral("dummy://Dummy.1")); ScreenshotsModel m; new QAbstractItemModelTester(&m, &m); const auto resources = fetchResources(m_appBackend->search(filter)); QCOMPARE(resources.count(), 1); AbstractResource* res = resources.first(); QVERIFY(res); m.setResource(res); QCOMPARE(res, m.resource()); int c=m.rowCount(); for(int i=0; i * * * * 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 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 . * ***************************************************************************/ #include "DummyTest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class UpdateDummyTest : public QObject { Q_OBJECT public: AbstractResourcesBackend* backendByName(ResourcesModel* m, const QString& name) { QVector backends = m->backends(); foreach(AbstractResourcesBackend* backend, backends) { if(QLatin1String(backend->metaObject()->className()) == name) { return backend; } } return nullptr; } UpdateDummyTest(QObject* parent = nullptr): QObject(parent) { m_model = new ResourcesModel(QStringLiteral("dummy-backend"), this); m_appBackend = backendByName(m_model, QStringLiteral("DummyBackend")); } private Q_SLOTS: void init() { QVERIFY(m_appBackend); while(m_appBackend->isFetching()) { QSignalSpy spy(m_appBackend, &AbstractResourcesBackend::fetchingChanged); QVERIFY(spy.wait()); } } void testInformation() { ResourcesUpdatesModel* rum = new ResourcesUpdatesModel(this); new QAbstractItemModelTester(rum, rum); UpdateModel* m = new UpdateModel(this); new QAbstractItemModelTester(m, m); m->setBackend(rum); rum->prepare(); QSignalSpy spySetup(m_appBackend->backendUpdater(), &AbstractBackendUpdater::progressingChanged); QVERIFY(!m_appBackend->backendUpdater()->isProgressing() || spySetup.wait()); - QCOMPARE(m_appBackend->updatesCount(), m_appBackend->property("startElements").toInt()*2/3); + QCOMPARE(m_appBackend->updatesCount(), m_appBackend->property("startElements").toInt()); QCOMPARE(m->hasUpdates(), true); - QCOMPARE(m->index(0,0).data(UpdateModel::ChangelogRole).toString(), {}); + QCOMPARE(m->index(0,0).data(UpdateModel::ChangelogRole).toString(), QString{}); QSignalSpy spy(m, &QAbstractItemModel::dataChanged); m->fetchUpdateDetails(0); QVERIFY(spy.count() || spy.wait()); QCOMPARE(spy.count(), 1); delete m; } void testUpdate() { ResourcesUpdatesModel* rum = new ResourcesUpdatesModel(this); new QAbstractItemModelTester(rum, rum); UpdateModel* m = new UpdateModel(this); new QAbstractItemModelTester(m, m); m->setBackend(rum); rum->prepare(); QSignalSpy spySetup(m_appBackend->backendUpdater(), &AbstractBackendUpdater::progressingChanged); QVERIFY(!m_appBackend->backendUpdater()->isProgressing() || spySetup.wait()); - QCOMPARE(m_appBackend->updatesCount(), m_appBackend->property("startElements").toInt()*2/3); + QCOMPARE(m_appBackend->updatesCount(), m_appBackend->property("startElements").toInt()); QCOMPARE(m->hasUpdates(), true); for(int i=0, c=m->rowCount(); iindex(i,0); QVERIFY(resourceIdx.isValid()); AbstractResource* res = qobject_cast(resourceIdx.data(UpdateModel::ResourceRole).value()); QVERIFY(res); QCOMPARE(Qt::CheckState(resourceIdx.data(Qt::CheckStateRole).toInt()), Qt::Checked); QVERIFY(m->setData(resourceIdx, int(Qt::Unchecked), Qt::CheckStateRole)); QCOMPARE(Qt::CheckState(resourceIdx.data(Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(resourceIdx.data(Qt::DisplayRole).toString(), res->name()); if (i!=0) { QVERIFY(m->setData(resourceIdx, int(Qt::Checked), Qt::CheckStateRole)); } } QSignalSpy spy(rum, &ResourcesUpdatesModel::progressingChanged); QVERIFY(!rum->isProgressing() || spy.wait()); QCOMPARE(rum->isProgressing(), false); QCOMPARE(m_appBackend->updatesCount(), m->rowCount()); QCOMPARE(m->hasUpdates(), true); rum->prepare(); spy.clear(); QCOMPARE(rum->isProgressing(), false); rum->updateAll(); QVERIFY(spy.count() || spy.wait()); QCOMPARE(rum->isProgressing(), true); QCOMPARE(TransactionModel::global()->rowCount(), 1); connect(TransactionModel::global(), &TransactionModel::progressChanged, this, []() { const int progress = TransactionModel::global()->progress(); static int lastProgress = -1; Q_ASSERT(progress >= lastProgress || (TransactionModel::global()->rowCount() == 0 && progress == 0)); lastProgress = progress; }); QTest::qWait(20); QScopedPointer rum2(new ResourcesUpdatesModel(this)); new QAbstractItemModelTester(rum2.data(), rum2.data()); QScopedPointer m2(new UpdateModel(this)); new QAbstractItemModelTester(m2.data(), m2.data()); m->setBackend(rum2.data()); QCOMPARE(rum->isProgressing(), true); QVERIFY(spy.wait()); QCOMPARE(rum->isProgressing(), false); QCOMPARE(m_appBackend->updatesCount(), 0); QCOMPARE(m->hasUpdates(), false); } private: ResourcesModel* m_model; AbstractResourcesBackend* m_appBackend; }; QTEST_MAIN(UpdateDummyTest) #include "UpdateDummyTest.moc" diff --git a/libdiscover/backends/FlatpakBackend/FlatpakSourcesBackend.cpp b/libdiscover/backends/FlatpakBackend/FlatpakSourcesBackend.cpp index 0ed5fdf6..f7e3fe04 100644 --- a/libdiscover/backends/FlatpakBackend/FlatpakSourcesBackend.cpp +++ b/libdiscover/backends/FlatpakBackend/FlatpakSourcesBackend.cpp @@ -1,324 +1,322 @@ /*************************************************************************** * Copyright © 2014 Aleix Pol Gonzalez * * Copyright © 2017 Jan Grulich * * * * 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 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 . * ***************************************************************************/ #include "FlatpakSourcesBackend.h" #include "FlatpakResource.h" #include "FlatpakBackend.h" #include #include #include #include #include #include #include #include #include #include #include class FlatpakSourceItem : public QStandardItem { public: FlatpakSourceItem(const QString &text) : QStandardItem(text) { } void setFlatpakInstallation(FlatpakInstallation *installation) { m_installation = installation; } FlatpakInstallation *flatpakInstallation() const { return m_installation; } private: FlatpakInstallation *m_installation; }; FlatpakSourcesBackend::FlatpakSourcesBackend(const QVector &installations, AbstractResourcesBackend * parent) : AbstractSourcesBackend(parent) , m_preferredInstallation(installations.constFirst()) , m_sources(new QStandardItemModel(this)) , m_flathubAction(new QAction(i18n("Add Flathub"), this)) , m_noSourcesItem(new QStandardItem(QStringLiteral("-"))) { m_flathubAction->setToolTip(QStringLiteral("flathub")); connect(m_flathubAction, &QAction::triggered, this, [this](){ addSource(QStringLiteral("https://flathub.org/repo/flathub.flatpakrepo")); }); for (auto installation : installations) { if (!listRepositories(installation)) { qWarning() << "Failed to list repositories from installation" << installation; } } m_noSourcesItem->setEnabled(false); if (m_sources->rowCount() == 0) { m_sources->appendRow(m_noSourcesItem); } } FlatpakSourcesBackend::~FlatpakSourcesBackend() { QStringList ids; for (int i = 0, c = m_sources->rowCount(); iitem(i); ids << it->data(IdRole).toString(); } auto conf = KSharedConfig::openConfig(); KConfigGroup group = conf->group("FlatpakSources"); group.writeEntry("Sources", ids); } QAbstractItemModel* FlatpakSourcesBackend::sources() { return m_sources; } bool FlatpakSourcesBackend::addSource(const QString &id) { FlatpakBackend* backend = qobject_cast(parent()); const QUrl flatpakrepoUrl(id); if (id.isEmpty() || !flatpakrepoUrl.isValid()) return false; - if (flatpakrepoUrl.isLocalFile()) { - auto res = backend->addSourceFromFlatpakRepo(flatpakrepoUrl); + auto addSource = [=](AbstractResource* res) { if (res) backend->installApplication(res); else Q_EMIT backend->passiveMessage(i18n("Could not add the source %1", flatpakrepoUrl.toDisplayString())); + }; + + if (flatpakrepoUrl.isLocalFile()) { + addSource(backend->addSourceFromFlatpakRepo(flatpakrepoUrl)); } else { AbstractResourcesBackend::Filters filter; filter.resourceUrl = flatpakrepoUrl; auto stream = new StoredResultsStream ({backend->search(filter)}); - connect(stream, &StoredResultsStream::finished, this, [backend, stream, flatpakrepoUrl]() { + connect(stream, &StoredResultsStream::finished, this, [addSource, stream]() { const auto res = stream->resources(); - if (!res.isEmpty()) { - Q_ASSERT(res.count() == 1); - backend->installApplication(res.first()); - } else { - Q_EMIT backend->passiveMessage(i18n("Could not add the source %1", flatpakrepoUrl.toDisplayString())); - } + addSource(res.value(0)); }); } return true; } QStandardItem * FlatpakSourcesBackend::sourceById(const QString& id) const { QStandardItem* sourceIt = nullptr; for (int i = 0, c = m_sources->rowCount(); iitem(i); if (it->data(IdRole) == id) { sourceIt = it; break; } } return sourceIt; } QStandardItem * FlatpakSourcesBackend::sourceByUrl(const QString& _url) const { QUrl url(_url); QStandardItem* sourceIt = nullptr; for (int i = 0, c = m_sources->rowCount(); iitem(i); if (url.matches(it->data(Qt::StatusTipRole).toUrl(), QUrl::StripTrailingSlash)) { sourceIt = it; break; } } return sourceIt; } bool FlatpakSourcesBackend::removeSource(const QString &id) { auto sourceIt = sourceById(id); if (sourceIt) { FlatpakSourceItem *sourceItem = static_cast(sourceIt); g_autoptr(GCancellable) cancellable = g_cancellable_new(); g_autoptr(GError) error = nullptr; if (flatpak_installation_remove_remote(sourceItem->flatpakInstallation(), id.toUtf8().constData(), cancellable, &error)) { m_sources->removeRow(sourceItem->row()); if (m_sources->rowCount() == 0) { m_sources->appendRow(m_noSourcesItem); } return true; } else { qWarning() << "Failed to remove " << id << " remote repository:" << error->message; return false; } } else { qWarning() << "couldn't find " << id; return false; } return false; } QList FlatpakSourcesBackend::actions() const { return { m_flathubAction }; } bool FlatpakSourcesBackend::listRepositories(FlatpakInstallation* installation) { Q_ASSERT(installation); g_autoptr(GCancellable) cancellable = g_cancellable_new(); g_autoptr(GPtrArray) remotes = flatpak_installation_list_remotes(installation, cancellable, nullptr); if (!remotes) { return false; } for (uint i = 0; i < remotes->len; i++) { FlatpakRemote *remote = FLATPAK_REMOTE(g_ptr_array_index(remotes, i)); if (flatpak_remote_get_noenumerate(remote)) { continue; } addRemote(remote, installation); } return true; } FlatpakRemote * FlatpakSourcesBackend::installSource(FlatpakResource *resource) { g_autoptr(GCancellable) cancellable = g_cancellable_new(); auto remote = flatpak_installation_get_remote_by_name(m_preferredInstallation, resource->flatpakName().toUtf8().constData(), cancellable, nullptr); if (remote) { - qWarning() << "Source " << resource->flatpakName() << " already exists"; + qWarning() << "Source " << resource->flatpakName() << " already exists in" << flatpak_installation_get_path(m_preferredInstallation); return nullptr; } remote = flatpak_remote_new(resource->flatpakName().toUtf8().constData()); flatpak_remote_set_url(remote, resource->getMetadata(QStringLiteral("repo-url")).toString().toUtf8().constData()); flatpak_remote_set_noenumerate(remote, false); flatpak_remote_set_title(remote, resource->comment().toUtf8().constData()); const QString gpgKey = resource->getMetadata(QStringLiteral("gpg-key")).toString(); if (!gpgKey.isEmpty()) { gsize dataLen = 0; g_autofree guchar *data = nullptr; g_autoptr(GBytes) bytes = nullptr; data = g_base64_decode(gpgKey.toUtf8().constData(), &dataLen); bytes = g_bytes_new(data, dataLen); flatpak_remote_set_gpg_verify(remote, true); flatpak_remote_set_gpg_key(remote, bytes); } else { flatpak_remote_set_gpg_verify(remote, false); } if (!resource->branch().isEmpty()) { flatpak_remote_set_default_branch(remote, resource->branch().toUtf8().constData()); } if (!flatpak_installation_modify_remote(m_preferredInstallation, remote, cancellable, nullptr)) { qWarning() << "Failed to add source " << resource->flatpakName(); return nullptr; } addRemote(remote, m_preferredInstallation); return remote; } void FlatpakSourcesBackend::addRemote(FlatpakRemote *remote, FlatpakInstallation *installation) { const QString id = QString::fromUtf8(flatpak_remote_get_name(remote)); const QString title = QString::fromUtf8(flatpak_remote_get_title(remote)); const QUrl remoteUrl(QString::fromUtf8(flatpak_remote_get_url(remote))); const auto theActions = actions(); for(QAction *action: theActions) { if (action->toolTip() == id) { action->setEnabled(false); action->setVisible(false); } } FlatpakSourceItem *it = new FlatpakSourceItem(!title.isEmpty() ? title : id); it->setData(remoteUrl.isLocalFile() ? remoteUrl.toLocalFile() : remoteUrl.host(), Qt::ToolTipRole); it->setData(remoteUrl, Qt::StatusTipRole); it->setData(id, IdRole); it->setFlatpakInstallation(installation); int idx = -1; { const auto conf = KSharedConfig::openConfig(); const KConfigGroup group = conf->group("FlatpakSources"); const auto ids = group.readEntry("Sources", QStringList()); const auto ourIdx = ids.indexOf(id); if (ourIdx<0) { //If not present, we put it on top idx = 0; } else { idx=0; for(int c=m_sources->rowCount(); idxitem(idx); const int compIdx = ids.indexOf(compIt->data(IdRole).toString()); if (compIdx >= ourIdx) { break; } } } } m_sources->insertRow(idx, it); if (m_sources->rowCount() == 1) Q_EMIT firstSourceIdChanged(); Q_EMIT lastSourceIdChanged(); if (m_sources->rowCount() > 0) { m_sources->takeRow(m_noSourcesItem->row()); } } QString FlatpakSourcesBackend::idDescription() { return i18n("Flatpak repository URI (*.flatpakrepo)"); } bool FlatpakSourcesBackend::moveSource(const QString& sourceId, int delta) { auto item = sourceById(sourceId); if (!item) return false; const auto row = item->row(); auto prevRow = m_sources->takeRow(row); Q_ASSERT(!prevRow.isEmpty()); const auto destRow = row + (delta>0? delta : delta); m_sources->insertRow(destRow, prevRow); if (destRow == 0 || row == 0) Q_EMIT firstSourceIdChanged(); if (destRow == m_sources->rowCount() - 1 || row == m_sources->rowCount() - 1) Q_EMIT lastSourceIdChanged(); return true; } int FlatpakSourcesBackend::originIndex(const QString& sourceId) const { auto item = sourceById(sourceId); return item ? item->row() : INT_MAX; } diff --git a/libdiscover/backends/FlatpakBackend/org.kde.discover.flatpak.appdata.xml b/libdiscover/backends/FlatpakBackend/org.kde.discover.flatpak.appdata.xml index d0cf947d..4a79b592 100644 --- a/libdiscover/backends/FlatpakBackend/org.kde.discover.flatpak.appdata.xml +++ b/libdiscover/backends/FlatpakBackend/org.kde.discover.flatpak.appdata.xml @@ -1,110 +1,110 @@ org.kde.discover.flatpak Flatpak backend سَند فلاتپاك Dorsal del Flatpak Dorsal de Flatpak Podpůrná vrstva Flatpak Flatpak-motor Flatpak-Backend Flatpak backend Motor Flatpak Flatpak bizkarraldeakoa Flatpak-taustaosa Moteur Flatpak Infraestrutura de Flatpak Backend Flatpak Motore Flatpak Flatpak 백엔드 Flatpak vidinė pusė Flatpak-motor Flatpak-backend Flatpak-motor ਫਲੈਟਪੈਕ ਬੈਕਐਡ Silnik Flatpak Infra-estrutura do Flatpak Infraestrutura Flatpak Модуль для работы с форматом Flatpak Backend Flatpak Zaledje Flatpak Gränssnitt för Flatpak Коркардкунандаи Flatpak Модуль Flatpak xxFlatpak backendxx Flatpak 后端 Flatpak 後端 Integrates Flatpak applications into Discover يُكامل تطبيقات ”فلاتپاك“ في «استكشف» Integra aplicacions del Flatpak al Discover Integra aplicacions de Flatpak a Discover Integrerer Flatpak-programmer i Discover Integriert Flatpak-Anwendungen in Discover Integrates Flatpak applications into Discover Integra aplicaciones Flatpak en Discover Flatpak aplikazioak Discover-ren integratzen ditu Yhdistää Flatpak-sovellukset Discoveriin Intègre les applications Flatpak au sein de Discover Integra aplicacións de Flatpak con Discover Aplikasi Flatpak terintegrasi ke dalam Discover Integra le applicazioni Flatpak in Discover Flatpak 프로그램을 살펴보기에 통합 Integruoja Flatpak programas į Discover Integrerer Flatpak-programmer i Discover Integreert Flatpak-toepassingen in Discover Integrerer Flatpak-program i Discover Integruje aplikacje Flatpak w Odkrywcy Integra as aplicações do Flatpak no Discover Integra aplicativos Flatpak no Discover Добавление поддержки формата Flatpak в центр программ Discover Integruje aplikácie Flatpad do Discoveru V Discover vgradi programe Flatpack Integrerar Flatpak-program i Discover Барномаҳои Flatpak-ро ба барномаи Кашфиёт дарунсохт мекунад Інтеграція програм Flatpak з Discover xxIntegrates Flatpak applications into Discoverxx 将 Flatpak 应用程序集成到Discover中 將 Flatpak 應用程式整合進 Discover 商店 org.kde.discover.desktop CC0-1.0 GPL-2.0+ Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Алейкс Пол Гонзалес (Aleix Pol Gonzalez) Aleix Pol Gonzalez xxAleix Pol Gonzalezxx Aleix Pol Gonzalez Aleix Pol Gonzalez system-software-install + - diff --git a/libdiscover/backends/FlatpakBackend/tests/FlatpakTest.cpp b/libdiscover/backends/FlatpakBackend/tests/FlatpakTest.cpp index 94751ef8..c235cb73 100644 --- a/libdiscover/backends/FlatpakBackend/tests/FlatpakTest.cpp +++ b/libdiscover/backends/FlatpakBackend/tests/FlatpakTest.cpp @@ -1,163 +1,171 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * 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 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 . * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include class FlatpakTest : public QObject { Q_OBJECT public: AbstractResourcesBackend* backendByName(ResourcesModel* m, const QString& name) { QVector backends = m->backends(); foreach(AbstractResourcesBackend* backend, backends) { if(QLatin1String(backend->metaObject()->className()) == name) { return backend; } } return nullptr; } FlatpakTest(QObject* parent = nullptr): QObject(parent) { + QStandardPaths::setTestModeEnabled(true); qputenv("FLATPAK_TEST_MODE", "ON"); m_model = new ResourcesModel(QStringLiteral("flatpak-backend"), this); m_appBackend = backendByName(m_model, QStringLiteral("FlatpakBackend")); } private Q_SLOTS: void init() { + QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/discover-flatpak-test")).removeRecursively(); + QVERIFY(m_appBackend); while(m_appBackend->isFetching()) { QSignalSpy spy(m_appBackend, &AbstractResourcesBackend::fetchingChanged); QVERIFY(spy.wait()); } } void testAddSource() { auto res = getAllResources(m_appBackend); QCOMPARE(res.count(), 0); auto m = SourcesModel::global(); auto bk = qobject_cast(m->index(0, 0).data(SourcesModel::SourcesBackend).value()); QSignalSpy initializedSpy(m_appBackend, SIGNAL(initialized())); if (m->rowCount() == 1) { QSignalSpy spy(m, &SourcesModel::rowsInserted); bk->actions().constFirst()->trigger(); - QVERIFY(spy.count() || spy.wait()); + QVERIFY(spy.count() || spy.wait(20000)); } - QVERIFY(initializedSpy.count() || initializedSpy.wait()); + QVERIFY(initializedSpy.count() || initializedSpy.wait(20000)); auto resFlathub = getAllResources(m_appBackend); QVERIFY(resFlathub.count() > 0); } void testListOrigin() { AbstractResourcesBackend::Filters f; f.origin = QStringLiteral("flathub"); auto resources= getResources(m_appBackend->search(f), true); QVERIFY(resources.count()>0); } void testInstallApp() { AbstractResourcesBackend::Filters f; f.resourceUrl = QUrl(QStringLiteral("appstream://com.github.rssguard.desktop")); const auto res = getResources(m_appBackend->search(f)); QVERIFY(res.count() == 1); const auto resRssguard = res.constFirst(); QCOMPARE(resRssguard->state(), AbstractResource::None); - waitTransaction(m_appBackend->installApplication(resRssguard)); + QCOMPARE(waitTransaction(m_appBackend->installApplication(resRssguard)), Transaction::DoneStatus); QCOMPARE(resRssguard->state(), AbstractResource::Installed); - waitTransaction(m_appBackend->removeApplication(resRssguard)); + QCOMPARE(waitTransaction(m_appBackend->removeApplication(resRssguard)), Transaction::DoneStatus); QCOMPARE(resRssguard->state(), AbstractResource::None); } void testCancelInstallation() { AbstractResourcesBackend::Filters f; f.resourceUrl = QUrl(QStringLiteral("appstream://com.github.rssguard.desktop")); const auto res = getResources(m_appBackend->search(f)); QVERIFY(res.count() == 1); const auto resRssguard = res.constFirst(); QCOMPARE(resRssguard->state(), AbstractResource::None); auto t = m_appBackend->installApplication(resRssguard); QSignalSpy spy(t, &Transaction::statusChanged); QVERIFY(spy.wait()); QCOMPARE(t->status(), Transaction::CommittingStatus); t->cancel(); QCOMPARE(t->status(), Transaction::CancelledStatus); } private: - void waitTransaction(Transaction* t) { - QSignalSpy spyInstalled(t->resource(), &AbstractResource::stateChanged); + Transaction::Status waitTransaction(Transaction* t) { + TransactionModel::global()->addTransaction(t); + QSignalSpy spyInstalled(TransactionModel::global(), &TransactionModel::transactionRemoved); QSignalSpy destructionSpy(t, &QObject::destroyed); + + Transaction::Status ret = t->status(); + connect(TransactionModel::global(), &TransactionModel::transactionRemoved, this, [t, &ret] { ret = t->status(); }); while (t && spyInstalled.count() == 0) { - qDebug() << t->status() << t->progress(); + qDebug() << "waiting, currently" << ret << spyInstalled.count() << destructionSpy.count(); spyInstalled.wait(100); } - QVERIFY(destructionSpy.count() || destructionSpy.wait()); + Q_ASSERT(destructionSpy.count() || destructionSpy.wait()); + return ret; } QVector getResources(ResultsStream* stream, bool canBeEmpty = true) { Q_ASSERT(stream); QSignalSpy spyResources(stream, &ResultsStream::destroyed); QVector resources; connect(stream, &ResultsStream::resourcesFound, this, [&resources](const QVector& res) { resources += res; }); Q_ASSERT(spyResources.wait(10000)); Q_ASSERT(!resources.isEmpty() || canBeEmpty); return resources; } QVector getAllResources(AbstractResourcesBackend* backend) { AbstractResourcesBackend::Filters f; if (CategoryModel::global()->rootCategories().isEmpty()) CategoryModel::global()->populateCategories(); f.category = CategoryModel::global()->rootCategories().constFirst(); return getResources(backend->search(f), true); } ResourcesModel* m_model; AbstractResourcesBackend* m_appBackend; }; QTEST_MAIN(FlatpakTest) #include "FlatpakTest.moc" diff --git a/libdiscover/backends/KNSBackend/KNSResource.cpp b/libdiscover/backends/KNSBackend/KNSResource.cpp index 950a9745..a40b5ac8 100644 --- a/libdiscover/backends/KNSBackend/KNSResource.cpp +++ b/libdiscover/backends/KNSBackend/KNSResource.cpp @@ -1,282 +1,282 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * 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 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 . * ***************************************************************************/ #include "KNSResource.h" #include "KNSBackend.h" #include #include #include #include #include #include "ReviewsBackend/Rating.h" #include KNSResource::KNSResource(const KNSCore::EntryInternal& entry, QStringList categories, KNSBackend* parent) : AbstractResource(parent) , m_categories(std::move(categories)) , m_entry(entry) , m_lastStatus(entry.status()) { connect(this, &KNSResource::stateChanged, parent, &KNSBackend::updatesCountChanged); } KNSResource::~KNSResource() = default; AbstractResource::State KNSResource::state() { switch(m_entry.status()) { case KNS3::Entry::Invalid: return Broken; case KNS3::Entry::Downloadable: return None; case KNS3::Entry::Installed: return Installed; case KNS3::Entry::Updateable: return Upgradeable; case KNS3::Entry::Deleted: case KNS3::Entry::Installing: case KNS3::Entry::Updating: return None; } return None; } KNSBackend * KNSResource::knsBackend() const { return qobject_cast(parent()); } QVariant KNSResource::icon() const { const QString thumbnail = m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1); return thumbnail.isEmpty() ? knsBackend()->iconName() : m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1); } QString KNSResource::comment() { QString ret = m_entry.shortSummary(); if(ret.isEmpty()) { ret = m_entry.summary(); int newLine = ret.indexOf(QLatin1Char('\n')); if(newLine>0) { ret.truncate(newLine); } ret.remove(QRegularExpression(QStringLiteral("\\[\\/?[a-z]*\\]"))); ret.remove(QRegularExpression(QStringLiteral("<[^>]*>"))); } return ret; } QString KNSResource::longDescription() { QString ret = m_entry.summary(); if (m_entry.shortSummary().isEmpty()) { const int newLine = ret.indexOf(QLatin1Char('\n')); if (newLine<0) ret.clear(); else ret = ret.mid(newLine+1).trimmed(); } ret.remove(QLatin1Char('\r')); ret.replace(QStringLiteral("[li]"), QStringLiteral("\n* ")); // Get rid of all BBCode markup we don't handle above ret.remove(QRegularExpression(QStringLiteral("\\[\\/?[a-z]*\\]"))); // Find anything that looks like a link (but which also is not some html // tag value or another already) and make it a link static const QRegularExpression urlRegExp(QStringLiteral("(^|\\s)([-a-zA-Z0-9@:%_\\+.~#?&//=]{2,256}\\.[a-z]{2,4}\\b(\\/[-a-zA-Z0-9@:;%_\\+.~#?&//=]*)?)"), QRegularExpression::CaseInsensitiveOption); ret.replace(urlRegExp, QStringLiteral("\\2")); return ret; } QString KNSResource::name() const { return m_entry.name(); } QString KNSResource::packageName() const { return m_entry.uniqueId(); } QStringList KNSResource::categories() { return m_categories; } QUrl KNSResource::homepage() { return m_entry.homepage(); } void KNSResource::setEntry(const KNSCore::EntryInternal& entry) { const bool diff = entry.status() != m_lastStatus; m_entry = entry; if (diff) { m_lastStatus = entry.status(); Q_EMIT stateChanged(); } } KNSCore::EntryInternal KNSResource::entry() const { return m_entry; } QJsonArray KNSResource::licenses() { return { QJsonObject{ {QStringLiteral("name"), m_entry.license()} } }; } int KNSResource::size() { const auto downloadInfo = m_entry.downloadLinkInformationList(); return downloadInfo.isEmpty() ? 0 : downloadInfo.at(0).size; } QString KNSResource::installedVersion() const { return m_entry.version(); } QString KNSResource::availableVersion() const { return !m_entry.updateVersion().isEmpty() ? m_entry.updateVersion() : m_entry.version(); } QString KNSResource::origin() const { return m_entry.providerId(); } QString KNSResource::section() { return m_entry.category(); } static void appendIfValid(QList& list, const QUrl &value, const QUrl &fallback = {}) { if (!list.contains(value)) { if (value.isValid() && !value.isEmpty()) list << value; else if (!fallback.isEmpty()) appendIfValid(list, fallback); } } void KNSResource::fetchScreenshots() { QList preview; appendIfValid(preview, QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1))); appendIfValid(preview, QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall2))); appendIfValid(preview, QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall3))); QList screenshots; appendIfValid(screenshots, QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewBig1)), QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1))); appendIfValid(screenshots, QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewBig2)), QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall2))); appendIfValid(screenshots, QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewBig3)), QUrl(m_entry.previewUrl(KNSCore::EntryInternal::PreviewSmall3))); emit screenshotsFetched(preview, screenshots); } void KNSResource::fetchChangelog() { emit changelogFetched(m_entry.changelog()); } QStringList KNSResource::extends() const { return knsBackend()->extends(); } QStringList KNSResource::executables() const { if (knsBackend()->engine()->hasAdoptionCommand()) return {knsBackend()->engine()->adoptionCommand(m_entry)}; else return {}; } QUrl KNSResource::url() const { return QUrl(QStringLiteral("kns://")+knsBackend()->name() + QLatin1Char('/') + QUrl(m_entry.providerId()).host() + QLatin1Char('/') + m_entry.uniqueId()); } void KNSResource::invokeApplication() const { QStringList exes = executables(); if(!exes.isEmpty()) { const QString exe = exes.constFirst(); auto args = KShell::splitArgs(exe); QProcess::startDetached(args.takeFirst(), args); } else { qWarning() << "cannot execute" << packageName(); } } QString KNSResource::executeLabel() const { if(knsBackend()->hasApplications()) { return i18n("Launch"); } return i18n("Use"); } QDate KNSResource::releaseDate() const { return m_entry.updateReleaseDate().isNull() ? m_entry.releaseDate() : m_entry.updateReleaseDate(); } QVector KNSResource::linkIds() const { QVector ids; const auto linkInfo = m_entry.downloadLinkInformationList(); for(const auto &e : linkInfo) { if (e.isDownloadtypeLink) ids << e.id; } return ids; } QUrl KNSResource::donationURL() { return QUrl(m_entry.donationLink()); } Rating * KNSResource::ratingInstance() { if (!m_rating) { const int noc = m_entry.numberOfComments(); const int rating = m_entry.rating(); Q_ASSERT(rating <= 100); return new Rating( packageName(), noc, - { { QStringLiteral("star5"), rating } } + rating / 10 ); } return m_rating; } QString KNSResource::author() const { return m_entry.author().name(); } diff --git a/libdiscover/backends/PackageKitBackend/org.kde.discover.packagekit.appdata.xml b/libdiscover/backends/PackageKitBackend/org.kde.discover.packagekit.appdata.xml index c62b1172..3801e598 100644 --- a/libdiscover/backends/PackageKitBackend/org.kde.discover.packagekit.appdata.xml +++ b/libdiscover/backends/PackageKitBackend/org.kde.discover.packagekit.appdata.xml @@ -1,115 +1,115 @@ org.kde.discover.packagekit PackageKit backend سَند عدّة الحزم Dorsal del PackageKit Dorsal de PackageKit Podpůrná vrstva PackageKit PackageKit-motor PackageKit-Backend PackageKit backend Motor PackageKit PackageKit bizkarraldekoa PackageKit-taustaosa Moteur PackageKit Infraestrutura de PackageKit Retro administration de PackageKit (equipamento de pacchetto) Backend PackageKit Motore PackageKit PackageKit 백엔드 PackageKit vidinė pusė PackageKit-motor PackageKit-backend PackageKit-motor ਪੈਕੇਜਕਿਟ ਬੈਕਐਂਡ Silnik PackageKit Infra-estrutura do PackageKit Infraestrutura PackageKit Модуль поддержки PackageKit Backend PackageKit Zaledje PackageKit Gränssnitt för PackageKit Коркардкунандаи PackageKit Модуль PackageKit xxPackageKit backendxx PackageKit 后端 PackageKit 後端 Integrates distribution applications into Discover يُكامل تطبيقات ”عُدّة الحزم“ في «استكشف» Integra aplicacions de distribució al Discover Integra aplicacions de distribució a Discover Integrerer distributionsprogrammer i Discover Integriert Distributions-Anwendungen in Discover Integrates distribution applications into Discover Integra aplicaciones de la distribución en Discover Banaketaren aplikazioak Dicover-ren integratzen ditu Yhdistää jakelun sovellukset Discoveriin Intègre les applications de distribution au sein de Discover Integra aplicacións da distribución con Discover Integrate appicationes de distribution in Discover Aplikasi distribusi terintegrasi ke dalam Discover Integra le applicazioni della distribuzione in Discover 배포판 프로그램을 발견에 통합 Integruoja platinimo programas į Discover Integrerer distribusjonsprogrammer i Discover Integreert distributie-toepassingen in Discover Integrerer distribusjonsprogram i Discover ਵੰਡਣ ਵਾਲੀਆਂ ਐਪਲੀਕੇਸ਼ਨਾਂ ਨੂੰ ਡਿਸਕਵਰ ਵਿੱਚ ਜੋੜਦਾ ਹੈ Integruje aplikacje dystrybucji w Odkrywcy Integra as aplicações da distribuição no Discover Integra aplicativos da distribuição no Discover Добавление поддержки приложений из дистрибутива ОС в центр программ Discover Integruje distribučné aplikácie do Discovera V Discover vgradi programe distribucije Integrerar distributionsprogram i Discover Барномаҳои низоми амалкунандаро ба барномаи Кашфиёт дарунсохт мекунад. Інтегрує програми зі сховищ дистрибутива до Discover xxIntegrates distribution applications into Discoverxx 将发行版应用程序集成到Discover中 將發行版的應用程式整合進 Discover 商店 org.kde.discover.desktop CC0-1.0 GPL-2.0+ Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez ਐਲਿਕਸ ਪੋਲ ਪੋਨਜ਼ਾਵੇਜ Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Алейкс Пол Гонзалес (Aleix Pol Gonzalez) Aleix Pol Gonzalez xxAleix Pol Gonzalezxx Aleix Pol Gonzalez Aleix Pol Gonzalez system-software-install + - diff --git a/libdiscover/backends/SnapBackend/org.kde.discover.snap.appdata.xml b/libdiscover/backends/SnapBackend/org.kde.discover.snap.appdata.xml index f08d0a7d..48394fbc 100644 --- a/libdiscover/backends/SnapBackend/org.kde.discover.snap.appdata.xml +++ b/libdiscover/backends/SnapBackend/org.kde.discover.snap.appdata.xml @@ -1,109 +1,109 @@ org.kde.discover.snap Snap backend سَند سناپ Dorsal de l'Snap Dorsal de Snap Podpůrná vrstva Snap Snap-motor Snap-Backend Snap backend Motor Snap Snap bizkarraldekoa Snap-taustaosa Moteur Snap Infraestrutura de Snap Backend Snap Motore Snap Snap 백엔드 Snap vidinė pusė Snap-motor Snap-backend Snap-motor Silnik Snap Infra-estrutura do Snap Infraestrutura Snap Модуль поддержки формата Snap Backend Snap Zaledje Snap Gränssnitt för Snap Коркардкунандаи Часпиш Модуль Snap xxSnap backendxx Snap 后端 Snap 後端 Integrates Snap applications into Discover يُكامل تطبيقات ”سناپ“ في «استكشف» Integra aplicacions de l'Snap al Discover Integra aplicacions de Snap a Discover Integrerer Snap-programmer i Discover Integriert Snap-Anwendungen in Discover Integrates Snap applications into Discover Integra aplicaciones Snap en Discover Snap aplikazioak Discover-ren integratzen ditu Yhdistää Snap-sovellukset Discoveriin Intègre les applications Snap au sein de Discover Integra aplicacións de Snap con Discover Aplikasi Snap terintegrasi ke dalam Discover Integra le applicazioni Snap in Discover Snap 프로그램을 발견에 통합 Integruoja Snap programas į Discover Integrerer Snap-programmer i Discover Integreert Snap-toepassingen in Discover Integrerer Snap-program i Discover Integruje aplikacje Snap w Odkrywcy Integra as aplicações do Snap no Discover Integra aplicativos Snap no Discover Добавление поддержки формата Snap в центр программ Discover Integruje aplikácie Snap do Discoveru V Discover vgradi programe Snap Integrerar Snap-program i Discover Барномаҳои Часпишро ба барномаи Кашфиёт дарунсохт мекунад Інтегрує програми Snap до Discover xxIntegrates Snap applications into Discoverxx 将 Snap 应用程序集成到Discover中 將 Snap 應用程式整合進 Discover 商店 org.kde.discover.desktop CC0-1.0 GPL-2.0+ Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Aleix Pol Gonzalez Алейкс Пол Гонзалес (Aleix Pol Gonzalez) Aleix Pol Gonzalez xxAleix Pol Gonzalezxx Aleix Pol Gonzalez Aleix Pol Gonzalez system-software-install + - diff --git a/notifier/main.cpp b/notifier/main.cpp index 064a12d5..c92e115a 100644 --- a/notifier/main.cpp +++ b/notifier/main.cpp @@ -1,96 +1,98 @@ /* * Copyright (C) 2019 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, 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 Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include "DiscoverNotifier.h" +#include "../DiscoverVersion.h" #include "NotifierItem.h" #include "../DiscoverVersion.h" int main(int argc, char** argv) { QApplication app(argc, argv); app.setOrganizationDomain(QStringLiteral("kde.org")); KCrash::setFlags(KCrash::AutoRestart); NotifierItem notifier; bool hide = false; { KAboutData about(QStringLiteral("DiscoverNotifier"), i18n("Discover Notifier"), version, i18n("System update status notifier"), KAboutLicense::GPL, i18n("© 2010-2019 Plasma Development Team")); about.addAuthor(QStringLiteral("Aleix Pol Gonzalez"), {}, QStringLiteral("aleixpol@kde.org")); about.setProductName("discover/discover"); about.setProgramLogo(app.windowIcon()); - about.setTranslator( i18ndc(nullptr, "NAME OF TRANSLATORS", "Your names"), i18ndc(nullptr, "EMAIL OF TRANSLATORS", "Your emails")); QCommandLineParser parser; QCommandLineOption replaceOption({QStringLiteral("replace")}, i18n("Replace an existing instance")); parser.addOption(replaceOption); QCommandLineOption hideOption({QStringLiteral("hide")}, i18n("Do not show the notifier"), i18n("hidden"), QStringLiteral("false")); parser.addOption(hideOption); about.setupCommandLine(&parser); parser.process(app); about.processCommandLine(&parser); if (parser.isSet(replaceOption)) { auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.DiscoverNotifier"), QStringLiteral("/MainApplication"), QStringLiteral("org.qtproject.Qt.QCoreApplication"), QStringLiteral("quit")); auto reply = QDBusConnection::sessionBus().call(message); //deliberately block until it's done, so we register the name after the app quits while (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.DiscoverNotifier"))) { QCoreApplication::processEvents(QEventLoop::AllEvents); } } const auto config = KSharedConfig::openConfig(); KConfigGroup group(config, "Behavior"); if (parser.isSet(hideOption)) { hide = parser.value(hideOption) == QLatin1String("true"); group.writeEntry("Hide", hide); config->sync(); } else { hide = group.readEntry("Hide", false); } } KDBusService service(KDBusService::Unique); notifier.setVisible(!hide); return app.exec(); }