File indexing completed on 2025-01-05 03:53:26
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2005-17-26 0007 * Description : a tool to export items to Facebook web service 0008 * 0009 * SPDX-FileCopyrightText: 2005-2008 by Vardhman Jain <vardhman at gmail dot com> 0010 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2008-2009 by Luka Renko <lure at kubuntu dot org> 0012 * SPDX-FileCopyrightText: 2018 by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "fbwindow.h" 0019 0020 // Qt includes 0021 0022 #include <QWindow> 0023 #include <QComboBox> 0024 #include <QMenu> 0025 #include <QFileInfo> 0026 #include <QCheckBox> 0027 #include <QCloseEvent> 0028 #include <QSpinBox> 0029 #include <QMessageBox> 0030 #include <QPointer> 0031 0032 // KDE includes 0033 0034 #include <klocalizedstring.h> 0035 #include <ksharedconfig.h> 0036 #include <kconfiggroup.h> 0037 0038 // Local includes 0039 0040 #include "digikam_debug.h" 0041 #include "dmetadata.h" 0042 #include "ditemslist.h" 0043 #include "digikam_version.h" 0044 #include "dprogresswdg.h" 0045 #include "wstoolutils.h" 0046 #include "fbitem.h" 0047 #include "fbtalker.h" 0048 #include "fbwidget.h" 0049 #include "fbnewalbumdlg.h" 0050 #include "previewloadthread.h" 0051 0052 namespace DigikamGenericFaceBookPlugin 0053 { 0054 0055 class Q_DECL_HIDDEN FbWindow::Private 0056 { 0057 public: 0058 0059 explicit Private(QWidget* const parent, DInfoInterface* const interface) 0060 { 0061 iface = interface; 0062 widget = new FbWidget(parent, iface, QLatin1String("Facebook")); 0063 imgList = widget->imagesList(); 0064 progressBar = widget->progressBar(); 0065 changeUserBtn = widget->getChangeUserBtn(); 0066 albumsCoB = widget->getAlbumsCoB(); 0067 newAlbumBtn = widget->getNewAlbmBtn(); 0068 reloadAlbumsBtn = widget->getReloadBtn(); 0069 resizeChB = widget->getResizeCheckBox(); 0070 dimensionSpB = widget->getDimensionSpB(); 0071 imageQualitySpB = widget->getImgQualitySpB(); 0072 imagesCount = 0; 0073 imagesTotal = 0; 0074 talker = nullptr; 0075 albumDlg = nullptr; 0076 } 0077 0078 FbWidget* widget; 0079 DItemsList* imgList; 0080 QPushButton* changeUserBtn; 0081 QComboBox* albumsCoB; 0082 QPushButton* newAlbumBtn; 0083 QPushButton* reloadAlbumsBtn; 0084 QCheckBox* resizeChB; 0085 QSpinBox* dimensionSpB; 0086 QSpinBox* imageQualitySpB; 0087 DProgressWdg* progressBar; 0088 0089 unsigned int imagesCount; 0090 unsigned int imagesTotal; 0091 QString tmpDir; 0092 QString tmpPath; 0093 0094 QString profileAID; 0095 QString currentAlbumID; 0096 0097 QList<QUrl> transferQueue; 0098 0099 FbTalker* talker; 0100 FbNewAlbumDlg* albumDlg; 0101 0102 DInfoInterface* iface; 0103 }; 0104 0105 FbWindow::FbWindow(DInfoInterface* const iface, QWidget* const /*parent*/) 0106 : WSToolDialog(nullptr, QLatin1String("Facebook Export Dialog")), 0107 d (new Private(this, iface)) 0108 { 0109 d->tmpPath.clear(); 0110 d->tmpDir = WSToolUtils::makeTemporaryDir("facebook").absolutePath() + QLatin1Char('/'); 0111 0112 setMainWidget(d->widget); 0113 setModal(false); 0114 0115 setWindowTitle(i18nc("@title:window", "Export to Facebook Web Service")); 0116 0117 startButton()->setText(i18nc("@action:button", "Start Upload")); 0118 startButton()->setToolTip(i18nc("@info:tooltip, button", "Start upload to Facebook web service")); 0119 0120 d->widget->setMinimumSize(700, 500); 0121 0122 d->changeUserBtn->setStyleSheet(QLatin1String("QPushButton {background-color: " 0123 "#3b5998; color: #ffffff;}")); 0124 d->changeUserBtn->setIcon(QIcon::fromTheme(QLatin1String("dk-facebook-white"))); 0125 d->changeUserBtn->setText(i18n("Continue with Facebook")); 0126 0127 // ------------------------------------------------------------------------ 0128 0129 connect(d->imgList, SIGNAL(signalImageListChanged()), 0130 this, SLOT(slotImageListChanged())); 0131 0132 connect(d->changeUserBtn, SIGNAL(clicked()), 0133 this, SLOT(slotUserChangeRequest())); 0134 0135 connect(d->newAlbumBtn, SIGNAL(clicked()), 0136 this, SLOT(slotNewAlbumRequest())); 0137 0138 connect(d->widget, SIGNAL(reloadAlbums(long long)), 0139 this, SLOT(slotReloadAlbumsRequest(long long))); 0140 0141 connect(startButton(), SIGNAL(clicked()), 0142 this, SLOT(slotStartTransfer())); 0143 0144 connect(this, SIGNAL(finished(int)), 0145 this, SLOT(slotFinished())); 0146 0147 connect(this, SIGNAL(cancelClicked()), 0148 this, SLOT(slotCancelClicked())); 0149 0150 d->albumDlg = new FbNewAlbumDlg(this, QLatin1String("Facebook")); 0151 0152 // ------------------------------------------------------------------------ 0153 0154 d->talker = new FbTalker(this); 0155 0156 connect(d->talker, SIGNAL(signalBusy(bool)), 0157 this, SLOT(slotBusy(bool))); 0158 0159 connect(d->talker, SIGNAL(signalLoginProgress(int,int,QString)), 0160 this, SLOT(slotLoginProgress(int,int,QString))); 0161 0162 connect(d->talker, SIGNAL(signalLoginDone(int,QString)), 0163 this, SLOT(slotLoginDone(int,QString))); 0164 0165 connect(d->talker, SIGNAL(signalAddPhotoDone(int,QString)), 0166 this, SLOT(slotAddPhotoDone(int,QString))); 0167 0168 connect(d->talker, SIGNAL(signalCreateAlbumDone(int,QString,QString)), 0169 this, SLOT(slotCreateAlbumDone(int,QString,QString))); 0170 0171 connect(d->talker, SIGNAL(signalListAlbumsDone(int,QString,QList<FbAlbum>)), 0172 this, SLOT(slotListAlbumsDone(int,QString,QList<FbAlbum>))); 0173 0174 connect(d->progressBar, SIGNAL(signalProgressCanceled()), 0175 this, SLOT(slotStopAndCloseProgressBar())); 0176 0177 // ------------------------------------------------------------------------ 0178 0179 readSettings(); 0180 buttonStateChange(false); 0181 0182 authenticate(false); 0183 } 0184 0185 FbWindow::~FbWindow() 0186 { 0187 WSToolUtils::removeTemporaryDir("facebook"); 0188 0189 delete d->albumDlg; 0190 delete d->talker; 0191 delete d; 0192 } 0193 0194 void FbWindow::slotStopAndCloseProgressBar() 0195 { 0196 // Cancel the operation 0197 slotCancelClicked(); 0198 0199 // Write settings and tidy up 0200 slotFinished(); 0201 0202 // Close the dialog 0203 reject(); 0204 } 0205 0206 void FbWindow::slotFinished() 0207 { 0208 writeSettings(); 0209 d->imgList->listView()->clear(); 0210 d->progressBar->progressCompleted(); 0211 } 0212 0213 void FbWindow::slotCancelClicked() 0214 { 0215 setRejectButtonMode(QDialogButtonBox::Close); 0216 d->talker->cancel(); 0217 d->transferQueue.clear(); 0218 d->imgList->cancelProcess(); 0219 d->progressBar->hide(); 0220 d->progressBar->progressCompleted(); 0221 } 0222 0223 void FbWindow::closeEvent(QCloseEvent* e) 0224 { 0225 if (!e) 0226 { 0227 return; 0228 } 0229 0230 slotFinished(); 0231 e->accept(); 0232 } 0233 0234 void FbWindow::readSettings() 0235 { 0236 KSharedConfigPtr config = KSharedConfig::openConfig(); 0237 KConfigGroup grp = config->group(QLatin1String("Facebook Settings")); 0238 0239 if (grp.readEntry("Resize", false)) 0240 { 0241 d->resizeChB->setChecked(true); 0242 d->dimensionSpB->setEnabled(true); 0243 } 0244 else 0245 { 0246 d->resizeChB->setChecked(false); 0247 d->dimensionSpB->setEnabled(false); 0248 } 0249 0250 d->currentAlbumID = grp.readEntry("Current Album", QString()); 0251 d->dimensionSpB->setValue(grp.readEntry("Maximum Width", 1600)); 0252 d->imageQualitySpB->setValue(grp.readEntry("Image Quality", 85)); 0253 } 0254 0255 void FbWindow::writeSettings() 0256 { 0257 KSharedConfigPtr config = KSharedConfig::openConfig(); 0258 KConfigGroup grp = config->group(QLatin1String("Facebook Settings")); 0259 0260 grp.writeEntry("Current Album", d->currentAlbumID); 0261 grp.writeEntry("Resize", d->resizeChB->isChecked()); 0262 grp.writeEntry("Maximum Width", d->dimensionSpB->value()); 0263 grp.writeEntry("Image Quality", d->imageQualitySpB->value()); 0264 } 0265 0266 void FbWindow::authenticate(bool forceLogin) 0267 { 0268 d->progressBar->show(); 0269 d->progressBar->setFormat(QLatin1String("")); 0270 setRejectButtonMode(QDialogButtonBox::Cancel); 0271 0272 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Calling Login method "; 0273 0274 if (forceLogin) 0275 { 0276 d->talker->link(); 0277 } 0278 else 0279 { 0280 d->talker->readSettings(); 0281 } 0282 } 0283 0284 void FbWindow::slotLoginProgress(int step, int maxStep, const QString& label) 0285 { 0286 DProgressWdg* const progressBar = d->progressBar; 0287 0288 if (!label.isEmpty()) 0289 { 0290 progressBar->setFormat(label); 0291 } 0292 0293 if (maxStep > 0) 0294 { 0295 progressBar->setMaximum(maxStep); 0296 } 0297 0298 progressBar->setValue(step); 0299 } 0300 0301 void FbWindow::slotLoginDone(int errCode, const QString& errMsg) 0302 { 0303 setRejectButtonMode(QDialogButtonBox::Close); 0304 d->progressBar->hide(); 0305 0306 buttonStateChange(d->talker->linked()); 0307 0308 if (d->talker->linked()) 0309 { 0310 d->changeUserBtn->setText(i18n("Log Out of Facebook")); 0311 } 0312 else 0313 { 0314 d->changeUserBtn->setText(i18n("Continue with Facebook")); 0315 } 0316 0317 FbUser user = d->talker->getUser(); 0318 setProfileAID(user.id.toLongLong()); 0319 0320 d->widget->updateLabels(user.name, user.profileURL); 0321 d->albumsCoB->clear(); 0322 0323 if (errCode == 0 && d->talker->linked()) 0324 { 0325 d->albumsCoB->addItem(i18n("<i>auto create</i>"), QString()); 0326 d->talker->listAlbums(); // get albums to fill combo box 0327 } 0328 else if (errCode > 0) 0329 { 0330 QMessageBox::critical(this, QString(), i18n("Facebook Call Failed: %1\n", errMsg)); 0331 } 0332 } 0333 0334 void FbWindow::slotListAlbumsDone(int errCode, 0335 const QString& errMsg, 0336 const QList<FbAlbum>& albumsList) 0337 { 0338 QString albumDebug = QLatin1String(""); 0339 0340 Q_FOREACH (const FbAlbum& album, albumsList) 0341 { 0342 albumDebug.append(QString::fromLatin1("%1: %2\n").arg(album.id).arg(album.title)); 0343 } 0344 0345 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received albums (errCode = " << errCode << ", errMsg = " 0346 << errMsg << "): " << albumDebug; 0347 0348 if (errCode != 0) 0349 { 0350 QMessageBox::critical(this, QString(), i18n("Facebook Call Failed: %1\n", errMsg)); 0351 return; 0352 } 0353 0354 d->albumsCoB->clear(); 0355 d->albumsCoB->addItem(i18n("<i>auto create</i>"), QString()); 0356 0357 for (int i = 0 ; i < albumsList.size() ; ++i) 0358 { 0359 QString albumIcon; 0360 0361 switch (albumsList.at(i).privacy) 0362 { 0363 case FB_ME: 0364 albumIcon = QLatin1String("secure-card"); 0365 break; 0366 0367 case FB_FRIENDS: 0368 albumIcon = QLatin1String("user-identity"); 0369 break; 0370 0371 case FB_FRIENDS_OF_FRIENDS: 0372 albumIcon = QLatin1String("system-users"); 0373 break; 0374 0375 case FB_EVERYONE: 0376 albumIcon = QLatin1String("folder-html"); 0377 break; 0378 0379 case FB_CUSTOM: 0380 albumIcon = QLatin1String("configure"); 0381 break; 0382 } 0383 0384 d->albumsCoB->addItem(QIcon::fromTheme(albumIcon), 0385 albumsList.at(i).title, 0386 albumsList.at(i).id); 0387 0388 if (d->currentAlbumID == albumsList.at(i).id) 0389 { 0390 d->albumsCoB->setCurrentIndex(i + 1); 0391 } 0392 } 0393 } 0394 0395 void FbWindow::buttonStateChange(bool state) 0396 { 0397 d->newAlbumBtn->setEnabled(state); 0398 d->reloadAlbumsBtn->setEnabled(state); 0399 startButton()->setEnabled(state); 0400 } 0401 0402 void FbWindow::slotBusy(bool val) 0403 { 0404 if (val) 0405 { 0406 setCursor(Qt::WaitCursor); 0407 d->changeUserBtn->setEnabled(false); 0408 buttonStateChange(false); 0409 } 0410 else 0411 { 0412 setCursor(Qt::ArrowCursor); 0413 d->changeUserBtn->setEnabled(true); 0414 buttonStateChange(d->talker->linked()); 0415 } 0416 } 0417 0418 void FbWindow::slotUserChangeRequest() 0419 { 0420 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Slot Change User Request"; 0421 0422 if (d->talker->linked()) 0423 { 0424 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Slot User Logout"; 0425 d->talker->logout(); 0426 } 0427 else 0428 { 0429 authenticate(true); 0430 } 0431 } 0432 0433 void FbWindow::slotReloadAlbumsRequest(long long userID) 0434 { 0435 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Reload Albums Request for UID:" << userID; 0436 0437 if (userID == 0) 0438 { 0439 FbUser user = d->talker->getUser(); 0440 setProfileAID(user.id.toLongLong()); 0441 d->talker->listAlbums(); // re-get albums from current user 0442 } 0443 else 0444 { 0445 setProfileAID(userID); 0446 d->talker->listAlbums(userID); // re-get albums for friend 0447 } 0448 } 0449 0450 void FbWindow::slotNewAlbumRequest() 0451 { 0452 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Slot New Album Request"; 0453 0454 if (d->albumDlg->exec() == QDialog::Accepted) 0455 { 0456 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Calling New Album method"; 0457 FbAlbum newAlbum; 0458 d->albumDlg->getAlbumProperties(newAlbum); 0459 d->talker->createAlbum(newAlbum); 0460 } 0461 } 0462 0463 void FbWindow::slotStartTransfer() 0464 { 0465 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "slotStartTransfer invoked"; 0466 0467 d->imgList->clearProcessedStatus(); 0468 d->transferQueue = d->imgList->imageUrls(); 0469 0470 if (d->transferQueue.isEmpty()) 0471 { 0472 return; 0473 } 0474 0475 d->currentAlbumID = d->albumsCoB->itemData(d->albumsCoB->currentIndex()).toString(); 0476 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "upload request got album id from widget: " << d->currentAlbumID; 0477 d->imagesTotal = d->transferQueue.count(); 0478 d->imagesCount = 0; 0479 0480 setRejectButtonMode(QDialogButtonBox::Cancel); 0481 d->progressBar->setFormat(i18n("%v / %m")); 0482 d->progressBar->setMaximum(d->imagesTotal); 0483 d->progressBar->setValue(0); 0484 d->progressBar->show(); 0485 d->progressBar->progressScheduled(i18n("Facebook export"), true, true); 0486 d->progressBar->progressThumbnailChanged(QIcon::fromTheme(QLatin1String("dk-facebook")).pixmap(22, 22)); 0487 0488 uploadNextPhoto(); 0489 } 0490 0491 void FbWindow::setProfileAID(long long userID) 0492 { 0493 // store AID of Profile Photos album 0494 // wiki.developers.facebook.com/index.php/Profile_archive_album 0495 0496 d->profileAID = QString::number((userID << 32) + (-3 & 0xFFFFFFFF)); 0497 } 0498 0499 QString FbWindow::getImageCaption(const QString& fileName) 0500 { 0501 DItemInfo info(d->iface->itemInfo(QUrl::fromLocalFile(fileName))); 0502 0503 // Facebook doesn't support image titles. Include it in descriptions if needed. 0504 0505 QStringList descriptions = QStringList() << info.title() << info.comment(); 0506 descriptions.removeAll(QLatin1String("")); 0507 0508 return descriptions.join(QLatin1String("\n\n")); 0509 } 0510 0511 bool FbWindow::prepareImageForUpload(const QString& imgPath, QString& caption) 0512 { 0513 QImage image = PreviewLoadThread::loadHighQualitySynchronously(imgPath).copyQImage(); 0514 0515 if (image.isNull()) 0516 { 0517 image.load(imgPath); 0518 } 0519 0520 if (image.isNull()) 0521 { 0522 return false; 0523 } 0524 0525 // get temporary file name 0526 0527 d->tmpPath = d->tmpDir + QFileInfo(imgPath).baseName().trimmed() + QLatin1String(".jpg"); 0528 0529 // rescale image if requested 0530 0531 int maxDim = d->dimensionSpB->value(); 0532 0533 if (d->resizeChB->isChecked() && 0534 (image.width() > maxDim || image.height() > maxDim)) 0535 { 0536 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Resizing to " << maxDim; 0537 image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, 0538 Qt::SmoothTransformation); 0539 } 0540 0541 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Saving to temp file: " << d->tmpPath; 0542 image.save(d->tmpPath, "JPEG", d->imageQualitySpB->value()); 0543 0544 // copy meta data to temporary image 0545 0546 QScopedPointer<DMetadata> meta(new DMetadata); 0547 0548 if (meta->load(imgPath)) 0549 { 0550 caption = getImageCaption(imgPath); 0551 meta->setItemDimensions(image.size()); 0552 meta->setItemOrientation(MetaEngine::ORIENTATION_NORMAL); 0553 meta->setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY); 0554 meta->save(d->tmpPath, true); 0555 } 0556 else 0557 { 0558 caption.clear(); 0559 } 0560 0561 return true; 0562 } 0563 0564 void FbWindow::uploadNextPhoto() 0565 { 0566 if (d->transferQueue.isEmpty()) 0567 { 0568 setRejectButtonMode(QDialogButtonBox::Close); 0569 d->progressBar->hide(); 0570 d->progressBar->progressCompleted(); 0571 return; 0572 } 0573 0574 d->imgList->processing(d->transferQueue.first()); 0575 QString imgPath = d->transferQueue.first().toLocalFile(); 0576 0577 d->progressBar->setMaximum(d->imagesTotal); 0578 d->progressBar->setValue(d->imagesCount); 0579 0580 QString caption; 0581 0582 if (d->resizeChB->isChecked()) 0583 { 0584 if (!prepareImageForUpload(imgPath, caption)) 0585 { 0586 slotAddPhotoDone(666, i18n("Cannot open file")); 0587 return; 0588 } 0589 0590 d->talker->addPhoto(d->tmpPath, d->currentAlbumID, caption); 0591 } 0592 else 0593 { 0594 caption = getImageCaption(imgPath); 0595 d->tmpPath.clear(); 0596 d->talker->addPhoto(imgPath, d->currentAlbumID, caption); 0597 } 0598 } 0599 0600 void FbWindow::slotAddPhotoDone(int errCode, const QString& errMsg) 0601 { 0602 // Remove temporary file if it was used 0603 0604 if (!d->tmpPath.isEmpty()) 0605 { 0606 QFile::remove(d->tmpPath); 0607 d->tmpPath.clear(); 0608 } 0609 0610 d->imgList->processed(d->transferQueue.first(), (errCode == 0)); 0611 0612 if (errCode == 0) 0613 { 0614 d->transferQueue.removeFirst(); 0615 d->imagesCount++; 0616 } 0617 else 0618 { 0619 if (QMessageBox::question(this, i18nc("@title:window", "Uploading Failed"), 0620 i18n("Failed to upload photo into Facebook: %1\n" 0621 "Do you want to continue?", errMsg)) 0622 != QMessageBox::Yes) 0623 { 0624 setRejectButtonMode(QDialogButtonBox::Close); 0625 d->progressBar->hide(); 0626 d->progressBar->progressCompleted(); 0627 d->transferQueue.clear(); 0628 return; 0629 } 0630 } 0631 0632 uploadNextPhoto(); 0633 } 0634 0635 void FbWindow::slotCreateAlbumDone(int errCode, const QString& errMsg, const QString& newAlbumID) 0636 { 0637 if (errCode != 0) 0638 { 0639 QMessageBox::critical(this, QString(), i18n("Facebook Call Failed: %1", errMsg)); 0640 return; 0641 } 0642 0643 // reload album list and automatically select new album 0644 0645 d->currentAlbumID = newAlbumID; 0646 d->talker->listAlbums(); 0647 } 0648 0649 void FbWindow::slotImageListChanged() 0650 { 0651 startButton()->setEnabled(!(d->imgList->imageUrls().isEmpty())); 0652 } 0653 0654 } // namespace DigikamGenericFaceBookPlugin 0655 0656 #include "moc_fbwindow.cpp"