Начальная редакция
commit
d524359250
|
@ -0,0 +1,49 @@
|
|||
syntax: glob
|
||||
|
||||
.gitignore
|
||||
node_modules
|
||||
.c9revisions
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
include
|
||||
man
|
||||
__pycache__
|
||||
public
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
nosetests.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
cms/webpack
|
||||
|
||||
# Ignore IDEA files
|
||||
.idea
|
|
@ -0,0 +1,4 @@
|
|||
0.0
|
||||
---
|
||||
|
||||
- Initial version.
|
|
@ -0,0 +1,2 @@
|
|||
include *.txt *.ini *.cfg *.rst
|
||||
recursive-include cms *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2
|
|
@ -0,0 +1,12 @@
|
|||
Radium Flat-file CMS
|
||||
==========
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
- Install flatfilecms
|
||||
|
||||
pip install flatfilecms
|
||||
|
||||
- Use flatfilecms:main as a WSGI application
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
from pyramid.config import Configurator
|
||||
from pyramid.path import AssetResolver
|
||||
from .resources import Root
|
||||
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class PagesEventHandler(FileSystemEventHandler):
|
||||
def __init__(self, root, root_path):
|
||||
super(PagesEventHandler, self).__init__()
|
||||
self.root = root
|
||||
self.root_path = root_path
|
||||
|
||||
def find_dir(self, path):
|
||||
folder = self.root
|
||||
path = path.relative_to(self.root_path)
|
||||
for name in path.parts[:-1]:
|
||||
folder = folder[name]
|
||||
return folder
|
||||
|
||||
def on_created(self, event):
|
||||
super(PagesEventHandler, self).on_created(event)
|
||||
path = Path(event.dest_path if hasattr(event, 'dest_path')
|
||||
else event.src_path)
|
||||
folder = self.find_dir(path)
|
||||
if event.is_directory:
|
||||
folder.create_dir(path)
|
||||
else:
|
||||
folder.create_file(path)
|
||||
|
||||
def on_deleted(self, event):
|
||||
super(PagesEventHandler, self).on_deleted(event)
|
||||
path = Path(event.src_path)
|
||||
folder = self.find_dir(path)
|
||||
name = path.stem
|
||||
if path.suffix not in ['.md', '.yaml', '.j2', '.jinja2'] \
|
||||
and path.name != 'index.html':
|
||||
name = path.name
|
||||
del folder[name]
|
||||
|
||||
def on_moved(self, event):
|
||||
super(PagesEventHandler, self).on_moved(event)
|
||||
self.on_deleted(event)
|
||||
self.on_created(event)
|
||||
|
||||
|
||||
def main(global_config, **settings):
|
||||
""" This function returns a Pyramid WSGI application.
|
||||
"""
|
||||
from pyramid.settings import asbool
|
||||
watchdog = asbool(settings.get(
|
||||
'watchdog', 'false'))
|
||||
settings['watchdog'] = watchdog
|
||||
settings.setdefault('pages_dir', 'pages')
|
||||
settings.setdefault('data_dir', 'data')
|
||||
|
||||
pages = Root(settings['pages_dir'])
|
||||
|
||||
def factory(request):
|
||||
return pages
|
||||
|
||||
config = Configurator(settings=settings,
|
||||
root_factory=factory)
|
||||
config.include('pyramid_jinja2')
|
||||
config.include('.routes')
|
||||
config.scan()
|
||||
if watchdog:
|
||||
path = AssetResolver().resolve(
|
||||
settings['pages_dir']).abspath()
|
||||
observer = Observer()
|
||||
event_handler = PagesEventHandler(pages, path)
|
||||
observer.schedule(
|
||||
event_handler,
|
||||
path,
|
||||
recursive=True)
|
||||
observer.start()
|
||||
return config.make_wsgi_app()
|
|
@ -0,0 +1,57 @@
|
|||
from jinja2 import contextfilter, Markup
|
||||
import markdown
|
||||
from pathlib import Path
|
||||
from markdown.extensions import Extension
|
||||
from markdown.blockprocessors import BlockProcessor
|
||||
|
||||
|
||||
class Jinja2Processor(BlockProcessor):
|
||||
def test(self, parent, block):
|
||||
return block.startswith('{%') or block.startswith('{{')
|
||||
|
||||
def run(self, parent, blocks):
|
||||
block = blocks.pop(0)
|
||||
sibling = self.lastChild(parent)
|
||||
if sibling is not None:
|
||||
if sibling.tail:
|
||||
sibling.tail = sibling.tail + block
|
||||
else:
|
||||
sibling.tail = block
|
||||
else:
|
||||
if parent.text:
|
||||
parent.text = parent.text + block
|
||||
else:
|
||||
parent.text = block
|
||||
return True
|
||||
|
||||
|
||||
class IgnoreExtension(Extension):
|
||||
def extendMarkdown(self, md, md_globals):
|
||||
md.parser.blockprocessors.add(
|
||||
'jinja2',
|
||||
Jinja2Processor(md.parser),
|
||||
">hashheader")
|
||||
|
||||
|
||||
@contextfilter
|
||||
def markdown2html(context, text, render=True):
|
||||
result = markdown.markdown(
|
||||
text,
|
||||
extensions=[IgnoreExtension(), 'markdown.extensions.extra'],
|
||||
output_format='html5',
|
||||
tab_length=2)
|
||||
if render:
|
||||
result = context.environment.from_string(result).render(context)
|
||||
if context.eval_ctx.autoescape:
|
||||
result = Markup(result)
|
||||
return result
|
||||
|
||||
|
||||
def merge_dict(a, b):
|
||||
if isinstance(a, list):
|
||||
return [merge_dict(i, b) for i in a]
|
||||
return {**a, **b}
|
||||
|
||||
|
||||
def fileglob(path, root):
|
||||
return [p.relative_to(root) for p in Path(root).glob(path)]
|
|
@ -0,0 +1,75 @@
|
|||
from pyramid.path import AssetResolver
|
||||
from pathlib import PurePath
|
||||
|
||||
|
||||
def flat(d, path):
|
||||
structure = []
|
||||
for k, v in d.items():
|
||||
if isinstance(v, dict):
|
||||
structure.extend(flat(v, f"{path}/{k}"))
|
||||
else:
|
||||
if k == 'index':
|
||||
structure.append(f"{path}/")
|
||||
else:
|
||||
structure.append(f"{path}/{k}")
|
||||
return structure
|
||||
|
||||
|
||||
class Folder(dict):
|
||||
def __init__(self, name, parent, path):
|
||||
self.__path__ = path
|
||||
self.__name__ = name
|
||||
self.__parent__ = parent
|
||||
for entry in AssetResolver().resolve(path).listdir():
|
||||
asset = f"{path}/{entry}"
|
||||
if AssetResolver().resolve(asset).isdir():
|
||||
self.create_dir(asset)
|
||||
else:
|
||||
self.create_file(asset)
|
||||
|
||||
def create_file(self, asset):
|
||||
path = PurePath(asset)
|
||||
if path.suffix == '.md':
|
||||
self[path.stem] = Markdown(path.stem, self, asset)
|
||||
elif path.suffix == '.yaml':
|
||||
self[path.stem] = YAML(path.stem, self, asset)
|
||||
elif path.suffix == '.j2' or path.suffix == '.jinja2':
|
||||
self[path.stem] = Jinja2(path.stem, self, asset)
|
||||
else:
|
||||
name = path.name
|
||||
# Если имя файла не index.html,
|
||||
# то отдавать по имени файла
|
||||
if name == 'index.html':
|
||||
name = 'index'
|
||||
self[name] = Document(name, self, asset)
|
||||
|
||||
def create_dir(self, asset):
|
||||
path = PurePath(asset)
|
||||
self[path.name] = Folder(path.name, self, asset)
|
||||
|
||||
def structure(self, base_dir=''):
|
||||
return flat(self, base_dir)
|
||||
|
||||
|
||||
class Root(Folder):
|
||||
def __init__(self, path):
|
||||
super(Root, self).__init__('', None, path)
|
||||
|
||||
|
||||
class Document(object):
|
||||
def __init__(self, name, parent, path):
|
||||
self.__name__ = name
|
||||
self.__parent__ = parent
|
||||
self.__path__ = path
|
||||
|
||||
|
||||
class Markdown(Document):
|
||||
pass
|
||||
|
||||
|
||||
class YAML(Document):
|
||||
pass
|
||||
|
||||
|
||||
class Jinja2(Document):
|
||||
pass
|
|
@ -0,0 +1,7 @@
|
|||
def includeme(config):
|
||||
settings = config.get_settings()
|
||||
if 'static' in settings:
|
||||
config.add_static_view(
|
||||
'static',
|
||||
settings['static'],
|
||||
cache_max_age=3600)
|
|
@ -0,0 +1,5 @@
|
|||
{% extends "layout.jinja2" %}
|
||||
|
||||
{% set title="404: страница не найдена!" %}
|
||||
{% set description="Попробуйте найти нужную вам страницу в меню сайта." %}
|
||||
{% set image="https://source.unsplash.com/Q1p7bh3SHj8" %}
|
|
@ -0,0 +1,18 @@
|
|||
<!doctype html>
|
||||
<html ⚡>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{title}}</title>
|
||||
<script async src="https://cdn.ampproject.org/v0.js"></script>
|
||||
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
|
||||
<link rel="canonical" href="{{canonical | default(request.url)}}">
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
|
||||
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}
|
||||
{{content}}
|
||||
{% endblock %}
|
||||
{% include 'amp-counter.jinja2' %}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
{%- from 'macros.jinja2' import b -%}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{request.locale_name}}">
|
||||
<head>
|
||||
{% block head -%}
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="{{description}}">
|
||||
<meta name="generator" content="flatfilecms">
|
||||
<link rel="icon" type="image/png" href="/static/img/favicon.png">
|
||||
<title>{{title}}</title>
|
||||
{% block links %}
|
||||
{%- endblock %}
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#333333">
|
||||
{%- if json_ld %}
|
||||
<script type="application/ld+json">
|
||||
{{ json_ld | merge_dict({"@context": "http://schema.org"}) | tojson }}
|
||||
</script>
|
||||
{%- endif -%}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% include 'counter.jinja2' %}
|
||||
{% block body %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<div style="background: url({{jumbotron_image}}) no-repeat center center; background-size: cover">
|
||||
<div class="jumbotron container {{jumbotron_class|default('text-light bg-darken')}}">
|
||||
<h2>{{jumbotron_header}}</h2>
|
||||
{{jumbotron_content}}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
<nav class="nav justify-content-left container py-3 footer-menu">
|
||||
{%- for item in menu recursive %}
|
||||
{% if item.href -%}
|
||||
<a href="{{item.href|e}}" class="nav-link text-light py-0">{{item.title}}</a>
|
||||
{%- endif -%}
|
||||
{{ loop(item.children) }}
|
||||
{%- endfor %}
|
||||
</nav>
|
|
@ -0,0 +1,60 @@
|
|||
<div{% if image %} style="background: linear-gradient(to bottom, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url({{image}}) no-repeat center center; background-size:cover"{% endif %}>
|
||||
<nav class="navbar navbar-expand-sm navbar-dark container">
|
||||
<a href="/" class="navbar-brand">
|
||||
{% include 'logo.jinja2' %}
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div id="navbarSupportedContent" class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
{%- for item in menu %}
|
||||
<li class="nav-item dropdown">
|
||||
{%- if item.children %}
|
||||
<a role="button" class="nav-link dropdown-toggle cursor-default" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{item.title}}</a>
|
||||
<ul class="dropdown-menu">
|
||||
{%- if item.href %}
|
||||
<li>
|
||||
<a href="{{item.href|e}}" class="dropdown-item">{{item.title}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="dropdown-divider"></div>
|
||||
</li>
|
||||
{%- endif -%}
|
||||
{%- for subitem in item.children recursive -%}
|
||||
{%- if subitem.children %}
|
||||
<li class="dropright">
|
||||
<button class="dropdown-item dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{subitem.title}}</button>
|
||||
<ul class="dropdown-menu">
|
||||
{%- if subitem.href %}
|
||||
<li>
|
||||
<a href="{{subitem.href|e}}" class="dropdown-item">{{subitem.title}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="dropdown-divider"></div>
|
||||
</li>
|
||||
{%- endif -%}
|
||||
{{ loop(subitem.children) }}
|
||||
</ul>
|
||||
{%- else -%}
|
||||
<li>
|
||||
<a href="{{subitem.href|e}}" class="dropdown-item">{{subitem.title}}</a>
|
||||
{%- endif -%}
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{%- else %}
|
||||
<a href="{{item.href|e}}" class="nav-link">{{item.title}}</a>
|
||||
{%- endif %}
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="jumbotron container text-white bg-transparent">
|
||||
<h1 class="display-4">{{title}}</h1>
|
||||
{%- if description %}
|
||||
<p class="lead">{{description}}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
{% extends "layout.jinja2" %}
|
||||
|
||||
{% block content %}
|
||||
{{ contents|safe }}
|
||||
{% endblock content %}
|
|
@ -0,0 +1,26 @@
|
|||
{% extends "base.jinja2" %}
|
||||
{% block links %}
|
||||
{% webpack 'layout', '.js' -%}
|
||||
<script src="{{ ASSET.url }}" async defer></script>
|
||||
{% endwebpack %}
|
||||
{% webpack 'layout', '.css' -%}
|
||||
<link rel="stylesheet" type="text/css" href="{{ ASSET.url }}">
|
||||
{% endwebpack %}
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<header>
|
||||
{% include "header.jinja2" %}
|
||||
</header>
|
||||
<main class="py-4">
|
||||
<article class="container">
|
||||
{% block content %}
|
||||
{% if content %}
|
||||
{{content|markdown}}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</article>
|
||||
</main>
|
||||
<footer class="page-footer">
|
||||
{% include "footer.jinja2" %}
|
||||
</footer>
|
||||
{% endblock %}
|
|
@ -0,0 +1,10 @@
|
|||
{% macro b(class=None, tag="div", id=None) -%}
|
||||
<{{ tag }}
|
||||
{%- if class is not none %} class="{{class}}" {%- endif -%}
|
||||
{%- if id is not none %} id="{{id}}" {%- endif -%}
|
||||
>
|
||||
{%- if caller %}
|
||||
{{ caller() }}
|
||||
{% endif -%}
|
||||
</{{ tag }}>
|
||||
{%- endmacro %}
|
|
@ -0,0 +1,42 @@
|
|||
{% extends "base.jinja2" %}
|
||||
{% block links %}
|
||||
{% webpack 'onepage', '.js' -%}
|
||||
<script src="{{ ASSET.url }}" async defer></script>
|
||||
{% endwebpack %}
|
||||
{% webpack 'onepage', '.css' -%}
|
||||
<link rel="stylesheet" type="text/css" href="{{ ASSET.url }}">
|
||||
{% endwebpack %}
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<header>
|
||||
<nav id="nav-page" class="navbar navbar-expand-sm navbar-dark fixed-top">
|
||||
<a href="/" class="navbar-brand">
|
||||
{% include 'logo.jinja2' %}
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse justify-content-end" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav">
|
||||
{%- for item in nav %}
|
||||
<li class="nav-item">
|
||||
<a href="{{item.link}}" class="nav-link">{{item.name}}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<article>
|
||||
{% block content %}
|
||||
{% if content %}
|
||||
{{content|markdown}}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</article>
|
||||
</main>
|
||||
<footer class="page-footer">
|
||||
{% include "footer.jinja2" %}
|
||||
</footer>
|
||||
{% endblock %}
|
|
@ -0,0 +1 @@
|
|||
{% extends "layout.jinja2" %}
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{%- for item in request.root.structure() %}{% if item != '/sitemap.xml' and item not in data.ignore %}
|
||||
<url>
|
||||
<loc>{{data.base_url}}{{item}}</loc>
|
||||
</url>
|
||||
{%- endif %}{%- endfor %}
|
||||
</urlset>
|
|
@ -0,0 +1,20 @@
|
|||
{% extends "layout.jinja2" %}
|
||||
{% block content %}
|
||||
{% filter markdown %}
|
||||
{% if content %}
|
||||
{{content|safe}}
|
||||
{% endif %}
|
||||
{% for name in ['why', 'what', 'who', 'call'] %}
|
||||
{% if name in data %}
|
||||
{{("{% call b(tag='section', id='"+name+"') %}")|safe}}
|
||||
|
||||
{{data[name]|safe}}
|
||||
|
||||
{{"{% endcall %}"|safe}}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if 'abbr' in data %}
|
||||
{{data['abbr']|safe}}
|
||||
{% endif %}
|
||||
{% endfilter %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
from pyramid.view import notfound_view_config
|
||||
|
||||
|
||||
@notfound_view_config(renderer='../templates/404.jinja2')
|
||||
def notfound_view(request):
|
||||
request.response.status = 404
|
||||
return {}
|
|
@ -0,0 +1,120 @@
|
|||
from pyramid.view import (
|
||||
view_config,
|
||||
render_view_to_response,
|
||||
)
|
||||
from pyramid.renderers import render_to_response
|
||||
from pyramid.httpexceptions import (HTTPFound, HTTPNotFound)
|
||||
from pyramid.response import FileResponse
|
||||
from pyramid.path import AssetResolver
|
||||
from pathlib import PurePath
|
||||
from ..resources import (Folder, Document, Markdown, YAML, Jinja2)
|
||||
import yaml
|
||||
import frontmatter
|
||||
|
||||
|
||||
class Loader(yaml.Loader):
|
||||
def __init__(self, stream):
|
||||
super(Loader, self).__init__(stream)
|
||||
Loader.add_constructor('!include', Loader.include)
|
||||
|
||||
def include(self, node):
|
||||
if isinstance(node, yaml.ScalarNode):
|
||||
return self.extractFile(self.construct_scalar(node))
|
||||
|
||||
elif isinstance(node, yaml.SequenceNode):
|
||||
return [self.extractFile(filename)
|
||||
for filename in self.construct_sequence(node)]
|
||||
|
||||
elif isinstance(node, yaml.MappingNode):
|
||||
result = {}
|
||||
for k, v in self.construct_mapping(node).items():
|
||||
result[k] = self.extractFile(v)
|
||||
return result
|
||||
|
||||
else:
|
||||
raise yaml.constructor.ConstructorError(
|
||||
"Error:: unrecognised node type in !include statement")
|
||||
|
||||
def extractFile(self, filename):
|
||||
path = PurePath(self.data_dir) / filename
|
||||
f = AssetResolver().resolve(str(path)).stream()
|
||||
if f:
|
||||
if path.suffix in ['.yaml', '.yml', '.json']:
|
||||
return yaml.load(f, Loader)
|
||||
return f.read().decode()
|
||||
|
||||
|
||||
def LoaderFactory(data_dir):
|
||||
cl = Loader
|
||||
cl.data_dir = data_dir
|
||||
return cl
|
||||
|
||||
|
||||
class CustomYAMLHandler(frontmatter.YAMLHandler):
|
||||
def __init__(self, data_dir):
|
||||
self.loader = LoaderFactory(data_dir)
|
||||
|
||||
def load(self, fm, **kwargs):
|
||||
return yaml.load(fm, self.loader)
|
||||
|
||||
|
||||
class PagesView:
|
||||
def __init__(self, context, request):
|
||||
self.context = context
|
||||
self.request = request
|
||||
|
||||
@view_config(context=Folder)
|
||||
def folder(self):
|
||||
if 'index' not in self.context:
|
||||
raise HTTPNotFound
|
||||
return render_view_to_response(self.context['index'], self.request)
|
||||
|
||||
@view_config(context=Document)
|
||||
def document(self):
|
||||
return FileResponse(
|
||||
AssetResolver().resolve(self.context.__path__).abspath(),
|
||||
request=self.request)
|
||||
|
||||
def process_yaml(self):
|
||||
if 'redirect' in self.post:
|
||||
return HTTPFound(location=self.post.redirect)
|
||||
if 'menu' not in self.post:
|
||||
self.post['menu'] = yaml.load(
|
||||
AssetResolver().resolve(
|
||||
str(PurePath(self.request.registry.settings['data_dir']) /
|
||||
'menu/default.yaml')).stream(),
|
||||
LoaderFactory(self.request.registry.settings['data_dir']))
|
||||
response = render_to_response(
|
||||
'{0}.jinja2'.format(
|
||||
self.post.get('template', 'default')),
|
||||
self.post,
|
||||
request=self.request
|
||||
)
|
||||
if 'content_type' in self.post:
|
||||
response.content_type = self.post['content_type']
|
||||
return response
|
||||
|
||||
@view_config(context=YAML)
|
||||
def yaml(self):
|
||||
self.post = yaml.load(
|
||||
AssetResolver().resolve(
|
||||
self.context.__path__).stream(),
|
||||
LoaderFactory(self.request.registry.settings['data_dir']))
|
||||
return self.process_yaml()
|
||||
|
||||
@view_config(context=Markdown)
|
||||
def markdown(self):
|
||||
self.post = frontmatter.load(
|
||||
AssetResolver().resolve(
|
||||
self.context.__path__).stream(),
|
||||
handler=CustomYAMLHandler(self.request.registry.settings['data_dir']),
|
||||
).to_dict()
|
||||
return self.process_yaml()
|
||||
|
||||
@view_config(context=Jinja2)
|
||||
def jinja2(self):
|
||||
return render_to_response(
|
||||
self.context.path,
|
||||
{},
|
||||
request=self.request
|
||||
)
|
|
@ -0,0 +1,64 @@
|
|||
import os
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
with open(os.path.join(here, 'README.txt')) as f:
|
||||
README = f.read()
|
||||
with open(os.path.join(here, 'CHANGES.txt')) as f:
|
||||
CHANGES = f.read()
|
||||
|
||||
requires = [
|
||||
'plaster_pastedeploy',
|
||||
'pyramid >= 1.9a',
|
||||
'pyramid_debugtoolbar',
|
||||
'pyramid_jinja2',
|
||||
'pyramid_retry',
|
||||
'pyramid_tm',
|
||||
'transaction',
|
||||
'waitress',
|
||||
'Markdown',
|
||||
'PyYAML',
|
||||
'python-frontmatter',
|
||||
'watchdog < 0.9.0',
|
||||
'pyramid-htmlmin',
|
||||
'pyramid-webpack',
|
||||
]
|
||||
|
||||
tests_require = [
|
||||
'WebTest >= 1.3.1', # py3 compat
|
||||
'pytest',
|
||||
'pytest-cov',
|
||||
]
|
||||
|
||||
setup(
|
||||
name='flatfilecms',
|
||||
version='0.0',
|
||||
description='Radium CMS',
|
||||
long_description=README + '\n\n' + CHANGES,
|
||||
classifiers=[
|
||||
'Programming Language :: Python',
|
||||
'Framework :: Pyramid',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
|
||||
],
|
||||
author='Anton Kasimov',
|
||||
author_email='kasimov@radium-it.ru',
|
||||
url='http://www.radium.group',
|
||||
keywords='web pyramid pylons flat-file CMS',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
extras_require={
|
||||
'testing': tests_require,
|
||||
},
|
||||
install_requires=requires,
|
||||
entry_points={
|
||||
'paste.app_factory': [
|
||||
'main = flatfilecms:main',
|
||||
],
|
||||
'console_scripts': [
|
||||
'generate_static_site = flatfilecms.scripts.generate:main',
|
||||
],
|
||||
},
|
||||
)
|
Reference in New Issue