Mon CV

Personnaliser un modèle de document LibreOffice (ou Word) avec des données d’un annuaire LDAP

4 janvier 2015

Si vous parcourez d’autres blogs, vous avez certainement repéré ce petit schéma qui résume bien l’approche que les technophiles ont des tâches répétitives. Je m’y reconnais bien : grosso modo, dés que je peux éviter de faire quelque chose manuellement si c’est amené à se répéter je préfère essayer d’automatiser.

Et inévitablement, au début ça prend plus de temps que de réaliser ladite tâche manuellement. D’où une certaine incompréhension des personnes moins enclines à coder.

Voici une petite application concrète qui va plaire à ceux qui se reconnaissent dans la “courbe” rouge.

 

Le problème de départ : personnaliser un modèle de document

Dans la structure pour laquelle je travaille, nous avons une procédure d’installation d’un poste informatique pour chaque nouvel arrivant (salarié, stagiaire, etc.).

Dans ce workflow une tâche est un peu pénible : nous créons pour chaque utilisateur un modèle de document (un template) sous LibreOffice qui contient le papier à entête de la structure en fond et un formatage prédéfini avec quelques informations déjà renseignées, comme par exemple le numéro de téléphone direct, l’email, etc. C’est donc un document modèle qui est différent pour chaque utilisateur.

Avant de m’y atteler, et donc d’y perdre au départ un peu de temps, nous faisions ainsi :

  • nous avions préparé un template LibreOffice “de base” qui ne contient que les informations communes,
  • nous installions le poste informatique de l’utilisateur,
  • nous nous connections à sa session sur son poste,
  • nous ouvrions le template LibreOffice “de base”, stocké sur un point de partage réseau,
  • nous modifions ce template en ajoutant les informations relatives à l’utilisateur,
  • et enfin, nous enregistrions le résultat sur le poste informatique de l’utilisateur concerné, dans son répertoire (LibreOffice 4 stocke ses templates par défaut dans ~/Library/Application Support/LibreOffice/4/user/template/).

Ce n’est pas la mer à boire direz vous, mais le simple fait de devoir ouvrir la session de l’utilisateur sur son poste allait à l’encontre du principe de départ de déploiement et de paramétrage des postes informatiques, que nous réalisons avec DeployStudio, des préférences gérées par OS X Server (MCX), quelques scripts au boot et à l’ouverture de session et Munki pour le déploiement des softwares, justement pour éviter de devoir paramétrer manuellement chaque poste.

Du coup, j’étais à la recherche d’une méthode pour éviter cette entorse à notre mode de fonctionnement. Et c’est une discussion avec Yoann Gini qui m’a mis sur la piste :

  • nous avons dans notre annuaire LDAP (OpenDirectory) toutes les informations personnelles que nous utilisons pour personnaliser les templates LibreOffice,
  • les documents LibreOffice (tous comme ceux de Microsoft Office) sont des dossiers compressés (zippés) … il doit donc être possible de les personnaliser en les dézippant, en modifiant les informations nécessaires puis en les rezippant.

Après quelques recherches sur Internet, j’ai trouvé aussi sur ce blog, un professeur qui avait mis en place un script pour générer en masse des fichiers LibreOffice .odt.

Mon idée est donc la suivante :

  1. je crée un modèle de document sous LibreOffice (mais cela n’aurait pas changé grand chose sous Word, puisque les fichiers .docx sont également des archives contenant des fichiers dissociant le contenu de la mise en forme),
  2. dans ce modèle, j’intègre des “tags” à la place des informations que je souhaite personnaliser ensuite (par exemple là où devra être inséré le numéro de téléphone direct, j’écrit “LIGNEDIRECTE”),
  3. je crée un script qui se chargera, pour chaque “tag” précédemment intégré au modèle, d’aller chercher l’information correspondante dans le LDAP et d’effectuer le remplacement (en réalité mon script est au final un peu plus complexe, comme je l’expliquerai plus bas),
  4. je déploierait le script (ainsi que les modèles nécessaires) grâce à Munki, dont j’ai déjà parlé à plusieurs reprises (et je vous conseille d’ailleurs le tutoriel de Yoann Gini pour l’installation),
  5. enfin, ce script appelé à chaque ouverture de session par un utilisateur, grâce à Outset, lui-même préalablement déployé sur les postes informatiques (c’est une alternative intéressante à mon outil maison scriptRunner.sh qui permet de lancer tous les scripts contenus dans un dossier au boot d’une machine et à l’ouverture de chaque session).

 

Etape 1 : créer le modèle LibreOffice

Pour débuter il faut donc créer un template sous LibreOffice. Il en existe des tas sur le repository de LibreOffice que vous pouvez personnaliser. On pourrait faire la même chose sous Word, puisque le format .docx est également une archive séparant le contenu et la mise en forme en les dissociant au travers de fichiers .xml et d’images.

Pour ma part, je vous propose ici de travailler sur un papier à entête fictif. Comme je souhaite que ce papier à entête soit compatible avec les enveloppes à fenêtre, j’ai intégré un tableau, dans lequel je dispose mes données de base (une zone avec la date pré-intégrée, une zone adresse, un cartouche avec les coordonnées directes, une zone avec le nom du signataire, etc.).

Intégrer une image de fond seulement sur la première page du courrier

Pour aller un peu plus loin, je souhaite que le logo et l’image de fond n’apparaissent que sur la première page de chaque courrier. Car c’est un peu plus sympa sur le plan esthétique et un peu plus économique au niveau de l’encre (seule la première page sera imprimée en couleur).

Pour cela, j’utilise le menu Format > Styles et formatage de LibreOffice :

Menu Styles et formatages

 

Qui m’amène sur cette petite boite de dialogue :

Style et formatage 1
 … et il suffit de cliquer sur la quatrième icône en haut de cette boite de dialogue (Styles de page) pour pouvoir définir un style de page.

Nous allons faire un clic droit sur “Première page” puis sélectionner “Modifier” pour pouvoir définir un arrière plan qui ne sera utilisé que sur la première page du document :

Styles et formatage : modifier uniquement la première page

Il nous faut ensuite aller dans l’onglet Arrière-plan, sélectionner une image en arrière plan (dans le menu déroulant) pour l’intégrer à la bonne disposition :

Intégrer une image en arrière plan 1

Intégrer une image en arrière plan 2

Enregistrer l’entête de courrier comme template LibreOffice

Rien de plus simple pour cela. Une fois que vous avez intégré tous les “Tags que vous souhaitiez, dans mon cas :

  • NOMCOMPLET
  • LIGNEDIRECTE
  • MOBILE
  • MAIL
  • MAIL
  • TITRE (c’est à dire la fonction).

Si vous êtes OK et que vous document est clean, faites un “Fichier > Enregistrer sous” puis sélectionnez le format “Modèle de texte ODF (.ott)” et déposer votre template à l’emplacement souhaité (sinon, par défaut, il ira se loger à l’emplacement utilisé par LibreOffice pour garder ses modèles : ~/Library/Application Support/LibreOffice/4/user/template/) :

Enregistrer sous modèle de texte ODF

Voici en téléchargement, pour vos éventuels tests, un fichier d’exemple réalisé.

 

Etape 2 : mon script LdapLibreOfficeTemplateGenerator.sh

Grosso modo mon script va rechercher une correspondance entre l’utilisateur courant (c’est à dire le résultat de la commande whoami) et une entrée dans la branche users du LDAP pour ensuite remplacer le contenu des “Tags” intégrés dans le template LibreOffice avec les informations correspondantes dans l’annuaire LDAP.

Si vous souhaitez prendre en charge d’autres attributs du LDAP, il faudra adapter le script.

 

La syntaxe attendue par mon outil est la suivante :

./LdapLibreOfficeTemplateGenerator.sh [-h] | -s <URL Serveur LDAP> -r <DN Racine>
-t <template LibreOffice source>
[-a <LDAP admin UID>] [-p <LDAP admin password>] [-u <DN relatif branche Users>] [-D <filtre domaine email>] [-d <domaine email prioritaire>] [-U <UID de l'utilisateur à traiter>] [-i <IP depuis lesquelles lancer le processus>] [-P <chemin export>] [-t <tag des exports>] [-j <log file>]

 

Paramètres obligatoires :

  • -r <DN Racine> : DN de base de l’ensemble des entrées du LDAP (ex. : ‘dc=server,dc=office,dc=com‘).
  • -s <URL Serveur LDAP> : URL du serveur LDAP (ex. : ‘ldap://ldap.serveur.office.com‘).
  • -t <template LibreOffice source> : Chemin complet du fichier template OTT LibreOffice source à utiliser, avec extension .ott (ex. : ‘/Users/moi/templates/master_template.ott‘).

Paramètres optionnels :

  • -a <LDAP admin UID> : UID de l’administrateur ou utilisateur LDAP si un Bind est nécessaire pour consulter l’annuaire (ex. : ‘diradmin‘).
  • -p <LDAP admin password> : Mot de passe de l’utilisateur si un Bind est nécessaire pour consulter l’annuaire (sera demandé si absent).
  • -u <DN relatif branche Users> : DN relatif de la branche Users du LDAP (ex. : ‘cn=allusers‘, par défaut : ‘cn=users‘)
  • -D <filtre domaine email> : L’utilisation de ce paramètre permet de restreindre la processus aux utilisateurs du LDAP disposant d’une adresse email contenant un domaine (ex. : ‘-D mondomaine.fr‘ ou ‘-D @serveur.mail.domaine.fr‘).
  • -d <domaine email prioritaire> : Permet de n’exporter que les adresses email contenant le domaine, (ex. : ‘-d mondomaine.fr‘ ou ‘-d @serveur.mail.domaine.fr‘).
  • -U <UID de l'utilisateur à traiter> : Utilisateur à rechercher dans le LDAP pour créer un template personnalisé, par défaut l’UID de l’utilisateur courant est utilisé
  • -i <IP> : Utiliser cette option pour restreindre le lancement de la commande uniquement depuis certaines adresse IP, séparées par le caractère ‘%‘ (ex : ‘123.123.123.123%12.34.56.789‘).
  • -P <chemin export> : Chemin complet du dossier vers lequel exporter le résultat (ex. : ‘~/Desktop/‘ ou ‘/var/templatesoffice/‘, par défaut : ‘~/Library/Application Support/LibreOffice/4/user/template
  • -T <tag des exports> : Tag à ajouter au début du nom de fichier du template généré pour l’identifier.
  • -j <fichier Log> : Assure la journalisation dans un fichier de log à renseigner en paramètre. (ex. : ‘/Users/moi/Library/logs/LdapLibreOfficeTemplateGenerator.log‘) ou utilisez ‘default’ (/Users/utilisateurcourant/Library/logs/LdapLibreOfficeTemplateGenerator.log)

 

 Si vous souhaitez aller plus loin en travaillant sur d’autres attributs et d’autres tags

Si vous souhaitez aller plus loin en travaillant sur d’autres attributs et d’autres tags il vous faudra modifier mon script :

  • en modifiant la ligne 262 pour extraire depuis le LDAP les attributs nécessaires,
  • en traitant ensuite ces résultats comme voulu (vous pouvez vous inspirer du traitement que je fais sur les autres variables entre la ligne 272 et 353),
  • en remplaçant les tags de votre template (lignes 356 à 358 à compléter/modifier).

J’ai tâché de le commenter suffisamment pour que vous puissiez vous y retrouver normalement.

 

Le script en question

Vous pouvez le récupérer sur GitHub (https://github.com/yvangodard/LdapLibreOfficeTemplateGenerator) ou bien le consulter ici :

#! /bin/bash

VERSION="LdapLibreOfficeTemplateGenerator v 1.6 - 2020 - Yvan GODARD - godardyvan@gmail.com - http://goo.gl/c62RYH"
SCRIPT_DIR=$(dirname $0)
SCRIPT_NAME=$(basename $0)
SCRIPT_NAME_WITHOUT_EXT=$(echo "${SCRIPT_NAME}" | cut -f1 -d '.')
LDAP_URL=""
LDAP_DN_BASE=""
RACINE=""
OTT_MASTER_FILE=""
WITH_LDAP_BIND="no"
LDAP_ADMIN_UID=""
LDAP_ADMIN_PASS=""
LDAP_DN_USER_BRANCH="cn=users"
FILTER_ON_DOMAIN=""
DOMAIN_EMAIL=""
IP_FILTER=0
HOME_DIR=$(echo ~)
DIR_EXPORT=${HOME_DIR%/}/Library/Application\ Support/LibreOffice/4/user/template
TAG=""
HELP="no"
LOG_ACTIVE=0
USER_UID=$(whoami)
LOG=${HOME_DIR%/}/Library/logs/${SCRIPT_NAME_WITHOUT_EXT}.log
# Fichier temp
LOG_TEMP=$(mktemp /tmp/${SCRIPT_NAME_WITHOUT_EXT}.XXXXX)
TEMP_IP=$(mktemp /tmp/${SCRIPT_NAME_WITHOUT_EXT}_tempip.XXXXX)
LISTE_IP=$(mktemp /tmp/${SCRIPT_NAME_WITHOUT_EXT}_listeIP.XXXXX)
CONTENT_USER=$(mktemp /tmp/${SCRIPT_NAME_WITHOUT_EXT}_fiche.XXXXX)
CONTENT_USER_BASE=$(mktemp /tmp/${SCRIPT_NAME_WITHOUT_EXT}_fiche_decode.XXXXX)
LISTE_MAIL=$(mktemp /tmp/${SCRIPT_NAME_WITHOUT_EXT}_mail.XXXXX)
LISTE_TEL=$(mktemp /tmp/${SCRIPT_NAME_WITHOUT_EXT}_tel.XXXXX)
LISTE_MOBILE=$(mktemp /tmp/${SCRIPT_NAME_WITHOUT_EXT}_mobile.XXXXX)

help () {
	echo -e "$VERSION\n"
	echo -e "Cet outil permet de personnaliser un template LibreOffice en utilisant des informations issues d'un serveur LDAP."
	echo -e "Cet outil est placé sous la licence Creative Commons 4.0 BY NC SA."
	echo -e "\nAvertissement:"
	echo -e "Cet outil est distribué dans support ou garantie, la responsabilité de l'auteur ne pourrait être engagée en cas de dommage causé à vos données."
	echo -e "\nUtilisation:"
	echo -e "./${SCRIPT_NAME} [-h] | -s <URL Serveur LDAP> -r <DN Racine>"
	echo -e "                                      -t <template LibreOffice source>" 
	echo -e "                                      [-a <LDAP admin UID>] [-p <LDAP admin password>]"
	echo -e "                                      [-u <DN relatif branche Users>]"
	echo -e "                                      [-D <filtre domaine email>] [-d <domaine email prioritaire>]"
	echo -e "                                      [-U <UID de l'utilisateur à traiter>]"
	echo -e "                                      [-i <IP depuis lesquelles lancer le processus>]"
	echo -e "                                      [-P <chemin export>] [-t <tag des exports>]"
	echo -e "                                      [-j <log file>]"
	echo -e "\n\t-h:                                   Affiche cette aide et quitte."
	echo -e "\nParamètres obligatoires :"
	echo -e "\t-r <DN Racine> :                      DN de base de l'ensemble des entrées du LDAP (ex. : 'dc=server,dc=office,dc=com')."
	echo -e "\t-s <URL Serveur LDAP> :               URL du serveur LDAP (ex. : 'ldap://ldap.serveur.office.com')."
	echo -e "\t-t <template LibreOffice source> :    Chemin complet du fichier template OTT LibreOffice source à utiliser,"
	echo -e "\t                                      avec extension ott (ex. : '/Users/moi/templates/master_template.ott')."
	echo -e "\nParamètres optionnels :"
	echo -e "\t-a <LDAP admin UID> :                 UID de l'administrateur ou utilisateur LDAP si un Bind est nécessaire"
	echo -e "\t                                      pour consulter l'annuaire (ex. : 'diradmin')."
	echo -e "\t-p <LDAP admin password> :            Mot de passe de l'utilisateur si un Bind est nécessaire pour consulter"
	echo -e "\t                                      l'annuaire (sera demandé si absent)."
	echo -e "\t-u <DN relatif branche Users> :       DN relatif de la branche Users du LDAP"
	echo -e "\t                                      (ex. : 'cn=allusers', par défaut : '${LDAP_DN_USER_BRANCH}')"
	echo -e "\t-D <filtre domaine email> :           L'utilisation de ce paramètre permet de restreindre la processus aux utilisateurs"
	echo -e "\t                                      du LDAP disposant d'une adresse email contenant un domaine"
	echo -e "\t                                      (ex. : '-D mondomaine.fr' ou '-D @serveur.mail.domaine.fr')."
	echo -e "\t-d <domaine email prioritaire> :      Permet de n'exporter que les adresses email contenant le domaine,"
	echo -e "\t                                      (ex. : '-d mondomaine.fr' ou '-d @serveur.mail.domaine.fr')."
	echo -e "\t-U <UID de l'utilisateur à traiter> : Utilisateur à rechercher dans le LDAP pour créer un template personnalisé,"
	echo -e "\t                                      par défaut l'UID suivant est utilisé : ${USER_UID}"
	echo -e "\t-i <IP> :                             Utiliser cette option pour restreindre le lancement de la commande"
	echo -e "\t                                      uniquement depuis certaines adresse IP, séparées par le caractère '%'"
	echo -e "\t                                      (ex. : '123.123.123.123%12.34.56.789')"
	echo -e "\t-P <chemin export> :                  Chemin complet du dossier vers lequel exporter le résultat"
	echo -e "\t                                      (ex. : '~/Desktop/' ou '/var/templatesoffice/', par défaut : '${STANDARD_DIR_EXPORT}'"
	echo -e "\t-T <tag des exports> :                Tag à ajouter au début du nom de fichier du template généré pour l'identifier."	
	echo -e "\t-j <fichier Log> :                    Assure la journalisation dans un fichier de log à renseigner en paramètre."
	echo -e "\t                                      (ex. : '${LOG}')"
	echo -e "\t                                      ou utilisez 'default' (${LOG})"
}

function error () {
	# 1 Pas de connection internet
	# 2 Internet OK mais pas connecté au réseau local
	# 3 LDAP injoignable
	# 4 User inexistant
	# 5 Pas de correspondance pour ce domaine
	# 6 Fichier source modèle inexistant
	# 7 Autre erreur
	echo -e "\n*** Erreur ${1} ***"
	echo -e ${2}
	alldone ${1}
}

function alldone () {
	# Journalisation si nécessaire et redirection de la sortie standard
	[ ${1} -eq 0 ] && echo "" && echo ">>> Processus terminé OK !"
	if [ ${LOG_ACTIVE} -eq 1 ]; then
		exec 1>&6 6>&-
		[[ ! -f ${LOG} ]] && touch ${LOG}
		cat ${LOG_TEMP} >> ${LOG}
		cat ${LOG_TEMP}
	fi
	# Suppression des fichiers et répertoires temporaires
	[ -f ${LOG_TEMP} ] && rm -r ${LOG_TEMP}
	[ -f ${TEMP_IP} ] && rm -r ${TEMP_IP}
	[ -f ${CONTENT_USER} ] && rm -r ${CONTENT_USER}
	[ -f ${LISTE_MOBILE} ] && rm -r ${LISTE_MOBILE}
	[ -f ${LISTE_TEL} ] && rm -r ${LISTE_TEL}
	[ -f ${LISTE_MAIL} ] && rm -r ${LISTE_MAIL}
	[ -f ${LISTE_IP} ] && rm -r ${LISTE_IP}
	[ -f ${CONTENT_USER_BASE} ] && rm -r ${CONTENT_USER_BASE}
	[[ -d ${DIR_MODELE} ]] && rm -r ${DIR_MODELE}
	exit ${1}
}

# Fonction utilisée plus tard pour les résultats de requêtes LDAP encodées en base64
function base64decode () {
	echo ${1} | grep :: > /dev/null 2>&1
	if [ $? -eq 0 ] 
		then
		VALUE=$(echo ${1} | grep :: | awk '{print $2}' | openssl enc -base64 -d )
		ATTRIBUTE=$(echo ${1} | grep :: | awk '{print $1}' | awk 'sub( ".$", "" )' )
		echo "${ATTRIBUTE} ${VALUE}"
	else
		echo ${1}
	fi
}

# Vérification des options/paramètres du script 
optsCount=0
while getopts "hr:s:t:a:p:u:D:d:i:P:T:j:U:" OPTION
do
	case "$OPTION" in
		h)	HELP="yes"
						;;
		r)	LDAP_DN_BASE=${OPTARG}
			let optsCount=$optsCount+1
						;;
	    s) 	LDAP_URL=${OPTARG}
			let optsCount=$optsCount+1
						;;
	    t) 	OTT_MASTER_FILE=${OPTARG}
			let optsCount=$optsCount+1
						;;
		a)	LDAP_ADMIN_UID=${OPTARG}
			[[ ${LDAP_ADMIN_UID} != "" ]] && WITH_LDAP_BIND="yes"
						;;
		p)	LDAP_ADMIN_PASS=${OPTARG}
                        ;;
		u) 	LDAP_DN_USER_BRANCH=${OPTARG}
						;;
		D)	FILTER_ON_DOMAIN=${OPTARG}
                        ;;
		d)	DOMAIN_EMAIL=${OPTARG}
                        ;;
        U)	USER_UID=${OPTARG}
                        ;;
		i)	[[ ! -z ${OPTARG} ]] && echo ${OPTARG} | perl -p -e 's/%/\n/g' | perl -p -e 's/ //g' | awk '!x[$0]++' >> $LISTE_IP
			IP_FILTER=1
                        ;;
        P) [[ ! -z ${OPTARG} ]] && DIR_EXPORT=${OPTARG%/}
						;;
        T)	TAG=${OPTARG}
                        ;;
        j)	[ $OPTARG != "default" ] && LOG=${OPTARG}
			LOG_ACTIVE=1
                        ;;
	esac
done

if [[ ${optsCount} != "3" ]]
	then
        help
        error 7 "Les paramètres obligatoires n'ont pas été renseignés."
fi

if [[ ${HELP} = "yes" ]]
	then
	help
fi

if [[ ${WITH_LDAP_BIND} = "yes" ]] && [[ ${LDAP_ADMIN_PASS} = "" ]]
	then
	echo "Entrez le mot de passe LDAP pour uid=$LDAP_ADMIN_UID,$LDAP_DN_USER_BRANCH,$LDAP_DN_BASE :" 
	read -s LDAP_ADMIN_PASS
fi

# Redirection de la sortie strandard vers le fichier de log
if [ $LOG_ACTIVE -eq 1 ]; then
	echo -e "\n >>> Please wait ... >>> Merci de patienter ..."
	exec 6>&1
	exec >> ${LOG_TEMP}
fi

echo -e "\n****************************** `date` ******************************\n"
echo -e "$0 démarré..."


# Test fichier template source
EXTENSION_TEMPLATE_MASTER=$(echo "${OTT_MASTER_FILE}" | sed 's/^.*\(...$\)/\1/' | perl -p -e 's/ //g' | perl -p -e 's/\.//g')
[[ ! ${EXTENSION_TEMPLATE_MASTER} == ott ]] && error 6 "Format de fichier modèle ${OTT_MASTER_FILE} incorrect."

# Par sécurité, attendons quelques instants
sleep 5

# Testons si une connection internet est ouverte
dig +short myip.opendns.com @resolver1.opendns.com > /dev/null 2>&1
[ $? -ne 0 ] && error 1 "Non connecté à internet."

# Récupérons notre IP dans un fichier temporaire
TEST_CONNECT=$(dig +short myip.opendns.com @resolver1.opendns.com)
echo ${TEST_CONNECT} > $TEMP_IP
echo "Adresse IP actuelle :"
cat ${TEMP_IP}

# Testons si nous sommes connectés sur un réseau ayant pour IP Publique une IP autorisée
if [[ ${IP_FILTER} -ne 0 ]]; then
	IP_OK=O
	for IP in $(cat ${LISTE_IP})
	do
		TEST_GREP=$(grep -c "$IP" ${TEMP_IP})
		[ $TEST_GREP -eq 1 ] && let IP_OK=$IP_OK+1 && echo "Connecté au réseau autorisé sur l'IP publique "$(cat ${TEMP_IP})"."
	done
	[[ ${IP_OK} -eq 0 ]] && error 2 "Connecté à internet hors du réseau local."
fi

# Test connection LDAP
echo -e "\nConnecting LDAP at $LDAP_URL..."
[[ ${WITH_LDAP_BIND} = "no" ]] && LDAP_COMMAND_BEGIN="ldapsearch -LLL -H ${LDAP_URL} -x"
[[ ${WITH_LDAP_BIND} = "yes" ]] && LDAP_COMMAND_BEGIN="ldapsearch -LLL -H ${LDAP_URL} -D uid=${LDAP_ADMIN_UID},${LDAP_DN_USER_BRANCH},${LDAP_DN_BASE} -w ${LDAP_ADMIN_PASS}"

${LDAP_COMMAND_BEGIN} -b ${LDAP_DN_USER_BRANCH},${LDAP_DN_BASE} > /dev/null 2>&1
[ $? -ne 0 ] && error 3 "Problème de connexion au serveur LDAP ${LDAP_URL}.\nVérifiez vos paramètres de connexion."

# Test si l'utilisateur existe dans le ldap
[[ -z $(${LDAP_COMMAND_BEGIN} -b ${LDAP_DN_USER_BRANCH},${LDAP_DN_BASE} -x uid=${USER_UID}) ]] && error 4 "Aucune correspondance avec l'identifiant ${USER_UID} trouvé dans le LDAP ${LDAP_URL}, dans la branche '${LDAP_DN_USER_BRANCH},${LDAP_DN_BASE}'."
[[ ! ${FILTER_ON_DOMAIN} == "" ]] && [[ -z $(${LDAP_COMMAND_BEGIN} -b ${LDAP_DN_USER_BRANCH},${LDAP_DN_BASE} -x uid=${USER_UID} mail | grep ${FILTER_ON_DOMAIN}) ]] && error 5 "Aucune correspondance pour ${FILTER_ON_DOMAIN} avec l'identifiant ${USER_UID} trouvé dans le LDAP ${LDAP_URL}, dans la branche '${LDAP_DN_USER_BRANCH},${LDAP_DN_BASE}'."

# Répertoires de travail
[[ ! -f ${OTT_MASTER_FILE} ]] && error 6 "Fichier modèle ${OTT_MASTER_FILE} inexistant."
DIR_OTT=$(dirname ${OTT_MASTER_FILE})
NAME_MODELE=$(basename ${OTT_MASTER_FILE})
RACINE=$(echo "${NAME_MODELE}" | cut -f1 -d '.')
DIR_MODELE="${DIR_OTT}/${RACINE}"
[[ -d ${DIR_MODELE} ]] && rm -r ${DIR_MODELE}
mkdir -p ${DIR_MODELE}
[ $? -ne 0 ] && error 7 "Problème pour créer le répertoire '${DIR_MODELE}'."
[[ ! -d ${DIR_MODELE} ]] && error 7 "Problème pour accéder au répertoire '${DIR_MODELE}'."
[[ ! -w ${DIR_MODELE} ]] && error 7 "Problème de droits d'accès en écriture au dossier ${DIR_MODELE}'."
cd ${DIR_MODELE}
echo -e "\nUNZIP vers '${DIR_MODELE}/' :"
unzip ${OTT_MASTER_FILE}
MODELE="${DIR_MODELE}/content.xml"
META="${DIR_MODELE}/meta.xml"

# Test répertoire export
[[ ! -d ${DIR_EXPORT} ]] && mkdir -p "${DIR_EXPORT}"
[[ ! -d ${DIR_EXPORT} ]] && error 7 "Problème pour accéder au répertoire '${DIR_EXPORT}'."
[[ ! -w ${DIR_EXPORT} ]] && error 7 "Problème de droits d'accès en écriture au dossier ${DIR_EXPORT}'."

# Récupérer les variables nécessaires
${LDAP_COMMAND_BEGIN} -b ${LDAP_DN_USER_BRANCH},${LDAP_DN_BASE} -x uid=${USER_UID} givenName sn cn title telephoneNumber mobile mail initials > ${CONTENT_USER_BASE}

# Correction to support LDIF splitted lines, thanks to Guillaume Bougard (gbougard@pkg.fr)
perl -n -e 'chomp ; print "\n" unless (substr($_,0,1) eq " " || !defined($lines)); $_ =~ s/^\s+// ; print $_ ; $lines++;' -i "${CONTENT_USER_BASE}"

# Décodage des informations
OLDIFS=$IFS; IFS=$'\n'
for LINE in $(cat ${CONTENT_USER_BASE})
do
	base64decode $LINE >> ${CONTENT_USER}
done
IFS=$OLDIFS

# Récupération des données
# Nom (de famille)
# Prénom 
# Titre (fonction)
NOMCOMPLET=$(cat ${CONTENT_USER} | grep ^cn: | perl -p -e 's/cn: //g')
NOM=$(cat ${CONTENT_USER} | grep ^sn: | perl -p -e 's/sn: //g')
PRENOM=$(cat ${CONTENT_USER} | grep ^givenName: | perl -p -e 's/givenName: //g')
TITRE=$(cat ${CONTENT_USER} | grep ^title: | perl -p -e 's/title: //g')
# Traitement des initiales
# Si existe dans LDAP, on récupère dans le LDAP
INITIALES=$(cat ${CONTENT_USER} | grep ^initials: | perl -p -e 's/initials: //g')
# Si les initiales ne sont pas renseignée dans le LDAP on les génère depuis le Nom et le Prénom
[[ -z ${INITIALES} ]] && INITIALES="${PRENOM:0:1}${NOM:0:1}"

# Email
# Si plusieurs emails sont renseignés pour l'utilisateur dans le LDAP on garde prioritairement 
# celui qui contient l'UID de l'utilisateur 
# et si le paramètre -d est utilisé on garde prioritairement (si l'un des emails correspond)
# une adresse email qui contient le nom de domaine renseigné en paramètre
cat ${CONTENT_USER} | grep ^mail: | perl -p -e 's/mail: //g' > ${LISTE_MAIL}
LINES_NUMBER=$(cat ${LISTE_MAIL} | grep "." | wc -l) 
if [ ${LINES_NUMBER} -eq 1 ]; then
	MAIL=$(head -n 1 ${LISTE_MAIL})
elif [ ${LINES_NUMBER} -gt 1 ]; then
	if [ -z ${DOMAIN_EMAIL} ]; then
		cat ${LISTE_MAIL} | grep ${USER_UID} > /dev/null 2>&1
		if [ $? -ne 0 ]; then
			MAIL=$(head -n 1 ${LISTE_MAIL})
		else
			MAIL=$(cat ${LISTE_MAIL} | grep ${USER_UID} | head -n 1)
		fi
	else
		cat ${LISTE_MAIL} | grep ${DOMAIN_EMAIL} | grep ${USER_UID} > /dev/null 2>&1
		if [ $? -eq 0 ]; then
			MAIL=$(cat ${LISTE_MAIL} | grep ${DOMAIN_EMAIL} | grep ${USER_UID} | head -n 1)
		else
			cat ${LISTE_MAIL} | grep ${DOMAIN_EMAIL} > /dev/null 2>&1
			if [ $? -eq 0 ]; then
				MAIL=$(cat ${LISTE_MAIL} | grep ${DOMAIN_EMAIL} | head -n 1)
			else
				cat ${LISTE_MAIL} | grep ${USER_UID} > /dev/null 2>&1
				if [ $? -eq 0 ]; then
					MAIL=$(cat ${LISTE_MAIL} | grep ${USER_UID} | head -n 1)
				else
					MAIL=$(head -n 1 ${LISTE_MAIL})
				fi
			fi
		fi
	fi
fi

# Fonction formatant au format international les numéros de téléphone
function telFormat () {
	NUMBER_TEL=$(echo ${1} | perl -p -e 's/\.//g' | perl -p -e 's/ //g' | perl -p -e 's/\(//g' | perl -p -e 's/\)//g')
	if [[ ${#NUMBER_TEL} -eq 10 ]] && [[ ${NUMBER_TEL:0:1} -eq 0 ]]; then
		echo ${NUMBER_TEL:0:2}" "${NUMBER_TEL:2:2}" "${NUMBER_TEL:4:2}" "${NUMBER_TEL:6:2}" "${NUMBER_TEL:8:2}
	elif [[ ${#NUMBER_TEL} -eq 12 ]] && [[ ${NUMBER_TEL:0:1} == "+" ]]; then
		echo ${NUMBER_TEL:0:3}" (0)"${NUMBER_TEL:3:1}" "${NUMBER_TEL:4:2}" "${NUMBER_TEL:6:2}" "${NUMBER_TEL:8:2}" "${NUMBER_TEL:10:2}
	elif [[ ${#NUMBER_TEL} -eq 13 ]] && [[ ${NUMBER_TEL:0:1} == "+" ]]; then
		echo ${NUMBER_TEL:0:3}" (0)"${NUMBER_TEL:4:1}" "${NUMBER_TEL:5:2}" "${NUMBER_TEL:7:2}" "${NUMBER_TEL:9:2}" "${NUMBER_TEL:11:2}
	else
		echo ${NUMBER_TEL}
	fi
}

# Traitement numéro de téléphone direct
OLDIFS=$IFS; IFS=$'\n'
for LINE in $(cat ${CONTENT_USER} | grep ^telephoneNumber: | perl -p -e 's/telephoneNumber: //g'| perl -p -e 's/ //g')
do
	 telFormat ${LINE} >> ${LISTE_TEL}
done
LIGNEDIRECTE=$(cat ${LISTE_TEL} | perl -p -e 's/\n/ - /g' | awk 'sub( "...$", "" )')
IFS=$OLDIFS

# Traitement numéro de téléphone portable pro
OLDIFS=$IFS; IFS=$'\n'
for LINE in $(cat ${CONTENT_USER} | grep ^mobile: | perl -p -e 's/mobile: //g'| perl -p -e 's/ //g')
do
	 telFormat ${LINE} >> ${LISTE_MOBILE}
done
MOBILE=$(cat ${LISTE_MOBILE} | perl -p -e 's/\n/ - /g' | awk 'sub( "...$", "" )')
IFS=$OLDIFS

# Modifier le fichier source (template LibreOffice)
cat ${MODELE} | perl -p -e "s/NOMCOMPLET/${NOMCOMPLET}/g" | sed "s/MAIL/${MAIL}/" | perl -p -e "s/TITREPERSONNE/${TITRE}/g" | perl -p -e "s/INITIALES/${INITIALES}/g"> ${MODELE}.new && mv ${MODELE} ${MODELE}.old && mv ${MODELE}.new ${MODELE} && rm ${MODELE}.old
[[ ! -z ${LIGNEDIRECTE} ]] && cat ${MODELE} | perl -p -e "s/LIGNEDIRECTE/${LIGNEDIRECTE}/g" > ${MODELE}.new && mv ${MODELE} ${MODELE}.old && mv ${MODELE}.new ${MODELE} && rm ${MODELE}.old
[[ -z ${LIGNEDIRECTE} ]] && cat ${MODELE} | perl -p -e "s/Ligne directe :/ /g" | perl -p -e "s/LIGNEDIRECTE/ /g" > ${MODELE}.new && mv ${MODELE} ${MODELE}.old && mv ${MODELE}.new ${MODELE} && rm ${MODELE}.old
[[ ! -z ${MOBILE} ]] && cat ${MODELE} | perl -p -e "s/MOBILE/${MOBILE}/g" > ${MODELE}.new && mv ${MODELE} ${MODELE}.old && mv ${MODELE}.new ${MODELE} && rm ${MODELE}.old
[[ -z ${MOBILE} ]] && cat ${MODELE} | perl -p -e "s/Mobile :/ /g" | perl -p -e "s/MOBILE/ /g" > ${MODELE}.new && mv ${MODELE} ${MODELE}.old && mv ${MODELE}.new ${MODELE} && rm ${MODELE}.old

# Préparation du Nom du fichier template LibreOffice
[[ ${TAG} == "" ]] && NOUVEAU_NOM=$(echo ${RACINE} | tr [a-z] [A-Z])-$(echo ${INITIALES} | tr [a-z] [A-Z])
[[ ! ${TAG} == "" ]] && NOUVEAU_NOM=$(echo ${TAG})-$(echo ${RACINE} | tr [a-z] [A-Z])-$(echo ${INITIALES} | tr [a-z] [A-Z])
# Modifier méta
cat ${META} | perl -p -e "s/${RACINE}/${NOUVEAU_NOM}/g" > ${META}.new && mv ${META} ${META}.old && mv ${META}.new ${META} && rm ${META}.old

OLD_LINE_TITLE=$(xmllint --format ${META} | grep "dc:title")
NEW_LINE_TITLE="<dc:title>${NOUVEAU_NOM}</dc:title>"

if [[ ! -z ${OLD_LINE_TITLE} ]]; then
	touch ${META}.new
	touch ${META}.new2
	xmllint --format ${META} | grep -v "dc:creator" | grep -v "meta:template" | grep -v "meta:initial-creator" | grep -v "meta:printed-by" | grep -v "meta:print-date" | perl -p -e "s#${OLD_LINE_TITLE}#${NEW_LINE_TITLE}#" > ${META}.new 
	xmllint --format ${META}.new > ${META}.new2
	mv ${META} ${META}.old && mv ${META}.new2 ${META} && rm ${META}.old && rm ${META}.new
fi

# ZIP fichier source
cd ${DIR_MODELE}
echo -e "\nZIP du nouveau template :"
zip -r newtemplate.ott *
[ $? -ne 0 ] && error 7 "Problème pour créer l'archive '${DIR_MODELE}/newtemplate.ott'."

# Déplacer dans le répertoire souhaité le résultat
echo -e "\nDéplacement vers '${DIR_EXPORT%/}/${NOUVEAU_NOM}.ott' :"
mv "${DIR_MODELE}/newtemplate.ott" "${DIR_EXPORT%/}/${NOUVEAU_NOM}.ott"
[ $? -ne 0 ] && error 7 "Problème pour déplacer le fichier à l'emplacement '${DIR_EXPORT%/}/${NOUVEAU_NOM}.ott'."
find "${DIR_EXPORT%/}/" -name ".DS_Store" -delete
[ $? -ne 0 ] && error 7 "Problème lors du nettoyage des fichiers .DS_Store à l'emplacement '${DIR_EXPORT%/}/'."
cd ~

alldone 0
LdapLibreOfficeTemplateGenerator.shview rawview file on GitHub

 

Pour installer cet outil, téléchargez le script dans le dossier où vous voulez l’installer :

wget --no-check-certificate https://github.com/yvangodard/LdapLibreOfficeTemplateGenerator/blob/master/LdapLibreOfficeTemplateGenerator.sh
sudo chmod 755 LdapLibreOfficeTemplateGenerator.sh

Pour une aide complète, installez le script et lancez le :

./LdapLibreOfficeTemplateGenerator.sh help

 

Tester, tester, tester …

Pour tester, vous pouvez par exemple lancer la commande suivante (à adapter évidemment en fonction de vos paramètres) :

/usr/local/LdapLibreOfficeTemplateGenerator/LdapLibreOfficeTemplateGenerator.sh -r dc=serveur,dc=monserveur,dc=fr -s ldap://serveurldap.monserveur.fr -t /usr/local/LdapLibreOfficeTemplateGenerator/entete_courrier.ott -i 188.710.2.46%199.19.109.77 -T NewCourrier -d @mondomaine.fr -j default

Dans ce cas :

  • on suppose que vous avez installé mon outil LdapLibreOfficeTemplateGenerator.sh à l’emplacement /usr/local/LdapLibreOfficeTemplateGenerator/,
  • on attaque notre annuaire LDAP à l’adresse ldap://serveurldap.monserveur.fr,
  • notre template “de base” (cf. étape 1) est stocké à l’emplacement : /usr/local/LdapLibreOfficeTemplateGenerator/entete_courrier.ott,
  • le DN de base de notre annuaire LDAP est dc=serveur,dc=monserveur,dc=fr,
  • les utilisateurs sont dans la branche standard du LDAP cn=users (du coup on n’utilise pas l’option -u),
  • notre serveur LDAP ne nécessite de Bind préalable pour la consultation en lecture seule, pas besoin d’utiliser les paramètres -a et -p,
  • on exporte prioritairement les adresses email contenant ‘@mondomaine.fr‘ d’où l’usage de -d @mondomaine.fr,
  • on garde une trace de l’exécution à l’emplacement standard dans un fichier de log (-j default),
  • on restreint l’utilisation de ce script uniquement si on est connecté avec comme adresse IP publique 188.710.2.46 ou 199.19.109.77 : cela est utile dans notre cas car notre LDAP est accessible en lecture seule uniquement depuis le réseau local de l’entreprise ou depuis un VPN,
  • on souhaite que le template personnalisé soit intitulé NewCourrier-*******.ott,
  • on réalise l’export à l’emplacement standard de LibreOffice, ce qui permet de retrouver notre modèle directement dans les modèles accessible depuis le menu Fichier > Nouveau > Modèles de LibreOffice.

Si cela ne produit pas le résultat escompté (vous ne trouvez pas votre fichier ‘~/Library/Application Support/LibreOffice/4/user/template‘) il vous faudra débugguer en vous servant du fichier de log que vous retrouverez à l’emplacement ‘~/Library/logs/LdapLibreOfficeTemplateGenerator.log').

 

Si vous voyez des améliorations à apporter à cet outil, n’hésitez pas à utiliser les commentaires !

 

Etape 3 : installer Outset

Si vous ne disposez pas d’Outset (ou d’un outil équivalent), l’automatisation ne sera pas complète et on perdra une bonne partie de l’intérêt de notre outil.

Pour vous faciliter la vie, voici en téléchargement, un package d’installation d’Outset. Ce package installera Outset à l’emplacement /usr/local/outset.

Comme je le disais plus haut, pour vous faciliter la vie, pensez à utiliser Munki pour déployer ce package (et tous les autres) sur votre parc informatique Apple.

 

Etape 4 : finaliser l’installation

Pour finaliser l’installation il nous faut :

  • déployer l’outil LdapLibreOfficeTemplateGenerator.sh sur les postes du parc informatique
  • déployer le (ou les) templates de base (cf. étape 1), dans mon cas je les dépose au même endroit que LdapLibreOfficeTemplateGenerator.sh, c’est à dire à l’emplacement /usr/local/LdapLibreOfficeTemplateGenerator/
  • créer un script parent qui va être lancé par Outset (cf. étape précédente). Dans mon cas, j’ai fait simple, et je le déposerai à l’emplacement /usr/local/outset/login-every/templatesLdapLibreOffice.sh pour qu’il soit lancé par Outset à chaque ouverture de session :
    #!/bin/sh
    # On vérifie que l'outil LdapLibreOfficeTemplateGenerator.sh existe
    [[ ! -e /usr/local/LdapLibreOfficeTemplateGenerator/LdapLibreOfficeTemplateGenerator.sh ]] && exit 1
    # On lance nos commandes, ici deux :
    /usr/local/LdapLibreOfficeTemplateGenerator/LdapLibreOfficeTemplateGenerator.sh -r dc=serveur,dc=monserveur,dc=fr -s ldap://serveurldap.monserveur.fr -t /usr/local/LdapLibreOfficeTemplateGenerator/entete_courrier.ott -i 188.710.2.46%199.19.109.77 -T NewCourrier -d @mondomaine.fr -j default
    /usr/local/LdapLibreOfficeTemplateGenerator/LdapLibreOfficeTemplateGenerator.sh -r dc=serveur,dc=monserveur,dc=fr -s ldap://serveurldap.monserveur.fr -t /usr/local/LdapLibreOfficeTemplateGenerator/entete_courrier2.ott -i 188.710.2.46%199.19.109.77 -T NewCourrier -j default
    exit 0

Il ne vous reste plus qu’à créer un petit package avec ces trois actions/fichiers et à le déployer sur votre parc !

 

En guise de conclusion …

Alors, oui, j’assume, ça m’a pris quelques temps pour écrire cet outil, le tester, le mettre en place … mais je ne m’en préoccupe plus du tout maintenant. Et au passage j’ai un peu progressé en rédigeant ce script Bash !

C’est tout bénéfice !

 

Posted in Apple et Macintosh, Unix et LinuxTags: