wcs/wcs/categories.py

356 lines
11 KiB
Python

# w.c.s. - web application for online forms
# Copyright (C) 2005-2010 Entr'ouvert
#
# 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; either version 2 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
import xml.etree.ElementTree as ET
from quixote import get_publisher
from quixote.html import htmltext
from .qommon import _
from .qommon.misc import simplify, xml_node_text
from .qommon.storage import StorableObject
from .qommon.substitution import Substitutions
from .qommon.xml_storage import XmlStorableObject
class Category(XmlStorableObject):
_names = 'categories'
xml_root_node = 'category'
backoffice_class = 'wcs.admin.categories.CategoryPage'
backoffice_base_url = 'forms/categories/'
verbose_name_plural = _('Categories')
name = None
url_name = None
description = None
position = None
redirect_url = None
_export_roles = None
_statistics_roles = None
_management_roles = None
# declarations for serialization
XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('redirect_url', 'str'),
('position', 'int'),
('export_roles', 'roles'),
('statistics_roles', 'roles'),
('management_roles', 'roles'),
]
def __init__(self, name=None):
StorableObject.__init__(self)
self.name = name
@classmethod
def get_object_class(cls):
from .formdef import FormDef
return FormDef
@classmethod
def get_by_urlname(cls, url_name, ignore_errors=False):
objects = [x for x in cls.select() if x.url_name == url_name]
if objects:
return objects[0]
if ignore_errors:
return None
raise KeyError()
get_by_slug = get_by_urlname
@property
def slug(self):
return self.url_name
@classmethod
def has_urlname(cls, url_name):
objects = [x for x in cls.select() if x.url_name == url_name]
if objects:
return True
return False
def get_admin_url(self):
return '%s/%s%s/' % (get_publisher().get_backoffice_url(), self.backoffice_base_url, self.id)
def store(
self, *args, comment=None, snapshot_store_user=True, application=None, store_snapshot=True, **kwargs
):
if not self.url_name:
existing_slugs = {
x.url_name: True for x in self.select(ignore_migration=True, ignore_errors=True)
}
base_slug = simplify(self.name)
if base_slug in get_publisher().root_directory_class._q_exports:
base_slug = 'cat-%s' % base_slug
self.url_name = base_slug
i = 2
while self.url_name in existing_slugs:
self.url_name = '%s-%s' % (base_slug, i)
i += 1
super().store(*args, **kwargs)
if get_publisher().snapshot_class and store_snapshot:
get_publisher().snapshot_class.snap(
instance=self, comment=comment, store_user=snapshot_store_user, application=application
)
@classmethod
def sort_by_position(cls, categories):
# move categories with no defined position to the end
categories.sort(key=lambda x: x.position if x and x.position is not None else 10000)
def remove_self(self):
for obj in self.get_object_class().select(lambda x: x.category_id == self.id):
obj.category_id = None
obj.store()
super().remove_self()
def get_substitution_variables(self, minimal=False):
d = {
'category_name': self.name,
'category_id': self.url_name,
'category_slug': self.url_name,
}
if not minimal:
d.update(
{
'category_description': self.description,
}
)
return d
def get_url(self):
base_url = get_publisher().get_frontoffice_url()
return '%s/%s/' % (base_url, self.url_name)
def get_description_html_text(self):
if not self.description:
return None
text = self.description
if text[0] != '<':
text = '<p>%s</p>' % text
return htmltext(text)
def has_permission(self, permission_name, user):
if user.is_admin:
return True
permission_roles = getattr(self, '%s_roles' % permission_name, None) or []
if not permission_roles:
return True
user_roles = set(user.get_roles()) if user else set()
return bool(user_roles.intersection([x.id for x in permission_roles]))
@classmethod
def object_category_xml_export(cls, obj, root, include_id):
if obj.category:
elem = ET.SubElement(root, 'category')
elem.attrib['slug'] = str(obj.category.slug)
elem.text = obj.category.name
if include_id:
elem.attrib['category_id'] = str(obj.category.id)
@classmethod
def object_category_xml_import(cls, obj, tree, include_id):
if tree.find('category') is None:
return
category_node = tree.find('category')
if include_id and category_node.attrib.get('category_id'):
category_id = str(category_node.attrib.get('category_id'))
if cls.has_key(category_id):
obj.category_id = category_id
elif category_node.attrib.get('slug'):
category = cls.get_by_slug(category_node.attrib.get('slug'), ignore_errors=True)
if category:
obj.category_id = category.id
else:
# legacy fallback to name lookup
category = xml_node_text(category_node)
for c in cls.select():
if c.name == category:
obj.category_id = c.id
break
@property
def export_roles(self):
return self._export_roles() if callable(self._export_roles) else self._export_roles
@export_roles.setter
def export_roles(self, value):
self._export_roles = value
@property
def statistics_roles(self):
return self._statistics_roles() if callable(self._statistics_roles) else self._statistics_roles
@statistics_roles.setter
def statistics_roles(self, value):
self._statistics_roles = value
@property
def management_roles(self):
return self._management_roles() if callable(self._management_roles) else self._management_roles
@management_roles.setter
def management_roles(self, value):
self._management_roles = value
def i18n_scan(self):
location = '%s:%s' % (self.xml_root_node, self.id)
yield location, None, self.name
yield location, None, self.description
class CardDefCategory(Category):
_names = 'carddef_categories'
xml_root_node = 'carddef_category'
backoffice_class = 'wcs.admin.categories.CardDefCategoryPage'
backoffice_base_url = 'cards/categories/'
verbose_name_plural = _('Categories')
# declarations for serialization
XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('position', 'int'),
('export_roles', 'roles'),
('management_roles', 'roles'),
]
@classmethod
def get_object_class(cls):
from .carddef import CardDef
return CardDef
class WorkflowCategory(Category):
_names = 'workflow_categories'
xml_root_node = 'workflow_category'
backoffice_class = 'wcs.admin.categories.WorkflowCategoryPage'
backoffice_base_url = 'workflows/categories/'
# declarations for serialization
XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('position', 'int'),
('management_roles', 'roles'),
]
@classmethod
def get_object_class(cls):
from .workflows import Workflow
return Workflow
class BlockCategory(Category):
_names = 'block_categories'
xml_root_node = 'block_category'
backoffice_class = 'wcs.admin.categories.BlockCategoryPage'
backoffice_base_url = 'forms/blocks/categories/'
verbose_name_plural = _('Categories')
# declarations for serialization
XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('position', 'int'),
]
@classmethod
def get_object_class(cls):
from .blocks import BlockDef
return BlockDef
class MailTemplateCategory(Category):
_names = 'mail_template_categories'
xml_root_node = 'mail_template_category'
backoffice_class = 'wcs.admin.categories.MailTemplateCategoryPage'
backoffice_base_url = 'workflows/mail-templates/categories/'
# declarations for serialization
XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('position', 'int'),
]
@classmethod
def get_object_class(cls):
from .mail_templates import MailTemplate
return MailTemplate
class CommentTemplateCategory(Category):
_names = 'comment_template_categories'
xml_root_node = 'comment_template_category'
backoffice_class = 'wcs.admin.categories.CommentTemplateCategoryPage'
backoffice_base_url = 'workflows/comment-templates/categories/'
verbose_name_plural = _('Categories')
# declarations for serialization
XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('position', 'int'),
]
@classmethod
def get_object_class(cls):
from .comment_templates import CommentTemplate
return CommentTemplate
class DataSourceCategory(Category):
_names = 'data_source_categories'
xml_root_node = 'data_source_category'
backoffice_class = 'wcs.admin.categories.DataSourceCategoryPage'
backoffice_base_url = 'forms/data-sources/categories/'
verbose_name_plural = _('Categories')
# declarations for serialization
XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('position', 'int'),
]
@classmethod
def get_object_class(cls):
from .data_sources import NamedDataSource
return NamedDataSource
Substitutions.register('category_name', category=_('General'), comment=_('Category Name'))
Substitutions.register('category_description', category=_('General'), comment=_('Category Description'))
Substitutions.register('category_id', category=_('General'), comment=_('Category Identifier'))