File indexing completed on 2024-05-12 16:42:09

0001 /*
0002     SPDX-FileCopyrightText: 2000-2002 Michael Edwardes <mte@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "knewbankdlg.h"
0008 
0009 // ----------------------------------------------------------------------------
0010 // QT Includes
0011 
0012 #include <QPushButton>
0013 #include <QTemporaryFile>
0014 #include <QTimer>
0015 #include <QDesktopServices>
0016 #include <QRegularExpression>
0017 
0018 // ----------------------------------------------------------------------------
0019 // KDE Includes
0020 
0021 #include <KMessageBox>
0022 #include <KLineEdit>
0023 #include <kguiutils.h>
0024 #include <KLocalizedString>
0025 #include <KIO/Scheduler>
0026 #include <KIO/Job>
0027 #include <KIOGui/KIO/FavIconRequestJob>
0028 
0029 // ----------------------------------------------------------------------------
0030 // Project Includes
0031 
0032 #include "ui_knewbankdlg.h"
0033 
0034 #include "mymoneyinstitution.h"
0035 #include "kmymoneyutils.h"
0036 #include "icons.h"
0037 
0038 #include <errno.h>
0039 
0040 class KNewBankDlgPrivate
0041 {
0042     Q_DISABLE_COPY(KNewBankDlgPrivate)
0043 
0044 public:
0045     KNewBankDlgPrivate() :
0046         ui(new Ui::KNewBankDlg)
0047     {
0048         m_iconLoadTimer.setSingleShot(true);
0049     }
0050 
0051     ~KNewBankDlgPrivate()
0052     {
0053         delete ui;
0054     }
0055 
0056     Ui::KNewBankDlg*                  ui;
0057     MyMoneyInstitution                m_institution;
0058     QTimer                            m_iconLoadTimer;
0059     QPointer<KIO::FavIconRequestJob>  m_favIconJob;
0060     QIcon                             m_favIcon;
0061     QString                           m_iconName;
0062     QUrl                              m_url;
0063 };
0064 
0065 KNewBankDlg::KNewBankDlg(MyMoneyInstitution& institution, QWidget *parent) :
0066     QDialog(parent),
0067     d_ptr(new KNewBankDlgPrivate)
0068 {
0069     Q_D(KNewBankDlg);
0070     d->ui->setupUi(this);
0071     d->m_institution = institution;
0072     setModal(true);
0073 
0074     d->ui->nameEdit->setFocus();
0075     d->ui->nameEdit->setText(institution.name());
0076     d->ui->cityEdit->setText(institution.city());
0077     d->ui->streetEdit->setText(institution.street());
0078     d->ui->postcodeEdit->setText(institution.postcode());
0079     d->ui->telephoneEdit->setText(institution.telephone());
0080     d->ui->sortCodeEdit->setText(institution.sortcode());
0081     d->ui->bicEdit->setText(institution.value(QStringLiteral("bic")));
0082     d->ui->urlEdit->setText(institution.value(QStringLiteral("url")));
0083 
0084     if (!institution.value(QStringLiteral("icon")).isEmpty()) {
0085         d->m_favIcon = Icons::loadIconFromApplicationCache(institution.value(QStringLiteral("icon")));
0086     }
0087     if (!d->m_favIcon.isNull()) {
0088         d->ui->iconButton->setEnabled(true);
0089         d->ui->iconButton->setIcon(d->m_favIcon);
0090     }
0091 
0092     d->ui->messageWidget->hide();
0093 
0094     connect(d->ui->buttonBox, &QDialogButtonBox::accepted, this, &KNewBankDlg::okClicked);
0095     connect(d->ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0096     connect(d->ui->nameEdit, &QLineEdit::textChanged, this, &KNewBankDlg::institutionNameChanged);
0097     connect(d->ui->urlEdit, &QLineEdit::textChanged, this, &KNewBankDlg::slotUrlChanged);
0098     connect(&d->m_iconLoadTimer, &QTimer::timeout, this, &KNewBankDlg::slotLoadIcon);
0099     connect(d->ui->iconButton, &QToolButton::pressed, this,
0100     [=] {
0101         QUrl url;
0102         url.setUrl(QString::fromLatin1("https://%1").arg(d->ui->urlEdit->text()));
0103         QDesktopServices::openUrl(url);
0104     });
0105 
0106     institutionNameChanged(d->ui->nameEdit->text());
0107     slotUrlChanged(d->ui->urlEdit->text());
0108 
0109     auto requiredFields = new KMandatoryFieldGroup(this);
0110     requiredFields->setOkButton(d->ui->buttonBox->button(QDialogButtonBox::Ok)); // button to be enabled when all fields present
0111     requiredFields->add(d->ui->nameEdit);
0112 }
0113 
0114 void KNewBankDlg::institutionNameChanged(const QString &_text)
0115 {
0116     Q_D(KNewBankDlg);
0117     d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!_text.isEmpty());
0118 }
0119 
0120 KNewBankDlg::~KNewBankDlg()
0121 {
0122     Q_D(KNewBankDlg);
0123     delete d;
0124 }
0125 
0126 void KNewBankDlg::okClicked()
0127 {
0128     Q_D(KNewBankDlg);
0129     if (d->ui->nameEdit->text().isEmpty()) {
0130         KMessageBox::information(this, i18n("The institution name field is empty.  Please enter the name."), i18n("Adding New Institution"));
0131         d->ui->nameEdit->setFocus();
0132         return;
0133     }
0134 
0135     d->m_institution.setName(d->ui->nameEdit->text());
0136     d->m_institution.setTown(d->ui->cityEdit->text());
0137     d->m_institution.setStreet(d->ui->streetEdit->text());
0138     d->m_institution.setPostcode(d->ui->postcodeEdit->text());
0139     d->m_institution.setTelephone(d->ui->telephoneEdit->text());
0140     d->m_institution.setSortcode(d->ui->sortCodeEdit->text());
0141     d->m_institution.setValue(QStringLiteral("bic"), d->ui->bicEdit->text());
0142     d->m_institution.setValue(QStringLiteral("url"), d->ui->urlEdit->text());
0143     d->m_institution.deletePair(QStringLiteral("icon"));
0144 
0145     if (d->ui->iconButton->isEnabled()) {
0146         d->m_institution.setValue(QStringLiteral("icon"), d->m_iconName);
0147         Icons::storeIconInApplicationCache(d->m_iconName, d->m_favIcon);
0148     }
0149     accept();
0150 }
0151 
0152 const MyMoneyInstitution& KNewBankDlg::institution()
0153 {
0154     Q_D(KNewBankDlg);
0155     return d->m_institution;
0156 }
0157 
0158 void KNewBankDlg::newInstitution(MyMoneyInstitution& institution)
0159 {
0160     institution.clearId();
0161     QPointer<KNewBankDlg> dlg = new KNewBankDlg(institution);
0162     if (dlg->exec() == QDialog::Accepted && dlg != 0) {
0163         institution = dlg->institution();
0164         KMyMoneyUtils::newInstitution(institution);
0165     }
0166     delete dlg;
0167 }
0168 
0169 void KNewBankDlg::slotUrlChanged(const QString& newUrl)
0170 {
0171     Q_D(KNewBankDlg);
0172 
0173     // remove a possible leading protocol since we only provide https for now
0174     QRegularExpression protocol(QStringLiteral("^[a-zA-Z]+://(?<url>.*)"), QRegularExpression::CaseInsensitiveOption);
0175     QRegularExpressionMatch matcher = protocol.match(newUrl);
0176     if (matcher.hasMatch()) {
0177         d->ui->urlEdit->setText(matcher.captured(QStringLiteral("url")));
0178         d->ui->messageWidget->setText(i18nc("@info:usagetip", "The protocol part has been removed by KMyMoney because it is fixed to https for security reasons."));
0179         d->ui->messageWidget->setMessageType(KMessageWidget::Information);
0180         d->ui->messageWidget->animatedShow();
0181     }
0182     d->m_iconLoadTimer.start(200);
0183 }
0184 
0185 void KNewBankDlg::slotLoadIcon()
0186 {
0187     Q_D(KNewBankDlg);
0188 
0189     // if currently a check is running, retry later
0190     if (d->m_favIconJob) {
0191         d->m_iconLoadTimer.start(200);
0192         return;
0193     }
0194 
0195     const auto path = d->ui->urlEdit->text();
0196     QRegularExpression urlRe(QStringLiteral("^(.*\\.)?[^\\.]{2,}\\.[a-z]{2,}"), QRegularExpression::CaseInsensitiveOption);
0197     QRegularExpressionMatch matcher = urlRe.match(path);
0198     d->ui->iconButton->setEnabled(false);
0199 
0200     if (matcher.hasMatch()) {
0201         d->ui->iconButton->setEnabled(true);
0202         d->m_url = QUrl(QString::fromLatin1("https://%1").arg(path));
0203         KIO::Scheduler::checkSlaveOnHold(true);
0204         d->m_favIconJob = new KIO::FavIconRequestJob(d->m_url);
0205         connect(d->m_favIconJob, &KIO::FavIconRequestJob::result, this, &KNewBankDlg::slotIconLoaded);
0206         // we force to end the job after 1 second to avoid blocking this mechanism in case the thing fails
0207         QTimer::singleShot(1000, this, &KNewBankDlg::killIconLoad);
0208     }
0209 }
0210 
0211 void KNewBankDlg::killIconLoad()
0212 {
0213     Q_D(KNewBankDlg);
0214     if (d->m_favIconJob) {
0215         d->m_favIconJob->kill();
0216         d->m_favIconJob->deleteLater();
0217     }
0218 }
0219 
0220 void KNewBankDlg::slotIconLoaded(KJob* job)
0221 {
0222     Q_D(KNewBankDlg);
0223 
0224     switch(job->error()) {
0225     case ECONNREFUSED:
0226         // There is an answer from the server, but no favicon. In case we
0227         // already have one, we keep it
0228         d->ui->iconButton->setEnabled(true);
0229         d->m_favIcon = Icons::get(Icons::Icon::Bank);
0230         d->m_iconName = QStringLiteral("enum:Bank");
0231         break;
0232     case 0:
0233         // There is an answer from the server, and the favicon is found
0234         d->ui->iconButton->setEnabled(true);
0235         d->m_favIcon = QIcon(dynamic_cast<KIO::FavIconRequestJob*>(job)->iconFile());
0236         d->m_iconName = QStringLiteral("favicon:%1").arg(d->m_url.host());
0237         break;
0238     default:
0239         // There is problem with the URL from
0240         qDebug() << "KIO::FavIconRequestJob error" << job->error();
0241     // intentional fall through
0242 
0243     case EALREADY:    // invalid URL, no server response
0244         d->ui->iconButton->setEnabled(false);
0245         d->m_favIcon = QIcon();
0246         d->m_iconName.clear();
0247         break;
0248     }
0249     d->ui->iconButton->setIcon(d->m_favIcon);
0250 }