Mettre à jour les ressources des applications web (resx, dll, sitemap...)

[Edition] voir en fin de post

Je cherchais depuis quelques temps à optimiser et faciliter la localisation des applications SharePoint. J'avais ainsi utilisé la méthode GetLocalizedString() fort pratique qui m'a ainsi permis de localiser mes pages layouts et mes webparts sans utiliser de satellites.

Pas mal, mais quand il s'agit de traduire au sein des master pages par exemple, et bien ça se complique. En effet, il existe bien un moyen de recopier des fichiers vers le répertoire physique de l'application web, mais cette recopie ne se fait que lors de la création de l'application, ce qui me gêne pour mettre à jour une application existante.

Et puis je suis tombé tout récemment sur ce bon billet de Tom sur SharePoint et les ressources : http://tomblog.insomniacminds.com/2008/02/25/sharepoint-internals-resources/. Un article à lire pour tout ceux qui se posent des questions sur comment utiliser au mieux les ressources et où les publier. Et là, j'aperçois une commande stsadm : copyappbincontent. C'est malheureux, mais il existe plus d'une centaine de commandes, et je découvre encore l'utilisation de certaines ! Mais que fait cette commande au juste ?

D'après Mr TechNet :

"Copies Web application–specific files, such as page resource (*.resx) files from their respective locations in the 12\CONFIG folder to the correct location in each Web application on the computer."

Ca me semble très intéressant, mais je suis aussi tombé sur une information supplémentaire dans le SDK stipulant qu'il faut le lancer sur chacun des frontaux. Pas super pratique... J'aimerais pouvoir mettre à jour mes serveurs sans pour autant avoir à m'y connecter et lancer stsadm.

Du coup, en furetant dans stsadm et sa commande -o copyappbincontent (classe SPCopyAppBinContent), je suis tombé sur ceci pour la méthode "run" (pour ceux qui débarquent sur le développement dans stsadm, je vous envoie à quelques révisions) :

public override void Run(StringDictionary keyValues)
{
    SPServiceInstance localContent = SPWebServiceInstance.LocalContent;
    if ((localContent != null) && (localContent.Status == SPObjectStatus.Online))
    {
        SPWebService.ContentService.ApplyApplicationContentToLocalServer();
    }
    localContent = SPWebServiceInstance.LocalAdministration;
    if ((localContent != null) && (localContent.Status == SPObjectStatus.Online))
    {
        SPWebService.AdministrationService.ApplyApplicationContentToLocalServer();
    }
}
 
 

Et là, le tour est presque joué. En réalisant un job (SPJobDefinition) qui se charge d'appeler le code ci-dessus et de l'exécuter sur chacun des serveurs (en laissant le SPLockJobType à None, la tâche sera accomplie sans pour autant lancer  moi-même la commande. Cerise sur le gâteau, je peux lier ce job à un événement sur actiation de Feature (FeatureActivated) ce qui me permet d'avoir une solution clé en main s'occupant de tout.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
 
namespace ResourcesProvisioner
{
    public class CopyAppBinContent : SPJobDefinition
    {
        public CopyAppBinContent() : base() { }
        public CopyAppBinContent(SPWebApplication webApplication)
            : base("CopyAppBinContent", webApplication, null, SPJobLockType.None)
        { this.Title = "CopyAppBinContent Job"; }
 
        public override void Execute(Guid targetInstanceId)
        {
            try
            {
                SPServiceInstance localContent = SPWebServiceInstance.LocalContent;
                if ((localContent != null) && (localContent.Status == SPObjectStatus.Online))
                {
                    SPWebService.ContentService.ApplyApplicationContentToLocalServer();
                }
                SPServiceInstance localAdmin = SPWebServiceInstance.LocalAdministration;
                if ((localAdmin != null) && (localAdmin.Status == SPObjectStatus.Online))
                {
                    SPWebService.AdministrationService.ApplyApplicationContentToLocalServer();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failed to copy the app bin content");
                Debug.WriteLine(ex);
                throw;
            }
        }
 
        public void SubmitJob()
        {
            Schedule = new SPOneTimeSchedule(DateTime.Now.AddHours(-2));
            Title = "CopyAppBinContent Job - " + Schedule.ToString();
            Update();
        }
    }
}

Et la feature :

    public class ResourcesProvisionerWebAppFeatureReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            CopyAppBinContent cabc = new CopyAppBinContent(properties.Feature.Parent as SPWebApplication);
            cabc.SubmitJob();
        }
...
    }

Je pense m'en servir très prochainement dans le projet CKS:EBE et p-e l'ajouter au projet des Features SharePoint sur CodePlex.

En espérant que cela vous donne quelques idées d'implémentation et surtout que vous réaliserez des applications supportant la localisation ! (hé oui, c'est bien de trouver des super features, mais c'est mieux quand elles s'adaptent à la langue du site ;))

[Edition]

C'est au final presque une bonne idée, car en fait le compte utilisé pour le timer n'est généralement pas admin de la machine (c'est même à déconseillé). Il reste donc l'option de l'impersonation afin d'arriver à ses fins, ou bien d'effectuer la commande à la mano, ce qui gâche pour beaucoup l'intérêt de cette action... Dommage !

Gat, reflector mon ami

 

Commentaires

Laisser un commentaire





Validation Image CAPTCHA