Tuesday, April 2, 2013

Render (Published Mode/Edit Mode) OOTB RSS Control

Worked on an OOTB Rss Control web part in O365. It suddenly stopped working on the Home Page. It still works fine on a couple of other pages, but stopped working on the Home Page. The stopped working means that the "loading graphic" stays on Home Page and the RSS Feed never gets rendered. The moment you go into the edit mode, the RSS feed is rendered correctly. This is one of the strangest issues I have seen either this is something related to Office 365?

Here is what I did
  • Master Page is same across the site. Same set of JS/CSS files are loaded on all pages.
  • PageLayout is different. Tried all combinations with using same page layout/rebuilding page layout/ adding components 1 by 1, but still would not work.
  • There are not many configuration settings that you can modify for OOTB RSS Feed.  
Spent lot of hours in order to troubleshoot this crazy issue. Still could not resolve the issue other than this:

Solution:
Used XMLViewer WebPart in place of RSS Control web part and then wrote XSLT used for rendering the content. The 2 things to note here are that
  1. Produce the same look & feel
  2. Limit the Results to a certain number.
This was accomplished, by literally adding the same styles that existed in XSLT/OOTB Control and adding logic to limit to certain number.







Tuesday, March 26, 2013

Change Ratings.png file SPOnline/Office365

There are at least 2 ways available for achieving this:

  1. You could do this in JQuery
  2. You could make a change in SP Designer by making change in the SiteOptions. In the SiteOptions, there is a property Ratings [ratings_imagestripurl], you can update this value. The only catch here is the APP pool has to be restarted.

Change Ratings for Blue to Golden - SP Online Office365

How do you change the ratings from blue to control on the RatingsControl that is rendered.

In SP Online, you have lot of limitations and your best friend is JQuery. There are several ways in which this can be accomplished. A simple way is to swap the class that is used when the ratings control is rendered on the page. Here is how the command will look.

$(".ms-rating_1").addClass("ms-rating_1_new").removeClass("ms-rating_1");

The only issue with this is that there is no matching class when the average rating is 1.5, 2.5...and so forth. In that scenario, you would have to create a new mirror opposite of the rating image and load it to your images folder.




This is an example of that type of code. In this, we would add a class and remove the existing class and then replace the image.

    //$(".ms-rating_4_5").addClass("ms-rating_4_5_new").removeClass("ms-rating_4_5").attr("src", "/sites/ynn/siteAssets/images/Ratings/ratingsEmptyCustom.png")

Alternatively, if you can live without averages rounded to .5 then you can simply do this:

$(".ms-rating_1_5").addClass("ms-rating_2_new").removeClass("ms-rating_1_5");


Wednesday, March 6, 2013

CQWP - Display content from 3rd(N) Position Onwards

Here are the steps:
  • Open ItemStyle.XSLT file
  • Simply create your own template by copying an existing template.
  • Insert the bold variable.
  • Add an "If" condition where the content is rendered.
Using this technique, you can get data from any position onwards.

<xsl:template name="Display2NDRowOnwards" match="Row[@Style='ImageTopCentered']" mode="itemstyle">
        <xsl:variable name="SafeLinkUrl">
            <xsl:call-template name="OuterTemplate.GetSafeLink">
                <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="SafeImageUrl">
            <xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
                <xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="DisplayTitle">
            <xsl:call-template name="OuterTemplate.GetTitle">
                <xsl:with-param name="Title" select="@Title"/>
                <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="ItemNumber">
             <xsl:number  format="1"></xsl:number>
        </xsl:variable>

        <div class="item centered4">
            <xsl:if test="string-length($SafeImageUrl) != 0">
                <div class="image-area-top">
                    <a href="{$SafeLinkUrl}" >
                      <xsl:if test="$ItemsHaveStreams = 'True'">
                        <xsl:attribute name="onclick">
                          <xsl:value-of select="@OnClickForWebRendering"/>
                        </xsl:attribute>
                      </xsl:if>
                      <xsl:if test="$ItemsHaveStreams != 'True' and @OpenInNewWindow = 'True'">
                        <xsl:attribute name="onclick">
                          <xsl:value-of disable-output-escaping="yes" select="$OnClickTargetAttribute"/>
                        </xsl:attribute>
                      </xsl:if>
                      <img class="image" src="{$SafeImageUrl}" title="{@ImageUrlAltText}">
                        <xsl:if test="$ImageWidth != ''">
                          <xsl:attribute name="width">
                            <xsl:value-of select="$ImageWidth" />
                          </xsl:attribute>
                        </xsl:if>
                        <xsl:if test="$ImageHeight != ''">
                          <xsl:attribute name="height">
                            <xsl:value-of select="$ImageHeight" />
                          </xsl:attribute>
                        </xsl:if>
                      </img>
                    </a>
                </div>
            </xsl:if>
            <div class="link-item">
                <xsl:call-template name="OuterTemplate.CallPresenceStatusIconTemplate"/>
                <a href="{$SafeLinkUrl}" title="{@LinkToolTip}">
                  <xsl:if test="$ItemsHaveStreams = 'True'">
                    <xsl:attribute name="onclick">
                      <xsl:value-of select="@OnClickForWebRendering"/>
                    </xsl:attribute>
                  </xsl:if>
                  <xsl:if test="$ItemsHaveStreams != 'True' and @OpenInNewWindow = 'True'">
                    <xsl:attribute name="onclick">
                      <xsl:value-of disable-output-escaping="yes" select="$OnClickTargetAttribute"/>
                    </xsl:attribute>
                  </xsl:if>
               
                    <xsl:if test="$ItemNumber > 2">
                          <xsl:value-of select="$ItemNumber" />
                          <xsl:value-of select="$DisplayTitle"/>
                      </xsl:if>


                </a>
                <div class="description">
                    <xsl:value-of select="@Description" />
                </div>
            </div>
        </div>
    </xsl:template>
   

Tuesday, November 20, 2012

SharePoint Discussion Board - Hide (View/Edit) Link

How do you conditionally hide (View Item) Link on the threaded view?
It is not the best way, in which the discussion board has the link displayed on the UI. Technically, there is a setting that you can set where a user who creates the post is only allowed to edit. However, that check only occurs when you click on Edit Item and SP will either display the edit page or will give access denied.

This is the JQuery I wrote to hide the link for each post comparing signed in user to the user who posted. If the 2 names are different, I hide the link and if they are same I replace the display value with "View/Edit".

$("a[href*='_layouts/userdisp.aspx']").each(function (index) {
            if ($(this).text().length > 0) {
                if ($(this).text().toLowerCase() != jQuery.trim(thisUserAccount.toLowerCase())) {
                    // Work $(this).parent().parent().parent().parent().parent()[0].firstChild.nextSibling.innerText = '';
                    $(this).parent().parent().parent().parent().parent()[0].firstChild.nextSibling.firstChild.firstChild.innerHTML = ''
                }
                else {
                    $(this).parent().parent().parent().parent().parent()[0].firstChild.nextSibling.firstChild.firstChild.innerHTML = '<NOBR>Edit/Delete</NOBR>';
                }
            }
        });

Monday, June 25, 2012

User Profile Service & ADFS in Multi-Tenant

For the past several weeks, I have been working in getting ADFS work in dedicated environment. The process is fairly simple once you have a server with ADFS running.
Here is a list of things you need in SP:
  1. Token Signing Certificate of ADFS Server.
  2. IIS Web Server Certificate of the server hosting ADFS.
  3. Atleast Self Signed SSL Cert of the IIS Server that is hosting SP.
  4. You need to define your claims in ADFS and then map them as well.
  5. Finally, you have to write a power shell script something like this in order to set up the new token identifier.


write-host "Adding ADSF Cert"
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("C:\ADFS\adfs.cer ")
New-SPTrustedRootAuthority -Name "Token Signing Cert" -Certificate $cert

$map = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" -IncomingClaimTypeDisplayName "EmailAddress" -SameAsIncoming
$map2 = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" -IncomingClaimTypeDisplayName "Role" -SameAsIncoming
$map3 = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" -IncomingClaimTypeDisplayName "GivenName" -SameAsIncoming
$map4 = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" -IncomingClaimTypeDisplayName "SurName" -SameAsIncoming
$map5 = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname" -IncomingClaimTypeDisplayName "windowsaccountname" -SameAsIncoming
$map6 = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid" -IncomingClaimTypeDisplayName "GroupsId" -SameAsIncoming

$realm = "urn:seo:sharepointMT"

write-host "Setting up STS in SharePoint"
$ap = New-SPTrustedIdentityTokenIssuer -Name "ADFS Provider" -Description "SharePoint secured by SAML" -realm $realm -ImportTrustCertificate $cert -ClaimsMappings $map,$map2,$map3,$map4,$map5,$map6 -SignInUrl "https://ADFSServer/adfs/ls" -IdentifierClaim "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"

#This is ADFS Web Serv cert
$certPath = "C:\ADFS\MachineCert.cer"
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(“$certPath”)
New-SPTrustedRootAuthority “ADFS web server” -Certificate $cert

#This is the cert associated with the server running IIS/SP
$mycert = New-Object System.Security.Cryptography.x509Certificates.x509Certificate2("C:\ADFS\VM55.cer");
New-SPTrustedRootAuthority –Name "IISSSL" –Certificate $mycert;


User Profile Service using ADFS



In order to get this to work, there are a few things that have to be done before synchronizing users.

Here is a list:
  • Port 389 has to be opened on network that has ADFS Server running.
  • An AD User who has delegate permission/replicate should be created and provided to SP
  • Then, you can  go to User Profile Service and add new connection and choose ADFS as login method. When you choose ADFS, your STS provider will be listed in a drop down. Choose that. Then, put the user and password of the AD User that was created above to save.
  • Finally, start incremental/full import.
 ADFS & Multi-Tenancy

I did not find a lot of information on this where ADFS will work in multi-tenancy. In Office 365, it works, but that is on cloud. I started on this after finishing the above 2 big technical blocks. I was able to SUCCESFULLY set up ADFS in a multi-tenant environment.

You may have a scenario where there are 2 Tenants that are connected to different ADFS servers and you only want to display the STS they are mapped to. This can be accomplished by creating a custom login page which has a drop down with the name of STS provider that tenant is mapped. This drop down can be populated in multiple ways.

UPS using ADFS in multi-Tenant

This was the grand daddy of all the above or in other words the cherry on top of this massive technical sundae. I got this to work as well. This was not bad, but the only catch here is that you have to set OU when provisioning tenants. There is a document on Harbar.net that provides details on how you set up OU, but other than that it is fairly similar to dedicated environment. The other noted difference is that you use the UPS of CA, to set up connection and so forth. However, you need to goto Tenant's CA, to see all the users.

If you are having difficulties in getting this to work, here is a list of things that you may want to check:
  • You should be able successfully login via ADFS.
  • Port 389 is open
  • User with appropriate permission has been used. (Note: A Farm Admin or System admin will not help)
  • Check Fore Front Identity Management for the successes and failures.
If the above still does not help, it is time to open SQL Server that is associated with SP. This not the thing you should be touching, but if above things are all correct, you would have to open the Profile database. In that database, look at Tenant table and look at SynchronizationOU column. This column should have the appropriate OU, you have defined for the Tenant. Then, finally look at the UserProfile_Full and ensure that the users exists and if they do, check the partition id so to ensure they are mapped to the right tenant.

Saturday, March 10, 2012

Supercharged Content Query WebPart

For the last 2 weeks, I have been working on a very challenging assignment that another senior SharePoint architect stated that cannot be done. Here is a scenario; we want to dynamically pull the data from a list based upon a hierarchy structure that is tied to the user who has logged in to the system. The most obvious choice would be to use Content Query Web Part. The challenge here is that we are limited by 3 filters and what if we want to bring the data from Parent and its 8 children. This is not possible unless we start writing massive CAML.

Here is the solution I implemented for solving this problem. You create a webpart that inherits directly from CQWP and then in that class you can set your filters depending upon your requirements. Remember we cannot have more than 3 filters with CQWP even when we overwrite via VS. The key part of the solution is to map our hierarchy structure with a Term Store. The reason why this is a very cool idea is because in SP 2010 term store are 100% integrated with CQWP. When you define a column in the list of type managed metadata then you can use that as one of the filters in CQWP. When you use this column in CQWP, it gives you an option to include children as this is now supported in SP2010.

The other key aspect of this solution was to via code to set the filter based on the term that is associated with the current user. In the initialize of the inherited subclass, we will add code something like this, which will dynamically set the filter every time the webpart is executed.

this.FilterField1 = “Taxonomy Field Name”;

this.FilterOperator1 = ContentByQueryWebPart.FilterFieldQueryOperator.ContainsAny;

this.Filter1ChainingOperator = ContentByQueryWebPart.FilterChainingOperator.Or;

this.FilterType1 = "TaxonomyFieldTypeMulti";

this.FilterIncludeChildren1 = ShouldHaveDescedant();

this.FilterValue1 = ResolveFilterValue();

I don't want to get into the details about the power of CQWP on its own, but now when we combine CQWP with taxonomy we are going to produce a supercharged version of CQWP.