mirror of
https://github.com/kiwix/libkiwix.git
synced 2025-12-29 17:38:06 -05:00
Compare commits
40 Commits
httplib
...
kiwix_serv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4ba474576 | ||
|
|
03a929e88e | ||
|
|
646502f9cf | ||
|
|
a8a96a99f4 | ||
|
|
a517d3b529 | ||
|
|
60f0f81286 | ||
|
|
2ed9a50eca | ||
|
|
bce922ab89 | ||
|
|
ad7a63a471 | ||
|
|
6e8200637e | ||
|
|
cc45c840d1 | ||
|
|
0590f27fa1 | ||
|
|
dd27c3a873 | ||
|
|
736841818d | ||
|
|
c1868e22f4 | ||
|
|
aabfc1d82e | ||
|
|
2effb3490e | ||
|
|
55672b0288 | ||
|
|
0abbeabfe2 | ||
|
|
1bf52e8ebe | ||
|
|
e2db1b3688 | ||
|
|
0b6b6716de | ||
|
|
18b6433322 | ||
|
|
b70c92cade | ||
|
|
09d843da3a | ||
|
|
fa83a61a54 | ||
|
|
967eb10cbf | ||
|
|
feeee25eac | ||
|
|
1c0b4502cd | ||
|
|
6f639144ab | ||
|
|
a94a03cd22 | ||
|
|
bc821638da | ||
|
|
bcece66960 | ||
|
|
c046f64d83 | ||
|
|
75b4d311d7 | ||
|
|
a236751c74 | ||
|
|
7d68926539 | ||
|
|
940368b8ac | ||
|
|
0594e60df3 | ||
|
|
b5c1b26761 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
ARCHIVE_NAME=deps2_osx_native_dyn_kiwix-lib.tar.xz
|
||||
ARCHIVE_NAME=deps2_osx_native_dyn_libkiwix.tar.xz
|
||||
wget -O- http://tmp.kiwix.org/ci/${ARCHIVE_NAME} | tar -xJ -C $HOME
|
||||
- name: Compile
|
||||
shell: bash
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
ARCHIVE_NAME=deps2_${OS_NAME}_${{matrix.target}}_kiwix-lib.tar.xz
|
||||
ARCHIVE_NAME=deps2_${OS_NAME}_${{matrix.target}}_libkiwix.tar.xz
|
||||
wget -O- http://tmp.kiwix.org/ci/${ARCHIVE_NAME} | tar -xJ -C /home/runner
|
||||
- name: Compile
|
||||
shell: bash
|
||||
|
||||
15
.github/workflows/package.yml
vendored
15
.github/workflows/package.yml
vendored
@@ -7,7 +7,12 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
distro: [ubuntu-hirsute, ubuntu-groovy, ubuntu-focal, ubuntu-bionic]
|
||||
distro:
|
||||
- ubuntu-impish
|
||||
- ubuntu-hirsute
|
||||
- ubuntu-groovy
|
||||
- ubuntu-focal
|
||||
- ubuntu-bionic
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -30,6 +35,14 @@ jobs:
|
||||
email: release+launchpad@kiwix.org
|
||||
distro: ${{ matrix.distro }}
|
||||
|
||||
- uses: legoktm/gh-action-build-deb@ubuntu-impish
|
||||
if: matrix.distro == 'ubuntu-impish'
|
||||
name: Build package for ubuntu-impish
|
||||
id: build-ubuntu-impish
|
||||
with:
|
||||
args: --no-sign
|
||||
ppa: ${{ steps.ppa.outputs.ppa }}
|
||||
|
||||
- uses: legoktm/gh-action-build-deb@ubuntu-hirsute
|
||||
if: matrix.distro == 'ubuntu-hirsute'
|
||||
name: Build package for ubuntu-hirsute
|
||||
|
||||
@@ -7,6 +7,7 @@ files=(
|
||||
"include/common/otherTools.h"
|
||||
"include/common/regexTools.h"
|
||||
"include/common/networkTools.h"
|
||||
"include/common/archiveTools.h"
|
||||
"include/manager.h"
|
||||
"include/reader.h"
|
||||
"include/kiwix.h"
|
||||
@@ -22,6 +23,7 @@ files=(
|
||||
"src/common/pathTools.cpp"
|
||||
"src/common/regexTools.cpp"
|
||||
"src/common/otherTools.cpp"
|
||||
"src/common/archiveTools.cpp"
|
||||
"src/common/networkTools.cpp"
|
||||
"src/common/stringTools.cpp"
|
||||
"src/xapianSearcher.cpp"
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <zim/archive.h>
|
||||
|
||||
#include "book.h"
|
||||
#include "bookmark.h"
|
||||
@@ -146,6 +147,7 @@ class Library
|
||||
{
|
||||
std::map<std::string, kiwix::Book> m_books;
|
||||
std::map<std::string, std::shared_ptr<Reader>> m_readers;
|
||||
std::map<std::string, std::shared_ptr<zim::Archive>> m_archives;
|
||||
std::vector<kiwix::Bookmark> m_bookmarks;
|
||||
class BookDB;
|
||||
std::unique_ptr<BookDB> m_bookDB;
|
||||
@@ -198,6 +200,7 @@ class Library
|
||||
const Book& getBookByPath(const std::string& path) const;
|
||||
Book& getBookByPath(const std::string& path);
|
||||
std::shared_ptr<Reader> getReaderById(const std::string& id);
|
||||
std::shared_ptr<zim::Archive> getArchiveById(const std::string& id);
|
||||
|
||||
/**
|
||||
* Remove a book from the library.
|
||||
|
||||
@@ -13,18 +13,8 @@ headers = [
|
||||
'search_renderer.h',
|
||||
'server.h',
|
||||
'kiwixserve.h',
|
||||
'name_mapper.h'
|
||||
'name_mapper.h',
|
||||
'tools.h'
|
||||
]
|
||||
|
||||
install_headers(headers, subdir:'kiwix')
|
||||
|
||||
install_headers(
|
||||
'tools/base64.h',
|
||||
'tools/networkTools.h',
|
||||
'tools/otherTools.h',
|
||||
'tools/pathTools.h',
|
||||
'tools/regexTools.h',
|
||||
'tools/stringTools.h',
|
||||
subdir:'kiwix/tools'
|
||||
)
|
||||
|
||||
|
||||
33
include/opds_catalog.h
Normal file
33
include/opds_catalog.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KIWIX_OPDS_CATALOG_H
|
||||
#define KIWIX_OPDS_CATALOG_H
|
||||
|
||||
|
||||
#include "library.h"
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
|
||||
std::string getSearchUrl(const Filter& f);
|
||||
|
||||
} // namespace kiwix
|
||||
|
||||
#endif // KIWIX_OPDS_CATALOG_H
|
||||
@@ -26,9 +26,6 @@
|
||||
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "tools/base64.h"
|
||||
#include "tools/pathTools.h"
|
||||
#include "tools/regexTools.h"
|
||||
#include "library.h"
|
||||
#include "reader.h"
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
#include <string>
|
||||
#include "common.h"
|
||||
#include "entry.h"
|
||||
#include "tools/pathTools.h"
|
||||
#include "tools/stringTools.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -41,26 +39,26 @@ namespace kiwix
|
||||
* The SuggestionItem is a helper class that contains the info about a single
|
||||
* suggestion item.
|
||||
*/
|
||||
|
||||
class SuggestionItem
|
||||
{
|
||||
// Functions
|
||||
private:
|
||||
// Temporarily making the constructor public until the code move is complete
|
||||
public:
|
||||
// Create a sugggestion item.
|
||||
explicit SuggestionItem(std::string title, std::string normalizedTitle,
|
||||
std::string path, std::string snippet = "") :
|
||||
explicit SuggestionItem(const std::string& title, const std::string& normalizedTitle,
|
||||
const std::string& path, const std::string& snippet = "") :
|
||||
title(title),
|
||||
normalizedTitle(normalizedTitle),
|
||||
path(path),
|
||||
snippet(snippet) {}
|
||||
|
||||
public:
|
||||
const std::string getTitle() {return title;}
|
||||
const std::string getNormalizedTitle() {return normalizedTitle;}
|
||||
const std::string getPath() {return path;}
|
||||
const std::string getSnippet() {return snippet;}
|
||||
const std::string& getTitle() const { return title;}
|
||||
const std::string& getNormalizedTitle() const { return normalizedTitle;}
|
||||
const std::string& getPath() const { return path;}
|
||||
const std::string& getSnippet() const { return snippet;}
|
||||
|
||||
const bool hasSnippet() {return !snippet.empty();}
|
||||
bool hasSnippet() const { return !snippet.empty();}
|
||||
|
||||
// Data
|
||||
private:
|
||||
@@ -91,6 +89,13 @@ class Reader
|
||||
* (.zim extesion).
|
||||
*/
|
||||
explicit Reader(const string zimFilePath);
|
||||
|
||||
/**
|
||||
* Create a Reader to read a zim file given by the Archive.
|
||||
*
|
||||
* @param archive The shared pointer to the Archive object.
|
||||
*/
|
||||
explicit Reader(const std::shared_ptr<zim::Archive> archive);
|
||||
#ifndef _WIN32
|
||||
explicit Reader(int fd);
|
||||
Reader(int fd, zim::offset_type offset, zim::size_type size);
|
||||
@@ -488,7 +493,7 @@ class Reader
|
||||
zim::Archive* getZimArchive() const;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<zim::Archive> zimArchive;
|
||||
std::shared_ptr<zim::Archive> zimArchive;
|
||||
std::string zimFilePath;
|
||||
|
||||
SuggestionsList_t suggestions;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#define KIWIX_SEARCH_RENDERER_H
|
||||
|
||||
#include <string>
|
||||
#include <zim/search.h>
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
@@ -40,6 +41,8 @@ class SearchRenderer
|
||||
* Used to generate pagination links.
|
||||
*/
|
||||
SearchRenderer(Searcher* searcher, NameMapper* mapper);
|
||||
SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper,
|
||||
unsigned int start, unsigned int estimatedResultCount);
|
||||
|
||||
~SearchRenderer();
|
||||
|
||||
@@ -74,7 +77,7 @@ class SearchRenderer
|
||||
|
||||
protected:
|
||||
std::string beautifyInteger(const unsigned int number);
|
||||
Searcher* mp_searcher;
|
||||
zim::SearchResultSet m_srs;
|
||||
NameMapper* mp_nameMapper;
|
||||
std::string searchContent;
|
||||
std::string searchPattern;
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "tools/pathTools.h"
|
||||
#include "tools/stringTools.h"
|
||||
|
||||
#include <zim/search.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -142,6 +142,11 @@ class Searcher
|
||||
*/
|
||||
unsigned int getEstimatedResultCount();
|
||||
|
||||
/**
|
||||
* Get a SearchResultSet object for current search
|
||||
*/
|
||||
zim::SearchResultSet getSearchResultSet();
|
||||
|
||||
unsigned int getResultStart() { return resultStart; }
|
||||
unsigned int getResultEnd() { return resultEnd; }
|
||||
|
||||
|
||||
171
include/tools.h
Normal file
171
include/tools.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2021 Matthieu Gautier <mgautier@kymeria.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KIWIX_TOOLS_H
|
||||
#define KIWIX_TOOLS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace kiwix {
|
||||
|
||||
/**
|
||||
* Return the current directory.
|
||||
*
|
||||
* @return the current directory (utf8 encoded)
|
||||
*/
|
||||
std::string getCurrentDirectory();
|
||||
|
||||
/**
|
||||
* Return the data directory.
|
||||
*
|
||||
* The data directory is a directory where to put data (zim files, ...)
|
||||
* It depends of the platform and it may be changed by user using environment variable.
|
||||
*
|
||||
* The resolution order is :
|
||||
* - `KIWIX_DATA_DIR` env variable (if set).
|
||||
* - On Windows :
|
||||
* . `$APPDATA/kiwix` if $APPDATA is set
|
||||
* . `$USERPROFILE/kiwix` if $USERPROFILE is set
|
||||
* - Else :
|
||||
* . `$XDG_DATA_HOME/kiwix`if $XDG_DATA_HOME is set
|
||||
* . `$HOME/.local/share/kiwx` if $HOWE is set
|
||||
* - current directory
|
||||
*
|
||||
* @return the path of the data directory (utf8 encoded)
|
||||
*/
|
||||
std::string getDataDirectory();
|
||||
|
||||
/** Return the path of the executable
|
||||
*
|
||||
* Some application may be packaged in auto extractible archive (Appimage) and the
|
||||
* real executable is different of the path of the archive.
|
||||
* If `realPathOnly` is true, return the path of the real executable instead of the
|
||||
* archive launched by the user.
|
||||
*
|
||||
* @param realPathOnly If we must return the real path of the executable.
|
||||
* @return the path of the executable (utf8 encoded)
|
||||
*/
|
||||
std::string getExecutablePath(bool realPathOnly = false);
|
||||
|
||||
/** Tell if the path is a relative path.
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools
|
||||
* to manipulate paths.
|
||||
*
|
||||
* @param path A utf8 encoded path.
|
||||
* @return true if the path is relative.
|
||||
*/
|
||||
bool isRelativePath(const std::string& path);
|
||||
|
||||
/** Append a path to another one.
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools
|
||||
* to manipulate paths.
|
||||
*
|
||||
* @param basePath the base path.
|
||||
* @param relativePath a path to add to the base path, must be a relative path.
|
||||
* @return The concatenation of the paths, using the right separator.
|
||||
*/
|
||||
std::string appendToDirectory(const std::string& basePath, const std::string& relativePath);
|
||||
|
||||
/** Remove the last element of a path.
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools
|
||||
* to manipulate paths.
|
||||
*
|
||||
* @param path a path.
|
||||
* @return The parent directory (or empty string if none).
|
||||
*/
|
||||
std::string removeLastPathElement(const std::string& path);
|
||||
|
||||
/** Get the last element of a path.
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools
|
||||
* to manipulate paths.
|
||||
*
|
||||
* @param path a path.
|
||||
* @return The base name of the path or empty string if none (ending with a separator).
|
||||
*/
|
||||
std::string getLastPathElement(const std::string& path);
|
||||
|
||||
/** Compute the absolute path of a relative path based on another one
|
||||
*
|
||||
* Equivalent to appendToDirectory followed by a normalization of the path.
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools
|
||||
* to manipulate paths.
|
||||
*
|
||||
* @param path the base path (if empty, current directory is taken).
|
||||
* @param relativePath the relative path.
|
||||
* @return a absolute path.
|
||||
*/
|
||||
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath);
|
||||
|
||||
/** Compute the relative path of a path relative to another one
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools
|
||||
* to manipulate paths.
|
||||
*
|
||||
* @param path the base path.
|
||||
* @param absolutePath the absolute path to find the relative path for.
|
||||
* @return a relative path (pointing to absolutePath, relative to path).
|
||||
*/
|
||||
std::string computeRelativePath(const std::string& path, const std::string& absolutePath);
|
||||
|
||||
/** Sleep the current thread.
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools.
|
||||
*
|
||||
* @param milliseconds The number of milliseconds to wait for.
|
||||
*/
|
||||
void sleep(unsigned int milliseconds);
|
||||
|
||||
/** Split a string
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools.
|
||||
*
|
||||
* Assuming text = "foo:;bar;baz,oups;"
|
||||
*
|
||||
* split(text, ":;", true, true) => ["foo", ":", ";", "bar", ";", "baz,oups", ";"]
|
||||
* split(text, ":;", true, false) => ["foo", "bar", "baz,oups"] (default)
|
||||
* split(text, ":;", false, true) => ["foo", ":", "", ";", "bar", ";", "baz,oups", ";", ""]
|
||||
* split(text, ":;", false, false) => ["foo", "", "bar", "baz,oups", ""]
|
||||
*
|
||||
* @param str The string to split.
|
||||
* @param delims A string of potential delimiters.
|
||||
* Each charater in the string can be a individual delimiters.
|
||||
* @param dropEmpty true if empty part must be dropped from the result.
|
||||
* @param keepDelim true if delimiter must be included from the result.
|
||||
* @return a list of part (potentially containing delimiters)
|
||||
*/
|
||||
std::vector<std::string> split(const std::string& str, const std::string& delims, bool dropEmpty=true, bool keepDelim = false);
|
||||
|
||||
/** Convert language code from iso2 code to iso3
|
||||
*
|
||||
* This function is provided as a small helper. It is probably better to use native tools
|
||||
* to manipulate locales.
|
||||
*
|
||||
* @param a2code a iso2 code string.
|
||||
* @return the corresponding iso3 code.
|
||||
* @throw std::out_of_range if iso2 code is not known.
|
||||
*/
|
||||
std::string converta2toa3(const std::string& a2code);
|
||||
}
|
||||
#endif // KIWIX_TOOLS_H
|
||||
@@ -6,10 +6,10 @@
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <tools/otherTools.h>
|
||||
#include <tools/pathTools.h>
|
||||
#include <tools/stringTools.h>
|
||||
#include <downloader.h> // For AriaError
|
||||
#include "tools.h"
|
||||
#include "tools/pathTools.h"
|
||||
#include "tools/stringTools.h"
|
||||
#include "downloader.h" // For AriaError
|
||||
|
||||
#ifdef _WIN32
|
||||
# define ARIA2_CMD "aria2c.exe"
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
#include "book.h"
|
||||
#include "reader.h"
|
||||
|
||||
#include "tools.h"
|
||||
#include "tools/base64.h"
|
||||
#include "tools/regexTools.h"
|
||||
#include "tools/networkTools.h"
|
||||
#include "tools/otherTools.h"
|
||||
#include "tools/stringTools.h"
|
||||
#include "tools/pathTools.h"
|
||||
|
||||
#include <pugixml.hpp>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "tools.h"
|
||||
#include "tools/pathTools.h"
|
||||
#include "tools/stringTools.h"
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "reader.h"
|
||||
#include "libxml_dumper.h"
|
||||
|
||||
#include "tools.h"
|
||||
#include "tools/base64.h"
|
||||
#include "tools/regexTools.h"
|
||||
#include "tools/pathTools.h"
|
||||
@@ -108,6 +109,7 @@ bool Library::removeBookById(const std::string& id)
|
||||
{
|
||||
m_bookDB->delete_document("Q" + id);
|
||||
m_readers.erase(id);
|
||||
m_archives.erase(id);
|
||||
return m_books.erase(id) == 1;
|
||||
}
|
||||
|
||||
@@ -146,11 +148,35 @@ std::shared_ptr<Reader> Library::getReaderById(const std::string& id)
|
||||
return m_readers.at(id);
|
||||
} catch (std::out_of_range& e) {}
|
||||
|
||||
try {
|
||||
auto reader = make_shared<Reader>(m_archives.at(id));
|
||||
m_readers[id] = reader;
|
||||
return reader;
|
||||
} catch (std::out_of_range& e) {}
|
||||
|
||||
auto book = getBookById(id);
|
||||
if (!book.isPathValid())
|
||||
return nullptr;
|
||||
auto sptr = make_shared<Reader>(book.getPath());
|
||||
m_readers[id] = sptr;
|
||||
|
||||
auto archive = make_shared<zim::Archive>(book.getPath());
|
||||
m_archives[id] = archive;
|
||||
auto reader = make_shared<Reader>(archive);
|
||||
m_readers[id] = reader;
|
||||
return reader;
|
||||
}
|
||||
|
||||
std::shared_ptr<zim::Archive> Library::getArchiveById(const std::string& id)
|
||||
{
|
||||
try {
|
||||
return m_archives.at(id);
|
||||
} catch (std::out_of_range& e) {}
|
||||
|
||||
auto book = getBookById(id);
|
||||
if (!book.isPathValid())
|
||||
return nullptr;
|
||||
|
||||
auto sptr = make_shared<zim::Archive>(book.getPath());
|
||||
m_archives[id] = sptr;
|
||||
return sptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
#include "libxml_dumper.h"
|
||||
#include "book.h"
|
||||
|
||||
#include "tools.h"
|
||||
#include "tools/base64.h"
|
||||
#include "tools/stringTools.h"
|
||||
#include "tools/otherTools.h"
|
||||
#include "tools/pathTools.h"
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
#include "tools.h"
|
||||
#include "tools/pathTools.h"
|
||||
|
||||
#include <pugixml.hpp>
|
||||
|
||||
@@ -19,6 +19,7 @@ kiwix_sources = [
|
||||
'tools/stringTools.cpp',
|
||||
'tools/networkTools.cpp',
|
||||
'tools/otherTools.cpp',
|
||||
'tools/archiveTools.cpp',
|
||||
'kiwixserve.cpp',
|
||||
'name_mapper.cpp',
|
||||
'server/byte_range.cpp',
|
||||
@@ -26,7 +27,8 @@ kiwix_sources = [
|
||||
'server/request_context.cpp',
|
||||
'server/response.cpp',
|
||||
'server/internalServer.cpp',
|
||||
'server/internalServer_catalog_v2.cpp'
|
||||
'server/internalServer_catalog_v2.cpp',
|
||||
'opds_catalog.cpp'
|
||||
]
|
||||
kiwix_sources += lib_resources
|
||||
|
||||
|
||||
74
src/opds_catalog.cpp
Normal file
74
src/opds_catalog.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2021 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "opds_catalog.h"
|
||||
#include "tools/stringTools.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
const char opdsSearchEndpoint[] = "/catalog/v2/entries";
|
||||
|
||||
enum Separator { AMP };
|
||||
|
||||
std::ostringstream& operator<<(std::ostringstream& oss, Separator sep)
|
||||
{
|
||||
if ( oss.tellp() > 0 )
|
||||
oss << "&";
|
||||
return oss;
|
||||
}
|
||||
|
||||
std::string buildSearchString(const Filter& f)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
if ( f.hasQuery() )
|
||||
oss << AMP << "q=" << urlEncode(f.getQuery());
|
||||
|
||||
if ( f.hasCategory() )
|
||||
oss << AMP << "category=" << urlEncode(f.getCategory());
|
||||
|
||||
if ( f.hasLang() )
|
||||
oss << AMP << "lang=" << urlEncode(f.getLang());
|
||||
|
||||
if ( f.hasName() )
|
||||
oss << AMP << "name=" << urlEncode(f.getName());
|
||||
|
||||
if ( !f.getAcceptTags().empty() )
|
||||
oss << AMP << "tag=" << urlEncode(join(f.getAcceptTags(), ";"));
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
std::string getSearchUrl(const Filter& f)
|
||||
{
|
||||
const std::string searchString = buildSearchString(f);
|
||||
|
||||
if ( searchString.empty() )
|
||||
return opdsSearchEndpoint;
|
||||
else
|
||||
return opdsSearchEndpoint + ("?" + searchString);
|
||||
}
|
||||
|
||||
} // namespace kiwix
|
||||
@@ -20,11 +20,12 @@
|
||||
#include "opds_dumper.h"
|
||||
#include "book.h"
|
||||
|
||||
#include "tools/otherTools.h"
|
||||
|
||||
#include "kiwixlib-resources.h"
|
||||
#include <mustache.hpp>
|
||||
|
||||
#include "tools/stringTools.h"
|
||||
#include "tools/otherTools.h"
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
#include <zim/item.h>
|
||||
#include <zim/error.h>
|
||||
|
||||
#include "tools.h"
|
||||
#include "tools/stringTools.h"
|
||||
#include "tools/otherTools.h"
|
||||
#include "tools/archiveTools.h"
|
||||
|
||||
inline char hi(char v)
|
||||
{
|
||||
@@ -86,6 +89,11 @@ Reader::Reader(const string zimFilePath)
|
||||
srand(time(nullptr));
|
||||
}
|
||||
|
||||
Reader::Reader(const std::shared_ptr<zim::Archive> archive)
|
||||
: zimArchive(archive),
|
||||
zimFilePath(archive->getFilename())
|
||||
{}
|
||||
|
||||
#ifndef _WIN32
|
||||
Reader::Reader(int fd)
|
||||
: zimArchive(new zim::Archive(fd)),
|
||||
@@ -183,14 +191,7 @@ Entry Reader::getMainPage() const
|
||||
|
||||
bool Reader::getFavicon(string& content, string& mimeType) const
|
||||
{
|
||||
try {
|
||||
auto item = zimArchive->getIllustrationItem();
|
||||
content = item.getData();
|
||||
mimeType = item.getMimetype();
|
||||
return true;
|
||||
} catch(zim::EntryNotFound& e) {};
|
||||
|
||||
return false;
|
||||
return kiwix::getArchiveFavicon(*zimArchive, content, mimeType);
|
||||
}
|
||||
|
||||
string Reader::getZimFilePath() const
|
||||
@@ -212,47 +213,32 @@ bool Reader::getMetadata(const string& name, string& value) const
|
||||
|
||||
string Reader::getName() const
|
||||
{
|
||||
METADATA("Name")
|
||||
return kiwix::getMetaName(*zimArchive);
|
||||
}
|
||||
|
||||
string Reader::getTitle() const
|
||||
{
|
||||
string value = zimArchive->getMetadata("Title");
|
||||
if (value.empty()) {
|
||||
value = getLastPathElement(zimFilePath);
|
||||
std::replace(value.begin(), value.end(), '_', ' ');
|
||||
size_t pos = value.find(".zim");
|
||||
value = value.substr(0, pos);
|
||||
}
|
||||
return value;
|
||||
return kiwix::getArchiveTitle(*zimArchive);
|
||||
}
|
||||
|
||||
string Reader::getCreator() const
|
||||
{
|
||||
METADATA("Creator")
|
||||
return kiwix::getMetaCreator(*zimArchive);
|
||||
}
|
||||
|
||||
string Reader::getPublisher() const
|
||||
{
|
||||
METADATA("Publisher")
|
||||
return kiwix::getMetaPublisher(*zimArchive);
|
||||
}
|
||||
|
||||
string Reader::getDate() const
|
||||
{
|
||||
METADATA("Date")
|
||||
return kiwix::getMetaDate(*zimArchive);
|
||||
}
|
||||
|
||||
string Reader::getDescription() const
|
||||
{
|
||||
string value;
|
||||
this->getMetadata("Description", value);
|
||||
|
||||
/* Mediawiki Collection tends to use the "Subtitle" name */
|
||||
if (value.empty()) {
|
||||
this->getMetadata("Subtitle", value);
|
||||
}
|
||||
|
||||
return value;
|
||||
return kiwix::getMetaDescription(*zimArchive);
|
||||
}
|
||||
|
||||
string Reader::getLongDescription() const
|
||||
@@ -262,7 +248,7 @@ string Reader::getLongDescription() const
|
||||
|
||||
string Reader::getLanguage() const
|
||||
{
|
||||
METADATA("Language")
|
||||
return kiwix::getMetaLanguage(*zimArchive);
|
||||
}
|
||||
|
||||
string Reader::getLicense() const
|
||||
@@ -272,13 +258,7 @@ string Reader::getLicense() const
|
||||
|
||||
string Reader::getTags(bool original) const
|
||||
{
|
||||
string tags_str;
|
||||
getMetadata("Tags", tags_str);
|
||||
if (original) {
|
||||
return tags_str;
|
||||
}
|
||||
auto tags = convertTags(tags_str);
|
||||
return join(tags, ";");
|
||||
return kiwix::getMetaTags(*zimArchive, original);
|
||||
}
|
||||
|
||||
|
||||
@@ -342,12 +322,8 @@ string Reader::getOrigId() const
|
||||
|
||||
Entry Reader::getEntryFromPath(const std::string& path) const
|
||||
{
|
||||
if (path.empty() || path == "/") {
|
||||
return getMainPage();
|
||||
}
|
||||
|
||||
try {
|
||||
return zimArchive->getEntryByPath(path);
|
||||
return kiwix::getEntryFromPath(*zimArchive, path);
|
||||
} catch (zim::EntryNotFound& e) {
|
||||
throw NoEntry();
|
||||
}
|
||||
@@ -460,12 +436,7 @@ bool Reader::searchSuggestions(const string& prefix,
|
||||
std::vector<std::string> Reader::getTitleVariants(
|
||||
const std::string& title) const
|
||||
{
|
||||
std::vector<std::string> variants;
|
||||
variants.push_back(title);
|
||||
variants.push_back(kiwix::ucFirst(title));
|
||||
variants.push_back(kiwix::lcFirst(title));
|
||||
variants.push_back(kiwix::toTitle(title));
|
||||
return variants;
|
||||
return kiwix::getTitleVariants(title);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,17 +30,29 @@
|
||||
|
||||
#include <mustache.hpp>
|
||||
#include "kiwixlib-resources.h"
|
||||
|
||||
#include "tools/stringTools.h"
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
|
||||
/* Constructor */
|
||||
SearchRenderer::SearchRenderer(Searcher* searcher, NameMapper* mapper)
|
||||
: mp_searcher(searcher),
|
||||
: m_srs(searcher->getSearchResultSet()),
|
||||
mp_nameMapper(mapper),
|
||||
protocolPrefix("zim://"),
|
||||
searchProtocolPrefix("search://?")
|
||||
searchProtocolPrefix("search://?"),
|
||||
estimatedResultCount(searcher->getEstimatedResultCount()),
|
||||
resultStart(searcher->getResultStart())
|
||||
{}
|
||||
|
||||
SearchRenderer::SearchRenderer(zim::SearchResultSet srs, NameMapper* mapper,
|
||||
unsigned int start, unsigned int estimatedResultCount)
|
||||
: m_srs(srs),
|
||||
mp_nameMapper(mapper),
|
||||
protocolPrefix("zim://"),
|
||||
searchProtocolPrefix("search://?"),
|
||||
estimatedResultCount(estimatedResultCount),
|
||||
resultStart(start)
|
||||
{}
|
||||
|
||||
/* Destructor */
|
||||
@@ -70,29 +82,26 @@ std::string SearchRenderer::getHtml()
|
||||
{
|
||||
kainjow::mustache::data results{kainjow::mustache::data::type::list};
|
||||
|
||||
mp_searcher->restart_search();
|
||||
Result* p_result = NULL;
|
||||
while ((p_result = mp_searcher->getNextResult())) {
|
||||
for (auto it = m_srs.begin(); it != m_srs.end(); it++) {
|
||||
kainjow::mustache::data result;
|
||||
result.set("title", p_result->get_title());
|
||||
result.set("url", p_result->get_url());
|
||||
result.set("snippet", p_result->get_snippet());
|
||||
result.set("resultContentId", mp_nameMapper->getNameForId(p_result->get_zimId()));
|
||||
result.set("title", it.getTitle());
|
||||
result.set("url", it.getPath());
|
||||
result.set("snippet", it.getSnippet());
|
||||
std::ostringstream s;
|
||||
s << it.getZimId();
|
||||
result.set("resultContentId", mp_nameMapper->getNameForId(s.str()));
|
||||
|
||||
if (p_result->get_wordCount() >= 0) {
|
||||
result.set("wordCount", kiwix::beautifyInteger(p_result->get_wordCount()));
|
||||
if (it.getWordCount() >= 0) {
|
||||
result.set("wordCount", kiwix::beautifyInteger(it.getWordCount()));
|
||||
}
|
||||
|
||||
results.push_back(result);
|
||||
delete p_result;
|
||||
}
|
||||
|
||||
// pages
|
||||
kainjow::mustache::data pages{kainjow::mustache::data::type::list};
|
||||
|
||||
auto resultStart = mp_searcher->getResultStart();
|
||||
auto resultEnd = 0U;
|
||||
auto estimatedResultCount = mp_searcher->getEstimatedResultCount();
|
||||
auto currentPage = 0U;
|
||||
auto pageStart = 0U;
|
||||
auto pageEnd = 0U;
|
||||
@@ -146,4 +155,4 @@ std::string SearchRenderer::getHtml()
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "searcher.h"
|
||||
#include "reader.h"
|
||||
@@ -26,6 +25,8 @@
|
||||
#include <zim/search.h>
|
||||
|
||||
#include <mustache.hpp>
|
||||
#include <cmath>
|
||||
#include "tools/stringTools.h"
|
||||
#include "kiwixlib-resources.h"
|
||||
|
||||
#define MAX_SEARCH_LEN 140
|
||||
@@ -228,6 +229,11 @@ unsigned int Searcher::getEstimatedResultCount()
|
||||
return this->estimatedResultCount;
|
||||
}
|
||||
|
||||
zim::SearchResultSet Searcher::getSearchResultSet()
|
||||
{
|
||||
return *(this->internal);
|
||||
}
|
||||
|
||||
_Result::_Result(zim::SearchResultSet::iterator iterator)
|
||||
: iterator(iterator)
|
||||
{
|
||||
|
||||
@@ -43,10 +43,11 @@ extern "C" {
|
||||
#include "microhttpd_wrapper.h"
|
||||
}
|
||||
|
||||
#include "tools/otherTools.h"
|
||||
#include "tools.h"
|
||||
#include "tools/pathTools.h"
|
||||
#include "tools/regexTools.h"
|
||||
#include "tools/stringTools.h"
|
||||
#include "tools/archiveTools.h"
|
||||
#include "library.h"
|
||||
#include "name_mapper.h"
|
||||
#include "entry.h"
|
||||
@@ -55,6 +56,10 @@ extern "C" {
|
||||
#include "opds_dumper.h"
|
||||
|
||||
#include <zim/uuid.h>
|
||||
#include <zim/error.h>
|
||||
#include <zim/search.h>
|
||||
#include <zim/entry.h>
|
||||
#include <zim/item.h>
|
||||
|
||||
#include <mustache.hpp>
|
||||
|
||||
@@ -323,22 +328,63 @@ std::unique_ptr<Response> InternalServer::build_homepage(const RequestContext& r
|
||||
return ContentResponse::build(*this, RESOURCE::templates::index_html, get_default_data(), "text/html; charset=utf-8", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive and Zim handlers begin
|
||||
**/
|
||||
|
||||
// TODO: retrieve searcher from caching mechanism
|
||||
SuggestionsList_t getSuggestions(const zim::Archive* const archive,
|
||||
const std::string& queryString, int suggestionCount)
|
||||
{
|
||||
SuggestionsList_t suggestions;
|
||||
if (archive->hasTitleIndex()) {
|
||||
auto searcher = zim::Searcher(*archive);
|
||||
zim::Query suggestionQuery;
|
||||
suggestionQuery.setQuery(queryString, true);
|
||||
auto suggestionSearch = searcher.search(suggestionQuery);
|
||||
auto suggestionResult = suggestionSearch.getResults(0, suggestionCount);
|
||||
|
||||
for (auto it = suggestionResult.begin(); it != suggestionResult.end(); it++) {
|
||||
SuggestionItem suggestion(it.getTitle(), kiwix::normalize(it.getTitle()),
|
||||
it.getPath(), it.getSnippet());
|
||||
suggestions.push_back(suggestion);
|
||||
}
|
||||
} else {
|
||||
// TODO: This case should be handled by libzim
|
||||
std::vector<std::string> variants = getTitleVariants(queryString);
|
||||
int currCount = 0;
|
||||
for (auto it = variants.begin(); it != variants.end() && currCount < suggestionCount; it++) {
|
||||
for (auto& entry: archive->findByTitle(*it)) {
|
||||
SuggestionItem suggestion(entry.getTitle(), kiwix::normalize(entry.getTitle()),
|
||||
entry.getPath());
|
||||
suggestions.push_back(suggestion);
|
||||
currCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive and Zim handlers end
|
||||
**/
|
||||
|
||||
std::unique_ptr<Response> InternalServer::handle_meta(const RequestContext& request)
|
||||
{
|
||||
std::string bookName;
|
||||
std::string bookId;
|
||||
std::string meta_name;
|
||||
std::shared_ptr<Reader> reader;
|
||||
std::shared_ptr<zim::Archive> archive;
|
||||
try {
|
||||
bookName = request.get_argument("content");
|
||||
bookId = mp_nameMapper->getIdForName(bookName);
|
||||
meta_name = request.get_argument("name");
|
||||
reader = mp_library->getReaderById(bookId);
|
||||
archive = mp_library->getArchiveById(bookId);
|
||||
} catch (const std::out_of_range& e) {
|
||||
return Response::build_404(*this, request, bookName, "");
|
||||
}
|
||||
|
||||
if (reader == nullptr) {
|
||||
if (archive == nullptr) {
|
||||
return Response::build_404(*this, request, bookName, "");
|
||||
}
|
||||
|
||||
@@ -346,23 +392,23 @@ std::unique_ptr<Response> InternalServer::handle_meta(const RequestContext& requ
|
||||
std::string mimeType = "text";
|
||||
|
||||
if (meta_name == "title") {
|
||||
content = reader->getTitle();
|
||||
content = getArchiveTitle(*archive);
|
||||
} else if (meta_name == "description") {
|
||||
content = reader->getDescription();
|
||||
content = getMetaDescription(*archive);
|
||||
} else if (meta_name == "language") {
|
||||
content = reader->getLanguage();
|
||||
content = getMetaLanguage(*archive);
|
||||
} else if (meta_name == "name") {
|
||||
content = reader->getName();
|
||||
content = getMetaName(*archive);
|
||||
} else if (meta_name == "tags") {
|
||||
content = reader->getTags();
|
||||
content = getMetaTags(*archive);
|
||||
} else if (meta_name == "date") {
|
||||
content = reader->getDate();
|
||||
content = getMetaDate(*archive);
|
||||
} else if (meta_name == "creator") {
|
||||
content = reader->getCreator();
|
||||
content = getMetaCreator(*archive);
|
||||
} else if (meta_name == "publisher") {
|
||||
content = reader->getPublisher();
|
||||
content = getMetaPublisher(*archive);
|
||||
} else if (meta_name == "favicon") {
|
||||
reader->getFavicon(content, mimeType);
|
||||
getArchiveFavicon(*archive, content, mimeType);
|
||||
} else {
|
||||
return Response::build_404(*this, request, bookName, "");
|
||||
}
|
||||
@@ -385,51 +431,54 @@ std::unique_ptr<Response> InternalServer::handle_suggest(const RequestContext& r
|
||||
|
||||
std::string bookName;
|
||||
std::string bookId;
|
||||
std::string term;
|
||||
std::shared_ptr<Reader> reader;
|
||||
std::string queryString;
|
||||
std::shared_ptr<zim::Archive> archive;
|
||||
try {
|
||||
bookName = request.get_argument("content");
|
||||
bookId = mp_nameMapper->getIdForName(bookName);
|
||||
term = request.get_argument("term");
|
||||
reader = mp_library->getReaderById(bookId);
|
||||
queryString = request.get_argument("term");
|
||||
archive = mp_library->getArchiveById(bookId);
|
||||
} catch (const std::out_of_range&) {
|
||||
return Response::build_404(*this, request, bookName, "");
|
||||
}
|
||||
|
||||
if (archive == nullptr) {
|
||||
return Response::build_404(*this, request, bookName, "");
|
||||
}
|
||||
|
||||
if (m_verbose.load()) {
|
||||
printf("Searching suggestions for: \"%s\"\n", term.c_str());
|
||||
printf("Searching suggestions for: \"%s\"\n", queryString.c_str());
|
||||
}
|
||||
|
||||
MustacheData results{MustacheData::type::list};
|
||||
|
||||
bool first = true;
|
||||
if (reader != nullptr) {
|
||||
/* Get the suggestions */
|
||||
SuggestionsList_t suggestions;
|
||||
reader->searchSuggestionsSmart(term, maxSuggestionCount, suggestions);
|
||||
for(auto& suggestion:suggestions) {
|
||||
MustacheData result;
|
||||
result.set("label", suggestion.getTitle());
|
||||
|
||||
if (suggestion.hasSnippet()) {
|
||||
result.set("label", suggestion.getSnippet());
|
||||
}
|
||||
/* Get the suggestions */
|
||||
SuggestionsList_t suggestions = getSuggestions(archive.get(), queryString, maxSuggestionCount);
|
||||
for(auto& suggestion:suggestions) {
|
||||
MustacheData result;
|
||||
result.set("label", suggestion.getTitle());
|
||||
|
||||
result.set("value", suggestion.getTitle());
|
||||
result.set("kind", "path");
|
||||
result.set("path", suggestion.getPath());
|
||||
result.set("first", first);
|
||||
first = false;
|
||||
results.push_back(result);
|
||||
suggestionCount++;
|
||||
if (suggestion.hasSnippet()) {
|
||||
result.set("label", suggestion.getSnippet());
|
||||
}
|
||||
|
||||
result.set("value", suggestion.getTitle());
|
||||
result.set("kind", "path");
|
||||
result.set("path", suggestion.getPath());
|
||||
result.set("first", first);
|
||||
first = false;
|
||||
results.push_back(result);
|
||||
suggestionCount++;
|
||||
}
|
||||
|
||||
|
||||
/* Propose the fulltext search if possible */
|
||||
if (reader->hasFulltextIndex()) {
|
||||
if (archive->hasFulltextIndex()) {
|
||||
MustacheData result;
|
||||
result.set("label", "containing '" + term + "'...");
|
||||
result.set("value", term + " ");
|
||||
result.set("label", "containing '" + queryString + "'...");
|
||||
result.set("value", queryString + " ");
|
||||
result.set("kind", "pattern");
|
||||
result.set("first", first);
|
||||
results.push_back(result);
|
||||
@@ -492,30 +541,34 @@ std::unique_ptr<Response> InternalServer::handle_search(const RequestContext& re
|
||||
} catch(const std::out_of_range&) {}
|
||||
catch(const std::invalid_argument&) {}
|
||||
|
||||
std::shared_ptr<Reader> reader(nullptr);
|
||||
std::shared_ptr<zim::Archive> archive;
|
||||
try {
|
||||
reader = mp_library->getReaderById(bookId);
|
||||
archive = mp_library->getArchiveById(bookId);
|
||||
} catch (const std::out_of_range&) {}
|
||||
|
||||
/* Make the search */
|
||||
if ( (!reader && !bookName.empty())
|
||||
if ( (!archive && !bookName.empty())
|
||||
|| (patternString.empty() && ! has_geo_query) ) {
|
||||
auto data = get_default_data();
|
||||
data.set("pattern", encodeDiples(patternString));
|
||||
auto response = ContentResponse::build(*this, RESOURCE::templates::no_search_result_html, data, "text/html; charset=utf-8");
|
||||
response->set_taskbar(bookName, reader ? reader->getTitle() : "");
|
||||
response->set_taskbar(bookName, archive ? getArchiveTitle(*archive) : "");
|
||||
response->set_code(MHD_HTTP_NOT_FOUND);
|
||||
return std::move(response);
|
||||
}
|
||||
|
||||
Searcher searcher;
|
||||
if (reader) {
|
||||
searcher.add_reader(reader.get());
|
||||
std::shared_ptr<zim::Searcher> searcher;
|
||||
if (archive) {
|
||||
searcher = std::make_shared<zim::Searcher>(*archive);
|
||||
} else {
|
||||
for (auto& bookId: mp_library->filter(kiwix::Filter().local(true).valid(true))) {
|
||||
auto currentReader = mp_library->getReaderById(bookId);
|
||||
if (currentReader) {
|
||||
searcher.add_reader(currentReader.get());
|
||||
auto currentArchive = mp_library->getArchiveById(bookId);
|
||||
if (currentArchive) {
|
||||
if (! searcher) {
|
||||
searcher = std::make_shared<zim::Searcher>(*currentArchive);
|
||||
} else {
|
||||
searcher->add_archive(*currentArchive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,21 +593,37 @@ std::unique_ptr<Response> InternalServer::handle_search(const RequestContext& re
|
||||
|
||||
/* Get the results */
|
||||
try {
|
||||
zim::Query query;
|
||||
if (patternString.empty()) {
|
||||
searcher.geo_search(latitude, longitude, distance,
|
||||
start, end, m_verbose.load());
|
||||
// Execute geo-search
|
||||
if (m_verbose.load()) {
|
||||
cout << "Performing geo query `" << distance << "&(" << latitude << ";" << longitude << ")'" << endl;
|
||||
}
|
||||
|
||||
query.setVerbose(m_verbose.load());
|
||||
query.setQuery("", false);
|
||||
query.setGeorange(latitude, longitude, distance);
|
||||
} else {
|
||||
searcher.search(patternString,
|
||||
start, end, m_verbose.load());
|
||||
// Execute Ft search
|
||||
if (m_verbose.load()) {
|
||||
cout << "Performing query `" << patternString << "'" << endl;
|
||||
}
|
||||
|
||||
std::string queryString = removeAccents(patternString);
|
||||
query.setQuery(queryString, false);
|
||||
query.setVerbose(m_verbose.load());
|
||||
}
|
||||
SearchRenderer renderer(&searcher, mp_nameMapper);
|
||||
|
||||
zim::Search search = searcher->search(query);
|
||||
SearchRenderer renderer(search.getResults(start, end), mp_nameMapper, start,
|
||||
search.getEstimatedMatches());
|
||||
renderer.setSearchPattern(patternString);
|
||||
renderer.setSearchContent(bookName);
|
||||
renderer.setProtocolPrefix(m_root + "/");
|
||||
renderer.setSearchProtocolPrefix(m_root + "/search?");
|
||||
renderer.setPageLength(pageLength);
|
||||
auto response = ContentResponse::build(*this, renderer.getHtml(), "text/html; charset=utf-8");
|
||||
response->set_taskbar(bookName, reader ? reader->getTitle() : "");
|
||||
response->set_taskbar(bookName, archive ? getArchiveTitle(*archive) : "");
|
||||
|
||||
return std::move(response);
|
||||
} catch (const std::exception& e) {
|
||||
@@ -571,23 +640,23 @@ std::unique_ptr<Response> InternalServer::handle_random(const RequestContext& re
|
||||
|
||||
std::string bookName;
|
||||
std::string bookId;
|
||||
std::shared_ptr<Reader> reader;
|
||||
std::shared_ptr<zim::Archive> archive;
|
||||
try {
|
||||
bookName = request.get_argument("content");
|
||||
bookId = mp_nameMapper->getIdForName(bookName);
|
||||
reader = mp_library->getReaderById(bookId);
|
||||
archive = mp_library->getArchiveById(bookId);
|
||||
} catch (const std::out_of_range&) {
|
||||
return Response::build_404(*this, request, bookName, "");
|
||||
}
|
||||
|
||||
if (reader == nullptr) {
|
||||
if (archive == nullptr) {
|
||||
return Response::build_404(*this, request, bookName, "");
|
||||
}
|
||||
|
||||
try {
|
||||
auto entry = reader->getRandomPage();
|
||||
return build_redirect(bookName, entry.getFinalEntry());
|
||||
} catch(kiwix::NoEntry& e) {
|
||||
auto entry = archive->getRandomEntry();
|
||||
return build_redirect(bookName, getFinalItem(*archive, entry));
|
||||
} catch(zim::EntryNotFound& e) {
|
||||
return Response::build_404(*this, request, bookName, "");
|
||||
}
|
||||
}
|
||||
@@ -734,22 +803,10 @@ std::string searchSuggestionHTML(const std::string& searchURL, const std::string
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
std::shared_ptr<Reader>
|
||||
InternalServer::get_reader(const std::string& bookName) const
|
||||
{
|
||||
std::shared_ptr<Reader> reader;
|
||||
try {
|
||||
const std::string bookId = mp_nameMapper->getIdForName(bookName);
|
||||
reader = mp_library->getReaderById(bookId);
|
||||
} catch (const std::out_of_range& e) {
|
||||
}
|
||||
return reader;
|
||||
}
|
||||
|
||||
std::unique_ptr<Response>
|
||||
InternalServer::build_redirect(const std::string& bookName, const kiwix::Entry& entry) const
|
||||
InternalServer::build_redirect(const std::string& bookName, const zim::Item& item) const
|
||||
{
|
||||
auto redirectUrl = m_root + "/" + bookName + "/" + kiwix::urlEncode(entry.getPath());
|
||||
auto redirectUrl = m_root + "/" + bookName + "/" + kiwix::urlEncode(item.getPath());
|
||||
return Response::build_redirect(*this, redirectUrl);
|
||||
}
|
||||
|
||||
@@ -765,8 +822,13 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
|
||||
if (bookName.empty())
|
||||
return build_homepage(request);
|
||||
|
||||
const std::shared_ptr<Reader> reader = get_reader(bookName);
|
||||
if (reader == nullptr) {
|
||||
std::shared_ptr<zim::Archive> archive;
|
||||
try {
|
||||
const std::string bookId = mp_nameMapper->getIdForName(bookName);
|
||||
archive = mp_library->getArchiveById(bookId);
|
||||
} catch (const std::out_of_range& e) {}
|
||||
|
||||
if (archive == nullptr) {
|
||||
std::string searchURL = m_root+"/search?pattern="+pattern; // Make a full search on the entire library.
|
||||
const std::string details = searchSuggestionHTML(searchURL, kiwix::urlDecode(pattern));
|
||||
|
||||
@@ -779,31 +841,31 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
|
||||
}
|
||||
|
||||
try {
|
||||
auto entry = reader->getEntryFromPath(urlStr);
|
||||
auto entry = getEntryFromPath(*archive, urlStr);
|
||||
if (entry.isRedirect() || urlStr.empty()) {
|
||||
// If urlStr is empty, we want to mainPage.
|
||||
// We must do a redirection to the real page.
|
||||
return build_redirect(bookName, entry.getFinalEntry());
|
||||
return build_redirect(bookName, getFinalItem(*archive, entry));
|
||||
}
|
||||
auto response = ItemResponse::build(*this, request, entry.getZimEntry().getItem());
|
||||
auto response = ItemResponse::build(*this, request, entry.getItem());
|
||||
try {
|
||||
dynamic_cast<ContentResponse&>(*response).set_taskbar(bookName, reader->getTitle());
|
||||
dynamic_cast<ContentResponse&>(*response).set_taskbar(bookName, getArchiveTitle(*archive));
|
||||
} catch (std::bad_cast& e) {}
|
||||
|
||||
if (m_verbose.load()) {
|
||||
printf("Found %s\n", entry.getPath().c_str());
|
||||
printf("mimeType: %s\n", entry.getMimetype().c_str());
|
||||
printf("mimeType: %s\n", entry.getItem(true).getMimetype().c_str());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch(kiwix::NoEntry& e) {
|
||||
} catch(zim::EntryNotFound& e) {
|
||||
if (m_verbose.load())
|
||||
printf("Failed to find %s\n", urlStr.c_str());
|
||||
|
||||
std::string searchURL = m_root+"/search?content="+bookName+"&pattern="+pattern; // Make a search on this specific book only.
|
||||
const std::string details = searchSuggestionHTML(searchURL, kiwix::urlDecode(pattern));
|
||||
|
||||
return Response::build_404(*this, request, bookName, reader->getTitle(), details);
|
||||
return Response::build_404(*this, request, bookName, getArchiveTitle(*archive), details);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ class InternalServer {
|
||||
|
||||
private: // functions
|
||||
std::unique_ptr<Response> handle_request(const RequestContext& request);
|
||||
std::unique_ptr<Response> build_redirect(const std::string& bookName, const kiwix::Entry& entry) const;
|
||||
std::unique_ptr<Response> build_redirect(const std::string& bookName, const zim::Item& item) const;
|
||||
std::unique_ptr<Response> build_homepage(const RequestContext& request);
|
||||
std::unique_ptr<Response> handle_skin(const RequestContext& request);
|
||||
std::unique_ptr<Response> handle_catalog(const RequestContext& request);
|
||||
@@ -89,7 +89,6 @@ class InternalServer {
|
||||
|
||||
MustacheData get_default_data() const;
|
||||
|
||||
std::shared_ptr<Reader> get_reader(const std::string& bookName) const;
|
||||
bool etag_not_needed(const RequestContext& r) const;
|
||||
ETag get_matching_if_none_match_etag(const RequestContext& request) const;
|
||||
|
||||
|
||||
121
src/tools/archiveTools.cpp
Normal file
121
src/tools/archiveTools.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2021 Maneesh P M <manu.pm55@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "archiveTools.h"
|
||||
|
||||
#include "tools.h"
|
||||
#include "pathTools.h"
|
||||
#include "otherTools.h"
|
||||
#include "stringTools.h"
|
||||
|
||||
#include <zim/error.h>
|
||||
#include <zim/item.h>
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
std::string getMetadata(const zim::Archive& archive, const std::string& name) {
|
||||
try {
|
||||
return archive.getMetadata(name);
|
||||
} catch (zim::EntryNotFound& e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string getArchiveTitle(const zim::Archive& archive) {
|
||||
std::string value = getMetadata(archive, "Title");
|
||||
if (value.empty()) {
|
||||
value = getLastPathElement(archive.getFilename());
|
||||
std::replace(value.begin(), value.end(), '_', ' ');
|
||||
size_t pos = value.find(".zim");
|
||||
value = value.substr(0, pos);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string getMetaDescription(const zim::Archive& archive) {
|
||||
std::string value;
|
||||
value = getMetadata(archive, "Description");
|
||||
|
||||
/* Mediawiki Collection tends to use the "Subtitle" name */
|
||||
if (value.empty()) {
|
||||
value = getMetadata(archive, "Subtitle");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string getMetaTags(const zim::Archive& archive, bool original) {
|
||||
std::string tags_str = getMetadata(archive, "Tags");
|
||||
if (original) {
|
||||
return tags_str;
|
||||
}
|
||||
auto tags = convertTags(tags_str);
|
||||
return join(tags, ";");
|
||||
}
|
||||
|
||||
bool getArchiveFavicon(const zim::Archive& archive,
|
||||
std::string& content, std::string& mimeType){
|
||||
try {
|
||||
auto item = archive.getIllustrationItem();
|
||||
content = item.getData();
|
||||
mimeType = item.getMimetype();
|
||||
return true;
|
||||
} catch(zim::EntryNotFound& e) {};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string getMetaLanguage(const zim::Archive& archive) {
|
||||
return getMetadata(archive, "Language");
|
||||
}
|
||||
|
||||
std::string getMetaName(const zim::Archive& archive) {
|
||||
return getMetadata(archive, "Name");
|
||||
}
|
||||
|
||||
std::string getMetaDate(const zim::Archive& archive) {
|
||||
return getMetadata(archive, "Date");
|
||||
}
|
||||
|
||||
std::string getMetaCreator(const zim::Archive& archive) {
|
||||
return getMetadata(archive, "Creator");
|
||||
}
|
||||
|
||||
std::string getMetaPublisher(const zim::Archive& archive) {
|
||||
return getMetadata(archive, "Publisher");
|
||||
}
|
||||
|
||||
zim::Item getFinalItem(const zim::Archive& archive, const zim::Entry& entry)
|
||||
{
|
||||
return entry.getItem(true);
|
||||
}
|
||||
|
||||
zim::Entry getEntryFromPath(const zim::Archive& archive, const std::string& path)
|
||||
{
|
||||
try {
|
||||
return archive.getEntryByPath(path);
|
||||
} catch (zim::EntryNotFound& e) {
|
||||
if (path.empty() || path == "/") {
|
||||
return archive.getMainEntry();
|
||||
}
|
||||
}
|
||||
throw zim::EntryNotFound("Cannot find entry for non empty path");
|
||||
}
|
||||
|
||||
} // kiwix
|
||||
47
src/tools/archiveTools.h
Normal file
47
src/tools/archiveTools.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2021 Maneesh P M <manu.pm55@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KIWIX_ARCHIVETOOLS_H
|
||||
#define KIWIX_ARCHIVETOOLS_H
|
||||
|
||||
#include <zim/archive.h>
|
||||
|
||||
/**
|
||||
* This file contains all the functions that would make handling data related to
|
||||
* an archive easier.
|
||||
**/
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
std::string getMetadata(const zim::Archive& archive, const std::string& name);
|
||||
std::string getArchiveTitle(const zim::Archive& archive);
|
||||
std::string getMetaDescription(const zim::Archive& archive);
|
||||
std::string getMetaTags(const zim::Archive& archive, bool original = false);
|
||||
bool getArchiveFavicon(const zim::Archive& archive,
|
||||
std::string& content, std::string& mimeType);
|
||||
std::string getMetaLanguage(const zim::Archive& archive);
|
||||
std::string getMetaName(const zim::Archive& archive);
|
||||
std::string getMetaDate(const zim::Archive& archive);
|
||||
std::string getMetaCreator(const zim::Archive& archive);
|
||||
std::string getMetaPublisher(const zim::Archive& archive);
|
||||
zim::Item getFinalItem(const zim::Archive& archive, const zim::Entry& entry);
|
||||
zim::Entry getEntryFromPath(const zim::Archive& archive, const std::string& path);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,7 +17,11 @@
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
// Implement function declared in tools.h and tools/otherTools.h
|
||||
#include "tools.h"
|
||||
#include "tools/otherTools.h"
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
@@ -32,9 +32,7 @@ namespace pugi {
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
void sleep(unsigned int milliseconds);
|
||||
std::string nodeToString(const pugi::xml_node& node);
|
||||
std::string converta2toa3(const std::string& a2code);
|
||||
|
||||
/*
|
||||
* Convert all format tag string to new format
|
||||
@@ -17,7 +17,10 @@
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
// Implement method defined in <kiwix/tools.h> and "tools/pathTools.h"
|
||||
#include "tools.h"
|
||||
#include "tools/pathTools.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef __APPLE__
|
||||
@@ -59,7 +62,6 @@
|
||||
#define PATH_MAX 1024
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string WideToUtf8(const std::wstring& wstr)
|
||||
{
|
||||
@@ -78,7 +80,7 @@ std::wstring Utf8ToWide(const std::string& str)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isRelativePath(const std::string& path)
|
||||
bool kiwix::isRelativePath(const std::string& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (path.size() < 3 ) {
|
||||
@@ -173,7 +175,7 @@ std::vector<std::string> normalizeParts(std::vector<std::string>& parts, bool ab
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string computeRelativePath(const std::string& path, const std::string& absolutePath)
|
||||
std::string kiwix::computeRelativePath(const std::string& path, const std::string& absolutePath)
|
||||
{
|
||||
auto parts = kiwix::split(path, SEPARATOR, false);
|
||||
auto pathParts = normalizeParts(parts, false);
|
||||
@@ -198,11 +200,11 @@ std::string computeRelativePath(const std::string& path, const std::string& abso
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath)
|
||||
std::string kiwix::computeAbsolutePath(const std::string& path, const std::string& relativePath)
|
||||
{
|
||||
std::string absolutePath = path;
|
||||
if (path.empty()) {
|
||||
absolutePath = getCurrentDirectory();
|
||||
absolutePath = kiwix::getCurrentDirectory();
|
||||
}
|
||||
|
||||
auto parts = kiwix::split(absolutePath, SEPARATOR, false);
|
||||
@@ -215,7 +217,7 @@ std::string computeAbsolutePath(const std::string& path, const std::string& rela
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string removeLastPathElement(const std::string& path)
|
||||
std::string kiwix::removeLastPathElement(const std::string& path)
|
||||
{
|
||||
auto parts_ = kiwix::split(path, SEPARATOR, false);
|
||||
auto parts = normalizeParts(parts_, false);
|
||||
@@ -226,7 +228,7 @@ std::string removeLastPathElement(const std::string& path)
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string appendToDirectory(const std::string& directoryPath, const std::string& filename)
|
||||
std::string kiwix::appendToDirectory(const std::string& directoryPath, const std::string& filename)
|
||||
{
|
||||
std::string newPath = directoryPath;
|
||||
if (!directoryPath.empty() && directoryPath.back() != SEPARATOR[0]) {
|
||||
@@ -236,7 +238,7 @@ std::string appendToDirectory(const std::string& directoryPath, const std::strin
|
||||
return newPath;
|
||||
}
|
||||
|
||||
std::string getLastPathElement(const std::string& path)
|
||||
std::string kiwix::getLastPathElement(const std::string& path)
|
||||
{
|
||||
auto parts_ = kiwix::split(path, SEPARATOR);
|
||||
auto parts = normalizeParts(parts_, false);
|
||||
@@ -366,7 +368,7 @@ bool copyFile(const std::string& sourcePath, const std::string& destPath)
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getExecutablePath(bool realPathOnly)
|
||||
std::string kiwix::getExecutablePath(bool realPathOnly)
|
||||
{
|
||||
if (!realPathOnly) {
|
||||
char* cAppImage = ::getenv("APPIMAGE");
|
||||
@@ -420,7 +422,7 @@ bool writeTextFile(const std::string& path, const std::string& content)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getCurrentDirectory()
|
||||
std::string kiwix::getCurrentDirectory()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wchar_t* a_cwd = _wgetcwd(NULL, 0);
|
||||
@@ -434,7 +436,7 @@ std::string getCurrentDirectory()
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string getDataDirectory()
|
||||
std::string kiwix::getDataDirectory()
|
||||
{
|
||||
// Try to get the dataDir from the `KIWIX_DATA_DIR` env var
|
||||
#ifdef _WIN32
|
||||
@@ -524,4 +526,3 @@ std::string getMimeTypeForFile(const std::string& filename)
|
||||
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,11 +26,6 @@
|
||||
std::string WideToUtf8(const std::wstring& wstr);
|
||||
std::wstring Utf8ToWide(const std::string& str);
|
||||
#endif
|
||||
bool isRelativePath(const std::string& path);
|
||||
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath);
|
||||
std::string computeRelativePath(const std::string& path, const std::string& absolutePath);
|
||||
std::string removeLastPathElement(const std::string& path);
|
||||
std::string appendToDirectory(const std::string& directoryPath, const std::string& filename);
|
||||
|
||||
unsigned int getFileSize(const std::string& path);
|
||||
std::string getFileSizeAsString(const std::string& path);
|
||||
@@ -39,10 +34,8 @@ bool fileExists(const std::string& path);
|
||||
bool makeDirectory(const std::string& path);
|
||||
std::string makeTmpDirectory();
|
||||
bool copyFile(const std::string& sourcePath, const std::string& destPath);
|
||||
std::string getLastPathElement(const std::string& path);
|
||||
std::string getExecutablePath(bool realPathOnly = false);
|
||||
std::string getCurrentDirectory();
|
||||
std::string getDataDirectory();
|
||||
bool writeTextFile(const std::string& path, const std::string& content);
|
||||
std::string getMimeTypeForFile(const std::string& filename);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <tools/stringTools.h>
|
||||
// Implement function declared in tools.h and tools/stringTools.h
|
||||
#include "tools.h"
|
||||
#include "tools/stringTools.h"
|
||||
|
||||
#include <tools/pathTools.h>
|
||||
#include "tools/pathTools.h"
|
||||
#include <unicode/normlzr.h>
|
||||
#include <unicode/rep.h>
|
||||
#include <unicode/translit.h>
|
||||
@@ -268,7 +270,7 @@ std::string kiwix::urlDecode(const std::string& value, bool component)
|
||||
/* Split string in a token array */
|
||||
std::vector<std::string> kiwix::split(const std::string& str,
|
||||
const std::string& delims,
|
||||
bool trimEmpty,
|
||||
bool dropEmpty,
|
||||
bool keepDelim)
|
||||
{
|
||||
std::string::size_type lastPos = 0;
|
||||
@@ -277,7 +279,7 @@ std::vector<std::string> kiwix::split(const std::string& str,
|
||||
while( (pos = str.find_first_of(delims, lastPos)) < str.length() )
|
||||
{
|
||||
auto token = str.substr(lastPos, pos - lastPos);
|
||||
if (!trimEmpty || !token.empty()) {
|
||||
if (!dropEmpty || !token.empty()) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
if (keepDelim) {
|
||||
@@ -287,7 +289,7 @@ std::vector<std::string> kiwix::split(const std::string& str,
|
||||
}
|
||||
|
||||
auto token = str.substr(lastPos);
|
||||
if (!trimEmpty || !token.empty()) {
|
||||
if (!dropEmpty || !token.empty()) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
return tokens;
|
||||
@@ -395,3 +397,11 @@ bool kiwix::startsWith(const std::string& base, const std::string& start)
|
||||
&& std::equal(start.begin(), start.end(), base.begin());
|
||||
}
|
||||
|
||||
std::vector<std::string> kiwix::getTitleVariants(const std::string& title) {
|
||||
std::vector<std::string> variants;
|
||||
variants.push_back(title);
|
||||
variants.push_back(kiwix::ucFirst(title));
|
||||
variants.push_back(kiwix::lcFirst(title));
|
||||
variants.push_back(kiwix::toTitle(title));
|
||||
return variants;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ void loadICUExternalTables();
|
||||
std::string urlEncode(const std::string& value, bool encodeReserved = false);
|
||||
std::string urlDecode(const std::string& value, bool component = false);
|
||||
|
||||
std::vector<std::string> split(const std::string& str, const std::string& delims, bool trimEmpty = true, bool keepDelim = false);
|
||||
std::string join(const std::vector<std::string>& list, const std::string& sep);
|
||||
|
||||
std::string ucAll(const std::string& word);
|
||||
@@ -70,5 +69,7 @@ T extractFromString(const std::string& str) {
|
||||
}
|
||||
|
||||
bool startsWith(const std::string& base, const std::string& start);
|
||||
|
||||
std::vector<std::string> getTitleVariants(const std::string& title);
|
||||
} //namespace kiwix
|
||||
#endif
|
||||
@@ -18,6 +18,11 @@ skin/jquery-ui/images/ui-bg_glass_95_fef1ec_1x400.png
|
||||
skin/jquery-ui/jquery-ui.theme.min.css
|
||||
skin/jquery-ui/jquery-ui.min.css
|
||||
skin/caret.png
|
||||
skin/bittorrent.png
|
||||
skin/magnet.png
|
||||
skin/download.png
|
||||
skin/hash.png
|
||||
skin/search-icon.svg
|
||||
skin/taskbar.js
|
||||
skin/langList.js
|
||||
skin/categoryList.js
|
||||
@@ -25,6 +30,9 @@ skin/iso6391To3.js
|
||||
skin/isotope.pkgd.min.js
|
||||
skin/index.js
|
||||
skin/taskbar.css
|
||||
skin/home.css
|
||||
skin/fonts/Poppins.ttf
|
||||
skin/fonts/Roboto.ttf
|
||||
skin/block_external.js
|
||||
templates/search_result.html
|
||||
templates/no_search_result.html
|
||||
|
||||
BIN
static/skin/bittorrent.png
Normal file
BIN
static/skin/bittorrent.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
static/skin/download.png
Normal file
BIN
static/skin/download.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 722 B |
BIN
static/skin/fonts/Poppins.ttf
Normal file
BIN
static/skin/fonts/Poppins.ttf
Normal file
Binary file not shown.
BIN
static/skin/fonts/Roboto.ttf
Normal file
BIN
static/skin/fonts/Roboto.ttf
Normal file
Binary file not shown.
BIN
static/skin/hash.png
Normal file
BIN
static/skin/hash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 705 B |
456
static/skin/home.css
Normal file
456
static/skin/home.css
Normal file
@@ -0,0 +1,456 @@
|
||||
*,
|
||||
*::after,
|
||||
*::before {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: #00b4e4;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.kiwixNav {
|
||||
background-color: #f4f6f8;
|
||||
width: 100%;
|
||||
padding: 20px 11vw 25px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.kiwixHomeBody__results {
|
||||
font-size: 1.6rem;
|
||||
color: #333333;
|
||||
font-family: poppins;
|
||||
margin: 10px 0 -5px;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
left: 13px;
|
||||
}
|
||||
|
||||
.kiwixNav__filters {
|
||||
display: grid;
|
||||
grid-gap: 20px;
|
||||
grid-template-columns: 231px 231px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.kiwixNav__kiwixFilter {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
-ms-appearance: none;
|
||||
appearance: none;
|
||||
outline: 0;
|
||||
box-shadow: none;
|
||||
border: 0 !important;
|
||||
background: white;
|
||||
background-image: none;
|
||||
border-radius: 1px;
|
||||
width: 195px;
|
||||
height: 35px;
|
||||
flex: 1;
|
||||
color: black;
|
||||
padding: 7px 10px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kiwixNav__kiwixFilter:-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.kiwixNav__select {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 231px;
|
||||
height: 35px;
|
||||
line-height: 3;
|
||||
background: #909090;
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #b5b2b2;
|
||||
}
|
||||
|
||||
.kiwixNav__select::after {
|
||||
content: '\25BC';
|
||||
color: #fff;
|
||||
background-color: #909090;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 0 1em;
|
||||
cursor: pointer;
|
||||
pointer-events: none;
|
||||
-webkit-transition: .25s all ease;
|
||||
-o-transition: .25s all ease;
|
||||
transition: .25s all ease;
|
||||
}
|
||||
|
||||
.kiwixNav__kiwixFilter option {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.kiwixSearch {
|
||||
height: 35px;
|
||||
width: 229px;
|
||||
border-radius: 10px;
|
||||
border: solid 1px #b5b2b2;
|
||||
padding: 10px;
|
||||
background-image: url('./search-icon.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
background-origin: content-box;
|
||||
background-size: 15px;
|
||||
}
|
||||
|
||||
.searchButton {
|
||||
margin: 0 17px;
|
||||
height: 35px;
|
||||
width: 100px;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
background-color: #909090;
|
||||
border: solid 1px #b5b2b2;
|
||||
}
|
||||
|
||||
.searchButton:hover {
|
||||
background-color: #858585;
|
||||
}
|
||||
|
||||
.kiwixHomeBody {
|
||||
padding: 20px 15vw;
|
||||
min-height: calc(100vh - 170px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.book__list {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.book {
|
||||
border-color: #acacac;
|
||||
}
|
||||
|
||||
.book__wrapper {
|
||||
color: #444343;
|
||||
height: 280px;
|
||||
width: 250px;
|
||||
margin: 15px;
|
||||
background-color: #f7f7f7;
|
||||
border: 1px solid #ececec;
|
||||
border-radius: 3px;
|
||||
display: grid;
|
||||
grid-template-columns: 65px 1fr;
|
||||
grid-template-rows: 70px 120px 1fr 1fr;
|
||||
grid-gap: 5px;
|
||||
transition: transform 0.25s;
|
||||
}
|
||||
|
||||
.book__wrapper:hover {
|
||||
transform: scale(1.07);
|
||||
}
|
||||
|
||||
.book__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
margin: 10px 0 0 10px;
|
||||
}
|
||||
|
||||
.book__icon--image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.book__title {
|
||||
display: grid;
|
||||
font-family: poppins;
|
||||
color: black;
|
||||
padding: 12px 10px 0 2px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
#bookSize {
|
||||
font-size: 1.1rem;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
#bookTitle {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
.book__description {
|
||||
grid-column: 1 / 3;
|
||||
margin: 10px 10px 5px;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 7;
|
||||
-webkit-box-orient: vertical;
|
||||
font-family: roboto;
|
||||
font-weight: 300;
|
||||
font-size: 1.2rem;
|
||||
color: #4d4d4d;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.book__languageTag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 2px #acacac;
|
||||
background-color: #ffffff;
|
||||
font-weight: bold;
|
||||
font-family: roboto;
|
||||
color: black;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
margin: 10px auto 0 10px;
|
||||
border-radius: 5px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.book__tags {
|
||||
display: flex;
|
||||
text-align: end;
|
||||
font-size: 1.1rem;
|
||||
justify-content: flex-end;
|
||||
color: #909090;
|
||||
font-family: roboto;
|
||||
margin-right: 10px;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.book__tags--wrapper {
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.book__links {
|
||||
display: flex;
|
||||
text-align: end;
|
||||
justify-content: flex-end;
|
||||
grid-column: 2 / 3;
|
||||
font-size: 1.1rem;
|
||||
margin: 10px 4px;
|
||||
color: #00b4e4;
|
||||
}
|
||||
|
||||
.book__links > a, .book__links > span {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: #00b4e4;
|
||||
position: relative;
|
||||
padding: 1px 6px 0;
|
||||
font-family: roboto;
|
||||
}
|
||||
|
||||
.book__links > a:hover, .book__links > span:hover {
|
||||
background: #00b4e4;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-wrapper {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
background-color: rgba(0, 0, 0, 30%);
|
||||
}
|
||||
|
||||
.modal {
|
||||
color: #444343;
|
||||
height: 280px;
|
||||
width: 250px;
|
||||
margin: 15px;
|
||||
background-color: #f7f7f7;
|
||||
border: 1px solid #ececec;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.modal-heading {
|
||||
background-color: #f0f0f0;
|
||||
height: 20%;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #ececec;
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
display: flex;
|
||||
font-size: 15px;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
font-family: poppins;
|
||||
}
|
||||
|
||||
.modal-close-button {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-content div {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.modal-content a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.modal-content a>div {
|
||||
position: relative;
|
||||
top: -28px;
|
||||
left: 50px;
|
||||
text-decoration: underline;
|
||||
font-size: 13px;
|
||||
font-family: poppins;
|
||||
color: #444343;
|
||||
}
|
||||
|
||||
.modal-content img {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.fadeOut {
|
||||
position: fixed;
|
||||
display: none;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(180deg, rgba(232, 232, 232, 0) 0%, #ffffff 100%);
|
||||
height: 80px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.noResults {
|
||||
font-size: 1.6rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.noResults > a {
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.loader-spinner {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: 50%;
|
||||
border: 5px solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 5px solid #3498db;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: auto;
|
||||
-webkit-animation: spin 1s linear infinite; /* Safari */
|
||||
animation: spin 1s linear infinite;
|
||||
margin-top: 35px;
|
||||
margin-bottom: -35px;
|
||||
z-index: 2;
|
||||
}
|
||||
/* Safari */
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: none;
|
||||
position: relative;
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kiwixfooter {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 1.6rem;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.kiwixfooter > a {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1100px) {
|
||||
|
||||
.kiwixHomeBody {
|
||||
padding: 20px 10vw;
|
||||
min-height: calc(100vh - 170px);
|
||||
}
|
||||
|
||||
.kiwixNav__filters {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 590px) {
|
||||
|
||||
.kiwixNav {
|
||||
height: 257px;
|
||||
}
|
||||
|
||||
.kiwixHomeBody {
|
||||
min-height: calc(100vh - 287px);
|
||||
}
|
||||
|
||||
.kiwixSearch {
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
.searchButton {
|
||||
margin: 19px 0;
|
||||
width: 229px;
|
||||
}
|
||||
|
||||
.kiwixNav__filters {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 340px) {
|
||||
.kiwixHomeBody {
|
||||
padding: 20px 5vw;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
const bookOrderMap = new Map();
|
||||
const filterCookieName = 'filters';
|
||||
const oneDayDelta = 86400000;
|
||||
let loader;
|
||||
let footer;
|
||||
let fadeOutDiv;
|
||||
let iso;
|
||||
@@ -15,6 +16,8 @@
|
||||
let noResultInjected = false;
|
||||
let filters = getCookie(filterCookieName);
|
||||
let params = new URLSearchParams(window.location.search || filters || '');
|
||||
let noFilter = params.has('nofilter') && params.get('nofilter');
|
||||
let inIframe = false;
|
||||
let timer;
|
||||
|
||||
function queryUrlBuilder() {
|
||||
@@ -41,12 +44,23 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
const humanFriendlySize = (fileSize) => {
|
||||
if (fileSize === 0) {
|
||||
return '';
|
||||
}
|
||||
const units = ['bytes', 'kB', 'MB', 'GB', 'TB'];
|
||||
let quotient = Math.floor(Math.log10(fileSize) / 3);
|
||||
quotient = quotient < units.length ? quotient : units.length - 1;
|
||||
fileSize /= (1000 ** quotient);
|
||||
return `${+fileSize.toFixed(2)} ${units[quotient]}`;
|
||||
};
|
||||
|
||||
function htmlEncode(str) {
|
||||
return str.replace(/[\u00A0-\u9999<>\&]/gim, (i) => `&#${i.charCodeAt(0)};`);
|
||||
}
|
||||
|
||||
function viewPortToCount(){
|
||||
return Math.floor(window.innerHeight/100 + 1)*(window.innerWidth>1000 ? 3 : 2);
|
||||
return Math.floor(window.innerHeight/300 + 1)*(window.innerWidth>1000 ? 4 : 3);
|
||||
}
|
||||
|
||||
function getInnerHtml(node, query) {
|
||||
@@ -59,22 +73,38 @@
|
||||
const description = getInnerHtml(book, 'summary');
|
||||
const id = getInnerHtml(book, 'id');
|
||||
const iconUrl = getInnerHtml(book, 'icon');
|
||||
const articleCount = getInnerHtml(book, 'articleCount');
|
||||
const mediaCount = getInnerHtml(book, 'mediaCount');
|
||||
|
||||
const linkTag = document.createElement('a');
|
||||
linkTag.setAttribute('class', 'book');
|
||||
linkTag.setAttribute('data-id', id);
|
||||
linkTag.setAttribute('href', link);
|
||||
if (sort) {
|
||||
linkTag.setAttribute('data-idx', bookOrderMap.get(id));
|
||||
const language = getInnerHtml(book, 'language');
|
||||
const tags = getInnerHtml(book, 'tags');
|
||||
let tagHtml = tags.split(';').filter(tag => {return !(tag.split(':')[0].startsWith('_'))})
|
||||
.map((tag) => {return tag.charAt(0).toUpperCase() + tag.slice(1)})
|
||||
.join(' | ').replace(/_/g, ' ');
|
||||
let downloadLink;
|
||||
let zimSize = 0;
|
||||
try {
|
||||
const downloadBookLink = book.querySelector('link[type="application/x-zim"]')
|
||||
zimSize = parseInt(downloadBookLink.getAttribute('length'));
|
||||
downloadLink = downloadBookLink.getAttribute('href').split('.meta4')[0];
|
||||
} catch {
|
||||
downloadLink = '';
|
||||
}
|
||||
linkTag.innerHTML = `<div class='book__background' style="background-image: url('${iconUrl}');">
|
||||
<div class='book__title' title='${title}'>${title}</div>
|
||||
humanFriendlyZimSize = humanFriendlySize(zimSize);
|
||||
|
||||
const divTag = document.createElement('div');
|
||||
divTag.setAttribute('class', 'book');
|
||||
divTag.setAttribute('data-id', id);
|
||||
if (sort) {
|
||||
divTag.setAttribute('data-idx', bookOrderMap.get(id));
|
||||
}
|
||||
divTag.innerHTML = `<div class="book__wrapper"><div class='book__icon' ><img class="book__icon--image" src='${iconUrl}'></div>
|
||||
<div class='book__title' title='${title}'>
|
||||
<div id="bookTitle">${title}</div>
|
||||
${humanFriendlyZimSize ? `<div id='bookSize'>${humanFriendlyZimSize}</div>`: ''}
|
||||
</div>
|
||||
<div class='book__description' title='${description}'>${description}</div>
|
||||
<div class='book__info'>${articleCount} articles, ${mediaCount} medias</div>
|
||||
</div>`;
|
||||
return linkTag;
|
||||
<div class='book__languageTag'>${language.substr(0, 2).toUpperCase()}</div>
|
||||
<div class='book__tags'><div class="book__tags--wrapper">${tagHtml}</div></div>
|
||||
<div class='book__links'> ${!inIframe ? `<a href="${link}" data-hover="Preview">Preview</a>` : ''}${downloadLink ? `${!inIframe ? ' | ' : ''}<span class="download" data-link=${downloadLink} class="modal-button">Download</span>` : ''} </div></div>`;
|
||||
return divTag;
|
||||
}
|
||||
|
||||
function toggleFooter(show=false) {
|
||||
@@ -86,8 +116,63 @@
|
||||
}
|
||||
}
|
||||
|
||||
function insertModal(button) {
|
||||
const downloadLink = button.getAttribute('data-link');
|
||||
button.addEventListener('click', () => {
|
||||
document.body.insertAdjacentHTML('beforeend', `<div class="modal-wrapper">
|
||||
<div class="modal">
|
||||
<div class="modal-heading">
|
||||
<div class="modal-title">
|
||||
<div>
|
||||
Download
|
||||
</div>
|
||||
</div>
|
||||
<div onclick="closeModal()" class="modal-close-button">
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7071 1.70711C14.0976 1.31658 14.0976
|
||||
0.683417 13.7071 0.292893C13.3166 -0.0976311 12.6834 -0.0976311 12.2929 0.292893L7 5.58579L1.70711
|
||||
0.292893C1.31658 -0.0976311 0.683417 -0.0976311 0.292893 0.292893C-0.0976311 0.683417
|
||||
-0.0976311 1.31658 0.292893 1.70711L5.58579 7L0.292893 12.2929C-0.0976311 12.6834
|
||||
-0.0976311 13.3166 0.292893 13.7071C0.683417 14.0976 1.31658 14.0976 1.70711 13.7071L7
|
||||
8.41421L12.2929 13.7071C12.6834 14.0976 13.3166 14.0976 13.7071 13.7071C14.0976 13.3166
|
||||
14.0976 12.6834 13.7071 12.2929L8.41421 7L13.7071 1.70711Z" fill="black" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-regular-download">
|
||||
<a href="${downloadLink}" download>
|
||||
<img src="../skin/download.png" alt="direct download" />
|
||||
<div>Direct</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="modal-regular-download">
|
||||
<a href="${downloadLink}.sha256" download>
|
||||
<img src="../skin/hash.png" alt="download hash" />
|
||||
<div>Sha256 hash</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="modal-regular-download">
|
||||
<a href="${downloadLink}.magnet" target="_blank">
|
||||
<img src="../skin/magnet.png" alt="download magnet" />
|
||||
<div>Magnet link</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="modal-regular-download">
|
||||
<a href="${downloadLink}.torrent" download>
|
||||
<img src="../skin/bittorrent.png" alt="download torrent" />
|
||||
<div>Torrent file</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
})
|
||||
}
|
||||
|
||||
async function loadBooks() {
|
||||
const loader = document.querySelector('.loader');
|
||||
loader.style.display = 'block';
|
||||
return await fetch(queryUrlBuilder()).then(async (resp) => {
|
||||
const data = new window.DOMParser().parseFromString(await resp.text(), 'application/xml');
|
||||
@@ -96,12 +181,23 @@
|
||||
bookOrderMap.set(getInnerHtml(book, 'id'), idx);
|
||||
});
|
||||
incrementalLoadingParams.start += books.length;
|
||||
if (parseInt(data.querySelector('totalResults').innerHTML) === bookOrderMap.size) {
|
||||
const results = parseInt(data.querySelector('totalResults').innerHTML)
|
||||
if (results === bookOrderMap.size) {
|
||||
incrementalLoadingParams.count = 0;
|
||||
toggleFooter(true);
|
||||
} else {
|
||||
toggleFooter();
|
||||
}
|
||||
const kiwixResultText = document.querySelector('.kiwixHomeBody__results')
|
||||
if (results) {
|
||||
let resultText = `${results} books`;
|
||||
if (results === 1) {
|
||||
resultText = `${results} book`;
|
||||
}
|
||||
kiwixResultText.innerHTML = resultText;
|
||||
} else {
|
||||
kiwixResultText.innerHTML = ``;
|
||||
}
|
||||
loader.style.display = 'none';
|
||||
return books;
|
||||
});
|
||||
@@ -115,28 +211,42 @@
|
||||
}
|
||||
|
||||
function checkAndInjectEmptyMessage() {
|
||||
const kiwixHomeBody = document.querySelector('.kiwixHomeBody');
|
||||
if (!bookOrderMap.size) {
|
||||
if (!noResultInjected) {
|
||||
noResultInjected = true;
|
||||
iso.remove(document.getElementsByClassName('book__list')[0].getElementsByTagName('a'));
|
||||
iso.remove(document.getElementsByClassName('book__list')[0].getElementsByTagName('div'));
|
||||
iso.layout();
|
||||
const spanTag = document.createElement('span');
|
||||
spanTag.setAttribute('class', 'noResults');
|
||||
spanTag.innerHTML = `No result. Would you like to <a href="/?lang=">reset filter?</a>`;
|
||||
document.querySelector('body').append(spanTag);
|
||||
spanTag.getElementsByTagName('a')[0].onclick = (event) => {
|
||||
event.preventDefault();
|
||||
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?lang=`);
|
||||
setCookie(filterCookieName, 'lang=');
|
||||
resetAndFilter();
|
||||
filterTypes.forEach(key => {document.getElementsByName(key)[0].value = params.get(key) || ''});
|
||||
};
|
||||
setTimeout(() => {
|
||||
const divTag = document.createElement('div');
|
||||
divTag.setAttribute('class', 'noResults');
|
||||
divTag.innerHTML = `No result. Would you like to <a href="/?lang=">reset filter</a>?`;
|
||||
kiwixHomeBody.append(divTag);
|
||||
kiwixHomeBody.setAttribute('style', 'display: flex; justify-content: center; align-items: center');
|
||||
divTag.getElementsByTagName('a')[0].onclick = (event) => {
|
||||
event.preventDefault();
|
||||
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?lang=`);
|
||||
setCookie(filterCookieName, 'lang=');
|
||||
resetAndFilter();
|
||||
document.querySelectorAll('.filter').forEach(filter => {
|
||||
filter.value = params.get(filter.name) || '';
|
||||
if (filter.value) {
|
||||
filter.style = 'background-color: #858585; color: #fff';
|
||||
} else {
|
||||
filter.style = 'background-color: #ffffff; color: black';
|
||||
}
|
||||
})
|
||||
};
|
||||
loader.setAttribute('style', 'position: absolute; top: 50%');
|
||||
}, 300);
|
||||
}
|
||||
return true;
|
||||
} else if (noResultInjected) {
|
||||
noResultInjected = false;
|
||||
document.getElementsByClassName('noResults')[0].remove();
|
||||
kiwixHomeBody.removeAttribute('style');
|
||||
}
|
||||
loader.removeAttribute('style');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -170,7 +280,13 @@
|
||||
});
|
||||
books = [...books].filter((book) => {return !booksToFilter.has(getInnerHtml(book, 'id'))});
|
||||
booksToDelete.forEach(book => {iso.remove(book);});
|
||||
books.forEach((book) => {iso.insert(generateBookHtml(book, sort))});
|
||||
books.forEach((book) => {
|
||||
iso.insert(generateBookHtml(book, sort))
|
||||
const downloadButton = document.querySelector(`[data-id="${getInnerHtml(book, 'id')}"] .download`);
|
||||
if (downloadButton) {
|
||||
insertModal(downloadButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function resetAndFilter(filterType = '', filterValue = '') {
|
||||
@@ -185,12 +301,19 @@
|
||||
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?${params.toString()}`);
|
||||
setCookie(filterCookieName, params.toString());
|
||||
}
|
||||
document.querySelectorAll('.filter').forEach(filter => {
|
||||
if (filter.value) {
|
||||
filter.style = 'background-color: #858585; color: #fff';
|
||||
} else {
|
||||
filter.style = 'background-color: #ffffff; color: black';
|
||||
}
|
||||
});
|
||||
await loadAndDisplayBooks(true);
|
||||
}
|
||||
|
||||
window.addEventListener('popstate', async () => {
|
||||
await resetAndFilter();
|
||||
filterTypes.forEach(key => {document.getElementsByName(key)[0].value = params.get(key) || ''});
|
||||
document.querySelectorAll('.filter').forEach(filter => {filter.value = params.get(filter.name) || ''});
|
||||
});
|
||||
|
||||
async function loadSubset() {
|
||||
@@ -215,6 +338,14 @@
|
||||
window.addEventListener('scroll', loadSubset);
|
||||
|
||||
window.onload = async () => {
|
||||
if (noFilter) {
|
||||
document.getElementById('kiwixNav').remove();
|
||||
}
|
||||
if (window.location !== window.parent.location) {
|
||||
document.getElementById('kiwixFooter').innerHTML = '';
|
||||
document.getElementById('kiwixHomeBody').style.padding = '20px';
|
||||
inIframe = true;
|
||||
}
|
||||
iso = new Isotope( '.book__list', {
|
||||
itemSelector: '.book',
|
||||
getSortData:{
|
||||
@@ -223,28 +354,40 @@
|
||||
return index ? parseInt(index) : Infinity;
|
||||
}
|
||||
},
|
||||
sortBy: 'weight'
|
||||
sortBy: 'weight',
|
||||
layoutMode: 'cellsByRow',
|
||||
cellsByRow: {
|
||||
columnWidth: '.book',
|
||||
rowHeight: '.book'
|
||||
}
|
||||
});
|
||||
footer = document.getElementById('kiwixfooter');
|
||||
footer = document.getElementById('kiwixFooter');
|
||||
fadeOutDiv = document.getElementById('fadeOut');
|
||||
loader = document.querySelector('.loader');
|
||||
await loadAndDisplayBooks();
|
||||
await loadAndDisplayOptions('#languageFilter', langList);
|
||||
await loadAndDisplayOptions('#categoryFilter', categoryList);
|
||||
filterTypes.forEach((filter) => {
|
||||
const filterTag = document.getElementsByName(filter)[0];
|
||||
filterTag.addEventListener('change', () => {resetAndFilter(filterTag.name, filterTag.value)});
|
||||
document.querySelectorAll('.filter').forEach(filter => {
|
||||
filter.addEventListener('change', () => {resetAndFilter(filter.name, filter.value)});
|
||||
});
|
||||
if (filters) {
|
||||
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?${params.toString()}`);
|
||||
}
|
||||
params.forEach((value, key) => {document.getElementsByName(key)[0].value = value});
|
||||
document.getElementById('kiwixSearchForm').onsubmit = (event) => {event.preventDefault()};
|
||||
if (!window.location.search) {
|
||||
if (!window.location.search && !inIframe) {
|
||||
const browserLang = navigator.language.split('-')[0];
|
||||
const langFilter = document.getElementById('languageFilter');
|
||||
langFilter.value = browserLang.length === 3 ? browserLang : iso6391To3[browserLang];
|
||||
langFilter.dispatchEvent(new Event('change'));
|
||||
}
|
||||
document.querySelectorAll('.filter').forEach(filter => {
|
||||
if (filter.value) {
|
||||
filter.style = 'background-color: #858585; color: #fff';
|
||||
} else {
|
||||
filter.style = 'background-color: #ffffff; color: black';
|
||||
}
|
||||
});
|
||||
setCookie(filterCookieName, params.toString());
|
||||
}
|
||||
})();
|
||||
|
||||
68
static/skin/isotope.pkgd.min.js
vendored
68
static/skin/isotope.pkgd.min.js
vendored
File diff suppressed because one or more lines are too long
BIN
static/skin/magnet.png
Normal file
BIN
static/skin/magnet.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 818 B |
6
static/skin/search-icon.svg
Normal file
6
static/skin/search-icon.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" width="512.000000" height="512.000000" preserveAspectRatio="xMidYMid meet" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/>
|
||||
|
||||
|
||||
<g class="currentLayer" style=""><title>Layer 1</title><g transform="translate(0,512) scale(0.10000000149011612,-0.10000000149011612) " fill="#484848" stroke="none" id="svg_1" class="selected" fill-opacity="1">
|
||||
<path d="M1858 5104 c-341 -50 -678 -192 -963 -408 -104 -78 -304 -278 -384 -384 -124 -164 -256 -409 -315 -583 -311 -929 47 -1926 877 -2442 662 -410 1535 -405 2188 14 l76 49 125 -128 c69 -70 352 -364 630 -653 574 -597 548 -575 688 -567 85 5 123 21 176 75 52 52 77 114 77 193 0 98 -24 142 -151 273 -420 434 -1132 1177 -1132 1182 0 3 22 33 49 68 164 209 291 471 362 746 159 614 28 1255 -363 1774 -78 104 -279 305 -386 386 -288 218 -618 355 -974 406 -130 19 -448 18 -580 -1z m502 -545 c308 -38 607 -184 844 -409 476 -455 606 -1146 327 -1745 -128 -273 -356 -519 -626 -674 -387 -222 -857 -259 -1282 -102 -626 231 -1040 879 -983 1540 13 146 33 241 77 371 214 634 830 1060 1493 1032 36 -1 103 -7 150 -13z" id="svg_2" fill="#484848" fill-opacity="1"/>
|
||||
</g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -2,6 +2,7 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Welcome to Kiwix Server</title>
|
||||
<script
|
||||
type="text/javascript"
|
||||
@@ -21,159 +22,20 @@
|
||||
href="{{root}}/skin/jquery-ui/jquery-ui.theme.min.css"
|
||||
rel="Stylesheet"
|
||||
/>
|
||||
<link
|
||||
type="text/css"
|
||||
href="{{root}}/skin/home.css"
|
||||
rel="Stylesheet"
|
||||
/>
|
||||
<style>
|
||||
html {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
@font-face {
|
||||
font-family: "poppins";
|
||||
src: url("{{root}}/skin/fonts/Poppins.ttf") format("truetype");
|
||||
}
|
||||
body {
|
||||
background: radial-gradient(#eeeeee 15%, transparent 16%) 0 0,
|
||||
radial-gradient(#eeeeee 15%, transparent 16%) 8px 8px,
|
||||
radial-gradient(rgba(255, 255, 255, 0.1) 15%, transparent 20%) 0 1px,
|
||||
radial-gradient(rgba(255, 255, 255, 0.1) 15%, transparent 20%) 8px 9px;
|
||||
background-color: #e8e8e8;
|
||||
background-size: 16px 16px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 1100px;
|
||||
min-height: 100%;
|
||||
}
|
||||
.book__list {
|
||||
text-align: center;
|
||||
}
|
||||
.kiwixHomeBody {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
min-height: 100%;
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
.book {
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
margin: 8px;
|
||||
padding: 12px 15px;
|
||||
width: 300px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
font-family: sans-serif;
|
||||
font-size: 13px;
|
||||
background-color: #f1f1f1;
|
||||
box-shadow: 2px 2px 5px 0 #ccc;
|
||||
}
|
||||
#kiwixfooter {
|
||||
text-align: center;
|
||||
margin: 0.5em;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 46%;
|
||||
}
|
||||
.kiwixHomeNavbar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.kiwixFilter {
|
||||
margin: 8px 10px;
|
||||
}
|
||||
.kiwixSearch, .searchButton {
|
||||
margin: 0 13px 0 0;
|
||||
}
|
||||
.kiwixSearchForm {
|
||||
margin: 8px 10px;
|
||||
float: right;
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.kiwixHomeBody {
|
||||
padding: 0 125px;
|
||||
}
|
||||
}
|
||||
.book:hover {
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: none;
|
||||
}
|
||||
.book__background {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 48px 48px;
|
||||
background-position: top right;
|
||||
}
|
||||
.book__title {
|
||||
padding: 0 55px 0 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 18px;
|
||||
color: #0645ad;
|
||||
line-height: 1em;
|
||||
}
|
||||
.book__description {
|
||||
padding: 5px 55px 5px 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 15px;
|
||||
line-height: 1em;
|
||||
}
|
||||
.book__info {
|
||||
color: #777;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
line-height: 1em;
|
||||
}
|
||||
a:link {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
.noResults {
|
||||
position: absolute;
|
||||
top: 48%;
|
||||
left: 42%;
|
||||
}
|
||||
.loader-spinner {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: 50%;
|
||||
border: 5px solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 5px solid #3498db;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: auto;
|
||||
-webkit-animation: spin 1s linear infinite; /* Safari */
|
||||
animation: spin 1s linear infinite;
|
||||
margin-top: 35px;
|
||||
margin-bottom: -35px;
|
||||
z-index: 2;
|
||||
}
|
||||
/* Safari */
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
.loader {
|
||||
position: relative;
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
}
|
||||
.fadeOut {
|
||||
position: fixed;
|
||||
display: none;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(180deg, rgba(232, 232, 232, 0) 0%, rgb(232, 232, 232) 100%);
|
||||
height: 80px;
|
||||
width: 100%;
|
||||
}
|
||||
.spacer {
|
||||
height: 20px;
|
||||
background: transparent;
|
||||
|
||||
@font-face {
|
||||
font-family: "roboto";
|
||||
src: url("{{root}}/skin/fonts/Roboto.ttf") format("truetype");
|
||||
}
|
||||
</style>
|
||||
<script src="{{root}}/skin/isotope.pkgd.min.js" defer></script>
|
||||
@@ -182,25 +44,38 @@
|
||||
<script src="{{root}}/skin/iso6391To3.js"></script>
|
||||
<script type="text/javascript" src="{{root}}/skin/index.js" defer></script>
|
||||
</head>
|
||||
<body class="kiwix">
|
||||
<div class='kiwixHomeNavbar'>
|
||||
<select name="lang" id="languageFilter" class='kiwixFilter'>
|
||||
<option value="" selected>All languages</option>
|
||||
</select>
|
||||
<select name="category" id="categoryFilter" class='kiwixFilter'>
|
||||
<option value="" selected>All categories</option>
|
||||
</select>
|
||||
<form id='kiwixSearchForm' class='kiwixSearchForm'>
|
||||
<input type="text" name="q" id="searchFilter" class='kiwixSearch'>
|
||||
<body>
|
||||
<div id='kiwixNav' class='kiwixNav'>
|
||||
<div class="kiwixNav__filters">
|
||||
<div class="kiwixNav__select">
|
||||
<select name="lang" id="languageFilter" class='kiwixNav__kiwixFilter filter'>
|
||||
<option value="" selected>All languages</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="kiwixNav__select">
|
||||
<select name="category" id="categoryFilter" class='kiwixNav__kiwixFilter filter'>
|
||||
<option value="" selected>All categories</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<form id='kiwixSearchForm' class='kiwixNav__SearchForm'>
|
||||
<input type="text" name="q" placeholder="Search" id="searchFilter" class='kiwixSearch filter'>
|
||||
<input type="submit" class="searchButton" value="Search"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="kiwixHomeBody">
|
||||
<div id="kiwixHomeBody" class="kiwixHomeBody">
|
||||
<h3 class="kiwixHomeBody__results"></h3>
|
||||
<div class="book__list"></div>
|
||||
<div id="fadeOut" class="fadeOut"></div>
|
||||
</div>
|
||||
<div class="loader"><div class="loader-spinner"></div></div>
|
||||
<div class="spacer"></div>
|
||||
<div id="kiwixfooter">Powered by <a href="https://kiwix.org">Kiwix</a></div>
|
||||
<div class="loader" style="position: absolute; top: 50%"><div class="loader-spinner"></div></div>
|
||||
<div id="kiwixFooter" class="kiwixfooter">Powered by <a href="https://kiwix.org">Kiwix</a></div>
|
||||
</body>
|
||||
<script>
|
||||
function closeModal() {
|
||||
for(modal of document.getElementsByClassName('modal-wrapper')) {
|
||||
modal.remove();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "../include/manager.h"
|
||||
#include "../include/library.h"
|
||||
#include "../include/book.h"
|
||||
#include "../include/tools.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
@@ -13,13 +14,13 @@ TEST(ManagerTest, addBookFromPathAndGetIdTest)
|
||||
auto bookId = manager.addBookFromPathAndGetId("./test/example.zim");
|
||||
ASSERT_NE(bookId, "");
|
||||
kiwix::Book book = lib.getBookById(bookId);
|
||||
EXPECT_EQ(book.getPath(), computeAbsolutePath("", "./test/example.zim"));
|
||||
EXPECT_EQ(book.getPath(), kiwix::computeAbsolutePath("", "./test/example.zim"));
|
||||
|
||||
const std::string pathToSave = "./pathToSave";
|
||||
const std::string url = "url";
|
||||
bookId = manager.addBookFromPathAndGetId("./test/example.zim", pathToSave, url, true);
|
||||
book = lib.getBookById(bookId);
|
||||
auto savedPath = computeAbsolutePath(removeLastPathElement(manager.writableLibraryPath), pathToSave);
|
||||
auto savedPath = kiwix::computeAbsolutePath(kiwix::removeLastPathElement(manager.writableLibraryPath), pathToSave);
|
||||
EXPECT_EQ(book.getPath(), savedPath);
|
||||
EXPECT_EQ(book.getUrl(), url);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ tests = [
|
||||
'kiwixserve',
|
||||
'book',
|
||||
'manager',
|
||||
'opds_catalog',
|
||||
'reader',
|
||||
'searcher'
|
||||
]
|
||||
|
||||
if build_machine.system() != 'windows'
|
||||
|
||||
85
test/opds_catalog.cpp
Normal file
85
test/opds_catalog.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2021 Veloman Yunkan <veloman.yunkan@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
|
||||
* NON-INFRINGEMENT. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../include/opds_catalog.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace kiwix;
|
||||
|
||||
TEST(OpdsCatalog, getSearchUrl)
|
||||
{
|
||||
#define EXPECT_SEARCH_URL(url) EXPECT_EQ(url, getSearchUrl(f))
|
||||
{
|
||||
Filter f;
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.query("abc");
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?q=abc");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.query("abc def");
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?q=abc%20def");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.category("ted");
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?category=ted");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.lang("eng");
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?lang=eng");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.name("second");
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?name=second");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.acceptTags({"paper", "plastic"});
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?tag=paper;plastic");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.query("abc");
|
||||
f.category("ted");
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?q=abc&category=ted");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.category("ted");
|
||||
f.query("abc");
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?q=abc&category=ted");
|
||||
}
|
||||
{
|
||||
Filter f;
|
||||
f.query("peru");
|
||||
f.category("scifi");
|
||||
f.lang("html");
|
||||
f.name("edsonarantesdonascimento");
|
||||
f.acceptTags({"body", "script"});
|
||||
EXPECT_SEARCH_URL("/catalog/v2/entries?q=peru&category=scifi&lang=html&name=edsonarantesdonascimento&tag=body;script");
|
||||
}
|
||||
#undef EXPECT_SEARCH_URL
|
||||
}
|
||||
@@ -20,7 +20,8 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../include/tools/pathTools.h"
|
||||
#include "../include/tools.h"
|
||||
#include "../src/tools/pathTools.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# define S "\\"
|
||||
@@ -101,61 +102,61 @@ TEST(pathTools, normalizePartsRelative)
|
||||
|
||||
TEST(pathTools, isRelativePath)
|
||||
{
|
||||
ASSERT_TRUE(isRelativePath("foo"));
|
||||
ASSERT_TRUE(isRelativePath(P2("foo","bar")));
|
||||
ASSERT_TRUE(isRelativePath(P3(".","foo","bar")));
|
||||
ASSERT_TRUE(isRelativePath(P2("..","foo")));
|
||||
ASSERT_TRUE(isRelativePath(P4("foo","","bar","")));
|
||||
ASSERT_FALSE(isRelativePath(A1("foo")));
|
||||
ASSERT_FALSE(isRelativePath(A2("foo", "bar")));
|
||||
ASSERT_TRUE(kiwix::isRelativePath("foo"));
|
||||
ASSERT_TRUE(kiwix::isRelativePath(P2("foo","bar")));
|
||||
ASSERT_TRUE(kiwix::isRelativePath(P3(".","foo","bar")));
|
||||
ASSERT_TRUE(kiwix::isRelativePath(P2("..","foo")));
|
||||
ASSERT_TRUE(kiwix::isRelativePath(P4("foo","","bar","")));
|
||||
ASSERT_FALSE(kiwix::isRelativePath(A1("foo")));
|
||||
ASSERT_FALSE(kiwix::isRelativePath(A2("foo", "bar")));
|
||||
#ifdef _WIN32
|
||||
ASSERT_FALSE(isRelativePath(P2(A_SAMBA, "foo")));
|
||||
ASSERT_FALSE(isRelativePath(P3(A_SAMBA, "foo", "bar")));
|
||||
ASSERT_FALSE(kiwix::isRelativePath(P2(A_SAMBA, "foo")));
|
||||
ASSERT_FALSE(kiwix::isRelativePath(P3(A_SAMBA, "foo", "bar")));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(pathTools, computeAbsolutePath)
|
||||
{
|
||||
ASSERT_EQ(computeAbsolutePath(A2("a","b"), "foo"),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A2("a","b"), "foo"),
|
||||
A3("a","b","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b",""), "foo"),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b",""), "foo"),
|
||||
A3("a","b","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A2("a","b"), P2(".","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A2("a","b"), P2(".","foo")),
|
||||
A3("a","b","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A2("a","b"), P2("..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A2("a","b"), P2("..","foo")),
|
||||
A2("a","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b",""), P2("..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b",""), P2("..","foo")),
|
||||
A2("a","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A5("a","b","c","d","e"), P2("..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A5("a","b","c","d","e"), P2("..","foo")),
|
||||
A5("a","b","c","d","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A5("a","b","c","d","e"), P5("..","..","..","g","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A5("a","b","c","d","e"), P5("..","..","..","g","foo")),
|
||||
A4("a","b","g","foo"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(computeAbsolutePath(P4(A_SAMBA,"a","b",""), P2("..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(P4(A_SAMBA,"a","b",""), P2("..","foo")),
|
||||
P3(A_SAMBA,"a","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(P6(A_SAMBA,"a","b","c","d","e"), P5("..","..","..","g","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(P6(A_SAMBA,"a","b","c","d","e"), P5("..","..","..","g","foo")),
|
||||
P5(A_SAMBA,"a","b","g","foo"));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(pathTools, computeRelativePath)
|
||||
{
|
||||
ASSERT_EQ(computeRelativePath(A2("a","b"), A3("a","b","foo")),
|
||||
ASSERT_EQ(kiwix::computeRelativePath(A2("a","b"), A3("a","b","foo")),
|
||||
"foo");
|
||||
ASSERT_EQ(computeRelativePath(A3("a","b",""), A3("a","b","foo")),
|
||||
ASSERT_EQ(kiwix::computeRelativePath(A3("a","b",""), A3("a","b","foo")),
|
||||
"foo");
|
||||
ASSERT_EQ(computeRelativePath(A2("a","b"), A2("a","foo")),
|
||||
ASSERT_EQ(kiwix::computeRelativePath(A2("a","b"), A2("a","foo")),
|
||||
P2("..","foo"));
|
||||
ASSERT_EQ(computeRelativePath(A3("a","b",""), A2("a","foo")),
|
||||
ASSERT_EQ(kiwix::computeRelativePath(A3("a","b",""), A2("a","foo")),
|
||||
P2("..","foo"));
|
||||
ASSERT_EQ(computeRelativePath(A5("a","b","c","d","e"), A5("a","b","c","d","foo")),
|
||||
ASSERT_EQ(kiwix::computeRelativePath(A5("a","b","c","d","e"), A5("a","b","c","d","foo")),
|
||||
P2("..","foo"));
|
||||
ASSERT_EQ(computeRelativePath(A5("a","b","c","d","e"), A4("a","b","g","foo")),
|
||||
ASSERT_EQ(kiwix::computeRelativePath(A5("a","b","c","d","e"), A4("a","b","g","foo")),
|
||||
P5("..","..","..","g","foo"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(computeRelativePath(P3(A_SAMBA,"a","b"), P3(A_SAMBA,"a","foo")),
|
||||
ASSERT_EQ(kiwix::computeRelativePath(P3(A_SAMBA,"a","b"), P3(A_SAMBA,"a","foo")),
|
||||
P2("..","foo"));
|
||||
ASSERT_EQ(computeRelativePath(P6(A_SAMBA,"a","b","c","d","e"), P5(A_SAMBA,"a","b","g","foo")),
|
||||
ASSERT_EQ(kiwix::computeRelativePath(P6(A_SAMBA,"a","b","c","d","e"), P5(A_SAMBA,"a","b","g","foo")),
|
||||
P5("..","..","..","g","foo"));
|
||||
|
||||
#endif
|
||||
@@ -163,73 +164,73 @@ TEST(pathTools, computeRelativePath)
|
||||
|
||||
TEST(pathTools, removeLastPathElement)
|
||||
{
|
||||
ASSERT_EQ(removeLastPathElement(P3("a","b","c")),
|
||||
ASSERT_EQ(kiwix::removeLastPathElement(P3("a","b","c")),
|
||||
P2("a","b"));
|
||||
ASSERT_EQ(removeLastPathElement(A3("a","b","c")),
|
||||
ASSERT_EQ(kiwix::removeLastPathElement(A3("a","b","c")),
|
||||
A2("a","b"));
|
||||
ASSERT_EQ(removeLastPathElement(P4("a","b","c","")),
|
||||
ASSERT_EQ(kiwix::removeLastPathElement(P4("a","b","c","")),
|
||||
P2("a","b"));
|
||||
ASSERT_EQ(removeLastPathElement(A4("a","b","c","")),
|
||||
ASSERT_EQ(kiwix::removeLastPathElement(A4("a","b","c","")),
|
||||
A2("a","b"));
|
||||
}
|
||||
|
||||
TEST(pathTools, appendToDirectory)
|
||||
{
|
||||
ASSERT_EQ(appendToDirectory(P3("a","b","c"), "foo.xml"),
|
||||
ASSERT_EQ(kiwix::appendToDirectory(P3("a","b","c"), "foo.xml"),
|
||||
P4("a","b","c","foo.xml"));
|
||||
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), "foo.xml"),
|
||||
ASSERT_EQ(kiwix::appendToDirectory(P4("a","b","c",""), "foo.xml"),
|
||||
P4("a","b","c","foo.xml"));
|
||||
ASSERT_EQ(appendToDirectory(P3("a","b","c"), P2("d","foo.xml")),
|
||||
ASSERT_EQ(kiwix::appendToDirectory(P3("a","b","c"), P2("d","foo.xml")),
|
||||
P5("a","b","c","d","foo.xml"));
|
||||
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), P2("d","foo.xml")),
|
||||
ASSERT_EQ(kiwix::appendToDirectory(P4("a","b","c",""), P2("d","foo.xml")),
|
||||
P5("a","b","c","d","foo.xml"));
|
||||
ASSERT_EQ(appendToDirectory(P3("a","b","c"), P2(".","foo.xml")),
|
||||
ASSERT_EQ(kiwix::appendToDirectory(P3("a","b","c"), P2(".","foo.xml")),
|
||||
P5("a","b","c",".","foo.xml"));
|
||||
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), P2(".","foo.xml")),
|
||||
ASSERT_EQ(kiwix::appendToDirectory(P4("a","b","c",""), P2(".","foo.xml")),
|
||||
P5("a","b","c",".","foo.xml"));
|
||||
}
|
||||
|
||||
|
||||
TEST(pathTools, goUp)
|
||||
{
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), ".."),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), ".."),
|
||||
A2("a", "b"));
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P2("..","..")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P2("..","..")),
|
||||
A1("a"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","..")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P3("..","..","..")),
|
||||
"c:");
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","..")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P4("..","..","..","..")),
|
||||
"c:");
|
||||
ASSERT_EQ(computeAbsolutePath(P4(A_SAMBA,"a","b","c"), ".."),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(P4(A_SAMBA,"a","b","c"), ".."),
|
||||
P3(A_SAMBA,"a", "b"));
|
||||
ASSERT_EQ(computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P2("..","..")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P2("..","..")),
|
||||
P2(A_SAMBA,"a"));
|
||||
ASSERT_EQ(computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P3("..","..","..")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P3("..","..","..")),
|
||||
A_SAMBA);
|
||||
ASSERT_EQ(computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P4("..","..","..","..")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P4("..","..","..","..")),
|
||||
A_SAMBA);
|
||||
|
||||
#else
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","..")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P3("..","..","..")),
|
||||
"/");
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","..")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P4("..","..","..","..")),
|
||||
"/");
|
||||
#endif
|
||||
|
||||
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P2("..", "foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P2("..", "foo")),
|
||||
A3("a", "b","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P3("..","..","foo")),
|
||||
A2("a","foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P4("..","..","..","foo")),
|
||||
A1("foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P5("..","..","..","..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(A3("a","b","c"), P5("..","..","..","..","foo")),
|
||||
A1("foo"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P4("..","..","..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P4("..","..","..","foo")),
|
||||
P2(A_SAMBA,"foo"));
|
||||
ASSERT_EQ(computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P5("..","..","..","..","foo")),
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(P4(A_SAMBA,"a","b","c"), P5("..","..","..","..","foo")),
|
||||
P2(A_SAMBA,"foo"));
|
||||
#endif
|
||||
}
|
||||
@@ -241,20 +242,20 @@ TEST(pathTools, dirChange)
|
||||
std::string p1("c:\\a\\b\\c");
|
||||
std::string p2("d:\\d\\e\\foo.xml");
|
||||
{
|
||||
std::string relative_path = computeRelativePath(p1, p2);
|
||||
std::string relative_path = kiwix::computeRelativePath(p1, p2);
|
||||
ASSERT_EQ(relative_path, p2);
|
||||
std::string abs_path = computeAbsolutePath(p1, relative_path);
|
||||
std::string abs_path = kiwix::computeAbsolutePath(p1, relative_path);
|
||||
ASSERT_EQ(abs_path, p2);
|
||||
ASSERT_EQ(computeAbsolutePath(p1, "..\\..\\..\\..\\..\\d:\\d\\e\\foo.xml"), p2);
|
||||
ASSERT_EQ(kiwix::computeAbsolutePath(p1, "..\\..\\..\\..\\..\\d:\\d\\e\\foo.xml"), p2);
|
||||
}
|
||||
std::string ps("\\\\samba\\d\\e\\foo.xml");
|
||||
{
|
||||
std::string relative_path = computeRelativePath(p1, ps);
|
||||
std::string relative_path = kiwix::computeRelativePath(p1, ps);
|
||||
ASSERT_EQ(relative_path, ps);
|
||||
std::string abs_path = computeAbsolutePath(p1, relative_path);
|
||||
std::string abs_path = kiwix::computeAbsolutePath(p1, relative_path);
|
||||
ASSERT_EQ(abs_path, ps);
|
||||
// I'm not sure this test is valid on windows :/
|
||||
// ASSERT_EQ(computeAbsolutePath(p1, "..\\..\\..\\..\\..\\\\samba\\d\\e\\foo.xml"), ps);
|
||||
// ASSERT_EQ(kiwix::computeAbsolutePath(p1, "..\\..\\..\\..\\..\\\\samba\\d\\e\\foo.xml"), ps);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
62
test/reader.cpp
Normal file
62
test/reader.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "../include/reader.h"
|
||||
#include "zim/archive.h"
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
/**
|
||||
* This test file is written primarily to demonstrate how Reader is simply a
|
||||
* wrapper over an archive. We will be dropping this wrapper soon.
|
||||
**/
|
||||
TEST (Reader, archiveWrapper) {
|
||||
Reader reader("./test/zimfile.zim");
|
||||
zim::Archive archive = *reader.getZimArchive();
|
||||
|
||||
std::ostringstream s;
|
||||
s << archive.getUuid();
|
||||
|
||||
ASSERT_EQ(reader.getId(), s.str());
|
||||
ASSERT_EQ(reader.getGlobalCount(), archive.getEntryCount());
|
||||
ASSERT_EQ(reader.getMainPage().getTitle(), archive.getMainEntry().getTitle());
|
||||
ASSERT_EQ(reader.hasFulltextIndex(), archive.hasFulltextIndex());
|
||||
ASSERT_NO_THROW(reader.getRandomPage());
|
||||
}
|
||||
|
||||
TEST (Reader, getFunctions) {
|
||||
zim::Archive archive("./test/zimfile.zim");
|
||||
Reader reader("./test/zimfile.zim");
|
||||
|
||||
auto archiveEntry = archive.getRandomEntry();
|
||||
ASSERT_TRUE(reader.pathExists(archiveEntry.getPath()));
|
||||
auto readerEntry = reader.getEntryFromPath(archiveEntry.getPath());
|
||||
ASSERT_EQ(readerEntry.getTitle(), archiveEntry.getTitle());
|
||||
|
||||
ASSERT_FALSE(reader.pathExists("invalidEntryPath"));
|
||||
ASSERT_THROW(reader.getEntryFromPath("invalidEntryPath"), NoEntry);
|
||||
|
||||
readerEntry = reader.getEntryFromTitle(archiveEntry.getTitle());
|
||||
ASSERT_EQ(readerEntry.getTitle(), archiveEntry.getTitle());
|
||||
}
|
||||
|
||||
TEST (Reader, suggestions) {
|
||||
Reader reader("./test/zimfile.zim");
|
||||
SuggestionsList_t suggestions;
|
||||
reader.searchSuggestionsSmart("The Genius", 4, suggestions);
|
||||
|
||||
std::vector<std::string> suggestionResult, expectedResult;
|
||||
std::string suggestionTitle;
|
||||
for (auto it = suggestions.begin(); it != suggestions.end(); it++) {
|
||||
suggestionResult.push_back(it->getTitle());
|
||||
}
|
||||
|
||||
expectedResult = {
|
||||
"The Genius After Hours",
|
||||
"The Genius Hits the Road",
|
||||
"The Genius Sings the Blues",
|
||||
"The Genius of Ray Charles"
|
||||
};
|
||||
|
||||
ASSERT_EQ(suggestionResult, expectedResult);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <string>
|
||||
|
||||
|
||||
#include "../include/tools/regexTools.h"
|
||||
#include "../src/tools/regexTools.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
25
test/searcher.cpp
Normal file
25
test/searcher.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "../include/searcher.h"
|
||||
#include "../include/reader.h"
|
||||
|
||||
namespace kiwix
|
||||
{
|
||||
|
||||
TEST(Searcher, search) {
|
||||
Reader reader("./test/example.zim");
|
||||
|
||||
Searcher searcher;
|
||||
searcher.add_reader(&reader);
|
||||
ASSERT_EQ(searcher.get_reader(0)->getTitle(), reader.getTitle());
|
||||
|
||||
searcher.search("wiki", 0, 2);
|
||||
searcher.restart_search();
|
||||
ASSERT_EQ(searcher.getEstimatedResultCount(), (unsigned int)2);
|
||||
|
||||
auto result = searcher.getNextResult();
|
||||
ASSERT_EQ(result->get_title(), "FreedomBox for Communities/Offline Wikipedia - Wikibooks, open books for an open world");
|
||||
result = searcher.getNextResult();
|
||||
ASSERT_EQ(result->get_title(), "Wikibooks");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "../include/manager.h"
|
||||
#include "../include/server.h"
|
||||
#include "../include/name_mapper.h"
|
||||
|
||||
#include "../include/tools.h"
|
||||
|
||||
using TestContextImpl = std::vector<std::pair<std::string, std::string> >;
|
||||
struct TestContext : TestContextImpl {
|
||||
@@ -81,8 +81,8 @@ private: // data
|
||||
ZimFileServer::ZimFileServer(int serverPort, std::string libraryFilePath)
|
||||
: manager(&this->library)
|
||||
{
|
||||
if ( isRelativePath(libraryFilePath) )
|
||||
libraryFilePath = computeAbsolutePath(getCurrentDirectory(), libraryFilePath);
|
||||
if ( kiwix::isRelativePath(libraryFilePath) )
|
||||
libraryFilePath = kiwix::computeAbsolutePath(kiwix::getCurrentDirectory(), libraryFilePath);
|
||||
manager.readFile(libraryFilePath, true, true);
|
||||
|
||||
run(serverPort);
|
||||
|
||||
Reference in New Issue
Block a user