Files
qt-creator/src/plugins/gitlab/gitlabprojectsettings.cpp
Christian Stenger fe63fb2881 GitLab: Add hint what link to gitlab is for
Change-Id: I5ee32fac4a2a73b3b40438d1697071491c17ac94
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
2022-06-03 11:40:37 +00:00

327 lines
13 KiB
C++

/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "gitlabprojectsettings.h"
#include "gitlaboptionspage.h"
#include "gitlabplugin.h"
#include "queryrunner.h"
#include "resultparser.h"
#include <git/gitclient.h>
#include <projectexplorer/project.h>
#include <utils/infolabel.h>
#include <utils/qtcassert.h>
#include <QComboBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QUrl>
#include <QVBoxLayout>
#include <QVariant>
namespace GitLab {
const char PSK_LINKED_ID[] = "GitLab.LinkedId";
const char PSK_SERVER[] = "GitLab.Server";
const char PSK_PROJECT[] = "GitLab.Project";
const char PSK_LAST_REQ[] = "GitLab.LastRequest";
static QString accessLevelString(int accessLevel)
{
const char trContext[] = "GitLab::GitLabProjectSettingsWidget";
switch (accessLevel) {
case 10: return QCoreApplication::translate(trContext, "Guest");
case 20: return QCoreApplication::translate(trContext, "Reporter");
case 30: return QCoreApplication::translate(trContext, "Developer");
case 40: return QCoreApplication::translate(trContext, "Maintainer");
case 50: return QCoreApplication::translate(trContext, "Owner");
}
return {};
}
std::tuple<QString, QString, int>
GitLabProjectSettings::remotePartsFromRemote(const QString &remote)
{
QString host;
QString path;
int port = -1;
if (remote.startsWith("git@")) {
int colon = remote.indexOf(':');
host = remote.mid(4, colon - 4);
path = remote.mid(colon + 1);
} else {
const QUrl url(remote);
host = url.host();
path = url.path().mid(1); // ignore leading slash
port = url.port();
}
if (path.endsWith(".git"))
path.chop(4);
return std::make_tuple(host, path, port);
}
GitLabProjectSettings::GitLabProjectSettings(ProjectExplorer::Project *project)
: m_project(project)
{
load();
connect(project, &ProjectExplorer::Project::settingsLoaded,
this, &GitLabProjectSettings::load);
connect(project, &ProjectExplorer::Project::aboutToSaveSettings,
this, &GitLabProjectSettings::save);
}
void GitLabProjectSettings::setLinked(bool linked)
{
m_linked = linked;
save();
}
void GitLabProjectSettings::load()
{
m_id = Utils::Id::fromSetting(m_project->namedSettings(PSK_LINKED_ID));
m_host = m_project->namedSettings(PSK_SERVER).toString();
m_currentProject = m_project->namedSettings(PSK_PROJECT).toString();
m_lastRequest = m_project->namedSettings(PSK_LAST_REQ).toDateTime();
// may still be wrong, but we avoid an additional request by just doing sanity check here
if (!m_id.isValid() || m_host.isEmpty())
m_linked = false;
else
m_linked = GitLabPlugin::globalParameters()->serverForId(m_id).id.isValid();
}
void GitLabProjectSettings::save()
{
if (m_linked) {
m_project->setNamedSettings(PSK_LINKED_ID, m_id.toSetting());
m_project->setNamedSettings(PSK_SERVER, m_host);
} else {
m_project->setNamedSettings(PSK_LINKED_ID, Utils::Id().toSetting());
m_project->setNamedSettings(PSK_SERVER, QString());
}
m_project->setNamedSettings(PSK_PROJECT, m_currentProject);
m_project->setNamedSettings(PSK_LAST_REQ, m_lastRequest);
}
GitLabProjectSettingsWidget::GitLabProjectSettingsWidget(ProjectExplorer::Project *project,
QWidget *parent)
: ProjectExplorer::ProjectSettingsWidget(parent)
, m_projectSettings(GitLabPlugin::projectSettings(project))
{
setUseGlobalSettingsCheckBoxVisible(false);
// setup ui
auto verticalLayout = new QVBoxLayout(this);
verticalLayout->setContentsMargins(0, 0, 0, 0);
auto formLayout = new QFormLayout;
m_hostCB = new QComboBox;
formLayout->addRow(tr("Host:"), m_hostCB);
m_linkedGitLabServer = new QComboBox;
formLayout->addRow(tr("Linked GitLab Configuration:"), m_linkedGitLabServer);
verticalLayout->addLayout(formLayout);
m_infoLabel = new Utils::InfoLabel;
m_infoLabel->setVisible(false);
verticalLayout->addWidget(m_infoLabel);
auto horizontalLayout = new QHBoxLayout;
horizontalLayout->setContentsMargins(0, 0, 0, 0);
m_linkWithGitLab = new QPushButton(tr("Link with GitLab"));
horizontalLayout->addWidget(m_linkWithGitLab);
m_unlink = new QPushButton(tr("Unlink from GitLab"));
m_unlink->setEnabled(false);
horizontalLayout->addWidget(m_unlink);
m_checkConnection = new QPushButton(tr("Test Connection"));
m_checkConnection->setEnabled(false);
horizontalLayout->addWidget(m_checkConnection);
horizontalLayout->addStretch(1);
verticalLayout->addLayout(horizontalLayout);
verticalLayout->addWidget(new QLabel(tr("Projects linked with GitLab receive event "
"notifications in the Version Control output pane.")));
connect(m_linkWithGitLab, &QPushButton::clicked, this, [this]() {
checkConnection(Link);
});
connect(m_unlink, &QPushButton::clicked,
this, &GitLabProjectSettingsWidget::unlink);
connect(m_checkConnection, &QPushButton::clicked, this, [this]() {
checkConnection(Connection);
});
connect(m_linkedGitLabServer, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this]() {
m_infoLabel->setVisible(false);
});
connect(m_hostCB, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this]() {
m_infoLabel->setVisible(false);
});
connect(GitLabPlugin::optionsPage(), &GitLabOptionsPage::settingsChanged,
this, &GitLabProjectSettingsWidget::updateUi);
updateUi();
}
void GitLabProjectSettingsWidget::unlink()
{
QTC_ASSERT(m_projectSettings->isLinked(), return);
m_projectSettings->setLinked(false);
m_projectSettings->setCurrentProject({});
updateEnabledStates();
GitLabPlugin::linkedStateChanged(false);
}
void GitLabProjectSettingsWidget::checkConnection(CheckMode mode)
{
const GitLabServer server = m_linkedGitLabServer->currentData().value<GitLabServer>();
const QString remote = m_hostCB->currentData().toString();
const auto [remoteHost, projName, port] = GitLabProjectSettings::remotePartsFromRemote(remote);
if (remoteHost != server.host) { // port check as well
m_infoLabel->setType(Utils::InfoLabel::NotOk);
m_infoLabel->setText(tr("Remote host does not match chosen GitLab configuration."));
m_infoLabel->setVisible(true);
return;
}
// temporarily disable ui
m_linkedGitLabServer->setEnabled(false);
m_hostCB->setEnabled(false);
m_checkConnection->setEnabled(false);
m_checkMode = mode;
const Query query(Query::Project, {projName});
QueryRunner *runner = new QueryRunner(query, server.id, this);
// can't use server, projName as captures inside the lambda below (bindings vs. local vars) :/
const Utils::Id id = server.id;
const QString projectName = projName;
connect(runner, &QueryRunner::resultRetrieved, this,
[this, id, remote, projectName](const QByteArray &result) {
onConnectionChecked(ResultParser::parseProject(result), id, remote, projectName);
});
connect(runner, &QueryRunner::finished, this, [runner]() { runner->deleteLater(); });
runner->start();
}
void GitLabProjectSettingsWidget::onConnectionChecked(const Project &project,
const Utils::Id &serverId,
const QString &remote,
const QString &projectName)
{
bool linkable = false;
if (!project.error.message.isEmpty()) {
m_infoLabel->setType(Utils::InfoLabel::Error);
m_infoLabel->setText(project.error.message);
} else {
if (project.accessLevel != -1) {
m_infoLabel->setType(Utils::InfoLabel::Ok);
m_infoLabel->setText(tr("Accessible (%1)")
.arg(accessLevelString(project.accessLevel)));
linkable = true;
} else {
m_infoLabel->setType(Utils::InfoLabel::Warning);
m_infoLabel->setText(tr("Read only access"));
}
}
m_infoLabel->setVisible(true);
if (m_checkMode == Link && linkable) {
m_projectSettings->setCurrentServer(serverId);
m_projectSettings->setCurrentServerHost(remote);
m_projectSettings->setLinked(true);
m_projectSettings->setCurrentProject(projectName);
GitLabPlugin::linkedStateChanged(true);
}
updateEnabledStates();
}
void GitLabProjectSettingsWidget::updateUi()
{
m_linkedGitLabServer->clear();
const QList<GitLabServer> allServers = GitLabPlugin::allGitLabServers();
for (const GitLabServer &server : allServers) {
const QString display = server.host + " (" + server.description + ')';
m_linkedGitLabServer->addItem(display, QVariant::fromValue(server));
}
const Utils::FilePath projectDirectory = m_projectSettings->project()->projectDirectory();
const auto *gitClient = Git::Internal::GitClient::instance();
const Utils::FilePath repository = gitClient
? gitClient->findRepositoryForDirectory(projectDirectory) : Utils::FilePath();
m_hostCB->clear();
if (!repository.isEmpty()) {
const QMap<QString, QString> remotes = gitClient->synchronousRemotesList(repository);
for (auto it = remotes.begin(), end = remotes.end(); it != end; ++it) {
const QString display = it.key() + " (" + it.value() + ')';
m_hostCB->addItem(display, QVariant::fromValue(it.value()));
}
}
const Utils::Id id = m_projectSettings->currentServer();
const QString serverHost = m_projectSettings->currentServerHost();
if (id.isValid()) {
const GitLabServer server = GitLabPlugin::gitLabServerForId(id);
auto [remoteHost, projName, port] = GitLabProjectSettings::remotePartsFromRemote(serverHost);
if (server.id.isValid() && server.host == remoteHost) { // found config
m_projectSettings->setLinked(true);
m_hostCB->setCurrentIndex(m_hostCB->findData(QVariant::fromValue(serverHost)));
m_linkedGitLabServer->setCurrentIndex(
m_linkedGitLabServer->findData(QVariant::fromValue(server)));
GitLabPlugin::linkedStateChanged(true);
} else {
m_projectSettings->setLinked(false);
GitLabPlugin::linkedStateChanged(false);
}
}
updateEnabledStates();
}
void GitLabProjectSettingsWidget::updateEnabledStates()
{
const bool isGitRepository = m_hostCB->count() > 0;
const bool hasGitLabServers = m_linkedGitLabServer->count();
const bool linked = m_projectSettings->isLinked();
m_linkedGitLabServer->setEnabled(isGitRepository && !linked);
m_hostCB->setEnabled(isGitRepository && !linked);
m_linkWithGitLab->setEnabled(isGitRepository && !linked && hasGitLabServers);
m_unlink->setEnabled(isGitRepository && linked);
m_checkConnection->setEnabled(isGitRepository && hasGitLabServers);
if (!isGitRepository) {
const Utils::FilePath projectDirectory = m_projectSettings->project()->projectDirectory();
const auto *gitClient = Git::Internal::GitClient::instance();
const Utils::FilePath repository = gitClient
? gitClient->findRepositoryForDirectory(projectDirectory) : Utils::FilePath();
if (repository.isEmpty())
m_infoLabel->setText(tr("Not a git repository."));
else
m_infoLabel->setText(tr("Local git repository without remotes."));
m_infoLabel->setType(Utils::InfoLabel::None);
m_infoLabel->setVisible(true);
}
}
} // namespace GitLab