zim-wiki team mailing list archive
-
zim-wiki team
-
Mailing list archive
-
Message #04921
Re: Searching on the server
On 08/05/20 16:13, Helder Guerreiro wrote:
Is it possible to add a search box to the internal Zim server?
Since it seems there is no easy way to add search to the server I made
the patch attached (against master). There's an example search box in
the "Default_with_index" template.
But this got me thinking, now I'm looking for other features, tag list,
task list, maybe even marking tasks directly from the server or editing
pages... This seems to be a little too much for this server. Using a
framework, Django or whatever, would be easier. Maybe a separated
project from Zim?
/Helder
diff --git a/data/templates/html/Default_with_index.html b/data/templates/html/Default_with_index.html
index e2191c6c..2863e41a 100644
--- a/data/templates/html/Default_with_index.html
+++ b/data/templates/html/Default_with_index.html
@@ -44,6 +44,9 @@
.menu{
float:left; width: 300px;
}
+ .search {
+ float: right;
+ }
hr{clear:both;}
</style>
</head>
@@ -68,6 +71,12 @@
[% ELSE %]
[ <span class='insen'>[% gettext("Next") %]</span> ]
[% END %]
+<div class="search">
+ <form method="GET" action="/search">
+ <input type="search" name="q" value="" maxlength="128" placeholder="Search">
+ <button type="submit" value="Submit">Go</button>
+ </form>
+</div>
</div>
<hr />
diff --git a/zim/www.py b/zim/www.py
index 09bc7eaa..82415689 100644
--- a/zim/www.py
+++ b/zim/www.py
@@ -42,9 +42,55 @@ from zim.export.exporters import createIndexPage
from zim.formats import get_format
+# For searching
+from zim.newfs.mock import MockFile
+from zim.formats import ParseTreeBuilder, \
+ FORMATTEDTEXT, HEADING, BULLETLIST, LISTITEM, LINK, PARAGRAPH
+from zim.search import SearchSelection, Query
+
logger = logging.getLogger('zim.www')
+def search(notebook, query):
+ if query and not query.isspace():
+ logger.info('Searching for: %s', query)
+ query = Query(query)
+ else:
+ return []
+
+ selection = SearchSelection(notebook)
+ selection.search(query)
+ return [path for path in selection]
+ # return [notebook.get_page(path) for path in selection]
+
+
+def createSearchResultPage(notebook, query):
+ pagelist = search(notebook, query)
+ builder = ParseTreeBuilder()
+
+ builder.start(FORMATTEDTEXT)
+ builder.append(HEADING, {'level': 1}, 'Search Results')
+
+ if pagelist:
+ builder.start(BULLETLIST)
+ for page in sorted(pagelist, key=lambda p: p.name):
+ builder.start(LISTITEM)
+ builder.append(LINK,
+ {'type': 'page', 'href': page.name}, page.name)
+ builder.end(LISTITEM)
+ builder.end(BULLETLIST)
+ else:
+ builder.append(PARAGRAPH, {}, 'No results')
+
+ builder.end(FORMATTEDTEXT)
+
+ tree = builder.get_parsetree()
+
+ indexpage = Page(Path(':'), False, MockFile('/search'), None)
+ indexpage.set_parsetree(tree)
+ return indexpage
+
+
class WWWError(Error):
'''Error with http error code'''
@@ -182,6 +228,15 @@ class WWWInterface(object):
if path == '/':
headers.add_header('Content-Type', 'text/html', charset='utf-8')
content = self.render_index()
+ elif path == '/search':
+ qs = urllib.parse.parse_qs(environ.get('QUERY_STRING', ''))
+ if 'q' not in qs:
+ qs['q'] = ['']
+ query = qs['q'][0]
+ if len(query) > 128:
+ raise WebPathNotValidError()
+ headers.add_header('Content-Type', 'text/html', charset='utf-8')
+ content = self.render_search(query)
elif path.startswith('/+docs/'):
dir = self.notebook.document_root
if not dir:
@@ -275,6 +330,14 @@ class WWWInterface(object):
page = createIndexPage(self.notebook, path, namespace)
return self.render_page(page)
+ def render_search(self, query):
+ '''Search Zim notebook
+ @param query: query string
+ @returns: list of matching pages
+ '''
+ page = createSearchResultPage(self.notebook, query)
+ return self.render_page(page)
+
def render_page(self, page):
'''Render a single page from the notebook
@param page: a L{Page} object
Follow ups
References