diff --git a/scribeengine/__init__.py b/scribeengine/__init__.py
index 678d7cd..388d520 100644
--- a/scribeengine/__init__.py
+++ b/scribeengine/__init__.py
@@ -1,23 +1,23 @@
# -*- 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 #
-# under the terms of the GNU General Public License as published by the Free #
-# Software Foundation; version 2 of the License. #
+# This file is part of ScribeEngine. #
# #
-# 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 #
-# 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. #
# #
# 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 #
+# with ScribeEngine. If not, see . #
###############################################################################
"""
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.models import User
from scribeengine.views.account import account
-from scribeengine.views.blog import blog
+from scribeengine.views.node import node_views
def _scribeengine_themes_loader(app):
@@ -69,7 +69,7 @@ def create_app(config_file=None):
UserManager(application, db, User)
# Register all the blueprints
application.register_blueprint(admin)
- application.register_blueprint(blog)
+ application.register_blueprint(node_views)
application.register_blueprint(account)
# Return the application object
return application
diff --git a/scribeengine/db.py b/scribeengine/db.py
index 18af498..572a02e 100644
--- a/scribeengine/db.py
+++ b/scribeengine/db.py
@@ -1,23 +1,23 @@
# -*- 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 #
-# under the terms of the GNU General Public License as published by the Free #
-# Software Foundation; version 2 of the License. #
+# This file is part of ScribeEngine. #
# #
-# 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 #
-# 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. #
# #
# 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 #
+# with ScribeEngine. If not, see . #
###############################################################################
"""
The :mod:`~scribeengine.db` module sets up SQLAlchemy
@@ -27,7 +27,7 @@ from flask_sqlalchemy import SQLAlchemy
# Get the db object
db = SQLAlchemy()
-# Extract the classes to make them prettier
+# Extract the objects to make them prettier
Model = db.Model
Table = db.Table
Column = db.Column
@@ -39,4 +39,5 @@ Boolean = db.Boolean
DateTime = db.DateTime
relationship = db.relationship
backref = db.backref
+inspect = db.inspect
session = db.session
diff --git a/scribeengine/helpers.py b/scribeengine/helpers.py
index 6c53cd2..8eb5a98 100644
--- a/scribeengine/helpers.py
+++ b/scribeengine/helpers.py
@@ -1,40 +1,76 @@
# -*- 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 #
-# under the terms of the GNU General Public License as published by the Free #
-# Software Foundation; version 2 of the License. #
+# This file is part of ScribeEngine. #
# #
-# 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 #
-# 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. #
# #
# 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 #
+# with ScribeEngine. If not, see . #
###############################################################################
"""
The :mod:`~scribeengine.helpers` module contains some theme helper methods
"""
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():
"""
Returns an object with the details of the site
"""
- return Site(current_app.config.get('SITE_NAME', 'Example Blog'),
- current_app.config.get('SITE_SLOGAN', 'From the Firehose'),
- current_app.config.get('SITE_ABOUT', ''))
+ return Site(get_variable('site-name', 'Example Site'),
+ get_variable('site-slogan', 'From the Firehose'),
+ get_variable('site-about', ''))
+
+
+def get_navigation(block='primary'):
+ """
+ Get the navigation
+ """
+ return Menu.query.filter(Menu.block == block).first()
def get_current_theme():
@@ -49,10 +85,36 @@ def render(template, **context):
"""
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)
+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):
"""
Render a template, after selecting a theme
diff --git a/scribeengine/models.py b/scribeengine/models.py
index d3106e4..89a6677 100644
--- a/scribeengine/models.py
+++ b/scribeengine/models.py
@@ -1,23 +1,23 @@
# -*- 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 #
-# under the terms of the GNU General Public License as published by the Free #
-# Software Foundation; version 2 of the License. #
+# This file is part of ScribeEngine. #
# #
-# 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 #
-# 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. #
# #
# 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 #
+# with ScribeEngine. If not, see . #
###############################################################################
"""
The :mod:`~scribeengine.models` module contains all the database models
@@ -28,7 +28,7 @@ from box import Box
from flask_user import UserMixin
from scribeengine.db import Model, Table, Column, ForeignKey, String, Text, Integer, Boolean, DateTime, \
- relationship
+ backref, inspect, relationship
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
"""
@@ -67,7 +75,7 @@ class Taxonomy(Model):
return self.name
-class Node(Model):
+class Node(Model, DictMixin):
"""
Nodes are the basic content type
"""
@@ -85,27 +93,18 @@ class Node(Model):
@property
def complete(self):
"""Return a "full" or "complete" node, with all fields and data"""
- node_dict = Box({
- '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
- })
+ node_dict = self.to_dict()
for field in self.fields:
- node_dict[field.name] = {
+ node_dict[field.name] = Box({
'name': field.name,
'type': field.type,
'title': field.field.title,
'data': field.data
- }
+ })
return node_dict
-class Field(Model):
+class Field(Model, DictMixin):
"""
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)
-class FieldRevision(Model):
+class FieldRevision(Model, DictMixin):
"""
A revision of a field on a node
"""
@@ -136,7 +135,7 @@ class FieldRevision(Model):
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
"""
@@ -155,7 +154,7 @@ class NodeField(Model):
field = relationship('Field', backref='node_field')
-class File(Model):
+class File(Model, DictMixin):
"""
A file in the media library.
"""
@@ -173,7 +172,7 @@ class File(Model):
return self.filename
-class MediaType(Model):
+class MediaType(Model, DictMixin):
"""
Distinguishes between different types of media.
"""
@@ -188,7 +187,7 @@ class MediaType(Model):
return self.title
-class Permission(Model):
+class Permission(Model, DictMixin):
"""
A single permission.
"""
@@ -202,7 +201,7 @@ class Permission(Model):
return self.name
-class Role(Model):
+class Role(Model, DictMixin):
"""
A role defines a set of permissions.
"""
@@ -218,7 +217,7 @@ class Role(Model):
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.
"""
@@ -233,7 +232,7 @@ class Term(Model):
return self.name
-class User(Model, UserMixin):
+class User(Model, UserMixin, DictMixin):
"""
The user.
"""
@@ -283,15 +282,15 @@ class User(Model, UserMixin):
return str(id(self))
-class Variable(Model):
+class Variable(Model, DictMixin):
"""
System variables.
"""
__tablename__ = 'variables'
- key = Column(String(100), primary_key=True, index=True)
- value = Column(String(100), nullable=False)
- type = Column(String(10), default='string')
+ key = Column(String, primary_key=True, index=True)
+ value = Column(Text, nullable=False)
+ type = Column(String, default='string')
class Site(object):
@@ -306,3 +305,33 @@ class Site(object):
self.name = name
self.slogan = slogan
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]))
diff --git a/scribeengine/views/blog.py b/scribeengine/views/blog.py
deleted file mode 100644
index 6264bab..0000000
--- a/scribeengine/views/blog.py
+++ /dev/null
@@ -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('//', methods=['GET'])
-def archive(year, month=None):
- posts = Post.query.limit(10).all()
- return render('/blog.html', title='ScribeEngine', posts=posts)
diff --git a/scribeengine/views/node.py b/scribeengine/views/node.py
new file mode 100644
index 0000000..9fd43f3
--- /dev/null
+++ b/scribeengine/views/node.py
@@ -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 . #
+###############################################################################
+"""
+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/', 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)