Showing posts with label sharepoint-2010. Show all posts
Showing posts with label sharepoint-2010. Show all posts

11 July 2012

Datasheet view is grayed out

This is an interesting issue: Some of our users couldn't bring up lists in SharePoint's Datasheet view, which makes the list appear almost like an Excel spreadsheet. The item was grayed out on the Internet Explorer 64-bit on Windows 7 64-bit. Solution? Use IE 32-bit. More on the issue here. It has to do with a 32-bit ActiveX control used to render the Datasheet View.

One other interesting item: Both of the IE shortcuts on the Programs menu of Windows 7 x64 are to the 64-bit version; it's somewhat misleading, because one has "(64-bit)" in parenthesis and the other doesn't, so one assumes the one without that is 32-bit. That's not the case. To run 32-bit IE, go to "C:\Program Files (x86)\Internet Explorer\iexplore.exe" and create a shortcut to it on your desktop.

27 October 2011

SharePoint: Get parent site title

In SharePoint, we can use subsites to handle navigation beyond the 2nd level; let's assume we have a structure such as this:
  • Site Collection Root
    • Site 1: site title - "Products & Software"
      • Subsite A: site title - "Widgets"
        • Subsite AA: site title - "Widget X"
        • Subsite BB: site title - "Widget Y"
    • Site 2
    • Site 3
It often makes sense to show Site 1's title "Products & Software" down that branch of subsites, regardless of how deep you are. This helps users know they're still under "Products & Software". To fetch the root parent's title (Site1, not the Site Collection Root), you have to use a recursive function, thanks to W0ut and help from Stack Exchange:
protected override void OnLoad(EventArgs e)
{
  // To ensure page behaves correctly, must call base.OnLoad(e).
  base.OnLoad(e);

  GetRootTitle();
}

private void GetRootTitle()
{
  string title = string.Empty;

  /* 
     On admin pages, this label object doesn't exist and will throw
     a NullReferenceException if we don't check it before trying
     to use it.
  */
  if (sectionTitle == null) return;

  using (SPSite site = new SPSite(SPContext.Current.Site.ID))
  {
    using (SPWeb web = site.OpenWeb(SPContext.Current.Web.ID))
    {
      title = IterateThroughParentsAndStoreInfo(web, title);
    }
  }

  sectionTitle.Text = title; // Set a label's value to the title.
}

private string IterateThroughParentsAndStoreInfo(SPWeb web, string title)
{
  if (web.ParentWeb != null)
  {
    title = web.Title;
    return IterateThroughParentsAndStoreInfo(web.ParentWeb, title);
  }
  return title;
}
Note the using statements; these ensure there are no memory leaks by disposing of the objects properly and also implicitly handling the try/catch/finally block. The change to W0ut's code is the addition of the OnLoad() handler, which can be either in your master page or page layout. Also, we've added the title variable, which gets set in the recursive function; note that its value is always of the title for the site one before the last. This is because when the web variable's ParentWeb becomes null, we've reached the site collection. This is exactly what we need.

One last thing to note: We do a null check for the label object which will hold the title's value. On some administrative (system) pages, the label isn't rendered so we'll throw a NullReferenceException. Another way to do this is to always add the title label as a new Label(); this way it's always present, regardless of which page we're on.

02 October 2011

SharePoint: Create an easy "Share page with friend" button

In SharePoint, it's useful to have an email icon that allows users to email the page to friends. Though we could build a popup box, we can also go with a simpler solution that uses the person's email client to do the heavy lifting. In another post, I covered how to fetch some of the server-side variables into a JavaScript array needed for generating this email. Now, let's use the variables and see how we send the email. One key to this feature is to keep it simple; this means, we'll leave the To field of the email blank and use the commonplace "mailto:" HREF attribute. So, let's get to the code:
function generateEmailTo(){
  var body = currentElements.currentUserName + ' has shared a page with you on the intranet.%0A%0APage Title: %22' +
    currentElements.currentTitle  + '%22%0A' + $(location).attr('href').replace('#',''); 
  var subject = currentElements.currentUserName + ' has shared an intranet page with you';
  var mailto = 'mailto: ?body=' + body + '&subject=' + subject;
  var anchor = '<a href="' + mailto + '"></a>';

  $("#send-email").wrap(anchor);
}
We can pass the body, subject, and mailto for the mail message. The ?body= and the &subject= allow the main message and the subject to be passed in the querystring. To provide line breaks, we use %0A hex values; so for two line breaks, we use %0A%0A in the querystring value. To pass quotes, we use %22. To pass a blank mailto:, we leave a space in front of it, before the ?body=. More notes on mailto, setting its cc, bcc, and special characters can be found here.

Once we've concatenated the various elements, we pass the anchor variable to the jQuery wrap() function, called on the email icon img tag (with ID of send-email). We call the above generateEmailTo() in the document.ready function. When you click the link, it produces something like this in the generated email (Outlook, Thunderbird, etc.):
Alex C has shared a page with you on the intranet. 

Page Title: "My Page Title" 
http://mysite.org/Pages/mypage.aspx
The subject, not shown, will be "Alex C has shared an intranet page with you". Note that the URL will appear as a clickable link in most email clients. Also note that we can't pass HTML to the body of the message, only plain text.

Hope you find this useful.

Update: Here's the IMG tag that is being referenced in the code above:
<img id="infoweb-email" title="Send this page to a friend." 
  src="<%= baseUrl %>/SiteAssets/media/icons/email-icon.gif"/>
The baseUrl server-side variable is set in the page layout's OnLoad event handler:
<script type="text/c#" runat="server">
protected override void OnLoad(EventArgs e)
{
  base.OnLoad(e); // Required for the SharePoint ribbon bar, etc., to work correctly.
  Microsoft.SharePoint.SPContext context = Microsoft.SharePoint.SPContext.GetContext(HttpContext.Current);
  baseUrl = context.Site.Url;
  // Other code...
}
</script>
For the page layout to allow code blocks, we must make the following change to the Web.config file:
<PageParserPaths>
   <PageParserPath VirtualPath="/_catalogs/masterpage/*" 
       CompilationMode="Always" 
       AllowServerSideScript="true" IncludeSubFolders="true"/>
</PageParserPaths>
Note that this allows all files in the masterpage folder to contain code blocks. If we use Firebug to examine the element in Firefox, we'll see this is the HTML generated by our server- and client-side code:
<a href="mailto: ?body=Alex%20C has shared a page with you on the intranet.%0A%0APage Title: %22My%20Page%20Title%22%http://mysite.org/Pages/mypage.aspx&subject=Alex%20C has shared a page with you on the intranet.">
<img id="send-email" src="http://mysite.org/SiteAssets/media/icons/email-icon.gif" title="Send this page to a friend.">
</a>
So the IMG element has been wrapped by an anchor tag which has the properties we set in JavaScript.

SharePoint: Fetch environment variables and store in JavaScript

For a header text, we needed the current site's name. In addition, to generate a "Share this page with a friend" button, we needed the current user's login name and full name. We could use SharePoint's Client Object Model, which does an asynchronous call from JavaScript to get the information.

However, we needed the site's name to be available with the DOM, not when the page loads; we tried the AJAX call and it took a second or two to fetch the data. During this time, the page had no header text saying which site the user was on.

What to do? We could use jQuery to get the site name from the breadcrumbs generated via the asp:SiteMapPath control, but there could be times when we wouldn't be using this control. Else we could fetch it from the top-left navigation tree dropdown; even that was clunky. So we decided to put the following code in the Page Layout's PlaceHolderAdditionalPageHead control; it can also be placed in the master page:
<asp:Content ContentPlaceholderID="PlaceHolderAdditionalPageHead" runat="server">
<%
String currentUserName = Microsoft.SharePoint.SPContext.Current.Web.CurrentUser.Name.ToString();
String currentUserEmail = Microsoft.SharePoint.SPContext.Current.Web.CurrentUser.LoginName.ToString().Replace("MYDOMAIN\\","");
String currentSite = Microsoft.SharePoint.SPContext.Current.Web.ToString();
String currentTitle = (Microsoft.SharePoint.SPContext.Current.Item["Title"] == null) ?
 "" : Microsoft.SharePoint.SPContext.Current.Item["Title"].ToString();

StringBuilder sb = new StringBuilder();
sb.Append("<script type='text/javascript'>var currentElements = { currentUserName: '");
sb.Append(currentUserName);
sb.Append("', currentUserEmail: '");
sb.Append(currentUserEmail);
sb.Append("', currentSite: '");
sb.Append(currentSite);
sb.Append("', currentTitle: '");
sb.Append(currentTitle);
sb.Append("'}</script>");
Response.Write(sb.ToString());
%>

....

</asp:Content>
We fetch the current user's name using the SPContext.Current property, which gives us a handle to the current HTTP request in SharePoint. Then we get the various items we need, such as the user's name, login name, web (current site name), and the publishing page's title.

Once we have the values, we plug them into a JavaScript array that we build server-side. When the page loads in the browser, the array will be instantiated with the values we set using C#. We use a StringBuilder object for optimal performance because of the string concatenations.

So what does it look like when the page loads? Here it is:
<script type='text/javascript'>var currentElements = { currentUserName: 'Alex C', currentUserEmail: 'myemailid', currentSite: 'My Site Name', currentTitle: 'Page Title'}</script>
To use any of the array elements in jQuery or elsewhere, just call it via something like this: currentElements.currentSite

A final note: If any of the values will have a single quote in them, just escape the value using a .Replace("'","\'")on the string in C#.

21 September 2011

SharePoint: Programmatically add JS/CSS to pages

John Chapman has a great article on creating a feature for your SharePoint installation that adds JavaScript and CSS files to all pages without touching the site's master page. The good folks on SharePoint.StackExchange.com helped me implement it and also modify the code to better utilize SharePoint's native control libraries.

First, follow the code provided by John Chapman. Then modify the CustomPageHead.ascx.cs to take advantage of the built-in SharePoint controls:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace CustomPageHead.CONTROLTEMPLATES.CustomPageHead
{
public partial class CustomPageHead : UserControl
{
protected override void CreateChildControls()
{
base.CreateChildControls();

this.Controls.Add(new ScriptLink()
{
Name = "/_layouts/CustomPageHead/jquery-1.6.2.min.js",
Language = "javascript",
Localizable = false
});

this.Controls.AddAt(0,new ScriptLink()
{
Name = "/_layouts/CustomPageHead/some-custom-code.js",
Language = "javascript",
Localizable = false
});

this.Controls.AddAt(1,new CssRegistration()
{
Name = "/_layouts/CustomPageHead/some-stylesheet.css"
});
}
}
}
Note that using the native controls, you don't have to worry about paths to where the files will be; SharePoint will place these items in the following location on your server:
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\CustomPageHead
Also, the advantage of using the this.Controls.AddAt() method is that you can specify where in the object hierarchy to add the specified object.

In addition, the solution can be generated into a *.WSP by selecting "Package" from Visual Studio's Build menu. The file can then be copied from the bin/Release folder and run on the command line to install the feature.

Special thanks goes to omlin and James Love for their help.

14 September 2011

SharePoint tabs UI for web parts

Christophe Humbert's beautiful Easy Tabs for SharePoint works wonderfully well; it automatically generates tabs for the web parts on the page. You simply drop the code Christophe provides into a content editor web part (CEWP) and off you go.

In a current project, we needed the ability to have all pages show tabs without dropping a CEWP on every page. The code needed to be on a page layout for a publishing site. The challenge? Christophe's code traverses up the DOM from the current CEWP to find the parent container of the web parts; unless you use a CEWP, it won't work.

Some serious trial-and-error, as well as posting to the SharePoint group on StackExchange.com, provided the clues; we need to provide a direct reference to the parent container of the web parts. Here's the part of the code that was moving up the DOM tree:
var el=document.getElementsByTagName("SCRIPT"),p=el[el.length-1],sT,a,sep,tabRow;
do {p=p.parentNode;sT=p.innerHTML.split("MSOZoneCell_WebPart");}while (sT.length<4 && p.parentNode.id!="MSO_ContentTable")
In the page layout, the web parts are in a Page Content control (PublishingWebControls:richhtmlfield), so here's the change to the JavaScript to reference that parent container:
var p,a,sep,tabRow;
p = document.getElementById('ctl00_PlaceHolderMain_ctl01__ControlWrapper_RichHtmlField');
Note that there's no need for the el variable or the do-while loop. You probably need to do some trial-and-error, using alert() statements to view the p.innerHtml and/or p.parentNode.innerHtml; this helped me discover what p was pointing to after the do-while and simply reference it directly.

07 September 2011

SharePoint: If your stylesheet isn't added to the page...

For a current project, I'm using a custom page layout within a publishing site. I had the following code in the my_page_layout.aspx file:
<asp:Content ContentPlaceholderID="PlaceHolderAdditionalPageHead" runat="server">
<link type="text/css" href="../../SiteAssets/js/jquery/jquery-ui-1.8.16/css/cupertino/jquery-ui-1.8.16.custom.css"/>
....
</asp:Content>
No matter what I did, the stylesheet wouldn't be available to the SharePoint page. However, another stylesheet was showing up. The difference? I was missing the rel="stylesheet" attribute on the bad link tag. Added it and everything worked.

28 August 2011

SharePoint: Remove link from list view title

Links can't be easily removed from list view titles (or web part titles, for that matter). That's where jQuery comes in handy:
$(document).ready(function () {

// Remove the link from the web part title and set its cursor to normal.
$('.ms-WPTitle > a').contents().unwrap().css('cursor','default');
$('.ms-WPTitle').css('cursor','default');
});
This is thanks to a tip from this post on StackOverflow.com. Note also that the code sets the cursor to default (instead of "hand").

23 August 2011

SharePoint: Automatic table of contents for a page's sections

If you have really long pages in SharePoint with lots of sections, you can use the nifty jQuery plugin from Rochester Oliveira to automate the creation of the table of contents (TOC). Note that this is different from the table of contents web part built into SharePoint; that tool only handles pages and sites, not content within pages.
  1. Ensure you've uploaded jQuery to your SharePoint site (or site collection); usually, these files will go into the Site Assets area so they're accessible to all sites. For example, http://mydomain.org/SiteAssets/js/jquery/jquery-1.6.2.min.js.

  2. Also place the jquery.stoc.js file necessary for this task in a similar location.

  3. Reference these two JS files from your master page, page layout, or content editor web part.

  4. Add some JavaScript to trigger the TOC creation (see code below). You'll also need an empty DIV tag that will have the TOC injected into it.

  5. Add the necessary CSS to style the TOC and the UL tags. Rochester Oliveira's plugin page has some great examples to get you started.

One other change is necessary for it to work correctly: For some reason, on SharePoint 2010, it was appending #undefined to all the table of contents items. So I modified the JS in the jquery.stoc.js to this:
if (id == "" || typeof id === "undefined") { //if it doesn't have only, of course

id = "h" + tagNumber + "_" + i;
cacheHN.attr('id', id);
}
Now if id is undefined, it will generate one. So you're ready to test. If you wish to get fancier, you can add a True/False dropdown to the page layout for editors to choose whether a page will have a TOC (for instructions on conditionally displaying the dropdown, please see this post). This code can generate the necessary JavaScript based on the page layout's custom content column EnableTOC value:
<%= (Microsoft.SharePoint.SPContext.Current.Item["EnableTOC"] == null ||

Microsoft.SharePoint.SPContext.Current.Item["EnableTOC"].ToString() == "False")? "" :
" "
%>
In the above code, if EnableTOC is set to True, we trigger the TOC plugin on the element with ID toc; the plugin will search the element main-content for tags of type H2 and H3 ("start: 2"), and generate the TOC based on that structure using UL tags. Also, the toc DIV tag is hidden by default; the code above changes its display property to block.

If you always want the TOC to be generated, take it out of the server-side code and make it entirely client-side. In addition, because of a bug in SharePoint 2010, named anchors might not work correctly; in other words, you might click an item in the TOC and not have the page go to that part of the document. In that case, you can add this code as well, courtesy of fcorbeil on the Microsoft MSDN forum:
<script type="text/javascript">

setTimeout(Reload,2000);
function Reload() {
window.location.hash=self.document.location.hash.substring(1);
}
</script>

16 August 2011

SharePoint: Icon doesn't show for Publishing Hyperlink

Ran into this today. Very strange problem. For whatever reason, the "Display icon" functionality of the Publishing Hyperlink field doesn't work on SharePoint 2010 Server for a site with Publishing enabled. Added URLs to a column of that type for PDF files, but no PDF icons appeared. In fact, the value for the "Display icon" field wasn't "sticky" -- it kept getting unchecked. This article saved the day, with a very easy, non-coding solution using only CSS attribute selectors.

I'm using a custom page layout in my publishing pages with an attached CSS file; in that file, I added the following items to correspond to the location of my icon files (inside SiteAssets); I also added .docx for Word 2007+ document types and .xlsx for Excel 2007+ docs:
a[href$='.pdf'] {

display:inline-block;
padding-left:20px;
line-height:18px;
background:transparent url(/SiteAssets/icons/icon_pdf_16x16.gif) center left no-repeat;
}
a[href$='.doc'], a[href$='.rtf'], a[href$='.txt'], a[href$='.docx'] {
display:inline-block;
padding-left:20px;
line-height:18px;
background:transparent url(/SiteAssets/icons/icon_doc_16x16.gif) center left no-repeat;
}
a[href$='.zip'], a[href$='.gzip'], a[href$='.rar'] {
display:inline-block;
padding-left:20px;
line-height:18px;
background:transparent url(/SiteAssets/icons/icon_zip_16x16.gif) center left no-repeat;
}
a[href$='.xls'], a[href$='.csv'], a[href$='.xlt'], a[href$='.xlw'], a[href$='.xlxs'] {
display:inline-block;
padding-left:20px;
line-height:18px;
background:transparent url(/SiteAssets/icons/icon_xls_16x16.gif) center left no-repeat;
}
The CSS works by finding all anchor tags with an href attribute that ends (using the dollar symbol $) with the specified string; so for PDFs, it uses a[href$='.pdf'] and applies the style rules. And we're only interested in those 4 file types, so those are the ones that have icons in our CSS file.

Reloaded the page and viola! The icon appears to the left of the item as if by magic :)

Also, the article from psyked has links to some nice (and free) icon libraries. All in all, a very good solution. However, we still don't know what's causing the built-in "Display icon" functionality to not work.

Hope this helps others. Thanks.

07 August 2011

SharePoint: Conditionally display content for authors and readers

For a current project, I needed to display an Article Date content field in a page layout. The author could pick a date when in Edit View; the reader would only see the value the author had selected, or nothing if no value had been picked.

To do server-side code blocks in a page layout, we'll first need to enable it from our app's web.config file; otherwise it'll throw an error saying "Code blocks are not allowed in this file," as described here. To enable code blocks, open your app's web.config and locate the PageParserPaths tag, which is empty, and modify it to look like this:


Note the VirtualPath; this needs to point to the location of your page layout (or ASPX) file.

Next, we'll use Bart McDonough's blog post to add the EditModePanel and AuthoringContainer; the nesting of these controls is well explained by Bart:











http://www.blogger.com/img/blank.gif
Note the DateTimeField element; that allows authors to edit the "Article Date" field.

Now we need to add some server-side code to determine if we can display the "Article Date," and to format it properly (this code is based on Johan Leino's blog post):
<%= (Microsoft.SharePoint.SPContext.Current.Item["Article Date"] == null) ? "" : 
"Last update on " + DateTime.Parse(Microsoft.SharePoint.SPContext.Current.Item["Article Date"].ToString()).ToString("d MMMM yyyy") +
"." %>
This code displays the text "Last update on 9 August 2011." if there's a value in the "Article Date" content field; otherwise, it displays nothing.