Commit caf251b3 authored by jan.koester's avatar jan.koester
Browse files

editor ported from qt to ajax webversion

parent 5d5ae1ff
Loading
Loading
Loading
Loading

editor/CMakeLists.txt

deleted100644 → 0
+0 −109
Original line number Diff line number Diff line
cmake_minimum_required(VERSION 3.18)

find_package(Qt6 REQUIRED COMPONENTS Core Widgets Quick QuickControls2 WebEngineQuick Network)
qt_standard_project_setup()
qt_policy(SET QTP0001 NEW)
qt_policy(SET QTP0004 OLD)

set(CMAKE_AUTOMOC ON)

qt_add_executable(blogi-editor
    main.cpp
    documentmanager.cpp
    documentmanager.h
    documenttreemodel.cpp
    documenttreemodel.h
    htmlimport.cpp
    htmlimport.h
    edit.cpp
    edit.h
    publishclient.cpp
    publishclient.h
    localapi.cpp
    localapi.h
    themeprovider.h
    authimageprovider.cpp
    authimageprovider.h
    gitfilesystemmodel.cpp
    gitfilesystemmodel.h
)

qt_add_qml_module(blogi-editor
    URI BlogiEditor
    VERSION 1.0
    QML_FILES
        qml/Main.qml
        qml/SidebarSection.qml
        qml/WidgetToolbar.qml
        qml/DocumentTree.qml
        qml/PropertyPanel.qml
        qml/HtmlEditorField.qml
        qml/PreviewPane.qml
        qml/RgbaColorPicker.qml
        qml/ConnectionPanel.qml
        qml/FileBrowserTab.qml
        qml/GitTab.qml
        qml/MediaBrowser.qml
        qml/PublishDialog.qml
        qml/publishhelper.js
        qml/AiPromptDialog.qml
        qml/AiTab.qml
        qml/LlmSettingsDialog.qml
        qml/SurveyListDialog.qml
        qml/SurveyEditorDialog.qml
)

target_link_libraries(blogi-editor PRIVATE
    Qt6::Core
    Qt6::Widgets
    Qt6::Quick
    Qt6::QuickControls2
    Qt6::WebEngineQuick
    Qt6::Network
    blogidev
    tinyxml2::tinyxml2
    json-c::json-c
    uuidp::uuidp
    htmlpp::htmlpp
)

target_include_directories(blogi-editor PRIVATE
    ${CMAKE_SOURCE_DIR}/src
)

target_compile_definitions(blogi-editor PRIVATE
    WEBEDIT_PLUGIN_DIR="${CMAKE_INSTALL_PREFIX}/lib/blogi/plugins/webedit"
)

add_subdirectory(widgets)

install(TARGETS blogi-editor DESTINATION bin)
install(FILES blogi-editor.desktop DESTINATION share/applications)
install(FILES ${CMAKE_SOURCE_DIR}/data/logo_runes.svg DESTINATION share/icons/hicolor/scalable/apps RENAME blogi-editor.svg)

if(APPLE)
    set_target_properties(blogi-editor PROPERTIES
        MACOSX_BUNDLE TRUE
        MACOSX_BUNDLE_GUI_IDENTIFIER "de.tuxist.blogi-editor"
        MACOSX_BUNDLE_BUNDLE_NAME "Blogi Editor"
        MACOSX_BUNDLE_BUNDLE_VERSION "1.0.0"
        MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
    )

    # Copy widget plugins into the app bundle after build
    set(_webedit_plugins
        accordion article button container customhtml grid
        image list popup section slider surveywidget webedit_table textbox video
    )
    set(_webedit_dest "$<TARGET_BUNDLE_DIR:blogi-editor>/Contents/PlugIns/webedit")
    add_custom_command(TARGET blogi-editor POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E make_directory "${_webedit_dest}"
    )
    foreach(_wplug ${_webedit_plugins})
        add_custom_command(TARGET blogi-editor POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E copy_if_different
                "$<TARGET_FILE:${_wplug}>"
                "${_webedit_dest}/"
        )
    endforeach()
endif()

editor/authimageprovider.cpp

deleted100644 → 0
+0 −143
Original line number Diff line number Diff line
/*******************************************************************************
 * Copyright (c) 2026, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include "authimageprovider.h"
#include "publishclient.h"

#include <QNetworkReply>
#include <QNetworkRequest>
#include <QQuickTextureFactory>
#include <QImage>
#include <QImageReader>
#include <QBuffer>
#include <QMetaObject>
#include <QDebug>

AuthImageResponse::AuthImageResponse(const QString &url, const QSize &requestedSize,
                                     PublishClient *pub)
{
    /*
     * requestImageResponse() is called from the QQuickPixmapReader thread,
     * but QNetworkAccessManager must be used from its owning (main) thread.
     * Dispatch the network request to the main thread via QueuedConnection.
     */
    QMetaObject::invokeMethod(pub, [this, url, requestedSize, pub]() {
        QNetworkAccessManager *nam = pub->networkManager();
        QUrl imageUrl = QUrl::fromEncoded(url.toUtf8());

        QNetworkRequest req(imageUrl);
        req.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
        req.setRawHeader("Accept", "image/png, image/jpeg, image/gif, image/bmp, image/webp, */*");

        QString token = pub->authToken();
        if (!token.isEmpty())
            req.setRawHeader("Cookie", ("authid=" + token).toUtf8());

        QNetworkReply *reply = nam->get(req);

        if (pub->ignoreSsl()) {
            connect(reply, &QNetworkReply::sslErrors,
                    reply, qOverload<>(&QNetworkReply::ignoreSslErrors));
        }

        /*
         * Use DirectConnection: reply lives on main thread, the lambda
         * runs on main thread, which is fine.  QQuickImageResponse::finished()
         * is documented as safe to emit from any thread.
         */
        connect(reply, &QNetworkReply::finished, this, [this, reply, requestedSize]() {
            reply->deleteLater();

            if (reply->error() != QNetworkReply::NoError) {
                _error = reply->errorString();
                emit finished();
                return;
            }

            QByteArray data = reply->readAll();
            if (data.isEmpty()) {
                _error = "Empty response from server";
                emit finished();
                return;
            }

            /* Derive format hint from Content-Type header */
            QByteArray formatHint;
            QString ct = reply->header(QNetworkRequest::ContentTypeHeader).toString().toLower();
            if (ct.contains("png"))        formatHint = "png";
            else if (ct.contains("jpeg") || ct.contains("jpg")) formatHint = "jpeg";
            else if (ct.contains("gif"))   formatHint = "gif";
            else if (ct.contains("webp"))  formatHint = "webp";
            else if (ct.contains("bmp"))   formatHint = "bmp";
            else if (ct.contains("svg"))   formatHint = "svg";

            /* Try with format hint first, then without (auto-detect) */
            if (!formatHint.isEmpty())
                _image = QImage::fromData(data, formatHint.constData());
            if (_image.isNull())
                _image = QImage::fromData(data);

            if (_image.isNull()) {
                /* Try QImageReader with explicit format probing */
                QBuffer buf(&data);
                buf.open(QIODevice::ReadOnly);
                QImageReader reader(&buf);
                reader.setDecideFormatFromContent(true);
                _image = reader.read();
            }

            if (_image.isNull()) {
                _error = "Cannot decode image (Content-Type: " + ct
                       + ", " + QString::number(data.size()) + " bytes, "
                       + "supported: " + QImageReader::supportedImageFormats().join(", ") + ")";
                qWarning() << "AuthImageProvider:" << _error;
                emit finished();
                return;
            }

            if (requestedSize.isValid() && !requestedSize.isEmpty())
                _image = _image.scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);

            emit finished();
        }, Qt::DirectConnection);
    }, Qt::QueuedConnection);
}

QQuickTextureFactory *AuthImageResponse::textureFactory() const {
    return _image.isNull() ? nullptr : QQuickTextureFactory::textureFactoryForImage(_image);
}

AuthImageProvider::AuthImageProvider(PublishClient *pub)
    : _pub(pub)
{
}

QQuickImageResponse *AuthImageProvider::requestImageResponse(
    const QString &id, const QSize &requestedSize)
{
    return new AuthImageResponse(id, requestedSize, _pub);
}

editor/authimageprovider.h

deleted100644 → 0
+0 −58
Original line number Diff line number Diff line
/*******************************************************************************
 * Copyright (c) 2026, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#pragma once

#include <QQuickAsyncImageProvider>
#include <QNetworkAccessManager>

class PublishClient;

class AuthImageResponse : public QQuickImageResponse {
    Q_OBJECT
public:
    AuthImageResponse(const QString &url, const QSize &requestedSize,
                      PublishClient *pub);

    QQuickTextureFactory *textureFactory() const override;
    QString errorString() const override { return _error; }

private:
    QImage  _image;
    QString _error;
};

class AuthImageProvider : public QQuickAsyncImageProvider {
public:
    explicit AuthImageProvider(PublishClient *pub);

    QQuickImageResponse *requestImageResponse(
        const QString &id, const QSize &requestedSize) override;

private:
    PublishClient *_pub;
};

editor/blogi-editor.desktop

deleted100644 → 0
+0 −9
Original line number Diff line number Diff line
[Desktop Entry]
Name=Blogi Editor
Comment=CMS Editor for Blogi
Exec=blogi-editor
Icon=blogi-editor
Terminal=false
Type=Application
Categories=Development;TextEditor;
StartupNotify=true

editor/documentmanager.cpp

deleted100644 → 0
+0 −859

File deleted.

Preview size limit exceeded, changes collapsed.

Loading