From d15345306868313b311b144870ae9e34125f7b04 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Tue, 29 Jun 2021 17:09:55 +0100 Subject: [PATCH 01/46] Replace KNS3::UploadDialog with one that loads a QML file --- src/CMakeLists.txt | 6 +- src/uploaddialog.cpp | 796 ++--------------------------------------- src/uploaddialog.h | 18 +- src/uploaddialog.qml | 32 ++ src/uploaddialog.qrc | 5 + src/uploaddialog.ui | 831 ------------------------------------------- src/uploaddialog_p.h | 120 ++----- 7 files changed, 126 insertions(+), 1682 deletions(-) create mode 100644 src/uploaddialog.qml create mode 100644 src/uploaddialog.qrc delete mode 100644 src/uploaddialog.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 98c708fb..dd283172 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,8 @@ list(APPEND KNewStuff_SRCS ) endif() +qt_add_resources(KNewStuff_RESOURCES uploaddialog.qrc) + ecm_qt_declare_logging_category(KNewStuff_SRCS HEADER knewstuff_debug.h IDENTIFIER KNEWSTUFF @@ -41,11 +43,10 @@ ecm_qt_declare_logging_category(KNewStuff_SRCS ki18n_wrap_ui(KNewStuff_SRCS downloadwidget.ui - uploaddialog.ui kmoretools/ui/kmoretoolsconfigwidget.ui ) -add_library(KF5NewStuff ${KNewStuff_SRCS} ) +add_library(KF5NewStuff ${KNewStuff_SRCS} ${KNewStuff_RESOURCES}) add_library(KF5::NewStuff ALIAS KF5NewStuff) ecm_generate_export_header(KF5NewStuff @@ -76,6 +77,7 @@ target_link_libraries(KF5NewStuff KF5::ItemViews # For buttons on download dialog KF5::IconThemes # For KIcon Qt5::Qml + Qt5::Quick KF5::TextWidgets # For KTextEdit in upload dialog ) diff --git a/src/uploaddialog.cpp b/src/uploaddialog.cpp index 390a102a..8eeeaebc 100644 --- a/src/uploaddialog.cpp +++ b/src/uploaddialog.cpp @@ -8,410 +8,41 @@ */ #include "uploaddialog.h" -#include "ui/widgetquestionlistener.h" +#include "knewstuff_debug.h" #include "uploaddialog_p.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include +#include +#include +#include using namespace KNS3; bool UploadDialogPrivate::init(const QString &configfile) { - QVBoxLayout *layout = new QVBoxLayout; - q->setLayout(layout); - - QWidget *_mainWidget = new QWidget(q); - ui.setupUi(_mainWidget); - - layout->addWidget(_mainWidget); - - backButton = new QPushButton; - KGuiItem::assign(backButton, KStandardGuiItem::back(KStandardGuiItem::UseRTL)); - - nextButton = new QPushButton; - nextButton->setText(i18nc("Opposite to Back", "Next")); - nextButton->setIcon(KStandardGuiItem::forward(KStandardGuiItem::UseRTL).icon()); - nextButton->setDefault(true); + bool success{true}; - finishButton = new QPushButton; - finishButton->setText(i18n("Finish")); - finishButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply"))); - - buttonBox = new QDialogButtonBox(q); - buttonBox->addButton(backButton, QDialogButtonBox::ActionRole); - buttonBox->addButton(nextButton, QDialogButtonBox::ActionRole); - buttonBox->addButton(finishButton, QDialogButtonBox::AcceptRole); - buttonBox->setStandardButtons(QDialogButtonBox::Cancel); - layout->addWidget(buttonBox); - - atticaHelper = new KNSCore::AtticaHelper(q); - - bool success = true; - QFileInfo fi(configfile); - if (!fi.exists()) { - if (!fi.isAbsolute()) - fi.setFile(QStandardPaths::locate(QStandardPaths::GenericConfigLocation, configfile)); - if (!fi.exists()) { - qCCritical(KNEWSTUFF) << "No knsrc file named '" << fi.absoluteFilePath() << "' was found."; - success = false; - } - } - - KConfig conf(fi.absoluteFilePath()); - if (conf.accessMode() == KConfig::NoAccess) { - qCCritical(KNEWSTUFF) << "Knsrc file named '" << fi.absoluteFilePath() << "' could not be accessed."; - success = false; - } - - KConfigGroup group; - if (conf.hasGroup("KNewStuff3")) { - qCDebug(KNEWSTUFF) << "Loading KNewStuff3 config: " << fi.absoluteFilePath(); - group = conf.group("KNewStuff3"); + QQuickView *view = new QQuickView; + QVBoxLayout *layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + q->setLayout(layout); + q->layout()->addWidget(QWidget::createWindowContainer(view)); + + view->rootContext()->setContextProperty(QStringLiteral("knsrcfile"), configfile); + view->setSource(QUrl(QStringLiteral("qrc:///uploaddialog.qml"))); + QQuickItem *item = view->rootObject(); + // If there is an error on the QML side of things we get a nullptr + if (item) { + q->resize(view->rootObject()->implicitWidth(), view->rootObject()->implicitHeight()); + // Forward relevant signals + QObject::connect(item, SIGNAL(closed()), q, SLOT(accept())); } else { - qCCritical(KNEWSTUFF) << "A knsrc file was found but it doesn't contain a KNewStuff3 section." << fi.absoluteFilePath(); + qCDebug(KNEWSTUFF) << "Failed to load the UploadDialog components. The QML Engine reported the following errors:" << view->errors(); success = false; } - - if (success) { - const QString providersFileUrl = group.readEntry("ProvidersUrl"); - - categoryNames = group.readEntry("UploadCategories", QStringList()); - // fall back to download categories - if (categoryNames.isEmpty()) { - categoryNames = group.readEntry("Categories", QStringList()); - } - - atticaHelper->addProviderFile(QUrl(providersFileUrl)); - } - - ui.mCategoryCombo->addItems(categoryNames); - - if (categoryNames.size() == 1) { - ui.mCategoryLabel->setVisible(false); - ui.mCategoryCombo->setVisible(false); - } - - qCDebug(KNEWSTUFF) << "Categories: " << categoryNames; - - q->connect(atticaHelper, SIGNAL(providersLoaded(QStringList)), q, SLOT(_k_providersLoaded(QStringList))); - q->connect(atticaHelper, SIGNAL(loginChecked(bool)), q, SLOT(_k_checkCredentialsFinished(bool))); - q->connect(atticaHelper, SIGNAL(licensesLoaded(Attica::License::List)), q, SLOT(_k_licensesLoaded(Attica::License::List))); - q->connect(atticaHelper, SIGNAL(categoriesLoaded(Attica::Category::List)), q, SLOT(_k_categoriesLoaded(Attica::Category::List))); - q->connect(atticaHelper, SIGNAL(contentByCurrentUserLoaded(Attica::Content::List)), q, SLOT(_k_contentByCurrentUserLoaded(Attica::Content::List))); - q->connect(atticaHelper, SIGNAL(contentLoaded(Attica::Content)), q, SLOT(_k_updatedContentFetched(Attica::Content))); - q->connect(atticaHelper, SIGNAL(detailsLinkLoaded(QUrl)), q, SLOT(_k_detailsLinkLoaded(QUrl))); - q->connect(atticaHelper, SIGNAL(currencyLoaded(QString)), q, SLOT(_k_currencyLoaded(QString))); - q->connect(atticaHelper, SIGNAL(previewLoaded(int, QImage)), q, SLOT(_k_previewLoaded(int, QImage))); - atticaHelper->init(); - - q->connect(ui.changePreview1Button, SIGNAL(clicked()), q, SLOT(_k_changePreview1())); - q->connect(ui.changePreview2Button, SIGNAL(clicked()), q, SLOT(_k_changePreview2())); - q->connect(ui.changePreview3Button, SIGNAL(clicked()), q, SLOT(_k_changePreview3())); - - q->connect(ui.providerComboBox, SIGNAL(currentIndexChanged(QString)), q, SLOT(_k_providerChanged(QString))); - q->connect(ui.radioUpdate, SIGNAL(toggled(bool)), q, SLOT(_k_updateContentsToggled(bool))); - - q->connect(ui.registerNewAccountLabel, SIGNAL(linkActivated(QString)), q, SLOT(_k_openRegisterAccountWebpage(QString))); - - // Busy widget - busyWidget = new KPixmapSequenceWidget(); - busyWidget->setSequence(KIconLoader::global()->loadPixmapSequence(QStringLiteral("process-working"), 22)); - busyWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - ui.busyWidget->setLayout(new QHBoxLayout()); - ui.busyWidget->layout()->addWidget(busyWidget); - busyWidget->setVisible(false); - - WidgetQuestionListener::instance(); - return success; } -void UploadDialogPrivate::setBusy(const QString &message) -{ - ui.busyLabel->setText(message); - busyWidget->setVisible(true); -} - -void UploadDialogPrivate::setIdle(const QString &message) -{ - ui.busyLabel->setText(message); - busyWidget->setVisible(false); -} - -void UploadDialogPrivate::_k_showPage(int page) -{ - ui.stackedWidget->setCurrentIndex(page); - setIdle(QString()); - - switch (ui.stackedWidget->currentIndex()) { - case UserPasswordPage: - ui.username->setFocus(); - setBusy(i18n("Fetching provider information...")); - break; - - case FileNewUpdatePage: - atticaHelper->loadLicenses(); - atticaHelper->loadCurrency(); - ui.uploadButton->setFocus(); - setBusy(i18n("Fetching license data from server...")); - break; - - case Details1Page: - if (ui.radioUpdate->isChecked()) { - // Fetch - atticaHelper->loadContent(ui.userContentList->currentItem()->data(Qt::UserRole).toString()); - setBusy(i18n("Fetching content data from server...")); - } - - ui.mNameEdit->setFocus(); - break; - - case UploadFinalPage: - if (previewFile1.isEmpty()) { - ui.uploadPreview1ImageLabel->setVisible(false); - ui.uploadPreview1Label->setVisible(false); - } - if (previewFile2.isEmpty()) { - ui.uploadPreview2ImageLabel->setVisible(false); - ui.uploadPreview2Label->setVisible(false); - } - if (previewFile3.isEmpty()) { - ui.uploadPreview3ImageLabel->setVisible(false); - ui.uploadPreview3Label->setVisible(false); - } - break; - } - - _k_updatePage(); -} - -void UploadDialogPrivate::_k_updatePage() -{ - bool firstPage = ui.stackedWidget->currentIndex() == 0; - backButton->setEnabled(!firstPage && !finished); - - bool nextEnabled = false; - switch (ui.stackedWidget->currentIndex()) { - case UserPasswordPage: - if (ui.providerComboBox->count() > 0 && !ui.username->text().isEmpty() && !ui.password->text().isEmpty()) { - nextEnabled = true; - } - break; - - case FileNewUpdatePage: - // FIXME: check if the file requester contains a valid file - if (!uploadFile.isEmpty() || ui.uploadFileRequester->url().isLocalFile()) { - if (ui.radioNewUpload->isChecked() || ui.userContentList->currentRow() >= 0) { - nextEnabled = true; - } - } - break; - - case Details1Page: - if (!ui.mNameEdit->text().isEmpty()) { - nextEnabled = true; - } - break; - - case Details2Page: - nextEnabled = true; - break; - - case UploadFinalPage: - break; - } - - nextButton->setEnabled(nextEnabled); - finishButton->setEnabled(finished); - - nextButton->setDefault(nextEnabled); - finishButton->setDefault(!nextEnabled); - - if (nextEnabled && buttonBox->button(QDialogButtonBox::Cancel)->hasFocus()) { - nextButton->setFocus(); - } -} - -void UploadDialogPrivate::_k_providersLoaded(const QStringList &providers) -{ - if (providers.isEmpty()) { - setIdle(i18n("Could not fetch provider information.")); - ui.stackedWidget->setEnabled(false); - qWarning() << "Could not load providers."; - return; - } - setIdle(QString()); - ui.providerComboBox->addItems(providers); - ui.providerComboBox->setCurrentIndex(0); - atticaHelper->setCurrentProvider(providers.at(0)); - - QString user; - QString pass; - if (atticaHelper->loadCredentials(user, pass)) { - ui.username->setText(user); - ui.password->setText(pass); - } - _k_updatePage(); -} - -void UploadDialogPrivate::_k_providerChanged(const QString &providerName) -{ - atticaHelper->setCurrentProvider(providerName); - QString registerUrl = atticaHelper->provider().getRegisterAccountUrl(); - if (!registerUrl.isEmpty()) { - ui.registerNewAccountLabel->setText(QStringLiteral("") + i18n("Register a new account") + QStringLiteral("")); - } else { - ui.registerNewAccountLabel->setText(QString()); - } - ui.username->clear(); - ui.password->clear(); - QString user; - QString pass; - if (atticaHelper->loadCredentials(user, pass)) { - ui.username->setText(user); - ui.password->setText(pass); - } - _k_updatePage(); -} - -void UploadDialogPrivate::_k_backPage() -{ - _k_showPage(ui.stackedWidget->currentIndex() - 1); -} - -void UploadDialogPrivate::_k_nextPage() -{ - // TODO: validate credentials after user name/password have been entered - if (ui.stackedWidget->currentIndex() == UserPasswordPage) { - setBusy(i18n("Checking login...")); - nextButton->setEnabled(false); - ui.providerComboBox->setEnabled(false); - ui.username->setEnabled(false); - ui.password->setEnabled(false); - atticaHelper->checkLogin(ui.username->text(), ui.password->text()); - } else { - _k_showPage(ui.stackedWidget->currentIndex() + 1); - } -} - -void UploadDialogPrivate::_k_checkCredentialsFinished(bool success) -{ - ui.providerComboBox->setEnabled(true); - ui.username->setEnabled(true); - ui.password->setEnabled(true); - - if (success) { - atticaHelper->saveCredentials(ui.username->text(), ui.password->text()); - _k_showPage(FileNewUpdatePage); - - atticaHelper->loadCategories(categoryNames); - setBusy(i18n("Fetching your previously updated content...")); - } else { - // TODO check what the actual error is - setIdle(i18n("Could not verify login, please try again.")); - } -} - -void UploadDialogPrivate::_k_licensesLoaded(const Attica::License::List &licenses) -{ - ui.mLicenseCombo->clear(); - for (const Attica::License &license : licenses) { - ui.mLicenseCombo->addItem(license.name(), license.id()); - } -} - -void UploadDialogPrivate::_k_currencyLoaded(const QString ¤cy) -{ - ui.priceCurrency->setText(currency); -} - -void UploadDialogPrivate::_k_contentByCurrentUserLoaded(const Attica::Content::List &contentList) -{ - setIdle(i18n("Fetching your previously updated content finished.")); - - for (const Attica::Content &content : contentList) { - QListWidgetItem *contentItem = new QListWidgetItem(content.name()); - contentItem->setData(Qt::UserRole, content.id()); - ui.userContentList->addItem(contentItem); - } - - if (ui.userContentList->count() > 0) { - ui.userContentList->setCurrentRow(0); - ui.radioUpdate->setEnabled(true); - _k_updatePage(); - } -} - -void UploadDialogPrivate::_k_updatedContentFetched(const Attica::Content &content) -{ - setIdle(i18n("Fetching content data from server finished.")); - - contentId = content.id(); - // fill in ui - ui.mNameEdit->setText(content.name()); - ui.mSummaryEdit->setText(content.description()); - ui.mVersionEdit->setText(content.version()); - ui.changelog->setText(content.changelog()); - ui.priceCheckBox->setChecked(content.attribute(QStringLiteral("downloadbuy1")) == QLatin1Char('1')); - ui.priceSpinBox->setValue(content.attribute(QStringLiteral("downloadbuyprice1")).toDouble()); - ui.priceReasonLineEdit->setText(content.attribute(QStringLiteral("downloadbuyreason1"))); - - bool conversionOk = false; - int licenseNumber = content.license().toInt(&conversionOk); - if (conversionOk) { - // check if that int is in list - int row = ui.mLicenseCombo->findData(licenseNumber, Qt::UserRole); - ui.mLicenseCombo->setCurrentIndex(row); - } else { - ui.mLicenseCombo->setEditText(content.license()); - } - - ui.contentWebsiteLink->setText(QLatin1String("") - + i18nc("A link to the website where the get hot new stuff upload can be seen", "Visit website") + QLatin1String("")); - ui.fetchContentLinkImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); -} - -void UploadDialogPrivate::_k_previewLoaded(int index, const QImage &image) -{ - switch (index) { - case 1: - ui.previewImage1->setPixmap(QPixmap::fromImage(image)); - break; - case 2: - ui.previewImage2->setPixmap(QPixmap::fromImage(image)); - break; - case 3: - ui.previewImage3->setPixmap(QPixmap::fromImage(image)); - break; - } -} - -void UploadDialogPrivate::_k_updateContentsToggled(bool update) -{ - ui.userContentList->setEnabled(update); -} - UploadDialog::UploadDialog(QWidget *parent) : QDialog(parent) , d(new UploadDialogPrivate(this)) @@ -434,141 +65,47 @@ UploadDialog::~UploadDialog() bool UploadDialog::init(const QString &configfile) { - bool success = d->init(configfile); - - setWindowTitle(i18n("Share Hot New Stuff")); - - d->_k_updatePage(); - - connect(d->ui.username, SIGNAL(textChanged(QString)), this, SLOT(_k_updatePage())); - - connect(d->ui.password, SIGNAL(textChanged(QString)), this, SLOT(_k_updatePage())); - connect(d->ui.mNameEdit, SIGNAL(textChanged(QString)), this, SLOT(_k_updatePage())); - connect(d->ui.uploadFileRequester, SIGNAL(textChanged(QString)), this, SLOT(_k_updatePage())); - connect(d->ui.priceCheckBox, SIGNAL(toggled(bool)), this, SLOT(_k_priceToggled(bool))); - - connect(d->ui.uploadButton, SIGNAL(clicked()), this, SLOT(_k_startUpload())); - - connect(d->backButton, SIGNAL(clicked()), this, SLOT(_k_backPage())); - connect(d->nextButton, SIGNAL(clicked()), this, SLOT(_k_nextPage())); - connect(d->buttonBox, &QDialogButtonBox::accepted, this, &UploadDialog::accept); - connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - - QString displayName = QGuiApplication::applicationDisplayName(); - if (displayName.isEmpty()) { - displayName = QCoreApplication::applicationName(); - } - d->ui.mTitleWidget->setText(i18nc("Program name followed by 'Add On Uploader'", "%1 Add-On Uploader", displayName)); - - if (success) { - d->_k_showPage(0); - } - - return success; -} - -void UploadDialog::setUploadFile(const QUrl &payloadFile) -{ - d->uploadFile = payloadFile; - - d->ui.uploadFileLabel->setVisible(false); - d->ui.uploadFileRequester->setVisible(false); - - QFile file(d->uploadFile.toLocalFile()); - if (!file.open(QIODevice::ReadOnly)) { - KMessageBox::error(this, i18n("File not found: %1", d->uploadFile.url()), i18n("Upload Failed")); - } + return d->init(configfile); } -void UploadDialog::setUploadName(const QString &name) +void UploadDialog::setUploadFile(const QUrl &) { - d->ui.mNameEdit->setText(name); } -void UploadDialog::selectCategory(const QString &category) +void UploadDialog::setUploadName(const QString &) { - d->ui.mCategoryCombo->setCurrentIndex(d->ui.mCategoryCombo->findText(category, Qt::MatchFixedString)); } -void UploadDialog::setChangelog(const QString &changelog) +void UploadDialog::selectCategory(const QString &) { - d->ui.changelog->setText(changelog); } -void UploadDialog::setDescription(const QString &description) +void UploadDialog::setChangelog(const QString &) { - d->ui.mSummaryEdit->setText(description); } -void UploadDialog::setPriceEnabled(bool enabled) +void UploadDialog::setDescription(const QString &) { - d->ui.priceCheckBox->setVisible(enabled); - d->ui.priceGroupBox->setVisible(enabled); } -void UploadDialog::setPrice(double price) +void UploadDialog::setPriceEnabled(bool) { - d->ui.priceCheckBox->setEnabled(true); - d->ui.priceSpinBox->setValue(price); } -void UploadDialog::setPriceReason(const QString &reason) +void UploadDialog::setPrice(double) { - d->ui.priceReasonLineEdit->setText(reason); } -void UploadDialog::setVersion(const QString &version) +void UploadDialog::setPriceReason(const QString &) { - d->ui.mVersionEdit->setText(version); } -void UploadDialog::setPreviewImageFile(uint number, const QUrl &file) +void UploadDialog::setVersion(const QString &) { - QPixmap preview(file.toLocalFile()); - switch (number) { - case 0: - d->previewFile1 = file; - d->ui.previewImage1->setPixmap(preview.scaled(d->ui.previewImage1->size())); - break; - case 1: - d->previewFile2 = file; - d->ui.previewImage2->setPixmap(preview.scaled(d->ui.previewImage2->size())); - break; - case 2: - d->previewFile3 = file; - d->ui.previewImage3->setPixmap(preview.scaled(d->ui.previewImage3->size())); - break; - default: - qCCritical(KNEWSTUFF) << "Wrong preview image file number"; - break; - } -} - -void UploadDialogPrivate::_k_priceToggled(bool priceEnabled) -{ - ui.priceGroupBox->setEnabled(priceEnabled); } -void UploadDialogPrivate::_k_categoriesLoaded(const Attica::Category::List &loadedCategories) +void UploadDialog::setPreviewImageFile(uint, const QUrl &) { - categories = loadedCategories; - - // at least one category is needed - if (categories.isEmpty()) { - KMessageBox::error(q, - i18np("The server does not recognize the category %2 to which you are trying to upload.", - "The server does not recognize any of the categories to which you are trying to upload: %2", - categoryNames.size(), - categoryNames.join(QLatin1String(", "))), - i18n("Error")); - // close the dialog - q->reject(); - return; - } - for (const Attica::Category &c : qAsConst(categories)) { - ui.mCategoryCombo->addItem(c.name(), c.id()); - } - atticaHelper->loadContentByCurrentUser(); } void UploadDialog::accept() @@ -576,273 +113,4 @@ void UploadDialog::accept() QDialog::accept(); } -void UploadDialogPrivate::_k_startUpload() -{ - // FIXME: this only works if categories are set in the .knsrc file - // TODO: ask for confirmation when closing the dialog - - backButton->setEnabled(false); - buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); - - ui.uploadButton->setEnabled(false); - - // idle back and forth, we need a fix in attica to get at real progress values - ui.uploadProgressBar->setMinimum(0); - ui.uploadProgressBar->setMaximum(0); - ui.uploadProgressBar->setValue(0); - - // check the category - QString categoryName = ui.mCategoryCombo->currentText(); - QList::const_iterator iter = categories.constBegin(); - Attica::Category category; - QList::const_iterator iterEnd = categories.constEnd(); - while (iter != iterEnd) { - if (iter->name() == categoryName) { - category = *iter; - break; - } - ++iter; - } - if (!category.isValid()) { - KMessageBox::error(q, i18n("The selected category \"%1\" is invalid.", categoryName), i18n("Upload Failed")); - return; - } - - // fill in the content object - Attica::Content content; - content.setName(ui.mNameEdit->text()); - QString summary = ui.mSummaryEdit->toPlainText(); - content.addAttribute(QStringLiteral("description"), summary); - content.addAttribute(QStringLiteral("version"), ui.mVersionEdit->text()); - - // for the license, if one of the licenses coming from the server was used, pass its id, otherwise the string - QString licenseId = ui.mLicenseCombo->itemData(ui.mLicenseCombo->currentIndex()).toString(); - if (licenseId.isEmpty()) { - // use other as type and add the string as text - content.addAttribute(QStringLiteral("licensetype"), QStringLiteral("0")); - content.addAttribute(QStringLiteral("license"), ui.mLicenseCombo->currentText()); - } else { - content.addAttribute(QStringLiteral("licensetype"), licenseId); - } - - content.addAttribute(QStringLiteral("changelog"), ui.changelog->toPlainText()); - - // TODO: add additional attributes - // content.addAttribute("downloadlink1", ui.link1->text()); - // content.addAttribute("downloadlink2", ui.link2->text()); - // content.addAttribute("homepage1", ui.homepage->text()); - // content.addAttribute("blog1", ui.blog->text()); - - content.addAttribute(QStringLiteral("downloadbuy1"), ui.priceCheckBox->isChecked() ? QStringLiteral("1") : QStringLiteral("0")); - content.addAttribute(QStringLiteral("downloadbuyprice1"), QString::number(ui.priceSpinBox->value())); - content.addAttribute(QStringLiteral("downloadbuyreason1"), ui.priceReasonLineEdit->text()); - - if (ui.radioNewUpload->isChecked()) { - // upload a new content - Attica::ItemPostJob *job = currentProvider().addNewContent(category, content); - q->connect(job, SIGNAL(finished(Attica::BaseJob *)), q, SLOT(_k_contentAdded(Attica::BaseJob *))); - job->start(); - } else { - // update old content - Attica::ItemPostJob *job = - currentProvider().editContent(category, ui.userContentList->currentItem()->data(Qt::UserRole).toString(), content); - q->connect(job, SIGNAL(finished(Attica::BaseJob *)), q, SLOT(_k_contentAdded(Attica::BaseJob *))); - job->start(); - } -} - -void UploadDialogPrivate::_k_changePreview1() -{ - const QStringList filters = _supportedMimeTypes(); - QPointer dialog = new QFileDialog(q, i18n("Select preview image")); - dialog->setMimeTypeFilters(filters); - if (dialog->exec() == QDialog::Accepted) { - QUrl url = dialog->selectedUrls().first(); - previewFile1 = url; - qCDebug(KNEWSTUFF) << "preview is: " << url.url(); - QPixmap preview(url.toLocalFile()); - ui.previewImage1->setPixmap(preview.scaled(ui.previewImage1->size())); - } - delete dialog; -} - -void UploadDialogPrivate::_k_changePreview2() -{ - const QStringList filters = _supportedMimeTypes(); - QPointer dialog = new QFileDialog(q, i18n("Select preview image")); - dialog->setMimeTypeFilters(filters); - if (dialog->exec() == QDialog::Accepted) { - QUrl url = dialog->selectedUrls().first(); - previewFile2 = url; - QPixmap preview(url.toLocalFile()); - ui.previewImage2->setPixmap(preview.scaled(ui.previewImage1->size())); - } - delete dialog; -} - -void UploadDialogPrivate::_k_changePreview3() -{ - const QStringList filters = _supportedMimeTypes(); - QPointer dialog = new QFileDialog(q, i18n("Select preview image")); - dialog->setMimeTypeFilters(filters); - if (dialog->exec() == QDialog::Accepted) { - QUrl url = dialog->selectedUrls().first(); - previewFile3 = url; - QPixmap preview(url.toLocalFile()); - ui.previewImage3->setPixmap(preview.scaled(ui.previewImage1->size())); - } - delete dialog; -} - -void UploadDialogPrivate::_k_contentAdded(Attica::BaseJob *baseJob) -{ - if (baseJob->metadata().error()) { - if (baseJob->metadata().error() == Attica::Metadata::NetworkError) { - KMessageBox::error(q, i18n("There was a network error."), i18n("Uploading Failed")); - return; - } - if (baseJob->metadata().error() == Attica::Metadata::OcsError) { - if (baseJob->metadata().statusCode() == 102) { - KMessageBox::error(q, i18n("Authentication error."), i18n("Uploading Failed")); - } - } - return; - } - - ui.createContentImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); - - Attica::ItemPostJob *job = static_cast *>(baseJob); - if (job->metadata().error() != Attica::Metadata::NoError) { - KMessageBox::error(q, i18n("Upload failed: %1", job->metadata().message())); - return; - } - - // only when adding new content we get an id returned, otherwise stick with the old one - QString id = job->result().id(); - if (!id.isEmpty()) { - contentId = id; - } - - if (!uploadFile.isEmpty()) { - doUpload(QString(), uploadFile); - } else { - doUpload(QString(), ui.uploadFileRequester->url()); - } - - // FIXME: status labels need to accommodate 3 previews - if (!previewFile1.isEmpty()) { - doUpload(QStringLiteral("1"), previewFile1); - } - if (!previewFile2.isEmpty()) { - doUpload(QStringLiteral("2"), previewFile2); - } - if (!previewFile3.isEmpty()) { - doUpload(QStringLiteral("3"), previewFile3); - } - - if (ui.radioNewUpload->isChecked()) { - atticaHelper->loadDetailsLink(contentId); - } -} - -void UploadDialogPrivate::_k_openRegisterAccountWebpage(QString) -{ - KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromUserInput(atticaHelper->provider().getRegisterAccountUrl()), QStringLiteral("text/html")); - job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, q)); - job->start(); -} - -void UploadDialogPrivate::doUpload(const QString &index, const QUrl &path) -{ - QFile file(path.toLocalFile()); - if (!file.open(QIODevice::ReadOnly)) { - KMessageBox::error(q, i18n("File not found: %1", uploadFile.url()), i18n("Upload Failed")); - q->reject(); - return; - } - - QByteArray fileContents; - fileContents.append(file.readAll()); - file.close(); - - QString fileName = QFileInfo(path.toLocalFile()).fileName(); - - Attica::PostJob *job = nullptr; - if (index.isEmpty()) { - job = currentProvider().setDownloadFile(contentId, fileName, fileContents); - q->connect(job, SIGNAL(finished(Attica::BaseJob *)), q, SLOT(_k_fileUploadFinished(Attica::BaseJob *))); - } else if (index == QLatin1Char('1')) { - job = currentProvider().setPreviewImage(contentId, index, fileName, fileContents); - q->connect(job, SIGNAL(finished(Attica::BaseJob *)), q, SLOT(_k_preview1UploadFinished(Attica::BaseJob *))); - } else if (index == QLatin1Char('2')) { - job = currentProvider().setPreviewImage(contentId, index, fileName, fileContents); - q->connect(job, SIGNAL(finished(Attica::BaseJob *)), q, SLOT(_k_preview2UploadFinished(Attica::BaseJob *))); - } else if (index == QLatin1Char('3')) { - job = currentProvider().setPreviewImage(contentId, index, fileName, fileContents); - q->connect(job, SIGNAL(finished(Attica::BaseJob *)), q, SLOT(_k_preview3UploadFinished(Attica::BaseJob *))); - } - if (job) { - job->start(); - } -} - -void UploadDialogPrivate::_k_fileUploadFinished(Attica::BaseJob *) -{ - ui.uploadContentImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); - finishedContents = true; - uploadFileFinished(); -} - -void UploadDialogPrivate::_k_preview1UploadFinished(Attica::BaseJob *) -{ - ui.uploadPreview1ImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); - finishedPreview1 = true; - uploadFileFinished(); -} - -void UploadDialogPrivate::_k_preview2UploadFinished(Attica::BaseJob *) -{ - ui.uploadPreview2ImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); - finishedPreview2 = true; - uploadFileFinished(); -} - -void UploadDialogPrivate::_k_preview3UploadFinished(Attica::BaseJob *) -{ - ui.uploadPreview3ImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); - finishedPreview3 = true; - uploadFileFinished(); -} - -void UploadDialogPrivate::uploadFileFinished() -{ - // FIXME multiple previews - if (finishedContents && (previewFile1.isEmpty() || finishedPreview1) && (previewFile2.isEmpty() || finishedPreview2) - && (previewFile3.isEmpty() || finishedPreview3)) { - finished = true; - ui.uploadProgressBar->setMinimum(0); - ui.uploadProgressBar->setMaximum(100); - ui.uploadProgressBar->setValue(100); - _k_updatePage(); - } -} - -void UploadDialogPrivate::_k_detailsLinkLoaded(const QUrl &url) -{ - ui.contentWebsiteLink->setText(QLatin1String("") - + i18nc("A link to the website where the get hot new stuff upload can be seen", "Visit website") + QLatin1String("")); - ui.fetchContentLinkImageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(16)); -} - -QStringList UploadDialogPrivate::_supportedMimeTypes() const -{ - QStringList mimeTypes; - const QList supported = QImageReader::supportedMimeTypes(); - mimeTypes.reserve(supported.count()); - for (const QByteArray &mimeType : supported) { - mimeTypes.append(QString::fromLatin1(mimeType)); - } - return mimeTypes; -} - #include "moc_uploaddialog.cpp" diff --git a/src/uploaddialog.h b/src/uploaddialog.h index 94d475b2..4f486061 100644 --- a/src/uploaddialog.h +++ b/src/uploaddialog.h @@ -4,6 +4,7 @@ SPDX-FileCopyrightText: 2007 Josef Spillner SPDX-FileCopyrightText: 2009 Jeremy Whiting SPDX-FileCopyrightText: 2009-2010 Frederik Gladhorn + SPDX-FileCopyrightText: 2021 Dan Leinir Turthra Jensen SPDX-License-Identifier: LGPL-2.1-or-later */ @@ -28,10 +29,18 @@ class UploadDialogPrivate; /** * @short KNewStuff file upload dialog. * - * Using this dialog, data can easily be uploaded to the Hotstuff servers. + * This dialog shows the user how to add new content to the remote service represented + * by the configuration file passed into it. + * + * @note This dialog originally allowed for performing direct uploads to an OCS service, + * however there is no such service available at this time, and it is unlikely we will + * have one any time soon (as we have an issue where such functionality is essentially + * just a vector for directly visible spam). As such, we have decided to let this dialog + * instead reflect reality, and just give information on how to manually perform those + * uploads through a remote service's web based upload system. * * \par Maintainer: - * Jeremy Whiting (jpwhiting@kde.org) + * Dan Leinir Turthra Jensen (admin@leinir.dk) * * @since 4.4 */ @@ -143,6 +152,10 @@ private: UploadDialogPrivate *const d; + // This section is deprecated, but can't be removed until we can break the + // BIC promise during the KF6 implementation period + // { + // TODO KF6 Remove this section (and the private slot implementation which these require) Q_PRIVATE_SLOT(d, void _k_nextPage()) Q_PRIVATE_SLOT(d, void _k_backPage()) Q_PRIVATE_SLOT(d, void _k_updatePage()) @@ -173,6 +186,7 @@ private: Q_PRIVATE_SLOT(d, void _k_detailsLinkLoaded(QUrl)) Q_PRIVATE_SLOT(d, void _k_openRegisterAccountWebpage(QString)) + // } end deprecated but unremovable section Q_DISABLE_COPY(UploadDialog) }; diff --git a/src/uploaddialog.qml b/src/uploaddialog.qml new file mode 100644 index 00000000..7999726f --- /dev/null +++ b/src/uploaddialog.qml @@ -0,0 +1,32 @@ +/* + SPDX-FileCopyrightText: 2021 Dan Leinir Turthra Jensen + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +import QtQuick 2.7 +import QtQuick.Layouts 1.11 as QtLayouts +import QtQuick.Window 2.15 +import org.kde.kirigami 2.14 as Kirigami +import org.kde.newstuff 1.85 as NewStuff + +Kirigami.ApplicationItem { + signal closed() + anchors.fill: parent + implicitWidth: Math.min(Kirigami.Units.gridUnit * 44, Screen.width) + implicitHeight: Math.min(Kirigami.Units.gridUnit * 30, Screen.height) + pageStack.defaultColumnWidth: pageStack.width + pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Auto + pageStack.globalToolBar.canContainHandles: true + pageStack.initialPage: NewStuff.UploadPage { + configFile: knsrcfile + onVisibleChanged: { + if (!visible) { + applicationWindow.closed(); + } + } + } + contextDrawer: Kirigami.ContextDrawer { + id: contextDrawer + } +} diff --git a/src/uploaddialog.qrc b/src/uploaddialog.qrc new file mode 100644 index 00000000..57b6444d --- /dev/null +++ b/src/uploaddialog.qrc @@ -0,0 +1,5 @@ + + + uploaddialog.qml + + diff --git a/src/uploaddialog.ui b/src/uploaddialog.ui deleted file mode 100644 index 6932968d..00000000 --- a/src/uploaddialog.ui +++ /dev/null @@ -1,831 +0,0 @@ - - - UploadDialog - - - - 0 - 0 - 538 - 395 - - - - Share Hot New Stuff - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - 24 - 24 - - - - - 24 - 24 - - - - - - - - - - 4 - - - - - - - Qt::Vertical - - - - 205 - 183 - - - - - - - - QLineEdit::Password - - - - - - - - 0 - 0 - - - - Password: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Username: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - 0 - 0 - - - - Provider: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - - - - - File to upload: - - - - - - - - - - New Upload - - - true - - - - - - - false - - - Update - - - - - - - false - - - - - - - - - - - - - - true - - - - GPL - - - - - LGPL - - - - - BSD - - - - - - - - Description: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - mSummaryEdit - - - - - - - true - - - Changelog: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Version: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - mVersionEdit - - - - - - - License: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - mLicenseCombo - - - - - - - Please fill out the information about your upload in English. - - - - - - - - - - - - - - - - Title: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - mNameEdit - - - - - - - - - - - - - - - - Category: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - mNameEdit - - - - - - - Name of the file as it will appear on the website - - - This should clearly describe the file content. It can be the same text as the title of the kvtml file. - - - - - - - - - - - - - - Preview Images - - - false - - - - - - Select Preview... - - - - - - - Select Preview... - - - - - - - Select Preview... - - - - - - - - 96 - 72 - - - - - 96 - 82 - - - - - - - Qt::AlignCenter - - - - - - - - 96 - 72 - - - - - 96 - 82 - - - - - - - Qt::AlignCenter - - - - - - - - 96 - 72 - - - - - 96 - 82 - - - - - - - Qt::AlignCenter - - - - - - - - - - Set a price for this item - - - - - - - false - - - Price - - - false - - - - - - Price: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 0.010000000000000 - - - 1.000000000000000 - - - - - - - - - - - - - - Reason for price: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - 22 - 16777215 - - - - - - - - - - - Fetch content link from server - - - - - - - - 0 - 0 - - - - Create content on server - - - - - - - - 0 - 0 - - - - Upload content - - - - - - - - 0 - 0 - - - - Upload first preview - - - - - - - - - - true - - - - - - - Qt::Vertical - - - - 91 - 199 - - - - - - - - Note: You can edit, update and delete your content on the website. - - - true - - - - - - - - 0 - 0 - - - - Upload second preview - - - - - - - - 0 - 0 - - - - Upload third preview - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - - - - - - - I ensure that this content does not violate any existing copyright, law or trademark. I agree for my IP address to be logged. (Distributing content without the permission of the copyright holder is illegal.) - - - true - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Start Upload - - - - - - - - - 1 - - - -1 - - - - - - - - - - - - - - KUrlRequester - QWidget -
kurlrequester.h
-
- - KTitleWidget - QWidget -
ktitlewidget.h
-
- - KComboBox - QComboBox -
kcombobox.h
-
- - KTextEdit - QTextEdit -
ktextedit.h
-
-
- - providerComboBox - username - password - uploadFileRequester - radioNewUpload - radioUpdate - userContentList - mNameEdit - mCategoryCombo - mVersionEdit - mLicenseCombo - mSummaryEdit - changelog - priceCheckBox - priceSpinBox - priceReasonLineEdit - uploadButton - - - -
diff --git a/src/uploaddialog_p.h b/src/uploaddialog_p.h index 10879f82..00ba9865 100644 --- a/src/uploaddialog_p.h +++ b/src/uploaddialog_p.h @@ -8,7 +8,6 @@ #define KNEWSTUFF3_UI_UPLOADDIALOG_P_H #include "ui_uploaddialog.h" -#include "upload/atticahelper_p.h" #include #include @@ -20,10 +19,6 @@ #include -class QDialogButtonBox; -class QPushButton; -class KPixmapSequenceWidget; - namespace KNS3 { class UploadDialogPrivate @@ -31,102 +26,61 @@ class UploadDialogPrivate public: UploadDialogPrivate(UploadDialog *q) : q(q) - , currentPage(UserPasswordPage) - , finished(false) - , finishedPreview1(false) - , finishedPreview2(false) - , finishedPreview3(false) - , finishedContents(false) { } UploadDialog *q; - enum WizardPage { - UserPasswordPage, - FileNewUpdatePage, - Details1Page, - Details2Page, - UploadFinalPage, - }; - WizardPage currentPage; - - Attica::Provider currentProvider() - { - return atticaHelper->provider(); - } - - Ui::UploadDialog ui; - QDialogButtonBox *buttonBox; - QPushButton *finishButton; - QPushButton *nextButton; - QPushButton *backButton; - KPixmapSequenceWidget *busyWidget; - - KNSCore::AtticaHelper *atticaHelper; - - QUrl uploadFile; - QUrl previewFile1; - QUrl previewFile2; - QUrl previewFile3; - QStringList categoryNames; - Attica::Category::List categories; - QString contentId; - bool finished; - bool finishedPreview1; - bool finishedPreview2; - bool finishedPreview3; - bool finishedContents; - bool init(const QString &configfile); - void setBusy(const QString &message); - void setIdle(const QString &message); + // This section is deprecated, but can't be removed until we can break the + // BIC promise during the KF6 implementation period + // { + // TODO KF6 Remove this section (and the private signal entries which make them required) // change to page, set the focus also calls updatePage() - void _k_showPage(int page); + void _k_showPage(int){}; // check after user input - for example enable the next button - void _k_updatePage(); + void _k_updatePage(){}; // next wizard page (next button clicked) - void _k_nextPage(); + void _k_nextPage(){}; // go back one page - void _k_backPage(); + void _k_backPage(){}; // after all has been done and said, do the uploading - void _k_startUpload(); + void _k_startUpload(){}; - void _k_providersLoaded(const QStringList &providerNames); - void _k_providerChanged(const QString &providerName); + void _k_providersLoaded(const QStringList &){}; + void _k_providerChanged(const QString &){}; // validation of login is done, go to next page if successful, otherwise ask again - void _k_checkCredentialsFinished(bool); - void _k_categoriesLoaded(const Attica::Category::List &loadedCategories); - void _k_licensesLoaded(const Attica::License::List &licenses); - void _k_currencyLoaded(const QString ¤cy); - void _k_contentByCurrentUserLoaded(const Attica::Content::List &contentList); - void _k_updatedContentFetched(const Attica::Content &content); - void _k_previewLoaded(int index, const QImage &image); - - void _k_changePreview1(); - void _k_changePreview2(); - void _k_changePreview3(); - void _k_preview1UploadFinished(Attica::BaseJob *); - void _k_preview2UploadFinished(Attica::BaseJob *); - void _k_preview3UploadFinished(Attica::BaseJob *); - - void _k_contentAdded(Attica::BaseJob *); - void _k_fileUploadFinished(Attica::BaseJob *); - - void uploadFileFinished(); - void doUpload(const QString &index, const QUrl &filePath); - - void _k_priceToggled(bool); - void _k_updateContentsToggled(bool update); - void _k_detailsLinkLoaded(const QUrl &url); - void _k_openRegisterAccountWebpage(QString); - - QStringList _supportedMimeTypes() const; + void _k_checkCredentialsFinished(bool){}; + void _k_categoriesLoaded(const Attica::Category::List &){}; + void _k_licensesLoaded(const Attica::License::List &){}; + void _k_currencyLoaded(const QString &){}; + void _k_contentByCurrentUserLoaded(const Attica::Content::List &){}; + void _k_updatedContentFetched(const Attica::Content &){}; + void _k_previewLoaded(int, const QImage &){}; + + void _k_changePreview1(){}; + void _k_changePreview2(){}; + void _k_changePreview3(){}; + void _k_preview1UploadFinished(Attica::BaseJob *){}; + void _k_preview2UploadFinished(Attica::BaseJob *){}; + void _k_preview3UploadFinished(Attica::BaseJob *){}; + + void _k_contentAdded(Attica::BaseJob *){}; + void _k_fileUploadFinished(Attica::BaseJob *){}; + + void uploadFileFinished(){}; + void doUpload(const QString &, const QUrl &){}; + + void _k_priceToggled(bool){}; + void _k_updateContentsToggled(bool){}; + void _k_detailsLinkLoaded(const QUrl &){}; + void _k_openRegisterAccountWebpage(QString){}; + // } end deprecated but unremovable section }; } -- GitLab From d04b44be97c4cdcdd22004e29fe5119ae8179fef Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Tue, 29 Jun 2021 17:10:44 +0100 Subject: [PATCH 02/46] Add a preliminary UploadPage component --- src/qtquick/qml/UploadPage.qml | 55 ++++++++++++++++++++++++++++++++++ src/qtquick/qmldir | 1 + 2 files changed, 56 insertions(+) create mode 100644 src/qtquick/qml/UploadPage.qml diff --git a/src/qtquick/qml/UploadPage.qml b/src/qtquick/qml/UploadPage.qml new file mode 100644 index 00000000..513a1871 --- /dev/null +++ b/src/qtquick/qml/UploadPage.qml @@ -0,0 +1,55 @@ +/* + SPDX-FileCopyrightText: 2019 Dan Leinir Turthra Jensen + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +/** + * @brief A Kirigami.Page component used for showing how to upload KNS entries to a service + * + * This page shows a short guide for uploading new content to the service provided by a KNewStuff + * provider. This attempts to use the information available through the provider itself, and + * otherwise just shows a link to the service's web page. + * + * While there are not currently any services which support direct OCS API based uploading of + * new content, we still need a way to guide people to how to do this. + * + * This component is functionally equivalent to the old UploadDialog + * @see KNewStuff::UploadDialog + * @since 5.63 + */ + +import QtQuick 2.11 +import QtQuick.Controls 2.11 as QtControls +import QtQuick.Layouts 1.11 as QtLayouts + +import org.kde.kcm 1.2 as KCM +import org.kde.kirigami 2.12 as Kirigami + +import org.kde.newstuff 1.83 as NewStuff + +import "private" as Private + +Kirigami.ScrollablePage { + id: component; + title: newStuffEngine.name + /** + * @brief The configuration file which describes the application (knsrc) + * + * The format and location of this file is found in the documentation for + * KNS3::DownloadDialog + */ + property alias configFile: newStuffEngine.configFile; + readonly property alias engine: newStuffEngine; + NewStuff.Engine { + id: newStuffEngine; + } + NewStuff.QuestionAsker {} + Private.ErrorDisplayer { engine: newStuffEngine; active: component.isCurrentPage; } + QtLayouts.ColumnLayout { + QtControls.Label { + QtLayouts.Layout.fillWidth: true; + text: i18nc("@knewstuff", "Something with words and whatever"); + } + } +} diff --git a/src/qtquick/qmldir b/src/qtquick/qmldir index 159489b2..924ba690 100644 --- a/src/qtquick/qmldir +++ b/src/qtquick/qmldir @@ -10,3 +10,4 @@ EntryDetails 1.1 qml/EntryDetails.qml Page 1.1 qml/Page.qml QuestionAsker 1.1 qml/QuestionAsker.qml Action 1.81 qml/Action.qml +UploadPage 1.85 qml/UploadPage.qml -- GitLab From 7fc1f1c5d89f93a0a02e815d1a6a020b353057e7 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Tue, 29 Jun 2021 17:11:05 +0100 Subject: [PATCH 03/46] Shut up the catalogue selector less angry --- tests/khotnewstuff_upload.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/khotnewstuff_upload.cpp b/tests/khotnewstuff_upload.cpp index ebda1198..525ce74d 100644 --- a/tests/khotnewstuff_upload.cpp +++ b/tests/khotnewstuff_upload.cpp @@ -19,12 +19,13 @@ int main(int argc, char **argv) { + QApplication i(argc, argv); + QCoreApplication::setApplicationName(QStringLiteral("khotnewstuff_upload")); QCoreApplication::setApplicationVersion(QStringLiteral("0.4")); QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); QApplication::setApplicationDisplayName(i18n("KHotNewStuff")); - QApplication i(argc, argv); if (i.arguments().count() > 1) { QString configfile = QLatin1String(argv[1]); -- GitLab From 030f70df013be366fcc70540e25a809a48289625 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Wed, 30 Jun 2021 10:41:54 +0100 Subject: [PATCH 04/46] Thanks for confirming that removing this is not BIC! :) --- src/uploaddialog.h | 36 -------------------------------- src/uploaddialog_p.h | 49 -------------------------------------------- 2 files changed, 85 deletions(-) diff --git a/src/uploaddialog.h b/src/uploaddialog.h index 4f486061..6fed7501 100644 --- a/src/uploaddialog.h +++ b/src/uploaddialog.h @@ -152,42 +152,6 @@ private: UploadDialogPrivate *const d; - // This section is deprecated, but can't be removed until we can break the - // BIC promise during the KF6 implementation period - // { - // TODO KF6 Remove this section (and the private slot implementation which these require) - Q_PRIVATE_SLOT(d, void _k_nextPage()) - Q_PRIVATE_SLOT(d, void _k_backPage()) - Q_PRIVATE_SLOT(d, void _k_updatePage()) - - Q_PRIVATE_SLOT(d, void _k_providerChanged(QString)) - Q_PRIVATE_SLOT(d, void _k_checkCredentialsFinished(bool)) - Q_PRIVATE_SLOT(d, void _k_contentByCurrentUserLoaded(Attica::Content::List)) - Q_PRIVATE_SLOT(d, void _k_providersLoaded(QStringList)) - Q_PRIVATE_SLOT(d, void _k_categoriesLoaded(Attica::Category::List)) - Q_PRIVATE_SLOT(d, void _k_licensesLoaded(Attica::License::List)) - Q_PRIVATE_SLOT(d, void _k_currencyLoaded(QString)) - Q_PRIVATE_SLOT(d, void _k_previewLoaded(int, QImage)) - - Q_PRIVATE_SLOT(d, void _k_changePreview1()) - Q_PRIVATE_SLOT(d, void _k_changePreview2()) - Q_PRIVATE_SLOT(d, void _k_changePreview3()) - Q_PRIVATE_SLOT(d, void _k_priceToggled(bool)) - Q_PRIVATE_SLOT(d, void _k_updateContentsToggled(bool update)) - - Q_PRIVATE_SLOT(d, void _k_startUpload()) - Q_PRIVATE_SLOT(d, void _k_contentAdded(Attica::BaseJob *)) - Q_PRIVATE_SLOT(d, void _k_fileUploadFinished(Attica::BaseJob *)) - Q_PRIVATE_SLOT(d, void _k_preview1UploadFinished(Attica::BaseJob *)) - Q_PRIVATE_SLOT(d, void _k_preview2UploadFinished(Attica::BaseJob *)) - Q_PRIVATE_SLOT(d, void _k_preview3UploadFinished(Attica::BaseJob *)) - - Q_PRIVATE_SLOT(d, void _k_updatedContentFetched(Attica::Content)) - Q_PRIVATE_SLOT(d, void _k_detailsLinkLoaded(QUrl)) - - Q_PRIVATE_SLOT(d, void _k_openRegisterAccountWebpage(QString)) - // } end deprecated but unremovable section - Q_DISABLE_COPY(UploadDialog) }; diff --git a/src/uploaddialog_p.h b/src/uploaddialog_p.h index 00ba9865..6283c00f 100644 --- a/src/uploaddialog_p.h +++ b/src/uploaddialog_p.h @@ -32,55 +32,6 @@ public: UploadDialog *q; bool init(const QString &configfile); - - // This section is deprecated, but can't be removed until we can break the - // BIC promise during the KF6 implementation period - // { - // TODO KF6 Remove this section (and the private signal entries which make them required) - // change to page, set the focus also calls updatePage() - void _k_showPage(int){}; - - // check after user input - for example enable the next button - void _k_updatePage(){}; - - // next wizard page (next button clicked) - void _k_nextPage(){}; - // go back one page - void _k_backPage(){}; - - // after all has been done and said, do the uploading - void _k_startUpload(){}; - - void _k_providersLoaded(const QStringList &){}; - void _k_providerChanged(const QString &){}; - - // validation of login is done, go to next page if successful, otherwise ask again - void _k_checkCredentialsFinished(bool){}; - void _k_categoriesLoaded(const Attica::Category::List &){}; - void _k_licensesLoaded(const Attica::License::List &){}; - void _k_currencyLoaded(const QString &){}; - void _k_contentByCurrentUserLoaded(const Attica::Content::List &){}; - void _k_updatedContentFetched(const Attica::Content &){}; - void _k_previewLoaded(int, const QImage &){}; - - void _k_changePreview1(){}; - void _k_changePreview2(){}; - void _k_changePreview3(){}; - void _k_preview1UploadFinished(Attica::BaseJob *){}; - void _k_preview2UploadFinished(Attica::BaseJob *){}; - void _k_preview3UploadFinished(Attica::BaseJob *){}; - - void _k_contentAdded(Attica::BaseJob *){}; - void _k_fileUploadFinished(Attica::BaseJob *){}; - - void uploadFileFinished(){}; - void doUpload(const QString &, const QUrl &){}; - - void _k_priceToggled(bool){}; - void _k_updateContentsToggled(bool){}; - void _k_detailsLinkLoaded(const QUrl &){}; - void _k_openRegisterAccountWebpage(QString){}; - // } end deprecated but unremovable section }; } -- GitLab From 47b2c8cda0f06c2a645c90b093b913041ae60439 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 1 Jul 2021 20:14:27 +0100 Subject: [PATCH 05/46] Add basic properties and config fetching capability to Provider --- src/attica/atticaprovider.cpp | 26 +++++++++ src/attica/atticaprovider_p.h | 7 +++ src/core/provider.cpp | 103 ++++++++++++++++++++++++++++++++++ src/core/provider.h | 84 +++++++++++++++++++++++++++ 4 files changed, 220 insertions(+) diff --git a/src/attica/atticaprovider.cpp b/src/attica/atticaprovider.cpp index 51fc0f30..d60565ec 100644 --- a/src/attica/atticaprovider.cpp +++ b/src/attica/atticaprovider.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,7 @@ AtticaProvider::AtticaProvider(const QStringList &categories, const QString &add connect(&m_providerManager, SIGNAL(authenticationCredentialsMissing(Provider)), SLOT(authenticationCredentialsMissing(Provider))); connect(this, &Provider::loadComments, this, &AtticaProvider::loadComments); connect(this, &Provider::loadPerson, this, &AtticaProvider::loadPerson); + connect(this, &Provider::loadBasics, this, &AtticaProvider::loadBasics); } AtticaProvider::AtticaProvider(const Attica::Provider &provider, const QStringList &categories, const QString &additionalAgentInformation) @@ -423,6 +425,30 @@ void AtticaProvider::loadedPerson(Attica::BaseJob *baseJob) Q_EMIT personLoaded(author); } +void AtticaProvider::loadBasics() +{ + Attica::ItemJob *configJob = m_provider.requestConfig(); + connect(configJob, &BaseJob::finished, this, &AtticaProvider::loadedConfig); + configJob->start(); +} + +void AtticaProvider::loadedConfig(Attica::BaseJob *baseJob) +{ + if (jobSuccess(baseJob)) { + auto *job = static_cast *>(baseJob); + Attica::Config config = job->result(); + setVersion(config.version()); + setSupportsSsl(config.ssl()); + QString protocol{QStringLiteral("http")}; + if (config.ssl()) { + protocol = QStringLiteral("https"); + } + setWebsite(QUrl(QLatin1String("%1://%2").arg(protocol).arg(config.website()))); + setHost(QUrl(QLatin1String("%1://%2").arg(protocol).arg(config.host()))); + setContactEmail(config.contact()); + } +} + void AtticaProvider::accountBalanceLoaded(Attica::BaseJob *baseJob) { if (!jobSuccess(baseJob)) { diff --git a/src/attica/atticaprovider_p.h b/src/attica/atticaprovider_p.h index deab3510..abb6f0e4 100644 --- a/src/attica/atticaprovider_p.h +++ b/src/attica/atticaprovider_p.h @@ -65,6 +65,12 @@ public: * @see Provider::loadPerson(const QString &username) */ Q_SLOT void loadPerson(const QString &username); + /** + * The slot which causes the provider's basic information to be fetched. + * For the Attica provider, this translates to an OCS Config call + * @see Provider::loadBasics() + */ + Q_SLOT void loadBasics(); bool userCanVote() override { @@ -90,6 +96,7 @@ private Q_SLOTS: void detailsLoaded(Attica::BaseJob *job); void loadedComments(Attica::BaseJob *job); void loadedPerson(Attica::BaseJob *job); + void loadedConfig(Attica::BaseJob *job); private: void checkForUpdates(); diff --git a/src/core/provider.cpp b/src/core/provider.cpp index 86cc148e..040621cd 100644 --- a/src/core/provider.cpp +++ b/src/core/provider.cpp @@ -14,14 +14,47 @@ #include +#include + namespace KNSCore { // TODO KF6 BCI: Add a real d-pointer class ProviderPrivate { public: + Provider *q; QStringList tagFilter; QStringList downloadTagFilter; + + QTimer *basicsThrottle{nullptr}; + QString version; + QUrl website; + QUrl host; + QString contactEmail; + bool supportsSsl{false}; + bool basicsGot{false}; + void updateOnFirstBasicsGet() + { + if (!basicsGot) { + basicsGot = true; + QTimer *basicsGetter = new QTimer(q); + basicsGetter->setInterval(0); + basicsGetter->setSingleShot(true); + QObject::connect(basicsGetter, &QTimer::timeout, q, &Provider::loadBasics); + QObject::connect(basicsGetter, &QTimer::timeout, basicsGetter, &QObject::deleteLater); + basicsGetter->start(); + } + }; + void throttleBasics() + { + if (!basicsThrottle) { + basicsThrottle = new QTimer(q); + basicsThrottle->setInterval(0); + basicsThrottle->setSingleShot(true); + QObject::connect(basicsThrottle, &QTimer::timeout, q, &Provider::basicsLoaded); + } + basicsThrottle->start(); + } }; typedef QHash ProviderPrivateHash; Q_GLOBAL_STATIC(ProviderPrivateHash, d_func) @@ -51,6 +84,7 @@ QString Provider::SearchRequest::hashForRequest() const Provider::Provider() { + d(this)->q = this; } Provider::~Provider() @@ -102,4 +136,73 @@ QDebug operator<<(QDebug dbg, const Provider::SearchRequest &search) return dbg; } +QString Provider::version() const +{ + d(this)->updateOnFirstBasicsGet(); + return d(this)->version; +} + +void Provider::setVersion(const QString &version) +{ + if (d(this)->version != version) { + d(this)->version = version; + d(this)->throttleBasics(); + } +} + +QUrl Provider::website() const +{ + d(this)->updateOnFirstBasicsGet(); + return d(this)->website; +} + +void Provider::setWebsite(const QUrl &website) +{ + if (d(this)->website != website) { + d(this)->website = website; + d(this)->throttleBasics(); + } +} + +QUrl Provider::host() const +{ + d(this)->updateOnFirstBasicsGet(); + return d(this)->host; +} + +void Provider::setHost(const QUrl &host) +{ + if (d(this)->host != host) { + d(this)->host = host; + d(this)->throttleBasics(); + } +} + +QString Provider::contactEmail() const +{ + d(this)->updateOnFirstBasicsGet(); + return d(this)->contactEmail; +} + +void Provider::setContactEmail(const QString &contactEmail) +{ + if (d(this)->contactEmail != contactEmail) { + d(this)->contactEmail = contactEmail; + d(this)->throttleBasics(); + } +} + +bool Provider::supportsSsl() const +{ + d(this)->updateOnFirstBasicsGet(); + return d(this)->supportsSsl; +} + +void Provider::setSupportsSsl(bool supportsSsl) +{ + if (d(this)->supportsSsl != supportsSsl) { + d(this)->supportsSsl = supportsSsl; + d(this)->throttleBasics(); + } +} } diff --git a/src/core/provider.h b/src/core/provider.h index 75b6b716..b42d2e8f 100644 --- a/src/core/provider.h +++ b/src/core/provider.h @@ -3,6 +3,7 @@ This file is part of KNewStuff2. SPDX-FileCopyrightText: 2009 Jeremy Whiting SPDX-FileCopyrightText: 2009 Frederik Gladhorn + SPDX-FileCopyrightText: 2021 Dan Leinir Turthra Jensen SPDX-License-Identifier: LGPL-2.1-or-later */ @@ -41,6 +42,11 @@ struct Comment; class KNEWSTUFFCORE_EXPORT Provider : public QObject { Q_OBJECT + Q_PROPERTY(QString version READ version WRITE setVersion NOTIFY basicsLoaded) + Q_PROPERTY(QUrl website READ website WRITE setWebsite NOTIFY basicsLoaded) + Q_PROPERTY(QUrl host READ host WRITE setHost NOTIFY basicsLoaded) + Q_PROPERTY(QString contactEmail READ contactEmail WRITE setContactEmail NOTIFY basicsLoaded) + Q_PROPERTY(bool supportsSsl READ supportsSsl WRITE setSupportsSsl NOTIFY basicsLoaded) public: typedef QList List; @@ -206,6 +212,79 @@ public: * @since 5.63 */ Q_SIGNAL void loadPerson(const QString &username); + /** + * Request loading of the basic information for this provider. The engine listens + * to the basicsLoaded() signal for the result, which is also the signal the respective + * properties listen to. + * + * This is fired automatically on the first attempt to read one of the properties + * which contain this basic information, and you will not need to call it as a user + * of the class (just listen to the properties, which will update when the information + * has been fetched). + * + * @note Implementation detail: All subclasses should connect to this signal + * and point it at a slot which does the actual work, if they support fetching + * this basic information (if the information is set during construction, you will + * not need to worry about this). + * + * TODO: KF6 This should be a virtual function, but can't do it now because BIC + * @see version() + * @see website() + * @see host(); + * @see contactEmail() + * @see supportsSsl() + * @since 5.85 + */ + Q_SIGNAL void loadBasics(); + /** + * @since 5.85 + */ + QString version() const; + /** + * @since 5.85 + */ + void setVersion(const QString &version); + /** + * @since 5.85 + */ + QUrl website() const; + /** + * @since 5.85 + */ + void setWebsite(const QUrl &website); + /** + * @since 5.85 + */ + QUrl host() const; + /** + * @param host The host used for this provider + * @since 5.85 + */ + void setHost(const QUrl &host); + /** + * The general contact email for this provider + * @return The general contact email for this provider + * @since 5.85 + */ + QString contactEmail() const; + /** + * Sets the general contact email address for this provider + * @param contactEmail The general contact email for this provider + * @since 5.85 + */ + void setContactEmail(const QString &contactEmail); + /** + * Whether or not the provider supports SSL connections + * @return True if the server supports SSL connections, false if not + * @since 5.85 + */ + bool supportsSsl() const; + /** + * Set whether or not the provider supports SSL connections + * @param supportsSsl True if the server supports SSL connections, false if not + * @since 5.85 + */ + void setSupportsSsl(bool supportsSsl); virtual bool userCanVote() { @@ -275,6 +354,11 @@ Q_SIGNALS: * @since 5.63 */ void personLoaded(const std::shared_ptr author); + /** + * Fired when the provider's basic information has been fetched and updated + * @since 5.85 + */ + void basicsLoaded(); /** * Fires when the provider has loaded search presets. These represent interesting -- GitLab From cad2f306a096787b3b7c3ae14924983d6b11e73f Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 1 Jul 2021 20:15:06 +0100 Subject: [PATCH 06/46] Add providerIDs as a property on Engine --- src/core/engine.cpp | 7 +++++++ src/core/engine.h | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 58198717..3597508d 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -466,6 +466,8 @@ void Engine::addProvider(QSharedPointer provider) connect(provider.data(), &Provider::signalInformation, this, [this](const QString &message) { Q_EMIT signalMessage(message); }); + connect(provider.data(), &Provider::basicsLoaded, this, &Engine::providersChanged); + Q_EMIT providersChanged(); } void Engine::providerJobStarted(KJob *job) @@ -1078,6 +1080,11 @@ QSharedPointer KNSCore::Engine::defaultProvider() const return nullptr; } +QStringList Engine::providerIDs() const +{ + return m_providers.keys(); +} + KNSCore::CommentsModel *KNSCore::Engine::commentsForEntry(const KNSCore::EntryInternal &entry) { CommentsModel *model = d->commentsModels[entry]; diff --git a/src/core/engine.h b/src/core/engine.h index dd062044..1dee1f49 100644 --- a/src/core/engine.h +++ b/src/core/engine.h @@ -78,6 +78,10 @@ class KNEWSTUFFCORE_EXPORT Engine : public QObject */ Q_PROPERTY(QString useLabel READ useLabel NOTIFY useLabelChanged) + /** + * @since 5.85 + */ + Q_PROPERTY(QStringList providerIDs READ providerIDs NOTIFY providersChanged) public: /** * Constructor. @@ -546,6 +550,20 @@ public: */ QSharedPointer defaultProvider() const; + /** + * The IDs of all providers known by this engine. Use this in combination with + * provider(const QString&) to iterate over all providers. + * @return The string IDs of all known providers + * @since 5.85 + */ + QStringList providerIDs() const; + + /** + * Fired whenever the list of providers changes + * @since 5.85 + */ + Q_SIGNAL void providersChanged(); + /** * This function will return an instance of a model which contains comments for * the entry passed to it. The model may be empty (if there are no comments for -- GitLab From f7a0246744b7d53f270611724fe1bd9588835c9b Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 1 Jul 2021 20:18:39 +0100 Subject: [PATCH 07/46] Add a ProvidersModel class so we can handle their info more gracefully --- src/core/CMakeLists.txt | 2 + src/core/providersmodel.cpp | 108 ++++++++++++++++++++++++++++++++++++ src/core/providersmodel.h | 60 ++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 src/core/providersmodel.cpp create mode 100644 src/core/providersmodel.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e02435f2..c7542f69 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -10,6 +10,7 @@ set(KNewStuffCore_SRCS installation.cpp itemsmodel.cpp provider.cpp + providersmodel.cpp security.cpp tagsfilterchecker.cpp xmlloader.cpp @@ -120,6 +121,7 @@ ecm_generate_headers(KNewStuffCore_CamelCase_HEADERS Installation ItemsModel Provider + ProvidersModel Question QuestionListener QuestionManager diff --git a/src/core/providersmodel.cpp b/src/core/providersmodel.cpp new file mode 100644 index 00000000..2be3c672 --- /dev/null +++ b/src/core/providersmodel.cpp @@ -0,0 +1,108 @@ +/* + SPDX-FileCopyrightText: 2021 Dan Leinir Turthra Jensen + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "providersmodel.h" + +namespace KNSCore +{ +class ProvidersModel::ProvidersModelPrivate +{ +public: + Engine *engine{nullptr}; + QStringList knownProviders; +}; + +ProvidersModel::ProvidersModel(QObject *parent) + : QAbstractListModel(parent) + , d(new ProvidersModelPrivate) +{ +} + +ProvidersModel::~ProvidersModel() = default; + +QHash KNSCore::ProvidersModel::roleNames() const +{ + static const QHash roles{{IdRole, "id"}, + {VersionRole, "version"}, + {WebsiteRole, "website"}, + {HostRole, "host"}, + {ContactEmailRole, "contactEmail"}, + {SupportsSslRole, "supportsSsl"}, + {ObjectRole, "object"}}; + return roles; +} + +int KNSCore::ProvidersModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + return d->knownProviders.count(); +} + +QVariant KNSCore::ProvidersModel::data(const QModelIndex &index, int role) const +{ + QVariant result; + if (checkIndex(index) && d->engine) { + QSharedPointer provider = d->engine->provider(d->knownProviders.value(index.row())); + if (provider) { + switch (role) { + case IdRole: + result.setValue(provider->id()); + break; + case VersionRole: + result.setValue(provider->version()); + break; + case WebsiteRole: + result.setValue(provider->website()); + break; + case HostRole: + result.setValue(provider->host()); + break; + case ContactEmailRole: + result.setValue(provider->contactEmail()); + break; + case SupportsSslRole: + result.setValue(provider->supportsSsl()); + break; + case ObjectRole: + result.setValue(provider.data()); + break; + default: + break; + } + } + } + return result; +} + +QObject *KNSCore::ProvidersModel::engine() const +{ + return d->engine; +} + +void KNSCore::ProvidersModel::setEngine(QObject *engine) +{ + if (d->engine != engine) { + if (d->engine) { + d->engine->disconnect(this); + } + d->engine = qobject_cast(engine); + Q_EMIT engineChanged(); + if (d->engine) { + connect(d->engine, &Engine::providersChanged, this, [this]() { + beginResetModel(); + d->knownProviders = d->engine->providerIDs(); + endResetModel(); + }); + beginResetModel(); + d->knownProviders = d->engine->providerIDs(); + endResetModel(); + } + } +} + +} diff --git a/src/core/providersmodel.h b/src/core/providersmodel.h new file mode 100644 index 00000000..65f2a649 --- /dev/null +++ b/src/core/providersmodel.h @@ -0,0 +1,60 @@ +/* + SPDX-FileCopyrightText: 2021 Dan Leinir Turthra Jensen + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef KNSCORE_PROVIDERSMODELL_H +#define KNSCORE_PROVIDERSMODELL_H + +#include + +#include "engine.h" +#include "knewstuffcore_export.h" + +#include + +namespace KNSCore +{ +/** + * @brief A model which holds information on all known Providers for a specific Engine + * + * @since 5.85 + */ +class KNEWSTUFFCORE_EXPORT ProvidersModel : public QAbstractListModel +{ + Q_OBJECT + /** + * The Engine for which this model displays Providers + */ + Q_PROPERTY(QObject *engine READ engine WRITE setEngine NOTIFY engineChanged) +public: + explicit ProvidersModel(QObject *parent = nullptr); + ~ProvidersModel() override; + + enum Roles { + IdRole = Qt::UserRole + 1, + VersionRole, + WebsiteRole, + HostRole, + ContactEmailRole, + SupportsSslRole, + ObjectRole, ///< The actual Provider object. Do not hold this locally and expect it to disappear at a moment's notice + }; + Q_ENUM(Roles) + + QHash roleNames() const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QObject *engine() const; + void setEngine(QObject *engine); + Q_SIGNAL void engineChanged(); + +private: + class ProvidersModelPrivate; + std::unique_ptr d; +}; +} + +#endif // KNSCORE_PROVIDERSMODELL_H -- GitLab From 6238766c0fa78001747a5af8b61f5636ed5592f6 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 1 Jul 2021 20:19:20 +0100 Subject: [PATCH 08/46] Expose ProvidersModel to QtQuick, and use it (basically for now) --- src/qtquick/qml/UploadPage.qml | 47 +++++++++++++++++++++++++++++++--- src/qtquick/qmlplugin.cpp | 4 +++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/qtquick/qml/UploadPage.qml b/src/qtquick/qml/UploadPage.qml index 513a1871..46b04da0 100644 --- a/src/qtquick/qml/UploadPage.qml +++ b/src/qtquick/qml/UploadPage.qml @@ -26,7 +26,7 @@ import QtQuick.Layouts 1.11 as QtLayouts import org.kde.kcm 1.2 as KCM import org.kde.kirigami 2.12 as Kirigami -import org.kde.newstuff 1.83 as NewStuff +import org.kde.newstuff 1.85 as NewStuff import "private" as Private @@ -47,9 +47,50 @@ Kirigami.ScrollablePage { NewStuff.QuestionAsker {} Private.ErrorDisplayer { engine: newStuffEngine; active: component.isCurrentPage; } QtLayouts.ColumnLayout { - QtControls.Label { + QtLayouts.Layout.fillWidth: true; + Item { QtLayouts.Layout.fillWidth: true; - text: i18nc("@knewstuff", "Something with words and whatever"); + opacity: implicitHeight > 0 ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } + visible: opacity > 0 + implicitHeight: uploaderBusy.running ? component.height / 2 : 0; + QtLayouts.Layout.preferredHeight: implicitHeight; + Behavior on QtLayouts.Layout.preferredHeight { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } + Rectangle { + anchors.fill: parent + color: Kirigami.Theme.backgroundColor + opacity: 0.7 + } + QtControls.BusyIndicator { + id: uploaderBusy + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.verticalCenter + bottomMargin: Kirigami.Units.largeSpacing + } + running: newStuffEngine.isLoading && newStuffEngine.isValid + } + QtControls.Label { + anchors { + top: parent.verticalCenter + left: parent.left + right: parent.right + margins: Kirigami.Units.largeSpacing + } + horizontalAlignment: Text.AlignHCenter + text: i18ndc("knewstuff5", "A text shown beside a busy indicator suggesting that data is being fetched", "Updating information...") + } + } + Repeater { + model: NewStuff.ProvidersModel { + engine: newStuffEngine.engine; + } + QtControls.Label { + QtLayouts.Layout.fillWidth: true; + Behavior on y { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } + wrapMode: Text.Wrap; + text: i18nc("@knewstuff", "Something with words and whatever, and this one is a provider with that there website thing %1 from ID %2", model.website, model.id); + } } } } diff --git a/src/qtquick/qmlplugin.cpp b/src/qtquick/qmlplugin.cpp index 8d1261ec..a0b1830d 100644 --- a/src/qtquick/qmlplugin.cpp +++ b/src/qtquick/qmlplugin.cpp @@ -18,6 +18,7 @@ #include "searchpresetmodel.h" #include "provider.h" +#include "providersmodel.h" #include "question.h" #include @@ -95,4 +96,7 @@ void QmlPlugins::registerTypes(const char *uri) 83, "SearchPresetModel", QStringLiteral("This should only be created by the Engine, and provides the SearchPresets available in that engine")); + + // Version 1.85 + qmlRegisterType(uri, 1, 85, "ProvidersModel"); } -- GitLab From a1048596e5bda278b0408f3c7a33ca6c0ac36b72 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 11:06:22 +0100 Subject: [PATCH 09/46] Add name and icon roles to ProvidersModel --- src/core/providersmodel.cpp | 8 ++++++++ src/core/providersmodel.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/core/providersmodel.cpp b/src/core/providersmodel.cpp index 2be3c672..723913a4 100644 --- a/src/core/providersmodel.cpp +++ b/src/core/providersmodel.cpp @@ -26,11 +26,13 @@ ProvidersModel::~ProvidersModel() = default; QHash KNSCore::ProvidersModel::roleNames() const { static const QHash roles{{IdRole, "id"}, + {NameRole, "name"}, {VersionRole, "version"}, {WebsiteRole, "website"}, {HostRole, "host"}, {ContactEmailRole, "contactEmail"}, {SupportsSslRole, "supportsSsl"}, + {IconRole, "icon"}, {ObjectRole, "object"}}; return roles; } @@ -53,6 +55,9 @@ QVariant KNSCore::ProvidersModel::data(const QModelIndex &index, int role) const case IdRole: result.setValue(provider->id()); break; + case NameRole: + result.setValue(provider->name()); + break; case VersionRole: result.setValue(provider->version()); break; @@ -68,6 +73,9 @@ QVariant KNSCore::ProvidersModel::data(const QModelIndex &index, int role) const case SupportsSslRole: result.setValue(provider->supportsSsl()); break; + case IconRole: + result.setValue(provider->icon()); + break; case ObjectRole: result.setValue(provider.data()); break; diff --git a/src/core/providersmodel.h b/src/core/providersmodel.h index 65f2a649..ba4e087b 100644 --- a/src/core/providersmodel.h +++ b/src/core/providersmodel.h @@ -34,11 +34,13 @@ public: enum Roles { IdRole = Qt::UserRole + 1, + NameRole, VersionRole, WebsiteRole, HostRole, ContactEmailRole, SupportsSslRole, + IconRole, ObjectRole, ///< The actual Provider object. Do not hold this locally and expect it to disappear at a moment's notice }; Q_ENUM(Roles) -- GitLab From 6d10178d12d41db742be8f68e8491be6202c5325 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 11:30:57 +0100 Subject: [PATCH 10/46] Make sure there's a close button as there used to be --- src/uploaddialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/uploaddialog.cpp b/src/uploaddialog.cpp index 8eeeaebc..5c837829 100644 --- a/src/uploaddialog.cpp +++ b/src/uploaddialog.cpp @@ -11,6 +11,7 @@ #include "knewstuff_debug.h" #include "uploaddialog_p.h" +#include #include #include #include @@ -27,6 +28,9 @@ bool UploadDialogPrivate::init(const QString &configfile) layout->setSpacing(0); q->setLayout(layout); q->layout()->addWidget(QWidget::createWindowContainer(view)); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, q); + QObject::connect(buttonBox, &QDialogButtonBox::rejected, q, &UploadDialog::accept); + q->layout()->addWidget(buttonBox); view->rootContext()->setContextProperty(QStringLiteral("knsrcfile"), configfile); view->setSource(QUrl(QStringLiteral("qrc:///uploaddialog.qml"))); -- GitLab From 437be76995bde7091d039f91ba144ad45c01cce3 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 11:31:24 +0100 Subject: [PATCH 11/46] Make the UploadPage component work more properly --- src/qtquick/qml/UploadPage.qml | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/qtquick/qml/UploadPage.qml b/src/qtquick/qml/UploadPage.qml index 46b04da0..494a73ea 100644 --- a/src/qtquick/qml/UploadPage.qml +++ b/src/qtquick/qml/UploadPage.qml @@ -32,7 +32,7 @@ import "private" as Private Kirigami.ScrollablePage { id: component; - title: newStuffEngine.name + title: i18nc("@knewstuff5", "Upload New Stuff: %1", newStuffEngine.name); /** * @brief The configuration file which describes the application (knsrc) * @@ -53,7 +53,7 @@ Kirigami.ScrollablePage { opacity: implicitHeight > 0 ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } visible: opacity > 0 - implicitHeight: uploaderBusy.running ? component.height / 2 : 0; + implicitHeight: uploaderBusy.running ? uploaderBusy.height + uploaderBusyInfo.height + Kirigami.Units.largeSpacing * 2 : 0; QtLayouts.Layout.preferredHeight: implicitHeight; Behavior on QtLayouts.Layout.preferredHeight { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } Rectangle { @@ -71,6 +71,7 @@ Kirigami.ScrollablePage { running: newStuffEngine.isLoading && newStuffEngine.isValid } QtControls.Label { + id: uploaderBusyInfo anchors { top: parent.verticalCenter left: parent.left @@ -85,11 +86,30 @@ Kirigami.ScrollablePage { model: NewStuff.ProvidersModel { engine: newStuffEngine.engine; } - QtControls.Label { - QtLayouts.Layout.fillWidth: true; - Behavior on y { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } - wrapMode: Text.Wrap; - text: i18nc("@knewstuff", "Something with words and whatever, and this one is a provider with that there website thing %1 from ID %2", model.website, model.id); + Kirigami.Card { + enabled: !uploaderBusy.running + banner { + title: model.name === "api.kde-look.org" ? i18ndc("knewstuff5", "The name of the KDE Store", "KDE Store") : newStuffEngine.name; + titleIcon: model.icon == "" ? "get-hot-new-stuff" : model.icon; + } + actions: [ + Kirigami.Action { + visible: model.website != "" + text: i18ndc("knewstuff5", "Text for an action which causes the specified website to be opened using the user's system default browser", "Open Website: %1", model.website) + onTriggered: Qt.openUrlExternally(model.website); + }, + Kirigami.Action { + visible: model.contactEmail != "" + text: i18ndc("knewstuff5", "Text for an action which will attempt to send an email using the user's system default email client", "Send Email To: %1", model.contactEmail) + onTriggered: Qt.openUrlExternally("mailto://" + model.contactEmail); + } + ] + contentItem: QtControls.Label { + wrapMode: Text.Wrap; + text: model.name === "api.kde-look.org" + ? i18ndc("knewstuff5", "A description of how to upload content to a generic provider", "To upload new entries, or to add content to an existing entry on the KDE Store, please open the website and log in. Once you have done this, you will be able to find the My Products entry in the menu which pops up when you click your user icon. Click on this entry to go to the product management system, where you can work on your products .") + : i18ndc("knewstuff5", "A description of how to upload content to the KDE Store specifically", "To upload new entries, or to add content to an existing entry, please open the provider's website and follow the instructions there. You will likely need to create a user and log in to a product management system, where you will need to follow the instructions for how to add. Alternatively, you might be required to contact the managers of the site directly to get new content added.") + } } } } -- GitLab From 74b3d09ad6c7908647347ae149e592b5de8b634b Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 11:51:52 +0100 Subject: [PATCH 12/46] Refactor to require an outside-provided Engine (and better docs) --- src/qtquick/qml/UploadPage.qml | 49 +++++++++++++++++----------------- src/uploaddialog.qml | 4 ++- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/qtquick/qml/UploadPage.qml b/src/qtquick/qml/UploadPage.qml index 494a73ea..223af884 100644 --- a/src/qtquick/qml/UploadPage.qml +++ b/src/qtquick/qml/UploadPage.qml @@ -9,10 +9,11 @@ * * This page shows a short guide for uploading new content to the service provided by a KNewStuff * provider. This attempts to use the information available through the provider itself, and - * otherwise just shows a link to the service's web page. + * shows a link to the service's web page, and email in case it is not the KDE Store. * * While there are not currently any services which support direct OCS API based uploading of - * new content, we still need a way to guide people to how to do this. + * new content, we still need a way to guide people to how to do this, hence this component's + * simplistic nature. * * This component is functionally equivalent to the old UploadDialog * @see KNewStuff::UploadDialog @@ -32,20 +33,25 @@ import "private" as Private Kirigami.ScrollablePage { id: component; - title: i18nc("@knewstuff5", "Upload New Stuff: %1", newStuffEngine.name); /** - * @brief The configuration file which describes the application (knsrc) - * - * The format and location of this file is found in the documentation for - * KNS3::DownloadDialog + * The NewStuffQuick Engine instance used to display content for this item. + * You can either pass in one that has already been set up (such as from a + * NewStuff.Page or NewStuff.Dialog), or you can construct a new one yourself, + * simply by doing something like this (which will use the wallpapers configuration): + \code + NewStuff.UploadPage { + engine: NewStuff.Engine { + configFile: "wallpapers.knsrc" + } + } + \endcode */ - property alias configFile: newStuffEngine.configFile; - readonly property alias engine: newStuffEngine; - NewStuff.Engine { - id: newStuffEngine; - } + required property QtObject engine + + title: i18nc("@knewstuff5", "Upload New Stuff: %1", component.engine.name); NewStuff.QuestionAsker {} - Private.ErrorDisplayer { engine: newStuffEngine; active: component.isCurrentPage; } + Private.ErrorDisplayer { engine: component.engine; active: component.isCurrentPage; } + QtLayouts.ColumnLayout { QtLayouts.Layout.fillWidth: true; Item { @@ -53,14 +59,9 @@ Kirigami.ScrollablePage { opacity: implicitHeight > 0 ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } visible: opacity > 0 - implicitHeight: uploaderBusy.running ? uploaderBusy.height + uploaderBusyInfo.height + Kirigami.Units.largeSpacing * 2 : 0; + implicitHeight: uploaderBusy.running ? uploaderBusy.height + uploaderBusyInfo.height + Kirigami.Units.largeSpacing * 4 : 0; QtLayouts.Layout.preferredHeight: implicitHeight; Behavior on QtLayouts.Layout.preferredHeight { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } - Rectangle { - anchors.fill: parent - color: Kirigami.Theme.backgroundColor - opacity: 0.7 - } QtControls.BusyIndicator { id: uploaderBusy anchors { @@ -68,7 +69,7 @@ Kirigami.ScrollablePage { bottom: parent.verticalCenter bottomMargin: Kirigami.Units.largeSpacing } - running: newStuffEngine.isLoading && newStuffEngine.isValid + running: component.engine.isLoading && component.engine.isValid } QtControls.Label { id: uploaderBusyInfo @@ -84,12 +85,12 @@ Kirigami.ScrollablePage { } Repeater { model: NewStuff.ProvidersModel { - engine: newStuffEngine.engine; + engine: component.engine.engine; } Kirigami.Card { enabled: !uploaderBusy.running banner { - title: model.name === "api.kde-look.org" ? i18ndc("knewstuff5", "The name of the KDE Store", "KDE Store") : newStuffEngine.name; + title: model.name === "api.kde-look.org" ? i18ndc("knewstuff5", "The name of the KDE Store", "KDE Store") : component.engine.name; titleIcon: model.icon == "" ? "get-hot-new-stuff" : model.icon; } actions: [ @@ -99,9 +100,9 @@ Kirigami.ScrollablePage { onTriggered: Qt.openUrlExternally(model.website); }, Kirigami.Action { - visible: model.contactEmail != "" + visible: model.contactEmail != "" && model.name != "api.kde-look.org" text: i18ndc("knewstuff5", "Text for an action which will attempt to send an email using the user's system default email client", "Send Email To: %1", model.contactEmail) - onTriggered: Qt.openUrlExternally("mailto://" + model.contactEmail); + onTriggered: Qt.openUrlExternally("mailto:" + model.contactEmail); } ] contentItem: QtControls.Label { diff --git a/src/uploaddialog.qml b/src/uploaddialog.qml index 7999726f..c201a42c 100644 --- a/src/uploaddialog.qml +++ b/src/uploaddialog.qml @@ -19,7 +19,9 @@ Kirigami.ApplicationItem { pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Auto pageStack.globalToolBar.canContainHandles: true pageStack.initialPage: NewStuff.UploadPage { - configFile: knsrcfile + engine: NewStuff.Engine { + configFile: knsrcfile + } onVisibleChanged: { if (!visible) { applicationWindow.closed(); -- GitLab From 1d334981768b9a87fece8bedfbc9d030db72bcec Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 13:59:00 +0100 Subject: [PATCH 13/46] Aaah, much better formatting (thanks!) --- src/core/providersmodel.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/core/providersmodel.cpp b/src/core/providersmodel.cpp index 723913a4..2c00c77a 100644 --- a/src/core/providersmodel.cpp +++ b/src/core/providersmodel.cpp @@ -25,15 +25,17 @@ ProvidersModel::~ProvidersModel() = default; QHash KNSCore::ProvidersModel::roleNames() const { - static const QHash roles{{IdRole, "id"}, - {NameRole, "name"}, - {VersionRole, "version"}, - {WebsiteRole, "website"}, - {HostRole, "host"}, - {ContactEmailRole, "contactEmail"}, - {SupportsSslRole, "supportsSsl"}, - {IconRole, "icon"}, - {ObjectRole, "object"}}; + static const QHash roles{ + {IdRole, "id"}, + {NameRole, "name"}, + {VersionRole, "version"}, + {WebsiteRole, "website"}, + {HostRole, "host"}, + {ContactEmailRole, "contactEmail"}, + {SupportsSslRole, "supportsSsl"}, + {IconRole, "icon"}, + {ObjectRole, "object"}, + }; return roles; } -- GitLab From cc804a4bfac8e3fbe00a0427277f6311a914aab5 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 14:01:07 +0100 Subject: [PATCH 14/46] pimpl class location correctness fix --- src/core/providersmodel.cpp | 2 +- src/core/providersmodel.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/providersmodel.cpp b/src/core/providersmodel.cpp index 2c00c77a..092f812f 100644 --- a/src/core/providersmodel.cpp +++ b/src/core/providersmodel.cpp @@ -8,7 +8,7 @@ namespace KNSCore { -class ProvidersModel::ProvidersModelPrivate +class ProvidersModelPrivate { public: Engine *engine{nullptr}; diff --git a/src/core/providersmodel.h b/src/core/providersmodel.h index ba4e087b..e0626633 100644 --- a/src/core/providersmodel.h +++ b/src/core/providersmodel.h @@ -16,6 +16,7 @@ namespace KNSCore { +class ProvidersModelPrivate; /** * @brief A model which holds information on all known Providers for a specific Engine * @@ -54,7 +55,6 @@ public: Q_SIGNAL void engineChanged(); private: - class ProvidersModelPrivate; std::unique_ptr d; }; } -- GitLab From 60d2bf9ece9bb16cba0ffed910805c22f7dff16e Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 14:23:23 +0100 Subject: [PATCH 15/46] Mark deprecated things as deprecated --- src/CMakeLists.txt | 2 +- src/uploaddialog.cpp | 4 ++++ src/uploaddialog.h | 40 +++++++++++++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd283172..656d6595 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,7 +56,7 @@ ecm_generate_export_header(KF5NewStuff VERSION ${KF_VERSION} DEPRECATED_BASE_VERSION 0 EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT} - DEPRECATION_VERSIONS 5.29 5.76 5.77 5.78 5.79 5.80 5.82 + DEPRECATION_VERSIONS 5.29 5.76 5.77 5.78 5.79 5.80 5.82 5.85 ) target_include_directories(KF5NewStuff diff --git a/src/uploaddialog.cpp b/src/uploaddialog.cpp index 5c837829..e1e06a98 100644 --- a/src/uploaddialog.cpp +++ b/src/uploaddialog.cpp @@ -72,6 +72,8 @@ bool UploadDialog::init(const QString &configfile) return d->init(configfile); } +#if KNEWSTUFF_BUILD_DEPRECATED_SINCE(5, 80) + void UploadDialog::setUploadFile(const QUrl &) { } @@ -112,6 +114,8 @@ void UploadDialog::setPreviewImageFile(uint, const QUrl &) { } +#endif + void UploadDialog::accept() { QDialog::accept(); diff --git a/src/uploaddialog.h b/src/uploaddialog.h index 6fed7501..72587b29 100644 --- a/src/uploaddialog.h +++ b/src/uploaddialog.h @@ -67,12 +67,15 @@ public: */ ~UploadDialog() override; +#if KNEWSTUFF_ENABLE_DEPRECATED_SINCE(5, 80) /** Set the file to be uploaded. This has to be set for the dialog to work, before displaying the dialog. @param payloadFile the payload data file - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setUploadFile(const QUrl &payloadFile); /** @@ -81,28 +84,36 @@ public: The name field will be left empty if no title was set. @param name the suggested name for the upload - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setUploadName(const QString &name); /** Set the suggested version displayed in the upload dialog. The user can still change this. @param version - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setVersion(const QString &version); /** Set the suggested description displayed in the upload dialog. The user can still change this. @param description - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setDescription(const QString &description); /** Set the suggested changelog displayed in the upload dialog. The user can still change this. @param version version - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setChangelog(const QString &changelog); /** @@ -111,28 +122,36 @@ public: @param number The number of the preview image to set, either 1, 2, or 3. @param file A URL to the file to be used as preview image @since 4.6 - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setPreviewImageFile(uint number, const QUrl &file); /** Enable the UI to let the user to set a price for the uploaded item. @param enabled enable the price option - it is enabled by default @since 4.5 + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setPriceEnabled(bool enabled); /** Set the suggested price displayed in the upload dialog. The user can still change this. @param version version - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setPrice(double price); /** Set the suggested rationale why this item costs something to download. The user can still change this. @param version version - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setPriceReason(const QString &reason); /** @@ -141,8 +160,11 @@ public: It does not add any new category to the list of available categories. @param category the suggested category for the upload - */ + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation + */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void selectCategory(const QString &category); +#endif public Q_SLOTS: void accept() override; -- GitLab From 07b5df5298614aacc40f8a2379eab5918b2f549b Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 14:32:59 +0100 Subject: [PATCH 16/46] Add an Upload... as a context action on NewStuff.Page --- src/qtquick/qml/Page.qml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index 6b87df04..44436fb7 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -20,7 +20,7 @@ import QtGraphicalEffects 1.11 as QtEffects import org.kde.kcm 1.2 as KCM import org.kde.kirigami 2.12 as Kirigami -import org.kde.newstuff 1.83 as NewStuff +import org.kde.newstuff 1.85 as NewStuff import "private" as Private import "private/entrygriddelegates" as EntryGridDelegates @@ -338,6 +338,13 @@ KCM.GridViewKCM { QtControls.ActionGroup.group: viewSortingActionGroup } }, + Kirigami.Action { + text: i18nd("knewstuff5", "Upload...") + iconName: "upload-media" + onTriggered: { + pageStack.push(uploadPage); + } + }, Kirigami.Action { text: i18nd("knewstuff5", "Go to...") iconName: "go-next"; @@ -438,6 +445,12 @@ KCM.GridViewKCM { id: detailsPage; NewStuff.EntryDetails { } } + Component { + id: uploadPage + NewStuff.UploadPage { + engine: newStuffEngine + } + } Item { anchors.fill: parent -- GitLab From c8349b9f740a096f2ce44339988404dec6411594 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 14:39:40 +0100 Subject: [PATCH 17/46] Add a way to disable to Upload... action for people who don't want it --- src/qtquick/qml/Page.qml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index 44436fb7..b7eb4ed5 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -36,6 +36,12 @@ KCM.GridViewKCM { property alias configFile: newStuffEngine.configFile; readonly property alias engine: newStuffEngine; + /** + * Whether or not to show the Upload... context action + * @since 5.85 + */ + property alias showUploadAction: uploadAction.visible + /** * Any generic message from the NewStuff.Engine * @param message The message to be shown to the user @@ -339,6 +345,7 @@ KCM.GridViewKCM { } }, Kirigami.Action { + id: uploadAction text: i18nd("knewstuff5", "Upload...") iconName: "upload-media" onTriggered: { -- GitLab From 98e6b1666909dd5746a00b098fff070464787fb6 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 17:30:47 +0100 Subject: [PATCH 18/46] Fully deprecate KNS3::UploadDialog now we have it exposed in NewStuff.Page --- src/uploaddialog.cpp | 9 +++++---- src/uploaddialog.h | 21 +++++++++------------ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/uploaddialog.cpp b/src/uploaddialog.cpp index e1e06a98..48531013 100644 --- a/src/uploaddialog.cpp +++ b/src/uploaddialog.cpp @@ -8,6 +8,9 @@ */ #include "uploaddialog.h" + +#if KNEWSTUFF_BUILD_DEPRECATED_SINCE(5, 80) + #include "knewstuff_debug.h" #include "uploaddialog_p.h" @@ -72,8 +75,6 @@ bool UploadDialog::init(const QString &configfile) return d->init(configfile); } -#if KNEWSTUFF_BUILD_DEPRECATED_SINCE(5, 80) - void UploadDialog::setUploadFile(const QUrl &) { } @@ -114,11 +115,11 @@ void UploadDialog::setPreviewImageFile(uint, const QUrl &) { } -#endif - void UploadDialog::accept() { QDialog::accept(); } +#endif + #include "moc_uploaddialog.cpp" diff --git a/src/uploaddialog.h b/src/uploaddialog.h index 72587b29..32335f07 100644 --- a/src/uploaddialog.h +++ b/src/uploaddialog.h @@ -43,7 +43,10 @@ class UploadDialogPrivate; * Dan Leinir Turthra Jensen (admin@leinir.dk) * * @since 4.4 + * @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which + * includes automatic integration for NewStuff.UploadPage */ +#if KNEWSTUFF_ENABLE_DEPRECATED_SINCE(5, 80) class KNEWSTUFF_EXPORT UploadDialog : public QDialog { Q_OBJECT @@ -52,22 +55,26 @@ public: Create a new upload dialog. @param parent the parent window + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which + includes automatic integration for NewStuff.UploadPage */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") explicit UploadDialog(QWidget *parent = nullptr); /** Create a new upload dialog. @param parent the parent window + @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which + includes automatic integration for NewStuff.UploadPage */ + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") explicit UploadDialog(const QString &configFile, QWidget *parent = nullptr); /** Destructor. */ ~UploadDialog() override; - -#if KNEWSTUFF_ENABLE_DEPRECATED_SINCE(5, 80) /** Set the file to be uploaded. This has to be set for the dialog to work, before displaying the dialog. @@ -75,7 +82,6 @@ public: @param payloadFile the payload data file @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setUploadFile(const QUrl &payloadFile); /** @@ -86,7 +92,6 @@ public: @param name the suggested name for the upload @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setUploadName(const QString &name); /** @@ -95,7 +100,6 @@ public: @param version @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setVersion(const QString &version); /** @@ -104,7 +108,6 @@ public: @param description @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setDescription(const QString &description); /** @@ -113,7 +116,6 @@ public: @param version version @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setChangelog(const QString &changelog); /** @@ -124,7 +126,6 @@ public: @since 4.6 @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setPreviewImageFile(uint number, const QUrl &file); /** @@ -133,7 +134,6 @@ public: @since 4.5 @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setPriceEnabled(bool enabled); /** @@ -142,7 +142,6 @@ public: @param version version @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setPrice(double price); /** @@ -151,7 +150,6 @@ public: @param version version @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void setPriceReason(const QString &reason); /** @@ -162,7 +160,6 @@ public: @param category the suggested category for the upload @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") void selectCategory(const QString &category); #endif -- GitLab From f013efc022b7218703523a2ace4244c7769ff0f8 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Fri, 2 Jul 2021 17:40:08 +0100 Subject: [PATCH 19/46] Block out all of the everything when disabling building deprecated stuff --- src/uploaddialog.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/uploaddialog.h b/src/uploaddialog.h index 32335f07..d0c7634f 100644 --- a/src/uploaddialog.h +++ b/src/uploaddialog.h @@ -11,11 +11,13 @@ #ifndef KNEWSTUFF3_UI_UPLOADDIALOG_H #define KNEWSTUFF3_UI_UPLOADDIALOG_H +#include "knewstuff_export.h" + +#if KNEWSTUFF_ENABLE_DEPRECATED_SINCE(5, 80) + #include #include -#include "knewstuff_export.h" - namespace Attica { class BaseJob; @@ -46,7 +48,6 @@ class UploadDialogPrivate; * @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which * includes automatic integration for NewStuff.UploadPage */ -#if KNEWSTUFF_ENABLE_DEPRECATED_SINCE(5, 80) class KNEWSTUFF_EXPORT UploadDialog : public QDialog { Q_OBJECT @@ -161,7 +162,6 @@ public: @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation */ void selectCategory(const QString &category); -#endif public Q_SLOTS: void accept() override; @@ -177,3 +177,5 @@ private: } #endif + +#endif -- GitLab From cf2bd49e8b52e1a7e6d91b6b0c792bfd80ab2345 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 5 Jul 2021 11:06:14 +0100 Subject: [PATCH 20/46] Add a new knsrc flag, and documentation for it (and use the result) --- README.md | 11 +++++++++++ src/core/engine.cpp | 8 ++++++++ src/core/engine.h | 20 ++++++++++++++++++++ src/qtquick/qml/Page.qml | 1 + 4 files changed, 40 insertions(+) diff --git a/README.md b/README.md index b55f5fab..a0e1e64c 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,17 @@ cases the word "Use" usually used as the label for the action which activates th To change this label, you can add an entry like the following to your knsrc file: `UseLabel=Read`, which in this case will show the word "Read" for that action instead of "Use". +### Upload Assistance + +While KNewStuff does not currently handle content uploading, the UI will attempt to guide users in how to upload new entries +on whatever provider their content is currently coming from. If you as an application developer want to explicitly not +suggest that users should do this, add an `UploadEnabled=false` entry to your configuration file. + +Not adding this entry to your configuration will cause KNewStuff to add an entry to NewStuff.Page's contextual actions +with the label "Upload...", which pushes a NewStuff.UploadPage to the stack. At this current time, this page simply +gives instructions on how uploading can be done (with specific attention paid to identifying the KDE Store and giving +more specific instructions for that). + ### Installation Control The `InstallationCommand` and `UninstallCommand` entries can be used to handle items once they have been put into their diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 3597508d..90ae6f21 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -143,6 +143,7 @@ public: Engine::BusyState busyState; QString busyMessage; QString useLabel; + bool uploadEnabled = false; }; Engine::Engine(QObject *parent) @@ -247,6 +248,8 @@ bool Engine::init(const QString &configfile) m_adoptionCommand = group.readEntry("AdoptionCommand"); d->useLabel = group.readEntry("UseLabel", i18n("Use")); Q_EMIT useLabelChanged(); + d->uploadEnabled = group.readEntry("UploadEnabled", true); + Q_EMIT uploadEnabledChanged(); m_providerFileUrl = group.readEntry("ProvidersUrl"); if (group.readEntry("UseLocalProvidersFile", "false").toLower() == QLatin1String{"true"}) { @@ -1206,3 +1209,8 @@ QString Engine::useLabel() const { return d->useLabel; } + +bool KNSCore::Engine::uploadEnabled() const +{ + return d->uploadEnabled; +} diff --git a/src/core/engine.h b/src/core/engine.h index 1dee1f49..78d1b356 100644 --- a/src/core/engine.h +++ b/src/core/engine.h @@ -78,6 +78,14 @@ class KNEWSTUFFCORE_EXPORT Engine : public QObject */ Q_PROPERTY(QString useLabel READ useLabel NOTIFY useLabelChanged) + /** + * Whether or not the configuration says that the providers are expected to support uploading. + * As it stands, this is used to determine whether or not to show the Upload... action where + * that is displayed (primarily NewStuff.Page). + * @since 5.85 + */ + Q_PROPERTY(bool uploadEnabled READ uploadEnabled NOTIFY uploadEnabledChanged) + /** * @since 5.85 */ @@ -637,6 +645,18 @@ public: */ Q_INVOKABLE void revalidateCacheEntries(); + /** + * Whether or not the configuration says that the providers are expected to support uploading. + * @return True if the providers are expected to support uploading + * @since 5.85 + */ + bool uploadEnabled() const; + + /** + * Fired when the uploadEnabled property changes + */ + Q_SIGNAL void uploadEnabledChanged(); + Q_SIGNALS: /** * Indicates a message to be added to the ui's log, or sent to a messagebox diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index b7eb4ed5..5083e761 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -348,6 +348,7 @@ KCM.GridViewKCM { id: uploadAction text: i18nd("knewstuff5", "Upload...") iconName: "upload-media" + visible: newStuffEngine.engine.uploadEnabled onTriggered: { pageStack.push(uploadPage); } -- GitLab From 3f38d5cfa5b89c32b81ff0f2e1d56992cd37eb88 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Tue, 6 Jul 2021 10:38:29 +0100 Subject: [PATCH 21/46] Make the upload dialog tester work without deprecated stuff --- tests/khotnewstuff_upload.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/khotnewstuff_upload.cpp b/tests/khotnewstuff_upload.cpp index 525ce74d..2aba649d 100644 --- a/tests/khotnewstuff_upload.cpp +++ b/tests/khotnewstuff_upload.cpp @@ -15,7 +15,7 @@ #include -#include +#include "uploaddialog.h" int main(int argc, char **argv) { @@ -26,17 +26,13 @@ int main(int argc, char **argv) QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); QApplication::setApplicationDisplayName(i18n("KHotNewStuff")); - if (i.arguments().count() > 1) { QString configfile = QLatin1String(argv[1]); QPointer dialog = new KNS3::UploadDialog(configfile); - if (i.arguments().count() > 2) { - dialog->setUploadFile(QUrl(QLatin1String(argv[2]))); - } dialog->exec(); delete dialog; } else { - std::cout << "Enter the knsrc file to use followed by a filename to upload\n"; + std::cout << "Enter the knsrc file to use\n"; return -1; } return 0; -- GitLab From 75fd41dad9e6ae5c9436224884640e7a36e9971d Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 12 Jul 2021 15:43:43 +0100 Subject: [PATCH 22/46] Use best-name for the provider card --- src/qtquick/qml/UploadPage.qml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/qtquick/qml/UploadPage.qml b/src/qtquick/qml/UploadPage.qml index 223af884..d519fece 100644 --- a/src/qtquick/qml/UploadPage.qml +++ b/src/qtquick/qml/UploadPage.qml @@ -90,7 +90,17 @@ Kirigami.ScrollablePage { Kirigami.Card { enabled: !uploaderBusy.running banner { - title: model.name === "api.kde-look.org" ? i18ndc("knewstuff5", "The name of the KDE Store", "KDE Store") : component.engine.name; + title: { + if (model.name === "api.kde-look.org") { + return i18ndc("knewstuff5", "The name of the KDE Store", "KDE Store"); + } else if (model.name !== "") { + return model.name; + } else if (component.engine.name !== "") { + return component.engine.name; + } else { + return i18ndc("knewstuff5", "An unnamed provider", "Your Provider"); + } + } titleIcon: model.icon == "" ? "get-hot-new-stuff" : model.icon; } actions: [ -- GitLab From bc527f0c9ed8a4bf8b39aa1734a9075a869fb445 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 12 Jul 2021 15:47:45 +0100 Subject: [PATCH 23/46] Bring the docs link in this knsrc test file more up to date --- tests/khotnewstuff_test.knsrc.in | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/khotnewstuff_test.knsrc.in b/tests/khotnewstuff_test.knsrc.in index e4acbf78..a5afa598 100644 --- a/tests/khotnewstuff_test.knsrc.in +++ b/tests/khotnewstuff_test.knsrc.in @@ -1,9 +1,6 @@ [KNewStuff2] ProvidersUrl=file://@CMAKE_CURRENT_SOURCE_DIR@/testdata/provider.xml LocalRegistryDir=/tmp/knewstuff2.metafiles - TargetDir=knewstuff2_test -# For more *.knsrc configuration file options, see doc/tutorial.txt. -# For a list of providers, see kstuff/hotstuff/providers in KStuff SVN. - +# For more *.knsrc configuration file options, see README.md -- GitLab From 0382d63c9be289dde58b38f3fcc34d00e13e96da Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 12 Jul 2021 16:02:32 +0100 Subject: [PATCH 24/46] Fix a couple of small loading issues for the StaticXML provider --- src/staticxml/staticxmlprovider.cpp | 34 ++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/staticxml/staticxmlprovider.cpp b/src/staticxml/staticxmlprovider.cpp index acf14706..97ffe651 100644 --- a/src/staticxml/staticxmlprovider.cpp +++ b/src/staticxml/staticxmlprovider.cpp @@ -65,14 +65,36 @@ bool StaticXmlProvider::setProviderXML(const QDomElement &xmldata) mIcon = iconurl; QDomNode n; + QLocale::Language systemLanguage = QLocale::system().language(); + QString firstName; for (n = xmldata.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.tagName() == QLatin1String("title")) { - // QString lang = e.attribute("lang"); - mName = e.text().trimmed(); - qCDebug(KNEWSTUFFCORE) << "add name for provider (" << this << "): " << e.text(); + const QString lang{e.attribute(QLatin1String("lang"))}; + bool useThisTitle{false}; + if (mName.isEmpty() && lang.isEmpty()) { + // If we have no title as yet, and we've also got no language defined, this is the default + // and name we need to set it, even if we might override it later + useThisTitle = true; + } else { + const QLocale locale(lang); + if (systemLanguage == locale.language()) { + useThisTitle = true; + } + } + if (useThisTitle) { + mName = e.text().trimmed(); + qCDebug(KNEWSTUFFCORE) << "add name for provider (" << this << "): " << e.text(); + } + if (firstName.isEmpty()) { + firstName = e.text().trimmed(); + } } } + if (mName.isEmpty()) { + // Just a fallback, because those are quite nice to have... + mName = firstName; + } // Validation if ((mNoUploadUrl.isValid()) && (mUploadUrl.isValid())) { @@ -85,6 +107,12 @@ bool StaticXmlProvider::setProviderXML(const QDomElement &xmldata) return false; } + if (mUploadUrl.isValid()) { + setWebsite(mUploadUrl); + } else { + setWebsite(mNoUploadUrl); + } + mId = mDownloadUrls[QString()].url(); if (mId.isEmpty()) { mId = mDownloadUrls[mDownloadUrls.begin().key()].url(); -- GitLab From a7d42ed0eb12068b38a91abc260f33fcaf4176df Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 12 Jul 2021 16:03:35 +0100 Subject: [PATCH 25/46] Handle absolute URLs passed to engine when using local providers --- src/core/engine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 90ae6f21..8a85840c 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -253,8 +253,9 @@ bool Engine::init(const QString &configfile) m_providerFileUrl = group.readEntry("ProvidersUrl"); if (group.readEntry("UseLocalProvidersFile", "false").toLower() == QLatin1String{"true"}) { + QString theConfig = isRelativeConfig ? actualConfig : configfile; // The local providers file is called "appname.providers", to match "appname.knsrc" - m_providerFileUrl = QLatin1String("%1.providers").arg(configFileName.left(configFileName.length() - 6)); + m_providerFileUrl = QLatin1String("file://%1.providers").arg(theConfig.left(theConfig.length() - 6)); } d->tagFilter = group.readEntry("TagFilter", QStringList(QStringLiteral("ghns_excluded!=1"))); -- GitLab From 3f7b47f7bc690acf0b929aead75234fdb164e3c9 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 12 Jul 2021 16:04:29 +0100 Subject: [PATCH 26/46] Fix up a couple of little bits for the entry test data --- tests/testdata/entry.xml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/testdata/entry.xml b/tests/testdata/entry.xml index e4b0af8b..a1023b56 100644 --- a/tests/testdata/entry.xml +++ b/tests/testdata/entry.xml @@ -1,6 +1,7 @@ + 1 Entry 1 (ghns excluded) Anonymous Guy GPL @@ -9,11 +10,12 @@ This is what it is all about. http://some.http.server/preview.png http://some.http.server/coolstuff.tar.gz - 10 + 50 0 ghns_excluded=1 + 2 Entry 2 (ghns included) Anonymous Guy GPL @@ -22,11 +24,12 @@ A short description in English (not ghns excluded). http://some.http.server/preview.png http://some.http.server/coolstuff.tar.gz - 10 + 50 0 + 3 Entry 3 (ghns excluded) Anonymous Guy GPL @@ -35,11 +38,12 @@ This is what it is all about. http://some.http.server/preview.png http://some.http.server/coolstuff.tar.gz - 10 + 100 0 ghns_excluded=1 + 4 Entry 4 (ghns included) Anonymous Guy GPL @@ -48,7 +52,7 @@ A short description in English (not ghns excluded). http://some.http.server/preview.png http://some.http.server/coolstuff.tar.gz - 10 + 100 0 -- GitLab From b120e6de4a62d6dfd2e9ecbd72866e32d9f8b468 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 12 Jul 2021 16:04:52 +0100 Subject: [PATCH 27/46] Add a multi-provider static xml test config file --- tests/testdata/entry2.xml | 59 +++++++++++++++++++++++++++++ tests/testdata/testconfig.knsrc | 5 +++ tests/testdata/testconfig.providers | 25 ++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 tests/testdata/entry2.xml create mode 100644 tests/testdata/testconfig.knsrc create mode 100644 tests/testdata/testconfig.providers diff --git a/tests/testdata/entry2.xml b/tests/testdata/entry2.xml new file mode 100644 index 00000000..0921b517 --- /dev/null +++ b/tests/testdata/entry2.xml @@ -0,0 +1,59 @@ + + + + 1 + Alternative Entry 1 (ghns excluded) + Anonymous Guy + GPL + 1.0 + 2005-06-17 + This is what it is all about. + http://some.http.server/preview.png + http://some.http.server/coolstuff.tar.gz + 100 + 0 + ghns_excluded=1 + + + 2 + Alternative Entry 2 (ghns included) + Anonymous Guy + GPL + 2.1git2 + 2018-06-05 + A short description in English (not ghns excluded). + http://some.http.server/preview.png + http://some.http.server/coolstuff.tar.gz + 70 + 0 + + + + 3 + Alternative Entry 3 (ghns excluded) + Anonymous Guy + GPL + 1.0 + 2005-06-17 + This is what it is all about. + http://some.http.server/preview.png + http://some.http.server/coolstuff.tar.gz + 50 + 0 + ghns_excluded=1 + + + 4 + Alternative Entry 4 (ghns included) + Anonymous Guy + GPL + 2.1git2 + 2018-06-05 + A short description in English (not ghns excluded). + http://some.http.server/preview.png + http://some.http.server/coolstuff.tar.gz + 10 + 0 + + + diff --git a/tests/testdata/testconfig.knsrc b/tests/testdata/testconfig.knsrc new file mode 100644 index 00000000..021459c1 --- /dev/null +++ b/tests/testdata/testconfig.knsrc @@ -0,0 +1,5 @@ +[KNewStuff3] +Name=Test Local Multi-providers +UseLocalProvidersFile=true +Categories=app/media +TargetDir=testdirectory diff --git a/tests/testdata/testconfig.providers b/tests/testdata/testconfig.providers new file mode 100644 index 00000000..110df970 --- /dev/null +++ b/tests/testdata/testconfig.providers @@ -0,0 +1,25 @@ + + + + Some cool stuff + Viele neue Dinge + + + Another Place With Coolness + Ein Andere Stelle Mit Coolness + + -- GitLab From 43508621bf151449d03884859e38d787b1c2a885 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Tue, 13 Jul 2021 10:06:27 +0100 Subject: [PATCH 28/46] We've no use-case for this, so let's just not for now --- src/qtquick/qml/Page.qml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index 5083e761..6fd7d919 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -36,12 +36,6 @@ KCM.GridViewKCM { property alias configFile: newStuffEngine.configFile; readonly property alias engine: newStuffEngine; - /** - * Whether or not to show the Upload... context action - * @since 5.85 - */ - property alias showUploadAction: uploadAction.visible - /** * Any generic message from the NewStuff.Engine * @param message The message to be shown to the user -- GitLab From b6da1495e64c93028f66f67cc60f6415c0e2d72a Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Tue, 13 Jul 2021 13:41:21 +0100 Subject: [PATCH 29/46] Actually make KNSCore::Engine handle truly relative config paths --- src/core/engine.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 8a85840c..72dd5500 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -203,23 +203,32 @@ bool Engine::init(const QString &configfile) setBusy(BusyOperation::Initializing, i18n("Initializing")); QScopedPointer conf; + QFileInfo configFileInfo(configfile); // TODO KF6: This is fallback logic for an old location for the knsrc files. This is deprecated in KF5 and should be removed in KF6 - bool isRelativeConfig = QFileInfo(configfile).isRelative(); + bool isRelativeConfig = configFileInfo.isRelative(); QString actualConfig; if (isRelativeConfig) { - // Don't do the expensive search unless the config is relative - actualConfig = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knsrcfiles/%1").arg(configfile)); + if (configfile.contains(QStringLiteral("/"))) { + // If this is the case, then we've been given an /actual/ relative path, not just the name of a knsrc file + actualConfig = configFileInfo.canonicalFilePath(); + } else { + // Don't do the expensive search unless the config is relative + actualConfig = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knsrcfiles/%1").arg(configfile)); + } } QString configFileName{configfile}; if (isRelativeConfig && d->configLocationFallback && actualConfig.isEmpty()) { conf.reset(new KConfig(configfile)); qCWarning(KNEWSTUFFCORE) << "Using a deprecated location for the knsrc file" << configfile << " - please contact the author of the software which provides this file to get it updated to use the new location"; - } else if (isRelativeConfig) { + } else if (isRelativeConfig && actualConfig.isEmpty()) { configFileName = QFileInfo(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knsrcfiles/%1").arg(configfile))).baseName(); conf.reset(new KConfig(QStringLiteral("knsrcfiles/%1").arg(configfile), KConfig::FullConfig, QStandardPaths::GenericDataLocation)); + } else if (isRelativeConfig) { + configFileName = configFileInfo.baseName(); + conf.reset(new KConfig(actualConfig)); } else { - configFileName = QFileInfo(configfile).baseName(); + configFileName = configFileInfo.baseName(); conf.reset(new KConfig(configfile)); } @@ -253,7 +262,7 @@ bool Engine::init(const QString &configfile) m_providerFileUrl = group.readEntry("ProvidersUrl"); if (group.readEntry("UseLocalProvidersFile", "false").toLower() == QLatin1String{"true"}) { - QString theConfig = isRelativeConfig ? actualConfig : configfile; + QString theConfig = actualConfig.isEmpty() ? configfile : actualConfig; // The local providers file is called "appname.providers", to match "appname.knsrc" m_providerFileUrl = QLatin1String("file://%1.providers").arg(theConfig.left(theConfig.length() - 6)); } -- GitLab From 6f9ac2ae2a1dd10c814d001fffcef9b35df01f9b Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Wed, 14 Jul 2021 13:20:37 +0100 Subject: [PATCH 30/46] Add a tooltip to the Upload... action --- src/qtquick/qml/Page.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index 6fd7d919..c939e0dc 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -341,6 +341,7 @@ KCM.GridViewKCM { Kirigami.Action { id: uploadAction text: i18nd("knewstuff5", "Upload...") + tooltip: i18nd("knewstuff5", "Learn how to add your own hot new stuff to this list") iconName: "upload-media" visible: newStuffEngine.engine.uploadEnabled onTriggered: { -- GitLab From 2bab1d12a6db839cbb08c861a0bd220b515dff11 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 15 Jul 2021 10:25:20 +0100 Subject: [PATCH 31/46] Don't manually hack a file:// in there, we have functions for that --- src/core/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 72dd5500..8844e9f9 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -264,7 +264,7 @@ bool Engine::init(const QString &configfile) if (group.readEntry("UseLocalProvidersFile", "false").toLower() == QLatin1String{"true"}) { QString theConfig = actualConfig.isEmpty() ? configfile : actualConfig; // The local providers file is called "appname.providers", to match "appname.knsrc" - m_providerFileUrl = QLatin1String("file://%1.providers").arg(theConfig.left(theConfig.length() - 6)); + m_providerFileUrl = QUrl::fromLocalFile(QLatin1String("%1.providers").arg(theConfig.left(theConfig.length() - 6))).toString(); } d->tagFilter = group.readEntry("TagFilter", QStringList(QStringLiteral("ghns_excluded!=1"))); -- GitLab From a3f36d900a954f72fef1f55eb3d068e6554f19c9 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 15 Jul 2021 10:27:11 +0100 Subject: [PATCH 32/46] Add @since --- src/core/engine.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/engine.h b/src/core/engine.h index 78d1b356..a146ab18 100644 --- a/src/core/engine.h +++ b/src/core/engine.h @@ -654,6 +654,7 @@ public: /** * Fired when the uploadEnabled property changes + * @since 5.85 */ Q_SIGNAL void uploadEnabledChanged(); -- GitLab From 042b1a9f00d5a9ccdaa08d82e35a039a9f05db91 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 15 Jul 2021 10:30:15 +0100 Subject: [PATCH 33/46] Mark m_providerFileUrl for QUrl-ification... --- src/core/engine.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/engine.h b/src/core/engine.h index a146ab18..a0d17536 100644 --- a/src/core/engine.h +++ b/src/core/engine.h @@ -777,6 +777,7 @@ private: QSharedPointer m_cache; QTimer *m_searchTimer; // The url of the file containing information about content providers + /// TODO KF6 This really wants to be turned into a QUrl (which will have implications for our public API, so not doing it just now) QString m_providerFileUrl; // Categories from knsrc file QStringList m_categories; -- GitLab From 8de070efab438e55d02c44ddf578d6f91bbb2dd4 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 15 Jul 2021 10:31:25 +0100 Subject: [PATCH 34/46] Initialise with a =, not {} --- src/uploaddialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uploaddialog.cpp b/src/uploaddialog.cpp index 48531013..9aa0e657 100644 --- a/src/uploaddialog.cpp +++ b/src/uploaddialog.cpp @@ -23,7 +23,7 @@ using namespace KNS3; bool UploadDialogPrivate::init(const QString &configfile) { - bool success{true}; + bool success = true; QQuickView *view = new QQuickView; QVBoxLayout *layout = new QVBoxLayout(); -- GitLab From 6110d10c403872e554b1361bf29890a1852fdd40 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 26 Jul 2021 09:42:58 +0100 Subject: [PATCH 35/46] Stray unintended change --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 10eb084c..ed4efb2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,9 @@ cmake_minimum_required(VERSION 3.16) set(KF_VERSION "5.85.0") # handled by release scripts -set(KF_DEP_VERSION "5.85.0") # handled by release scripts +set(KF_DEP_VERSION "5.84.0") # handled by release scripts project(KNewStuff VERSION ${KF_VERSION}) -set(CMAKE_CXX_EXTENSIONS OFF) - include(FeatureSummary) find_package(ECM 5.85.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") -- GitLab From 9b4dbd5ed88d8869ffbf1a29fb70d93d8a083bff Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 26 Jul 2021 09:45:42 +0100 Subject: [PATCH 36/46] More unintended stuff... --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed4efb2b..10eb084c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,11 @@ cmake_minimum_required(VERSION 3.16) set(KF_VERSION "5.85.0") # handled by release scripts -set(KF_DEP_VERSION "5.84.0") # handled by release scripts +set(KF_DEP_VERSION "5.85.0") # handled by release scripts project(KNewStuff VERSION ${KF_VERSION}) +set(CMAKE_CXX_EXTENSIONS OFF) + include(FeatureSummary) find_package(ECM 5.85.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") -- GitLab From 0b059450951ed1704c0988bf00c62536c7105def Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 26 Jul 2021 15:11:03 +0100 Subject: [PATCH 37/46] Force the search action to be always visible --- src/qtquick/qml/Page.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index c939e0dc..8d59aa06 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -18,7 +18,7 @@ import QtQuick.Layouts 1.11 as QtLayouts import QtGraphicalEffects 1.11 as QtEffects import org.kde.kcm 1.2 as KCM -import org.kde.kirigami 2.12 as Kirigami +import org.kde.kirigami 2.14 as Kirigami import org.kde.newstuff 1.85 as NewStuff @@ -357,6 +357,7 @@ KCM.GridViewKCM { Kirigami.Action { text: i18nd("knewstuff5", "Search...") iconName: "system-search"; + displayHint: Kirigami.DisplayHint.KeepVisible displayComponent: Kirigami.SearchField { enabled: engine.isValid id: searchField -- GitLab From ea9815baebd0253f605767607af24ac5c1593ac8 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Mon, 26 Jul 2021 16:01:40 +0100 Subject: [PATCH 38/46] Put the "Upload" button in the footer's buttonbox While this doesn't /exactly/ match what we would probably prefer, it will result in the whole thing looking consistent for everybody (that is, even on RTL devices, where the buttons will still be arranged correctly, as well as on systems where the button order is different) --- src/qtquick/qml/Dialog.qml | 9 +++++++++ src/qtquick/qml/DialogContent.qml | 1 + src/qtquick/qml/Page.qml | 12 +++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/qtquick/qml/Dialog.qml b/src/qtquick/qml/Dialog.qml index 24e3b972..7d207b18 100644 --- a/src/qtquick/qml/Dialog.qml +++ b/src/qtquick/qml/Dialog.qml @@ -124,6 +124,15 @@ QtDialogs.Dialog { } standardButtons: QtControls.DialogButtonBox.Close onRejected: component.close() + QtControls.Button { + text: i18nd("knewstuff5", "Contribute your own...") + icon.name: "upload-media" + visible: newStuffPage.engine.engine.uploadEnabled + onClicked: { + pageStack.push(uploadPage); + } + QtControls.DialogButtonBox.buttonRole: QtControls.DialogButtonBox.HelpRole + } } } } diff --git a/src/qtquick/qml/DialogContent.qml b/src/qtquick/qml/DialogContent.qml index 270f3826..dbdca235 100644 --- a/src/qtquick/qml/DialogContent.qml +++ b/src/qtquick/qml/DialogContent.qml @@ -61,6 +61,7 @@ Kirigami.ApplicationItem { pageStack.globalToolBar.canContainHandles: true pageStack.initialPage: NewStuff.Page { id: newStuffPage + showUploadAction: false function showMessage(message) { // As the Page shows something nice and friendly while loading, // there's no reason to do the passive notification thing for those. diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index 8d59aa06..29a51a46 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -59,6 +59,16 @@ KCM.GridViewKCM { */ signal errorMessage(string message); + /** + * Whether or not to show the Upload... context action + * Usually this will be bound to the engine's property which usually defines + * this, but you can override it programmatically by setting it here. + * @since 5.85 + * @see KNSCore::Engine::uploadEnabled + */ + property alias showUploadAction: uploadAction.visible + + /** * Show the details page for a specific entry. * If you call this function before the engine initialisation has been completed, @@ -343,7 +353,7 @@ KCM.GridViewKCM { text: i18nd("knewstuff5", "Upload...") tooltip: i18nd("knewstuff5", "Learn how to add your own hot new stuff to this list") iconName: "upload-media" - visible: newStuffEngine.engine.uploadEnabled + visible: root.showUploadAction && newStuffEngine.engine.uploadEnabled onTriggered: { pageStack.push(uploadPage); } -- GitLab From fdfd1d9be9b41c8a1e24a73fa7be96d9c5d1be28 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Wed, 28 Jul 2021 13:12:45 +0100 Subject: [PATCH 39/46] Only enable the upload button when it's not already the current page (also actually make the pushing work) --- src/qtquick/qml/Dialog.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/qtquick/qml/Dialog.qml b/src/qtquick/qml/Dialog.qml index 7d207b18..0d4f275d 100644 --- a/src/qtquick/qml/Dialog.qml +++ b/src/qtquick/qml/Dialog.qml @@ -21,7 +21,7 @@ import QtQuick.Layouts 1.11 as QtLayouts import QtQuick.Dialogs 1.3 as QtDialogs import org.kde.kirigami 2.7 as Kirigami -import org.kde.newstuff 1.62 as NewStuff +import org.kde.newstuff 1.85 as NewStuff QtDialogs.Dialog { id: component @@ -128,11 +128,19 @@ QtDialogs.Dialog { text: i18nd("knewstuff5", "Contribute your own...") icon.name: "upload-media" visible: newStuffPage.engine.engine.uploadEnabled + enabled: !(newStuffPage.pageStack.currentItem instanceof NewStuff.UploadPage) onClicked: { - pageStack.push(uploadPage); + newStuffPage.pageStack.push(uploadPage); } QtControls.DialogButtonBox.buttonRole: QtControls.DialogButtonBox.HelpRole } } } + Component { + id: uploadPage + NewStuff.UploadPage { + objectName: "uploadPage" + engine: newStuffPage.engine + } + } } -- GitLab From b361dd544fb09447f26441e506f37a11aafe415e Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 29 Jul 2021 13:53:41 +0100 Subject: [PATCH 40/46] Sneaky little parent for the window container --- src/uploaddialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uploaddialog.cpp b/src/uploaddialog.cpp index 9aa0e657..401c0ba8 100644 --- a/src/uploaddialog.cpp +++ b/src/uploaddialog.cpp @@ -30,7 +30,7 @@ bool UploadDialogPrivate::init(const QString &configfile) layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); q->setLayout(layout); - q->layout()->addWidget(QWidget::createWindowContainer(view)); + q->layout()->addWidget(QWidget::createWindowContainer(view, q)); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, q); QObject::connect(buttonBox, &QDialogButtonBox::rejected, q, &UploadDialog::accept); q->layout()->addWidget(buttonBox); -- GitLab From 2d0a5e2d78008773667e46618d047104f685f896 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 29 Jul 2021 13:54:22 +0100 Subject: [PATCH 41/46] Don't just blindly assume there's no protocol already --- src/attica/atticaprovider.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/attica/atticaprovider.cpp b/src/attica/atticaprovider.cpp index d60565ec..a248cb57 100644 --- a/src/attica/atticaprovider.cpp +++ b/src/attica/atticaprovider.cpp @@ -439,13 +439,23 @@ void AtticaProvider::loadedConfig(Attica::BaseJob *baseJob) Attica::Config config = job->result(); setVersion(config.version()); setSupportsSsl(config.ssl()); + setContactEmail(config.contact()); QString protocol{QStringLiteral("http")}; if (config.ssl()) { protocol = QStringLiteral("https"); } - setWebsite(QUrl(QLatin1String("%1://%2").arg(protocol).arg(config.website()))); - setHost(QUrl(QLatin1String("%1://%2").arg(protocol).arg(config.host()))); - setContactEmail(config.contact()); + // There is usually no protocol in the website and host, but in case + // there is, trust what's there + if (config.website().contains(QLatin1String("://"))) { + setWebsite(QUrl(config.website())); + } else { + setWebsite(QUrl(QLatin1String("%1://%2").arg(protocol).arg(config.website()))); + } + if (config.host().contains(QLatin1String("://"))) { + setHost(QUrl(config.host())); + } else { + setHost(QUrl(QLatin1String("%1://%2").arg(protocol).arg(config.host()))); + } } } -- GitLab From 093c22d607cb2899d5a148dc9c4ecb1dfc4c1b92 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 29 Jul 2021 14:01:58 +0100 Subject: [PATCH 42/46] Just use a normal singleshot timer (the other way 'round's way too much) --- src/core/provider.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/core/provider.cpp b/src/core/provider.cpp index 040621cd..a8f7e1d9 100644 --- a/src/core/provider.cpp +++ b/src/core/provider.cpp @@ -37,12 +37,7 @@ public: { if (!basicsGot) { basicsGot = true; - QTimer *basicsGetter = new QTimer(q); - basicsGetter->setInterval(0); - basicsGetter->setSingleShot(true); - QObject::connect(basicsGetter, &QTimer::timeout, q, &Provider::loadBasics); - QObject::connect(basicsGetter, &QTimer::timeout, basicsGetter, &QObject::deleteLater); - basicsGetter->start(); + QTimer::singleShot(0, q, &Provider::loadBasics); } }; void throttleBasics() -- GitLab From 19ffb1b0a122d461bcf365e64b15c3ba5b36038f Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 29 Jul 2021 14:05:21 +0100 Subject: [PATCH 43/46] Fix a stray @since --- src/qtquick/qml/UploadPage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qtquick/qml/UploadPage.qml b/src/qtquick/qml/UploadPage.qml index d519fece..6b4957a0 100644 --- a/src/qtquick/qml/UploadPage.qml +++ b/src/qtquick/qml/UploadPage.qml @@ -17,7 +17,7 @@ * * This component is functionally equivalent to the old UploadDialog * @see KNewStuff::UploadDialog - * @since 5.63 + * @since 5.85 */ import QtQuick 2.11 -- GitLab From 9ae58665bc838604a0b2ef6d2e245b1e32963071 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 29 Jul 2021 14:08:57 +0100 Subject: [PATCH 44/46] Better deprecation warning (so people get told what to actually do) --- src/uploaddialog.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/uploaddialog.h b/src/uploaddialog.h index d0c7634f..d94a22be 100644 --- a/src/uploaddialog.h +++ b/src/uploaddialog.h @@ -59,7 +59,11 @@ public: @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which includes automatic integration for NewStuff.UploadPage */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") + KNEWSTUFF_DEPRECATED_VERSION( + 5, + 85, + "Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which " + "includes automatic integration for NewStuff.UploadPage") explicit UploadDialog(QWidget *parent = nullptr); /** @@ -69,7 +73,11 @@ public: @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which includes automatic integration for NewStuff.UploadPage */ - KNEWSTUFF_DEPRECATED_VERSION(5, 85, "Upload functionality is no longer directly supported and needs complete reimplementation") + KNEWSTUFF_DEPRECATED_VERSION( + 5, + 85, + "Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which " + "includes automatic integration for NewStuff.UploadPage") explicit UploadDialog(const QString &configFile, QWidget *parent = nullptr); /** -- GitLab From 9e48286d0ce3b281113c6c7120341b5949a53dc3 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 29 Jul 2021 14:14:59 +0100 Subject: [PATCH 45/46] Minor rejig and variable name fixing for our config full path store --- src/core/engine.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 8844e9f9..821556ca 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -216,6 +216,9 @@ bool Engine::init(const QString &configfile) actualConfig = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knsrcfiles/%1").arg(configfile)); } } + // We need to always have a full path in some cases, and the given config name in others, so let's just + // store this in a variable with a useful name + QString configFullPath = actualConfig.isEmpty() ? configfile : actualConfig; QString configFileName{configfile}; if (isRelativeConfig && d->configLocationFallback && actualConfig.isEmpty()) { conf.reset(new KConfig(configfile)); @@ -262,9 +265,8 @@ bool Engine::init(const QString &configfile) m_providerFileUrl = group.readEntry("ProvidersUrl"); if (group.readEntry("UseLocalProvidersFile", "false").toLower() == QLatin1String{"true"}) { - QString theConfig = actualConfig.isEmpty() ? configfile : actualConfig; // The local providers file is called "appname.providers", to match "appname.knsrc" - m_providerFileUrl = QUrl::fromLocalFile(QLatin1String("%1.providers").arg(theConfig.left(theConfig.length() - 6))).toString(); + m_providerFileUrl = QUrl::fromLocalFile(QLatin1String("%1.providers").arg(configFullPath.left(configFullPath.length() - 6))).toString(); } d->tagFilter = group.readEntry("TagFilter", QStringList(QStringLiteral("ghns_excluded!=1"))); -- GitLab From 486bddd9c05713a7a3898574f5c3c7aecb758322 Mon Sep 17 00:00:00 2001 From: Dan Leinir Turthra Jensen Date: Thu, 29 Jul 2021 19:04:31 +0100 Subject: [PATCH 46/46] Rework the UploadDialog deprecation message to suggest reading the apidocs --- src/uploaddialog.h | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/uploaddialog.h b/src/uploaddialog.h index d94a22be..95415ac6 100644 --- a/src/uploaddialog.h +++ b/src/uploaddialog.h @@ -57,13 +57,10 @@ public: @param parent the parent window @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which - includes automatic integration for NewStuff.UploadPage + includes automatic integration for NewStuff.UploadPage. If the OCS backend supports upload, you can use KNSCore::AtticaHelper to do so, or implement it + manually. */ - KNEWSTUFF_DEPRECATED_VERSION( - 5, - 85, - "Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which " - "includes automatic integration for NewStuff.UploadPage") + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "See API documentation") explicit UploadDialog(QWidget *parent = nullptr); /** @@ -71,13 +68,10 @@ public: @param parent the parent window @deprecated Since 5.85, Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which - includes automatic integration for NewStuff.UploadPage + includes automatic integration for NewStuff.UploadPage. If the OCS backend supports upload, you can use KNSCore::AtticaHelper to " + "do so, or implement it manually. */ - KNEWSTUFF_DEPRECATED_VERSION( - 5, - 85, - "Upload functionality is no longer directly supported and needs complete reimplementation. Use KNS3::QtQuickDialogWrapper which " - "includes automatic integration for NewStuff.UploadPage") + KNEWSTUFF_DEPRECATED_VERSION(5, 85, "See API documentation") explicit UploadDialog(const QString &configFile, QWidget *parent = nullptr); /** -- GitLab