Monday, April 22, 2013

Map open generic class to open generic interface in StructureMap

Suppose that you have the following generic interface:

   1: public interface IMapper<T, U>
   2: {
   3:     U Map(T source);
   4: }

And you use Automapper in implementation:

   1: public class Mapper<T, U> : IMapper<T, U>
   2: {
   3:     public U Map(T source)
   4:     {
   5:         return Mapper.Map<T, U>(source);
   6:     }
   7: }

I.e. in your code you will have bunch of mapping interfaces like IMapper<Foo1, Bar1>, IMapper<Foo2, Bar2>, …, which will be implemented by Mapper<Foo1, Bar1>, Mapper<Foo2, Bar2>, …, i.e. by the same class with the same generic parameters. How to configure mapping for these interfaces in StructureMap? First way is to write all possible mappings in the custom registry:

   1: public class MyRegistry : Registry
   2: {
   3:     public MyRegistry()
   4:     {
   5:         Scan(x =>
   6:                  {
   7:                      x.Assembly("MyAssembly");
   8:                  });
   9:         For(typeof(IMapper<Foo1,Bar1>)).Use(typeof(Mapper<Foo1,Bar1>));
  10:         For(typeof(IMapper<Foo2,Bar2>)).Use(typeof(Mapper<Foo2,Bar2>));
  11:         ...
  12:         For(typeof(IMapper<Foo_n,Bar_n>)).Use(typeof(Mapper<Foo_n,Bar_n>));
  13:     }
  14: }

But with this approach you will need to add new mapping for each new pair of classes which you will add in the future. Is there an easier way to define mapping for open generic interface IMapper<T,U> to open generic class Mapper<T,U>? If you would have own class for each mapping (like Foo1Bar1Mapper, Foo2Bar2Mapper, …), then you would be able to use ConnectImplementationsToTypesClosing method, but not in this example, because we have the same class for all mappings. However, it is also possible for our case:

   1: public class MyRegistry : Registry
   2: {
   3:     public MyRegistry()
   4:     {
   5:         Scan(x =>
   6:                  {
   7:                      x.Assembly("MyProcessor");
   8:                  });
   9:         For(typeof(IMapper<,>)).Use(typeof(Mapper<,>));
  10:     }
  11: }

Mapping is defined in line 9. With this configuration Mapper<,> class will be used for all new mappings which you will use in the project.

Sunday, April 21, 2013

Managed metadata navigation and friendly URLs in Sharepoint 2013 – part 3. Configure sub sites and pages

In this article I continue overview of the managed metadata navigation and friendly URLs features in Sharepoint 2013. In the previous parts (first and second) I described basic functionalities available in the standard UI. Now let’s see how managed metadata works during creation of sub sites and pages what is very important for content production.

Scenario 1. Create sub sites.

First of all let’s try to create sub site and configure managed metadata navigation with friendly URL for this sub site. Interface for creating sub sites is almost the same as in Sharepoint 2010:

image

In our example we will create sub site Products with the same URL. In the Navigation Inheritance set “Use the top link bar from the parent site” to Yes.

After creation will be completed we will be redirected to /Products sub site, but it won’t appear in the navigation: neither in current nor in global. This is first finding: Sharepoint doesn’t create navigation nodes for sub sites in the managed metadata navigation as it does in case of regular structural navigation. In order to show it here we need to go to Term store management and add new term manually (also we can add it via WYSIWYG navigation editor). It is important that new term will have “Navigation node type” set to “Term-Driven Page with Friendly URL” and that friendly URL will be the same as URL which we specified on new sub site page (Products in our case):

image

In this case term will be related with sub site automatically, i.e. Products will appear in the navigation and when we will go to this sub site, navigation node will be shown as selected:

image

The same will happen if we will create sub site of 2nd level, e.g. Products > Catalog. In this case we need to create Catalog term as child term under Products term:

image

After that it will be shown in the navigation:

image

Of course if we will delete sub site or will change URL via Site settings > Title, description and logo, we will need to sync these changes in term store manually.

Scenario 2. Create pages.

At first let’s create new page (e.g. with Legal title) on the root site of the site collection:

image

Sharepoint automatically shows friendly URL of the new page on the create page dialog. In the example above we can see that it will point to the root site, which is logical in this case.

After creating of the new page new Legal item will appear in the navigation automatically:

image

I.e. in opposite to the sub sites, Sharepoint creates navigation node for new pages automatically. Also it will automatically synchronize changes in Term store management if we will delete this page or change URL via Edit properties in the ribbon. Also if we will create new term and will associate it with some page on the root site manually Sharepoint also will automatically synchronize changes for it in the term store.

Ok, on the root site friendly URLs for pages work quite good. Now let’s create new page on the Products sub site which we created above. Go to Products sub site and click Site settings > Add a page. And here we face with the first problem: Sharepoint will create this page on the root site, not on the sub site:

image

Ok, although it is not so convenient for content producers, we can avoid this problem by creating the page on the sub site via Site manager. We need to choose sub site in the left tree view and then select New > Page from the toolbar:

image

After creating we will be redirected to the new page and will see regular not friendly URL in the address bar:

/Products/Pages/Info.aspx

Also navigation term won’t be created automatically. Let’s try to configure it by ourselves, i.e. to create new navigation term under Products and set URL of the target page manually:

image

New navigation node will appear in the navigation, but when we will click on it we will get “Page not found” error:

image

This looks like a bug in the managed metadata navigation: friendly URLs work for the pages on the root site, but doesn’t work on sub sites, except site welcome pages (for welcome pages friendly URLs work on all levels).

At the current moment we found the following workaround: we need to change sub site URL so it won’t equal to the friendly URL of the associated navigation term. In our example we need to change URL of the Products sub site from /products to e.g. /products-site. After that we need to manually set the target URL for Products term to the /products-site/pages/default.aspx:

image

and fix target URL for the all sub terms, including term which corresponds to our page Info.aspx. After that friendly URLs become working for the pages on sub sites. However you still need to use Site manager for creating the pages, because Sharepoint will still create pages on the root site when you will select Site actions > Add a page. The good thing is that synchronization with term store will still work with this solution, i.e. if we will change URL of the page on the sub site or delete it, changes will be synced with term store automatically.

Summarizing, it seems like that there are 2 bugs in the current implementation:

  1. When you create a page on sub site when use managed metadata navigation with friendly URLs via Site settings > Add a page, Sharepoint creates it on the root site.
  2. Friendly URLs for all pages on the sub sites, except welcome page (default.aspx), don’t work if the real URL of the sub site equals to the friendly URL, configured in the navigation term settings. In order to make it work we need to change site URL (e.g. by adding “-site” suffix) and configure navigation term sets’ target URLs manually.

We created support ticket from MS about these problems, will update when/if we will get some news about them. Hope that this information will help you when you will deal with managed metadata navigation.

Thursday, April 18, 2013

How to test Sharepoint sites on iPad, iPhone and other mobile devices

In Sharepoint development we often use VMWare images for development environments. In most of the projects in my practice we used this model. It is useful when there are configured ready for use development images, so developers only need to get the image and start to work. But how we can test sites which are developed on the VMWare images on mobile devices? At the current moment role of mobile devices in web development is growing and Sharepoint 2013 reflects this fact with new features like device channels for improving user experience for using sites on smartphones or tablets. Responsive design becomes more and more popular, and it became a trend in the public web sites development. If some time ago the usual situation was that site has good design for desktop browser, but poor design for mobile, then now I already saw sites which looks nice on mobile and awful on desktop. Fun example, but it is true.

If you develop new site on Sharepoint 2013 (or any other version of Sharepoint) and want to test it against real devices (not simulators), there are several ways:

  1. configure DNS – add A record like foo.example.com which points to some external IP address of your organization (you need to use IP address of organization because in most cases in our daily work we use organization network and domain) and then ask administrators to configure proxy servers to forward requests for this domain name to your development image. It is possible, but has risks, especially if you are working in big organization and this process requires approval on several levels;
  2. setup builds on separate build server with configured DNS as in previous step. But it is also problematic when during development you need to test small changes. Build can take time, so deploying changes to another server will make the process slower;
  3. configure ports forwarding and use IP address instead of domain name for testing.

In this post I will describe how to make it using 2nd way.

Step 1. Connect to the WiFi network (guest network) of your organization.

If you want to test your dev sites on mobile devices you need to connect your host machine to the WiFi network (I don’t know is it possible to use LAN e.g. on iPhone, but knowing how technologies are growing, it won’t be surprise for me if there is some gadget which allows to do that). Many organizations have guest networks with limited access and we may use this network for our need.

Step 2. Get IP address of the host in the guest network.

Once you have connected to the WiFi get IP address of the host machine using the ipconfig command. You need to copy “IPv4 Address” property of the WiFi connection. For our example suppose that it will be the following:

10.20.30.40

Step 3. Allow incoming requests to 81 port on the host and guest.

For testing it is simpler to use some port which differs from default http 80 port, which can be used on the host for other purposes. In our example we will use port 81. In order to allow connections using this port go to Administrative tools > Windows Firewall with Advanced Security > Inbound Rules > New Rule:

On the 1st step choose “Port” and click Next:

image

On 2nd step add 81 to “Specify local ports”:

image

On 3rd step leave “Allow connection”:

image

On the 4th step leave all options checked:

image

On the last 5th step specify name for the new rule:

image

Make the same configuration on guest.

Step 4. Configure port forwarding in VMWare virtual network editor.

In order to configure port forwarding your virtual image should use NAT network connection. Go to VMWare > Edit > Virtual network editor, choose the network connection used by your image and click NAT Settings button. In the opened window click Add under “Port Forwarding”:

image

In the opened “Map Incoming Port” window specify the following values:

  • Host port: 81 – port which will be used for accessing the site from mobile device;
  • Type: TCP;
  • Virtual machine IP address: 11.22.33:44 – here you should specify IP address of the virtual image. You may check it using ipconfig inside the guest image. For our example we will assume that it will be 11.22.33.44;
  • Virtual machine port: 81 – it is also better to use different than default 80 port on the guest image, because when you setup port forwarding for port 80 in VMWare, you loose internet connection on the guest.

Window with values should look like this after all:

image

After that click Ok on all windows so changes will take effect.

Step 5. Setup bindings in IIS site.

Inside virtual image go to IIS manager, select your Sharepoint web site, click Bindings > Add:

image

In IP address you need to specify IP address of the virtual images, the same as was specified on step 4 in the “Map Incoming Ports” window. Port should be set to 81: also the same which was used on steps 3 and 4.

Step 6. Add alternate access mappings in Central Administration.

Go to CA > Configure alternate access mappings and filter AAM collection for your Sharepoint site. Then click “Edit public URLs”. In some free zone add the following URL: http://10.20.30.40:81 (i.e. use IP address of the host as it will be used for opening the site in mobile device’s browser):

image

After that click “Add Internal URLs” and add URL http://11.22.33.44:81 (i.e. use IP address of guest system here) for zone where you specified public URL above – Intranet in our example. So alternate access mappings should look like this:

image

After that get your iPhone, connect it to the guest WiFi, enter http://10.20.30.40:81 in the browser and test your Sharepoint site in real mobile browser. Hope this guide will help you in development of sites with great user experience for mobile devices.

Saturday, April 13, 2013

Script for copying and archiving files to zip for making regular backups in cloud using built-in Windows features

Often we need to backup some files on the servers on regular basis. There are 2 important things about backups which you should keep in mind and use:

  • store backups on another server (not on the same where files are located);
  • test restore scenario.

In order to store backups on another servers you may use online storages, like box.com, dropbox, Google drive, MS SkyDrive and others. Also many cloud hosting providers have own backup solutions, for example Rackspace Cloud Backup, which requires installation of additional backup agent on the server (it is free). It is really convenient solution, because you don’t need to have separate server image which is not cheap. However cloud storages are also not free and in most cases you have to pay as much as many megabytes you use. So in order to reduce costs at first you need to take care about deleting old backups from the cloud. Some cloud providers have own solutions for it. E.g. currently Rackspace Cloud Storage contains 3 options to delete old backups:

  • store backups 30 days;
  • store backups 60 days;
  • store backups infinitely.

Also some backups software have option to delete files from backups using configurable rules. At second you need to decrease file size which is stored in the cloud storage. Many backup programs also can do that, but it can be done only by using Windows built-in features. In order to do it you may use simple cmd script which copies original file from its folder to folder for backup (in order to not affect work of the programs which use this file) and archives it to zip:

   1: xcopy C:\source_folder\source_file.dat C:\target_backup_folder\temp /y
   2: CScript zip.vbs C:\target_backup_folder\temp C:\target_backup_folder\backup.zip

As you can see it copies file using xcopy command (line 1) and then packages it to zip archive (line 2) using second script zip.vbs. If you will google for this script you will find many references, however not all of them work. Here is the script which works for me:

   1: Set Args = Wscript.Arguments
   2: source = Args(0)
   3: target = Args(1)
   4:  
   5: ' make sure source folder has \ at end
   6: If Right(source, 1) <> "\" Then
   7:     source = source & "\"
   8: End If
   9:  
  10: Set objFSO = CreateObject("Scripting.FileSystemObject")
  11: Set zip = objFSO.OpenTextFile(target, 2, vbtrue)
  12: ' this is the header to designate a file as a zip
  13: zip.Write "PK" & Chr(5) & Chr(6) & String( 18, Chr(0) )
  14: zip.Close
  15: Set zip = nothing
  16:  
  17: wscript.sleep 500
  18:  
  19: Set objApp = CreateObject( "Shell.Application" )
  20: intSkipped = 0
  21:  
  22: ' Loop over items within folder and use CopyHere to put them into the zip folder
  23: For Each objItem in objApp.NameSpace( source ).Items
  24:     If objItem.IsFolder Then
  25:         Set objFolder = objFSO.GetFolder( objItem.Path )
  26:         ' if this folder is empty, then skip it as it can't compress empty folders
  27:         If objFolder.Files.Count + objFolder.SubFolders.Count = 0 Then
  28:             intSkipped = intSkipped + 1
  29:         Else
  30:             objApp.NameSpace( target ).CopyHere objItem
  31:         End If
  32:     Else
  33:         objApp.NameSpace( target ).CopyHere objItem
  34:     End If
  35: Next
  36:  
  37: intSrcItems = objApp.NameSpace( source ).Items.Count
  38: wscript.sleep 250
  39:  
  40: ' delay until at least items at the top level are available
  41: Do Until objApp.NameSpace( target ).Items.Count + intSkipped = intSrcItems
  42:     wscript.sleep 200
  43: Loop
  44:  
  45: 'cleanup
  46: Set objItem = nothing
  47: Set objFolder = nothing
  48: Set objApp = nothing
  49: Set objFSO = nothing

It has 2 parameters: first is folder which has to be archived and second is path to output zip file. Unfortunately I found it quite long time ago and can’t provide the link to the original site, where I found it. But all credits goes to its author.

After that you need to setup scheduler task which will run this cmd script by schedule, e.g. daily. And the remaining thing is to setup copy of zip archive to your cloud storage. Using this technique you will be able to reduce the costs which you have to pay for storing backups in the cloud.

Sunday, April 7, 2013

Managed metadata navigation and friendly URLs in Sharepoint 2013 – part 2

In the first part of the article I described basic functionality of managed metadata navigation, introduced in Sharepoint 2013. In this second part I will continue overview of new feature and will show how friendly URLs are working in conjunction with managed metadata navigation (Update 2013-04-21: third part is here).

Let’s examine URLs of the navigation terms which we created in the 1st part:

Navigation term URL
Company /company
Company > About us /company/about-us
Company > Contacts /company/contacts
Company > Investors /company/investors
News /news
News > News archive /news/news-archive
News > Press releases /news/press-releases

As you can see URLs are similar to navigation term titles and they don’t contain any /Pages/default.aspx part. These are friendly and clean URLs, which are created automatically from term title. Note that spaces in the titles are replaced by dashes.

Let’s check properties of the navigation terms in Site settings > Term store management and find local site collection’s managed metadata group (remember that this group is visible only when term store is opened in context of its parent site collection. I.e. it won’t be shown if we open e.g. Managed metadata service application from Central administration). For each term there are several tabs with properties available:

image

These tabs are:

  • General
  • Custom sort
  • Navigation
  • Term-driven pages
  • Custom properties

General tab is similar to those which was in Sharepoint 2010. The great addition in Sharepoint 2013 is that now it is not necessary to install language pack in order to be able to specify term labels for different languages. On the picture above English language is selected. In order to add more languages select Managed metadata services root node in the left tree in the term store and in “Working languages" select “Other locales”. After that it will allow to add all available locales regardless of installed language packs:

image

In the example above I’ve selected Russian language, although I don’t have Russian language pack on my dev env. After that on the General tab of the navigation term Russian language will be shown:

image

Custom sort tab allows to specify sorting order of the navigation terms in the navigation controls:

image

If you remember from 1st part it is possible to change order of navigation terms using WYSIWYG editor and drag and drop. Custom sort tab contains duplicating functionality, which is less convenient, but will work of WYSWIG editor won’t work because of some reason (e.g. if custom controls are used).

Navigation tab contains navigation settings for the term. This is one of the important tabs in context of our subject:

image

“Navigation Node Title”, “Navigation Hover Text” and “Visibility In Menus” settings are quite obvious. “Navigation Node Type” is quite interesting. It determines will users see friendly URL when they will select the current term’s node in navigation or not. As shown on the picture above option “Term-Driven Page with Friendly URL” is used for that. If we would change it to “Simple Link or Header”, then users will see well known “old” URL in the browser address bar.

URL can be specified manually or using standard asset picker. If you specify URL manually you can type everything, including URL which points to external site or to another site collection or sub site within current web application. When you specify URL via asset picker, then you may only select pages from current site and its sub sites.

Another interesting setting is Associated folder. Description of this setting is quite straightforward:

“Pages in this folder will be associated with this navigation node. When you navigate to one of these pages, this navigation node will appear selected in navigation menus, unless that page is already a target page for another navigation node in this term set.”

For testing I’ve created Products sub site and set it to the Associated Folder property of Company term. After that if I go to any page on Products sub site “Company” navigation node was selected:

image

Term-driven pages tab contains settings, which allow to configure friendly URLs for the term (and some other settings, related with Catalog publishing – this is another new feature, which I will describe in separate posts):

image

“Configure Friendly URL for this term” option allows to overwrite default friendly URL which is created by Sharepoint automatically based on the term’s title. “Target Page Settings” settings allow to specify the real physical page which is loaded when user navigates to the current term’s friendly URL. I.e. by default after navigation term set has been created, when user will click on any navigation node, he/she will go to appropriate friendly URL, but as there are no any real page behind it, user is redirected to standard “Page not found” page (PageNotFoundError.aspx). But if we create e.g. regular publishing page /pages/company.aspx and then will specify it in “Change target page for this term”, then user will see this page when will click Company link in the navigation:

image

I.e. it is kind if standard URL rewriting in Sharepoint.

Also on “Term-driven pages” tab it is possible to specify target URL of the children terms of the current term using “Change target page for children of this term”. It will be possible to override this URL for each children term by editing properties of this children term.

In “Category image” setting it is possible to specify image for this term which then can be displayed using Term Property web part. “Catalog Item Page Settings” are used in conjunction with cross-site publishing and catalog settings. I will write about them in separate posts. Managed metadata navigation and friendly URLs are great features by themselves, but with cross-site publishing and catalogs, it is even more powerful.

Custom properties tab is the last tab in the term settings:

image

It is kind of property bag of the term. It allows to add different properties and then use them e,g, in OTB Search item reuse web part, which is used on the Catalog item pages with cross-site publishing. But of course it is not the only application of these properties. You may store any additional information in these properties even if you don’t use managed metadata navigation. “Shared properties” can be used in all reused or pinned instances of the current term in the term store, while “Local Properties” are available only for the current term.

This was second part of my overview. In the next third part of the overview I will show some dynamic behaviors for managed metadata navigation.

Tuesday, April 2, 2013

Sharepoint MVP 2013

Today I’ve got email from MS with congratulations of being awarded for Sharepoint Server MVP in 2013 year (I wrote about previous here and here). This is my 3rd award for last 3 years and I really appreciate recognitions of my efforts for community. Sharepoint is changing and growing, in the end of 2012 new version of Sharepoint was released and it introduced new approaches and new challenges for developers. Software which we work with become more complicated and from my point of view community ecosystem has crucial role in our work. E.g. in my every day work I use blogs and forums very frequently. And I’m glad to be part of community and share my knowledge and experience via blogs, forums, open source projects, etc. in order to help to other developers.

Thanks to all readers of my blog, and thanks MS for the award. See you with more findings and articles about Sharepoint, .Net, ASP.Net, MVC and other technologies.