Tuesday, November 20, 2012

Resize SP Modal Popup (SP.UI.ModalDialog)

It has been a very long time since I posted a new blog post. I was working temporarily on MVC which is very exciting, interesting and hell lot to learn .....

Now coming to SP 2010, recently I had a requirement to re-size the modal popup dynamically based on the content on the page. As everyone know that we can set the width and height of the modal popup before opening the popup as below.

var options = {
    url: "/_layouts/useregistration.aspx",
    title: 'Login/Register',
    allowMaximize: true,
    showClose: true,
    autoSize: false,
    y: 50,
    width: 400,
    height: 600
};
var currentDialog = SP.UI.ModalDialog.showModalDialog(options);

The width and height parameters will be applied when the autoSize option is set to false. But I could not find any methods or options to re-size an already opened up popup using any functions. If the autoSize option is set to true, it re-sizes the window if the page gets posted back.

What happens when some dynamic content gets shown/hidden using css/jQuery? SP does not resize the popup based on this.

So I had to write a java script function to do that. I added the below function to a common JS file that is references in every page of my application to make use of it. Below is the function to re-size the popup which was opened up already.

this.Utility.resizeSPPopupWindow = function (width, height) {
    $(window.frameElement).parents('.ms-dlgContent').width(width);
    $(window.frameElement).parents('.ms-dlgContent').find('.ms-dlgBorder, .ms-dlgTitle').width(width);
    $(window.frameElement).width(width);
   
    try {
        var _intHeight = parseInt(height);
        height = _intHeight + 34; //34 is added to the height,the title bar's height adds up
    }
    catch (ex) {
    }


    $(window.frameElement).parents('.ms-dlgContent').height(height);
    $(window.frameElement).parents('.ms-dlgContent').find('.ms-dlgBorder').height(height);
    $(window.frameElement).height(height);
}

Of course it needs jQuery to work, it is one-way that I know of re-sizing already opened up modal popup.

If anyone out there knows any other way to achieve the same, I would love to know about it. Hope this helps some one.

UPDATE: If the popup modal is a new url, both the parent and the modal popup page should be from the same domain and should be using the same protocol (http or https).

Happy Coding !!!
Senthil S

Thursday, May 31, 2012

Copying additional dlls (assemblies) to GAC with SharePoint project deployment

Frequently, we reference some external assemblies in any project event SharePoint projects as well. The need in the SharePoint project is that we may need to copy those dependent assemblies to GAC with the SharePoint project assembly.

We do not want to manually copy all the dependent assemblies to GAC by ourselves. How to copy the assemblies to the GAC with the deployment itself?

The trick is to add the reference in thePackage.package file to tell the SharePoint to copy the assemblies also to the GAC. Just edit the Package.package file in a notepad or any xml editor and add the reference just like below.


Things to Note:
  • All the assemblies to be copied to GAC needs to be signed with a strong key.
  • The "sourcepath" value is the relative path of the location of the referenced assembly
  • "SafeControl" entry may be needed based on how the control is being used. (mostly needed to be used as any UI control)

Tuesday, May 29, 2012

Manage SPListItem publishing programatically

Recently, I had to work on converting a regular SPList to have similar to a publishing feature. Like, have an item edited, review it and then publish the page for public.

I made the below modifications to the list settings to enable versioning of list items.
Go to the List Setting page and select "Versioning Settings" and set the options to the same as below.

 
This enables the list items in the list to be version controlled. The author can save a version of the list item, review it and publish it at any point of time or revert to its previously published version.

I wanted to do it programatically using a layouts page (application page) since we are using the application pages as the default display form of the particular content type.

For saving a new SPListItem, the code should look similar to this. This will save the list item content and assign the status to "Pending".

var item = SPContext.Current.ListItemitem["Title"] = SPHttpUtility.ConvertSimpleHtmlToText(this.txtTitle.Text, 255);
//this will set the status of the current item to "Pending"
item.Update();


To know how to get the CurrentListItem in an app page, please see my other post here

To publish the list item, the code would look like this.

if (SPContext.Current.ListItem.ModerationInformation != null)
{
    SPContext.Current.ListItem.ModerationInformation.Status = SPModerationStatusType.Approved;
    SPContext.Current.ListItem.ModerationInformation.Comment = String.Format("Published on {0} by {1}" 
                                                   DateTime.Now.ToString(), SPContext.Current.Web.CurrentUser.Email);
    SPContext.Current.ListItem.Update();
    
    //Added to refresh the page content and hide the publishing controls 
    Response.Redirect(SPContext.Current.ListItem.Url);
}
 
To revert a saved list item to it's previously published version, the code would look like below:

if (SPContext.Current.ListItem.ModerationInformation != null && SPContext.Current.ListItem.Level != SPFileLevel.Published)
{
    //Getting the last published version of the current item
    var lastPublishedVersionInfo = SPContext.Current.ListItem.Versions.Cast<SPListItemVersion>()
                                                                            .Where(v => v.Level == SPFileLevel.Published)
                                                                            .OrderByDescending(l => l.Created).Take(1);
    SPContext.Current.ListItem.Versions.RestoreByID(lastPublishedVersionInfo.First().VersionId);
 
    SPContext.Current.ListItem.ModerationInformation.Status = SPModerationStatusType.Approved;
    SPContext.Current.ListItem.ModerationInformation.Comment = String.Format("Reverted on {0} by {1}", 
                                                               DateTime.Now.ToString(), SPContext.Current.Web.CurrentUser.Email);
    SPContext.Current.ListItem.Update();
 
    Response.Redirect(BlogManager.GetBlogPostUrl(SPContext.Current.Web, SPContext.Current.List.ID, SPContext.Current.ListItem.ID));
}

Hope this helps some one or even myself somewhere in the future.

Thanks
Senthil S

Tuesday, May 8, 2012

Filtering list items by user id using CAML in SPSiteDataQuery

I had some struggle finding out how to filter the list items by a particular user (SharePoint user). I am sure that this can be done but the filter does not seem to work for me if it is using the field value as "Integer". It was working very fine if the filter is applied for current logged in user as below:

<Query>
  <Where>
    <Eq>
      <FieldRef Name="Author" />
      <Value Type="Integer">
        <UserID Type="Integer" />
      </Value>
    </Eq>
  </Where>
</Query>
 
Of course, this only works if the filtering is applied for the currently logged in user. so, what does need to happen when the query needs to filter based on another user?

I tried the above CAML by replacing the <UserID Type="Integer" /> with the particular user's id value (SPUser.ID). This does not seem to work. Always it returns no items.

<Query>
  <Where>
    <Eq>
      <FieldRef Name="Author" />
      <Value Type="Integer">
        75
      </Value>
    </Eq>
  </Where>
</Query>


It turns out to be an attribute "LookupId" needs to be added to the <FieldRef> element as below.
 
<Query>
  <Where>
    <Eq>
      <FieldRef Name="Author" LookupId="True"/>
      <Value Type="Integer">
        75
      </Value>
    </Eq>
  </Where>
</Query>
 
Happy Coding.
Senthil S

Tuesday, May 1, 2012

Get items from multiple webs using SPSiteDataQuery

I know there is no easy way to get list items from multiple webs using SPSiteDataQuery. But I had to work on a similar situation for one of my projects. Looking deep into the list items attributes I came to know that there is a possibility to do the filtering based on the web itself (given some other small conditions)

Properties "ServerUrl" (server Relative Url) and "EncodedAbsUrl" (Encoded Absolute Url) cannot be used since the field values are of type "Computed" and SPSiteDataQuery does not filter based on "Computed" field values (there are some workarounds for some computed fields as well)

So I started looking into "FileRef" (URL Path) and "FileDirRef" (Path) for filtering. It seems to be working for me.

For a particular SPFile/SPListItem the above field values would give the exact location of the item.the values would look similar like below:


Publishing Content Types:
FileDirRef: SubSite1/SubSite12/Pages
FileRef: SubSite1/SubSite12/Pages/default.aspx

Non-Publishing Content Types: (normal SPListItems)
FileDirRef: SubSite1/SubSite12/Lists/{ListRootFolderUrl}
FileRef: SubSite1/SubSite12/Lists/{ListRootFolderUrl}/{ItemName}

Using the above field values and with proper CAML filter xml, items can be filtered from multiple webs.

In my case I was querying against non-publishing content types. So I was applying ContentTypeId filter primarily to filter based on content type. Above that, I was applying filters for "FileRef" values to get the items from multiple webs.

Sample CAML Query would look like this:
(The first one with value "Lists" is to filter the items in the root web)
In addition to the above filters, I was also using the "ContentTypeId" filter to get list items of particular type.

NOTE: If you are using just "SubSite1/SubSite12" to filter on the items, it would include the sub webs as well. Include the "/Lists" or "/Pages" at the end of web relative url to restrict to a single site.

If you have any questions, please add a comment.

Thanks
Senthil S

Thursday, April 26, 2012

Error while adding Site columns (fields) to list content types

Recently, I had a situation where I need to add site columns (available in root web) to a content type reference in a particular list under a particular web.

I wrote the code below 
 
var field = web.Site.RootWeb.AvailableFields[new Guid("<<FieldID>>")];
foreach (SPContentType cType in web.Lists["ListName"].ContentTypes)
{
    cType.Fields.Add(field);
    cType.Update();
}

but it was not working as expected and throwing the below exception

"This functionality is unavailable for field collections not associated with a list."

After some research, I came to know that the field needs to be added as a SPFieldLink and not as SPField itself since the field already exists in the root web. The below modified code is working fine for me.

var field = web.Site.RootWeb.AvailableFields[new Guid("<<FieldID>>")];
foreach (SPContentType cType in web.Lists["ListName"].ContentTypes)
{
    SPFieldLink fieldLink = new SPFieldLink(field);
    cType.FieldLinks.Add(fieldLink);
    cType.Update();
}

Hopefully this helps some one or may be myself in the future when I struggle with the same issue.

Thanks
Senthil S

Monday, April 2, 2012

Java script error when trying to change Page Layout

Recently, I faced a weird issue with SharePoint publishing pages and changing the layout of the page. I was able to add a new publishing page with different layouts in a pages library. But when I try to change the page layout of an existing page, I got a java script error in cui.debug.js. Just like the one below, I was not even be able to get the drop down with different page layouts to change


To fix this issue I had to debug the java script (CUI.debug.js) file in IE 8 or greater using the Developer tools. Once the exception occurred in the java script, traced the function calls using the "Call Stack" option and found a variable "$v_0.PopulationXML" and inspected the XML in VS and found the malformed XML.



After some research, I came to know that there was a orphaned page layout in the "Master Pages and Page Layouts" gallery. I was testing something else with page layout and content type relationships and left it there. But I removed the content type associated with the page layout.

After I deleted the particular page layout from the gallery, everything started to work fine.

Thanks
Senthil S

Monday, March 12, 2012

Refreshing user roles using custom role provider without logging out

Recently, I had a situation where the user's roles needed to refresh to allow the user to gain access into some site's resources. We were using a custom role provider and custom membership provider to manage our users. We were able to assign roles to user when they click on a button control. But, the problem we faced with this is that the user has to log out and log back in to get the role he/she just got assigned. Obviously, no one liked it and it should be seamless to the end user.

After a long research it turns out to be that the SP 2010 somehow caches or stores the user's roles in memory until the user logs off or the session ends. so I was searching for a way to force SharePoint to refresh the role assignments for the current user. I came across this great post

Force FBA Token Refresh 

You do need to have your own global.asax file to the webroot (where your web.config lives for the front-end servers).

After implementing the above method everything worked perfectly. I found that this particular event handler code is getting called for every web request to the server. so if you are planning on doing any costly operation, do it with caution.

Thanks
Senthil S

Thursday, January 19, 2012

Custom FormUrl s (Display, Edit & New) does not work for a custom content type

I was developing a custom list template with a custom content type. I had to implement our own custom forms for our custom content type (MyContentType). You can get how to add custom FormUrls for a content type from this MSDN documentation (http://msdn.microsoft.com/en-us/library/aa544142.aspx).

I did change the <FormUrls> in the custom content type definition as below. Even after I tried deploying quite a few times, it did not seems to take effect.

I was totally confused. After some struggle, I changed the inheriting content type to "Item" (0x0100) instead of "Post" (0x0110) content type, then the custom forms took the effect.



So, somehow it seems to me that some OOTB content types have display forms defined and it is not allowed to be overridden by our custom forms urls.

Happy coding
Senthil S

Wednesday, January 18, 2012

SPSiteDataQuery not returning all results when using ContentType filter

Recently, I was working on a case where all the SPListItems using a specific content type needs to be listed from the whole site collection. As you all know, SPSiteDataQuery is the way to go.

As I was getting to the end I noticed it was not returning all the items from the whole site collection. Instead it is returning only a few items, more like list items only from one particular list (weird!!! right) and no errors as well. If the same web part is added in some specific inner webs and tried to get all teh items from current web and all the sub webs the query works perfectly well. I was totally confused ...

After some researches, I came across this thread (http://social.technet.microsoft.com/Forums/en/sharepoint2010programming/thread/1294669a-546d-44f1-8b7d-6972bc11bc34) where it said the SPSiteDataQuery has some issues with filtering with "Computed" fields such as "ContentType" field. So I changed the filter from "ContentType" to "ContentTypeID" which f type "ContentTypeId" which seems to be working perfectly fine.

ContentType Filter:

<Where>
    <Eq>
        <FieldRef Name="ContentType" />
        <Value Type="Computed">My Content Type</Value>
    </Eq>
</Where>

ConteTypeId Filter:

<Where>
    <BeginsWith>
        <FieldRef Name="ContentTypeId" />
        <Value Type="ContentTypeId">My Content Type</Value>
    </BeginsWith>
</Where>

Above filter goes for the ".Query" attribute of the SPSiteDataQuery to filter by ContentType name and ContentTypeID respectively.

Hope this helps someone facing the same issue.

Thanks
Senthil S

Monday, January 9, 2012

Adding Metadata fields as View fields in Schema.xml does not capture values

Recently, I worked on creating a custom list definition from a custom content type. That was not a big deal with the awesome VS 2010. BUT, I faced an issue with one field (Enterprise Keywords) with capturing data selected in it.

I was using custom forms for the content type's Display, Edit and Delete forms. First I was thinking that I was doing something wrong with my custom forms. I changed the forms to default OOTB default list forms for that content type and deployed. But, even with the default forms also it was failing.

Then i found out if I remove the "Enterprise Keywords" column from the list fields (not the content type fields) and re-add the same, it was working fine.

After a long research, I found this blog (http://www.sharepointconfig.com/2011/03/the-complete-guide-to-provisioning-sharepoint-2010-managed-metadata-fields) which explained me about the list event receivers get added for capturing the Taxonomy Lookup fields.


After adding the above mentioned Item Event receivers based on the list template everything seemed to be working fine. Use the below elements.xml to add the above mentioned item event receivers.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Receivers ListTemplateId="10002">
    <Receiver>
      <Name>TaxonomyItemSynchronousAddedEventReceiver</Name>
      <Type>ItemAdding</Type>
      <Assembly>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0,
 Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
      <Class>Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver</Class>
      <SequenceNumber>10000</SequenceNumber>
    </Receiver>
    <Receiver>
      <Name>TaxonomyItemUpdatingEventReceiver</Name>
      <Type>ItemUpdating</Type>
      <Assembly>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, 
Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
      <Class>Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver</Class>
      <SequenceNumber>10000</SequenceNumber>
    </Receiver>
  </Receivers>
</Elements>

Just make sure the "ListTemplateId" matches with the "Type" attribute defined in the List Template definition .

Happy coding.
Senthil S

Wednesday, January 4, 2012

Using master page and layouts page in custom list view

I was struggling for couple of days now on how to make use of any master page in a layouts page which is used for a view of a custom list created using custom list schema.xml.

How to create a custom list definition is explained here (http://msdn.microsoft.com/en-us/library/ff728096.aspx)

I was able to create a custom list definition and added a custom list view page also in the schema.xml file itself. Here is how to add a custom list view using a layouts page (application page)

After adding a custom list schema, find the node "<Views>" and add a new "<View>" tag. You can just copy and paste the already existing "<View>" tag which has the BaseViewID="1" (this is the one which constitutes for the web page view. It is important that you should be copying the whole "<View>" node with all the internal elements like below.



To make use of the layouts page for the view, just change the "SetupPath" parameter to point to the corresponding layouts page without the "/" in front of the "layouts" folder (i.e., should be starting as "layouts/setup/settings.aspx") and change "Url" parameter to the name you want to see in the URL.

When I changed the "MasterPageFile" parameter to an application master page like below,

<%@ Page Language="C#" AutoEventWireup="true" 
                           MasterPageFile="~/_layouts/AppMasterPages/Simple.master" %>
 
the custom view gave me the below error

"The referenced file '/_layouts/AppMasterPages/Simple.master' is not allowed on this page"

If the "MasterPageFile" is change to "~masterurl/default.master", everything works well. If you are using a custom master page for the site you may make use of that by setting the "MasterPageFile" to "~masterurl/custom.master".

Happy coding.
Senthil S