Wednesday, December 4, 2013

SharePoint 2010 Mobile Site - A Clever Work-Around

If you're faced with a challenge of attempting to accommodate mobile browsers on your SharePoint site, you've probably encountered a frustrating limitation of SharePoint - the "scaled, mobile edition", which is stripped to text-only browsing within SharePoint sites, lists, etc. I've read of ways to conquer this via mods to the web.config, but this solution unfortunately wasn't a good fit in my case, as I was limited to designer access only to the SharePoint collection (No back-end access what-so-ever). If this headache is / was yours as well, please read on!

SharePoint uses a "homepage" flag to determine which page within a site should be the landing page. This feature can be utilized to your advantage. These are the steps that allowed me to arrive at victory (Noting that the scenario is using a SharePoint 2010 site as a public facing website):


  • Create a document library, allowing Anonymous the right to view all objects (I would advise breaking inheritance and manually setting the privilege, as inheritance doesn't usually work for Anonymous rights)
  • Upload any graphics needed for the mobile version of the site into this library
  • Create an html or aspx page within the library that will be the new mobile homepage (This would be the page you would like mobile visitors to see)
  • Create a "dummy" html page that is blank, but contains some basic Javascript to re-route, based on the detected browser (Code sample further below)
  • Right click the "dummy" html page and select "Set as Home Page"
  • *Note - it's key to setup this "special document library" and nest the landing page inside it, as simply rerouting to your full version homepage triggers an authentication challenge on mobile browsers without tweaking the web.config
This does assume that the domain or DNS entry connecting to SharePoint is pointed without targeting a specific page, but rather a site. This will effectively bypass the "mobile version" of SharePoint and allow you to control the UX with much more granularity. Just be sure to hook the function into the onLoad handler. Enjoy!


function mobileCatch()
{
if ((navigator.userAgent.indexOf('iPhone') != -1) || (navigator.userAgent.indexOf('iPod') != -1) || (navigator.userAgent.indexOf('iPad') != -1) || (navigator.userAgent.indexOf('Mobile') !=-1))
{
location.href='MobileWebSite.aspx';
}
else
{
        location.href='FullWebSite.aspx';
}
}


Friday, October 11, 2013

Programmatically Upload to SharePoint Document Library

Programmatically uploading to a SharePoint Document Library seems like a simple thing, given that it's one of the more well-advertised features of SharePoint, but as it turns out, trying to craft your own from scratch is actually a bit of a challenge. I found quite a bit of documentation on how to design a method (C#), but none of them actually worked in the way I needed. All make the assumption that a static file will be uploaded, and none are designed to allow for dynamic selection of a file (Any file that will safely upload into a SharePoint document library). My intended use was to create a custom upload for use within a SharePoint form. I couldn't rely on the built in "list attachments" feature, as this breaks (And still fizzles into a spectacular wreck, I dare say - Not sure why Microsoft hasn't taken the hint from every previous version that has this flaw, which is all of them, but I'm not bitter; much) when you customize the list form.

Regardless, in the end, I had to detach the custom uploader control and method to a separate page, as I have yet been unable to determine the wrapper enveloping the control when used inside a webpart (Using the method and control inside a DataForm Web Part caused the control to be "lost" - no amount of wrapping, based on the Page, form, or webpart ID seemed to isolate the control). 

Here's my method and control that accomplished the goals (ASP.NET, Framework 3.5, C#):
<script runat="server">
public void documentUpload_OnClick(object sender, System.EventArgs e)
{       
String fileToUpload = FileUpload1.PostedFile.FileName;
String sharePointSite = "http://sharepointsite/";
String documentLibraryName = "Library Name";

using (SPSite oSite = new SPSite(sharePointSite))
{
    using (SPWeb oWeb = oSite.OpenWeb())
    {
    if(FileUpload1.HasFile)
    {
    status.Text = "Uploading...";
    FileUpload1.SaveAs(FileUpload1.PostedFile.FileName);
    SPFolder myLibrary = oWeb.Folders[documentLibraryName];

      
        Boolean replaceExistingFiles = true;
        String fileName = System.IO.Path.GetFileName(FileUpload1.FileName);
        FileStream fileStream = File.OpenRead(FileUpload1.PostedFile.FileName);

        SPFile spfile = myLibrary.Files.Add(fileName, fileStream, replaceExistingFiles);

        myLibrary.Update();
        status.Text = "Upload Complete";
        }
    }
  }
}
</script>
<asp:FileUpload runat="server" id="FileUpload1"/>
<asp:Button runat="server" OnClick="documentUpload_OnClick" Text="Upload PDF" id="Button1"/>
<asp:Label runat="server" ID="status" Text="Ready"/>

Friday, August 23, 2013

SharePoint Workflow Triggers - Email Item Does Not Exist

I made a valuable discovery when troubleshooting a VS2010 sequential workflow. I had created a document library in SharePoint 2010 to receive email, then implemented an override on the event receiver SPEmailReceived to capture other facets of the email, as well as catch and wipe colons (For example "FW:"), as "odd" characters, such as ": . / \". I had designed the workflow to be triggered when a new item was "created" or added to the document library, and this is where the trouble started - it would complete ~75% of the time when fired, producing an error that the email object did not exist. I realized that the receiver override was still invoking changes and collecting information when the "created" trigger would fire. To fix this, I set the workflow trigger to "on change", which does not begin to fire until after the item has been created with the event receiver override steps completed.

Wednesday, July 24, 2013

Connection Timeout vs. Command Timeout

I was doing some work with gridviews and mssql and encountered a very difficult issue that took pointed, dedicated research to resolve. The issue was that the page with the gridview would time out after thirty seconds, even though the timeout property was set in the SQLDATASOURCE connection string to "0", or infinite. Remote queries were set on the SQL server instance to run until complete (Again, as "0"). IIS was set to break the connection at 600 seconds. Plenty of time all around for a query that completes at worst in ~40 seconds.

I dug through forum after forum that insisted that the timeout issue was due to the Connection Timeout property, but no amount of tweaking made any difference; the query still timed out at exactly thirty seconds. I tested setting a two minute threshold of the httpRuntime in web.config, which made no difference. After some creative Googling, I discovered the existance of the CommandTimeout property, which cannot be set from the gridview. The difference between ConnectionTimeout and CommandTimeout is this:

  • ConnectionTimeout = maximum time in seconds allowed to make a connection to a datasource
  • CommandTimeout = maximum time in seconds allowed to complete a command (Say, a query for example)


I created the following function, hooked to the OnSelecting handler of the gridview, which finally did the trick and allowed the query to complete (Granted, 5000 seconds is a lot of time and is used as an example - roughly 83 minutes - use your discretion for this value!)

<script runat="server">
protected void SqlDataSourceSelectingEventHandler(object sender, SqlDataSourceSelectingEventArgs e)
{
e.Command.CommandTimeout = 5000
}

</script>

Wednesday, April 10, 2013

JQuery - Imitating a "pause \ resume" with ".animate"

I spent some time working on a project to use the core ".animate" method provided in JQuery and became quickly astounded that it included only a measure for halting the animation(".stop()" or "clearQueue()"), but not resuming. Google showed me a few additional plugins to add this functionality, but I wasn't in the mood to mix libraries to accomplish this goal. A bit of trial, error, and clever use of style properties landed me with victory. I used the stop function to halt at the event handler onmouseover, then passed the current margin to lock in the new starting margin style property, then re-initiated the original animation via onmouseout to carry to the previously coded margin. Presto!

*NOTE - this is based on 1.8.2/jquery.min.js"

 <script type="text/javascript">
function startSpotLight()
{
$("#AnimateMe").animate({
     "margin-top" : "-725px"}, 20000, 'linear');
}

function stopSpotLight()
{
var myDiv = $("#AnimateMe");
myDiv.clearQueue();
myDiv.stop();
}

function setSpotLightStream(pause)
{
document.getElementById('AnimateMe').style.marginTop=pause;
startSpotLight();

}
</script>


HTML Later on...

<div id="AnimateMe" onmouseover="javascript: stopSpotLight();" onmouseout="javascript: setSpotLightStream(document.getElementById(this.id).style.marginTop);startSpotLight();">

<!--IMAGES-->

</div>