mirror of
				https://github.com/tiyn/beaker-blog.git
				synced 2025-11-04 12:51:15 +01:00 
			
		
		
		
	added search page, updated style
This commit is contained in:
		@@ -13,6 +13,9 @@ via plain text files.
 | 
				
			|||||||
  - [x] HTML files (.html)
 | 
					  - [x] HTML files (.html)
 | 
				
			||||||
  - [x] Markdown Files (.md)
 | 
					  - [x] Markdown Files (.md)
 | 
				
			||||||
- [x] Infinite-scroll blog page
 | 
					- [x] Infinite-scroll blog page
 | 
				
			||||||
 | 
					- [x] Search page
 | 
				
			||||||
 | 
					-   [x] Full-text search
 | 
				
			||||||
 | 
					-   [x] Preview panel
 | 
				
			||||||
- [x] Archive page
 | 
					- [x] Archive page
 | 
				
			||||||
  - [x] Months as headings
 | 
					  - [x] Months as headings
 | 
				
			||||||
  - [x] Links to scrolling blog page
 | 
					  - [x] Links to scrolling blog page
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										101
									
								
								src/app.py
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								src/app.py
									
									
									
									
									
								
							@@ -1,10 +1,9 @@
 | 
				
			|||||||
from flask import Flask, make_response, render_template, abort
 | 
					from flask import Flask, abort, make_response, render_template, request
 | 
				
			||||||
 | 
					from flask_font_awesome import FontAwesome
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import content as con_gen
 | 
					 | 
				
			||||||
import config
 | 
					import config
 | 
				
			||||||
 | 
					import content as con_gen
 | 
				
			||||||
 | 
					from forms import SearchForm, register_csrf
 | 
				
			||||||
app = Flask(__name__)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
TITLE = config.TITLE
 | 
					TITLE = config.TITLE
 | 
				
			||||||
STITLE = config.STITLE
 | 
					STITLE = config.STITLE
 | 
				
			||||||
@@ -14,47 +13,105 @@ DESCRIPTION = config.DESCRIPTION
 | 
				
			|||||||
WEBSITE = config.WEBSITE
 | 
					WEBSITE = config.WEBSITE
 | 
				
			||||||
MAIL = config.MAIL
 | 
					MAIL = config.MAIL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app = Flask(__name__)
 | 
				
			||||||
 | 
					register_csrf(app)
 | 
				
			||||||
 | 
					font_awesome = FontAwesome(app)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.errorhandler(404)
 | 
					@app.errorhandler(404)
 | 
				
			||||||
def page_not_found(e):
 | 
					def page_not_found(e):
 | 
				
			||||||
    return render_template("error.html", title=TITLE, stitle=STITLE, errorcode="404", style=STYLE, language=LANGUAGE), 404
 | 
					  return render_template("error.html",
 | 
				
			||||||
 | 
					                         title=TITLE,
 | 
				
			||||||
 | 
					                         stitle=STITLE,
 | 
				
			||||||
 | 
					                         errorcode="404",
 | 
				
			||||||
 | 
					                         style=STYLE,
 | 
				
			||||||
 | 
					                         language=LANGUAGE), 404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route("/")
 | 
					@app.route("/")
 | 
				
			||||||
@app.route("/index.html")
 | 
					@app.route("/index.html")
 | 
				
			||||||
def index():
 | 
					def index():
 | 
				
			||||||
    content = con_gen.gen_index_string()
 | 
					  content = con_gen.gen_index_string()
 | 
				
			||||||
    return render_template("index.html", title=TITLE, stitle=STITLE, content_string=content, style=STYLE, language=LANGUAGE)
 | 
					  return render_template("index.html",
 | 
				
			||||||
 | 
					                         title=TITLE,
 | 
				
			||||||
 | 
					                         stitle=STITLE,
 | 
				
			||||||
 | 
					                         content_string=content,
 | 
				
			||||||
 | 
					                         style=STYLE,
 | 
				
			||||||
 | 
					                         language=LANGUAGE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route("/search", methods=["GET", "POST"])
 | 
				
			||||||
 | 
					@app.route("/search.html", methods=["GET", "POST"])
 | 
				
			||||||
 | 
					def search():
 | 
				
			||||||
 | 
					  form = SearchForm()
 | 
				
			||||||
 | 
					  if request.method == "POST":
 | 
				
			||||||
 | 
					    query_str = request.form["query_str"]
 | 
				
			||||||
 | 
					    content = con_gen.gen_query_res_string(query_str)
 | 
				
			||||||
 | 
					    return render_template("search.html",
 | 
				
			||||||
 | 
					                           title=TITLE,
 | 
				
			||||||
 | 
					                           stitle=STITLE,
 | 
				
			||||||
 | 
					                           style=STYLE,
 | 
				
			||||||
 | 
					                           form=form,
 | 
				
			||||||
 | 
					                           content=content,
 | 
				
			||||||
 | 
					                           language=LANGUAGE), 200
 | 
				
			||||||
 | 
					  return render_template("search.html",
 | 
				
			||||||
 | 
					                         title=TITLE,
 | 
				
			||||||
 | 
					                         stitle=STITLE,
 | 
				
			||||||
 | 
					                         style=STYLE,
 | 
				
			||||||
 | 
					                         form=form,
 | 
				
			||||||
 | 
					                         content="",
 | 
				
			||||||
 | 
					                         language=LANGUAGE), 200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route("/imprint")
 | 
					@app.route("/imprint")
 | 
				
			||||||
@app.route("/imprint.html")
 | 
					@app.route("/imprint.html")
 | 
				
			||||||
def imprint():
 | 
					def imprint():
 | 
				
			||||||
    return render_template("imprint.html", title=TITLE, stitle=STITLE, mail=MAIL, style=STYLE, language=LANGUAGE)
 | 
					  return render_template("imprint.html",
 | 
				
			||||||
 | 
					                         title=TITLE,
 | 
				
			||||||
 | 
					                         stitle=STITLE,
 | 
				
			||||||
 | 
					                         mail=MAIL,
 | 
				
			||||||
 | 
					                         style=STYLE,
 | 
				
			||||||
 | 
					                         language=LANGUAGE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route("/archive")
 | 
					@app.route("/archive")
 | 
				
			||||||
@app.route("/archive.html")
 | 
					@app.route("/archive.html")
 | 
				
			||||||
def archive():
 | 
					def archive():
 | 
				
			||||||
    content = con_gen.gen_arch_string()
 | 
					  content = con_gen.gen_arch_string()
 | 
				
			||||||
    return render_template("archive.html", title=TITLE, stitle=STITLE, content_string=content, style=STYLE, language=LANGUAGE)
 | 
					  return render_template("archive.html",
 | 
				
			||||||
 | 
					                         title=TITLE,
 | 
				
			||||||
 | 
					                         stitle=STITLE,
 | 
				
			||||||
 | 
					                         content_string=content,
 | 
				
			||||||
 | 
					                         style=STYLE,
 | 
				
			||||||
 | 
					                         language=LANGUAGE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route("/entry/<path>")
 | 
					@app.route("/entry/<path>")
 | 
				
			||||||
def entry(path):
 | 
					def entry(path):
 | 
				
			||||||
    content = con_gen.gen_stand_string(path)
 | 
					  content = con_gen.gen_stand_string(path)
 | 
				
			||||||
    if content != "":
 | 
					  if content != "":
 | 
				
			||||||
        return render_template("standalone.html", title=TITLE, stitle=STITLE, content_string=content, style=STYLE, language=LANGUAGE)
 | 
					    return render_template("standalone.html",
 | 
				
			||||||
    abort(404)
 | 
					                           title=TITLE,
 | 
				
			||||||
 | 
					                           stitle=STITLE,
 | 
				
			||||||
 | 
					                           content_string=content,
 | 
				
			||||||
 | 
					                           style=STYLE,
 | 
				
			||||||
 | 
					                           language=LANGUAGE)
 | 
				
			||||||
 | 
					  abort(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route("/feed.xml")
 | 
					@app.route("/feed.xml")
 | 
				
			||||||
@app.route("/rss.xml")
 | 
					@app.route("/rss.xml")
 | 
				
			||||||
def feed():
 | 
					def feed():
 | 
				
			||||||
    content = con_gen.get_rss_string()
 | 
					  content = con_gen.get_rss_string()
 | 
				
			||||||
    rss_xml = render_template("rss.xml", content_string=content, title=TITLE,
 | 
					  rss_xml = render_template("rss.xml",
 | 
				
			||||||
                              description=DESCRIPTION, website=WEBSITE)
 | 
					                            content_string=content,
 | 
				
			||||||
    response = make_response(rss_xml)
 | 
					                            title=TITLE,
 | 
				
			||||||
    response.headers["Content-Type"] = "application/rss+xml"
 | 
					                            description=DESCRIPTION,
 | 
				
			||||||
    return response
 | 
					                            website=WEBSITE)
 | 
				
			||||||
 | 
					  response = make_response(rss_xml)
 | 
				
			||||||
 | 
					  response.headers["Content-Type"] = "application/rss+xml"
 | 
				
			||||||
 | 
					  return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    app.run(host="0.0.0.0")
 | 
					  app.run(host="0.0.0.0")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,3 +18,6 @@ LANGUAGE = "en-us"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Mail address for the imprint
 | 
					# Mail address for the imprint
 | 
				
			||||||
MAIL = "dummy@mail.com"
 | 
					MAIL = "dummy@mail.com"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Directory to store entries in
 | 
				
			||||||
 | 
					ENTRY_DIR = "templates/entry"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,17 +4,17 @@ import pathlib
 | 
				
			|||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
from os import path
 | 
					from os import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import config
 | 
					 | 
				
			||||||
import markdown
 | 
					import markdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENTRY_DIR = "templates/entry"
 | 
					import config
 | 
				
			||||||
 | 
					import search
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY_DIR = config.ENTRY_DIR
 | 
				
			||||||
LANGUAGE = config.LANGUAGE
 | 
					LANGUAGE = config.LANGUAGE
 | 
				
			||||||
LOCAL = "de_DE.UTF-8" if LANGUAGE == "de-de" else "en_US.UTF-8"
 | 
					LOCAL = "de_DE.UTF-8" if LANGUAGE == "de-de" else "en_US.UTF-8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
locale.setlocale(locale.LC_TIME, LOCAL)
 | 
					locale.setlocale(locale.LC_TIME, LOCAL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
standalone_str = "Artikel" if LANGUAGE == "de-de" else "standalone"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def gen_arch_string():
 | 
					def gen_arch_string():
 | 
				
			||||||
  """
 | 
					  """
 | 
				
			||||||
@@ -70,7 +70,7 @@ def gen_index_string():
 | 
				
			|||||||
    contents = sorted(full_list, key=os.path.getmtime)
 | 
					    contents = sorted(full_list, key=os.path.getmtime)
 | 
				
			||||||
    for file in reversed(contents):
 | 
					    for file in reversed(contents):
 | 
				
			||||||
      filename = pathlib.PurePath(file)
 | 
					      filename = pathlib.PurePath(file)
 | 
				
			||||||
      purefile = filename
 | 
					      # purefile = filename
 | 
				
			||||||
      title = open(filename).readline().rstrip("\n")
 | 
					      title = open(filename).readline().rstrip("\n")
 | 
				
			||||||
      text = open(filename).readlines()[1:]
 | 
					      text = open(filename).readlines()[1:]
 | 
				
			||||||
      filename = filename.name
 | 
					      filename = filename.name
 | 
				
			||||||
@@ -161,11 +161,11 @@ def get_rss_string():
 | 
				
			|||||||
    string: rss-string of everything that is in the ENTRY_DIR.
 | 
					    string: rss-string of everything that is in the ENTRY_DIR.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
  path_ex = ENTRY_DIR
 | 
					  path_ex = ENTRY_DIR
 | 
				
			||||||
 | 
					  content_string = ""
 | 
				
			||||||
  if path.exists(path_ex):
 | 
					  if path.exists(path_ex):
 | 
				
			||||||
    name_list = os.listdir(path_ex)
 | 
					    name_list = os.listdir(path_ex)
 | 
				
			||||||
    full_list = [os.path.join(path_ex, i) for i in name_list]
 | 
					    full_list = [os.path.join(path_ex, i) for i in name_list]
 | 
				
			||||||
    contents = sorted(full_list, key=os.path.getmtime)
 | 
					    contents = sorted(full_list, key=os.path.getmtime)
 | 
				
			||||||
    content_string = ""
 | 
					 | 
				
			||||||
    for file in reversed(contents):
 | 
					    for file in reversed(contents):
 | 
				
			||||||
      filename = pathlib.PurePath(file)
 | 
					      filename = pathlib.PurePath(file)
 | 
				
			||||||
      title = open(filename).readline().rstrip("\n")
 | 
					      title = open(filename).readline().rstrip("\n")
 | 
				
			||||||
@@ -186,3 +186,66 @@ def get_rss_string():
 | 
				
			|||||||
      content_string += "</description>\n"
 | 
					      content_string += "</description>\n"
 | 
				
			||||||
      content_string += "</item>\n"
 | 
					      content_string += "</item>\n"
 | 
				
			||||||
  return content_string
 | 
					  return content_string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def gen_query_res_string(query_str):
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					    Return the results of a query.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Parameters:
 | 
				
			||||||
 | 
					    query_str (string): term to search
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					    string: html-formated search result
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					  src_results = search.search(query_str)
 | 
				
			||||||
 | 
					  res_string = ""
 | 
				
			||||||
 | 
					  for result in src_results:
 | 
				
			||||||
 | 
					    title = result["title"]
 | 
				
			||||||
 | 
					    path = result["path"]
 | 
				
			||||||
 | 
					    filename = pathlib.PurePath(path)
 | 
				
			||||||
 | 
					    filename = filename.name
 | 
				
			||||||
 | 
					    if filename[0] != ".":
 | 
				
			||||||
 | 
					      filename = filename.split(".", 1)[0]
 | 
				
			||||||
 | 
					    curr_date = datetime.fromtimestamp(os.path.getmtime(path)).strftime("%Y-%m-%d")
 | 
				
			||||||
 | 
					    is_markdown = path.endswith(".md")
 | 
				
			||||||
 | 
					    preview = create_preview(path, is_markdown)
 | 
				
			||||||
 | 
					    path = "/entry/" + path.split("/", 2)[2]
 | 
				
			||||||
 | 
					    res_string += "<div class=\"entry\">"
 | 
				
			||||||
 | 
					    res_string += "<a href=\"" + path + "\"><h2>" + title + "</h2></a>"
 | 
				
			||||||
 | 
					    res_string += "<small>"
 | 
				
			||||||
 | 
					    res_string += "<a href=\"" + "/index.html#" + \
 | 
				
			||||||
 | 
					        filename + "\">" + curr_date + "</a>"
 | 
				
			||||||
 | 
					    res_string += "</small><br><br>"
 | 
				
			||||||
 | 
					    res_string += preview + "</div>"
 | 
				
			||||||
 | 
					  return res_string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_preview(path, is_markdown):
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					    Create a preview of a given article and return it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Parameters:
 | 
				
			||||||
 | 
					    path (string): path to the article
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					    string: html-formated preview
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					  file = open(path, "r", encoding="utf-8")
 | 
				
			||||||
 | 
					  first_lines = file.readlines()
 | 
				
			||||||
 | 
					  preview = ""
 | 
				
			||||||
 | 
					  preview_length = 3
 | 
				
			||||||
 | 
					  for i, line in enumerate(first_lines):
 | 
				
			||||||
 | 
					    if i == 0:
 | 
				
			||||||
 | 
					      continue
 | 
				
			||||||
 | 
					    if i > preview_length:
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    if not line.isspace():
 | 
				
			||||||
 | 
					      if is_markdown:
 | 
				
			||||||
 | 
					        preview += markdown.markdown(line)
 | 
				
			||||||
 | 
					      else:
 | 
				
			||||||
 | 
					        preview += line
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					      preview_length += 1
 | 
				
			||||||
 | 
					  preview += "<br>...<br>"
 | 
				
			||||||
 | 
					  return preview
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								src/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/forms.py
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										72
									
								
								src/search.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/search.py
									
									
									
									
									
										Normal file
									
								
							@@ -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)
 | 
				
			||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
:root {
 | 
					:root {
 | 
				
			||||||
    --bg0: rgb(29,32,33);
 | 
					    --bg0: rgb(29,32,33);
 | 
				
			||||||
    --color0: rgb(220,120,0);
 | 
					    --color0: rgb(220,120,0);
 | 
				
			||||||
 | 
					    --color1: rgb(280,180,0);
 | 
				
			||||||
    --error: rgb(255,0,0);
 | 
					    --error: rgb(255,0,0);
 | 
				
			||||||
    --footerbg0: rgb(29,32,33);
 | 
					    --footerbg0: rgb(29,32,33);
 | 
				
			||||||
    --link0: rgb(220, 120, 0);
 | 
					    --link0: rgb(220, 120, 0);
 | 
				
			||||||
@@ -83,3 +84,13 @@ span {
 | 
				
			|||||||
.entry h2 {
 | 
					.entry h2 {
 | 
				
			||||||
    color: var(--text1);
 | 
					    color: var(--text1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form.search button[type=submit] {
 | 
				
			||||||
 | 
					  background: var(--color0);
 | 
				
			||||||
 | 
					  color: var(--bg0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form.search button[type=submit]:hover {
 | 
				
			||||||
 | 
					  background: var(--color1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
:root {
 | 
					:root {
 | 
				
			||||||
    --bg0: rgb(255,255,255);
 | 
					    --bg0: rgb(255,255,255);
 | 
				
			||||||
    --color0: rgb(0,0,120);
 | 
					    --color0: rgb(0,0,120);
 | 
				
			||||||
 | 
					    --color1: rgb(0,0,200);
 | 
				
			||||||
    --error: rgb(255,0,0);
 | 
					    --error: rgb(255,0,0);
 | 
				
			||||||
    --footerbg0: rgb(192,192,192);
 | 
					    --footerbg0: rgb(192,192,192);
 | 
				
			||||||
    --link0: rgb(0,0,120);
 | 
					    --link0: rgb(0,0,120);
 | 
				
			||||||
@@ -91,3 +92,12 @@ span {
 | 
				
			|||||||
.entry h2 {
 | 
					.entry h2 {
 | 
				
			||||||
    color: var(--text1);
 | 
					    color: var(--text1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form.search button[type=submit] {
 | 
				
			||||||
 | 
					  background: var(--color0);
 | 
				
			||||||
 | 
					  color: var(--bg0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form.search button[type=submit]:hover {
 | 
				
			||||||
 | 
					  background: var(--color1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -157,6 +157,10 @@ footer .center {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form {
 | 
				
			||||||
 | 
					    margin-bottom: 40px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.entry {
 | 
					.entry {
 | 
				
			||||||
    border-radius: 0 10px 30px 0;
 | 
					    border-radius: 0 10px 30px 0;
 | 
				
			||||||
    margin-bottom: 20px;
 | 
					    margin-bottom: 20px;
 | 
				
			||||||
@@ -186,6 +190,15 @@ h3 {
 | 
				
			|||||||
    padding-bottom: 0;
 | 
					    padding-bottom: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.blogarchive h1:first-child {
 | 
				
			||||||
 | 
					    padding-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.blogarchive h2:first-child {
 | 
				
			||||||
 | 
					    padding-top: 0;
 | 
				
			||||||
 | 
					    padding-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.entry ul {
 | 
					.entry ul {
 | 
				
			||||||
    padding-left: 20;
 | 
					    padding-left: 20;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -216,3 +229,28 @@ code {
 | 
				
			|||||||
    white-space: pre;
 | 
					    white-space: pre;
 | 
				
			||||||
    display: inline-block
 | 
					    display: inline-block
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form.search input[type=text] {
 | 
				
			||||||
 | 
					  padding: 10px;
 | 
				
			||||||
 | 
					  font-size: 17px;
 | 
				
			||||||
 | 
					  border: 1px solid grey;
 | 
				
			||||||
 | 
					  float: left;
 | 
				
			||||||
 | 
					  width: 50%;
 | 
				
			||||||
 | 
					  background: #f1f1f1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form.search button[type=submit] {
 | 
				
			||||||
 | 
					  float: left;
 | 
				
			||||||
 | 
					  width: 5%;
 | 
				
			||||||
 | 
					  padding: 12px;
 | 
				
			||||||
 | 
					  font-size: 17px;
 | 
				
			||||||
 | 
					  border: 1px solid grey;
 | 
				
			||||||
 | 
					  border-left: none; /* Prevent double borders */
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form.search::after {
 | 
				
			||||||
 | 
					  content: "";
 | 
				
			||||||
 | 
					  clear: both;
 | 
				
			||||||
 | 
					  display: table;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 %}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										19
									
								
								src/templates/search.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/templates/search.html
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user