Is RIM the Next Novell?

At one time, Novell was the king of the networking hill. Anyone interesting in becoming a network administrator had to know Novell. Then Microsoft came along and took their lunch money pitting the graphical and desktop friendly NT Networking against NetWare. Novell was seen as lacking vision and lagging behind their competitors. Sounds an awful lot like RIM today. Their last smartphone entries have been failures, and the upcoming PlayBook doesn’t look like an improvement in their track record. One of the strengths of the BlackBerry platform has been messaging, and they are releasing a product that doesn’t do that without being tethered to an existing BlackBerry device. The PlayBook is going to support Android apps instead of fostering their own third party developers because it’s another new platform that is coming out before there is a developer ecosystem. It feels rushed. RIM’s main customer is the enterprise, not the consumer. Enterprise executives want the sexiness of an iPhone or iPad with the security of a BlackBerry. RIM is not making that happen.

RIM has a definite hold on the enterprise market for mobile phones. This is in large part due to their tremendous security on the devices. The BES server is encrypting data transfers and improving network response time to the devices. Everything stored on the device is encrypted. Administrators can remotely wipe data from the devices. None of this is going to come from Apple. The enterprise is not their main customer, it’s the consumer. Google might be able to do this with Android, but more than likely it will come from the cloud. Google does not have the internal server market in hand.

Microsoft has built a huge empire in the enterprise with sever software and networking. They are the most likely competitor to take out RIM. Why haven’t they focused on extending their enterprise platform for mobile devices? If they came out with a secure phone that was integrated with their products (Exchange, Office, SharePoint, CRM) RIM would be out of the game. Microsoft has been blinded by the iPhone’s success (and now Android) in the consumer market.  Recently, they have seen consumer success with the XBox and Kinect. It seems to me they want Windows 7 phones to do the same thing. I really don’t think that is the market for them. RIM is trying to give the enterprise market to someone, why isn’t Microsoft taking it this time?

Posted: Tuesday, April 12, 2011 10:29:42 AM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
Mobile

Merging The History of Two Documents in SharePoint 2010

The problem I needed to solve was this: Two users were independently creating the same document. Once this was discovered, one of them was chosen to be the master document. But the users didn’t want to lose the other document, even though it was not chosen as the master. The users wanted to merge this document into the history of the master document. Technically, it is not possible to alter the history of any document in SharePoint. So I decided we would create a new third document, which was generated based on the two documents to be merged. By using the SharePoint API I could control the dates and merge the histories of the two documents into a third one. Once that was done, the original documents could be deleted.

The basic algorithm works like this:

  1. Choose 2 documents, one to be the master, one to merge into the master
  2. Fetch the history of both items into a List<>
  3. Sort the list by date
  4. Loop through the list and write the historical files to a new list item, creating a new history
  5. Write the current version of the merge document  to the new list item
  6. Write the current version of the master document to the new list item, including the properties

I did try to keep the properties to all the previous versions, but there was some resulting weirdness that went away when that part was removed. Since the metadata wasn’t a strict requirement, but a nice-to-have, I didn’t investigate that any further. YMMV. Here is the code for the algorithm:

/// <summary>
/// Merges the history of two SPListItems into a single SPListItem
/// </summary>
/// <param name="siteUrl">The URL of the SharePoint site</param>
/// <param name="listName">The Name of a list containing the documents to be merged</param>
/// <param name="keepDocName">The name of the document which is the "Master"</param>
/// <param name="mergeDocName">The name of the document which is merging it's history into the master.</param>
/// <param name="newName">The name of a third document which is the destination of the other two</param>
protected void MergeDocuments(string siteUrl, string listName, string keepDocName, string mergeDocName, string newName)
{
    
//Attach to SharePoint, get the list and documents
     var site = new SPSite(siteUrl);
     var web = site.OpenWeb();
     var list = web.Lists[listName];
     var keepUrl = string.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, keepDocName);
     var discardUrl = string.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, mergeDocName);
     var keep = web.GetListItem(keepUrl);
     var merge = web.GetListItem(discardUrl);
     //Push the versions from each document into a list that can be sorted by date
     var allVersions = keep.Versions.Cast<SPListItemVersion>().ToList();
     allVersions.AddRange(merge.Versions.Cast<SPListItemVersion>());
     allVersions.Sort(new SortVersionsByDate()); //SortVersionsByDate is a simple
IComparer for the created date
     //Create the upload url for the new document being merged
     var newUrl = string.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, newName);
     //Write each old version as the new file to create a merged history
     foreach (var version in allVersions)
     {
          if (version.IsCurrentVersion)
                continue; //Skip the current versions of each, we need for them to be the last two docs
          var oldFile = version.ListItem.File.Versions.GetVersionFromID(version.VersionId);
          var oldestFile = list.RootFolder.Files.Add(newUrl, oldFile.OpenBinaryStream(),
                                                     null, version.CreatedBy.User,
                                                     version.CreatedBy.User, version.Created, version.Created,
                                                     oldFile.CheckInComment, true);
          //Even though the dates were set in the call above, it doesn't work
         
// when setting them in the past, so call this fixes the issue
          UpdateListItem(oldestFile.Item, version.Created, version.Created);
          list.Update();
      }
      //Add the last version of the merged document
      WriteFileToSharePoint(list, merge, newUrl, false);
      //Add the Final version of the document
      WriteFileToSharePoint(list, keep, newUrl, true);
}
/// <summary>
///
Helper function to set the created and modified dates on a list item.
///
The reason for this function is to allow the dates to be set in the past.
/// 
</summary>
/// <param name="item">a SPListItem
</param>
/// <param name="created">Date created
</param>
/// <param name="modified">Date modified
</param>
protected void UpdateListItem(SPListItem item, DateTime created, DateTime modified)
{
     
//Update the modification/creation dates.
      item[SPBuiltInFieldId.Modified] = modified.ToLocalTime();
      item[SPBuiltInFieldId.Created] = created.ToLocalTime();
      item.UpdateOverwriteVersion(); //Keep the changes to the date/time from making a new version of the item
}
/// <summary>
///
Upload a file to a list based on an existing SPListItem
/// 
</summary>
/// <param name="list">The target list for the upload
</param>
/// <param name="doc">The existing SPListItem
</param>
/// <param name="newUrl">The url to upload the file to
</param>
/// 
<param name="final"></param>
protected void WriteFileToSharePoint(SPList list, SPListItem doc, string newUrl, bool final)
{
     Hashtable props = null;
     if (final)
        props = doc.Properties;
     var lastMergedFile = list.RootFolder.Files.Add(newUrl, doc.File.OpenBinaryStream(), props, doc.File.Author,
                          doc.File.Author, doc.File.TimeCreated, doc.File.TimeCreated,
                          doc.File.CheckInComment, true);
     UpdateListItem(lastMergedFile.Item, doc.File.TimeCreated, doc.File.TimeLastModified);
     list.Update();
}

I deployed this as a PowerShell cmdlet, which is explained here and here. Essentially that route was chosen because the merging was to be performed on request by an administrator. It would be easy enough to wire this up to a ribbon command in the UI though.

Posted: Thursday, March 03, 2011 1:49:46 PM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
SharePoint

“Keyset does not exist” Error while Installing SharePoint 2010

In my continuing battle with SharePoint, I needed to set up a new development environment. This time I am constrained to using Windows 7. Clearly, this has been done before and good instructions exist. But, as is frequently the case with me and SharePoint, it just doesn’t work as advertised. I was performing an install with a local account instead of a domain account (a constraint I have no control over), which I have had issues with before. This time I got a new error when trying to create the configuration database using the new-SPCofigurationDatabase cmdlet:

Keyset does not exist

Turns out this was happening whether I tried to do a complete install with the SharePoint version of SQL Express or with a full version of SQL Server. I found one post which purported to have the solution, but it wasn’t the case for me. I think Sasha was trying to do a standalone install, which is not what I wanted. I have SQL 2008 R2 installed, and I wanted to use that instead of getting another SQL Express instance installed.

This post pushed me in the right direction. Unfortunately, I did not have the directory pointed out in the post, possibly because I was on Windows 7. For me the path was:

C:\Users\All Users\Microsoft\Crypto\RSA

The service account I was using during the install did not have permission to that directory. So I gave it Read/Write access, and was able to create the configuration databases and using the SharePoint Products Configuration Wizard to complete the setup and configuration of SharePoint.

Posted: Thursday, February 03, 2011 12:55:21 PM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
PowerShell | SharePoint | Strange Problems

Executing PowerShell in a Post-Build Event

This is another one of those tasks where I thought it should be simple. That should be a red flag right there. I ended up spending way too much time on this, and as it turns out it is really only a matter of syntax. I ran across a lot of incorrect information out there, so I figured I should post this and help make the links to the right content easier to find.

I was actually building my own cmdLet for PowerShell in C#, and decided I should deploy it using PowerShell, which seemed appropriate. For me, the basic stumbling block for getting this working was quotes. I had multiple paths in my script, all of which had spaces in them, which necessitated the use of nested quotes. This was quite hard to figure out, but Philippe had the right information. He also pointed to this article, which had a thorough explanation of how to call PowerShell from the command line, which is essentially what you are doing inside a post-build event.

Since my cmdLet was really for SharePoint, I later figured out how to add it using the SharePoint 2010 project type in Visual Studio and some declarative XML, making all the previous pain a moot point.

Posted: Friday, January 28, 2011 4:17:18 PM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
PowerShell | SharePoint | Visual Studio

Shrinking SQL Log Files During SharePoint Development

I have been developing for SharePoint lately, working on timer jobs. This resulted in tons of deployments and job runs, and before I knew it my SharePoint_Config database was huge! I was working in a virtual machine with disk partitions, and my data disk ran out of space. At least it was on a partition and I didn’t hose my OS….

So it seems to be a simple problem to solve. Backup the database, shrink the log file. Not so fast partner, this is SharePoint. The database is not in Simple recovery mode, it’s in Full recovery mode (for a reason I am sure, I just don’t know what it is). You can truncate the log file and remove the data, but the physical file won’t shrink. My colleague J Wolfgang Goerlich pointed out one of his old blog posts that was just the ticket. Essentially you are changing the mode, shrinking the file and changing the mode back all in one operation, which won’t upset SharePoint. The log file went from about 9 gigs to 600k. Nice.

As noted in the blog post, this is not a good thing to do to a production system, there are consequences. But for a dev system I wouldn’t worry about it.

Posted: Tuesday, January 25, 2011 6:25:20 PM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
SharePoint | SQL Server

Why Use var?

Ever since the var keyword was released in C#, I’ve struggled with using it. To me var felt like using variant or object in VB, even though logically I know it is not.

List<string> items = new List<string>();
vs.
var items = new List<string>();

Since I bought Resharper, it’s been nagging me to use var in my declarations, so I  have acquiesced. Today that paid off. I had to refactor something which caused a few type changes. It was so much easier to deal with because I used var instead of writing out the declaration the old way, there were just fewer changes. I’m now a complete convert to var, thanks Resharper.

Posted: Thursday, January 06, 2011 3:22:32 PM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
.Net 3.5 | Programming | Tools

Printing a PDF from a web page

The requirement in this case is to print a PDF document, but not allow the user to manipulate the PDF in any other way. The user should see the Print Dialog box and be able to manipulate the settings. This requirement was born out of an auditing policy where the document is audited differently for printing or viewing the PDF.  I found lots of web discussion about printing a pop-up window from the parent window, or trying to print the contents of an IFrame, but none of it worked for me. I was hit with security errors from IE about cross-domain issues, even though my pages were in the same domain, or just got a blank page. So to get a working solution, I had to step out of the web page.

The tool that really allowed me to do this is iTextSharp, a C# implementation of the iText open source java library for manipulating PDF files, and the fact that you can use JavaScript within a PDF file. I used an ASPX page which manipulates and streams the PDF file into a zero-height, zero-width IFrame to keep the user from manipulating the PDF in any way (I realize tech-savvy users could still get the PDF).  I embedded the JavaScript into the PDF, where it runs once the document is loaded and pops up the Print dialog from Adobe Reader (not the browser).

The JavaScript for printing a PDF is not so complex, I embedded it into a function:

protected string GetAutoPrintJs()
{
    var script = new StringBuilder();

    script.Append("var pp = getPrintParams();");
    script.Append("pp.interactive = pp.constants.interactionLevel.full;");
    script.Append("print(pp);");

    return script.ToString();
}

In the code-behind for the ASPX page that will stream the PDF and place the script inside the PDF file, I built this function:

protected void StreamPdf(Stream pdfSource)
{
    var outputStream = new MemoryStream();

    var pdfReader = new PdfReader(pdfSource);

    var pdfStamper = new PdfStamper(pdfReader, outputStream);

    //Add the auto-print javascript
    var writer = pdfStamper.Writer;
    writer.AddJavaScript(GetAutoPrintJs());

    pdfStamper.Close();

    var content = outputStream.ToArray();
    outputStream.Close();
    Response.ContentType = "application/pdf";
    Response.BinaryWrite(content);
    Response.End();
    outputStream.Close();
    outputStream.Dispose();
}

The PDFReader and PDFStamper  classes are members of the iTextSharp library. I used the classes together to get the stream for output and also access to PDFWriter object that allowed me to add the JavaScript.

Posted: Monday, December 20, 2010 3:12:11 PM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
.Net 3.5 | ASP.NET | JavaScript/AJAX

Configuring CRM 4.0 for SSRS Without the Data Connector

I’m sure the first question someone would ask me is why do this, if installing the  SRS Connector does the job just fine? And I would answer that it does work, and that’s what we did. For the first install. We were creating two instances of CRM, one for a production environment, and one for a test environment. Each instance of the CRM application was on its own virtual web server, and the databases were to be housed in a single physical SQL Server. In this situation, we wanted both the sets of the CRM databases on the same database server. CRM always names the config database MSCRM_Config, so you can’t install a second instance in the same database server because unlike SharePoint, the configuration database name is always the same, and the second install just simply fails. So the initial solution to the problem was to use a second instance of SQL Server on the same physical server. This worked great, until we had to install the SRS Connector on the SQL Server. During the install of the SRS Connector you configure the URL to Reporting Services. The connector only works for one instance of SSRS on the physical box where it is installed. This left us out in the cold for the other instance in terms of the SRS Connector. Since CRM 3.0 didn’t have the SRS Connector, it’s pretty well-known how to configure Kerberos trusts so that SSRS can work from inside CRM without the SRS Connector, so that’s what we needed to do.

There are two ways to accomplish this:

  • Run the CRM site IIS application pool under a domain account
    • Set the SPN for that account
    • Set up trust for delegation to that account
  • Run the CRM site IIS application pool under a local account
    • Set a trust for the machine hosting CRM

Since it seemed to be fewer steps and less to maintain, we went with the second option, setting up an Active Directory trust for the web server hosting CRM.

It was not difficult to set the trust:

  1. On your domain controller (Server 2008 for me), open Active Directory Users and Computers.
  2. Navigate to (or find) the computer hosting CRM
  3. Open the properties of the computer
  4. Choose the Delegation tab
  5. Select Trust this computer for delegation to specified services only
  6. Select Kerberos only
  7. Click the Add button
  8. Click the Users or Computers button
  9. Enter the name of the CRM web server
  10. In the resulting list, choose http in the Service Type Column.
  11. Click a couple of OK buttons
  12. Reset IIS on the CRM server

It should look something like this when you are done:

Any other DC’s may have to sync, but at this point you should be all set.

Posted: Thursday, December 02, 2010 1:52:43 PM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
CRM | SQL Server | SSRS

Don’t Forget Your Place

It’s easy to get wrapped up in the technology you use, thinking everyone is using it because everyone you interact with is using it. But when you back up and look at the bigger picture (which you should do), you may not be where you think you are. Tiobe Software creates a monthly Programming Community Index rating the use of programming languages. Our beloved C# comes in a number 6, behind (Visual) Basic of all things! I’m not really surprised by the leading languages (Java, C, C++), and it shows that while Microsoft is strong in the marketplace, it’s development tools and technologies are not leading the pack. But when looking at the charts over time, two of those leading languages have been declining in use for many years. C and (Visual) Basic have been consistent for a long time, and C# has been growing. I think the programming landscape has been fragmenting, the dynamic languages like Ruby and Python are gaining more mainstream acceptance each year, and the popularity of new platforms like mobile phones is going to keep changing that landscape.

So does that mean my choice of Microsoft as a platform isn’t the best choice? Hard to say, but I believe it’s a solid, reliable choice. I certainly think that it’s getting harder and harder to be specialized in any one language. Employable developers are going to need many programming tools in their tool chest. The end result is that fewer developers will truly be an expert with their tools, but will be good enough to get some work done. I don’t believe this bodes well for software quality going forward. There is already too much to know and be good at, I certainly struggle with this problem myself. As a consultant, learning lots of different tools is a necessity, so I will continue to keep working towards expertise in the tools I know, and not letting my interests get too fragmented.

Posted: Monday, August 16, 2010 9:00:13 AM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
Programming | Reviews | Tools

Cloud Services for Developers

This table is just a quick comparison for the types of services that the major cloud providers offer as of the posting date. This is by no means a comprehensive comparison, as I am sure there are other providers and types of services I’m not listing. And I’m not being feature or platform specific either. For instance I realize that Google’s App Engine and Windows Azure don’t allow for the same development platforms. But they do provide a similar service at a high level. Also I’m sure the features for each category vary by provider.

  Microsoft Amazon Google Rackspace
App Platform Azure   App Engine Cloud Sites
Relational DB SQLAzure RDS   Cloud Sites
Blob Storage Azure S3 App Engine Cloud Files
Table Storage Azure SimpleDB App Engine  
Pure VM planned EC2   Cloud Servers
CDN Azure CDN Cloudfront   Cloud Files
Queue Azure SQS App Engine  
On-Premise planned   App Engine Open Stack
Posted: Thursday, August 12, 2010 1:13:36 PM (Eastern Standard Time, UTC-05:00)  #    Comments - Trackback
Azure | Cloud | Tools