2017-06-09 06:25:41 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
###############################################################################
|
2021-06-11 21:31:14 +00:00
|
|
|
# ScribeEngine - Open Source Content Management System #
|
2017-06-09 06:25:41 +00:00
|
|
|
# --------------------------------------------------------------------------- #
|
2021-06-11 21:31:14 +00:00
|
|
|
# Copyright (c) 2010-2021 Raoul Snyman #
|
2017-06-09 06:25:41 +00:00
|
|
|
# --------------------------------------------------------------------------- #
|
2021-06-11 21:31:14 +00:00
|
|
|
# This file is part of ScribeEngine. #
|
|
|
|
# #
|
|
|
|
# ScribeEngine is free software: you can redistribute it and/or modify it #
|
2017-06-09 06:25:41 +00:00
|
|
|
# under the terms of the GNU General Public License as published by the Free #
|
2021-06-11 21:31:14 +00:00
|
|
|
# Software Foundation, either version 3 of the License, or (at your option) #
|
|
|
|
# any later version. #
|
2017-06-09 06:25:41 +00:00
|
|
|
# #
|
2021-06-11 21:31:14 +00:00
|
|
|
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
|
2017-06-09 06:25:41 +00:00
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
2021-06-11 21:31:14 +00:00
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
2017-06-09 06:25:41 +00:00
|
|
|
# more details. #
|
|
|
|
# #
|
|
|
|
# You should have received a copy of the GNU General Public License along #
|
2021-06-11 21:31:14 +00:00
|
|
|
# with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
|
2017-06-09 06:25:41 +00:00
|
|
|
###############################################################################
|
|
|
|
"""
|
|
|
|
The :mod:`~scribeengine.models` module contains all the database models
|
|
|
|
"""
|
2021-01-30 04:19:48 +00:00
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
from box import Box
|
2017-06-09 06:25:41 +00:00
|
|
|
from flask_user import UserMixin
|
|
|
|
|
|
|
|
from scribeengine.db import Model, Table, Column, ForeignKey, String, Text, Integer, Boolean, DateTime, \
|
2021-06-11 21:31:14 +00:00
|
|
|
backref, inspect, relationship
|
2017-06-09 06:25:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-01-30 04:19:48 +00:00
|
|
|
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)
|
2017-06-09 06:25:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
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):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
2021-01-30 04:19:48 +00:00
|
|
|
This is a grouping of related terms, like a tags or categories
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
2021-01-30 04:19:48 +00:00
|
|
|
__tablename__ = 'taxonomies'
|
2017-06-09 06:25:41 +00:00
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
|
|
name = Column(String(100), nullable=False)
|
|
|
|
description = Column(Text)
|
2021-01-30 04:19:48 +00:00
|
|
|
slug = Column(String(255), nullable=False, index=True, unique=True)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
2017-06-09 06:25:41 +00:00
|
|
|
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class Node(Model, DictMixin):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
2021-01-30 04:19:48 +00:00
|
|
|
Nodes are the basic content type
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
2021-01-30 04:19:48 +00:00
|
|
|
__tablename__ = 'nodes'
|
2017-06-09 06:25:41 +00:00
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True)
|
2021-01-30 04:19:48 +00:00
|
|
|
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"""
|
2021-06-11 21:31:14 +00:00
|
|
|
node_dict = self.to_dict()
|
2021-01-30 04:19:48 +00:00
|
|
|
for field in self.fields:
|
2021-06-11 21:31:14 +00:00
|
|
|
node_dict[field.name] = Box({
|
2021-01-30 04:19:48 +00:00
|
|
|
'name': field.name,
|
|
|
|
'type': field.type,
|
|
|
|
'title': field.field.title,
|
|
|
|
'data': field.data
|
2021-06-11 21:31:14 +00:00
|
|
|
})
|
2021-01-30 04:19:48 +00:00
|
|
|
return node_dict
|
|
|
|
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class Field(Model, DictMixin):
|
2021-05-28 02:22:47 +00:00
|
|
|
"""
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class FieldRevision(Model, DictMixin):
|
2021-01-30 04:19:48 +00:00
|
|
|
"""
|
2021-05-28 02:22:47 +00:00
|
|
|
A revision of a field on a node
|
2021-01-30 04:19:48 +00:00
|
|
|
"""
|
2021-05-28 02:22:47 +00:00
|
|
|
__tablename__ = 'field_revisions'
|
2021-01-30 04:19:48 +00:00
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True)
|
2021-05-28 02:22:47 +00:00
|
|
|
version = Column(Integer, nullable=False)
|
2021-01-30 04:19:48 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class NodeField(Model, DictMixin):
|
2021-01-30 04:19:48 +00:00
|
|
|
"""
|
2021-05-28 02:22:47 +00:00
|
|
|
A node field is a field on a particular node
|
2021-01-30 04:19:48 +00:00
|
|
|
"""
|
2021-05-28 02:22:47 +00:00
|
|
|
__tablename__ = 'node_fields'
|
2021-01-30 04:19:48 +00:00
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True)
|
2021-05-28 02:22:47 +00:00
|
|
|
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)
|
2021-01-30 04:19:48 +00:00
|
|
|
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)
|
2017-06-09 06:25:41 +00:00
|
|
|
|
2021-05-28 02:22:47 +00:00
|
|
|
current_revision = relationship('FieldRevision', backref='node_field')
|
|
|
|
field = relationship('Field', backref='node_field')
|
|
|
|
|
2017-06-09 06:25:41 +00:00
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class File(Model, DictMixin):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
|
|
|
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)
|
|
|
|
|
2021-01-30 04:19:48 +00:00
|
|
|
def __str__(self):
|
|
|
|
return self.filename
|
|
|
|
|
2017-06-09 06:25:41 +00:00
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class MediaType(Model, DictMixin):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
|
|
|
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')
|
|
|
|
|
2021-01-30 04:19:48 +00:00
|
|
|
def __str__(self):
|
|
|
|
return self.title
|
2017-06-09 06:25:41 +00:00
|
|
|
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class Permission(Model, DictMixin):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
|
|
|
A single permission.
|
|
|
|
"""
|
|
|
|
__tablename__ = 'permissions'
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
|
|
name = Column(String(80), nullable=False, index=True)
|
|
|
|
description = Column(Text)
|
|
|
|
|
2021-01-30 04:19:48 +00:00
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
2017-06-09 06:25:41 +00:00
|
|
|
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class Role(Model, DictMixin):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
|
|
|
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)
|
|
|
|
|
2021-01-30 04:19:48 +00:00
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
|
|
|
|
2017-06-09 06:25:41 +00:00
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class Term(Model, DictMixin):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
2021-01-30 04:19:48 +00:00
|
|
|
A term is an item in a taxonomy. A term can be a category name, or a tag in a blog post.
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
2021-01-30 04:19:48 +00:00
|
|
|
__tablename__ = 'terms'
|
2017-06-09 06:25:41 +00:00
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
|
|
name = Column(String(100), nullable=False)
|
2021-01-30 04:19:48 +00:00
|
|
|
slug = Column(String(255), nullable=False, index=True)
|
|
|
|
taxonomy_id = Column(Integer, ForeignKey('taxonomies.id'))
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
2017-06-09 06:25:41 +00:00
|
|
|
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class User(Model, UserMixin, DictMixin):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
|
|
|
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')
|
|
|
|
|
2019-02-14 23:01:27 +00:00
|
|
|
comments = relationship('Comment', backref='user')
|
|
|
|
files = relationship('File', backref='user')
|
2021-01-30 04:19:48 +00:00
|
|
|
posts = relationship('Post', backref='author')
|
2017-06-09 06:25:41 +00:00
|
|
|
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
|
|
|
|
|
2021-01-30 04:19:48 +00:00
|
|
|
def __str__(self):
|
|
|
|
return str(id(self))
|
|
|
|
|
2017-06-09 06:25:41 +00:00
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
class Variable(Model, DictMixin):
|
2017-06-09 06:25:41 +00:00
|
|
|
"""
|
|
|
|
System variables.
|
|
|
|
"""
|
|
|
|
__tablename__ = 'variables'
|
|
|
|
|
2021-06-11 21:31:14 +00:00
|
|
|
key = Column(String, primary_key=True, index=True)
|
|
|
|
value = Column(Text, nullable=False)
|
|
|
|
type = Column(String, default='string')
|
2019-02-14 23:01:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
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
|
2021-06-11 21:31:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
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]))
|