Peter Sterpe's Blog

July 27, 2009

Thoughts on an Active Community of Practice Engine

Filed under: knowledge management — Tags: , , — psterpe @ 11:09 pm

In my last post on how organizations should look at employee knowledge, I suggested that we need new kinds of tools — other than word processors — for helping people contribute to the organizational knowledge base. I didn’t say how I thought such tools could work, so in this post, I’m making some suggestions. The lens through which I look at the corporate knowledge base is that of a community of practice. To me, the knowledge base isn’t the goal; it’s a side effect of helping would-be collaborators find each other and interact. Let people query each other, help each other, teach each other, lecture each other, cite each other, rewrite each other — collaborations take many forms — and give them an online home in which to do it, and you’ll have laid the foundation for the organization’s knowledge repository.

When I say “community of practice,” I’m thinking of any set of people who wish to collaborate, seek advice, “compare notes,” and stay abreast of relevant events or knowledge in their field because they do the same job or have the same expertise. Members of a community of practice (COP):

  • May be colleagues, but are not necessarily
  • May be geographically close, but need not be
  • May know most other members, but often don’t
  • May know others who should be members, but often haven’t discovered those people yet
  • Want to interact and collaborate, but don’t necessarily know how best to do this
  • May have an online “home” in which to operate, but often don’t

I take it as a given that organizations benefit by allowing COPs to form, operate, record their knowledge, and thrive. Without meaning to make any political commentary, I can certainly imagine organizational structures and philosophies that would find all this fraternization dangerous. I probably can’t convince those folks of my point of view. My target is people who see that communities of practice develop new knowledge and facilitate knowledge transfer within the organization; that COP knowledge can foster innovation and can identify specialties the organization didn’t know it had; and that COP members derive a sense of belonging to the smaller group which transfers to a sense of loyalty to the larger organization.

So why am I convinced we need new tools for COP practitioners? It’s because the usual suspects let us down. We give people a mailing list, a shared drive for posting files, and maybe a way to conduct online discussions, and then we wait for the magic to happen. >>Crickets chirping….<< Well, why doesn’t the magic happen? Why don’t online communities just “work?” At first, sometimes they do work if a few motivated and communicative members keep the community alive with questions and content. But the mavens don’t necessarily know valuable ways to encourage participation and attract new blood. Tired of the lukewarm community response to their efforts, the mavens move on or fade into a passive role.

Online communities are still new to us. At least we act as if they are. We don’t inherently know how to operate them. Lacking a model for success, online COPs usually fizzle. Technology can help solve this problem, but not the static kind of technology that just sits there and waits for people to use it. What we need is an active engine that drives and guides participation.

What if a community of practice operated on a software platform that “knew” what to do? What if the platform:

  • Coached members on how to play the necessary roles to keep the COP vibrant?
  • Solicited participation from members?
  • Suggested actions for keeping the community going and the content fresh?
  • Provided tools and instructions for carrying out its suggestions?

To make this just a little less abstract, here are some things I think an active COP engine could do:

  • Maintain a list of key activities that someone in the community should perform, e.g.:
    • Ask a question
    • Contribute a document or a link to content
    • Run a survey
    • Organize an event
    • Launch a discussion topic
    • Run a conference call or meeting
    • Comment on existing content
    • Propose a new content area
  • Remind practitioners periodically to perform a key activity that has not been done in a while
  • Seek participation from members, e.g., postings to discussions, answers to questions, comments on content, etc.
  • Seek missing metadata, e.g., questions need answers, meetings need agendas and minutes, discussion topics need resolutions, etc.
  • Seek fresh content in weak or stale areas

The active COP engine itself would probably be executable code running on the same server as whatever collaboration platform was in use. There’s no reason to write a new collaboration platform; many exist that provide decent functions and can be extended. The COP engine could use the same underlying database as the collaboration platform for its metadata and any other data it needed to store. It could also use the local messaging capability, e.g., email server, for communicating its guidance and reminders out to the user community. The following figure, in addition to illustrating why I got poor grades in art class, also diagrams a (very) high level hypothetical architecture.
CommunityOfPracticeArchitecture

Ideally, the engine’s behavior would be largely data-driven, or said less geekily, a configuration file would tell the engine what to do. For example, you could configure the engine to know that a given activity (“Run a survey”) should be carried out by a particular role (perhaps any user could run one, or maybe you’d decide that only administrators should be able to run surveys) with a given frequency (monthly). You could also specify a set of tools to give the user as aids in carrying out the activity; the tools could be native platform capabilities (e.g., many collaboration platforms have a native capability to launch a discussion) or specially written wizards to supply capabilities and guidance that the platform lacks.

I admit I leave a lot to the imagination, but this is a blog post, not a functional spec or technical design. I’m not proposing anything technically difficult or groundbreaking here — agents that “do stuff” are not even close to a new idea. And of course you’d have to devise ways to keep the engine from being an obtrusive pain in the neck or a disrespected amusement (remember the smiling paper clip in Word?) The potential flies in the ointment are addressable without needing any breakthroughs in the field of computer science, though. Worth some consideration, IMHO, are three ideas. First, the idea that communities of practice need guidance and an operating model in order to succeed. Second, the idea that software agents can supply this guidance as an overlay to an existing collaboration platform. And third, the idea that you needn’t work so hard at building the corporate knowledge base; thriving communities of practice will leave one behind as an artifact of their activities.

I’m champing at the bit to build this. Anyone have funding?

July 24, 2009

Don’t Mine Knowledge, Cultivate It

Filed under: knowledge management — Tags: — psterpe @ 4:52 pm

Departure today from the technical how-to topics I have been blogging on. This time I’m waxing poetic on why I think organizations that depend on “knowledge work” have the wrong attitude and approach toward knowledge. I’m moved to write about this by a recent professional experience that struck a chord from my consulting past during which “knowledge management” was something you could say without apologizing (well, in certain crowds, anyway). Regardless of the heading you put this under and how buzzword compliant that heading is, the basic situation is the same for most of us knowledge workers:

  • Our success depends upon what we know and what we can learn that others know
  • Our employers/clients expect us to have a lot of this knowledge stuff already, but they also expect us to have clever (and inexpensive) ways of getting more of it
  • Nobody has the faintest clue what knowledge really is and how to get more of it.

OK, the gauntlet is down. I make this claim because in the 25 years that I have been working in and around software products and systems that help people do their jobs better, I haven’t seen anyone — except knowledge management consultants — pay attention to this valuable asset called knowledge. Pretty much everyone is all over the idea of information, which they think is a synonym for knowledge. But I don’t think people really get the knowledge thing.

What separates knowledge from information is so basic it feels silly to write it down — knowledge is what people — breathing humans — know. I realize that was a tautology; let me try to get out of that doghouse. What makes something knowledge is your experiences and the way you have connected these to the facts and figures you understand. The facts and figures are not knowledge — they’re just information. Data. You combine that data with your experiences to produce a bit of knowledge. Here’s an example that Bostonians will appreciate given that June in Boston this year was so wet, cool, and murky it was how you imagine the forests to be when you read a Tolkien novel. Anyhow, if I have the info that it’s the middle of June in Boston and this morning it’s cold enough for me to wear a fleece pullover, I have a good hunch it’ll rain today. I don’t have the information on the future of my local weather, I have the knowledge that if you get nasty cold in Boston in June, that’s not customary — something is happening, and from my experience, that something is likely to be rain. I know to carry an umbrella or wear a hat.

So who cares whether knowledge and information are the same or different? Did you ever fly in a plane? If so, you should care about the difference. What every dial and lever in that cockpit means and what it controls is nothing more than a large body of information. If I were armed with that information (the manuals are carried on every airplane), would you feel comfortable letting me fly that plane? Didn’t think so. Even if I had a week to read all the manuals first? Um…nope. And why not? Because flying a plane depends on so much more than the assembled facts and figures of how a plane operates. OK, so why should an insurance business or a telecommunications carrier or a retail chain care about knowledge? They send a lot of their people to training — doesn’t this give the employees what they need to know to do their jobs?

Only a little — and only temporarily. Training classes have a notoriously low rate of retention (ask any instructional design maven), AND, a lot of class content is information, not knowledge. People get more training on how to work the customer service computer program (“hit F5 after you enter the phone number”) than they do on how to provide good customer service (“let an angry caller tell his story before interrupting to ask the phone number”). To do most “knowledge worker” jobs well takes more than classroom learning. You need enough experience to have seen some of the exceptional cases; you need to develop some intuition about what will happen based on your decisions and actions. This is what makes the hot shots hot; what they know goes beyond the mechanics of the ordinary. Experts can operate in the realm of what will happen or what should be the case or what mustn’t be done. And doesn’t every knowledge intensive business want as many employees as possible to perform like experts?

This knowledge stuff is valuable — in more extreme cases, it can keep people alive, and in ordinary cases, it can help you acquire customers and retain them; lack of it can certainly help you lose them. So why don’t businesses approach knowledge the right way? Some confuse knowledge with information, and those businesses think they are doing the right thing. They run training and they cram binders with factoids (and you can even take the course materials away on a USB memory stick!). Other businesses see that, despite the need to impart information to their workers, this alone won’t result in expert or near-expert performance. These businesses do see the need to harness their workers’ knowledge, but sadly, they usually look for it in the wrong place. If knowledge is what people know, then the right place to find it is in people’s HEADS. You are pretty unlikely to find it in their documents, spreadsheets, and PowerPoint presentations. But that’s exactly where most organizations think knowledge resides — the “K: drive,” that shared place where we store all our files. This isn’t impossible — a person can put his wisdom down in writing — it’s just improbable. Documents are most often about info, not knowledge. Did you ever turn to the Encyclopedia Brittanica to solve a thorny problem or make a tough decision?

This notion that all these documents we business folk keep cranking out constitute the institutional knowledge base is not only wrong, it breeds two assumptions that throw us further off the scent from being able to capture the real knowledge:

  • Mistaken Assumption #1: A continuous supply of knowledge is a given
  • Mistaken Assumption #2: Knowledge is something to be mined

A bounteous knowledge supply is not a given because knowledge has to come from people, and people don’t usually commit their knowledge to writing because they are over-worked and under-motivated. And even if they were moved to do it, there’s no home for their knowledge in what corporations traditionally (and mistakenly) think of as the knowledge base. Valuable knowledge walks out the door at 6:00 every day because people have nowhere to put it and the contribution method — cranking out a document — is too difficult.

Because businesses think the document dumping ground is a knowledge base, they are led to their second faulty point of view, which is that knowledge is something to be mined. Hey, there’s a lot of dreck on the K: drive, so if there are any nuggets of wisdom in there, you’re gonna need to do some mining, right? The whole enterprise search industry depends on this attitude. But mining is the wrong point of view. It implies a scarce supply of something valuable trapped inside a nearly endless supply of something worthless. Sound like your company’s shared drive?

What we need to do is cultivate knowledge, not mine it. But how do you get at what people are carrying around in their heads? We know from the explosion of the web that there are a lot of would-be content contributors out there. It’s no longer a hope or wish that if you give people a way to contribute what they know, many of them will do it freely. We need to make it easy, though. And we need to stop being afraid of it — some organizations legislate this sort of behavior out of existence. Here’s what I think are the essentials:

  • Provide new kinds of tools so people can easily capture and contribute their insights without having to slog through the process of writing a document
  • Give people tools that connect them either to the knowledge they are looking for or to like-minded collaborators
  • Take active steps to stimulate the flow of ideas between collaborators so that insights and innovations can occur

I see this as a self-reinforcing loop: capture what people know (both their long-standing know-how and their newly-formed “Eureka!” moments), connect people to knowledge and to each other, stimulate the creation and flow of ideas, and repeat from the top. With this loop in place, you really can have a knowledge repository rather than a file archive. And with more know-how and less dreck in the repository, employees stand a much better chance of latching onto an insight that will improve their performance and make an actual difference for their organization.

June 2, 2009

Running a SharePoint App From Your Own Copy of the Layouts Folder

Filed under: SharePoint — Tags: — psterpe @ 5:14 pm

Did you ever want to tinker with some of the ASPX pages in the layouts folder of the SharePoint hive? You might want to customize the appearance of application.master, for example. Or, you might want to diverge from the released _layouts pages so that some web apps use your customized versions. When Microsoft releases patches, they’ll be installed in the hive, thereby giving you the chance to run a diff to see what you need to migrate into your copied folder. With sufficient permissions, you can edit the layouts pages directly of course, but it’d be safer to work with your own private copy until you know your changes are safe to be unleashed on an unsuspecting populous. Here’s how to point a web app at your local copy of the layouts folder.

  1. Make your own copy of the layouts folder

    SharePoint’s _layouts pages are at this oh-so-easy-to-remember location: C:\Program Files\Common Files\Microsoft Shared\Web Server Extension\12\template\layouts. Copy this folder and paste it wherever you want if you’re just hacking around. If you plan to run a production web app from your copy of layouts, a good place to put the copy might be C:\Inetpub\wwwroot\webapps\your_web_app\layouts. You might have to adjust the file system permissions on the folder containing your layouts copy — whichever user your web app runs as (typically ASPNET) will need Read access.

  2. Point your web app at your copy

    Open IIS Manager, navigate to your web app, and expand the tree so that you can see the _layouts folder; it will look like the figure below.

    IIS_layouts_1

    Right-click the _layouts folder, and select Properties from the context menu. On the Virtual Directory tab, select the radio button indicating that the content should come from “A directory on this computer” and in the Local path field, enter the full path to the location of your copy of the layouts folder. Make sure the box for Read access is checked, and check any others that pertain. Then click OK.

    IIS_layouts_2

  3. Run the iisreset command.

Your app will now use the files in your copy of layouts for all of SharePoint’s _layouts pages.

May 31, 2009

Can’t Deactivate Trackpad in Leopard — A (Lame) Workaround

Filed under: Uncategorized — Tags: , , — psterpe @ 4:03 pm

Almost a year ago, I became a “switcher” — someone who abandons the PC and Windows for the Macintosh and Mac OS. I joined the party as of the “Leopard” release of Mac OS X. I have dutifully installed the Leopard point releases as they have come along. The release notes haven’t told me much, and after the first leap of faith worked out fine, I just kept on installing the updates. Two weeks ago, I installed the 10.5.7 update, and for the first time, I was sorry I had updated.

I have to believe this is a bug — someone at Apple has to have goofed, because what they did with the trackpad is just plain dumb. You used to be able to deactivate the trackpad while an external (in my case, USB) mouse was connected. As someone whom Mavis Beacon would flunk for poor typing technique, I tend to lean on the trackpad when I don’t mean to, which causes unintentional clicks and selections. At times whole new windows appear and disappear mid-sentence, and I have no clue what I leaned on to cause this. My best solution was simply to disable the trackpad when I had another mouse to work with.

The ability to deactivate the trackpad used to be a setting; at some point (I think 10.5.6, but I’m happy to stand corrected on that), the option disappeared because the capability was just built into the OS – connect a device, the trackpad deactivates. For me, perfection. With the 10.5.7 release, however, you CANNOT deactivate the trackpad. Connecting an external mouse doesn’t do it, nor can you find a setting for it. One clever forum poster suggested you could enable the accessibility feature “Mouse Keys” and thereby disable the trackpad — nopers. Other forum posters familiar with Leopard’s default system tried tinkering with the setting "com.apple.mouse.ignoreTrackpadIfMousePresent" = 1, but this setting is now ignored. I resolved to start looking for a PC to replace my Macbook Pro.

And then the obvious hit me. It was a Homer Simpson-esque “D’oh!!” moment. The trackpad has features. You may not be able to kill it, but certainly you can stop it from doing things, right? Right. When I looked at the trackpad settings, I saw this:
Trackpad1

All I had to do was uncheck the boxes for Tap to click and Dragging. I ended up with this:
Trackpad2

For me, this solves the problem. When I sloppily lean on the trackpad, the cursor still moves, but the trackpad cannot cause a click or a drag to occur. So, where my next typed character gets inserted cannot be altered. (I guess if you were running X11 and had enabled the setting to give focus to a window once the mouse enters it, my workaround would not solve your problems. I personally abandoned that X11 setting in 1989, but I digress….)

When I disconnect my external mouse and really want to use the trackpad, it still works for moving the mouse cursor. I can’t tap the trackpad to click, though — I have that feature disabled. I can either re-enable that feature through settings, or I can just hit the (too large to ever miss) button. I use the button — tapping is less comfy for me anyway.

Disgruntled users have discussed this “feature” in 10.5.7 in this Apple discussions thread. I’ll be following the thread to see if anyone learns whether Apple intended this feature change or just goofed.

May 24, 2009

Adding Users With SharePoint Forms Based Authentication

Filed under: SharePoint — Tags: , , — psterpe @ 5:53 pm

Some folks have found my blog by searching for “how to add users to SharePoint with Forms Based Authentication.” In my second article on FBA, I mostly pointed to other sites at which people have done a nice job describing this, but I didn’t show the code I used. In case that’d be helpful for someone, I’ll show a few of the highlights here. There is nothing SharePoint-y about adding users through Forms Based Authentication (FBA) — it’s all .NET-y, done most easily with the ASP.NET CreateUserWizard control. What follows are some pointers on how to use that control.

The CreateUserWizard control gives you a way to gather several fields of information about a prospective user and create a database record for that person in the membership table. As Microsoft’s documentation of the control shows, it can do a lot; here I show the basics. What makes the control a wizard is that it allows you to architect a multi-step, multi-page process for gathering factoids in which you don’t need to build the pages or even the inter-page buttons. You use tags to describe what you want, and the control supplies the data entry fields, the navigation, and the processing logic.

Below is a sample usage of the CreateUserWizard control to gather a username and password. In this example, we use an email address as a username (and even enforce this, syntactically, with a regular expression validator). This may look like sausage making, but it isn’t all that involved, and I’ll explain each ingredient. Aside from the page directives at the top that make this behave like a SharePoint page, this is .NET all the way.

<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Page Language="C#" Inherits="Microsoft.SharePoint.ApplicationPages.LoginPage" MasterPageFile="~/_layouts/simple.master"%>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<asp:CreateUserWizard runat="server" ID="CreateUserWizard1" OnCreatedUser="CreateUserWizard1_CreatedUser"
    RequireEmail="True" DisableCreatedUser="false" OnSendingMail="CreateUserWizard1_SendingMail">
    <WizardSteps>
	<asp:CreateUserWizardStep runat="server" Title="Register" ID="TheStep">
	    <ContentTemplate>
		<table>
		    <tr>
			<td colspan="2" align="center">
			    Make an Account</td>
		    </tr>
		    <tr>
			<td colspan="2" style="color:red">
			    <asp:Literal ID="ErrorMessage" runat="server" EnableViewState="False"></asp:Literal>
			</td>
		    </tr>
		    <tr>
			<td colspan="2">
			    User Name &amp; Password</td>
		    </tr>
		    <tr>
			<td class="ms-formlabel" style="width: 25%;">
			    Email <font size="-2">(will be your user name)</font></td>
			<td class="ms-formbody" style="width: 75%;">
			    <asp:TextBox runat="server" ID="UserName" Columns="50" />
			    <asp:RequiredFieldValidator ValidationGroup="CreateUserWizard1" runat="server" ID="UserNameValidator"
				ControlToValidate="UserName" Display="Dynamic" ErrorMessage="Email is required. This will be your username." />
			    <asp:RegularExpressionValidator ValidationGroup="CreateUserWizard1" runat="server"
				ID="RegexValidator0" ControlToValidate="UserName" Display="Dynamic" ErrorMessage="Please enter a valid email address."
				ValidationExpression="\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" />
			</td>
		    </tr>
		    <tr>
			<td class="ms-formlabel">
			    Password</td>
			<td class="ms-formbody">
			    <asp:TextBox runat="server" ID="Password" TextMode="Password" />
			    <asp:RequiredFieldValidator ValidationGroup="CreateUserWizard1" runat="server" ID="RequiredFieldValidator10"
				ControlToValidate="Password" ErrorMessage="Password is required." />
			</td>
		    </tr>
		    <tr>
			<td class="ms-formlabel">
			    Confirm Password</td>
			<td class="ms-formbody">
			    <asp:TextBox runat="server" ID="ConfirmPassword" TextMode="Password" />
			    <asp:RequiredFieldValidator ValidationGroup="CreateUserWizard1" runat="server" ID="RequiredFieldValidator13"
				ControlToValidate="ConfirmPassword" ErrorMessage="Confirm Password is required." />
			</td>
		    </tr>
		    <tr>
			<td colspan="2">
			    Personal Information</td>
		    </tr>
		    <tr>
			<td class="ms-formlabel">
			    First Name</td>
			<td class="ms-formbody">
			    <asp:TextBox runat="server" ID="FirstName" Columns="32" MaxLength="64" />
			    <asp:RequiredFieldValidator ValidationGroup="CreateUserWizard1" runat="server" ID="RequiredFieldValidatorFName"
				ControlToValidate="FirstName" ErrorMessage="First name is required." />
			</td>
		    </tr>
		    <tr>
			<td class="ms-formlabel">
			    Last Name</td>
			<td class="ms-formbody">
			    <asp:TextBox runat="server" ID="LastName" Columns="32" MaxLength="64" />
			    <asp:RequiredFieldValidator ValidationGroup="CreateUserWizard1" runat="server" ID="RequiredFieldValidatorLName"
				ControlToValidate="LastName" ErrorMessage="Last name is required." />
			</td>
		    </tr>
		    <tr>
			<td colspan="2">
			    <asp:CompareValidator ID="PasswordCompare" runat="server" ControlToCompare="Password"
				ControlToValidate="ConfirmPassword" Display="Dynamic" ErrorMessage="The Password and Confirmation Password must match."></asp:CompareValidator>
			</td>
		    </tr>
		</table>
		<asp:TextBox runat="server" ID="Email" Style="visibility: hidden" />
	    </ContentTemplate>
	</asp:CreateUserWizardStep>
	<asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server">
	</asp:CompleteWizardStep>
    </WizardSteps>
    <SideBarStyle BackColor="#5D7B9D" BorderWidth="0px" Font-Size="0.9em" VerticalAlign="Top" />
    <TitleTextStyle BackColor="#5D7B9D" ForeColor="White" Font-Bold="True" />
    <SideBarButtonStyle ForeColor="White" BorderWidth="0px" Font-Names="Verdana" />
    <NavigationButtonStyle BackColor="#FFFBFF" BorderStyle="Solid" ForeColor="#284775"
	BorderWidth="1px" BorderColor="#CCCCCC" Font-Names="Verdana" />
    <HeaderStyle BackColor="#5D7B9D" BorderStyle="Solid" ForeColor="White" HorizontalAlign="Center"
	Font-Size="0.9em" Font-Bold="True" />
    <CreateUserButtonStyle BackColor="#FFFBFF" BorderStyle="Solid" ForeColor="#284775"
	BorderWidth="1px" BorderColor="#CCCCCC" Font-Names="Verdana" />
    <ContinueButtonStyle BackColor="#FFFBFF" BorderStyle="Solid" ForeColor="#284775"
	BorderWidth="1px" BorderColor="#CCCCCC" Font-Names="Verdana" />
    <StepStyle BorderWidth="0px" />
    <StepNavigationTemplate>
	<asp:Button ID="StepPreviousButton" runat="server" BackColor="#FFFBFF" BorderColor="#CCCCCC"
	    BorderStyle="Solid" BorderWidth="1px" CausesValidation="False" CommandName="MovePrevious"
	    Font-Names="Verdana" ForeColor="#284775" Text="Previous" />
	<asp:Button ID="StepNextButton" runat="server" BackColor="#FFFBFF" BorderColor="#CCCCCC"
	    BorderStyle="Solid" BorderWidth="1px" CommandName="MoveNext" Font-Names="Verdana"
	    ForeColor="#284775" Text="Next" />
    </StepNavigationTemplate>
</asp:CreateUserWizard>

Here’s a screenshot of what the code above produces:

Rendered CreateUserWizard control for making a new user account

Rendered CreateUserWizard control for making a new user account

Now let’s dig into each bit of the code above and talk about why it’s there and what it produces. First up is the opening tag of the CreateUserWizard control, formatted here with plenty of line breaks so you can read it more easily:

<asp:CreateUserWizard runat="server" ID="CreateUserWizard1"
    OnCreatedUser="CreateUserWizard1_CreatedUser"
    OnSendingMail="CreateUserWizard1_SendingMail"
    RequireEmail="True"
    DisableCreatedUser="false" >

Of course don’t forget the runat="server," and definitely give the control an ID. You’ll need the ID in any code you write to tweak how things work; I’ll show some of my C# code later in the post. The OnCreatedUser and OnSendingMail attributes specify event handlers, that is, they name C# methods that should be called when these events fire. Here’s a summary of the two events I’m handling:

  • OnCreatedUser. When the wizard has succeeded in inserting a database record for the user, this event fires. So what’s left to do once the user’s database record has been created? Nothing is required, so you don’t have to have this handler, but if you’re using the custom profile provider for storing ancillary user information, this is when you’d create the user’s profile record. See CreateUserWizard1_CreatedUser() for a simple example.
  • OnSendingMail. You can arrange for the CreateUserWizard control to send an email to the newly created user; you’d most likely make this a welcome message. Just before that welcome email is sent, this event fires. This is another event that you don’t have to handle, but there’s something very useful you can do in the handler, namely, to substitute data values on-the-fly into the otherwise canned text of your welcome email. See CreateUserWizard1_SendingMail() for a simple example.

    To get the wizard to send the mail at all, you do three things:

    1. Ensure that you have defined a field within the control whose ID is “Email.”
    2. In the control’s opening tag, put RequireEmail="True".
    3. Set the control’s MailDefinition properties, being sure that the BodyFileName property points to a file that exists and is readable. You can set the MailDefinition properties declaratively with tags, or you can do it in code. Here’s an example of doing it in code in the Page_Load() handler:

      CreateUserWizard1.MailDefinition.BodyFileName = "~/MailFiles/MailFile.txt";
      CreateUserWizard1.MailDefinition.From = "peter.sterpe@sympraxisconsulting.com";
      CreateUserWizard1.MailDefinition.Subject = "Welcome to the Site";
      CreateUserWizard1.MailDefinition.IsBodyHtml = true;

    In our case, you might have noticed that the “Email” field is hidden:

    <asp:TextBox runat="server" ID="Email" Style="visibility: hidden" />
    

    We’re capturing the username in a field whose ID is “UserName” rather than using the “Email” field for this purpose. Necessary? No. I just wanted any application code to refer to “Username” when that’s what it’s really working with. This value will be used for authentication, so I wanted to call it what it is. To get the wizard to send the welcome mail, there has to be an “Email” field, so that field exists — hidden — and a little JavaScript copies the Username value into it. My post on how to do client-side event handlers in Javascript shows you the code for doing this kind of thing.

Completing our look at the opening tag, the attribute DisableCreatedUser="false" tells the wizard whether the newly created user account should be enabled or disabled. If it is enabled, the user can log in immediately; if the account is disabled, an activation step has to occur before the user can login. In the latter case, you’d typically include a link in the welcome mail that, once followed, causes the user’s account to be activated. This technique provides a little bit of fraud protection — although I can try to impersonate you by registering with your email address, I won’t receive the welcome message and therefore won’t be able to activate the account. To accomplish the activation, you instantitate a MembershipUser object, set its IsApproved property to True, and update it. Here’s a code snippet for that:

// Declare userID as a GUID and set it to the user's internal id value from the aspnet_Membership table. Then use the following
// lines to activate the user's account.
MembershipUser userInfo = Membership.GetUser(userID);
userInfo.IsApproved = true;
Membership.UpdateUser(userInfo);

After the CreateUserWizard opening tag, the control must contain one or more steps, declared with the element, all nested within a WizardStepselement. Each step will correspond to a new page. In our simple example, we define only one step whose id is “TheStep.” The wizard lets you designate a special completion step, using the element, for any final processing. We declare that element but put nothing in it since we have nothing special to do. You could use this step to force the wizard to back up if, say, certain criteria hadn’t been met.

So what does our wizard do with its one step? Gather account registration data using text fields — noting earth-shattering. We put the fields in a 2-column table, one column for field labels and one for the data entry controls. All of this is embedded in a ContentTemplate element. The fields are typical .NET controls such as ; you can use drop-down lists or any other kinds of controls you want. In this example, I have defined some field validators, too, to enforce that required fields have a value and to enforce that the username field looks like an email address. To make everything look SharePoint-like, give the field labels the CSS class “ms-formlabel” and the data fields the CSS class “ms-formbody.” This is all basic .NET stuff described in a million places, so I’m not going to belabor it here.

The remainder of the elements in the CreateUserWizard control specify styles for the various regions of the control’s on-screen appearance, including its buttons. This is all optional; if you’re OK with the defaults, you can omit these elements entirely.

So, that’s all you need to do. No database code, no inter-page navigation — just declare the control and let the .NET framework do the rest.

I did decide to enrich the example a little by handling some events, so let’s look at what those handlers are like. You don’t have to provide a handler for the OnCreatedUser event, but one thing you can do in such a handler is create your new user’s profile record. The database table for the .NET Membership provider, aspnet_Membership, holds just enough info to log a user in; it doesn’t store any related information like the user’s first and last name. (OK, I think that’s a bit streamlined.) To store those pesky factoids, you need the .NET Profile provider. Here’s a simple handler:

void CreateUserWizard1_CreatedUser(object sender, EventArgs e)
{
 TextBox UserNameTextBox = (TextBox)TheStep.ContentTemplateContainer.FindControl("UserName");

 // Save profile values common to all kinds of users.
 PJS.UserProfile profile = PJS.UserProfile.GetUserProfile(UserNameTextBox.Text);

 profile.FirstName = ((TextBox)TheStep.ContentTemplateContainer.FindControl("FirstName")).Text;
 profile.LastName = ((TextBox)TheStep.ContentTemplateContainer.FindControl("LastName")).Text;

 profile.Save();
}
 

You also don’t have to handle the wizard’s OnSendingMail event, but one thing you can do in this handler is substitute data values into the text of your otherwise boilerplate welcome email. Here’s some sample email text that you’d store in a file:

<p>
Welcome to the site, <%UserName%>. Click on this link below to activate your membership: <a href="<%ActivationURL%>"><%ActivationURL%></a>
</p>

In the text, anything between is a placeholder for which you can substitute a value before the message goes out. Here’s some sample handler code that substitutes values for the UserName and ActivationURL placeholders:

void CreateUserWizard1_SendingMail(object sender, MailMessageEventArgs e)
{
    TextBox UserNameTextBox = (TextBox)TheStep.ContentTemplateContainer.FindControl("UserName");
    MembershipUser userInfo = Membership.GetUser(UserNameTextBox.Text);

    String activationURL = Request.Url.GetLeftPart(UriPartial.Authority) +
			   Page.ResolveUrl("~/_layouts/ActivateAccount.aspx?ID=" +
			   userInfo.ProviderUserKey.ToString());
    e.Message.Body = e.Message.Body.Replace("<%ActivationURL%>", activationURL);
}

In an upcoming post, I’ll go into more detail about using a welcome email to get a newly registered user’s account activated.

May 14, 2009

Lite Survey of eCommerce Platforms

Filed under: eCommerce — Tags: — psterpe @ 10:02 pm

I’m helping a client get the lay of the land for ecommerce tools and platforms. If I lived in this land, I’d have to ask for directions a lot — the landscape is vast and featureless. I say “featureless” with tongue in cheek; ecommerce platforms are now bloatware, in my opinion, chock full of features, but nearly all that I have encountered have about the same feature set. They’re so similar “you could throw a hat over them,” as my dad used to say. So how did I find a standout in such a me-too field?

Before I answer my own rhetorical question, here’s some background on the platforms I considered, and how I classified them. First of all, what are these things? Some products are toolkits of software and templates with which a fairly technical person can construct an online store, while other products are complete online stores right out of the box. And some are hybrids, giving you the skeleton store plus some tools and templates for making enhancements and customizations. So are they tools? Platforms? I opt to call them “platforms” with the understanding that some of them really are just SDKs.

How did I find these things? A good starting place is at Authorize.net where they list the platforms that have been certified to work with their payment gateway. Their Certified Solution Directory is at http://www.authorize.net/certified_solution_directory.asp. I looked for “Shopping Cart” types of solutions and got over 70 choices on the day I ran the search. I was not able to exhaustively investigate all the listed solutions; I had to perform some triage and narrow in quickly on one that would meet my client’s needs. I didn’t want gigantic enterprise class solution suites that could drive commerce for a small country. I mostly cared about the ability to maintain control over all aspects of the online store, so my favored solutions were those that gave you the tools to build/maintain the store and deploy it anywhere. I specifically did not want a “just add water” solution in which a hosting provider controls the store and dictates what I can do with it while all I do is enter my product data. I realize that for non-technical proprietors, especially those operating very small businesses, the “Poof! Here’s your store!” solutions may be nirvana. If that sort of solution is what you need, I have good news. You have choices — lots of ‘em. I don’t think you can even count how many choices you have; you need to measure them by the metric ton.

As I narrowed down the field, I arrived at three categories: Worth A Second Look, Interesting Maybes, and The Rest. The grids I created appear below. These grids are sparse — I didn’t look deeply into every platform. Repeat, these grids are sparse. One more time: sparse grids below. If you know factoids about a platform that I didn’t capture, please leave a comment! Perhaps with some collaboration we can flesh out these grids (did I mention that they’re sparse?)

The platform I went with is AbleCommerce. For me, it stands out. Part of what attracted me to AbleCommerce was the feeling, before I knew it intimately, that I could get somewhere with it. And that intuition has been borne out. I easily developed a working prototype store into which I loaded sample data for 18,000+ products.

Some things I like about AbleCommerce:

  • It’s feature-rich, but not over the top. It can handle product variants (e.g., size, color), product kits (the ability to build or customize something out of several parts each of which adds to or subtracts from the base price), digital products (things the customer downloads so nothing has to be shipped), promotion codes, volume discounts, customer reviews, customer ratings, marketing to a mailing list, email a friend, most popular.
  • A non-programmer can customize page layout and content, and most of this can be done from within the store pages without needing to visit the administration module
  • It has a nice administration module for the things that you do need to do there
  • It has a flexible architecture that isn’t ridiculous. There aren’t layers and layers of abstractions that beget other abstractions that beget factories that — if you can STAND to read that much documentation — eventually beget the silly little tweak you wanted to make. I easily copied their ASPX and C# code, made my own versions of things, and extended the system. And let’s just say that nobody has ever mistaken me for a hot shot .NET programmer.
  • The AbleCommerce developer forums are active — people use this platform and they help each other.
  • The bulk data upload/export tool, DataPort, works pretty well and has a very nice user interface. It lets you upload from either CSV or XML, and once it has read your source file, it lets you map your columns to the AbleCommerce data model very flexibly: you can map on-screen on the fly using the importer and save what you did as a template, you can map using an existing template, or you can map using their defaults. It can get picky during import, and its error messages need work, but I loaded over 18,000 product records without pulling my hair out.

I wrote this post because I looked at, at least a little, a whole bunch of platforms, and I thought it might help someone conducting the same sort of search to see what I turned up. Have I directly used lots of tools in this field? No. So could other tools be as good as or even better than the ones I have singled out? Of course they could. And if you know things I don’t know, please leave a comment and educate us all.

Here are the products I considered.

Worth a second look…

I short-listed these because they seemed to meet my need of giving the store creator a lot of control. My technology preferences were ASP.NET and SQL Server, but I did consider platforms based on PHP, Java, or ColdFusion as well as those based on other databases. I was also interested in platforms that would allow bulk upload of product data from a file.

Product URL Notes Hosted Software Prog. Language Database Pricing
AbleCommerce http://www.ablecommerce.com Does have bulk upload; free trial available. Easy to get going, permits plenty of
customization without programming. Most source included.
x x ASP.NET $995
AspDotNetStorefront http://www.aspdotnetstorefront.com PABP certified

Feature matrix at http://www.aspdotnetstorefront.com/t-features.aspx

Bulk import only with Excel for Express; need
regular ML to use XML or web services

ASP.NET SQL Server 2005/2008 ML/Express $695; ML $1295
Click Cart Pro http://www.kryptronic.com/Shopping-Cart-Software Feature list at http://www.kryptronic.com/Shopping-Cart-Software-Features

Demo looks nice and runs well; somewhat busy,
but probably to illustrate all you can do; Demo at http://www.kryptronic.com/demo/index.php

x PHP SQL Server $99 limited offer (1999 price
rollback)
Interspire Shopping Cart http://www.interspire.com/shoppingcart Template-based with slick WYSIWYG drag-n-drop
editing;

Feature list at
http://www.interspire.com/shoppingcart/features.php

x PHP MySQL Starter ($295), Professional
($995), Ultimate($1,795)
VP-ASP Shopping Cart http://www.vpasp.com Free trial supports only Access database;

3 payment gateways per license

Style templates $65; install with file overwrite

Admin very table-based, lots of id fields
floating around

Admin screens are easy to understand with
decent on-screen help; lots of store features are configurable

Source code in VB; oldish, not .NET

x ASP MySQL, SQL Server from $245-$495
X-Cart http://www.x-cart.com 30-day trial x x PHP MySQL Gold $229, Pro $575

Interesting Maybes…

Product URL Notes Hosted Software Prog. Language Database Pricing
123 Shop Pro http://www.123ShopPro.com Features at http://www.123shoppro.com/feature.htm;
http://www.123shoppro.com/demo.htm
x ASP SQL Server Standard $550, Advanced $850
5th Avenue Shoppe http://www.5th-avenue-software.com/ http://artists-showcase.org/phpstore/store_pages/mainpage.php

http://winelovers.5th-avenue-demo.com/store/store_pages/mainpage.php

x PHP;

Windows or Linux

MySQL or PostgreSQL $395
WebAssist http://www.webassist.com Various products; PowerStore seems like the right one; no free trial x PHP MySQL $299
Hostway Merchant Manager 4.5 http://www.hostway.com/ecommerce/index.html Signed up for online demo; always get 404 error x
OS Commerce http://www.oscommerce.com/solutions Open source x PHP MySQL typically Free

The rest…

The products in this grid are not necessarily inferior or weak compared to the ones in my most favored category. They mostly are just the wrong kind of product for what I wanted to do — most of these are hosted solutions geared toward non-technical people who wouldn’t want to change the programming of their online store even if learning to do so would grant them eternal life and an endless supply of donuts. No shame in that — they know their strengths. But solutions geared for that audience aren’t right for mine, so they quickly fell to the bottom of my list.

Product URL Notes Hosted Software Prog. Language Database Pricing
Doba www.doba.com Interface to wholesalers who will drop ship x
WordPress http://wordpress.org/support/topic/224202 WordPress with shopping cart plugin; only good for very small shops. x
Elastic Path Software http://www.elasticpath.com They say, “If your annual online revenue is less than $5 million,
Elastic Path Commerce is probably too expensive for you.” Well, alrighty then.
x Java, Velocity, Spring Independent; uses Apache
OpenJPA
Not posted
.netCART http://www.dotnetcart.com Seems to have the capabilities; very
programming-oriented; not very pretty

Latest version 2.9.2844, 10/15/2007

http://www.dotnetcart.com/demo/

x ASP.NET $295 per store
1ShoppingCart.com http://www.1shoppingcart.com/ x Basic $59/month or $599/year;
Professional $99/month or $999/year
AAcart http://www.aacart.com Demo stores work very well; http://www.aacart.com/ecommerce-shopping-cart-software/shopping-cart-demo-store.html x Setup from $0-399; monthly
hosting from $19-69
Advanced Cart v2.11A http://www.bsmstore.com Standalone shopping cart only; not catalog
management
Agile Cart http://www.eastland.com/carts.html http://www.eastland.com/AgileDemo.html x ColdFusion and PHP $395
Americart http://www.americart.com x
ampleShop http://www.amplecom.com “Our company is based in Macedonia with representatives
in the US and Switzerland”
Andale Auction Management Tools http://www.andale.com
Apple Cart http://www.spads.com Seems too low level for my needs
ASecureCart x
B2I_Shopping_Cart http://www.b2ishoppingcart.com Looks like a one-person company x x
Cart Manager http://www.cartmanager.net/ x
CartFusion http://www.tradestudios.com/ Under new management and confusing
CashCowCart http://www.cashcowcart.com Could use polish x
CatalogIntegrator Cart http://www.CatalogIntegrator.com Shell site; could use polish
cf_ezcart ColdFusion Shopping
Cart
http://www.cf-ezcart.com/ Requires ColdFusion x
Colehosting.com http://www.colehosting.com Smallish design/hosting provider
Comersus ASP Shopping Cart
& Mall
http://www.comersus.com Could use polish x $299 for PowerPack (free
version doesn’t do enough)
CoreAve Web Design & Web
Site Hosting
http://coreave.com One man shop
CyberOffice Shopping Cart http://www.smartwin.com.au/cybershop.htm Could use polish X ASP ODBC
DesignCart http://www.designcart.com x
Earth Skater Custom eCommerce http://www.earthskater.net x
EasyCart http://www.easycart.com Too canned for my needs x
EasyStoreCreator http://www.easystorecreator.com Too canned for my needs x
Ecommerce Shopping Cart http://www.roicart.com x
Ecommerce Templates http://www.ecommercetemplates.com Seems a bit simple x
eCOMpal http://www.ecompal.com x
Ecompro http://ecompro.com/content.cfm/shoppingcart x
e-Merchanizer http://www.lynchinteractive.com For smaller businesses; Typo? C’mon. x
Evocative Commerce http://www.evocative.com Hosting provider x
FORT eCommerce http://www.fortsystem.com
Fractal Commerce http://www.rvbs.com/index_en.html 404; can’t find site
Free AFCommerce Shopping Cart http://www.afcommerce.com Could use polish
Freedom Networking Solutions http://www.freedomnetworking.com x
GoECart http://www.goecart.com x
GoldbarOne http://www.goldbar.net x
InOrder http://www.morsedata.com Basically an ERP for the SMB market, which can
include an ecommerce module; Company is Morse Data; HQ in Dover, NH
Internet Commerce Engine 5 http://www.ice5software.com x $3,995 for Small Business
Edition (!!)
iSell Shopping Cart http://www.macdock.com/mac/webhosting/ecommerce/isell.php 404
King Cart Services http://www.king-cart.com x
Make-a-Store http://www.make-a-store.com x Heavy Metal starts at $2,995
MalÕs E-commerce http://www.mals-e.com x
MightyMerchant http://www.mightymerchant.com x
MIVA Merchant http://smallbusiness.miva.com/products/merchant Software, but written in proprietary MivaScript x
MonsterCommerce Pro E-commerce http://www.networksolutions.com/e-commerce/ecommerce-pro-package.jsp Hosted by Network Solutions x
MyCart.net http://www.MyCart.net x
MyWebSiteTool http://www.mywebsitetool.com x
NetStores http://www.netstores.com x
Next Gen Cart http://www.weaveyourwebdreams.com Could use polish x
PDG Shopping Cart http://www.pdgsoft.com Something about this doesn’t inspire confidence x PDG Commerce ranges from $999
to $1,799
PepperCart http://peppercart.com Whaaaa???
Pinnacle Cart http://www.pinnaclecart.com Not seeing anything compelling x x PHP, Apache MySQL $597; $697 for developer
license (includes source code)
POWERSTOREASP http://www.powerstoreasp.com 404
Priusant http://www.priusant.com/products_sc.htm 404; no ecommerce products on site
ProductCart http://www.earlyimpact.com Others seem better x $695
ProStores www.prostores.com Feature comparison at http://www.prostores.com/shopping_cart_features.html x
Quick Cart http://www.quickcart.com x
QuickEStore http://www.quickestore.com Template-based; could be nicer looking x
Ruby Business Platform http://www.rubensteintech.com/ruby Not enough info to tell
SalesCart http://www.SalesCart.com x ASP.NET $499.99
SCartFree SCartServer Online
Shopping Cart Network
http://shopping.scartserver.com Ad-sponsored x
SecureNetShop http://www.securenetshop.com x
SEO-Cart http://www.seo-cart.com x
ShopFactory http://www.shopfactory.com Unclear; too hard to find info x
Shop-Script http://www.shop-script.com Could use polish x PHP
ShopSite http://www.shopsite.com Mostly Hosted; can get Software but expensive;
AviaDirect store uses it
x x Manager $690/Pro $1490
SmartCart http://www.smartcart.com x
StoreFront http://www.storefront.net To get source, need the XE version x x XE is $3,295; the AE version is
$1,995
SunShop Shopping Cart http://www.turnkeywebtools.com x $549.99 for an “owned” license
UltraCart http://www.ultracart.com x
Veracart http://www.veracart.com x
Virtualshop http://www.virtualshop.co.uk Doesn’t seem industrial strength x
VirtualStore 2000 http://www.vs2000.net OMG – they have a Y2K banner on their home page
Volusion http://www.volusion.com x
Zoovy e-Commerce http://www.zoovy.com Seems comprehensive x

March 19, 2009

Adventures in SharePoint Forms Based Authentication – Part 4

Filed under: SharePoint — Tags: , — psterpe @ 2:22 pm

This fourth and last part of my FBA mini-series (here are links to Part 1, Part 2, and Part 3), is about permissions. You’re using Forms Based Authentication which stores the information about all those “outsider” users in SQL Server rather than cluttering your Active Directory. But if these folks are not known to your environment as Windows users, how can you assign them permissions in SharePoint?

The ASP.NET custom role provider makes this easy. What happens is that any roles you define are visible in SharePoint as users. Since a role shows up as a user, you can add a role to a permission group as if it were a person. How does that help us? When we register our users with the ASP.NET Membership provider, we can also assign each user to the role he/she will play.

For example, I recently worked on a project in which some of the users would be credentialed health care professionals and some would not. To represent those users, we could define two roles: “Registered – HCP” for the health care people (it gets as tiresome to type “health care professional” as it does to read it, so I’ll be abbreviating it HCP from now on) and “Registered – General” for everyone else. When we create an account for an HCP, we assign that user to the “Registered – HCP” role; correspondingly, non-health care pros would be assigned to the “Registered – General” role. Those roles, in turn, we will add to SharePoint permission groups so that, say, HCP users can see certain content that General users cannot.

Here’s a short cookbook for setting this up.

  1. Configure your SharePoint web application to use the Microsoft-provided custom role provider that stores role information in SQL Server. Part 1 of this series outlines how to create the SQL database that will hold user information. Here’s what you put in web.config to start using the custom role provider:
    <roleManager enabled="true" defaultProvider="PJSAspNetSqlRoleProvider">
    <providers>
    <add name="PJSAspNetSqlRoleProvider"
        type="System.Web.Security.SqlRoleProvider, System.Web, &#xA; Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        connectionStringName="PJSSqlConnString"
        applicationName="/"/>
    </providers>
    </roleManager>
    

    Most of this is boilerplate. The parts that aren’t are the places where you name things. In the <add name=”"> declaration, supply any name you like for this provider. Make sure to use this same name above in the defaultProvider=”" declaration. The other name you have to supply is a value for “connectionStringName”; this must correspond to the element in the <connectionStrings> section of web.config that describes how to connect to your database server.

  2. From Visual Studio, open the project for the web application you’re working on and launch the ASP.NET Configuration tool to ensure the custom role provider is in use. You launch the tool from the Website menu; it’s Website->ASP.NET Configuration. The configuration tool is a web application; launching it will either start Internet Explorer for you, or if you already have IE running, it will open a new tab. Select the Provider Configuration link, and on the subsequent page, select the link “Select a different provider for each feature (advanced).” You’ll see a page that looks like the illustration below. Under Role Provider, you should see the name you used in web.config; select your provider. If you want, click the Test link to make sure your connection string is correct and that you can successfully connect to the database.
    Provider configuration in the ASP.NET Configuration tool

    Provider configuration in the ASP.NET Configuration tool

  3. Now go to the Security tab of the configuration tool so you can define roles that correspond to the categories your users will fall into. Using my example, we would define two roles: “Registered – HCP” and “Registered – General.” You do this by selecting the “Create or Manage roles” link in the center of the page. The next page you’ll see is illustrated below. Role creation is simple: type a role name where it says “New role name:” and click the Add Role button. This is all you’ll need to do in the ASP.NET configuration tool.
    Role creation with the ASP.NET Configuration tool

    Role creation with the ASP.NET Configuration tool

  4. Navigate to your SharePoint application and login as an administrative user. Navigate to the site or sub-site containing special content that only the HCP folks should see.
  5. Create a permission group named “SpecialContent” and add the “Registered – HCP” role to the group. If you’re unclear on how to create a permission group, here are the basic steps:
    • From the Site Actions menu, pick Site Settings, and then pick Advanced permissions
    • From the New menu, pick New group
    • Fill in a name for the group. In this example, we’re using the name “SpecialContent.” You can accept the defaults for most of the subsequent sections of this page. At the bottom, you will have to choose which level of permisions members of this group can have, e.g., Read, Contribute, etc. Then click the Create button.
  6. You will now be on a page labeled “People and Groups: SpecialContent.” You’ll see that the administrative user you’re logged in as was automatically made a member of the group. Now you want to add the “Registered – HCP” role as a group member. Here’s what to do:
    • From the New menu, pick Add Users. In the Users/Groups textarea, type in “Registered – HCP” exactly and click the “Check names” icon (looks like a little person with a green check mark superimposed). After a few seconds, the text you typed in should become underlined — your “user” was looked up through the role provider and found to be valid.
    • In the Give Permissions sections, select “Add users to a SharePoint group” and pick “SpecialContent” from the dropdown.
    • In the send E-Mail section, be sure to uncheck “Send welcome e-mail to the new users” since our user in this case is a role, not a real person.
    • Click OK
  7. Now navigate to the site or sub-site containing the generally available content that all users can see. Following the same steps above, create a permission group “GeneralContent”, grant it Read access, and add both roles “Registered – General” and “Registered – HCP” (as if you were adding users) to the group.

To test this out, you can use the ASP.NET Configuration tool to create two users and assign them different roles — one user gets the “Registered – HCP” role and the other gets the “Registered – General” role. Then login as each user and verify that you can see only the content you expect to see.

March 17, 2009

Client-Side Javascript Event Handlers for ASP.NET Controls — How??

Filed under: ASP.NET — Tags: , — psterpe @ 6:57 pm

When developing with ASP.NET server controls such as asp:DropDownList or asp:Textbox that you have declared as runat="server", have you struggled with getting an OnChange or OnBlur event handler to be called? If you just do what you would have done with a plain old <input type="text"> or <select> control, which is to give the control an event attribute like onchange="myHandler()", you end up sad — your Javascript event handler never gets called. So how can you get it to be called?

The trick is to assign the event handler to your control programmatically. Here’s an example of doing this in C# as part of the page load event:

<script runat="server">

    void Page_Load(object sender, EventArgs e)
    {
        // Find the control we care about.
        DropDownList ddlist = (DropDownList)FindControl("id_of_dropdown");

        // Give the control our script as an event handler. Note that you
        // don't have to pass a reference to "this" as an argument; if you
        // don't need it, omit it.
        ddlist.Attributes.Add("onchange", "OnChangeHandler(this)");
    }
</script>

<!-- Page content goes here. -->

<script>

    // This is the implementation of our event handling
    // script that was referenced above.

    function OnChangeHandler(obj)
    {
        // Javascript implementation of event handler goes here.
    }

    <!-- Other event handler scripts could go here -->

</script>

<!-- HTML for page goes here -->

In this example, I used the FindControl() method to find the control by id, and then I added my event handler script as an attribute. Although I passed “this” as an argument to my handler, that isn’t required. It was useful for my implementation because I used the same handler for more than one control. If you want no arguments, include the parentheses with nothing between them. Note that the handler is defined in a<script> tag that does not say runat="server" — you want this script to run on the client once your control’s OnChange event fires due to user interaction.

March 10, 2009

Adventures in SharePoint Forms Based Authentication – Part 3

Filed under: SharePoint — Tags: , — psterpe @ 10:23 pm

Part 1 and Part 2 of this series on Forms Based Authentication in SharePoint began painting a mental picture of how FBA works. What might not be clear is how the user gets presented with a form for logging in. Here are a few words (and a couple of pictures) on that.

When you hit the landing page of a SharePoint site that is using FBA, you see a “Sign In” link in the upper right corner:

Sign In link on FBA-enabled SharePoint application

Sign In link on FBA-enabled SharePoint application

This link takes you to an HTML form — the “F” in “FBA” — where you’ll enter your login credentials. You don’t have to edit any pages to cause this link to appear; you get it for free once you have done all the steps to configure your web application for FBA. (There are lots of configuration steps, though, so “for free” is debatable.)

So what about the form where you enter your credentials? Do you have to create it? No, you don’t. SharePoint comes out of the box with a login form, named login.aspx, which is located in the _layouts folder of your installation. (The full path to _layouts is C:\Program Files\Common Files\Microsoft Shared\Web Server Extension\12\template\layouts.) The provided form is, understandably, simple:

Basic login screen included with SharePoint

Basic login screen included with SharePoint

Although you don’t have to create your own, you will probably want to. If you do create your own login form, what’s the magic that causes the “Sign In” link to display your form instead of SharePoint’s form? That happens in web.config, your application’s configuration file. Here’s the relevant section:

<authentication mode="Forms">
<forms loginUrl="/_layouts/login.aspx"/>
</authentication>

If you create your own login form, name it something other than login.aspx, put it in the _layouts folder, and edit this config entry to point to your file. Don’t edit the login.aspx in _layouts no matter how simple your change may be. A mistake in that file will affect all applications sharing this installation of SharePoint.

March 6, 2009

Adventures in SharePoint Forms Based Authentication – Part 2

Filed under: SharePoint — Tags: , — psterpe @ 3:50 pm

As I said in Part 1, you can get FBA fully set up by using Andrew Connell’s cookbook HOWTO: Configuring a Office SharePoint Server 2007 Publishing Site with Dual Authentication Providers and Anonymous Access. Before you dive into the steps, here’s an overview that I hope will give you a mental picture of how the whole thing will hang together once you’re done.

Let’s talk about your users and where you will store their information. They won’t be listed in Active Directory or any other corporate repository. They will be non-employees who will come to your site anonymously and complete a registration form, at which point you’ll create an “account” for them. In this case, the “account” ends up being a record in some storage mechanism, usually a SQL database.

So where does this database come from and how do you manipulate it? There’s good news on this front — you get a lot for free if you’re using SQL Server as your database. Microsoft ships a utility with .NET called aspnet_regsql.exe that will create a SQL Server database containing the tables you need for managing user information. You’ll find the utility in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727. (Even if you’re on .NET Framework 3.x, this utility will still be in the v2.0.50727 folder.)

You could run this utility from the command line, but you don’t have to — double-click it and it will give you a GUI-based wizard. You will need to know the answer to an important question, though, which is what capabilities you want the wizard to create tables for. You definitely want it to create tables for managing user identities, but you can also have it create a table for managing user profile information. Profile information is anything ancillary you want to keep track of beyond username, password, and email address. If you want to store profile info in SQL too, and you probably do, have the wizard create the profile table; for command-line invocation, you’d supply the -p option.

There’s a term you’ll encounter that you need at least a passing familiarity with. You’ll see lots of references to .NET’s provider model. This refers to a layer of abstraction that now exists for storing and retrieving certain kinds of data. The smarties at Microsoft know you need a way to store user info, and so you don’t have to code your own, they have created a set of providers — each provider is basically a set of methods your application can call for storing and retrieving some kind of data. What makes this a provider model is that it’s extensible; it’s just an interface agreement. Any body of code that implements the defined set of methods constitutes a provider. A “membership” provider deals with basic, core user info; a “profile” provider deals with additional, ancillary user info.

OK, so the way you manage user info is by calling either a membership or profile provider whose methods let you do the usual things you’d want to do with user info. But where do the providers come from? Do you have to write your own? Again, no. You can, but Microsoft supplies both membership and profile providers for the case when the user info will be stored in SQL Server.

Let’s recap. Your application will store user data in a database, typically SQL Server. You create that database using a Microsoft-supplied utility. You access those tables using Microsoft-supplied membership and profile providers. So does your app have to contain code that directly calls the provider methods? Nope. Microsoft has also supplied ASP controls that you use in your pages to accomplish tasks like creating a new user account and logging in (or denying) a user. For creating new users, you can use the CreateUserWizard control (asp:CreateUserWizard), and for letting users login, you can use the Login control (asp:Login). When a form containing either of these controls is submitted, the controls take care of calling the appropriate provider methods.

The folks at 4GuysFromRolla.com have written some fantastic articles on the .NET provider model, the related ASP.NET controls, and how to work with all of that in applications. The whole series is worth reading; definitely look at the first article for a great overview.

In the next part of this series, we’ll look in some detail at how you create your own forms for creating new user accounts and logging in existing users.

Older Posts »

Blog at WordPress.com.