scribeengine/scribeengine/models.py

338 lines
11 KiB
Python

# -*- 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.models` module contains all the database models
"""
from datetime import datetime
from box import Box
from flask_user import UserMixin
from scribeengine.db import Model, Table, Column, ForeignKey, String, Text, Integer, Boolean, DateTime, \
backref, inspect, relationship
permissions_roles = Table(
'permissions_roles',
Column('permission_id', Integer, ForeignKey('permissions.id'), primary_key=True),
Column('role_id', Integer, ForeignKey('roles.id'), primary_key=True)
)
nodes_taxonomies = Table(
'nodes_terms',
Column('node_id', Integer, ForeignKey('nod.id'), primary_key=True),
Column('taxonomy_id', Integer, ForeignKey('taxonomy.id'), primary_key=True)
)
roles_users = Table(
'roles_users',
Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
Column('role_id', Integer, ForeignKey('roles.id'), primary_key=True)
)
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
"""
__tablename__ = 'taxonomies'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
description = Column(Text)
slug = Column(String(255), nullable=False, index=True, unique=True)
def __str__(self):
return self.name
class Node(Model, DictMixin):
"""
Nodes are the basic content type
"""
__tablename__ = 'nodes'
id = Column(Integer, primary_key=True)
slug = Column(String(255), nullable=False, index=True, unique=True)
type = Column(String(255), nullable=False, index=True)
created = Column(DateTime, nullable=False, default=datetime.utcnow)
modified = Column(DateTime, nullable=False, default=datetime.utcnow)
revision_id = Column(Integer, ForeignKey('revisions.id'), nullable=False, index=True)
current_revision = relationship('Revision', backref='node')
@property
def complete(self):
"""Return a "full" or "complete" node, with all fields and data"""
node_dict = self.to_dict()
for field in self.fields:
node_dict[field.name] = Box({
'name': field.name,
'type': field.type,
'title': field.field.title,
'data': field.data
})
return node_dict
class Field(Model, DictMixin):
"""
A field is a model for field types on nodes
"""
__tablename__ = 'fields'
id = Column(Integer, primary_key=True)
name = Column(String(255), nullable=False, unique=True, index=True)
type = Column(String(255), nullable=False)
title = Column(String(255), nullable=False)
description = Column(Text)
created = Column(DateTime, nullable=False, default=datetime.utcnow)
modified = Column(DateTime, nullable=False, default=datetime.utcnow)
class FieldRevision(Model, DictMixin):
"""
A revision of a field on a node
"""
__tablename__ = 'field_revisions'
id = Column(Integer, primary_key=True)
version = Column(Integer, nullable=False)
title = Column(String(255), nullable=False)
body = Column(Text)
format = Column(Text, nullable=False)
slug = Column(String(255), nullable=False)
created = Column(DateTime, nullable=False, index=True, default=datetime.utcnow)
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=False)
class NodeField(Model, DictMixin):
"""
A node field is a field on a particular node
"""
__tablename__ = 'node_fields'
id = Column(Integer, primary_key=True)
field_id = Column(Integer, ForeignKey('fields.id'))
node_id = Column(Integer, ForeignKey('nodes.id'))
revision_id = Column(Integer, ForeignKey('field_revisions.id'), nullable=False, index=True)
title = Column(String(255), nullable=False)
description = Column(Text)
created = Column(DateTime, nullable=False, default=datetime.utcnow)
modified = Column(DateTime, nullable=False, default=datetime.utcnow)
current_revision = relationship('FieldRevision', backref='node_field')
field = relationship('Field', backref='node_field')
class File(Model, DictMixin):
"""
A file in the media library.
"""
__tablename__ = 'files'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
media_type_id = Column(Integer, ForeignKey('media_types.id'), nullable=False)
filename = Column(String(255), nullable=False, index=True)
mimetype = Column(String(255))
path = Column(String(255))
size = Column(Integer, default=0)
def __str__(self):
return self.filename
class MediaType(Model, DictMixin):
"""
Distinguishes between different types of media.
"""
__tablename__ = 'media_types'
id = Column(Integer, primary_key=True)
title = Column(String(255), nullable=False, index=True)
files = relationship('File', backref='media_type')
def __str__(self):
return self.title
class Permission(Model, DictMixin):
"""
A single permission.
"""
__tablename__ = 'permissions'
id = Column(Integer, primary_key=True)
name = Column(String(80), nullable=False, index=True)
description = Column(Text)
def __str__(self):
return self.name
class Role(Model, DictMixin):
"""
A role defines a set of permissions.
"""
__tablename__ = 'roles'
id = Column(Integer, primary_key=True)
name = Column(String(80), nullable=False, index=True)
description = Column(Text)
permissions = relationship('Permission', backref='roles', secondary=permissions_roles)
def __str__(self):
return self.name
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.
"""
__tablename__ = 'terms'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
slug = Column(String(255), nullable=False, index=True)
taxonomy_id = Column(Integer, ForeignKey('taxonomies.id'))
def __str__(self):
return self.name
class User(Model, UserMixin, DictMixin):
"""
The user.
"""
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
email = Column(String(200), nullable=False, unique=True, index=True)
username = Column(String(200), nullable=False, unique=True, index=True)
password = Column(String(64), nullable=False)
nick = Column(String(50), nullable=False, index=True)
first_name = Column(String(100), default='')
last_name = Column(String(100), default='')
homepage = Column(String(200), default='')
timezone = Column(String(200), default='UTC')
activation_key = Column(String(40), default=None)
confirmed_at = Column(DateTime)
active = Column('is_active', Boolean, nullable=False, server_default='0')
comments = relationship('Comment', backref='user')
files = relationship('File', backref='user')
posts = relationship('Post', backref='author')
roles = relationship('Role', backref='users', secondary=roles_users)
def has_permission(self, permission):
if isinstance(permission, str):
for role in self.roles:
for perm in role.permissions:
if perm.name == permission:
return True
return False
elif isinstance(permission, Permission):
for role in self.roles:
for perm in role.permissions:
if perm == permission:
return True
return False
elif isinstance(permission, list):
for role in self.roles:
for perm in role.permissions:
if perm.name in permission:
return True
return False
else:
return False
def __str__(self):
return str(id(self))
class Variable(Model, DictMixin):
"""
System variables.
"""
__tablename__ = 'variables'
key = Column(String, primary_key=True, index=True)
value = Column(Text, nullable=False)
type = Column(String, default='string')
class Site(object):
"""
Generic model to hold the site-specfic data
"""
name = None
slogan = None
about = None
def __init__(self, name, slogan=None, about=None):
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]))