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
- Sharepoint 2010 doesn't allow to create a
custom master page for your mobile site
- Sharepoint doesn't allow creating custom
style sheet and classes for mobile
- Publishing portal template is not
supported
- Any layout or any customization in the
master pages will affect all other sites on the same farm
The Approach Used in My project
- Disable SharePoint default mobile
redirection feature to stop the redirection to the mbllists.aspx page.
- Create Http Module which detects that the
request is coming from a Mobile device and redirect to the Mobile home page.
- Create a new sub site for mobile with an
optimized master page and layouts for mobile. (Physical pages and user
controls).
- Multilanguage support
- Create the Search Control and search
result page from scratch (Querying the Crawler results).
- 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.
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
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;