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"