File indexing completed on 2024-04-21 04:18:48
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2009 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "importer.h" 0023 0024 // Qt 0025 #include <QDateTime> 0026 #include <QTemporaryDir> 0027 #include <QUrl> 0028 0029 // KF 0030 #include <KFileItem> 0031 #include <KIO/CopyJob> 0032 #include <KIO/DeleteJob> 0033 #include <KIO/JobUiDelegate> 0034 #include <KIO/MkpathJob> 0035 #include <KIO/SimpleJob> 0036 #include <KJobWidgets> 0037 #include <KLocalizedString> 0038 0039 // stdc++ 0040 #include <memory> 0041 0042 // Local 0043 #include "gwenview_importer_debug.h" 0044 #include <QDir> 0045 #include <filenameformater.h> 0046 #include <fileutils.h> 0047 #include <lib/timeutils.h> 0048 #include <lib/urlutils.h> 0049 0050 namespace Gwenview 0051 { 0052 struct ImporterPrivate { 0053 Importer *q = nullptr; 0054 QWidget *mAuthWindow = nullptr; 0055 std::unique_ptr<FileNameFormater> mFileNameFormater; 0056 QUrl mTempImportDirUrl; 0057 QTemporaryDir *mTempImportDir = nullptr; 0058 QUrl mDestinationDirUrl; 0059 0060 /* @defgroup reset Should be reset in start() 0061 * @{ */ 0062 QList<QUrl> mUrlList; 0063 QList<QUrl> mImportedUrlList; 0064 QList<QUrl> mSkippedUrlList; 0065 QList<QUrl> mFailedUrlList; 0066 QList<QUrl> mFailedSubFolderList; 0067 int mRenamedCount; 0068 int mProgress; 0069 int mJobProgress; 0070 /* @} */ 0071 0072 QUrl mCurrentUrl; 0073 0074 bool createImportDir(const QUrl &url) 0075 { 0076 KIO::Job *job = KIO::mkpath(url, QUrl(), KIO::HideProgressInfo); 0077 KJobWidgets::setWindow(job, mAuthWindow); 0078 if (!job->exec()) { 0079 Q_EMIT q->error(i18n("Could not create destination folder.")); 0080 return false; 0081 } 0082 0083 // Check if local and fast url. The check for fast url is needed because 0084 // otherwise the retrieved date will not be correct: see implementation 0085 // of TimeUtils::dateTimeForFileItem 0086 if (UrlUtils::urlIsFastLocalFile(url)) { 0087 QString tempDirPath = url.toLocalFile() + "/.gwenview_importer-XXXXXX"; 0088 mTempImportDir = new QTemporaryDir(tempDirPath); 0089 } else { 0090 mTempImportDir = new QTemporaryDir(); 0091 } 0092 0093 if (!mTempImportDir->isValid()) { 0094 Q_EMIT q->error(i18n("Could not create temporary upload folder.")); 0095 return false; 0096 } 0097 0098 mTempImportDirUrl = QUrl::fromLocalFile(mTempImportDir->path() + QLatin1Char('/')); 0099 if (!mTempImportDirUrl.isValid()) { 0100 Q_EMIT q->error(i18n("Could not create temporary upload folder.")); 0101 return false; 0102 } 0103 0104 return true; 0105 } 0106 0107 void importNext() 0108 { 0109 if (mUrlList.empty()) { 0110 q->finalizeImport(); 0111 return; 0112 } 0113 mCurrentUrl = mUrlList.takeFirst(); 0114 QUrl dst = mTempImportDirUrl; 0115 dst.setPath(dst.path() + mCurrentUrl.fileName()); 0116 KIO::Job *job = KIO::copy(mCurrentUrl, dst, KIO::HideProgressInfo | KIO::Overwrite); 0117 KJobWidgets::setWindow(job, mAuthWindow); 0118 QObject::connect(job, &KJob::result, q, &Importer::slotCopyDone); 0119 QObject::connect(job, SIGNAL(percent(KJob *, ulong)), q, SLOT(slotPercent(KJob *, ulong))); 0120 } 0121 0122 void renameImportedUrl(const QUrl &src) 0123 { 0124 QUrl dst = mDestinationDirUrl; 0125 QString fileName; 0126 if (mFileNameFormater.get()) { 0127 KFileItem item(src); 0128 item.setDelayedMimeTypes(true); 0129 // Get the document time, but do not cache the result because the 0130 // 'src' url is temporary: if we import "foo/image.jpg" and 0131 // "bar/image.jpg", both images will be temporarily saved in the 0132 // 'src' url. 0133 const QDateTime dateTime = TimeUtils::dateTimeForFileItem(item, TimeUtils::SkipCache); 0134 fileName = mFileNameFormater->format(src, dateTime); 0135 } else { 0136 fileName = src.fileName(); 0137 } 0138 dst.setPath(dst.path() + QLatin1Char('/') + fileName); 0139 0140 FileUtils::RenameResult result; 0141 // Create additional subfolders if needed (e.g. when extra slashes in FileNameFormater) 0142 QUrl subFolder = dst.adjusted(QUrl::RemoveFilename); 0143 KIO::Job *job = KIO::mkpath(subFolder, QUrl(), KIO::HideProgressInfo); 0144 KJobWidgets::setWindow(job, mAuthWindow); 0145 if (!job->exec()) { // if subfolder creation fails 0146 qCWarning(GWENVIEW_IMPORTER_LOG) << "Could not create subfolder:" << subFolder; 0147 if (!mFailedSubFolderList.contains(subFolder)) { 0148 mFailedSubFolderList << subFolder; 0149 } 0150 result = FileUtils::RenameFailed; 0151 } else { // if subfolder creation succeeds 0152 result = FileUtils::rename(src, dst, mAuthWindow); 0153 } 0154 0155 switch (result) { 0156 case FileUtils::RenamedOK: 0157 mImportedUrlList << mCurrentUrl; 0158 break; 0159 case FileUtils::RenamedUnderNewName: 0160 mRenamedCount++; 0161 mImportedUrlList << mCurrentUrl; 0162 break; 0163 case FileUtils::Skipped: 0164 mSkippedUrlList << mCurrentUrl; 0165 break; 0166 case FileUtils::RenameFailed: 0167 mFailedUrlList << mCurrentUrl; 0168 qCWarning(GWENVIEW_IMPORTER_LOG) << "Rename failed for" << mCurrentUrl; 0169 } 0170 q->advance(); 0171 importNext(); 0172 } 0173 }; 0174 0175 Importer::Importer(QWidget *parent) 0176 : QObject(parent) 0177 , d(new ImporterPrivate) 0178 { 0179 d->q = this; 0180 d->mAuthWindow = parent; 0181 } 0182 0183 Importer::~Importer() 0184 { 0185 delete d; 0186 } 0187 0188 void Importer::setAutoRenameFormat(const QString &format) 0189 { 0190 if (format.isEmpty()) { 0191 d->mFileNameFormater.reset(nullptr); 0192 } else { 0193 d->mFileNameFormater = std::make_unique<FileNameFormater>(format); 0194 } 0195 } 0196 0197 void Importer::start(const QList<QUrl> &list, const QUrl &destination) 0198 { 0199 d->mDestinationDirUrl = destination; 0200 d->mUrlList = list; 0201 d->mImportedUrlList.clear(); 0202 d->mSkippedUrlList.clear(); 0203 d->mFailedUrlList.clear(); 0204 d->mFailedSubFolderList.clear(); 0205 d->mRenamedCount = 0; 0206 d->mProgress = 0; 0207 d->mJobProgress = 0; 0208 0209 emitProgressChanged(); 0210 Q_EMIT maximumChanged(d->mUrlList.count() * 100); 0211 0212 if (!d->createImportDir(destination)) { 0213 qCWarning(GWENVIEW_IMPORTER_LOG) << "Could not create import dir"; 0214 return; 0215 } 0216 d->importNext(); 0217 } 0218 0219 void Importer::slotCopyDone(KJob *_job) 0220 { 0221 auto job = static_cast<KIO::CopyJob *>(_job); 0222 const QUrl url = job->destUrl(); 0223 if (job->error()) { 0224 // Add document to failed url list and proceed with next one 0225 d->mFailedUrlList << d->mCurrentUrl; 0226 advance(); 0227 d->importNext(); 0228 return; 0229 } 0230 0231 d->renameImportedUrl(url); 0232 } 0233 0234 void Importer::finalizeImport() 0235 { 0236 delete d->mTempImportDir; 0237 Q_EMIT importFinished(); 0238 } 0239 0240 void Importer::advance() 0241 { 0242 ++d->mProgress; 0243 d->mJobProgress = 0; 0244 emitProgressChanged(); 0245 } 0246 0247 void Importer::slotPercent(KJob *, unsigned long percent) 0248 { 0249 d->mJobProgress = percent; 0250 emitProgressChanged(); 0251 } 0252 0253 void Importer::emitProgressChanged() 0254 { 0255 Q_EMIT progressChanged(d->mProgress * 100 + d->mJobProgress); 0256 } 0257 0258 QList<QUrl> Importer::importedUrlList() const 0259 { 0260 return d->mImportedUrlList; 0261 } 0262 0263 QList<QUrl> Importer::skippedUrlList() const 0264 { 0265 return d->mSkippedUrlList; 0266 } 0267 0268 QList<QUrl> Importer::failedUrlList() const 0269 { 0270 return d->mFailedUrlList; 0271 } 0272 0273 QList<QUrl> Importer::failedSubFolderList() const 0274 { 0275 return d->mFailedSubFolderList; 0276 } 0277 0278 int Importer::renamedCount() const 0279 { 0280 return d->mRenamedCount; 0281 } 0282 0283 } // namespace 0284 0285 #include "moc_importer.cpp"