Compare commits

..

1 Commits

Author SHA1 Message Date
Veloman Yunkan
ebf0fe8b8f More predictable Downloader::startDownload()
Before this change `Downloader::startDownload()` might avoid starting a
new download when a download with the specified URI was already present
in its cache. This might be confusing for the following reasons:

1. uri is not the only parameter of `Downloader::startDownload()` - a
   target download directory may also be specified through the second
   `options` parameter. Thus calling `Downloader::startDownload()` twice
   with the same URI but different download directories would not save
   files into the second directory.

2. Files of a completed download may be removed, whereupon downloading the same
   files again won't be a no-op. However in such a situation
   `Downloader` refuses to actually repeat a previous download.
2024-02-13 18:42:15 +04:00
34 changed files with 89 additions and 1273 deletions

View File

@@ -29,8 +29,8 @@ jobs:
run: |
brew update
brew unlink python3
# upgrade from python@3.12 to python@3.12.2 fails to overwrite those
rm -f /usr/local/bin/2to3 /usr/local/bin/2to3-3.12 /usr/local/bin/idle3 /usr/local/bin/idle3.12 /usr/local/bin/pydoc3 /usr/local/bin/pydoc3.12 /usr/local/bin/python3 /usr/local/bin/python3-config /usr/local/bin/python3.12 /usr/local/bin/python3.12-config
# upgrade from python@3.11.2_1 to python@3.11.3 fails to overwrite those
rm -f /usr/local/bin/2to3 /usr/local/bin/2to3-3.11 /usr/local/bin/idle3 /usr/local/bin/idle3.11 /usr/local/bin/pydoc3 /usr/local/bin/pydoc3.11 /usr/local/bin/python3 /usr/local/bin/python3-config /usr/local/bin/python3.11 /usr/local/bin/python3.11-config
brew install pkg-config ninja meson
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1

View File

@@ -1,14 +1,3 @@
libkiwix 13.1.0
===============
* Server:
- Properly translated error pages (@veloman-yunkan #1032)
- Properly translated search result page (@veloman-yunkan #1046)
- Default UI language is resolved in frontend (@veloman-yunkan #1044)
- Better support of older Web browsers by polyfilling replaceAll() (@veloman-yunkan #1054)
* New API to migrate bookmarks between books (@mgautierfr #1043)
* Fixed compilation on Haiku OS (@Begasus #1048)
libkiwix 13.0.0
===============
@@ -64,6 +53,8 @@ libkiwix 12.1.0
* Remove libkiwix android publisher from the repository (@kelson42 #884)
* Various fixes of meson and CI. (@mgautierfr @kelson42)
libkiwix 12.0.0
===============
@@ -103,6 +94,7 @@ libkiwix 12.0.0
* Fix documentation (@kelson42 #816)
* Udpate translation (#787 #839 #847)
libkiwix 11.0.0
===============

View File

@@ -29,33 +29,19 @@ class xml_node;
namespace kiwix
{
class Book;
/**
* A class to store information about a bookmark (an article in a book)
*/
class Bookmark
{
public:
/**
* Create an empty bookmark.
*
* Bookmark must be populated with `set*` methods
*/
Bookmark();
/**
* Create a bookmark given a Book, a path and a title.
*/
Bookmark(const Book& book, const std::string& path, const std::string& title);
~Bookmark();
void updateFromXml(const pugi::xml_node& node);
const std::string& getBookId() const { return m_bookId; }
const std::string& getBookTitle() const { return m_bookTitle; }
const std::string& getBookName() const { return m_bookName; }
const std::string& getBookFlavour() const { return m_bookFlavour; }
const std::string& getUrl() const { return m_url; }
const std::string& getTitle() const { return m_title; }
const std::string& getLanguage() const { return m_language; }
@@ -63,8 +49,6 @@ class Bookmark
void setBookId(const std::string& bookId) { m_bookId = bookId; }
void setBookTitle(const std::string& bookTitle) { m_bookTitle = bookTitle; }
void setBookName(const std::string& bookName) { m_bookName = bookName; }
void setBookFlavour(const std::string& bookFlavour) { m_bookFlavour = bookFlavour; }
void setUrl(const std::string& url) { m_url = url; }
void setTitle(const std::string& title) { m_title = title; }
void setLanguage(const std::string& language) { m_language = language; }
@@ -73,8 +57,6 @@ class Bookmark
protected:
std::string m_bookId;
std::string m_bookTitle;
std::string m_bookName;
std::string m_bookFlavour;
std::string m_url;
std::string m_title;
std::string m_language;

View File

@@ -55,22 +55,6 @@ enum supportedListMode {
NOVALID = 1 << 5
};
enum MigrationMode {
/** When migrating bookmarks, do not allow to migrate to an older book than the currently pointed one
* (or date stored in the bookmark if book is invalid)
*
* If no newer books are found, no upgrade is made.
*/
UPGRADE_ONLY = 0,
/** Try hard to do a migration. This mostly does:
* - Try to find a newer book.
* - If book is invalid: find a best book, potentially older.
* Older book will never be returned if current book is a valid one.
*/
ALLOW_DOWNGRADE = 1,
};
class Filter {
public: // types
using Tags = std::vector<std::string>;
@@ -87,7 +71,6 @@ class Filter {
std::string _query;
bool _queryIsPartial;
std::string _name;
std::string _flavour;
public: // functions
Filter();
@@ -147,7 +130,6 @@ class Filter {
Filter& maxSize(size_t size);
Filter& query(std::string query, bool partial=true);
Filter& name(std::string name);
Filter& flavour(std::string flavour);
Filter& clearLang();
Filter& clearCategory();
@@ -170,9 +152,6 @@ class Filter {
bool hasCreator() const;
const std::string& getCreator() const { return _creator; }
bool hasFlavour() const;
const std::string& getFlavour() const { return _flavour; }
const Tags& getAcceptTags() const { return _acceptTags; }
const Tags& getRejectTags() const { return _rejectTags; }
@@ -271,7 +250,7 @@ class Library: public std::enable_shared_from_this<Library>
void addBookmark(const Bookmark& bookmark);
/**
* Remove a bookmark
* Remove a bookmarkk
*
* @param zimId The zimId of the bookmark.
* @param url The url of the bookmark.
@@ -279,66 +258,6 @@ class Library: public std::enable_shared_from_this<Library>
*/
bool removeBookmark(const std::string& zimId, const std::string& url);
/**
* Migrate all invalid bookmarks.
*
* All invalid bookmarks (ie pointing to unknown books, no check is made on bookmark pointing to
* invalid articles of valid book) will be migrated (if possible) to a better book.
* "Better book", will be determined using method `getBestTargetBookId`.
*
* @return A tuple<int, int>: <The number of bookmarks updated>, <Number of invalid bookmarks before migration was performed>.
*/
std::tuple<int, int> migrateBookmarks(MigrationMode migrationMode = ALLOW_DOWNGRADE);
/**
* Migrate all bookmarks associated to a specific book.
*
* All bookmarks associated to `sourceBookId` book will be migrated to a better book.
* "Better book", will be determined using method `getBestTargetBookId`.
*
* @param sourceBookId the source bookId of the bookmarks to migrate.
* @param migrationMode how we will find the best book.
* @return The number of bookmarks updated.
*/
int migrateBookmarks(const std::string& sourceBookId, MigrationMode migrationMode = UPGRADE_ONLY);
/**
* Migrate bookmarks
*
* Migrate all bookmarks pointing to `source` to `destination`.
*
* @param sourceBookId the source bookId of the bookmarks to migrate.
* @param targetBookId the destination bookId to migrate the bookmarks to.
* @return The number of bookmarks updated.
*/
int migrateBookmarks(const std::string& sourceBookId, const std::string& targetBookId);
/**
* Get the best available bookId for a bookmark.
*
* Given a bookmark, return the best available bookId.
* "best available bookId" is determined using heuristitcs based on book name, flavour and date.
*
* @param bookmark The bookmark to search the bookId for.
* @param migrationMode The migration mode to use.
* @return A bookId. Potentially empty string if no suitable book found.
*/
std::string getBestTargetBookId(const Bookmark& bookmark, MigrationMode migrationMode) const;
/**
* Get the best bookId for a combination of book's name, flavour and date.
*
* Given a bookName (mandatory), try to find the best book.
* If preferedFlavour is given, will try to find a book with the same flavour. If not found, return a book with a different flavour.
* If minDate is given, return a book newer than minDate. If not found, return a empty bookId.
*
* @param bookName The name of the book
* @param preferedFlavour The prefered flavour.
* @param minDate the minimal book date acceptable. Must be a string in the format "YYYY-MM-DD".
* @return A bookId corresponding to the query, or empty string if not found.
*/
std::string getBestTargetBookId(const std::string& bookName, const std::string& preferedFlavour="", const std::string& minDate="") const;
// XXX: This is a non-thread-safe operation
const Book& getBookById(const std::string& id) const;
// XXX: This is a non-thread-safe operation
@@ -484,13 +403,12 @@ private: // functions
AttributeCounts getBookAttributeCounts(BookStrPropMemFn p) const;
std::vector<std::string> getBookPropValueSet(BookStrPropMemFn p) const;
BookIdCollection filterViaBookDB(const Filter& filter) const;
std::string getBestFromBookCollection(BookIdCollection books, const Bookmark& bookmark, MigrationMode migrationMode) const;
unsigned int getBookCount_not_protected(const bool localBooks, const bool remoteBooks) const;
void updateBookDB(const Book& book);
void dropCache(const std::string& bookId);
private: //data
mutable std::recursive_mutex m_mutex;
mutable std::mutex m_mutex;
Library::Revision m_revision;
std::map<std::string, Entry> m_books;
using ArchiveCache = ConcurrentCache<std::string, std::shared_ptr<zim::Archive>>;

View File

@@ -1,5 +1,5 @@
project('libkiwix', 'cpp',
version : '13.1.0',
version : '13.0.0',
license : 'GPLv3+',
default_options : ['c_std=c11', 'cpp_std=c++17', 'werror=true'])

View File

@@ -18,7 +18,6 @@
*/
#include "bookmark.h"
#include "book.h"
#include <pugixml.hpp>
@@ -29,17 +28,6 @@ Bookmark::Bookmark()
{
}
Bookmark::Bookmark(const Book& book, const std::string& path, const std::string& title):
m_bookId(book.getId()),
m_bookTitle(book.getTitle()),
m_bookName(book.getName()),
m_bookFlavour(book.getFlavour()),
m_url(path),
m_title(title),
m_language(book.getCommaSeparatedLanguages()),
m_date(book.getDate())
{}
/* Destructor */
Bookmark::~Bookmark()
{
@@ -50,8 +38,6 @@ void Bookmark::updateFromXml(const pugi::xml_node& node)
auto bookNode = node.child("book");
m_bookId = bookNode.child("id").child_value();
m_bookTitle = bookNode.child("title").child_value();
m_bookName = bookNode.child("name").child_value();
m_bookFlavour = bookNode.child("flavour").child_value();
m_language = bookNode.child("language").child_value();
m_date = bookNode.child("date").child_value();
m_title = node.child("title").child_value();

View File

@@ -169,12 +169,6 @@ std::vector<std::string> Downloader::getDownloadIds() const {
std::shared_ptr<Download> Downloader::startDownload(const std::string& uri, const std::vector<std::pair<std::string, std::string>>& options)
{
std::unique_lock<std::mutex> lock(m_lock);
for (auto& p: m_knownDownloads) {
auto& d = p.second;
auto& uris = d->getUris();
if (std::find(uris.begin(), uris.end(), uri) != uris.end())
return d;
}
std::vector<std::string> uris = {uri};
auto gid = mp_aria->addUri(uris, options);
m_knownDownloads[gid] = std::make_shared<Download>(mp_aria, gid);

View File

@@ -110,7 +110,7 @@ Library::~Library() = default;
bool Library::addBook(const Book& book)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
++m_revision;
/* Try to find it */
updateBookDB(book);
@@ -141,13 +141,13 @@ bool Library::addBook(const Book& book)
void Library::addBookmark(const Bookmark& bookmark)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
m_bookmarks.push_back(bookmark);
}
bool Library::removeBookmark(const std::string& zimId, const std::string& url)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
for(auto it=m_bookmarks.begin(); it!=m_bookmarks.end(); it++) {
if (it->getBookId() == zimId && it->getUrl() == url) {
m_bookmarks.erase(it);
@@ -157,159 +157,6 @@ bool Library::removeBookmark(const std::string& zimId, const std::string& url)
return false;
}
std::tuple<int, int> Library::migrateBookmarks(MigrationMode migrationMode) {
std::set<std::string> sourceBooks;
int invalidBookmarks = 0;
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
for(auto& bookmark:m_bookmarks) {
if (m_books.find(bookmark.getBookId()) == m_books.end()) {
invalidBookmarks += 1;
sourceBooks.insert(bookmark.getBookId());
}
}
}
int changed = 0;
for(auto& sourceBook:sourceBooks) {
changed += migrateBookmarks(sourceBook, migrationMode);
}
return std::make_tuple(changed, invalidBookmarks);
}
std::string Library::getBestFromBookCollection(BookIdCollection books, const Bookmark& bookmark, MigrationMode migrationMode) const {
// This function try to get the best book for a bookmark from a book collection.
// It assumes that all books in the collection are "acceptable".
// (this definiton is not clear but for now it is book's name is equal to bookmark's bookName)
//
// The algorithm first sort the colletion by "flavour equality" and date.
// "flavour equality" is if book's flavour is same that bookmark's flavour (let's say "flavourA" here)
// So we have the sorted collection:
// - flavourA, date 5
// - flavourA, date 4
// - flavourB, date 6
// - flavourC, date 5
// - flavourB, date 3
//
// Then, depending of migrationMode:
// - If ALLOW_DOWNGRADE => take the first one
// - If UPGRADE_ONLY => loop on books until we find a book newer than bookmark.
// So if bookmark date is 5 => flavourB, date 6
// if bookmark date is 4 => flavourA, date 5
// if bookmark date is 7 => No book
if (books.empty()) {
return "";
}
sort(books, DATE, false);
stable_sort(books.begin(), books.end(), [&](const std::string& bookId1, const std::string& bookId2) {
const auto& book1 = getBookById(bookId1);
const auto& book2 = getBookById(bookId2);
bool same_flavour1 = book1.getFlavour() == bookmark.getBookFlavour();
bool same_flavour2 = book2.getFlavour() == bookmark.getBookFlavour();
// return True if bookId1 is before bookId2, ie if same_flavour1 and not same_flavour2
return same_flavour1 > same_flavour2;
});
if (migrationMode == ALLOW_DOWNGRADE) {
return books[0];
} else {
for (const auto& bookId: books) {
const auto& book = getBookById(bookId);
if (book.getDate() >= bookmark.getDate()) {
return bookId;
}
}
}
return "";
}
std::string remove_quote(std::string input) {
std::replace(input.begin(), input.end(), '"', ' ');
return input;
}
std::string Library::getBestTargetBookId(const std::string& bookName, const std::string& preferedFlavour, const std::string& minDate) const {
// Let's reuse our algorithm based on bookmark.
MigrationMode migrationMode = UPGRADE_ONLY;
auto bookmark = Bookmark();
bookmark.setBookName(bookName);
bookmark.setBookFlavour(preferedFlavour);
if (minDate.empty()) {
migrationMode = ALLOW_DOWNGRADE;
} else {
bookmark.setDate(minDate);
}
return getBestTargetBookId(bookmark, migrationMode);
}
std::string Library::getBestTargetBookId(const Bookmark& bookmark, MigrationMode migrationMode) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
// Search for a existing book with the same name
auto book_filter = Filter();
if (!bookmark.getBookName().empty()) {
book_filter.name(bookmark.getBookName());
} else {
// We don't have a name stored (older bookmarks)
// Fallback on title (All bookmarks should have one, but let's be safe against wrongly filled bookmark)
if (bookmark.getBookTitle().empty()) {
// No bookName nor bookTitle, no way to find target book.
return "";
}
book_filter.query("title:\"" + remove_quote(bookmark.getBookTitle()) + "\"");
}
auto targetBooks = filter(book_filter);
auto bestBook = getBestFromBookCollection(targetBooks, bookmark, migrationMode);
if (bestBook.empty()) {
try {
getBookById(bookmark.getBookId());
return bookmark.getBookId();
} catch (std::out_of_range&) {}
}
return bestBook;
}
int Library::migrateBookmarks(const std::string& sourceBookId, MigrationMode migrationMode) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
Bookmark firstBookmarkToChange;
for(auto& bookmark:m_bookmarks) {
if (bookmark.getBookId() == sourceBookId) {
firstBookmarkToChange = bookmark;
break;
}
}
if (firstBookmarkToChange.getBookId().empty()) {
return 0;
}
std::string betterBook = getBestTargetBookId(firstBookmarkToChange, migrationMode);
if (betterBook.empty()) {
return 0;
}
return migrateBookmarks(sourceBookId, betterBook);
}
int Library::migrateBookmarks(const std::string& sourceBookId, const std::string& targetBookId) {
if (sourceBookId == targetBookId) {
return 0;
}
int changed = 0;
for (auto& bookmark:m_bookmarks) {
if (bookmark.getBookId() == sourceBookId) {
bookmark.setBookId(targetBookId);
changed +=1;
}
}
return changed;
}
void Library::dropCache(const std::string& id)
{
@@ -319,7 +166,7 @@ void Library::dropCache(const std::string& id)
bool Library::removeBookById(const std::string& id)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
m_bookDB->delete_document("Q" + id);
dropCache(id);
// We do not change the cache size here
@@ -337,7 +184,7 @@ bool Library::removeBookById(const std::string& id)
Library::Revision Library::getRevision() const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
return m_revision;
}
@@ -345,7 +192,7 @@ uint32_t Library::removeBooksNotUpdatedSince(Revision libraryRevision)
{
BookIdCollection booksToRemove;
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
for ( const auto& entry : m_books) {
if ( entry.second.lastUpdatedRevision <= libraryRevision ) {
booksToRemove.push_back(entry.first);
@@ -370,7 +217,7 @@ const Book& Library::getBookById(const std::string& id) const
Book Library::getBookByIdThreadSafe(const std::string& id) const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
return getBookById(id);
}
@@ -428,7 +275,7 @@ std::shared_ptr<ZimSearcher> Library::getSearcherByIds(const BookIdSet& ids)
unsigned int Library::getBookCount(const bool localBooks,
const bool remoteBooks) const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
return getBookCount_not_protected(localBooks, remoteBooks);
}
@@ -441,7 +288,7 @@ bool Library::writeToFile(const std::string& path) const
dumper.setBaseDir(baseDir);
std::string xml;
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
xml = dumper.dumpLibXMLContent(allBookIds);
};
return writeTextFile(path, xml);
@@ -457,7 +304,7 @@ bool Library::writeBookmarksToFile(const std::string& path) const
Library::AttributeCounts Library::getBookAttributeCounts(BookStrPropMemFn p) const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
AttributeCounts propValueCounts;
for (const auto& pair: m_books) {
@@ -489,7 +336,7 @@ std::vector<std::string> Library::getBooksLanguages() const
Library::AttributeCounts Library::getBooksLanguagesWithCounts() const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
AttributeCounts langsWithCounts;
for (const auto& pair: m_books) {
@@ -505,7 +352,7 @@ Library::AttributeCounts Library::getBooksLanguagesWithCounts() const
std::vector<std::string> Library::getBooksCategories() const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
std::set<std::string> categories;
for (const auto& pair: m_books) {
@@ -536,7 +383,7 @@ const std::vector<kiwix::Bookmark> Library::getBookmarks(bool onlyValidBookmarks
}
std::vector<kiwix::Bookmark> validBookmarks;
auto booksId = getBooksIds();
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
for(auto& bookmark:m_bookmarks) {
if (std::find(booksId.begin(), booksId.end(), bookmark.getBookId()) != booksId.end()) {
validBookmarks.push_back(bookmark);
@@ -547,7 +394,7 @@ const std::vector<kiwix::Bookmark> Library::getBookmarks(bool onlyValidBookmarks
Library::BookIdCollection Library::getBooksIds() const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
BookIdCollection bookIds;
for (auto& pair: m_books) {
@@ -590,7 +437,6 @@ void Library::updateBookDB(const Book& book)
indexer.index_text(normalizeText(book.getCreator()), 1, "A");
indexer.index_text(normalizeText(book.getPublisher()), 1, "XP");
doc.add_term("XN"+normalizeText(book.getName()));
indexer.index_text(normalizeText(book.getFlavour()), 1, "XF");
indexer.index_text(normalizeText(book.getCategory()), 1, "XC");
for ( const auto& tag : split(normalizeText(book.getTags()), ";") ) {
@@ -631,7 +477,6 @@ Xapian::Query buildXapianQueryFromFilterQuery(const Filter& filter)
queryParser.add_prefix("title", "S");
queryParser.add_prefix("description", "XD");
queryParser.add_prefix("name", "XN");
queryParser.add_prefix("flavour", "XF");
queryParser.add_prefix("category", "XC");
queryParser.add_prefix("lang", "L");
queryParser.add_prefix("publisher", "XP");
@@ -658,11 +503,6 @@ Xapian::Query nameQuery(const std::string& name)
return Xapian::Query("XN" + normalizeText(name));
}
Xapian::Query flavourQuery(const std::string& name)
{
return Xapian::Query("XF" + normalizeText(name));
}
Xapian::Query multipleParamQuery(const std::string& commaSeparatedList, const std::string& prefix)
{
Xapian::Query q;
@@ -730,9 +570,6 @@ Xapian::Query buildXapianQuery(const Filter& filter)
if ( filter.hasName() ) {
q = Xapian::Query(Xapian::Query::OP_AND, q, nameQuery(filter.getName()));
}
if ( filter.hasFlavour() ) {
q = Xapian::Query(Xapian::Query::OP_AND, q, flavourQuery(filter.getFlavour()));
}
if ( filter.hasCategory() ) {
q = Xapian::Query(Xapian::Query::OP_AND, q, categoryQuery(filter.getCategory()));
}
@@ -763,7 +600,7 @@ Library::BookIdCollection Library::filterViaBookDB(const Filter& filter) const
BookIdCollection bookIds;
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
Xapian::Enquire enquire(*m_bookDB);
enquire.set_query(query);
const auto results = enquire.get_mset(0, m_books.size());
@@ -778,7 +615,7 @@ Library::BookIdCollection Library::filter(const Filter& filter) const
{
BookIdCollection result;
const auto preliminaryResult = filterViaBookDB(filter);
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
for(auto id : preliminaryResult) {
if(filter.accept(m_books.at(id))) {
result.push_back(id);
@@ -852,7 +689,7 @@ void Library::sort(BookIdCollection& bookIds, supportedListSortBy sort, bool asc
// NOTE: for the entire duration of the sort. Will need to obtain (under a
// NOTE: lock) the required atributes from the books once, and then the
// NOTE: sorting will run on a copy of data without locking.
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
switch(sort) {
case TITLE:
std::sort(bookIds.begin(), bookIds.end(), Comparator<TITLE>(this, ascending));
@@ -898,7 +735,6 @@ enum filterTypes {
QUERY = FLAG(12),
NAME = FLAG(13),
CATEGORY = FLAG(14),
FLAVOUR = FLAG(15),
};
Filter& Filter::local(bool accept)
@@ -1000,13 +836,6 @@ Filter& Filter::name(std::string name)
activeFilters |= NAME;
return *this;
}
Filter& Filter::flavour(std::string flavour)
{
_flavour = flavour;
activeFilters |= FLAVOUR;
return *this;
}
Filter& Filter::clearLang()
{
@@ -1052,12 +881,6 @@ bool Filter::hasCreator() const
return ACTIVE(_CREATOR);
}
bool Filter::hasFlavour() const
{
return ACTIVE(FLAVOUR);
}
bool Filter::accept(const Book& book) const
{
auto local = !book.getPath().empty();

View File

@@ -97,15 +97,11 @@ void LibXMLDumper::handleBookmark(Bookmark bookmark, pugi::xml_node root_node) {
auto book = library->getBookByIdThreadSafe(bookmark.getBookId());
ADD_TEXT_ENTRY(book_node, "id", book.getId());
ADD_TEXT_ENTRY(book_node, "title", book.getTitle());
ADD_TEXT_ENTRY(book_node, "name", book.getName());
ADD_TEXT_ENTRY(book_node, "flavour", book.getFlavour());
ADD_TEXT_ENTRY(book_node, "language", book.getCommaSeparatedLanguages());
ADD_TEXT_ENTRY(book_node, "date", book.getDate());
} catch (...) {
ADD_TEXT_ENTRY(book_node, "id", bookmark.getBookId());
ADD_TEXT_ENTRY(book_node, "title", bookmark.getBookTitle());
ADD_TEXT_ENTRY(book_node, "name", bookmark.getBookName());
ADD_TEXT_ENTRY(book_node, "flavour", bookmark.getBookFlavour());
ADD_TEXT_ENTRY(book_node, "language", bookmark.getLanguage());
ADD_TEXT_ENTRY(book_node, "date", bookmark.getDate());
}
@@ -139,7 +135,7 @@ std::string LibXMLDumper::dumpLibXMLBookmark()
pugi::xml_node bookmarksNode = doc.append_child("bookmarks");
if (library) {
for (auto& bookmark: library->getBookmarks(false)) {
for (auto& bookmark: library->getBookmarks()) {
handleBookmark(bookmark, bookmarksNode);
}
}

View File

@@ -1,8 +1,6 @@
skin/i18n/ar.json
skin/i18n/bn.json
skin/i18n/br.json
skin/i18n/cs.json
skin/i18n/dag.json
skin/i18n/de.json
skin/i18n/dga.json
skin/i18n/el.json
@@ -10,12 +8,10 @@ skin/i18n/en.json
skin/i18n/es.json
skin/i18n/fi.json
skin/i18n/fr.json
skin/i18n/ha.json
skin/i18n/he.json
skin/i18n/hi.json
skin/i18n/hy.json
skin/i18n/ia.json
skin/i18n/ig.json
skin/i18n/it.json
skin/i18n/ja.json
skin/i18n/ko.json

View File

@@ -17,7 +17,6 @@ skin/fonts/Poppins.ttf
skin/fonts/Roboto.ttf
skin/search_results.css
skin/blank.html
skin/polyfills.js
skin/viewer.js
skin/i18n.js
skin/languages.js

View File

@@ -8,8 +8,6 @@
"404-page-heading": "পাওয়া যায়নি",
"500-page-title": "অভ্যন্তরীণ সার্ভার ত্রুটি",
"500-page-heading": "অভ্যন্তরীণ সার্ভার ত্রুটি",
"search-result-book-info": "{{BOOK_TITLE}} থেকে",
"word-count": "{{COUNT}}টি শব্দ",
"library-button-text": "স্বাগত পাতায় চলুন",
"home-button-text": "'{{BOOK_TITLE}}'-এর প্রধান পাতায় চলুন",
"searchbox-tooltip": "'{{BOOK_TITLE}}' অনুসন্ধান করুন",

View File

@@ -1,42 +0,0 @@
{
"@metadata": {
"authors": [
"Adriendelucca",
"Y-M D"
]
},
"name": "brezhoneg",
"suggest-full-text-search": "E lec'h emañ \"{{{SEARCH_TERMS}}}\"...",
"no-such-book": "Neus ket eus al levr-mañ: {{BOOK_NAME}}",
"no-book-found": "Neus levr ebet a glot gant an dezverkoù-se",
"url-not-found": "Neo ket bet kavet an URL \"{{url}}\" goulennet war ar servijer-mañ.",
"random-article-failure": "Chaous! Nhon eus ket gellet dibab ur pennad dre ziouer evidoch :(",
"400-page-title": "Reked amwiriek",
"400-page-heading": "Reked amwiriek",
"404-page-heading": "N'eo ket bet kavet",
"500-page-title": "Fazi diabarzh ar servijer",
"500-page-heading": "Fazi diabarzh ar servijer",
"search-results-page-title": "Klask: {{SEARCH_PATTERN}}",
"search-results-page-header": "Disochoù <b>{{START}}-{{END}}</b> diwar <b>{{COUNT}}</b> evit <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Disoch ebet kavet evit <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "diouzh {{BOOK_TITLE}}",
"word-count": "{{COUNT}} a cherioù",
"library-button-text": "Mont dar bajenn degemer",
"home-button-text": "Mont da bajenn degemer \"{{BOOK_TITLE}}\"",
"random-page-button-text": "Mont dur bajenn dre zegouezh",
"searchbox-tooltip": "Klask '{{BOOK_TITLE}}'",
"powered-by-kiwix-html": "Lusket gant&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Klask",
"book-filtering-all-categories": "An holl rummadoù",
"book-filtering-all-languages": "An holl yezhoù",
"count-of-matching-books": "{{COUNT}} levr",
"download": "Pellgargañ",
"direct-download-link-text": "Eeun",
"filter-by-tag": "Silañ gant an dikedenn \"{{TAG}}\"",
"stop-filtering-by-tag": "Paouez da silañ gant an dikedenn \"{{TAG}}\"",
"welcome-to-kiwix-server": "Degemer mat er servijer Kiwix",
"download-links-heading": "Liammoù pellgargañ evit <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Pellgargañ al levr",
"preview-book": "Rakwelet",
"unknown-error": "Fazi dianav"
}

View File

@@ -1,55 +0,0 @@
{
"@metadata": {
"authors": [
"Kalakpagh",
"Ruky Wunpini"
]
},
"name": "Silimiinsili",
"suggest-full-text-search": "Gbubi la '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Lala buku kani:{{BOOK_NAME}}",
"too-many-books": "Buku nima pam ka bɛ daa suhi ({{NB_BOOKS}}) din ni ka tariga nyɛ {{LIMIT}}",
"no-book-found": "Buku kani lu zahim a ni piigi yaɣa shɛli",
"url-not-found": "URL \"{{url}}\" shɛli bɛ ni daa suhi daa kani n-ti tum tumda ŋɔ.",
"suggest-search": "Niŋmi lahabali pali vihigu zaŋ n-ti <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Oops! Zaɣisiya ni di gahim piigi lahabali :(",
"invalid-raw-data-type": "{{DATATYPE}} nyɛla din suhibu bi niŋ viɛnyɛla zaŋ n-ti lahabali kahili.",
"invalid-request": "URL \"{{{url}}}\" shɛli bɛ ni daa suhi ŋɔ nyɛla din bi suhi viɛnyɛla.",
"no-value-for-arg": "Dariza shɛli bi ti zaŋ n-ti nangban'kpeeni {{ARGUMENT}}",
"no-query": "Yɛlshɛli bi yiina",
"raw-entry-not-found": "Ku tooi nya {{DATATYPE}} kpɛbu {{ENTRY}}",
"400-page-title": "Suhigu din bi niŋ viɛnyɛla",
"400-page-heading": "Suhigu din bi niŋ viɛnyɛla",
"404-page-title": "Lahabali kani",
"404-page-heading": "Kani",
"500-page-title": "Puuni tum tumda chiriŋ",
"500-page-heading": "Puuni tum tumda chiriŋ",
"500-page-text": "Puuni tum tumda chiriŋ niŋya. Ti niŋ yolitem zaŋ jɛndi li :/",
"fulltext-search-unavailable": "Lahabali pali vihigu kani",
"search-results-page-title": "Vihima:{{SEARCH_PATTERN}}",
"search-results-page-header": "Chaɣili nima <b>{{START}}-{{END}}</b> of <b>{{COUNT}}</b> for <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Chaɣili daa kani zaŋ n-ti\n <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "yina {{BOOK_TITLE}}",
"word-count": "{{COUNT}} bachi nima",
"library-button-text": "Cham solɔɣu",
"home-button-text": "Cham yaɣili maŋmaŋ zaŋ n-ti\n'{{BOOK_TITLE}}'",
"random-page-button-text": "Cham gahim piigi yaɣili",
"searchbox-tooltip": "Vihima '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Buku nima ayi bee gari balli koŋkoba nyɛ din yɛn be vihigu ŋɔ ni ka di ni tooi chɛ ka di laasabu wali.",
"welcome-page-overzealous-filter": "Labisibu kani. A ni yu ni a\n<a href=\"{{URL}}\">reset filter</a>?",
"powered-by-kiwix-html": "Din niŋ li nyɛ &nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Vihima",
"book-filtering-all-categories": "Pubu zaa",
"book-filtering-all-languages": "Bala zaa",
"count-of-matching-books": "{{COUNT}} Buku(nima)",
"download": "Yihibu",
"direct-download-link-text": "Tibi",
"direct-download-alt-text": "Tibi deebu",
"hash-download-link-text": "Sha256 hash",
"hash-download-alt-text": "Deebu daliŋ",
"welcome-to-kiwix-server": "Maraba Kiwix tum tumda",
"download-links-heading": "Deemi soli zaŋ n-ti <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Yaa mi buku",
"preview-book": "Labi lihi",
"unknown-error": "Chiriŋ din bi tooi baŋ"
}

View File

@@ -3,7 +3,6 @@
"authors": [
"IMayBeABitShy",
"Lucas Werkmeister",
"Rofiatmustapha12",
"ThisCarthing"
]
},
@@ -16,7 +15,6 @@
"suggest-search": "Führe eine Volltextsuche nach <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a> durch",
"random-article-failure": "Hoppla! Konnte keinen zufälligen Artikel auswählen :(",
"invalid-raw-data-type": "{{DATATYPE}} ist keine gültige Anfrage für unverarbeiteten Inhalt",
"invalid-request": "Die angeforderte URL „{{{url}}}“ ist keine gültige Anfrage.",
"no-value-for-arg": "Kein Wert für den Parameter {{ARGUMENT}} gegeben",
"no-query": "Keine Suchanfrage gegeben.",
"raw-entry-not-found": "Eintrag {{ENTRY}} des Typs {{DATATYPE}} konnte nicht gefunden werden.",
@@ -26,14 +24,8 @@
"404-page-heading": "Nicht gefunden",
"500-page-title": "Interner Server-Fehler",
"500-page-heading": "Interner Server-Fehler",
"500-page-text": "Es ist ein interner Serverfehler aufgetreten. Das tut uns leid :/",
"fulltext-search-unavailable": "Die Volltestsuche steht nicht zur Verfügung.",
"no-search-results": "Die Volltextsuche ist für diesen Inhalt nicht verfügbar.",
"search-results-page-title": "Suche: {{SEARCH_PATTERN}}",
"search-results-page-header": "Ergebnisse <b>{{START}}-{{END}}</b> von <b>{{COUNT}}</b> für <b>„{{{SEARCH_PATTERN}}}“</b>",
"empty-search-results-page-header": "Für <b>„{{{SEARCH_PATTERN}}}“</b> wurden keine Ergebnisse gefunden.",
"search-result-book-info": "von {{BOOK_TITLE}}",
"word-count": "{{COUNT}} Wörter",
"library-button-text": "Zur Willkommensseite gehen",
"home-button-text": "Zur Hauptseite von '{{BOOK_TITLE}}' gehen",
"random-page-button-text": "Zu einer zufällig ausgewählten Seite gehen",
@@ -61,6 +53,5 @@
"welcome-to-kiwix-server": "Wilkommen beim Kiwix Server",
"download-links-heading": "Download Links für <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Buch herunterladen",
"preview-book": "Vorschau",
"unknown-error": "Unbekannter Fehler"
"preview-book": "Vorschau"
}

View File

@@ -1,12 +1,9 @@
{
"@metadata": {
"authors": [
"Adriendelucca",
"Gomoko",
"Melimeli",
"Stephane",
"Thibaut120094",
"Urhixidur",
"Verdy p",
"Vikoula5",
"Wladek92"
@@ -34,11 +31,6 @@
"500-page-text": "Une erreur de serveur interne s'est produite. Nous en sommes désolés :/",
"fulltext-search-unavailable": "Recherche en texte intégral non disponible",
"no-search-results": "Le moteur de recherche en texte intégral nest pas disponible pour ce contenu.",
"search-results-page-title": "Rechercher : {{SEARCH_PATTERN}}",
"search-results-page-header": "Résultats <b>{{START}}-{{END}}</b> sur<b> {{COUNT}}</b> pour <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Aucun résultat na été trouvé pour <b>« {{{SEARCH_PATTERN}}} »</b>",
"search-result-book-info": "à partir de {{BOOK_TITLE}}",
"word-count": "{{COUNT}} mots",
"library-button-text": "Aller à la page de bienvenue",
"home-button-text": "Aller à la page principale de « {{BOOK_TITLE}} »",
"random-page-button-text": "Aller à une page sélectionnée aléatoirement",

View File

@@ -1,67 +0,0 @@
{
"@metadata": {
"authors": [
"Abelidokoo",
"El-hussain14",
"Rofiatmustapha12",
"Smshika",
"Yusuf Sa'adu"
]
},
"name": "Turanci",
"suggest-full-text-search": "dauke da ''{{{SEARCH_TERMS}}}''...",
"no-such-book": "Babu irin wannan littafin: {{BOOK_NAME}}",
"too-many-books": "An nemi littattafai da yawa ({{NB_BOOKS}}) inda iyaka shine {{LIMIT}}",
"no-book-found": "Babu wani littafi da ya dace da ma'aunin zaɓi",
"url-not-found": "Ba a sami URL ɗin da ake nema \"{{url}}\" akan wannan sabar ba.",
"suggest-search": "Yi cikakken bincike na rubutu don <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Kash! An kasa ɗaukar labarin bazuwar :(",
"invalid-raw-data-type": "{{DATATYPE}} ba ingantaccen buƙatun ɗanyen abun ciki bane.",
"invalid-request": "URL ɗin da ake nema \"{{{url}}}\" ba buƙatu mai inganci bane.",
"no-value-for-arg": "Babu ƙima da aka bayar don hujja {{ARGUMENT}}",
"no-query": "Ba a bayar da tambaya ba.",
"raw-entry-not-found": "Ba a iya samun shigarwar {{DATATYPE}} {{ENTRY}}",
"400-page-title": "nema mara inganci",
"400-page-heading": "nema mara inganci",
"404-page-title": "Ba a samo abun ciki ba",
"404-page-heading": "Ba a Samu ba",
"500-page-title": "Kuskuren na Cikin Saba",
"500-page-heading": "Kuskuren na Cikin Saba",
"500-page-text": "An sami kuskuren uwar garken ciki. Munyi nadama akan hakan :/",
"fulltext-search-unavailable": "Babu binciken cikakken rubutu",
"no-search-results": "Babu injin binciken cikakken rubutu don wannan abun ciki.",
"search-results-page-title": "Bincika: {{SEARCH_PATTERN}}",
"search-results-page-header": "Sakamako <b>{{START}}-{{END}}</b> na <b>{{COUNT}}</b> na <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Ba a sami sakamakon <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "daga {{BOOK_TITLE}}",
"word-count": "{{COUNT}} kalmomi",
"library-button-text": "Je zuwa shafin maraba",
"home-button-text": "Jeka babban shafin '{{BOOK_TITLE}}'",
"random-page-button-text": "Je zuwa shafin da aka zaɓa ba da gangan ba",
"searchbox-tooltip": "Bincika '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Littattafai biyu ko fiye a cikin harsuna daban-daban za su shiga cikin bincike, wanda zai iya haifar da sakamako mai ruɗani.",
"welcome-page-overzealous-filter": "Babu sakamako. Kuna so a <a href=\"{{URL}}\">sake saita tace</a>?",
"powered-by-kiwix-html": "<a href=\"https://kiwix.org\">Kiwix</a> ne ke ƙarfafa shi",
"search": "Nema",
"book-filtering-all-categories": "Dukkanin nau'o'in",
"book-filtering-all-languages": "Duka harsuna",
"count-of-matching-books": "{{COUNT}} littafi(s)",
"download": "Sauke",
"direct-download-link-text": "Kai tsaye",
"direct-download-alt-text": "saukewa kai tsaye",
"hash-download-link-text": "Sha256 hash",
"hash-download-alt-text": "sauke hash",
"magnet-link-text": "Magnet link",
"magnet-alt-text": "Magnet ɗin saukewa",
"torrent-download-link-text": "Torrent fayil",
"torrent-download-alt-text": "download torrent",
"library-opds-feed-all-entries": "Ciyarwar OPDS Library - Duk shigarwar",
"filter-by-tag": "Tace da alamar \"{{TAG}}\"",
"stop-filtering-by-tag": "Dakatar da tacewa ta hanyar \"{{TAG}}\"",
"library-opds-feed-parameterised": " OPDS ciyar wa OPDS- entries matching {{#LANG}}\nLanguage: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategory: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nQuery: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Barka da zowa manhajar Kiwix",
"download-links-heading": "Bring out ways through which people can join hands together .{{BOOK_TITLE}}",
"download-links-title": "Sauke littafin",
"preview-book": "Dubawa",
"unknown-error": "Kuskuren da ba a sani ba"
}

View File

@@ -27,11 +27,6 @@
"500-page-text": "אירעה שגיאת שרת פנימית. אנחנו מצטערים על זה :/",
"fulltext-search-unavailable": "חיפוש בטקסט מלא אינו זמין",
"no-search-results": "מנוע החיפוש בטקסט מלא אינו זמין עבור התוכן הזה.",
"search-results-page-title": "חיפוש: {{SEARCH_PATTERN}}",
"search-results-page-header": "תוצאות <b>{{START}} עד {{END}}</b> מתוך <b>{{COUNT}}</b> עבור <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "לא נמצאו תוצאות עבור <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "מתוך {{BOOK_TITLE}}",
"word-count": "{{COUNT}} מילים",
"library-button-text": "מעבר לדף הבית \"ברוך בואך\"",
"home-button-text": "מעבר לדף הראשי של \"{{BOOK_TITLE}}\"",
"random-page-button-text": "מעבר לדף שנבחר אקראית",

View File

@@ -1,65 +0,0 @@
{
"@metadata": {
"authors": [
"Accuratecy051",
"Ngostary2k",
"Oby Ezeilo"
]
},
"name": "Bekee",
"suggest-full-text-search": "nwere {{{SEARCH_TERMS}}}'",
"no-such-book": "Enweghị akwụkwọ dị otú a: {{BOOK_NAME}}",
"too-many-books": "Arịrịọ ọtụtụ akwụkwọ ({{NB_BOOKS}}) ebe oke bụ {{LIMIT}}",
"no-book-found": "Ọ nweghị akwụkwọ dabara na nhọpụta nhọrọ",
"url-not-found": "Ahụghị URL a rịọrọ \"{{url}}\" na nkesa a.",
"suggest-search": "Mee ọchụchọ ederede zuru oke maka <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ee! Ịhọrọ akụkọ enweghị usoro :(",
"invalid-raw-data-type": "{{DATATYPE}} abụghị arịrịọ ziri ezi maka ọdịnaya raw.",
"invalid-request": "Arịrịọ gbasara URL \"{{{url}}}\" e zighi ezi.",
"no-value-for-arg": "Ọ nweghị uru enyere maka arụmụka {{ARGUMENT}}",
"no-query": "Ọnweghị ajụjụ enyere.",
"raw-entry-not-found": "Enweghị ike ịchọta ntinye {{DATATYPE}} {{ENTRY}}",
"400-page-title": "Arịrịọ na-ezighi ezi",
"400-page-heading": "Arịrịọ na-ezighi ezi",
"404-page-title": "Ahụghị ọdịnaya",
"404-page-heading": "Ahụghị",
"500-page-title": "Mperi Sava Ime",
"500-page-heading": "Mperi Sava Ime",
"500-page-text": "Enwere mperi ihe nkesa dị n'ime. Ọ dị anyị nwute na nke ahụ :/",
"fulltext-search-unavailable": "Ọchịchọ ederede zuru ezu adịghị",
"no-search-results": "Igwe nchọta ederede zuru oke adịghị maka ọdịnaya a.",
"search-results-page-title": "Chọọ: {{SEARCH_PATTERN}}",
"search-results-page-header": "Rịzọlt ga <b>{{START}}-{{END}}</b> nke <b>{{COUNT}}</b> maka <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Ọnweghị rịzọlt ahụrụ maka <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "sitere na {{BOOK_TITLE}}",
"word-count": "Okwu {{COUNT}}",
"library-button-text": "Gaa na ibe nnabata",
"home-button-text": "Gaa na isi ibe nke '{{BOOK_TITLE}}'",
"random-page-button-text": "Gaa na ibe ahọpụtara enweghị usoro",
"searchbox-tooltip": "Chọọ '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Akwụkwọ abụọ ma ọ bụ karịa n'asụsụ dị iche iche ga-esonye na nchọ, nke nwere ike ibute nsonaazụ mgbagwoju anya.",
"welcome-page-overzealous-filter": "Enweghị nsonaazụ. Ọ ga-amasị gị <a href=\"{{URL}}\">ịtọgharịa nzacha</a> ?",
"powered-by-kiwix-html": "<a href=\"https://kiwix.org\">Kiwix</a> kwadoro ya",
"search": "Chọọ",
"book-filtering-all-categories": "Nkeji niile",
"book-filtering-all-languages": "Asụsụ niile",
"count-of-matching-books": "akwụkwọ {{COUNT}}",
"download": "Budata",
"direct-download-link-text": "Gosi",
"direct-download-alt-text": "nbudata ozugbo",
"hash-download-link-text": "Sha256 hash",
"hash-download-alt-text": "budata hash",
"magnet-link-text": "Njikọ magnet",
"magnet-alt-text": "ibudata magnet",
"torrent-download-link-text": " faịlụ nke Torrent",
"torrent-download-alt-text": "Budata torrent",
"library-opds-feed-all-entries": "Ọbá akwụkwọ OPDS Feed - Ihe niile",
"filter-by-tag": "Wepụta site na mkpado \"{{TAG}}\"",
"stop-filtering-by-tag": "Kwụsị nzacha site na mkpado \"{{TAG}}\"",
"library-opds-feed-parameterised": "Ọbá akwụkwọ OPDS nri - ndenye dabara na {{#LANG}}\nAsụsụ: {{LANG}} {{/LANG}}{{#CATEGORY}}\n Kategori: {{CATEGORY}} {{/CATEGORY}} {{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\n Ajụjụ: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Nabata na Kiwix Server",
"download-links-heading": "Budata njikọ maka <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Budata akwụkwọ",
"preview-book": "Ziwe nkirimaàtụ̀",
"unknown-error": "amaghị m njehie"
}

View File

@@ -24,11 +24,6 @@
"500-page-title": "Errore interno del server",
"500-page-heading": "Errore interno del server",
"500-page-text": "Si è verificato un errore interno del server. Ci dispiace :/",
"search-results-page-title": "Cerca: {{SEARCH_PATTERN}}",
"search-results-page-header": "Risultati <b>{{START}}-{{END}}</b> di <b>{{COUNT}}</b> per <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Non è stato trovato alcun risultato per <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "da {{BOOK_TITLE}}",
"word-count": "{{COUNT}} parole",
"library-button-text": "Vai alla pagina di benvenuto",
"home-button-text": "Vai alla pagina principale di '{{BOOK_TITLE}}'",
"random-page-button-text": "Vai a una pagina selezionata casualmente",

View File

@@ -27,11 +27,6 @@
"500-page-text": "Настана внатрешна грешка во опслужувачот. Жал ни е :/",
"fulltext-search-unavailable": "Целотекстното пребарување е недостапно",
"no-search-results": "Погонот за целотекстно пребарување не е достапен за оваа содржина.",
"search-results-page-title": "Пребарување: {{SEARCH_PATTERN}}",
"search-results-page-header": "Исход <b>{{START}}-{{END}}</b> од <b>{{COUNT}}</b> за <b>„{{{SEARCH_PATTERN}}}“</b>",
"empty-search-results-page-header": "Не најдов ништо за <b>„{{{SEARCH_PATTERN}}}“</b>",
"search-result-book-info": "од {{BOOK_TITLE}}",
"word-count": "{{COUNT}} зборови",
"library-button-text": "Оди на воведната страница",
"home-button-text": "Оди на главната страница на „{{BOOK_TITLE}}“",
"random-page-button-text": "Оди на случајно избрана страница",

View File

@@ -25,13 +25,6 @@
"home-button-text": "Przejdź do głównej strony '{{BOOK_TITLE}}'",
"random-page-button-text": "Przejdź do losowo wybranej strony",
"searchbox-tooltip": "Szukaj '{{BOOK_TITLE}}'",
"search": "Szukaj",
"book-filtering-all-categories": "Wszystkie Kategorie",
"book-filtering-all-languages": "Wszystkie języki",
"download": "Pobierz",
"direct-download-link-text": "Bezpośrednio",
"direct-download-alt-text": "bezpośrednie pobieranie",
"torrent-download-link-text": "Plik torrent",
"welcome-to-kiwix-server": "Witamy na serwerze Kiwix",
"download-links-title": "Pobierz książkę",
"preview-book": "Podgląd"

View File

@@ -6,7 +6,6 @@
"Okras",
"Pacha Tchernof",
"Razno0",
"Rofiatmustapha12",
"Smavrina"
]
},
@@ -19,7 +18,6 @@
"suggest-search": "Выполните полнотекстовый поиск для <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ой! Не удалось выбрать случайную статью :(",
"invalid-raw-data-type": "{{DATATYPE}} не является допустимым запросом необработанного контента.",
"invalid-request": "Запрошенный URL-адрес «{{{url}}}» не является допустимым запросом.",
"no-value-for-arg": "Не указано значение для аргумента {{ARGUMENT}}",
"no-query": "Не предоставлен запрос.",
"raw-entry-not-found": "Не удаётся найти запись {{ENTRY}} типа {{DATATYPE}}",
@@ -29,41 +27,30 @@
"404-page-heading": "Не найдено",
"500-page-title": "Внутренняя ошибка сервера",
"500-page-heading": "Внутренняя ошибка сервера",
"500-page-text": "Произошла внутренняя ошибка сервера. Мы сожалеем об этом :/",
"fulltext-search-unavailable": "Полнотекстовый поиск недоступен",
"no-search-results": "Полнотекстовая поисковая система недоступна для этого содержания.",
"search-results-page-title": "Поиск: {{SEARCH_PATTERN}}",
"search-results-page-header": "Результаты <b>{{START}}-{{END}}</b> из <b>{{COUNT}}</b> для <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "По запросу <b>\"{{{SEARCH_PATTERN}}}\"</b> результатов не найдено.",
"search-result-book-info": "из {{BOOK_TITLE}}",
"word-count": "{{COUNT}} слов",
"library-button-text": "Перейти на страницу-приветствие",
"home-button-text": "Перейти на главную страницу '{{BOOK_TITLE}}'",
"random-page-button-text": "Перейти на случайно выбранную страницу",
"searchbox-tooltip": "Искать '{{BOOK_TITLE}}'",
"confusion-of-tongues": "В поиске будут участвовать две или более книг на разных языках, что может привести к запутанным результатам.",
"welcome-page-overzealous-filter": "Безрезультатно. Хотите <a href=\"{{URL}}\">сбросить фильтр</a> ?",
"powered-by-kiwix-html": "При поддержке&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Найти",
"book-filtering-all-categories": "Все категории",
"book-filtering-all-languages": "Все языки",
"count-of-matching-books": "{{COUNT}} книг(и)",
"download": "Скачать",
"direct-download-link-text": "Прямой",
"direct-download-alt-text": "прямая загрузка",
"hash-download-link-text": "Хэш Sha256",
"hash-download-alt-text": "скачать хэш",
"magnet-link-text": "Магнитная ссылка",
"magnet-alt-text": "скачать магнит",
"torrent-download-link-text": "Торрент-файл",
"torrent-download-alt-text": "скачать торрент",
"library-opds-feed-all-entries": "Канал библиотеки OPDS  все записи",
"filter-by-tag": "Фильтровать по тегу \"{{TAG}}\"",
"stop-filtering-by-tag": "Прекратить фильтрацию по тегу \"{{TAG}}\"",
"library-opds-feed-parameterised": "Канал OPDS библиотеки – записи, соответствующие {{#LANG}}\nLanguage: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategory: {{CATEGORY}} {{/CATEGORY}} {{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nЗапрос: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Добро пожаловать на сервер Kiwix",
"download-links-heading": "Ссылки для скачивания <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Скачать книгу",
"preview-book": "Предпросмотр",
"unknown-error": "Неизвестная ошибка"
"preview-book": "Предпросмотр"
}

View File

@@ -2,8 +2,7 @@
"@metadata": {
"authors": [
"Eleassar",
"Kelson",
"Rofiatmustapha12"
"Kelson"
]
},
"name": "slovenščina",
@@ -28,11 +27,6 @@
"500-page-text": "Prišlo je do notranje napake strežnika. Žal nam je za to. :/",
"fulltext-search-unavailable": "Iskanje po celotnem besedilu ni na voljo",
"no-search-results": "Iskalnik po celotnem besedilu za to vsebino ni na voljo.",
"search-results-page-title": "Iskanje: {{SEARCH_PATTERN}}",
"search-results-page-header": "Zadetki <b>{{START}}{{END}}</b> od <b>{{COUNT}}</b> za »<b>{{{SEARCH_PATTERN}}}</b>«",
"empty-search-results-page-header": "Ni zadetkov za »<b>{{{SEARCH_PATTERN}}}</b>«",
"search-result-book-info": "iz {{BOOK_TITLE}}",
"word-count": "{{COUNT}} besed",
"library-button-text": "Pojdite na pozdravno stran",
"home-button-text": "Pojdite na glavno stran »{{BOOK_TITLE}}«",
"random-page-button-text": "Pojdite na naključno izbrano stran",

View File

@@ -2,7 +2,6 @@
"@metadata": {
"authors": [
"Jopparn",
"Rofiatmustapha12",
"Sabelöga",
"WikiPhoenix"
]
@@ -29,11 +28,6 @@
"500-page-text": "Ett internt serverfel uppstod. Vi ber om ursäkt för det :/",
"fulltext-search-unavailable": "Fulltextsökning är inte tillgänglig",
"no-search-results": "Sökmaskinen för fulltext är inte tillgänglig för detta innehåll.",
"search-results-page-title": "Sök: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultat <b>{{START}}-{{END}}</b> av <b>{{COUNT}}</b> för <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Inga resultat hittades för <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "från {{BOOK_TITLE}}",
"word-count": "{{COUNT}} ord",
"library-button-text": "Gå till hemsidan",
"home-button-text": "Gå till huvudsidan för \"{{BOOK_TITLE}}\"",
"random-page-button-text": "Gå till en slumpmässigt utvald sida",

View File

@@ -1,8 +1,7 @@
{
"@metadata": {
"authors": [
"Hedda",
"Rofiatmustapha12"
"Hedda"
]
},
"name": "Türkçe",
@@ -14,7 +13,6 @@
"suggest-search": "<a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a> için tam metin araması yapın",
"random-article-failure": "Hata! Rastgele bir madde seçilemedi :(",
"invalid-raw-data-type": "{{DATATYPE}}, ham içerik için geçerli bir istek değil.",
"invalid-request": "İstenen \"{{{url}}}\" URL'si geçerli bir istek değil.",
"no-value-for-arg": "{{ARGUMENT}} bağımsız değişkeni için değer sağlanmadı",
"no-query": "Sorgu sağlanmadı.",
"raw-entry-not-found": "{{DATATYPE}} {{ENTRY}} girişi bulunamadı",
@@ -24,41 +22,10 @@
"404-page-heading": "Bulunamadı",
"500-page-title": "İç Sunucu Hatası",
"500-page-heading": "İç Sunucu Hatası",
"500-page-text": "Dahili bir sunucu hatası oluştu. Bunun için üzgünüz :/",
"fulltext-search-unavailable": "Tam metin araması kullanılamıyor",
"no-search-results": "Tam metin arama motoru bu içerik için kullanılamaz.",
"search-results-page-title": "Arama: {{SEARCH_PATTERN}}",
"search-results-page-header": "<b>\"{{{SEARCH_PATTERN}}}\"</b> için <b>{{COUNT}}</b> sonuçtan <b>{{START}}-{{END}}</b> arası sonuçlar",
"empty-search-results-page-header": "<b>\"{{{SEARCH_PATTERN}}}\"</b> için sonuç bulunamadı",
"search-result-book-info": "{{BOOK_TITLE}} adlı kitaptan",
"word-count": "{{COUNT}} kelime",
"library-button-text": "Karşılama sayfasına git",
"home-button-text": "'{{BOOK_TITLE}}' anasayfasına gidin",
"random-page-button-text": "Rastgele seçilen bir sayfaya git",
"searchbox-tooltip": "'{{BOOK_TITLE}}' ara",
"confusion-of-tongues": "Aramaya farklı dillerde iki veya daha fazla kitap katılacak ve bu da kafa karıştırıcı sonuçlara yol açabilecektir.",
"welcome-page-overzealous-filter": "Sonuç yok. <a href=\"{{URL}}\">Filtreyi sıfırlamak</a> ister misiniz?",
"powered-by-kiwix-html": "<a href=\"https://kiwix.org\">Kiwix</a> tarafından desteklenmektedir",
"search": "Ara",
"book-filtering-all-categories": "Tüm kategoriler",
"book-filtering-all-languages": "Tüm diller",
"count-of-matching-books": "{{COUNT}} kitap",
"download": "İndir",
"direct-download-link-text": "Doğrudan",
"direct-download-alt-text": "direkt indirme",
"hash-download-link-text": "Sha256 haşesi",
"hash-download-alt-text": "csv indir",
"magnet-link-text": "Mıknatıs bağlantısı",
"magnet-alt-text": "mıknatısı indir",
"torrent-download-link-text": "Hedef dosya",
"torrent-download-alt-text": "torrenti indir",
"library-opds-feed-all-entries": "Kütüphane OPDS Akışı - Tüm girişler",
"filter-by-tag": "\"{{TAG}}\" etiketine göre filtrele",
"stop-filtering-by-tag": "\"{{TAG}}\" etiketine göre filtrelemeyi durdur",
"library-opds-feed-parameterised": "Kütüphane OPDS Özet Akışı - {{#LANG}}\nLanguage: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategory: {{CATEGORY}} {{/CATEGORY}} ile eşleşen girişler {{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nQuery: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Kiwix Sunucusuna Hoş Geldiniz",
"download-links-heading": "<b><i>{{BOOK_TITLE}}</i></b> için indirme bağlantıları",
"download-links-title": "Kitapları indir",
"preview-book": "Önizleme",
"unknown-error": "Bilinmeyen hata"
"searchbox-tooltip": "'{{BOOK_TITLE}}' ara"
}

View File

@@ -2,63 +2,24 @@
"@metadata": {
"authors": [
"GuoPC",
"IceButBin",
"StarrySky",
"Sunai",
"XtexChooser"
]
},
"name": "英语",
"suggest-full-text-search": "正在查找「{{{SEARCH_TERMS}}}」…",
"no-such-book": "没有名为“{{BOOK_NAME}}”的图书",
"too-many-books": "请求的图书过多 ({{NB_BOOKS}}),上限为 {{LIMIT}}",
"no-book-found": "没有符合搜索要求的图书",
"url-not-found": "在此服务器上找不到请求的 URL{{url}}",
"suggest-search": "对<a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>进行全文搜索",
"random-article-failure": "抱歉!随机条目失败了 (⁠〒⁠﹏⁠〒⁠)",
"invalid-raw-data-type": "{{DATATYPE}} 对原请求无效。",
"invalid-request": "请求的URL无效{{{url}}}",
"no-value-for-arg": "参数{{ARGUMENT}}无值",
"no-query": "未提供查询。",
"raw-entry-not-found": "找不到 {{DATATYPE}} 条目 {{ENTRY}}",
"400-page-title": "无效请求",
"400-page-heading": "无效请求",
"404-page-title": "未找到内容",
"404-page-heading": "未找到",
"500-page-title": "内部服务器错误",
"500-page-heading": "内部服务器错误",
"500-page-text": "内部服务器出现错误。真的十分抱歉 (;⁠ŏ⁠﹏⁠ŏ⁠)",
"fulltext-search-unavailable": "全文搜索不可用",
"no-search-results": "全文搜索引擎不适用于该内容。",
"search-results-page-title": "搜索:{{SEARCH_PATTERN}}",
"search-results-page-header": "<b>“{{{SEARCH_PATTERN}}}”</b>的<b>第 {{START}}-{{END}}</b>个结果(共<b>{{COUNT}}</b>个)",
"empty-search-results-page-header": "未找到<b>“{{{SEARCH_PATTERN}}}”</b>的结果",
"search-result-book-info": "来自{{BOOK_TITLE}}",
"word-count": "{{COUNT}} 个字",
"library-button-text": "前往欢迎页面",
"home-button-text": "转到“{{BOOK_TITLE}}”的主页",
"random-page-button-text": "前往随机选择的页面",
"searchbox-tooltip": "搜索“{{BOOK_TITLE}}”",
"confusion-of-tongues": "两本或多本不同语言的图书将同时被搜索,这可能会导致搜索结果混乱。",
"welcome-page-overzealous-filter": "没有结果。您想<a href=\"{{URL}}\">重置过滤器</a>吗?",
"powered-by-kiwix-html": "由<a href=\"https://kiwix.org\">Kiwix</a>提供技术支持",
"search": "搜索",
"book-filtering-all-categories": "所有分类",
"book-filtering-all-languages": "所有语言",
"count-of-matching-books": "{{COUNT}} 本书",
"download": "下载",
"direct-download-link-text": "直接",
"direct-download-alt-text": "直接下載",
"hash-download-link-text": "Sha256 哈希值",
"hash-download-alt-text": "下载哈希值",
"magnet-link-text": "磁力链接",
"magnet-alt-text": "下载磁力链接",
"torrent-download-link-text": "种子文件",
"torrent-download-alt-text": "下载种子文件",
"library-opds-feed-all-entries": "图书馆 OPDS Feed - 所有条目",
"filter-by-tag": "按标签“{{TAG}}”过滤",
"stop-filtering-by-tag": "停止按标签“{{TAG}}”过滤",
"library-opds-feed-parameterised": "图书馆 OPDS Feed - 匹配的项目 {{#LANG}}\n语言{{LANG}} {{/LANG}}{{#CATEGORY}}\n分类{{CATEGORY}} {{/CATEGORY}}{{#TAG}}\n标签{{TAG}} {{/TAG}}{{#Q}}\n查询{{Q}} {{/Q}}",
"welcome-to-kiwix-server": "欢迎来到 Kiwix 服务器",
"preview-book": "预览"
}

View File

@@ -28,11 +28,6 @@
"500-page-text": "內部伺服器發生錯誤。對此我們深感抱歉:/",
"fulltext-search-unavailable": "全文搜尋無效",
"no-search-results": "全文搜尋引擎不適用此內容。",
"search-results-page-title": "搜尋:{{SEARCH_PATTERN}}",
"search-results-page-header": "<b>「{{{SEARCH_PATTERN}}}」</b>的<b>第{{START}}-{{END}}筆</b>結果(共<b>{{COUNT}}</b>筆)",
"empty-search-results-page-header": "未找到<b>「{{{SEARCH_PATTERN}}}」</b>的結果",
"search-result-book-info": "來自{{BOOK_TITLE}}",
"word-count": "{{COUNT}}個字",
"library-button-text": "前往歡迎首頁",
"home-button-text": "前往「{{BOOK_TITLE}}」的首頁",
"random-page-button-text": "前往隨機選取頁面",

View File

@@ -7,27 +7,17 @@ const uiLanguages = [
{
"iso_code": "bn",
"self_name": "বাংলা",
"translation_count": 14
},
{
"iso_code": "br",
"self_name": "brezhoneg",
"translation_count": 35
"translation_count": 12
},
{
"iso_code": "cs",
"self_name": "Čeština",
"translation_count": 25
},
{
"iso_code": "dag",
"self_name": "Silimiinsili",
"translation_count": 24
},
{
"iso_code": "de",
"self_name": "Deutsch",
"translation_count": 57
"translation_count": 49
},
{
"iso_code": "en",
@@ -47,17 +37,12 @@ const uiLanguages = [
{
"iso_code": "fr",
"self_name": "Français",
"translation_count": 57
},
{
"iso_code": "ha",
"self_name": "Turanci",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "he",
"self_name": "עברית",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "hi",
@@ -74,15 +59,10 @@ const uiLanguages = [
"self_name": "interlingua",
"translation_count": 49
},
{
"iso_code": "ig",
"self_name": "Bekee",
"translation_count": 57
},
{
"iso_code": "it",
"self_name": "italiano",
"translation_count": 34
"translation_count": 29
},
{
"iso_code": "ja",
@@ -107,7 +87,7 @@ const uiLanguages = [
{
"iso_code": "mk",
"self_name": "македонски",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "ms",
@@ -132,12 +112,12 @@ const uiLanguages = [
{
"iso_code": "pl",
"self_name": "Polski",
"translation_count": 31
"translation_count": 24
},
{
"iso_code": "ru",
"self_name": "русский",
"translation_count": 57
"translation_count": 45
},
{
"iso_code": "sc",
@@ -157,7 +137,7 @@ const uiLanguages = [
{
"iso_code": "sl",
"self_name": "slovenščina",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "sq",
@@ -167,7 +147,7 @@ const uiLanguages = [
{
"iso_code": "sv",
"self_name": "Svenska",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "te",
@@ -177,16 +157,16 @@ const uiLanguages = [
{
"iso_code": "tr",
"self_name": "Türkçe",
"translation_count": 57
"translation_count": 25
},
{
"iso_code": "zh-hans",
"self_name": "英语",
"translation_count": 54
"translation_count": 16
},
{
"iso_code": "zh-hant",
"self_name": "繁體中文",
"translation_count": 57
"translation_count": 52
}
]

View File

@@ -1,21 +0,0 @@
// A few browsers do not support the use of String.prototype.replaceAll method.
// Hence we define it once we verify that it isn't supported. For documentation
// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function (pattern, replacement) {
// verify parameter: It must either be a string or a RegExp with a global flag.
if (typeof pattern[Symbol.replace] === 'function') {
// the pattern is a RegExp check for the presence of g flag.
if (pattern.global) {
return this.replace(pattern, replacement);
} else {
throw new TypeError('Global flag for regular expressions')
}
}
// the pattern is not a RegExp, hence it must be a string.
if (typeof pattern !== 'string') {
throw new TypeError('pattern must either be a string or a RegExp with a global (g) flag.')
}
return this.replace(new RegExp(pattern, 'g'), replacement);
}
}

View File

@@ -31,7 +31,6 @@
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="{{root}}/skin/favicon/browserconfig.xml?KIWIXCACHEID">
<meta name="theme-color" content="#ffffff">
<script type="text/javascript" src="./skin/polyfills.js?KIWIXCACHEID"></script>
<script type="text/javascript" src="./viewer_settings.js"></script>
<script type="module" src="{{root}}/skin/i18n.js?KIWIXCACHEID" defer></script>
<script type="text/javascript" src="{{root}}/skin/languages.js?KIWIXCACHEID" defer></script>

View File

@@ -11,7 +11,6 @@
<link type="text/css" href="./skin/kiwix.css?KIWIXCACHEID" rel="Stylesheet" />
<link type="text/css" href="./skin/taskbar.css?KIWIXCACHEID" rel="Stylesheet" />
<link type="text/css" href="./skin/autoComplete/css/autoComplete.css?KIWIXCACHEID" rel="Stylesheet" />
<script type="text/javascript" src="./skin/polyfills.js?KIWIXCACHEID"></script>
<script type="text/javascript" src="./viewer_settings.js"></script>
<script type="module" src="./skin/i18n.js?KIWIXCACHEID" defer></script>
<script type="text/javascript" src="./skin/languages.js?KIWIXCACHEID" defer></script>

View File

@@ -20,6 +20,7 @@
#include "gtest/gtest.h"
#include <string>
const char * sampleOpdsStream = R"(
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/terms/"
@@ -27,12 +28,12 @@ const char * sampleOpdsStream = R"(
<id>00000000-0000-0000-0000-000000000000</id>
<entry>
<title>Encyclopédie de la Tunisie</title>
<name>wikipedia_fr_tunisie</name>
<flavour>novid</flavour>
<name>wikipedia_fr_tunisie_novid_2018-10</name>
<flavour>unforgettable</flavour>
<id>urn:uuid:0c45160e-f917-760a-9159-dfe3c53cdcdd</id>
<icon>/meta?name=favicon&amp;content=wikipedia_fr_tunisie_novid_2018-10</icon>
<updated>2018-10-08T00:00::00:Z</updated>
<dc:issued>2018-10-08T00:00::00:Z</dc:issued>
<dc:issued>8 Oct 2018</dc:issued>
<language>fra</language>
<summary>Le meilleur de Wikipédia sur la Tunisie</summary>
<tags>wikipedia;novid;_ftindex</tags>
@@ -48,53 +49,9 @@ const char * sampleOpdsStream = R"(
<mediaCount>1100</mediaCount>
<articleCount>172</articleCount>
</entry>
<entry>
<title>Encyclopédie de la Tunisie</title>
<name>wikipedia_fr_tunisie</name>
<flavour>novid</flavour>
<id>urn:uuid:0c45160e-f917-760a-9159-dfe3c53cdcdd_updated1yearlater</id>
<updated>2019-10-08T00:00::00:Z</updated>
<dc:issued>2019-10-08T00:00::00:Z</dc:issued>
<language>fra</language>
<summary>Le meilleur de Wikipédia sur la Tunisie. Updated in 2019</summary>
<author>
<name>Wikipedia</name>
</author>
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/wikipedia/wikipedia_fr_tunisie_novid_2018-10.zim.meta4" length="90030080" />
</entry>
<entry>
<title>Encyclopédie de la Tunisie</title>
<name>wikipedia_fr_tunisie</name>
<flavour>other_flavour</flavour>
<id>urn:uuid:0c45160e-f917-760a-9159-dfe3c53cdcdd_flavour</id>
<updated>2018-10-08T00:00::00:Z</updated>
<dc:issued>2018-10-08T00:00::00:Z</dc:issued>
<language>fra</language>
<summary>Le meilleur de Wikipédia sur la Tunisie. With another flavour</summary>
<author>
<name>Wikipedia</name>
</author>
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/wikipedia/wikipedia_fr_tunisie_novid_2018-10.zim.meta4" length="90030080" />
</entry>
<entry>
<title>Encyclopédie de la Tunisie</title>
<name>wikipedia_fr_tunisie</name>
<flavour>other_flavour</flavour>
<id>urn:uuid:0c45160e-f917-760a-9159-dfe3c53cdcdd_updated1yearlater_flavour</id>
<updated>2019-10-08T00:00::00:Z</updated>
<dc:issued>2019-10-08T00:00::00:Z</dc:issued>
<language>fra</language>
<summary>Le meilleur de Wikipédia sur la Tunisie. Updated in 2019, and other flavour</summary>
<author>
<name>Wikipedia</name>
</author>
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/wikipedia/wikipedia_fr_tunisie_novid_2018-10.zim.meta4" length="90030080" />
</entry>
<entry>
<title>Tania Louis</title>
<id>urn:uuid:0d0bcd57-d3f6-cb22-44cc-a723ccb4e1b2</id>
<name>biologie-tout-compris_fr_all</name>
<flavour>full</flavour>
<icon>/meta?name=favicon&amp;content=biologie-tout-compris_fr_all_2018-06</icon>
<updated>2018-06-23T00:00::00:Z</updated>
<language>fra</language>
@@ -110,8 +67,6 @@ const char * sampleOpdsStream = R"(
<entry>
<title>Wikiquote</title>
<id>urn:uuid:0ea1cde6-441d-6c58-f2c7-21c2838e659f</id>
<name>wikiquote_fr_all</name>
<flavour>full</flavour>
<icon>/meta?name=favicon&amp;content=wikiquote_fr_all_nopic_2019-06</icon>
<updated>2019-06-05T00:00::00:Z</updated>
<language>fra,ita</language>
@@ -128,8 +83,6 @@ const char * sampleOpdsStream = R"(
<entry>
<title>Géographie par Wikipédia</title>
<id>urn:uuid:1123e574-6eef-6d54-28fc-13e4caeae474</id>
<name>wikipedia_fr_geography</name>
<flavour>full</flavour>
<icon>/meta?name=favicon&amp;content=wikipedia_fr_geography_nopic_2019-06</icon>
<updated>2019-06-02T00:00::00:Z</updated>
<summary>Une sélection d'articles de Wikipédia sur la géographie</summary>
@@ -146,8 +99,6 @@ const char * sampleOpdsStream = R"(
<entry>
<title>Mathématiques</title>
<id>urn:uuid:14829621-c490-c376-0792-9de558b57efa</id>
<name>wikipedia_fr_mathematics</name>
<flavour>novid</flavour>
<icon>/meta?name=favicon&amp;content=wikipedia_fr_mathematics_nopic_2019-05</icon>
<updated>2019-05-13T00:00::00:Z</updated>
<language>fra</language>
@@ -164,8 +115,6 @@ const char * sampleOpdsStream = R"(
<entry>
<title>Granblue Fantasy Wiki</title>
<id>urn:uuid:006cbd1b-16d8-b00d-a584-c1ae110a94ed</id>
<name>grandbluefantasy_en_all</name>
<flavour>novid</flavour>
<icon>/meta?name=favicon&amp;content=granbluefantasy_en_all_all_nopic_2018-10</icon>
<updated>2018-10-14T00:00::00:Z</updated>
<language>eng</language>
@@ -181,8 +130,6 @@ const char * sampleOpdsStream = R"(
<entry>
<title>Movies &amp; TV Stack Exchange</title>
<id>urn:uuid:00f37b00-f4da-0675-995a-770f9c72903e</id>
<name>movies.stackexchange.com_en_all</name>
<flavour>novid</flavour>
<icon>/meta?name=favicon&amp;content=movies.stackexchange.com_en_all_2019-02</icon>
<updated>2019-02-03T00:00::00:Z</updated>
<language>eng</language>
@@ -196,10 +143,8 @@ const char * sampleOpdsStream = R"(
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&amp;content=movies.stackexchange.com_en_all_2019-02" />
</entry>
<entry>
<title>TED"talks" - Business</title>
<title>TED talks - Business</title>
<id>urn:uuid:0189d9be-2fd0-b4b6-7300-20fab0b5cdc8</id>
<name>ted_en_business</name>
<flavour>nodet</flavour>
<icon>/meta?name=favicon&amp;content=ted_en_business_2018-07</icon>
<updated>2018-07-23T00:00::00:Z</updated>
<language>eng</language>
@@ -212,28 +157,9 @@ const char * sampleOpdsStream = R"(
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/ted/ted_en_business_2018-07.zim.meta4" length="8855827456" />
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&amp;content=ted_en_business_2018-07" />
</entry>
<entry>
<title>Business talks about TED</title>
<id>Dummy id </id>
<name>speak_business</name>
<flavour>nodet</flavour>
<icon>/meta?name=favicon&amp;content=ted_en_business_2018-07</icon>
<updated>2018-08-23T00:00::00:Z</updated>
<language>eng</language>
<summary>Ideas worth spreading</summary>
<tags></tags>
<link type="text/html" href="/ted_en_business_2018-07" />
<author>
<name>TED</name>
</author>
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="http://download.kiwix.org/zim/ted/ted_en_business_2018-07.zim.meta4" length="8855827456" />
<link rel="http://opds-spec.org/image/thumbnail" type="image/png" href="/meta?name=favicon&amp;content=ted_en_business_2018-07" />
</entry>
<entry>
<title>Mythology &amp; Folklore Stack Exchange</title>
<id>urn:uuid:028055ac-4acc-1d54-65e0-a96de45e1b22</id>
<name>mythology.stackexchange.com_en_all</name>
<flavour>novid</flavour>
<icon>/meta?name=favicon&amp;content=mythology.stackexchange.com_en_all_2019-02</icon>
<updated>2019-02-03T00:00::00:Z</updated>
<language>eng</language>
@@ -249,8 +175,6 @@ const char * sampleOpdsStream = R"(
<entry>
<title>Islam Stack Exchange</title>
<id>urn:uuid:02e9c7ff-36fc-9c6e-6ac7-cd7085989029</id>
<name>islam.stackexchange.com_en_all</name>
<flavour>novid</flavour>
<icon>/meta?name=favicon&amp;content=islam.stackexchange.com_en_all_2019-01</icon>
<updated>2019-01-31T00:00::00:Z</updated>
<language>eng</language>
@@ -305,7 +229,6 @@ const char sampleLibraryXML[] = R"(
#include "../include/library.h"
#include "../include/manager.h"
#include "../include/book.h"
#include "../include/bookmark.h"
namespace
@@ -319,17 +242,17 @@ TEST(LibraryOpdsImportTest, allInOne)
kiwix::Manager manager(lib);
manager.readOpds(sampleOpdsStream, "library-opds-import.unittests.dev");
EXPECT_EQ(14U, lib->getBookCount(true, true));
EXPECT_EQ(10U, lib->getBookCount(true, true));
{
const kiwix::Book& book1 = lib->getBookById("0c45160e-f917-760a-9159-dfe3c53cdcdd");
EXPECT_EQ(book1.getTitle(), "Encyclopédie de la Tunisie");
EXPECT_EQ(book1.getName(), "wikipedia_fr_tunisie");
EXPECT_EQ(book1.getFlavour(), "novid");
EXPECT_EQ(book1.getName(), "wikipedia_fr_tunisie_novid_2018-10");
EXPECT_EQ(book1.getFlavour(), "unforgettable");
EXPECT_EQ(book1.getLanguages(), Langs{ "fra" });
EXPECT_EQ(book1.getCommaSeparatedLanguages(), "fra");
EXPECT_EQ(book1.getDate(), "2018-10-08");
EXPECT_EQ(book1.getDate(), "8 Oct 2018");
EXPECT_EQ(book1.getDescription(), "Le meilleur de Wikipédia sur la Tunisie");
EXPECT_EQ(book1.getCreator(), "Wikipedia");
EXPECT_EQ(book1.getPublisher(), "Wikipedia Publishing House");
@@ -349,9 +272,9 @@ TEST(LibraryOpdsImportTest, allInOne)
{
const kiwix::Book& book2 = lib->getBookById("0189d9be-2fd0-b4b6-7300-20fab0b5cdc8");
EXPECT_EQ(book2.getTitle(), "TED\"talks\" - Business");
EXPECT_EQ(book2.getName(), "ted_en_business");
EXPECT_EQ(book2.getFlavour(), "nodet");
EXPECT_EQ(book2.getTitle(), "TED talks - Business");
EXPECT_EQ(book2.getName(), "");
EXPECT_EQ(book2.getFlavour(), "");
EXPECT_EQ(book2.getLanguages(), Langs{ "eng" });
EXPECT_EQ(book2.getCommaSeparatedLanguages(), "eng");
EXPECT_EQ(book2.getDate(), "2018-07-23");
@@ -386,18 +309,11 @@ class LibraryTest : public ::testing::Test {
manager.readXml(sampleLibraryXML, false, "./test/library.xml", true);
}
kiwix::Bookmark createBookmark(const std::string &id, const std::string& url="", const std::string& title="") {
kiwix::Bookmark bookmark;
bookmark.setBookId(id);
bookmark.setUrl(url);
bookmark.setTitle(title);
return bookmark;
};
kiwix::Bookmark createBookmark(const kiwix::Book& book, const std::string& url="", const std::string& title="") {
kiwix::Bookmark bookmark(book, url, title);
return bookmark;
};
kiwix::Bookmark createBookmark(const std::string &id) {
kiwix::Bookmark bookmark;
bookmark.setBookId(id);
return bookmark;
};
TitleCollection ids2Titles(const BookIdCollection& ids) {
TitleCollection titles;
@@ -411,320 +327,28 @@ class LibraryTest : public ::testing::Test {
std::shared_ptr<kiwix::Library> lib;
};
TEST_F(LibraryTest, createBookMark)
{
auto bookId = "0c45160e-f917-760a-9159-dfe3c53cdcdd";
auto book = lib->getBookById(bookId);
auto bookmark = createBookmark(book, "/a/url", "A title");
EXPECT_EQ(bookmark.getUrl(), "/a/url");
EXPECT_EQ(bookmark.getTitle(), "A title");
EXPECT_EQ(bookmark.getBookId(), bookId);
EXPECT_EQ(bookmark.getBookName(), book.getName());
EXPECT_EQ(bookmark.getBookName(), "wikipedia_fr_tunisie");
EXPECT_EQ(bookmark.getBookTitle(), book.getTitle());
EXPECT_EQ(bookmark.getDate(), book.getDate());
EXPECT_EQ(bookmark.getBookFlavour(), book.getFlavour());
EXPECT_EQ(bookmark.getLanguage(), book.getCommaSeparatedLanguages());
}
TEST_F(LibraryTest, getBookMarksTest)
{
auto bookId1 = "0c45160e-f917-760a-9159-dfe3c53cdcdd";
auto bookId2 = "0189d9be-2fd0-b4b6-7300-20fab0b5cdc8";
auto book1 = lib->getBookById(bookId1);
auto book2 = lib->getBookById(bookId2);
lib->addBookmark(createBookmark(book1));
lib->addBookmark(createBookmark("invalid-book-id"));
lib->addBookmark(createBookmark(book2));
auto onlyValidBookmarks = lib->getBookmarks();
auto allBookmarks = lib->getBookmarks(false);
EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-book-id");
EXPECT_EQ(allBookmarks[2].getBookId(), bookId2);
}
TEST_F(LibraryTest, bookmarksSerializationTest)
{
auto bookId1 = lib->getBooksIds()[0];
auto bookId2 = lib->getBooksIds()[1];
auto book1 = lib->getBookById(bookId1);
auto book2 = lib->getBookById(bookId2);
// Create bookmarks using three different ways.
lib->addBookmark(createBookmark(bookId1, "a/url", "Article title1"));
lib->addBookmark(createBookmark("invalid-book-id", "another/url", "Unknown title"));
lib->addBookmark(createBookmark(book2, "a/url/2", "Article title2"));
lib->writeBookmarksToFile("__test__bookmarks.xml");
// Build a new library
auto new_lib = kiwix::Library::create();
{
kiwix::Manager manager(new_lib);
manager.readOpds(sampleOpdsStream, "foo.urlHost");
manager.readXml(sampleLibraryXML, false, "./test/library.xml", true);
manager.readBookmarkFile("__test__bookmarks.xml");
}
std::remove("__test__bookmarks.xml");
auto onlyValidBookmarks = new_lib->getBookmarks();
auto allBookmarks = new_lib->getBookmarks(false);
ASSERT_EQ(onlyValidBookmarks.size(), 2U);
EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId2);
ASSERT_EQ(allBookmarks.size(), 3U);
auto bookmark1 = allBookmarks[0];
EXPECT_EQ(bookmark1.getBookId(), bookId1);
EXPECT_EQ(bookmark1.getBookTitle(), book1.getTitle());
EXPECT_EQ(bookmark1.getBookName(), book1.getName());
EXPECT_EQ(bookmark1.getBookFlavour(), book1.getFlavour());
EXPECT_EQ(bookmark1.getUrl(), "a/url");
EXPECT_EQ(bookmark1.getTitle(), "Article title1");
EXPECT_EQ(bookmark1.getLanguage(), book1.getCommaSeparatedLanguages());
EXPECT_EQ(bookmark1.getDate(), book1.getDate());
auto bookmark2 = allBookmarks[1];
EXPECT_EQ(bookmark2.getBookId(), "invalid-book-id");
EXPECT_EQ(bookmark2.getBookTitle(), "");
EXPECT_EQ(bookmark2.getBookName(), "");
EXPECT_EQ(bookmark2.getBookFlavour(), "");
EXPECT_EQ(bookmark2.getUrl(), "another/url");
EXPECT_EQ(bookmark2.getTitle(), "Unknown title");
EXPECT_EQ(bookmark2.getLanguage(), "");
EXPECT_EQ(bookmark2.getDate(), "");
auto bookmark3 = allBookmarks[2];
EXPECT_EQ(bookmark3.getBookId(), bookId2);
EXPECT_EQ(bookmark3.getBookTitle(), book2.getTitle());
EXPECT_EQ(bookmark3.getBookName(), book2.getName());
EXPECT_EQ(bookmark3.getBookFlavour(), book2.getFlavour());
EXPECT_EQ(bookmark3.getUrl(), "a/url/2");
EXPECT_EQ(bookmark3.getTitle(), "Article title2");
EXPECT_EQ(bookmark3.getLanguage(), book2.getCommaSeparatedLanguages());
EXPECT_EQ(bookmark3.getDate(), book2.getDate());
}
TEST_F(LibraryTest, MigrateBookmark)
{
std::string bookId1 = "0c45160e-f917-760a-9159-dfe3c53cdcdd";
std::string bookId2 = "0189d9be-2fd0-b4b6-7300-20fab0b5cdc8";
auto book1 = lib->getBookById(bookId1);
auto book1Flavour = lib->getBookById(bookId1+"_flavour");
auto book2 = lib->getBookById(bookId2);
lib->addBookmark(createBookmark(book1));
lib->addBookmark(createBookmark("invalid-book-id"));
lib->addBookmark(createBookmark(book2));
auto wrongIdBookmark = createBookmark(book1);
wrongIdBookmark.setBookId("wrong-book-id");
lib->addBookmark(wrongIdBookmark);
auto wrongIdBookmarkNoName = createBookmark(book2);
wrongIdBookmarkNoName.setBookId("wrong-book-id-noname");
wrongIdBookmarkNoName.setBookName("");
lib->addBookmark(wrongIdBookmarkNoName);
auto wrongIdFlavourBookmark = createBookmark(book1Flavour);
wrongIdFlavourBookmark.setBookId("wrong-book-flavour-id");
lib->addBookmark(wrongIdFlavourBookmark);
lib->addBookmark(createBookmark(bookId1));
lib->addBookmark(createBookmark("invalid-bookmark-id"));
lib->addBookmark(createBookmark(bookId2));
auto onlyValidBookmarks = lib->getBookmarks();
auto allBookmarks = lib->getBookmarks(false);
ASSERT_EQ(onlyValidBookmarks.size(), 2U);
EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId2);
ASSERT_EQ(allBookmarks.size(), 6U);
EXPECT_EQ(allBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-book-id");
EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-bookmark-id");
EXPECT_EQ(allBookmarks[2].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[3].getBookId(), "wrong-book-id");
EXPECT_EQ(allBookmarks[4].getBookId(), "wrong-book-id-noname");
EXPECT_EQ(allBookmarks[5].getBookId(), "wrong-book-flavour-id");
ASSERT_EQ(lib->migrateBookmarks("no-existant-book"), 0);
ASSERT_EQ(lib->migrateBookmarks(), std::make_tuple(3, 4));
onlyValidBookmarks = lib->getBookmarks();
allBookmarks = lib->getBookmarks(false);
ASSERT_EQ(onlyValidBookmarks.size(), 5U);
EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[2].getBookId(), bookId1+"_updated1yearlater");
EXPECT_EQ(onlyValidBookmarks[3].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[4].getBookId(), bookId1+"_updated1yearlater_flavour");
ASSERT_EQ(allBookmarks.size(), 6U);
EXPECT_EQ(allBookmarks[0].getBookId(), bookId1);
EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-book-id");
EXPECT_EQ(allBookmarks[2].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[3].getBookId(), bookId1+"_updated1yearlater");
EXPECT_EQ(allBookmarks[4].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[5].getBookId(), bookId1+"_updated1yearlater_flavour");
ASSERT_EQ(lib->migrateBookmarks(), std::make_tuple(0, 1));
ASSERT_EQ(lib->migrateBookmarks(bookId1), 1);
allBookmarks = lib->getBookmarks(false);
ASSERT_EQ(allBookmarks.size(), 6U);
EXPECT_EQ(allBookmarks[0].getBookId(), bookId1+"_updated1yearlater");
EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-book-id");
EXPECT_EQ(allBookmarks[2].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[3].getBookId(), bookId1+"_updated1yearlater");
EXPECT_EQ(allBookmarks[4].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[5].getBookId(), bookId1+"_updated1yearlater_flavour");
ASSERT_EQ(lib->migrateBookmarks(bookId1, bookId2), 0); // No more bookId1 bookmark
ASSERT_EQ(lib->migrateBookmarks(bookId1+"_updated1yearlater", bookId2), 2);
onlyValidBookmarks = lib->getBookmarks();
allBookmarks = lib->getBookmarks(false);
ASSERT_EQ(onlyValidBookmarks.size(), 5U);
EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[2].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[3].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[4].getBookId(), bookId1+"_updated1yearlater_flavour");
ASSERT_EQ(allBookmarks.size(), 6U);
EXPECT_EQ(allBookmarks[0].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-book-id");
EXPECT_EQ(allBookmarks[2].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[3].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[4].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[5].getBookId(), bookId1+"_updated1yearlater_flavour");
ASSERT_EQ(lib->migrateBookmarks("invalid-book-id", bookId1), 1);
onlyValidBookmarks = lib->getBookmarks();
allBookmarks = lib->getBookmarks(false);
ASSERT_EQ(onlyValidBookmarks.size(), 6U);
EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId1);
EXPECT_EQ(onlyValidBookmarks[2].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[3].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[4].getBookId(), bookId2);
EXPECT_EQ(onlyValidBookmarks[5].getBookId(), bookId1+"_updated1yearlater_flavour");
ASSERT_EQ(allBookmarks.size(), 6U);
EXPECT_EQ(allBookmarks[0].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[1].getBookId(), bookId1);
EXPECT_EQ(allBookmarks[2].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[3].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[4].getBookId(), bookId2);
EXPECT_EQ(allBookmarks[5].getBookId(), bookId1+"_updated1yearlater_flavour");
}
TEST_F(LibraryTest, GetBestTargetBookIdOlder)
{
auto bookId = std::string("0c45160e-f917-760a-9159-dfe3c53cdcdd");
auto book = lib->getBookById(bookId);
auto validBookmark = createBookmark(book);
lib->addBookmark(validBookmark);
ASSERT_EQ(lib->getBestTargetBookId(validBookmark, kiwix::UPGRADE_ONLY), bookId+"_updated1yearlater");
ASSERT_EQ(lib->getBestTargetBookId(validBookmark, kiwix::ALLOW_DOWNGRADE), bookId+"_updated1yearlater");
}
TEST_F(LibraryTest, GetBestTargetBookIdNewer)
{
auto bookId = std::string("0c45160e-f917-760a-9159-dfe3c53cdcdd_updated1yearlater");
auto book = lib->getBookById(bookId);
EXPECT_EQ(book.getDate(), "2019-10-08");
auto validBookmark = createBookmark(book);
// Make the bookmark more recent than any books in the library.
// (But still pointing to existing book)
validBookmark.setDate("2020-10-08");
lib->addBookmark(validBookmark);
// The best book for the bookmark is bookId...
ASSERT_EQ(lib->getBestTargetBookId(validBookmark, kiwix::UPGRADE_ONLY), bookId);
// but there is not migration to do as the bookmark already point to it.
ASSERT_EQ(lib->migrateBookmarks(bookId, kiwix::UPGRADE_ONLY), 0);
ASSERT_EQ(lib->getBestTargetBookId(validBookmark, kiwix::ALLOW_DOWNGRADE), bookId);
}
TEST_F(LibraryTest, GetBestTargetBookIdInvalidOlder)
{
auto bookId = std::string("0c45160e-f917-760a-9159-dfe3c53cdcdd");
auto book = lib->getBookById(bookId);
auto invalidBookmark = createBookmark(book);
invalidBookmark.setBookId("invalid-book-id");
lib->addBookmark(invalidBookmark);
ASSERT_EQ(lib->getBestTargetBookId(invalidBookmark, kiwix::UPGRADE_ONLY), bookId+"_updated1yearlater");
ASSERT_EQ(lib->getBestTargetBookId(invalidBookmark, kiwix::ALLOW_DOWNGRADE), bookId+"_updated1yearlater");
}
TEST_F(LibraryTest, GetBestTargetBookIdInvalidNewer)
{
auto bookId = std::string("0c45160e-f917-760a-9159-dfe3c53cdcdd");
auto book = lib->getBookById(bookId);
EXPECT_EQ(book.getDate(), "2018-10-08");
auto invalidBookmark = createBookmark(book);
invalidBookmark.setBookId("invalid-book-id");
invalidBookmark.setDate("2020-10-08");
lib->addBookmark(invalidBookmark);
ASSERT_EQ(lib->getBestTargetBookId(invalidBookmark, kiwix::UPGRADE_ONLY), "");
ASSERT_EQ(lib->getBestTargetBookId(invalidBookmark, kiwix::ALLOW_DOWNGRADE), bookId+"_updated1yearlater");
}
TEST_F(LibraryTest, GetBestTargetBookIdFlavour)
{
auto bookId = std::string("0c45160e-f917-760a-9159-dfe3c53cdcdd_flavour");
auto book = lib->getBookById(bookId);
EXPECT_EQ(book.getDate(), "2018-10-08");
auto invalidBookmark = createBookmark(book);
invalidBookmark.setBookId("invalid-book-id");
invalidBookmark.setDate("2020-10-08");
lib->addBookmark(invalidBookmark);
ASSERT_EQ(lib->getBestTargetBookId(invalidBookmark, kiwix::UPGRADE_ONLY), "");
ASSERT_EQ(lib->getBestTargetBookId(invalidBookmark, kiwix::ALLOW_DOWNGRADE), "0c45160e-f917-760a-9159-dfe3c53cdcdd_updated1yearlater_flavour");
}
TEST_F(LibraryTest, GetBestTargetBookIdName)
{
ASSERT_EQ(lib->getBestTargetBookId("wikipedia_fr_tunisie"), "0c45160e-f917-760a-9159-dfe3c53cdcdd_updated1yearlater");
ASSERT_EQ(lib->getBestTargetBookId("wikipedia_fr_tunisie", "novid"), "0c45160e-f917-760a-9159-dfe3c53cdcdd_updated1yearlater");
ASSERT_EQ(lib->getBestTargetBookId("wikipedia_fr_tunisie", "other_flavour"), "0c45160e-f917-760a-9159-dfe3c53cdcdd_updated1yearlater_flavour");
ASSERT_EQ(lib->getBestTargetBookId("wikipedia_fr_tunisie", "other_flavour", "2020-12-12"), "");
}
TEST_F(LibraryTest, sanityCheck)
{
EXPECT_EQ(lib->getBookCount(true, true), 16U);
EXPECT_EQ(lib->getBookCount(true, true), 12U);
EXPECT_EQ(lib->getBooksLanguages(),
std::vector<std::string>({"deu", "eng", "fra", "ita", "spa"})
);
@@ -776,10 +400,6 @@ TEST_F(LibraryTest, filterLocal)
);
EXPECT_FILTER_RESULTS(kiwix::Filter().local(false),
"Business talks about TED",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Granblue Fantasy Wiki",
"Géographie par Wikipédia",
@@ -787,7 +407,7 @@ TEST_F(LibraryTest, filterLocal)
"Mathématiques",
"Movies & TV Stack Exchange",
"Mythology & Folklore Stack Exchange",
"TED\"talks\" - Business",
"TED talks - Business",
"Tania Louis",
"Wikiquote"
);
@@ -796,10 +416,6 @@ TEST_F(LibraryTest, filterLocal)
TEST_F(LibraryTest, filterRemote)
{
EXPECT_FILTER_RESULTS(kiwix::Filter().remote(true),
"Business talks about TED",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Granblue Fantasy Wiki",
"Géographie par Wikipédia",
@@ -808,7 +424,7 @@ TEST_F(LibraryTest, filterRemote)
"Movies & TV Stack Exchange",
"Mythology & Folklore Stack Exchange",
"Ray Charles",
"TED\"talks\" - Business",
"TED talks - Business",
"Tania Louis",
"Wikiquote"
);
@@ -821,23 +437,21 @@ TEST_F(LibraryTest, filterRemote)
TEST_F(LibraryTest, filterByLanguage)
{
EXPECT_FILTER_RESULTS(kiwix::Filter().lang("eng"),
"Business talks about TED",
"Granblue Fantasy Wiki",
"Islam Stack Exchange",
"Movies & TV Stack Exchange",
"Mythology & Folklore Stack Exchange",
"Ray Charles",
"TED\"talks\" - Business"
"TED talks - Business"
);
EXPECT_FILTER_RESULTS(kiwix::Filter().query("lang:eng"),
"Business talks about TED",
"Granblue Fantasy Wiki",
"Islam Stack Exchange",
"Movies & TV Stack Exchange",
"Mythology & Folklore Stack Exchange",
"Ray Charles",
"TED\"talks\" - Business"
"TED talks - Business"
);
EXPECT_FILTER_RESULTS(kiwix::Filter().query("eng"),
@@ -845,25 +459,6 @@ TEST_F(LibraryTest, filterByLanguage)
);
}
TEST_F(LibraryTest, filterByFlavour)
{
EXPECT_FILTER_RESULTS(kiwix::Filter().flavour("full"),
"Géographie par Wikipédia",
"Tania Louis",
"Wikiquote"
);
EXPECT_FILTER_RESULTS(kiwix::Filter().query("flavour:full"),
"Géographie par Wikipédia",
"Tania Louis",
"Wikiquote"
);
EXPECT_FILTER_RESULTS(kiwix::Filter().query("full"),
/* no results */
);
}
TEST_F(LibraryTest, filterByTags)
{
EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"stackexchange"}),
@@ -963,9 +558,6 @@ TEST_F(LibraryTest, filterByQuery)
EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki"),
"An example ZIM archive", // due to the "wikibooks" tag
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Granblue Fantasy Wiki",
"Géographie par Wikipédia",
"Mathématiques", // due to the "wikipedia" tag
@@ -984,10 +576,6 @@ TEST_F(LibraryTest, filteringByEmptyQueryReturnsAllEntries)
{
EXPECT_FILTER_RESULTS(kiwix::Filter().query(""),
"An example ZIM archive",
"Business talks about TED",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Granblue Fantasy Wiki",
"Géographie par Wikipédia",
@@ -996,7 +584,7 @@ TEST_F(LibraryTest, filteringByEmptyQueryReturnsAllEntries)
"Movies & TV Stack Exchange",
"Mythology & Folklore Stack Exchange",
"Ray Charles",
"TED\"talks\" - Business",
"TED talks - Business",
"Tania Louis",
"Wikiquote"
);
@@ -1005,9 +593,6 @@ TEST_F(LibraryTest, filteringByEmptyQueryReturnsAllEntries)
TEST_F(LibraryTest, filterByCreator)
{
EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Wikipedia"),
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Géographie par Wikipédia",
"Mathématiques",
@@ -1049,9 +634,6 @@ TEST_F(LibraryTest, filterByCreator)
);
EXPECT_FILTER_RESULTS(kiwix::Filter().query("creator:Wikipedia"),
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Géographie par Wikipédia",
"Mathématiques",
@@ -1159,9 +741,6 @@ TEST_F(LibraryTest, filterByMaxSize)
TEST_F(LibraryTest, filterByMultipleCriteria)
{
EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki").creator("Wikipedia"),
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Géographie par Wikipédia",
"Mathématiques", // due to the "wikipedia" tag
@@ -1169,17 +748,11 @@ TEST_F(LibraryTest, filterByMultipleCriteria)
);
EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki").creator("Wikipedia").maxSize(100000000UL),
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Ray Charles"
);
EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki").creator("Wikipedia").maxSize(100000000UL).local(false),
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie"
);
}
@@ -1237,10 +810,6 @@ TEST_F(LibraryTest, removeBooksNotUpdatedSince)
{
EXPECT_FILTER_RESULTS(kiwix::Filter(),
"An example ZIM archive",
"Business talks about TED",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Encyclopédie de la Tunisie",
"Granblue Fantasy Wiki",
"Géographie par Wikipédia",
@@ -1249,7 +818,7 @@ TEST_F(LibraryTest, removeBooksNotUpdatedSince)
"Movies & TV Stack Exchange",
"Mythology & Folklore Stack Exchange",
"Ray Charles",
"TED\"talks\" - Business",
"TED talks - Business",
"Tania Louis",
"Wikiquote"
);
@@ -1263,7 +832,7 @@ TEST_F(LibraryTest, removeBooksNotUpdatedSince)
const uint64_t rev2 = lib->getRevision();
EXPECT_EQ(13u, lib->removeBooksNotUpdatedSince(rev));
EXPECT_EQ(9u, lib->removeBooksNotUpdatedSince(rev));
EXPECT_GT(lib->getRevision(), rev2);

View File

@@ -84,7 +84,7 @@ const ResourceCollection resources200Compressible{
// TODO: implement cache management of i18n resources
//{ STATIC_CONTENT, "/ROOT%23%3F/skin/i18n/test.json?cacheid=unknown" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/languages.js" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/languages.js?cacheid=5be77f5c" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/languages.js?cacheid=9ccd43fd" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/search" },
@@ -146,8 +146,6 @@ const ResourceCollection resources200Uncompressible{
{ STATIC_CONTENT, "/ROOT%23%3F/skin/hash.png?cacheid=f836e872" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/magnet.png" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/magnet.png?cacheid=73b6bddf" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/polyfills.js" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/polyfills.js?cacheid=a0e0343d" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/search-icon.svg" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/search-icon.svg?cacheid=b10ae7ed" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/search_results.css" },
@@ -287,9 +285,8 @@ R"EXPECTEDRESULT( href="/ROOT%23%3F/skin/kiwix.css?cacheid=2158fad9"
<link rel="mask-icon" href="/ROOT%23%3F/skin/favicon/safari-pinned-tab.svg?cacheid=8d487e95" color="#5bbad5">
<link rel="shortcut icon" href="/ROOT%23%3F/skin/favicon/favicon.ico?cacheid=92663314">
<meta name="msapplication-config" content="/ROOT%23%3F/skin/favicon/browserconfig.xml?cacheid=f29a7c4a">
<script type="text/javascript" src="./skin/polyfills.js?cacheid=a0e0343d"></script>
<script type="module" src="/ROOT%23%3F/skin/i18n.js?cacheid=071abc9a" defer></script>
<script type="text/javascript" src="/ROOT%23%3F/skin/languages.js?cacheid=5be77f5c" defer></script>
<script type="text/javascript" src="/ROOT%23%3F/skin/languages.js?cacheid=9ccd43fd" defer></script>
<script src="/ROOT%23%3F/skin/isotope.pkgd.min.js?cacheid=2e48d392" defer></script>
<script src="/ROOT%23%3F/skin/iso6391To3.js?cacheid=ecde2bb3"></script>
<script type="text/javascript" src="/ROOT%23%3F/skin/index.js?cacheid=ce19da2a" defer></script>
@@ -321,9 +318,8 @@ R"EXPECTEDRESULT( <img src="${root}/skin/download
R"EXPECTEDRESULT( <link type="text/css" href="./skin/kiwix.css?cacheid=2158fad9" rel="Stylesheet" />
<link type="text/css" href="./skin/taskbar.css?cacheid=e014a885" rel="Stylesheet" />
<link type="text/css" href="./skin/autoComplete/css/autoComplete.css?cacheid=ef30cd42" rel="Stylesheet" />
<script type="text/javascript" src="./skin/polyfills.js?cacheid=a0e0343d"></script>
<script type="module" src="./skin/i18n.js?cacheid=071abc9a" defer></script>
<script type="text/javascript" src="./skin/languages.js?cacheid=5be77f5c" defer></script>
<script type="text/javascript" src="./skin/languages.js?cacheid=9ccd43fd" defer></script>
<script type="text/javascript" src="./skin/viewer.js?cacheid=5fc4badf" defer></script>
<script type="text/javascript" src="./skin/autoComplete/autoComplete.min.js?cacheid=1191aaaf"></script>
const blankPageUrl = root + "/skin/blank.html?cacheid=6b1fa032";
@@ -1137,27 +1133,17 @@ R"EXPECTEDRESPONSE(const uiLanguages = [
{
"iso_code": "bn",
"self_name": "বাংলা",
"translation_count": 14
},
{
"iso_code": "br",
"self_name": "brezhoneg",
"translation_count": 35
"translation_count": 12
},
{
"iso_code": "cs",
"self_name": "Čeština",
"translation_count": 25
},
{
"iso_code": "dag",
"self_name": "Silimiinsili",
"translation_count": 24
},
{
"iso_code": "de",
"self_name": "Deutsch",
"translation_count": 57
"translation_count": 49
},
{
"iso_code": "en",
@@ -1177,17 +1163,12 @@ R"EXPECTEDRESPONSE(const uiLanguages = [
{
"iso_code": "fr",
"self_name": "Français",
"translation_count": 57
},
{
"iso_code": "ha",
"self_name": "Turanci",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "he",
"self_name": "עברית",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "hi",
@@ -1204,15 +1185,10 @@ R"EXPECTEDRESPONSE(const uiLanguages = [
"self_name": "interlingua",
"translation_count": 49
},
{
"iso_code": "ig",
"self_name": "Bekee",
"translation_count": 57
},
{
"iso_code": "it",
"self_name": "italiano",
"translation_count": 34
"translation_count": 29
},
{
"iso_code": "ja",
@@ -1237,7 +1213,7 @@ R"EXPECTEDRESPONSE(const uiLanguages = [
{
"iso_code": "mk",
"self_name": "македонски",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "ms",
@@ -1262,12 +1238,12 @@ R"EXPECTEDRESPONSE(const uiLanguages = [
{
"iso_code": "pl",
"self_name": "Polski",
"translation_count": 31
"translation_count": 24
},
{
"iso_code": "ru",
"self_name": "русский",
"translation_count": 57
"translation_count": 45
},
{
"iso_code": "sc",
@@ -1287,7 +1263,7 @@ R"EXPECTEDRESPONSE(const uiLanguages = [
{
"iso_code": "sl",
"self_name": "slovenščina",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "sq",
@@ -1297,7 +1273,7 @@ R"EXPECTEDRESPONSE(const uiLanguages = [
{
"iso_code": "sv",
"self_name": "Svenska",
"translation_count": 57
"translation_count": 52
},
{
"iso_code": "te",
@@ -1307,17 +1283,17 @@ R"EXPECTEDRESPONSE(const uiLanguages = [
{
"iso_code": "tr",
"self_name": "Türkçe",
"translation_count": 57
"translation_count": 25
},
{
"iso_code": "zh-hans",
"self_name": "英语",
"translation_count": 54
"translation_count": 16
},
{
"iso_code": "zh-hant",
"self_name": "繁體中文",
"translation_count": 57
"translation_count": 52
}
])EXPECTEDRESPONSE");
}