Compare commits

...

27 commits

Author SHA1 Message Date
Benjamin Renard 151b229c3d
LSformElement_postaladdress: update OSM nominatim search URL 2024-04-24 11:22:39 +02:00
Benjamin Renard 118b784a5d
API: allow to execute LSobject's custom actions 2024-04-04 16:11:06 +02:00
Benjamin Renard 2db4d0fbae
Add possibily to make global search using API 2024-03-28 12:44:18 +01:00
Benjamin Renard bd98a8b8ef
LSldapObject: add force_generation_if_empty parameter 2024-03-04 11:36:05 +01:00
Benjamin Renard 1a88707f87
LSldapObject: defaulty set attribute default value on creation even if is not present in form
Could be configured using the new set_default_value_on_creation_if_empty 
parameter.
2024-03-04 11:34:41 +01:00
Benjamin Renard 4ce95e54b8
LSattr_html_gpg_pub_key: improve CSS 2024-02-28 19:10:48 +01:00
Benjamin Renard 2ccf579125
LSattr_html_gpg_pub_key & LSformRule_gpg_pub_key: fix setting GnuPG home dir in PHP 7.3 2024-02-28 18:47:09 +01:00
Benjamin Renard 171161ae18
Add LSattr_html_gpg_pub_key & LSformRule_gpg_pub_key 2024-02-28 18:18:57 +01:00
Benjamin Renard d26c52400e
LSaddon::mail: fix handling CC/BCC headers if not provided as array 2024-02-22 19:13:35 +01:00
Benjamin Renard 62759330a8
LSaddon::mail: remove useless label 2024-02-22 18:52:11 +01:00
Benjamin Renard 5e9773a015
LSaddon::mail: improve CSS of templates editor view 2024-02-22 18:10:28 +01:00
Benjamin Renard 557db5d456
Fix Gitlab CI tests 2024-02-22 17:03:29 +01:00
Benjamin Renard 15f67ef00d
Improve docker image to make it smaller 2024-02-22 16:57:18 +01:00
Benjamin Renard 753f47fa97
Move mail template in LSaddon::mail and add editor view and docs 2024-02-22 16:15:00 +01:00
Benjamin Renard 89c363bd80
Fix handling mime type on serving libs's static files 2024-02-22 15:10:33 +01:00
Benjamin Renard e59ab4a94c
Add common LStabs CSS classes to handle tabs content
Based on stuff provided by LSaccessRightsMatrixView.
2024-02-22 15:09:20 +01:00
Benjamin Renard 87e58e6425
LSmail: add possibility to send email using template 2024-02-19 20:19:52 +01:00
Benjamin Renard e9c49c242f
LSaddon::phpldapadmin: fix default config to avoid PHP warning in CLI mode 2024-02-19 19:58:51 +01:00
Benjamin Renard e8781ab779
LScli::unquote_word: Fix PHP warning 2024-02-19 19:58:04 +01:00
Benjamin Renard 15e3ffe6ef
LSaddon::mail: Fix handling multiple recipients 2024-02-19 19:46:27 +01:00
Benjamin Renard 34396a5fe1
Improve test_send_mail CLI command 2024-02-19 19:46:26 +01:00
Benjamin Renard dc8e08b5d1 Fix PHP 8.2 error on sorting values 2024-02-10 18:35:54 +01:00
Benjamin Renard bf1a3affe4
Add selection boxes feature on objects list page (for LSsearch customActions) 2024-02-01 14:25:59 +01:00
Benjamin Renard 2104187b14
LSaddon::ppolicy: fix registering exportPpolicyInfo API method 2023-12-05 13:02:45 +01:00
Benjamin Renard 78af62d428
LSaddon::ppolicy: improve password expiration extra displayed column
Add LStip to show password last changed date and password max age.
2023-12-05 12:51:54 +01:00
Benjamin Renard 2d08374a53
build-deb.sh: exclude docker & build-deb.sh related commits in generated changelog 2023-10-26 18:22:21 +02:00
Benjamin Renard 129cfa537b
Docker: add build-and-push-all.sh 2023-10-26 18:19:20 +02:00
58 changed files with 2618 additions and 485 deletions

View file

@ -7,6 +7,7 @@ require '/usr/share/php/phpseclib/autoload.php';
require 'CAS.php';
require 'Mail.php';
require 'Mail/mime.php';
require 'Html2Text.php';
spl_autoload_register(array('LSsession', 'loadLSclass'));
LSsession :: initialize();

View file

@ -114,10 +114,11 @@ $GITDCH \
--code-name $DEBIAN_CODENAME \
--output $DIST_DIR/debian/changelog \
--release-notes ../release-notes.md \
--exclude "^Docker: " \
--exclude "^CI: " \
--exclude "^debian: " \
--exclude "\.gitlab-ci\.yml" \
--exclude "build\.sh" \
--exclude "build-deb\.sh" \
--exclude "README\.md" \
--exclude "^Merge branch " \
--verbose "${GITDCH_EXTRA_ARGS[@]}"

2
debian/control vendored
View file

@ -7,7 +7,7 @@ Maintainer: Benjamin Renard <brenard@easter-eggs.com>
Package: ldapsaisie
Architecture: all
Depends: apache2 | httpd, php-ldap | php5-ldap, php-fpm | libapache2-mod-php5 | libapache2-mod-php | php5-cli | php-cli, smarty | smarty3, php-net-ldap2, php-console-table
Recommends: php-mbstring, php-phpseclib, php-unidecode, php-zxcvbn, php-ftp, php-mail, php-mail-mime
Recommends: php-mbstring, php-phpseclib, php-unidecode, php-zxcvbn, php-ftp, php-mail, php-mail-mime, php-html2text, php-gnupg
Description: web based interface for managing LDAP servers content
LdapSaisie is a Web application developed to manage LDAP directory.
It has been written in PHP / JavaScript and is published under the

View file

@ -97,6 +97,7 @@ nav:
- Introduction: conf/LSobject/LSattribute/LSattr_html/index.md
- boolean: conf/LSobject/LSattribute/LSattr_html/LSattr_html_boolean.md
- date: conf/LSobject/LSattribute/LSattr_html/LSattr_html_date.md
- gpg_pub_key: conf/LSobject/LSattribute/LSattr_html/LSattr_html_gpg_pub_key.md
- image: conf/LSobject/LSattribute/LSattr_html/LSattr_html_image.md
- jsonCompositeAttribute: conf/LSobject/LSattribute/LSattr_html/LSattr_html_jsonCompositeAttribute.md
- labeledValue: conf/LSobject/LSattribute/LSattr_html/LSattr_html_labeledValue.md
@ -127,6 +128,7 @@ nav:
- differentPassword: conf/LSobject/LSattribute/check_data/differentPassword.md
- email: conf/LSobject/LSattribute/check_data/email.md
- filesize: conf/LSobject/LSattribute/check_data/filesize.md
- gpg_pub_key: conf/LSobject/LSattribute/check_data/gpg_pub_key.md
- imagefile: conf/LSobject/LSattribute/check_data/imagefile.md
- imagesize: conf/LSobject/LSattribute/check_data/imagesize.md
- inarray: conf/LSobject/LSattribute/check_data/inarray.md

View file

@ -176,7 +176,8 @@ HTTP 404 sera générée.
Permet de réclamer un résultat de recherche dans lequel, la clé `objects` sera une liste et
non un dictionnaire. Dans ce cas, le DN de l'objet est fourni dans la clé `dn` des détails
des objets.
des objets. Seul la présence de ce paramètre suffit à activer ce comportement, sa valeur n'a pas
d'importance.
- `withoutCache`
@ -397,6 +398,35 @@ HTTP 404 sera générée.
}
```
- `/api/1.0/object/[object type]/[dn]/customAction/[customAction]`
Cette méthode permet d'exécuter une [action personnalisée](../conf/LSobject/customActions.md) sur
un objet dans l'annuaire. Le nom de l'action ainsi que le type de l'objet et son DN sont précisés
dans l'URL et doivent être encodés en conséquence.
**Exemple :**
```
# curl -u username:secret 'https://ldapsaisie/api/1.0/object/LSpeople/uid=foo.bar,ou=people,o=ls/customAction/action?pretty'
{
"dn": "uid=foo.bar,ou=people,o=ls",
"type": "LSpeople",
"name": "Foo Bar",
"success": true,
"messages": [
"L'action personnalis\u00e9e action a \u00e9t\u00e9 correctement ex\u00e9cut\u00e9e sur Foo Bar.",
]
}
```
!!! note
Par défaut, une action personnalisée ne retourne qu'un booléen permettant de savoir si
l'action a correctement été exécutée ou non. En outre, dans un contexte d'appel via l'API, il
est possible de retourner des informations via un tableau associatif dont le contenu sera
fusionné avec les données retournées par la requête. Pour plus d'informations à ce sujet,
consultez la [documentation sur l'écriture d'une fonction implémentant une customAction](../conf/LSobject/customActions.md#ecriture-dune-fonction-implementant-une-customaction).
- `/api/1.0/object/[object type]/import`
Cette méthode permet d'importer des objets d'un type en particulier à partir de données d'import
@ -560,3 +590,168 @@ HTTP 404 sera générée.
]
}
```
- `/api/1.0/search`
Cette méthode permet d'effectuer une recherche sur plusieurs types d'objets de l'annuaire à la
fois. Par mimétisme du comportement de l'interface web, la recherche est paginée et accepte des
paramètres similaires en plus de paramètre plus appropriés à un fonctionnement programmatique.
Les paramètres acceptés par cette méthode sont sensiblement les mêmes que ceux acceptés par la
méthode de recherche d'un type d'objet de l'annuaire en particulier et ils ne seront donc pas
tous redocumentés ici :
- `filter`
- `predefinedFilter`
- `pattern`
- `approx`
- `basedn`
- `subDn`
- `scope`
- `recursive`
- `displayFormat`
- `extraDisplayedColumns`
- `attributes`
- `attributesDetails`
- `page`
- `all`
- `as_list`
- `withoutCache`
- `keepParamsBetweenSearches`
- `nbObjectsByPage`
Permet de préciser le nombre maximum d'objets retournés par type d'objet ET par page du résultat
de recherche.
- `types`
Permet de limiter les types d'objets à inclure dans le résultat de recherche. Par défaut, tous
les types d'objets auxquels l'utilisateur à accès et dont la recherche globale n'est pas
désactivée seront inclus.
- `splited_result`
Permet de faire en sorte que les objets inclus dans le résultat de recherche soient séparés par
type dans des sous-clés de `objects`. Par défaut, tous les objets sont retournés dans la clé
`objects` et une sous-clé `type` est ajouté à chacun d'eux pour les distinguer. Seul la présence
de ce paramètre suffit à activer ce comportement, sa valeur n'a pas d'importance.
!!! important
**Pour chaque type d'objets inclus dans la recherche, un filtre et/ou un mot clé de recherche
doit être spécifié.** Cette méthode n'a pas vocation à permettre de lister tous les objets de
l'annuaire.
**Exemple :**
```
# curl -u username:secret 'https://ldapsaisie/api/1.0/search?pattern=LdapSaisie&pretty'
{
"success": true,
"objects": {
"uid=s.ldapsaisie,ou=people,o=ls": {
"name": "Secretariat LdapSaisie",
"type": "LSpeople",
"Mail": "secretariat@ldapsaisie.biz"
},
"uid=ls,ou=people,o=ls": {
"name": "LdapSaisie",
"type": "LSpeople",
"Mail": "ldap.saisie@ls.com"
},
"uid=erwpa,ou=people,o=ls": {
"name": "Erwan PAGE",
"type": "LSpeople",
"Mail": "erwan.page@ldapsaisie.biz"
},
"uid=invite,ou=people,o=ls": {
"name": "Utilisateur de passage",
"type": "LSpeople",
"Mail": "invite@ldapsaisie.biz"
},
"uid=demo,ou=people,o=ls": {
"name": "Demonstration LdapSaisie",
"type": "LSpeople",
"Mail": "demo@ls.com"
},
"uid=admin,ou=people,o=ls": {
"name": "Administration LdapSaisie",
"type": "LSpeople",
"Mail": "admin@ls.com"
},
"uid=admin3,ou=people,o=ls": {
"name": "ZAdministration LdapSaisie",
"type": "LSpeople",
"Mail": "admin@ls.com"
},
"uid=ldapsaisie,ou=sysaccounts,o=ls": {
"name": "ldapsaisie",
"type": "LSsysaccount"
}
},
"total": null,
"params": {
"keepParamsBetweenSearches": false,
"LSpeople": {
"filter": null,
"pattern": "LdapSaisie",
"predefinedFilter": false,
"basedn": null,
"scope": null,
"sizelimit": 0,
"attronly": false,
"approx": false,
"recursive": true,
"attributes": [],
"onlyAccessible": true,
"sortDirection": null,
"sortBy": null,
"sortlimit": 0,
"displayFormat": "%{cn}",
"nbObjectsByPage": 25,
"withoutCache": false,
"extraDisplayedColumns": true
},
"LSsysaccount": {
"filter": null,
"pattern": "LdapSaisie",
"predefinedFilter": false,
"basedn": null,
"scope": null,
"sizelimit": 0,
"attronly": false,
"approx": false,
"recursive": false,
"attributes": [],
"onlyAccessible": true,
"sortDirection": null,
"sortBy": null,
"sortlimit": 0,
"displayFormat": "%{uid}",
"nbObjectsByPage": 30,
"withoutCache": false,
"extraDisplayedColumns": true
}
},
"page": 1,
"nbPages": 1
}
```

View file

@ -1,8 +1,35 @@
# LSaddon_mail
Cet [LSaddon](index.md#configuration-des-lsaddons) est utilisé pour gérer l'envoie de mail. Il
Cet [LSaddon](index.md#configuration-des-lsaddons) est utilisé pour gérer l'envoi de courriels. Il
utilise pour cela les librairies [PEAR](http://pear.php.net/) *Mail* et *Mail_Mime* qui doivent être
installés. Cet [LSaddon](index.md#configuration-des-lsaddons) doit être configuré en éditant son
installés.
Cet [LSaddon](index.md#configuration-des-lsaddons) offre aussi la possibilité d'envoyer des
courriels dont le contenu est construit à partir de modèles. Ces modèles sont enregistrés dans des
fichiers textes stockés (voir `$GLOBALS['MAIL_TEMPLATES_DIRECTORIES']`). Pour chaque modèle, vous
devez fournir trois fichiers portant le même nom mais avec des extensions différentes :
- `template.subject` : le sujet du courriel. Note : seule la première ligne du fichier est utilisé
(et passée dans la fonction `trim()`)
- `template.html` : le contenu HTML du courriel
- `template.txt`: le contenu texte du courriel
Ces trois fichiers sont utilisés en tant que modèle [Smarty](http://www.smarty.net/) et seront
construit en utilisant les variables fournies dans le contexte d'envoi des courriels. À noter que le
moteur Smarty utilisé pour la génération du contenu de ces courriels n'est pas le même que celui
utilisé par LdapSaisie pour l'affichage des pages.
Par ailleurs, cet [LSaddon](index.md#configuration-des-lsaddons) fourni une vue de gestion des
modèles de courriels existants (voir `$GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS']` pour la
configuration des accès).
!!! warning
Cette vue n'est pas conçues pour être mise entre toutes les mains. La sécurisation de modèles de
courriels étant très complexe, il est fortement recommandé de n'ouvrir l'accès à cette vue
qu'aux utilisateurs avertis et de confiances.
Cet [LSaddon](index.md#configuration-des-lsaddons) doit être configuré en éditant son
fichier de configuration `config.LSaddons.mail.php`.
```php
@ -67,10 +94,43 @@ $GLOBALS['MAIL_HEARDERS = array();
// Catch all sent emails
$GLOBALS['MAIL_CATCH_ALL'] = array();
/**
* Email templates
*
* This addon offer ability to send email by using templates. Email templates are stored in
* full-text files in configured directories (see $GLOBALS['MAIL_TEMPLATES_DIRECTORIES']). For each
* template, you have to provide three files with the same name but with different extensions:
* - template.subject: the email subject. Note: only the first line is used (and stripped)
* - template.html: the HTML content of the email
* - template.txt: the text content of the email
* All these files will be used as Smarty templates and will be computed using variables provided
* in the sending context. Note that the Smarty object used to compute the template is not the same
* as the one used by LdapSaisie to display pages.
*
* Futhermore, this addon offer a view to list and edit existing template (see
* $GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] to configured access).
*/
// List of directory paths where as stored mail templates
// Notes:
// - provided path could be absolute or relative. Relative path are relative to the root base
// sources LdapSaisie directory (commonly /usr/share/ldapsaisie or the src directory if you
// installed it from sources). On Debian installation, you can specify 'local/email_templates' to
// refer to /etc/ldapsaisie/local/email_templates directory/
// - Multiple directories could be specified, sorted so that the first ones take priority over
// the last one.
// - To allow users to edit them using the editor view, these directories must be
// writable by PHP process (commonly runed as www-data).
$GLOBALS['MAIL_TEMPLATES_DIRECTORIES'] = array('local/email_templates');
// List of granted LSprofiles to access mail templates editor view
// WARNING: Sanitizing mail templates is hell... EXPOSE THIS VIEW ONLY TO TRUSTED USERS!
$GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] = array('admin');
```
Cet [LSaddon](index.md#configuration-des-lsaddons) offre la possibilité d'utilisé la fonction PHP
`sendMail()` :
Cet [LSaddon](index.md#configuration-des-lsaddons) offre avant tout la possibilité d'envoyer des
courriels en utilisant la fonction PHP `sendMail()` :
```
bool sendMail(
@ -84,3 +144,16 @@ bool sendMail(
<boolean> $html
);
```
Pour l'envoi de courriels en utilisant un modèle, il faut utiliser la fonction PHP
`sendMailFromTemplate()` :
```
bool sendMailFromTemplate(
<string> $tplname,
<string> $to,
<array> $variables,
<array(string)> $headers,
<array> $attachments
);
```

View file

@ -0,0 +1,4 @@
# LSattr_html_gpg_pub_key
Ce type est utilisé pour la gestion des attributs dont la valeur est une clef publique GPG. Il
permet dans l'interface, d'avoir un affichage adapté à ce type de donnée.

View file

@ -0,0 +1,4 @@
# gpg_pub_key
Cette règle vérifie que la valeur est une clé publique GPG. Pour cela, la clé est importée dans un
_keyring_ GnuPG.

View file

@ -33,6 +33,8 @@ tableau, les clé les noms des attributs et les valeurs liés sont la configurat
'generate_function' => 'fonction1',
'generate_value_format' => '[LSformat]',
'default_value' => 'valeur1',
'set_default_value_on_creation_if_empty' => [booleen],
'force_generation_if_empty' => [booleen],
'check_data' => array (
// Régle de vérification syntaxique des données saisies
),
@ -157,6 +159,23 @@ tableau, les clé les noms des attributs et les valeurs liés sont la configurat
l'attribut si aucune autre méthode n'est disponible (via une fonction ou un
[LSformat](../../global/LSformat.md#format-parametrable)).
- `set_default_value_on_creation_if_empty`
Booléen permettant de définir si la valeur de l'attribut doit être initialisée avec sa valeur par
défaut à la création de l'objet si aucune autre valeur n'as été fournie dans le contexte de
création (par défaut : *1*).
- `force_generation_if_empty`
Booléen permettant de définir si la valeur de l'attribut doit être générée si elle est vide, que
ce soit à la création ou la modification de l'objet (par défaut : *0*).
!!! warning
Si la génération échoue, cela bloquera l'action. Par ailleurs, cette génération est
prioritaire sur l'utilisation de la valeur par défaut de l'attribut induit par le paramètre
`set_default_value_on_creation_if_empty`.
- `check_data`
Tableau associatif contenant les règles de vérification syntaxique des données de l'attribut.

View file

@ -70,7 +70,8 @@ $GLOBALS['LSobjects']['[nom du type d'LSobject]']['LSsearch'] = array (
),
'customActions' => array (
// Configuration des customActions pour les recherches de ce type d'objet
)
),
'showSelectionBoxes' => [boolean],
);
```
@ -295,6 +296,14 @@ $GLOBALS['LSobjects']['[nom du type d'LSobject]']['LSsearch'] = array (
Tableau associatif contenant les paramètres de configuration des
[customActions](#customactions). [Voir la section concernée](#customactions).
- `showSelectionBoxes`
Booléen permettant de définir si les cases à cocher de sélections des objets doivent être
affichées. Lorsqu'elles sont affichées, l'utilisateur pourra sélectionner un ou plusieurs objets
dans la liste avant de déclencher une [customAction](#customsactions). Dans ce cas, les DNs de ces
objets seront passés à la page d'exécution de la [customAction](#customsactions) via le paramètre
`selected`.
## Les actions personnalisées (customActions)
Cette section décrit la manière de configurer les actions personnalisées exécutables sur les

View file

@ -17,6 +17,10 @@ $GLOBALS['LSobjects']['[nom du type d'LSobject]']['customActions'] = array (
'noConfirmation' => '[booléen]',
'redirectToObjectList' => '[booléen]',
'noRedirect' => '[booléen]',
'accessMethods' => array(
'web',
'api',
),
'rights' => array(
'LSprofile1',
'LSprofile2',
@ -87,6 +91,13 @@ $GLOBALS['LSobjects']['[nom du type d'LSobject]']['customActions'] = array (
[LSprofiles](../global/ldap/LSprofile.md#profils-dutilisateurs) ayant le droit d'exécuter cette
action.
- `accessMethods`
Tableau permetant de restreindre les moyens d'accès possibles à cette action. Par défaut, tous les
moyens d'accès possibles sont autorisés. Valeurs possibles : `web` pour les accès via l'interface
web et `api` pour les accès via l'API.
## Écriture d'une fonction implémentant une customAction
Une fonction implémentant une *customAction* se déclare de la manière suivante :
@ -101,6 +112,9 @@ Une fonction implémentant une *customAction* se déclare de la manière suivant
* Valeurs retournées :
* - True : Tout s'est bien passé
* - False : Une erreur est survenue
* - Cas particulier pour une exécution via l'API : un tableau des données
* à retourner. Exemple :
* ["success" => true, "extra_info1" => "...", "extra_info2" => "..."]
*/
function maFonction ($object) {
@ -114,6 +128,14 @@ Cette fonction doit prendre pour seul paramètre, le [LSobject](index.md#configu
lequel l'action personnalisée doit être exécutée et doit retourner soit `True` si tout s'est bien
passé, soit `False` en cas de problème.
Une *customAction* pourra également être appelé via l'API. Dans ce cas, il est possible de
retourner un tableau associatif et non un simple booléen. Le résultat retourné sera alors
fusionné avec les données retournées par la requête. Ce tableau devra contenir à minima la clé
`success` qui indiquera via un booléen si l'exécution est un succès ou non. Il est possible de
détecter si la méthode est appelée via l'API en appelant la méthode
`LSsession :: get('api_mode')`. Vous pouvez prendre exemple sur le code de la méthode
`showTechInfo()` fournie par le LSaddon [showTechInfo](../LSaddon/LSaddon_showTechInfo.md).
!!! note
Ces fonctions sont le plus couramment définies au sein d'

View file

@ -1,38 +0,0 @@
FROM debian:11
# Update/upgrade
RUN apt-get update
RUN apt-get upgrade -y
# Install LdapSaisie APT repository
RUN apt-get install -y --force-yes wget gnupg lsb-release
RUN wget -O - http://ldapsaisie.org/debian/ldapsaisie.gpg.key | apt-key add -
RUN echo "deb http://ldapsaisie.org/debian $( lsb_release -c -s ) main" > /etc/apt/sources.list.d/ldapsaisie.list
RUN apt-get update
# Install dependencies
RUN DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install -y git slapd apache2 php-ldap libapache2-mod-php php-cli smarty3 php-net-ldap2 php-net-ftp php-mail php-mail-mime php-console-table ldapvi locales sed bash-completion liquidprompt vim curl jq iproute2 net-tools composer php-cas php-zxcvbn php-phpseclib php-zip
# Add fr_FR* locales
RUN sed -i 's/^# fr_FR/fr_FR/' /etc/locale.gen
RUN locale-gen
# Clone sources
RUN git clone https://gitlab.easter-eggs.com/ee/ldapsaisie.git /var/www/ldapsaisie
# Fix www-data permission on temporary directory
RUN chown www-data: -R /var/www/ldapsaisie/src/tmp/
# Configure slapd and load lsexample directory
RUN killall slapd || echo slapd is not running
RUN /var/www/ldapsaisie/lsexample/restore_lsexample -v
# Configure and enable ldapsaisie VirtualHost (as default)
RUN a2dissite 000-default
COPY apache2.conf /etc/apache2/sites-available/ldapsaisie.conf
RUN a2ensite ldapsaisie
RUN a2enmod rewrite
RUN service apache2 restart
# Install ldapsaisie binary (with its bash-completion config)
RUN ln -s /var/www/ldapsaisie/src/bin/ldapsaisie.php /usr/local/sbin/ldapsaisie
RUN ln -s /var/www/ldapsaisie/debian/ldapsaisie.bash-completion /usr/share/bash-completion/completions/ldapsaisie
# Install vimrc.local file
COPY vimrc.local /etc/vim/vimrc.local
# Install entrypoint
COPY bashrc /root/.bashrc
COPY entrypoint.sh /usr/local/sbin/entrypoint.sh
RUN echo "ldapsaisie-dev" > /etc/hostname
ENTRYPOINT /usr/local/sbin/entrypoint.sh
EXPOSE 80 389

View file

@ -0,0 +1,5 @@
# syntax = edrevo/dockerfile-plus
FROM debian:bookworm-slim
INCLUDE+ Dockerfile.common

View file

@ -0,0 +1,5 @@
# syntax = edrevo/dockerfile-plus
FROM debian:bullseye-slim
INCLUDE+ Dockerfile.common

5
docker/Dockerfile.buster Normal file
View file

@ -0,0 +1,5 @@
# syntax = edrevo/dockerfile-plus
FROM debian:buster-slim
INCLUDE+ Dockerfile.common

55
docker/Dockerfile.common Normal file
View file

@ -0,0 +1,55 @@
ARG DEBIAN_FRONTEND=noninteractive
# Update/upgrade, configure LdapSaisie APT repo and install dependencies
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y --force-yes wget gnupg lsb-release && \
wget -O - http://ldapsaisie.org/debian/ldapsaisie.gpg.key | apt-key add - && \
echo "deb http://ldapsaisie.org/debian $( lsb_release -c -s ) main" > /etc/apt/sources.list.d/ldapsaisie.list && \
apt-get update && \
apt-get install -y \
git \
slapd \
apache2 \
php-ldap \
libapache2-mod-php \
php-cli \
smarty3 \
php-net-ldap2 \
php-net-ftp \
php-mail \
php-mail-mime \
php-html2text \
php-console-table \
ldapvi \
locales \
sed \
bash-completion \
liquidprompt \
vim \
curl \
jq \
iproute2 \
net-tools \
composer \
php-cas \
php-zxcvbn \
php-phpseclib \
php-zip && \
apt-get clean && \
rm -fr rm -rf /var/lib/apt/lists/*
COPY rootfs /
# Install LdapSaisie from sources, configure slapd and load lsexample directory
RUN git clone https://gitlab.easter-eggs.com/ee/ldapsaisie.git /var/www/ldapsaisie && \
ln -s /var/www/ldapsaisie/src/bin/ldapsaisie.php /usr/local/sbin/ldapsaisie && \
ln -s /var/www/ldapsaisie/debian/ldapsaisie.bash-completion /usr/share/bash-completion/completions/ldapsaisie && \
chown www-data: -R /var/www/ldapsaisie/src/tmp/ && \
sed -i 's/^# fr_FR/fr_FR/' /etc/locale.gen && \
locale-gen && \
a2dissite 000-default && \
a2ensite ldapsaisie && \
a2enmod rewrite && \
/var/www/ldapsaisie/lsexample/restore_lsexample -v
# Install entrypoint
ENTRYPOINT /entrypoint.sh
EXPOSE 80 389

22
docker/build-and-push-all.sh Executable file
View file

@ -0,0 +1,22 @@
#!/bin/bash
cd $( dirname $0 )
DIST="$1"
# Need to use Dockerfile+ (https://github.com/edrevo/dockerfile-plus)
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
DISTS=(bookworm bullseye buster)
LATEST_DIST=${DISTS[0]}
for dist in ${DISTS[@]}
do
[ -n "$DIST" -a "$DIST" != "$dist" ] && continue
docker build -t docker.io/brenard/ldapsaisie:$dist -f Dockerfile.$dist .
[ $? -eq 0 ] && docker push docker.io/brenard/ldapsaisie:$dist
done
[ -n "$DIST" -a "$DIST" != "latest" ] && exit
docker build -t docker.io/brenard/ldapsaisie:latest -f Dockerfile.$LATEST_DIST .
[ $? -eq 0 ] && docker push docker.io/brenard/ldapsaisie:latest

View file

@ -32,6 +32,9 @@ define('PEAR_MAIL','/usr/share/php/Mail.php');
// Pear :: Mail_mime
define('PEAR_MAIL_MIME','/usr/share/php/Mail/mime.php');
// Html2Text
define('HTML2TEXT','/usr/share/php/Html2Text.php');
/*
* Méthode d'envoie :
* - mail : envoie avec la méthode PHP mail()
@ -82,3 +85,36 @@ $GLOBALS['MAIL_HEARDERS'] = array();
// Catch all sent emails
$GLOBALS['MAIL_CATCH_ALL'] = array();
/**
* Email templates
*
* This addon offer ability to send email by using templates. Email templates are stored in
* full-text files in configured directories (see $GLOBALS['MAIL_TEMPLATES_DIRECTORIES']). For each
* template, you have to provide three files with the same name but with different extensions:
* - template.subject: the email subject. Note: only the first line is used (and stripped)
* - template.html: the HTML content of the email
* - template.txt: the text content of the email
* All these files will be used as Smarty templates and will be computed using variables provided
* in the sending context. Note that the Smarty object used to compute the template is not the same
* as the one used by LdapSaisie to display pages.
*
* Futhermore, this addon offer a view to list and edit existing template (see
* $GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] to configured access).
*/
// List of directory paths where as stored mail templates
// Notes:
// - provided path could be absolute or relative. Relative path are relative to the root base
// sources LdapSaisie directory (commonly /usr/share/ldapsaisie or the src directory if you
// installed it from sources). On Debian installation, you can specify 'local/email_templates' to
// refer to /etc/ldapsaisie/local/email_templates directory/
// - Multiple directories could be specified, sorted so that the first ones take priority over
// the last one.
// - To allow users to edit them using the editor view, these directories must be
// writable by PHP process (commonly runed as www-data).
$GLOBALS['MAIL_TEMPLATES_DIRECTORIES'] = array('local/email_templates');
// List of granted LSprofiles to access mail templates editor view
// WARNING: Sanitizing mail templates is hell... EXPOSE THIS VIEW ONLY TO TRUSTED USERS!
$GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] = array('admin');

View file

@ -21,4 +21,8 @@
******************************************************************************/
// PhpLdapAdmin View Object URL format
define('LS_PHPLDAPADMIN_VIEW_OBJECT_URL_FORMAT','//'.$_SERVER['SERVER_NAME'].'/phpldapadmin/cmd.php?cmd=template_engine&server_id=0&dn=%{dn}');
define(
'LS_PHPLDAPADMIN_VIEW_OBJECT_URL_FORMAT',
'//'.(isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:gethostname()).
'/phpldapadmin/cmd.php?cmd=template_engine&server_id=0&dn=%{dn}'
);

View file

@ -125,50 +125,6 @@ span.LSaccessRightsMatrixView_inherit {
opacity: 0.3;
}
/*
* Tabs
*/
ul.LSaccessRightsMatrixView_tabs {
list-style-type: none;
padding: 0;
margin: 1em;
margin-bottom: 0;
display: flex;
flex-wrap: nowrap;
overflow: auto;
}
ul.LSaccessRightsMatrixView_tabs li a {
text-decoration: none;
color: #fff;
}
ul.LSaccessRightsMatrixView_tabs li {
text-decoration: none;
color: #fff;
background-color: #52BCE5;
border-radius: 3px 3px 0px 0px;
padding: 4px;
display: inline;
margin-right: 0.3rem;
}
ul.LSaccessRightsMatrixView_tabs li.LSaccessRightsMatrixView_active_tab {
color: #fff;
background-color: #0072B8;
}
div.LSaccessRightsMatrixView_tab_content {
border: 1px solid #0072B8;
margin: 1em;
padding: 0.5em;
margin-top: 0;
}
div.LSaccessRightsMatrixView_tab_content h2 {
margin: 0;
}
/*
* Legend
*/
@ -198,15 +154,6 @@ div.LSaccessRightsMatrixView_tab_content h2 {
@media (max-width: 1024px) {
div.LSaccessRightsMatrixView_tab_content {
margin: 0;
}
ul.LSaccessRightsMatrixView_tabs {
margin: 0;
margin-top: 0.3rem;
}
#LSaccessRightsMatrixView table thead th:first-of-type, #LSaccessRightsMatrixView table tbody th {
max-width: 25vw;
}

View file

@ -0,0 +1,35 @@
/*
* LSformElement_gpg_pub_key
*/
.LSformElement_gpg_pub_key_value {
display: none;
width: 30em;
font-style: italic;
font-family: courier;
background-color: #b5e4f6;
border: 1px solid #ccc;
overflow: auto;
white-space: pre;
}
.LSformElement_gpg_pub_key_short_display {
font-style: italic;
font-family: courier;
cursor: pointer;
}
textarea.LSformElement_gpg_pub_key {
height: 10em;
}
@media (max-width: 1024px) {
.LSformElement_gpg_pub_key_value {
width: 40vw;
}
}
@media (max-width: 400px) {
.LSformElement_gpg_pub_key_value {
width: calc(100% - 1em);
}
}

View file

@ -242,6 +242,15 @@ td.LSobject-list-names {
cursor: pointer;
}
/* Selection */
td.LSobject-list-selection, th.LSobject-list-selection {
width: 1em;
}
.LSobject-list-selection input[type=checkbox] {
border: 1px solid #ccc;
}
/*
* Liste des pages
*/
@ -375,6 +384,55 @@ input[type='submit'].LSview_search, button {
margin-left: 1.2rem;
}
/*
* Tabs
*/
ul.LStabs {
list-style-type: none;
padding: 0;
margin: 1em;
margin-bottom: 0;
display: flex;
flex-wrap: nowrap;
overflow: auto;
}
ul.LStabs li a {
text-decoration: none;
color: #fff;
}
ul.LStabs li {
text-decoration: none;
color: #fff;
background-color: #52BCE5;
border-radius: 3px 3px 0px 0px;
padding: 4px;
display: inline;
margin-right: 0.3rem;
}
ul.LStabs li.LStabs_active {
color: #fff;
background-color: #0072B8;
}
div.LStabs_content {
border: 1px solid #0072B8;
margin: 1em;
padding: 0.5em;
margin-top: 0;
display: none;
}
div.LStabs_content_active {
display: block;
}
div.LStabs_content h2 {
margin: 0;
}
/*
*********************
* Error page
@ -474,6 +532,22 @@ input[type='submit'].LSview_search, button {
margin-right: 1vw;
}
table.LStable {
margin-left: 0;
}
/*
* LStabs
*/
div.LStabs_content {
margin: 0;
}
ul.LStabs {
margin: 0;
margin-top: 0.3rem;
}
/*
* Manage menu toggle
*/

View file

@ -0,0 +1,51 @@
div.LStabs_content form {
display: block;
width: calc(100% - 0.5em);
}
div.LStabs_content form input[type=text] {
width: 100%;
outline: none;
}
div.LStabs_content form textarea {
width: 100%;
min-height: 60vh;
}
div.LStabs_content form div.form-footer {
text-align: center;
width: 100%;
margin-top: 1em;
}
th.mail_subject, td.mail_subject {
width: 20vw;
white-space: nowrap;
overflow: hidden;
}
th.mail_content, td.mail_content {
width: 25vw;
white-space: nowrap;
overflow: hidden;
}
@media (max-width: 1024px) {
th.mail_subject, td.mail_subject {
width: auto;
max-width: 30vw;
}
th.mail_content, td.mail_content {
max-width: 12em;
}
}
@media (max-width: 500px) {
th.mail_subject, td.mail_subject {
max-width: 50vw;
}
th.mail_content, td.mail_content {
display: none;
}
}

View file

@ -253,7 +253,7 @@ td.LSobject-list, tr.LSobject-list, table.LStable tbody td, table.LStable th {
}
}
li.LSform_layout_active, ul.LSselect_selectable_object_types li, ul.LSaccessRightsMatrixView_tabs li {
li.LSform_layout_active, ul.LSselect_selectable_object_types li, ul.LStabs li {
border-radius: 0px;
border: none;
}
@ -287,11 +287,11 @@ ul.LSformElement_image_actions {
/* ---- Medium ---- */
li.LSform_layout_active, tr.LSobject-list:hover, table.LStable tr:hover, table.LStable tr.bis:hover, ul.LSaccessRightsMatrixView_tabs li, ul.LSselect_selectable_object_types li {
li.LSform_layout_active, tr.LSobject-list:hover, table.LStable tr:hover, table.LStable tr.bis:hover, ul.LStabs li, ul.LSselect_selectable_object_types li {
background-color: var(--medium-color);
}
div.loginform, div.recoverpasswordform, .loginform input[type='text'], .loginform input[type='password'], .recoverpasswordform input[type='text'], .recoverpasswordform input[type='password'], li.LSview-actions, div.LSform_layout, h1, form.LSglobal_search, div.LSformElement_image {
div.loginform, div.recoverpasswordform, .loginform input[type='text'], .loginform input[type='password'], .recoverpasswordform input[type='text'], .recoverpasswordform input[type='password'], li.LSview-actions, div.LSform_layout, h1, form.LSglobal_search, div.LSformElement_image, div.LStabs_content {
border-color: var(--medium-color);
}
@ -328,7 +328,7 @@ tr.LSobject-list-bis, table.LStable tr.bis, .LSform input[type=text], .LSform in
}
/* ---- Dark ---- */
li.LSform_layout_current, .LSform input[type=submit], input[type='submit'].LSview_search, button, ul.LSaccessRightsMatrixView_tabs li.LSaccessRightsMatrixView_active_tab, ul.LSselect_selectable_object_types li.current {
li.LSform_layout_current, .LSform input[type=submit], input[type='submit'].LSview_search, button, ul.LStabs li.LStabs_active, ul.LSselect_selectable_object_types li.current {
background-color: var(--dark-color);
}
@ -336,7 +336,7 @@ li.menu a, a.menu, h1 {
color: var(--dark-color);
}
input[type='submit'].LSview_search, input[type='text'].LSview_search, button, div.LSaccessRightsMatrixView_tab_content, ul.LSselect_selectable_object_types {
input[type='submit'].LSview_search, input[type='text'].LSview_search, button, ul.LSselect_selectable_object_types {
border-color: var(--dark-color);
}

View file

@ -111,8 +111,11 @@ function exportSearchResultAsCSV($LSsearch) {
return false;
}
$selected = isset($_REQUEST['selected'])?ensureIsArray($_REQUEST['selected']):[];
foreach ($LSsearch -> getSearchEntries() as $e) {
if ($selected && !in_array($e -> dn, $selected))
continue;
$row = array(
$e -> displayName,
$e -> dn

View file

@ -24,19 +24,33 @@
// Support
LSerror :: defineError('MAIL_SUPPORT_01',
___("MAIL Support : Pear::MAIL is missing.")
___("MAIL Support: Pear::MAIL is missing.")
);
LSerror :: defineError('MAIL_SUPPORT_02',
___("MAIL Support : Pear::MAIL_MIME is missing.")
___("MAIL Support: Pear::MAIL_MIME is missing.")
);
LSerror :: defineError('MAIL_SUPPORT_03',
___("MAIL Support: Html2Text\Html2Text is missing.")
);
// Other errors
LSerror :: defineError('MAIL_00',
___("MAIL Error : %{msg}")
___("MAIL Error: %{msg}")
);
LSerror :: defineError('MAIL_01',
___("MAIL : Error sending your email")
___("MAIL: Error sending your email")
);
LSerror :: defineError('MAIL_02',
___("MAIL: Unknown template %{name}.")
);
LSerror :: defineError('MAIL_03',
___("MAIL: Template %{name} is incomplete.")
);
LSerror :: defineError('MAIL_04',
___("MAIL: No writable place to save your changes on this template.")
);
LSerror :: defineError('MAIL_05',
___("MAIL: An error occured saving your changes on this template.")
);
/**
@ -64,20 +78,89 @@ function LSaddon_mail_support() {
}
}
if ($retval)
LScli :: add_command(
'test_send_mail',
'cli_test_send_mail',
'Send a test email',
"[-s subject] [-b body] [recipient]",
array (
" -s/--subject The test email subject (optional)",
" -b/--body The test email body (optional)",
" recipient The test email recipient (required)",
if (!$retval)
return false;
$GLOBALS['MAIL_LOGGER'] = LSlog :: get_logger('LSaddon_mail');
LScli :: add_command(
'test_send_mail',
'cli_test_send_mail',
'Send a test email',
"[-s subject] [-b body] [-H] [recipient1] [...]",
array (
" -s/--subject The test email subject (optional)",
" -b/--body The test email body (optional)",
" -H/--HTML Enable HTML email body mode (optional)",
" --header Email header using format:",
" header_name=header_value",
" Multiple headers could be specified by using this",
" optional argument multiple time.",
" -a|--attachment Email attachment using format:",
" /path/to/attachment.file[:filename]",
" The filename is optional (default: using source filename).",
" Multiple attachments could be specified by using this",
" optional argument multiple time.",
" --bcc Add Blind Carbon Copy (BCC) recipient(s)",
" --cc Add Carbon Copy (CC) recipient(s)",
" recipients The test email recipient(s) (required).",
),
false, // This command does not need LDAP connection
'cli_test_send_mail_autocompleter'
);
// Handle mail templates stuff
LScli :: add_command(
'test_send_mail_template',
'cli_test_send_mail_template',
'Test to send an email template',
'[template] [-V var1=value] [recipient1] [recipient2]',
array(
' - Positional arguments :',
' - email template name',
' - email recipient(s)',
'',
' - Optional arguments :',
' -V|--variable Template variable using format:',
' variable_name=variable_value',
' Multiple variables could be specified by using this',
' optional argument multiple time.',
' -H|--header Email header using format:',
' header_name=header_value',
' Multiple headers could be specified by using this',
' optional argument multiple time.',
' -a|--attachment Email attachment using format:',
' /path/to/attachment.file[:filename]',
' The filename is optional (default: using source filename).',
' Multiple attachments could be specified by using this',
' optional argument multiple time.',
' --bcc Add Blind Carbon Copy (BCC) recipient(s)',
' --cc Add Carbon Copy (CC) recipient(s)',
),
false, // This command does not need LDAP connection
'cli_test_send_mail_autocompleter'
);
'cli_test_send_mail_from_template_autocompleter'
);
if (
isset($GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'])
&& is_array($GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'])
&& $GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS']
&& list_mail_templates()
) {
if (!class_exists('\Html2Text\Html2Text')) {
if(!LSsession::includeFile(HTML2TEXT, true)) {
LSerror :: addErrorCode('MAIL_SUPPORT_03');
$retval = false;
}
}
if ($retval)
LSsession :: registerLSaddonView(
'mail', 'templates',
_('Email templates'),
'email_templates_view',
$GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS']
);
}
return $retval;
}
@ -142,12 +225,11 @@ function sendMail($to, $subject, $msg, $headers=null, $attachments=null,
$headers["To"] = $to;
$to = array (
'To' => $to
);
$to = ensureIsArray($to);
foreach(array_keys($headers) as $header) {
if(in_array(strtoupper($header), array('BCC', 'CC'))) {
$headers[$header] = ensureIsArray($headers[$header]);
if (isset($MAIL_CATCH_ALL) && $MAIL_CATCH_ALL) {
$logger -> debug("Mail catched: remove $header header");
$msg .= sprintf(
@ -157,11 +239,12 @@ function sendMail($to, $subject, $msg, $headers=null, $attachments=null,
_("\n%s: %s")
),
strtoupper($header),
(is_array($headers[$header])?implode(',', $headers[$header]):$headers[$header]));
implode(', ', $headers[$header])
);
unset($headers[$header]);
continue;
}
$to[strtoupper($header)] = $headers[$header];
$to = array_merge($to, $headers[$header]);
}
}
@ -205,6 +288,295 @@ function sendMail($to, $subject, $msg, $headers=null, $attachments=null,
return true;
}
/**
* List email templates directories
* @return array<SplFileInfo>
*/
function list_mail_templates_directories() {
if (isset($GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE']))
return $GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE'];
if (
!isset($GLOBALS['MAIL_TEMPLATES_DIRECTORIES'])
|| !is_array($GLOBALS['MAIL_TEMPLATES_DIRECTORIES'])
)
return [];
$GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE'] = [];
foreach($GLOBALS['MAIL_TEMPLATES_DIRECTORIES'] as $directory) {
$path = new SplFileInfo(
substr($directory, 0,1) == '/'?
$directory:
LS_ROOT_DIR."/".$directory
);
if ($path && $path->isDir())
$GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE'][] = $path;
else
$GLOBALS['MAIL_LOGGER'] -> warning(
"list_mail_templates_directories(): directory {$directory} does not exists."
);
}
$GLOBALS['MAIL_LOGGER'] -> debug(
"list_mail_templates_directories(): directories = ".
implode(', ', $GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE'])
);
return $GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE'];
}
/**
* List exiting email templates
* @return array<string,array<string,string|null>>
* [
* '[name]' => [
* 'subject' => '/path/to/name.subject' or null,
* 'html' => '/path/to/name.html' or null,
* 'txt' => '/path/to/name.txt' or null,
* ],
* [...]
* ]
*/
function list_mail_templates() {
if (isset($GLOBALS['MAIL_TEMPLATES']) && $GLOBALS['MAIL_TEMPLATES'])
return $GLOBALS['MAIL_TEMPLATES'];
$GLOBALS['MAIL_TEMPLATES'] = [];
$expected_extensions = ['subject', 'html', 'txt'];
foreach(list_mail_templates_directories() as $directory) {
foreach (new DirectoryIterator($directory) as $fileInfo) {
if(
$fileInfo->isDot()
|| !$fileInfo->isFile()
|| !$fileInfo->isReadable()
|| !in_array($fileInfo->getExtension(), $expected_extensions)
)
continue;
$name = $fileInfo->getBasename(".".$fileInfo->getExtension());
if (!array_key_exists($name, $GLOBALS['MAIL_TEMPLATES'])) {
$GLOBALS['MAIL_TEMPLATES'][$name] = [];
foreach($expected_extensions as $ext) $GLOBALS['MAIL_TEMPLATES'][$name][$ext] = null;
}
if (!$GLOBALS['MAIL_TEMPLATES'][$name][$fileInfo->getExtension()])
$GLOBALS['MAIL_TEMPLATES'][$name][$fileInfo->getExtension()] = $fileInfo->getRealPath();
}
}
return $GLOBALS['MAIL_TEMPLATES'];
}
/**
* Search for writable path to save change of a template file
* @param string $template The template name
* @param string $extension The template file extension (subject, html or txt)
* @return string|false The path of writable template file if found, false otherwise
*/
function get_mail_template_saved_path($template, $extension) {
$templates = list_mail_templates();
if (!array_key_exists($template, $templates))
return false;
$found = false;
$first_writable = false;
foreach(list_mail_templates_directories() as $directory) {
$file_path = new SplFileInfo("$directory/$template.$extension");
if ($file_path->isFile()) {
// File exist, check is writable
if ($file_path->isWritable())
return $file_path->getRealPath();
// If we don't find previously a writable file, trigger an error
if (!$first_writable) {
$GLOBALS['MAIL_LOGGER'] -> error(
"get_mail_template_saved_path($template, $extension): file '{$file_path->getRealPath()}' ".
"is not writable, can't saved this template file."
);
return false;
}
continue;
}
else if (!$first_writable && $directory->isWritable()) {
$first_writable = strval($file_path);
}
}
// No existing writable file found
if ($first_writable) return $first_writable;
$GLOBALS['MAIL_LOGGER'] -> error(
"get_mail_template_saved_path($template, $extension): ".
"no writable path found, can't saved this template file."
);
return false;
}
/**
* Send email from template
* @param string $tplname The email template name
* @param string $to The email recipient
* @param array<string,mixed> $variables Variables to use to compute the template
* @param array<string,string>|null $headers Email headers
* @param array<string,string>|null $attachments Email attachments as an array with
* filepath as key and filename as value
* @return boolean True if the email was sent, false otherwise
*/
function sendMailFromTemplate(
$tplname, $to, $variables=null, $headers=null, $attachments=null
) {
$templates = list_mail_templates();
if (!array_key_exists($tplname, $templates)) {
LSerror :: addErrorCode('MAIL_02', $tplname);
return False;
}
$tpl = $templates[$tplname];
if (!$tpl['subject'] || !($tpl['txt'] || $tpl['html'])) {
LSerror :: addErrorCode('MAIL_03', $tplname);
return False;
}
$smarty = new Smarty();
$smarty -> setCompileDir(LS_TMP_DIR_PATH);
if (is_array($variables))
array_map([$smarty, "assign"], array_keys($variables), array_values($variables));
try {
$subject = $smarty -> fetch("file:{$tpl['subject']}");
// Multiple line from subject cause problem, trim it and only the first line
$subject = explode("\n", trim($subject))[0];
$GLOBALS['MAIL_LOGGER'] -> debug(
"sendMailFromTemplate($tplname, ".implode("|", $to)."): ".
"subject compute from '{$tpl['subject']}'."
);
if ($tpl['html']) {
$message = $smarty -> fetch("file:{$tpl['html']}");
$html = true;
$GLOBALS['MAIL_LOGGER'] -> debug(
"sendMailFromTemplate($tplname, ".implode("|", $to)."): ".
"HTML content compute from '{$tpl['html']}'."
);
}
else {
$message = $smarty -> fetch("file:{$tpl['txt']}");
$html = false;
$GLOBALS['MAIL_LOGGER'] -> debug(
"sendMailFromTemplate($tplname, ".implode("|", $to)."): ".
"text content compute from '{$tpl['txt']}'."
);
}
}
catch (Exception $e) {
$GLOBALS['MAIL_LOGGER'] -> exception(
$e, getFData(
_("An exception occured forging message from email template '%{template}'"),
$tplname
),
false
);
return false;
}
return sendMail($to, $subject, $message, $headers, $attachments, "\n", "utf8", $html);
}
/**
* Email templates management view
* @return void
*/
function email_templates_view() {
$template = isset($_REQUEST['name'])?$_REQUEST['name']:null;
$templates = [];
foreach(list_mail_templates() as $name => $tpl) {
if ($template && $template != $name)
continue;
$templates[$name] = [
'name' => $name,
'subject' => $tpl['subject']?file_get_contents($tpl['subject']):null,
'html' => $tpl['html']?file_get_contents($tpl['html']):null,
'txt' => $tpl['txt']?file_get_contents($tpl['txt']):null,
];
if ($template) continue;
if ($templates[$name]['html']) {
$Html2Text = new \Html2Text\Html2Text($templates[$name]['html']);
$templates[$name]['html'] = substr($Html2Text->getText(), 0, 70)."...";
}
if ($templates[$name]['txt']) {
$templates[$name]['txt'] = substr($templates[$name]['txt'], 0, 70)."...";
}
}
if ($template) {
if (!array_key_exists($template, $templates)) {
LSurl::redirect("addon/mail/templates");
}
LStemplate :: assign('pagetitle', getFData(_('Email template: %{name}'), $template));
$tab = isset($_REQUEST['tab'])?$_REQUEST['tab']:'subject';
$path = false;
switch ($tab) {
case 'subject':
$path = get_mail_template_saved_path($template, $tab);
if (array_key_exists('subject', $_POST)) {
if (!$path)
LSerror :: addErrorCode('MAIL_04');
elseif (file_put_contents($path, $_POST['subject']) !== false) {
LSsession :: addInfo(_("Your changes have been saved."));
LSurl::redirect("addon/mail/templates?name=".urlencode($template)."&tab=$tab");
}
else {
LSerror :: addErrorCode('MAIL_05');
$tpl['subject'] = $_POST['subject'];
}
}
break;
case 'html':
$path = get_mail_template_saved_path($template, $tab);
if (array_key_exists('html', $_POST)) {
if (!$path)
LSerror :: addErrorCode('MAIL_04');
elseif (file_put_contents($path, $_POST['html']) !== false) {
LSsession :: addInfo(_("Your changes have been saved."));
LSurl::redirect("addon/mail/templates?name=".urlencode($template)."&tab=$tab");
}
else {
LSerror :: addErrorCode('MAIL_05');
$tpl['html'] = $_POST['html'];
}
}
LStemplate :: addLibJSscript('tinymce/js/tinymce/tinymce.min.js');
LStemplate :: addJSscript('email_templates.js');
break;
case 'txt':
$path = get_mail_template_saved_path($template, $tab);
if (array_key_exists('txt', $_POST)) {
if (!$path)
LSerror :: addErrorCode('MAIL_04');
elseif (file_put_contents($path, $_POST['txt']) !== false) {
LSsession :: addInfo(_("Your changes have been saved."));
LSurl::redirect("addon/mail/templates?name=".urlencode($template)."&tab=$tab");
}
else {
LSerror :: addErrorCode('MAIL_05');
$tpl['txt'] = $_POST['txt'];
}
}
break;
default:
LSurl::redirect("addon/mail/templates?name=".urlencode($template));
}
LStemplate :: assign('template', $templates[$template]);
LStemplate :: assign('tab', $tab);
LStemplate :: assign('writable', boolval($path));
$LSview_actions = array();
$LSview_actions['return'] = array (
'label' => _('Go back'),
'url' => 'addon/mail/templates',
'action' => 'view'
);
LStemplate :: assign('LSview_actions', $LSview_actions);
LSsession :: setTemplate('email_template.tpl');
}
else {
LStemplate :: assign('pagetitle', _('Email templates'));
LStemplate :: assign('templates', $templates);
LSsession :: setTemplate('email_templates.tpl');
}
LStemplate :: addCssFile('email_templates.css');
}
if (php_sapi_name() != 'cli')
return true;
@ -215,9 +587,12 @@ if (php_sapi_name() != 'cli')
* @return bool
*/
function cli_test_send_mail($command_args) {
$recipient = null;
$recipients = array();
$subject = "Test email";
$body = "This is a test message.";
$html = false;
$headers = array();
$attachments = array();
for ($i=0; $i < count($command_args); $i++) {
switch ($command_args[$i]) {
case '--subject':
@ -240,23 +615,68 @@ function cli_test_send_mail($command_args) {
LScli :: usage("Invalid body provided.");
break;
case '--html':
case '-H':
$html = true;
break;
case '--header':
$i++;
LScli :: unquote_word($command_args[$i]);
$parts = explode('=', $command_args[$i]);
if (count($parts) != 2)
LScli :: usage('Invalid header string ('.$command_args[$i].').');
if (array_key_exists($parts[0], $headers))
LScli :: usage('Header "'.$parts[0].'" already specified.');
$headers[$parts[0]] = $parts[1];
break;
case '-a':
case '--attachment':
$i++;
LScli :: unquote_word($command_args[$i]);
$parts = explode(':', $command_args[$i]);
$path = $parts[0];
if (!is_file($path))
LScli :: usage('Invalid attachment "'.$command_args[$i].'": file not found.');
$attachments[$path] = count($parts) >= 2?$parts[1]:basename($path);
break;
case '--bcc':
$i++;
LScli :: unquote_word($command_args[$i]);
if (!checkEmail($command_args[$i]))
LScli :: usage('Invalid BCC recipient "'.$command_args[$i].'".');
$headers['BCC'] = isset($headers['BCC'])?ensureIsArray($headers['BCC']):[];
$headers['BCC'][] = $command_args[$i];
break;
case '--cc':
$i++;
LScli :: unquote_word($command_args[$i]);
if (!checkEmail($command_args[$i]))
LScli :: usage('Invalid CC recipient "'.$command_args[$i].'".');
$headers['CC'] = isset($headers['CC'])?ensureIsArray($headers['CC']):[];
$headers['CC'][] = $command_args[$i];
break;
default:
if (!$recipient && checkEmail($command_args[$i]))
$recipient = $command_args[$i];
if (checkEmail($command_args[$i]))
$recipients[] = $command_args[$i];
else
LScli :: usage("Invalid parameter '".$command_args[$i]."'.");
}
}
if (!$recipient)
LScli :: usage("You must provide test email recipient as first positional parameter");
if (!$recipients)
LScli :: usage("You must provide as least one email recipient as positional parameter");
$logger = LSlog :: get_logger('LSaddon_mail');
if (!sendMail($recipient, $subject, $body)) {
$logger -> error("Fail to send test email sent to '$recipient'.");
if (!sendMail($recipients, $subject, $body, $headers, $attachments, null, null, $html)) {
$logger -> error("Fail to send test email sent to '".implode(', ', $recipients)."'.");
return false;
}
$logger -> info("Test email sent to '$recipient'.");
$logger -> info("Test email sent to '".implode(', ', $recipients)."'.");
return true;
}
@ -271,19 +691,158 @@ function cli_test_send_mail($command_args) {
* @return array<string> List of available options for the word to autocomplete
**/
function cli_test_send_mail_autocompleter($comp_words, $comp_word_num, $comp_word, $opts) {
switch ($comp_words[$comp_word_num-1]) {
case '-s':
case '--subject':
case '-b':
case '--body':
return array();
break;
}
if (isset($comp_words[$comp_word_num-1]))
switch ($comp_words[$comp_word_num-1]) {
case '-s':
case '--subject':
case '-b':
case '--body':
case '--header':
case '-a':
case '--attachment':
case '--bcc':
case '--cc':
return array();
break;
}
$opts = array_merge(
$opts,
array (
'-s', '--subject',
'-b', '--body',
'-H', '--html',
'--header',
'-a', '--attachment',
'--bcc', '--cc',
)
);
return LScli :: autocomplete_opts($opts, $comp_word);
}
/**
* CLI test_send_mail_template command
*
* @param array $command_args Command arguments :
* - Positional arguments :
* - template name
* - recipient
* - Optional arguments :
* - -V|--variable: template variable (format: variable=value)
* - -H|--header: header (format: header=value)
* - -a|--attachent: (format: /path/to/file.ext:filename or just /path/to/file.ext)
* - -bcc: BCC recipient(s)
* - -cc: CC recipient(s)
*
* @return boolean True on success, false otherwise
**/
function cli_test_send_mail_template($command_args) {
$template = null;
$recipients = array();
$variables = array();
$headers = array();
$attachments = array();
for ($i=0; $i < count($command_args); $i++) {
LScli :: unquote_word($command_args[$i]);
if (in_array($command_args[$i], array('-V', '--variable'))) {
$i++;
LScli :: unquote_word($command_args[$i]);
$parts = explode('=', $command_args[$i]);
if (count($parts) != 2)
LScli :: usage('Invalid variable string ('.$command_args[$i].').');
if (array_key_exists($parts[0], $variables))
LScli :: usage('Variable "'.$parts[0].'" already specified.');
$variables[$parts[0]] = $parts[1];
}
elseif (in_array($command_args[$i], array('-H', '--header'))) {
$i++;
LScli :: unquote_word($command_args[$i]);
$parts = explode('=', $command_args[$i]);
if (count($parts) != 2)
LScli :: usage('Invalid header string ('.$command_args[$i].').');
if (array_key_exists($parts[0], $headers))
LScli :: usage('Header "'.$parts[0].'" already specified.');
$headers[$parts[0]] = $parts[1];
}
elseif (in_array($command_args[$i], array('-a', '--attachent'))) {
$i++;
LScli :: unquote_word($command_args[$i]);
$parts = explode(':', $command_args[$i]);
$path = $parts[0];
if (!is_file($path))
LScli :: usage('Invalid attachment "'.$command_args[$i].'": file not found.');
$attachments[$path] = count($parts) >= 2?$parts[1]:basename($path);
}
elseif ($command_args[$i] == '--bcc') {
$i++;
LScli :: unquote_word($command_args[$i]);
if (!checkEmail($command_args[$i]))
LScli :: usage('Invalid BCC recipient "'.$command_args[$i].'".');
$headers['BCC'] = isset($headers['BCC'])?ensureIsArray($headers['BCC']):[];
$headers['BCC'][] = $command_args[$i];
}
elseif ($command_args[$i] == '--cc') {
$i++;
LScli :: unquote_word($command_args[$i]);
if (!checkEmail($command_args[$i]))
LScli :: usage('Invalid CC recipient "'.$command_args[$i].'".');
$headers['CC'] = isset($headers['CC'])?ensureIsArray($headers['CC']):[];
$headers['CC'][] = $command_args[$i];
}
else if (is_null($template)) {
$template = $command_args[$i];
}
else if (checkEmail($command_args[$i])) {
$recipients[] = $command_args[$i];
}
else
LScli :: usage('Invalid recipient "'.$command_args[$i].'".');
}
if (is_null($template) || empty($recipients))
LScli :: usage('You must provide email template name and at least one recipient.');
return sendMailFromTemplate(
$template,
$recipients,
$variables,
$headers,
$attachments
);
}
/**
* Args autocompleter for CLI test_send_mail_from_template command
*
* @param array<string> $comp_words List of already typed words of the command
* @param int $comp_word_num The command word number to autocomplete
* @param string $comp_word The command word to autocomplete
* @param array<string> $opts List of global available options
*
* @return array<string> List of available options for the word to autocomplete
**/
function cli_test_send_mail_from_template_autocompleter(
$comp_words, $comp_word_num, $comp_word, $opts
) {
if (isset($comp_words[$comp_word_num-1]))
switch ($comp_words[$comp_word_num-1]) {
case '-v':
case '--variable':
case '-H':
case '--header':
case '-a':
case '--attachment':
case '--bcc':
case '--cc':
return array();
break;
}
$opts = array_merge(
$opts,
array (
'-v', '--variable',
'-H', '--header',
'-a', '--attachment',
'--bcc', '--cc',
)
);
return LScli :: autocomplete_opts($opts, $comp_word);

View file

@ -86,7 +86,7 @@ function LSaddon_ppolicy_support() {
if ($retval) {
LSurl :: add_handler(
'#^api/1.0/exportPpolicyInfo/(?P<LSobject>[^/]+)/?$#',
'handle/export_api_LSobject_exportPpolicyInfo',
'handle_api_LSobject_exportPpolicyInfo',
true, false, true);
}
@ -137,20 +137,22 @@ function get_ppolicy_password_max_age($ppolicy_dn=null) {
* @param string $text The text of the badge
* @param string $bg_color The background color of the badge (optional, default: green)
* @param string $color The text color of the badge (optional, default: white)
* @param string $title The text title of the badge (optional, default: empty string)
*
* @return string The HTML code of the badge
*
* @author Benjamin Renard <brenard@easter-eggs.com>
*/
function _ppolicy_badge($text, $bg_color='green', $color='white') {
function _ppolicy_badge($text, $bg_color='green', $color='white', $title='') {
// Disable HTML formating on PHP cli
if (php_sapi_name() == 'cli') return $text;
return sprintf(
'<span style="
background-color: %s; color: %s;
padding: 0.2em; font-size: 0.8em; border-radius: 0.4em;"
class="LStips" title="%s"
>%s</span>',
$bg_color, $color, $text
$bg_color, $color, $title, $text
);
}
@ -170,7 +172,10 @@ function ppolicy_extraDisplayColumn_password_expiration($entry) {
$change_time = ldapDate2Timestamp($change_time);
$max_age = get_ppolicy_password_max_age($entry->pwdPolicySubentry);
if ($max_age === false)
return _ppolicy_badge(__('Unknown'), 'gray');
return _ppolicy_badge(
__('Unknown'), 'gray', 'white',
sprintf(_('Last password changed: %s, no duration of validity configured.'), date('Y-m-d H:i', $change_time))
);
if (!$max_age)
return _('Never');
$expiration_date = $change_time + $max_age;
@ -178,7 +183,9 @@ function ppolicy_extraDisplayColumn_password_expiration($entry) {
if ($expiration_date <= $now)
return _ppolicy_badge(
sprintf(_('Expired (since %s)'), date('Y-m-d H:i', $expiration_date)),
'black');
'black', 'white',
sprintf(_('Last password changed: %s, duration of validity: %s days'), date('Y-m-d H:i', $change_time), floor($max_age/86400))
);
$delta = $expiration_date - $now;
if ($delta <= LS_PPOLICY_CRITICAL_EXPIRATION_THRESHOLD)
$badge_color = 'red';
@ -188,7 +195,9 @@ function ppolicy_extraDisplayColumn_password_expiration($entry) {
$badge_color = 'green';
return _ppolicy_badge(
sprintf(_('Expire on %s'), date('Y-m-d H:i', $expiration_date)),
$badge_color);
$badge_color, 'white',
sprintf(_('Last password changed: %s, duration of validity: %s days'), date('Y-m-d H:i', $change_time), floor($max_age/86400))
);
}
/**

View file

@ -110,6 +110,17 @@ function showTechInfo($object) {
// Sort other internal attributes by name
ksort($internal_attrs);
// Handle API mode
if (LSsession :: get('api_mode')) {
return [
"object_classes" => $object_classes,
"structural_object_class" => $structural_object_class,
"special_internal_attributes" => $special_internal_attributes,
"other_internal_attrs" => $internal_attrs,
"success" => true,
];
}
LStemplate :: assign('pagetitle', getFData(_('%{name}: Technical information'), $object -> getDisplayName()));
$LSview_actions=array();

View file

@ -0,0 +1,32 @@
<?php
/*******************************************************************************
* Copyright (C) 2007 Easter-eggs
* https://ldapsaisie.org
*
* Author: See AUTHORS file in top-level directory.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/
/**
* HTML attribute type for GPG public key
*
* @author Benjamin Renard <brenard@easter-eggs.com>
*/
class LSattr_html_gpg_pub_key extends LSattr_html {
var $LSformElement_type = 'gpg_pub_key';
}

View file

@ -187,11 +187,11 @@ class LSattr_html_select_list extends LSattr_html{
* Function use with uasort to sort two values in ASC order
*
* @param string $va One value
* @param string $va One value
* @param string $vb Another value
*
* @return int Value for uasort
**/
protected static function _sortTwoValuesAsc(&$va,&$vb) {
protected static function _sortTwoValuesAsc($va,$vb) {
if (is_array($va)) {
// Force sub-options at the end
$nva='ZZZZ'.$va['label'];
@ -217,11 +217,11 @@ class LSattr_html_select_list extends LSattr_html{
* Function use with uasort to sort two values in DESC order
*
* @param string $va One value
* @param string $va One value
* @param string $vb Another value
*
* @return int Value for uasort
**/
protected static function _sortTwoValuesDesc(&$va,&$vb) {
protected static function _sortTwoValuesDesc($va,$vb) {
return (-1 * static :: _sortTwoValuesAsc($va,$vb));
}

View file

@ -543,22 +543,22 @@ class LSattribute extends LSlog_staticLoggerClass {
}
/**
* Vérifie si l'attribut est obligatoire
* Check if the attribute is required
*
* @author Benjamin Renard <brenard@easter-eggs.com>
*
* @return boolean true si l'attribut est obligatoire, false sinon
* @return boolean True if the attribute is required, false otherwise.
*/
public function isRequired() {
return $this -> getConfig('required', false, 'bool');
}
/**
* Vérifie si la valeur de l'attribut peut être générée
* Check if the attribute value could be generated
*
* @author Benjamin Renard <brenard@easter-eggs.com>
*
* @return boolean true si la valeur de l'attribut peut être générée, false sinon
* @return boolean True if the attribute value could be generated, false otherwise.
*/
public function canBeGenerated() {
$format = $this -> getConfig('generate_value_format', $this -> getConfig('default_value'));
@ -571,11 +571,12 @@ class LSattribute extends LSlog_staticLoggerClass {
}
/**
* Génere la valeur de l'attribut à partir de la fonction de génération
* Generate attribute value using its generation function, its generation format or its default
* value.
*
* @author Benjamin Renard <brenard@easter-eggs.com>
*
* @return boolean true si la valeur à put être générée, false sinon
* @return boolean True if value has been generated, false otherwise.
*/
public function generateValue() {
$value = $this -> getConfig('default_value', false);
@ -595,6 +596,23 @@ class LSattribute extends LSlog_staticLoggerClass {
return false;
}
/**
* Set attribute value as default
*
* @author Benjamin Renard <brenard@easter-eggs.com>
*
* @return boolean True if value has been set as default, false otherwise
*/
public function setValueAsDefault() {
$value = $this -> getConfig('default_value', false);
if ($value !== false) {
$this -> updateData = ensureIsArray($value);
self :: log_debug($this."setValueAsDefault(): default values = ".varDump($this -> updateData));
return true;
}
return false;
}
/**
* Retourne la valeur de l'attribut pour son enregistrement dans l'annuaire
* si l'attribut à été modifié.
@ -820,29 +838,35 @@ class LSattribute extends LSlog_staticLoggerClass {
* Error Codes
**/
LSerror :: defineError('LSattribute_01',
___("LSattribute : Attribute %{attr} : LDAP or HTML types unknow (LDAP = %{ldap} & HTML = %{html}).")
___("LSattribute: Attribute %{attr} : LDAP or HTML types unknow (LDAP = %{ldap} & HTML = %{html}).")
);
LSerror :: defineError('LSattribute_02',
___("LSattribute : The function %{func} to display the attribute %{attr} is unknow.")
___("LSattribute: The function %{func} to display the attribute %{attr} is unknow.")
);
LSerror :: defineError('LSattribute_03',
___("LSattribute : The rule %{rule} to validate the attribute %{attr} is unknow.")
___("LSattribute: The rule %{rule} to validate the attribute %{attr} is unknow.")
);
LSerror :: defineError('LSattribute_04',
___("LSattribute : Configuration data to verify the attribute %{attr} are incorrect.")
___("LSattribute: Configuration data to verify the attribute %{attr} are incorrect.")
);
LSerror :: defineError('LSattribute_05',
___("LSattribute : The function %{func} to save the attribute %{attr} is unknow.")
___("LSattribute: The function %{func} to save the attribute %{attr} is unknow.")
);
LSerror :: defineError('LSattribute_06',
___("LSattribute : The value of the attribute %{attr} can't be generated.")
___("LSattribute: The value of the attribute %{attr} can't be generated.")
);
LSerror :: defineError('LSattribute_07',
___("LSattribute : Generation of the attribute %{attr} failed.")
___("LSattribute: Generation of the attribute %{attr} failed.")
);
LSerror :: defineError('LSattribute_08',
___("LSattribute : Generation of the attribute %{attr} did not return a correct value.")
___("LSattribute: Generation of the attribute %{attr} did not return a correct value.")
);
LSerror :: defineError('LSattribute_09',
___("LSattribute : The attr_%{type} of the attribute %{name} is not yet defined.")
___("LSattribute: The attr_%{type} of the attribute %{name} is not yet defined.")
);
LSerror :: defineError('LSattribute_10',
___("LSattribute: The default value of the attribute %{attr} is invalid.")
);
LSerror :: defineError('LSattribute_11',
___("LSattribute: Fail to set attribute %{attr} value as default.")
);

View file

@ -819,7 +819,7 @@ class LScli extends LSlog_staticLoggerClass {
* @return string The quote character or an empty string if word if not quoted
*/
public static function unquote_word(&$word) {
if (in_array($word[0], array('"', "'"))) {
if ($word && in_array($word[0], array('"', "'"))) {
$quote_char = $word[0];
$word = substr($word, 1);
if ($word[strlen($word)-1] == $quote_char)

View file

@ -0,0 +1,114 @@
<?php
/*******************************************************************************
* Copyright (C) 2007 Easter-eggs
* https://ldapsaisie.org
*
* Author: See AUTHORS file in top-level directory.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/
/**
* Form element for GPG public key
*
* @author Benjamin Renard <brenard@easter-eggs.com>
*/
class LSformElement_gpg_pub_key extends LSformElement {
var $template = 'LSformElement_gpg_pub_key.tpl';
var $fieldTemplate = 'LSformElement_gpg_pub_key_field.tpl';
/**
* Parse one value
*
* @param string $value The value to parse
* @param boolean $details Enable/disable details return (optional, default: true)
*
* @return array|string Parsed value as array is $details is enabled, the raw value otherwise
*/
public function parseValue($value, $details=true) {
if (!$details)
return $value;
if (class_exists('gnupg')) {
// The home_dir parameter passed to gnupg_init() is not correctly handled in PHP 7.3, also set
// the GNUPGHOME environment variable.
putenv('GNUPGHOME='.LS_TMP_DIR_PATH);
$gpg = new gnupg(["home_dir" => LS_TMP_DIR_PATH]);
// Don't warn about (GNUPG_ERROR_SILENT is currently the default but ensure it)
$gpg -> seterrormode(GNUPG_ERROR_SILENT);
$info = $gpg -> import($value);
}
else {
LSerror :: addErrorCode('LSformElement_gpg_pub_key_01');
$info = false;
}
if (
is_array($info)
// @phpstan-ignore-next-line
&& ($info['imported'] + $info['unchanged']) != 1
)
$info = false;
return array(
'fingerprint' => is_array($info)?$info["fingerprint"]:null,
'value' => $value
);
}
/**
* Retourne les infos d'affichage de l'élément
*
* Cette méthode retourne les informations d'affichage de l'élement
*
* @return array
*/
public function getDisplay(){
LStemplate :: addCssFile('LSformElement_gpg_pub_key.css');
$return = $this -> getLabelInfos();
$params = array();
if (!$this -> isFreeze()) {
$params['values_txt'] = $this -> values;
}
else {
LStemplate :: addJSscript('LSformElement_gpg_pub_key.js');
LStemplate :: addHelpInfo(
'LSformElement_gpg_pub_key',
array(
'display' => _("Display the full key.")
)
);
$values_txt = array();
foreach ($this -> values as $value) {
$parsedValue = $this -> parseValue($value);
$values_txt[] = $parsedValue;
}
$params['values_txt'] = $values_txt;
$params['invalidValueTxt'] = _('Invalid value');
}
$return['html'] = $this -> fetchTemplate(NULL, $params);
return $return;
}
}
/*
* Error Codes
*/
LSerror :: defineError('LSformElement_gpg_pub_key_01',
___("LSformElement_gpg_pub_key: PHP GnuPG extension is missing, can't parse value.")
);

View file

@ -48,7 +48,7 @@ class LSformElement_postaladdress extends LSformElement_textarea {
$return = parent :: getDisplay();
if ($this -> isFreeze()) {
if (!empty($this->values)) {
$map_url_format = $this -> getParam('html_options.map_url_format', 'http://nominatim.openstreetmap.org/search.php?q=%{pattern}', 'string');
$map_url_format = $this -> getParam('html_options.map_url_format', 'https://nominatim.openstreetmap.org/ui/search.html?q=%{pattern}', 'string');
$map_url_pattern_generate_function = $this -> getParam('html_options.map_url_pattern_generate_function');
$map_url_pattern_format = $this -> getParam('html_options.map_url_pattern_format');
if ($map_url_pattern_generate_function) {

View file

@ -149,11 +149,11 @@ class LSformElement_select_object extends LSformElement {
* Function use with uasort to sort two values
*
* @param array $va One value
* @param array $va One value
* @param array $va Another value
*
* @return int Value for uasort
**/
private function _sortTwoValues(&$va,&$vb) {
private function _sortTwoValues($va, $vb) {
if ($this -> getParam('html_options.sortDirection') == 'DESC') {
$dir=-1;
}

View file

@ -0,0 +1,62 @@
<?php
/*******************************************************************************
* Copyright (C) 2007 Easter-eggs
* https://ldapsaisie.org
*
* Author: See AUTHORS file in top-level directory.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/
/**
* LSformRule to check GPG public key
*
* @author Benjamin Renard <brenard@easter-eggs.com>
*/
class LSformRule_gpg_pub_key extends LSformRule {
/**
* Validate SSH public key value
*
* @param string $value The value to validate
* @param array $options Validation options
* @param LSformElement &$formElement The related formElement object
*
* @return boolean true if the value is valide, false if not
*/
public static function validate($value, $options, &$formElement) {
if (!class_exists('gnupg')) {
LSerror :: addErrorCode('LSformRule_gpg_pub_key_01');
return false;
}
// The home_dir parameter passed to gnupg_init() is not correctly handled in PHP 7.3, also set
// the GNUPGHOME environment variable.
putenv('GNUPGHOME='.LS_TMP_DIR_PATH);
$gpg = new gnupg(["home_dir" => LS_TMP_DIR_PATH]);
// Don't warn about (GNUPG_ERROR_SILENT is currently the default but ensure it)
$gpg -> seterrormode(GNUPG_ERROR_SILENT);
$info = $gpg -> import($value);
// @phpstan-ignore-next-line
return is_array($info) && ($info['imported'] + $info['unchanged']) == 1 && $info['fingerprint'];
}
}
/*
* Error Codes
*/
LSerror :: defineError('LSformRule_gpg_pub_key_01',
___("LSformRule_gpg_pub_key: PHP GnuPG extension is missing, can't validate value.")
);

View file

@ -516,9 +516,10 @@ class LSldapObject extends LSlog_staticLoggerClass {
unset($forceGeneration[$key]);
}
}
if(
if (
in_array($attr_name, $forceGeneration)
|| (empty($attr_values) && $attr -> isRequired())
|| (empty($attr_values) && $attr -> getConfig('force_generation_if_empty', false, 'bool'))
) {
if ( $attr -> canBeGenerated()) {
if ($attr -> generateValue()) {
@ -541,6 +542,23 @@ class LSldapObject extends LSlog_staticLoggerClass {
$retval = false;
}
}
elseif (
empty($attr_values)
&& !is_empty($attr -> getConfig('default_value'))
&& $idForm == 'create'
&& $attr -> getConfig('set_default_value_on_creation_if_empty', true, 'bool')
) {
if ($attr -> setValueAsDefault()) {
if (!$this -> validateAttrData($LSform, $attr, $justCheck)) {
LSerror :: addErrorCode('LSattribute_10',$attr -> getLabel());
$retval = false;
}
}
else {
LSerror :: addErrorCode('LSattribute_11',$attr -> getLabel());
$retval = false;
}
}
}
}
return $retval;

View file

@ -20,7 +20,7 @@
******************************************************************************/
class LSmail {
class LSmail extends LSlog_staticLoggerClass {
/*
* Méthode chargeant les dépendances d'affichage

View file

@ -658,11 +658,13 @@ class LSsearch extends LSlog_staticLoggerClass {
}
/**
* Define search parameters by reading request data ($_REQUEST)
* Define search parameters by reading request data
*
* @param array<string,mixed>|null $request The request (optional, default: $_REQUEST)
*
* @return boolean True if all parameters found in request data are handled, False otherwise
*/
public function setParamsFromRequest() {
public function setParamsFromRequest($request=null) {
$allowedParams = array(
'pattern', 'approx', 'recursive', 'extraDisplayedColumns', 'nbObjectsByPage',
'attributes', 'sortBy', 'sortDirection', 'withoutCache', 'predefinedFilter',
@ -670,8 +672,9 @@ class LSsearch extends LSlog_staticLoggerClass {
'filter', 'basedn', 'subDn', 'scope', 'attributes', 'displayFormat',
);
$data = array();
$request = $request?$request:$_REQUEST;
foreach($_REQUEST as $key => $value) {
foreach($request as $key => $value) {
if (!in_array($key, $allowedParams))
continue;
switch($key) {
@ -1392,12 +1395,12 @@ class LSsearch extends LSlog_staticLoggerClass {
/**
* Function use with uasort to sort two entry
*
* @param LSsearchEntry &$a One entry of result
* @param LSsearchEntry &$b One entry of result
* @param LSsearchEntry $a One entry of result
* @param LSsearchEntry $b Another entry of result
*
* @return int Value for uasort
**/
private function _sortTwoEntry(&$a,&$b) {
private function _sortTwoEntry($a, $b) {
$sortBy = $this -> params['sortBy'];
$sortDirection = $this -> params['sortDirection'];
if ($sortDirection=='ASC') {

View file

@ -3210,6 +3210,9 @@ class LSsession {
LSerror :: defineError('LSsession_27',
___("LSsession : You always seem to use %{old} in your custom code: Please upgrade it and use %{new}.<pre>\nContext:\n%{context}</pre>")
);
LSerror :: defineError('LSsession_28',
___("LSsession : This custom action can not be executed by this way.")
);
}
/**

View file

@ -0,0 +1,23 @@
var LSformElement_gpg_pub_key = new Class({
initialize: function(){
$$('span.LSformElement_gpg_pub_key_short_display').each(function(span) {
span.addEvent('click',this.onShortDisplayClick.bind(this,span));
varLSdefault.addHelpInfo(span,'LSformElement_gpg_pub_key','display');
}, this);
},
onShortDisplayClick: function(span) {
var p = span.getParent().getFirst('p.LSformElement_gpg_pub_key_value');
if (typeof(p)) {
if (p.getStyle('display')=='none') {
p.setStyle('display','block');
}
else {
p.setStyle('display',' none');
}
}
}
});
window.addEvent(window.ie ? 'load' : 'domready', function() {
varLSformElement_gpg_pub_key = new LSformElement_gpg_pub_key();
});

View file

@ -50,6 +50,10 @@ var LSview = new Class({
}, this);
this.onWindowResized();
window.addEvent('resize', this.onWindowResized.bind(this));
this.listSelectAll = $('LSobject-list-select-all');
if (this.listSelectAll)
this.listSelectAll.addEvent('click', this.toogleListSelectAll.bind(this));
},
onWindowResized: function() {
@ -127,10 +131,10 @@ var LSview = new Class({
},
onCustomActionBtnClick: function(event,a) {
if (a.hasClass('LScustomActions_noConfirmation')) {
return true;
}
Event(event).stop();
if (a.hasClass('LScustomActions_noConfirmation')) {
return this.executeCustomActionFromA(a);
}
if (!this._confirmBoxOpen) {
this._confirmBoxOpen = 1;
var getName = new RegExp('customAction/([^/]*)');
@ -157,6 +161,11 @@ var LSview = new Class({
executeCustomActionFromA: function(a) {
var validatedURL = new URL(a.href);
validatedURL.searchParams.set('valid', '1');
$$('td.LSobject-list-selection input')
.filter(function(x) {return x.checked})
.each(function(el, idx) {
validatedURL.searchParams.set(`selected[${idx}]`, el.value);
})
document.location = validatedURL.href;
},
@ -164,6 +173,12 @@ var LSview = new Class({
if (ul.hasClass('LSview-actions-dropdown')) {
ul.toggleClass('LSview-actions-dropdown-opened');
}
},
toogleListSelectAll: function() {
$$('td.LSobject-list-selection input').each(function(el){
el.checked = !el.checked;
}, this);
}
});

View file

@ -0,0 +1,20 @@
window.addEvent(window.ie ? 'load' : 'domready', function() {
var textarea = document.getElement('textarea[name=html]');
if (!textarea) return;
var options = {};
if (textarea.disabled) {
options.readonly = 1;
options.setup = function(editor) {
editor.on('skinLoaded', function(e) {
editor.notificationManager.open({
text: textarea.get('title'),
type: 'warning'
});
});
};
}
options.target = textarea;
options.language = varLSdefault.getCurrentLang();
tinymce.init(options);
});

View file

@ -311,11 +311,12 @@ LSurl :: add_handler('#^favicon\.ico#', 'handle_favicon_ico_view', false, true,
function handle_libs_file($request) {
$path = LStemplate :: getLibFilePath($request -> file);
if ($path && is_file($path)) {
switch (strtolower(substr($path, -4))) {
case '.css':
$info = new SplFileInfo($path);
switch ($info -> getExtension()) {
case 'css':
$mime_type = 'text/css';
break;
case '.js':
case 'js':
$mime_type = 'text/javascript';
break;
default:
@ -529,7 +530,10 @@ function handle_LSobject_search($request) {
'hiddenFields' => $LSsearch -> getHiddenFieldForm(),
'predefinedFilter' => $LSsearch -> getParam('predefinedFilter')
));
LStemplate :: assign(
'showSelectionBoxes',
LSconfig :: get("LSobjects.$LSobject.LSsearch.showSelectionBoxes", false, "bool")
);
if (LSsession :: loadLSclass('LSform')) {
LSform :: loadDependenciesDisplayView($object, true);
@ -1075,7 +1079,13 @@ function handle_LSobject_show($request) {
$customActionsConfig = LSconfig :: get('LSobjects.'.$LSobject.'.customActions');
if (is_array($customActionsConfig)) {
foreach($customActionsConfig as $name => $config) {
if (LSsession :: canExecuteCustomAction($dn, $LSobject, $name)) {
if (
LSsession :: canExecuteCustomAction($dn, $LSobject, $name)
&& (
! LSconfig :: get('accessMethods', [], 'array', $config)
|| in_array('web', LSconfig :: get('accessMethods', [], 'array', $config))
)
) {
$LSview_actions[] = array (
'label' => ((isset($config['label']))?__($config['label']):__($name)),
'hideLabel' => ((isset($config['hideLabel']) && $config['hideLabel'])?$config['hideLabel']:False),
@ -1369,6 +1379,15 @@ function handle_LSobject_customAction($request) {
$config = LSconfig :: get("LSobjects.$LSobject.customActions.$customAction");
$title = isset($config['label'])?__($config['label']):$customAction;
if (
LSconfig :: get('accessMethods', [], 'array', $config)
&& !in_array('web', LSconfig :: get('accessMethods', [], 'array', $config))
) {
LSerror :: addErrorCode('LSsession_28');
LSsession :: displayTemplate();
return;
}
// Check customAction function
$function = LSconfig :: get('function', null, null, $config);
if (!is_callable($function)) {
@ -1556,6 +1575,235 @@ function get_LSobject_from_API_request($request, $instanciate=true, $check_acces
return get_LSobject_from_request($request, $instanciate, $check_access, true);
}
/**
* Handle API global search request
* @param LSurlRequest $request The request
* @return void
*/
function handle_api_global_search($request) {
// Check global search is enabled
if (!LSsession :: globalSearch()) {
LSurl :: error_404($request);
return;
}
if (!LSsession :: loadLSclass('LSsearch')) {
LSerror :: addErrorCode('LSsession_05','LSsearch');
LSsession :: displayAjaxReturn();
return;
}
if (!LSsession :: loadLSclass('LSform')) {
LSerror :: addErrorCode('LSsession_05','LSform');
LSsession :: displayAjaxReturn();
return;
}
$onlyLSobjects = (isset($_REQUEST['types'])?ensureIsArray($_REQUEST['types']):[]);
$keepParamsBetweenSearches = (
isset($_REQUEST['keepParamsBetweenSearches'])?
boolval($_REQUEST['keepParamsBetweenSearches']):
false
);
$all = isset($_REQUEST['all']);
$page_nb = (isset($_REQUEST['page'])?(int)$_REQUEST['page']:1);
$allowedParams = array(
'pattern', 'approx', 'recursive', 'extraDisplayedColumns', 'nbObjectsByPage',
'attributes', 'withoutCache', 'predefinedFilter', 'filter', 'basedn', 'subDn',
'scope', 'attributes', 'displayFormat',
);
// Handle JSON output
$data = array(
'success' => true,
'objects' => array(),
'total' => 0,
'params' => array(
'keepParamsBetweenSearches' => $keepParamsBetweenSearches,
),
);
if (!$all) {
$data['page'] = $page_nb;
$data['nbPages'] = 1;
}
foreach (LSsession :: getLSaccess() as $LSobject => $label) {
if ( $LSobject == "SELF" || !LSsession :: loadLSobject($LSobject) )
continue;
if (!LSconfig::get("LSobjects.$LSobject.globalSearch", true, 'bool'))
continue;
if ($onlyLSobjects && !in_array($LSobject, $onlyLSobjects))
continue;
$object = new $LSobject();
$search = new LSsearch(
$LSobject,
'api',
null,
!$keepParamsBetweenSearches
);
$search -> setParam(
'extraDisplayedColumns',
LSconfig::get("LSobjects.$LSobject.globalSearch_extraDisplayedColumns", true, 'bool')
);
$search -> setParam('onlyAccessible', True);
$params = [];
foreach($_REQUEST as $key => $value)
if (in_array($key, $allowedParams))
$params[$key] = $value;
if (isset($_REQUEST['type_params']) && isset($_REQUEST['type_params'][$LSobject]))
foreach(ensureIsArray($_REQUEST['type_params'][$LSobject]) as $key => $value)
if (in_array($key, $allowedParams))
$params[$key] = $value;
if (
(!isset($params['pattern']) || !$params['pattern'])
&& (!isset($params['filter']) || !$params['filter'])
) {
LSerror :: addErrorCode(
false,
_("No pattern or filter provided for $LSobject (required in global search).")
);
LSsession :: displayAjaxReturn();
return;
}
if (!$search -> setParamsFromRequest($params)) {
LSerror :: addErrorCode(false, "Invalid search parameters for $LSobject.");
LSsession :: displayAjaxReturn();
return;
}
// Run search
if (!$search -> run())
LSlog :: fatal("Fail to run search on $LSobject.");
if ($search -> total <= 0)
continue;
$data['total'] += $search -> total;
if ($all) {
$entries = $search -> listEntries();
if (!is_array($entries))
LSlog :: fatal("Fail to retrieve search result for $LSobject.");
}
else {
// Retrieve page
$page = $search -> getPage($page_nb);
/*
* $page = array(
* 'nb' => $page,
* 'nbPages' => 1,
* 'list' => array(),
* 'total' => $this -> total
* );
*/
// Check page
if (!is_array($page))
LSlog :: fatal("Fail to retrieve page #$page_nb for $LSobject.");
if ($page['nb'] >= $data['page'])
$data['page'] = $page['nb'];
if ($page['nbPages'] >= $data['nbPages'])
$data['nbPages'] = $page['nbPages'];
}
// Export search parameters
$exportedParams = array(
'filter', 'pattern', 'predefinedFilter', 'basedn', 'scope', 'sizelimit', 'attronly',
'approx', 'recursive', 'attributes', 'onlyAccessible', 'sortDirection', 'sortBy', 'sortlimit',
'displayFormat', 'nbObjectsByPage', 'withoutCache', 'extraDisplayedColumns'
);
if (LSsession :: subDnIsEnabled())
$exportedParams = array_merge($exportedParams, array('displaySubDn', 'subDn'));
$data['params'][$LSobject] = [];
foreach ($exportedParams as $param) {
$data['params'][$LSobject][$param] = $search->getParam($param);
if ($param == 'filter' && $data['params'][$LSobject][$param])
$data['params'][$LSobject][$param] = $data['params'][$LSobject][$param] -> as_string();
}
// Instanciate LSform export to handle custom requested attributes
$object = new $LSobject();
$export = new LSform($object, 'export');
foreach ($search -> attributes as $attr) {
if (array_key_exists($attr, $object -> attrs))
$object -> attrs[$attr] -> addToExport($export);
}
// Reset & increase time limit: allow one seconds by object to handle,
// with a minimum of 30 seconds
$timeout = count($all?$entries:$page['list']); // @phpstan-ignore-line
set_time_limit($timeout>30?$timeout:30);
// Handle objects
$data['objects'][$LSobject] = [];
foreach(($all?$entries:$page['list']) as $obj) { // @phpstan-ignore-line
$data['objects'][$LSobject][$obj -> dn] = array(
'name' => $obj -> displayName,
);
// When as_list enabled, put object DN in object details (otherwise, it's the key)
if (isset($_REQUEST['as_list']))
$data['objects'][$LSobject][$obj -> dn]['dn'] = $obj -> dn;
// When splited_result is disabled, put object type in object details (otherwise, present as key)
if (!isset($_REQUEST['splited_result']))
$data['objects'][$LSobject][$obj -> dn]['type'] = $LSobject;
if ($search -> displaySubDn)
$data['objects'][$LSobject][$obj -> dn][$search -> label_level] = $obj -> subDn;
if ($search -> extraDisplayedColumns) {
foreach ($search -> visibleExtraDisplayedColumns as $cid => $conf) {
$data['objects'][$LSobject][$obj -> dn][$conf['label']] = $obj -> $cid;
}
}
foreach ($search -> attributes as $attr) {
if (!LSsession :: canAccess($LSobject, $obj -> dn, 'r', $attr))
continue;
$export -> elements[$attr] -> setValue(
$object -> attrs[$attr] -> html -> refreshForm(
$object -> attrs[$attr] -> getFormVal($obj -> $attr)
)
);
$data['objects'][$LSobject][$obj -> dn][$attr] = $export -> elements[$attr] -> getApiValue(
isset($_REQUEST['attributesDetails'])
);
}
}
$search -> afterUsingResult();
}
if (!$all && $data['page'] > $data['nbPages']) {
LSerror :: addErrorCode(
false,
"Requested page too hight ({$data['page']} > {$data['nbPages']})."
);
LSsession :: displayAjaxReturn();
return;
}
// Handle as_list parameter
if (isset($_REQUEST['as_list']))
foreach(array_keys($data['objects']) as $LSobject)
$data['objects'][$LSobject] = array_values($data['objects'][$LSobject]);
// Handle splited_result parameter
if (!isset($_REQUEST['splited_result'])) {
$objects = [];
foreach(array_keys($data['objects']) as $LSobject) {
$objects = array_merge($objects, $data['objects'][$LSobject]);
unset($data['objects'][$LSobject]);
}
$data['objects'] = $objects;
}
LSsession :: displayAjaxReturn($data);
}
LSurl :: add_handler('#^api/1.0/search/?$#', 'handle_api_global_search', true, false, true);
/*
* Handle API LSobject search
*
@ -1958,6 +2206,94 @@ function handle_api_LSobject_remove($request) {
}
LSurl :: add_handler('#^api/1.0/object/(?P<LSobject>[^/]+)/(?P<dn>[^/]+)/remove/?$#', 'handle_api_LSobject_remove', true, false, true);
/*
* Handle API LSobject custom action request
*
* @param LSurlRequest $request The request
*
* @return void
**/
function handle_api_LSobject_customAction($request) {
$object = get_LSobject_from_API_request($request);
if (!$object)
return;
$data = array(
'dn' => $object -> getDn(),
'type' => $object -> getType(),
'name' => $object -> getDisplayName(),
'success' => false,
);
$customAction = $request -> customAction;
if ( !LSsession :: canExecuteCustomAction($data["dn"], $data["type"], $customAction) ) {
LSerror :: addErrorCode('LSsession_11');
LSsession :: displayAjaxReturn($data);
return;
}
$config = LSconfig :: get("LSobjects.{$data["type"]}.customActions.$customAction");
$title = isset($config['label'])?__($config['label']):$customAction;
$objectname = $object -> getDisplayName();
if (
LSconfig :: get('accessMethods', [], 'array', $config)
&& !in_array('api', LSconfig :: get('accessMethods', [], 'array', $config))
) {
LSerror :: addErrorCode('LSsession_28');
LSsession :: displayAjaxReturn($data);
return;
}
// Check customAction function
$function = LSconfig :: get('function', null, null, $config);
if (!is_callable($function)) {
LSerror :: addErrorCode(
'LSsession_13',
array(
'customAction' => $title,
'function' => ($function?format_callable($function):_('undefined'))
)
);
LSsession :: displayAjaxReturn($data);
return;
}
// Run customAction
$result = call_user_func_array($function, array(&$object));
// Handle result: if its an array, merge it with returned data
if (is_array($result))
$data = array_merge($data, $result);
if ($result && (!is_array($result) || $data["success"])) {
$data["success"] = true;
if (!LSconfig :: get('disableOnSuccessMsg', false, 'bool', $config)) {
$msg_format = LSconfig :: get('onSuccessMsgFormat', null, 'string', $config);
if ($msg_format) {
$msg = getFData(__($msg_format), $objectname);
} else {
$msg = getFData(
_('The custom action %{customAction} have been successfully execute on %{objectname}.'),
array('objectname' => $objectname, 'customAction' => $customAction)
);
}
LSsession :: addInfo($msg);
}
}
else {
LSerror :: addErrorCode(
'LSldapObject_31',
array('objectname' => $objectname, 'customAction' => $customAction)
);
}
LSsession :: displayAjaxReturn($data);
}
LSurl :: add_handler(
'#^api/1.0/object/(?P<LSobject>[^/]+)/(?P<dn>[^/]+)/customAction/(?P<customAction>[^/]+)/?$#',
'handle_api_LSobject_customAction', true, false, true);
/*
* Handle API LSobject relation request
*

File diff suppressed because it is too large Load diff

View file

@ -22,26 +22,37 @@ msgstr ""
msgid "Password policy: An error occured writing a CSV row."
msgstr ""
#: includes/addons/LSaddons.ppolicy.php:169
#: includes/addons/LSaddons.ppolicy.php:175
#: includes/addons/LSaddons.ppolicy.php:171
#: includes/addons/LSaddons.ppolicy.php:180
msgid "Never"
msgstr ""
#: includes/addons/LSaddons.ppolicy.php:173
#: includes/addons/LSaddons.ppolicy.php:176
msgid "Unknown"
msgstr ""
#: includes/addons/LSaddons.ppolicy.php:180
#: includes/addons/LSaddons.ppolicy.php:177
#, php-format
msgid "Last password changed: %s, no duration of validity configured."
msgstr ""
#: includes/addons/LSaddons.ppolicy.php:185
#, php-format
msgid "Expired (since %s)"
msgstr ""
#: includes/addons/LSaddons.ppolicy.php:190
#: includes/addons/LSaddons.ppolicy.php:187
#: includes/addons/LSaddons.ppolicy.php:199
#, php-format
msgid "Last password changed: %s, duration of validity: %s days"
msgstr ""
#: includes/addons/LSaddons.ppolicy.php:197
#, php-format
msgid "Expire on %s"
msgstr ""
#: includes/addons/LSaddons.ppolicy.php:260
#: includes/addons/LSaddons.ppolicy.php:269
msgid "Not set"
msgstr ""
@ -159,27 +170,51 @@ msgid "ExportSearchResultAsCSV Error : An error occured writing a CSV row."
msgstr ""
#: includes/addons/LSaddons.mail.php:27
msgid "MAIL Support : Pear::MAIL is missing."
msgid "MAIL Support: Pear::MAIL is missing."
msgstr ""
#: includes/addons/LSaddons.mail.php:30
msgid "MAIL Support : Pear::MAIL_MIME is missing."
msgid "MAIL Support: Pear::MAIL_MIME is missing."
msgstr ""
#: includes/addons/LSaddons.mail.php:35
msgid "MAIL Error : %{msg}"
#: includes/addons/LSaddons.mail.php:33
msgid "MAIL Support: Html2Text\\Html2Text is missing."
msgstr ""
#: includes/addons/LSaddons.mail.php:39
msgid "MAIL : Error sending your email"
#: includes/addons/LSaddons.mail.php:38
msgid "MAIL Error: %{msg}"
msgstr ""
#: includes/addons/LSaddons.mail.php:125
#: includes/addons/LSaddons.mail.php:41
msgid "MAIL: Error sending your email"
msgstr ""
#: includes/addons/LSaddons.mail.php:44
msgid "MAIL: Unknown template %{name}."
msgstr ""
#: includes/addons/LSaddons.mail.php:47
msgid "MAIL: Template %{name} is incomplete."
msgstr ""
#: includes/addons/LSaddons.mail.php:50
msgid "MAIL: No writable place to save your changes on this template."
msgstr ""
#: includes/addons/LSaddons.mail.php:53
msgid "MAIL: An error occured saving your changes on this template."
msgstr ""
#: includes/addons/LSaddons.mail.php:158 includes/addons/LSaddons.mail.php:573
msgid "Email templates"
msgstr ""
#: includes/addons/LSaddons.mail.php:208
#, php-format
msgid "</hr><p><small>Mail initialy intended for %s.</small></p>"
msgstr ""
#: includes/addons/LSaddons.mail.php:126
#: includes/addons/LSaddons.mail.php:209
#, php-format
msgid ""
"\n"
@ -188,18 +223,38 @@ msgid ""
"Mail initialy intended for %s."
msgstr ""
#: includes/addons/LSaddons.mail.php:156
#: includes/addons/LSaddons.mail.php:238
#, php-format
msgid "<p><small>%s: %s</small></p>"
msgstr ""
#: includes/addons/LSaddons.mail.php:157
#: includes/addons/LSaddons.mail.php:239
#, php-format
msgid ""
"\n"
"%s: %s"
msgstr ""
#: includes/addons/LSaddons.mail.php:463
msgid "An exception occured forging message from email template '%{template}'"
msgstr ""
#: includes/addons/LSaddons.mail.php:503
msgid "Email template: %{name}"
msgstr ""
#: includes/addons/LSaddons.mail.php:513 includes/addons/LSaddons.mail.php:529
#: includes/addons/LSaddons.mail.php:547
msgid "Your changes have been saved."
msgstr ""
#: includes/addons/LSaddons.mail.php:565
#: includes/addons/LSaddons.showSupportInfo.php:78
#: includes/addons/LSaddons.accesslog.php:248
#: includes/addons/LSaddons.showTechInfo.php:128
msgid "Go back"
msgstr ""
#: includes/addons/LSaddons.phpldapadmin.php:27
msgid "PhpLdapAdmin Support : The constant %{const} is not defined."
msgstr ""
@ -216,12 +271,6 @@ msgstr ""
msgid "Download"
msgstr ""
#: includes/addons/LSaddons.showSupportInfo.php:78
#: includes/addons/LSaddons.accesslog.php:248
#: includes/addons/LSaddons.showTechInfo.php:117
msgid "Go back"
msgstr ""
#: includes/addons/LSaddons.dyngroup.php:27
msgid "Dynamic groups support: The constant %{const} is not defined."
msgstr ""
@ -317,8 +366,8 @@ msgstr ""
#: includes/class/class.LSrelation.php:679 includes/class/class.LSform.php:346
#: includes/class/class.LSformElement_select_object.php:75
#: includes/class/class.LSformElement_select_object.php:91
#: includes/class/class.LSsearchEntry.php:237 includes/routes.php:1068
#: includes/routes.php:1212
#: includes/class/class.LSsearchEntry.php:237 includes/routes.php:1072
#: includes/routes.php:1222
msgid "Delete"
msgstr ""
@ -331,8 +380,8 @@ msgstr ""
#: includes/class/class.LSrelation.php:736
#: includes/class/class.LSformElement_select_object.php:74
#: includes/class/class.LSformElement_supannLabeledValue.php:90
#: includes/class/class.LSsearchEntry.php:221 includes/routes.php:1052
#: includes/routes.php:1220 includes/routes.php:1305 includes/routes.php:1451
#: includes/class/class.LSsearchEntry.php:221 includes/routes.php:1056
#: includes/routes.php:1230 includes/routes.php:1315 includes/routes.php:1470
msgid "Modify"
msgstr ""
@ -340,7 +389,7 @@ msgstr ""
msgid "Modify RDN"
msgstr ""
#: includes/addons/LSaddons.accesslog.php:35 includes/routes.php:517
#: includes/addons/LSaddons.accesslog.php:35 includes/routes.php:518
#: templates/default/select.tpl:28 templates/default/global_search.tpl:6
msgid "Search"
msgstr ""
@ -368,7 +417,7 @@ msgstr ""
#: includes/addons/LSaddons.accesslog.php:243
#: includes/class/class.LSsession.php:1875 includes/routes.php:157
#: includes/routes.php:474 templates/default/select.tpl:29
#: includes/routes.php:475 templates/default/select.tpl:29
msgid "Refresh"
msgstr ""
@ -584,7 +633,7 @@ msgstr ""
msgid "No"
msgstr ""
#: includes/addons/LSaddons.showTechInfo.php:113
#: includes/addons/LSaddons.showTechInfo.php:124
msgid "%{name}: Technical information"
msgstr ""
@ -639,8 +688,10 @@ msgstr ""
#: includes/class/class.LSformRule.php:89
#: includes/class/class.LSformRule.php:292
#: includes/class/class.LSformElement_gpg_pub_key.php:101
#: includes/class/class.LSattr_html_date.php:47
#: includes/class/class.LSattr_html_select_list.php:63
#: templates/default/LSformElement_gpg_pub_key_field.tpl:9
msgid "Invalid value"
msgstr ""
@ -768,7 +819,7 @@ msgstr ""
#: includes/class/class.LSconfirmBox.php:37
#: includes/class/class.LSsmoothbox.php:39 includes/class/class.LSform.php:175
#: includes/routes.php:638 includes/routes.php:1292 includes/routes.php:1438
#: includes/routes.php:642 includes/routes.php:1302 includes/routes.php:1457
#: templates/default/recoverpassword.tpl:21
msgid "Validate"
msgstr ""
@ -870,6 +921,7 @@ msgid ""
msgstr ""
#: includes/class/class.LSformElement_ssh_key.php:83
#: includes/class/class.LSformElement_gpg_pub_key.php:91
msgid "Display the full key."
msgstr ""
@ -1258,51 +1310,58 @@ msgid ""
"LSattr_ldap :: password. It's not the case of the attribure %{attr}."
msgstr ""
#: includes/class/class.LSattribute.php:823
msgid ""
"LSattribute : Attribute %{attr} : LDAP or HTML types unknow (LDAP = %{ldap} "
"& HTML = %{html})."
msgstr ""
#: includes/class/class.LSattribute.php:826
msgid ""
"LSattribute : The function %{func} to display the attribute %{attr} is "
"unknow."
msgstr ""
#: includes/class/class.LSattribute.php:829
msgid ""
"LSattribute : The rule %{rule} to validate the attribute %{attr} is unknow."
msgstr ""
#: includes/class/class.LSattribute.php:832
msgid ""
"LSattribute : Configuration data to verify the attribute %{attr} are "
"incorrect."
msgstr ""
#: includes/class/class.LSattribute.php:835
msgid ""
"LSattribute : The function %{func} to save the attribute %{attr} is unknow."
msgstr ""
#: includes/class/class.LSattribute.php:838
msgid "LSattribute : The value of the attribute %{attr} can't be generated."
msgstr ""
#: includes/class/class.LSattribute.php:841
msgid "LSattribute : Generation of the attribute %{attr} failed."
msgid ""
"LSattribute: Attribute %{attr} : LDAP or HTML types unknow (LDAP = %{ldap} & "
"HTML = %{html})."
msgstr ""
#: includes/class/class.LSattribute.php:844
msgid ""
"LSattribute : Generation of the attribute %{attr} did not return a correct "
"value."
"LSattribute: The function %{func} to display the attribute %{attr} is unknow."
msgstr ""
#: includes/class/class.LSattribute.php:847
msgid ""
"LSattribute : The attr_%{type} of the attribute %{name} is not yet defined."
"LSattribute: The rule %{rule} to validate the attribute %{attr} is unknow."
msgstr ""
#: includes/class/class.LSattribute.php:850
msgid ""
"LSattribute: Configuration data to verify the attribute %{attr} are "
"incorrect."
msgstr ""
#: includes/class/class.LSattribute.php:853
msgid ""
"LSattribute: The function %{func} to save the attribute %{attr} is unknow."
msgstr ""
#: includes/class/class.LSattribute.php:856
msgid "LSattribute: The value of the attribute %{attr} can't be generated."
msgstr ""
#: includes/class/class.LSattribute.php:859
msgid "LSattribute: Generation of the attribute %{attr} failed."
msgstr ""
#: includes/class/class.LSattribute.php:862
msgid ""
"LSattribute: Generation of the attribute %{attr} did not return a correct "
"value."
msgstr ""
#: includes/class/class.LSattribute.php:865
msgid ""
"LSattribute: The attr_%{type} of the attribute %{name} is not yet defined."
msgstr ""
#: includes/class/class.LSattribute.php:868
msgid "LSattribute: The default value of the attribute %{attr} is invalid."
msgstr ""
#: includes/class/class.LSattribute.php:871
msgid "LSattribute: Fail to set attribute %{attr} value as default."
msgstr ""
#: includes/class/class.LSformRule_callable.php:71
@ -1493,209 +1552,218 @@ msgid ""
"%{context}</pre>"
msgstr ""
#: includes/class/class.LSsession.php:3214
msgid "LSsession : This custom action can not be executed by this way."
msgstr ""
#: includes/class/class.LSformRule_mimetype.php:53
#: includes/class/class.LSformRule_mimetype.php:57
msgid "Invalid file type (%{type})."
msgstr ""
#: includes/class/class.LSldapObject.php:585
#: includes/class/class.LSldapObject.php:603
msgid "The attribute %{attr} is not valid."
msgstr ""
#: includes/class/class.LSldapObject.php:3165
#: includes/class/class.LSldapObject.php:3183
msgid "LSldapObject : Object type unknown."
msgstr ""
#: includes/class/class.LSldapObject.php:3168
#: includes/class/class.LSldapObject.php:3186
msgid "LSldapObject : Update form is not defined for the object %{obj}."
msgstr ""
#: includes/class/class.LSldapObject.php:3171
#: includes/class/class.LSldapObject.php:3189
msgid "LSldapObject : No form exists for the object %{obj}."
msgstr ""
#: includes/class/class.LSldapObject.php:3174
#: includes/class/class.LSldapObject.php:3192
msgid ""
"LSldapObject : The function %{func} to validate the attribute %{attr} the "
"object %{obj} is unknow."
msgstr ""
#: includes/class/class.LSldapObject.php:3177
#: includes/class/class.LSldapObject.php:3195
msgid ""
"LSldapObject : Configuration data are missing to validate the attribute "
"%{attr} of the object %{obj}."
msgstr ""
#: includes/class/class.LSldapObject.php:3181
#: includes/class/class.LSldapObject.php:3199
msgid ""
"LSldapObject : The function %{func} to be executed on the object event "
"%{event} doesn't exist."
msgstr ""
#: includes/class/class.LSldapObject.php:3184
#: includes/class/class.LSldapObject.php:3202
msgid ""
"LSldapObject : The %{func} execution on the object event %{event} failed."
msgstr ""
#: includes/class/class.LSldapObject.php:3188
#: includes/class/class.LSldapObject.php:3206
msgid ""
"LSldapObject : Class %{class}, which method %{meth} to be executed on the "
"object event %{event}, doesn't exist."
msgstr ""
#: includes/class/class.LSldapObject.php:3191
#: includes/class/class.LSldapObject.php:3209
msgid ""
"LSldapObject : Method %{meth} within %{class} class to be executed on object "
"event %{event}, doesn't exist."
msgstr ""
#: includes/class/class.LSldapObject.php:3194
#: includes/class/class.LSldapObject.php:3212
msgid ""
"LSldapObject : Error during execute %{meth} method within %{class} class, to "
"be executed on object event %{event}."
msgstr ""
#: includes/class/class.LSldapObject.php:3198
#: includes/class/class.LSldapObject.php:3216
msgid ""
"LSldapObject : Some configuration data of the object type %{obj} are missing "
"to generate the DN of the new object."
msgstr ""
#: includes/class/class.LSldapObject.php:3201
#: includes/class/class.LSldapObject.php:3219
msgid ""
"LSldapObject : The attibute %{attr} of the object is not yet defined. Can't "
"generate DN."
msgstr ""
#: includes/class/class.LSldapObject.php:3204
#: includes/class/class.LSldapObject.php:3222
msgid "LSldapObject : Without DN, the object could not be changed."
msgstr ""
#: includes/class/class.LSldapObject.php:3207
#: includes/class/class.LSldapObject.php:3225
msgid ""
"LSldapObject : The attribute %{attr_depend} depending on the attribute "
"%{attr} doesn't exist."
msgstr ""
#: includes/class/class.LSldapObject.php:3210
#: includes/class/class.LSldapObject.php:3228
msgid "LSldapObject : Error during deleting the object %{objectname}."
msgstr ""
#: includes/class/class.LSldapObject.php:3214
#: includes/class/class.LSldapObject.php:3232
msgid ""
"LSldapObject : Error during actions to be executed before renaming the objet."
msgstr ""
#: includes/class/class.LSldapObject.php:3217
#: includes/class/class.LSldapObject.php:3235
msgid ""
"LSldapObject : Error during actions to be executed after renaming the objet."
msgstr ""
#: includes/class/class.LSldapObject.php:3221
#: includes/class/class.LSldapObject.php:3239
msgid ""
"LSldapObject : Error during actions to be executed before deleting the objet."
msgstr ""
#: includes/class/class.LSldapObject.php:3224
#: includes/class/class.LSldapObject.php:3242
msgid ""
"LSldapObject : Error during actions to be executed after deleting the objet."
msgstr ""
#: includes/class/class.LSldapObject.php:3228
#: includes/class/class.LSldapObject.php:3246
msgid ""
"LSldapObject : Error during the actions to be executed before creating the "
"object."
msgstr ""
#: includes/class/class.LSldapObject.php:3231
#: includes/class/class.LSldapObject.php:3249
msgid ""
"LSldapObject : Error during the actions to be executed after creating the "
"object. It was created anyway."
msgstr ""
#: includes/class/class.LSldapObject.php:3235
#: includes/class/class.LSldapObject.php:3253
msgid ""
"LSldapObject : The function %{func} to be executed before creating the "
"object doesn't exist."
msgstr ""
#: includes/class/class.LSldapObject.php:3238
#: includes/class/class.LSldapObject.php:3256
msgid ""
"LSldapObject : Error executing the function %{func} to be execute after "
"deleting the object."
msgstr ""
#: includes/class/class.LSldapObject.php:3241
#: includes/class/class.LSldapObject.php:3259
msgid ""
"LSldapObject : The function %{func} to be executed after deleting the object "
"doesn't exist."
msgstr ""
#: includes/class/class.LSldapObject.php:3244
#: includes/class/class.LSldapObject.php:3262
msgid ""
"LSldapObject : Error executing the function %{func} to be execute after "
"creating the object."
msgstr ""
#: includes/class/class.LSldapObject.php:3248
#: includes/class/class.LSldapObject.php:3266
msgid ""
"LSldapObject : %{func} function, to be executed on object event %{event}, "
"doesn't exist."
msgstr ""
#: includes/class/class.LSldapObject.php:3251
#: includes/class/class.LSldapObject.php:3269
msgid ""
"LSldapObject : Error during the execution of %{func} function on object "
"event %{event}."
msgstr ""
#: includes/class/class.LSldapObject.php:3255
#: includes/class/class.LSldapObject.php:3273
msgid ""
"LSldapObject : %{meth} method, to be executed on object event %{event}, "
"doesn't exist."
msgstr ""
#: includes/class/class.LSldapObject.php:3258
#: includes/class/class.LSldapObject.php:3276
msgid ""
"LSldapObject : Error during execution of %{meth} method on object event "
"%{event}."
msgstr ""
#: includes/class/class.LSldapObject.php:3261
#: includes/class/class.LSldapObject.php:3279
msgid "LSldapObject : Error during generate LDAP filter for %{LSobject}."
msgstr ""
#: includes/class/class.LSldapObject.php:3265
#: includes/class/class.LSldapObject.php:3283
msgid ""
"LSldapObject : Error during execution of the custom action %{customAction} "
"on %{objectname}."
msgstr ""
#: includes/class/class.LSldapObject.php:3269
#: includes/class/class.LSldapObject.php:3287
msgid "LSldapObject : Fail to retrieve container DN."
msgstr ""
#: includes/class/class.LSldapObject.php:3272
#: includes/class/class.LSldapObject.php:3290
msgid ""
"LSldapObject : The function %{func} to generate container DN is not callable."
msgstr ""
#: includes/class/class.LSldapObject.php:3275
#: includes/class/class.LSldapObject.php:3293
msgid "LSldapObject : Error during generating container DN : %{error}"
msgstr ""
#: includes/class/class.LSldapObject.php:3278
#: includes/class/class.LSldapObject.php:3296
msgid ""
"LSldapObject : An LDAP object with the same DN as generated for this new one "
"already exists. Please verify your configuration."
msgstr ""
#: includes/class/class.LSldapObject.php:3283
#: includes/class/class.LSldapObject.php:3301
msgid ""
"LSrelation : Some parameters are missing in the call of methods to handle "
"standard relations (Method : %{meth})."
msgstr ""
#: includes/class/class.LSformElement_gpg_pub_key.php:113
msgid ""
"LSformElement_gpg_pub_key: PHP GnuPG extension is missing, can't parse value."
msgstr ""
#: includes/class/class.LSformRule_password.php:58
msgid "Password is too long (maximum: %{maxLength})."
msgstr ""
@ -1904,8 +1972,8 @@ msgstr ""
msgid "No object."
msgstr ""
#: includes/class/class.LSrelation.php:747 includes/routes.php:456
#: includes/routes.php:997
#: includes/class/class.LSrelation.php:747 includes/routes.php:457
#: includes/routes.php:1001
msgid "New"
msgstr ""
@ -2102,11 +2170,11 @@ msgid ""
"support."
msgstr ""
#: includes/class/class.LSform.php:327 includes/routes.php:634
#: includes/class/class.LSform.php:327 includes/routes.php:638
msgid "Do you really want to execute custom action %{title} on this search ?"
msgstr ""
#: includes/class/class.LSform.php:333 includes/routes.php:1432
#: includes/class/class.LSform.php:333 includes/routes.php:1451
msgid ""
"Do you really want to execute custom action %{customAction} on "
"%{objectname} ?"
@ -2296,8 +2364,8 @@ msgstr ""
msgid "Display RSS stack."
msgstr ""
#: includes/class/class.LSattr_ldap_password.php:108 includes/routes.php:587
#: includes/routes.php:1379
#: includes/class/class.LSattr_ldap_password.php:108 includes/routes.php:591
#: includes/routes.php:1398 includes/routes.php:2256
msgid "undefined"
msgstr ""
@ -2405,88 +2473,88 @@ msgstr ""
msgid "Pedagogical element"
msgstr ""
#: includes/class/class.LSsearch.php:1318
#: includes/class/class.LSsearch.php:1321
msgid "Actions"
msgstr ""
#: includes/class/class.LSsearch.php:1321
#: includes/class/class.LSsearch.php:1324
#: templates/default/global_search.tpl:16
msgid "This search didn't get any result."
msgstr ""
#: includes/class/class.LSsearch.php:1963
#: includes/class/class.LSsearch.php:1966
msgid "LSsearch : Invalid filter : %{filter}."
msgstr ""
#: includes/class/class.LSsearch.php:1966
#: includes/class/class.LSsearch.php:1969
msgid "LSsearch : Invalid basedn (%{basedn})."
msgstr ""
#: includes/class/class.LSsearch.php:1969
#: includes/class/class.LSsearch.php:1972
msgid "LSsearch : Invalid value for %{param} parameter."
msgstr ""
#: includes/class/class.LSsearch.php:1972
#: includes/class/class.LSsearch.php:1975
msgid ""
"LSsearch : Invalid size limit. Must be an integer greater or equal to 0."
msgstr ""
#: includes/class/class.LSsearch.php:1975
#: includes/class/class.LSsearch.php:1978
msgid "LSsearch : Invalid parameter %{param}. Must be an boolean."
msgstr ""
#: includes/class/class.LSsearch.php:1978
#: includes/class/class.LSsearch.php:1981
msgid ""
"LSsearch : Invalid parameter attributes. Must be an string or an array of "
"strings."
msgstr ""
#: includes/class/class.LSsearch.php:1981
#: includes/class/class.LSsearch.php:1984
msgid "LSsearch : Can't build attributes list for make filter."
msgstr ""
#: includes/class/class.LSsearch.php:1984
#: includes/class/class.LSsearch.php:1987
msgid ""
"LSsearch : Error building filter with attribute '%{attr}' and pattern "
"'%{pattern}'"
msgstr ""
#: includes/class/class.LSsearch.php:1987
#: includes/class/class.LSsearch.php:1990
msgid "LSsearch : Error combining filters."
msgstr ""
#: includes/class/class.LSsearch.php:1990
#: includes/class/class.LSsearch.php:1993
msgid "LSsearch : Invalid pattern."
msgstr ""
#: includes/class/class.LSsearch.php:1993
#: includes/class/class.LSsearch.php:1996
msgid "LSsearch : Invalid attribute %{attr} in parameters."
msgstr ""
#: includes/class/class.LSsearch.php:1996
#: includes/class/class.LSsearch.php:1999
msgid "LSsearch : Error during the search."
msgstr ""
#: includes/class/class.LSsearch.php:1999
#: includes/class/class.LSsearch.php:2002
msgid "LSsearch : Error sorting the search."
msgstr ""
#: includes/class/class.LSsearch.php:2002
#: includes/class/class.LSsearch.php:2005
msgid ""
"LSsearch : The function of the custum information %{name} is not callable."
msgstr ""
#: includes/class/class.LSsearch.php:2005
#: includes/class/class.LSsearch.php:2008
msgid ""
"LSsearch : Invalid predefinedFilter for LSobject type %{type} : %{label} "
"(filter : %{filter})."
msgstr ""
#: includes/class/class.LSsearch.php:2008
#: includes/class/class.LSsearch.php:2011
msgid "LSsearch : Error during execution of the custom action %{customAction}."
msgstr ""
#: includes/class/class.LSsearch.php:2011
#: includes/class/class.LSsearch.php:2014
msgid "LSsearch : Invalid search pattern."
msgstr ""
@ -2744,12 +2812,17 @@ msgstr ""
msgid "Unknown error : %{error}"
msgstr ""
#: includes/class/class.LSsearchEntry.php:213 includes/routes.php:1204
#: includes/routes.php:1297 includes/routes.php:1443
#: includes/class/class.LSformRule_gpg_pub_key.php:61
msgid ""
"LSformRule_gpg_pub_key: PHP GnuPG extension is missing, can't validate value."
msgstr ""
#: includes/class/class.LSsearchEntry.php:213 includes/routes.php:1214
#: includes/routes.php:1307 includes/routes.php:1462
msgid "View"
msgstr ""
#: includes/class/class.LSsearchEntry.php:229 includes/routes.php:1060
#: includes/class/class.LSsearchEntry.php:229 includes/routes.php:1064
msgid "Copy"
msgstr ""
@ -2775,64 +2848,64 @@ msgstr ""
msgid "You must provide pattern for global search."
msgstr ""
#: includes/routes.php:462 includes/routes.php:818
#: includes/routes.php:463 includes/routes.php:822
msgid "Import"
msgstr ""
#: includes/routes.php:467 includes/routes.php:884
#: includes/routes.php:468 includes/routes.php:888
msgid "Export"
msgstr ""
#: includes/routes.php:479
#: includes/routes.php:480
msgid "Reset"
msgstr ""
#: includes/routes.php:518 templates/default/select.tpl:31
#: includes/routes.php:519 templates/default/select.tpl:31
msgid "Approximative search"
msgstr ""
#: includes/routes.php:519 templates/default/select.tpl:32
#: includes/routes.php:520 templates/default/select.tpl:32
msgid "Recursive search"
msgstr ""
#: includes/routes.php:608
#: includes/routes.php:612
msgid ""
"The custom action %{title} have been successfully execute on this search."
msgstr ""
#: includes/routes.php:956
#: includes/routes.php:960
msgid "Data entry form"
msgstr ""
#: includes/routes.php:962 includes/routes.php:1736
#: includes/routes.php:966 includes/routes.php:1984
msgid "Object has been added."
msgstr ""
#: includes/routes.php:1099
#: includes/routes.php:1109
msgid "My account"
msgstr ""
#: includes/routes.php:1162 includes/routes.php:1908
#: includes/routes.php:1172 includes/routes.php:2156
msgid "The object has been partially modified."
msgstr ""
#: includes/routes.php:1165 includes/routes.php:1911
#: includes/routes.php:1175 includes/routes.php:2159
msgid "The object has been modified successfully."
msgstr ""
#: includes/routes.php:1280 includes/routes.php:1951
#: includes/routes.php:1290 includes/routes.php:2199
msgid "%{objectname} has been successfully deleted."
msgstr ""
#: includes/routes.php:1289
#: includes/routes.php:1299
msgid "Deleting : %{objectname}"
msgstr ""
#: includes/routes.php:1290
#: includes/routes.php:1300
msgid "Do you really want to delete <strong>%{displayName}</strong> ?"
msgstr ""
#: includes/routes.php:1400
#: includes/routes.php:1419 includes/routes.php:2278
msgid ""
"The custom action %{customAction} have been successfully execute on "
"%{objectname}."
@ -3079,6 +3152,10 @@ msgstr ""
msgid "File"
msgstr ""
#: templates/default/LSformElement_gpg_pub_key_field.tpl:3
msgid "Fingerprint:"
msgstr ""
#: templates/default/login.tpl:34
msgid "Forgot your password ?"
msgstr ""
@ -3093,6 +3170,11 @@ msgstr ""
msgid "Global search"
msgstr ""
#: templates/default/email_templates.tpl:9
#: templates/default/email_template.tpl:12
msgid "HTML content"
msgstr ""
#: templates/default/import.tpl:148
msgid "Hook: %(name)"
msgstr ""
@ -3134,7 +3216,7 @@ msgstr ""
msgid "Messages"
msgstr ""
#: templates/default/viewSearch.tpl:113
#: templates/default/viewSearch.tpl:128
msgid "Nb / page :"
msgstr ""
@ -3142,6 +3224,10 @@ msgstr ""
msgid "No access log found for this object."
msgstr ""
#: templates/default/email_templates.tpl:23
msgid "No existing email template."
msgstr ""
#: templates/default/LSform_view.tpl:34
msgid "No field."
msgstr ""
@ -3154,6 +3240,20 @@ msgstr ""
msgid "No value"
msgstr ""
#: templates/default/email_template.tpl:29
msgid ""
"No writable path to save your changes on the HTML message of this template."
msgstr ""
#: templates/default/email_template.tpl:24
msgid "No writable path to save your changes on the subject of this template."
msgstr ""
#: templates/default/email_template.tpl:34
msgid ""
"No writable path to save your changes on the text message of this template."
msgstr ""
#: templates/default/import.tpl:45
msgid "Object %(idx)"
msgstr ""
@ -3235,14 +3335,28 @@ msgstr ""
msgid "Right inherited from all connected users profile"
msgstr ""
#: templates/default/email_template.tpl:39
msgid "Save"
msgstr ""
#: templates/default/base_connected.tpl:6
msgid "Show/hide menu"
msgstr ""
#: templates/default/LSmail.tpl:21
#: templates/default/email_templates.tpl:8
#: templates/default/email_template.tpl:9 templates/default/LSmail.tpl:21
msgid "Subject"
msgstr ""
#: templates/default/email_templates.tpl:7
msgid "Template name"
msgstr ""
#: templates/default/email_templates.tpl:10
#: templates/default/email_template.tpl:15
msgid "Text content"
msgstr ""
#: templates/default/LSaccessRightsMatrixView.tpl:58
msgid "Their relations with other objects"
msgstr ""

View file

@ -2,13 +2,13 @@
{block name="content"}
<div id='LSaccessRightsMatrixView'>
<h1>{$pagetitle}</h1>
<ul class="LSaccessRightsMatrixView_tabs">
<ul class="LStabs">
{foreach $LSobjects as $obj => $obj_conf}
<li{if $LSobject==$obj} class="LSaccessRightsMatrixView_active_tab"{/if}><a href="addon/LSaccessRightsMatrixView/accessRightsMatrix?LSobject={$obj}">{$obj_conf.label}</a></li>
<li{if $LSobject==$obj} class="LStabs_active"{/if}><a href="addon/LSaccessRightsMatrixView/accessRightsMatrix?LSobject={$obj}">{$obj_conf.label}</a></li>
{/foreach}
</ul>
<div class='LSaccessRightsMatrixView_tab_content'>
<div class='LStabs_content LStabs_content_active'>
<h2>{$LSobjects[$LSobject]['label']}</h2>
<table class="table-header-rotated">

View file

@ -0,0 +1,7 @@
<ul class='LSform{if $multiple && !$freeze} LSformElement_multiple'{/if}' id='{$attr_name|escape:"quotes"}'>
{foreach from=$values_txt item=value}
<li>{include file="ls:$fieldTemplate"}</li>
{foreachelse}
<li>{include file="ls:$fieldTemplate"}</li>
{/foreach}
</ul>

View file

@ -0,0 +1,16 @@
{if $freeze}
{if $value.fingerprint}
{tr msg="Fingerprint:"}
<span class='LSformElement_gpg_pub_key_short_display' title='{$span_title|escape:"htmlall"}'>
{$value.fingerprint|escape:"htmlall"}
</span>
<p class='LSformElement_gpg_pub_key_value'>{$value.value|escape:"htmlall"}</p>
{elseif $value}
<span class='LSformElement_gpg_pub_key_short_display'>{tr msg="Invalid value"}</span>
<p class='LSformElement_gpg_pub_key_value'>{$value.value|escape:"htmlall"}</p>
{else}
{$noValueTxt|escape:"htmlall"}
{/if}
{else}
<textarea name='{$attr_name|escape:"quotes"}[]' class='LSform LSformElement_gpg_pub_key'>{$value|escape:"htmlall"}</textarea>
{/if}

View file

@ -0,0 +1,43 @@
{extends file='ls:base_connected.tpl'}
{block name="content"}
<h1>{$pagetitle}</h1>
{include file='ls:LSview_actions.tpl'}
<ul class="LStabs">
<li{if $tab=="subject"} class="LStabs_active"{/if}>
<a href="addon/mail/templates?name={$template.name}&tab=subject">{tr msg="Subject"}</a>
</li>
<li{if $tab=="html"} class="LStabs_active"{/if}>
<a href="addon/mail/templates?name={$template.name}&tab=html">{tr msg="HTML content"}</a>
</li>
<li{if $tab=="txt"} class="LStabs_active"{/if}>
<a href="addon/mail/templates?name={$template.name}&tab=txt">{tr msg="Text content"}</a>
</li>
</ul>
<div class='LStabs_content LStabs_content_active'>
<form action="addon/mail/templates?name={$template.name}&tab={$tab}" method="POST">
{if $tab == "subject"}
<input type="text" name="subject" value="{$template.subject|escape:"htmlall"}"
{if !$writable}disabled="disabled" class="LStips"
title="{tr msg="No writable path to save your changes on the subject of this template."}"
{/if}
/>
{elseif $tab == "html"}
<textarea name="html" {if !$writable}disabled="disabled"
title="{tr msg="No writable path to save your changes on the HTML message of this template."}"
{/if}
>{$template.html}</textarea>
{else}
<textarea name="txt" {if !$writable}disabled="disabled" class="LStips"
title="{tr msg="No writable path to save your changes on the text message of this template."}"
{/if}
>{$template.txt}</textarea>
{/if}
<div class="form-footer">
<button type="submit">{tr msg="Save"}</button>
</div>
</form>
</div>
{/block}

View file

@ -0,0 +1,28 @@
{extends file='ls:base_connected.tpl'}
{block name="content"}
<h1>{$pagetitle}</h1>
<table class="LStable">
<thead>
<tr>
<th>{tr msg="Template name"}</th>
<th class="mail_subject">{tr msg="Subject"}</th>
<th class="mail_content">{tr msg="HTML content"}</th>
<th class="mail_content">{tr msg="Text content"}</th>
</tr>
</thead>
<tbody>
{foreach $templates as $name => $tpl}
<tr class="{cycle values=",bis"}">
<td><a href="addon/mail/templates?name={$name|escape:"url"}">{$name}</td>
<td class="mail_subject">{$tpl.subject|escape:"htmlall"}</td>
<td class="mail_content">{$tpl.html|escape:"htmlall"}</td>
<td class="mail_content">{$tpl.txt|escape:"htmlall"}</td>
</tr>
{foreachelse}
<tr>
<td colspan="4" class="center">{tr msg="No existing email template."}</td>
</tr>
{/foreach}
</tbody>
</table>
{/block}

View file

@ -33,6 +33,11 @@
<table class='LSobject-list'>
<tr class='LSobject-list'>
{if $showSelectionBoxes}
<th class='LSobject-list LSobject-list-selection'>
<input type="checkbox" class="LSobject-list-selection" id="LSobject-list-select-all"/>
</td>
{/if}
<th class='LSobject-list'>
{if $LSsearch->sort}
<a href='object/{$LSsearch->LSobject|escape:"url"}?sortBy=displayName&amp;nocache={$smarty.now}'>
@ -85,18 +90,28 @@
</tr>
{foreach from=$page.list item=object}
<tr class='{cycle values="LSobject-list,LSobject-list LSobject-list-bis"}'>
<td class='LSobject-list LSobject-list-names'><a href='object/{$LSsearch->LSobject|escape:"url"}/{$object->dn|escape:'url'}' class='LSobject-list'>{$object->displayName|escape:"htmlall"}</a> </td>
{if $LSsearch->displaySubDn}<td class='LSobject-list'>{$object->subDn|escape:"htmlall"}</td>{/if}
{if $LSsearch->extraDisplayedColumns}
{foreach from=$LSsearch->visibleExtraDisplayedColumns item=conf key=cid}
<td class='LSobject-list'{if isset($conf.cssStyle) && $conf.cssStyle} style='{$conf.cssStyle|escape:"htmlall"}'{/if}>{if !isset($conf.escape) || $conf.escape}{$object->$cid|escape:"htmlall"}{else}{$object->$cid}{/if}</td>
{/foreach}
{/if}
<td class='LSobject-list LSobject-list-actions'>
{foreach from=$object->actions item=item}
<a href='{$item.url}' class='LSobject-list-actions'><img src='{img name=$item.action}' alt='{$item.label|escape:"htmlall"}' title='{$item.label|escape:"htmlall"}'/></a>
{if $showSelectionBoxes}
<td class='LSobject-list LSobject-list-selection'>
<input type="checkbox" name="selected" value="{$object->dn|escape:'quotes'}"/>
</td>
{/if}
<td class='LSobject-list LSobject-list-names'>
<a href='object/{$LSsearch->LSobject|escape:"url"}/{$object->dn|escape:'url'}'
class='LSobject-list'>
{$object->displayName|escape:"htmlall"}
</a>
</td>
{if $LSsearch->displaySubDn}<td class='LSobject-list'>{$object->subDn|escape:"htmlall"}</td>{/if}
{if $LSsearch->extraDisplayedColumns}
{foreach from=$LSsearch->visibleExtraDisplayedColumns item=conf key=cid}
<td class='LSobject-list'{if isset($conf.cssStyle) && $conf.cssStyle} style='{$conf.cssStyle|escape:"htmlall"}'{/if}>{if !isset($conf.escape) || $conf.escape}{$object->$cid|escape:"htmlall"}{else}{$object->$cid}{/if}</td>
{/foreach}
</td>
{/if}
<td class='LSobject-list LSobject-list-actions'>
{foreach from=$object->actions item=item}
<a href='{$item.url}' class='LSobject-list-actions'><img src='{img name=$item.action}' alt='{$item.label|escape:"htmlall"}' title='{$item.label|escape:"htmlall"}'/></a>
{/foreach}
</td>
</tr>
{foreachelse}
<tr class='LSobject-list'>

2
src/tmp/.gitignore vendored
View file

@ -1,3 +1,5 @@
*.php
*.tmp
*.log
*.kbx
*.gpg