Enhance node rendering, add template selection logic, add menu items to models and helpers.

This commit is contained in:
Raoul Snyman 2021-06-11 14:31:14 -07:00
parent a966f018f7
commit 2e853e4c5c
6 changed files with 217 additions and 123 deletions

View File

@ -1,23 +1,23 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
############################################################################### ###############################################################################
# ScribeEngine - Open Source Blog Software # # ScribeEngine - Open Source Content Management System #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Copyright (c) 2010-2017 Raoul Snyman # # Copyright (c) 2010-2021 Raoul Snyman #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it # # This file is part of ScribeEngine. #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# # # #
# This program is distributed in the hope that it will be useful, but WITHOUT # # ScribeEngine is free software: you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. # # more details. #
# # # #
# You should have received a copy of the GNU General Public License along # # You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`~scribeengine` module sets up and runs ScribeEngine The :mod:`~scribeengine` module sets up and runs ScribeEngine
@ -34,7 +34,7 @@ from scribeengine.config import read_config_from_file
from scribeengine.db import db from scribeengine.db import db
from scribeengine.models import User from scribeengine.models import User
from scribeengine.views.account import account from scribeengine.views.account import account
from scribeengine.views.blog import blog from scribeengine.views.node import node_views
def _scribeengine_themes_loader(app): def _scribeengine_themes_loader(app):
@ -69,7 +69,7 @@ def create_app(config_file=None):
UserManager(application, db, User) UserManager(application, db, User)
# Register all the blueprints # Register all the blueprints
application.register_blueprint(admin) application.register_blueprint(admin)
application.register_blueprint(blog) application.register_blueprint(node_views)
application.register_blueprint(account) application.register_blueprint(account)
# Return the application object # Return the application object
return application return application

View File

@ -1,23 +1,23 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
############################################################################### ###############################################################################
# ScribeEngine - Open Source Blog Software # # ScribeEngine - Open Source Content Management System #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Copyright (c) 2010-2017 Raoul Snyman # # Copyright (c) 2010-2021 Raoul Snyman #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it # # This file is part of ScribeEngine. #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# # # #
# This program is distributed in the hope that it will be useful, but WITHOUT # # ScribeEngine is free software: you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. # # more details. #
# # # #
# You should have received a copy of the GNU General Public License along # # You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`~scribeengine.db` module sets up SQLAlchemy The :mod:`~scribeengine.db` module sets up SQLAlchemy
@ -27,7 +27,7 @@ from flask_sqlalchemy import SQLAlchemy
# Get the db object # Get the db object
db = SQLAlchemy() db = SQLAlchemy()
# Extract the classes to make them prettier # Extract the objects to make them prettier
Model = db.Model Model = db.Model
Table = db.Table Table = db.Table
Column = db.Column Column = db.Column
@ -39,4 +39,5 @@ Boolean = db.Boolean
DateTime = db.DateTime DateTime = db.DateTime
relationship = db.relationship relationship = db.relationship
backref = db.backref backref = db.backref
inspect = db.inspect
session = db.session session = db.session

View File

@ -1,40 +1,76 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
############################################################################### ###############################################################################
# ScribeEngine - Open Source Blog Software # # ScribeEngine - Open Source Content Management System #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Copyright (c) 2010-2017 Raoul Snyman # # Copyright (c) 2010-2021 Raoul Snyman #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it # # This file is part of ScribeEngine. #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# # # #
# This program is distributed in the hope that it will be useful, but WITHOUT # # ScribeEngine is free software: you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. # # more details. #
# # # #
# You should have received a copy of the GNU General Public License along # # You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`~scribeengine.helpers` module contains some theme helper methods The :mod:`~scribeengine.helpers` module contains some theme helper methods
""" """
from flask import current_app from flask import current_app
from flask_themes2 import get_theme, render_theme_template from flask_themes2 import get_theme, render_theme_template, template_exists as ft2_template_exists
from jinja2.loaders import TemplateNotFound, contextfunction
from scribeengine.models import Site from scribeengine.models import Menu, Site, Variable
@contextfunction
def template_exists(context, template_name):
"""
Add the template_exists() method into the template context
"""
return ft2_template_exists(template_name)
def get_variable(name, default_value=None):
def _type_cast(type_, value, default_value):
try:
return type_(var.value)
except (TypeError, ValueError):
return default_value
var = Variable.get(name)
if var:
if var.type.lower().startswith('int'):
return _type_cast(int, var.value, default_value)
elif var.type.lower().startswith('float'):
return _type_cast(float, var.value, default_value)
elif var.type.lower().startswith('bool'):
return var.value.lower().strip() in ['yes', 'y', '1', 'true', 't']
return var.value
else:
return default_value
def get_site_details(): def get_site_details():
""" """
Returns an object with the details of the site Returns an object with the details of the site
""" """
return Site(current_app.config.get('SITE_NAME', 'Example Blog'), return Site(get_variable('site-name', 'Example Site'),
current_app.config.get('SITE_SLOGAN', 'From the Firehose'), get_variable('site-slogan', 'From the Firehose'),
current_app.config.get('SITE_ABOUT', '')) get_variable('site-about', ''))
def get_navigation(block='primary'):
"""
Get the navigation
"""
return Menu.query.filter(Menu.block == block).first()
def get_current_theme(): def get_current_theme():
@ -49,10 +85,36 @@ def render(template, **context):
""" """
Render a template, after selecting a theme Render a template, after selecting a theme
""" """
context.update({'site': get_site_details(), 'archives': []}) context.update({'site': get_site_details(), 'navigation': get_navigation()})
return render_theme_template(get_current_theme(), template, **context) return render_theme_template(get_current_theme(), template, **context)
def render_node(node):
"""
Render a template, after selecting a theme
"""
context = {'node': node.complete}
template_names = ['/node-{}.html'.format(node.type), '/node.html']
for template in template_names:
if ft2_template_exists(template):
return render(template, **context)
raise TemplateNotFound('Could not find a node template')
def render_node_list(nodes):
"""
Render a list of nodes
"""
context = {'nodes': [node.complete for node in nodes]}
template_names = ['/node-list.html']
if nodes:
template_names.insert(0, '/node-{}-list.html'.format(nodes[0].type))
for template in template_names:
if ft2_template_exists(template):
return render(template, **context)
raise TemplateNotFound('Could not find a node-list template')
def render_admin(template, **context): def render_admin(template, **context):
""" """
Render a template, after selecting a theme Render a template, after selecting a theme

View File

@ -1,23 +1,23 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
############################################################################### ###############################################################################
# ScribeEngine - Open Source Blog Software # # ScribeEngine - Open Source Content Management System #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Copyright (c) 2010-2017 Raoul Snyman # # Copyright (c) 2010-2021 Raoul Snyman #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it # # This file is part of ScribeEngine. #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# # # #
# This program is distributed in the hope that it will be useful, but WITHOUT # # ScribeEngine is free software: you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. # # more details. #
# # # #
# You should have received a copy of the GNU General Public License along # # You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`~scribeengine.models` module contains all the database models The :mod:`~scribeengine.models` module contains all the database models
@ -28,7 +28,7 @@ from box import Box
from flask_user import UserMixin from flask_user import UserMixin
from scribeengine.db import Model, Table, Column, ForeignKey, String, Text, Integer, Boolean, DateTime, \ from scribeengine.db import Model, Table, Column, ForeignKey, String, Text, Integer, Boolean, DateTime, \
relationship backref, inspect, relationship
permissions_roles = Table( permissions_roles = Table(
@ -52,7 +52,15 @@ roles_users = Table(
) )
class Taxonomy(Model): class DictMixin(object):
"""
A mixin class to add a "to_dict" method to each model
"""
def to_dict(self):
return Box({c.key: getattr(self, c.key) for c in inspect(self).mapper.column_attrs})
class Taxonomy(Model, DictMixin):
""" """
This is a grouping of related terms, like a tags or categories This is a grouping of related terms, like a tags or categories
""" """
@ -67,7 +75,7 @@ class Taxonomy(Model):
return self.name return self.name
class Node(Model): class Node(Model, DictMixin):
""" """
Nodes are the basic content type Nodes are the basic content type
""" """
@ -85,27 +93,18 @@ class Node(Model):
@property @property
def complete(self): def complete(self):
"""Return a "full" or "complete" node, with all fields and data""" """Return a "full" or "complete" node, with all fields and data"""
node_dict = Box({ node_dict = self.to_dict()
'id': self.id,
'slug': self.slug,
'type': self.type,
'created': self.created,
'modified': self.modified,
'title': self.current_revision.title,
'body': self.current_revision.body,
'format': self.current_revision.format
})
for field in self.fields: for field in self.fields:
node_dict[field.name] = { node_dict[field.name] = Box({
'name': field.name, 'name': field.name,
'type': field.type, 'type': field.type,
'title': field.field.title, 'title': field.field.title,
'data': field.data 'data': field.data
} })
return node_dict return node_dict
class Field(Model): class Field(Model, DictMixin):
""" """
A field is a model for field types on nodes A field is a model for field types on nodes
""" """
@ -120,7 +119,7 @@ class Field(Model):
modified = Column(DateTime, nullable=False, default=datetime.utcnow) modified = Column(DateTime, nullable=False, default=datetime.utcnow)
class FieldRevision(Model): class FieldRevision(Model, DictMixin):
""" """
A revision of a field on a node A revision of a field on a node
""" """
@ -136,7 +135,7 @@ class FieldRevision(Model):
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=False) node_id = Column(Integer, ForeignKey('nodes.id'), nullable=False)
class NodeField(Model): class NodeField(Model, DictMixin):
""" """
A node field is a field on a particular node A node field is a field on a particular node
""" """
@ -155,7 +154,7 @@ class NodeField(Model):
field = relationship('Field', backref='node_field') field = relationship('Field', backref='node_field')
class File(Model): class File(Model, DictMixin):
""" """
A file in the media library. A file in the media library.
""" """
@ -173,7 +172,7 @@ class File(Model):
return self.filename return self.filename
class MediaType(Model): class MediaType(Model, DictMixin):
""" """
Distinguishes between different types of media. Distinguishes between different types of media.
""" """
@ -188,7 +187,7 @@ class MediaType(Model):
return self.title return self.title
class Permission(Model): class Permission(Model, DictMixin):
""" """
A single permission. A single permission.
""" """
@ -202,7 +201,7 @@ class Permission(Model):
return self.name return self.name
class Role(Model): class Role(Model, DictMixin):
""" """
A role defines a set of permissions. A role defines a set of permissions.
""" """
@ -218,7 +217,7 @@ class Role(Model):
return self.name return self.name
class Term(Model): class Term(Model, DictMixin):
""" """
A term is an item in a taxonomy. A term can be a category name, or a tag in a blog post. A term is an item in a taxonomy. A term can be a category name, or a tag in a blog post.
""" """
@ -233,7 +232,7 @@ class Term(Model):
return self.name return self.name
class User(Model, UserMixin): class User(Model, UserMixin, DictMixin):
""" """
The user. The user.
""" """
@ -283,15 +282,15 @@ class User(Model, UserMixin):
return str(id(self)) return str(id(self))
class Variable(Model): class Variable(Model, DictMixin):
""" """
System variables. System variables.
""" """
__tablename__ = 'variables' __tablename__ = 'variables'
key = Column(String(100), primary_key=True, index=True) key = Column(String, primary_key=True, index=True)
value = Column(String(100), nullable=False) value = Column(Text, nullable=False)
type = Column(String(10), default='string') type = Column(String, default='string')
class Site(object): class Site(object):
@ -306,3 +305,33 @@ class Site(object):
self.name = name self.name = name
self.slogan = slogan self.slogan = slogan
self.about = about self.about = about
class Menu(Model, DictMixin):
"""
A menu
"""
__tablename__ = 'menus'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
description = Column(Text)
block = Column(String)
items = relationship('MenuItem', backref='menu')
class MenuItem(Model, DictMixin):
"""
A menu item
"""
__tablename__ = 'menu_items'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
url = Column(String, nullable=False)
weight = Column(Integer, default=0)
menu_id = Column(Integer, ForeignKey('menus.id'))
parent_id = Column(Integer, ForeignKey('menu_items.id'))
items = relationship('MenuItem', backref=backref('parent', remote_side=[id]))

View File

@ -1,42 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# ScribeEngine - Open Source Blog Software #
# --------------------------------------------------------------------------- #
# Copyright (c) 2010-2017 Raoul Snyman #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~scribeengine.views.blog` module contains the blog post views
"""
from flask import Blueprint
from scribeengine.helpers import render
from scribeengine.models import Post
blog = Blueprint('blog', __name__, url_prefix='/')
@blog.route('', methods=['GET'])
def index():
posts = Post.query.limit(10).all()
return render('/blog.html', title='ScribeEngine', posts=posts)
@blog.route('/<int:year>/<int:month>', methods=['GET'])
def archive(year, month=None):
posts = Post.query.limit(10).all()
return render('/blog.html', title='ScribeEngine', posts=posts)

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
###############################################################################
# ScribeEngine - Open Source Content Management System #
# --------------------------------------------------------------------------- #
# Copyright (c) 2010-2021 Raoul Snyman #
# --------------------------------------------------------------------------- #
# This file is part of ScribeEngine. #
# #
# ScribeEngine is free software: you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
###############################################################################
"""
The :mod:`~scribeengine.views.node` module contains methods that actually render content nodes
"""
from flask import Blueprint
from scribeengine.helpers import render
from scribeengine.models import Node
node_views = Blueprint('node', __name__, url_prefix='/')
@node_views.route('', methods=['GET'])
def index():
nodes = Node.query.limit(10).all()
return render('/node-list.html', title='ScribeEngine', nodes=[n.complete for n in nodes])
@node_views.route('/node/<node_id>', methods=['GET'])
def view(node_id):
node = Node.get(node_id)
if not node:
return render('/404.html', title='ScribeEngine'), 404
return render('/blog.html', title='ScribeEngine', node=node.complete)