mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
ImageHost: add Gitee host
This commit is contained in:
parent
f1d931c276
commit
c03884c429
@ -1 +1 @@
|
|||||||
Subproject commit c53fc8dbf6df14b9e1327de0a4907700ffee6049
|
Subproject commit ac58baf5f07643f57a2a208629b7056c24516ffc
|
@ -35,10 +35,11 @@ QString MarkdownBuffer::insertImage(const QImage &p_image, const QString &p_imag
|
|||||||
void MarkdownBuffer::fetchInitialImages()
|
void MarkdownBuffer::fetchInitialImages()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_initialImages.isEmpty());
|
Q_ASSERT(m_initialImages.isEmpty());
|
||||||
vte::MarkdownLink::TypeFlags linkFlags = vte::MarkdownLink::TypeFlag::LocalRelativeInternal | vte::MarkdownLink::TypeFlag::Remote;
|
// There is compilation error on Linux and macOS using TypeFlags directly.
|
||||||
|
int linkFlags = vte::MarkdownLink::TypeFlag::LocalRelativeInternal | vte::MarkdownLink::TypeFlag::Remote;
|
||||||
m_initialImages = vte::MarkdownUtils::fetchImagesFromMarkdownText(getContent(),
|
m_initialImages = vte::MarkdownUtils::fetchImagesFromMarkdownText(getContent(),
|
||||||
getResourcePath(),
|
getResourcePath(),
|
||||||
linkFlags);
|
static_cast<vte::MarkdownLink::TypeFlags>(linkFlags));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkdownBuffer::addInsertedImage(const QString &p_imagePath, const QString &p_urlInLink)
|
void MarkdownBuffer::addInsertedImage(const QString &p_imagePath, const QString &p_urlInLink)
|
||||||
@ -57,11 +58,11 @@ QHash<QString, bool> MarkdownBuffer::clearObsoleteImages()
|
|||||||
|
|
||||||
Q_ASSERT(!isModified());
|
Q_ASSERT(!isModified());
|
||||||
const bool discarded = state() & StateFlag::Discarded;
|
const bool discarded = state() & StateFlag::Discarded;
|
||||||
const vte::MarkdownLink::TypeFlags linkFlags = vte::MarkdownLink::TypeFlag::LocalRelativeInternal | vte::MarkdownLink::TypeFlag::Remote;
|
const int linkFlags = vte::MarkdownLink::TypeFlag::LocalRelativeInternal | vte::MarkdownLink::TypeFlag::Remote;
|
||||||
const auto latestImages =
|
const auto latestImages =
|
||||||
vte::MarkdownUtils::fetchImagesFromMarkdownText(!discarded ? getContent() : m_provider->read(),
|
vte::MarkdownUtils::fetchImagesFromMarkdownText(!discarded ? getContent() : m_provider->read(),
|
||||||
getResourcePath(),
|
getResourcePath(),
|
||||||
linkFlags);
|
static_cast<vte::MarkdownLink::TypeFlags>(linkFlags));
|
||||||
QSet<QString> latestImagesPath;
|
QSet<QString> latestImagesPath;
|
||||||
for (const auto &link : latestImages) {
|
for (const auto &link : latestImages) {
|
||||||
if (link.m_type & vte::MarkdownLink::TypeFlag::Remote) {
|
if (link.m_type & vte::MarkdownLink::TypeFlag::Remote) {
|
||||||
|
203
src/imagehost/giteeimagehost.cpp
Normal file
203
src/imagehost/giteeimagehost.cpp
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
#include "giteeimagehost.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
#include <utils/utils.h>
|
||||||
|
#include <utils/webutils.h>
|
||||||
|
|
||||||
|
using namespace vnotex;
|
||||||
|
|
||||||
|
const QString GiteeImageHost::c_apiUrl = "https://gitee.com/api/v5";
|
||||||
|
|
||||||
|
GiteeImageHost::GiteeImageHost(QObject *p_parent)
|
||||||
|
: ImageHost(p_parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GiteeImageHost::ready() const
|
||||||
|
{
|
||||||
|
return !m_personalAccessToken.isEmpty() && !m_userName.isEmpty() && !m_repoName.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageHost::Type GiteeImageHost::getType() const
|
||||||
|
{
|
||||||
|
return Type::Gitee;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject GiteeImageHost::getConfig() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
obj[QStringLiteral("personal_access_token")] = m_personalAccessToken;
|
||||||
|
obj[QStringLiteral("user_name")] = m_userName;
|
||||||
|
obj[QStringLiteral("repository_name")] = m_repoName;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GiteeImageHost::setConfig(const QJsonObject &p_jobj)
|
||||||
|
{
|
||||||
|
parseConfig(p_jobj, m_personalAccessToken, m_userName, m_repoName);
|
||||||
|
|
||||||
|
m_imageUrlPrefix = QString("https://gitee.com/%1/%2/raw/master/").arg(m_userName, m_repoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GiteeImageHost::testConfig(const QJsonObject &p_jobj, QString &p_msg)
|
||||||
|
{
|
||||||
|
p_msg.clear();
|
||||||
|
|
||||||
|
QString token, userName, repoName;
|
||||||
|
parseConfig(p_jobj, token, userName, repoName);
|
||||||
|
|
||||||
|
if (token.isEmpty() || userName.isEmpty() || repoName.isEmpty()) {
|
||||||
|
p_msg = tr("PersonalAccessToken/UserName/RepositoryName should not be empty.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reply = getRepoInfo(token, userName, repoName);
|
||||||
|
p_msg = QString::fromUtf8(reply.m_data);
|
||||||
|
return reply.m_error == QNetworkReply::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
vte::NetworkAccess::RawHeaderPairs GiteeImageHost::prepareCommonHeaders()
|
||||||
|
{
|
||||||
|
vte::NetworkAccess::RawHeaderPairs rawHeader;
|
||||||
|
rawHeader.push_back(qMakePair(QByteArray("Content-Type"), QByteArray("application/json;charset=UTF-8")));
|
||||||
|
return rawHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GiteeImageHost::addAccessToken(const QString &p_token, QString p_url)
|
||||||
|
{
|
||||||
|
if (p_url.contains(QLatin1Char('?'))) {
|
||||||
|
p_url += QString("&access_token=%1").arg(p_token);
|
||||||
|
} else {
|
||||||
|
p_url += QString("?access_token=%1").arg(p_token);
|
||||||
|
}
|
||||||
|
return p_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
vte::NetworkReply GiteeImageHost::getRepoInfo(const QString &p_token, const QString &p_userName, const QString &p_repoName) const
|
||||||
|
{
|
||||||
|
auto rawHeader = prepareCommonHeaders();
|
||||||
|
auto urlStr = QString("%1/repos/%2/%3").arg(c_apiUrl, p_userName, p_repoName);
|
||||||
|
auto reply = vte::NetworkAccess::request(QUrl(addAccessToken(p_token, urlStr)), rawHeader);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GiteeImageHost::parseConfig(const QJsonObject &p_jobj,
|
||||||
|
QString &p_token,
|
||||||
|
QString &p_userName,
|
||||||
|
QString &p_repoName)
|
||||||
|
{
|
||||||
|
p_token = p_jobj[QStringLiteral("personal_access_token")].toString();
|
||||||
|
p_userName = p_jobj[QStringLiteral("user_name")].toString();
|
||||||
|
p_repoName = p_jobj[QStringLiteral("repository_name")].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isEmptyResponse(const QByteArray &p_data)
|
||||||
|
{
|
||||||
|
return p_data == QByteArray("[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GiteeImageHost::create(const QByteArray &p_data, const QString &p_path, QString &p_msg)
|
||||||
|
{
|
||||||
|
QString destUrl;
|
||||||
|
|
||||||
|
if (p_path.isEmpty()) {
|
||||||
|
p_msg = tr("Failed to create image with empty path.");
|
||||||
|
return destUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ready()) {
|
||||||
|
p_msg = tr("Invalid GitHub image host configuration.");
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rawHeader = prepareCommonHeaders();
|
||||||
|
const auto urlStr = QString("%1/repos/%2/%3/contents/%4").arg(c_apiUrl, m_userName, m_repoName, p_path);
|
||||||
|
|
||||||
|
// Check if @p_path already exists.
|
||||||
|
auto reply = vte::NetworkAccess::request(QUrl(addAccessToken(m_personalAccessToken, urlStr)), rawHeader);
|
||||||
|
if (reply.m_error == QNetworkReply::NoError) {
|
||||||
|
if (!isEmptyResponse(reply.m_data)) {
|
||||||
|
p_msg = tr("The resource already exists at the image host (%1).").arg(p_path);
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
} else if (reply.m_error != QNetworkReply::ContentNotFoundError) {
|
||||||
|
p_msg = tr("Failed to query the resource at the image host (%1) (%2) (%3).").arg(urlStr, reply.errorStr(), reply.m_data);
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the content.
|
||||||
|
QJsonObject requestDataObj;
|
||||||
|
requestDataObj[QStringLiteral("access_token")] = m_personalAccessToken;
|
||||||
|
requestDataObj[QStringLiteral("message")] = QString("VX_ADD: %1").arg(p_path);
|
||||||
|
requestDataObj[QStringLiteral("content")] = QString::fromUtf8(p_data.toBase64());
|
||||||
|
auto requestData = Utils::toJsonString(requestDataObj);
|
||||||
|
reply = vte::NetworkAccess::post(QUrl(urlStr), rawHeader, requestData);
|
||||||
|
if (reply.m_error != QNetworkReply::NoError) {
|
||||||
|
p_msg = tr("Failed to create resource at the image host (%1) (%2) (%3).").arg(urlStr, reply.errorStr(), reply.m_data);
|
||||||
|
return QString();
|
||||||
|
} else {
|
||||||
|
auto replyObj = Utils::fromJsonString(reply.m_data);
|
||||||
|
Q_ASSERT(!replyObj.isEmpty());
|
||||||
|
auto targetUrl = replyObj[QStringLiteral("content")].toObject().value(QStringLiteral("download_url")).toString();
|
||||||
|
if (targetUrl.isEmpty()) {
|
||||||
|
p_msg = tr("Failed to create resource at the image host (%1) (%2) (%3).").arg(urlStr, reply.errorStr(), reply.m_data);
|
||||||
|
} else {
|
||||||
|
qDebug() << "created resource" << targetUrl;
|
||||||
|
}
|
||||||
|
return targetUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GiteeImageHost::ownsUrl(const QString &p_url) const
|
||||||
|
{
|
||||||
|
return p_url.startsWith(m_imageUrlPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GiteeImageHost::remove(const QString &p_url, QString &p_msg)
|
||||||
|
{
|
||||||
|
Q_ASSERT(ownsUrl(p_url));
|
||||||
|
|
||||||
|
if (!ready()) {
|
||||||
|
p_msg = tr("Invalid GitHub image host configuration.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString resourcePath = WebUtils::purifyUrl(p_url.mid(m_imageUrlPrefix.size()));
|
||||||
|
|
||||||
|
auto rawHeader = prepareCommonHeaders();
|
||||||
|
const auto urlStr = QString("%1/repos/%2/%3/contents/%4").arg(c_apiUrl, m_userName, m_repoName, resourcePath);
|
||||||
|
|
||||||
|
// Get the SHA of the resource.
|
||||||
|
auto reply = vte::NetworkAccess::request(QUrl(addAccessToken(m_personalAccessToken, urlStr)), rawHeader);
|
||||||
|
if (reply.m_error != QNetworkReply::NoError || isEmptyResponse(reply.m_data)) {
|
||||||
|
p_msg = tr("Failed to fetch information about the resource (%1).").arg(resourcePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto replyObj = Utils::fromJsonString(reply.m_data);
|
||||||
|
Q_ASSERT(!replyObj.isEmpty());
|
||||||
|
const auto sha = replyObj[QStringLiteral("sha")].toString();
|
||||||
|
if (sha.isEmpty()) {
|
||||||
|
p_msg = tr("Failed to fetch SHA about the resource (%1) (%2).").arg(resourcePath, QString::fromUtf8(reply.m_data));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete.
|
||||||
|
QJsonObject requestDataObj;
|
||||||
|
requestDataObj[QStringLiteral("access_token")] = m_personalAccessToken;
|
||||||
|
requestDataObj[QStringLiteral("message")] = QString("VX_DEL: %1").arg(resourcePath);
|
||||||
|
requestDataObj[QStringLiteral("sha")] = sha;
|
||||||
|
auto requestData = Utils::toJsonString(requestDataObj);
|
||||||
|
reply = vte::NetworkAccess::deleteResource(QUrl(urlStr), rawHeader, requestData);
|
||||||
|
if (reply.m_error != QNetworkReply::NoError) {
|
||||||
|
p_msg = tr("Failed to delete resource (%1) (%2).").arg(resourcePath, QString::fromUtf8(reply.m_data));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "deleted resource" << resourcePath;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
57
src/imagehost/giteeimagehost.h
Normal file
57
src/imagehost/giteeimagehost.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef GITEEIMAGEHOST_H
|
||||||
|
#define GITEEIMAGEHOST_H
|
||||||
|
|
||||||
|
#include "imagehost.h"
|
||||||
|
|
||||||
|
#include <vtextedit/networkutils.h>
|
||||||
|
|
||||||
|
namespace vnotex
|
||||||
|
{
|
||||||
|
class GiteeImageHost : public ImageHost
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit GiteeImageHost(QObject *p_parent);
|
||||||
|
|
||||||
|
bool ready() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
Type getType() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
QJsonObject getConfig() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void setConfig(const QJsonObject &p_jobj) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
bool testConfig(const QJsonObject &p_jobj, QString &p_msg) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
QString create(const QByteArray &p_data, const QString &p_path, QString &p_msg) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
bool remove(const QString &p_url, QString &p_msg) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
bool ownsUrl(const QString &p_url) const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Used to test.
|
||||||
|
vte::NetworkReply getRepoInfo(const QString &p_token, const QString &p_userName, const QString &p_repoName) const;
|
||||||
|
|
||||||
|
static void parseConfig(const QJsonObject &p_jobj,
|
||||||
|
QString &p_token,
|
||||||
|
QString &p_userName,
|
||||||
|
QString &p_repoName);
|
||||||
|
|
||||||
|
static vte::NetworkAccess::RawHeaderPairs prepareCommonHeaders();
|
||||||
|
|
||||||
|
static QString addAccessToken(const QString &p_token, QString p_url);
|
||||||
|
|
||||||
|
QString m_personalAccessToken;
|
||||||
|
|
||||||
|
QString m_userName;
|
||||||
|
|
||||||
|
QString m_repoName;
|
||||||
|
|
||||||
|
QString m_imageUrlPrefix;
|
||||||
|
|
||||||
|
static const QString c_apiUrl;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GITEEIMAGEHOST_H
|
@ -105,14 +105,6 @@ QString GitHubImageHost::create(const QByteArray &p_data, const QString &p_path,
|
|||||||
return destUrl;
|
return destUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
destUrl = createResource(p_data, p_path, p_msg);
|
|
||||||
return destUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString GitHubImageHost::createResource(const QByteArray &p_content, const QString &p_path, QString &p_msg) const
|
|
||||||
{
|
|
||||||
Q_ASSERT(!p_path.isEmpty());
|
|
||||||
|
|
||||||
if (!ready()) {
|
if (!ready()) {
|
||||||
p_msg = tr("Invalid GitHub image host configuration.");
|
p_msg = tr("Invalid GitHub image host configuration.");
|
||||||
return QString();
|
return QString();
|
||||||
@ -134,7 +126,7 @@ QString GitHubImageHost::createResource(const QByteArray &p_content, const QStri
|
|||||||
// Create the content.
|
// Create the content.
|
||||||
QJsonObject requestDataObj;
|
QJsonObject requestDataObj;
|
||||||
requestDataObj[QStringLiteral("message")] = QString("VX_ADD: %1").arg(p_path);
|
requestDataObj[QStringLiteral("message")] = QString("VX_ADD: %1").arg(p_path);
|
||||||
requestDataObj[QStringLiteral("content")] = QString::fromUtf8(p_content.toBase64());
|
requestDataObj[QStringLiteral("content")] = QString::fromUtf8(p_data.toBase64());
|
||||||
auto requestData = Utils::toJsonString(requestDataObj);
|
auto requestData = Utils::toJsonString(requestDataObj);
|
||||||
reply = vte::NetworkAccess::put(QUrl(urlStr), rawHeader, requestData);
|
reply = vte::NetworkAccess::put(QUrl(urlStr), rawHeader, requestData);
|
||||||
if (reply.m_error != QNetworkReply::NoError) {
|
if (reply.m_error != QNetworkReply::NoError) {
|
||||||
|
@ -29,12 +29,19 @@ namespace vnotex
|
|||||||
|
|
||||||
bool ownsUrl(const QString &p_url) const Q_DECL_OVERRIDE;
|
bool ownsUrl(const QString &p_url) const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_personalAccessToken;
|
||||||
|
|
||||||
|
QString m_userName;
|
||||||
|
|
||||||
|
QString m_repoName;
|
||||||
|
|
||||||
|
QString m_imageUrlPrefix;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Used to test.
|
// Used to test.
|
||||||
vte::NetworkReply getRepoInfo(const QString &p_token, const QString &p_userName, const QString &p_repoName) const;
|
vte::NetworkReply getRepoInfo(const QString &p_token, const QString &p_userName, const QString &p_repoName) const;
|
||||||
|
|
||||||
QString createResource(const QByteArray &p_content, const QString &p_path, QString &p_msg) const;
|
|
||||||
|
|
||||||
static void parseConfig(const QJsonObject &p_jobj,
|
static void parseConfig(const QJsonObject &p_jobj,
|
||||||
QString &p_token,
|
QString &p_token,
|
||||||
QString &p_userName,
|
QString &p_userName,
|
||||||
@ -46,14 +53,6 @@ namespace vnotex
|
|||||||
|
|
||||||
static vte::NetworkAccess::RawHeaderPairs prepareCommonHeaders(const QString &p_token);
|
static vte::NetworkAccess::RawHeaderPairs prepareCommonHeaders(const QString &p_token);
|
||||||
|
|
||||||
QString m_personalAccessToken;
|
|
||||||
|
|
||||||
QString m_userName;
|
|
||||||
|
|
||||||
QString m_repoName;
|
|
||||||
|
|
||||||
QString m_imageUrlPrefix;
|
|
||||||
|
|
||||||
static const QString c_apiUrl;
|
static const QString c_apiUrl;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,9 @@ QString ImageHost::typeString(ImageHost::Type p_type)
|
|||||||
case Type::GitHub:
|
case Type::GitHub:
|
||||||
return tr("GitHub");
|
return tr("GitHub");
|
||||||
|
|
||||||
|
case Type::Gitee:
|
||||||
|
return tr("Gitee");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
return QString("Unknown");
|
return QString("Unknown");
|
||||||
|
@ -18,6 +18,7 @@ namespace vnotex
|
|||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
GitHub = 0,
|
GitHub = 0,
|
||||||
|
Gitee,
|
||||||
MaxHost
|
MaxHost
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
QT += widgets
|
QT += widgets
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
$$PWD/giteeimagehost.h \
|
||||||
$$PWD/githubimagehost.h \
|
$$PWD/githubimagehost.h \
|
||||||
$$PWD/imagehost.h \
|
$$PWD/imagehost.h \
|
||||||
$$PWD/imagehostmgr.h \
|
$$PWD/imagehostmgr.h \
|
||||||
$$PWD/imagehostutils.h
|
$$PWD/imagehostutils.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
$$PWD/giteeimagehost.cpp \
|
||||||
$$PWD/githubimagehost.cpp \
|
$$PWD/githubimagehost.cpp \
|
||||||
$$PWD/imagehost.cpp \
|
$$PWD/imagehost.cpp \
|
||||||
$$PWD/imagehostmgr.cpp \
|
$$PWD/imagehostmgr.cpp \
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <core/editorconfig.h>
|
#include <core/editorconfig.h>
|
||||||
|
|
||||||
#include "githubimagehost.h"
|
#include "githubimagehost.h"
|
||||||
|
#include "giteeimagehost.h"
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -64,6 +65,9 @@ ImageHost *ImageHostMgr::createImageHost(ImageHost::Type p_type, QObject *p_pare
|
|||||||
case ImageHost::Type::GitHub:
|
case ImageHost::Type::GitHub:
|
||||||
return new GitHubImageHost(p_parent);
|
return new GitHubImageHost(p_parent);
|
||||||
|
|
||||||
|
case ImageHost::Type::Gitee:
|
||||||
|
return new GiteeImageHost(p_parent);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ namespace vnotex
|
|||||||
ImageHostUtils() = delete;
|
ImageHostUtils() = delete;
|
||||||
|
|
||||||
// According to @p_buffer, generate the relative path on image host for images.
|
// According to @p_buffer, generate the relative path on image host for images.
|
||||||
// Return the relative path folder.
|
|
||||||
static QString generateRelativePath(const Buffer *p_buffer);
|
static QString generateRelativePath(const Buffer *p_buffer);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <vtextedit/markdownutils.h>
|
#include <vtextedit/markdownutils.h>
|
||||||
#include <vtextedit/vtextedit.h>
|
#include <vtextedit/vtextedit.h>
|
||||||
#include <vtextedit/texteditutils.h>
|
#include <vtextedit/texteditutils.h>
|
||||||
|
#include <vtextedit/markdownutils.h>
|
||||||
#include <vtextedit/networkutils.h>
|
#include <vtextedit/networkutils.h>
|
||||||
#include <vtextedit/theme.h>
|
#include <vtextedit/theme.h>
|
||||||
|
|
||||||
@ -1337,16 +1338,22 @@ void MarkdownEditor::setImageHost(ImageHost *p_host)
|
|||||||
m_imageHost = p_host;
|
m_imageHost = p_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MarkdownEditor::saveToImageHost(const QByteArray &p_imageData, const QString &p_destFileName)
|
static QString generateImageHostFileName(const Buffer *p_buffer, const QString &p_destFileName)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_imageHost);
|
auto destPath = ImageHostUtils::generateRelativePath(p_buffer);
|
||||||
|
|
||||||
auto destPath = ImageHostUtils::generateRelativePath(m_buffer);
|
|
||||||
if (destPath.isEmpty()) {
|
if (destPath.isEmpty()) {
|
||||||
destPath = p_destFileName;
|
destPath = p_destFileName;
|
||||||
} else {
|
} else {
|
||||||
destPath += "/" + p_destFileName;
|
destPath += "/" + p_destFileName;
|
||||||
}
|
}
|
||||||
|
return destPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MarkdownEditor::saveToImageHost(const QByteArray &p_imageData, const QString &p_destFileName)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_imageHost);
|
||||||
|
|
||||||
|
const auto destPath = generateImageHostFileName(m_buffer, p_destFileName);
|
||||||
|
|
||||||
QString errMsg;
|
QString errMsg;
|
||||||
|
|
||||||
@ -1390,4 +1397,79 @@ void MarkdownEditor::uploadImagesToImageHost()
|
|||||||
auto act = static_cast<QAction *>(sender());
|
auto act = static_cast<QAction *>(sender());
|
||||||
auto host = ImageHostMgr::getInst().find(act->data().toString());
|
auto host = ImageHostMgr::getInst().find(act->data().toString());
|
||||||
Q_ASSERT(host);
|
Q_ASSERT(host);
|
||||||
|
|
||||||
|
// Only LocalRelativeInternal images.
|
||||||
|
// Descending order of the link position.
|
||||||
|
auto images = vte::MarkdownUtils::fetchImagesFromMarkdownText(m_buffer->getContent(),
|
||||||
|
m_buffer->getResourcePath(),
|
||||||
|
vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||||
|
if (images.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QProgressDialog proDlg(tr("Uploading local images..."),
|
||||||
|
tr("Abort"),
|
||||||
|
0,
|
||||||
|
images.size(),
|
||||||
|
this);
|
||||||
|
proDlg.setWindowModality(Qt::WindowModal);
|
||||||
|
proDlg.setWindowTitle(tr("Upload Images To Image Host"));
|
||||||
|
|
||||||
|
int cnt = 0;
|
||||||
|
auto cursor = m_textEdit->textCursor();
|
||||||
|
cursor.beginEditBlock();
|
||||||
|
for (int i = 0; i < images.size(); ++i) {
|
||||||
|
const auto &link = images[i];
|
||||||
|
|
||||||
|
proDlg.setValue(i + 1);
|
||||||
|
if (proDlg.wasCanceled()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
proDlg.setLabelText(tr("Upload image (%1)").arg(link.m_path));
|
||||||
|
|
||||||
|
Q_ASSERT(i == 0 || link.m_urlInLinkPos < images[i - 1].m_urlInLinkPos);
|
||||||
|
|
||||||
|
QByteArray ba;
|
||||||
|
try {
|
||||||
|
ba = FileUtils::readFile(link.m_path);
|
||||||
|
} catch (Exception &e) {
|
||||||
|
MessageBoxHelper::notify(MessageBoxHelper::Warning,
|
||||||
|
QString("Failed to read local image file (%1) (%2).").arg(link.m_path, e.what()),
|
||||||
|
this);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ba.isEmpty()) {
|
||||||
|
qWarning() << "Skipped uploading empty image" << link.m_path;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto destPath = generateImageHostFileName(m_buffer, PathUtils::fileName(link.m_path));
|
||||||
|
QString errMsg;
|
||||||
|
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||||
|
const auto targetUrl = host->create(ba, destPath, errMsg);
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
if (targetUrl.isEmpty()) {
|
||||||
|
MessageBoxHelper::notify(MessageBoxHelper::Warning,
|
||||||
|
QString("Failed to upload image to image host (%1) as (%2).").arg(host->getName(), destPath),
|
||||||
|
QString(),
|
||||||
|
errMsg,
|
||||||
|
this);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the link URL.
|
||||||
|
cursor.setPosition(link.m_urlInLinkPos);
|
||||||
|
cursor.setPosition(link.m_urlInLinkPos + link.m_urlInLink.size(), QTextCursor::KeepAnchor);
|
||||||
|
cursor.insertText(targetUrl);
|
||||||
|
++cnt;
|
||||||
|
}
|
||||||
|
cursor.endEditBlock();
|
||||||
|
|
||||||
|
proDlg.setValue(images.size());
|
||||||
|
|
||||||
|
if (cnt > 0) {
|
||||||
|
m_textEdit->setTextCursor(cursor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user