FIM Custom Activity – Guide de création (partie 3)

Voici la suite de mon article sur la création d’une Custom Activity pour FIM 2010.

Cette activité a été créé dans la FIMCustomActivityLibrary préparée dans la première partie de ce guide : FIM Custom Activity – Guide de création (partie 1)

Un deuxième article montre comment ajouter des activités et écrire le code source de ces activités : FIM Custom Activity – Guide de création (partie 2)

Dans cette troisième partie, je vais décrire comment créer l’interface homme machine (plus communément appelé GUI) de cette activité. Cette interface est un formulaire qui sera affiché dans le portail FIM et nous permettra d’ajouter des activités dans l’éditeur de Worklfow de FIM 2010.

Création de la classe SettingsPart

Pour commencer nous allons ajouter à notre projet Visual Studio une nouvelle classe nommée RequestLoggingActivitySettingsPart.cs

Le code source généré est le suivant :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FIMCustomActivityLibrary.RequestLoggingActivity
{
     class RequestLoggingActivitySettingsPart
     {
     }
}

Nous allons y ajouter les informations de références nécessaires, changer l’espace de nom pour accéder aux classes de l’interface utilisateur du portail FIM (classe WebUI) et également hériter la classe que l’on vient de créer de la classe ActivitySettingsPart :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// Added references
using System.Web.UI.WebControls;
using System.Workflow.ComponentModel;
using Microsoft.IdentityManagement.WebUI.Controls;
using Microsoft.ResourceManagement.Workflow.Activities;
using FIMCustomActivityLibrary.RequestLoggingActivity.RequestLoggingActivity;

namespace FIMCustomActivityLibrary.RequestLoggingActivity.WebUI
{
     class RequestLoggingActivitySettingsPart : ActivitySettingsPart
     {
     }
}

Maintenant nous pouvons instancier les méthodes de la classe WebUI en effectuant un clic droit sur le mot clef ActivitySettingsPart et en choisissant Implémenter une classe abstraite

Méthodes utiles

Afin de générer des éléments dans l’interface WebUI, nous allons devoir écrire quelques méthodes permettant de dessiner un tableau, écrire du texte, lire du texte, etc. A chaque fois que nous aurons à écrire une nouvelle activité personnalisée, nous aurons besoin de ces méthodes. Dans cet exemple seule des méthodes permettant de dessiner des champs de texte simpe sont utilisées, mais on peut imaginer ici dessiner n’importe quel champs de saisie classique de type radio bouton, liste déroulante, etc.

Ajoutons donc une région à notre classe avec les méthodes utiles:

#region Utility Functions
// Create a TableRow that contains a label and a textbox.
private TableRow AddTableRowTextBox(String labelText, String controlID, Int32 width, Int32 maxLength, Boolean multiLine, String defaultValue)
{
     TableRow row = new TableRow();
     TableCell labelCell = new TableCell();
     TableCell controlCell = new TableCell();
     Label oLabel = new Label();
     TextBox oText = new TextBox();

     oLabel.Text = labelText;
     oLabel.CssClass = base.LabelCssClass;
     labelCell.Controls.Add(oLabel);
     oText.ID = controlID;
     oText.CssClass = base.TextBoxCssClass;
     oText.Text = defaultValue;
     oText.MaxLength = maxLength;
     oText.Width = width;
     if (multiLine)
     {
          oText.TextMode = TextBoxMode.MultiLine;
          oText.Rows = System.Math.Min(6, (maxLength + 60) / 60);
          oText.Wrap = true;
     }
     controlCell.Controls.Add(oText);
     row.Cells.Add(labelCell);
     row.Cells.Add(controlCell);
     return row;
}

string GetText(String textBoxID)
{
     TextBox textBox = (TextBox)this.FindControl(textBoxID);
     return textBox.Text ?? String.Empty;
}

void SetText(String textBoxID, String text)
{
     TextBox textBox = (TextBox)this.FindControl(textBoxID);
     if (textBox != null)
          textBox.Text = text;
     else
          textBox.Text = "";
}

// Set the text box to read mode or read/write mode
void SetTextBoxReadOnlyOption(String textBoxID, Boolean readOnly)
{
     TextBox textBox = (TextBox)this.FindControl(textBoxID);
     textBox.ReadOnly = readOnly;
}
#endregion

Méthode CreateChildControls

La méthode CreateChildControls va nous permettre de dessiner un tableau contenant les différents contrôle de formulaire de la WebUI de notre activité. On ajoute cette méthode à la liste des méthodes instanciées précédemment :

/// <summary>
///  Creates a Table that contains the controls used by the activity UI
///  in the Workflow Designer of the FIM portal. Adds that Table to the
///  collection of Controls that defines each activity that can be selected
///  in the Workflow Designer of the FIM Portal. Calls the base class of
///  ActivitySettingsPart to render the controls in the UI.
/// </summary>
protected override void CreateChildControls()
{
     Table controlLayoutTable = new Table();

     // Width is set to 100% of the control size
     controlLayoutTable.Width = Unit.Percentage(100.0);
     controlLayoutTable.BorderWidth = 0;
     controlLayoutTable.CellPadding = 2;
     // Add a TableRow for each textbox in the UI
     controlLayoutTable.Rows.Add(this.AddTableRowTextBox("Logging Activity Name:", "txtLoggingActivityName", 400, 100, false, "Enter the logging activity name (description title)."));
     controlLayoutTable.Rows.Add(this.AddTableRowTextBox("Log File Path:", "txtLogFilePath", 400, 100, false, "Enter the log file Path."));
     this.Controls.Add(controlLayoutTable);

     base.CreateChildControls();
}

Méthode GenerateActivityOnWorkflow

Cette méthode instancie l’objet de type RequestLoggingActivity et y associe les propriétés présentes dans les contrôles de la WebUI (c’est le constructeur de notre activité) :

/// <summary>
/// Called when a user clicks the Save button in the Workflow Designer.
/// Returns an instance of the RequestLoggingActivity class that
/// has its properties set to the values entered into the text box controls
/// used in the UI of the activity.
/// </summary>
public override Activity GenerateActivityOnWorkflow(SequentialWorkflow workflow)
{
     if (!this.ValidateInputs())
     {
          return null;
     }
     RequestLoggingActivity LoggingActivity = new RequestLoggingActivity();
     LoggingActivity.LoggingActivityName = this.GetText("txtLoggingActivityName");
     LoggingActivity.LogFilePath = this.GetText("txtLogFilePath");
     return LoggingActivity;
}

Méthode LoadActivitySettings

Cette méthode est appelée par FIM à chaque fois que l’activité est rechargée. Elle permet de lire les propriétés de l’activité actuellement instanciée par le Workflow :

/// <summary>
/// Called by FIM when the UI for the activity must be reloaded.
/// It passes us an instance of our workflow activity so that we can
/// extract the values of the properties to display in the UI.
/// </summary>
public override void LoadActivitySettings(Activity activity)
{
     RequestLoggingActivity LoggingActivity = activity as RequestLoggingActivity;
     if (null != LoggingActivity)
     {
          this.SetText("txtLoggingActivityName", LoggingActivity.LoggingActivityName);
          this.SetText("txtLogFilePath", LoggingActivity.LogFilePath);
     }
}

Méthode PersistSettings

Cette méthode permet d’enregistrer les valeurs saisies dans les contrôles vers les propriétés de l’activité. Cette méthode est appelée lors de l’appuie du bouton de validation d’une activité dans l’interface WebUI de FIM.

/// <summary>
/// Saves the activity settings.
/// </summary>
public override ActivitySettingsPartData PersistSettings()
{
     ActivitySettingsPartData data = new ActivitySettingsPartData();
     data["LoggingActivityName"] = this.GetText("txtLoggingActivityName");
     data["LogFilePath"] = this.GetText("txtLogFilePath");
     return data;
}

Méthode RestoreSettings

Cette méthode permet de lire les propriétés de l’activité et de les afficher dans la WebUI de FIM. C’est cette méthode qui est appelée lorsque l’on édite une activité existante.

/// <summary>
///  Restores the activity settings in the UI
/// </summary>
public override void RestoreSettings(ActivitySettingsPartData data)
{
     if (null != data)
     {
          this.SetText("txtLoggingActivityName", (String)data["LoggingActivityName"]);
          this.SetText("txtLogFilePath", (String)data["LogFilePath"]);
     }
}

Méthode SwitchMode

Cette méthode permet de passer les contrôles de la WebUI de lecture seule à lecture/écriture. Lorsque l’on affiche une activité existante, cette méthode est appelé quand le bouton Edit est pressé.

/// <summary>
///  Switches the activity between read only and read/write mode
/// </summary>
public override void SwitchMode(ActivitySettingsPartMode mode)
{
     Boolean readOnly = (mode == ActivitySettingsPartMode.View);
     this.SetTextBoxReadOnlyOption("txtLoggingActivityName", readOnly);
     this.SetTextBoxReadOnlyOption("txtLogFilePath", readOnly);
}

Méthode Title

Cette méthode permet d’afficher le titre (ou description) de l’activité. Très utile lorsque l’on créé des activités génériques manipulant des attributs différents. Par exemple si l’on créé plusieurs activités de log de requête : une à chaque création d’utilisateur, une autre à la suppression d’un utilisateur et enfin une à chaque modification de groupe, permettre de nommer différemment ces activités lorsqu’on les créé dans le portail devient indispensable !

/// <summary>
///  Returns the activity name.
/// </summary>
public override String Title
{
     get
     {
          if (String.IsNullOrEmpty(this.GetText("txtLoggingActivityName")))
               return "Request Logging Activity";
          else
               return "Request Logging Activity: " + this.GetText("txtLoggingActivityName");
     }
}

Méthode ValidateInputs

Cette méthode permet de vérifier la validité des valeurs saisies au moment de l’enregistrement de l’activité. Cette méthode doit retourner Vrai si les valeurs sont valides et Faux dans le cas contraire.

/// <summary>
///  This method should be used to validate information entered
///  by the user when the activity is added to a workflow in the Workflow
///  Designer.
///  We could add code to verify that the log file path already exists on
///  the server that is hosting the FIM Portal and check that the activity
///  has permission to write to that location. However, the code
///  would only check if the log file path exists when the
///  activity is added to a workflow in the workflow designer.
///  If you don't want to implement this class, just return true.
/// </summary>
public override Boolean ValidateInputs()
{
     Boolean InputsValid = false;
     // Test Log file path
     String Path = this.GetText("txtLogFilePath");
     if (System.IO.Directory.Exists(Path))
     {
          InputsValid = true;
     }
     return InputsValid;
}

Compilation et ajout dans le portail FIM

Notre librairie est maintenant prête à être compilée. Il ne nous restera plus qu’à ajouter cette librairie d’activités au portail FIM.

Je décrirai cette étape dans un quatrième article consacré aux Custom Activities FIM 2010.

Leave a Comment

*