#
# Univention Directory Reports
# creates a report document
#
# SPDX-FileCopyrightText: 2007-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only
import codecs
import copy
import os
import subprocess
import sys
import tempfile
import trml2pdf
from univention.directory.reports import admin
from univention.directory.reports.error import ReportError
from univention.directory.reports.interpreter import Interpreter
from univention.directory.reports.output import Output
from univention.directory.reports.parser import Parser
from univention.lib.i18n import Translation
_ = Translation('univention-directory-reports').translate
[docs]
class Document:
(TYPE_LATEX, TYPE_CSV, TYPE_RML, TYPE_UNKNOWN) = range(4)
[docs]
@classmethod
def get_type(cls, template):
if template.endswith('.tex'):
return cls.TYPE_LATEX
elif template.endswith('.csv'):
return cls.TYPE_CSV
elif template.endswith('.rml'):
return cls.TYPE_RML
return cls.TYPE_UNKNOWN
def __init__(self, template, header=None, footer=None):
self._template = template
self._header = header
self._footer = footer
self._type = self.get_type(self._template)
self.__check_files()
def __check_files(self):
if self._type in (Document.TYPE_LATEX, Document.TYPE_RML):
files = (self._header, self._footer, self._template)
elif self._type == Document.TYPE_CSV:
files = (self._template, )
else:
files = ()
for filename in files:
if not os.path.isfile(filename):
raise ReportError(_("Configuration error: File %r could not be opened.") % (filename,))
def __create_tempfile(self):
if self._type == Document.TYPE_LATEX:
suffix = '.src'
elif self._type == Document.TYPE_CSV:
suffix = '.csv'
else:
suffix = self._template.rsplit('.', 1)[1]
fd, filename = tempfile.mkstemp(suffix, 'univention-directory-reports-')
os.chmod(filename, 0o644)
os.close(fd)
return filename
def __append_file(self, fd, filename, obj=None):
parser = Parser(filename=filename)
parser.tokenize()
tks = copy.deepcopy(parser._tokens)
interpret = Interpreter(obj, tks)
interpret.run()
output = Output(tks, fd=fd)
output.write()
[docs]
def create_source(self, objects=[]):
"""Create report from objects (list of DNs)."""
tmpfile = self.__create_tempfile()
admin.set_format(self._type)
parser = Parser(filename=self._template)
parser.tokenize()
tokens = parser._tokens
fd = codecs.open(tmpfile, 'wb+', encoding='utf8')
if parser._header:
fd.write(parser._header.data)
elif self._header:
self.__append_file(fd, self._header)
for dn in objects:
if isinstance(dn, str):
obj = admin.get_object(None, dn)
else:
obj = admin.cache_object(dn)
if obj is None:
print("warning: dn '%s' not found, skipped." % dn, file=sys.stderr)
continue
tks = copy.deepcopy(tokens)
interpret = Interpreter(obj, tks)
interpret.run()
output = Output(tks, fd=fd)
output.write()
if parser._footer:
fd.write(parser._footer.data)
elif self._footer:
self.__append_file(fd, self._footer)
fd.close()
return tmpfile
[docs]
def create_pdf(self, latex_file):
"""Run pdflatex on latex_file and return path to generated file or None on errors."""
cmd = ['/usr/bin/pdflatex', '-interaction=nonstopmode', '-halt-on-error', '-output-directory=%s' % os.path.dirname(latex_file), latex_file]
devnull = open(os.path.devnull, 'w')
try:
env_vars = {'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 'HOME': '/var/cache/univention-directory-reports'}
if not subprocess.call(cmd, stdout=devnull, stderr=devnull, env=env_vars) and not subprocess.call(cmd, stdout=devnull, stderr=devnull, env=env_vars):
return '%s.pdf' % latex_file.rsplit('.', 1)[0]
raise ReportError(_('Failed creating PDF file.'))
finally:
devnull.close()
basefile = latex_file.rsplit('.', 1)[0] # strip suffix
for file_ in [latex_file] + ['%s.%s' % (basefile, suffix) for suffix in ('aux', 'log')]:
try:
os.unlink(file_)
except OSError:
pass
[docs]
def create_rml_pdf(self, rml_file):
output = '%s.pdf' % (os.path.splitext(rml_file)[0],)
with open(rml_file, 'rb') as fd:
outputfile = trml2pdf.parseString(fd.read(), output)
try:
os.unlink(rml_file)
except OSError:
pass
return outputfile