File indexing completed on 2025-01-05 03:53:44

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2018-07-03
0007  * Description : Web Service authentication container.
0008  *
0009  * SPDX-FileCopyrightText: 2018 by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "wsauthentication.h"
0016 
0017 // Qt includes
0018 
0019 #include <QApplication>
0020 #include <QMessageBox>
0021 #include <QFile>
0022 #include <QFileInfo>
0023 #include <QScopedPointer>
0024 
0025 // KDE includes
0026 
0027 #include <klocalizedstring.h>
0028 
0029 // Local includes
0030 
0031 #include "digikam_debug.h"
0032 #include "digikam_version.h"
0033 #include "dmetadata.h"
0034 #include "previewloadthread.h"
0035 #include "wstoolutils.h"
0036 #include "wstalker.h"
0037 #include "dbtalker.h"
0038 #include "fbtalker.h"
0039 #include "fbnewalbumdlg.h"
0040 #include "flickrtalker.h"
0041 #include "gptalker.h"
0042 #include "gdtalker.h"
0043 #include "imgurtalker.h"
0044 #include "smugtalker.h"
0045 
0046 namespace DigikamGenericUnifiedPlugin
0047 {
0048 
0049 class Q_DECL_HIDDEN WSAuthentication::Private
0050 {
0051 public:
0052 
0053     explicit Private()
0054       : wizard(0),
0055         iface(0),
0056         talker(0),
0057         ws(WSSettings::WebService::FLICKR),
0058         albumDlg(0),
0059         imagesCount(0),
0060         imagesTotal(0)
0061     {
0062     }
0063 
0064     WSWizard*               wizard;
0065     DInfoInterface*         iface;
0066 
0067     WSTalker*               talker;
0068 
0069     WSSettings::WebService  ws;
0070     QString                 serviceName;
0071 
0072     WSNewAlbumDialog*       albumDlg;
0073     QString                 currentAlbumId;
0074     WSAlbum                 baseAlbum;
0075 
0076     QStringList             tmpPath;
0077     QString                 tmpDir;
0078     unsigned int            imagesCount;
0079     unsigned int            imagesTotal;
0080     QMap<QString, QString>  imagesCaption;
0081 
0082     QList<QUrl>             transferQueue;
0083 };
0084 
0085 WSAuthentication::WSAuthentication(QWidget* const parent, DInfoInterface* const iface)
0086     : d(new Private())
0087 {
0088     d->wizard = dynamic_cast<WSWizard*>(parent);
0089 
0090     if (d->wizard)
0091     {
0092         d->iface = d->wizard->iface();
0093     }
0094     else
0095     {
0096         d->iface = iface;
0097     }
0098 
0099     /* --------------------
0100      * Temporary path to store images before uploading
0101      */
0102 
0103     d->tmpPath.clear();
0104     d->tmpDir = WSToolUtils::makeTemporaryDir(d->serviceName.toUtf8().data()).absolutePath() + QLatin1Char('/');
0105 }
0106 
0107 WSAuthentication::~WSAuthentication()
0108 {
0109     slotCancel();
0110     delete d;
0111 }
0112 
0113 void WSAuthentication::createTalker(WSSettings::WebService ws, const QString& serviceName)
0114 {
0115     d->ws          = ws;
0116     d->serviceName = serviceName;
0117     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "create " << serviceName << "talker";
0118 
0119     switch (ws)
0120     {
0121         case WSSettings::WebService::FLICKR:
0122             //d->talker = new DigikamGenericFlickrPlugin::FlickrTalker(d->wizard, serviceName, d->iface);
0123             break;
0124         case WSSettings::WebService::DROPBOX:
0125             //d->talker = new DigikamGenericDropBoxPlugin::DBTalker(d->wizard);
0126             break;
0127         case WSSettings::WebService::IMGUR:
0128             //d->talker = new DigikamGenericImgUrPlugin::ImgurTalker(d->wizard);
0129             break;
0130         case WSSettings::WebService::FACEBOOK:
0131             //d->albumDlg = new DigikamGenericFaceBookPlugin::FbNewAlbumDlg(d->wizard, d->serviceName);
0132             //d->talker   = new DigikamGenericFaceBookPlugin::FbTalker(d->wizard, d->albumDlg);
0133             break;
0134         case WSSettings::WebService::SMUGMUG:
0135             //d->talker = new DigikamGenericSmugMugPlugin::SmugTalker(d->iface, d->wizard);
0136             break;
0137         case WSSettings::WebService::GDRIVE:
0138             //d->talker = new DigikamGenericGoogleServicesPlugin::GDTalker(d->wizard);
0139             break;
0140         case WSSettings::WebService::GPHOTO:
0141             //d->talker = new DigikamGenericGoogleServicesPlugin::GPTalker(d->wizard);
0142             break;
0143     }
0144 
0145     connect(d->talker, SIGNAL(signalOpenBrowser(QUrl)),
0146             this, SIGNAL(signalOpenBrowser(QUrl)));
0147 
0148     connect(d->talker, SIGNAL(signalCloseBrowser()),
0149             this, SIGNAL(signalCloseBrowser()));
0150 
0151     connect(d->talker, SIGNAL(signalAuthenticationComplete(bool)),
0152             this, SIGNAL(signalAuthenticationComplete(bool)));
0153 
0154     connect(this, SIGNAL(signalResponseTokenReceived(QMap<QString,QString>)),
0155             d->talker, SLOT(slotResponseTokenReceived(QMap<QString,QString>)));
0156 
0157     connect(d->talker, SIGNAL(signalCreateAlbumDone(int,QString,QString)),
0158             this, SIGNAL(signalCreateAlbumDone(int,QString,QString)));
0159 
0160     connect(d->talker, SIGNAL(signalListAlbumsDone(int,QString,QList<WSAlbum>)),
0161             this, SLOT(slotListAlbumsDone(int,QString,QList<WSAlbum>)));
0162 
0163     connect(d->talker, SIGNAL(signalAddPhotoDone(int,QString)),
0164             this, SLOT(slotAddPhotoDone(int,QString)));
0165 }
0166 
0167 void WSAuthentication::cancelTalker()
0168 {
0169     if (d->talker)
0170     {
0171         d->talker->cancel();
0172     }
0173 }
0174 
0175 QString WSAuthentication::webserviceName()
0176 {
0177     return d->serviceName;
0178 }
0179 
0180 void WSAuthentication::authenticate()
0181 {
0182     d->talker->authenticate();
0183 }
0184 
0185 void WSAuthentication::reauthenticate()
0186 {
0187     d->talker->reauthenticate();
0188 }
0189 
0190 bool WSAuthentication::authenticated() const
0191 {
0192     return d->talker->linked();
0193 }
0194 
0195 void WSAuthentication::parseTreeFromListAlbums(const QList <WSAlbum>& albumsList,
0196                                                QMap<QString, AlbumSimplified>& albumTree,
0197                                                QStringList& rootAlbums)
0198 {
0199     Q_FOREACH (const WSAlbum& album, albumsList)
0200     {
0201         if (albumTree.contains(album.id))
0202         {
0203             albumTree[album.id].title      = album.title;
0204             albumTree[album.id].uploadable = album.uploadable;
0205         }
0206         else
0207         {
0208             AlbumSimplified item(album.title, album.uploadable);
0209             albumTree[album.id] = item;
0210         }
0211 
0212         if (album.isRoot)
0213         {
0214             rootAlbums << album.id;
0215         }
0216         else
0217         {
0218             if (albumTree.contains(album.parentID))
0219             {
0220                 albumTree[album.parentID].childrenIDs << album.id;
0221             }
0222             else
0223             {
0224                 AlbumSimplified parentAlbum;
0225                 parentAlbum.childrenIDs << album.id;
0226                 albumTree[album.parentID] = parentAlbum;
0227             }
0228         }
0229     }
0230 }
0231 
0232 QString WSAuthentication::getImageCaption(const QString& fileName)
0233 {
0234     DItemInfo info(d->iface->itemInfo(QUrl::fromLocalFile(fileName)));
0235 
0236     // If webservice doesn't support image titles, include it in descriptions if needed.
0237 
0238     QStringList descriptions = QStringList() << info.title() << info.comment();
0239     descriptions.removeAll(QLatin1String(""));
0240 
0241     return descriptions.join(QLatin1String("\n\n"));
0242 }
0243 
0244 void WSAuthentication::prepareForUpload()
0245 {
0246     d->transferQueue  = d->wizard->settings()->inputImages;
0247     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "prepareForUpload invoked";
0248 
0249     if (d->transferQueue.isEmpty())
0250     {
0251         Q_EMIT signalMessage(QLatin1String("transferQueue is empty"), true);
0252         return;
0253     }
0254 
0255     d->currentAlbumId = d->wizard->settings()->currentAlbumId;
0256     d->imagesTotal    = d->transferQueue.count();
0257     d->imagesCount    = 0;
0258     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "upload request got album id from widget: " << d->currentAlbumId;
0259 
0260     if (d->wizard->settings()->imagesChangeProp)
0261     {
0262         Q_FOREACH (const QUrl& imgUrl, d->transferQueue)
0263         {
0264             QString imgPath = imgUrl.toLocalFile();
0265             QImage image = PreviewLoadThread::loadHighQualitySynchronously(imgPath).copyQImage();
0266 
0267             if (image.isNull())
0268             {
0269                 image.load(imgPath);
0270             }
0271 
0272             if (image.isNull())
0273             {
0274                 Q_EMIT d->talker->signalAddPhotoDone(666, i18n("Cannot open image at %1\n", imgPath));
0275                 return;
0276             }
0277 
0278             // get temporary file name
0279             d->tmpPath << d->tmpDir + QFileInfo(imgPath).baseName().trimmed() + d->wizard->settings()->format();
0280 
0281             // rescale image if requested
0282             int maxDim = d->wizard->settings()->imageSize;
0283 
0284             if (image.width() > maxDim || image.height() > maxDim)
0285             {
0286                 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Resizing to " << maxDim;
0287                 image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio,
0288                                      Qt::SmoothTransformation);
0289             }
0290 
0291             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Saving to temp file: " << d->tmpPath.last();
0292             image.save(d->tmpPath.last(), "JPEG", d->wizard->settings()->imageCompression);
0293 
0294             // copy meta data to temporary image and get caption for image
0295             QScopedPointer<DMetadata> meta(new DMetadata);
0296             QString caption = QLatin1String("");
0297 
0298             if (meta->load(imgPath))
0299             {
0300                 meta->setItemDimensions(image.size());
0301                 meta->setItemOrientation(MetaEngine::ORIENTATION_NORMAL);
0302                 meta->setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY);
0303                 meta->save(d->tmpPath.last(), true);
0304                 caption = getImageCaption(imgPath);
0305             }
0306 
0307             d->imagesCaption[imgPath] = caption;
0308         }
0309     }
0310 }
0311 
0312 unsigned int WSAuthentication::numberItemsUpload()
0313 {
0314     return d->imagesTotal;
0315 }
0316 
0317 void WSAuthentication::uploadNextPhoto()
0318 {
0319     if (d->transferQueue.isEmpty())
0320     {
0321         Q_EMIT signalDone();
0322         return;
0323     }
0324 
0325     /*
0326      * This comparison is a little bit complicated and may seem unnecessary, but it will be useful later
0327      * when we will be able to choose to change or not image properties for EACH image.
0328      */
0329     QString imgPath = d->transferQueue.first().toLocalFile();
0330     QString tmpPath = d->tmpDir + QFileInfo(imgPath).baseName().trimmed() + d->wizard->settings()->format();
0331 
0332     if (!d->tmpPath.isEmpty() && tmpPath == d->tmpPath.first())
0333     {
0334         d->talker->addPhoto(tmpPath, d->currentAlbumId, d->imagesCaption[imgPath]);
0335         d->tmpPath.removeFirst();
0336     }
0337     else
0338     {
0339         d->talker->addPhoto(imgPath, d->currentAlbumId, d->imagesCaption[imgPath]);
0340     }
0341 }
0342 
0343 void WSAuthentication::startTransfer()
0344 {
0345     uploadNextPhoto();
0346 }
0347 
0348 void WSAuthentication::slotCancel()
0349 {
0350     // First we cancel talker
0351     cancelTalker();
0352 
0353     // Then the folder containing all temporary photos to upload will be removed after all.
0354     QDir tmpDir(d->tmpDir);
0355     if (tmpDir.exists())
0356     {
0357         tmpDir.removeRecursively();
0358     }
0359 
0360     Q_EMIT signalProgress(0);
0361 }
0362 
0363 void WSAuthentication::slotNewAlbumRequest()
0364 {
0365     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Slot create New Album";
0366 
0367     if (d->albumDlg->exec() == QDialog::Accepted)
0368     {
0369         qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Calling New Album method";
0370         d->talker->createNewAlbum();
0371     }
0372 }
0373 
0374 void WSAuthentication::slotListAlbumsRequest()
0375 {
0376     d->talker->listAlbums();
0377 }
0378 
0379 void WSAuthentication::slotListAlbumsDone(int errCode, const QString& errMsg, const QList<WSAlbum>& albumsList)
0380 {
0381     QString albumDebug = QLatin1String("");
0382 
0383     Q_FOREACH (const WSAlbum &album, albumsList)
0384     {
0385         albumDebug.append(QString::fromLatin1("%1: %2\n").arg(album.id).arg(album.title));
0386     }
0387 
0388     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received albums (errCode = " << errCode << ", errMsg = "
0389                                      << errMsg << "): " << albumDebug;
0390 
0391     if (errCode != 0)
0392     {
0393         QMessageBox::critical(QApplication::activeWindow(),
0394                               i18nc("@title:window", "%1 Call Failed",  d->serviceName),
0395                               i18n("Code: %1. %2", errCode, errMsg));
0396         return;
0397     }
0398 
0399     QMap<QString, AlbumSimplified> albumTree;
0400     QStringList rootAlbums;
0401     parseTreeFromListAlbums(albumsList, albumTree, rootAlbums);
0402 
0403     Q_EMIT signalListAlbumsDone(albumTree, rootAlbums, QLatin1String(""));
0404 }
0405 
0406 void WSAuthentication::slotAddPhotoDone(int errCode, const QString& errMsg)
0407 {
0408     if (errCode == 0)
0409     {
0410         Q_EMIT signalMessage(QDir::toNativeSeparators(d->transferQueue.first().toLocalFile()), false);
0411         d->transferQueue.removeFirst();
0412 
0413         d->imagesCount++;
0414         Q_EMIT signalProgress(d->imagesCount);
0415     }
0416     else
0417     {
0418         if (QMessageBox::question(d->wizard, i18nc("@title:window", "Uploading Failed"),
0419                 i18n("Failed to upload photo: %1\n"
0420                 "Do you want to continue?", errMsg))
0421             != QMessageBox::Yes)
0422         {
0423             d->transferQueue.clear();
0424             return;
0425         }
0426     }
0427 
0428     uploadNextPhoto();
0429 }
0430 
0431 } // namespace DigikamGenericUnifiedPlugin
0432 
0433 #include "moc_wsauthentication.cpp"