dimanche 15 août 2010

DNN : Comment modifier le mot de passe d'un utilisateur

Dans un précédent billet je décrivais comment récupérer le mot de passe d'un utilisateur, et bien là j'en suis à vouloir le modifier. Là encore il y a une petite astuce.

Il faut dans un premier temps récupérer les informations de l'utilisateur sur lequel on souhaite travailler :

Imports Imports DotNetNuke.Entities
...
dim oUserInfo as Users.UserInfo 
'Récupération des informations de l'utilisateur à partir du "user name"
oUserInfo = Users.UserController.GetUserByName(iPortalId, sUserName)
'Récupération des informations relatives au membership de cet utilisateur
Users.UserController.GetUserMembership(oUserInfo)

On note ici la récupération du "membership" de l'utilisateur. Ces informations sont nécessaires car nous allons devoir récupérer l'ancien mot de passe et cela n'est pas possible sans la propriété "PasswordAnswer" :

'Récupération de l'ancien mot de passe 
dim sOldPassword as string = Users.UserController.GetPassword(oUserInfo, oUserInfo.Membership.PasswordAnswer)

Il ne reste plus qu'à modifier l'ancien mot de passe ainsi :

'Modification du mot de passe
Users.UserController.ChangePassword(oUserInfo, sOldPassword, sNewPassword)

jeudi 5 août 2010

VS 2010 : Comment se placer sur le fichier en cours d'édition

Lorsque je suis passé sous Visual Studio 2010, j'ai rapidement été assez agacé par la perte d'une petite fonctionnalité qui était fort pratique : le fait qu'un fichier édité soit automatiquement sélectionné dans l'explorateur de solution.

Cette fonctionnalité est pratique notamment lorsque l'on a un nombre conséquent de fichiers en cours d'édition et que l'on souhaite accéder au fichier .aspx du fichier codebehind que l'on est train d'éditer.

Il est heureusement possible d'activer cette fonctionnalité à partir des options de VS, vous la trouverez en passant par le menu "Options", puis la section "Projets et solutions", puis "Général" et là vous avez une petite case à cocher "Suivre un élément actif dans l'explorateur de solutions"

samedi 31 juillet 2010

DNN : Comment modifier le "username" d'un utilisateur

DNN vous offre les méthodes pour modifier toutes les propriétés d'un utilisateur sauf une : le username. Pour se faire vous êtes donc condamnés à modifier directement en base cette propriété mais attention, celle-ci ne se trouve pas que dans une table.

Voici les tables et les champs devant être impactées pour que la modification de la propriété soit efficace :

  • Table "Users", champ "UserName"
  • Table "aspnet_Users", champ "UserName" ET "LoweredUserName"
Pour être un peu plus complet, voici une petite fonction mettant à jour ces différentes tables :

       Public Sub UpdateUserName(ByVal NewUserName As String, ByVal OldUserName As String)
            Dim oConnexion As SqlConnection = New SqlConnection(_connectionString)
            oConnexion.Open()
            Dim sSQL As String
            sSQL = "update users set username=@NewUserName where username=@OldUserName"
            Dim oCommand As SqlCommand = New SqlCommand(sSQL, oConnexion)
            oCommand.Parameters.AddWithValue("@NewUserName", NewUserName)
            oCommand.Parameters.AddWithValue("@OldUserName", OldUserName)
            oCommand.ExecuteNonQuery()
            sSQL = "update aspnet_Users set username=@NewUserName ,loweredusername=@NewUserName where username=@OldUserName"
            oCommand = New SqlCommand(sSQL, oConnexion)
            oCommand.Parameters.AddWithValue("@NewUserName", NewUserName)
            oCommand.Parameters.AddWithValue("@OldUserName", OldUserName)
            oCommand.ExecuteNonQuery()
            oConnexion.Close()
        End Sub

Vous remarquerez la variable "_connectionString", vous penserez bien à la valoriser et puis vérifiez que votre table "User" n'est pas préfixée (cela est possible si la propriété "objectQualifier" dans votre web.config a été valorisé à l'installation de votre DNN). Le but de cette fonction n'est pas de couvrir tous les besoins mais juste de vous donner une piste.

mardi 20 juillet 2010

SQL Server : Ramener sur une seule ligne les informations provenant de plusieurs lignes

J'avais déjà été confronter à ce petit soucis il y a quelque mois, j'ai dû me replonger dans mes souvenirs pour ressortir la solution à ce problème.

Voici le besoin (ce n'est pas mon besoin, c'est un cas d'école) : je souhaite afficher sur une seule ligne le nom des 10 premiers utilisateurs présent dans ma base. Comment faire cela en une seule requête SQL sans rien de plus ?

Petite précision qui a son importance, je suis sous SQL Server Express 2008, cette syntaxe fonctionne pour la version 2005 mais antérieure à cela... rien n'est moins sûr.

Voici la syntaxe "magique" :

SELECT TOP 10 UserName + ' ' FROM Users FOR XML PATH('')

Et nous voilà donc avec les noms des 10 premiers utilisateurs sur une seule ligne (et une seule colonne) séparés par un espace (attention un espace se trouve à la fin de la ligne).

lundi 19 juillet 2010

Problème d'affichage de texte lors d'un upload d'image dans le module "Text/HTML" (RadEditor) de DNN

Voici un problème auquel j'ai été confronté et pour lequel je n'ai trouvé aucune information sur le net. Deux possibilités pouvant expliquer cela :
  • je n'ai pas utilisé les bons termes de recherche dans google,
  • personne n'a le problème.
Aucun où quelqu'un d'autre tomberait sur ce soucis, en voici les symptômes, lorsque j'édite le contenu d'un module "Text/HTML", j'utilise le composant RadEditor de Telerik. Il se trouve que toutes les fenêtres filles que j'ouvre à partir de ce composant sont sans texte. 

Exemple : La fenêtre permettant d'insérer une image


Aucun texte n'est visible sur les onglets, les boutons,...

Pour remédier à cela il faut placer dans le dossier "Providers\HtmlEditorProviders\Telerik\App_LocalResources" de votre instance DNN les fichiers ressources français de Telerik, en voici la liste :
  • RadEditor.Dialogs.fr-FR.resx
  • RadEditor.Main.fr-FR.resx
  • RadEditor.Modules.fr-FR.resx
  • RadEditor.Tools.fr-FR.resx
Voici le même fenêtre que ci-dessus après avoir rajouté les fichiers listés :



Suite aux différents commentaires échangés sur le sujet, voici un petit rajout : pour que tout fonctionne correctement il faut aussi modifier le fichier "SharedResources.fr-FR.resx" se trouvant dans le répertoire "App_GlobalResources" de DNN. La clef à modifier est "PortalRoot.Text", il faut lui affecter la valeur d'origine qui est "Portal Root". Cette modification est essentielle.

jeudi 15 juillet 2010

Utilisation d'un RadTreeview dans un RadComboBox

Dans le cadre d'un projet sous DNN, j'ai dû utiliser les contrôles Telerik, notemment un RadComboBox contenant un RadTreeview, celui-ci étant chargé dynamiquement. Je me suis trouvé confronté au problème suivant : à la sélection d'un node du treeview, je souhaitais afficher dans la combobox le libellé de l'élément sélectionné.

On trouve sur le site de Telerik (éditeur de ces contrôles) pas mal d'exemples de ce type, la réponse passe par du code javascript exécuté lors du click sur le node, voici un exemple de code à mettre en place :

function nodeClicking(sender, args)
{
var comboBox = $find("RadComboBox1");

var node = args.get_node();

comboBox.set_text(node.get_text());

comboBox.hideDropDown();
}


Comme vous pouvez le constater dans ce script, le nom de la combobox est connu. Mais dans le cas d'une utilisation sous DNN, le problème est légèrement différent.

En effet, nous pouvons trouvez plusieurs modules utilisant ce fonctionnement sur une même page et dans ce cas, le nom de la combobox change bien entendu. Il faut donc procéder autrement. Nous n'utiliserons plus la fonction $find, il faut ici récupérer la combobox parente du treeview à l'origine de l'appel à la fonction nodeClicking. Voilà ce que nous pourrions avoir :


function nodeClicking(sender, args)
{
//Récupération du client Id du treeview à l'origine de l'appel
var treeviewId=sender.get_id();
//Ce Client Id contient le nom de la combobox parente
//On récupère sa position dans le client Id du treeview (ici l'Id serveur de la combobox est "RadComboBox1")
var index=treeviewId.lastIndexOf(""RadComboBox1"");
//Récupération du client Id de la combobox parente, le 12 correspond à la longueur de l'Id serveur de la combobox
var comboBoxId=treeviewId.substring(0,index+12);

//Récupération de l'instance de la combobox
var comboBox = $find(ClientID);

var node = args.get_node();

comboBox.set_text(node.get_text());

comboBox.hideDropDown();
}



Voici les deux liens qui m'ont permis de rédiger ce billet :
http://www.telerik.com/community/forums/aspnet-ajax/treeview/radcombobox-radtreeview-loadondemand.aspx
http://www.telerik.com/community/forums/aspnet-ajax/combobox/access-radtreeview-parent-control.aspx

Visual SourceSafe et Visual Studio 2010

Petit problème auquel je suis confronté aujourd'hui : comment intégrer une application dans Visual SourceSafe à partir de Visual Studio 2010. De prime abord, c'est TFS (Team Foundation Server) qui est utilisé par défaut, cela veut-il dire que SourceSafe n'est plus supporté ?

Du tout, il suffit d'activer la bonne option, la difficulté est là : il faut juste trouver la bonne option. Pour cela, passez par le menu "Outils / Options" et là cochez la case "Afficher tous les paramètres" se trouvant en bas à gauche de l'écran. Vous verrez alors apparaître dans la liste déroulante l'option "Contrôle de code source", placez vous dessus et sélectionnez le plug-in "Microsoft Visual SourceSafe".

A partir de là vous retrouverez le fonctionnement classique de SourceSafe.

dimanche 11 juillet 2010

Récupération du mot de passe d'un utilisateur sous DNN

Ce matin j'avais besoin d'exporter les informations relatives aux utilisateurs présents dans mon portail DNN. Mais je me suis rapidement heurté à un petit soucis (auquel je m'attendais) : la propriété "Password" n'est pas valorisée lorsque je récupère les informations d'un utilisateur.

Voici déjà l'extrait de code me permettant de récupérer les informations de chaque utilisateurs :

Dim oUsers as ArrayList = UserController.GetUsers(0) '0 étant l'Id de mon portail que j'ai mis ici en dur
For Each oUser as UserInfo in oUsers
...
Next

Auparavant j'ai déclaré un "import" sur le namespace "DotNetNuke.Entities.Users".

Alors comment récupérer le mot de passe de chaque utilisateur ? Comme toujours cela n'est pas bien compliqué il faut juste le savoir. Voici le code que j'ai rajouté dans ma boucle :

UserController.GetPassword(oUser, oUser.Membership.PasswordAnswer)

 Cela entraine une valorisation (complémentaire) de la variable "oUser" (qui est passée ici en référence), mais il faut fournir aussi en paramètre la réponse à la question sur le mot de passe. Le mot de passe est alors accessible ainsi : "oUser.Membership.Password".

mardi 22 juin 2010

IIS 6 : erreur 404 sur fichier sans extension

Le problème du jour : après avoir déployé mon projet sur un serveur Windows 2003, je me trouve face à une erreur 404 lorsque je souhaite accéder à un fichier se trouvant sur mon serveur. Après avoir fait les vérifications d'usages (présence du fichier, droits d'accès), le problème semble plus compliqué que ce qu'il ne pouvait paraître.

Il se trouve que ce fichier est sans extension, forcément cela n'est pas un hasard, je rajoute donc une extension à mon fichier et l'erreur 404 disparait. Je me dis que cela a surement à voir avec les types MIME, j'essaye donc un paramétrage avec l'extension "." mais sans succès. Je me tourne donc vers mon ami Google, et voici le lien qui m'apporta la solution.

En résumé, il suffit de créer un type MIME "application/octet-stream" sur l'extension ".*". Voici la copie d'écran de ce que cela donne sous IIS :



Modification :
Avec un tel paramétrage je rencontre un gros problème avec Google Chrome : les fichiers CSS ne passe plus !
Donc retour arrière une fois de plus, à la place du ".*", il suffit de mettre "." en gardant le même type MIME et là plus de soucis (apparemment en tout cas). S'il n'y a pas de modification à ce billet c'est que cela fonctionne.

lundi 21 juin 2010

RadAnsyncUpload et DNN

Un petit billet suite à un problème rencontre avec le contrôle "RadAsyncUpload" de Telerik sous DotNetNuke.

Une petite présentation concernant ce contrôle pour ceux qui ne connaitraient pas : ce contrôle permet de remonter des fichiers de façon asynchrone sur le serveur. Exemple d'utilisation : vous souhaitez donner la possibilité à vos utilisateur de remonter des fichiers images sur votre site et de les visualiser dans la foulée sur la même page sans valider cette dernière (pas de postback donc).

Après avoir mis en place ce contrôle dans un module DNN, je me trouvais face à joli problème : chaque upload de fichier tombait en erreur (un point d'exclamation rouge à gauche du nom du fichier). Aucun événement serveur ne se levait et sur le client l'évément "onClientFileUploadFailed" se levait bien mais la méthode "args.get_serverResponse()" ne retournait aucune erreur (voir ce message sur le forum de Telerik).

Pourtant j'avais déjà utilisé ce contrôle sous DNN avec succès sous un autre projet. Alors pourquoi cela a-t-il déjà marché et cela ne marche-t-il plus ? En fait le projet sous lequel le contrôle marchait avait une particularité : les utilisateurs ne s'identifiaient pas ! La clef était là, en effet si je ne m'identifiais pas le contrôle fonctionnait sinon il y avait systématiquement l'erreur.

Me voilà donc rendu à éplucher le module de connexion DNN, et après avoir désactivé chaque ligne j'ai enfin trouvé LA cause du problème : Response.Redirect(..., True). Dans le module de connexion, une fois que l'utilisateur est identifié, une redirection est effectuée avec le paramètre "True" (indiquant que le traitement de la page en-cours doit s'arrêter). En passant ce paramètre à "False", plus de problème.

Heureusement pour moi j'avais mon propre module de connexion, donc pas de soucis pour modifier cela. Si ce n'est pas votre cas... vous savez ce qu'il vous reste à faire.

vendredi 4 juin 2010

Visual Studio 2010 - Désactiver le débugage de script (Script Debugging)

Sous Visual Studio 2010 (Idem pour 2008), lorsque vous exécuter un projet web ASP.NET vous voyez rapidement "débouler" dans votre explorateur de solution une nouvelle branche "Documents de Script" dont le contenu varie suivant la page où vous vous trouvez. Ces informations permettent de déboguer le javascript se trouvant sur vos pages, le gros inconvénient c'est que cela ralenti sensiblement VS.

Pour désactiver cette affichage, vous devez mettre à jour une clef en base de registre. Et tant qu'a faire compliquer autant y aller jusqu'au bout : suivant que vous êtes sous un OS 32 ou 64 bits la méthode à suivre pour désactiver ou réactiver cette fonctionnalité est différente.

Sur un OS 32 bits, utiliser la commande "cmd" que vous devez bien connaitre pour ouvrir une fenêtre DOS. Sur un OS 64 bits, il vous faut utiliser le command prompt 32 bits voilà pourquoi vous lancerez plutôt "c:\windows\syswow64\cmd.exe".

Ensuite les commandes sont identiques que vous soyez en 32 ou 64 bits :

  • pour désactiver la fonctionnalité :

reg add HKLM\SOFTWARE\Microsoft\VisualStudio\10.0\AD7Metrics\Engine\{F200A7E7-DEA5-11D0-B854-00A0244A1DE2} /v ProgramProvider /d {4FF9DEF4-8922-4D02-9379-3FFA64D1D639} /f

  • pour réactiver la fonctionnalité :

reg add HKLM\SOFTWARE\Microsoft\VisualStudio\10.0\AD7Metrics\Engine\{F200A7E7-DEA5-11D0-B854-00A0244A1DE2} /v ProgramProvider /d {170EC3FC-4E80-40AB-A85A-55900C7C70DE} /f

Si vous êtes sous Visual Studio 2008, remplacez le 10.0 par 9.0.

Pensez à faire ces manipulations avec VS fermé.

Retour sur les gestion des erreurs dans un UserControl

Bans mon précédent billet, je faisais référence à la gestion des erreurs dans un UserControl. Et bien retour arrière, la méthode décrite par Cyril ne me donnant pas satisfaction (toutes les erreurs n'étant pas catchées), je reviens à la bonne vieille méthode du try - catch que l'on met en place dans chaque événement.

Ce n'est pas compliqué en soit, il faut juste y penser.

dimanche 9 mai 2010

Erreur dans un UserControl, comment gérer cela ?

Voilà une problématique à laquelle je me suis heurté plusieurs fois sans vraiment trouver de solution. Cet après-midi il me fallait absolument trouver une solution car je souhaite mettre en place une classe dont tous mes modules hériteraient et où les erreurs seraient gérées. Cela simplifierait grandement la gestion des erreurs dans l'absolue plus besoin de créer des blocs try-catch, toutes les erreurs seraient attrapées au vol par ma classe parente.

Après quelques minutes de recherche, je tombe sur le blog de Cyril Durand (que je ne connais pas personnellement) qui est très actif sur les forums de codes-sources. Il présente une solution intéressante qui dans mon cas devrait parfaitement convenir (l'avenir le dira en tout cas). Cliquez ici pour accéder au post de son blog.

Comment savoir si une classe implémente une interface

Alors quel est la rapport avec DNN ici ? Si ce n'est dotnet, il n'y en a pas vraiment.

J'ai voulu le référencer ici car ce n'est pas la première fois que je me pose la question, bien entendu on trouve facilement des ressources sur le net sur le sujet mais il faut aller les chercher et elles sont principalement anglophones, alors pourquoi pas le mettre ici sur un blog français (au moins je saurais où le trouver la prochaine fois) ?!

La syntaxe n'est pas vraiment naturelle, voilà ce que cela donne en vb.net (je vous laisse faire la conversion en c#) :

Dim oParent As Object
'oParent doit bien sur être valorisé, dans cet exemple ce n'est pas le cas
'Vérifie si l'objet oParent supporte l'interface IParent
If oParent.GetType.GetInterface(GetType(IParent).ToString) IsNot Nothing Then
     'oParent implémente l'interface IParent
End If

Dans le cas où, dans l'intellisense, vous n'avez pas accès à GetInterface, cliquez sur l'onglet "Tout".

vendredi 7 mai 2010

Fichier manifest - component "Cleanup" - suppression d'un dossier

Voici donc mon premier post purement technique, le sujet : les fichiers manifest et tout particulièrement les components de type "Cleanup".

Le fichier manifest d'un module permet de définir les éléments à mettre en place (fichiers, requêtes SQL, ...) lors de l'installation d'un module mais il permet aussi de supprimer des fichiers installés par des versions antérieurs de ce même module.

Deux syntaxes sont possibles pour ce type de component :
  • une syntaxe où les fichiers à supprimer sont définis directement dans le fichier manifest,
  • une syntaxe où les fichiers à supprimer sont définis dans un fichier texte accompagnant le module.
Je me pencherais plus particulièrement sur cette seconde syntaxe. Pour mon cas précis j'avais besoin de supprimer des dossiers et leur contenu, j'avais bien vu à droite à gauche sur le web que cela était possible mais sans vraiment savoir comment.

Je me suis finalement décidé à aller directement à la source pour trouver la réponse à mes questions, rien ne vaut un petit tour dans le code.

J'ai trouvé ma réponse dans la fonction DeleteFiles de la classe FileSystemUtils, cette dernière est appellée par la fonction ProcessCleanFile de la classe CleanupInstaller. Pour pouvoir supprimer le contenu du dossier il suffit de définir dans notre fichier texte utilisé par le cleanup la syntaxe suivante :

Dossier1\Dossier2\

Vous noterez bien le "\" en fin de ligne c'est ainsi que la fonction de nettoyage distingue un fichier d'un répertoire, et lorsqu'un répertoire est défini c'est tout son contenu qui est supprimé.

Version de DNN utilisée : 5.4.1

Création du blog

Pourquoi créer ce blog ? Je développe sous DNN depuis quelques années maintenant (depuis la version 3), j'ai donc suivi les évolutions de ce superbe outils de CMS. J'accumule les connaissances mais j'ai un regret : le manque de ressources francophone.
Autre raison qui m'a motivée à créer ce blog : il s'agit aussi d'une démarche personnelle. Je souhaite regrouper dans un seul et même endroits tous les points un peu tordus que j'ai pu aborder autour de DNN. Cela me permettra de les retrouver plus facilement, alors si cela peut aussi aider d'autres personnes pourquoi se priver ?!