#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright 2021-2025 Univention GmbH
#
# http://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <http://www.gnu.org/licenses/>.
"""
Base class for all Python based hooks for ucsschool.lib.model classes derived
from UCSSchoolHelperAbstractClass.
"""
import logging
from typing import TYPE_CHECKING, Any, Dict, Type, Union # noqa: F401
from ..pyhooks.pyhook import PyHook
from .utils import ucr
if TYPE_CHECKING:
from ucsschool.lib.models.base import UCSSchoolHelperAbstractClassTV # noqa: F401
from univention.admin.uldap import access as LoType # noqa: F401
from univention.config_registry import ConfigRegistry # noqa: F401
[docs]
class Hook(PyHook):
"""
Base class for all (1) Python based hooks for ucsschool.lib.model classes
derived from UCSSchoolHelperAbstractClass.
The :py:attr:`model` attribute must be set to the class object the hook is
intended for.
Examples are provided in /usr/share/doc/ucs-school-lib-common/hook_example*.py
The following attributes are available:
* self.lo # LDAP connection object
* self.logger # Python logging instance
* self.ucr # UCR instance
If multiple hook classes are found, hook methods with higher priority
numbers run before those with lower priorities. None disables a method (no
need to remove it or comment it out).
(1) While it also works for :py:class:`ucsschool.importer.models.import_user.ImportUser`,
it is strongly recommended to use the dedicated hook class
:py:class:`ucsschool.importer.utils.user_pyhook.UserPyHook` for it. It adds
attributes that are useful in the user import context.
"""
model = None # type: Type[UCSSchoolHelperAbstractClassTV]
priority = {
"pre_create": None,
"post_create": None,
"pre_modify": None,
"post_modify": None,
"pre_move": None,
"post_move": None,
"pre_remove": None,
"post_remove": None,
} # type: Dict[str, Union[int, None]]
def __init__(self, lo, *args, **kwargs): # type: (LoType, *Any, **Any) -> None
super(Hook, self).__init__(*args, **kwargs)
from .base import UCSSchoolHelperAbstractClass # prevent circular dependency
try:
# issubclass will raise TypeError if self.model is not a class object
if not issubclass(self.model, UCSSchoolHelperAbstractClass):
raise TypeError
except TypeError:
raise TypeError('Hooks "model" attribute must be a ucsschool.lib.model class object.')
self.lo = lo # type: LoType
self.logger = logging.getLogger(
"ucsschool.lib.hook.{}".format(self.__class__.__name__)
) # type: logging.Logger
self.ucr = ucr # type: ConfigRegistry
self.model.hook_init(self)
[docs]
def pre_create(self, obj): # type: (UCSSchoolHelperAbstractClassTV) -> None
"""
Run code before creating an object.
* The object does not exist in LDAP, yet.
* `obj.dn` is the future DN of the obj, if objname and school does not change.
* set `priority["pre_create"]` to an `int`, to enable this method
:param base.UCSSchoolHelperAbstractClass obj: ucsschool.lib.model object
:return: None
"""
pass
[docs]
def post_create(self, obj): # type: (UCSSchoolHelperAbstractClassTV) -> None
"""
Run code after creating an object.
* The hook is only executed if adding the object succeeded.
* Do *not* run :py:meth:`obj.modify()`, it will create a recursion. If
you must modify the object use :py:meth:`obj.modify_without_hooks()`.
* set `priority["post_create"]` to an int, to enable this method
:param base.UCSSchoolHelperAbstractClass obj: ucsschool.lib.model object
:return: None
"""
pass
[docs]
def pre_modify(self, obj): # type: (UCSSchoolHelperAbstractClassTV) -> None
"""
Run code before modifying an object.
* set `priority["pre_modify"]` to an int, to enable this method
:param base.UCSSchoolHelperAbstractClass obj: ucsschool.lib.model object
:return: None
"""
pass
[docs]
def post_modify(self, obj): # type: (UCSSchoolHelperAbstractClassTV) -> None
"""
Run code after modifying an object.
* The hook is only executed if modifying the object succeeded.
* Do *not* run :py:meth:`obj.modify()`, it will create a recursion. If
you must modify the object use :py:meth:`obj.modify_without_hooks()`.
* set `priority["post_modify"]` to an `int`, to enable this method
:param base.UCSSchoolHelperAbstractClass obj: ucsschool.lib.model object
:return: None
"""
pass
[docs]
def pre_move(self, obj): # type: (UCSSchoolHelperAbstractClassTV) -> None
"""
Run code before changing an objects position in the LDAP tree. This
usually happens when modifying the primary school
(:py:attr:`obj.school`).
* set `priority["pre_move"]` to an `int`, to enable this method
:param base.UCSSchoolHelperAbstractClass obj: ucsschool.lib.model object
:return: None
"""
pass
[docs]
def post_move(self, obj): # type: (UCSSchoolHelperAbstractClassTV) -> None
"""
Run code before changing an objects position in the LDAP tree. This
usually happens when modifying the primary school
(:py:attr:`obj.school`).
* The hook is only executed if moving the object succeeded.
* Do *not* run :py:meth:`obj.modify()`, it will create a recursion. If
you must modify the object use :py:meth:`obj.modify_without_hooks()`.
* set `priority["post_move"]` to an `int`, to enable this method
:param base.UCSSchoolHelperAbstractClass obj: ucsschool.lib.model object
:return: None
"""
pass
[docs]
def pre_remove(self, obj): # type: (UCSSchoolHelperAbstractClassTV) -> None
"""
Run code before deleting an object.
* set `priority["pre_remove"]` to an `int`, to enable this method
:param base.UCSSchoolHelperAbstractClass obj: ucsschool.lib.model object
:return: None
"""
pass
[docs]
def post_remove(self, obj): # type: (UCSSchoolHelperAbstractClassTV) -> None
"""
Run code after deleting an object.
* The hook is only executed if the deleting the object succeeded.
* The object was removed, do not try to :py:meth:`modify()` it.
* set `priority["post_remove"]` to an `int`, to enable this method
:param base.UCSSchoolHelperAbstractClass obj: ucsschool.lib.model object
:return: None
"""
pass