some fixed, increased version
This commit is contained in:
parent
69a3d6de9d
commit
1cffbcb468
132
CMakeLists.txt
132
CMakeLists.txt
|
@ -23,7 +23,6 @@ set(HEADER_FILES
|
|||
alpm/packagefinder.h
|
||||
)
|
||||
set(SRC_FILES
|
||||
main.cpp
|
||||
alpm/manager.cpp
|
||||
alpm/package.cpp
|
||||
alpm/utilities.cpp
|
||||
|
@ -44,6 +43,21 @@ set(SRC_FILES
|
|||
network/userrepository.cpp
|
||||
network/networkaccessmanager.cpp
|
||||
)
|
||||
set(CLI_HEADER_FILES
|
||||
)
|
||||
set(CLI_SRC_FILES
|
||||
cli/main.cpp
|
||||
)
|
||||
set(GUI_HEADER_FILES
|
||||
gui/mainwindow.h
|
||||
gui/webpage.h
|
||||
gui/webviewprovider.h
|
||||
)
|
||||
set(GUI_SRC_FILES
|
||||
gui/main.cpp
|
||||
gui/mainwindow.cpp
|
||||
gui/webpage.cpp
|
||||
)
|
||||
set(WEB_FILES
|
||||
web/3rdparty/bootstrap/css/bootstrap-theme.min.css
|
||||
web/3rdparty/bootstrap/css/bootstrap.min.css
|
||||
|
@ -71,13 +85,13 @@ set(WEB_FILES
|
|||
|
||||
# meta data
|
||||
set(META_PROJECT_NAME repoindex)
|
||||
set(META_APP_NAME "Repository browser")
|
||||
set(META_APP_NAME "Repository Browser")
|
||||
set(META_APP_AUTHOR "Martchus")
|
||||
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
|
||||
set(META_APP_DESCRIPTION "Arch Linux repository browser")
|
||||
set(META_APP_DESCRIPTION "Repository browser for Arch Linux")
|
||||
set(META_VERSION_MAJOR 0)
|
||||
set(META_VERSION_MINOR 2)
|
||||
set(META_VERSION_PATCH 1)
|
||||
set(META_VERSION_PATCH 2)
|
||||
|
||||
# stringification of meta data
|
||||
set(META_PROJECT_NAME_STR "\"${META_PROJECT_NAME}\"")
|
||||
|
@ -119,6 +133,9 @@ if(MINGW)
|
|||
enable_language(RC)
|
||||
endif(MINGW)
|
||||
|
||||
# read cached variables
|
||||
set(WEBVIEW_PROVIDER "auto" CACHE STRING "specifies the webview provider: auto, webkit or webengine")
|
||||
|
||||
# check required Qt 5 modules
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Concurrent REQUIRED)
|
||||
|
@ -126,6 +143,36 @@ find_package(Qt5Network REQUIRED)
|
|||
find_package(Qt5WebSockets REQUIRED)
|
||||
find_package(KF5Archive REQUIRED)
|
||||
|
||||
# select Qt module providing webview (either Qt WebKit or Qt WebEngine)
|
||||
if(${WEBVIEW_PROVIDER} STREQUAL "none")
|
||||
set(WEBVIEW_PROVIDER OFF)
|
||||
message(STATUS "Webview disabled, not building GUI.")
|
||||
elseif(${WEBVIEW_PROVIDER} STREQUAL "auto")
|
||||
find_package(Qt5WebEngineWidgets)
|
||||
if(Qt5WebEngineWidgets_FOUND)
|
||||
set(WEBVIEW_PROVIDER Qt5::WebEngineWidgets)
|
||||
set(WEBVIEW_DEFINITION -DREPOINDEX_USE_WEBENGINE)
|
||||
message(STATUS "No webview provider explicitely specified, defaulting to Qt WebEngine.")
|
||||
else()
|
||||
find_package(Qt5WebKitWidgets REQUIRED)
|
||||
set(WEBVIEW_PROVIDER Qt5::WebKitWidgets)
|
||||
message(STATUS "No webview provider explicitely specified, defaulting to Qt WebKit.")
|
||||
endif()
|
||||
else()
|
||||
if(${WEBVIEW_PROVIDER} STREQUAL "webkit")
|
||||
find_package(Qt5WebKitWidgets REQUIRED)
|
||||
set(WEBVIEW_PROVIDER Qt5::WebKitWidgets)
|
||||
message(STATUS "Using Qt WebKit as webview provider.")
|
||||
elseif(${WEBVIEW_PROVIDER} STREQUAL "webengine")
|
||||
find_package(Qt5WebEngineWidgets REQUIRED)
|
||||
set(WEBVIEW_PROVIDER Qt5::WebEngineWidgets)
|
||||
set(WEBVIEW_DEFINITION -DREPOINDEX_USE_WEBENGINE)
|
||||
message(STATUS "Using Qt WebEngine as webview provider.")
|
||||
else()
|
||||
message(FATAL_ERROR "The specified webview provider '${WEBVIEW_PROVIDER}' is unknown.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# enable moc
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
@ -134,18 +181,33 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
|||
add_definitions(
|
||||
-D_GLIBCXX_USE_CXX11_ABI=0
|
||||
-DCMAKE_BUILD
|
||||
${WEBVIEW_DEFINITION}
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DDEBUG_BUILD)
|
||||
message(STATUS "Debug build enabled.")
|
||||
endif()
|
||||
|
||||
# executable and linking
|
||||
add_executable(${META_PROJECT_NAME} ${HEADER_FILES} ${SRC_FILES} ${WEB_FILES} ${RES_FILES})
|
||||
target_link_libraries(${META_PROJECT_NAME} c++utilities Qt5::Core Qt5::Concurrent Qt5::Network Qt5::WebSockets KF5::Archive)
|
||||
add_library(${META_PROJECT_NAME}-lib SHARED ${HEADER_FILES} ${SRC_FILES} ${WEB_FILES})
|
||||
target_link_libraries(${META_PROJECT_NAME}-lib c++utilities Qt5::Core Qt5::Concurrent Qt5::Network Qt5::WebSockets KF5::Archive)
|
||||
set_target_properties(${META_PROJECT_NAME}-lib PROPERTIES
|
||||
VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}
|
||||
SOVERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}
|
||||
CXX_STANDARD 11
|
||||
OUTPUT_NAME ${META_PROJECT_NAME}
|
||||
)
|
||||
add_executable(${META_PROJECT_NAME} ${CLI_HEADER_FILES} ${CLI_SRC_FILES})
|
||||
target_link_libraries(${META_PROJECT_NAME} c++utilities ${META_PROJECT_NAME}-lib Qt5::Core Qt5::Network Qt5::WebSockets)
|
||||
set_target_properties(${META_PROJECT_NAME} PROPERTIES
|
||||
CXX_STANDARD 11
|
||||
)
|
||||
if(NOT ${WEBVIEW_PROVIDER} STREQUAL "none")
|
||||
add_executable(${META_PROJECT_NAME}-gui ${GUI_HEADER_FILES} ${GUI_SRC_FILES})
|
||||
target_link_libraries(${META_PROJECT_NAME}-gui c++utilities qtutilities ${META_PROJECT_NAME}-lib Qt5::Core Qt5::Network Qt5::WebSockets ${WEBVIEW_PROVIDER})
|
||||
set_target_properties(${META_PROJECT_NAME}-gui PROPERTIES
|
||||
CXX_STANDARD 11
|
||||
)
|
||||
endif()
|
||||
|
||||
# add install target for web files / minimizing
|
||||
# -> don't minimize debug builds
|
||||
|
@ -211,6 +273,11 @@ foreach(WEB_FILE ${WEB_FILES})
|
|||
)
|
||||
endif()
|
||||
endforeach()
|
||||
install(
|
||||
FILES resources/icons/hicolor/scalable/apps/${META_PROJECT_NAME}.svg
|
||||
DESTINATION share/${META_PROJECT_NAME}/web/img
|
||||
COMPONENT web
|
||||
)
|
||||
# add target for minimizing
|
||||
if(HTML_MIN_FILES)
|
||||
add_custom_target(htmlmin ALL DEPENDS ${HTML_MIN_FILES})
|
||||
|
@ -220,10 +287,32 @@ if(JS_MIN_FILES)
|
|||
endif()
|
||||
|
||||
# add install target
|
||||
foreach(HEADER_FILE ${HEADER_FILES})
|
||||
get_filename_component(HEADER_DIR ${HEADER_FILE} DIRECTORY)
|
||||
install(
|
||||
FILES ${HEADER_FILE}
|
||||
DESTINATION include/${META_PROJECT_NAME}/${HEADER_DIR}
|
||||
COMPONENT header
|
||||
)
|
||||
endforeach()
|
||||
install(TARGETS ${META_PROJECT_NAME}
|
||||
RUNTIME DESTINATION bin
|
||||
COMPONENT binary
|
||||
)
|
||||
if(NOT ${WEBVIEW_PROVIDER} STREQUAL "none")
|
||||
install(TARGETS ${META_PROJECT_NAME}-gui
|
||||
RUNTIME DESTINATION bin
|
||||
COMPONENT binary-gui
|
||||
)
|
||||
endif()
|
||||
install(TARGETS ${META_PROJECT_NAME}-lib
|
||||
RUNTIME DESTINATION bin
|
||||
COMPONENT binary
|
||||
LIBRARY DESTINATION lib
|
||||
COMPONENT binary
|
||||
ARCHIVE DESTINATION lib
|
||||
COMPONENT binary
|
||||
)
|
||||
install(FILES resources/systemd/${META_PROJECT_NAME}.service
|
||||
DESTINATION lib/systemd/system
|
||||
COMPONENT service
|
||||
|
@ -232,18 +321,45 @@ install(FILES resources/settings/${META_PROJECT_NAME}.conf.js
|
|||
DESTINATION share/${META_PROJECT_NAME}/skel
|
||||
COMPONENT config
|
||||
)
|
||||
install(FILES resources/icons/hicolor/scalable/apps/${META_PROJECT_NAME}.svg
|
||||
DESTINATION share/icons/hicolor/scalable/apps
|
||||
COMPONENT desktop
|
||||
)
|
||||
install(FILES resources/desktop/applications/${META_PROJECT_NAME}.desktop
|
||||
DESTINATION share/applications
|
||||
COMPONENT desktop
|
||||
)
|
||||
if(NOT TARGET install-binary)
|
||||
add_custom_target(install-binary
|
||||
DEPENDS ${META_PROJECT_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=binary -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
|
||||
)
|
||||
endif()
|
||||
if((NOT ${WEBVIEW_PROVIDER} STREQUAL "none") AND (NOT TARGET install-binary-gui))
|
||||
set(GUI_INSTALL_TARGET "install-binary-gui")
|
||||
add_custom_target(install-binary-gui
|
||||
DEPENDS ${META_PROJECT_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=binary-gui -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
|
||||
)
|
||||
endif()
|
||||
if(NOT TARGET install-header)
|
||||
add_custom_target(install-header
|
||||
DEPENDS ${META_PROJECT_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=header -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
|
||||
)
|
||||
endif()
|
||||
if(NOT TARGET install-service)
|
||||
add_custom_target(install-service
|
||||
DEPENDS ${META_PROJECT_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=service -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
|
||||
)
|
||||
endif()
|
||||
if(NOT TARGET install-desktop)
|
||||
add_custom_target(install-desktop
|
||||
DEPENDS ${META_PROJECT_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=desktop -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
|
||||
)
|
||||
endif()
|
||||
if(NOT TARGET install-config)
|
||||
add_custom_target(install-config
|
||||
DEPENDS ${META_PROJECT_NAME}
|
||||
|
@ -258,7 +374,7 @@ if(NOT TARGET install-web)
|
|||
endif()
|
||||
if(NOT TARGET install-mingw-w64)
|
||||
add_custom_target(install-mingw-w64
|
||||
DEPENDS install-binary
|
||||
DEPENDS install-binary ${GUI_INSTALL_TARGET} install-header
|
||||
)
|
||||
endif()
|
||||
if(NOT TARGET install-binary-strip)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace ChronoUtilities;
|
||||
|
||||
namespace RepoIndex {
|
||||
|
||||
|
@ -37,25 +38,31 @@ using namespace Utilities;
|
|||
class LoadPackage
|
||||
{
|
||||
public:
|
||||
LoadPackage(AlpmDatabase *database, PackageOrigin origin) :
|
||||
LoadPackage(AlpmDatabase *database, PackageOrigin origin, DateTime descriptionsLastModified) :
|
||||
m_db(database),
|
||||
m_origin(origin)
|
||||
m_origin(origin),
|
||||
m_descriptionsLastModified(descriptionsLastModified)
|
||||
{}
|
||||
|
||||
void operator()(const QPair<QString, QList<QByteArray> > &description)
|
||||
{
|
||||
m_db->addPackageFromDescription(description.first, description.second, m_origin);
|
||||
m_db->addPackageFromDescription(description.first, description.second, m_origin, m_descriptionsLastModified);
|
||||
}
|
||||
|
||||
private:
|
||||
AlpmDatabase *const m_db;
|
||||
const PackageOrigin m_origin;
|
||||
const DateTime m_descriptionsLastModified;
|
||||
};
|
||||
|
||||
DatabaseError AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions)
|
||||
DatabaseError AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions, ChronoUtilities::DateTime *lastModified)
|
||||
{
|
||||
QFileInfo pathInfo(databasePath());
|
||||
if(pathInfo.isDir()) {
|
||||
if(lastModified) {
|
||||
// just use current date here since this is usually the local db
|
||||
*lastModified = DateTime::gmtNow();
|
||||
}
|
||||
static const QStringList relevantFiles = QStringList() << QStringLiteral("desc") << QStringLiteral("files");
|
||||
QDir dbDir(databasePath());
|
||||
QStringList pkgDirNames = dbDir.entryList(QDir::Dirs | QDir::Readable | QDir::Executable | QDir::NoDotAndDotDot);
|
||||
|
@ -83,6 +90,9 @@ DatabaseError AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArr
|
|||
}
|
||||
}
|
||||
} else if(pathInfo.isFile()) {
|
||||
if(lastModified) {
|
||||
*lastModified = DateTime::fromTimeStampGmt(pathInfo.lastModified().toUTC().toTime_t());
|
||||
}
|
||||
KTar tar(databasePath());
|
||||
const KArchiveDirectory *dbDir;
|
||||
if(tar.open(QIODevice::ReadOnly) && (dbDir = tar.directory())) {
|
||||
|
@ -125,8 +135,8 @@ DatabaseError AlpmDatabase::loadDescriptions(QList<QPair<QString, QList<QByteArr
|
|||
AlpmPackageLoader::AlpmPackageLoader(AlpmDatabase *repository, PackageOrigin origin) :
|
||||
m_db(repository)
|
||||
{
|
||||
if((m_error = repository->loadDescriptions(m_descriptions)) == DatabaseError::NoError) {
|
||||
m_future = QtConcurrent::map(m_descriptions, LoadPackage(repository, origin));
|
||||
if((m_error = repository->loadDescriptions(m_descriptions, &m_descriptionsLastModified)) == DatabaseError::NoError) {
|
||||
m_future = QtConcurrent::map(m_descriptions, LoadPackage(repository, origin, m_descriptionsLastModified));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,9 +167,6 @@ AlpmPackageLoader *AlpmDatabase::internalInit()
|
|||
origin = PackageOrigin::SyncDb;
|
||||
}
|
||||
|
||||
// wipe current packages
|
||||
wipePackages();
|
||||
|
||||
// initialization of packages is done concurrently via AlpmPackageLoader
|
||||
return new AlpmPackageLoader(this, origin);
|
||||
|
||||
|
@ -211,17 +218,19 @@ QNetworkRequest AlpmDatabase::filesDatabaseRequest()
|
|||
* - Does nothing if there is not at least one server URL available.
|
||||
* - Status messages are printed via cerr.
|
||||
*/
|
||||
void AlpmDatabase::downloadDatabase(const QString &targetDir, bool filesDatabase)
|
||||
bool AlpmDatabase::downloadDatabase(const QString &targetDir, bool filesDatabase)
|
||||
{
|
||||
QWriteLocker locker(lock());
|
||||
if(serverUrls().isEmpty()) {
|
||||
return; // no server URLs available
|
||||
return false; // no server URLs available
|
||||
}
|
||||
addBusyFlag();
|
||||
cerr << shchar << "Downloading " << (filesDatabase ? "files" : "regular") << " database for [" << name().toLocal8Bit().data() << "] from mirror " << serverUrls().front().toLocal8Bit().data() << " ..." << endl;
|
||||
QNetworkReply *reply = networkAccessManager().get(filesDatabase ? filesDatabaseRequest() : regularDatabaseRequest());
|
||||
reply->setProperty("filesDatabase", filesDatabase);
|
||||
m_downloadTargetDir = targetDir.isEmpty() ? QString(QChar('.')) : targetDir;
|
||||
connect(reply, &QNetworkReply::finished, this, &AlpmDatabase::databaseDownloadFinished);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -234,10 +243,8 @@ void AlpmDatabase::downloadDatabase(const QString &targetDir, bool filesDatabase
|
|||
*/
|
||||
void AlpmDatabase::refresh(const QString &targetDir)
|
||||
{
|
||||
if(serverUrls().isEmpty()) {
|
||||
if(!downloadDatabase(targetDir, true)) {
|
||||
init();
|
||||
} else {
|
||||
downloadDatabase(targetDir, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,6 +259,7 @@ std::unique_ptr<Package> AlpmDatabase::emptyPackage()
|
|||
*/
|
||||
void AlpmDatabase::databaseDownloadFinished()
|
||||
{
|
||||
removeBusyFlag();
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
bool filesDatabase = reply->property("filesDatabase").toBool();
|
||||
|
|
|
@ -38,6 +38,7 @@ private:
|
|||
AlpmDatabase *const m_db;
|
||||
DatabaseError m_error;
|
||||
QList<QPair<QString, QList<QByteArray> > > m_descriptions;
|
||||
ChronoUtilities::DateTime m_descriptionsLastModified;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -73,7 +74,7 @@ public:
|
|||
void setDatabasePath(const QString &dbPath);
|
||||
|
||||
// updating/refreshing
|
||||
void downloadDatabase(const QString &targetDir, bool filesDatabase = true);
|
||||
bool downloadDatabase(const QString &targetDir, bool filesDatabase = true);
|
||||
void refresh(const QString &targetDir);
|
||||
|
||||
protected:
|
||||
|
@ -83,7 +84,7 @@ private slots:
|
|||
void databaseDownloadFinished();
|
||||
|
||||
private:
|
||||
DatabaseError loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions);
|
||||
DatabaseError loadDescriptions(QList<QPair<QString, QList<QByteArray> > > &descriptions, ChronoUtilities::DateTime *lastModified = nullptr);
|
||||
QNetworkRequest regularDatabaseRequest();
|
||||
QNetworkRequest filesDatabaseRequest();
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
@ -40,6 +41,7 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
|
|||
rootdirArg("root-dir", "r", "specifies the root directory (default is /)"),
|
||||
dbpathArg("db-path", "d", "specifies the pacman database path (default is /var/lib/pacman)"),
|
||||
pacmanConfArg("pacman-conf", "p", "specifies the path of the pacman config file (default is /etc/pacman.conf"),
|
||||
reposFromPacmanConfEnabled("repos-from-pacman-conf", string(), "enables repositories from the pacman config file"),
|
||||
websocketAddrArg("addr", string(), "specifies the listening address for the websocket server, default is 127.0.0.1"),
|
||||
websocketPortArg("port", string(), "specifies the listening port for the websocket server, default is 1234"),
|
||||
certFileArg("cert-file", string(), "specifies the SSL certificate"),
|
||||
|
@ -84,6 +86,7 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
|
|||
pacmanConfArg.setCombinable(true);
|
||||
pacmanConfArg.setValueNames(pathValueName);
|
||||
pacmanConfArg.setRequiredValueCount(1);
|
||||
reposFromPacmanConfEnabled.setCombinable(true);
|
||||
websocketAddrArg.setCombinable(true);
|
||||
websocketAddrArg.setValueNames({"IP address"});
|
||||
websocketAddrArg.setRequiredValueCount(1);
|
||||
|
@ -127,7 +130,7 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
|
|||
shSyntaxArg.setCombinable(true);
|
||||
repoArg.setRequiredValueCount(1);
|
||||
repoArg.setValueNames({"repo name"});
|
||||
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &certFileArg, &keyFileArg, &websocketAddrArg, &websocketPortArg, &insecureArg, &aurArg, &shSyntaxArg});
|
||||
serverArg.setSecondaryArguments({&rootdirArg, &dbpathArg, &pacmanConfArg, &reposFromPacmanConfEnabled, &certFileArg, &keyFileArg, &websocketAddrArg, &websocketPortArg, &insecureArg, &aurArg, &shSyntaxArg});
|
||||
upgradeLookupArg.setSecondaryArguments({&shSyntaxArg});
|
||||
buildOrderArg.setSecondaryArguments({&aurArg, &addSourceOnlyDepsArg, &requireSourcesArg, &verboseArg, &shSyntaxArg});
|
||||
mingwBundleArg.setSecondaryArguments({&targetDirArg, &targetNameArg, &targetFormatArg, &iconThemesArg, &defaultIconThemeArg, &extraPackagesArg});
|
||||
|
@ -137,7 +140,7 @@ ConfigArgs::ConfigArgs(ArgumentParser &parser) :
|
|||
storageDirArg.setCombinable(true);
|
||||
storageDirArg.setRequiredValueCount(1);
|
||||
storageDirArg.setValueNames(pathValueName);
|
||||
parser.setMainArguments({&buildOrderArg, &upgradeLookupArg, &serverArg, &mingwBundleArg, &repoindexConfArg, &repoindexConfArg, &cacheDirArg, &helpArg});
|
||||
parser.setMainArguments({&buildOrderArg, &upgradeLookupArg, &serverArg, &mingwBundleArg, &repoindexConfArg, &repoindexConfArg, &cacheDirArg, &storageDirArg, &helpArg});
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -157,6 +160,7 @@ Config::Config() :
|
|||
m_websocketServerListeningAddr(QHostAddress::LocalHost),
|
||||
m_websocketServerListeningPort(1234),
|
||||
m_serverInsecure(false),
|
||||
m_serverCloseable(true),
|
||||
m_localEnabled(true),
|
||||
m_reposFromPacmanConfEnabled(false),
|
||||
m_aurEnabled(true),
|
||||
|
@ -280,8 +284,8 @@ void Config::loadFromConfigFile(const ConfigArgs &args)
|
|||
loadFromConfigFile(QString::fromLocal8Bit(args.repoindexConfArg.values().front().data()));
|
||||
return;
|
||||
} else {
|
||||
for(const auto &defaultPath : {QStringLiteral("./repoindex.conf"), QStringLiteral("/etc/repoindex.conf")}) {
|
||||
if(QFile::exists(defaultPath)) {
|
||||
for(const auto &defaultPath : {QStringLiteral("./repoindex.conf"), QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QStringLiteral("/repoindex.conf"), QStringLiteral("/etc/repoindex.conf")}) {
|
||||
if(!defaultPath.isEmpty() && QFile::exists(defaultPath)) {
|
||||
loadFromConfigFile(defaultPath);
|
||||
return;
|
||||
}
|
||||
|
@ -322,6 +326,18 @@ void Config::loadFromArgs(const ConfigArgs &args)
|
|||
if(args.storageDirArg.isPresent()) {
|
||||
m_storageDir = QString::fromLocal8Bit(args.storageDirArg.values().front().data());
|
||||
}
|
||||
if(args.reposFromPacmanConfEnabled.isPresent()) {
|
||||
m_reposFromPacmanConfEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Ensures that the server is only accessable from the local machine.
|
||||
*/
|
||||
void Config::loadLocalOnlySetup()
|
||||
{
|
||||
m_websocketServerListeningAddr = QHostAddress::LocalHost;
|
||||
m_serverInsecure = true;
|
||||
}
|
||||
|
||||
RepoEntry::RepoEntry() :
|
||||
|
|
|
@ -33,6 +33,7 @@ public:
|
|||
ApplicationUtilities::Argument rootdirArg;
|
||||
ApplicationUtilities::Argument dbpathArg;
|
||||
ApplicationUtilities::Argument pacmanConfArg;
|
||||
ApplicationUtilities::Argument reposFromPacmanConfEnabled;
|
||||
ApplicationUtilities::Argument websocketAddrArg;
|
||||
ApplicationUtilities::Argument websocketPortArg;
|
||||
ApplicationUtilities::Argument certFileArg;
|
||||
|
@ -143,6 +144,8 @@ public:
|
|||
const QString &serverCertFile() const;
|
||||
const QString &serverKeyFile() const;
|
||||
bool serverInsecure() const;
|
||||
bool isServerCloseable() const;
|
||||
void setServerCloseable(bool closeable);
|
||||
bool isLocalDatabaseEnabled() const;
|
||||
bool areReposFromPacmanConfEnabled() const;
|
||||
const QList<RepoEntry> &repoEntries() const;
|
||||
|
@ -154,6 +157,7 @@ public:
|
|||
void loadFromConfigFile(const QString &args);
|
||||
void loadFromConfigFile(const ConfigArgs &args);
|
||||
void loadFromArgs(const ConfigArgs &args);
|
||||
void loadLocalOnlySetup();
|
||||
|
||||
private:
|
||||
QString m_alpmRootDir;
|
||||
|
@ -167,6 +171,7 @@ private:
|
|||
QString m_serverCertFile;
|
||||
QString m_serverKeyFile;
|
||||
bool m_serverInsecure;
|
||||
bool m_serverCloseable;
|
||||
|
||||
QList<RepoEntry> m_repoEntries;
|
||||
bool m_localEnabled;
|
||||
|
@ -226,6 +231,16 @@ inline bool Config::serverInsecure() const
|
|||
return m_serverInsecure;
|
||||
}
|
||||
|
||||
inline bool Config::isServerCloseable() const
|
||||
{
|
||||
return m_serverCloseable;
|
||||
}
|
||||
|
||||
inline void Config::setServerCloseable(bool closeable)
|
||||
{
|
||||
m_serverCloseable = closeable;
|
||||
}
|
||||
|
||||
inline bool Config::isLocalDatabaseEnabled() const
|
||||
{
|
||||
return m_localEnabled;
|
||||
|
|
|
@ -523,9 +523,6 @@ void Manager::initAlpmDataBases()
|
|||
delete loader;
|
||||
}
|
||||
}
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
syncDbEntry.second->updateGroups();
|
||||
}
|
||||
if(m_config.isVerbose() || m_config.runServer()) {
|
||||
cerr << "DONE" << endl;
|
||||
}
|
||||
|
@ -547,6 +544,10 @@ void Manager::computeRequiredBy(Repository *repo)
|
|||
} else {
|
||||
relevantDbs.reserve(m_syncDbs.size());
|
||||
for(auto &syncDb : m_syncDbs) {
|
||||
if(syncDb->isBusy()) {
|
||||
syncDb->asSoonAsPossible(bind(&Manager::computeRequiredBy, this, repo));
|
||||
return;
|
||||
}
|
||||
relevantDbs << syncDb.get();
|
||||
}
|
||||
}
|
||||
|
@ -590,6 +591,7 @@ void Manager::writeCache()
|
|||
// could iterate through all repos and check isCachingUseful() but
|
||||
// currently its just the AUR which is needed to be cached
|
||||
if(userRepository()) {
|
||||
QDir().mkpath(config().cacheDir());
|
||||
QFile file(config().cacheDir() % QChar('/') % userRepository()->name() % QStringLiteral(".cache"));
|
||||
if(file.open(QFileDevice::WriteOnly)) {
|
||||
QDataStream stream(&file);
|
||||
|
@ -690,13 +692,17 @@ void Manager::setAutoUpdateEnabled(bool enabled)
|
|||
void Manager::updateAlpmDatabases()
|
||||
{
|
||||
if(localDatabase()) {
|
||||
QReadLocker locker(localDatabase()->lock());
|
||||
if(localDatabase()->hasOutdatedPackages()) {
|
||||
locker.unlock();
|
||||
localDatabase()->init();
|
||||
}
|
||||
}
|
||||
for(auto &syncDbEntry : m_syncDbMap) {
|
||||
if(syncDbEntry.second->hasOutdatedPackages()) {
|
||||
syncDbEntry.second->refresh(m_config.storageDir() + QStringLiteral("/sync"));
|
||||
for(auto &syncDb : m_syncDbs) {
|
||||
QReadLocker locker(syncDb->lock());
|
||||
if(syncDb->hasOutdatedPackages()) {
|
||||
locker.unlock();
|
||||
syncDb->refresh(m_config.storageDir() + QStringLiteral("/sync"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ namespace RepoIndex {
|
|||
Package::Package(const QString &name, Repository *repository) :
|
||||
m_origin(PackageOrigin::Unknown),
|
||||
m_repository(repository),
|
||||
m_timeStamp(DateTime::now()),
|
||||
m_hasGeneralInfo(false),
|
||||
m_hasAllGeneralInfo(false),
|
||||
m_name(name),
|
||||
|
|
|
@ -229,6 +229,7 @@ public:
|
|||
PackageOrigin origin() const;
|
||||
Repository *repository() const;
|
||||
ChronoUtilities::DateTime timeStamp() const;
|
||||
void setTimeStamp(ChronoUtilities::DateTime timeStamp);
|
||||
bool hasGeneralInfo() const;
|
||||
bool hasAllGeneralInfo() const;
|
||||
const QString &name() const;
|
||||
|
@ -427,6 +428,14 @@ inline ChronoUtilities::DateTime Package::timeStamp() const
|
|||
return m_timeStamp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the package's timestamp.
|
||||
*/
|
||||
inline void Package::setTimeStamp(ChronoUtilities::DateTime timeStamp)
|
||||
{
|
||||
m_timeStamp = timeStamp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether general information is available for the package.
|
||||
*/
|
||||
|
|
|
@ -73,6 +73,12 @@ const QStringList Repository::packageNames() const
|
|||
return names;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Updates the groups.
|
||||
*
|
||||
* This method is automatically after initialization, so there is usually no need
|
||||
* to call this method manually.
|
||||
*/
|
||||
void Repository::updateGroups()
|
||||
{
|
||||
m_groups.clear();
|
||||
|
@ -101,9 +107,12 @@ PackageLoader *Repository::init()
|
|||
{
|
||||
addBusyFlag();
|
||||
QWriteLocker locker(lock());
|
||||
// wipe current packages
|
||||
wipePackages();
|
||||
if(PackageLoader *loader = internalInit()) {
|
||||
if(loader->future().isRunning()) {
|
||||
auto watcher = new QFutureWatcher<void>;
|
||||
connect(watcher, &QFutureWatcher<void>::finished, this, &Repository::updateGroups);
|
||||
connect(watcher, &QFutureWatcher<void>::finished, this, &Repository::removeBusyFlag);
|
||||
connect(watcher, &QFutureWatcher<void>::finished, this, &Repository::initialized);
|
||||
connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater);
|
||||
|
@ -111,20 +120,30 @@ PackageLoader *Repository::init()
|
|||
}
|
||||
return loader;
|
||||
} else {
|
||||
updateGroups();
|
||||
removeBusyFlag();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Repository::initAsSoonAsPossible()
|
||||
{
|
||||
asSoonAsPossible(bind(&Repository::init, this));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Performs the specified \a operation as soon as possible.
|
||||
*/
|
||||
void Repository::asSoonAsPossible(std::function<void ()> operation)
|
||||
{
|
||||
if(isBusy()) {
|
||||
auto connection = make_shared<QMetaObject::Connection>();
|
||||
*connection = connect(this, &Repository::available, [connection, this] {
|
||||
*connection = connect(this, &Repository::available, [connection, operation] {
|
||||
disconnect(*connection);
|
||||
init();
|
||||
operation();
|
||||
});
|
||||
} else {
|
||||
init();
|
||||
operation();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,30 +302,6 @@ QList<Package *> Repository::packageByFilter(std::function<bool (const Package *
|
|||
* \cond
|
||||
*/
|
||||
|
||||
class Blocker
|
||||
{
|
||||
public:
|
||||
Blocker(const QList<Repository *> &relevantRepos)
|
||||
{
|
||||
m_blockedRepos.reserve(relevantRepos.size());
|
||||
for(Repository *repo : relevantRepos) {
|
||||
if(repo->lock()->tryLockForWrite()) {
|
||||
m_blockedRepos << repo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Blocker()
|
||||
{
|
||||
for(Repository *repo : m_blockedRepos) {
|
||||
repo->lock()->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QList<Repository *> m_blockedRepos;
|
||||
};
|
||||
|
||||
class ComputeRequired
|
||||
{
|
||||
public:
|
||||
|
@ -555,7 +550,7 @@ void Repository::restoreFromCacheStream(QDataStream &in, bool skipOutdated)
|
|||
quint32 packageCount;
|
||||
in >> packageCount;
|
||||
bool good = true;
|
||||
const auto now = DateTime::now();
|
||||
const auto now = DateTime::gmtNow();
|
||||
for(quint32 i = 0; i < packageCount && good && in.status() == QDataStream::Ok; ++i) {
|
||||
if(auto package = emptyPackage()) {
|
||||
package->restoreFromCacheStream(in);
|
||||
|
@ -632,7 +627,7 @@ void Repository::cleanOutdatedPackages()
|
|||
if(maxPackageAge().isInfinity()) {
|
||||
return;
|
||||
}
|
||||
auto now = DateTime::now();
|
||||
auto now = DateTime::gmtNow();
|
||||
for(auto i = m_packages.begin(); i != m_packages.end(); ) {
|
||||
const Package &pkg = *i->second;
|
||||
if((now - pkg.timeStamp()) > maxPackageAge()) {
|
||||
|
@ -652,10 +647,9 @@ bool Repository::hasOutdatedPackages()
|
|||
if(maxPackageAge().isInfinity()) {
|
||||
return false;
|
||||
}
|
||||
auto now = DateTime::now();
|
||||
for(auto i = m_packages.begin(); i != m_packages.end(); ) {
|
||||
const Package &pkg = *i->second;
|
||||
if((now - pkg.timeStamp()) > maxPackageAge()) {
|
||||
auto now = DateTime::gmtNow();
|
||||
for(const auto &pkgEntry : m_packages) {
|
||||
if((now - pkgEntry.second->timeStamp()) > maxPackageAge()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -866,7 +860,7 @@ void Repository::parseDescriptions(const QList<QByteArray> &descriptions, QStrin
|
|||
* \returns Returns the added/updated packages. In the case of a split package more then
|
||||
* one package is returned.
|
||||
*/
|
||||
QList<Package *> Repository::addPackagesFromSrcInfo(const QByteArray &srcInfo)
|
||||
QList<Package *> Repository::addPackagesFromSrcInfo(const QByteArray &srcInfo, ChronoUtilities::DateTime timeStamp)
|
||||
{
|
||||
// define states
|
||||
enum {
|
||||
|
@ -965,6 +959,7 @@ QList<Package *> Repository::addPackagesFromSrcInfo(const QByteArray &srcInfo)
|
|||
pkg = emptyPackage();
|
||||
}
|
||||
currentPackage = pkg.get();
|
||||
currentPackage->setTimeStamp(timeStamp);
|
||||
packageInfo.clear();
|
||||
}
|
||||
// add field to ...
|
||||
|
@ -1007,7 +1002,7 @@ QList<Package *> Repository::addPackagesFromSrcInfo(const QByteArray &srcInfo)
|
|||
* - If \a name is empty and the description doesn't provide a name either, the package can not be added.
|
||||
* \returns Returns the added/updated package or nullptr if no package could be added.
|
||||
*/
|
||||
Package *Repository::addPackageFromDescription(QString name, const QList<QByteArray> &descriptions, PackageOrigin origin)
|
||||
Package *Repository::addPackageFromDescription(QString name, const QList<QByteArray> &descriptions, PackageOrigin origin, DateTime timeStamp)
|
||||
{
|
||||
// parse fields
|
||||
QList<QPair<QString, QStringList> > fields;
|
||||
|
@ -1021,6 +1016,7 @@ Package *Repository::addPackageFromDescription(QString name, const QList<QByteAr
|
|||
// find/create package for description
|
||||
auto pkg = emptyPackage();
|
||||
Package *pkgRawPtr = pkg.get();
|
||||
pkgRawPtr->setTimeStamp(timeStamp);
|
||||
pkgRawPtr->putDescription(name, fields, origin);
|
||||
{
|
||||
QWriteLocker locker(&m_lock);
|
||||
|
|
|
@ -212,16 +212,16 @@ public:
|
|||
bool isPackageOnly() const;
|
||||
std::map<QString, QList<Package *> > &groups();
|
||||
const std::map<QString, QList<Package *> > &groups() const;
|
||||
void updateGroups();
|
||||
Q_SLOT void updateGroups();
|
||||
const QStringList &serverUrls() const;
|
||||
QStringList &serverUrls();
|
||||
SignatureLevel sigLevel() const;
|
||||
void setSigLevel(SignatureLevel sigLevel);
|
||||
|
||||
// gathering data
|
||||
public:
|
||||
PackageLoader *init();
|
||||
Q_SLOT PackageLoader *init();
|
||||
void initAsSoonAsPossible();
|
||||
void asSoonAsPossible(std::function<void(void)> operation);
|
||||
virtual PackageLoader *internalInit();
|
||||
virtual PackageDetailAvailability requestsRequired(PackageDetail packageDetail = PackageDetail::Basics) const;
|
||||
virtual SuggestionsReply *requestSuggestions(const QString &phrase);
|
||||
|
@ -273,8 +273,8 @@ public:
|
|||
// parsing src/pkg info
|
||||
static void parsePkgInfo(const QByteArray &pkgInfo, QString &name, QList<QPair<QString, QString> > packageInfo);
|
||||
static void parseDescriptions(const QList<QByteArray> &descriptions, QString &name, QList<QPair<QString, QStringList> > &fields);
|
||||
QList<Package *> addPackagesFromSrcInfo(const QByteArray &srcInfo);
|
||||
Package *addPackageFromDescription(QString name, const QList<QByteArray> &descriptions, PackageOrigin origin);
|
||||
QList<Package *> addPackagesFromSrcInfo(const QByteArray &srcInfo, ChronoUtilities::DateTime timeStamp);
|
||||
Package *addPackageFromDescription(QString name, const QList<QByteArray> &descriptions, PackageOrigin origin, ChronoUtilities::DateTime timeStamp);
|
||||
|
||||
// thread synchronization
|
||||
QReadWriteLock *lock() const;
|
||||
|
@ -576,6 +576,7 @@ inline void Repository::setMaxPackageAge(ChronoUtilities::TimeSpan maxPackageAge
|
|||
inline void Repository::wipePackages()
|
||||
{
|
||||
m_packages.clear();
|
||||
m_groups.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <QReadWriteLock>
|
||||
|
||||
using namespace std;
|
||||
using namespace ApplicationUtilities;
|
||||
using namespace RepoIndex;
|
|
@ -0,0 +1,106 @@
|
|||
#include "./alpm/manager.h"
|
||||
#include "./alpm/utilities.h"
|
||||
#include "./alpm/config.h"
|
||||
|
||||
#include "./network/server.h"
|
||||
|
||||
#include "./gui/mainwindow.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <qtutilities/resources/qtconfigarguments.h>
|
||||
#include <qtutilities/resources/resources.h>
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
#include <c++utilities/conversion/stringconversion.h>
|
||||
#include <c++utilities/application/failure.h>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace ApplicationUtilities;
|
||||
using namespace RepoIndex;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// setup the argument parser
|
||||
ArgumentParser parser;
|
||||
SET_APPLICATION_INFO;
|
||||
SET_QT_APPLICATION_INFO;
|
||||
QT_CONFIG_ARGUMENTS qtConfigArgs;
|
||||
ConfigArgs configArgs(parser);
|
||||
parser.setIgnoreUnknownArguments(false);
|
||||
Argument webdirArg("web-dir", string(), "specifies the directory of the web files");
|
||||
webdirArg.setCombinable(true);
|
||||
webdirArg.setRequiredValueCount(1);
|
||||
webdirArg.setValueNames({"path"});
|
||||
for(Argument *arg : initializer_list<Argument *>{&configArgs.rootdirArg, &configArgs.dbpathArg, &configArgs.pacmanConfArg, &configArgs.reposFromPacmanConfEnabled, &configArgs.aurArg}) {
|
||||
qtConfigArgs.qtWidgetsGuiArg().addSecondaryArgument(arg);
|
||||
}
|
||||
parser.setMainArguments({&qtConfigArgs.qtWidgetsGuiArg(), &configArgs.repoindexConfArg, &configArgs.repoindexConfArg, &webdirArg, &configArgs.cacheDirArg, &configArgs.storageDirArg, &configArgs.helpArg});
|
||||
// parse command line arguments
|
||||
try {
|
||||
parser.parseArgs(argc, argv);
|
||||
} catch (const Failure &e) {
|
||||
cerr << shchar << "Unable to parse arguments: " << e.what() << endl;
|
||||
return 2;
|
||||
}
|
||||
try {
|
||||
// load configuration
|
||||
Config config;
|
||||
config.loadFromConfigFile(configArgs);
|
||||
config.loadFromArgs(configArgs);
|
||||
config.loadLocalOnlySetup();
|
||||
config.setServerCloseable(false);
|
||||
|
||||
if(qtConfigArgs.qtWidgetsGuiArg().isPresent()) {
|
||||
// configure Qt
|
||||
qtConfigArgs.applySettings();
|
||||
|
||||
// find directory with web files
|
||||
QString webdir;
|
||||
if(webdirArg.isPresent()) {
|
||||
webdir = QString::fromLocal8Bit(webdirArg.values().front().data());
|
||||
} else {
|
||||
webdir = QStringLiteral("/usr/share/" PROJECT_NAME "/web");
|
||||
}
|
||||
|
||||
// create app
|
||||
QApplication application(argc, argv);
|
||||
MainWindow mainWindow(webdir);
|
||||
mainWindow.show();
|
||||
|
||||
// setup manager
|
||||
Manager manager(config);
|
||||
cerr << shchar << "Loading databases ..." << endl;
|
||||
if(config.areReposFromPacmanConfEnabled()) {
|
||||
manager.addDataBasesFromPacmanConfig();
|
||||
}
|
||||
manager.addDatabasesFromRepoIndexConfig();
|
||||
manager.initAlpmDataBases();
|
||||
cerr << shchar << "Restoring cache ... ";
|
||||
manager.restoreCache();
|
||||
cerr << shchar << "DONE" << endl;
|
||||
|
||||
// setup the server
|
||||
Server server(manager, manager.config());
|
||||
manager.setAutoCacheMaintenanceEnabled(true);
|
||||
manager.setAutoUpdateEnabled(true);
|
||||
|
||||
// run Qt event loop
|
||||
return application.exec();
|
||||
|
||||
} else if(!configArgs.helpArg.isPresent()) {
|
||||
if(useShSyntax) {
|
||||
cerr << "export REPOINDEX_ERROR='No command line arguments specified. See --help for available commands.'" << endl;
|
||||
} else {
|
||||
cerr << "No command line arguments specified. See --help for available commands." << endl;
|
||||
}
|
||||
}
|
||||
} catch (const exception &ex) {
|
||||
Utilities::printError(ex);
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
#include "./mainwindow.h"
|
||||
#include "./webpage.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QClipboard>
|
||||
#include <QSettings>
|
||||
#include <QStringBuilder>
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
|
||||
namespace RepoIndex {
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new main window.
|
||||
*/
|
||||
MainWindow::MainWindow(const QString &webdir)
|
||||
{
|
||||
QSettings settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
|
||||
setWindowTitle(QStringLiteral(APP_NAME));
|
||||
setWindowIcon(QIcon::fromTheme(QStringLiteral(PROJECT_NAME)));
|
||||
m_webView.setPage(new WebPage(&m_webView));
|
||||
QUrl url(QStringLiteral("file://") % webdir % QStringLiteral("/index.html"));
|
||||
url.setFragment(settings.value(QStringLiteral("fragment"), QStringLiteral("packages")).toString());
|
||||
m_webView.setUrl(url);
|
||||
m_webView.setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(&m_webView, &QWidget::customContextMenuRequested, this, &MainWindow::showInfoWebViewContextMenu);
|
||||
setCentralWidget(&m_webView);
|
||||
restoreGeometry(settings.value(QStringLiteral("geometry")).toByteArray());
|
||||
}
|
||||
|
||||
bool MainWindow::event(QEvent *event)
|
||||
{
|
||||
switch(event->type()) {
|
||||
case QEvent::KeyRelease: {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
switch(keyEvent->key()) {
|
||||
case Qt::Key_Left:
|
||||
case Qt::Key_Back:
|
||||
m_webView.back();
|
||||
return true;
|
||||
case Qt::Key_Right:
|
||||
case Qt::Key_Forward:
|
||||
m_webView.forward();
|
||||
return true;
|
||||
default:
|
||||
;
|
||||
}
|
||||
} case QEvent::MouseButtonPress: {
|
||||
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
|
||||
switch(mouseEvent->button()) {
|
||||
case Qt::BackButton:
|
||||
m_webView.back();
|
||||
return true;
|
||||
case Qt::ForwardButton:
|
||||
m_webView.forward();
|
||||
return true;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
} case QEvent::Close: {
|
||||
// save settings
|
||||
QSettings settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
|
||||
settings.setValue(QStringLiteral("geometry"), saveGeometry());
|
||||
settings.setValue(QStringLiteral("fragment"), m_webView.url().fragment());
|
||||
break;
|
||||
} default:
|
||||
;
|
||||
}
|
||||
return QMainWindow::event(event);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Shows the context menu for the info web view.
|
||||
*/
|
||||
void MainWindow::showInfoWebViewContextMenu(const QPoint &)
|
||||
{
|
||||
QAction copyAction(QIcon::fromTheme(QStringLiteral("edit-copy")), tr("Copy"), nullptr);
|
||||
copyAction.setDisabled(m_webView.selectedText().isEmpty());
|
||||
connect(©Action, &QAction::triggered, this, &MainWindow::copyInfoWebViewSelection);
|
||||
QMenu menu;
|
||||
menu.addAction(©Action);
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Copies the current selection of the info web view.
|
||||
*/
|
||||
void MainWindow::copyInfoWebViewSelection()
|
||||
{
|
||||
QGuiApplication::clipboard()->setText(m_webView.selectedText());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include "./webviewprovider.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#ifdef REPOINDEX_USE_WEBENGINE
|
||||
# include <QWebEngineView>
|
||||
#else
|
||||
# include <QWebView>
|
||||
#endif
|
||||
|
||||
namespace RepoIndex {
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MainWindow(const QString &webdir);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event);
|
||||
|
||||
private slots:
|
||||
void showInfoWebViewContextMenu(const QPoint &);
|
||||
void copyInfoWebViewSelection();
|
||||
|
||||
private:
|
||||
WEB_VIEW_PROVIDER m_webView;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,51 @@
|
|||
#include "./webpage.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#ifdef REPOINDEX_USE_WEBENGINE
|
||||
# include <QWebEngineSettings>
|
||||
# include <QWebEngineView>
|
||||
#else
|
||||
# include <QWebSettings>
|
||||
# include <QWebView>
|
||||
#endif
|
||||
|
||||
namespace RepoIndex {
|
||||
|
||||
WebPage::WebPage(WEB_VIEW_PROVIDER *view) :
|
||||
WEB_PAGE_PROVIDER(view),
|
||||
m_view(view)
|
||||
{
|
||||
#ifdef REPOINDEX_USE_WEBENGINE
|
||||
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
|
||||
#else
|
||||
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
|
||||
#endif
|
||||
if(!m_view) {
|
||||
// delegate to external browser if no view is assigned
|
||||
connect(this, &WebPage::urlChanged, this, &WebPage::delegateToExternalBrowser);
|
||||
m_view = new WEB_VIEW_PROVIDER;
|
||||
m_view->setPage(this);
|
||||
}
|
||||
}
|
||||
|
||||
WEB_PAGE_PROVIDER *WebPage::createWindow(QWebEnginePage::WebWindowType type)
|
||||
{
|
||||
return new WebPage;
|
||||
}
|
||||
|
||||
void WebPage::delegateToExternalBrowser(const QUrl &url)
|
||||
{
|
||||
openUrlExternal(url);
|
||||
// this page and the associated view are useless
|
||||
m_view->deleteLater();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void WebPage::openUrlExternal(const QUrl &url)
|
||||
{
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef WEBPAGE_H
|
||||
#define WEBPAGE_H
|
||||
|
||||
#include "./webviewprovider.h"
|
||||
|
||||
#ifdef REPOINDEX_USE_WEBENGINE
|
||||
# include <QWebEnginePage>
|
||||
#else
|
||||
# include <QWebPage>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(WEB_VIEW_PROVIDER)
|
||||
|
||||
namespace RepoIndex {
|
||||
|
||||
class WebPage : public WEB_PAGE_PROVIDER
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebPage(WEB_VIEW_PROVIDER *view = nullptr);
|
||||
|
||||
public slots:
|
||||
void openUrlExternal(const QUrl &url);
|
||||
|
||||
protected:
|
||||
WEB_PAGE_PROVIDER *createWindow(WebWindowType type);
|
||||
|
||||
private slots:
|
||||
void delegateToExternalBrowser(const QUrl &url);
|
||||
|
||||
private:
|
||||
WEB_VIEW_PROVIDER *m_view;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WEBPAGE_H
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef WEB_VIEW_PROVIDER
|
||||
#ifdef REPOINDEX_USE_WEBENGINE
|
||||
# define WEB_VIEW_PROVIDER QWebEngineView
|
||||
# define WEB_PAGE_PROVIDER QWebEnginePage
|
||||
#else
|
||||
# define WEB_VIEW_PROVIDER QWebView
|
||||
# define WEB_PAGE_PROVIDER QWebPage
|
||||
#endif
|
||||
#endif
|
|
@ -132,7 +132,7 @@ void Connection::handleCmd(const QJsonObject &obj)
|
|||
const auto what = obj.value(QStringLiteral("w")).toString();
|
||||
const auto id = obj.value(QStringLiteral("id"));
|
||||
if(what == QLatin1String("stop")) {
|
||||
if(m_socket->peerAddress().isLoopback()) {
|
||||
if(m_manager.config().isServerCloseable() && m_socket->peerAddress().isLoopback()) {
|
||||
cerr << shchar << "Info: Server stopped via web interface." << endl;
|
||||
QCoreApplication::quit();
|
||||
} else {
|
||||
|
|
|
@ -55,6 +55,7 @@ void AurPackageReply::processData(QNetworkReply *reply)
|
|||
QJsonParseError error;
|
||||
const auto doc = QJsonDocument::fromJson(reply->readAll(), &error);
|
||||
if(error.error == QJsonParseError::NoError) {
|
||||
auto now = DateTime::gmtNow();
|
||||
QWriteLocker locker(m_repo->lock());
|
||||
auto &packages = m_repo->packages();
|
||||
for(const auto &result : doc.object().value(QStringLiteral("results")).toArray()) {
|
||||
|
@ -67,6 +68,7 @@ void AurPackageReply::processData(QNetworkReply *reply)
|
|||
} else {
|
||||
package = make_unique<AurPackage>(obj, static_cast<UserRepository *>(m_userRepo));
|
||||
}
|
||||
package->setTimeStamp(now);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -113,7 +115,7 @@ void AurFullPackageReply::processData(QNetworkReply *reply)
|
|||
if(srcInfoEntry && srcInfoEntry->isFile()) {
|
||||
const auto srcInfo = static_cast<const KArchiveFile *>(srcInfoEntry)->data();
|
||||
QWriteLocker locker(m_userRepo->lock());
|
||||
const auto packages = m_userRepo->addPackagesFromSrcInfo(srcInfo);
|
||||
const auto packages = m_userRepo->addPackagesFromSrcInfo(srcInfo, DateTime::gmtNow());
|
||||
// TODO: error handling
|
||||
for(const auto &entryName : baseDir->entries()) {
|
||||
if(entryName != QLatin1String(".SRCINFO")) {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env xdg-open
|
||||
[Desktop Entry]
|
||||
Name=Repository Browser
|
||||
Comment=An Arch Linux repository browser.
|
||||
Exec=sh -c "repoindex-gui --repos-from-pacman-conf --cache-dir \\"\\$HOME/.cache/Martchus/Repository Browser\\" --storage-dir \\"\\$HOME/.config/Martchus/Repository Browser\\""
|
||||
Icon=repoindex
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;
|
|
@ -0,0 +1,286 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="128.47652mm"
|
||||
height="128.47652mm"
|
||||
viewBox="0 0 455.23172 455.23175"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="repoindex.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4156">
|
||||
<stop
|
||||
style="stop-color:#0088cc;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4158" />
|
||||
<stop
|
||||
style="stop-color:#005b88;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4160" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4342">
|
||||
<stop
|
||||
style="stop-color:#ffe247;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4344" />
|
||||
<stop
|
||||
id="stop4350"
|
||||
offset="0.67710614"
|
||||
style="stop-color:#ffe113;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#eeff88;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4346" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="-30.507242 : 472.74589 : 1"
|
||||
inkscape:vp_y="0 : 590.9609 : 0"
|
||||
inkscape:vp_z="1084.0486 : 472.74589 : 1"
|
||||
inkscape:persp3d-origin="526.77064 : 369.09507 : 1"
|
||||
id="perspective4148" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4342"
|
||||
id="radialGradient4352"
|
||||
cx="321.2179"
|
||||
cy="524.50543"
|
||||
fx="321.2179"
|
||||
fy="524.50543"
|
||||
r="150.79008"
|
||||
gradientTransform="matrix(1,0,0,1.0631051,-255.83411,84.518479)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4156"
|
||||
id="linearGradient4162"
|
||||
x1="291.74536"
|
||||
y1="713.38061"
|
||||
x2="452.79324"
|
||||
y2="761.86792"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4156"
|
||||
id="linearGradient4178"
|
||||
x1="396.44601"
|
||||
y1="557.31207"
|
||||
x2="439.7518"
|
||||
y2="461.3476"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4156"
|
||||
id="linearGradient4186"
|
||||
x1="134.08012"
|
||||
y1="532.63721"
|
||||
x2="333.1778"
|
||||
y2="477.0788"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="172.38789"
|
||||
inkscape:cy="222.01635"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1039"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="18"
|
||||
inkscape:window-maximized="1"
|
||||
showguides="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-95.085314,-386.88471)">
|
||||
<path
|
||||
style="fill:#6ccfff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.954;stroke-linecap:butt;stroke-miterlimit:1.45000005;stroke-dasharray:none;stroke-dashoffset:1.99954295;stroke-opacity:1"
|
||||
d="m 154.4949,579.62112 167.59343,-58.47155 164.83869,74.78194 -189.49553,65.00691 z"
|
||||
id="rect4358"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:url(#linearGradient4178);fill-opacity:1;stroke:#ffffff;stroke-width:2.95759749;stroke-linecap:butt;stroke-miterlimit:1.45000005;stroke-dasharray:none;stroke-dashoffset:2;stroke-opacity:1"
|
||||
d="m 319.56719,519.77647 69.28807,-53.41761 160.99173,68.71253 -61.3275,58.59649 z"
|
||||
id="rect4214-0"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:url(#linearGradient4186);fill-opacity:1;stroke:#ffffff;stroke-width:2.95759749;stroke-linecap:butt;stroke-miterlimit:1.45000005;stroke-dasharray:none;stroke-dashoffset:1;stroke-opacity:1"
|
||||
d="m 96.802346,515.47264 162.667844,-52.81423 65.66299,57.4282 -171.00252,60.24593 z"
|
||||
id="rect4214"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:url(#radialGradient4352);fill-opacity:1;fill-rule:nonzero;stroke:#434343;stroke-width:2.854;stroke-linecap:butt;stroke-miterlimit:1.45000005;stroke-dasharray:none;stroke-dashoffset:2;stroke-opacity:1"
|
||||
id="path4140"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="74.886345"
|
||||
sodipodi:cy="642.12262"
|
||||
sodipodi:rx="159.61505"
|
||||
sodipodi:ry="159.61505"
|
||||
sodipodi:start="0.55248493"
|
||||
sodipodi:end="5.7870476"
|
||||
d="M 210.75436,725.88925 A 159.61505,159.61505 0 0 1 29.193706,795.05773 159.61505,159.61505 0 0 1 -84.665363,637.62628 159.61505,159.61505 0 0 1 37.879134,486.85694 159.61505,159.61505 0 0 1 215.25624,566.14067 L 74.886345,642.12262 Z"
|
||||
transform="matrix(0.91759584,-0.39751462,0.39751462,0.91759584,0,0)" />
|
||||
<circle
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#434343;stroke-width:2.183;stroke-linecap:butt;stroke-miterlimit:1.45000005;stroke-dasharray:none;stroke-dashoffset:1.99954295;stroke-opacity:1"
|
||||
id="path4144"
|
||||
cx="95.899529"
|
||||
cy="548.30194"
|
||||
r="21.144077"
|
||||
transform="matrix(0.91759584,-0.39751462,0.39751462,0.91759584,0,0)" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4202"
|
||||
style="fill:url(#linearGradient4162);fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2.95759749;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.45000005;stroke-dasharray:none;stroke-dashoffset:2;stroke-opacity:1"
|
||||
d="m 293.22416,660.48706 2.11255,172.15066 195.37368,-65.01376 0,-173.50047 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4202-6"
|
||||
style="fill:#0088cc;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2.95759749;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.45000005;stroke-dasharray:none;stroke-dashoffset:2;stroke-opacity:1"
|
||||
d="m 298.74404,658.26061 -2.11338,172.15066 -146.8618,-84.02688 4.22513,-167.16276 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<g
|
||||
id="g4269"
|
||||
transform="matrix(0.40448316,-0.14264259,0,0.40448316,264.26281,440.48667)">
|
||||
<g
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
id="g2809"
|
||||
transform="matrix(0.55789384,0,0,0.55789384,-34.632645,718.71563)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2284"
|
||||
d="m 339.96875,309.09375 c -14.47141,-0.0239 -26.4812,2.94367 -31.125,4.5625 l -4.78125,25.8125 c -0.0116,0.0951 23.79543,-6.34855 34.28125,-5.96875 17.36158,0.62381 18.95948,6.63541 18.65625,14.75 0.29595,0.47462 -4.47933,-7.33192 -19.5,-7.59375 -18.94961,-0.32687 -45.69284,6.70947 -45.65625,35.3125 -0.51086,32.17412 24.03361,41.63882 40.75,41.8125 15.02821,-0.27364 22.0777,-5.69136 25.9375,-8.59375 5.07124,-5.30236 10.87308,-10.63447 16.40625,-17.03125 -5.23567,9.51278 -9.77472,16.0898 -14.5,21.125 l 0,4.25 22.84375,-3.84375 0.15625,-62.09375 c -0.23141,-8.78839 5.04123,-42.41827 -43.46875,-42.5 z m -3.28125,54.0625 c 9.46889,0.12995 20.32788,4.79708 20.34375,16.03125 0.049,10.21821 -12.80005,15.71183 -21.15625,15.625 -8.35976,-0.0868 -19.45093,-6.56982 -19.5,-16.53125 0.16016,-8.90444 10.45953,-15.35418 20.3125,-15.125 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2286"
|
||||
d="m 398.50106,314.83145 -0.15505,102.82693 26.61213,-5.12724 0.0449,-58.30157 c 0.006,-8.68089 12.40554,-18.82451 27.9627,-18.66287 3.30202,-5.97408 9.5087,-21.24219 11.02088,-24.71514 -34.75649,-0.0833 -35.19897,9.98993 -41.24398,14.94517 -0.0631,-9.45285 -0.0213,-15.12741 -0.0213,-15.12741 l -24.2202,4.16213 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2288"
|
||||
d="m 548.2688,328.33058 c -0.25696,-0.12068 -13.87938,-15.93419 -41.26638,-16.0589 -25.65249,-0.42638 -54.42578,9.51895 -54.88631,52.5328 0.22457,37.81852 27.6402,52.59809 55.0314,52.88627 29.31292,0.30451 40.97654,-18.32947 41.67615,-18.79124 -3.49762,-3.0321 -16.59792,-16.0131 -16.59792,-16.0131 0,0 -8.18236,11.65102 -24.05802,11.79913 -15.87942,0.1512 -29.68245,-12.27325 -29.87805,-29.60905 -0.20349,-17.33595 12.68881,-26.72821 29.99725,-27.48687 14.98466,-0.003 23.6297,9.67334 23.6297,9.67334 l 16.35218,-18.93238 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2290"
|
||||
d="m 581.8125,278.84375 -25.125,5.90625 0.1875,133.9375 24.75,-4.46875 0.28125,-63.03125 c 0.0529,-6.60927 9.56127,-16.75916 25.4375,-16.4375 15.17973,0.15775 18.57236,10.11767 18.53125,11.375 l 0.4375,72.96875 24.40625,-4.3125 0.0937,-77.375 c 0.1607,-7.44539 -16.30833,-23.16954 -42.78125,-23.28125 -12.58087,0.0202 -19.54815,2.86825 -23.09375,4.96875 -6.06656,4.68565 -12.9998,9.17543 -19.8125,14.90625 6.29809,-8.09099 11.58551,-13.68516 16.75,-17.84375 l -0.0625,-37.3125 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
id="g5326"
|
||||
transform="matrix(0.82595102,0,0.01168815,0.82595102,1.9788752,356.03908)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2292"
|
||||
d="m 400.67581,629.79609 7.68167,-1.91575 -0.92851,91.20792 -7.79574,1.32426 1.04258,-90.61643 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2294"
|
||||
d="m 421.10266,657.01757 6.75064,-2.9867 -0.86808,65.39931 -6.49779,1.33915 0.61523,-63.75176 z m -1.26059,-23.58316 5.47167,-4.41533 4.42261,4.99952 -5.47558,4.53221 -4.4187,-5.1164 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2296"
|
||||
d="m 440.44273,655.82614 7.67755,-1.56201 -0.1573,13.6722 c -0.007,0.58717 4.4194,-15.27364 24.68502,-14.92094 19.67986,0.10952 22.68401,15.34634 22.5291,18.76237 l -0.43759,48.0783 -6.73044,1.45631 0.63316,-47.489 c 0.0974,-1.38684 -2.88144,-13.11441 -16.78906,-13.15754 -13.90509,-0.0404 -23.68364,10.10048 -23.75821,16.57937 l -0.48127,41.83477 -7.80388,2.0313 0.63292,-65.28513 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2298"
|
||||
d="m 561.53301,720.20203 -7.6776,1.56186 0.15737,-13.67198 c 0.007,-0.58742 -4.42201,15.27361 -24.68504,14.92086 -19.67983,-0.10944 -22.68399,-15.34626 -22.52908,-18.76229 l 0.43757,-48.07861 8.15674,-1.64226 -0.54644,47.48988 c -0.0149,1.29682 1.36845,13.29979 15.27604,13.3426 13.90511,0.0405 23.76622,-8.37359 24.01453,-21.04416 l 0.43105,-37.46902 7.5978,-1.93195 -0.63294,65.28507 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path2300"
|
||||
d="m 577.45461,655.28678 -5.42715,4.20017 20.19894,26.93328 -22.39092,31.11622 5.63499,4.226 21.04365,-28.8967 20.8779,29.58159 5.32727,-4.20103 -22.37578,-31.62866 18.56963,-25.5775 -5.53193,-4.73429 -16.92109,23.66778 -19.00551,-24.68686 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.8746356,0,0,0.8746356,-66.545022,716.81646)"
|
||||
style="font-style:normal;font-weight:normal;font-size:8.44138241px;font-family:'DejaVu Sans Mono';fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
id="text2634">
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
id="path7858"
|
||||
d="m 685.46692,263.83624 0,-5.32944 -1.99082,0 0,-0.71307 4.7895,0 0,0.71307 -1.99906,0 0,5.32944 -0.79962,0"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
id="path7860"
|
||||
d="m 689.0982,263.83624 0,-6.04251 1.20355,0 1.43026,4.2784 c 0.13189,0.39843 0.22806,0.69658 0.28852,0.89442 0.0687,-0.21983 0.17586,-0.5427 0.3215,-0.96862 l 1.44674,-4.2042 1.07578,0 0,6.04251 -0.77077,0 0,-5.05741 -1.75587,5.05741 -0.72131,0 -1.74763,-5.14396 0,5.14396 -0.77077,0"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="text2638"
|
||||
style="font-style:normal;font-weight:normal;font-size:8.25130367px;font-family:'DejaVu Sans Mono';fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
transform="matrix(0.8746356,0,0,0.8746356,136.49564,557.21236)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 239.84053,313.69965 0,-5.20945 -1.94598,0 0,-0.697 4.68164,0 0,0.697 -1.95404,0 0,5.20945 -0.78162,0"
|
||||
id="path7853"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 243.39004,313.69965 0,-5.90645 1.17646,0 1.39805,4.18205 c 0.12892,0.38947 0.22293,0.6809 0.28202,0.87429 0.0671,-0.21488 0.1719,-0.53048 0.31426,-0.94681 l 1.41417,-4.10953 1.05155,0 0,5.90645 -0.75341,0 0,-4.94353 -1.71634,4.94353 -0.70506,0 -1.70828,-5.02814 0,5.02814 -0.75342,0"
|
||||
id="path7855"
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#0088cc;fill-opacity:1;stroke:#ffffff;stroke-width:2.95759749;stroke-linecap:butt;stroke-miterlimit:1.45000005;stroke-dasharray:none;stroke-dashoffset:1;stroke-opacity:1"
|
||||
d="m 106.65363,647.21618 46.11221,-67.92278 146.48421,78.50104 -44.86588,74.74063 z"
|
||||
id="rect4214-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 397.52991,637.10169 c -4.91286,13.77761 -7.87608,22.70147 -13.34592,36.31744 3.3537,2.37223 7.47021,5.0604 14.15539,7.37845 -7.18724,-0.42289 -12.08981,-1.66325 -15.75358,-3.4524 -7.00035,17.07601 -17.9679,41.75123 -40.22458,89.59085 17.49302,-16.26801 31.05326,-27.27622 43.69078,-34.10863 -0.54266,-2.1426 -0.85118,-4.55844 -0.83023,-7.20003 l 0.0207,-0.56771 c 0.27757,-11.30518 6.10759,-21.97959 13.01382,-23.82991 6.90623,-1.85034 12.27437,5.82171 11.99681,17.1269 -0.0522,2.12726 -0.29008,4.23986 -0.7057,6.26802 12.50031,-1.96301 25.91556,-0.48374 43.17189,3.39314 -3.40263,-5.06448 -6.43974,-9.64038 -9.34007,-13.99569 -4.56852,-1.92979 -9.3337,-4.85783 -19.05376,-6.41897 6.68102,-0.62015 11.46453,-0.30414 15.19319,0.61964 -29.48858,-44.50331 -31.87652,-50.95656 -41.98881,-71.12108 z"
|
||||
id="path2518-2"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 16 KiB |
|
@ -21,10 +21,12 @@
|
|||
},
|
||||
|
||||
"repos": {
|
||||
"localEnabled": true,
|
||||
"fromPacmanConfig": true,
|
||||
|
||||
"add": [
|
||||
"//add": [
|
||||
{"name": "examplerepo",
|
||||
"maxAge": 3600
|
||||
"dataBaseFile": "path/to/database/file",
|
||||
"sourcesDir": "path/to/local/source/dir",
|
||||
"packagesDir": "path/to/local/pkg/dir",
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
"upgradeSources": ["aur"],
|
||||
"server": [
|
||||
"https://localhost/repo/arch/$repo/os/$arch"
|
||||
]
|
||||
],
|
||||
"maxAge": 3600
|
||||
},
|
||||
|
||||
{"name": "local", "maxAge": 3600},
|
||||
|
|
|
@ -229,6 +229,7 @@
|
|||
<h4 class="modal-title">About</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="text-align: center;">
|
||||
<img src="img/@META_PROJECT_NAME@.svg" style="width: 256px; height: 256px;" />
|
||||
<h2>@META_APP_NAME@</h2>
|
||||
<p>@META_APP_VERSION@</p>
|
||||
<p style="margin: 25px 0px;">
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
};
|
||||
|
||||
this.socket.onerror = function(event) {
|
||||
repoindex.pageManager.addError("Connecting to server failed: " + event.reason);
|
||||
repoindex.pageManager.addError("Connecting to server failed.");
|
||||
repoindex.pageManager.setConnectionStatus(repoindex.ConnectionStatus.Disconnected);
|
||||
};
|
||||
};
|
||||
|
@ -514,11 +514,14 @@
|
|||
};
|
||||
|
||||
// create a global client used within the entire document
|
||||
repoindex.client = new Client((window.location.protocol === "https:" ? "wss://" : "ws://") + document.domain + ":1234");
|
||||
repoindex.client = new Client((window.location.protocol === "https:" ? "wss://" : "ws://") + (window.location.protocol === "file:" ? "localhost" : document.domain) + ":1234");
|
||||
if(!repoindex.isLoopback(document.domain)) {
|
||||
// hide stop button if server is not on loopback interface
|
||||
document.getElementById("nav_server").style.display = "none";
|
||||
}
|
||||
if(window.location.protocol === "file:") {
|
||||
document.getElementById("nav_connect").style.display = "none";
|
||||
}
|
||||
|
||||
return repoindex;
|
||||
|
||||
|
|
|
@ -86,6 +86,8 @@
|
|||
repoArray.push({index: entry.info.index, name: entry.info.name});
|
||||
}
|
||||
entriesRequired = true;
|
||||
} else {
|
||||
entry.updateTableRow();
|
||||
}
|
||||
}, mgr.filteredEntries.length);
|
||||
var updateTableRows = function() {
|
||||
|
|
|
@ -37,7 +37,9 @@
|
|||
return false;
|
||||
};
|
||||
this.link.appendChild(document.createTextNode(repoName));
|
||||
this.link.title = repoInfo.desc;
|
||||
if(repoInfo.desc) {
|
||||
this.link.title = repoInfo.desc;
|
||||
}
|
||||
|
||||
// use Bootstrap tooltip
|
||||
this.link.setAttribute("data-placement", "bottom");
|
||||
|
|
Loading…
Reference in New Issue