File indexing completed on 2024-05-19 05:05:44

0001 /***************************************************************************
0002  *   SPDX-License-Identifier: GPL-2.0-or-later
0003  *                                                                         *
0004  *   SPDX-FileCopyrightText: 2004-2020 Thomas Fischer <fischer@unix-ag.uni-kl.de>
0005  *                                                                         *
0006  *   This program is free software; you can redistribute it and/or modify  *
0007  *   it under the terms of the GNU General Public License as published by  *
0008  *   the Free Software Foundation; either version 2 of the License, or     *
0009  *   (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, see <https://www.gnu.org/licenses/>. *
0018  ***************************************************************************/
0019 
0020 #include "associatedfiles.h"
0021 
0022 #include <QFileInfo>
0023 #include <QDir>
0024 
0025 #include <KIO/CopyJob>
0026 #include <KJobWidgets>
0027 
0028 #include <Preferences>
0029 #include "logging_networking.h"
0030 
0031 QString AssociatedFiles::relativeFilename(const QUrl &documentUrl, const QUrl &baseUrl) {
0032     if (!documentUrl.isValid()) {
0033         /// Document URL has to point to a file location or URL but is invalid
0034         return QString();
0035     }
0036     if (!baseUrl.isValid() || baseUrl.isRelative()) {
0037         /// Base URL has to point to an absolute file location or URL and must be valid
0038         return documentUrl.url(QUrl::PreferLocalFile);
0039     }
0040     if (documentUrl.scheme() != baseUrl.scheme() || (documentUrl.scheme() != QStringLiteral("file") && documentUrl.host() != baseUrl.host())) {
0041         /// Document URL and base URL do not match (protocol, host, ...)
0042         return documentUrl.url(QUrl::PreferLocalFile);
0043     }
0044 
0045     /// First, resolve the provided document URL to an absolute URL
0046     /// using the given base URL
0047     QUrl internaldocumentUrl = documentUrl;
0048     if (internaldocumentUrl.isRelative())
0049         internaldocumentUrl = baseUrl.resolved(internaldocumentUrl);
0050 
0051     /// Get the absolute path of the base URL
0052     const QString baseUrlDirectory = QFileInfo(baseUrl.path()).absolutePath();
0053 
0054     /// Let QDir calculate the relative directory
0055     return QDir(baseUrlDirectory).relativeFilePath(internaldocumentUrl.path());
0056 }
0057 
0058 QString AssociatedFiles::absoluteFilename(const QUrl &documentUrl, const QUrl &baseUrl) {
0059     if (!documentUrl.isValid()) {
0060         /// Document URL has to point to a file location or URL but is invalid
0061         return QString();
0062     }
0063     if (documentUrl.isRelative() && (!baseUrl.isValid() || baseUrl.isRelative())) {
0064         /// Base URL has to point to an absolute, valid file location or URL if the document URL is relative
0065         return documentUrl.url(QUrl::PreferLocalFile);
0066     }
0067     if (!documentUrl.isRelative() && (documentUrl.scheme() != baseUrl.scheme() || (documentUrl.scheme() != QStringLiteral("file") && documentUrl.host() != baseUrl.host()))) {
0068         /// Document URL and base URL do not match (protocol, host, ...), but necessary if the document URL is relative
0069         return documentUrl.url(QUrl::PreferLocalFile);
0070     }
0071 
0072     /// Resolve the provided document URL to an absolute URL
0073     /// using the given base URL
0074     QUrl internaldocumentUrl = documentUrl;
0075     if (internaldocumentUrl.isRelative())
0076         internaldocumentUrl = baseUrl.resolved(internaldocumentUrl);
0077 
0078     return internaldocumentUrl.url(QUrl::PreferLocalFile);
0079 }
0080 
0081 QString AssociatedFiles::insertUrl(const QUrl &documentUrl, QSharedPointer<Entry> &entry, const File *bibTeXFile, PathType pathType) {
0082     const QString finalUrl = computeAssociateString(documentUrl, bibTeXFile, pathType);
0083 
0084     bool alreadyContained = false;
0085     for (QMap<QString, Value>::ConstIterator it = entry->constBegin(); !alreadyContained && it != entry->constEnd(); ++it) {
0086         const Value v = it.value();
0087         for (Value::ConstIterator vit = v.constBegin(); !alreadyContained && vit != v.constEnd(); ++vit) {
0088             if (PlainTextValue::text(*vit) == finalUrl)
0089                 alreadyContained = true;
0090         }
0091     }
0092     if (!alreadyContained) {
0093         const QString field = documentUrl.isLocalFile() ? (Preferences::instance().bibliographySystem() == Preferences::BibliographySystem::BibTeX ? Entry::ftLocalFile : Entry::ftFile) : Entry::ftUrl;
0094         Value value = entry->value(field);
0095         value.append(QSharedPointer<VerbatimText>(new VerbatimText(finalUrl)));
0096         entry->insert(field, value);
0097     }
0098 
0099     return finalUrl;
0100 }
0101 
0102 QString AssociatedFiles::computeAssociateString(const QUrl &documentUrl, const File *bibTeXFile, PathType pathType) {
0103     const QUrl baseUrl = bibTeXFile != nullptr && bibTeXFile->hasProperty(File::Url) ? bibTeXFile->property(File::Url).toUrl() : QUrl();
0104     return pathType == PathType::Absolute ? absoluteFilename(documentUrl, baseUrl) : relativeFilename(documentUrl, baseUrl);
0105 }
0106 
0107 QPair<QUrl, QUrl> AssociatedFiles::computeSourceDestinationUrls(const QUrl &sourceUrl, const QString &entryId, const File *bibTeXFile, RenameOperation renameOperation, const QString &userDefinedFilename)
0108 {
0109     if (entryId.isEmpty() && renameOperation == RenameOperation::EntryId) {
0110         /// If no entry id was given but still a rename after entry id was requested,
0111         /// revert choice and enforce keeping the original name
0112         renameOperation = RenameOperation::KeepName;
0113     }
0114 
0115     const QUrl bibTeXFileUrl = bibTeXFile != nullptr && bibTeXFile->hasProperty(File::Url) ? bibTeXFile->property(File::Url).toUrl() : QUrl();
0116     if (!bibTeXFileUrl.isValid()) return QPair<QUrl, QUrl>();
0117 
0118     /// If URL of document to be associated is relative and current file has an URL, resolve relative URL
0119     const QUrl absoluteSourceUrl = sourceUrl.isRelative() ? bibTeXFileUrl.resolved(sourceUrl) : sourceUrl;
0120 
0121     /// Compute the filename for the move/copy operation's target
0122     const QFileInfo internalSourceInfo(absoluteSourceUrl.path());
0123     QString filename = internalSourceInfo.fileName();
0124     QString suffix = internalSourceInfo.suffix();
0125     if (suffix.isEmpty())
0126         suffix = QStringLiteral("html");
0127     if (filename.isEmpty() || renameOperation == RenameOperation::EntryId)
0128         filename = entryId + QLatin1Char('.') + suffix;
0129     if (filename.isEmpty() || renameOperation == RenameOperation::UserDefined)
0130         filename = userDefinedFilename;
0131 
0132     /// Assemble the target URL from the bibliography's URL and the filename as computed above
0133     QUrl targetUrl = bibTeXFileUrl;
0134     const QString targetPath = QFileInfo(targetUrl.path()).absolutePath();
0135     targetUrl.setPath(targetPath + QDir::separator() + filename);
0136 
0137     return QPair<QUrl, QUrl>(absoluteSourceUrl, targetUrl);
0138 }
0139 
0140 QUrl AssociatedFiles::copyDocument(const QUrl &sourceUrl, const QString &entryId, const File *bibTeXFile, RenameOperation renameOperation, MoveCopyOperation moveCopyOperation, QWidget *widget, const QString &userDefinedFilename)
0141 {
0142     if (moveCopyOperation == MoveCopyOperation::None)
0143         /// If nothing to be copied or moved, the target URL is equal to the source URL
0144         return sourceUrl;
0145 
0146     const QPair<QUrl, QUrl> r = computeSourceDestinationUrls(sourceUrl, entryId, bibTeXFile, renameOperation, userDefinedFilename);
0147     const QUrl internalSourceUrl = r.first, targetUrl = r.second;
0148 
0149     bool success = true;
0150     if (internalSourceUrl.isLocalFile() && targetUrl.isLocalFile()) {
0151         QFile(targetUrl.path()).remove();
0152         success &= QFile::copy(internalSourceUrl.path(), targetUrl.path());
0153         if (success && moveCopyOperation == MoveCopyOperation::Move) {
0154             success &= QFile(internalSourceUrl.path()).remove();
0155         }
0156     } else if (internalSourceUrl.isValid() && targetUrl.isValid()) {
0157         // FIXME non-blocking
0158         KIO::CopyJob *moveCopyJob = moveCopyOperation == MoveCopyOperation::Move ? KIO::move(sourceUrl, targetUrl, KIO::HideProgressInfo | KIO::Overwrite) : KIO::copy(sourceUrl, targetUrl, KIO::HideProgressInfo | KIO::Overwrite);
0159         KJobWidgets::setWindow(moveCopyJob, widget);
0160         success &= moveCopyJob->exec();
0161     } else {
0162         qCWarning(LOG_KBIBTEX_NETWORKING) << "Either sourceUrl or targetUrl is invalid";
0163         return QUrl();
0164     }
0165 
0166     if (!success) return QUrl(); ///< either copy/move or delete operation failed
0167 
0168     return targetUrl;
0169 }