![]() |
|
Spaces home Gael Duhamel - SharePoin...PhotosProfileFriends | ![]() |
|
|
June 25 [SharePoint] Filtrer et récupérer des éléments d’une liste de configuration grâce à Linq et les méthodes d’extensions - UPDATE 25-06-2008-- UPDATE -- J'ai changé ma méthode pour récupérer directement une valeur dans ma liste de configuration. Je faisais un query qui me retournais une collection que je filtrais ensuite pour récupérer ma valeur. Alors qu'il suffisait simplement de le faire directement dans mon query -- UPDATE -- Dans mes applications SharePoint, j’utilise très souvent une liste ou je stocke l’ensemble de mes éléments de configuration comme par exemple :
Le gros avantage est que le webmaster du site peut facilement (à ses risques et périls tout de même La structure de ma liste est toute simple :
J’avais donc implémenté une classe qui me permettait de récupérer ces valeurs via un CAML query sur ma liste. Mais suite à un article posté par Philippe Sentenac sur les méthodes d’extensions, j’ai décidé de refaire cette classe en utilisant ces extensions et les nombreuses autres classes generics du framework .Net. Pour commencer, j’ai étendu l’objet SPWeb pour accéder à notre Liste de configuration (dans mon cas, j’ai aussi besoin d’un paramètre « Category » pour me retourner l’ensemble des valeurs car je ne souhaite pas faire une requête pour chaque entrée). Cette méthode, me retourne une interface générique de type ILookup < TKey, TElement>. Cette interface, va me permettre ensuite de récupérer très facilement une entrée de ma configuration : public static ILookup<String, String> ConfigurationList(this SPWeb spWeb, String Category) { SPList spListConfiguration; ILookup<String, String> configurationEntries = null; // La liste n'est pas accessible aux end users. // On doit donc s'y connecter sur le compte admin pour y accéder // Optionnel, si vous avez décidé de mettre la liste en lecture pour tout vos utilisateurs SPSecurity.RunWithElevatedPrivileges(delegate() { using (SPSite mySite = new SPSite(spWeb.Site.ID)) { using (SPWeb myWeb = mySite.OpenWeb(spWeb.ID)) { Je récupère ma liste « Configuration » grâce à la méthode d’extension TryGet de Philippe : if (myWeb.Lists.TryGet("Configuration", out spListConfiguration)) { J’exécute ensuite mon query pour récupérer mes éléments : SPQuery spQuery = new SPQuery(); spQuery.Query = String.Format(<Where><Eq><FieldRef Name='Category' /><Value Type='Choice'>{0}</Value></Eq></Where>", Category); Et je stock les résultats dans un document XML Linq (XDocument) : XDocument xml = XDocument.Parse(spListConfiguration.GetItems(spQuery).Xml); Je définie mon namespace pour me simplifier la lecture du code : XNamespace z = "#RowsetSchema";Je renvoie tout ce flux dans mon interface générique en lui spécifiant :
configurationEntries = (from xmlItems in xml.Descendants(z + "row").ToArray() select new ConfigurationEntry { Key = (String)xmlItems.Attribute("ows_Title"), Value = (String)xmlItems.Attribute("ows_Value") }) .ToLookup(key => key.Key, element => element.Value, EqualityComparer<String>.Default); }); } onfigurationEntries; } J’utile cette classe pour stocker ma clé ainsi que la valeur associée dans mon query Linq : public class ConfigurationEntry { public String Key; public String Value; } Ensuite, il ne me reste plus qu’a écrire la classe qui va me rechercher mon élément dans ma collection (on peut ici choisir de lui donner un objet SPWeb ou d’utiliser le context) public sealed class Configuration { private ILookup<String, String> configurationValues; public Configuration(SPWeb spWeb, String category) { if (spWeb == null) throw new ArgumentNullException("A SPWeb object cannot be null"); if (String.IsNullOrEmpty(category)) throw new ArgumentNullException("A category cannot be empty"); configurationValues = spWeb.ConfigurationList(category); } public Configuration(String category) { if (SPContext.Current == null) throw new ArgumentNullException("SPContext cannot be null. Please ensure that you're running a SharePoint application"); if (String.IsNullOrEmpty(category)) throw new ArgumentNullException("A category cannot be empty"); configurationValues = SPContext.Current.Web.ConfigurationList(category); } public String GetEntry(String key) { return Convert.ToString(configurationValues[key].FirstOrDefault()); } } Notez l’utilisation du générique FirstOrDefault, qui nous permet ici de nous retourner un String.Empty s’il n’y a pas de valeur à retourner. Ensuite, pour l’appel, c’est très simple. Instanciez un objet Configuration et utilisez la méthode GetEntry pour récupérer vos valeurs: Configuration configValues = new Configuration(spWeb,"SSRS"); Console.WriteLine("SSRS URL:" + configValues.GetEntry("SSRSUrl")); Console.WriteLine("SSRS Path:" + configValues.GetEntry("SSRSPath")); Console.WriteLine("SSRS WebPartPage:" + configValues.GetEntry("SSRSWebPartPage")); On peut aussi ajouter une variante à notre méthode d’extension ConfigurationList pour récupérer directement la valeur d’une clé dans notre liste :
La récupération de votre valeur se faisant comme ceci: Console.WriteLine("SSRS Url:" + spWeb.ConfigurationList("SSRS", "SSRSURL")); Comme promis, voici ici un exemple de classe qui vous permet de comparer 2 Strings sans distinction de la casse : public class StringNoCaseSensitive : IEqualityComparer<String> { public Boolean Equals(String val1, String val2) { return (val1.ToLower() == val2.ToLower()); } public int GetHashCode(string obj) { return obj.ToLower().GetHashCode(); } } Dans l’appel du ILookup,remplacer EqualityComparer<String>.Default par new StringNoCaseSensitive() et le tour est joué... Tags: June 10 [Telerik] Petit bug sur le control RadHtmlFieldDans le cadre d’un de mes projets de publication web sous MOSS, j’utilise très souvent le RAD editor pour SharePoint de Telerik. Il est vraiment très performant et d’une grande facilité d’utilisation. Mais j’ai détecté un petit bug dans son utilisation lorsque vous souhaitez valider vos pages pour éviter les attaques par script injection (XSS) via l’attribut validateRequest=True. <pages enableSessionState="true" enableViewState="true" enableViewStateMac="true" validateRequest="true" pageParserFilterType="Microsoft.SharePoint.ApplicationRuntime.SPPageParserFilter, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" asyncTimeout="7"> Il y a apparemment un bug sur certaines valeurs attribuées aux Id de ce control. Dans mon cas, j’avais ce control sur ma page : <Telerik:RadHtmlField ID="Request" FieldName="Request" runat="server"/> Ce qui provoquait une erreur de type: Un rapide coup d’œil dans l’event viewer me donne un message d’erreur un peu plus explicite : Ce message d’erreur est très explicite, il nous indique que le framework n’a pu trouver la methode validateInput et donc valider l’intégrité d’un control (du au fait du validateRequest à vrai). En effet, dès que je mettais mon attribut validateRequest à faux, ma page s’affichait correctement. Intrigué et surpris par le fait qu’un control ne puisse satisfaire à cette règle élémentaire du framework, j’ai tente de trouver sur le net (et en particulier le forum de telerik) quelques réponses. Mais absolument rien à ce sujet. N’étant pas le seul au monde à utiliser ce control et n’ayant rien trouvé dans mes investigations googleienne, je me suis dit que ca devait venir forcement d’une petite bricole sur ma page. J’ai donc vérifié l’appel à l’assembly dans mes attributs de pages (version, publickeytoken,…) ainsi que quelques propriétés du control (IsValid par exemple). Mais toujours rien… Et en relisant bien mon control, j’ai vu que la valeur de mon id était « Request ». Dans le doute et sans autres solutions, j’ai supprimé cet id en étant sur que ca n’avait aucun impact… et bien si. Détrompez-vous ! Je ne sais pas pour quelle raison, mais chez Telerik si votre id à pour valeur Request et que vous configuré votre application web pour éviter les XSS et bien ca plante… Ce n’est pas un gros problème en soit car il suffit juste de changer la valeur, mais c’est quand même surprenant… Tags: June 06 [SharePoint] Mettre à jour son web.config par l'intermediaire d'un fichier XML de configuration - UPDATED ON 06-JUNE-2008MISE A JOUR DU 06 JUIN 2008 <add path="configuration/system.web" id="{45C74BC1-DBA5-489f-A6E9-6932C25F1D97}"> <xhtmlConformance mode="Strict" /> </add> Ceci évitant la recréation multiple de vos actions. Grand merci à Gaetan Bouveret pour cet éclaircissement. Comme quoi la communauté SharePoint est plus forte que le SDK ------------------------------- Je reprend ici un post de Gaetan Bouveret, au sujet de la mise à jour du fichier web.config sans ligne de code. Je ne connaissais pas ce système de mise à jour via un flux xml. J'utilisais pour ce genre de déploiement le modèle objet et son SPWebConfigModification. Intéressé par son post, j'ai donc essayé de le mettre en place sur un de mes projets. J'ai donc ajouté dans un fichier xml (déployé par ma feature dans le répertoire config du 12) cette ligne: <add path="configuration/system.web"> <xhtmlConformance mode="Strict" /> </add> Pour déployer ce fichier dans votre web.config, il vous faut exécuter la commande : stsadm -o copyappbincontent Lors de la toute première commande tout marche parfaitement bien, mon noeud est bien ajouté là ou je le voulais. <system.web> .... <xhtmlConformance mode="Strict" /> </system.web> Le problème de cette commande add est qu'elle ne vérifie pas la présence du noeud à ajouter. Ce qui fais que si vous exécutez une nouvelle fois la commande stsadm -o copyappbincontent, ce noeud est ajouté une deuxième fois. Et dans le cas précis de cet exemple, cela fais planter mon application web car le noeud xhtmlConformance doit être unique. Pour remédier à cela, j'ai simplement ajouté une commande remove juste avant la commande add: <remove path="configuration/system.web/xhtmlConformance" /> Pour info, la valeur de path est une fonction xpath, vous pouvez donc allez chercher l'élément à supprimer même si les noeuds fils ont le même nom. <remove path="configuration/system.web/httpModules/add[@name='Session']" /> <add path="configuration/system.web/httpModules"> <add name="Session" type="System.Web.SessionState.SessionStateModule"/> </add> ps: Attention pour les fermes, le SDK stipule bien que cette commande doit être exécutée sur chaque serveur de la ferme. Tags: May 31 [SharePoint] SPBuiltInFieldId ou la classe qui vous rend serviceDans le cadre d'un développement d'une feature, je suis tombé complètement par hasard sur une classe des plus sympathique! Il s'agit de la classe SPBuiltInFieldId. Cette classe vous permet de récupérer les Ids de tous les champs de base de SharePoint. Jugez-en plutot par ce petit screenshot: Par contre, chose des plus étranges, cette classe n'est pas du tout documenté Tags: May 27 [SharePoint] Custom Field - Variation labels sur CodePlexJe viens de poster sur CodePlex un nouveau projet de type champ personnalisé. Ce champ permet d'afficher la liste des « variation labels » définit pour votre site web. Bien qu'il soit très facile de faire cela via le model objet de SharePoint (voir l'exemple à la fin de ce billet), il est parfois utile d'avoir un champ personnalisé car celui-ci vous laisse plus de souplesse quant à son utilisation (novice ou non). Pour commencer, vous devez installer la solution et activer la feature (un guide d'installation et configuration est disponible dans l'onglet release du projet)
Une fois cela fait, vous pouvez maintenant ajouter votre nouveau type de colonne dans votre liste :
En mode ajout/modification, une liste déroulante vous permet de sélectionner une des variations installées sur votre serveur de publication MOSS.
Ci-dessous, la liste des variations installées (comme preuve de ma bonne fois
Pour ajouter ce type de colonne à une liste via un peu de lignes de code, vous pouvez faire comme ceci: String languageId = spList.Fields.AddFieldAsXml(String.Format("<Field Type=\"VariationLabelsFieldType\" DisplayName=\"{0}\" Name=\"{0}\" Required=\"TRUE\" />", SPGael.SharedConstants.LANGUAGECOLUMNNAME)); Pour ajouter un lookup colonne basé sur votre liste de variation, vous pouvez faire comme ceci : using (SPSite spSite = new SPSite(SPContext.Current.Site.ID)) Rendez-vous donc sur http://www.codeplex.com/VariationLabelsCsFld pour télécharger, tester et me remonter les éventuels bogues, évolutions, ou autres… Tags: May 21 [Windows Live] Bon anniversaireCela fait maintenant 11 ans que je suis sur Hotmail... Une éternité dans notre monde informatique ou tout évolue tellement vite. Rendez-vous compte, à mes débuts, les modems atteignaient la vitesse stupéfiante de 14400kbs, le javascript en était à ses débuts et la bataille faisait rage entre Internet Explorer 4.0 et Netscape Communicator... Tags: May 18 [SharePoint] MOSS ROBOTS meta tagJe suis en train de travailler sur un site internet sous MOSS et j’ai pas mal de travail à faire sur le rendu d’une page MOSS, qui n’est absolument pas accessible. Avant de commencer un projet (aussi bien en développement qu’en conseil), j’aime toujours relire les guidances ou autres proof of concepts, afin d’être certains de choisir la bonne solution. Cela me prend souvent beaucoup de temps, mais je pense qu’en prenant soin de lire ce que les autres acteurs on pu faire, on gagne un temps précieux à ne pas commettre les mêmes erreurs. Donc pour en revenir au sujet, je lisais ce qu’il se faisait en termes de modification du rendu de control SharePoint. Ce que j’ai constaté, c’est qu’en majorité, la plupart des articles propose de réécrire son propre custom control afin d’être sur de bien maitriser le rendu. Dans un de ses excellents articles, Stefan Goßner (je vous recommande la lecture de son blog, qui est une véritable mine d’or) proposait de changer le rendu du tag « RobotsMetaTag » par un custom control : Adjusting the MOSS ROBOTS meta tag for 3rd party search engines. Ma question a été, pourquoi ne pas utiliser à la place un control adapter ? Cette solution me paraissait plus « propre », car l’on continue à se baser sur le contrôle de base. Stefan a eu la gentillesse de me répondre et d’approuver ma solution Tags: [SharePoint] Ajouter une langue dans la liste des variations d’un site de publication MOSS (maj le 19 mai 2008)Comme vous le savez, toutes les langues ne possèdent pas encore de localisation propre. Ces langues sont souvent celles dite « minoritaire » comme le sont le flamand, le catalan, le gaélique, le basque, etc… Microsoft, prend toutefois soin à en répertorier le maximum et de créer pour ces langues un LocaleID (pour en avoir une liste : http://www.microsoft.com/globaldev/reference/lcid-all.mspx) en attendant de leur offrir une vrai « culture » (vous pouvez en trouver une liste ici : http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.aspx) et donc le module linguistique pour SharePoint (les langues disponibles sont ici: http://www.microsoft.com/downloads/details.aspx?displaylang=fr&FamilyID=36ee1bf0-652c-4e38-b247-f29b3eefa048) qui vous permettra de voir votre SharePoint « parler » flamand Dans mon cas « client », je dois réaliser un site internet public Irlandais basé sur MOSS. Comme vous ne le savez peut-être pas, mais ici en Irlande, tout site public doit être disponible en gaélique et en anglais. Bien qu’il existe une culture pour l’Irlande qui est « En-Ie » (et un LCID 6153), celle-ci ne fait que « gérer » du formatage comme pour le calendrier (jj/mm/aaaa) ou la devise (€) basé sur la langue anglaise. Donc, comme vu un peu plus haut, le gaélique ne possède pas de « culture » (au sens informatique du terme bien sur ^^) et donc encore moins de module linguistique. Ce qui fait donc, qu’il n’est pas « possible » de le trouver par défaut dans la liste des variations disponibles. Pour y remédier, il vous suffit:
N’oubliez pas le IISRESET pour prendre en compte votre modification et constater le résultat : Attention: si votre langue ne possède pas sa propre ressource, MOSS ne pourra pas vous rediriger par défaut sur celle-ci. En effet, la redirection ce base sur le HTTP_ACCEPT_LANGUAGE. Tags: May 15 [Linq] Grouper les items renvoyés par un query sur une listeAuparavant, lorsque je voulais regrouper mes valeurs issues d’un query sur une liste. Je devais faire tout un tas de manipulations, comme : // Ma liste SPList spList = null; try { spList = SPContext.Current.Site.RootWeb.Lists["MaListe"]; } catch { } // si null, exception if (spList == null) throw new NullReferenceException("La liste n'existe pas"); // Recuperation du nom interne de la colonne if (!spList.Fields.ContainsField("Categorie")) throw new NullReferenceException("La colonne categorie n'existe pas"); String categoryColumnName = spList.Fields["Categorie"].InternalName; // Recuperation des categories distinctes triees SPQuery spQuery = new SPQuery(); spQuery.Query = String.Format("<OrderBy><FieldRef Name='{0}' /></OrderBy>", categoryColumnName); // Chargement du Xml XDocument xml = XDocument.Parse(spList.GetItems(spQuery).Xml); // Ajout du namesplace pour recuperer mes valeurs XNamespace z = "#RowsetSchema"; // IMPORTANT: n'oubliez pas de typer votre regroupement var groupedCategories = from allcategories in xml.Descendants(z + "row") group allcategories by (string)allcategories.Attribute("ows_Categorie") into distinctCategories select distinctCategories; // Ensuite il nous suffit de recuperer le resultat // Ici en l'ajoutant a un control de type Checkbox list foreach (var cat in groupedCategories) { this.cblCategories.Items.Add(new ListItem((string)cat.Key)); } Simple non? Pour allez un peu plus loin, ou si vous débutez comme moi, allez jeter un oeil sur le blog de Wriju. Il y a d'excellentes choses à lire. |