Compare commits
No commits in common. "master" and "web" have entirely different histories.
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,4 +0,0 @@
|
||||||
*.egg-info
|
|
||||||
*.pyc
|
|
||||||
test_site/
|
|
||||||
example/target/
|
|
12
README.md
12
README.md
|
@ -10,8 +10,14 @@ Just run:
|
||||||
|
|
||||||
pip install .
|
pip install .
|
||||||
|
|
||||||
## Usage
|
## Example
|
||||||
|
|
||||||
TODO
|
The `example` folder contains a ready to use basic project.
|
||||||
|
|
||||||
`ciclostile --help`
|
To build the example run:
|
||||||
|
|
||||||
|
cd example
|
||||||
|
mkdir target
|
||||||
|
ciclostile_render
|
||||||
|
|
||||||
|
A `test.html` file will appear inside the `target` folder.
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
import os
|
|
||||||
import markdown
|
|
||||||
import jinja2
|
|
||||||
|
|
||||||
|
|
||||||
def read_markdown(page_name, markdown_path):
|
|
||||||
md_file = os.path.join(markdown_path, page_name + '.md')
|
|
||||||
|
|
||||||
with open(md_file) as f:
|
|
||||||
md_text = f.read()
|
|
||||||
|
|
||||||
return md_text
|
|
||||||
|
|
||||||
|
|
||||||
def render_from_text(md_text, template_path):
|
|
||||||
md = markdown.Markdown(extensions=['meta'], output_format='html5')
|
|
||||||
html = md.convert(md_text)
|
|
||||||
|
|
||||||
# get markdown metadata
|
|
||||||
data = {key: md.Meta[key][0] for key in md.Meta.keys()}
|
|
||||||
|
|
||||||
data.update({
|
|
||||||
'content': html,
|
|
||||||
# 'page_name': page_name, # Needed?
|
|
||||||
})
|
|
||||||
|
|
||||||
template_name = data.get('template', 'default')
|
|
||||||
template_file = os.path.join(template_path, template_name + '.html')
|
|
||||||
|
|
||||||
with open(template_file) as f:
|
|
||||||
template = f.read()
|
|
||||||
|
|
||||||
page = jinja2.Template(template).render(**data)
|
|
||||||
return page
|
|
||||||
|
|
||||||
|
|
||||||
def render(page_name, markdown_path, template_path):
|
|
||||||
md_text = read_markdown(page_name, markdown_path)
|
|
||||||
page = render_from_text(md_text, template_path)
|
|
||||||
return page
|
|
||||||
|
|
||||||
|
|
||||||
def save(md_text, page_name, markdown_path):
|
|
||||||
md_file = os.path.join(markdown_path, page_name + '.md')
|
|
||||||
|
|
||||||
with open(md_file, 'w') as f:
|
|
||||||
f.write(md_text)
|
|
|
@ -1,13 +0,0 @@
|
||||||
Template: default
|
|
||||||
Title: Page one
|
|
||||||
Description: Test page one
|
|
||||||
When: today
|
|
||||||
|
|
||||||
|
|
||||||
# Header
|
|
||||||
|
|
||||||
Some text
|
|
||||||
|
|
||||||
- one
|
|
||||||
- two
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
Template: default
|
|
||||||
Title: Page two
|
|
||||||
When: tomorrow
|
|
||||||
|
|
||||||
|
|
||||||
# Header
|
|
||||||
|
|
||||||
- three
|
|
||||||
- four
|
|
||||||
|
|
||||||
Bottom text
|
|
8
example/markdown/test.md
Normal file
8
example/markdown/test.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Title: Home Page
|
||||||
|
Author: anon
|
||||||
|
|
||||||
|
|
||||||
|
# Site
|
||||||
|
|
||||||
|
Some text
|
||||||
|
|
8
example/markdown/test_other_template.md
Normal file
8
example/markdown/test_other_template.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Title: Other Test Page
|
||||||
|
Template: other_template
|
||||||
|
|
||||||
|
|
||||||
|
# Title
|
||||||
|
|
||||||
|
Some other text
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
<head>
|
<body>
|
||||||
<title>{{ title }}</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="content">
|
|
||||||
{{ content }}
|
{{ content }}
|
||||||
</div>
|
</body>
|
||||||
</body>
|
</html>
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>INDEX</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{% for pname, page in pages.items() %}
|
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
<a href="{{page.link}}">{{page.title}}</a>
|
|
||||||
</p>
|
|
||||||
<p>{{page.description}}</p>
|
|
||||||
<p>{{page.when}}</p>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
11
example/templates/other_template.html
Normal file
11
example/templates/other_template.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Other Template</h1>
|
||||||
|
{{ content }}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,17 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<rss version="2.0">
|
|
||||||
|
|
||||||
<channel>
|
|
||||||
<title>Unit</title>
|
|
||||||
<link>https://unit.macaomilano.org</link>
|
|
||||||
<description>unit</description>
|
|
||||||
{% for pname, page in pages.items() %}
|
|
||||||
<item>
|
|
||||||
<title>{{ page.title }}</title>
|
|
||||||
<link>{{ page.link }}</link>
|
|
||||||
<description>{{ page.description }}</description>
|
|
||||||
</item>
|
|
||||||
{% endfor %}
|
|
||||||
</channel>
|
|
||||||
|
|
||||||
</rss>
|
|
14
setup.py
Executable file → Normal file
14
setup.py
Executable file → Normal file
|
@ -1,10 +1,10 @@
|
||||||
from setuptools import setup
|
from distutils.core import setup
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
here = path.abspath(path.dirname(__file__))
|
here = path.abspath(path.dirname(__file__))
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='ciclostile',
|
name='Ciclostile',
|
||||||
version='0.0.0.dev0',
|
version='0.0.0.dev0',
|
||||||
|
|
||||||
description='Static site generator',
|
description='Static site generator',
|
||||||
|
@ -16,11 +16,7 @@ setup(
|
||||||
|
|
||||||
package_dir={'': 'src'},
|
package_dir={'': 'src'},
|
||||||
packages=['ciclostile'],
|
packages=['ciclostile'],
|
||||||
# scripts=['src/ciclostile_cli'],
|
scripts=['src/ciclostile_render', 'src/ciclostile_web'],
|
||||||
entry_points='''
|
|
||||||
[console_scripts]
|
|
||||||
ciclostile=cli:cli
|
|
||||||
''',
|
|
||||||
|
|
||||||
install_requires=['Click', 'markdown', 'jinja2', 'flask', 'flask-httpauth']
|
install_requires=['markdown', 'jinja2', 'flask', 'flask-httpauth']
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,95 +1,47 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import os
|
import os
|
||||||
import markdown
|
import markdown
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
|
|
||||||
def parse(mdtext, parser=None):
|
def read_markdown(page_name, markdown_path):
|
||||||
if parser is None:
|
md_file = os.path.join(markdown_path, page_name + '.md')
|
||||||
parser = markdown.Markdown(extensions=['meta'], output_format='html5')
|
|
||||||
html = parser.convert(mdtext)
|
with open(md_file) as f:
|
||||||
|
md_text = f.read()
|
||||||
|
|
||||||
|
return md_text
|
||||||
|
|
||||||
|
|
||||||
|
def render_from_text(md_text, template_path):
|
||||||
|
md = markdown.Markdown(extensions=['meta'], output_format='html5')
|
||||||
|
html = md.convert(md_text)
|
||||||
|
|
||||||
# get markdown metadata
|
# get markdown metadata
|
||||||
data = {key: parser.Meta[key][0] for key in parser.Meta.keys()}
|
data = {key: md.Meta[key][0] for key in md.Meta.keys()}
|
||||||
|
|
||||||
data.update({
|
data.update({
|
||||||
'content': html,
|
'content': html,
|
||||||
|
# 'page_name': page_name, # Needed?
|
||||||
})
|
})
|
||||||
return data
|
|
||||||
|
template_name = data.get('template', 'default')
|
||||||
|
template_file = os.path.join(template_path, template_name + '.html')
|
||||||
|
|
||||||
|
with open(template_file) as f:
|
||||||
|
template = f.read()
|
||||||
|
|
||||||
|
page = jinja2.Template(template).render(**data)
|
||||||
|
return page
|
||||||
|
|
||||||
|
|
||||||
def parse_file(filepath, parser=None):
|
def render(page_name, markdown_path, template_path):
|
||||||
with open(filepath) as fh:
|
md_text = read_markdown(page_name, markdown_path)
|
||||||
return parse(fh.read(), parser=parser)
|
page = render_from_text(md_text, template_path)
|
||||||
|
return page
|
||||||
|
|
||||||
|
|
||||||
def index(folder):
|
def save(md_text, page_name, markdown_path):
|
||||||
data = {'pages': []}
|
md_file = os.path.join(markdown_path, page_name + '.md')
|
||||||
parser = markdown.Markdown(extensions=['meta'], output_format='html5')
|
|
||||||
for dirpath, _, files in os.walk(folder):
|
|
||||||
valid_files = (os.path.join(dirpath, f)
|
|
||||||
for f in files
|
|
||||||
if f.endswith('.md'))
|
|
||||||
for fpath in valid_files:
|
|
||||||
data['pages'].append(parse_file(fpath, parser=parser))
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
with open(md_file, 'w') as f:
|
||||||
def compile(template, data):
|
f.write(md_text)
|
||||||
t = jinja2.Template(template)
|
|
||||||
return t.render(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def compile_all(root_path):
|
|
||||||
md_path = os.path.join(root_path, 'markdown')
|
|
||||||
template_path = os.path.join(root_path, 'templates')
|
|
||||||
target_path = os.path.join(root_path, 'target')
|
|
||||||
|
|
||||||
data = {'pages': {}}
|
|
||||||
templates = {}
|
|
||||||
|
|
||||||
md_files = [f
|
|
||||||
for f in os.listdir(md_path)
|
|
||||||
if f.endswith('.md')]
|
|
||||||
template_files = [f
|
|
||||||
for f in os.listdir(template_path)
|
|
||||||
if f.endswith('.html')]
|
|
||||||
rss_file = 'templates/rss.xml'
|
|
||||||
|
|
||||||
for md_fname in md_files:
|
|
||||||
full_path = os.path.join(md_path, md_fname)
|
|
||||||
name, _ = os.path.splitext(md_fname)
|
|
||||||
|
|
||||||
data['pages'][name] = parse_file(full_path)
|
|
||||||
data['pages'][name]['link'] = f"/{name}.html"
|
|
||||||
|
|
||||||
for file_name in template_files:
|
|
||||||
full_path = os.path.join(template_path, file_name)
|
|
||||||
name, _ = os.path.splitext(file_name)
|
|
||||||
templates[name] = full_path
|
|
||||||
|
|
||||||
for page_name, page in data['pages'].items():
|
|
||||||
t_name = page.get('template', 'default')
|
|
||||||
|
|
||||||
with open(templates[t_name]) as tf:
|
|
||||||
t = jinja2.Template(tf.read())
|
|
||||||
|
|
||||||
out_path = os.path.join(target_path, page_name + '.html')
|
|
||||||
out = t.render(**data['pages'][page_name])
|
|
||||||
|
|
||||||
with open(out_path, 'w') as f:
|
|
||||||
f.write(out)
|
|
||||||
|
|
||||||
# index
|
|
||||||
with open(templates['index']) as fh:
|
|
||||||
index_template = jinja2.Template(fh.read())
|
|
||||||
out = index_template.render(**data)
|
|
||||||
out_path = os.path.join(target_path, 'index.html')
|
|
||||||
with open(out_path, "w") as f:
|
|
||||||
f.write(out)
|
|
||||||
|
|
||||||
# rss
|
|
||||||
with open(rss_file) as fh:
|
|
||||||
rss_template = jinja2.Template(fh.read())
|
|
||||||
out = rss_template.render(**data)
|
|
||||||
out_path = os.path.join(target_path, 'rss.xml')
|
|
||||||
with open(out_path, "w") as f:
|
|
||||||
f.write(out)
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
import click
|
|
||||||
from flask import Flask, send_from_directory, render_template, request
|
|
||||||
from flask_httpauth import HTTPDigestAuth
|
|
||||||
|
|
||||||
from .api import api
|
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.register_blueprint(api, url_prefix="/api")
|
|
||||||
|
|
||||||
app.config['SECRET_KEY'] = 'secret key here'
|
|
|
@ -1,65 +0,0 @@
|
||||||
import os
|
|
||||||
from glob import glob
|
|
||||||
|
|
||||||
from flask import Blueprint, send_from_directory, render_template, request, jsonify
|
|
||||||
from flask_httpauth import HTTPDigestAuth
|
|
||||||
import ciclostile
|
|
||||||
|
|
||||||
api = Blueprint('api', __name__)
|
|
||||||
|
|
||||||
# TODO: get from cli param
|
|
||||||
POST_DIR = "."
|
|
||||||
|
|
||||||
# auth = HTTPDigestAuth()
|
|
||||||
# users = {
|
|
||||||
# 'admin': 'password',
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
# @auth.get_password
|
|
||||||
# def get_pw(username):
|
|
||||||
# if username in users:
|
|
||||||
# return users.get(username)
|
|
||||||
# return None
|
|
||||||
|
|
||||||
|
|
||||||
# @api.route('/assets/<path:path>')
|
|
||||||
# def assets(path):
|
|
||||||
# assets_path = os.path.join(target_path, 'assets')
|
|
||||||
# return send_from_directory(assets_path, path)
|
|
||||||
|
|
||||||
|
|
||||||
# @api.route('/<string:page_name>')
|
|
||||||
# @api.route('/<string:page_name>.html')
|
|
||||||
# def page(page_name):
|
|
||||||
# return ciclostile.render(page_name, markdown_path, template_path)
|
|
||||||
|
|
||||||
|
|
||||||
# @api.route('/<string:page_name>/edit')
|
|
||||||
# @auth.login_required
|
|
||||||
# def edit(page_name):
|
|
||||||
# md_text = ciclostile.read_markdown(page_name, markdown_path)
|
|
||||||
# return render_template('edit.html', **locals())
|
|
||||||
|
|
||||||
|
|
||||||
# @api.route('/edit', methods=['POST'])
|
|
||||||
# @auth.login_required
|
|
||||||
# def edit_actions():
|
|
||||||
# page_name = request.form['page_name']
|
|
||||||
# md_text = request.form['md_text']
|
|
||||||
|
|
||||||
# if request.form['action'] == 'preview':
|
|
||||||
# return ciclostile.render_from_text(md_text, template_path)
|
|
||||||
|
|
||||||
# if request.form['action'] == 'save':
|
|
||||||
# ciclostile.save(md_text, page_name, markdown_path)
|
|
||||||
# return page(page_name)
|
|
||||||
|
|
||||||
|
|
||||||
@api.route("/posts")
|
|
||||||
def posts_list():
|
|
||||||
"""
|
|
||||||
Returns all .md files under POST_DIR
|
|
||||||
"""
|
|
||||||
posts = glob(f"{POST_DIR}/**/*.md", recursive=True)
|
|
||||||
return jsonify(posts)
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/home/subnixr/.virtualenvs/ciclostile/bin/python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
import ciclostile
|
import ciclostile
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/home/subnixr/.virtualenvs/ciclostile/bin/python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
from flask import Flask, send_from_directory, render_template, request
|
from flask import Flask, send_from_directory, render_template, request
|
||||||
from flask_httpauth import HTTPDigestAuth
|
from flask_httpauth import HTTPDigestAuth
|
56
src/cli.py
56
src/cli.py
|
@ -1,56 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import click
|
|
||||||
import ciclostile
|
|
||||||
|
|
||||||
# from ciclostile.web import app as webapp
|
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
|
||||||
def cli():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.option('-t', '--template', type=click.File('r'))
|
|
||||||
@click.option('-o', '--output', type=click.File('w'), default=sys.stdout)
|
|
||||||
@click.argument('markdown', type=click.File('r'), default=sys.stdin)
|
|
||||||
def compile(template, output, markdown):
|
|
||||||
data = ciclostile.parse(markdown.read())
|
|
||||||
html = ciclostile.compile(template.read(), data)
|
|
||||||
output.write(html)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.option('-t', '--template', type=click.File('r'))
|
|
||||||
@click.option('-o', '--output', type=click.File('w'), default=sys.stdout)
|
|
||||||
@click.argument('folder', type=click.Path(exists=True,
|
|
||||||
file_okay=False))
|
|
||||||
def index(template, output, folder):
|
|
||||||
data = ciclostile.index(folder)
|
|
||||||
html = ciclostile.compile(template.read(), data)
|
|
||||||
output.write(html)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.argument("input_dir", default=os.getcwd(),
|
|
||||||
type=click.Path(exists=True,
|
|
||||||
file_okay=False))
|
|
||||||
def compile_all(input_dir):
|
|
||||||
ciclostile.compile_all(input_dir)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.option("-p", "--port", type=int, default=12345)
|
|
||||||
@click.argument("input_dir", default=os.getcwd(),
|
|
||||||
type=click.Path(exists=True,
|
|
||||||
file_okay=False))
|
|
||||||
def app(port, input_dir):
|
|
||||||
print({'port': port,
|
|
||||||
'input': input_dir})
|
|
||||||
# webapp.run(port=port)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
cli()
|
|
Loading…
Reference in New Issue
Block a user