File indexing completed on 2024-05-05 04:41:00

0001 /*
0002     SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com>
0003     SPDX-FileCopyrightText: 2008 Andreas Pakulat <apaku@gmx.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "kdevsvnplugin.h"
0009 
0010 #include <QAction>
0011 #include <QDialog>
0012 #include <QMenu>
0013 #include <QVBoxLayout>
0014 #include <QVariant>
0015 
0016 #include <KFile>
0017 #include <KIO/Global>
0018 #include <KLocalizedString>
0019 #include <KMessageBox>
0020 #include <KPluginFactory>
0021 #include <KUrlRequester>
0022 #include <KUrlRequesterDialog>
0023 
0024 #include <interfaces/idocument.h>
0025 #include <interfaces/icore.h>
0026 #include <interfaces/iruncontroller.h>
0027 #include <project/projectmodel.h>
0028 #include <interfaces/context.h>
0029 #include <interfaces/contextmenuextension.h>
0030 #include <vcs/vcsrevision.h>
0031 #include <vcs/vcsevent.h>
0032 #include <vcs/vcsstatusinfo.h>
0033 #include <vcs/vcsannotation.h>
0034 #include <vcs/widgets/vcsdiffwidget.h>
0035 #include <vcs/widgets/vcscommitdialog.h>
0036 
0037 #include "kdevsvncpp/apr.hpp"
0038 
0039 #include "svncommitjob.h"
0040 #include "svnstatusjob.h"
0041 #include "svnaddjob.h"
0042 #include "svnrevertjob.h"
0043 #include "svnremovejob.h"
0044 #include "svnupdatejob.h"
0045 #include "svninfojob.h"
0046 #include "svndiffjob.h"
0047 #include "svncopyjob.h"
0048 #include "svnmovejob.h"
0049 #include "svnlogjob.h"
0050 #include "svnblamejob.h"
0051 #include "svnimportjob.h"
0052 #include "svncheckoutjob.h"
0053 
0054 #include "svnimportmetadatawidget.h"
0055 #include <vcs/vcspluginhelper.h>
0056 #include <vcs/widgets/standardvcslocationwidget.h>
0057 #include "svnlocationwidget.h"
0058 #include "debug.h"
0059 
0060 K_PLUGIN_FACTORY_WITH_JSON(KDevSvnFactory, "kdevsubversion.json", registerPlugin<KDevSvnPlugin>();)
0061 
0062 KDevSvnPlugin::KDevSvnPlugin(QObject *parent, const QVariantList &)
0063         : KDevelop::IPlugin(QStringLiteral("kdevsubversion"), parent)
0064         , m_common(new KDevelop::VcsPluginHelper(this, this))
0065         , copy_action( nullptr )
0066         , move_action( nullptr )
0067         , m_jobQueue(new ThreadWeaver::Queue(this))
0068 {
0069     qRegisterMetaType<KDevelop::VcsStatusInfo>();
0070     qRegisterMetaType<SvnInfoHolder>();
0071     qRegisterMetaType<KDevelop::VcsEvent>();
0072     qRegisterMetaType<KDevelop::VcsRevision>();
0073     qRegisterMetaType<KDevelop::VcsRevision::RevisionSpecialType>();
0074     qRegisterMetaType<KDevelop::VcsAnnotation>();
0075     qRegisterMetaType<KDevelop::VcsAnnotationLine>();
0076 }
0077 
0078 KDevSvnPlugin::~KDevSvnPlugin()
0079 {
0080 }
0081 
0082 bool KDevSvnPlugin::isValidRemoteRepositoryUrl(const QUrl& remoteLocation)
0083 {
0084     const QString scheme = remoteLocation.scheme();
0085     if (scheme == QLatin1String("svn") ||
0086         scheme == QLatin1String("svn+ssh")) {
0087         return true;
0088     }
0089     return false;
0090 }
0091 
0092 
0093 bool KDevSvnPlugin::isVersionControlled(const QUrl &localLocation)
0094 {
0095     ///TODO: also check this in the other functions?
0096     if (!localLocation.isValid()) {
0097         return false;
0098     }
0099 
0100     auto* job = new SvnInfoJob(this);
0101 
0102     job->setLocation(localLocation);
0103 
0104     if (job->exec()) {
0105         QVariant result = job->fetchResults();
0106 
0107         if (result.isValid()) {
0108             SvnInfoHolder h = result.value<SvnInfoHolder>();
0109             return !h.name.isEmpty();
0110         }
0111     } else {
0112         qCDebug(PLUGIN_SVN) << "Couldn't execute job";
0113     }
0114 
0115     return false;
0116 }
0117 
0118 KDevelop::VcsJob* KDevSvnPlugin::repositoryLocation(const QUrl &localLocation)
0119 {
0120     auto* job = new SvnInfoJob(this);
0121 
0122     job->setLocation(localLocation);
0123     job->setProvideInformation(SvnInfoJob::RepoUrlOnly);
0124     return job;
0125 }
0126 
0127 KDevelop::VcsJob* KDevSvnPlugin::status(const QList<QUrl>& localLocations,
0128                                         KDevelop::IBasicVersionControl::RecursionMode mode)
0129 {
0130     auto* job = new SvnStatusJob(this);
0131     job->setLocations(localLocations);
0132     job->setRecursive((mode == KDevelop::IBasicVersionControl::Recursive));
0133     return job;
0134 }
0135 
0136 KDevelop::VcsJob* KDevSvnPlugin::add(const QList<QUrl>& localLocations,
0137                                      KDevelop::IBasicVersionControl::RecursionMode recursion)
0138 {
0139     auto* job = new SvnAddJob(this);
0140     job->setLocations(localLocations);
0141     job->setRecursive((recursion == KDevelop::IBasicVersionControl::Recursive));
0142     return job;
0143 }
0144 
0145 KDevelop::VcsJob* KDevSvnPlugin::remove(const QList<QUrl>& localLocations)
0146 {
0147     auto* job = new SvnRemoveJob(this);
0148     job->setLocations(localLocations);
0149     return job;
0150 }
0151 
0152 KDevelop::VcsJob* KDevSvnPlugin::edit(const QUrl& /*localLocation*/)
0153 {
0154     return nullptr;
0155 }
0156 
0157 KDevelop::VcsJob* KDevSvnPlugin::unedit(const QUrl& /*localLocation*/)
0158 {
0159     return nullptr;
0160 }
0161 
0162 KDevelop::VcsJob* KDevSvnPlugin::localRevision(const QUrl &localLocation, KDevelop::VcsRevision::RevisionType type)
0163 {
0164     auto* job = new SvnInfoJob(this);
0165 
0166     job->setLocation(localLocation);
0167     job->setProvideInformation(SvnInfoJob::RevisionOnly);
0168     job->setProvideRevisionType(type);
0169     return job;
0170 }
0171 
0172 KDevelop::VcsJob* KDevSvnPlugin::copy(const QUrl &localLocationSrc, const QUrl& localLocationDstn)
0173 {
0174     auto* job = new SvnCopyJob(this);
0175     job->setSourceLocation(localLocationSrc);
0176     job->setDestinationLocation(localLocationDstn);
0177     return job;
0178 }
0179 
0180 KDevelop::VcsJob* KDevSvnPlugin::move(const QUrl &localLocationSrc, const QUrl& localLocationDst)
0181 {
0182     auto* job = new SvnMoveJob(this);
0183     job->setSourceLocation(localLocationSrc);
0184     job->setDestinationLocation(localLocationDst);
0185     return job;
0186 }
0187 
0188 KDevelop::VcsJob* KDevSvnPlugin::revert(const QList<QUrl>& localLocations,
0189                                         KDevelop::IBasicVersionControl::RecursionMode recursion)
0190 {
0191     auto* job = new SvnRevertJob(this);
0192     job->setLocations(localLocations);
0193     job->setRecursive((recursion == KDevelop::IBasicVersionControl::Recursive));
0194     return job;
0195 }
0196 
0197 KDevelop::VcsJob* KDevSvnPlugin::update(const QList<QUrl>& localLocations,
0198                                         const KDevelop::VcsRevision& rev,
0199                                         KDevelop::IBasicVersionControl::RecursionMode recursion)
0200 {
0201     auto* job = new SvnUpdateJob(this);
0202     job->setLocations(localLocations);
0203     job->setRevision(rev);
0204     job->setRecursive((recursion == KDevelop::IBasicVersionControl::Recursive));
0205     return job;
0206 }
0207 
0208 KDevelop::VcsJob* KDevSvnPlugin::commit(const QString& message, const QList<QUrl>& localLocations,
0209                                         KDevelop::IBasicVersionControl::RecursionMode recursion)
0210 {
0211     auto* job = new SvnCommitJob(this);
0212     qCDebug(PLUGIN_SVN) << "Committing locations:" << localLocations;
0213     job->setUrls(localLocations);
0214     job->setCommitMessage(message) ;
0215     job->setRecursive((recursion == KDevelop::IBasicVersionControl::Recursive));
0216     return job;
0217 }
0218 
0219 KDevelop::VcsJob* KDevSvnPlugin::diff(const QUrl &fileOrDirectory,
0220                                       const KDevelop::VcsRevision& srcRevision,
0221                                       const KDevelop::VcsRevision& dstRevision,
0222                                       KDevelop::IBasicVersionControl::RecursionMode recurse)
0223 {
0224     KDevelop::VcsLocation loc(fileOrDirectory);
0225     return diff2(loc, loc, srcRevision, dstRevision, recurse);
0226 }
0227 
0228 KDevelop::VcsJob* KDevSvnPlugin::diff2(const KDevelop::VcsLocation& src,
0229                                        const KDevelop::VcsLocation& dst,
0230                                        const KDevelop::VcsRevision& srcRevision,
0231                                        const KDevelop::VcsRevision& dstRevision,
0232                                        KDevelop::IBasicVersionControl::RecursionMode recurse)
0233 {
0234     auto* job = new SvnDiffJob(this);
0235     job->setSource(src);
0236     job->setDestination(dst);
0237     job->setSrcRevision(srcRevision);
0238     job->setDstRevision(dstRevision);
0239     job->setRecursive((recurse == KDevelop::IBasicVersionControl::Recursive));
0240     return job;
0241 }
0242 
0243 KDevelop::VcsJob* KDevSvnPlugin::log(const QUrl &localLocation, const KDevelop::VcsRevision& rev, unsigned long limit)
0244 {
0245     auto* job = new SvnLogJob(this);
0246     job->setLocation(localLocation);
0247     job->setStartRevision(rev);
0248     job->setLimit(limit);
0249     return job;
0250 }
0251 
0252 KDevelop::VcsJob* KDevSvnPlugin::log(const QUrl &localLocation,
0253                                      const KDevelop::VcsRevision& startRev,
0254                                      const KDevelop::VcsRevision& endRev)
0255 {
0256     auto* job = new SvnLogJob(this);
0257     job->setLocation(localLocation);
0258     job->setStartRevision(startRev);
0259     job->setEndRevision(endRev);
0260     return job;
0261 }
0262 
0263 KDevelop::VcsJob* KDevSvnPlugin::annotate(const QUrl &localLocation,
0264         const KDevelop::VcsRevision& rev)
0265 {
0266     auto* job = new SvnBlameJob(this);
0267     job->setLocation(localLocation);
0268     job->setEndRevision(rev);
0269     return job;
0270 }
0271 
0272 KDevelop::VcsJob* KDevSvnPlugin::merge(const KDevelop::VcsLocation& localOrRepoLocationSrc,
0273                                        const KDevelop::VcsLocation& localOrRepoLocationDst,
0274                                        const KDevelop::VcsRevision& srcRevision,
0275                                        const KDevelop::VcsRevision& dstRevision,
0276                                        const QUrl &localLocation)
0277 {
0278     // TODO implement merge
0279     Q_UNUSED(localOrRepoLocationSrc)
0280     Q_UNUSED(localOrRepoLocationDst)
0281     Q_UNUSED(srcRevision)
0282     Q_UNUSED(dstRevision)
0283     Q_UNUSED(localLocation)
0284     return nullptr;
0285 }
0286 
0287 KDevelop::VcsJob* KDevSvnPlugin::resolve(const QList<QUrl>& /*localLocations*/,
0288         KDevelop::IBasicVersionControl::RecursionMode /*recursion*/)
0289 {
0290     return nullptr;
0291 }
0292 
0293 KDevelop::VcsJob* KDevSvnPlugin::import(const QString & commitMessage, const QUrl &sourceDirectory, const KDevelop::VcsLocation & destinationRepository)
0294 {
0295     auto* job = new SvnImportJob(this);
0296     job->setMapping(sourceDirectory, destinationRepository);
0297     job->setMessage(commitMessage);
0298     return job;
0299 }
0300 
0301 KDevelop::VcsJob* KDevSvnPlugin::createWorkingCopy(const KDevelop::VcsLocation & sourceRepository, const QUrl &destinationDirectory, KDevelop::IBasicVersionControl::RecursionMode recursion)
0302 {
0303     auto* job = new SvnCheckoutJob(this);
0304     job->setMapping(sourceRepository, destinationDirectory, recursion);
0305     return job;
0306 }
0307 
0308 
0309 KDevelop::ContextMenuExtension KDevSvnPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent)
0310 {
0311     m_common->setupFromContext(context);
0312 
0313     const QList<QUrl> & ctxUrlList  = m_common->contextUrlList();
0314 
0315     bool hasVersionControlledEntries = false;
0316     for (const QUrl& url : ctxUrlList) {
0317         if (isVersionControlled(url) || isVersionControlled(KIO::upUrl(url))) {
0318             hasVersionControlledEntries = true;
0319             break;
0320         }
0321     }
0322 
0323     qCDebug(PLUGIN_SVN) << "version controlled?" << hasVersionControlledEntries;
0324 
0325     if (!hasVersionControlledEntries)
0326         return IPlugin::contextMenuExtension(context, parent);
0327 
0328 
0329     QMenu* svnmenu = m_common->commonActions(parent);
0330     svnmenu->addSeparator();
0331 
0332     if( !copy_action )
0333     {
0334         copy_action = new QAction(i18nc("@action:inmenu", "Copy..."), this);
0335         connect(copy_action, &QAction::triggered, this, &KDevSvnPlugin::ctxCopy);
0336     }
0337     svnmenu->addAction(copy_action);
0338 
0339     if( !move_action )
0340     {
0341         move_action = new QAction(i18nc("@action:inmenu", "Move..."), this);
0342         connect(move_action, &QAction::triggered, this, &KDevSvnPlugin::ctxMove);
0343     }
0344     svnmenu->addAction(move_action);
0345 
0346     KDevelop::ContextMenuExtension menuExt;
0347     menuExt.addAction(KDevelop::ContextMenuExtension::VcsGroup, svnmenu->menuAction());
0348 
0349     return menuExt;
0350 }
0351 
0352 void KDevSvnPlugin::ctxCopy()
0353 {
0354     QList<QUrl> const & ctxUrlList = m_common->contextUrlList();
0355     if (ctxUrlList.count() > 1) {
0356         KMessageBox::error(nullptr, i18n("Please select only one item for this operation"));
0357         return;
0358     }
0359 
0360     QUrl source = ctxUrlList.first();
0361 
0362     if (source.isLocalFile()) {
0363         QUrl dir = source;
0364         bool isFile = QFileInfo(source.toLocalFile()).isFile();
0365 
0366         if (isFile) {
0367             dir = dir.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash);
0368         }
0369 
0370         KUrlRequesterDialog dlg(dir, i18nc("@label", "Destination file/directory"), nullptr);
0371 
0372         if (isFile) {
0373             dlg.urlRequester()->setMode(KFile::File | KFile::Directory | KFile::LocalOnly);
0374         } else {
0375             dlg.urlRequester()->setMode(KFile::Directory | KFile::LocalOnly);
0376         }
0377 
0378         if (dlg.exec() == QDialog::Accepted) { // krazy:exclude=crashy
0379             KDevelop::ICore::self()->runController()->registerJob(copy(source, dlg.selectedUrl()));
0380         }
0381     } else {
0382         KMessageBox::error(nullptr, i18n("Copying only works on local files"));
0383         return;
0384     }
0385 
0386 }
0387 
0388 void KDevSvnPlugin::ctxMove()
0389 {
0390     QList<QUrl> const & ctxUrlList = m_common->contextUrlList();
0391     if (ctxUrlList.count() != 1) {
0392         KMessageBox::error(nullptr, i18n("Please select only one item for this operation"));
0393         return;
0394     }
0395 
0396     QUrl source = ctxUrlList.first();
0397 
0398     if (source.isLocalFile()) {
0399         QUrl dir = source;
0400         bool isFile = QFileInfo(source.toLocalFile()).isFile();
0401 
0402         if (isFile) {
0403             dir = source.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash);
0404         }
0405 
0406         KUrlRequesterDialog dlg(dir, i18n("Destination file/directory"), nullptr);
0407 
0408         if (isFile) {
0409             dlg.urlRequester()->setMode(KFile::File | KFile::Directory | KFile::LocalOnly);
0410         } else {
0411             dlg.urlRequester()->setMode(KFile::Directory | KFile::LocalOnly);
0412         }
0413 
0414         if (dlg.exec() == QDialog::Accepted) { // krazy:exclude=crashy
0415             KDevelop::ICore::self()->runController()->registerJob(move(source, dlg.selectedUrl()));
0416         }
0417     } else {
0418         KMessageBox::error(nullptr, i18n("Moving only works on local files/dirs"));
0419         return;
0420     }
0421 }
0422 
0423 QString KDevSvnPlugin::name() const
0424 {
0425     return i18n("Subversion");
0426 }
0427 
0428 KDevelop::VcsImportMetadataWidget* KDevSvnPlugin::createImportMetadataWidget(QWidget* parent)
0429 {
0430     return new SvnImportMetadataWidget(parent);
0431 }
0432 
0433 KDevelop::VcsLocationWidget* KDevSvnPlugin::vcsLocation(QWidget* parent) const
0434 {
0435     return new SvnLocationWidget(parent);
0436 }
0437 
0438 ThreadWeaver::Queue* KDevSvnPlugin::jobQueue() const
0439 {
0440     return m_jobQueue;
0441 }
0442 
0443 #include "kdevsvnplugin.moc"
0444 #include "moc_kdevsvnplugin.cpp"