Commit 8957cbb7 authored by gouarin's avatar gouarin
Browse files

add search page

parent 7473e73b
Pipeline #3630 passed with stages
in 1 minute and 10 seconds
......@@ -43,13 +43,13 @@ DEFAULT_PAGINATION = False
#RELATIVE_URLS = True
THEME = 'themes/calcul/'
DIRECT_TEMPLATES = ['index', 'categories', 'archives'] # Defined so that the authors page is not generated
DIRECT_TEMPLATES = ['index', 'categories', 'archives', 'tags', 'search'] # Defined so that the authors page is not generated
MARKUP = ('md', 'ipynb')
PLUGIN_PATHS = ['./plugins']
PLUGINS = ['ipynb.markup', 'pelican_dynamic', 'rst_directives', 'extract_toc', 'calcul_reader', 'calcul_filters',
'rst_include', 'sitemap', 'pelican-deadlinks']
'rst_include', 'sitemap', 'pelican-deadlinks', 'tipue_search']
# Useful for 'rst_include' plugin (elative to content directory)
RST_GLOBAL_INCLUDES = ['../plugins/rst_include/include.rst']
......
Tipue Search
============
A Pelican plugin to serialize generated HTML to a JS variable that can be used by jQuery plugin - Tipue Search.
Copyright (c) Talha Mansoor
Author | Talha Mansoor
----------------|-----
Author Email | talha131@gmail.com
Author Homepage | http://onCrashReboot.com
Github Account | https://github.com/talha131
Why do you need it?
===================
Static sites do not offer search feature out of the box. [Tipue Search](http://www.tipue.com/search/)
is a jQuery plugin that search the static site without using any third party service, like DuckDuckGo or Google.
Tipue search requires the textual content of site in a JS variable.
Requirements
============
Tipue Search requires BeautifulSoup.
```bash
pip install beautifulsoup4
```
How Tipue Search works
=========================
Tipue Search serializes the generated HTML into JSON and saves it into a JS variable. Format of JSON is as follows
```javascript
var tipuesearch = {
"pages": [
{
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero.",
"tags": "Example Category",
"url" : "http://oncrashreboot.com/plugin-example.html",
"title": "Everything you want to know about Lorem Ipsum"
},
{
"text": "Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh.",
"tags": "Example Category",
"url" : "http://oncrashreboot.com/plugin-example-2.html",
"title": "Review of the book Lorem Ipsum"
}
]
};
```
JS variable is written to file `tipuesearch_content.js` which is created in the root of `output` directory.
How to use
==========
Your theme needs to have Tipue Search properly configured in it. [Official documentation](http://www.tipue.com/search/help/) has the required details.
In addition to the instructions from Tipue, the following has to be added in `pelicanconf.py`.
```python
PLUGIN_PATH = 'plugins'
PLUGINS = ['tipue_search']
DIRECT_TEMPLATES = ['index', 'tags', 'categories', 'authors', 'archives', 'search']
```
Furthermore, the generated JavaScript variable has to be sourced in the relevant html pages.
```html
<script src="{{ SITEURL }}tipuesearch_content.js"></script>
```
Pelican [Elegant Theme](https://github.com/talha131/pelican-elegant) and [Plumage theme](https://github.com/kdeldycke/plumage) have Tipue Search configured. You can view their code to understand the configuration.
Backward Compatibility
======================
This plugin requires Tipue Search Version 7.0 or higher.
Tipue used to expect content in a json file. Around Version 7.0, Tipue maintainers made a switch to JavaScript variable. tipue_search plugin was updated to reflect this change in commit `4a5f171fc`. Latest version of tipue_search plugin will not work with older versions of Tipue Search.
If you are using older Tipue Search, prior to 7.0 release, then you will find old version of tipue_search plugin in commit `2dcdca8c8`.
from .tipue_search import *
# -*- coding: utf-8 -*-
"""
Tipue Search
============
A Pelican plugin to serialize generated HTML to JSON
that can be used by jQuery plugin - Tipue Search.
Copyright (c) Talha Mansoor
"""
from __future__ import unicode_literals
import os.path
import json
from bs4 import BeautifulSoup
from codecs import open
try:
from urlparse import urljoin
except ImportError:
from urllib.parse import urljoin
from pelican import signals
class Tipue_Search_JSON_Generator(object):
def __init__(self, context, settings, path, theme, output_path, *null):
self.output_path = output_path
self.context = context
self.siteurl = settings.get('SITEURL')
self.relative_urls = settings.get('RELATIVE_URLS')
self.tpages = settings.get('TEMPLATE_PAGES')
self.output_path = output_path
self.json_nodes = []
def create_json_node(self, page):
if getattr(page, 'status', 'published') != 'published':
return
soup_title = BeautifulSoup(page.title.replace('&nbsp;', ' '), 'html.parser')
page_title = soup_title.get_text(' ', strip=True).replace('“', '"').replace('”', '"').replace('’', "'").replace('^', '&#94;')
soup_text = BeautifulSoup(page.content, 'html.parser')
page_text = soup_text.get_text(' ', strip=True).replace('“', '"').replace('”', '"').replace('’', "'").replace('¶', ' ').replace('^', '&#94;')
page_text = ' '.join(page_text.split())
page_category = page.category.name if getattr(page, 'category', 'None') != 'None' else ''
page_url = '.'
if page.url:
page_url = page.url if self.relative_urls else (self.siteurl + '/' + page.url)
node = {'title': page_title,
'text': page_text,
'tags': page_category,
'url': page_url,
'loc': page_url} # changed from 'url' following http://blog.siphos.be/2015/08/updates-on-my-pelican-adventure/ (an update to Pelican made it not work, because the update (e.g., in the theme folder, static/tipuesearch/tipuesearch.js is looking for the 'loc' attribute.
self.json_nodes.append(node)
def create_tpage_node(self, srclink):
srcfile = open(os.path.join(self.output_path, self.tpages[srclink]), encoding='utf-8')
soup = BeautifulSoup(srcfile, 'html.parser')
page_title = soup.title.string if soup.title is not None else ''
page_text = soup.get_text()
# Should set default category?
page_category = ''
page_url = urljoin(self.siteurl, self.tpages[srclink])
node = {'title': page_title,
'text': page_text,
'tags': page_category,
'url': page_url}
self.json_nodes.append(node)
def generate_output(self, writer):
path = os.path.join(self.output_path, 'tipuesearch_content.js')
pages = self.context['pages'] + self.context['articles']
for article in self.context['articles']:
pages += article.translations
for srclink in self.tpages:
self.create_tpage_node(srclink)
for page in pages:
self.create_json_node(page)
root_node = {'pages': self.json_nodes}
root_node_js = 'var tipuesearch = ' + json.dumps(root_node, separators=(',', ':'), ensure_ascii=False) + ';'
with open(path, 'w', encoding='utf-8') as fd:
fd.write(root_node_js)
def get_generators(generators):
return Tipue_Search_JSON_Generator
def register():
signals.get_generators.connect(get_generators)
......@@ -81,10 +81,10 @@ a:hover{
}
.nav-link {
font-family: Roboto;
color: #000000;
font-size: 14px;
font-weight: 400;
font-family: Roboto;
color: #000000;
font-size: 14px;
font-weight: 400;
}
.inscription-button {
......@@ -142,10 +142,10 @@ a:hover{
}
#follow.nav-link {
font-family: Roboto;
color: #000000;
font-size: 12.5px;
font-weight: 700;
font-family: Roboto;
color: #000000;
font-size: 12.5px;
font-weight: 700;
}
#header {
......@@ -163,17 +163,17 @@ a:hover{
}
.header-content h1 {
font-family: PlayfairDisplay;
color: #000000;
font-size: 44px;
font-weight: 400;
font-family: PlayfairDisplay;
color: #000000;
font-size: 44px;
font-weight: 400;
}
.header-content h4 {
font-family: PlayfairDisplay;
color: #000000;
font-size: 14px;
font-weight: 400;
font-family: PlayfairDisplay;
color: #000000;
font-size: 14px;
font-weight: 400;
}
.header-content span {
......@@ -224,37 +224,37 @@ a:hover{
.circle-journee{
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #ff6868;
height: 8px;
border-radius: 50%;
background-color: #ff6868;
}
.circle-formation{
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #ffb400;
height: 8px;
border-radius: 50%;
background-color: #ffb400;
}
.circle-job{
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #78c8ce;
height: 8px;
border-radius: 50%;
background-color: #78c8ce;
}
#couleur1{
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #8ab200;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #8ab200;
}
#couleur2{
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #3fc0dc;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #3fc0dc;
}
......@@ -265,20 +265,20 @@ a:hover{
}
#header-list {
height: 28px;
height: 28px;
font-family: Roboto;
color: #000000;
font-size: 3.19px;
font-weight: 700;
transform: scale(4.393,4.393);
color: #000000;
font-size: 3.19px;
font-weight: 700;
transform: scale(4.393,4.393);
}
#header-text.p{
font-family: Roboto;
color: #000000;
font-size: 18px;
font-weight: 300;
transform: scale(1.091,1.091);
font-family: Roboto;
color: #000000;
font-size: 18px;
font-weight: 300;
transform: scale(1.091,1.091);
}
.header-toc {
......@@ -305,17 +305,17 @@ a:hover{
}
#detail{
height: 42px;
border-width: 1px;
border-color: #000000;
border-style: solid;
background-color: #3fc3e3;
font-family: Roboto;
color: #000000;
font-size: 12.83px;
font-weight: 700;
transform: scale(1.091,1.091);
height: 42px;
border-width: 1px;
border-color: #000000;
border-style: solid;
background-color: #3fc3e3;
font-family: Roboto;
color: #000000;
font-size: 12.83px;
font-weight: 700;
transform: scale(1.091,1.091);
}
.border-dark {
......@@ -350,18 +350,18 @@ a:hover{
line-height: 15px;
}
.event .date {
font-family: Roboto;
color: #000000;
font-size: 14px;
font-weight: 700;
#event .date {
font-family: Roboto;
color: #000000;
font-size: 14px;
font-weight: 700;
}
.event .category {
font-family: Roboto;
color: rgba(0, 0, 0, 0.76);
font-size: 10px;
font-weight: 700;
#event .category {
font-family: Roboto;
color: rgba(0, 0, 0, 0.76);
font-size: 10px;
font-weight: 700;
}
.event .card-footer {
......@@ -385,18 +385,18 @@ a:hover{
}
#toutagenda{
color: #000000;
font-family: 'PlayfairDisplay';
font-size: 34px;
font-weight: 400;
color: #000000;
font-family: 'PlayfairDisplay';
font-size: 34px;
font-weight: 400;
}
#evtpasse{
color: #000000;
font-family: 'Roboto';
font-size: 14px;
font-weight: 700;
line-height: 22px;
color: #000000;
font-family: 'Roboto';
font-size: 14px;
font-weight: 700;
line-height: 22px;
}
#special.card-footer {
......@@ -932,3 +932,113 @@ a.year-toc {
color: #3fc5dc;
}
}
/* Tipue style (search)*/
#tipue_search_results_count
{
font-family: PlayfairDisplay;
color: #000000;
font-size: 20px;
font-weight: 400;
text-align: center;
}
.tipue_search_result
{
padding-top: 25px;
}
.tipue_search_image_close
{
display: none;
}
.tipue_search_content_title
{
font-family: PlayfairDisplay;
color: #000000;
font-size: 26px;
font-weight: 400;
margin-bottom: 0.5rem;
/* text-align: center; */
}
.tipue_search_content_url
{
font-family: Roboto, serif;
color: #000000;
font-size: 16px;
font-weight: 400;
margin-bottom: 0.5rem;
/* text-align: center; */
}
.tipue_search_content_url a {
text-decoration: underline;
}
.tipue_search_content_url a:hover {
color: #3fc5dc;
}
.tipue_search_content_text
{
font-family: Roboto;
color: #000000;
font-size: 16px;
font-weight: 300;
line-height: 22.915px;
text-align: justify;
}
.tipue_search_content_bold
{
font-weight: 500;
}
#tipue_search_foot
{
margin: 51px 0 21px 0;
}
#tipue_search_foot_boxes
{
padding: 0;
margin: 0;
cursor: pointer;
}
#tipue_search_foot_boxes li
{
display: inline;
list-style: none;
margin: 0;
padding: 0;
}
#tipue_search_foot_boxes li a
{
padding: 10px 17px 11px 17px;
border-radius: 3px;
margin-right: 7px;
text-decoration: none;
text-align: center;
transition: 0.2s;
background-color: #f7f7f7;
color: #666;
}
#tipue_search_foot_boxes li a:hover
{
background: #252525;
color: #ccc;
}
#tipue_search_foot_boxes li.current
{
padding: 10px 17px 11px 17px;
border-radius: 3px;
margin-right: 7px;
text-align: center;
background: #252525;
color: #ccc;
}
\ No newline at end of file
......@@ -54,7 +54,7 @@
<a class="nav-link" href="{{ FOLLOW_US_LINK }}">SUIVEZ-NOUS</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<a class="nav-link" href="{{ SITEURL }}/search.html">
<img src="{{ SITEURL }}/theme/img/search.png" alt="Bouton rechercher dans le site"/>
</a>
</li>
......@@ -74,6 +74,10 @@
</div>
</div>
{% block toc_content %}
<div class="container text-center link">
<div class="row d-flex justify-content-center">
</div>
</div>
{% endblock toc_content %}
</header>
......@@ -130,6 +134,27 @@
header.style['background-image'] = 'url(' + pattern.png() + ')';
</script>
<script type="text/javascript" src="{{ SITEURL }}/theme/js/tipuesearch/tipuesearch_set.js"></script>
<script type="text/javascript" src="{{ SITEURL }}/theme/js/tipuesearch/tipuesearch.min.js"></script>
<script src="{{ SITEURL }}/tipuesearch_content.js"></script>
<script>
$(document).ready(function() {
$('#tipue_search_input').tipuesearch({
{% if 'tipue_search' in PLUGINS %}
'mode' : 'json',
{% else %}
'mode': 'live',
{% endif %}
'show': 10,
'newWindow': false,
{# I cannot place following statements in the conditionals above because then Tipue Search fails to work. Possibly a bug in Tipue Search. #}
{% if not 'tipue_search' in PLUGINS %}
'liveDescription': '.article-content'
{% endif %}
});
});
</script>
{% block script %}
{% endblock script %}
</body>
......
......@@ -15,13 +15,13 @@
</div>
</div>
</div>
<div class="container text-center link">
<div class="row d-flex justify-content-center">
</div>
</div>
{% endmacro %}