v1.1
This commit is contained in:
		
							
								
								
									
										17
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,5 +1,22 @@
 | 
				
			|||||||
# Changelog
 | 
					# Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## v1.1 2021.03.28
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Added
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **owl** switched to `markdown` module.
 | 
				
			||||||
 | 
					- `markdown_alerts` extension. Short admonition syntax.
 | 
				
			||||||
 | 
					- `markdown_del_ins` extension. Deleted and inserted text support.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Changed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Fixed `PASSWORD_FILE` detecting.
 | 
				
			||||||
 | 
					- Fixed path resolving. Now uses `os.path.join()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Removed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `markdown2`. Replaced by `markdown` module.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## v1.0 2021.03.23
 | 
					## v1.0 2021.03.23
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Added
 | 
					### Added
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
# owl
 | 
					# owl
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**owl** — is the minimalistic kn**owl**edge base web app written in Python.
 | 
					**owl** — is the minimalistic kn**owl**edge base web app written in Python.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -22,13 +22,11 @@ App is now available at [http://localhost:5000/](http://localhost:5000/).
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
This solution is suitable for creating documentation or maintaining a personal knowledge base.
 | 
					This solution is suitable for creating documentation or maintaining a personal knowledge base.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
New in `v1.0`:
 | 
					New in `v1.1`:
 | 
				
			||||||
- This is brand new owl!
 | 
					- Switched to [Python-Markdown](https://github.com/Python-Markdown/markdown).
 | 
				
			||||||
- New frontend and refactored backend.
 | 
					- Added some new Markdown extensions.
 | 
				
			||||||
- Bootstrap 5
 | 
					 | 
				
			||||||
- Optional authentication.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
See [CHANGELOG.md](CHANGELOG.md)
 | 
					See [CHANGELOG.md](CHANGELOG.md).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# License
 | 
					# License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								config.py
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								config.py
									
									
									
									
									
								
							@@ -1,16 +1,32 @@
 | 
				
			|||||||
class Config(object):
 | 
					class Config(object):
 | 
				
			||||||
    DEBUG = False
 | 
					    DEBUG = False
 | 
				
			||||||
    SECRET_KEY = 'top_secret'
 | 
					    SECRET_KEY = None
 | 
				
			||||||
    PASSWORD_FILE = '.pw'
 | 
					    PASSWORD_FILE = '.pw'
 | 
				
			||||||
    SIGN_IN = False  # Enable or disable authentication
 | 
					    SIGN_IN = False  # Enable or disable authentication
 | 
				
			||||||
    MARKDOWN_ROOT = 'docs/'  # Path to your .md files
 | 
					    MARKDOWN_ROOT = 'docs/'  # Path to your .md files
 | 
				
			||||||
    MARKDOWN_DOWLOADS = True
 | 
					    MARKDOWN_DOWLOADS = True
 | 
				
			||||||
    # See https://github.com/trentm/python-markdown2/wiki/Extras
 | 
					    MARKDOWN_EXTRAS = [
 | 
				
			||||||
    MARKDOWN2_EXTRAS = [
 | 
					        'admonition',
 | 
				
			||||||
        'fenced-code-blocks',
 | 
					        'attr_list',
 | 
				
			||||||
        'markdown-in-html',
 | 
					        'codehilite',
 | 
				
			||||||
        'code-friendly',
 | 
					        'def_list',
 | 
				
			||||||
        'header-ids',
 | 
					        'fenced_code',
 | 
				
			||||||
        'strike',
 | 
					        'md_in_html',
 | 
				
			||||||
        'tables'
 | 
					        'markdown_alerts',
 | 
				
			||||||
 | 
					        'markdown_del_ins',
 | 
				
			||||||
 | 
					        'tables',
 | 
				
			||||||
 | 
					        'toc'
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					    MARKDOWN_EXTRAS_CONFIGS = {
 | 
				
			||||||
 | 
					        'markdown_alerts': {
 | 
				
			||||||
 | 
					            'info': 'alert alert-info',
 | 
				
			||||||
 | 
					            'note': 'alert alert-primary',
 | 
				
			||||||
 | 
					            'tip': 'alert alert-success',
 | 
				
			||||||
 | 
					            'success': 'alert alert-success',
 | 
				
			||||||
 | 
					            'warning': 'alert alert-warning',
 | 
				
			||||||
 | 
					            'danger': 'alert alert-danger'
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'toc': {
 | 
				
			||||||
 | 
					            'toc_depth': '2-5'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
### Contents
 | 
					# @v@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [Home](/)
 | 
					- [Home](/)
 | 
				
			||||||
							
								
								
									
										16
									
								
								docs/HOME.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								docs/HOME.md
									
									
									
									
									
								
							@@ -1,6 +1,16 @@
 | 
				
			|||||||
# @v@ owl took off!
 | 
					# owl took off!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Read the [Docs](https://owl.gch.icu/docs/) to get started.
 | 
					<pre class='raw'>
 | 
				
			||||||
 | 
					 ,   ,
 | 
				
			||||||
 | 
					 /\ /\
 | 
				
			||||||
 | 
					((@v@))
 | 
				
			||||||
 | 
					((;;; (\
 | 
				
			||||||
 | 
					 \ ;;; \'
 | 
				
			||||||
 | 
					  ,V.V  `
 | 
				
			||||||
 | 
					 `` ``
 | 
				
			||||||
 | 
					</pre>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Also there is project's [git repository](https://gitea.gch.icu/gd/owl) ([mirror](https://github.com/gechandesu/owl)).
 | 
					Read the [Docs](https://owl.gch.icu/docs/){target="_blank"} to get started.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Also there is project's [git repository](https://gitea.gch.icu/gd/owl) ([mirror on GitHub](https://github.com/gechandesu/owl)).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								owl.py
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								owl.py
									
									
									
									
									
								
							@@ -1,10 +1,12 @@
 | 
				
			|||||||
 | 
					__version__ = '1.1'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
from functools import wraps
 | 
					from functools import wraps
 | 
				
			||||||
from datetime import timedelta
 | 
					from datetime import timedelta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pygments
 | 
					import pygments
 | 
				
			||||||
from markdown2 import Markdown
 | 
					from markdown import Markdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from flask import Flask
 | 
					from flask import Flask
 | 
				
			||||||
from flask import request
 | 
					from flask import request
 | 
				
			||||||
@@ -30,31 +32,35 @@ def read_file(filepath: str) -> str:
 | 
				
			|||||||
    except IOError:
 | 
					    except IOError:
 | 
				
			||||||
        return 'Error: Cannot read file: {}'.format(filepath)
 | 
					        return 'Error: Cannot read file: {}'.format(filepath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_path(path: str) -> str:
 | 
				
			||||||
 | 
					    return os.path.join(app.config['MARKDOWN_ROOT'], path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def render_html(filepath: str) -> str:
 | 
					def render_html(filepath: str) -> str:
 | 
				
			||||||
    markdown = Markdown(extras=app.config['MARKDOWN2_EXTRAS'])
 | 
					    html = Markdown(
 | 
				
			||||||
    return markdown.convert(
 | 
					                extensions = app.config['MARKDOWN_EXTRAS'],
 | 
				
			||||||
                    read_file(
 | 
					                extension_configs = app.config['MARKDOWN_EXTRAS_CONFIGS']
 | 
				
			||||||
                        app.config['MARKDOWN_ROOT'] + filepath
 | 
					            )
 | 
				
			||||||
                    )
 | 
					    return html.convert(
 | 
				
			||||||
                )
 | 
					            read_file(get_path(filepath))
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_title_from_markdown(filepath: str) -> str:
 | 
					def parse_title_from_markdown(filepath: str) -> str:
 | 
				
			||||||
    # This function parses article title from first level heading.
 | 
					    # This function parses article title from first level heading.
 | 
				
			||||||
    # It returns the occurrence of the first heading, and there
 | 
					    # It returns the occurrence of the first heading, and there
 | 
				
			||||||
    # can be nothing before it except empty lines and spaces.
 | 
					    # can be nothing before it except empty lines and spaces.
 | 
				
			||||||
    article = read_file(app.config['MARKDOWN_ROOT'] + filepath)
 | 
					    article = read_file(get_path(filepath))
 | 
				
			||||||
    pattern = re.compile(r'^\s*#\s.*')
 | 
					    pattern = re.compile(r'^\s*#\s.*')
 | 
				
			||||||
    if pattern.search(article):
 | 
					    if pattern.search(article):
 | 
				
			||||||
        return pattern.search(article).group().strip()[2:]
 | 
					        return pattern.search(article).group().strip()[2:]
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return 'Error: Cannot parse title from file:'.format(filepath)
 | 
					        return 'Error: Cannot parse title from file: {}'.format(filepath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_content_links(filepath: str) -> list:
 | 
					def parse_content_links(filepath: str) -> list:
 | 
				
			||||||
    # This function returns a list of links from a Markdown file.
 | 
					    # This function returns a list of links from a Markdown file.
 | 
				
			||||||
    # Only links contained in the list (ul ol li) are parsed.
 | 
					    # Only links contained in the list (ul ol li) are parsed.
 | 
				
			||||||
    r = re.compile(r'(.*(-|\+|\*|\d).?\[.*\])(\(.*\))', re.MULTILINE)
 | 
					    r = re.compile(r'(.*(-|\+|\*|\d).?\[.*\])(\(.*\))', re.MULTILINE)
 | 
				
			||||||
    links = []
 | 
					    links = []
 | 
				
			||||||
    for tpl in r.findall(read_file(app.config['MARKDOWN_ROOT'] + filepath)):
 | 
					    for tpl in r.findall(read_file(get_path(filepath))):
 | 
				
			||||||
        for item in tpl:
 | 
					        for item in tpl:
 | 
				
			||||||
            if re.match(r'\(.*\)', item):
 | 
					            if re.match(r'\(.*\)', item):
 | 
				
			||||||
                if item == '(/)':
 | 
					                if item == '(/)':
 | 
				
			||||||
@@ -67,8 +73,8 @@ def parse_content_links(filepath: str) -> list:
 | 
				
			|||||||
    return links
 | 
					    return links
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def check_password(password: str) -> bool:
 | 
					def check_password(password: str) -> bool:
 | 
				
			||||||
    if os.path.exists('.pw'):
 | 
					    if os.path.exists(app.config['PASSWORD_FILE']):
 | 
				
			||||||
        pw_hash = read_file('.pw')
 | 
					        pw_hash = read_file(app.config['PASSWORD_FILE'])
 | 
				
			||||||
        return bcrypt.check_password_hash(pw_hash, password)
 | 
					        return bcrypt.check_password_hash(pw_hash, password)
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
@@ -129,7 +135,7 @@ def index():
 | 
				
			|||||||
@app.route('/<path:path>/')
 | 
					@app.route('/<path:path>/')
 | 
				
			||||||
@login_required
 | 
					@login_required
 | 
				
			||||||
def get_article(path):
 | 
					def get_article(path):
 | 
				
			||||||
    if os.path.exists(app.config['MARKDOWN_ROOT'] + path + '.md'):
 | 
					    if os.path.exists(get_path(path) + '.md'):
 | 
				
			||||||
        return render_template(
 | 
					        return render_template(
 | 
				
			||||||
            'index.j2',
 | 
					            'index.j2',
 | 
				
			||||||
            title = parse_title_from_markdown(path + '.md'),
 | 
					            title = parse_title_from_markdown(path + '.md'),
 | 
				
			||||||
@@ -144,7 +150,7 @@ def get_article(path):
 | 
				
			|||||||
@app.route('/<path:path>.md')
 | 
					@app.route('/<path:path>.md')
 | 
				
			||||||
@login_required
 | 
					@login_required
 | 
				
			||||||
def download_article(path):
 | 
					def download_article(path):
 | 
				
			||||||
    if os.path.exists(app.config['MARKDOWN_ROOT'] + path + '.md') \
 | 
					    if os.path.exists(get_path(path) + '.md') \
 | 
				
			||||||
    and app.config['MARKDOWN_DOWLOADS']:
 | 
					    and app.config['MARKDOWN_DOWLOADS']:
 | 
				
			||||||
        return send_from_directory(
 | 
					        return send_from_directory(
 | 
				
			||||||
            app.config['MARKDOWN_ROOT'],
 | 
					            app.config['MARKDOWN_ROOT'],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
flask>=1.1
 | 
					flask>=1.1
 | 
				
			||||||
flask-bcrypt>=0.7
 | 
					flask-bcrypt>=0.7
 | 
				
			||||||
markdown2>=2.3
 | 
					 | 
				
			||||||
pygments>=2.6
 | 
					pygments>=2.6
 | 
				
			||||||
 | 
					markdown>=3.3.4
 | 
				
			||||||
 | 
					markdown-alerts>=0.1
 | 
				
			||||||
 | 
					markdown-del-ins>=1.0.0
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
body {
 | 
					body {
 | 
				
			||||||
    font-family: 'Ubuntu', sans-serif;
 | 
					    font-family: 'Ubuntu', sans-serif;
 | 
				
			||||||
    font-size: 19px;
 | 
					    font-size: 18px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
h1,
 | 
					h1,
 | 
				
			||||||
@@ -41,10 +41,28 @@ blockquote {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
blockquote p { margin: 0; }
 | 
					blockquote p { margin: 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dl dt {
 | 
				
			||||||
 | 
					    padding:0;
 | 
				
			||||||
 | 
					    margin-top:16px;
 | 
				
			||||||
 | 
					    font-size:1em;
 | 
				
			||||||
 | 
					    font-style:italic;
 | 
				
			||||||
 | 
					    font-weight:bold
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dl dd {
 | 
				
			||||||
 | 
					    padding:0 2rem;
 | 
				
			||||||
 | 
					    margin-bottom:16px
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Delete margin for admonitions */
 | 
				
			||||||
 | 
					.alert p:last-child {
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Details and summary */
 | 
					/* Details and summary */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
details, summary {
 | 
					details, summary {
 | 
				
			||||||
    display: block;
 | 
					 | 
				
			||||||
    margin: 1rem 0;
 | 
					    margin: 1rem 0;
 | 
				
			||||||
    transition: 200ms linear;
 | 
					    transition: 200ms linear;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -54,37 +72,13 @@ summary {
 | 
				
			|||||||
    transition: .3s;
 | 
					    transition: .3s;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Hide defaul marker */
 | 
					 | 
				
			||||||
details > summary { list-style: none; }
 | 
					 | 
				
			||||||
details summary::-webkit-details-marker { display: none; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
details[open] summary ~ * {
 | 
					details[open] summary ~ * {
 | 
				
			||||||
  animation: open 0.3s ease-in-out;
 | 
					  animation: open 0.3s ease-in-out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@keyframes open {
 | 
					@keyframes open {
 | 
				
			||||||
  0% {
 | 
					  0% { opacity: 0; }
 | 
				
			||||||
    opacity: 0;
 | 
					  100% { opacity: 1; }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  100% {
 | 
					 | 
				
			||||||
    opacity: 1;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
details summary:before {
 | 
					 | 
				
			||||||
    content: '+';
 | 
					 | 
				
			||||||
    font-family: 'Ubuntu Mono';
 | 
					 | 
				
			||||||
    font-size: 20px;
 | 
					 | 
				
			||||||
    display: inline-flex;
 | 
					 | 
				
			||||||
    padding: 0 0.3rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
details[open] summary:before {
 | 
					 | 
				
			||||||
    content: '-';
 | 
					 | 
				
			||||||
    font-family: 'Ubuntu Mono';
 | 
					 | 
				
			||||||
    font-size: 20px;
 | 
					 | 
				
			||||||
    display: inline-flex;
 | 
					 | 
				
			||||||
    padding: 0 0.3rem;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Code styling */
 | 
					/* Code styling */
 | 
				
			||||||
@@ -92,7 +86,7 @@ details[open] summary:before {
 | 
				
			|||||||
code,
 | 
					code,
 | 
				
			||||||
pre {
 | 
					pre {
 | 
				
			||||||
    font-family: 'Ubuntu Mono', monospace;
 | 
					    font-family: 'Ubuntu Mono', monospace;
 | 
				
			||||||
    font-size: 19px;
 | 
					    font-size: 18px;
 | 
				
			||||||
    color: #d0d0d0;
 | 
					    color: #d0d0d0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -111,7 +105,7 @@ code {
 | 
				
			|||||||
    border-radius: 6px;
 | 
					    border-radius: 6px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.raw-pre {
 | 
					.raw {
 | 
				
			||||||
    color: unset;
 | 
					    color: unset;
 | 
				
			||||||
    background: unset;
 | 
					    background: unset;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -168,15 +162,6 @@ code {
 | 
				
			|||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Header bar */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.header {
 | 
					 | 
				
			||||||
    display: block;
 | 
					 | 
				
			||||||
    position: fixed;
 | 
					 | 
				
			||||||
    height: 5rem;
 | 
					 | 
				
			||||||
    background: unset;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Sidebar */
 | 
					/* Sidebar */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.sidebar-toggle-btn {
 | 
					.sidebar-toggle-btn {
 | 
				
			||||||
@@ -227,6 +212,13 @@ code {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.sidebar a:hover { text-decoration: underline; }
 | 
					.sidebar a:hover { text-decoration: underline; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.sidebar h1,
 | 
				
			||||||
 | 
					.sidebar h2,
 | 
				
			||||||
 | 
					.sidebar h3,
 | 
				
			||||||
 | 
					.sidebar h4 {
 | 
				
			||||||
 | 
					    margin-left: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.sidebar ul,
 | 
					.sidebar ul,
 | 
				
			||||||
.sidebar ol,
 | 
					.sidebar ol,
 | 
				
			||||||
.sidebar li {
 | 
					.sidebar li {
 | 
				
			||||||
@@ -301,7 +293,8 @@ article.wide { max-width: 980px; }
 | 
				
			|||||||
@media (max-width: 1200px) {
 | 
					@media (max-width: 1200px) {
 | 
				
			||||||
    .header { background: #ffffff; }
 | 
					    .header { background: #ffffff; }
 | 
				
			||||||
    .sidebar { left: -300px; }
 | 
					    .sidebar { left: -300px; }
 | 
				
			||||||
    .sidebar-toggle-btn { left: 1rem; }
 | 
					    .sidebar-toggle-btn { left: 0.5rem; }
 | 
				
			||||||
 | 
					    .signout-btn { right: 0.5rem; }
 | 
				
			||||||
    .content { margin-left: 0px; }
 | 
					    .content { margin-left: 0px; }
 | 
				
			||||||
    .sidebar.hide { left: 0px; }
 | 
					    .sidebar.hide { left: 0px; }
 | 
				
			||||||
    .sidebar-toggle-btn.click { left: 316px; }
 | 
					    .sidebar-toggle-btn.click { left: 316px; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,12 +23,6 @@
 | 
				
			|||||||
    </head>
 | 
					    </head>
 | 
				
			||||||
    <body>
 | 
					    <body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {% if session['logged_in'] %}
 | 
					 | 
				
			||||||
            <div class="signout-btn">
 | 
					 | 
				
			||||||
                <a href="/signout/" title="Sign out"><i class="bi bi-box-arrow-right"></i></a>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        {% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        {% block content %}
 | 
					        {% block content %}
 | 
				
			||||||
            No content here
 | 
					            No content here
 | 
				
			||||||
        {% endblock %}
 | 
					        {% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,34 +6,38 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- Header bar -->
 | 
					 | 
				
			||||||
<div class="headerbar">
 | 
					<div class="headerbar">
 | 
				
			||||||
    <!-- Sidebar toggle button -->
 | 
					    {# Sidebar toggle button #}
 | 
				
			||||||
    <div class="sidebar-toggle-btn">
 | 
					    <div class="sidebar-toggle-btn">
 | 
				
			||||||
        <i class="bi bi-layout-sidebar-inset"></i>
 | 
					        <i class="bi bi-layout-sidebar-inset"></i>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    {# Sign out button #}
 | 
				
			||||||
 | 
					    {% if session['logged_in'] %}
 | 
				
			||||||
 | 
					        <div class="signout-btn">
 | 
				
			||||||
 | 
					            <a href="/signout/" title="Sign out"><i class="bi bi-box-arrow-right"></i></a>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<nav class="sidebar">
 | 
					<nav class="sidebar">
 | 
				
			||||||
    <!-- Sidebar content -->
 | 
					    {# Sidebar content #}
 | 
				
			||||||
    {{ contents | safe }}
 | 
					    {{ contents | safe }}
 | 
				
			||||||
</nav>
 | 
					</nav>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<main class="content">
 | 
					<main class="content">
 | 
				
			||||||
    <!-- Page main content -->
 | 
					    {# Page main content #}
 | 
				
			||||||
    <div class="blank-1"></div>
 | 
					    <div class="blank-1"></div>
 | 
				
			||||||
    <div class="blank-2"></div>
 | 
					    <div class="blank-2"></div>
 | 
				
			||||||
    {# CURRENT PATH {{ current_path }} #}
 | 
					 | 
				
			||||||
    <article>
 | 
					    <article>
 | 
				
			||||||
        {{ article | safe }}
 | 
					        {{ article | safe }}
 | 
				
			||||||
        <div class="blank-2"></div>
 | 
					        <div class="blank-2"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Pagination -->
 | 
					        {# Pagination #}
 | 
				
			||||||
        {% for link in links %}
 | 
					        {% for link in links %}
 | 
				
			||||||
            {% if link == current_path %}
 | 
					            {% if link == current_path %}
 | 
				
			||||||
                <div class="container">
 | 
					                <div class="container">
 | 
				
			||||||
                    <div class="row">
 | 
					                    <div class="row">
 | 
				
			||||||
                        <div class="col-sm-6" style="float: left; padding: 0 15px 0 0;">
 | 
					                        <div class="col-sm-6 float-sm-start" style="margin-top: 8px;">
 | 
				
			||||||
                            {% if (links.index(link) - 1) > -1 %}
 | 
					                            {% if (links.index(link) - 1) > -1 %}
 | 
				
			||||||
                                <div class="list-group">
 | 
					                                <div class="list-group">
 | 
				
			||||||
                                    <a href="{{ links[links.index(link) - 1] }}" class="list-group-item list-group-item-action">
 | 
					                                    <a href="{{ links[links.index(link) - 1] }}" class="list-group-item list-group-item-action">
 | 
				
			||||||
@@ -42,18 +46,18 @@
 | 
				
			|||||||
                                            {# Unique handler for root URL #}
 | 
					                                            {# Unique handler for root URL #}
 | 
				
			||||||
                                            <h5 class="mb-1">« {{ get_title('HOME/') }}</h5>
 | 
					                                            <h5 class="mb-1">« {{ get_title('HOME/') }}</h5>
 | 
				
			||||||
                                        {% else %}
 | 
					                                        {% else %}
 | 
				
			||||||
                                            <h5 class="mb-1">« {{ get_title(links[links.index(link) - 1]) }}</h5>
 | 
					                                            <h5 class="mb-1">« {{ get_title(links[links.index(link) - 1][1:]) }}</h5>
 | 
				
			||||||
                                        {% endif %}
 | 
					                                        {% endif %}
 | 
				
			||||||
                                    </a>
 | 
					                                    </a>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
                            {% endif %}
 | 
					                            {% endif %}
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        <div class="col-sm-6" style="float: right; padding: 0 0 0 15px;">
 | 
					                        <div class="col-sm-6 float-sm-end" style="margin-top: 8px;">
 | 
				
			||||||
                            {% if (links.index(link) + 1) <= (get_len(links) - 1) %}
 | 
					                            {% if (links.index(link) + 1) <= (get_len(links) - 1) %}
 | 
				
			||||||
                                <div class="list-group">
 | 
					                                <div class="list-group">
 | 
				
			||||||
                                    <a href="{{ links[links.index(link) + 1] }}" class="list-group-item list-group-item-action" style="text-align: right;">
 | 
					                                    <a href="{{ links[links.index(link) + 1] }}" class="list-group-item list-group-item-action" style="text-align: right;">
 | 
				
			||||||
                                        <small class="mb-1">Next</small>
 | 
					                                        <small class="mb-1">Next</small>
 | 
				
			||||||
                                        <h5 class="mb-1">{{ get_title(links[links.index(link) + 1]) }} »</h5>
 | 
					                                        <h5 class="mb-1">{{ get_title(links[links.index(link) + 1][1:]) }} »</h5>
 | 
				
			||||||
                                    </a>
 | 
					                                    </a>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
                            {% endif %}
 | 
					                            {% endif %}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user