[Conférence] Windows Phone 7

Rendez-vous le mercredi 8 décembre à 19h sur le campus d’Epitech Paris pour une présentation de Windows Phone 7. Ce nouveau système d’exploitation pour Smartphones est décrypté par plusieurs anciens d’Epitech, Arnaud Auroux (promotion 2009), Florent Santin (promotion 2006) et Adrien Sifferman (promotion 2009), ainsi que par Niels Freier (promotion 2011), responsable du laboratoire Microsoft d’Epitech. Même si certains thèmes abordés sont techniques, cette conférence est avant tout destinée au grand public.

« Réinventer l’expérience du mobile » est l’objectif que s’est fixé Microsoft avec Windows Phone 7. Sur un marché très concurrentiel avec l’IOS d’Apple et Android de Google, Microsoft a décidé de marquer une rupture avec son précédent système Windows Mobile 6.5 en repartant de zéro. Nous vous présenterons les arcanes de ce nouveau système, ses capacités, ses pré-requis, ses atouts ainsi que les choix techniques sur lesquels il a été bâti.

Au programme de cette conférence
  • Le nouveau design de Windows Phone 7, intitulé Métro, qui le distingue de ses concurrents
  • Une présentation de différents téléphones, dont certains en démonstration sur place
  • MarketPlace, la bibliothèque en ligne d’applications
  • L’environnement de développement de Windows Phone 7 : Visual Studio 2010, Silverlight
  • Développement de jeux vidéo pour ces Smartphones
  • Expression Blend pour Windows Phone 7, comment en tirer parti
  • Démonstrations de plusieurs applications professionnelles pertinentes ainsi que de projets étudiants innovants.

La conférence est suivie d’un cocktail pour échanger avec les intervenants et les auditeurs.

Inscription obligatoire (places limitées) avant le 1er décembre sur Internet

[Internet Explorer 9] Activer la fonctionnalité “Pinned Site” sur votre site ASP.Net

Internet Explorer 9 apporte son lot de nouvelles fonctionnalités en terme d’expérience de navigation, et parmi elles la possibilité d’épingler un site Web à la barre des tâches de Windows 7. L’idée générale est que certains sites Web sont réellement des applications Web, que l’on ouvre le matin et qu’on ne referme que le soir : c’est le cas par exemple pour votre site SharePoint, Facebook, Gmail, et plus encore pour des applications métiers.

Avec cette nouvelle fonctionnalité, IE9 nous permet de lancer en un clic une application Web, mais aussi de bénéficier d’une liste d’actions spécifiques à cette dernière, de recevoir des notifications visuelles ou de contrôler le site à partir d’une barre d’actions dans la miniature de la barre des tâches.

 

Comment ça marche ?

Pour ceux qui n’auraient pas encore eu le temps ou l’occasion de tester tout ça, commençons par un petit aperçu fonctionnel Sourire

Je navigue sur Facebook, et je fais un glisser déposer de l’onglet (ou de la favicon) sur ma barre des tâches Windows 7.

J’ai alors une grande icône qui s’ajoute dans ma barre des tâches, et le clic droit m’ouvre un menu avec des tâches personnalisées (en l’occurrence un accès direct aux actualités, aux messages…)

La fenêtre de mon application Facebook est elle aussi personnalisée : on retrouve l’icône en haut à gauche, et les boutons de navigation adoptent la couleur dominante de celle-ci.

Quand une nouvelle notification arrive sur Facebook, l’icône de la barre des tâches se modifie pour me le notifier de façon plus visuelle.

De la même manière, nous pouvons aussi épingler un site Web au menu Démarrer grâce à une option dans le menu Fichier d’Internet Explorer 9.

Nous retrouvons ainsi une icône similaire, avec la même liste de tâches.

 

Comment personnaliser ce bouton pour notre application Web ?

Le contrôle du comportement des boutons de lancement d’un site Web épinglé se fait de deux manières : grâce à des balises meta dans la balise head des pages du site et une API JavaScript. Nous allons nous concentrer ici sur la première partie qui permet la personnalisation générale du bouton et de la liste des tâches.

 

Personnalisation générale du bouton

Cinq balises meta spécifiques permettent de personnaliser le bouton qui va être épinglé dans la barre des tâches.

Nom Valeur Exemple
application-name Le nom du bouton. Si il n’est pas précisé, celui-ci sera automatiquement assigné avec le titre de la page Web.
<meta name="application-name" content="Mon application"/>
msapplication-tooltip Un texte qui sera affiché en bulle d’aide sur le survol du bouton du site épinglé.
<meta name="msapplication-tooltip" content="Ouvrir mon application !"/>
msapplication-starturl L’URL relative ou absolue à ouvrir sur le clic du bouton. Par défaut, ce sera la page affichée au moment où l’utilisateur épingle le site Web.
<meta name="msapplication-starturl" content="/"/>
msapplication-navbutton-color La couleur des boutons Retour et Suivant. Par défaut, cette couleur se base sur la couleur dominante de la favicon du site.
<meta name="msapplication-navbutton-color" content="#008eb0"/>
msapplication-window La largeur et la hauteur de la fenêtre à la première ouverture. Ces valeurs sont écrasées si l’utilisateur redimensionne la fenêtre.
<meta name="msapplication-window" content="width=800;height=600"/>

Pour l’icône, c’est encore plus simple, puisque Internet Explorer 9 identifie automatiquement la favicon définie sur le site pour l’assigner au bouton de la barre des tâches de Windows 7. Un petit exemple :

    <link rel="shortcut icon" type="image/x-icon" href="/Images/myicon.ico" /> 
    <link rel="icon" type="image/ico" href="/Images/myicon.ico" />

Nous pouvons cependant améliorer la résolution et le graphisme de notre icône. En effet, une favicon affichée dans un navigateur a une taille de 16x16 pixels, alors que celle de notre bouton sur Windows 7 est de 32x32 pixels. Vous pouvez donc inclure ces deux résolutions au sein de votre icône.

 

Personnalisation de la liste des tâches

Là encore, nous allons pouvoir définir une liste de tâches pour notre application Web avec des balises meta. Le nom de la balise doit être msapplication-task et sa valeur peut définir trois propriétés : le nom de la tâche, l’URL de la page et l’icône à afficher.

    <meta name="msapplication-task" content="name=Ma tâche;action-uri=/Page1.aspx;icon-uri=/Images/icon1.ico"/>

L’URL peut aussi être absolue ou relative. Attention cependant : l’URL sera relative à la page depuis laquelle l’utilisateur aura épinglé le site Web. Il n’y a pas de restrictions de domaine, vous pouvez donc très bien pointer sur un autre site. Enfin, un site Web peut définir jusqu’à 5 tâches différentes.

Une autre information importante, dont nous allons tirer parti dans la suite de cet article : la liste des tâches peut être mise à jour ! Il suffit de changer nos balises meta, et la liste sera modifiée au prochain lancement de notre application Web.

 

Comment tirer profit d’ASP.Net pour mettre en œuvre cette fonctionnalité ?

Maintenant que nous avons vu les principes de base, comment allons-nous pouvoir implémenter cela facilement dans une application ASP.Net ?

Si l’ajout de quelques balises meta semble trivial, la réalité est pourtant un peu plus complexe. Idéalement, voici ce dont on pourrait avoir envie pour la personnalisation de notre site épinglé :

  • Le nom et la bulle d’aide du bouton doivent être affichés dans la langue de l’utilisateur
  • De même, les noms des tâches doivent être localisés
  • Les tâches étant in fine des liens vers des pages existantes de mon application Web, j’aimerais pouvoir “marquer” les cinq pages que je veux mettre en avant
  • J’aimerais également pouvoir afficher des tâches différentes selon mes utilisateurs
  • Enfin, cette fonctionnalité étant un “plus” ergonomique, j’aimerais bien pouvoir mettre tout ça en œuvre rapidement, et sans écrire des pages de code ! Sourire

Pour la démonstration, nous allons partir d’un site très simple (basé sur le modèle de projet Visual Studio 2010). Vous trouverez la solution complète à la fin de cet article.

L’authentification par formulaire est activée sur le site, avec les Provider SQL par défaut.

Le site contient trois parties distinctes :

  • Une page d’accueil publique
  • Une zone “Utilisateur”
    Composée de trois pages, elle n’est accessible qu’aux utilisateurs identifiés du rôle “User”. Physiquement, elles se trouvent dans un même dossier “User” sécurisé grâce à un Web.config.
  • Une zone “Administrateur”
    Composée de cinq pages, elle n’est accessible qu’aux utilisateurs identifiés du rôle “Admin”. Physiquement, elles se trouvent dans un même dossier “Admin” sécurisé grâce à un Web.config.

Pour l’affichage du menu, nous utilisons comme il se doit un fichier SiteMap, qui précise pour chaque page quel est le rôle nécessaire à son affichage :

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" enableLocalization="true">
  <siteMapNode url="" title=""  description="" roles="*">
    <siteMapNode url="~/Default.aspx" resourceKey="Home" roles="*" />
    <siteMapNode url="" roles="Admin" resourceKey="Admin">
      <siteMapNode roles="Admin" resourceKey="AdminPage1" url="~/Admin/AdminPage1.aspx" />
      <siteMapNode roles="Admin" resourceKey="AdminPage2" url="~/Admin/AdminPage2.aspx" />
      <siteMapNode roles="Admin" resourceKey="AdminPage3" url="~/Admin/AdminPage3.aspx" />
      <siteMapNode roles="Admin" resourceKey="AdminPage4" url="~/Admin/AdminPage4.aspx" />
      <siteMapNode roles="Admin" resourceKey="AdminPage5" url="~/Admin/AdminPage5.aspx" />
    </siteMapNode>
    <siteMapNode roles="User" resourceKey="UserPage1" url="~/User/UserPage1.aspx" />
    <siteMapNode roles="User" resourceKey="UserPage2" url="~/User/UserPage2.aspx" />
    <siteMapNode roles="User" resourceKey="UserPage3" url="~/User/UserPage3.aspx" />
  </siteMapNode>
</siteMap>

Ce fichier est également localisé, grâce à l’attribut enableLocalization et aux attributs resourceKey qui précisent pour chaque nœud quelle ressource doit être affichée. Les titres sont alors définis dans le fichier de ressources global attaché à notre SiteMap :

Afin que les rôles soient correctement pris en compte pour le filtrage de notre SiteMap, nous devons ajouter quelques lignes dans notre Web.config à la racine.

<configuration>
  <!-- ... -->
  <system.web>
    <!-- ... -->
    <siteMap defaultProvider="SiteMapProvider" enabled="true">
      <providers>
        <add name="SiteMapProvider" type="System.Web.XmlSiteMapProvider" securityTrimmingEnabled="true" siteMapFile="Web.sitemap"/>
      </providers>
    </siteMap>
    <!-- ... -->
  </system.web>
  <!-- ... -->
</configuration>

Voilà pour la présentation de notre application ASP.Net, passons maintenant au cœur du sujet ! Sourire

 

Personnalisation générale du bouton

Cette première partie ne présente pas de difficultés particulières. Nous allons simplement ajouter les balises meta et link dont nous avons besoin dans la balise head de notre MasterPage :

    <link rel="shortcut icon" type="image/x-icon" href="/Images/Icons/Imajin.ico" /> 
    <link rel="icon" type="image/ico" href="/Images/Icons/Imajin.ico" />

    <meta name="application-name" runat="server" meta:resourcekey="ApplicationName" />
    <meta name="msapplication-tooltip" runat="server" meta:resourcekey="ApplicationToolTip"/>
    <meta name="msapplication-starturl" content="/"/>
    <meta name="msapplication-window" content="width=800;height=600"/>
    <meta name="msapplication-navbutton-color" content="#008eb0"/>

Notons simplement que nous avons localisé le nom et la bulle d’aide avec les attributs meta:resourcekey. Ainsi, nous pouvons définir les valeurs dans le fichier de ressources local pour notre MasterPage.

 

Personnalisation de la liste des tâches

Nous allons maintenant devoir ruser un peu Clignement d'œil

Récapitulons nos besoins :

  • On veut afficher une liste de balises meta, avec un nom localisé et une URL pour chacune
  • On veut que cette liste change selon l’utilisateur connecté
  • On veut pouvoir marquer simplement les pages à afficher dans cette liste

Au final, cela ressemble de très près à un menu, tel que nous l’avons mis en place avec le fichier SiteMap ! Pour choisir quelles pages vont être affichées, nous pouvons ajouter des attributs personnalisés dans notre fichier SiteMap, et filtrer nos éléments en fonction de cet attribut. Voilà ce que ça donne :

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" enableLocalization="true">
  <siteMapNode url="" title=""  description="" roles="*">
    <siteMapNode url="~/Default.aspx" resourceKey="Home" roles="*" />
    <siteMapNode url="" roles="Admin" resourceKey="Admin">
      <siteMapNode roles="Admin" resourceKey="AdminPage1" url="~/Admin/AdminPage1.aspx" />
      <siteMapNode roles="Admin" resourceKey="AdminPage2" url="~/Admin/AdminPage2.aspx" IncludeInTasks="true" TaskIcon="/Images/Icons/Templates.ico" />
      <siteMapNode roles="Admin" resourceKey="AdminPage3" url="~/Admin/AdminPage3.aspx" />
      <siteMapNode roles="Admin" resourceKey="AdminPage4" url="~/Admin/AdminPage4.aspx" IncludeInTasks="true" TaskIcon="/Images/Icons/Users.ico" />
      <siteMapNode roles="Admin" resourceKey="AdminPage5" url="~/Admin/AdminPage5.aspx" IncludeInTasks="true" TaskIcon="/Images/Icons/Groups.ico" />
    </siteMapNode>
    <siteMapNode roles="User" resourceKey="UserPage1" url="~/User/UserPage1.aspx" IncludeInTasks="true" TaskIcon="/Images/Icons/Profile.ico" />
    <siteMapNode roles="User" resourceKey="UserPage2" url="~/User/UserPage2.aspx" IncludeInTasks="true" TaskIcon="/Images/Icons/Signatures.ico" />
    <siteMapNode roles="User" resourceKey="UserPage3" url="~/User/UserPage3.aspx" />
  </siteMapNode>
</siteMap>

Comme vous le constatez, nous en avons profité pour inclure ici aussi l’icône que nous allons afficher pour chacune de nos tâches.

Pour l’affichage, nous allons tout simplement utiliser un Repeater directement dans la balise head de notre MasterPage. Celui-ci sera bindé sur la SiteMapDataSource que nous utilisons déjà pour notre menu.

    <asp:Repeater ID="rptTasks" runat="server" EnableViewState="false" DataSourceId="SiteMapDataSource" OnItemDataBound="OnTasksItemDataBound">
        <ItemTemplate>

            <meta name="msapplication-task" content="<%# "name=" + Eval("Title") + ";action-uri=" + Eval("Url") + ";icon-uri=" + Eval("[TaskIcon]") %>"/>

            <asp:Repeater ID="rptSubTasks" runat="server" EnableViewState="false" DataSource='<%# Eval("ChildNodes") %>' OnItemDataBound="OnTasksItemDataBound">
                <ItemTemplate>
                    <meta name="msapplication-task" content="<%# "name=" + Eval("Title") + ";action-uri=" + Eval("Url") + ";icon-uri=" + Eval("[TaskIcon]") %>"/>
                </ItemTemplate>
            </asp:Repeater>

        </ItemTemplate>
    </asp:Repeater>

Nous avons en fait mis deux Repeater imbriqués pour pouvoir parcourir les deux niveaux de notre SiteMap. L’attribut content de nos balises meta se construit simplement en concaténant les trois propriétés à définir, qui nous viennent tout droit des SiteMapNode.

A ce stade, nous affichons tous les éléments, il nous faut encore écrire un peu de code sur l’évènement OnItemDataBound pour pouvoir les filtrer en fonction de notre attribut IncludeInTasks :

    protected void OnTasksItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        SiteMapNode node = e.Item.DataItem as SiteMapNode;
        string include = node["IncludeInTasks"];
   
        if (String.IsNullOrEmpty(include) || include != "true")
        {
            if (node.HasChildNodes)
            {
                e.Item.Controls[0].Visible = false;
            }
            else
            {
                e.Item.Visible = false;
            }
        }
    }

Pour chaque nœud bindé, nous vérifions la valeur de l’attribut IncludeInTasks. Si celui-ci n’est pas présent ou que sa valeur n’est pas égale à true, nous le cachons.

Et le tour est joué ! Sourire

Résultat quand on n’est pas identifié sur le site :

Résultat en tant que membre du rôle “User” :

Résultat en tant que membre des rôles “Admin” et “User” :

Vous pouvez télécharger la solution complète ci-dessous :

La base de données contient deux utilisateurs :

  • Login : user
    Password : user
    Rôles : User
  • Login : admin
    Password : admin
    Rôles : User, Admin

 

Nous avons vu aujourd’hui comment bénéficier des dernières nouveautés d’Internet Explorer 9 en matière d’expérience de navigation sur nos applications ASP.Net, et ce sans déployer trop d’efforts d’implémentation.

J’espère que cela vous sera utile !

Adrien

[Visual Studio 2010] ProjectTemplates SharePoint

Philippe nous en avait parlé il y a quelques mois, Visual Studio 2010 propose des templates de projet spécifiques à SharePoint, simplifiant ainsi grandement le processus de développement…

Etant abonné MSDN, j’ai récupéré la Beta 1 de Visual Studio 2010 dès que sa sortie a été annoncée avec l’espoir de pouvoir enfin tester tout ça !

Première constatation : c’est beau ! Au delà de ça, on voit effectivement un dossier “SharePoint” dans la liste des templates installés… mais, malheureusement :

Et on a pas plus de chance en changeant la cible de Framework… On retrouve néanmoins les deux templates de projet de Workflows dont on disposait déjà en 2008.

Il faudra encore patienter et attendre la prochaine build de Visual Studio 2010 pour pouvoir constater les améliorations que nous a préparées Microsoft en terme d’expérience développeur avec SharePoint.

Cependant, la déception n’est que passagère, à la vue de toutes les fonctionnalités à découvrir dans cette nouvelle version ! Etienne, Matthieu et Thomas ont d’ailleurs commencé à vous en parler :)

Stay tuned !

Adrien

[SharePoint 2007] La famille SharePointOfView s’agrandit : SharePoint FxCop Rules

[English version @ http://sovfxcoprules.codeplex.com]

Après :

  • SharePointOfView, qui regroupe un ensemble de fonctionnalités dans le but simplifier le développement avec SharePoint,
  • SPReporting, le framework de développement permettant de créer facilement et rapidement ses propres rapports pour les administrateurs SharePoint,

voici un nouveau venu dans la famille SharePointOfView : SharePoint FxCop Rules !

Ce projet a pour but d’analyser la qualité et la performance du code produit pour SharePoint 2007, en vérifiant les Dispose Patterns tels que publiés par Microsoft.

Les règles sont utilisables à la fois avec Code Analysis directement dans Visual Studio Team System 2008 Development Edition, ou avec l’exécutable Microsoft FxCop 1.6.

Voici la liste des règles mises en place à l’heure actuelle :

N’hésitez pas à me faire part de vos retours (idées, bugs, demande de fonctionnalités) sur CodePlex (Issue Tracker et Discussions).

J’espère que ce projet vous sera utile !

Adrien

[MOSS 2007] Publier ses formulaires InfoPath via feature

Lorsque l'on veut publier des formulaires InfoPath sur un serveur SharePoint, il faut passer par l'administration centrale pour le télécharger sur le serveur, puis l'activer pour chacune des collections de sites pour lesquelles on veut le rendre disponible. Cette solution, bien que simple, nécessite forcemment une intervention humaine, ce qui peut être pénalisant dans le cadre de multiples déploiements, ou tout simplement si l'on souhaite activer un formulaire InfoPath à la création d'une collection de sites.

Analysons de plus près ce que génèrent ces actions dans l'administration centrale.

Lorsqu'on choisit de télécharger un nouveau formulaire InfoPath, on peut se rendre compte qu'une solution est automatiquement ajoutée au magasin du serveur. Le nom de cette solution commence par "form-", suivi par le nom du formulaire précédemment téléchargé.

De plus, dans la liste des fonctionnalités de la collection de site, on voit apparaître une nouvelle fonctionnalité du nom de notre formulaire.

Et lorsque nous activons un formulaire pour une collection de sites, cette fonctionnalité se retrouve tout simplement activée.

Super ! Des solutions, des fonctionnalités, voilà un modèle de déploiement auquel nous sommes habitués dans SharePoint, et qui peut être automatisé. Il va donc nous falloir reproduire le comportement de celles qui sont générées par l'interface d'administration.

Grâce à une petite application console, on peut récupérer tous les fichiers .wsp correspondants aux solutions actuellement installés sur le serveur :

SPSite site = new SPSite("http://localhost");

SPFarm farm = site.WebApplication.Farm;

     foreach (SPSolution solution in farm.Solutions)

     {

         SPPersistedFile solutionFile = solution.SolutionFile;

         solutionFile.SaveAs(solution.Name);

     }

En renommant le fichier solution en .cab, on s'aperçoit qu'il contient les éléments suivants :

Il s'agit donc d'une solution assez simple, avec une seule feature, contenant un fichier de définition, un fichier de provisionning et le modèle de formulaire .xsn. La gestion des multiples versions du formulaire est assurée par un système de dossiers différents pour chaque version.

Pour pouvoir reproduire une telle fonctionnalité, il s'agit d'identifier les informations clés des fichiers XML. Il s'agit notamment d'un FeatureReceiver particulier, de quelques propriétés et du téléchargement du fichier XSN dans une librairie spécifique. Ainsi, voilà comment faire une simple fonctionnalité d'activation de formulaire InfoPath :

Feature.xml

<Feature xmlns="http://schemas.microsoft.com/sharepoint/"

    Id="D2BD3DFB-178E-4fda-89E1-77F67C45641D"

    Title="Identity Form"

    Description="Provides a simple form to give his identity."

    Hidden="FALSE"

    Scope="Site"

    Version="1.0.0.0"

    ReceiverClass="Microsoft.Office.InfoPath.Server.Administration.XsnFeatureReceiver"

    ReceiverAssembly="Microsoft.Office.InfoPath.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">

  <ElementManifests>

    <ElementManifest Location="Elements.xml" />

  </ElementManifests>

  <Properties>

    <Property Key="FeatureName" Value="IdentityForm" />

    <Property Key="OriginalFileName" Value="Identity.xsn" />

  </Properties>

  <ActivationDependencies>

    <!-- XsnFeatureReceiver -->

    <ActivationDependency FeatureId="C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5" />

    <!-- WSS Fields -->

    <ActivationDependency FeatureId="CA7BD552-10B1-4563-85B9-5ED1D39C962A" />

  </ActivationDependencies>

</Feature>

Elements.xml

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <Module Name="XSN" Url="FormServerTemplates" RootWebOnly="TRUE">

    <File Url="Identity.xsn" Name="Identity.xsn" Type="GhostableInLibrary"/>

  </Module>

</Elements>

Il suffit juste d'ajouter le fichier .xsn dans le répertoire de la fonctionnalité pour que cela fonctionne. Comme nous avons déterminé un GUID spécifique pour notre fonctionnalité, nous pouvons alors demander automatiquement son activation dans le fichier de définition d'un site par exemple, ou tout simplement l'inclure dans une solution globale.

A noter que si votre formulaire contient du code C# ou VB pour exécuter des actions spécifiques, il faut alors juste mettre la DLL de ce code dans le répertoire de la fonctionnalité.

J'espère que cela vous aidera !

Adrien

[MOSS 2007] Définir les propriétés de navigation de portail dans le fichier Onet.xml

Si l'on observe le fichier Onet.xml du template de site Publishing fourni en standard, on peut noter l'activation d'une feature de scope Web déterminant les options de navigation de portail :

      <WebFeatures>

      ...

        <Feature ID="541F5F57-C847-4e16-B59A-B31E90E6F9EA">

          <Properties xmlns="http://schemas.microsoft.com/sharepoint/">

            <Property Key="InheritGlobalNavigation" Value="true"/>

            <Property Key="ShowSiblings" Value="true"/>

            <Property Key="IncludeSubSites" Value="true"/>

          </Properties>

        </Feature>

      ...

      </WebFeatures>

On voit que la feature est accompagnée de propriétés. Ce qui nous intéresse alors est de savoir quelles sont les propriétés acceptées par la feature, quelles valeurs peut-on leur donner, et quel impact cela aura sur la navigation du site.

En poussant un peu notre investigation, on retrouve cette feature dans le répertoire [12]\TEMPLATE\FEATURES\NavigationProperties. Le fichier Feature.xml qu'il contient définit la feature comme suit :

<Feature  Id="541F5F57-C847-4e16-B59A-B31E90E6F9EA"

          Title="Portal Navigation Properties"

          Description="Set per-site navigation properties."

          Version="12.0.0.0"

          Scope="Web"

          Hidden="TRUE"

          ReceiverAssembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"

          ReceiverClass="Microsoft.SharePoint.Publishing.NavigationFeatureHandler"

          xmlns="http://schemas.microsoft.com/sharepoint/">

  <ElementManifests>

    <ElementManifest Location="NavigationSiteSettings.xml"/>

  </ElementManifests>

</Feature>

Notons au passage que le fichier NavigationSiteSettings.xml déclaré en tant que ElementManifest est un fichier vide. Nous allons alors explorer la classe Microsoft.SharePoint.Publishing.NavigationFeatureHandler avec Reflector pour voir quelles informations elle traite.

 

Nous pouvons alors voir toutes les propriétes qu'accepte la feature "Portal Navigation Properties", à savoir :

  • IncludeInGlobalNavigation
  • IncludeInCurrentNavigation
  • InheritGlobalNavigation
  • InheritCurrentNavigation
  • ShowSiblings
  • IncludeSubSites
  • IncludePages
  • OrderingMethod
  • AutomaticSortingMathod (oui, Mathod et pas Method)
  • SortAscending

Ces noms évoquent sans doute quelque chose aux habitués de la plateforme de publication de MOSS 2007. En effet, on peut paramétrer ces options sur un site grâce à l'interface utilisateur dans la page "/_layouts/AreaNavigationSettings.aspx" accessible depuis le menu des actions du site

Afin de mieux se rendre compte de l'impact de chaque option sur la navigation, nous allons voir pour chacune d'entre elles les valeurs possibles ainsi que la répercussion sur cette page d'administration.

 

IncludeInGlobalNavigation et IncludeInCurrentNavigation

Ces deux options sont un peu particulières puisqu'elles définissent si le site courant doit être inclus dans la navigation actuelle ou globale du site parent. Ainsi, pour agir sur ces options avec l'interface utilisateur, il faut remonter au niveau supérieur et choisir de masquer ou non le sous-site dans les sections de navigation actuelle et globale.

Valeurs possibles :

  • true - Inclut le site dans la navigation (actuelle ou globale).
  • false - N'inclut pas le site dans la navigation (actuelle ou globale).

 

IncludeSubSites et IncludePages

Ces deux options indiquent si les pages ou les sous-sites doivent être automatiquement inclus dans la navigation. Notons que si la navigation hérite du site parent, ces options n'ont aucun sens.

Valeurs possibles :

  • true - Inclut les pages ou les sous-sites.
  • false - N'inclut pas les pages ou les sous-sites.

 

OrderingMethod

Cette option permet de définir le tri de tous les éléments de navigation.

Valeurs possibles :

  • Automatic - Trie tous les types d'éléments automatiquement, et groupe les pages après tous les autres types.
  • ManualWithAutomaticPageSorting - Trie tous les types d'éléments manuellement, à l'exception des pages, qui sont groupées et triées automatiquement après les autres types.
  • Manual - Trie tous les types d'éléments manuellement.

 

AutomaticSortingMathod

Cette option, qui n'a de sens que si OrderingMethod n'a pas été défini à "Manual", permet de préciser le critère de tri automatique des éléments de navigation.

Valeurs possibles :

  • Title - Trie les éléments alphabétiquement par leur titre.
  • CreatedDate - Trie les éléments par leur date de création.
  • LastModifiedDate - Trie les éléments par leur date de dernière modification.

 

SortAscending

Cette option, qui n'a de sens que si OrderingMethod n'a pas été défini à "Manual", permet de préciser l'ordre de tri automatique des éléments de navigation.

Valeurs possibles :

  • true - Trie les éléments par ordre croissant.
  • false - Trie les éléments par ordre décroissant.

 

InheritGlobalNavigation

Cette option permet de définir l'héritage ou non de la navigation globale du site.

Valeurs possibles :

  • true - Affiche les mêmes éléments de navigation que le site parent.
  • false - Affiche les éléments de navigation sous le site actuel.

 

InheritCurrentNavigation et ShowSiblings

Il est un peu moins trivial de comprendre l'impact de ces deux options. Premièrement, il est à noter qu'elles agissent sur l'affichage de la navigation actuelle. Or, si l'on regarde l'interface utilisateur, on s'aperçoit que trois options différentes nous sont proposées :

Pour reproduire ces paramètres dans les propriétés de la feature, il va nous falloir combiner les valeurs des deux options InheritCurrentNavigation et ShowSiblings.

  • InheritCurrentNavigation = true et ShowSiblings = false donnera :

     
  • InheritCurrentNavigation = false et ShowSiblings = true donnera :


  • InheritCurrentNavigation = false et ShowSiblings = false donnera :

 

Grâce à toutes ces propriétés de la feature "Portal Navigation Properties", vous avez la possibilité de définir précisemment la navigation de vos sites de publication personnalisés directement à sa création.

Il est à noter que vous pouvez également changer toutes ces options via le modèle objet en récupérant l'objet PublishingWeb correspondant à votre site.

 

            using (SPSite site = new SPSite("http://localhost"))

            {

                using (SPWeb web = site.RootWeb)

                {

                    PublishingWeb pubWeb = PublishingWeb.GetPublishingWeb(web);

 

                    pubWeb.IncludeInGlobalNavigation = true;

                    pubWeb.IncludeInCurrentNavigation = true;

 

                    pubWeb.IncludeSubSitesInNavigation = true;

                    pubWeb.IncludePagesInNavigation = true;

 

                    pubWeb.NavigationOrderingMethod = OrderingMethod.ManualWithAutomaticPageSorting;

                    pubWeb.NavigationAutomaticSortingMethod = AutomaticSortingMethod.Title;

                    pubWeb.NavigationSortAscending = false;

 

                    pubWeb.InheritGlobalNavigation = true;

 

                    pubWeb.InheritCurrentNavigation = false;

                    pubWeb.NavigationShowSiblings = true;

                }

            }

 

Adrien

[SharePoint 2007] Développer des User Defined Functions pour Excel Services

Les Excel Services sont une nouvelle technologie fournie avec Microsoft Office SharePoint Server (MOSS) Entreprise 2007, et qui permet de simplement utiliser, partager et gérer des fichiers Microsoft Office Excel 2007 en tant que rapports interactifs sur un site d'entreprise.

Pour les classeurs publiés par ce moyen sur une plateforme SharePoint, Microsoft propose d'étendre la librairie de fonctions déjà disponible avec Excel grâce aux User Defined Functions (UDF). Ces dernières peuvent être exécutées depuis une feuille de calcul comme toute autre fonction Excel standard. Comme le développement des UDF s'appuie sur le Framework .Net, les possibilités d'extension sont pratiquement illimitées.

Côté technique, l'écriture d'UDF ne se distingue des développements classiques que par un attribut venant préfixer les méthodes exposées. Elles représentent, entres autres, un moyen simple de centraliser des formules jusqu'alors disponibles sous forme de macros, sans en dévoiler les règles métier. Par exemple, pour extraire le dernier mot d'une phrase dans une cellule, on pourrait écrire la formule :

Avec les UDF, on permet à l'utilisateur de simplement saisir le nom d'une nouvelle fonction :

Vous trouverez l'article complet, co-rédigé avec mon collègue et néanmoins ami Philippe Sentenac, sur le site Tech Head Brothers à cette adresse : Développer des User Defined Functions pour Excel Services.

Adrien

[SharePoint 2007] Création de Workflow avec Visual Studio 2008

Comme Philippe l'a annoncé sur son blog, et suite à la session qu'il a présentée aux TechDays 2008, nous nous sommes lancés dans la co-rédaction d'une série d'articles sur la création de Workflow pour Windows SharePoint Services 3.0 avec Visual Studio 2008.

Ainsi, nous évoquerons les sujets suivants :

  • Les avantages de Visual Studio 2008 pour le développement de Workflow SharePoint 
  • Les différences entre Workflows séquentiel et à états
  • Le design d'un Workflow à états
  • L'utilisation de correlation token
  • Des activités comme OnWorkflowActivated, Delay, ...
  • La création de formulaires ASP.Net et Infopath + Form Services
  • L'interaction entre les Workflows SharePoint et la suite Office 2007
  • La gestion de la sécurité sur les tâches et sur les documents liés au Workflow
  • La gestion d'un processus d'escalade dans les deux types de Workflow
  • La gestion des erreurs dans un Workflow
  • La création de types de contenu personnalisés pour un Workflow
  • Le packaging et le déploiement sur votre environnement

Le premier article de la série est disponible à cette adresse : Développer un Workflow « SharePoint » avec Visual Studio 2008

Bonne lecture !

Adrien


Classé sous , , ,

[MOSS 2007] Intégrer des champs de type Link et Image à ses types de contenu de page de publication

Nous allons voir aujourd'hui comment créer un type de contenu héritant de Page et intégrant des champs personnalisés de type Link et Image, ainsi que son modèle de page associé. Commençons par créer le fichier de définition du type de contenu.

CustomPage.xml :

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <ContentType

    ID="0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39005AB43615533C4e09BF40969BDF9856AE"

    Name="CustomPage"

    Group="Types de contenu personnalisés"

    Description="Définit un type de contenu d'une page de publication personnalisée."

    Version="1">

    <FieldRefs>

      <FieldRef ID="{87F3E3CF-0861-4629-8386-7E87034A7A12}" Name="CustomPageLogo" />

      <FieldRef ID="{8558B229-B874-4c84-BE54-4AD6FA82B186}" Name="CustomPageContact" />

    </FieldRefs>

  </ContentType>

</Elements>

L'identifiant de notre type de contenu est constitué de l'identifiant du type de contenu Page suivi d'un séparateur "00" et d'un nouveau GUID. Cette mécanique permet de définir que CustomPage hérite de Page. Pour ceux qui ne sont pas familiarisés avec les ID des types de contenu, je conseille la lecture de cet article MSDN. Dans la partie FieldRefs, nous renseignons les références aux champs personnalisés que nous allons définir dans un deuxième fichier.

CustomPage.Fields.xml :

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <Field

    ID="{87F3E3CF-0861-4629-8386-7E87034A7A12}"

    Type="Image"

    Name="CustomPageLogo"

    DisplayName="Logo de la page personnalisée"

    StaticName="CustomPageLogo"

    Group="Custom"

    RichText="True"

    RichTextMode="FullHtml">

  </Field>

  <Field

    ID="{8558B229-B874-4c84-BE54-4AD6FA82B186}"

    Type="Link"

    Name="CustomPageContact"

    DisplayName="Contact de la page personnalisée"

    StaticName="CustomPageContact"

    Group="Custom"

    RichText="True"

    RichTextMode="FullHtml">

  </Field>

</Elements>

Pour les champs de type Link et Image, il nous faut préciser les propriétés RichText et RichTextMode respectivement à "True" et "FullHtml". Ces propriétés permettent en effet de déterminer l'affichage des champs de contenu dans les modèles de page comme nous le verrons plus loin. Nous déployons à présent ces deux fichiers grâce à une feature SharePoint.

Feature.xml :

<Feature xmlns="http://schemas.microsoft.com/sharepoint/"

    Id="FF8936BA-2BFB-48b3-B0B2-99E2F4E2E51B"

    Title="Publishing Custom Page Content Type"

    Description="Defines a simple custom publishing page content type"

    Hidden="FALSE"

    Scope="Site"

    Version="1.0.0.0">

  <ElementManifests>

    <ElementManifest Location="CustomPage.Fields.xml" />

    <ElementManifest Location="CustomPage.xml" />

  </ElementManifests>

  <ActivationDependencies>

    <!-- Publishing Feature -->

    <ActivationDependency FeatureId="AEBC918D-B20F-4a11-A1DB-9ED84D79C87E" />

  </ActivationDependencies>

</Feature>

Après le déploiement et l'activation de notre feature sur un site de publication, nous retrouvons bien notre type de contenu avec les colonnes personnalisées dans les pages de paramètres du site.

Pour pouvoir créer une page de type CustomPage, il nous faut à présent créer un modèle de page pour ce type de contenu. Grâce à SharePoint Designer, nous insérons nos deux champs de contenu dans un nouveau modèle de page.

CustomPageLayout.aspx

<%@ Page="" language="C#"  Inherits="Microsoft.SharePoint.Publishing.PublishingLayoutPage,Microsoft.SharePoint.Publishing,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" %>

<%@ Register="" Tagprefix="SharePointWebControls" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register="" Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register="" Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register="" Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ContentPlaceholderID="PlaceHolderPageTitle" runat="server">

  <SharePointWebControls:FieldValue id="PageTitle" FieldName="Title" runat="server"/>

</asp:Content>

<asp:Content ContentPlaceholderID="PlaceHolderMain" runat="server">

  <PublishingWebControls:RichLinkField FieldName="CustomPageContact" runat="server" />

  <PublishingWebControls:RichImageField FieldName="CustomPageLogo" runat="server" />

</asp:Content>

Voilà, nous pouvons alors créer une nouvelle page se basant sur le modèle de page CustomPageLayout. En mode édition, tout est comme nous l'attendions, et nous pouvons éditer le contenu de nos champs de type Link et Image.

 

Mais lorsque nous publions la page, on remarque alors que les champs sont rendus sous forme de texte simple, et pas comme du code HTML... In fine, le rendu est comme si nous n'avions pas précisé les deux propriétés RichText et RichTextMode dont je vous parlais plus haut.

 

Là se trouve la subtilité des champs de type Link et Image. En effet, si la propriété RichText n'est pas prise en compte, c'est parce que la valeur de cette propriété est sensible à la casse (sic !). Il s'agit alors de remplacer dans le fichier CustomPage.Field.xml de définition des champs les lignes :

    RichText="True"

par

    RichText="TRUE"

En redéployant notre type de contenu, le rendu de la page est tel que nous le souhaitions ! 

Adrien

[Silverlight] Créer une application modulaire (Partie 2)

Dans le précédent billet, nous avons vu comment créer un module en Silverlight qui dispense un objet Control, ainsi qu'un WebService qui permet de donner une liste de tous les modules disponibles côté serveur. Nous allons voir aujourd'hui comment, à partir de la liste des modules récupérée depuis le WebService, nous allons pouvoir les télécharger au chargement de la page, puis incorporer les Control que ces derniers dispensent dans le Canvas principal.

J'ai écrit une classe Modman qui s'occupe de la première partie. Il appelle le WebService pour récupérer les URI relatives, télécharge la DLL grâce au Downloader puis créé une instance de la classe qui implémente l'interface IModule avec Activator.CreateInstance. Toutes les instances des modules sont alors stockées dans une liste accessible en lecture.

    public class Modman

    {

        private List<IModule> _modules = new List<IModule>();

        private ModmanWS.Modman _ws = new ModmanWS.Modman();

        private List<ModuleAssembly> _modAssemblyList = new List<ModuleAssembly>();

 

        public event EventHandler OnAllModulesLoaded;

        public event EventHandler OnModuleDownloadFailed;

        public event EventHandler<ModuleErrorEventArgs> OnModuleLoadError;

 

        public List<IModule> ModulesInstances

        {

            get { return _modules; }

        }

 

        public void DownloadAndLoadAllModules()

        {

            string[][] modList = _ws.GetModulesList();

 

            foreach (var mod in modList)

            {

                ModuleAssembly assembly = new ModuleAssembly(mod[0], mod[1], new Uri(mod[2], UriKind.Relative));

                _modAssemblyList.Add(assembly);

 

                Downloader loader = new Downloader();

                loader.Completed += new EventHandler(onDownloadComplete);

                loader.DownloadFailed += new ErrorEventHandler(onDownloadFailed);

 

                loader.Open("GET", assembly.Url);

                loader.Send();

            }

        }

 

        private void onDownloadComplete(object sender, EventArgs e)

        {

            ModuleAssembly assemblyInfo = retrieveAssemblyDownloaded(sender as Downloader);

 

            if (assemblyInfo != null)

            {

                loadModule(assemblyInfo);

                _modAssemblyList.Remove(assemblyInfo);

                if (_modAssemblyList.Count == 0 && this.OnAllModulesLoaded != null)

                    this.OnAllModulesLoaded(this, new EventArgs());

            }

        }

 

        private void onDownloadFailed(object sender, EventArgs e)

        {

            if (this.OnModuleDownloadFailed != null)

                this.OnModuleDownloadFailed(this, new EventArgs());

        }

 

        private ModuleAssembly retrieveAssemblyDownloaded(Downloader loader)

        {

            ModuleAssembly assemblyInfo = null;

            foreach (ModuleAssembly assembly in _modAssemblyList)

            {

                if (assembly.Url == loader.Uri)

                {

                    assemblyInfo = assembly;

                    break;

                }

            }

            return assemblyInfo;

        }

 

        private void loadModule(ModuleAssembly assemblyInfo)

        {

            try

            {

                Assembly assembly = Assembly.Load(assemblyInfo.AssemblyName);

                foreach (Type type in assembly.GetTypes())

                {

                    if (type.GetInterface(typeof(IModule).FullName, false) != null)

                    {

                        IModule instance = (IModule)Activator.CreateInstance(type);

                        _modules.Add(instance);

                    }

                }

            }

            catch (Exception ex)

            {

                if (OnModuleLoadError != null)

                    OnModuleLoadError(this, new ModuleErrorEventArgs(ex));

            }

        }

    }

Notez que j'utilise un objet ModuleAssembly, qui ne me sert en fait qu'à stocker les informations d'un module que retourne le WebService.

On y est presque ! Dans le code de ma page principale Silverlight, je m'abonne aux évènements de Modman, pour pouvoir afficher l'état du chargement des modules dans mon Canvas, et lorsque tous les modules ont été chargés, je récupère la liste des instances de modules, et j'ajoute pour chacun d'entre eux le MainControl au Canvas.

    public partial class Page : Canvas

    {

        public void Page_Loaded(object o, EventArgs e)

        {

            InitializeComponent();

            Modman mm = new Modman();

 

            mm.OnAllModulesLoaded += new EventHandler(ModMan_OnAllModulesLoaded);

            mm.OnModuleDownloadFailed += new EventHandler(ModMan_OnModuleDownloadFailed);

            mm.OnModuleLoadError += new EventHandler<ModuleErrorEventArgs>(ModMan_OnModuleLoadError);

 

            mm.DownloadAndLoadAllModules();

        }

 

        void ModMan_OnModuleLoadError(object sender, ModuleErrorEventArgs e)

        {

            this.statusText.Text += e.Exception.Message;

        }

 

        void ModMan_OnModuleDownloadFailed(object sender, EventArgs e)

        {

            this.statusText.Text += "Cannot download module";

        }

 

        void ModMan_OnAllModulesLoaded(object sender, EventArgs e)

        {

            try

            {

                Modman moduleManager = sender as Modman;

                foreach (IModule module in moduleManager.ModulesInstances)

                {

                    Control ctrl = module.MainControl;

 

                    ctrl.SetValue<double>(Canvas.LeftProperty, 50);

                    ctrl.SetValue<double>(Canvas.TopProperty, 50);

                    ctrl.Visibility = Visibility.Visible;

 

                    this.Children.Add(ctrl);

                }

            }

            catch (Exception ex)

            {

                this.statusText.Text += ex.Message;

            }

        }

    }

Et voilà, nous avons une application Silverlight complètement modulaire ! Les modules sont libres de personnaliser le contrôle qu'ils veulent mettre à disposition, tandis que l'application centrale garde la main sur la disposition de ces derniers dans la Canvas principal. De plus, il suffit de modifier le WebService en filtrant la liste des modules qu'il retourne pour apporter une logique métier aux modules activés (selon l'utilisateur par exemple).

Adrien


Classé sous

[Silverlight] Créer une application modulaire (Partie 1)

Lorsque l'on souhaite écrire une application modulaire en Silverlight, deux questions essentielles se posent :

  • Comment charger dynamiquement les DLL des modules ?
  • Comment récupérer le contrôle XAML correspondant ?

Nous allons voir dans ce billet une solution qui répond à ces deux questions.
La première chose à faire est de définir l'interface dont héritera tous nos modules :

    public interface IModule

    {

        string Name { get; }

        string Description { get; }

        string Version { get; }

        string Author { get; }

        System.Windows.Controls.Control MainControl { get; }

    }

Jusque là, rien de bien méchant, on a quatre propriétés pour les informations générales, et une cinquième qui va nous retourner un objet Control. Ce dernier hérite indirectement de Visual, et peut donc être directement ajouté aux enfants d'un objet Canvas. De plus, il a l'avantage d'implémenter une méthode InitializeFromXaml qui prend en paramètre une chaine de caractères correspondant au XAML et qui retourne un objet FrameworkElement.

Voyons à présent l'implémentation d'un tel module. In fine, un module est constitué d'une classe implémentant l'interface IModule définie ci-dessus, et d'un Control. Ainsi, nous allons ajouter à notre projet un fichier XAML et son fichier source associé, et fixer l'action de génération pour le XAML comme ressource incorporée.

Cela va permettre de récupérer le contenu du fichier XAML dans le code pour générer l'objet FrameworkElement associé au Control grâce à la méthode InitializeFromXaml dont je vous parlais plus haut.

MainControl.xaml :

<Canvas x:Name="parentCanvas"

        xmlns="http://schemas.microsoft.com/client/2007"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Width="250"

        Height="20"

        Background="White"

        >

  <TextBlock x:Name="Label" Text="Hello World !"></TextBlock>

</Canvas>

MainControl.xaml.cs :

    public class MainControl : Control

    {

        private FrameworkElement _actualControl;

 

        public MainControl()

        {

            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("ModuleSample.MainControl.xaml");

            _actualControl = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());

 

            _actualControl.Cursor = Cursors.Hand;

 

            TextBlock btnLabel = _actualControl.FindName("Label") as TextBlock;

            btnLabel.Text = "Control from Module Sample";

        }

    }

 ModuleSample.cs :

    public class ModuleSample : IModule

    {

        #region IModule Membres

 

        string IModule.Name

        {

            get { return "Module Sample"; }

        }

 

        string IModule.Description

        {

            get { return "This is just a sample of a module implementation."; }

        }

 

        string IModule.Version

        {

            get { return "1.0.0.0"; }

        }

 

        string IModule.Author

        {

            get { return "Adrien Siffermann"; }

        }

 

        Control IModule.MainControl

        {

            get { return new MainControl(); }

        }

 

        #endregion

    }

Voilà pour notre petit module. Il va s'agir maintenant de récupérer tout cela dans notre application principale Silverlight et d'ajouter le Control créé dans notre Canvas principal.

En fait, nous allons ajouter notre DLL du module dans le répertoire ClientBin de la Web Application hôte, et publier depuis cette dernière un WebService qui retourne une liste de tous les modules disponibles. En l'occurence, j'ai juste créer une liste de listes de chaines de caractères que je remplis en dur pour l'exemple, mais on peut très bien imaginer que cette méthode scanne le répertoire ClientBin, ou aille chercher en base de données une liste des modules enregistrés... On a donc pour chaque module l'information de son nom ainsi que du nom complet et de l'URI relative de son assembly.

    [WebService(Namespace = "http://tempuri.org/")]

    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

    [ToolboxItem(false)]

    [ScriptService]

    public class Modman : WebService

    {

        [WebMethod]

        [ScriptMethod]

        public List<List<string>> GetModulesList()

        {

            List<List<string>> resultList = new List<List<string>>();

 

            List<string> list = new List<string>();

            list.Add("Sample");

            list.Add("ModuleSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");

            list.Add("ClientBin/ModuleSample.dll");

            resultList.Add(list);

 

            return resultList;

        }

    }

Dans le prochain billet, nous verrons comment, à partir de la liste des modules récupérée depuis le WebService, nous allons pouvoir les télécharger au chargement de la page, puis incorporer les Control que ces derniers dispensent dans le Canvas principal.

Adrien 


Classé sous

[Présentation] Visual Studio 2008 et le framework 3.5

Une fois n'est pas coutume, ce billet ne sera pas technique... En effet, je voulais vous annoncer que nous organisons une présentation sur Visual Studio 2008 et le framework 3.5 avec mes collègues David Verrière et Arnaud Auroux.

Au programme :

  • Introduction à Visual Studio 2008
  • Les nouveautés du C# 3.0
  • Focus sur Linq
  • Introduction à Windows Presentation Foundation
  • Introduction à Silverlight

Cette présentation, destinée aux personnes qui souhaitent découvrir ces nouvelles technologies, aura lieu Jeudi 07 Février à partir de 18h à 20h dans les locaux d'Epitech, 24 rue Pasteur - 94270 Le Kremlin Bicêtre.

J'espère vous y voir nombreux !

Adrien

SPListItem.Update() jette une Exception "L'opération n'est pas valide en raison de l'état actuel de l'objet"

J'ai été confronté aujourd'hui à une exception jetée sur un bout de code pourtant bien anodin et commun : l'ajout d'un nouvel élément à une liste.
Pour être plus précis, c'est la mise à jour des champs de cet élément à l'intérieur d'un bloc SPSecurity.RunWithElevatedPrivileges qui provoquait cette exception.

Après quelques essais infructueux et quelques recherches, il s'avère que sortir la création et la mise à jour des champs de mon élément en dehors du bloc résout le problème. Ainsi, on ne fait que l'instanciation du SPSite et du SPWeb dans le bloc SPSecurity.RunWithElevatedPrivileges, comme dans l'exemple de code ci-dessous :

        public string CreateNewListItem()

        {

            string result = "";

            SPWeb rootWeb = null;

 

            SPSecurity.RunWithElevatedPrivileges(delegate()

            {

                using (SPSite site = new SPSite("http://localhost"))

                    rootWeb = site.RootWeb;

            });

 

            SPList list = rootWeb.Lists["Sample List"];

            if (list != null)

            {

                try

                {

                    rootWeb.AllowUnsafeUpdates = true;

 

                    SPListItem item = list.Items.Add();

                    item["Title"] = "Update OK";

                    item.Update();

 

                    rootWeb.AllowUnsafeUpdates = false;

                }

                catch (Exception ex)

                {

                    result = "ListItem cannot be created (" + ex.Message + ") !";

                }

            }

            else

                result = "List 'Sample List' cannot be found !";

 

            rootWeb.Dispose();

            return result;

        }

A vrai dire, je n'arrive pas à comprendre pourquoi faire la création et la mise à jour de l'élément dans le bloc SPSecurity.RunWithElevatedPrivileges ne fonctionne pas, et je trouvais ce "tourne-autour" assez particulier pour être publié...

Adrien

Un petit nouveau...

Pour mon premier post sur mon premier blog, il est de rigueur que je vous serve une rapide présentation de qui je suis, et de ce que je compte poster sur ce blog...

Je m'appelle Adrien Siffermann et j'ai 23 ans. Je suis actuellement étudiant en 4ème année à Epitech, et je travaille en alternance à Winwise dans le pôle collaboratif.
Ainsi, même si ma formation est assez hétéroclite en matière de technologies, mon intérêt se porte plus particulièrement sur SharePoint et l'Office System. Dans le cadre de mes études, je mène également un projet de WebOS en Silverlight.

Vous l'aurez donc compris, sur ce blog je vous parlerai essentiellement de Windows SharePoint Service 3.0, de Microsoft Office SharePoint Server 2007, mais aussi de Silverlight et de .Net en général...

Je remercie au passage Philippe et Florent qui m'ont orienté vers Codes Sources pour mon blog, et Cyril qui m'a permit de l'ouvrir.


Les 10 derniers blogs postés

- Office 365: Script PowerShell pour assigner des droits Full Control à un groupe défini par Blog Technique de Romelard Fabrice le 04-30-2017, 09:22

- SharePoint 20XX: Script PowerShell pour exporter en CSV toutes les listes d’une ferme pour auditer le contenu avant migration par Blog Technique de Romelard Fabrice le 03-28-2017, 17:53

- Les pièges de l’installation de Visual Studio 2017 par Blog de Jérémy Jeanson le 03-24-2017, 13:05

- UWP or not UWP sur Visual Studio 2015 ? par Blog de Jérémy Jeanson le 03-08-2017, 19:12

- Désinstallation de .net Core RC1 Update 1 ou SDK de Core 1 Preview 2 par Blog de Jérémy Jeanson le 03-07-2017, 19:29

- Office 365: Ajouter un utilisateur ou groupe dans la liste des Site collection Administrator d’un site SharePoint Online via PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-24-2017, 18:52

- Office 365: Comment créer une document library qui utilise les ContentTypeHub avec PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-22-2017, 17:06

- [TFS] Supprimer en masse les dépendances à SQL Enterprise ou Developer avant de procéder à une migration par Blog de Jérémy Jeanson le 02-20-2017, 20:30

- Office 365: Attention au volume utilisé par les fichiers de Thèmes de SharePoint Online par Blog Technique de Romelard Fabrice le 02-07-2017, 18:19

- [SCVMM] Supprimer une machine bloquée par Blog de Jérémy Jeanson le 01-31-2017, 21:22