Les greffons - avancé
From LimeSurvey Manual
Vue d'ensemble
LimeSurvey peut être étendu grâce aux extensions. LimeSurvey est déjà livré avec un bon nombre d'extensions du noyau. Consultez également notre liste d'extensions tierces et, pourquoi pas, ajoutez-y un jour votre propre extension !
Les plugins permettent aux utilisateurs de personnaliser les fonctionnalités de leur installation tout en bénéficiant de mises à jour logicielles régulières.
Cette documentation est destinée aux développeurs qui étendent LimeSurvey pour leur propre usage ou pour leurs clients ; les utilisateurs finaux ne seront pas aidés par cette documentation.
Les plugins doivent implémenter l'interface iPlugin. Nous vous recommandons d'étendre votre classe de plugin à partir de la classe PluginBase.
Les extensions sont développées autour d'un mécanisme d'événements.
Paramètres du plugin
En étendant, vous bénéficiez des fonctionnalités communes requises par les plugins que nous avons déjà implémentés pour vous. L'une de ces fonctions est l'implémentation de la fonction getPluginSettings. Cette fonction doit renvoyer un tableau décrivant les options de configuration pour l'utilisateur.
L'exemple de plugin expose un seul paramètre configurable, le message qu'il affichera.
protected $settings = array(
'logo' => array(
'type' => 'logo',
'path' => 'assets/logo.png'
) ,
'message' => array(
'type' => 'string',
'label' => 'Message'
)
);
Le tableau contient un nom pour chaque paramètre sous forme de clé. Les valeurs sont des tableaux contenant les métadonnées requises.
Les types pris en charge sont :
- logo
- int (nombre entier)
- chaîne (alphanumérique)
- texte
- html
- pertinence
- info
- mot de passe
- date!
- sélectionner
Outre le type, un certain nombre d'autres clés sont disponibles :
- label : définit une étiquette
- default : définit une valeur à afficher si aucune valeur n'est spécifiée (uniquement pour les paramètres généraux, pas pour les paramètres du questionnaire)
- current : définit la valeur courante.
- readOnly : affiche le paramètre en lecture seule
- htmlOptions : les htmlOptions de la partie saisie (voir le manuel Yii [[1]])
- pluginOptions : pour certains paramètres (html ou select) : définit l'option du widget
- labelOptions : htmlOptions du label
- controlOptions : htmlOptions de l'élément englobant le label et la saisie
- localized : si true, la valeur est sauvegardée par langue. La saisie est envoyée sous la forme
name[language]etcurrentdoit être un tableau indexé par code de langue. - language : à utiliser avec localized. Soit un seul code de langue, soit (New in X.Y.Z ) un tableau de codes de langue pour afficher une saisie par langue (voir Paramètres d'extension localisés)
Vous pouvez trouver un exemple de plugin utilisant tous les paramètres réels sur exampleSettings
Lire et écrire les paramètres du plugin
Il est possible de lire et d'écrire les paramètres du plugin directement à partir du code de votre plugin.
Exemple:
$mySetting = $this->get('mySetting');
$this->set('mySetting', $mySetting + 1);
Vous pouvez obtenir une valeur par défaut si le paramètre est nul :
$mySetting = $this->get('mySetting', null, null, 10); // 10 is default
Paramètres d'extension spécifiques à un questionnaire
Deux événements sont utilisés pour créer des paramètres d'extension spécifiques à un questionnaire :
- newSurveySettings
- beforeSurveySettings
Exemple pour désactiver une extension pour un questionnaire donné :
public function init()
{
$this->subscribe('beforeSurveySettings');
$this->subscribe('newSurveySettings');
// Other events...
}
public function beforeSurveySettings()
{
$event = $this->event;
$surveyId = intval($event->get('survey'));
$event->set(
"surveysettings.{$this->id}",
[
'name' => get_class($this),
'settings' => [
'isActive' => [
'type' => 'boolean',
'label' => 'isActive',
'current' => $this->getIsActive($surveyId),
'help' => 'Activate plugin for this survey'
],
]
]
);
}
public function newSurveySettings()
{
$event = $this->event;
foreach ($event->get('settings') as $name => $value)
{
$this->set($name, $value, 'Survey', $event->get('survey'), false);
}
}
private function getIsActive(int $sid): bool
{
return (bool) $this->get('isActive', 'Survey', $sid, false);
}
<span id="Localized_plugin_settings_ (New in 7.4 )">
Paramètres d'extension localisés (New in 7.4 )
Pour afficher une saisie par langue du questionnaire pour un même paramètre, définissez localized à true et transmettez la liste des langues dans language. Le nom de la langue est ajouté à l'étiquette de chaque saisie.
public function beforeSurveySettings()
{
$event = $this->event;
$surveyId = intval($event->get('survey'));
$survey = Survey::model()->findByPk($surveyId);
$event->set(
"surveysettings.{$this->id}",
[
'name' => get_class($this),
'settings' => [
'errorText' => [
'type' => 'text',
'label' => $this->gT('Error text'),
'localized' => true,
'language' => $survey->getAllLanguages(),
// Tableau indexé par code de langue, par ex. ['en' => '...', 'de' => '...']
'current' => $this->get('errorText', 'Survey', $surveyId, []),
],
]
]
);
}
La valeur envoyée est un tableau indexé par code de langue ; la fonction newSurveySettings de l'exemple ci-dessus la sauvegarde donc sans modification. Pour lire le texte dans la langue courante :
$errorTexts = $this->get('errorText', 'Survey', $surveyId, []);
$errorText = $errorTexts[App()->getLanguage()] ?? '';
Événements
Les plugins s'abonnent aux événements et peuvent interagir avec LimeSurvey lorsque l'événement est déclenché. Pour une liste des événements actuellement disponibles, consultez Événements du plugin.
API
Les plugins ne doivent étendre LimeSurvey que via son API "publique". Cela signifie qu’utiliser directement les classes trouvées dans le code source est une mauvaise pratique. Bien que nous ne puissions pas vous y obliger, vous risquez d'avoir un plugin défectueux à chaque mise à jour mineure que nous effectuons.
Dans la mesure du possible, interagissez avec LimeSurvey uniquement via les méthodes décrites ici. Il en va de même pour les événements.
L'objet API est disponible via $this->api lors de l'extension depuis PluginBase, sinon vous pouvez l'obtenir à partir de l'instance PluginManager qui est transmise au constructeur de vos plugins.
De nouvelles fonctions peuvent être ajoutées à l'objet API sur demande.
<span id="Form_extension (New in 6 )">
Extension de formulaire (New in 6 )
Présentation
Le système d'extension de formulaire est un moyen plus général d'étendre les formulaires dans le noyau de LimeSurvey sans ajouter de nouvel événement pour chaque formulaire.
Il se compose des éléments suivants :
- Un module global appelé FormExtensionService
- Une bibliothèque de classes d'entrée que les plugins peuvent ajouter à l'initialisation du module ci-dessus
- Un widget, ainsi que moteurs de rendu personnalisés, utilisés dans les fichiers de vue LimeSurvey
Chaque formulaire est identifié par une chaîne de position, comme<form name><dot><tab name> . Exemple : globalsettings.general ou globalsettings.security .
L'objectif d'un système basé sur des classes sans HTML est de libérer les auteurs de plugins de l'œuvre pour qu'ils mettent à jour le code HTML lorsque le code HTML principal change. Néanmoins, l'auteur peut utiliser le type RawHtmlInput si nécessaire.
Une chose que vous ne pouvez pas faire dans ce système est d'ajouter de « nouveaux onglets de formulaire ».
Exemple
Pour ajouter une nouvelle entrée à un formulaire à partir d'un plugin, utilisez le code suivant de votre fonction init() :
TODO : Enregistrer dans les paramètres du plugin au lieu de global
// En haut du fichier
utilisez LimeSurvey\Libraries\FormExtension\Inputs\TextInput;
utilisez LimeSurvey\Libraries\FormExtension\SaveFailedException;
// À l'intérieur de init()
Yii::app()->formExtensionService->add(
'globalsettings.general',
new TextInput([
'name' => 'myinput',
'label' => 'Label',
'disabled' => true,
'tooltip' => 'Moo moo moo',
'help' => 'Un texte d'aide',
'save' => function($request, $connection) {
$value = $request->getPost('myinput');
if ($value === 'une valeur invalide') {
throw new SaveFailedException("Impossible d'enregistrer l'entrée personnalisée 'myinput'");
} else {
SettingGlobal::setSetting('myinput', $value);
}
} ,
'load' => function () {
return getGlobalSetting('myinput');
}
])
);
Validation
La validation de la saisie se fait dans la fonction save (voir exemple ci-dessus). Si la valeur publiée n'est pas valide, lancez une SaveFailedException et un message flash d'avertissement sera affiché à l'utilisateur.
Formulaires pris en charge
Les formulaires suivants peuvent être étendus :
- globalsettings.general (New in 6.0.0 )
Si vous souhaitez ajouter la prise en charge d'un autre formulaire principal, vous devez appliquer la modification suivante dans une pull-request :
Dans le fichier de vue, ajoutez :
<?php
use LimeSurvey\Libraries\FormExtension\FormExtensionWidget;
use LimeSurvey\Libraries\FormExtension\Inputs\DefaultBaseRenderer;
?>
... more HTML
<?= FormExtensionWidget::render(
App()->formExtensionService->getAll('globalsettings.security'),
new DefaultBaseRenderer()
); ?>
Vous devrez peut-être créer une nouvelle classe de rendu basée sur DefaultBaseRenderer , si le formulaire HTML est différent des autres formulaires. Vous devrez peut-être également étendre la classe de rendu par défaut avec des types d'entrée non encore ajoutés.
La deuxième modification que vous devez effectuer est d'ajouter un appel à la classe de service d'extension de formulaire dans l'action du contrôleur qui enregistre le formulaire :
$request = App()->request;
Yii::app()->formExtensionService->applySave('globalsettings', $request);
C'est ça!
<span id="Localization_ (New in 3 )">
Localisation (New in 3 )
Il est possible pour les plugins d'ajouter leurs propres fichiers de paramètres régionaux. Le format de fichier utilisé est .mo, identique aux traductions principales. Les fichiers doivent être stockés dans
<plugin root folder>/lieu/<language> /<language> .mo
où "<language> " est un mot de deux lettres comme "de" ou "fr".
Pour utiliser le fichier de paramètres régionaux spécifique, utilisez la fonction plugin gT :
$this->gT("A plugin text that needs to be translated");
Si la chaîne donnée ne peut pas être trouvée dans le fichier de paramètres régionaux spécifique au plugin, la fonction recherchera dans les fichiers de paramètres régionaux principaux. Il est donc prudent d'utiliser des chaînes telles que « Annuler » :
$this->gT("Cancel"); // Will be translated even if "Cancel" is not in the plugin locale file
Si vous utilisez des vues avec votre plugin, vous devez utiliser
$plugin->gT("Translate me");
pour faire une traduction spécifique au plugin à votre avis.
Vous pouvez utiliser le fichier limesurvey.pot comme exemple de ce à quoi peut ressembler un fichier pot. Celui-ci est importé dans votre outil de traduction.
Outils
Un outil open source pour éditer les fichiers po et mo est Poedit.
<span id="Logging_ (New in 3 )">
Journalisation (New in 3 )
Si vous souhaitez enregistrer quelque chose depuis votre plugin, écrivez simplement
$this->log("Your message");
Le niveau de journalisation par défaut est trace, mais vous pouvez donner un autre niveau de journalisation comme deuxième argument facultatif :
$this->log("Something went wrong!", CLogger::LEVEL_ERROR);
Le fichier journal se trouve dans le dossier
<limesurvey root folder>/tmp/runtime/plugin.log
Le nom de votre plugin est automatiquement utilisé comme catégorie. Une bonne façon de voir uniquement les erreurs de votre plugin consiste à utiliser grep (sous Linux) :
$ tail -f tmp/runtime/plugin.log | grep<your plugin name>
<span id="Logging_plugin_deactivation_due_to_error_ (New in 6.13.3 )">
Journalisation de la désactivation d'une extension suite à une erreur (New in 6.13.3 )
Depuis la version 5 de LimeSurvey, une extension peut être automatiquement désactivée en cas d'erreur PHP.
Un courriel a été envoyé automatiquement à l'administrateur de LimeSurvey. Vous pouvez utiliser le système de journalisation pour générer une alerte destinée aux autres administrateurs du serveur.
Par exemple, pour envoyer un message d'alerte :
// Plugin error happen, plugin was disable : send email using CLogRoute
'pluginError' => array(
'class' => 'CEmailLogRoute', // https://www.yiiframework.com/doc/api/1.1/CEmailLogRoute
'categories' => 'application.model.plugin.setLoadError', // A plugin was disable
'subject' => '[ERROR] Plugin deactivated',
'emails' => ['webmaster@example.org'],
'sentFrom' => 'noreply@example.org',
'enabled' => 1, // enabled here
)
Plus d'informations sur la configuration de la journalisation dans Yii 1 : Optional_settings/fr#Logging_settings.
<span id="Extension_updates_ (New in 4 )">
Mises à jour de l'extension (New in 4 )
Depuis la version 4.0.0 de LimeSurvey, un système est en place pour gérer les mises à jour des plugins et autres extensions. Pour utiliser ce système, votre fichier d'extension config.xml doit inclure la configuration du programme de mise à jour.
<updaters>
<updater>
<stable>1</stable>
<type>rest</type>
<source>https://comfortupdate.limesurvey.org/index.php?r=limestorerest</source>
<manualUpdateUrl>https://somedownloadlink.com/maybegithub</manualUpdateUrl>
</updater>
</updaters>
(La balise source ci-dessus pointe vers l'API REST LimeStore, qui sera utilisée pour toutes les extensions disponibles dans notre LimeStore.)
| Balise | Description |
|---|---|
| stable | "1" si cette source ne fournit que des numéros de version stables ; "0" si la source fournit également des versions instables, comme 0.3.3-beta.
|
| type | Pour l'instant, seul le type rest est pris en charge. Il est facile d'ajouter de nouveaux types de programmes de mise à jour (vérificateurs de versions), comme git, wget, etc.
|
| source | Le lien à partir duquel récupérer les nouvelles versions. |
| manualUpdateUrl | Lien vers lequel l'utilisateur peut se rendre pour mettre à jour l'extension vers la dernière version. |
| automaticUpdateUrl | À FAIRE |
Si vous ne souhaitez pas fournir de programme de mise à jour, vous devez mettre le texte suivant dans votre fichier XML de configuration :
<updaters disabled="disabled">
</updaters>
De cette façon, vous indiquez au système que vous avez délibérément désactivé le système de mise à jour et que vous n'avez pas simplement oublié de l'ajouter.
Le nouveau plugin UpdateCheck - installé et activé par défaut - vérifie les nouvelles mises à jour pour toutes les extensions installées lorsqu'un super administrateur se connecte, de manière asynchrone, au maximum une fois toutes les 24 heures. Si de nouvelles versions sont trouvées, une notification est envoyée.
Si une nouvelle mise à jour de sécurité est trouvée, la notification s'ouvrira automatiquement et sera classée en classe « danger ».
Vous pouvez vérifier manuellement les mises à jour en accédant à la vue du gestionnaire de plugins et en cliquant sur « Vérifier les mises à jour ». Notez que ce bouton n'est visible que si le plugin UpdateCheck est activé.
Sous le capot
Cette section fournit un bref aperçu de l'implémentation du programme de mise à jour de l'extension.
Le programme de mise à jour de l'extension fait partie de la bibliothèque ExtensionInstaller. Vous trouverez ci-dessous un diagramme UML pour les classes liées au processus de mise à jour.
Déroulement du programme au démarrage de Yii :
Yii init
VersionFetcherServiceLocator->init()
Add REST version fetcher
ExtensionUpdaterServiceLocator->init()
Add PluginUpdater
TODO: Add an updater for each extension type (theme, question template, ...)
Déroulement du programme lors de l'exécution du plugin UpdaterCheck :
Get all updaters from ExtensionUpdaterServiceLocator
Loop each updater
For each updater, loop through version fetchers configured by <updater> XML
For each version fetcher, contact remote source and get version information
Compose all versions into a notification
La méthode checkAll du plugin UpdateCheck fournit un exemple de la façon d'interroger toutes les extensions pour les nouvelles versions.
Ajout de nouveaux récupérateurs de version
Pour ajouter un nouveau récupérateur de version personnalisé, exécutez ceci lors de l'initialisation de Yii :
$service = \Yii::app()->versionFetcherServiceLocator
$service->addVersionFetcherType(
'myNewVersionFetcherType',
function (\SimpleXMLElement $updaterXml) {
return new MyNewVersionFetcher($updaterXml);
}
);
Bien entendu, la classe MyNewVersionFetcher doit sous-classer VersionFetcher .
Pour utiliser votre nouveau récupérateur de version, configurez la balise type dans le XML du programme de mise à jour pour utiliser
myNewVersionFetcherType(au lieu par exemplerest).
Ajout de nouveaux programmes de mise à jour d'extension
Pour ajouter un nouveau programme de mise à jour d'extension personnalisé, exécutez ceci lors de l'initialisation de Yii :
$service = \Yii::app()->extensionUpdaterServiceLocator;
$service->addUpdaterType(
'myNewExtensionUpdater',
function () {
return MyNewExtensionUpdater::createUpdaters();
}
);
La classe MyNewExtensionUpdater doit sous-classer ExtensionUpdater .
La balise type supérieure dans config.xml (« plugin », « thème », ...) contrôlera quel programme de mise à jour d'extension est utilisé pour cette extension. Le système n'est pas encore entièrement personnalisable, puisque vous devez également ajouter un ExtensionInstaller personnalisé, des éléments de menu, etc. Mais en théorie, et peut-être dans le futur, il devrait être possible d'ajouter un nouveau type d'extension de cette façon.
Installateur d'extensions
La bibliothèque d'installation d'extension se compose de deux classes abstraites :
- ExtensionInstaller
- FileFetcher
ExtensionInstaller est une sous-classe pour chaque type d'extension, comme PluginInstaller, QuestionThemeInstaller, etc.
Le FileFetcher est sous-classé pour chaque manière différente de récupérer des fichiers. Actuellement, seuls les fichiers zip téléchargés sont pris en charge, mais à l'avenir, il pourrait également y avoir un outil de récupération Github ou LimeStore.
Tutoriel
Ce tutoriel pas à pas montre comment créer une extension qui envoie une requête POST à chaque soumission d'une réponse du questionnaire. Il explique comment créer et sauvegarder des paramètres généraux et des paramètres propres à chaque questionnaire, comment enregistrer des événements, et bien plus encore.




