Créer des travaux du minuteur à distance dans SharePoint

Créez des travaux du minuteur à distance pour gérer SharePoint en surveillant et gérant des données SharePoint. Les travaux du minuteur à distance ne s’exécutent pas sur votre serveur SharePoint. Au lieu de cela, les travaux du minuteur à distance sont des tâches planifiées qui s’exécutent sur un autre serveur.

Voici des exemples d’utilisation des travaux du minuteur :

  • Exécution de tâches de gouvernance, telles que l’affichage d’un message sur le site quand certains critères ne sont pas remplis, ou l’application de stratégies de rétention.
  • Exécution de processus planifiés nécessitant d’importantes ressources processeur.

Avant de commencer

Pour commencer, téléchargez l’exemple de complément Core.TimerJobs.Samples à partir du projet Pratiques et modèles Office 365 Développeur sur GitHub.

Remarque

Le code dans cet article est fourni tel quel, sans garantie d’aucune sorte, expresse ou implicite, y compris mais sans s’y limiter, aucune garantie implicite d’adéquation à un usage particulier, à une qualité marchande ou une absence de contrefaçon.

Pour commencer à utiliser la solution Core.TimerJobs.Samples, vous devez sélectionner un projet de démarrage, tel que le projet SimpleJob, en ouvrant le menu contextuel (clic droit) pour Core.TimerJobs.Samples.SimpleJob , puis en choisissant Définir comme projet de démarrage.

Remarque

Lorsque vous créez un projet dans Visual Studio, pour commencer à créer votre nouveau travail de minuteur à distance, ajoutez le package NuGet OfficeDevPnP.Core NuGet à votre projet. Dans Visual Studio, choisissez Outils>Gestionnaire de package NuGet>Gérer les packages NuGet pour la solution.

Planifier votre travail du minuteur à distance

Un travail du minuteur peut être planifié pour s’exécuter une ou plusieurs fois. Pour planifier le travail du minuteur à distance dans votre environnement de production, vous devez compiler votre code dans un fichier .exe, puis exécuter celui-ci à l’aide du Planificateur de tâches Windows ou d’une tâche web Microsoft Azure. Pour plus d’informations, voir Options de déploiement de travail du minuteur.

Utilisation du complément Core.TimerJobs.Samples.SimpleJob

Dans Core.TimerJobs.Samples.SimpleJob, Main dans Program.cs effectue les opérations suivantes :

  1. Crée un objet SimpleJob qui hérite de la classe de base OfficeDevPnP.Core.Framework.TimerJobs.TimerJob.

  2. Définit les informations d’identification d’utilisateur Office 365 à utiliser lors de l’exécution du travail du minuteur à l’aide de TimerJob.UseOffice365Authentication. Les informations d’identification d’utilisateur doivent permettre d’accéder de façon appropriée aux collections de sites. Pour en savoir plus, voir Authentification.

  3. Ajoute des sites sur lesquels le travail du minuteur doit effectuer des tâches à l’aide de TimerJob.AddSite. Vous pouvez également répéter l’instruction TimerJob.AddSite pour ajouter plusieurs sites ou tous les sites sous une URL spécifique en utilisant le caractère générique *. Par exemple, http://contoso.sharepoint.com/sites/* exécute le travail du minuteur sur tous les sites figurant dans le chemin d’accès géré de sites.

  4. Imprime les informations du travail du minuteur et exécute celui-ci en utilisant PrintJobSettingsAndRunJob.

     static void Main(string[] args)
             {
                 SimpleJob simpleJob = new SimpleJob();
    
                 // The user credentials must have access to the site collections you supply.
                 simpleJob.UseOffice365Authentication(User, Password);
    
                 // Use the following code if you are using SharePoint Server on-premises. 
                 //simpleJob.UseNetworkCredentialsAuthentication(User, Password, Domain);
    
                 // Add one or more sites that the timer job should work with.
                 simpleJob.AddSite("https://contoso.sharepoint.com/sites/dev");
    
                 // Prints timer job information and then calls Run().
                 PrintJobSettingsAndRunJob(simpleJob);
             }
    

Lors de l’instanciation de l’objet SimpleJob, le constructeur SimpleJob effectue les opérations suivantes :

  1. Appelle le constructeur de classe de base TimerJob.

  2. Affecte le gestionnaire d’événements SimpleJob_TimerJobRun pour gérer les événements TimerJobRun . SimpleJob_TimerJobRun s’exécute lorsqu’un appel est effectué à TimerJob.Run, qui est décrit plus en détail plus loin dans cet article.

     public SimpleJob() : base("SimpleJob")
             {
                 TimerJobRun += SimpleJob_TimerJobRun;
             }
    

Lorsque PrintJobSettingsAndRunJob s’exécute, la sortie de TimerJob est écrite dans la fenêtre console, puis TimerJob.Run est appelé.

 private static void PrintJobSettingsAndRunJob(TimerJob job)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("************************************************");
            Console.WriteLine("Job name: {0}", job.Name);
            Console.WriteLine("Job version: {0}", job.Version);
            Console.WriteLine("Use threading: {0}", job.UseThreading);
            Console.WriteLine("Maximum threads: {0}", job.MaximumThreads);
            Console.WriteLine("Expand sub sites: {0}", job.ExpandSubSites);
            Console.WriteLine("Authentication type: {0}", job.AuthenticationType);
            Console.WriteLine("Manage state: {0}", job.ManageState);
            Console.WriteLine("SharePoint version: {0}", job.SharePointVersion);
            Console.WriteLine("************************************************");
            Console.ForegroundColor = ConsoleColor.Gray;

            // Run job.
            job.Run();
        }

TimerJob.Run déclenche des événements TimerJobRun. TimerJob.Run appelle SimpleJob_TimerJobRun dans SimpleJob.cs, que vous définissez comme gestionnaire d’événements pour gérer les événements TimerJobRun dans le constructeur de SimpleJob.

Dans SimpleJob_TimerJobRun, vous pouvez ajouter votre code personnalisé que vous souhaitez que votre travail du minuteur effectue lors de l’exécution du travail du minuteur. SimpleJob_TimerJobRun exécute votre code personnalisé sur les sites que vous avez ajoutés à l’aide de TimerJob.AddSite dans Program.cs.

Dans cet exemple de code, SimpleJob_TimerJobRun utilise le ClientContext du TimerJob pour écrire le titre du site dans la fenêtre de console. Si plusieurs sites ont été ajoutés à l’aide de TimerJob.AddSite, SimpleJob_TimerJobRun est appelé pour chaque site.

 void SimpleJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            e.WebClientContext.Load(e.WebClientContext.Web, p => p.Title);
            e.WebClientContext.ExecuteQueryRetry();
            Console.WriteLine("Site {0} has title {1}", e.Url, e.WebClientContext.Web.Title);
        }

Exemple : travail du minuteur d’application de rétention de type de contenu

Le projet Core.TimerJobs.Samples.ContentTypeRetentionEnforcementJob montre comment utiliser des travaux du minuteur SharePoint pour appliquer des stratégies de rétention à des types de contenus. À l’aide de l’élément ContentTypeRetentionPolicyPeriod dans app.config, spécifiez :

  • key, c’est-à-dire l’ID du type de contenu auquel la période de rétention s’applique.
  • value, c’est-à-dire la période de rétention exprimée en jours. La période de rétention est appliquée à tous les éléments répertoriés créés à l’aide du type de contenu spécifié dans key.
<ContentTypeRetentionPolicyPeriod>
    <!-- Key is the content type ID, and value is the retention period in days -->
    <!-- Specifies an audio content type should be kept for 183 days -->
    <add key="0x0101009148F5A04DDD49cbA7127AADA5FB792B006973ACD696DC4858A76371B2FB2F439A" value="183" />
    <!-- Specifies a document content type should be kept for 365 days -->   
    <add key="0x0101" value="365" />
</ContentTypeRetentionPolicyPeriod>

ContentTypeRetentionEnforcementJob_TimerJobRun est défini comme gestionnaire d’événements pour l’événement TimerJobRun . Lorsque TimerJob.Run est appelé dans Program.cs, ContentTypeRetentionEnforcementJob_TimerJobRun effectue les étapes suivantes sur chaque site ajouté à l’aide de TimerJob.AddSite dans Program.cs :

  1. Obtient tous les bibliothèques de documents sur le site.
  2. Pour chaque bibliothèque de documents sur le site, lit les informations de configuration spécifiées par ContentTypeRetentionPolicyPeriod dans app.config. ApplyRetentionPolicy est appelé pour chaque paire ID de type de contenu et période de rétention lue dans le fichier app.config.
 void ContentTypeRetentionEnforcementJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            try
            {
                Log.Info("ContentTypeRetentionEnforcementJob", "Scanning web {0}", e.Url);

                // Get all document libraries. Lists are excluded.
                var documentLibraries = GetAllDocumentLibrariesInWeb(e.WebClientContext, e.WebClientContext.Web);

                // Iterate through all document libraries.
                foreach (var documentLibrary in documentLibraries)
                {
                    Log.Info("ContentTypeRetentionEnforcementJob", "Scanning library {0}", documentLibrary.Title);

                    // Iterate through configured content type retention policies specified in app.config.
                    foreach (var contentTypeName in configContentTypeRetentionPolicyPeriods.Keys)
                    {
                        var retentionPeriods = configContentTypeRetentionPolicyPeriods.GetValues(contentTypeName as string);
                        if (retentionPeriods != null)
                        {
                            var retentionPeriod = int.Parse(retentionPeriods[0]);
                            ApplyRetentionPolicy(e.WebClientContext, documentLibrary, contentTypeName, retentionPeriod);
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                Log.Error("ContentTypeRetentionEnforcementJob", "Exception processing site {0}. Exception is {1}", e.Url, ex.Message);
            }
        }

ApplyRetentionPolicy applique vos actions de stratégie de rétention personnalisées comme suit :

  1. Calcule la valeur validationDate. La méthode ApplyRetentionPolicy applique des actions de stratégie de rétention aux documents modifiés avant la date indiquée par la valeur validationDate. La valeur validationDate est alors mise en forme en tant que date CAML, puis attribuée à camlDate.

  2. Exécute une requête CAML pour filtrer les documents dans la bibliothèque de documents en fonction de l’ID de type de contenu spécifié dans le fichier app.config, où la valeur Modified By est inférieure à la valeur camlDate.

  3. Pour chaque élément de liste, applique des actions de rétention personnalisées à effectuer sur les documents à l’aide de code personnalisé.

private void ApplyRetentionPolicy(ClientContext clientContext, List documentLibrary, object contentTypeId, int retentionPeriodDays)
        {
            // Calculate validation date. You need to enforce the retention policy on any document modified before validation date.
            var validationDate = DateTime.Now.AddDays(-retentionPeriodDays);
            var camlDate = validationDate.ToString("yyyy-MM-ddTHH:mm:ssZ");

            // Get old documents in the library that match the content type.
            if (documentLibrary.ItemCount > 0)
            {
                var camlQuery = new CamlQuery();
                
                camlQuery.ViewXml = String.Format(
                    @"<View>
                        <Query>
                            <Where><And>
                                <BeginsWith><FieldRef Name='ContentTypeId'/><Value Type='ContentTypeId'>{0}</Value></BeginsWith>
                                <Lt><FieldRef Name='Modified' /><Value Type='DateTime'>{1}</Value></Lt>
                            </And></Where>
                        </Query>
                    </View>", contentTypeId, camlDate);

                var listItems = documentLibrary.GetItems(camlQuery);
                clientContext.Load(listItems,
                    items => items.Include(
                        item => item.Id,
                        item => item.DisplayName,
                        item => item.ContentType));

                clientContext.ExecuteQueryRetry(); 

                foreach (var listItem in listItems)
                {
                    Log.Info("ContentTypeRetentionEnforcementJob", "Document '{0}' has been modified earlier than {1}. Retention policy will be applied.", listItem.DisplayName, validationDate);
                    Console.WriteLine("Document '{0}' has been modified earlier than {1}. Retention policy will be applied.", listItem.DisplayName, validationDate);
                    
                    // Apply your custom retention actions here. For example, archiving documents, or starting a disposition workflow.
                }
            }
        }

Exemple : travail du minuteur de gouvernance

Le projet Core.TimerJobs.Samples.GovernanceJob utilise des travaux du minuteur pour s’assurer que les deux administrateurs sont affectés à vos collections de sites. Si ce n’est pas le cas, il affiche un message de notification sur le site.

SiteGovernanceJob_TimerJobRun est défini comme gestionnaire d’événements pour l’événement TimerJobRun . Lorsque TimerJob.Run est appelé dans Program.cs, SiteGovernanceJob_TimerJobRun effectue les étapes suivantes sur chaque collection de sites ajoutée à l’aide de TimerJob.AddSite dans Program.cs :

  1. Obtient le nombre d’administrateurs affectés à la collection de sites en utilisant la méthode d’extension GetAdministrators qui fait partie de OfficeDevPnP.Core.

  2. Charge le fichier JavaScript dans la liste SiteAssets ou Bibliothèque de styles en utilisant UploadFile qui fait partie de OfficeDevPnP.Core.

  3. Si le site comporte moins de deux administrateurs, AddJSLink ajoute un message de notification à un site à l’aide de JavaScript. Pour en savoir plus, voir Personnaliser l’interface utilisateur de votre site SharePoint à l’aide de JavaScript.

  4. Si le site comporte au poins deux administrateurs, le message de notification est supprimé à l’aide de DeleteJsLink.

void SiteGovernanceJob_TimerJobRun(object o, TimerJobRunEventArgs e)
        {
            try
            {
                string library = "";

                // Get the number of administrators.
                var admins = e.WebClientContext.Web.GetAdministrators();

                Log.Info("SiteGovernanceJob", "ThreadID = {2} | Site {0} has {1} administrators.", e.Url, admins.Count, Thread.CurrentThread.ManagedThreadId);

                // Get a reference to the list.
                library = "SiteAssets";
                List list = e.WebClientContext.Web.GetListByUrl(library);

                if (!e.GetProperty("ScriptFileVersion").Equals("1.0", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (list == null)
                    {
                        // Get a reference to the list.
                        library = "Style%20Library";
                        list = e.WebClientContext.Web.GetListByUrl(library);
                    }

                    if (list != null)
                    {
                        // Upload js file to list.
                        list.RootFolder.UploadFile("sitegovernance.js", "sitegovernance.js", true);

                        e.SetProperty("ScriptFileVersion", "1.0");
                    }
                }

                if (admins.Count < 2)
                {
                    // Show notification message because you need at least two site collection administrators.
                    e.WebClientContext.Site.AddJsLink(SiteGovernanceJobKey, BuildJavaScriptUrl(e.Url, library));
                    Console.WriteLine("Site {0} marked as incompliant!", e.Url);
                    e.SetProperty("SiteCompliant", "false");
                }
                else
                {
                    // Remove the notification message because two administrators are assigned.
                    e.WebClientContext.Site.DeleteJsLink(SiteGovernanceJobKey);
                    Console.WriteLine("Site {0} is compliant", e.Url);
                    e.SetProperty("SiteCompliant", "true");
                }

                e.CurrentRunSuccessful = true;
                e.DeleteProperty("LastError");
            }
            catch(Exception ex)
            {
                Log.Error("SiteGovernanceJob", "Error while processing site {0}. Error = {1}", e.Url, ex.Message);
                e.CurrentRunSuccessful = false;
                e.SetProperty("LastError", ex.Message);
            }
        }

Voir aussi