| Colin's profileMSN and Windows LivePhotosBlogLists | Help |
|
May 28 Beginning JavaScript and CSS Development with JQuery - Review
Technorati Tags: JavaScript, JQuery, Book Review, Wrox Press, Beginning JavaScript and CSS Development with JQuery, Richard York Every so often in computing a language extension or new technology comes along that is the buzz of the development community. JQuery is one of those. Lots of developers are talking about it and using it, even Microsoft are including native support for it in their upcoming Visual Studio 2010 release. With that in mind I thought it was about time to look at what all the hype is about and learn the language. JQuery is really a library of JavaScript routines that makes your life easier. In a way it is akin to the Microsoft AJAX extensions that originally were a separate release but are now baked into the .Net Framework. Microsoft’s AJAX extensions went beyond just handling AJAX calls and put a whole framework behind JavaScript to make it easier for .Net OO developers to code in JavaScript. JQuery does the same thing, makes it easier to code certain things in JavaScript and is cross-browser compatible taking out one of the headaches a lot of developers inevitably come across. Beginning JavaScript and CSS Development with JQuery is a beautiful full color book from Wrox Press weighing in at just over 500 pages. Richard York assumes that you are already familiar with JavaScript and also CSS and therefore this is not a book for the complete beginner, only a beginner with JQuery. The book is well laid out with chapters devoted to each of JQuery’s main areas, selectors, AJAX, Manipulating the DOM, Event handling etc. etc. with numerous Appendixes that can be used as a quick reference. Each chapter has numerous examples explaining the particular JQuery concepts and constructs being discussed culminating in one big example program at the end of the chapter that brings together everything discussed in that particular chapter finally ending with a quick question exercise to make sure that the keywords and constructs discussed have truly sunk in. This is an excellent way for a beginners book to be constructed as it really hammers home what has been discussed during each chapter. One major annoyance I have with Richards writing style however is that he comes across as a Mac snob and throughout the book takes snipes at Microsoft and especially Internet Explorer. Whilst some of this may be deserving it is off-putting and a detraction from what is ultimately a good book. If you can gloss over the snobbishness I would actually recommend this book for beginners of JQuery. The full color screenshots highlight the various examples superbly and Richard does give you a lot of useful information without overwhelming you with every nuance of JQuery. May 22 Windows Live Web Toolbar Mashup – MessyTwitTechnorati Tags: Windows Live, Windows Live Web Toolkit, Windows Live UI Controls, Mashup, jQuery, Personal Messages, Twitter A lot of people nowadays have Twitter accounts, a Facebook account etc. and if you’re reading this then I’m also assuming you have a Windows Live account and some interest in Windows Live Services. Each of these have their own “Personal Message” where you can tell people what you are currently doing and there-in lies the problem. You have to visit each of these places to update them individually. Why not have a central place where you can update them all? The good news is that you can. There are various ways of doing this, whether through a website or a Silverlight application. Basically anything that can use web services. With Microsoft’s recent introduction of the Windows Live Web Toolbar, I thought I’d do just that and show you how easy it is. I will be building upon my previous articles regarding the new Windows Live controls so please go check them out if you haven’t already as I’ve discussed a lot of the code present in this mashup in previous articles. So first up, lets take a look at our actual web page :- Here you can see that I’ve sectioned off the page. At the top of the page we will display the users Windows Live login picture along with their personal message that is tied to that account. Just below that will be the list of their contacts. This is an easy way to display the personal messages of their Windows Live contacts. The next section will be their Twitter account showing their own personal messages that they have sent to Twitter and at the bottom you will see the Windows Live Web Toolbar that we will use to sign people into their Windows Live accounts and hook in with the other Windows Live Web Controls that we display on the page. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:msgr="http://messenger.live.com/2009/ui-tags"> <head runat="server"> <title>MessyTwit</title> <link type="text/css" rel="Stylesheet" href="MessyTwit.css" /> <script type="text/javascript" src="http://www.wlmessenger.net/api/3.0/loader.js"></script> <script type="text/javascript" src="JScript/JQuery.js"></script> <script type="text/javascript" src="JScript/Messenger.js"></script> <script type="text/javascript" src="JScript/Twit.js"></script> <script type="text/javascript"> Microsoft.Live.Core.Loader.load(['Messenger.UI', 'Messenger.UI.Styles.Core'], null); </script> </head> <body> <msgr:app privacy-url="Privacy.html" channel-url="Channel.htm" application-verifier-token="<%= appVerifier %>" token-url="RefreshMessengerToken.aspx" onauthenticated="onAuthenticated"></msgr:app> <form id="form1" runat="server"> <div id="msgrDisplayPic"> </div> <hr /> <div> <msgr:contact-list word-wheel-enabled="false"></msgr:contact-list> </div> <hr /> <%-- <div id="ctrls"> <msgr:personal-message cid='$user' id='persmsg' editable='true'></msgr:personal-message>
</div>
--%> <div id="msgs"> <div id="TwitCreds" class="msgHidden"> <span>Twitter Username </span> <input type="text" id="acct" /><br /> <span>Twitter Password </span> <input type="password" id="pwd" /><br /> <span id="TwitLogin">Login to Twitter</span> </div> </div> <hr /> <div id="persMsg" class="msgHidden"> <input type="text" id="msg" value="Enter a personal message" size="80" /> <span id="msgsend">Send</span> </div> <div> <msgr:bar sign-in-enabled="true"></msgr:bar> </div> </form> </body> </html> Above is the default.aspx page. As you can see, it’s not very complicated at all. Most of this was covered in my previous articles so I’ll just give a quick rundown here. At the top we include various Javascript files which we’ll cover later and we tell the page to load the messenger controls when the xhtml page has loaded. Next we have the messenger application section in which we supply various required variables such as where our privacy and channel pages are and we also set an event for when the user has authenticated (logged in to) their Windows Live Account. After that there is a placeholder for where we will put the users display picture and Windows Live personal message. Following the horizontal rule (I’ve only put that in the display physically break the page into sections) we have the Windows Live Contact List control. It’s as simple as that one line of markup to display a very nice list of the users contacts. The next section on the page is where we will gather the users’ Twitter credentials so that we can interact with their Twitter account. Towards the bottom we have a simple input box that the user will type their personal message in to and finally we display the Windows Live Web Toolbar at the bottom of the page. For completeness here is the code behind for the default.aspx page :- using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using WindowsLive;
using System.Diagnostics;
public partial class _Default : System.Web.UI.Page {
WindowsLiveLogin wll = new WindowsLiveLogin(true); public string appVerifier {
get
{
return wll.GetAppVerifier();
} } } I’ve covered this before so won’t go into it here. The corresponding style sheet associated with this page is very basic. Here is MessyTwit.css :- input#msg
{
border-width: 0px; } .msgHidden
{
display: none; } .msgShow
{
display: block; } We hide the border around the personal message input box and we define two classes to hide or show various elements of our page. There are two main Javascript files that cover all of the functionality on the page. The Messenger.js file I’ve basically covered before :- var userToken = null; var msgrUser = null; function onAuthenticated(e)
{
msgrUser = e.get_user(); msgrUser.add_signInCompleted(SignInCompleted); } function onUserConsentCompleted(e)
{
userToken = e.get_consentToken(); } function SignInCompleted(sender, e)
{
if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success)
{
if (typeof (InvokeOnUserSignIn) === 'function') {
InvokeOnUserSignIn(); } } else
{
OnUserSignedOut(); } var userAddress = msgrUser.get_address().get_address();
var usercid = msgrUser.get_identity().get_cid();
var signinframe = document.getElementById("msgrDisplayPic"); var tag = Microsoft.Live.Messenger.UI.Tags.TagsFactory.createTag('display-picture', { 'cid': usercid, 'presence-enabled': 'true', 'size': 'Large', 'logo-enabled': 'true' });
$("#msgrDisplayPic").append(tag);
var tag2 = Microsoft.Live.Messenger.UI.Tags.TagsFactory.createTag('personal-message', { 'cid': '$user', 'editable': 'false', 'id':'Persmsg2'}); $("#msgrDisplayPic").append(tag2);
$("div#TwitCreds").removeClass("msgHidden"); $("div#TwitCreds").addClass("msgShow"); } function OnUserSignedOut()
{
//add code for sign-out.
} As a quick overview we have the onAuthenticated event handler that we tied up in the messenger application block on the default.aspx page. As mentioned this gets fired when the user logs in to their Windows Live account. Once they login we capture that event and extract the user object then setup another event to capture when sign-in has been completed. The API fires off multiple events at differing stages of login. Another one of those events is when the user gives consent to send information back to our web site. In this event handler we simply capture the consent token that is sent. Once the user has completed sign-in we make sure that sign-in was successful (i.e. the correct username and password were supplied). Next we create the users display picture and also their personal message. These are two different Windows Live controls. The documentation for the Personal Message control says that it must have the CID of the user in order to display their personal message. This makes sense. However if you pass in the CID as we do for the display picture, the Personal Message control does not work. You HAVE to pass the $user string that is a reserved variable created by the Windows Live API. Next we simply use some jQuery to insert these controls onto our page. We also now use some jQuery to show the Twitter credentials portion of our web page which is hidden until after the user signs in to their Windows Live account. After the user has signed into their Windows Live account, this is what you will see. On this screenshot, my personal message is not displayed as I don’t have a personal message currently tied to this account however the control is present as you’ll see later. The second of the two main Javascript files, twit.js, contains the jQuery functions for manipulating the page. This is all new so I’ll break it down a bit :- $(document).ready ( function()
{
$('span#msgsend').click
( function()
{
var acct = $("input#acct").val(); var pwd = $("input#pwd").val(); var msg = $("input#msg").val(); So in the ready section we attach an event handler to the “send” text next to the personal message input. I just used text here but it could just have easily been a button.
When the text gets clicked we first get the username and password for the users Twitter account and we also get the personal message they want to send.
$.post("/MessyTwit/TwitterService.asmx/SendTweet",
{ 'account': acct, 'pass': pwd, 'msg': msg },
function(_xml)
{
$('div#msgs').text(''); $xml = $(_xml); $xml.find("status").each
( function()
{
$('div#msgs').append("<div><img src='" + $(this).find("profile_image_url").text() + "'/>" + "<span> " + $(this).find('text').text() + "</span></div>"); } ); } , 'xml');
To finish off this AJAX call we define an anonymous function that gets called once the call returns to our script. Here we first blank out any Twitter messages that may already be displayed on the screen. Then we take the XML that is returned by the AJAX call and parse it. We loop through the XML parsing each individual message. For each message we take the display picture of the user and also the text of the message and display it in the placeholder we marked out in our default.aspx page. The final part to this is just saying that the AJAX call will use XML rather than JSON or some other format. if (msgrUser)
{
msgrUser.get_presence().set_personalMessage(msg); } So we have posted the users personal message to their Twitter account, next we need to do the same for their Windows Live account. In the code above, this is exactly what we do. We first check to see that the user object isn’t null and if it’s not then we simply call the set_personalMessage method to update their Windows Live personal message. $('input#msg').val(''); } ); Finally for this event handler we do some cleanup. Here I’m blanking out the personal message input box ready for their next message.
$('input#msg').focus
( function()
{
$(this).val(''); } ); $('input#msg').blur
( function()
{
if (this.value == '') {
this.value = 'Enter your personal Message'; } } ); Here is the personal message input box before the user has clicked on it :- and after they have clicked on it :- The next two event handlers we define are tied to the personal message input box. All that this does is blank the input box when the user focuses on it (clicks or tabs into it). If the user clicks or tabs away from it and the input box is blank then we insert the message “Enter your personal Message” into it so the user knows where to type. $('span#TwitLogin').click
( function()
{
$('div#persMsg').removeClass('msgHidden'); $('div#persMsg').addClass('msgShow'); $('div#TwitCreds').removeClass('msgShow'); $('div#TwitCreds').addClass('msgHidden'); var acct = $("input#acct").val(); $.post("/MessyTwit/TwitterService.asmx/GetTweet",
{ 'account': acct },
function(_xml)
{
$xml = $(_xml); $xml.find("status").each
( function()
{
$('div#msgs').append("<div><img src='" + $(this).find("profile_image_url").text() + "'/>" + "<span> " + $(this).find('text').text() + "</span></div>"); } ); } , 'xml');
} ); } ); The final event handler is tied to the text I’ve displayed on the page for logging in to their Twitter account. Again, I’ve just used some text but it could just as well be a button control. When the user clicks on the login text we hide the twitter login credentials area and display the personal message input box. Next we get the users username and again make an AJAX call. This time only to retrieve the users’ own Twitter messages. Finally we use the same anonymous function as we had above to parse through the returned XML and extract the display picture and message from each message returned and display them onscreen. As soon as the user enters their Twitter credentials you should see the following :- Now you can take this code and expand on it so that when the user clicks on the login text you actually do a check against their Twitter credentials and display an error message if they are not correct. Twitter has lots of web service calls that you can use to make this code more robust. See the Twitter API for more details. This really only leaves one piece left, our web service :- using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Net;
/// <summary> /// Summary description for TwitterService /// </summary> [WebService(Namespace = "blah")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class TwitterService : System.Web.Services.WebService { public TwitterService () {
//Uncomment the following line if using designed components
//InitializeComponent();
} [WebMethod]
public XmlDocument GetTweet(string account) {
string url = "http://www.twitter.com/statuses/user_timeline/" + account + ".xml?count=5"; HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(url); Request.Method = "GET";
WebResponse Response = Request.GetResponse();
StreamReader Reader = new StreamReader(Response.GetResponseStream()); string Result = Reader.ReadToEnd();
Reader.Close(); XmlDocument doc = new XmlDocument(); doc.LoadXml(Result); return doc;
} [WebMethod]
public XmlDocument SendTweet(string account, string pass, string msg) {
string url = "http://www.twitter.com/statuses/update.json"; NetworkCredential creds = new NetworkCredential(account, pass); string EncodedText = "status=" + HttpUtility.UrlEncode(msg); HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(url); Request.Method = "POST";
Request.Credentials = creds; Request.ContentType = "application/x-www-form-urlencoded";
Request.ContentLength = EncodedText.Length; Request.UserAgent = "MessyTwit";
Request.Timeout = 10000; System.Net.ServicePointManager.Expect100Continue = false; Stream reqStream = Request.GetRequestStream();
StreamWriter Writer = new StreamWriter(reqStream); Writer.Write(EncodedText); Writer.Close(); WebResponse Response = Request.GetResponse();
StreamReader Reader = new StreamReader(Response.GetResponseStream()); string Results = Reader.ReadToEnd();
Reader.Close(); Response.Close(); reqStream.Close(); XmlDocument doc = GetTweet(account);
return doc;
} } This is a standard .Net web service except that we need to un-comment the line that reads :- [System.Web.Script.Services.ScriptService]
This is so that our Javascript can actually make calls to it. The first method that we have simply calls the Twitter web service and gets the top 5 messages the user has posted and returns that as XML back to our AJAX call. The second method is the one that actually posts the message to Twitter. After we have posted our message to using the Twitter API we make the call to the first method to retrieve the new updated top 5 messages. The main line to take note of in this method is :- System.Net.ServicePointManager.Expect100Continue = false; Without this line any posts to Twitter will fail with a 417 return code. This only applies to posts, not to gets. And there you have it. One web page that updates both a users’ Windows Live personal message and also their Twitter account as can be seen in the screenshot below. The Windows Live Personal-Message control now contains my latest message at the top of the page and also my latest Twitter entry has the same message :- This should give you an idea of the kind of mashups that you can do using the new Windows Live Messenger UI controls. I’ve expanded this example to include Facebook although I didn’t put the code here but it’s fairly easy to do and the above code will give you all the basics you need. You can expand it to include any other site that does some kind of personal message you wish. One final point should be noted. The Windows Live Web Toolbar includes it’s own area in which a user can type their personal message. When the personal message gets posted you can capture the event using the following :- // attach event for future changes
This also applies to the Personal Message UI control if you allow it to be edited. This way you can handle whether the user types their personal message into the Web Toolbar or using a control that you’ve specifically placed on the page. May 13 Windows Live UI ControlsTechnorati Tags: Windows Live, Windows Live Messenger UI, UI Controls, How to, display picture control Following on from my previous tutorial on how to integrate the Windows Live Web Bar into your site, we’ll build upon this and show you some of the other UI controls that you can use in your site. One thing that a lot of people might want to do is display the signed-on users’ display picture somewhere on the site other than in the web-bar. As with most everything Microsoft does in the web space there are a couple of ways of doing this. Lets start by doing it the easy way. Take the default.aspx page from the last tutorial and add a new <div> element under the <form> tag :- <form id="form1" runat="server"> <div id="msgrDisplayPic"></div> Into the div tag we will add the display picture UI control. This control has a few different options you can specify but only one is actually required, the cid of the user who’s image you wish to display. How do you get the cid of the signed-in user? Again there are a couple of ways of doing this but we’ll take the easy route again. For use with their tags on the aspx page, Microsoft has thoughtfully provided a variable that automatically gets populated with the users’ cid, $user. So the base tag that we need to enter into our div looks like this :- <msgr:display-picture cid='$user'></msgr:display-picture> When you run the page you’ll notice the default “blank” user image as nobody has signed in yet :- When a user signs in then this image will change to the users’ display picture as seen above. There are a couple of options that you can add to the control. You can adjust it’s size by supplying it with a size attribute. Size can take three pre-defined values, Small, Medium and Large. By default the control is set to Medium size. You can also tell it whether to enable or disable presence information (the “glow” around the border of the image). By default this is set to true so presence information is displayed. Finally you can also state whether a logo is displayed in the bottom right of the image or not by setting the logo-enabled attribute. The default is set to false. When set to true, a small logo is displayed :- So here is our final display-picture tag :- <msgr:display-picture cid='$user' presence-enabled='true' size='Large' logo-enabled='true'></msgr:display-picture> Microsoft also allow you to override their default style sheets for this control giving you a range of options :-
And that’s it, you can now add the users’ display picture to anywhere on your site with one tag. If you want more fine grained control over the process or you wish to do all this programmatically Microsoft also allows you to do this. First remove the <msgr:display-picture> tag from the default.aspx, but keep the <div> tag in place. Next create a Javascript file and add a reference to it at the top of the default.aspx :- <script type="text/javascript" src="JScript/Messenger.js"></script> In this Javascript file we will programmatically insert the users’ display picture control onto the site. I’ve added a couple of extra event handlers to this file that you don’t necessarily need but are very handy to have as well. First off we want to declare a couple of global variables, one to hold the users’ token that gets generated when they give their consent to sign-in to your site, and the second is to hold the actual user object :- var userToken = null; var msgrUser = null; Our first event handler gets fired once the user gives his permission to sign into your site :- function onUserConsentCompleted(e)
{
userToken = e.get_consentToken(); } All that we’re doing here is getting the consent token that gets generated and storing it in our global variable for use later. The next event handler gets fired once the user has authenticated (i.e. the windows live credentials are correct) :- function onAuthenticated(e)
{
msgrUser = e.get_user(); msgrUser.add_signInCompleted(SignInCompleted); } Here we capture the actual user object (the most important object you use when dealing programmatically with Windows Live Web Messenger) and store it in our global variable. Then we add an event handler for when the sign-in process has completed. function SignInCompleted(sender, e)
{
if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success)
{
if (typeof (InvokeOnUserSignIn) === 'function') {
InvokeOnUserSignIn(); } } else
{
OnUserSignedOut(); } var usercid = msgrUser.get_identity().get_cid();
var signinframe = document.getElementById("msgrDisplayPic"); var tag = Microsoft.Live.Messenger.UI.Tags.TagsFactory.createTag('display-picture', { 'cid': usercid, 'presence-enabled': 'true', 'size': 'Large', 'logo-enabled': 'true' });
signinframe.appendChild(tag); } The SignInCompleted handler first of all checks to see if authorization was correct (did the user really sign in or did they perhaps have a typo and sign-in wasn’t completed successfully). If sign-in was not correct then we make sure our page reflects that by calling our sign-out routine which should house any clean-up code. If the user has successfully signed in then we grab their cid. Remember, this is the one required field for the display-picture control (and most other controls as well). Next we get a reference to our <div> element where we want to place the display-picture control. All Windows Live Messenger UI Controls are instantiated through the TagFactory object. Here we simply call the method createTag and pass into it two values. The first is the type of control that we wish to create, in this case the display-picture control. The second value is a set of key:value pairs containing any attributes that we wish to supply. In the example code above we are simply re-creating the <msgr:display-picture> tag we created earlier on the page itself. The only difference here is that we are passing in the cid manually. Finally we simply add this control into the <div> tag we got a reference for earlier. The final entry in our Javascript file is the event handler for when the user signs out :- function OnUserSignedOut()
{
//add code for sign-out.
} And that’s it, you will end up with exactly the same screen as we did earlier, only this time you have gained knowledge in how to do it programmatically. The basics on what we have covered here applies to all the other Web Messnger UI controls. For example, if we wanted to add the contact-list control to our page we simply add the appropriate tag within our page or do it programmatically :- <msgr:contact-list word-wheel-enabled="false"></msgr:contact-list> And this results in the following :- There are a lot of UI controls to explore. You can find more information about what’s available, their methods and parameters etc. over on msdn :- http://msdn.microsoft.com/en-us/library/microsoft.live.messenger.ui.tags.aspx April 28 Windows Live Web Toolbar – How to integrate into your siteA few months ago at Mix 09, the Windows Live Team launched a number of exciting new additions under the Windows Live Web Toolkit. One of which was the Windows Live Web Toolbar. This toolbar allows you include presence information into your site as well as other social experiences. Unfortunately due to the limitations of Windows Live Spaces I cannot post the article on here. I’ve posted the article on Liveside.Net, so please hop over and see the article in full there. April 21 UI Controls for Web MessengerMicrosoft have now made your job even easier if you are developing your own Messenger application on your website. Not only have they produced an excellent API that I have written about before but they have now released a whole bunch of controls. If you head over to http://msdn.microsoft.com/en-us/library/dd570052.aspx you will find all the new controls listed by section. Examples of some of these controls are :- The Messenger Contact Picker control
The Messenger Display Name control
The Messenger Profile control
and of course the new Messenger Web Bar. There are lots more controls to be had. Go check them out and look here in the coming weeks on tutorials on how to use them in your own sites. April 06 Data-Driven Services with Silverlight 2 - Review
Developers and businesses alike are starting to see the benefits of Silverlight 2 and more and more are starting to take advantage of it’s capabilities. Most web-developers have a good knowledge of Microsoft’s .Net platform, they also have good knowledge of HTML, CSS and all the other technologies surrounding the web. However presentation fairly much still is lacking on the web front, even with the acceptance of AJAX, it’s still like an amateurs representation of the Mona Lisa compared with the desktop cousins. The first version of Silverlight was mainly geared towards video and audio and was restricted to the Javascript language. With the introduction of Silverlight 2, you now have pre-built controls at your disposal, C# and VB.Net programming access and a whole lot more. The painting has now gone from being amateurish to professional but still not quite the original. Now however developers can re-use their existing .Net Framework knowledge without having to learn another language like Flash and have a rich user experience. One thing that you will definitely need is data. Data is king in any application and gaining access to your data is what this book is all about. Silverlight 2 introduced various ways in which you can gain access to your data, through RESTful web services, SOAP, RSS, AtomPub, POX, JSON, direct from a backend database etc. etc. You can write your own data layers or bind directly to controls and can even use LINQ. John Papa takes you by the hand and shows you how to do all of the above scenarios and more. The book is very well written, easy to read and understand and no filler (something I’m really disliking about certain books, putting filler in just to increase page count). There are whole chapters dedicated to each of the main ways to gain access to your data and bind it to your front end application with numerous examples throughout including how to read Tweets, access and consume Amazon RESTful services, the ADO.Net Entity Framework etc. If there is a way to access data you will find out how to incorporate that into your Silverlight 2 application with this book. And of course, you can also take the techniques John has so masterfully expounded on in this book and use them in your standard ASP.Net web sites as well. If you’re a Silverlight developer, this is one of those must have books. You’ll refer to it over and over. Even if you’re a standard ASP.Net developer it is worth taking a look through this book as most of the content can very easily be applied to a normal ASP.Net site. Technorati Tags: O'Reilly, Data-Driven Services with Silverlight 2, Book Review, Silverlight 2, REST, SOAP, RSS, AtomPub, POX, JSON, Entity Framework, John Papa March 20 Using Linq to amend a Word documentI got handed an interesting little problem as an emergency project yesterday. The basis of it was that I had to dynamically insert content into a word document (user name, email address, phone number, physical address etc.). This had to be done from a web site so I couldn’t just add some code to word and distribute the word document. So the basis of my solution was to get hold of the word document that needed to be amended and insert bookmarks where ever the data had to be inserted. Then split out the word document into it’s constituent parts (a docx file is really just a zip document). When you unzip a word document (by renaming the extension from docx to zip then unzipping with something like winzip) you end up with the following documents and folders :- What we’re really interested in however is what’s in the word folder :- In this folder you’ll find document.xml which is the file that actually contains all the text etc. for the word document. So what we want to do is create a little program that parses the document.xml file for all of our bookmarks, insert the appropriate content and save the new file out. It’s not as hard as what it seems. Here’s where Linq comes in very handy. Just copy the unzipped word document files into a folder under the website root :-
Here’s the code from my quick test program :- using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Xml;
using System.Xml.Linq;
/// <summary> /// Summary description for WordDocx /// </summary> public class WordDocx {
public WordDocx()
{
} public void AmmendDocx(string username) {
IEnumerable<XElement> bookmarks; //List<XElement> bookmarks = new List<XElement>();
XDocument doc = XDocument.Load("E:\\WordDocSite\\Docx\\word\\document.xml"); XNamespace namesp = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; bookmarks = doc.Descendants(namesp + "bookmarkStart");
int count = bookmarks.Count();
foreach (XElement el in bookmarks) {
string attr = el.Attribute(namesp + "name").Value; if (attr.ToLower() == "username") {
el.AddAfterSelf(new XElement(namesp + "r", new XElement(namesp + "t", "value"))); } } doc.Save("E:\\WordDocSite\\Docx\\word\\document2.xml");
} }
This isn’t the final code that I wrote, just a quick proof of concept for manipulating the content of the word document.
In the final code you simply do a select…case statement against the “w:name” attribute and have routines for adding the appropriate content. Once all of this is done you simply need to zip up all the files within the Docx folder. For this I used the NSoftware IP*Works Zip library (which is excellent and very easy to use. NSoftware has a whole range of tools to help you with things like this. I would recommend that you go check out what they have to offer). Once it’s zipped up, rename the extension back to docx and do a simple response.redirect to point to the file so that the end user can download it. March 07 The Hectic Life of an MVPThings are starting to insane around here. I’ve got enough projects and things to do to last me for about a year and I’ve got around about a month or slightly longer to do it all in. My main projects at the moment are :-
This is all extra curricular activities, on top of this I’ve got my normal job to do and also lead a life (over the next 3 weeks for example I have 3 birthday parties to attend, one each weekend. A number of weddings to go to that are coming up etc.). I guess there’s going to be some long nights ahead. A quick word about Windows 7There’s a big buzz about Windows 7 at the moment, Microsoft’s upcoming client Operating System that’s currently in both Technical and Public beta’s. I’ve been running Windows 7 for a few months and my I think it’s a good operating system (yes there are bugs but it’s still in Beta). There are some obvious improvements and UI changes, a lot of fixes over previous operating systems and more. My biggest feeling about it however is that it’s not this all new great operating system from Microsoft. If you remember back a few years, Microsoft released Windows XP SP2. This service pack was mainly a security overhaul but also introduced a lot of new features and enhancements and made windows XP a stable and good operating System. I still use Windows XP as my main operating at work for development. Well Windows 7 is, to me anyway,very much akin to XP SP2 but for Windows Vista. It’s not a great new operating system but builds on and improves Windows Vista to the point that makes it a very good operating system. I’m not trying to take away from Vista, I’ve used that on various computers since the very first beta but it did leave a very bad taste when it was launched, mainly because it wasn’t ready for release due to third party driver support. A lot of my peripheral hardware did not work with Vista and I suspect this was the same for quite a few users as well. Drivers did come out for most of my peripherals but there was still some that won’t work even today (Netgear WG111 wireless adapter being one of them). Once you get a bad initial impression of something then it’s hard to recover from that. Along with that the amount of resources that the operating system itself took up was amazing, especially when compared to Windows XP. Well a lot of this has been fixed in Windows 7, there are a lot more drivers available now and it doesn’t use as many system resources as Vista did. Things are looking up. I’m actually typing this blog in Windows Live Writer running on my Windows 7 laptop at the moment. Lets see what the next release (Release Candidate) brings and take things from there. February 06 Raxco PerfectDisk 10 - ReviewEverything mechanical requires maintenance at some point. You take your car in to get serviced according to it’s schedule, you get it’s oil and filters changed every 3000-6000 miles. Well the same applies to your computers hard drives. Every so often you should defragment them. When you install programs onto your hard drive, windows places the files according to free space available on your drive. This free space may not be contiguous, you may have a chunk of free space here, a chunk there so the operating system will install the program as best it can according to what is free and where. Over time you will delete programs and this frees up that space however in doing so you eventually fragment your drive. This causes things to slow down as the heads on your drive have to travel further and lowers the expected life of your drive as well. When you de-fragment your drive, the defragmentation program takes all the programs and puts them contiguously on our drive, speeding it up and at the same extending the life of your drive. It all puts all the free space on your drive together making it easier and quicker to install programs. I’m a bit wary of defragmentation programs due to the wear it will cause on the drive however I do realize that it’s something that should be done and therefore every so often I will get out my favorite defragger and run it. Raxco has just released their newest version of PerfectDisk 10, so I thought I’d give it a spin as I’ve not defragmented my drive in a while. They have multiple versions for all the different types of computer uses out there, server, home, professional and I’m glad to see they also have a version specifically for Windows Home server, something I’ve just purchased recently and am still in the process of fully configuring. PerfectDisk 10 is easy to install and has a small footprint (I’m running PerfectDisk 10 Professional for this review). During install you can set various options as to which drives you’d like it to scan (PerfectDisk 10 supports not only internal hard drives but also USB and Firewire drives as well as USB Flash drives). Once installed you get an easy to use front end. After running the Analyze function you can see the state of a particular drive. As you can see from the above screen shot, my drive state isn’t the best in the word and does actually need defragging, so this is a perfect test for Raxcos’ new product. After the analyzer has run it displays the results to you in various formats. The above screenshot shows the summery view. Below is the actual drive sector view. There are lots of options at your fingertips including do not run when on battery power, the close behavior of the program, how aggressive you want it to scan etc. etc. The actual time it takes to defrag your drive obviously depends on the size of your drive but also on how badly it is currently fragmented. After defragging your drive(s) you PerfectDisk gives you various statistics including how much performance increase you’re likely to see. So for the drive I just defragged I should see a roughly 40% increase. PerfectDisk 10 does one thing and one thing only, it defrags your hard drive. However what it does it does extremely well, safely and you will notice an increase in your systems performance after using it if you have a badly fragmented drive. For the price, nothing comes close. I would highly recommend that PerfectDisk 10 to anyone. I know that I will be using it. Technorati Tags: Raxco, PerfectDisk 10, Fragmentation, Review, Defragrementation, Defrag, Performance Increase, Hard Drive January 28 Why do books do this?
Have you ever gazed longingly at a supercar? Say a Ferrari F430. The sleek lines, the speed and power, the potential. Someone hands you keys to your dream machine. You get in and luxuriate in the saffron feel of the interior, insert the keys ready to hear the roar of the V8, start the car and all you hear is a putt, putt of a small 1.4L engine inside. The emperor has all the clothes but no body to put them on. This is exactly what this book (and others) is like. Now to be fair this isn’t a full of the above book as I’ve still to read a lot of it but I’m curious as to why books do this? Have a lot of completely unnecessary filler. I got this book yesterday (it is one of a few that I am currently reading) and started to read it. I was in-fact looking forward to getting this book. This is (as far as I know) the only book on K2 Blackpearl that is currently out there and Blackpearl is a technology that I use so I thought great, I’ll be able to learn more about this product. Open the book and the first chapter is an introduction to Blackpearl and process driven applications. Now this is all fine and well if you know absolutely nothing about process management but if you want to learn about that then a couple of chapters in a book that is meant to be dedicated to something else is a complete waste of space and time reading. The discussions on BPM are so high level as to be almost useless. OK you think, surely the next chapter will actually discuss Blackpearl. Nope more BPM, and the next. In-fact the whole first section of this book is dedicated to BPM (the first 4 chapters), but discusses it at such a high level as to be useless to anyone who does not have any BPM experience. Knowing a little can be far more dangerous than knowing nothing at all. If you want to learn about BPM, go study it. It’s going to take a lot more than 4 fairly think chapters on the subject to know about it. Therefore the whole first section of this book is basically just filler, an unnecessary waste. There seems to be more and more books coming out that are doing this kind of thing. You read chapters or sections and gain no useful knowledge from them. Now in defense of other publishers, the worst offender of this seems to be Wrox Press. Having a small introduction chapter is no crime. Giving you a background of the product or technology, what the book hopes to cover etc. but having multiple chapters or whole sections that are really just filler should be a crime. It is the customers time that you are wasting. The rest of the book does however look to be promising and I’ll give a full review soon, but please publishers, stop with the unnecessary filler. If I wanted filler I’d buy more jam and cream for my scone. January 15 Coding 4 Fun – Book Review
Coding 4 Fun is one of these strange books that’s not really a tutorial on a specific topic like most IT books are but it shows you some of the things you can actually accomplish with relatively little work. Hopefully the ideas and solutions presented in the book will give you ideas on how to enhance or further use either the technologies that it covers in the book or go out and research your ideas for integrating new technologies. Some of the projects covered in the book are hardware projects which if you’re not comfortable using a soldering iron then perhaps you shouldn’t try whilst others are pure software solutions. The range of topics covered is very diverse from programming a game using XNA for the Xbox 360 or Popfly to turning your computer into a streaming media machine to creating an interactive whiteboard with a Wii remote control. As the title suggests these are all fun projects for home computer enthusiasts and not something you would likely use in the workplace. The average length of each project presented in the book is 3-6 hours. The book is laid out very well, with a quick introduction to each chapter explaining what you will need, roughly how long the project should take, the rough cost (if any) etc. Followed by a quick introduction as to what you should get out of the project then onto the actual project itself. Each project is split into manageable chunks with good reasonable explanations given. The explanations aren’t in-depth but give you enough information that you can hop onto the web for a fuller explanation if you like (for example in the WHSMail project it does not cover the API calls that you are actually making but gives you enough information that you can go to MSDN and find the specific API and related ones if you wish). The authors take you through the project step by step and include diagrams and photographs where necessary. These are especially helpful on the hardware projects. Overall this is a great book for coders that are looking for something to do or to give them ideas about what is possible in a relatively short time period. My main concern about this book is that it may have limited appeal and isn’t really a reference manual. That is not to say that the content of the book isn’t worthwhile but I’ve marked down my rating taking this into consideration. December 18 WSS 3.0 SP1 upgrade has bug – Sharepoint Configuration Wizard FailsI’m currently installing Sharepoint Server 2007 in a small farm environment and have come across a bug that I’d previously run into however forgotten about, and have just spent the last couple of hours tracking down the problem yet again. Therefore hopefully this small posting will save people some time. The bug applies if you have a small server farm. We are setting up a development environment that has two front end servers and a separate search and index server. The two front end servers are load balanced and as such I setup MOSS Central Config web application on both of these servers, however the Search and Index server is purely just that and therefore told the initial install NOT to setup Central Admin on this server. That all works ok however when you come to upgrade your MOSS farm to SP1, the first thing you have to do is upgrade to WSS SP1. After installing the necessary components on your farm you need to run through the Sharepoint Products and Technologies Configuration Wizard in order for the upgrade to take. When you run the Configuration Wizard on a server that does not have central admin installed on it then you will run into a problem. This can be attributed to either the initial MOSS installation not placing a file in the appropriate directory or the WSS SP1 upgrade having a bug in which it insists on that particular file being present when it’s not. The file in question is web.config which lives in the 12 hive under Template\Layouts folder. If you install Central Admin on your server then the web.config file will be present in this directory however if you do not install Central Admin on your server (as in the case of our Search and Index server) then the initial MOSS install will not place this file in the Template\Layouts folder. This causes WSS 3.0 SP1 upgrade to fail as it specifically looks for this file. There is an easy fix to this problem however, simply copy the web.config file over from one of your front end servers, re-run the Configuration Wizard and everything will work fine. Technorati Tags: Sharepoint 2007, MOSS 2007, WSS 3.0, Service Pack 1, WSS 3.0 SP1, MOSS SP1, Configuration Wizard Fails Web.config missing December 12 Everything you know about CSS is Wrong - Review
This is a very strange title for a book and I was extremely intrigued by it. I’ve been laying out web pages for years now with CSS and for a book to come along and suggest that everything I had been doing was wrong was a bit of a bold statement. This is quite a short book weighing in at just over 110 pages and really only deals with one topic, however it does that in-depth. The style of writing is good and flowing and it feels like you’re just reading a magazine article. This book deals with CSS layout for the latest browsers and talks a lot about the upcoming version of IE8 and how it fixes things so that now you can use CSS 2 styles on your site and they will work across all major browsers (as long as you’re running the latest ones, Firefox 3, Opera 9 etc.) and now that Internet Explorer is finally supporting web standards it will work on Microsoft’s browser platform as well. In that respect, all the tricks and work-arounds that you’ve had to learn in the past to get layouts looking correct are indeed wrong as you will no longer need to use them, for example using floats to get multiple columns. The main thrust of this book is positioning using CSS tables. The book explains what these are, how they are different from HTML tables but create the same results, some of the pitfalls you may come across (there is no equivalent to colspan or rowspan for example) and how to code for these instances. The introduction of web standards across all major browser platforms and the adherence to CSS 2 specifications will make web designers and programmers immensely easier and this book explains how. There is also a chapter dedicated to backwards compatibility and what you should be doing with layouts and yet still make things look on older browsers like Internet Explorer versions 6 and 7. The final chapter of the book deals with some things to look forward that are currently part of the CSS 3 working draft and how they will make your job even easier yet. This is one of those books that I actually have a hard time reviewing, not for the content, the style and layout etc. but for giving it a rating. I think this is a must read book for anybody who designs or layouts web pages, whether you a graphic designer or programmer or in-between however the book is very short and really only deals with a single topic, even though it does it in-depth. The book is in full color which makes it a beautiful book however it’s also a book that you would perhaps only read or twice and wouldn’t be used a reference book so the longevity takes a hit. I wish the book had more to it and covered a few more topics. That said, I still stick by what I say in that everyone who designs or codes web pages should read this as it will make their jobs a lot easier. Technorati Tags: Book Review, Everything you know about CSS is Wrong!, Rachel Andrew, Kevin Yank, CSS, Guidance December 08 Busy weekend repairing and installingHad a busy weekend this weekend. A while back my main server’s power supply blew. I hoped that it was only the power supply and that it didn’t affect the motherboard, RAM, Hard Drives etc. as there was a big bang and smoke involved. So a last week I ordered a new PSU from Amazon (http://www.amazon.com/CORSAIR-750w-Certified-Power-Supply/dp/B000X2677A/ref=pd_bbs_sr_1?ie=UTF8&s=electronics&qid=1228756781&sr=8-1) My previous power supply was only a 500 watt supply so I went for a 750W this time. Amazon had a good deal with a $20 rebate making it slightly under $100 for the supply. I’d forgotten how much time it takes to do things like this. This server was a server I built from scratch a couple of years ago and it had been lying dormant for the past 3-4 months due to the blown power supply. Well this weekend fixed all that. Case open and all the power leads unhooked. Power supply removed The new power supply unboxed. Comes in a neat little carrying pouch. Here it is, the new power supply, complete with a whopping great fan New power supply inserted. Now just had to reconnect all the wires. Wires hooked up It worked. Everything else about the server was fine. Turned out just to be the power supply. Phew! Just replacing the power supply took roughly 2-3 hours. The new Corsair has a great number of connectors for just about any type of system, number of drives, number of graphics cards (it’s SLI certified) etc. The next thing on my list was to add more memory to another server. This time my brand new Windows Home Server. Fellow MVP Donavon West has an excellent article about it here. That took less than an hour but after that I had to install and configure it (which I’m still in the process of doing). One of the next things on my list is how to extend it. Using the WHS API, adding a Sharepoint site to it and also my own web sites. There was various other projects I carried out this weekend, but the two above were my main ones which I’m happy to say, both succeeded. December 03 Creating a Web Messenger control from Scratch – Part 11Technorati Tags: Web Messenger Control, Web Messenger, Windows Live, Windows Live Messenger, Programming, API In this the final installment of this series of tutorials for the moment we will be completing the Web Messenger control by adding the ability for users to actually hold conversations. We will also add some clean-up code, and generally tidy up some things that were left. So first off lets re-visit our signInCompleted routine. As you remember, when a user signs in, two events will get fired. The first being authentication completed then once the user has authenticated correctly, sign in completed will get called. We have already wired this event handler up but there are a couple of additions to make to it. Firstly we need to add a delegate for whenever a conversation property takes place. This happens for example if you’ve signed in and another user starts a conversation with you :- //Add delegate for when conversation changesmsgruser.get_conversations().add_propertyChanged(Delegate.create(null, conversation_collectionChanged));If you remember way back to the beginning of this series, the user class has a property :- This property retries the Conversation Collection for the user. The Conversation collection class has a number of events that you can tie into :-
The one we’re interested in is the PropertyChanged event. In the code snippet above, we simply get the Conversation Collection for the signed in user, then hook into the PropertyChanged event and point it to a function conversation_collectionChanged. The other change we’re making to the signInCompleted event handler is to create a TimeOut to call a function a little later. We are doing this due to the way that we initially create and display Contacts :- //Now we need to set a single timout so that we have time for the users//contacts to be enumerated through and the ContactsList array to be//created. Then we can assign the contact presence changed delegate//We'll set the timeout to 10 seconds.contactPresenceTimeout = setTimeout(ContactPresenceDelegate, 10000); The function that we call simply iterates through the our array of contact objects and sets an event handler up to call a function whenever a contact’s online presence changes :- function ContactPresenceDelegate(){if (ContactsList != null) { //Only clear the timeout if the contacts list has been built.clearTimeout(contactPresenceTimeout); var contactcount = ContactsList.getLength();for (var counter = 0; counter < contactcount; counter++) { var address = ContactsList[counter].get_Address(); //This will fire whenever a contacts online presence changes. address.get_presence().add_propertyChanged(Delegate.create(null, presence_PropertyChanged));} //We also want to close the groups hereToggleContacts(); } } First off we clear the Timeout so that it doesn’t fire again and at the end of the routine we simply collapse the Contacts list displayed on screen so that it look a bit tidier. /* Called by the Messenger Library when presence status changes occur (i.e. contacts sign in or out). */function presence_PropertyChanged(sender, e){displayContacts(); } The presence_PropertyChanged function that we hook into simply calls the displayContacts routines which will refresh all the contacts and their statuses.
function conversation_collectionChanged(sender, e){displayConversations(); } The conversation_collectionChanged routine simply calls displayConversations() function as shown above. function displayConversations(){ ConversationArray = new Array();var sb = new StringBuilder('<p><b>Active Conversations: (click a conversation to resume):</b></p>'); var item = 0; var enum1 = msgruser.get_conversations().getEnumerator(); while (enum1.moveNext()) { var conv = enum1.get_current(); ConversationArray.push(conv + ":" + item); if (conv.get_closed()) continue;sb.append(conversationLink(conv, item)); sb.append("<hr />");item++; } $get('divConversations').innerHTML = sb.toString();} The displayConversations simply adds a link to our open conversations box, one for each conversation that is currently ongoing as you can see below :- function conversationLink(conv, item){ var roster = conv.get_roster(); var enum1 = roster.getEnumerator();var names = new Array(); while (enum1.moveNext()) { var dispName = enum1.get_current().get_presence().get_displayName(); var dispEmail = enum1.get_current().get_address();if (dispName !== '') {names.push(dispName); } else {names.push(dispEmail); } } var sb = new StringBuilder(); sb.append('<a href=\"javascript:switchConv(' + item + ')\">'); if (conv == Conversations) sb.append('<b>'); sb.append(names.join(', ')); if (conv == Conversations) sb.append('</b>'); sb.append('</a>'); sb.append(' ');sb.append('<a href=\"javascript:closeConversation(' + item + ')\">'); sb.append('close</a>'); return sb.toString();} The displayConversations in turn calls the conversationLink function above to actually create the link. All we are doing here is creating a link so that the user can switch to that conversation and also creating a close link that will end that particular conversation. /* Close the current conversation. */function closeConversation(id){ var conv = msgruser.get_conversations().get_item(id);convArray.splice(id, 1); conv.close(); if (conv == Conversations) { removeChildrenFromNode('txtConv');} displayConversations(); if (msgruser.get_conversations().get_count() == 0)document.getElementById('btnSend').disabled = true; } The closeConversation function takes in the ID of the conversation to close (which is created whenever a conversation starts). We get the actual conversation through the Item(id) method of the Conversation Collection then close that item. If the conversation we are closing is the currently displayed conversation then we clear the conversation from the display area and disable the button to send a conversation item. We’re nearly there, only another few routines to go through. Whenever a user clicks on a Contact, the createConv function is called :- function createConv(index){ var newConv = msgruser.get_conversations().create(ContactsList[index].get_Address()); var convId = msgruser.get_conversations().get_count();switchConv(newConv.convId); } This routine simply creates a conversation object based on the users current address and an id. The id is simply the number of conversations that are currently open, plus 1. Since the Conversation Collection is zero based, when you get a count of current open conversations, it will always be the index + 1. function switchConv(id){ var conversation = msgruser.get_conversations().get_item(id); if (conversation) { if (Conversations) {Conversations.remove_messageReceived(ConversationSink); } ConversationSink = Delegate.create(null, receiveMessage);Conversations = conversation; Conversations.add_messageReceived(ConversationSink); removeChildrenFromNode('txtConv'); /* Display all messages from the conversation history. */ var hist = conversation.get_history(); var histEnum = hist.getEnumerator(); while (histEnum.moveNext()) {displayMsg(histEnum.get_current()); } document.getElementById('btnSend').disabled = false; } displayConversations(); document.getElementById('txtMessage').focus();} We then call the switchConv function passing in the conversation id as described above. This routine gets the Conversation object from the Conversation Collection, creates a delegate for if a message is received on that particular conversation object and displays all the messages that have been sent and received on this particular conversation thread. We also enable the Send button to allow the user to send a message on this Conversation thread. /* Called by the Messenger Library when a new message is received. */function receiveMessage(sender, e){ var message = e.get_message();displayMessage(message); document.getElementById('msgLastRecv').innerText = 'Last message at: ' + Conversations.get_history().get_lastReceived().toString(); } When we receive a message we simply display it. /* Send a message in the current conversation. */function sendMessage(){var txtMessage = $get('txtMessage'); var messageText = txtMessage.value;var message = new Microsoft.Live.Messenger.TextMessage(messageText, null); if (msgruser) { Conversations.sendMessage(message, null);} displayMessage(message); txtMessage.value = '';txtMessage.focus(); } Whenever the user clicks on the “Send” button to send a line of text in the conversation, the above routine gets called. This simply grabs the line of the text from the input textbox on the page, adds this line of text to the current conversation object and sends the line of text to the recipient. Then we clear the line of the text from the input box ready for the next message to be sent. That’s all there is to sending and receiving text messages to a users’ contacts. The reason for the brief description above is that I’ve actually covered this in more depth in a previous article that I have written which you can find on this site. The last thing we want to do is complete our signOutCompleted handler :- function signOutCompleted(sender, e){ //Fill in laterif (signincontroldisplayed == false) {ToggleSignIn() } Conversations = null; ContactsList = null; userInfo = null; //Blank the user interface. removeChildrenFromNode("divConversations"); removeChildrenFromNode("divContacts"); removeChildrenFromNode("searchResults");$get('divConversations').innerHTML = ""; $get('txtConv').innerHTML = ""; $get('btnSend').disabled = true; } Here we are just tidying things up. We show the sign-in Control and remove all conversations, user contact lists etc. from memory and also from the screen display. There you have it. A fully functional web messenger control. I will be entire code for the above control soon and will post where I have uploaded it to. There are numerous improvements we can make to this control, e.g. each conversation actually takes place in a popup window rather than having to switch conversation in a single control etc. and these are things that I will continue to do with this control but the main reason for not going into all that here is that it’s mainly cosmetic changes and I want to start getting into Live FX programming. I will be revisiting this code in the future however. If you’ve followed each article in this series then you should have the complete code already. The only thing that you will need to change is the following :- var privacyUrl = 'http://localhost:44444/WLWebMessenger/WebMessenger/Privacy.htm'; var channelUrl = 'http://localhost:44444/WLWebMessenger/WebMessenger/Channel.htm'; December 02 Creating a Web Messenger control from Scratch – Part 10Technorati Tags: Windows Live, Web Messenger, Windows Live Messenger, Programming, SDK, How To, Web Messenger Control So far in this series we have covered quite a bit of ground including customizing the sign-in control, groups, contacts, new features of the 2.5 API including user images etc. For this part we will do a simple search function. If you can remember, one of our original goals was to mimic the Messenger Client as closely as possible with the limitations that a browser based User Control will have. If you take a look at the messenger client you will see a search bar :- This search bar is used to both search the web and also as a quick search through your contacts list. We won’t bother with the internet search at the moment but will implement the quick contacts based search. We already have the basis for everything we need. It’s just a matter of adding some more code and another function to our existing MessengerContacts.js class. First off let’s add the code to our base file ascx to enable searching. Right under the “setPeronalMessage” section we will add the following :- <tr> <td> <div id="searchContacts"> <span><b>Search for Contact: </b></span> <input id="search" type="text" /> <input onclick="btnSearchClick();" id="btnSearch" type="image" src="WebMessenger/Images/SearchContact.gif" value="Search" /> </div> <div id="searchResults"> </div> </td> </tr> When you click on the search button we kick off simply invoke a simple Javascript function :- function btnSearchClick(){ removeChildrenFromNode("searchResults");var searchfor = $get("search").value; if (ContactsList != null) { var ContactsLength = ContactsList.getLength();for (var counter = 0; counter < ContactsLength; counter++) { var name = ContactsList[counter].get_ContactName();var regexp = new RegExp(searchfor); var res = regexp.exec(name);if (res != null) {ContactsList[counter].DisplaySearchContact(); } } } else { alert("You currently do not have any contacts to search”);} } First off we clear out the current search area using the removeChildrenFromNode function we already had, passing in the id of the search results DIV. Next we get the value that the user entered in the search text box. We already have a list of Contacts stored as an array in the variable ContactsList when we created all the contacts. So here we simply iterate through that array of contacts, get the Contact Name from each object and perform a simple regular expression search on the contact name to see if the characters that the user typed in the search box match. If they do then we call the DisplayContactSearch method of the corresponding MessengerContacts class to display the contact in the search results area. If no match then we continue through the loop. The only other thing we need is the new DisplayContactSearch method in our MessengerContacts class :- DisplaySearchContact: function(){if ($get("SearchContact" + this.ContactName) == null) { //Get base group DOM elementvar link = document.createElement("a"); link.href = "javascript:createConv('" + this.Index + "');"; link.className = "ContactLink";link.id = "SearchContactImgLink" + this.ContactName; var textlink = document.createElement("a"); textlink.href = "javascript:createConv('" + this.Index + "');"; textlink.className = "ContactLink";textlink.id = "SearchContactTextLink" + this.ContactName; var outerdiv = document.createElement("div"); outerdiv.id = "SearchContact" + this.ContactName; //Status and picture holdervar PicOutline = document.createElement("div"); PicOutline.id = "SearchContactStatus" + this.ContactName; PicOutline.className = "ContactImgOutline";var ContactPicHolder = document.createElement("div"); ContactPicHolder.id = "SearchContactImgHolder" + this.ContactName; ContactPicHolder.className = "ContactImgHolder"; //Create a span to place the name of the contactvar ContactNameHolder = document.createElement("div"); ContactNameHolder.id = "SearchContactNameHolder" + this.ContactName; ContactNameHolder.className = "ContactNameHolder";var name = document.createElement("span"); name.className = "ContactName";name.id = "SearchContactName" + this.ContactName; //create a horizontal rule to seperate contactvar hr = document.createElement("hr"); //Build the heirarchyContactPicHolder.appendChild(link); PicOutline.appendChild(ContactPicHolder); textlink.appendChild(name); ContactNameHolder.appendChild(PicOutline); ContactNameHolder.appendChild(textlink); outerdiv.appendChild(ContactNameHolder); outerdiv.appendChild(hr); this.DisplayContactImage(link) this.ChangeContactStatus(PicOutline); this.DisplayContactName(name); //get the groupDivvar grp = $get("searchResults"); //add the contact to the search resultsgrp.appendChild(outerdiv); } }, Now all of the above code should look very familiar to you as it’s basically just the DisplayContact method we talked about and implemented in the last part of this tutorial series. The only difference between the two are that all of the IDs have changed to include the word “Search” so that they are different from the normal Contacts list. e.g. :- link.id = "ContactImgLink" + this.ContactName; Now becomes :- link.id = "SearchContactImgLink" + this.ContactName; The only other difference is at the end of the code where we get the add the contact to. Instead of :- //get the groupDivvar grp = $get("group" + this.GroupName); We have changed this to :- //get the groupDivvar grp = $get("searchResults"); And we end up with the following :- There you have it. A quick Contact Search function just like in the Messenger Client. November 21 Creating a Web Messenger control from Scratch – Part 9After a little break due to a very heavy work schedule, we’re back with Part 9 of this mini-series explaining how to create a Web Messenger control that you can just drop into your web sites. It’s hard to believe that we’re currently on Part 9 of this series of tutorials. In the last part we discussed Groups within Web Messenger. Users can create their own groups and place contacts within these groups, e.g. Co-Workers, Friends, Family etc. Now that we’ve got groups, it’s now time to place the actual contacts within the appropriate group. What we will end up is something similar to the above. We will grab each contacts picture or assign a default one if the user does not have a custom image. Display their name and email address and create links so that you can start a conversation with them. I’ve split groups and contacts into two separate classes. As you may recall we call the groups function from within our signInCompleted event handler :- function signInCompleted(sender, e) { if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success) { msgruser.get_presence().add_propertyChanged(Delegate.create(null, user_Presence_PropertyChanged));var selectStatus = $get('selectStatus'); selectStatus.selectedIndex = 6; getMessengerLinks(); //Get the users IMAddressPresencedisplayUserInfo(); //Now that the user has signed in, get their contactsdisplayContactGroups(); } } Previously this function was displayContacts, I’ve now renamed it displayContactGroups just to separate it from Contacts. The function remains the same except for one addition. We now call the function to display contacts at the end. function displayContactGroups(){ var enumgroups = msgruser.get_groups().getEnumerator(); while (enumgroups.moveNext()) { var currentGroup = enumgroups.get_current(); var name = currentGroup.get_name();var group = new Messenger.Group(name, currentGroup); group.DisplayGroup(); //Show the users some progresscontactTimeout = setTimeout(displayContacts, 500); } } We do this by setting a Javascript timeout. The main reason for doing this is that it shows users some progress. It will display the groups onscreen whilst it comes back to create the contacts rather than processing everything at once and making the user wait until everything has completed. function displayContacts(){ //clear any timeouts.clearTimeout(contactTimeout); var enumgroups = msgruser.get_groups().getEnumerator();index = 0; while (enumgroups.moveNext()) { var currentGroup = enumgroups.get_current(); var name = currentGroup.get_name(); var enumContacts = currentGroup.get_contacts().getEnumerator(); while (enumContacts.moveNext()) { var currentContact = enumContacts.get_current(); //Need to pass in the contact and the group namevar contact = new Messenger.Contacts(currentContact, name, index); contact.DisplayContact(); index++; } } } The displayContacts function first clears any timeouts that may have been created (e.g. when calling from the displayContactGroups function). Next it gets a reference to each of the groups that the user has. Once we have that we iterate through each group and get the contacts for each of the groups. The Web Messenger Group class exposes two properties as shown above. We have already used the Name property in our own Group class.
The other property gets a collection of all the Contacts for that particular Group. We use both of these properties for our Contacts class. We need the Name property so that we can tell which contact belongs to which group and we obviously need the list of Contacts. As you can see from the code above, we iterate through the Contacts collection the same way that we iterate through the Groups collection. For each Contact in the Contacts collection we instantiate a new MessengerContact class. Then we call the Display method of this class to display the contact and repeat until all the contacts for that particular group have been processed, then we go onto the next group and repeat until all contacts have been displayed.
Our MessengerContact class defines a few key properties :- Type.registerNamespace("Messenger");Messenger.Contacts = function(contact, groupName, index){ this.Contact = contact; this.GroupName = groupName; this.Index = index; var _contactName = contact.get_displayName();if (_contactName.indexOf("@") > -1) {this.ContactName = _contactName.substr(0, _contactName.indexOf("@")); } else { this.ContactName = _contactName;} } We pass in the contact object itself, the name of the group that this contact belongs to and also an index (which will be used for conversations. This is simply an incremental number). Note that the groupName that is passed in we could have gotten from the Groups property that is exposed in the Contact object however if a contact is placed in multiple groups (which is allowed) then we want a separate instance of the contact for each group, hence why we pass in this value. The last chunk of code assigns the name of the Contact. We will use this later. Some Contacts do not have display names and instead their email address is substituted for their display name. If this is the case then we want their display name to be the first part of their email address (the bit before the “@”). Next we define our actual class prototype and start off with some simple getters and setters :- Messenger.Contacts.prototype = { get_ContactName: function() {return this.ContactName; }, set_ContactName: function(value) {if (value.indexOf("@") > -1) {this.ContactName = value.substr(0, value.indexOf("@")); } else { this.ContactName = value;} }, get_Index: function() {return this.Index; }, set_Index: function(value) { this.Index = value;}, get_GroupName: function() {return this.GroupName; }, set_GroupName: function(value) { this.GroupName = value;}, get_Contact: function() {return this.Contact; }, set_Contact: function(value) { this.Contact = value;}, Next we define our DisplayContact method, which gets called immediately after the class is instantiated :- DisplayContact: function(){if ($get("Contact" + this.ContactName) == null) { //Get base group DOM elementvar link = document.createElement("a"); link.href = "javascript:createConv('" + this.Index + "');"; link.className = "ContactLink";link.id = "ContactImgLink" + this.ContactName; var textlink = document.createElement("a"); textlink.href = "javascript:createConv('" + this.Index + "');"; textlink.className = "ContactLink";textlink.id = "ContactTextLink" + this.ContactName; var outerdiv = document.createElement("div"); outerdiv.id = "Contact" + this.ContactName; //Status and picture holdervar PicOutline = document.createElement("div"); PicOutline.id = "ContactStatus" + this.ContactName; PicOutline.className = "ContactImgOutline";var ContactPicHolder = document.createElement("div"); ContactPicHolder.id = "ContactImgHolder" + this.ContactName; ContactPicHolder.className = "ContactImgHolder"; //Create a span to place the name of the contactvar ContactNameHolder = document.createElement("div"); ContactNameHolder.id = "ContactNameHolder" + this.ContactName; ContactNameHolder.className = "ContactNameHolder";var name = document.createElement("span"); name.className = "ContactName";name.id = "ContactName" + this.ContactName; //create a horizontal rule to seperate contactvar hr = document.createElement("hr"); //Build the heirarchyContactPicHolder.appendChild(link); PicOutline.appendChild(ContactPicHolder); textlink.appendChild(name); ContactNameHolder.appendChild(PicOutline); ContactNameHolder.appendChild(textlink); outerdiv.appendChild(ContactNameHolder); outerdiv.appendChild(hr); this.DisplayContactImage(link) this.ChangeContactStatus(PicOutline); this.DisplayContactName(name); //get the groupDivvar grp = $get("group" + this.GroupName); //add the contact to the groupgrp.appendChild(outerdiv); } }, This may look complicated but in reality it’s not. Most of the code above simply defines a hierarchy of html elements for placement of the various elements of our contact. In particular we have the contact picture, the contact display name and email address and to be like the new Wave 3 client Messenger product we’re also introducing a presence halo. Both the Contact image and their name/email address will be hyperlinks so that we can start a conversation when the user clicks on that particular contact.
DisplayContactImage: function(ContactPicHolder){ this.RemoveChildrenFromNode(ContactPicHolder.id);var img = new Image(); img.onerror = function() { img.src = "WebMessenger/Images/MsgrNoImage.gif";} img.src = this.Contact.get_presence().get_displayPictureUrl(); //Now we need to scale the image down to 40 pixels width //and the appropriate height var scale = Math.round(img.width / 40); img.style.width = "40"; if (scale > 0) {img.style.height = Math.round(img.height / scale) } img.id = "ContactPic" + this.ContactName; img.className = "ContactImage";ContactPicHolder.appendChild(img); }, The DisplayContact method makes a call into DisplayContactImage. This routine simply gets the Contacts picture from the Presence object, which we’ve talked about before when we were display the Users own picture and presence. We also apply some very rough scaling to the image and then put it in place. ChangeContactStatus: function(StatusDiv){var stat = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, this.Contact.get_presence().get_status()) switch (stat) {case "online": StatusDiv.style.backgroundColor = "#00cc00"; break;case "offline": StatusDiv.style.backgroundColor = "#cc0000"; break; default: StatusDiv.style.backgroundColor = "#E3AC4F"; break;} }, The ChangeContactStatus method simply changes the color of the halo around our Contacts image to indicate whether they are busy (orange), offline (red) or online (green). The Contacts presence can be gathered from the Presence object and is actually an enum. Again we have covered this previously when we were talking about the actual user Object that we created. DisplayContactName: function(NameDiv){ this.RemoveChildrenFromNode(NameDiv); //Now we need to check if the name is actually a name //or an email address.var displayName = this.ContactName; if (this.ContactName.indexOf("@") == -1) { displayName += " ("; displayName += this.Contact.get_currentAddress().get_address(); displayName += ")";} NameDiv.innerHTML = displayName; }, The DisplayContactName function simply displays the contacts name and concatenates their email address onto the end of it. Therefore you end up with something like “Colin – abc@ba.com”. RemoveChildrenFromNode: function(id) { var node = document.getElementById(id);if (node == undefined || node == null) { return;} var len = node.childNodes.length; while (node.hasChildNodes()) {node.removeChild(node.firstChild); } }, dispose: function() { }} Messenger.Contacts.registerClass("Messenger.Contacts", null, Sys.IDisposable); if (typeof (Sys) !== "undefined") {Sys.Application.notifyScriptLoaded(); } The rest of the class is straightforward and code that we’ve used in other classes. The RemoveChildrenFromNode simply removes any inner content from a particular html element. The standard Dispose functionality and lastly we actually register the class with the Asp.Net Ajax ClientScript object which will be found in the hosting page or it’s master page. In our WebMessenger.ascx.cs code behind file for our Server Control page we need to register our Javascript object so that we can access it :- if (!sm.Scripts.Contains(new ScriptReference("WebMessenger/Script/MessengerContacts.js"))) {sm.Scripts.Add(new ScriptReference("WebMessenger/Script/MessengerContacts.js")); } As with other classes that we’ve defined, I’ve also included a stylesheet object so that end users can modify how things look and the layout without having to delve into any code. Therefore again in our code behind we need to register the CSS style sheet we have for the MessengerContacts class :- HtmlLink ContactSylteSheet = new HtmlLink(); ContactSylteSheet.Attributes.Add("href", "WebMessenger/Contact.css"); ContactSylteSheet.Attributes.Add("rel", "stylesheet"); ContactSylteSheet.Attributes.Add("type", "text/css"); header.Controls.Add(ContactSylteSheet); And here is the appropriate StyleSheet :- .ContactImage {border: 0px; text-decoration: none; border-width: 0px; } .ContactName{text-decoration: none; font-size: 0.9em; padding: 2px; text-align: center; } .ContactLink {border: 0px; border-width: 0px; text-decoration: none; } .ContactNameHolder{position: relative; text-decoration: none; } .ContactImgOutline{width: 44px; padding: 2px; } .ContactImgHolder{width: 40px; } November 07 Review – Silverlight 1.0
First off I would like to point out that I’ve changed my book review rating system a little. I used to give ratings out of 5 in 0.5 increments however after reviewing a great number of books now this system was too limiting with a number of books for example receiving 4 out of 5 when some books were slightly more deserving of that than others but those “others” weren’t deserving enough for a 3.5 out of 5. Therefore I’ve moved to a very simple “out of 100” rating system. This gives me more scope to play with. Now with saying this onto my first review under the new rating system, Silverlight 1.0 from Wrox Press. With the very recent release of Silverlight 2.0 to the web by Microsoft, this book is now slightly dated however I’m sure Wrox will be releasing an updated version of this book shortly to cover the new 2.0 release and all the enhancements that it brings with it. One thing that immediately strikes you about this book is that it is in color. With the subject matter at hand I think that this decision was a very good one after all it is would be very difficult to show off the power of Silverlight and to get some of the examples showing correctly with just grey scale. For a Wrox book this one is fairly small weighing in at just 288 pages, however you will find everything you need to know to get you started using Silverlight in this book. It does not cover everything in detail and therefore I would consider it to be a “starter” book. One that you will pick up to get the basics of the technology, know how it works and how to do use it quickly and easily. Silverlight 1.0 is built atop a cut-down version of XAML, Microsoft’s graphical language that is used mainly in desktop applications and was introduced with Microsoft .Net 3.0. The book starts with an explanation of what Silverlight is. It does mention the “forthcoming” Silverlight 1.1 stack which is what was recently released as Silverlight 2.0. The book continues by giving a quick overview of the tools that you can use to build a Silverlight application, where to get the SDK and runtime, some examples of it’s uses then gives a quick breakdown of default Silverlight application that is used in Visual Studio. The next chapter gives a brief introduction to XAML and some of the various elements that Silverlight makes use of. There are plenty of examples given and some visual tricks thrown in for those of us that are not graphically inclined (I definitely include myself in this department). There is enough in this chapter to give you the basics for building your own Silverlight application including animations, video and basic shapes and transforms. With the introduction of XAML, Microsoft released a tool specifically for designing applications using this language, Microsoft Expression Blend. Chapter 3 deals with how you use blend to create your XAML files and how to incorporate these into your Visual Studio project. One of the benefits of Expression Blend is that you can hand off the design aspect of the project to a graphical designer on your team and the resulting code fits very neatly in with Visual Studio so that developers don’t need to worry about this aspect. Again this is a quick introduction to Expression Blend and it does not go into depth of every option that Expression Blend offers but gives you enough that you can get started and build projects. Silverlight 1.0 was programmed with Javascript only. This has changed with the release of Silverlight 2.0 which now offers a subsection of the .Net Framework and the ability to program in C#, VB.Net etc. The next couple of chapters deal with programming Silverlight 1.0, hooking up events, how to incorporate Silverlight into your page and how it can interact with the code in your web page. Again plenty of examples are given. A quick introduction to Silverlight 1.1 is given however this has now been superseded with Silverlight 2.0 and some of the features have changed since Silverlight 1.1 so the small chapter on this is not really relevant anymore. Finally the authors give you a full blown Silverlight Video application and explain it works. The previous chapters in this book give you enough information to create applications such as this and Video was mainly the target for Silverlight 1.0, therefore this example is great for showing what you can create and the potential power of Silverlight 1.0. Although this book is really a brief introduction to Silverlight 1.0, the authors explain the concepts well and give you enough information to get you going, creating your own Silverlight applications. As mentioned, this book is targeted towards people who are interested in learning Silverlight and not an in-depth desktop reference for those who are already using it. The easy to understand explanations mixed with the full color graphics and code markup makes this an excellent book for those who are wanting to get their feet with with Microsoft Silverlight. The main downfall of this book being the very recent release of Silverlight 2.0, although the concepts learned in this book are just as relevant for Silverlight 2.0 as they are for Silverlight 1.0. October 30 Asp.Net Ajax Programmers Reference
Technorati Tags: Asp.Net Ajax,Book Review,Shahram Khosravi,Wrox Press,Asp.Net Ajax Programmers Reference Asp.Net Ajax Programmers Reference weighs in at a hefty 1500+ pages. Most other books I’ve read on Microsoft’s Asp.Net Ajax weigh in at between 300 and 500 pages. So are all the extra pages just filler or is the content actually worth it? This book definitely is not for the light hearted or beginners. You need to have a very good grasp of Javascript to get anywhere with this book. After a brief introduction on what Ajax is and the methods and thought processes behind Ajax the book jumps straight in with how Microsoft have extended Javascript to give it a .Net Framework style of programming so those used to Asp.Net code behind and the .Net Framework will be more at home. Dr Khosravi doesn’t just give you brief explanations as to how Microsoft have extended Javascript but also gives you an in-depth explanation with code as to how Microsoft actually done it. Basically he takes apart the Javascript that Microsoft has used and explains step by step what they have done. This is where a lot of the extra 1000+ pages come from. Most programmers, whilst they might be interested in the actual internals, won’t actually need this in-depth an explanation of each of the features and therefore you could argue that a lot of this is just filler in the book. Some programmers however will eat this up as it shows not only how Microsoft has extended Javascript but also gives you a base as to how you can extend it too using the best practices that Microsoft has employed. The reading also is not lightweight and is something more like what you would find in a university classroom than a normal textbook, however Dr Khosravi does get the pertinent points across and with the sheer level of detail that he goes into, you’d be hard pressed to find another book of this magnitude. The book is up to date covering Asp.Net Ajax as released in it’s original form as an add-on to the .Net framework and also the newer .Net 3.5 integrated Asp.Net Ajax (there were some enhancements made although it is backwards compatible). The book also covers everything you would need from the extensions Microsoft has made to the base Javascript types, to communicating with Web Services, using the out of the box Update Panel control to developing your own custom client controls with Ajax. Basically everything you would need to know about Asp.Net Ajax is covered in this book and covered in detail. So given that this book is basically a one stop shop for Asp.Net Ajax, why not give it a 5 out of 5 rating? The reasons for this I’ve outlined above. It is not the easiest book in the world to read and for a lot of programmers there is no real need to dissect every extension Microsoft has made to Javascript with the actual code that they have used. There is this feeling that this will be an excellent book to have at your computer beside you when you’re actually coding however to start learning Asp.Net Ajax you’d also want to pick up another book that quite go into the detail of this book and one that is slightly easier to read. That said, if you already know Asp.Net Ajax and want to know more or are looking for a good book to have handy when you’re coding your project, then this is the book to have. October 24 Sharepoint Server 2007 Survival Guide - Review
First off I must point out that this review is based off an early preview of this book and therefore things may be subject to change before this book gets fully published in April next year. I would also like to say that this review is based on most chapters of the book, but not all. Sharepoint Server 2007 Survival Guide is aimed squarely at end users, power users and business users of the Sharepoint 2007 Office System. The book is broken down into logical parts, each chapter can be read in it’s own right however the concepts in the book do build upon what you have previously learned in previous chapters. The good news for end users is that there is absolutely no prior knowledge of Sharepoint required. It starts off with a quick overview of what Sharepoint Server 2007 (MOSS) is and a brief history lesson of where it came from. The next chapter that I had access to was on Lists, Libraries and Content Types. This is essential knowledge for anyone using MOSS. The authors break these core concepts down and describe the functions of each with a few very easy to follow step by step directions on how to set them up, what metadata is and how to create and collect it, different views that are available to the user etc. This concept is repeated throughout the book. The authors give you a oversight on what the chapter focuses on, then gives you very easy to follow step by step instructions on how to use/create that feature followed by a more in-depth explanation of related topics and features within MOSS. Web content management and document/records management are core features of MOSS and the authors cover these topics very nicely, giving good hints on what you should and shouldn’t do, however they do fail to point out some limitations of MOSS that I feel should be conveyed to a user for example document libraries should really only handle roughly 2000 documents. Libraries can hold more than that but after the 2000 limit performance drops off considerably so being able to plan ahead for this is a necessity. Also when considering security, if you break the inherited permissions of a library you may run into ACL problems when you have over roughly 1820 unique user entries in your library. This is a windows limitation that unfortunately MOSS inherits but again an important one that people should be aware of whilst planning out their MOSS deployments. Business Intelligence is a big part of this book. Once you’ve got the information into your MOSS system, how can you use that information? It starts with an overview of what business intelligence is then breaks it apart into it’s core constituents dedicating a chapter to each of the major components. At this point you’re getting into slightly more advanced topics but the authors handle it well and explain it easily understood terms and examples. Depending upon your MOSS installation, not all of the features explained in these chapters may be available to you however if they are then you’ll get a great deal of information and use from these chapters. I was initially surprised to see on a chapter on the Business Data Catalog as this is more developer oriented, creating the backend BDC XML etc. however the book takes the standpoint that somebody from within the companies IT department will do this for you and leads you by the hand from after this has already been setup. The final section of book is dedicated to customizing MOSS. Something most companies will want to do and there are whole books written on just this subject alone. The authors first give you a brief introduction to Sharepoint Designer 2007 which is an end user tool for customizing MOSS followed by a step by step example on how to customize a site, move things around the page, give it a new look and feel, incorporate your corporate identity and logo etc. These chapters are definitely aimed at power users and not the average business user. Developers might also pick up some information here on how to do things quickly through Sharepoint Designer as opposed to using Visual Studio for developing MOSS solutions. Having a knowledge of stylesheets / CSS, HTML and ASPX pages, XML and XSLT are a must here. The authors do a good job as an overview and how to do some fairly basic customization tasks, however there isn’t quite as much in-depth knowledge conveyed that would be necessary to get the most out of Sharepoint Designer, for example how to manipulate views and pass information across to pages using the Querystring. Only a very brief mention is given to XSLT which is fairly integral to MOSS development. Overall there is a lot of very useful information within this book for new users of Sharepoint and Power Users / Business Users to give them the knowledge and confidence to explore and make the best use out of MOSS. Concepts are explained in easy terms and examples are given to underscore those concepts and give the users enough knowledge to do things on their own. If you are a MOSS developer then this book is not for you, however I did learn a few new things whilst reading it. If you are perhaps in an organization where you have to teach end users how to use MOSS then again I would recommend this book as it will give you most of what you will need to convey to your users. This review was based on a preview of the book. There were some missing chapters and I do expect there will be edits made before the book is finally published, however from this preview, this is a book I would recommend to intended target audience of end users and power users. |
|
|