diff --git a/management/univention-directory-reports/conffiles/etc/univention/directory/reports/config.ini b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/config.ini index af5efbf..5195266 100644 --- a/management/univention-directory-reports/conffiles/etc/univention/directory/reports/config.ini +++ b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/config.ini @@ -8,7 +8,7 @@ footer=/etc/univention/directory/reports/footer.tex # default report name @!@ -print 'report=%s' % configRegistry.get('directory/reports/default', 'Standard Report') +print 'report=%s' % configRegistry.get('directory/reports/default', 'PDF Document') @!@ [reports] diff --git a/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/de_DE/footer.rml b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/de_DE/footer.rml new file mode 100644 index 0000000..b0f0f16 --- /dev/null +++ b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/de_DE/footer.rml @@ -0,0 +1,2 @@ + + diff --git a/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/de_DE/header.rml b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/de_DE/header.rml new file mode 100644 index 0000000..d39d930 --- /dev/null +++ b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/de_DE/header.rml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Auswertung der Verzeichnisdienstobjekte + + Erstellt am <@date format="%A %d. %B %Y"@> + + diff --git a/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/en_US/footer.rml b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/en_US/footer.rml new file mode 100644 index 0000000..b0f0f16 --- /dev/null +++ b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/en_US/footer.rml @@ -0,0 +1,2 @@ + + diff --git a/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/en_US/header.rml b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/en_US/header.rml new file mode 100644 index 0000000..35c75cf --- /dev/null +++ b/management/univention-directory-reports/conffiles/etc/univention/directory/reports/default/en_US/header.rml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Report of directory service objects + + Created on <@date@> + + diff --git a/management/univention-directory-reports/debian/changelog b/management/univention-directory-reports/debian/changelog index 90f00b2..024e3ec 100644 --- a/management/univention-directory-reports/debian/changelog +++ b/management/univention-directory-reports/debian/changelog @@ -1,3 +1,9 @@ +univention-directory-reports (9.0.1-1) unstable; urgency=medium + + * Bug #39239: remove dependency to latex + + -- Florian Best Wed, 14 Jun 2017 14:09:00 +0200 + univention-directory-reports (9.0.0-1) unstable; urgency=medium * Bug #41963: UCS-4.2 version bump diff --git a/management/univention-directory-reports/debian/control b/management/univention-directory-reports/debian/control index a0c9995..796d328 100644 --- a/management/univention-directory-reports/debian/control +++ b/management/univention-directory-reports/debian/control @@ -15,14 +15,10 @@ Architecture: all Depends: ${misc:Depends}, python-univention-directory-reports, shell-univention-lib, - texlive, - texlive-latex-recommended, - texlive-latex-extra, - texlive-lang-german, - python-univention-lib (>= 1.0.25-1) + python-univention-lib (>= 1.0.25-1), Description: Creates reports for UCS Directory Manager objects UCS Report Generator creates reports in PDF format using - LaTeX template files that contain a special template syntax to + template files that contain a special template syntax to describe LDAP attributes and objects that should replace the patterns. Package: python-univention-directory-reports @@ -30,8 +26,22 @@ Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python-univention-directory-manager, univention-config, + python-trml2pdf, Provides: ${python:Provides} Description: Creates reports for UCS Directory manager objects UCS Report Generator creates reports in PDF format using + RML template files that contain a special template syntax to + describe LDAP attributes and objects that should replace the patterns. + +Package: univention-directory-reports-latex +Architecture: all +Depends: univention-directory-reports, + univention-config, + texlive, + texlive-latex-recommended, + texlive-latex-extra, + texlive-lang-german, +Description: Creates reports for UCS Directory manager objects + UCS Report Generator creates reports in PDF format using LaTeX template files that contain a special template syntax to describe LDAP attributes and objects that should replace the patterns. diff --git a/management/univention-directory-reports/debian/dirs b/management/univention-directory-reports/debian/dirs deleted file mode 100644 index 01e5690..0000000 --- a/management/univention-directory-reports/debian/dirs +++ /dev/null @@ -1,4 +0,0 @@ -usr/share/univention-directory-reports -etc/univention/directory/reports/default/de_DE -etc/univention/directory/reports/default/en_US -usr/bin diff --git a/management/univention-directory-reports/debian/rules b/management/univention-directory-reports/debian/rules index b873925..688fc57 100755 --- a/management/univention-directory-reports/debian/rules +++ b/management/univention-directory-reports/debian/rules @@ -30,7 +30,8 @@ # /usr/share/common-licenses/AGPL-3; if not, see # . -TEMPLATE_FILES=$(shell find templates -name '*.tex' -o -name '*.csv' | sed 's/^templates\///') +TEMPLATE_FILES=$(shell find templates -name '*.rml' -o -name '*.csv' | sed 's/^templates\///') +LATEX_TEMPLATE_FILES=$(shell find templates -name '*.tex' | sed 's/^templates\///') PO_FILES := $(shell find modules -name '*.po') MO_FILES := $(PO_FILES:%.po=%.mo) @@ -52,6 +53,9 @@ override_dh_auto_install: @for i in $(TEMPLATE_FILES); do\ install -m 644 templates/$$i debian/univention-directory-reports/etc/univention/directory/reports/default/$$i;\ done + @for i in $(LATEX_TEMPLATE_FILES); do\ + install -m 644 templates/$$i debian/univention-directory-reports-latex/etc/univention/directory/reports/default/$$i;\ + done @for i in $(MO_FILES); do\ lang=`basename $$i .mo`;\ domain=`dirname $$i | sed 's,^modules/,,;s,/,-,g'`;\ diff --git a/management/univention-directory-reports/debian/univention-directory-reports-latex.dirs b/management/univention-directory-reports/debian/univention-directory-reports-latex.dirs new file mode 100644 index 0000000..22a54d2 --- /dev/null +++ b/management/univention-directory-reports/debian/univention-directory-reports-latex.dirs @@ -0,0 +1,2 @@ +etc/univention/directory/reports/default/de_DE +etc/univention/directory/reports/default/en_US diff --git a/management/univention-directory-reports/debian/univention-directory-reports-latex.postinst b/management/univention-directory-reports/debian/univention-directory-reports-latex.postinst new file mode 100644 index 0000000..7196137 --- /dev/null +++ b/management/univention-directory-reports/debian/univention-directory-reports-latex.postinst @@ -0,0 +1,44 @@ +#!/bin/bash +# +# Univention Reports LaTex +# postinst file for the debian package +# +# Copyright 2017 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 +# . + +. /usr/share/univention-lib/all.sh + +#DEBHELPER# + +eval "$(univention-config-registry shell)" + +univention-config-registry set \ + directory/reports/templates/pdf/user2?'users/user "PDF Latex Document" /etc/univention/directory/reports/default users.tex' \ + directory/reports/templates/pdf/group2?'groups/group "PDF Latex Document" /etc/univention/directory/reports/default groups.tex' \ + directory/reports/templates/pdf/computer2?'computers/computer "PDF Latex Document" /etc/univention/directory/reports/default computers.tex' \ + +exit 0 diff --git a/management/univention-directory-reports/debian/univention-directory-reports-latex.univention-config-registry b/management/univention-directory-reports/debian/univention-directory-reports-latex.univention-config-registry new file mode 100644 index 0000000..49fd9c6 --- /dev/null +++ b/management/univention-directory-reports/debian/univention-directory-reports-latex.univention-config-registry @@ -0,0 +1,13 @@ +Type: file +File: etc/univention/directory/reports/default/en_US/header.tex +Variables: directory/reports/logo + +Type: file +File: etc/univention/directory/reports/default/en_US/footer.tex + +Type: file +File: etc/univention/directory/reports/default/de_DE/header.tex +Variables: directory/reports/logo + +Type: file +File: etc/univention/directory/reports/default/de_DE/footer.tex diff --git a/management/univention-directory-reports/debian/univention-directory-reports.dirs b/management/univention-directory-reports/debian/univention-directory-reports.dirs new file mode 100644 index 0000000..01e5690 --- /dev/null +++ b/management/univention-directory-reports/debian/univention-directory-reports.dirs @@ -0,0 +1,4 @@ +usr/share/univention-directory-reports +etc/univention/directory/reports/default/de_DE +etc/univention/directory/reports/default/en_US +usr/bin diff --git a/management/univention-directory-reports/debian/univention-directory-reports.postinst b/management/univention-directory-reports/debian/univention-directory-reports.postinst index 9dccff5..35574b2 100644 --- a/management/univention-directory-reports/debian/univention-directory-reports.postinst +++ b/management/univention-directory-reports/debian/univention-directory-reports.postinst @@ -38,52 +38,16 @@ create_logfile /var/log/univention/directory-reports.log "root:adm" 640 eval "$(univention-config-registry shell)" -# remove old variables -if [ -n "$directory_reports_templates_default1" ]; then - univention-config-registry unset directory/reports/templates/default1 -fi -if [ -n "$directory_reports_templates_default2" ]; then - univention-config-registry unset directory/reports/templates/default2 -fi - -# migration from older package version -if dpkg --compare-versions "$2" lt-nl 3.0.1-1; then - # unset old UCR variables if they have not been changed in the meanwhile - echo 'directory/reports/templates/csv/computer1 computers/computer "Standard CSV Report" /etc/univention/directory/reports/default/computers.csv - directory/reports/templates/csv/group1 groups/group "Standard CSV Report" /etc/univention/directory/reports/default/groups.csv - directory/reports/templates/csv/user1 users/user "Standard CSV Report" /etc/univention/directory/reports/default/users.csv - directory/reports/templates/pdf/computer1 computers/computer "Standard Report" /etc/univention/directory/reports/default/computers.tex - directory/reports/templates/pdf/group1 groups/group "Standard Report" /etc/univention/directory/reports/default/groups.tex - directory/reports/templates/pdf/user1 users/user "Standard Report" /etc/univention/directory/reports/default/users.tex' \ - | ( - keyList=() - while read key val; do - [ "$(univention-config-registry get "$key")" = "$val" ] && keyList+=("$key") - done - - # unset all keys at once - [ "${#keyList[@]}" -gt 0 ] && univention-config-registry unset "${keyList[@]}" - ) - - # for compatibility reasons, try to set a link to the header.tex and footer.tex - # files, so that old reports do not break .. only do this in case the original - # template has not been modified - tmpPath=/etc/univention/directory/reports - for ifile in header.tex footer.tex; do - if [ ! -e /etc/univention/templates/files/$tmpPath/$ifile ]; then - rm -f $tmpPath/$ifile - ln -s $tmpPath/default/de_DE/$ifile $tmpPath/$ifile - fi - done +if dpkg --compare-versions "$2" lt-nl 9.0.1-1; then + univention-config-registry unset directory/reports/templates/pdf/user1 directory/reports/templates/pdf/group1 directory/reports/templates/pdf/computer1 fi -# set default values for UCR variables univention-config-registry set directory/reports/logo?/usr/share/univention-directory-reports/univention_logo.png \ - directory/reports/templates/pdf/user1?'users/user "Standard Report" /etc/univention/directory/reports/default users.tex' \ - directory/reports/templates/pdf/group1?'groups/group "Standard Report" /etc/univention/directory/reports/default groups.tex' \ - directory/reports/templates/pdf/computer1?'computers/computer "Standard Report" /etc/univention/directory/reports/default computers.tex' \ - directory/reports/templates/csv/user1?'users/user "Standard CSV Report" /etc/univention/directory/reports/default users.csv' \ - directory/reports/templates/csv/group1?'groups/group "Standard CSV Report" /etc/univention/directory/reports/default groups.csv' \ - directory/reports/templates/csv/computer1?'computers/computer "Standard CSV Report" /etc/univention/directory/reports/default computers.csv' + directory/reports/templates/pdf/user1?'users/user "PDF Document" /etc/univention/directory/reports/default users.rml' \ + directory/reports/templates/pdf/group1?'groups/group "PDF Document" /etc/univention/directory/reports/default groups.rml' \ + directory/reports/templates/pdf/computer1?'computers/computer "PDF Document" /etc/univention/directory/reports/default computers.rml' \ + directory/reports/templates/csv/user1?'users/user "CSV Report" /etc/univention/directory/reports/default users.csv' \ + directory/reports/templates/csv/group1?'groups/group "CSV Report" /etc/univention/directory/reports/default groups.csv' \ + directory/reports/templates/csv/computer1?'computers/computer "CSV Report" /etc/univention/directory/reports/default computers.csv' exit 0 diff --git a/management/univention-directory-reports/debian/univention-directory-reports.univention-config-registry b/management/univention-directory-reports/debian/univention-directory-reports.univention-config-registry index 77f6802..cf15afa 100644 --- a/management/univention-directory-reports/debian/univention-directory-reports.univention-config-registry +++ b/management/univention-directory-reports/debian/univention-directory-reports.univention-config-registry @@ -4,18 +4,18 @@ Variables: directory/reports/templates/.* Variables: directory/reports/default Type: file -File: etc/univention/directory/reports/default/en_US/header.tex +File: etc/univention/directory/reports/default/en_US/header.rml Variables: directory/reports/logo Type: file -File: etc/univention/directory/reports/default/en_US/footer.tex +File: etc/univention/directory/reports/default/en_US/footer.rml Type: file -File: etc/univention/directory/reports/default/de_DE/header.tex +File: etc/univention/directory/reports/default/de_DE/header.rml Variables: directory/reports/logo Type: file -File: etc/univention/directory/reports/default/de_DE/footer.tex +File: etc/univention/directory/reports/default/de_DE/footer.rml Type: file File: etc/logrotate.d/univention-directory-reports diff --git a/management/univention-directory-reports/man/univention-directory-reports.1 b/management/univention-directory-reports/man/univention-directory-reports.1 index 19741f2..90debe2 100644 --- a/management/univention-directory-reports/man/univention-directory-reports.1 +++ b/management/univention-directory-reports/man/univention-directory-reports.1 @@ -15,7 +15,7 @@ This manual page documents briefly the .B univention\-directory\-reports command. It is a tool to generate a report over UDM objects stored in the LDAP tree. -Currently supported output formats include Comma Separated Values (CSV) and PDF generated through LaTeX. +Currently supported output formats include Comma Separated Values (CSV) and PDF generated through RML or LaTeX. .SH OPTIONS A summary of options is included below. @@ -52,9 +52,6 @@ Use \fB-l\fP to list the available reports. \fB-l\fP, \fB--list\fP List names of available modules and reports. .TP -\fB\-n\fP, \fB\-\-no\-cleanup\fP -Do not remove the temporary LaTeX files (for debugging). -.TP \fB\-c\fP \fICONFIG\fP, \fB\-\-config=\fP\fICONFIG\fP Location of an alternative configuration file. .TP diff --git a/management/univention-directory-reports/modules/univention/directory/reports/__init__.py b/management/univention-directory-reports/modules/univention/directory/reports/__init__.py index b7f8207..52ddeaa 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/__init__.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/__init__.py @@ -30,6 +30,11 @@ # /usr/share/common-licenses/AGPL-3; if not, see # . -from document import * -from admin import * -from config import * + +from univention.directory.reports.error import ReportError +from univention.directory.reports.report import Report +from univention.directory.reports.document import Document +from univention.directory.reports.admin import connect, get_object, cache_object, connected, identify, set_format +from univention.directory.reports.config import Config + +__all__ = ['Report', 'ReportError', 'Document', 'connect', 'get_object', 'cache_object', 'connected', 'identify', 'set_format', 'Config'] diff --git a/management/univention-directory-reports/modules/univention/directory/reports/admin.py b/management/univention-directory-reports/modules/univention/directory/reports/admin.py index 9f8cba7..60eafff 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/admin.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/admin.py @@ -30,18 +30,19 @@ # /usr/share/common-licenses/AGPL-3; if not, see # . +import cgi + import univention.admin.uldap as ua_ldap import univention.admin.objects as ua_objects import univention.admin.modules as ua_modules import univention.admin.mapping as ua_mapping -import univention.admin.syntax as ua_syntax import univention.admin.config as ua_config import univention.admin.uexceptions as ua_exceptions from univention.config_registry import ConfigRegistry import univention.debug as ud -from filter import filter_get +from univention.directory.reports.filter import filter_get __all__ = ['connect', 'get_object', 'cache_object', 'connected', 'identify', 'set_format'] @@ -85,7 +86,7 @@ def texClean(str): class AdminConnection(object): - def __init__(self, userdn=None, password=None, host='localhost', base=None, start_tls=2, access=None, format=True): + def __init__(self, userdn=None, password=None, host='localhost', base=None, start_tls=2, access=None, format=None): self._cached = {} self._modules = {} self._policies = {} @@ -163,7 +164,8 @@ def get_object_real(self, module, dn): except: pass for key, value in new.items(): - if self._format: + from univention.directory.reports.document import Document + if self._format in (Document.TYPE_LATEX, Document.TYPE_RML): i, j = self.format_property(new.descriptions, key, value) new.info[i] = j else: @@ -215,18 +217,26 @@ def format_property_real(self, props, key, value): for v in value: if isinstance(v, (list, tuple)): for i in v: - result.append(texClean(str(i))) + result.append(self.escape(str(i))) else: - result.append(texClean(str(v))) + result.append(self.escape(str(v))) value = result elif value: - value = texClean(value) + value = self.escape(value) filter = filter_get(prop.syntax) if filter: return filter(prop, key, value) return (key, value) + def escape(self, value): + from univention.directory.reports.document import Document + if self._format == Document.TYPE_LATEX: + return texClean(value) + elif self._format == Document.TYPE_RML: + return cgi.escape(value, quote=True) + return value + def _get_policies(self, obj): dict = {} policies = self._access.getPolicies(obj.dn) @@ -239,7 +249,8 @@ def _get_policies(self, obj): dict[attr_name] = value_dict['value'] for key, value in ua_mapping.mapDict(module.mapping, dict).items(): - if self._format: + from univention.directory.reports.document import Document + if self._format in (Document.TYPE_LATEX, Document.TYPE_RML): i, j = self.format_property(module.property_descriptions, key, value) obj.info[i] = j else: diff --git a/management/univention-directory-reports/modules/univention/directory/reports/config.py b/management/univention-directory-reports/modules/univention/directory/reports/config.py index 0517c16..e609d8d 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/config.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/config.py @@ -115,17 +115,17 @@ def _guess_path(self, directory, fileName, alternativePath=''): return path return None - def get_header(self, module, name=None): + def get_header(self, module, name=None, suffix='.tex'): report = self._get_report_entry(module, name) if not report: return None - return self._guess_path(report[1], 'header.tex', self._oldHeader) + return self._guess_path(report[1], 'header%s' % (suffix,), self._oldHeader) - def get_footer(self, module, name=None): + def get_footer(self, module, name=None, suffix='.tex'): report = self._get_report_entry(module, name) if not report: return None - return self._guess_path(report[1], 'footer.tex', self._oldFooter) + return self._guess_path(report[1], 'footer%s' % (suffix,), self._oldFooter) def get_report_names(self, module): reports = self._reports.get(module, []) diff --git a/management/univention-directory-reports/modules/univention/directory/reports/document.py b/management/univention-directory-reports/modules/univention/directory/reports/document.py index efa9781..300c445 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/document.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/document.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # Univention Directory Reports -# creates a report document +# creates a report document # # Copyright 2007-2017 Univention GmbH # @@ -37,29 +37,40 @@ import tempfile import subprocess -from parser import Parser -from output import Output -from interpreter import Interpreter -import admin +from univention.directory.reports.error import ReportError +from univention.directory.reports.parser import Parser +from univention.directory.reports.output import Output +from univention.directory.reports.interpreter import Interpreter +from univention.directory.reports import admin + +import trml2pdf + +from univention.lib.i18n import Translation +_ = Translation('univention-directory-reports').translate class Document(object): - (TYPE_LATEX, TYPE_CSV, TYPE_UNKNOWN) = range(3) + (TYPE_LATEX, TYPE_CSV, TYPE_RML, TYPE_UNKNOWN) = range(4) + + @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 - if self._template.endswith('.tex'): - self._type = Document.TYPE_LATEX - elif self._template.endswith('.csv'): - self._type = Document.TYPE_CSV - else: - self._type = Document.TYPE_UNKNOWN + self._type = self.get_type(self._template) self.__check_files() def __check_files(self): - if self._type == Document.TYPE_LATEX: + 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, ) @@ -67,7 +78,7 @@ def __check_files(self): files = tuple() for filename in files: if not os.path.isfile(filename): - raise NameError("error: required file '%s' does not exist or is not readable" % filename) + raise ReportError(_("Configuration error: File %r could not be opened.") % (filename,)) def __create_tempfile(self): if self._type == Document.TYPE_LATEX: @@ -82,15 +93,19 @@ def __create_tempfile(self): return filename - def __append_file(self, fd, filename): - tmpfd = open(filename, 'r') - fd.write(tmpfd.read()) - tmpfd.close() + 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() def create_source(self, objects=[]): """Create report from objects (list of DNs).""" tmpfile = self.__create_tempfile() - admin.set_format(self._type == Document.TYPE_LATEX) + admin.set_format(self._type) parser = Parser(filename=self._template) parser.tokenize() tokens = parser._tokens @@ -106,7 +121,7 @@ def create_source(self, objects=[]): else: obj = admin.cache_object(dn) if obj is None: - print >>sys.stderr, "warning: dn '%s' not found, skipped." % dn + print >> sys.stderr, "warning: dn '%s' not found, skipped." % dn continue tks = copy.deepcopy(tokens) interpret = Interpreter(obj, tks) @@ -130,7 +145,22 @@ def create_pdf(self, latex_file): if not subprocess.call(cmd, stdout=devnull, stderr=devnull, env=env_vars): if not subprocess.call(cmd, stdout=devnull, stderr=devnull, env=env_vars): return '%s.pdf' % latex_file.rsplit('.', 1)[0] - print >>sys.stderr, "error: failed to create PDF file" - return None + 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 EnvironmentError: + pass + + 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 EnvironmentError: + pass + return outputfile diff --git a/management/univention-directory-reports/modules/univention/directory/reports/error.py b/management/univention-directory-reports/modules/univention/directory/reports/error.py new file mode 100644 index 0000000..d46d610 --- /dev/null +++ b/management/univention-directory-reports/modules/univention/directory/reports/error.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Univention Directory Reports +# +# Copyright 2017 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 +# . + + +class ReportError(Exception): + pass diff --git a/management/univention-directory-reports/modules/univention/directory/reports/filter.py b/management/univention-directory-reports/modules/univention/directory/reports/filter.py index 28d6505..75aec2f 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/filter.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/filter.py @@ -77,9 +77,11 @@ def _email_address(prop, key, value): def _samba_group_type(prop, key, value): # need to call str() directly in order to force a correct translation - types = {'2': str(_('Domain Group')), - '3': str(_('Local Group')), - '5': str(_('Well-Known Group'))} + types = { + '2': str(_('Domain Group')), + '3': str(_('Local Group')), + '5': str(_('Well-Known Group')) + } if value in types.keys(): value = types[value] return (key, value) diff --git a/management/univention-directory-reports/modules/univention/directory/reports/interpreter.py b/management/univention-directory-reports/modules/univention/directory/reports/interpreter.py index 9ec0f59..211cec6 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/interpreter.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/interpreter.py @@ -30,21 +30,20 @@ # /usr/share/common-licenses/AGPL-3; if not, see # . -import univention.admin.modules as ua_modules +import re +import copy +import fnmatch +import datetime + import univention.admin.objects as ua_objects -import univention.admin.uldap as ua_ldap -from tokens import QueryToken, ResolveToken, TextToken, AttributeToken, PolicyToken -import admin +from univention.directory.reports.tokens import QueryToken, ResolveToken, TextToken, AttributeToken, PolicyToken, DateToken +from univention.directory.reports import admin import univention.admin.localization translation = univention.admin.localization.translation('univention-directory-reports') _ = translation.translate -import copy -import fnmatch -import re - class Interpreter(object): @@ -101,6 +100,8 @@ def run(self, tokens=[], base_objects=[]): token.value = token.attrs['prepend'] + token.value elif isinstance(token, PolicyToken): self.policy(token, base_objects[0]) + elif isinstance(token, DateToken): + token.value = datetime.datetime.today().strftime(token.attrs.get('format', "%A %B %d, %Y")) def resolve(self, token, base): if 'module' in token.attrs: diff --git a/management/univention-directory-reports/modules/univention/directory/reports/output.py b/management/univention-directory-reports/modules/univention/directory/reports/output.py index a0813f3..f0386ab 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/output.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/output.py @@ -33,7 +33,7 @@ import codecs import os -from tokens import TextToken, ResolveToken, QueryToken, AttributeToken, PolicyToken +from tokens import TextToken, ResolveToken, QueryToken, AttributeToken, PolicyToken, DateToken class Output(object): @@ -69,5 +69,5 @@ def write(self, tokens=[]): elif isinstance(token, (ResolveToken, QueryToken)): if len(token): self.write(token) - elif isinstance(token, (AttributeToken, PolicyToken)): + elif isinstance(token, (DateToken, AttributeToken, PolicyToken)): self._fd.write(token.value) diff --git a/management/univention-directory-reports/modules/univention/directory/reports/parser.py b/management/univention-directory-reports/modules/univention/directory/reports/parser.py index 0a7cadc..939d18a 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/parser.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/parser.py @@ -33,7 +33,7 @@ import re import shlex -from tokens import Token, TextToken, AttributeToken, PolicyToken, QueryToken, HeaderToken, FooterToken, IContextToken, ResolveToken +from tokens import Token, TextToken, AttributeToken, PolicyToken, QueryToken, HeaderToken, FooterToken, IContextToken, ResolveToken, DateToken class Parser(object): @@ -109,13 +109,15 @@ def next_token(self): return HeaderToken(attrs, closing) elif name == 'footer': return FooterToken(attrs, closing) + elif name == 'date': + return DateToken(attrs) else: raise SyntaxError('Unknown tag: %s' % name) def tokenize(self): token = self.next_token() while token: - if isinstance(token, (TextToken, AttributeToken, PolicyToken)): + if isinstance(token, (TextToken, AttributeToken, PolicyToken, DateToken)): if isinstance(token, TextToken): if token.data == '\n' and len(self._context) and isinstance(self._context[-1], HeaderToken): # ignore line feed after header diff --git a/management/univention-directory-reports/modules/univention/directory/reports/report.py b/management/univention-directory-reports/modules/univention/directory/reports/report.py new file mode 100644 index 0000000..4adc995 --- /dev/null +++ b/management/univention-directory-reports/modules/univention/directory/reports/report.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# +# Univention Directory Reports +# +# Copyright 2017 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 +# . + +import os + +from univention.directory.reports.error import ReportError +from univention.directory.reports.document import Document +from univention.directory.reports.admin import connect, clear_cache +from univention.directory.reports.config import Config + +from univention.lib.i18n import Translation +_ = Translation('univention-directory-reports').translate + + +class Report(object): + + def __init__(self, lo, config=None): + self.lo = lo + self.config = config or Config() + + def create(self, module, report, objects): + """Create a report of objects for the specified module in the specified report type format""" + connect(access=self.lo) + clear_cache() + + template = self.config.get_report(module, report) + if template is None: + if not module: + raise ReportError(_('Please specify a module.')) + if module not in self.config._reports: + raise ReportError(_('No report for the specified module %r exists.') % (module,)) + if report: + raise ReportError(_('The report %r does not exists or is misconfigured.') % (report,)) + raise ReportError(_('No %r report exists for the module %r.') % (report, module)) + + suffix = '.rml' if Document.get_type(template) == Document.TYPE_RML else '.tex' + header = self.config.get_header(module, report, suffix) + footer = self.config.get_footer(module, report, suffix) + doc = Document(template, header=header, footer=footer) + + tmpfile = doc.create_source(objects) + pdffile = tmpfile + func = {Document.TYPE_RML: doc.create_rml_pdf, Document.TYPE_LATEX: doc.create_pdf}.get(doc._type) + if func: + pdffile = func(tmpfile) + if not pdffile or not os.path.exists(pdffile): + raise ReportError(_('The report could not be created.')) + return pdffile diff --git a/management/univention-directory-reports/modules/univention/directory/reports/tokens.py b/management/univention-directory-reports/modules/univention/directory/reports/tokens.py index 1509db0..b0d83ba 100644 --- a/management/univention-directory-reports/modules/univention/directory/reports/tokens.py +++ b/management/univention-directory-reports/modules/univention/directory/reports/tokens.py @@ -118,3 +118,10 @@ class PolicyToken(TemplateToken): def __init__(self, attrs={}, value=''): TemplateToken.__init__(self, 'policy', attrs) self.value = value + + +class DateToken(TemplateToken): + + def __init__(self, attrs={}, value=''): + TemplateToken.__init__(self, 'date', attrs) + self.value = value diff --git a/management/univention-directory-reports/templates/de_DE/computers.rml b/management/univention-directory-reports/templates/de_DE/computers.rml new file mode 100644 index 0000000..7622196 --- /dev/null +++ b/management/univention-directory-reports/templates/de_DE/computers.rml @@ -0,0 +1,11 @@ + + +

Rechner <@attribute name="name"@>

+IP-Adresse:<@attribute name="ip"@> +MAC-Adresse:<@attribute name="mac"@> +Network:<@resolve module="networks/network" dn-attribute="network" alternative="nicht zugewiesen"@><@attribute name="name"@><@/resolve@> +Inventarnummer:<@attribute name="inventoryNumber"@> +
+ diff --git a/management/univention-directory-reports/templates/de_DE/groups.rml b/management/univention-directory-reports/templates/de_DE/groups.rml new file mode 100644 index 0000000..1a5303e --- /dev/null +++ b/management/univention-directory-reports/templates/de_DE/groups.rml @@ -0,0 +1,42 @@ +

Gruppen-Report: <@attribute name="name"@>

+ + + +

Gruppeneinzelnachweis: <@attribute name="name"@>

+Name:<@attribute name="name"@> +Beschreibung:<@attribute name="description" default="-"@> +Gruppen ID:<@attribute name="gidNumber"@> +Relative ID:<@attribute name="sambaRID"@> +Gruppentyp:<@attribute name="sambaGroupType"@> +E-Mail-Adresse:<@attribute name="mailAddress" default="-"@> +
+ + + +

Mitglied folgender Gruppen

+ +<@resolve module="groups/group" dn-attribute="memberOf" alternative="Diese Gruppe ist in keiner anderen Gruppe Mitglied."@> + <@attribute name="name"@> (<@attribute name="description"@>) +<@/resolve@> + + +

Gruppen in dieser Gruppe

+ +<@query module="groups/group" start="nestedGroup" next="nestedGroup" alternative="Keine Gruppe ist Mitglied dieser Gruppe."@> + <@attribute name="name"@> (<@attribute name="description"@>) +<@/query@> + +

Benutzer in dieser Gruppe

+ +<@resolve module="users/user" dn-attribute="users" alternative="Kein Benutzer ist Mitglied dieser Gruppe."@> + <@attribute name="username"@> (<@attribute name="lastname"@>, <@attribute name="firstname"@>) +<@/resolve@> + +<@query module="groups/group" start="nestedGroup" next="nestedGroup" alternative="Keine indirekten Gruppenmitgliedschaften."@> +

Gruppe: <@attribute name="name"@> (<@attribute name="description"@>)

+ <@resolve module="users/user" dn-attribute="users" alternative="Keine Mitglieder in dieser Gruppe"@> + <@attribute name="username"@> (<@attribute name="lastname"@>) + <@/resolve@> +<@/query@> + + diff --git a/management/univention-directory-reports/templates/de_DE/users.rml b/management/univention-directory-reports/templates/de_DE/users.rml new file mode 100644 index 0000000..d5e0e7d --- /dev/null +++ b/management/univention-directory-reports/templates/de_DE/users.rml @@ -0,0 +1,96 @@ +

Benutzer-Report: <@attribute name="lastname"@><@attribute name="firstname" prepend=", "@>

+ + + + + + +

Benutzereinzelnachweis: <@attribute name="lastname"@>, <@attribute name="firstname"@>

+Name:<@attribute name="lastname"@><@attribute name="firstname" prepend=", "@> +Benutzer:<@attribute name="username"@> +Beschreibung:<@attribute name="description"@> +Organisation:<@attribute name="organisation"@> +Mitarbeiternummer:<@attribute name="employeeNumber"@> +Mitarbeitertyp:<@attribute name="employeeType"@> +Raumnummer:<@attribute name="roomNumber"@> +Vorgesetzter: +<@resolve module="users/user" dn-attribute="secretary" alternative=""@> + <@attribute name="lastname"@><@attribute name="firstname" prepend=", "@> +<@/resolve@> +
+ + + + +

Kontakt

+ +Telefon:<@attribute name="phone"@> +Mobiltelefon:<@attribute name="mobileTelephoneNumber"@> +Straße:<@attribute name="street"@> + +
+ + + +

Kontoeinstellungen

+Deaktiviert:<@attribute name="disabled" default="Nein"@> +Gesperrt:<@attribute name="locked" default="Nein"@> +Kontoablaufdatum:<@attribute name="userexpiry" default="Nicht gesetzt"@> +Passwortablaufdatum:<@attribute name="passwordexpiry" default="Nicht gesetzt"@> +Passwortänderung:<@attribute name="pwdChangeNextLogin" default="Nein"@> +
+ + + + + +

Linux/UNIX

+Benutzer-ID:<@attribute name="uidNumber"@> +Gruppen-ID:<@attribute name="gidNumber"@> +Login-Shell:<@attribute name="shell"@> +Heimatverzeichnis:<@attribute name="unixhome"@> +Freigabepfad:<@attribute name="homeSharePath"@> +Freigabe:<@resolve module="shares/share" dn-attribute="homeShare"@> + <@attribute name="printablename"@> +<@/resolve@> +
+ + + + +

Passwort-Richtlinie

+Ererbt:<@policy module="policies/pwdhistory" inherited=""@> +History Länge:<@attribute name="length"@> +Passwort Länge:<@attribute name="pwLength"@> +Ablaufintervall:<@attribute name="expiryInterval" default="-"@> +
+ + + +

Mail

+E-Mail-Adresse:<@attribute name="mailPrimaryAddress" default="Keine"@> +weitere Adressen:<@attribute name="mailAlternativeAddress" default="Keine"@> +Globaler SPAM-Ordner:<@attribute name="mailGlobalSpamFolder" default="-"@> +
+ + + +

Windows

+Heimatverzeichnis:<@attribute name="sambahome"@> +Laufwerk für Heimatverzeichnis:<@attribute name="homedrive"@> +Profilverzeichnis:<@attribute name="profilepath"@> +Anmeldeskript:<@attribute name="scriptpath"@> +Relative ID:<@attribute name="sambaRID"@> +
+ + +
+ + +

Mitglied in folgenden Gruppen

+ +<@query module="groups/group" start="groups" next="memberOf" regex="name=^[^(s\-)([a\-)(i\-)(h\-)]" alternative="Der Benutzer ist in keiner weiteren Gruppen enthalten"@> + <@attribute name="name"@> (<@attribute name="description"@>) +<@/query@> + + diff --git a/management/univention-directory-reports/templates/en_US/computers.rml b/management/univention-directory-reports/templates/en_US/computers.rml new file mode 100644 index 0000000..3c157c0 --- /dev/null +++ b/management/univention-directory-reports/templates/en_US/computers.rml @@ -0,0 +1,12 @@ + + +

Hostname <@attribute name="name"@>

+IP address:<@attribute name="ip"@> +MAC address:<@attribute name="mac"@> +Network:<@resolve module="networks/network" dn-attribute="network" alternative="nicht zugewiesen"@><@attribute name="name"@><@/resolve@> +Inventory number:<@attribute name="inventoryNumber"@> +
+ +\ihead{Hostname report} diff --git a/management/univention-directory-reports/templates/en_US/groups.rml b/management/univention-directory-reports/templates/en_US/groups.rml new file mode 100644 index 0000000..f43ab93 --- /dev/null +++ b/management/univention-directory-reports/templates/en_US/groups.rml @@ -0,0 +1,42 @@ +

Group report: <@attribute name="name"@>

+ + + +

Group record: <@attribute name="name"@>

+Name:<@attribute name="name"@> +Description:<@attribute name="description" default="-"@> +Group ID:<@attribute name="gidNumber"@> +Relative ID:<@attribute name="sambaRID"@> +Group type:<@attribute name="sambaGroupType"@> +E-Mail address:<@attribute name="mailAddress" default="-"@> +
+ + + +

Member of the following groups

+ +<@resolve module="groups/group" dn-attribute="memberOf" alternative="This group is no member of any other group."@> + <@attribute name="name"@> (<@attribute name="description"@>) +<@/resolve@> + + +

Group members of this group

+ +<@query module="groups/group" start="nestedGroup" next="nestedGroup" alternative="No group is member of this group."@> + <@attribute name="name"@> (<@attribute name="description"@>) +<@/query@> + +

User members of this group

+ +<@resolve module="users/user" dn-attribute="users" alternative="No user is member of this group."@> + <@attribute name="username"@> (<@attribute name="lastname"@>, <@attribute name="firstname"@>) +<@/resolve@> + +<@query module="groups/group" start="nestedGroup" next="nestedGroup" alternative="No indirect group members."@> +

Group: <@attribute name="name"@> (<@attribute name="description"@>)

+ <@resolve module="users/user" dn-attribute="users" alternative="No members of this group."@> + <@attribute name="username"@> (<@attribute name="lastname"@>) + <@/resolve@> +<@/query@> + + diff --git a/management/univention-directory-reports/templates/en_US/users.rml b/management/univention-directory-reports/templates/en_US/users.rml new file mode 100644 index 0000000..45ef120 --- /dev/null +++ b/management/univention-directory-reports/templates/en_US/users.rml @@ -0,0 +1,96 @@ +

User report: <@attribute name="lastname"@><@attribute name="firstname" prepend=", "@>

+ + + + + + +

User record: <@attribute name="lastname"@>, <@attribute name="firstname"@>

+Name:<@attribute name="lastname"@><@attribute name="firstname" prepend=", "@> +User name:<@attribute name="username"@> +Description:<@attribute name="description"@> +Organisation:<@attribute name="organisation"@> +Employee number:<@attribute name="employeeNumber"@> +Employee type:<@attribute name="employeeType"@> +Room number:<@attribute name="roomNumber"@> +Superior: +<@resolve module="users/user" dn-attribute="secretary" alternative=""@> + <@attribute name="lastname"@><@attribute name="firstname" prepend=", "@> +<@/resolve@> +
+ + + + +

Contact

+ +Phone:<@attribute name="phone"@> +Mobile phone:<@attribute name="mobileTelephoneNumber"@> +Street:<@attribute name="street"@> + +
+ + + +

Account settings

+Disabled:<@attribute name="disabled" default="No"@> +Locked:<@attribute name="locked" default="No"@> +Account expiry date:<@attribute name="userexpiry" default="Not set"@> +Password expiry date:<@attribute name="passwordexpiry" default="Not set"@> +Change password:<@attribute name="pwdChangeNextLogin" default="No"@> +
+ + + + + +

Linux/UNIX

+User ID:<@attribute name="uidNumber"@> +Group ID:<@attribute name="gidNumber"@> +Login shell:<@attribute name="shell"@> +Home directory:<@attribute name="unixhome"@> +Share path:<@attribute name="homeSharePath"@> +Share:<@resolve module="shares/share" dn-attribute="homeShare"@> + <@attribute name="printablename"@> +<@/resolve@> +
+ + + + +

Password policy

+Inherited:<@policy module="policies/pwdhistory" inherited=""@> +History length:<@attribute name="length"@> +Password length:<@attribute name="pwLength"@> +Expiry interval:<@attribute name="expiryInterval" default="-"@> +
+ + + +

Mail

+E-mail address:<@attribute name="mailPrimaryAddress" default="None"@> +Alternative address:<@attribute name="mailAlternativeAddress" default="None"@> +Global SPAM folder:<@attribute name="mailGlobalSpamFolder" default="-"@> +
+ + + +

Windows

+Home directory:<@attribute name="sambahome"@> +Home drive:<@attribute name="homedrive"@> +Profile path:<@attribute name="profilepath"@> +Logon script:<@attribute name="scriptpath"@> +Relative ID:<@attribute name="sambaRID"@> +
+ + +
+ + +

Member of the following groups

+ +<@query module="groups/group" start="groups" next="memberOf" regex="name=^[^(s\-)([a\-)(i\-)(h\-)]" alternative="The user is not member of any other group"@> + <@attribute name="name"@> (<@attribute name="description"@>) +<@/query@> + + diff --git a/management/univention-directory-reports/templates/en_US/users.tex b/management/univention-directory-reports/templates/en_US/users.tex index 7d64dc5..e8b5ef1 100644 --- a/management/univention-directory-reports/templates/en_US/users.tex +++ b/management/univention-directory-reports/templates/en_US/users.tex @@ -126,7 +126,7 @@ Relative ID & <@attribute name="sambaRID"@> \\ \section*{Member of the following groups} -<@query module="groups/group" start="groups" next="memberOf" regex="name=^[^(s\-)([a\-)(i\-)(h\-)]" header="\begin{description}" footer="\end{description}" alternative="The user is not member of any other gropu"@> +<@query module="groups/group" start="groups" next="memberOf" regex="name=^[^(s\-)([a\-)(i\-)(h\-)]" header="\begin{description}" footer="\end{description}" alternative="The user is not member of any other group"@> \item[<@attribute name="name"@>] (<@attribute name="description"@>) <@/query@> diff --git a/management/univention-directory-reports/univention-directory-reports b/management/univention-directory-reports/univention-directory-reports index 0358a4f..926bd32 100755 --- a/management/univention-directory-reports/univention-directory-reports +++ b/management/univention-directory-reports/univention-directory-reports @@ -31,19 +31,21 @@ # /usr/share/common-licenses/AGPL-3; if not, see # . -import univention.debug as ud - +import sys +import locale from optparse import OptionParser -import os -import sys +import univention.debug as ud +import univention.admin.uldap -import univention.directory.reports as udr +from univention.directory.reports import Report, ReportError, Config from univention.config_registry import ConfigRegistry +from univention.lib.i18n import Translation +_ = Translation('univention-directory-reports').translate -baseConfig = ConfigRegistry() -baseConfig.load() +ucr = ConfigRegistry() +ucr.load() def dump_modules(cfg, module=None, out=sys.stdout): @@ -53,13 +55,13 @@ def dump_modules(cfg, module=None, out=sys.stdout): else: modules = [module] for module in modules: - print >>out, 'Reports for module: %s' % module + print >> out, 'Reports for module: %s' % module for name in cfg.get_report_names(module): - print >>out, ' - %s' % name + print >> out, ' - %s' % name def main(): - cfg = udr.Config() + cfg = Config() parser = OptionParser(usage='usage: %prog -m [options] dn1 dn2 ...') parser.add_option( @@ -80,11 +82,11 @@ def main(): help='file containing the footer for the report') parser.add_option( '-s', '--server', action='store', - dest='server', default=baseConfig.get('ldap/server/name', 'localhost'), + dest='server', default=ucr.get('ldap/server/name', 'localhost'), help='LDAP server [%default]') parser.add_option( '-b', '--base', action='store', - dest='base', default=baseConfig.get('ldap/base', ''), + dest='base', default=ucr.get('ldap/base', ''), help='LDAP base [%default]') parser.add_option( '-m', '--module', action='store', @@ -99,10 +101,6 @@ def main(): dest='list_reports', default=False, help='List names of available reports') parser.add_option( - '-n', '--no-cleanup', action='store_true', - dest='no_cleanup', default=False, - help='do not remove the temporary LaTeX files (for debugging)') - parser.add_option( '-c', '--config', action='store', dest='config', default='/etc/univention/directory/reports/config.ini', help='location of the configuration file [%default]') @@ -113,96 +111,44 @@ def main(): (options, args) = parser.parse_args() - if not options.user or not options.password: - try: - pwdfile = '/etc/machine.secret' - options.user = baseConfig['ldap/hostdn'] - if baseConfig['server/role'] == 'domaincontroller_master': - pwdfile = '/etc/ldap.secret' - options.user = 'cn=admin,%s' % baseConfig['ldap/base'] - fd = open(pwdfile, 'r') - options.password = fd.readline()[: -1] - fd.close() - except: - print >>sys.stderr, "error: user and/or password not specified" - parser.print_help(sys.stderr) - sys.exit(1) - - ud.init('/var/log/univention/directory-reports.log', 1, 1) + ud.init('/var/log/univention/directory-reports.log', ud.FLUSH, ud.FUNCTION) ud.set_level(ud.ADMIN, options.debug) - cfg = udr.Config(options.config) + locale.setlocale(locale.LC_ALL, locale.getdefaultlocale()) + + cfg = Config(options.config) if options.list_reports: dump_modules(cfg, options.module) sys.exit(0) - template = cfg.get_report(options.module, options.report) - if template is None: - parser.print_usage(sys.stderr) - if options.module is None: - print >>sys.stderr, "error: module not specified (use -m)" - elif options.module not in cfg._reports: - print >>sys.stderr, "error: specified module '%s' does not exist" % options.module - options.module = None - elif options.report is not None: - report_entry = cfg._get_report_entry(options.module, options.report) - if report_entry is None: - print >>sys.stderr, "error: specified report '%s' does not exist" % options.report - else: - print >>sys.stderr, "error: specified report '%s' is unavailable" % options.report - name, dir, filename = report_entry - if not os.path.exists(filename): - print >>sys.stderr, "Template file '%s' seems to be missing." % filename - else: - print >>sys.stderr, "Check settings in file '%s'." % cfg._filename - sys.exit(2) - else: - print >>sys.stderr, "error: no report found for module '%s'" % options.module - dump_modules(cfg, options.module, sys.stderr) - sys.exit(2) - if not args: parser.print_usage(sys.stderr) - print >>sys.stderr, "error: no DNs specified on command line" + print >> sys.stderr, _("Error: no DNs specified on command line") sys.exit(2) - udr.admin.connect(options.user, options.password, host=options.server, base=options.base, start_tls=0) + # FIXME: why is start_tls=0 used here? + if options.user and options.password: + lo = univention.admin.uldap.access(host=options.server, base=options.base, binddn=options.user, bindpw=options.password, start_tls=0) + else: + try: + if ucr['server/role'] == 'domaincontroller_master': + lo, po = univention.admin.uldap.getAdminConnection(start_tls=0) + else: + lo, po = univention.admin.uldap.getMachineConnection(start_tls=0) + except IOError: + print >> sys.stderr, _("Error: user and/or password not specified") + parser.print_help(sys.stderr) + sys.exit(1) + report = Report(lo, config=cfg) try: - if options.header is None: - options.header = cfg.get_header(options.module, options.report) - if options.footer is None: - options.footer = cfg.get_footer(options.module, options.report) - doc = udr.Document(template, header=options.header, footer=options.footer) - except NameError as e: # missing file - print >>sys.stderr, e + filename = report.create(options.module, options.report, args) + except ReportError as exc: + print >> sys.stderr, _("Error: The %s report could not be created: %s") % (options.report, exc) sys.exit(1) - tmpfile = doc.create_source(args) - if doc._type == udr.Document.TYPE_LATEX: - type = 'PDF' - outfile = doc.create_pdf(tmpfile) - if options.no_cleanup: - print >>sys.stderr, 'kept temporary source file at %s' % tmpfile - else: - basefile = tmpfile.rsplit('.', 1)[0] # strip suffix - for file in [tmpfile] + ['%s.%s' % (basefile, suffix) for suffix in ('aux', 'log')]: - try: - os.unlink(file) - except OSError as e: - pass - elif doc._type == udr.Document.TYPE_CSV: - type = 'CSV' - outfile = tmpfile - else: - type = '' - outfile = tmpfile - if os.path.exists(outfile): - print 'created %s file: %s' % (type, outfile) - else: - print >>sys.stderr, "error: report could not be created" - sys.exit(1) + print _('The %s has been created: %s') % (options.report, filename) if __name__ == "__main__": diff --git a/management/univention-management-console-module-udm/umc/js/udm/CreateReportDialog.js b/management/univention-management-console-module-udm/umc/js/udm/CreateReportDialog.js index 15331ca..5d12731 100644 --- a/management/univention-management-console-module-udm/umc/js/udm/CreateReportDialog.js +++ b/management/univention-management-console-module-udm/umc/js/udm/CreateReportDialog.js @@ -224,23 +224,13 @@ define([ this.set('title', _('Creating the report ...')); - var request_data = {objects: this.objects, report: options.report}; - this.standbyDuring(this.umcpCommand('udm/reports/create', request_data, this._form)).then(lang.hitch(this, function(data) { - var title = ''; - var message = ''; - + this.standbyDuring(this.umcpCommand('udm/reports/create', {objects: this.objects, report: options.report}, this._form)).then(lang.hitch(this, function(data) { this._container.removeChild(this._form); this._container.removeChild(waiting); waiting.destroy(); - if (true === data.result.success) { - message = lang.replace('

{0}

', [_standbyDuringSuccessText(data.result.docType, data.result.URL)]); - title = _('Report has been created'); - } else { - title = _('Report creation has failed'); - message = _('The report could not be created. Details for the problems can be found in the log files.'); - } - this.set('title', title); + var message = lang.replace('

{0}

', [_standbyDuringSuccessText(options.report, data.result.URL)]); + this.set('title', _('Report has been created')); this._container.addChild(new Text({content: message})); var btnContainer = new ContainerWidget({ diff --git a/management/univention-management-console-module-udm/umc/python/udm/__init__.py b/management/univention-management-console-module-udm/umc/python/udm/__init__.py index ac404af..4c5c3e3 100644 --- a/management/univention-management-console-module-udm/umc/python/udm/__init__.py +++ b/management/univention-management-console-module-udm/umc/python/udm/__init__.py @@ -51,7 +51,8 @@ from univention.management.console.modules.decorators import simple_response, sanitize, multi_response, prevent_xsrf_check from univention.management.console.modules.sanitizers import ( Sanitizer, LDAPSearchSanitizer, EmailSanitizer, ChoicesSanitizer, - ListSanitizer, StringSanitizer, DictSanitizer, BooleanSanitizer + ListSanitizer, StringSanitizer, DictSanitizer, BooleanSanitizer, + DNSanitizer ) from univention.management.console.modules.mixins import ProgressMixin from univention.management.console.log import MODULE @@ -579,62 +580,37 @@ def _thread(request): def reports_query(self, request): """Returns a list of reports for the given object type""" - self.finished(request.id, self.reports_cfg.get_report_names(request.flavor)) + self.finished(request.id, sorted(self.reports_cfg.get_report_names(request.flavor))) def sanitize_reports_create(self, request): choices = self.reports_cfg.get_report_names(request.flavor) return dict( report=ChoicesSanitizer(choices=choices, required=True), - objects=ListSanitizer(StringSanitizer(minimum=1), required=True, min_elements=1) + objects=ListSanitizer(DNSanitizer(minimum=1), required=True, min_elements=1) ) @sanitize_func(sanitize_reports_create) def reports_create(self, request): - """Creates a report for the given LDAP DNs and returns the file - - requests.options = {} - 'report' -- name of the report - 'objects' -- list of LDAP DNs to include in the report - - return: report file - """ + """Creates a report for the given LDAP DNs and returns the URL to access the file""" @LDAP_Connection def _thread(request, ldap_connection=None, ldap_position=None): - udr.admin.connect(access=ldap_connection) - udr.admin.clear_cache() - cfg = udr.Config() - template = cfg.get_report(request.flavor, request.options['report']) - doc = udr.Document(template, header=cfg.get_header(request.flavor, request.options['report']), footer=cfg.get_footer(request.flavor, request.options['report'])) - tmpfile = doc.create_source(request.options['objects']) - if doc._type == udr.Document.TYPE_LATEX: - doc_type = _('PDF document') - pdffile = doc.create_pdf(tmpfile) - os.unlink(tmpfile) - else: - doc_type = _('text file') - pdffile = tmpfile + report = udr.Report(ldap_connection) try: - os.unlink(tmpfile[: -4] + 'aux') - os.unlink(tmpfile[: -4] + 'log') - except: - pass - if pdffile: - path = '/var/www/univention-directory-reports' - shutil.copy(pdffile, path) - os.unlink(pdffile) - - www_data_user = pwd.getpwnam('www-data') - www_data_grp = grp.getgrnam('www-data') - filename = os.path.join(path, os.path.basename(pdffile)) - os.chown(filename, www_data_user.pw_uid, www_data_grp.gr_gid) - os.chmod(filename, 0o644) - url = '/univention-directory-reports/%s' % os.path.basename(pdffile) - # link = '%s (%s)' % ( url, module.name, request.options[ 'report' ] ) - - return {'success': True, 'count': len(request.options['objects']), 'URL': url, 'docType': doc_type} - else: - return {'success': False, 'count': 0, 'URL': None, 'docType': doc_type} + report_file = report.create(request.flavor, request.options['report'], request.options['objects']) + except udr.ReportError as exc: + raise UMC_Error(str(exc)) + + www_data_user = pwd.getpwnam('www-data') + www_data_grp = grp.getgrnam('www-data') + path = '/var/www/univention-directory-reports/' + filename = os.path.join(path, os.path.basename(report_file)) + + shutil.move(report_file, path) + os.chown(filename, www_data_user.pw_uid, www_data_grp.gr_gid) + os.chmod(filename, 0o644) + url = '/univention-directory-reports/%s' % os.path.basename(report_file) + return {'URL': url} thread = notifier.threads.Simple('ReportsCreate', notifier.Callback(_thread, request), notifier.Callback(self.thread_finished_callback, request)) thread.run()