mirror of https://github.com/tiyn/beaker-blog
parent
a936fd5ee6
commit
8796169ff7
@ -0,0 +1,16 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from flask_wtf import CSRFProtect, FlaskForm
|
||||||
|
from wtforms import StringField, SubmitField, ValidationError, validators
|
||||||
|
|
||||||
|
|
||||||
|
def register_csrf(app):
|
||||||
|
csrf = CSRFProtect()
|
||||||
|
SECRET_KEY = os.urandom(32)
|
||||||
|
app.secret_key = SECRET_KEY
|
||||||
|
csrf.init_app(app)
|
||||||
|
|
||||||
|
|
||||||
|
class SearchForm(FlaskForm):
|
||||||
|
query_str = StringField("Query", [validators.DataRequired("Please enter the search term")])
|
||||||
|
# submit = SubmitField("Search")
|
@ -1,2 +1,7 @@
|
|||||||
Flask
|
Flask
|
||||||
Markdown
|
Markdown
|
||||||
|
Whoosh
|
||||||
|
WTForms
|
||||||
|
Flask_WTF
|
||||||
|
MarkupSafe
|
||||||
|
Font-Awesome-Flask
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from whoosh import scoring
|
||||||
|
from whoosh.fields import ID, TEXT, Schema
|
||||||
|
from whoosh.index import create_in, open_dir
|
||||||
|
from whoosh.qparser import QueryParser
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
INDEX_DIR = "indexdir"
|
||||||
|
DEF_TOPN = 10
|
||||||
|
ENTRY_DIR = config.ENTRY_DIR
|
||||||
|
|
||||||
|
|
||||||
|
def createSearchableData(root):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Schema definition: title(name of file), path(as ID), content(indexed but not stored), textdata (stored text content)
|
||||||
|
source:
|
||||||
|
https://appliedmachinelearning.blog/2018/07/31/developing-a-fast-indexing-and-full-text-search-engine-with-whoosh-a-pure-pythhon-library/
|
||||||
|
"""
|
||||||
|
schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT)
|
||||||
|
if not os.path.exists(INDEX_DIR):
|
||||||
|
os.mkdir(INDEX_DIR)
|
||||||
|
ix = create_in(INDEX_DIR, schema)
|
||||||
|
writer = ix.writer()
|
||||||
|
for r, _, f in os.walk(root):
|
||||||
|
for file in f:
|
||||||
|
path = os.path.join(r, file)
|
||||||
|
fp = open(path, encoding="utf-8")
|
||||||
|
title = fp.readline()
|
||||||
|
text = title + fp.read()
|
||||||
|
writer.add_document(title=title, path=path, content=text)
|
||||||
|
fp.close()
|
||||||
|
writer.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def search_times(query_str, topN):
|
||||||
|
"""
|
||||||
|
Search for a given term and returns a specific amount of results.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
query_str (string): term to search for
|
||||||
|
topN (int): number of results to return
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: html-formatted string including the hits of the search
|
||||||
|
"""
|
||||||
|
ix = open_dir(INDEX_DIR)
|
||||||
|
results = []
|
||||||
|
with ix.searcher(weighting=scoring.BM25F) as s:
|
||||||
|
query = QueryParser("content", ix.schema).parse(query_str)
|
||||||
|
matches = s.search(query, limit=topN)
|
||||||
|
for match in matches:
|
||||||
|
results.append({"title": match["title"], "path": match["path"], "match": match.score})
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def search(query_str):
|
||||||
|
"""
|
||||||
|
Search for a given term and show the predefined amount of results.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
query_str (string): term to search for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: html-formatted string including the hits of the search
|
||||||
|
"""
|
||||||
|
return search_times(query_str, DEF_TOPN)
|
||||||
|
|
||||||
|
|
||||||
|
createSearchableData(ENTRY_DIR)
|
@ -1,11 +1,11 @@
|
|||||||
{% extends "template.html" %}
|
{% extends "template.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="blogarchive">
|
<div class="blogarchive">
|
||||||
<h1>{% if language=="de-de" %}Archiv{% else %}Archive{% endif %}</h1><br>
|
<h1>{% if language=="de-de" %}Archiv{% else %}Archive{% endif %}</h1><br>
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{{ content_string }}
|
{{ content_string }}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
{% extends "template.html" %}
|
{% extends "template.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="important">
|
<div class="important">
|
||||||
{% if language=="de-de" %}Fehler{% else %}Error{% endif %}<br>
|
{% if language=="de-de" %}Fehler{% else %}Error{% endif %}<br>
|
||||||
<span>{{ errorcode }}</span>
|
<span>{{ errorcode }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{% extends "template.html" %}
|
{% extends "template.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="blog">
|
<div class="blog">
|
||||||
<h1>Feed</h1><br>
|
<h1>Feed</h1><br>
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{{ content_string }}
|
{{ content_string }}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
{% extends "template.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="search">
|
||||||
|
<h1>{% if language=="de-de" %}Suche{% else %}Search{% endif %}</h1><br>
|
||||||
|
<form class="search" action="{{ url_for('search') }}" method=post>
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
{{ form.query_str }}
|
||||||
|
<!-- {{ form.submit }} -->
|
||||||
|
<button id="submit" name="submit" type="submit" value="Search">{{ font_awesome.render_icon("fas fa-search")
|
||||||
|
}}</button>
|
||||||
|
</form>
|
||||||
|
{% autoescape off %}
|
||||||
|
{{ content }}
|
||||||
|
{% endautoescape %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -1,10 +1,10 @@
|
|||||||
{% extends "template.html" %}
|
{% extends "template.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="standalone">
|
<div class="standalone">
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{{ content_string }}
|
{{ content_string }}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,38 +1,43 @@
|
|||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>{{ title }}</title>
|
<title>{{ title }}</title>
|
||||||
<link href="{{ url_for('static', filename='css/' + style + '.css') }}" rel="stylesheet" type="text/css">
|
<link href="{{ url_for('static', filename='css/' + style + '.css') }}" rel="stylesheet" type="text/css">
|
||||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='graphics/logo.png') }}">
|
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='graphics/logo.png') }}">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width" initial-scale=1.0>
|
<meta name="viewport" content="width=device-width" initial-scale=1.0>
|
||||||
|
{{ font_awesome.load_js() }}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- Menu -->
|
<!-- Menu -->
|
||||||
<div class="main-menu-dropdown">
|
<div class="main-menu-dropdown">
|
||||||
<a href="{{ url_for('index') }}">
|
<a href="{{ url_for('index') }}">
|
||||||
<img class="logo" src="{{ url_for('static', filename='graphics/logo.png') }}">
|
<img class="logo" src="{{ url_for('static', filename='graphics/logo.png') }}">
|
||||||
{{ stitle }}
|
{{ stitle }}
|
||||||
</a>
|
</a>
|
||||||
<input type="checkbox" id="main-menu-check">
|
<input type="checkbox" id="main-menu-check">
|
||||||
<label for="main-menu-check" class="show-menu">☰</label>
|
<label for="main-menu-check" class="show-menu">☰</label>
|
||||||
<div class="main-menu">
|
<div class="main-menu">
|
||||||
<a href="{{ url_for('index') }}">Blog</a>
|
<a href="{{ url_for('index') }}">Blog</a>
|
||||||
<a href="{{ url_for('archive') }}">{% if language=="de-de" %}Archiv{% else %}Archive{% endif %}</a>
|
<a href="{{ url_for('archive') }}">{% if language=="de-de" %}Archiv{% else %}Archive{% endif %}</a>
|
||||||
<label for="main-menu-check" class="hide-menu">X</label>
|
<a href="{{ url_for('search') }}">{% if language=="de-de" %}Suche{% else %}Search{% endif %}</a>
|
||||||
</div>
|
<label for="main-menu-check" class="hide-menu">X</label>
|
||||||
</div>
|
</div>
|
||||||
<!-- Menu -->
|
</div>
|
||||||
<!-- Content -->
|
<!-- Menu -->
|
||||||
{% block content %}
|
<!-- Content -->
|
||||||
{% endblock %}
|
{% block content %}
|
||||||
<!-- Content -->
|
{% endblock %}
|
||||||
<footer>
|
<!-- Content -->
|
||||||
<div class="center">
|
<footer>
|
||||||
<a href="{{ url_for('imprint') }}">
|
<div class="center">
|
||||||
{% if language=="de-de" %}Impressum und Kontakt{% else %}Imprint and Contact{% endif %}
|
<a href="{{ url_for('imprint') }}">
|
||||||
</a><br>
|
{% if language=="de-de" %}Impressum und Kontakt{% else %}Imprint and Contact{% endif %}
|
||||||
Made with <a href="https://github.com/tiyn/beaker-blog">Beaker Blog</a>.
|
</a><br>
|
||||||
</div>
|
Made with <a href="https://github.com/tiyn/beaker-blog">Beaker Blog</a>.
|
||||||
</footer>
|
</div>
|
||||||
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in new issue