Creating a Sharepoint Portal for Mobile


Introduction

This article discusses the approach I used to develop a mobile version for a Sharepoint 2010 publishing portal, after a lot of research I found that Sharepoint out of the box features will not fit my business needs, so you will find in this article the approach I used.

Note: I'm not sure that this is the best way to develop a mobile version for sharepoint portal so feel free to send me your feedback and comments if you have a better approach.

Sharepoint 2010 Mobile support limitations

Sharepoint 2010 has a mobile support but after research we found the following limitations

  1. Sharepoint 2010 doesn't allow to create a custom master page for your mobile site
     
  2. Sharepoint doesn't allow creating custom style sheet and classes for mobile
     
  3. Publishing portal template is not supported
     
  4. Any layout or any customization in the master pages will affect all other sites on the same farm

The Approach Used in My project

  1. Disable SharePoint default mobile redirection feature to stop the redirection to the mbllists.aspx page.
     
  2. Create Http Module which detects that the request is coming from a Mobile device and redirect to the Mobile home page.
     
  3. Create a new sub site for mobile with an optimized master page and layouts for mobile. (Physical pages and user controls).
     
  4. Multilanguage support
     
  5. Create the Search Control and search result page from scratch (Querying the Crawler results).
     
  6. Create optimized style sheets and Masterpage for mobile.

Limitations of this approach

  • The customer will not be able to create any pages for the mobile site from the back end
     
  • The customer will not be able to create any subsites for the mobile site from the back end
     
  • Will not be able to use SharePoint search, and the way around is to use SharePoint API and create the search controls and the result page from scratch.

The following sections will explain how to implement each point

1. Disable SharePoint default mobile redirection feature to stop the redirection to the mbllists.aspx page

a. Open compat.browser file you will find it in the following directory

C:\inetpub\wwwroot\wss\VirtualDirectories\[port number]\App_Browsers

b. change isMobileDevice for each browser from True to False and save the file.

Example

<capabilities>
<capability name="isMobileDevice" value="false" />
<capability name="platform" value="WinCE" />
<capability name="supportsTouchScreen" value="true" />
</capabilities>

2. Create Http Module which detects that the request is coming from a Mobile device and redirect to the Mobile home page

This is a normal Http Module which detects that the request is coming from a mobile phone and redirect to the mobile site

Sample function used in the Module

public static bool isMobileBrowser()
        {
            //GETS THE CURRENT USER CONTEXT
            HttpContext context = HttpContext.Current;

            //FIRST TRY BUILT IN ASP.NT CHECK
            if (context.Request.Browser.IsMobileDevice)
            {
                return true;
            }
            //THEN TRY CHECKING FOR THE HTTP_X_WAP_PROFILE HEADER
            if (context.Request.ServerVariables["HTTP_X_WAP_PROFILE"]
                != null)
            {
                return true;
            }
            //THEN TRY CHECKING THAT HTTP_ACCEPT EXISTS AND CONTAINS WAP
            if (context.Request.ServerVariables["HTTP_ACCEPT"] != null &&
                context.Request.ServerVariables["HTTP_ACCEPT"].ToLower().Contains("wap"))
            {
                return true;
            }
            //AND FINALLY CHECK THE HTTP_USER_AGENT
            //HEADER VARIABLE FOR ANY ONE OF THE FOLLOWING
            if (context.Request.ServerVariables["HTTP_USER_AGENT"] != null)
            {
                //Create a list of all mobile types
                string[] mobiles =
                    new[]
                {
                    "midp", "j2me", "avant", "docomo",
                    "novarra", "palmos", "palmsource",
                    "240x320", "opwv", "chtml",
                    "pda", "windows ce", "mmp/",
                    "blackberry", "mib/", "symbian",
                    "wireless", "nokia", "hand","android", "mobi",
                    "phone", "cdm", "up.b", "audio",
                    "SIE-", "SEC-", "samsung", "htc",
                    "mot-", "mitsu", "sagem", "sony"
                    , "alcatel", "lg", "eric", "vx",
                    "philips", "mmm", "xx",
                    "panasonic", "sharp", "wap", "sch",
                    "rover", "pocket", "benq", "java",
                    "pt", "pg", "vox", "amoi",
                    "bird", "compal", "kg", "voda",
                    "sany", "kdd", "dbt", "sendo",
                    "sgh", "gradi", "jb", "dddi",
                    "moto", "iphone"
                };

                //Loop through each item in the list created above
                //and check if the header contains that text
                foreach (string s in mobiles)
                {
                    if (context.Request.ServerVariables["HTTP_USER_AGENT"].
                                                        ToLower().Contains(s.ToLower()))
                    {
                        return true;
                    }
                }
            }
 
            return false;
        }


We call this function in the OnBeginRequest event handler as the following

public void OnBeginRequest(Object s, EventArgs e)
        {
 
            HttpApplication app = s as HttpApplication;
            HttpRequest request = HttpContext.Current.Request;

            if (isMobileBrowser())
            {
                if (!request.Url.ToString().ToLower().Contains("/mobilesite/"))
                {
                   app.Context.Response.Redirect("/mobilesite/default.aspx");
                }
            }
        }



3. Create a new sub site for mobile with an optimized master page and layouts for mobile

a. To do this you create the pages and user controls using the normal ASP.NET

b. To deploy you have to options

1. Create a physical folder for the mobile site under the virtual directory then copy the pages and user controls and master page into it

2. Create the folder under the Sharepoint â€" using sharepoint designer â€" then copy the pages and user controls into it (what we implemented)

The benefit of this way is that you don't have to copy the physical files every time you restore the portal, it will be included in the backup file.


moca2.jpg



c. Web.config

1. Each name space must be registered as safe in web.config file

Example

<SafeControl Assembly="MOCA.VideoGallery, Version=1.0.0.0, Culture=neutral, PublicKeyToken=25c226a5c87bfac0" Namespace="MOCA.VideoGallery.VideoGallery" TypeName="*" Safe="True" SafeAgainstScript="False" />

<SafeControl Assembly="MOCA.UserControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=737b390bc690dbba" Namespace="Linkdev.SharePoint.Search.Controls" TypeName="*" Safe="True" SafeAgainstScript="False" />


4. Multilanguage support

The portal saves the selected language by the user in a cookie then reads this value and changes the culture accordingly

Example:

Save the culture

protected void btnEnglish_click(object sender, EventArgs e)
        {
            if (Request.Cookies["PMOMobileCulture"] == null)
            {
                HttpCookie CultureCookie = new HttpCookie("PMOMobileCulture");
                CultureCookie.Value = "en";
                Response.Cookies.Add(CultureCookie);
            }
            else
            {
                Response.Cookies["PMOMobileCulture"].Value = "en";
            }
        }

Read the culture

if (Request.Cookies["PMOMobileCulture"] != null)
            {
                HttpCookie CultureCookie = Request.Cookies["PMOMobileCulture"];
                if (!String.IsNullOrEmpty(CultureCookie.Value))
                {
                    if (CultureCookie.Value.ToLower().Contains("en"))
                        selectedLanguage = "en";
                    else
                        selectedLanguage = "ar-AE";
                }
                else
                {
                    selectedLanguage = "ar-AE";
                }
            }
            else
            {
                selectedLanguage = "ar-AE";
           

            Thread.CurrentThread.CurrentCulture =
                CultureInfo.CreateSpecificCulture(selectedLanguage);
            Thread.CurrentThread.CurrentUICulture = new

                CultureInfo(selectedLanguage);

5. Create the Search Control and search result page from scratch (Querying the Crawler results).


1. Site Crowling

a. From Central Administration Create a new content source under the Search Service Application

b. Select the type of content to be crawled to be (web sites)

c. Add the URL of the portal you want to crawl

search.jpg




Very Important Note: All links in the site must be normal HTML anchors otherwise the crawler won't be able to crawl the content


d. Start the Crawler

2. Search Controls and the result page

a. The following code querying the crawling results using the keywords entered by the user

private DataTable Search(string Keywords, out int totalCount)
        {
 
            DataTable resultsDataTable = new DataTable();
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                SearchQueryAndSiteSettingsServiceProxy settingsProxy = SPFarm.Local.ServiceProxies.GetValue<SearchQueryAndSiteSettingsServiceProxy>();
                SearchServiceApplicationProxy searchProxy = settingsProxy.ApplicationProxies.GetValue<SearchServiceApplicationProxy>("MOCA_Search_Service_Application");

                string squery = string.Empty;
                StringBuilder sb = new StringBuilder();
                sb.Append("SELECT Title, Path, Description, Write, Author,Rank, Size FROM Scope() WHERE ContentSource = 'Mobile Content Source' and FREETEXT(DEFAULTPROPERTIES,'" + Keywords + "') ORDER BY Rank desc");

                FullTextSqlQuery sqlQuery = new FullTextSqlQuery(searchProxy);
                sqlQuery.ResultTypes = ResultType.RelevantResults;
                sqlQuery.EnableStemming = true;
                sqlQuery.TrimDuplicates = true;
                sqlQuery.QueryText = sb.ToString();
                ResultTableCollection resultsTableCollection = sqlQuery.Execute();

                ResultTable searchResultsTable = resultsTableCollection[ResultType.RelevantResults];

                resultsDataTable.TableName = "Results";
                resultsDataTable.Load(searchResultsTable, LoadOption.OverwriteChanges);
            });
 
return resultsDataTable;
 
 

 }