Adding Users With SharePoint Forms Based Authentication

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.

Tagged with: , ,
Posted in SharePoint

Lite Survey of eCommerce Platforms

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
Tagged with:
Posted in eCommerce

Adventures in SharePoint Forms Based Authentication – Part 4

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.

Tagged with: ,
Posted in SharePoint

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

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.

Tagged with: ,
Posted in ASP.NET

Adventures in SharePoint Forms Based Authentication – Part 3

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.

Tagged with: ,
Posted in SharePoint

Adventures in SharePoint Forms Based Authentication – Part 2

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.

Tagged with: ,
Posted in SharePoint

Adventures in SharePoint Forms Based Authentication – Part 1

In SharePoint, the out-of-the-box path to happiness is for your SharePoint users to also be Windows users. But what if you want an Internet-facing SharePoint site to serve outsiders, people who are not members of your organization, who do not have a Windows account with you, and whom you don’t want to give a Windows account? The answer is to implement Forms Based Authentication (“FBA”). I recently did this for a client and found it to be intricate; not so much difficult as, well, involved. Fortunately, other bloggers have documented the steps, but that’s “bloggers” plural — you’ll hit snags or want to do something a bit special, and that will send you scouring the net for tips. I thought it might be useful to pull together the tricks I needed (with references to where I found them, of course) to get my FBA implementation working. This will be a series of posts.

For starters, let’s do a quick terminology orientation — what is meant by “Forms Based Authentication?” If you aren’t a Windows development jockey (and I am not), you may think that every time a system prompts you to login, you’re filling out a form, so describing an authentication method as “forms based” sounds like a pointless thing to say. I just filled in my username and password — wasn’t that a form?  Well, no. “Form” here refers to an HTML form displayed by an Internet browser, the kind of thing with text boxes and a “submit” button. It doesn’t refer to the dialog box that Windows pops up to gather your login credentials for a site. If you were going to see one of those, you’d be expected to have a Windows account, and the whole idea here is handling users who don’t have a Windows account on your servers.

What makes the authentication “forms based” is that you need to create (and get the system to display) an HTML form for gathering user credentials, and further, when that form is submitted to the web server, some special logic (not the Windows operating system) will have to look up the credentials in some repository of user information (not the one where Windows stores its user information). So this kind of authentication isn’t just “forms based;” the form is at the front end collecting the username and password, but there are other required parts — a separate repository of users, and some logic for looking up users in that repository.

In subsequent posts, I’ll talk about the basics of getting FBA working. I’ll point out some of the problems I had to solve, and I’ll give references to bloggers whose work I relied upon. For starters, anyone wishing to implement FBA should read HOWTO: Configuring a Office SharePoint Server 2007 Publishing Site with Dual Authentication Providers and Anonymous Access by Andrew Connell. This is a wonderful and indispensable reference on how to set up FBA. At the top of the article, he says it was updated in December 2006, but don’t think it’s too old to be useful — it isn’t.

Tagged with: ,
Posted in SharePoint