mardi 19 octobre 2010

Sharepoint – Création d’une liste avec un content type spécifique (par code)

Voici le besoin :
Je dois créer une bibliothèque d’images spécifique, donc avec un type de contenu personnalisé.
La solution que j’ai choisi, c’est de faire 2 features:
  • Une pour le déploiement de tous les content types et des webparts avec un scope Site
  • Une autre pour la création de ma bibliothèque dans un sous-site choisi donc scope web

Je ne vais pas présenter le détail de la première feature.
Ce qu’il faut savoir, c’est qu’elle déploie des types de contenus.
Je ne montrerai pas non plus comment créer des solutions wsp pour le déploiement, je pars du principe que tout déploiement dans SP passe par ce biais.
Ma seconde feature, le fichier Feature.xml :
<?xml version="1.0" encoding="utf-8"?>
<Feature  Id="4d60eeca-55a8-4f6a-a49a-b036f616c1cc"
Title="Mon titre de feature"
Description="La description"
Version="1.0.0.0"
Hidden="FALSE"
Scope="Web"
DefaultResourceFile="core"
ReceiverAssembly="MonAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0a51357f54252cab"
ReceiverClass="MonAssembly.MonReceiver"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ActivationDependencies>
<ActivationDependency FeatureId="510155f1-4eaa-4b63-845f-03718f48fdbf"/>
</ActivationDependencies>
<ElementManifests>  
</ElementManifests>
</Feature>


On remarque 2 choses dans le Feature.xml:

  • On a une ActivationDependency, ici elle permet de vérifier que la Feature 1 est bien activée, sinon, on ne peut activer la feature 2.
    Logique, sinon, la création d’une liste héritant d’un content type inexistant posera un problème.
  • On fait appel à un SPReceiver, en effet, c’est lors de l’activation de la feature que l’on va créer la liste.

Le Receiver :MonReceiver.cs
public class MonReceiver : SPFeatureReceiver
{
private const String LIST_NAME = "maListe";
private const String LIST_DESC = "La desc";
private const String CONTENTTYPE_NAME = "MonContentType";

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
if (properties.Feature.Parent is SPWeb)
{
SPWeb web = (SPWeb)properties.Feature.Parent;
SPSite site = web.Site;

try { SPList list = web.Lists[LIST_NAME]; }
catch //La liste n'existe pas, on va la creer
{
try
{
//Création de la list
Guid listId = web.Lists.Add(LIST_NAME, LIST_DESC, SPListTemplateType.PictureLibrary);
//on selectionne la lise ajoutée
SPList list = web.Lists[listId];
list.ContentTypesEnabled = true;
list.OnQuickLaunch = false;
list.EnableFolderCreation = false;
list.Update(); //Mise à jour de la liste avec les options

//Ajout du type de contenu souhaité pour la liste
SPContentType ct = web.AvailableContentTypes[CONTENTTYPE_NAME];
list.ContentTypes.Add(ct);

//On sélectionne le contentype précédent comme  l'unique CT de la liste
SPContentType[] ctToDisplay =  new SPContentType[1];
for (int i = 0; i < list.RootFolder.ContentTypeOrder.Count; i++)
{
if (list.RootFolder.ContentTypeOrder[i].Name == CONTENTTYPE_NAME)
{
ctToDisplay[0] = list.RootFolder.ContentTypeOrder[i];
}

}
list.RootFolder.UniqueContentTypeOrder = ctToDisplay;
list.RootFolder.Update();

}
catch (Exception ex) { throw ex; }
}
}
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
if (properties.Feature.Parent is SPWeb)
{
SPWeb web = (SPWeb)properties.Feature.Parent;
SPSite site = web.Site;

try
{
SPList list = web.Lists[LIST_NAME];
if (list.ItemCount < 1)
{
list.Delete();
}
}
catch {}
}
}

public override void FeatureInstalled(SPFeatureReceiverProperties properties){ }

public override void FeatureUninstalling(SPFeatureReceiverProperties properties) { }
}


Ce que fait l’activation de la feature :

  • Elle crée la liste si celle-ci n’existe pas (de type PictureLibrary dans notre cas)
  • Elle ajoute le type de contenu souhaité pour la liste
  • Ensuite, on redéfini le type de contenu par défaut de la liste, via le RootFolder.UniqueContentTypeOrder
    Il s’agit d’une collection de SPContentType qui permet de définir l’ordre et le type de contenu par défaut du bouton “Nouveau” de la liste.

Lors de la désactivation de la feature, si la liste est vide, on la supprime.

Voilà, ça n’est pas plus compliqué.

vendredi 15 octobre 2010

Sharepoint – Webpart et ValidationGroup en WCM

Si vous déployez une webpart, contenant un formulaire avec validation, dans une page de publication, au moment de l’archivage de la page, vous obtenez le message suivant :

Cette page contient du contenu ou une mise en forme non valide. Vous trouverez plus d'informations dans les sections concernées.
Options :  Quitter sans enregistrer

La solution est de remplir tous les champs du formulaire pour satisfaire la validation : Bof, pas très productif et pour expliquer ça aux collaborateurs du website !!

L’idée, pour éviter ça, est de ne pas afficher le formulaire lorsque la page est en mode “Edition” :

WebPartManager wp = WebPartManager.GetCurrentWebPartManager(this.Page);
if (wp.DisplayMode == WebPartManager.BrowseDisplayMode)
{
Control control = this.Page.LoadControl(ASCX_PATH);
Controls.Add(control);
}
else
{
Controls.Add(new LiteralControl("Mode édition - Pas d'affichage du formulaire"));
}

Donc en mettant le code ci-dessus dans le “CreateChildControls” de la WebPart, en mode édition, on affiche un message au lieu du formulaire.

Après archivage, on visualise le formulaire.


Donc plus de problème de validation.

Sharepoint – Obtenir le SMTP Host de la config en code-behind

Pour obtenir le SMTP Host configuré dans notre ferme :

if (SPContext.Current.Site.WebApplication.OutboundMailServiceInstance != null)
{
//Obtient le SMTP host de "Outgoing e-mail settings"
smtp.Host = SPContext.Current.Site.WebApplication.OutboundMailServiceInstance.Parent.Name;
}

jeudi 14 octobre 2010

MOSS 2007 – Reverse proxy, site public et MOSS

Voici la configuration :

  • Une webapp étendue MOSS en WCM public sur le port 82 du IIS
  • Une url public externe www.monsite.fr
  • Une url interne intra.monsite.fr:82
  • une reverse proxy pour la publication
  • Les “acces mappings” de MOSS configurés

Jusqu’ici tout va bien.

La page à afficher contient une “Content Query Webpart”, en interne, pas de problème tout fonctionne.

En externe, ça se complique, la webpart ne fonctionne pas.

1ére solution:

La CQWP ne fonctionne pas avec un accès anonyme car le XSLT utilise la variable “SafeLinkUrl”, donc la modification de ItemStyle.xsl paraissait logique.
Voici un exemple de modifications clair : Content Query Web Part (CQWP) with Anonymous Access

Pour autant, ça ne fonctionne pas.

2ème solution

Une petite analyse de la response avec Fiddler et je m’aperçoit que la webpart à comme url : www.monsite.fr:82

Donc en ajoutant un “access mapping” sur la zone correspondante de type
Url interne : www.monsite.fr:82
Url public : www.monsite.fr

La CQWP fonctione.

Infos : Où se trouve les Access Mappings :

  • Ouvrez la console d’administration de Sharepoint
  • Opérations image

 

 

 

 

  • Rubrique Configuration globale
    image
  • Mappages des accès de substitution
  • image

Linq - Juste un exemple de concaténation de texte avec Aggregate

Pour mes problèmes de mémoire, voici un petit exemple de concaténation de texte avec Linq to Object et la méthode Aggregate :

var checkedValues = 
MaCheckBoxList.Items.Cast<ListItem>()
.Where(c => c.Selected)
.Select(c => c.Value);

//On vérifie qu'il y a bien des valeurs sélectionnées
String maString = MaCheckBoxList.Count() > 0
? checkedValue.Aggregate((c, next) => c + ", " + next)
: String.Empty;