LightSwitch for Games Part 3: Creating a Web Interface for your Users
NOTE: Although this series is aimed at small game developers, it is equally applicable to anyone wishing to learn how to use LightSwitch.
In this article, I present a fast crash course in a raft of commonly used LightSwitch techniques. You will learn:
- how to make your web site base URL re-direct to your LightSwitch application
- how to enable remote error viewing in a LightSwitch HTML client application
- how to create new LightSwitch screens (web pages and dialog boxes)
- the difference between Browse, View Detail and Add/Edit Detail screens
- how to add buttons that allow the user to navigate between screens
- how to associate queries to LightSwitch tables and use them as the basis for new screens
- how LightSwitch categorizes data sets into All, Set and Single types, and how each data set type can be used
- how to create local properties (client-side variables that are associated with a single screen)
- how to make table rows and local properties on one screen input parameters to another screen
- how to create a button which executes custom code when clicked
- how to access table rows and local properties from client-side JavaScript
- how to perform custom client-side validation in JavaScript
- what the new LightSwitch ServerApplicationContext API is and how to use it
- how to make generic handlers (.ashx scripts) which can be used from client-side JavaScript to query and change data on the server from a LightSwitch HTML screen
- how to use jQuery to send data (with HTTP POST) and retrieve the response from a web page (in this case, .ashx scripts)
- how to use promises in LightSwitch to store a future result that is currently unknown (generally, the result of a request to the server) and make asynchronous (non-blocking) requests to the server
- how to create a custom control in JavaScript to represent a masked text box (a text box where the contents are replaced with asterisks or dots, like a password entry box)
- how to access the name of the currently logged in user with client-side JavaScript (for rendering purposes)
- how to change the default theme of a LightSwitch HTML application
- how to customize the default images in your LightSwitch HTML client application
- how to customize the LightSwitch HTML client login screen to match the rest of your web site
This article assumes some familiarity with:
- HTTP GET and POST methods
- HTML and CSS
- JSON
- a basic understanding of JavaScript (no jQuery knowledge is needed)
- a basic understanding of C#
Project Goals
In part 2 of this series we looked at how to securely allow anonymous users to create full user accounts for themselves, and how to expose this as a service via OData over HTTP to our future game clients. We looked at the basics of OData, WCF RIA Services, how to assign permissions and roles and how to limit a user to only accessing their own data. In this part, we’ll look at how to create a web-based user interface to allow gamers to edit/update their accounts. Our goals:
- allow the user to log in to a web site using their LightSwitch account
- allow the user to view and edit their profile (except their username)
- provide a change password dialog which requires the old and new passwords, and the new password to be typed twice
- customize the appearance of the site to your own company style
NOTE: You need Visual Studio Professional 2012 Update 2 or Update 3, or Visual Studio 2013 (Preview) to complete the tutorials in this article. Note that in Visual Studio 2013 (Preview), the organization of items in Solution Explorer has changed so some items may be in different places to those indicated below.
LightSwitch Architecture Review
Let’s have a quick re-cap on where everything fits together in our 3-tier LightSwitch project. Figure 1 shows which parts of our software go where and how they interact.
The SQL server stores all of the tables created when you choose Add New Table in a LightSwitch project in Visual Studio. The code we have written in part 2 to enforce permissions checking and keep the internal user database synced with our user profiles table is called business logic and is essentially the code you write in C# or VB.NET that is attached to any tables (or queries) you create. This code always runs on the server no matter where a particular request comes from, so there is no backdoor around executing the business logic. This is good because it makes sure everything stays consistent and that we don’t have to implement different rules or code for different clients.
The LightSwitch server code exposes OData endpoints which represent XML or JSON-formatted views onto each table (or query); by using HTTP GET, POST, PUT and DELETE methods we can emulate the functionality of SQL SELECT, INSERT, UPDATE and DELETE statements respectively, using the OData query syntax discussed in part 2. When we call one of these methods, any associated business logic you have written will be executed – so for example, when we try to add a new user via OData (an HTTP POST method to UserProfiles in our example project), the business logic will check that the current user has permission to do this, and complete the automatic role assignment and other tasks we implemented in part 2.
As discussed earlier, when we come to write the game client itself, all of its communication with the LightSwitch application will be via OData, and so far we have therefore concentrated on learning about how to use OData and make sure our OData endpoints are secure. We will very likely, however, want to provide the user with a web-based interface to perform tasks that we don’t necessarily want to implement straight into our game, for example updating their email address, managing their friends list, browsing leaderboards on a web page and so on. This is where the LightSwitch HTML Client comes in, and is what we shall be focusing on in this article.
LightSwitch HTML Client and Screens
The LightSwitch HTML Cient is a RAD (Rapid Application Development) tool specifically designed to let us very quickly create forms-over-data type applications, that is to say, HTML forms which let you add or edit a row of a specific table in the database. When you deploy an HTML Client from Visual Studio, LightSwitch builds a web site which you can then visit to access these forms. By customising the behaviour of the HTML Client, you can make it do more or less whatever you want.
The fundamental unit of a LightSwitch HTML Client is the screen. A screen is basically the interface over the table data. It can be a web page, a dialog box or a pop-up. There are a few basic types of screens for browsing a list of records (rows), viewing a single record or adding/editing a record, however any screen can be customized with any desired amount of code if the basic templates don’t suit your needs, and we shall look at some examples of this below.
Screens have two main categories of items: data items and layout items. The data items are the table rows, fields and any local variables (known as local properties) you define for the screen. This may include data that has been passed to the screen by another screen or piece of custom code, and these data are known as parameters. Layout items are comprised of the controls used to represent these data items in the web page, as well as various dividers, tabs, bars and any buttons you add. Not all of the data items have to be visible in the layout, and the way the visible items are displayed can be customized. If you want to create custom actions, you can add buttons which execute your own custom code.
It is crucial to understand that unlike the code we have dealt with so far, any code attached to a screen runs on the client, ie. in the user’s web browser, not the server. First this means that you have to write it in JavaScript which does not have complete access to everything on the server (hence the name client-side logic in figure 1), secondly it means that any code you write is only applicable to the HTML Client and not any other clients you write. Screen data and logic is not available as OData endpoints. This has some important consequences for both functionality and security. Specifically, you should never rely on client-side validation of user input; while you may want to do this on the client too to avoid the user waiting unnecessarily or to keep the interface clean, validation of any data sent via a screen or custom JavaScript code should always be done as business logic on the server. Similarly, don’t trust data coming from the client. Suppose you want to fetch some data based on the current user; you might pass a local property with the current user’s name from JavaScript to some OData query on the server which fetches rows matching that user name. But a malicious hacker could change the user name to someone else’s and steal their data! If you are running queries like this, discard the client’s parameters and replace them with server-calculated values (the server always knows who is logged in, for example, so you can easily obtain this information with your server-side business logic). Finally, if there is functionality you want all clients to have (or access through OData), it should be done at the server level by using the various Write Code hooks on your data tables and queries as we discussed in part 2.
Gearing Up
The project we will start with in this article is that we have at the end of method 3 in part 2 of the series. We’ve got our UserProfiles table and all the associated business logic we need to make it work properly. We have also deployed an HTML Client with a simple Welcome screen which you can see by visiting yourwebsite.com/HTMLClient/. We will use this as a starting point to build our user interface.
If you need to create a test user, open up your HTTP request tool like Hurl and do so. As a reminder, we created the special anonymous user __userRegistrant who exists solely to allow other users to be added. An HTTP request like this will do the trick, creating a user sometestuser with password whatever:
URL: yourwebsite.com/ApplicationData.svc/UserProfiles/
HTTP Method: POST
HTTP-Auth: Basic
HTTP-Basic username: __userRegistrant
HTTP-Basic password: __userRegistrant
Headers:
Accept: application/json Content-Type: application/json
Post body:
{UserName:"sometestuser",FullName:"Some Test User",Password:"whatever",Email:"test@test.com"}
Let’s start with the easy stuff first.
Make your web site base URL launch the LightSwitch HTML Client application
By default, the HTML Client is deployed into a sub-folder of your web site called /HTMLClient. There is no easy way to change this, and having to append the folder name to your site URL isn’t very professional, so let’s start by fixing it so that when you visit yourwebsite.com you are automatically re-directed to yourwebsite.com/HTMLClient/. We will do this by adding a page to the project’s root folder which redirects to the desired page, and set this new page as the default page to load.
- Switch to File View in Solution Explorer by clicking the Toggle View icon on the toolbar
- Right-click GameNetwork.Server and choose Add -> Web Form (if Web Form isn’t one of the options, select New Item… and pick it from the list). Call it default (the name is important here, don’t change it to something else!). The new HTML form default.aspx will open in the code editor.
- Right-click in the document or on default.aspx in Solution Explorer and choose View Code. Some auto-generated code will appear, including an empty Page_Load function.
- Inside the Page_Load function, add the following line of code:
Response.Redirect("/HTMLClient/");
This creates the re-direct page. We now have to configure the application to use this page by default when no other path is specified in the URL.
- In the GameNetwork.Server node in Solution Explorer, double-click Web.config to open it.
- In the XML section entitled <system.webServer> there is a <defaultDocument> tag which defines a list of default documents to use when no URL leaf name is supplied. There is one existing item, <add value=”default.htm” />. Don’t change this as it is needed by LightSwitch, but add a new line immediately underneath:
<add value="default.aspx" />
This will cause default.aspx to be loaded if no leaf filename is supplied in the URL. Since we have just created a file with this exact name in the root of the web site, visiting the web site’s base URL yoursite.com will now cause default.aspx to run, which will in turn redirect to yourwebsite.com/HTMLClient/. Test this works by publishing the project and navigating to your base URL. If all went well, you should be greeted by your LightSwitch application’s welcome screen or a login page.
Enable detailed error viewing
By default, when an error occurs in the LightSwitch HTML Client, you get a rather unhelpful error message saying that the web server configuration is preventing an error from being displayed. More specifically, errors will be shown if you connect to the server locally (ie. via localhost) but if you are debugging a LightSwitch HTML Client you have deployed on a remote server and viewing via your own PC’s browser, error details will be suppressed for security reasons.
To enable remote error viewing:
- Switch to File View in Solution Explorer
- In the GameNetwork.Server node, open Web.config
- In the XML section entitled <system.web>, add the following tag:
<customErrors mode="Off" />
You will now be able to see the full error messages, stack traces and so on when an error occurs in the HTML Client. Do not forget to remove this when deploying a production server.
Getting data into the View User Profile screen
At this point we want to make a screen to allow the user to view their own profile, and this is where some of the quirks of LightSwitch start to kick in. Showing all the fields in a single row of data – as is the case for our user profiles where each user has one row in UserProfiles, identified by their UserName – is usually done by creating a View Details screen, which would in our case use the UserProfiles table (entity set) as its source data. To make the screen appear, we have to pass in an existing UserProfile (row / entity) or a flag specifying that a new one should be created from wherever we are navigating from, so that the screen knows which row (entity) to display.
Considering we are going to navigate to a user’s profile via the welcome screen, there are loads of ways to do this, including:
- base the view profile screen’s data on UserProfiles, and either:
- fetch and store the user’s profile row silently on the welcome screen as a data item and pass it as the parameter to the view profile screen if the button is clicked, or
- wait for the view profile screen navigation button to be clicked, then fetch the user’s profile row and pass it as the parameter to the view profile screen
- base the view profile screen’s data on a parameterized query based on UserProfiles which takes a UserName as a paremeter, and either:
- use a local property on the welcome screen populated with the user’s user name as the query parameter value when creating the button in the designer interface, or
- create a custom action (button method) on the welcome screen which sets the query parameter value using code when the button is clicked
- base the view profile screen’s data on a non-parameterized query based on UserProfiles which always returns a single row (entity) corresponding to the current user
Method 1 is a big pain in the ass because you have to do all this row fetching in JavaScript, and more importantly you would have to do it on any screen from where you might want to navigate to the view profile screen. The difference between 1.1 and 1.2 is that the first fetches the profile row when the screen loads; the second fetches it only if the view profile navigation button is clicked. Of the two, method 1.2 is preferred but can introduce lag time between the user pressing a button and something happening while the query result from the server is waited on.
Methods 2.1 and 2.2 are usually the way to go in most cases where security isn’t a concern. Method 2.2 is more flexible because the designer only lets you use local properties as parameters to another screen, whereas with a custom method you can generate the parameters using any code you like. If we are querying tables that anyone can view – such as a game leaderboard – this is a good solution if you need to do client-side querying. For our case of fetching a user’s own profile, it is insecure because it relies on the client-side JavaScript to pass an untampered local property containing the user name of the profile to view to the server. If this is changed, someone else’s user profile could be fetched (if there is no business logic preventing it).
As it happens, we already secured our UserProfiles table with the UserProfiles_Filter function (business logic), ensuring that the currently logged in user can only select their own row. Therefore there is little point in passing a user name parameter from the client anyway, and on top of that, knowing the correct user name from within the JavaScript code in the first place would require fetching it from the server (see below for more on that). Method 3 is perfect in this case: we just use a pre-built query which takes no parameters and uses the filter logic we already wrote in part 2 to always returns the current user’s profile row.
Creating a query to fetch a single user profile
The query we create here will be used as the source data for our view profile screen.
- Right-click the UserProfiles table in Logical View in Solution Explorer and choose Add Query
- Rename the new query that appears from Query1 to CurrentUserProfile
- In the Properties pane, change Number of Results Returned from Many to One.
This query inherits the filter we have already defined on the table in part 2, and since we haven’t made any other modifications, returns a single row (entity) consisting of the profile for the current user, just as we would get if we queried the table directly.
At this point, since the returned row is the same whether we reference the query we just created or the table directly, you may be wondering why we didn’t just use the UserProfiles table directly. The answer is to do with the way LightSwitch categorizes data sets (query results and table rows) and what those data sets can be subsequently used for. Data sets can be one of three distinct types:
- All – this represents all of the rows in a table (after any filter code from TableName_Filter has been applied) and is the data set type you get when you retrieve data from a table directly.
- Set – this represents any sub-set of the rows in a table (possibly all rows, N rows, one row or no rows) and is the data set type you get when you retrieve data from a query attached to a table whose Number of Results Returned property is set to Many.
- Single – this represents exactly one row (no more or less) and is the data set type you get when you retrieve data from a query attached to a table whose Number of Results Returned property is set to One.
And herein lies the rub: you can only create a browse screen based on data sets of the All or Set data set types, and you can only create view screens and edit screens based on data sets of the Single data set type. Since retrieving rows from the UserProfiles table directly will return an All data set type, we cannot use it as the data source for a view screen despite the fact UserProfiles_Filter ultimately forces any direct read on the table to return only a single row (the current user’s profile). Instead, we have to make a new query with no additional code and change its Number of Results Returned property to One so that it yields a Single data set type, as we did above.
Create a View User Profile screen
Immediately, there is a problem. Normally we would now choose to add a screen, select View Details Screen, choose CurrentUserProfile as the screen data and click OK. The problem here is that LightSwitch expects any row to be displayed on a view or edit screen to be passed in as a parameter; that is to say, the view and edit screens themselves do not query to find the row to view or edit, rather they are passed in by the caller (screen or code) that navigated to the view or edit screen. When you create a screen in this way, CurrentUserProfile will be set as an input parameter and a compatible row (ie. a previous query result from CurrentUserProfile) must be passed in. If you disable the behaviour by unchecking the Is Parameter box on the CurrentUserProfile data item in the screen designer, you just end up with an empty, un-populated row.
The only screen type which runs a SELECT-style (row retrieval) query in this way is the Browse screen type. If we had followed the steps above, then deleted the CurrentUserProfile item and re-added it, it would be added as a regular query to be executed while the page loads, effectively transforming the screen into a Browse screen type. So rather than wasting our time going through all that, we might as well just create a Browse screen in the first place.
Wait! Didn’t you just say that we made the CurrentUserProfile query so that it would return a Single data set type that is compatible with View Details screens? So why are we now making a Browse screen? Yes, but we will also be allowing the user to modify their profile row with an Add/Edit Details screen type and that also requires a Single data set type item, which we will be passing in as a parameter from the query result from CurrentUserProfile via our Browse screen. Also, if we query UserProfiles directly on a Browse screen, even though there will only be one row because of our filter, the row will be presented as a ‘list of one’ which can be clicked on in its entirety (if you don’t understand what I mean, try it!).
If this all seems pretty bizarre and non-sensical, bear in mind that LightSwitch has no way to know that we have filtered UserProfiles such that it only ever returns one row, or that the row returned is always the same for a given logged in user. In traditional data environments (ie. master-detail record views and editing), LightSwitch’s paradigms work really well – as we will see when we come to deal with leaderboards and such – but for anonymous users to be able to make accounts and then edit them in isolation is not really what LightSwitch is designed for, so we are working a little bit out of the box here.
With explanations over, here are the actual steps:
- Right-click on HTMLClient in Logical View in Solution Explorer and choose Add Screen.
- Select a Browse Data Screen, set the name to ViewUserProfile, leave the Screen Data as None and click OK.
- Add the query to fetch the current user’s profile: Click Add Data Item… in the toolbar, select the Query radio box, choose CurrentUserProfile and click OK.
- Build the layout by dragging UserName, FullName and Email (but not the Password field) from the data item pane into the layout designer underneath the Command Bar item.
Link two screens together with a button
Now we create a button on the Welcome screen to allow the user to visit the View User Profile screen:
- Double-click the Welcome screen in Solution Explorer
- Click on Rows Layout in the layout designer, then click the Add buton. Choose New Button… from the menu
- Select the Choose an existing method radio box and select showViewUserProfile from the drop-down
Now re-publish your project and log in. If all has gone well there should be a Show View User Profile button on the Welcome screen, and when you click it, your user profile should be displayed on a new page.
Appearance
Don’t forget you can change the names of any buttons, controls or titles you like. I’ve called my project “Simple Gaming Network” and made these changes:
- changed the Display Name of the Welcome screen to “Simple Gaming Network – Account Management”
- changed the Display Name of the Show View User Profile button to “My SGN Profile”
- changed the Display Name of the ViewUserProfile screen to “My Gamer Profile”
Create a screen to allow the user to edit their own profile
Compared to all the conceptual roadblocks of viewing a single profile, allowing the profile being viewed to be edited is elegantly simple. We will create an Add/Edit Details screen using the data from our ViewUserProfile page as the source data:
- In the ViewUserProfile screen designer, click on Rows Layout then click Add and choose New Button…
- Select the edit option under the Choose an existing method drop-down (CurrentUserProfile.edit will be displayed in the drop-down box). The Navigate To box will be set to (New Screen…), click OK to proceed.
- Change the screen name to EditUserProfile and click OK.
You have now created an edit profile dialog and a button on the ViewUserProfile screen which causes the dialog to pop up when the button is clicked. Notice that if you click on the UserProfile data item in the EditUserProfile screen designer, its Is Parameter check box is ticked, meaning that it will receive a UserProfile entity (row) from the calling screen, ie. the one selected by our CurrentUserProfile query when ViewUserProfile is displayed.
- We don’t want the user to be able to edit their user name so click the control type drop-down arrow on the left of User Name in the EditUserProfile screen designer and change it from Text Box to Text. This effectively changes the way the field is rendered to be a static label rather than a text edit box.
- We don’t want the password field to be displayed (it will be blank anyway due to the way we designed the table) so click on it in the screen designer and press Delete (or right-click and choose Delete from the menu) to remove it from the screen.
- Re-arrange the layout to your liking by dragging the fields around. By default the fields are shown in two columns; I removed the second Rows Layout group so that there is just one column, and changed the Display Name of the screen to Edit My Profile. You should end up with something like the display in figure 2.
Note on email addresses: Email address fields can be edited with the Email Address Editor control which ensures a valid email address is entered. If your Email field has been displayed with a Text Box by default, you may need to change the type of the Email field in the table from String to Email Address. Do this by opening the UserProfiles table designer and change the type of the Email field with the drop-down selection box in the Type column. This will automatically change the edit control type to Email Address Editor in the EditUserProfile screen, and the view control type to Email Address Viewer in the ViewUserProfile screen (the viewer allows email addresses to be clicked as a hyperlink; if you don’t want this behaviour, change the control type to Text).
Enabling the user to change their password
This is where things get a bit tricky. What we would like to do is present the commonly seen update password-type dialog where the user must enter their old password correctly, then a desired new password into two boxes (to confirm they have typed it correctly) (see figure 3). The password must then be changed in the intrinsic user database of the LightSwitch application (SecurityData) without changing the UserProfiles table, yet at the same time we need the user’s UserName so that we know which user to update. We need to provide both client-side validation (for a smooth user interface experience) and server-side validation (for security). If there is a validation problem we would like to present an error message in the dialog itself.
If we create a standard Add/Edit Details screen, LightSwitch provides a Save button (the floppy disk-in-a-circle icon by default) which will attempt to update the row being edited in UserProfiles. While we could certainly add server-side code to UserProfiles_Inserting to process a password change, this is messy because there is no field in UserProfiles in which to supply a previous password (nor would we want one as this makes no sense from a table design standpoint), and the actual write to UserProfiles would need to be suppressed. You can fudge the supply of an old and new password by merging them with a delimiter character in the Password field supplied to UserProfiles_Inserting, but this is really a sloppy way of coding and we are better off creating a new execution path altogether for password changes.
To avoid the automatic Save button appearing, we must therefore create a new Browse or View Details screen instead and create a button with custom code to perform the password change. This will run in JavaScript on the client and make a request to the server to change the user’s password. Which screen type to choose? Well, since the server will restrict only the user who is using the dialog box to be able to change their password (ie. the user name will not be supplied from the client as it cannot be trusted), we don’t need to track in the client who is doing the editing, therefore we don’t need any fields from any tables, so we can use a Browse screen.
Now we know what screen type to use, let’s focus on making the user interface first and the implementation details afterwards.
- Right-click on the HTMLClient folder in Logical View in Solution Explorer and choose Add Screen…
- Select Browse Data Screen, change the screen name to ChangePassword, leave Screen Data blank and click OK to create the change password dialog.
- In the ChangePassword screen designer, click Show As Dialog in the screen designer so that the change password screen appears as a dialog box rather than a separate web page.
- We will use Local Properties to provide edit fields for the old and new passwords. You can create a local property by clicking Add Data Item… in the screen designer toolbar, selecting Local Property, selecting a type, entering a name for the property and clicking OK. The property will then appear in the data item pane of the screen designer. Create four local properties, all of String type, called OldPassword, NewPassword, ConfirmNewPassword and ValidationResult. The first 3 will be editable fields for entering the passwords, the 4th property will be a text label used to report validation errors. Set the first 3 items to Is Required but not the ValidationResult.
- Drag each item from the item data pane into the layout designer. Set the three password fields as Text Boxes and the validation result as Text so that it appears as a label. Place the Validation Result item at the bottom, underneath the three password edit fields.
- Change the Label Position property of the Validation Result label to None. This prevents the actual text ‘Validation Result’ from being displayed above the error message. Then uncheck the Is Visible property. This prevents the field from being rendered initially. This is purely for good layout: if the field is visible but empty, a large empty space will be included between the bottom password edit field and the update password button we shall add. If some validation error is to be displayed, we will make the field visible from our JavaScript code later.
- Create a button, select Write my own method, call it UpdatePassword and click OK. This will create a button attached to custom code that we can fill in later to perform the password change.
- Now switch to the ViewUserProfile screen as we need to add a button here to navigate to the ChangePassword screen. Click on the Rows Layout group, click Add, and select showChangePassword from the Choose an existing method drop-down list. Change the Display Name of the button in the properties pane to something else if you like.
If all has gone to plan, your screen designer should now look something like figure 4.
Creating a Generic Handler
We can’t call our existing OData endpoints to change a user’s password because they expect complete rows of data. Instead we have to create a new page which accepts the password data in an HTTP POST request and sends a response depending on whether or not the password change succeeded.
Fortunately, thanks to generic handlers, this is very easy to accomplish:
- Switch to File View in Solution Explorer
- Expand GameNetwork.Server. You should see a folder called Web. Right-click on this and select Add -> New Item…
- From the Add New Item window, scroll down until you find Generic Handler and click on it once. Change the name at the bottom to ChangePassword.ashx and click Add.
This adds a new endpoint at yourwebsite.com/Web/ChangePassword.ashx with some stub code that just returns Hello World by default.
NOTE: If you want to change the handler’s class name after you have created it, you must also right-click on the .ashx file in Solution Explorer, select View Markup to display the XML for the handler and change the Class attribute of the WebHandler to match the new class name.
The LightSwitch ServerApplicationContext API
New releases of LightSwitch include the ServerApplicationContext API which makes it a cakewalk for custom code files in your LightSwitch project to access all of the data sources in your application. All you need to do in your C# code file is add one line of code:
var serverContext = ServerApplicationContext.CreateContext();
You can now use serverContext just as if you were using this in your main LightSwitch code. Let’s see how we can use this to implement a change password feature in our generic handler.
Writing the change password server-side code
First the code, then the explanation:
public void ProcessRequest(HttpContext context) { using (var serverContext = ServerApplicationContext.CreateContext()) { context.Response.ContentType = "text/plain"; try { serverContext.DataWorkspace.SecurityData.ChangePassword( serverContext.Application.User.Name, context.Request.Form["OldPassword"], context.Request.Form["NewPassword"]); context.Response.Write("Password changed successfully"); } catch (Exception e) { context.Response.Write(e.Message); } } }
(you can leave the rest of the auto-generated code as is)
When you attempt to fetch the web page ChangePassword.ashx, the ProcessRequest method is invoked to handle the request. The HttpContext object supplied contains all the details of the HTTP request, crucially including any GET query string or POST parameters. These are stored in the Request.QueryString and Request.Form dictionaries respectively.
Here we accept two POST parameters – OldPassword and NewPassword – which as you might expect, contain the user’s current and desired new passwords respectively (validation that the user typed the same password into the ConfirmNewPassword field on the ChangePassword screen will be done in JavaScript on the client, and the duplicate value in ConfirmNewPassword is simply discarded afterwards as there is no further use for it).
The SecurityData object provides the ability to change any user’s password in the intrinsic SecurityData database via the ChangePassword method. It takes three arguments:
- the user’s user name
- the current password
- the new password
In the code, we call this method with the currently logged in user’s name stored on the server (to avoid allowing malicious client users to change passwords other than their own) and the supplied POST parameters. If there is a problem, an exception is thrown by ChangePassword which we capture and output it’s error text to the browser (or calling JavaScript code that made the HTTP request in this case). Otherwise, we output a success message. The method context.Response.Write(string) simply appends the supplied string to the end of the HTTP response.
That’s all there is to it. If you want to test the handler standalone, you can use Hurl to send a POST request as an authenticated user and check the response.
Now all we need to do is hook up our Update Password button in the HTML client to call the generic handler on the server with the text fields supplied by the user. First we will need some background on a few JavaScript techniques.
Using jQuery to send an HTTP POST request
LightSwitch HTML clients are built on jQuery Mobile, a popular 3rd party JavaScript library which provides a huge library of functions to make working with JavaScript easier. You don’t have to understand all the nasty details to use it though, so let’s just dive into a snippet of code showing how to use jQuery to make a web request:
function doSomeRequest() { $.ajax({ type: 'post', data: {}, url: '../Web/SomeHandler.ashx', success: function success(result) { /* do something on success */ }, error: function error(jqXHR, textStatus, errorThrown) { /* do something on failure */ } }); };
This code sends an HTTP request to the URL specified in url, with a method specified in type (which can be get or post). The code does not block (it’s asynchronous) so you don’t have to wait for the HTTP response to come back. When the response comes, the success or error functions will be called. result will contain the body of the HTTP response.
If you want to use a GET request and add a query string, you can add it directly in the URL. If you want to supply parameters in a POST body, you can change data as follows:
data: "FirstParam=" + encodeURIComponent(firstValue) + "&SecondParam=" + encodeURIComponent(secondValue)
and so on.
Using Promises in LightSwitch JavaScript code
A promise is a variable or object which represents a value that is currently unknown, but will be determined later. The idea of promises is that you can define a function with some long-running operation (like waiting for an HTTP response), use the promise immediately (while its value is still unknown), and when the operation has completed, the value stored in the promise is filled in.
Promises can be created in LightSwitch in a few different ways, but for direct use we employ the msls.promiseOperation method. This method takes one argument which is the function to execute. The function receives one argument known as a future object. When your long running operation completes, you call either complete() or error(errorThrown) on the future to notify the promise that the value is ready (or there has been a problem).
LightSwitch allows you to use promises as return values at various places in your code. Here is how to return a promise that wraps the jQuery HTTP request above:
return msls.promiseOperation(function (operation) { $.ajax({ type: 'post', data: "FirstParam=" + encodeURIComponent(firstValue) + "&SecondParam=" + encodeURIComponent(secondValue), url: '../Web/SomeHandler.ashx', success: function success(result) { operation.complete(); }, error: function error(jqXHR, textStatus, errorThrown) { operation.error(errorThrown); } }); };
Notice that we receive the future in operation and call the complete() and error() methods on it when the HTTP response has been received.
Writing the change password client-side code
We are now ready to write the code which connects the HTML Client to our ChangePassword.ashx server-side script.
In the ChangePassword screen designer, right-click the UpdatePassword method we created earlier in the data item pane and select Edit Execute Code. If you recall, this method will be executed when the user clicks the Update Password button after entering their current and desired new password.
First we’ll deal with the client-side validation:
myapp.ChangePassword.UpdatePassword_execute = function (screen) { var oldPassword = $.trim(screen.OldPassword); var newPassword = $.trim(screen.NewPassword); var confirmPassword = $.trim(screen.ConfirmNewPassword); screen.findContentItem("ValidationResult").isVisible = true; if (newPassword == "" || oldPassword == "" || confirmPassword == "") { screen.ValidationResult = "All fields are required"; return false; } if (newPassword != confirmPassword) { screen.ValidationResult = "Passwords do not match"; return false; }
First we use the handy jQuery function $.trim to remove any leading and trailing spaces from what the user has typed. Local properties and data sources from the screen are exposed directly as properties of the screen object, making them easy to access from JavaScript.
Then we make the ValidationResult property visible in the dialog box (but it does not contain any text yet). By using the screen.findContentItem(string) method, we can access and modify any layout item on the screen. Here we set the isVisible property to true to enable ValidationResult to be rendered.
Now we perform the client-side validation. First we check that all the fields have been entered. Although we set the Is Required property on these fields anyway, if any of them contain only whitespace they will slip through the net.
Next we check that the NewPassword and ConfirmNewPassword fields contain the same text, and if not we fill the ValidationResult label with some notification text and return.
NOTE: You will likely want to add extra validation here, such as checking the password lengths and complexities, as the error messages returned from the server are not very user-friendly.
Once we have gotten this far, we can send a password change request to our ChangePassword.ashx handler, which will run the server-side validation, change the password and return a result as discussed above. We wrap the request in a promise object and return it directly:
return msls.promiseOperation(function (operation) { $.ajax({ type: 'post', data: "OldPassword=" + encodeURIComponent(oldPassword) + "&NewPassword=" + encodeURIComponent(newPassword), url: '../Web/ChangePassword.ashx', success: function success(result) { operation.complete(); screen.ValidationResult = result; }, error: function error(jqXHR, textStatus, errorThrown) { operation.error(errorThrown); } }); }); };
As you can see, we supply the fields the user has entered as OldPassword and NewPassword parameters in the POST request body.
The promise object is returned to LightSwitch, and when the request has completed any changes made to ValidationResult are rendered on the screen automatically.
If you have gotten this far, congratulations! Users can now edit their passwords via the web interface. There is, however, a problem. If you publish the project and try it, you will find that the password text is displayed un-obscured in the text fields. We want to mask the text entry so that each character appears as an asterisk (*) or dot. Unfortunately, LightSwitch does not provide such a text edit box among the built-in controls, so we have to roll our own.
Creating Custom Controls in LightSwitch
Custom controls are HTML content items (just like the labels and text boxes we have been using so far, and other standard controls) except that we have to provide the rendering code for them ourselves. This is done in JavaScript. We will change the standard text boxes we are currently using for the password edit fields into custom controls which display the text edit box with the contents replaced with asterisks (*), as is the standard for masked password text boxes.
The first step is to open our ChangePassword screen in the LightSwitch Designer, then click on the drop-down arrow next to the icons on each of the three fields Old Password, New Password and Confirm New Password and change the content type from Text Box to Custom Control.
Now click on any of the three fields and in the Properties box click Edit Render Code. This will open the ChangePassword.js file we were editing in the previous section and add a function definition at the bottom for you:
myapp.ChangePassword.OldPassword_render = function (element, contentItem) { // Write code here. };
The element argument indicates where in the DOM model (the browser and JavaScript’s internal representation of the HTML page) the content should be rendered. The contentItem argument is a JavaScript representation of the field we are editing in our LightSwitch screen, just like the one we fetched with screen.findContentItem(“ValidationResult”) earlier.
We will write a function PasswordBoxStyleRender which renders a password edit box, and will be called by the render code for each of the three password fields on the page. To accomplish this, replace the above code with:
myapp.ChangePassword.OldPassword_render = function (element, contentItem) { myapp.PasswordBoxStyleRender(element, contentItem, "OldPassword"); }; myapp.ChangePassword.NewPassword_render = function (element, contentItem) { myapp.PasswordBoxStyleRender(element, contentItem, "NewPassword"); }; myapp.ChangePassword.ConfirmNewPassword_render = function (element, contentItem) { myapp.PasswordBoxStyleRender(element, contentItem, "ConfirmNewPassword"); };
(the third argument will be the HTML ID to use for the input field, and can be chosen arbitrarily)
To create the control we need to perform four steps:
- Create the HTML for the control
- Add it to the document (web page or dialog)
- When the contents of the control change, update the corresponding LightSwitch data field
- When the contents of the LightSwitch data field change, update the contents of the control
The code looks like this:
myapp.PasswordBoxStyleRender = function (element, contentItem, fieldName) { var password = $('<input id="' + fieldName + '" type="password" />'); password.appendTo($(element)); password.change(function () { if (contentItem.stringValue != password.val()) contentItem.stringValue = password.val(); }); contentItem.dataBind("stringValue", function (newValue) { password.val(newValue); }); };
There is quite a bit of new material here so let’s look at it line by line.
The first line in the function body creates an HTML <input> tag which is the text edit box. This will render with exactly the same appearance as a standard LightSwitch text box because the CSS for the page defines a default appearance for all <input> tags, so we don’t need to worry about the control looking different to the standard controls. Crucially, setting the type attribute to password causes the contents of the field to be masked with asterisks – a built-in feature of HTML’s <input> tag.
The call to $() invokes jQuery to create a DOM representation of the text version of the <input> tag we just created. This is the representation we need in order to insert it into the HTML document.
password.appendTo($(element)); again invokes jQuery to insert the tag into the document as a child of element. LightSwitch sets element for us so that the custom control shows up at the right place on the page.
The password.change() line defines what will happen when the user modifies the text in the <input> field. We compare the value of the field to its corresponding LightSwitch field, and update the LightSwitch field if they don’t match. In this way, whenever the content of the <input> field changes, the LightSwitch field will be updated to have the same value automatically, thus keeping them in sync.
The contentItem.dataBind() line does the same thing but the other way around: if the LightSwitch field value changes, password.val(newValue) changes the <input> field to have the same value.
This technique of updating one of two pieces of data to match the other whenever one of them changes is called bi-directional data binding and is very common in applications with user interfaces. In a Windows appliation for example, a Windows TextBox control may be bound to a C++ string and a CheckBox control may be bound to a bool in your application.
If you now re-publish the project, log in and go to the change password dialog, you will see that the password fields are masked with asterisks, but if you change your password, log out and try to log in again, the password has been correctly changed.
Displaying the username of the logged in user
The client-side JavaScript API provides no way of displaying the name of the currently logged in user, but it is sometimes nice to be able to display this, for example you might want a small “Logged in as MyUsername” label in the corner of your pages. Fortunately, using the generic handler technique discussed above, it is easy to add this functionality. For illustration, we will add the logged in user’s username to the Welcome screen.
Follow the steps in Creating a Generic Handler above to create a new handler (.ashx file) called GetUserName. Replace the ProcessRequest function as follows and leave the rest of the generated code as is:
public void ProcessRequest(HttpContext context) { using (var serverContext = ServerApplicationContext.CreateContext()) { context.Response.ContentType = "text/plain"; context.Response.Write(serverContext.Application.User.Name); } }
This creates a very simple endpoint on our site – /Web/GetUserName.ashx – which simply outputs in plain text the name of the currently logged in user. We can now use the techniques we learned above using jQuery and promises to call our ChangePassword handler from the client to call GetUserName and display the result somewhere on a page.
Go to the Welcome screen in the LightSwitch Designer and choose the created method from the Write Code button in the toolbar at the top. This code will be executed when the screen is loaded. Replace the generated code with the following:
myapp.Welcome.created = function (screen) { msls.promiseOperation(CallGetUserName).then(function PromiseSuccess(PromiseResult) { currentUser = PromiseResult; screen.details.displayName = "Simple Gaming Network - " + currentUser; }); }; function CallGetUserName(operation) { $.ajax({ type: 'post', data: {}, url: '../Web/GetUserName.ashx', success: operation.code(function AjaxSuccess(AjaxResult) { operation.complete(AjaxResult); }) }); }
The function CallGetUserName uses the jQuery AJAX technique discussed earlier to call our GetUserName generic handler (with no parameters – the data field is empty) and return the result. The myapp.Welcome.created function – which is called when the Welcome screen loads – creates a promise which calls CallGetUserName, fetching the result (the name of the logged in user) into a temporary variable named currentUser then sets the title of the page to show it (note how you can modify screen.details.displayName to change a screen’s title).
Re-publish the project and you will find that when you log in, your username will be displayed in the title of the Welcome screen after a short delay while it is fetched from the server.
Changing the overall appearance of the web interface
Let’s round off this epic adventure in LightSwitch user interface creation with a look at some simple tweaks you can apply to give your site a slightly more unique appearance.
1. Changing the HTML page title and loading screen title
In File View, open GameNetwork.HTMLClient -> default.htm. This page is used as the base template for all the pages and dialogs in the HTML client, so modifying it will modify the appearance of all pages.
You can change the HTML page title (that which appears in the browser window’s title bar) by modifying the <title> tag. I used “Simple Gaming Network” (without quotes) as the text here.
You can change the text that appears at the bottom of the loading screen by modifying the contents of the <div> inside <div class=”ui-bottom-load”> in the page body. Once again, I used “Simple Gaming Network” as the text.
2. Changing the site theme
LightSwitch HTML clients use the so-called light theme by default. A dark theme is also included and you can use this by changing the stylesheet references in default.htm from:
<link rel="stylesheet" type="text/css" href="Content/light-theme.css" /> <link rel="stylesheet" type="text/css" href="Content/msls-light.css" />
to:
<link rel="stylesheet" type="text/css" href="Content/dark-theme.css" /> <link rel="stylesheet" type="text/css" href="Content/msls-dark.css" />
Of course, you might want a customized theme. LightSwitch supports CSS files (“themes”) made with the jQueryMobile ThemeRoller web tool. With this tool you can visually tweak the styles you want to use on the site, then download an auto-generated CSS file which replaces the default LightSwitch CSS. For more information see Creating a Custom Theme with ThemeRoller in the jQuery online documentation. You don’t need any jQuery knowledge to do this, just an idea of how CSS works!
For the sake of this article, I have simply switched the project to use the built-in dark theme.
3. Customizing the log in page
The log in page uses its own code and its own CSS styles separate to the rest of the HTML client. The file can be found in File View under GameNetwork.Server -> LogIn.aspx. A couple of things you can customize include:
- Change the contents of the <h1> tag to change the text above the username and password boxes from Log In to something else (I used Sign in to Simple Gaming Network)
- You can add text underneath the log in fields by adding custom HTML after the close of the <asp:ValidationSummary> tag. For example, to add a copyright message you could use something like:
<p style="padding-top: 20px; font-size: small">© Katy Coe 2013 - <a style="color: #dddddd" href="http://www.djkaty.com" target="_blank">www.djkaty.com</a></p>
- The CSS in the <head> section can be changed to make the page theme look the same as the other pages. For completeness, the required CSS to match the dark theme is as follows:
<style type="text/css"> /* Here you can customize your login screen */ html { background: #191919; } html, body, .labelStyle { color: #ffffff; } h1 { color: #ffffff; } .requiredStyle { color: #FF1B1B; } input.buttonStyle { color: #ffffff; background-color: #555555; border: 1px solid #ababab; } input.buttonStyle:hover { background-color: #666666; } input.buttonStyle:active { background-color: #888888; } .textBoxStyle { color: #ffffff; background-color: #3e81af; border: 1px solid #ababab; } .failureNotification { color: #ffdddd; } /* login layout styling */ * { margin: 0px; } html { height: 100%; width: 100%; } html, body { font-family: 'Segoe UI','Frutiger','Helvetica Neue',Helvetica,Arial,sans-serif; font-size: 16px; font-weight: normal; } h1 { font-family: 'Segoe Light','Segoe UI Light','Frutiger','Helvetica Neue',Helvetica,Arial,sans-serif; font-size: 40px; text-align: left; letter-spacing: -1pt; font-weight: normal!important; margin-bottom: 12px; } .accountInfo { width: 95%; max-width: 310px; position: absolute; top: 50%; margin-top: -144px; left: 50%; margin-left: -155px; } .labelStyle { font-family: 'Segoe UI Semibold', 'Frutiger','Helvetica Neue Semibold',Helvetica,Arial,sans-serif; font-weight: 700; } .requiredStyle { font-size: 24px; line-height: 14px; height: 12px; vertical-align: bottom; margin-left: 5px; } input.buttonStyle { font-family: 'Segoe UI','Frutiger','Helvetica Neue',Helvetica,Arial,sans-serif; padding: 5px 10px; font-weight: bold; border-radius: 0px; font-size: 16px; cursor: pointer; -webkit-appearance: none; } .textBoxStyle { background-image: none; font-size: 16px; display: block; outline: 0; height: 36px; padding: 1px 8px; margin: 0px; width: 100%; max-width: 292px; line-height: 36px; } .submit-login { margin-top: 10px; } .rememberme { margin-bottom: 10px; } input[type=checkbox] { margin: 0px 6px 0px 0px; vertical-align: -1px; cursor: pointer; } .checkStyle label { font-size: 15px; } </style>
4. Changing the header logo and splash screen logo
The user-logo.png and user-splash-screen.png files contain the graphics used in the top-left corner of each page (by default) and the centre of the loading splash screen page. These can be found in File View under GameNetwork.HTMLClient -> Content -> Images. Simply replace them with graphics of your choice to customize them.
Notice that the files must be PNGs and they must be of the correct size. A useful free online tool for converting and scaling to PNG can be found at Images.My-Addr.com for those of us artistically challenged folks who don’t have an image editor installed.
Wrapping up
I hope you found this guide comprehensive and helpful! Now that we have a working web interface that allows users to view and edit their profiles, we shall in Part 4 see various ways to write code in C++ to handle log in and registration from our game code so that a user doesn’t need to use their web browser to sign up or sign in, as well as generic table queries, inserts, updates and deletes plus a simple C++ client class framework for your game network code.
Until next time!
References
If you’d like more details on the topics covered above, here are some pages I found useful while researching this article:
Signed-In Part 4 – Authentication and Branding by Andy Kung @ MSDN Blogs
Global variables in LightSiwtch @ MSDN Social
LightSwitch – Creating a Masked Password TextBox Part 2 @ DotNetIM
Custom Controls and Data Binding in the LightSwitch HTML Client by Joe Binder @ MSDN Blogs
HUY Volume II – Visual Studio LightSwitch Advanced JavaScript Examples @ LightSwitchHelpWebSite.com
How to: Modify an HTML Screen by Using Code @ MSDN
Full Control LightSwitch (ServerApplicationContext And Generic File Handlers And Ajax Calls) @ LightSwitchHelpWebSite.com
not interested
thank you so much, this is really helpful to me right now, im learning a lot with your posts 😀
You’re most welcome 🙂 These comments make it all worthwhile!