19 Commits
0.5.0 ... 0.6.0

Author SHA1 Message Date
Matthieu Gautier
0efbb2461d Merge pull request #203 from kiwix/version_0.6.0
New version 0.6.0
2018-06-15 19:29:59 +02:00
Matthieu Gautier
b251e18af1 New version 0.6.0 2018-06-15 18:34:09 +02:00
Matthieu Gautier
5c040d3ee6 Merge pull request #200 from kiwix/improved_kiwix_serve_taskbar
Improved taskbar #160 #59
2018-06-15 18:32:06 +02:00
Matthieu Gautier
bb1afb5120 Also set magnify glass in the global taskbar. 2018-06-15 18:28:04 +02:00
Kelson
8fccbc4c99 Improved taskbar #160 #59 2018-06-15 18:08:48 +02:00
Matthieu Gautier
d0dc9ac81b Merge pull request #202 from kiwix/gcc4.8
[TRAVIS] Compile using default gcc (4.8)
2018-06-15 18:03:49 +02:00
Matthieu Gautier
f9edd75f6c [TRAVIS] Compile using default gcc (4.8) 2018-06-15 08:50:13 +02:00
Matthieu Gautier
9571148375 Merge pull request #201 from kiwix/compile_gcc4.8
Compile gcc4.8
2018-06-14 18:20:19 +02:00
Matthieu Gautier
282b85c341 Do not use std::sto[fi] or std::to_string. 2018-06-14 18:01:15 +02:00
Matthieu Gautier
4c3acd06de Add missing include.
Needed by printf.
2018-06-14 17:35:40 +02:00
Kelson
4cd9d78d21 Merge pull request #199 from kiwix/kiwix_server_404
return 404 for missing resources
2018-06-09 16:21:17 +02:00
Philip Munaawa
efd4a1434e return 404 for missing resources 2018-06-09 15:29:40 +02:00
Matthieu Gautier
dfc601dacf Merge pull request #196 from kiwix/no_install
Remove kiwix-install tool.
2018-05-21 12:18:30 +02:00
Matthieu Gautier
7c254544ca Remove kiwix-install tool.
Fix #189
2018-05-21 12:09:13 +02:00
Kelson
b22ee94f10 Merge pull request #195 from kiwix/proper_exit_code
Proper exit code
2018-05-20 08:34:41 +02:00
Kelson
3766c4882b Proper exit code #194 2018-05-19 20:59:18 +02:00
Matthieu Gautier
26da54f9c3 Merge pull request #187 from swiftugandan/kiwix_serve-fix-global_page_static_resources-1
fix static resources on home page when using --nosearchbar
2018-04-24 14:16:12 +02:00
Philip Munaawa
4433421c48 also replace __CONTENT_ESCAPED__ for --nosearchbar 2018-04-24 13:06:46 +01:00
Philip Munaawa
c00f0be7ef fix static resources on home page when using --nosearchbar 2018-04-24 11:29:41 +01:00
17 changed files with 103 additions and 372 deletions

View File

@@ -2,22 +2,11 @@ language: cpp
dist: trusty
sudo: required
cache: ccache
before_install:
- eval "${MATRIX_EVAL}"
- ${CXX} --version
install: travis/install_deps.sh
script: travis/compile.sh
env:
global:
- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
matrix:
- PLATFORM="native_static"
- PLATFORM="native_dyn"
- PLATFORM="win32_static"
- PLATFORM="win32_dyn"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-5

View File

@@ -1,3 +1,22 @@
kiwix-tools 0.6.0
=================
* remove kiwix-install tool.
kiwix-serve
-----------
* Improved taskbar #160
* Fix global page when using the option `--nosearchbar`
* Return 404 for missing resources
* Fix compilation for gcc 4.8.
kiwix-manage
------------
* Returns proper exit code (not always 0)
kiwix-tools 0.5.0
=================
@@ -5,7 +24,7 @@ kiwix-tools 0.5.0
* Compile without warning.
kiwix-server
kiwix-serve
------------
* Serve metadata information using the "/meta" url.

View File

@@ -1,5 +1,5 @@
project('kiwix-tools', 'cpp',
version : '0.5.0',
version : '0.6.0',
license : 'GPL',
default_options: ['c_std=c11', 'cpp_std=c++11', 'werror=true'])
@@ -11,7 +11,7 @@ if static_linkage
endif
thread_dep = dependency('threads')
kiwixlib_dep = dependency('kiwix', version:'>=2.0.0', static:static_linkage)
kiwixlib_dep = dependency('kiwix', version:'>=2.0.1', static:static_linkage)
microhttpd_dep = dependency('libmicrohttpd', static:static_linkage)
z_dep = dependency('zlib', static:static_linkage)

View File

@@ -1,173 +0,0 @@
/*
* Copyright 2011-2014 Emmanuel Engelhart <kelson@kiwix.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <getopt.h>
#include <kiwix/common/pathTools.h>
#include <kiwix/manager.h>
#include <kiwix/reader.h>
enum supportedAction { NONE, ADDCONTENT };
void usage()
{
cout << "Usage: kiwix-install [--verbose] addcontent ZIM_PATH KIWIX_PATH"
<< endl;
exit(1);
}
int main(int argc, char** argv)
{
/* Init the variables */
const char* contentPath = NULL;
const char* kiwixPath = NULL;
supportedAction action = NONE;
bool verboseFlag = false;
int option_index = 0;
int c = 0;
/* Argument parsing */
while (42) {
static struct option long_options[]
= {{"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}};
if (c != -1) {
c = getopt_long(argc, argv, "vi", long_options, &option_index);
switch (c) {
case 'v':
verboseFlag = true;
break;
}
} else {
if (optind < argc) {
if (action == NONE) {
string actionString = argv[optind++];
if (actionString == "addcontent" || actionString == "ADDCONTENT") {
action = ADDCONTENT;
}
} else if (contentPath == NULL) {
contentPath = argv[optind++];
} else if (kiwixPath == NULL) {
kiwixPath = argv[optind++];
} else {
usage();
}
} else {
break;
}
}
}
/* Check if we have enough arguments */
if (contentPath == NULL || kiwixPath == NULL) {
usage();
}
/* Make the action */
if (action == ADDCONTENT) {
/* Check if the content path exists and is readable */
if (verboseFlag) {
std::cout << "Check if the ZIM file exists..." << std::endl;
}
if (!fileExists(contentPath)) {
cerr << "The content path '" << contentPath
<< "' does not exist or is not readable." << endl;
exit(1);
}
/* Check if this is a ZIM file */
try {
if (verboseFlag) {
std::cout << "Check if the ZIM file is valid..." << std::endl;
}
kiwix::Reader* reader = new kiwix::Reader(contentPath);
delete reader;
} catch (exception& e) {
cerr << "The content available at '" << contentPath
<< "' is not a ZIM file." << endl;
exit(1);
}
string contentFilename = getLastPathElement(contentPath);
/* Check if kiwixPath/kiwix/kiwix.exe exists */
if (verboseFlag) {
std::cout << "Check if the Kiwix path is valid..." << std::endl;
}
string kiwixBinaryPath = computeAbsolutePath(kiwixPath, "kiwix/kiwix.exe");
if (!fileExists(kiwixBinaryPath)) {
kiwixBinaryPath = computeAbsolutePath(kiwixPath, "kiwix/kiwix");
if (!fileExists(kiwixBinaryPath)) {
cerr << "Unable to find the Kiwix binary at '" << kiwixBinaryPath
<< "[.exe]'." << endl;
exit(1);
}
}
/* Check if the directory "data" structure exists */
if (verboseFlag) {
std::cout << "Check the target data directory structure..." << std::endl;
}
string dataPath = computeAbsolutePath(kiwixPath, "data/");
if (!fileExists(dataPath)) {
makeDirectory(dataPath);
}
/* Check if the directory "data/content" structure exists */
string dataContentPath = computeAbsolutePath(kiwixPath, "data/content/");
if (!fileExists(dataContentPath)) {
makeDirectory(dataContentPath);
}
/* Check if the directory "data/library" structure exists */
string dataLibraryPath = computeAbsolutePath(kiwixPath, "data/library/");
if (!fileExists(dataLibraryPath)) {
makeDirectory(dataLibraryPath);
}
/* Copy the file to the data/content directory */
if (verboseFlag) {
std::cout << "Copy ZIM file to the target directory..." << std::endl;
}
string newContentPath
= computeAbsolutePath(dataContentPath, contentFilename);
if (!fileExists(newContentPath)
|| getFileSize(contentPath) != getFileSize(newContentPath)) {
copyFile(contentPath, newContentPath);
}
/* Add the file to the library.xml */
if (verboseFlag) {
std::cout << "Create the library XML file..." << std::endl;
}
kiwix::Manager libraryManager;
string libraryPath
= computeAbsolutePath(dataLibraryPath, contentFilename + ".xml");
string bookId = libraryManager.addBookFromPathAndGetId(
newContentPath, "../content/" + contentFilename, "", false);
if (!bookId.empty()) {
libraryManager.setCurrentBookId(bookId);
libraryManager.writeFile(libraryPath);
} else {
cerr << "Unable to build or save library file '" << libraryPath << "'"
<< endl;
}
}
exit(0);
}

View File

@@ -1,4 +0,0 @@
executable('kiwix-install', ['kiwix-install.cpp'],
dependencies:all_deps,
install:true,
install_rpath: join_paths(get_option('prefix'), get_option('libdir')))

View File

@@ -1,40 +0,0 @@
.TH KIWIX 1 "21 May 2012"
.SH NAME
kiwix\-install \- Installeur de fichiers ZIM
.SH SYNOPSIS
.IX Header SYNOPSIS
kiwix\-install [\-\-verbose|-v] [\-\-backend|\-b=xapian|clucene] [\-\-buildIndex|\-i] addcontent ZIM_PATH KIWIX_PATH
.SH DESCRIPTION
.PP
Créé une arborescence contenant Kiwix et des données pour redistribution.
.br
Si demandé, créé aussi un index plein texte.
.TP
\fB\-\-verbose\fR
Active le mode verbeux de la sortie.
.TP
\fB\-\-backend=xapian|clucene\fR
Séléctionne un moteur d'indexation.
.TP
\fB\-\-buildIndex\fR
Créer un index plein texte pour le fichier ZIM.
.TP
\fBZIM_PATH\fR
Chemin du fichier de contenu ZIM.
.TP
\fBKIWIX_PATH\fR
Chemin d'accès dossier kiwix/.
.br
Le chemin doit contenit le binaire statique de Kiwix.
.SH SEE ALSO
kiwix(1) kiwix\-serve(1) kiwix\-manage(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -1,52 +0,0 @@
.TH KIWIX 1 "12 juin 2012"
.SH NAME
Kiwix \- Lecteur hors-ligne de fichiers ZIM
.SH SYNOPSIS
.B kiwix [-jsconsole] [-articleByUrl] [-articleByTitle] [FILE]
.SH DESCRIPTION
.PP
Kiwix est un lecteur de content au format ZIM.
.br
Les fichiers ZIM sont des archives de contenus compressés (généralement HTML)
.br
Les fichiers ZIM les plus populaires sont ceux de Wikipédia et Wikileaks.
.TP
\fB\-jsconsole\fR
Active la console de debogage JavaScript de Xulrunner.
\fB\-articleByUrl url\fR
Ouvre un article en particulier en renseignant son url. FILE doit être donné.
.TP
\fB\-articleByTitle title\fR
Ouvre un article en particulier en renseignant son titre. FILE doit être donné.
.PP
Fonctionnalités:
* Lecteur de ZIM natif
* Moteur de recherche plein texte
* Signets et Notes
* Serveur Web pour diffuser les fichiers ZIM sur le réseau
* Export HTML et PDF
* Traduits dans de nombreuses langues
* Suggestions de recherche
* Indexation des fichiers ZIM
* Onglets de navigation
* Bibliothèque de contenus intégrée
.SH SEE ALSO
kiwix\-install(1) kiwix\-serve(1)
.br
kiwix\-manage(1)
.SH TROUBLESHOOTING
Aller à http://reportabug.kiwix.org pour plus de détails sur comment rapporter un bogue.
.SH AUTHORS
Emmanuel Engelhart <kelson@kiwix.org>
Guillaume Duhamel <gduhamel@linterweb.com>
Fabien Coullon <fcoulon@linterweb.com>
Renaud Gaudin <rgaudin@gmail.com>
Wilfredo Rodriguez <wilfredor@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -1,4 +1,3 @@
install_man('kiwix-install.1',
'kiwix-manage.1',
install_man('kiwix-manage.1',
'kiwix-serve.1',
install_dir:get_option('mandir')+'/fr/man1')
install_dir:get_option('mandir')+'/fr/man1')

View File

@@ -1,40 +0,0 @@
.TH KIWIX 1 "21 May 2012"
.SH NAME
kiwix\-install \- Kiwix ZIM Installer
.SH SYNOPSIS
.IX Header SYNOPSIS
kiwix\-install [\-\-verbose|-v] [\-\-backend|\-b=xapian|clucene] [\-\-buildIndex|\-i] addcontent ZIM_PATH KIWIX_PATH
.SH DESCRIPTION
.PP
Creates a standalone Kiwix + data hierarchy.
.br
If specified, also creates a full\-text index.
.TP
\fB\-\-verbose\fR
Enable verbose output.
.TP
\fB\-\-backend=xapian|clucene\fR
Select an indexing backend.
.TP
\fB\-\-buildIndex\fR
Build an index for the ZIM file.
.TP
\fBZIM_PATH\fR
ZIM content file path.
.TP
\fBKIWIX_PATH\fR
Path to the kiwix/ folder.
.br
The path must contain the kiwix standalone binary.
.SH SEE ALSO
kiwix(1) kiwix\-serve(1) kiwix\-manage(1)
.SH AUTHOR
Emmanuel Engelhart <kelson@kiwix.org>
.br
Vasudev Kamath <kamathvasudev@gmail.com> (Manual)

View File

@@ -1,4 +1,3 @@
install_man('kiwix-install.1',
'kiwix-manage.1',
install_man('kiwix-manage.1',
'kiwix-serve.1')
subdir('fr')

View File

@@ -68,13 +68,14 @@ void usage()
}
void handle_show(kiwix::Manager* libraryManager, const std::string& libraryPath,
bool handle_show(kiwix::Manager* libraryManager, const std::string& libraryPath,
int argc, char* argv[])
{
show(libraryManager->cloneLibrary());
return(0);
}
void handle_add(kiwix::Manager* libraryManager, const std::string& libraryPath,
bool handle_add(kiwix::Manager* libraryManager, const std::string& libraryPath,
int argc, char* argv[])
{
string zimPath;
@@ -86,6 +87,7 @@ void handle_add(kiwix::Manager* libraryManager, const std::string& libraryPath,
bool setCurrent = false;
int option_index = 0;
int c = 0;
bool resultCode = 0;
if (argc > 3) {
zimPath = argv[3];
@@ -153,17 +155,22 @@ void handle_add(kiwix::Manager* libraryManager, const std::string& libraryPath,
} else {
cerr << "Unable to build or save library file '" << libraryPath << "'"
<< endl;
resultCode = 1;
}
} else {
std::cerr << "Invalid zim file path" << std::endl;
resultCode = 1;
}
return(resultCode);
}
void handle_remove(kiwix::Manager* libraryManager, const std::string& libraryPath,
bool handle_remove(kiwix::Manager* libraryManager, const std::string& libraryPath,
int argc, char* argv[])
{
unsigned int bookIndex = 0;
const unsigned int totalBookCount = libraryManager->getBookCount(true, true);
bool exitCode = 0;
if (argc > 3) {
bookIndex = atoi(argv[3]);
@@ -176,12 +183,16 @@ void handle_remove(kiwix::Manager* libraryManager, const std::string& libraryPat
std::cerr
<< "Invalid book index number. Please give a number between 1 and "
<< totalBookCount << std::endl;
exitCode = 1;
} else {
std::cerr
<< "Invalid book index number. Library is empty, no book to delete."
<< std::endl;
exitCode = 1;
}
}
return(exitCode);
}
int main(int argc, char** argv)
@@ -216,12 +227,13 @@ int main(int argc, char** argv)
libraryManager.readFile(libraryPath, false);
/* SHOW */
bool exitCode = 0;
if (action == SHOW) {
handle_show(&libraryManager, libraryPath, argc, argv);
exitCode = handle_show(&libraryManager, libraryPath, argc, argv);
} else if (action == ADD) {
handle_add(&libraryManager, libraryPath, argc, argv);
exitCode = handle_add(&libraryManager, libraryPath, argc, argv);
} else if (action == REMOVE) {
handle_remove(&libraryManager, libraryPath, argc, argv);
exitCode = handle_remove(&libraryManager, libraryPath, argc, argv);
}
/* Rewrite the library file */
@@ -229,5 +241,5 @@ int main(int argc, char** argv)
libraryManager.writeFile(libraryPath);
}
exit(0);
exit(exitCode);
}

View File

@@ -1,5 +1,4 @@
subdir('installer')
subdir('manager')
subdir('reader')
subdir('searcher')

View File

@@ -105,6 +105,27 @@ static pthread_mutex_t searchLock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t compressorLock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t regexLock = PTHREAD_MUTEX_INITIALIZER;
template<typename T>
inline std::string _tostring(const T& value)
{
std::ostringstream stream;
stream << value;
return stream.str();
}
std::pair<kiwix::Reader*, kiwix::Searcher*>
get_from_humanReadableBookId(const std::string& humanReadableBookId) {
kiwix::Searcher* searcher
= searchers.find(humanReadableBookId) != searchers.end()
? searchers.find(humanReadableBookId)->second
: globalSearcher;
kiwix::Reader* reader = readers.find(humanReadableBookId) != readers.end()
? readers.find(humanReadableBookId)->second
: NULL;
return std::pair<kiwix::Reader*, kiwix::Searcher*>(reader, searcher);
}
/* Try to get the mimeType from the file extension */
static std::string getMimeTypeForFile(const std::string& filename)
{
@@ -138,7 +159,10 @@ static bool startswith(const std::string& base, const std::string& start)
void introduceTaskbar(string& content, const string& humanReadableBookId)
{
string zimTitle;
pthread_mutex_lock(&regexLock);
if (!noSearchBarFlag) {
content = appendToFirstOccurence(
content,
@@ -160,10 +184,18 @@ void introduceTaskbar(string& content, const string& humanReadableBookId)
RESOURCE::taskbar_html_part,
humanReadableBookId,
"__CONTENT__"));
auto reader = get_from_humanReadableBookId(humanReadableBookId).first;
if (reader != nullptr) {
zimTitle = reader->getTitle();
}
}
content = replaceRegex(content, rootLocation, "__ROOT_LOCATION__");
content = replaceRegex(content, replaceRegex(humanReadableBookId, "%26", "&"), "__CONTENT_ESCAPED__");
}
content = replaceRegex(content, rootLocation, "__ROOT_LOCATION__");
content = replaceRegex(content, replaceRegex(zimTitle, "%26", "&"), "__ZIM_TITLE__");
content = replaceRegex(content, replaceRegex(humanReadableBookId, "%26", "&"), "__CONTENT_ESCAPED__");
pthread_mutex_unlock(&regexLock);
}
@@ -352,7 +384,7 @@ static struct MHD_Response* build_callback_response_from_entry(
MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_RANGE, oss.str().c_str());
MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_LENGTH,
std::to_string(range_len).c_str());
_tostring(range_len).c_str());
/* Specify the mime type */
MHD_add_response_header(
@@ -369,18 +401,6 @@ static struct MHD_Response* build_callback_response_from_entry(
return response;
}
std::pair<kiwix::Reader*, kiwix::Searcher*>
get_from_humanReadableBookId(const std::string& humanReadableBookId) {
kiwix::Searcher* searcher
= searchers.find(humanReadableBookId) != searchers.end()
? searchers.find(humanReadableBookId)->second
: globalSearcher;
kiwix::Reader* reader = readers.find(humanReadableBookId) != readers.end()
? readers.find(humanReadableBookId)->second
: NULL;
return std::pair<kiwix::Reader*, kiwix::Searcher*>(reader, searcher);
}
static struct MHD_Response* handle_meta(RequestContext* request)
{
std::string humanReadableBookId;
@@ -443,7 +463,7 @@ static struct MHD_Response* handle_suggest(RequestContext* request)
humanReadableBookId = request->get_argument("content");
term = request->get_argument("term");
} catch (const std::out_of_range&) {
return build_homepage(request);
return build_404(request, "");
}
if (isVerbose.load()) {
@@ -628,12 +648,12 @@ static struct MHD_Response* handle_random(RequestContext* request)
try {
humanReadableBookId = request->get_argument("content");
} catch (const std::out_of_range&) {
return build_homepage(request);
return build_404(request, "");
}
auto reader = get_from_humanReadableBookId(humanReadableBookId).first;
if (reader == nullptr) {
return build_homepage(request);
return build_404(request, "");
}
try {
@@ -728,7 +748,11 @@ static struct MHD_Response* handle_content(RequestContext* request)
auto reader = get_from_humanReadableBookId(humanReadableBookId).first;
if (reader == nullptr) {
return build_homepage(request);
if (humanReadableBookId.size() == 0){
return build_homepage(request);
} else {
return build_404(request, "");
}
}
auto urlStr = request->get_url().substr(humanReadableBookId.size()+1);
@@ -842,7 +866,7 @@ static int accessHandlerCallback(void* cls,
request.httpResponseCode = request.has_range() ? MHD_HTTP_PARTIAL_CONTENT : MHD_HTTP_OK;
if (! request.is_valid_url()) {
response = build_homepage(&request);
response = build_404(&request, "");
} else {
if (startswith(request.get_url(), "/skin/")) {
response = handle_skin(&request);

View File

@@ -23,6 +23,7 @@
#include <string.h>
#include <stdexcept>
#include <sstream>
#include <cstdio>
RequestContext::RequestContext(struct MHD_Connection* connection,
std::string rootLocation,
@@ -196,16 +197,6 @@ std::string RequestContext::get_argument(const std::string& name) {
return arguments.at(name);
}
template<>
unsigned int RequestContext::get_argument(const std::string& name) {
return std::stoi(arguments.at(name).c_str());
}
template<>
float RequestContext::get_argument(const std::string& name) {
return std::stof(arguments.at(name).c_str());
}
std::string RequestContext::get_header(const std::string& name) {
return headers.at(name);
}

View File

@@ -62,7 +62,13 @@ class RequestContext {
std::string get_header(const std::string& name);
template<typename T=std::string>
T get_argument(const std::string& name);
T get_argument(const std::string& name) {
std::istringstream stream(arguments.at(name));
T v;
stream >> v;
return v;
}
RequestMethod get_method();
std::string get_url();
@@ -96,5 +102,7 @@ class RequestContext {
static int fill_argument(void *, enum MHD_ValueKind, const char*, const char*);
};
template<> std::string RequestContext::get_argument(const std::string& name);
#endif //REQUEST_CONTEXT_H

View File

@@ -4,7 +4,7 @@
<div class="kiwix_searchform">
<form class="kiwixsearch" method="GET" action="__ROOT_LOCATION__/search" id="kiwixsearchform">
<input autocomplete="off" class="ui-autocomplete-input" id="kiwixsearchbox" name="pattern" type="text">
<input type="submit" value="Search">
<input type="submit" value="&#x1f50d;">
</form>
</div>
</div>

View File

@@ -2,15 +2,15 @@
<span id="kiwixtoolbar" class="ui-widget-header">
<div class="kiwix_centered">
<div class="kiwix_button_wrapper">
<a id="kiwix_serve_taskbar_library_button" href="__ROOT_LOCATION__/"><button>Library</button></a>
<a id="kiwix_serve_taskbar_home_button" href="__ROOT_LOCATION__/__CONTENT__/"><button>Home</button></a>
<a id="kiwix_serve_taskbar_random_button" href="__ROOT_LOCATION__/random?content=__CONTENT_ESCAPED__"><button>Random</button></a>
<a id="kiwix_serve_taskbar_library_button" href="__ROOT_LOCATION__/"><button>&#x1f3e0;</button></a>
<a id="kiwix_serve_taskbar_home_button" href="__ROOT_LOCATION__/__CONTENT__/"><button>__ZIM_TITLE__</button></a>
<a id="kiwix_serve_taskbar_random_button" href="__ROOT_LOCATION__/random?content=__CONTENT_ESCAPED__"><button>&#x1F3B2;</button></a>
</div>
<div class="kiwix_searchform">
<form class="kiwixsearch" method="GET" action="__ROOT_LOCATION__/search" id="kiwixsearchform">
<input type="hidden" name="content" value="__CONTENT__" />
<input autocomplete="off" class="ui-autocomplete-input" id="kiwixsearchbox" name="pattern" type="text">
<input type="submit" value="Search">
<input type="submit" value="&#x1f50d;">
</form>
</div>
</div>