Launch, manage and govern Yammer in your organization

Yammer is one of the most powerful enterprise social networking tools available today. It is very easy to use and designed for company collaboration, file sharing, and knowledge exchange and team efficiency. It is like a company’s internal ‘Facebook’ and has a similar interface too.

You can use Yammer to

  1.             Meet fellow coworkers who share your interests
  2.             Find the expert help you need to complete a task
  3.             Share important news with those who need to know
  4.             Discuss major decisions and manage change
  5.             Gather opinions and ideas from across our company
  6.             Provide peer-to-peer recognition
  7.             Gain visibility into what’s happening
  8.             Share best practices and solutions
  9.             Discover valuable information to inform your work
  10.            Communicate in real time, while you’re on the go

Yammer drives value in the following manner -

1.            Share & leverage knowledge

  •             Increase cross-selling
  •             Faster responses to customer inquiries

2.            Discover expertise & existing resources

  •             Get answers without knowing who can help
  •             Improve operations with best practices
  •             Avoid duplication & rework

3.            Form global, virtual workgroups

  •             Advance projects faster
  •             Increase innovation & research productivity
  •              Ideate globally

1. Versions of Yammer

Yammer is available in two different versions – free and enterprise. Anybody with a ‘@company.com’ email address can sign up for Yammer and start interacting with fellow colleagues who have signed up using similar ‘@company.com’ email addresses. It is very easy.

The difference between free and enterprise version is that the enterprise version provides extra features for control/administration like user management, branding, analytics, keyword monitoring, profile management, usage policy, SSO, Directory Sync, 3rd party integration etc. However, there are many glaring misses in the feature list like –

1)      Unable to moderate every post before posting

2)      Preventing/moderating users from creating groups

3)      No feature to flag posts as inappropriate

4)      Inability to post company-wide announcements without sending email alerts to everyone

Having said that, Yammer is still one of the best enterprise networking tools available today and Yammer enterprise has many governance capabilities that justifies the cost of having it. The best part is that it is free with many versions of Office 365 and has integration with SharePoint Online, Salesforce etc( and virtually any web-based system using embeds).

 

2. Yammer governance

Governance has a reputation in enterprise social for being kind of a dirty word.  It is widely observed that the best social networks deliver the most value when operating freely, unencumbered by overmuch policy. While this is largely true, enterprise governance is essential to make sure that the platform complies with the organizational requirements, achieves the desired goals and also scales effectively going forward.

Following are the pillars of Yammer governance –

  1. Yammer Admin Identification
  2. Usage Policy
  3. Data Security
  4. Group Administration
  5. IP Blocking
  6. Password Policy
  7. User Management
  8. External Networks
  9. Contingent worker policy
  10. Data Retention Policy (Soft delete)
  11. Configure user profile fields

2.1 Yammer Admins

There are basically 3 types of admins in Yammer. They are –

  1. Group Admin: Group admins can configure certain options for the group they are an admin of. The privileges include uploading group images, setting group name and description, managing group members, moderating content, making announcements and setting privacy controls. Full details on Group Admin privileges is available on the link below –

https://about.yammer.com/success/activate/admin-guide/additional-features/group-admin-privileges/#admin-content

  1. Network Admin: Network admins can configure the group management options for public groups, but can only access the private groups they belong to. In addition, they can configure network settings, features, applications, usage policy, new user defaults, invites, user profile configuration, delete messages, manage users, delete posts etc. More details are listed below.
  2. Verified Admin: In addition to all group administration and Network Admin privileges, they can bulk update users, monitor keywords, set data policies, configure security and perform integrations with SharePoint, AD, Salesforce, setup  SSO etc. More details are listed below.

AdminComp

Courtesy: Yammer site

Identify the Network and Verified admins. Equip them with the admin guide and train them to do their duties effectively. In addition, designate group admins for each of the departments and other company wide communities (e.g – Book club, any sports related communities, Carpooling etc.). More details on how to manage groups is detailed below under ‘Group administration’.

2.2 Usage Policy

The usage policy is the first line of defense when you want to ensure users are using the tool appropriately. Usage policies in Yammer can discuss general items such as no profanity, religious discussions, political arguments, union discussions, and so on. They can link to other corporate policies for employees to abide by. Policy can be presented to users in the following manner -

  1. Users can be required to accept the policy during sign-up and after any changes are made to the policy.
  2. A link to the policy can also be made available in the right side bar so that users can quickly reference the information.
  3. Also, the policy can be pushed to users at regular intervals (like every 6 months).

2.3 Data Security (Keyword monitoring & Data export)

Data security is very important for every enterprise. To maintain data security, Yammer provides a powerful feature called ‘Keyword Monitoring’. This feature enables sending of alert emails whenever designated keywords or phrases are posted in any message on Yammer. In other words any single word, phrase, or regular expression can be monitored.

For Example – Secured information like credit card info, SSN data etc can be monitored using regular expression codes in Keyword Monitoring feature

Admins can also use Data Export to manage entire network data according to company’s policies. To preserve the integrity of business, they can schedule daily or weekly data exports to meet legal and regulatory compliance. Once the Data Export download is complete, they can open the file to access records of all the Groups, Messages, Topics, Users, and more added to the network in the designated timeframe.

2.4 Group Administration

Yammer groups provide collaboration and administrative capabilities that makes teams more connected and productive. This is the primary feature of Yammer and every user has the ability to create a private/public group. Since this feature drives a lot of value and cannot be restricted (Refer here) by pre-defining groups, guiding and administering them is very important. Following are some of the ways to administer groups –

  1. Include a section in the usage policy helping to define what types of groups are appropriate on the network
  2. Within the best practices of the usage policy there should be a mention of searching for a group first, prior to creation, as this will keep from having duplicate content areas.
  3. Monitor the phrase “has created the” to keep track of the group creators. This phrase is always used when someone creates a group and it can help in multiple ways. Use it to invite users to a Group Administrators group with tips, tricks, and best practices for group administrators. You can also use it to determine if the group is a valid, not duplicate group and take the appropriate action if necessary.

It is recommended to create official groups for each of the departments and specify a consistent branding (image, title & description) for them so that users can easily distinguish them from other groups.

2.5 IP Blocking

This feature gives an ability to lock down company’s Yammer network to a trusted range of IP addresses. If we want that users should only be able to access Yammer when they are at an office, the IP range of devices should be added here. This will also block mobile devices unless it is specified accordingly.

2.6 Password Policy

If Yammer SSO is implemented, users can sign in to yammer seamlessly without extra login prompts & extra passwords. But in the absence of SSO, company’s password complexity requirements can be enforced for Yammer too using its password policy settings. Password policies can be set for minimum length, different complexity requirements from none to requiring upper and lower case letters, numbers, and special characters. Password reset restrictions can also be enforced.

2.7 User Management

Directory Sync provides a great way of managing users in Yammer. Directory Sync synchronizes user accounts between the directory infrastructure and Yammer. This includes adding and removing accounts as well as syncing up certain user attributes. However, in the absence of Directory Sync, care must be taken to ensure that deactivated and blocked user accounts are also removed from Yammer manually. This can be done by exporting the list of users from Yammer and comparing them against active directory at regular intervals.

2.8 External Networks

External networks can provide a significant advantage in collaborating with customers, partners, and vendors without having to worry about creating authentication methods for those external users. With the Yammer network an external network has no external user limitations. Those external users will not be able to access the internal Yammer network for your organization; they are completely separate instances.

By default, external networks can be created by any user and its password policies cannot be enforced. It is advised to lock this down to an admin only function. Restrictions can also be put in place to restrict company users from joining other networks and to hide the ‘Related External Networks’ directory.

2.9 Contingent worker policy

Contingent workers should be carefully added to company’s private/public groups as they may be exposed to company info/discussions that shouldn’t be shared with them. Normally, companies add a suffix/prefix to contingent workers’ names in Yammer so that other users can easily identify them and may not add them to groups that may contain some level of sensitive material.

2.10 Data retention policy

Choose between Hard Delete and Soft Delete options to meet your company’s needs. Hard Delete permanently removes data when it is deleted on the network and is recommended for most networks. Soft Delete keeps deleted data visible in data exports and is recommended for networks that have strict data retention policies.

 

Useful resources for Yammer

Yammer in 5 mins

http://www.slideshare.net/bryonycoleslides/yammer-in-5-minutes

How to administer Yammer

http://blog.pluralsight.com/how-to-administer-yammer

Cargill Yammer setup

http://channel9.msdn.com/Events/SharePoint-Conference/2014/SPC295

Yammer official admin guide

https://about.yammer.com/success/activate/admin-guide/

http://success.yammer.com – Has ton of resources and videos. Official site for Yammer documentation

Join YCN (Yammer customer network) for excellent resources on Yammer management & documentation

Join Yammer Developer Network for development discussions on Yammer API, OpenGraph & Embeds

Fix for Visual Studio 2012 crash on publish of SharePoint wsp

Have you ever wondered why VS2012 sometimes crashes when you try to publish a wsp? I faced this issue several times and then spent some time to analyze the cause.

I found that if you donot specify a Site Url in the SP Project properties and try to publish a wsp, VS will simply crash and restart. To solve the problem, simply do the following -

1) In the VS Solution explorer window, select the SharePoint project and click F4 button (project properties).

2) In the project properties section, enter a valid Site Url and save the project. (Then the project properties will show “Online” instead of “Cannot connect to SharePoint farm”.)

Publish the wsp again. It works without crashing :-) …Whoa!!!

VS Bug

 

REST API Security Trimming SharePoint 2013

REST API is a great means of interacting with data in SharePoint 2013 on-premise and online.  It was introduced in SP2010 and is greatly enhanced in SP2013. However, the results in REST queries are not always security trimmed.

Let us consider a simple scenario : We want to display the list of subsites which are accessible to the logged in user using Javascript. A normal REST query for this scenario will be like this -

http://site/_api/web/webs/?$select=title

However, to your surprise you will find that the results returned by this query are not security trimmed. In other words, it will display all the subsites irrespective of whether the user can access it or not. This requirement can be done using Client object model. But what if you want to access data from a different site collection? CSOM will not help in that case. We have to use REST for that scenario. So, how do we proceed ?

Fortunately, there is a way. Simply use the following query :


http://site/_api/web/webs/?$select=title,effectivebasepermissions&$filter=effectivebasepermissions/high%20gt%2032

This query will only return the list of subsites which are accessible to current user :-) .

Explanation :

As you can see, the trick is the filter “effectivebasepermissions” which filters the results. If you are curious about the number 32, have a look at the following links to understand the permission numbers -

http://jamestsai.net/Blog/post/Understand-SharePoint-Permissions-Part-2-Check-SharePoint-usergroup-permissions-with-Permissions-web-service-and-JavaScript.aspx

http://jamestsai.net/Blog/post/Understand-SharePoint-Permissions—Part-1-SPBasePermissions-in-Hex2c-Decimal-and-Binary—The-Basics.aspx

Then, fire a query like this -


http://site/_api/web/roledefinitions

This will return you the list of available role definitons and you can analyze that for all the roles having high value < 32, user cannot access the web. This is how we get the lucky number "32".

<?xml version="1.0" encoding="utf-8" ?> 
- <feed xml:base="https://yourcompany.com/sites/projects/_api/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
  <id>ae4644c5-38a4-4a34-836e-5defb670188b</id> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741829)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741829)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">2147483647</d:High> 
  <d:Low m:type="Edm.Int64">4294967295</d:Low> 
  </d:BasePermissions>
  <d:Description>Has full control.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741829</d:Id> 
  <d:Name>Full Control</d:Name> 
  <d:Order m:type="Edm.Int32">1</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">5</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741828)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741828)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">432</d:High> 
  <d:Low m:type="Edm.Int64">1012866047</d:Low> 
  </d:BasePermissions>
  <d:Description>Can view, add, update, delete, approve, and customize.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741828</d:Id> 
  <d:Name>Design</d:Name> 
  <d:Order m:type="Edm.Int32">32</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">4</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741830)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741830)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">432</d:High> 
  <d:Low m:type="Edm.Int64">1011030767</d:Low> 
  </d:BasePermissions>
  <d:Description>Can add, edit and delete lists; can view, add, update and delete list items and documents.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741830</d:Id> 
  <d:Name>Edit</d:Name> 
  <d:Order m:type="Edm.Int32">48</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">6</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741827)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741827)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">432</d:High> 
  <d:Low m:type="Edm.Int64">1011028719</d:Low> 
  </d:BasePermissions>
  <d:Description>Can view, add, update, and delete list items and documents.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741827</d:Id> 
  <d:Name>Contribute</d:Name> 
  <d:Order m:type="Edm.Int32">64</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">3</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741826)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741826)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">176</d:High> 
  <d:Low m:type="Edm.Int64">138612833</d:Low> 
  </d:BasePermissions>
  <d:Description>Can view pages and list items and download documents.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741826</d:Id> 
  <d:Name>Read</d:Name> 
  <d:Order m:type="Edm.Int32">128</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">2</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741825)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741825)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">16</d:High> 
  <d:Low m:type="Edm.Int64">134283264</d:Low> 
  </d:BasePermissions>
  <d:Description>Can view specific lists, document libraries, list items, folders, or documents when given permissions.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">true</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741825</d:Id> 
  <d:Name>Limited Access</d:Name> 
  <d:Order m:type="Edm.Int32">160</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">1</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741924)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741924)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">176</d:High> 
  <d:Low m:type="Edm.Int64">138612801</d:Low> 
  </d:BasePermissions>
  <d:Description>Can view pages, list items, and documents. Document types with server-side file handlers can be viewed in the browser but not downloaded.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741924</d:Id> 
  <d:Name>View Only</d:Name> 
  <d:Order m:type="Edm.Int32">2147483647</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">0</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741925)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741925)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">432</d:High> 
  <d:Low m:type="Edm.Int64">1011028991</d:Low> 
  </d:BasePermissions>
  <d:Description>Can edit and approve pages, list items, and documents.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741925</d:Id> 
  <d:Name>Approve</d:Name> 
  <d:Order m:type="Edm.Int32">2147483647</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">0</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741926)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741926)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">1073742320</d:High> 
  <d:Low m:type="Edm.Int64">2129075183</d:Low> 
  </d:BasePermissions>
  <d:Description>Can create sites and edit pages, list items, and documents.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741926</d:Id> 
  <d:Name>Manage Hierarchy</d:Name> 
  <d:Order m:type="Edm.Int32">2147483647</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">0</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741927)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741927)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">0</d:High> 
  <d:Low m:type="Edm.Int64">196641</d:Low> 
  </d:BasePermissions>
  <d:Description>Can view pages and documents, but cannot view historical versions or user permissions.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741927</d:Id> 
  <d:Name>Restricted Read</d:Name> 
  <d:Order m:type="Edm.Int32">2147483647</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">0</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
- <entry>
  <id>https://yourcompany.com/sites/projects/_api/Web/RoleDefinitions(1073741928)</id> 
  <category term="SP.RoleDefinition" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
  <link rel="edit" href="Web/RoleDefinitions(1073741928)" /> 
  <title /> 
  <updated>2013-08-18T17:42:37Z</updated> 
- <author>
  <name /> 
  </author>
- <content type="application/xml">
- <m:properties>
- <d:BasePermissions m:type="SP.BasePermissions">
  <d:High m:type="Edm.Int64">32</d:High> 
  <d:Low m:type="Edm.Int64">65536</d:Low> 
  </d:BasePermissions>
  <d:Description>Can open lists and folders, and use remote interfaces.</d:Description> 
  <d:Hidden m:type="Edm.Boolean">false</d:Hidden> 
  <d:Id m:type="Edm.Int32">1073741928</d:Id> 
  <d:Name>Restricted Interfaces for Translation</d:Name> 
  <d:Order m:type="Edm.Int32">2147483647</d:Order> 
  <d:RoleTypeKind m:type="Edm.Int32">0</d:RoleTypeKind> 
  </m:properties>
  </content>
  </entry>
  </feed>

Hope it helps :-)

Fix for Your My Site profile not displaying the Organization Browser/chart in SharePoint 2013 + Inherit/override/modify SharePoint built in webpart

Have you ever wondered why your profile doesn’t show the Organization chart (or browser) when you see it yourself but shows up to other users when they visit your profile ? This behaviour is actually by design. The KB article from Microsoft clearly mentions this statement -

“This behavior is by design”

and suggests to use the Organization Browser webpart which is totally different. The Organization webpart that shows up when you view a user details page (mysite url/Person.aspx) looks very clean and many people and organizations like it. One interesting fact is that if you Edit “Person.aspx”, the organization chart shows up for your own profile too but disappears once you save the page and go back to View mode :-) . So, the question is how to display it in all the scenarios. Unfortunately, there is no webpart property also which can help us achieve our requirement.

In this blog post, I will clearly explain how to achieve this requirement and also explain how to override/modify SharePoint out of the box webpart using reflection. To research this issue, let’s see how Microsoft has designed this webpart. The best way to do this is to open up reflector (my favourite :-) ) and check this webpart’s code. The Organization webpart is located in the dll called “Microsoft.SharePoint.Portal.dll” under the namespace “Microsoft.SharePoint.Portal.WebControls”. The class name is “ProfileManages”. The code from this class is shown below -

using Microsoft.Office.Server.Administration;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.Office.Server.Utilities;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Portal.WebControls.UserProfileHelper;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;

namespace Microsoft.SharePoint.Portal.WebControls
{
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel=true)]
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel=true)]
    [XmlRoot(Namespace="urn:schemas-microsoft-com:sharepoint:portal:orgchartwebpart")]
    public class ProfileManages : WebPartLoc, IDesignTimeHtmlProvider
    {
        private ProfilePropertyLoader m_objLoader;

        private UserProfile[] m_objManagers;

        private UserProfile[] m_objDirectReports;

        private UserProfile[] m_objPeers;

        private int m_CompatibilityLevel = 14;

        private int CompatibilityLevel
        {
            get
            {
                return this.m_CompatibilityLevel;
            }
            set
            {
                this.m_CompatibilityLevel = value;
            }
        }

        public ProfileManages()
        {
            CssRegistration.Register("Themable/portal.css");
            base.UseDefaultStyles = false;
            base.Load += new EventHandler(this.LoadControl);
        }

        private string GetPersonLink(string strPreferredName, string strAccountName, Guid guid, string idPrefix)
        {
            string str;
            string str1 = strAccountName;
            if (strPreferredName != null && strPreferredName.Length > 0)
            {
                str1 = strPreferredName;
            }
            str = (string.IsNullOrEmpty(strAccountName) ? UserProfileGlobal.GetUserProfileURL("?guid=", guid.ToString()) : UserProfileGlobal.GetUserProfileURL("?accountname=", strAccountName));
            string[] strArrays = new string[] { "<a class=\"ms-link ms-textSmall ms-subtleLink\" id=\"", HttpEncodingUtility.HtmlAttributeEncode(string.Concat(idPrefix, "_l")), "\" href=\"", SPHttpUtility.HtmlUrlAttributeEncode(str), "\"><b>", HttpUtility.HtmlEncode(str1), "</b></a>" };
            return string.Concat(strArrays);
        }

        internal static bool IsAssistant(UserProfile myProfile, UserProfile myReport)
        {
            string value = (string)myProfile["Assistant"].Value;
            string str = (string)myReport["AccountName"].Value;
            if (string.IsNullOrEmpty(value))
            {
                return false;
            }
            return value.Equals(str, StringComparison.OrdinalIgnoreCase);
        }

        private void LoadControl(object sender, EventArgs e)
        {
            base.StopProcessingRequestIfNotNeeded();
            if (string.IsNullOrEmpty(this.Title))
            {
                this.Title = StringResourceManager.GetString(1946);
            }
            bool isRenderingDesignTime = DesignTimeHtmlProviderHelper.IsRenderingDesignTime;
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel=true)]
        string Microsoft.SharePoint.WebControls.IDesignTimeHtmlProvider.GetDesignTimeHtml()
        {
            return base.GetNormalDesignTimeHtml();
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel=true)]
        protected override void OnInit(EventArgs e)
        {
            bool flag;
            bool flag1;
            bool flag2;
            bool flag3;
            SPSite site = SPContext.Current.Site;
            if (site != null)
            {
                this.CompatibilityLevel = site.CompatibilityLevel;
            }
            if (!UserProfileApplicationProxy.IsAvailable(SPServiceContext.Current))
            {
                return;
            }
            this.m_objLoader = ProfilePropertyLoader.FindLoader(this.Page);
            IPersonalPage page = this.Page as IPersonalPage;
            if (this.m_objLoader != null && page != null)
            {
                flag = (base.DesignMode || base.WebPartManager.DisplayMode == WebPartManager.DesignDisplayMode ? true : base.WebPartManager.DisplayMode == WebPartManager.EditDisplayMode);
                bool flag4 = flag;
                if (!flag4 && page.ProfilePropertyLoader.IsSelf)
                {
                    this.Hidden = true;
                    return;
                }
                this.m_objLoader.ProfileLoaded.LoadOrganizationFromHierarchyCache(true);
                this.m_objManagers = this.m_objLoader.ProfileLoaded.GetManagersFromCache();
                flag1 = (this.m_objManagers == null ? false : (int)this.m_objManagers.Length > 0);
                bool flag5 = flag1;
                this.m_objDirectReports = this.m_objLoader.ProfileLoaded.GetDirectReportsFromCache();
                flag2 = (this.m_objDirectReports == null ? false : (int)this.m_objDirectReports.Length > 0);
                bool flag6 = flag2;
                if (!flag6)
                {
                    this.m_objPeers = this.m_objLoader.ProfileLoaded.GetPeersFromCache();
                }
                flag3 = (this.m_objPeers == null ? false : (int)this.m_objPeers.Length > 0);
                bool flag7 = flag3;
                if (!flag4 && !flag5 && !flag6 && !flag7)
                {
                    this.Hidden = true;
                }
            }
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel=true)]
        protected override void OnPreRender(object sender, EventArgs e)
        {
            if (this.CompatibilityLevel >= 15)
            {
                WebPartTitleTranslator.TranslateTitle(this);
            }
        }

        private void RenderManagerRows(HtmlTextWriter writer, Colleague objLink, LocStringId locid, string prefix)
        {
            StringResourceManager.GetString(1962);
            writer.WriteLine("<TR>");
            writer.WriteLine("<TD valign=middle align=center height='16px' width='16px' style=\"font-family:Wingdings 3;font-size:8px\">");
            writer.Write("&#211;");
            writer.WriteLine("</TD>");
            writer.WriteLine("<TD CLASS=\"ms-announcementtitle\" WIDTH=\"100%\" style=\"vertical-align: middle\"><span class=\"ms-profilesection\">");
            writer.WriteLine(HttpUtility.HtmlEncode(StringResourceManager.GetString(locid)));
            writer.WriteLine("</span></TD></TR>");
            if (objLink == null)
            {
                writer.Write("<TR><td width=\"100%\" colspan=2><span>");
                writer.Write(HttpUtility.HtmlEncode(StringResourceManager.GetString(1966)));
                writer.WriteLine("</span></TD></TR>");
                return;
            }
            string value = (string)objLink.Profile["AccountName"].Value;
            string str = (string)objLink.Profile["PreferredName"].Value;
            string value1 = (string)objLink.Profile["WorkEmail"].Value;
            string str1 = (string)objLink.Profile["SPS-SipAddress"].Value;
            Guid d = objLink.Profile.ID;
            this.RenderOnePersonRow(writer, prefix, value1, str1, string.Empty, this.GetPersonLink(str, value, d, prefix));
        }

        private void RenderMultipleRows(HtmlTextWriter writer, UserProfile[] objProfiles, UserProfile objStartProfile, string prefix)
        {
            bool flag = objStartProfile == null;
            for (int i = 0; i < (int)objProfiles.Length; i++)
            {
                UserProfile userProfile = objProfiles[i];
                if (objStartProfile != null && objStartProfile.ID == userProfile.ID)
                {
                    flag = true;
                }
                if (flag)
                {
                    Guid d = userProfile.ID;
                    string value = (string)userProfile["AccountName"].Value;
                    string str = (string)userProfile["PreferredName"].Value;
                    string value1 = (string)userProfile["Title"].Value;
                    string str1 = (string)userProfile["WorkEmail"].Value;
                    string value2 = (string)userProfile["SPS-SipAddress"].Value;
                    this.RenderOnePersonRow(writer, string.Concat(prefix, i.ToString(CultureInfo.InvariantCulture)), str1, value2, value1, this.GetPersonLink(str, value, d, string.Concat(prefix, i.ToString(CultureInfo.InvariantCulture))));
                }
            }
        }

        private void RenderOnePersonRow(HtmlTextWriter writer, string strId, string strEmail, string strSipAddress, string strTitle, string strPersonLink)
        {
            if (strId == null)
            {
                strId = string.Empty;
            }
            if (strEmail == null)
            {
                strEmail = string.Empty;
            }
            writer.Write("<TR><TD width=\"100%\" colspan=2>");
            writer.Write("<table width=\"100%\" cellspacing=0 cellpadding=0><tr><td height='16px' width='16px' valign='middle' align='center'>");
            if (!strId.StartsWith("r", StringComparison.OrdinalIgnoreCase))
            {
                if (strId.StartsWith("m", StringComparison.OrdinalIgnoreCase))
                {
                    string str = StringResourceManager.GetString(1962);
                    writer.Write("<img border='0' src='{0}' alt='{1}'/>", ResourceFilePath.SharedImageUrl("reportsup.gif"), HttpUtility.HtmlAttributeEncode(str));
                }
            }
            else
            {
                writer.Write("<SPAN>");
                AdminUIGlobal.RenderIMPawn(writer, strId, strEmail, strSipAddress, true);
                writer.Write("</SPAN>");
            }
            writer.Write(string.Concat("</td><td align='", SiteInfo.GetLeftAlignValue(), "'"));
            if (!strId.StartsWith("r", StringComparison.OrdinalIgnoreCase))
            {
                writer.Write(" style='padding-top: 3px; padding-bottom: 3px'");
            }
            else
            {
                writer.Write(" style='padding-left: 21px; padding-top: 3px; padding-bottom: 3px' ");
            }
            writer.Write(">");
            writer.Write(strPersonLink);
            if (!string.IsNullOrEmpty(strTitle))
            {
                writer.Write("<span>&nbsp;&nbsp;<nobr>{0}</nobr></span>", HttpUtility.HtmlEncode(strTitle));
            }
            writer.Write("</td></tr></table>");
            writer.WriteLine("</TD></TR>");
        }

        private void RenderProfileAdditionalData(HtmlTextWriter writer, ProfileBase profile)
        {
            UserProfile userProfile = profile as UserProfile;
            if (userProfile != null)
            {
                string value = (string)userProfile["Title"].Value;
                if (!string.IsNullOrEmpty(value))
                {
                    writer.Write("<span class=\"ms_metadata \">&nbsp;&nbsp;<nobr>{0}</nobr></span>", HttpUtility.HtmlEncode(value));
                }
            }
        }

        private void RenderProfileUrl(HtmlTextWriter writer, ProfileBase profile)
        {
            UserProfile userProfile = profile as UserProfile;
            OrganizationProfile organizationProfile = profile as OrganizationProfile;
            if (userProfile != null)
            {
                writer.Write(this.GetPersonLink((string)userProfile["PreferredName"].Value, (string)userProfile["AccountName"].Value, userProfile.ID, string.Empty));
                return;
            }
            if (organizationProfile != null)
            {
                string[] str = new string[] { "<a id=\"", null, null, null, null, null, null };
                long recordId = organizationProfile.RecordId;
                str[1] = recordId.ToString();
                str[2] = "_2\" href=\"";
                str[3] = SPHttpUtility.HtmlUrlAttributeEncode(organizationProfile.PublicUrl.ToString());
                str[4] = "\">";
                str[5] = HttpUtility.HtmlEncode(organizationProfile.DisplayName);
                str[6] = "</a>";
                writer.Write(string.Concat(str));
            }
        }

        private void RenderSectionHeader(string sectionTitle, HtmlTextWriter writer)
        {
            writer.Write("<tr class='ms-profilehierarchysectionheader'><td height='16px' colspan=2><div class=\"ms-profilehierarchysectionheader\"><nobr>");
            SPHttpUtility.HtmlEncode(sectionTitle, writer);
            writer.WriteLine("</nobr></div></td></tr>");
        }

        private void RenderSubProfiles(HtmlTextWriter writer, ProfileBase[] objSubProfiles, bool bCurrentProfileIncluded)
        {
            writer.WriteLine("<tr><td width=\"8px;\">&nbsp;</td><td><table cellSpacing=0 cellPadding=0 width='100%'>");
            for (int i = 0; i < (int)objSubProfiles.Length; i++)
            {
                ProfileBase profileBase = objSubProfiles[i];
                writer.Write("<tr height='100%'><td><table height='100%' width='16' cellpadding=0 cellspacing=0 border='0'>");
                writer.Write("<tr height='9'><td valign='top' ");
                writer.Write("style='background-image: url(");
                writer.Write(SPHttpUtility.HtmlUrlAttributeEncode(ResourceFilePath.GetResourceFileUrl(ResourceFileContext.SharedImage, "hierbottom.gif")));
                writer.Write(")'></td></tr>");
                string str = StringResourceManager.GetString(1965);
                writer.Write(string.Concat("<tr height='1px'><td alt='", str, "' style='background-image: url("));
                if (!SiteInfo.IsUICultureRTL)
                {
                    writer.Write(SPHttpUtility.HtmlUrlAttributeEncode(ResourceFilePath.GetResourceFileUrl(ResourceFileContext.SharedImage, "hiertop.gif")));
                }
                else
                {
                    writer.Write(SPHttpUtility.HtmlUrlAttributeEncode(ResourceFilePath.GetResourceFileUrl(ResourceFileContext.SharedImage, "hiertoprtl.gif")));
                }
                writer.Write(")'/></tr><tr height='9'><td ");
                if (i < checked((int)objSubProfiles.Length - 1))
                {
                    writer.Write("style='background-image: url(");
                    writer.Write(SPHttpUtility.HtmlUrlAttributeEncode(ResourceFilePath.GetResourceFileUrl(ResourceFileContext.SharedImage, "hierbottom.gif")));
                    writer.Write(")'");
                }
                writer.Write("><IMG SRC='/_layouts/images/blank.gif' width=1 height=1 alt=''></td></tr></table></td>");
                if (!bCurrentProfileIncluded || i != 0)
                {
                    writer.Write(string.Concat("<td width='100%' class=\"ms-orgname\" align='", SiteInfo.GetLeftAlignValue(), "'>"));
                }
                else
                {
                    writer.Write(string.Concat("<td width='100%' class=\"ms-orgme ms-orgname\" align='", SiteInfo.GetLeftAlignValue(), "'>"));
                }
                this.RenderProfileUrl(writer, profileBase);
                this.RenderProfileAdditionalData(writer, profileBase);
                writer.Write("</td></tr>");
            }
            writer.Write("</table></td></tr>");
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel=true)]
        protected override void RenderWebPart(HtmlTextWriter writer)
        {
            base.StopProcessingRequestIfNotNeeded();
            if (this.Hidden)
            {
                return;
            }
            if (!DesignTimeHtmlProviderHelper.IsRenderingDesignTime)
            {
                if (!UserProfileApplicationProxy.IsAvailable(SPServiceContext.Current))
                {
                    return;
                }
                if (this.m_objLoader != null && !this.m_objLoader.IsError && this.m_objLoader.LoadedProfile != null)
                {
                    this.RenderWebPartHierarchySection(writer);
                    this.RenderWebPartOrgBrowserLink(writer);
                }
                return;
            }
            writer.WriteLine("<table style=\"table-layout:fixed;word-wrap:break-word\" width=\"100%\">");
            if (this.ChromeType == PartChromeType.None)
            {
                this.RenderSectionHeader(this.Title, writer);
            }
            this.RenderManagerRows(writer, null, 1962, "m");
            writer.WriteLine("</table>");
        }

        private void RenderWebPartHierarchySection(HtmlTextWriter writer)
        {
            if (this.m_objLoader.Type == ProfileType.User)
            {
                writer.WriteLine("<table width=\"100%\" id=\"ReportingHierarchy\" cellspacing='0' cellpadding='0' border='0'>");
                UserProfile commonManager = this.m_objLoader.ProfileLoaded.GetCommonManager();
                if (this.m_objManagers != null && (int)this.m_objManagers.Length > 0)
                {
                    this.RenderMultipleRows(writer, this.m_objManagers, commonManager, "m");
                }
                List<UserProfile> userProfiles = new List<UserProfile>();
                if (this.m_objDirectReports != null)
                {
                    UserProfile[] mObjDirectReports = this.m_objDirectReports;
                    for (int i = 0; i < (int)mObjDirectReports.Length; i++)
                    {
                        UserProfile userProfile = mObjDirectReports[i];
                        userProfiles.Add(userProfile);
                    }
                }
                if (userProfiles.Count <= 0)
                {
                    List<UserProfile> userProfiles1 = new List<UserProfile>();
                    userProfiles1.Add(this.m_objLoader.ProfileLoaded);
                    if (this.m_objPeers != null)
                    {
                        UserProfile[] mObjPeers = this.m_objPeers;
                        for (int j = 0; j < (int)mObjPeers.Length; j++)
                        {
                            UserProfile userProfile1 = mObjPeers[j];
                            userProfiles1.Add(userProfile1);
                        }
                    }
                    this.RenderSubProfiles(writer, userProfiles1.ToArray(), true);
                }
                else
                {
                    string str = StringResourceManager.GetString(1963);
                    writer.Write("\n<tr height=3><td></td></tr>\n");
                    writer.Write("<TR class=\"ms-orgme\"><TD width=\"100%\" colspan=2>");
                    writer.Write("<table width=\"100%\" cellspacing=0 cellpadding=0 ><tr><td height='16px' width='16px' valign='middle' align='center'>");
                    string[] strArrays = new string[] { "<img src=\"", SPHttpUtility.HtmlUrlAttributeEncode(ResourceFilePath.GetResourceFileUrl(ResourceFileContext.SharedImage, "reportsup.gif")), "\" alt=\"", HttpUtility.HtmlEncode(str), "\">" };
                    writer.Write(string.Concat(strArrays));
                    writer.Write("</TD>");
                    writer.Write(string.Concat("<td class=\"ms-orgme\" style='padding-left:2px;padding-right:2px;padding-bottom:3px;padding-top:3px;font-weight:bold' class='ms-textSmall ms-subtleLink' align='", SiteInfo.GetLeftAlignValue(), "'>"));
                    writer.Write(HttpUtility.HtmlEncode((string)this.m_objLoader.ProfileLoaded["PreferredName"].Value));
                    string value = (string)this.m_objLoader.ProfileLoaded["Title"].Value;
                    if (!string.IsNullOrEmpty(value))
                    {
                        writer.Write("<span class='ms_metadata'>&nbsp;&nbsp;<nobr>{0}</nobr></span>", HttpUtility.HtmlEncode(value));
                    }
                    writer.WriteLine("</TD></TR></TABLE></td></tr>");
                    this.RenderSubProfiles(writer, userProfiles.ToArray(), false);
                }
                writer.WriteLine("</table>");
            }
        }

        private void RenderWebPartOrgBrowserLink(HtmlTextWriter writer)
        {
            string originalString = this.m_objLoader.LoadedProfile.PublicOrganizationViewUrl.OriginalString;
            if (this.m_objLoader.Type == ProfileType.User)
            {
                originalString = this.m_objLoader.ProfileLoaded.PublicOrganizationViewUrl.OriginalString;
            }
            if (this.CompatibilityLevel < 15)
            {
                string str = StringResourceManager.GetString(1959);
                writer.WriteLine(string.Concat("<table width=\"100%\"><tr><td align='", SiteInfo.GetLeftAlignValue(), "'>"));
                writer.Write("<span style=\"height:16px;width:16px;position:relative;display:inline-block;overflow:hidden;\"><img src=\"/_layouts/15/images/mossfgimg.png?rev=23\" alt=\"\" style=\"left:-0px !important;top:-116px !important;position:absolute;\"  /></span>");
                string[] strArrays = new string[] { " <a href=\"", SPHttpUtility.HtmlUrlAttributeEncode(originalString), "\">", HttpUtility.HtmlEncode(str), "</a>" };
                writer.Write(string.Concat(strArrays));
                writer.WriteLine("</td></tr></table>");
                return;
            }
            string str1 = StringResourceManager.GetString(1960);
            writer.Write("<div class=\"ms-profile-organizationLink\">");
            string[] strArrays1 = new string[] { "<a class=\"ms-commandLink\" href=\"", SPHttpUtility.HtmlUrlAttributeEncode(originalString), "\" id=\"orgBrowserLink_", this.ClientID, "\">", HttpUtility.HtmlEncode(str1), "</a>" };
            writer.Write(string.Concat(strArrays1));
            writer.Write("</div>");
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel=true)]
        public override bool RequiresWebPartClientScript()
        {
            return false;
        }
    }
}

If you analyze the class properly, you will see many scenarios where there are specific checks to prevent display of the webpart if the user is viewing his own profile. Check the following glaring line for example in the “OnInit” method -

if (!flag4 &amp;&amp; page.ProfilePropertyLoader.IsSelf)
{
this.Hidden = true;
return;
}

The above line is just one of the many checks that prevents its display for the logged-in user while visiting the self profile. Now after seeing the code it is very clear why the webpart is behaving in this manner.

To overcome this problem, let us build our own webpart which will inherit “ProfileManages” class and use reflection to override all the code which is causing all the trouble.
Following are the steps to do this -

1) Create an empty SharePoint project in VS2012 (You may use an existing project also).

2) Add a New item -> Webpart (not visula webpart).

3) In the project references, reference the following dlls -

Microsoft.Office.Server.dll
Microsoft.Office.Server.Search.dll
Microsoft.Office.Server.UserProfiles.dll
Microsoft.SharePoint.portal.dll

4) In the webpart class, type the following code -

using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Portal.WebControls;
using Microsoft.SharePoint.Portal;
using System.Reflection;
using Microsoft.Office.Server.UserProfiles;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Administration;
using Microsoft.Office.Server.Administration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace "Your namespace"
{
    [ToolboxItemAttribute(false)]
    public class Organization : ProfileManages
    {

        private int m_CompatibilityLevel = 14;
        private ProfilePropertyLoader m_objLoader;
        private UserProfile[] m_objManagers;
        private UserProfile[] m_objDirectReports;
        private UserProfile[] m_objPeers;

        private int CompatibilityLevel
        {
            get
            {
                return this.m_CompatibilityLevel;
            }
            set
            {
                this.m_CompatibilityLevel = value;
            }
        }       

        protected override void OnInit(EventArgs e)
        {
            
            base.OnInit(e);            
            bool flag1;
            bool flag2;
            bool flag3;
            SPSite site = SPContext.Current.Site;

            this.Hidden = false;

            if (site != null)
            {
                this.CompatibilityLevel = site.CompatibilityLevel;
            }

            if (!IsUserProfileApplicationProxyAvailable())
             {
                 return;
             } 
            this.m_objLoader = ProfilePropertyLoader.FindLoader(this.Page);
            IPersonalPage page = this.Page as IPersonalPage;
            if (this.m_objLoader != null && page != null)
            {
                MethodInfo method = this.m_objLoader.ProfileLoaded.GetType().GetMethod("LoadOrganizationFromHierarchyCache", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(bool) }, null);
                method.Invoke(this.m_objLoader.ProfileLoaded, BindingFlags.InvokeMethod, null, new object[] { true }, CultureInfo.CurrentCulture);          
                
                MethodInfo method2 = this.m_objLoader.ProfileLoaded.GetType().GetMethod("GetManagersFromCache", BindingFlags.NonPublic | BindingFlags.Instance);
                this.m_objManagers = (UserProfile[])method2.Invoke(this.m_objLoader.ProfileLoaded, null);

                flag1 = (this.m_objManagers == null ? false : (int)this.m_objManagers.Length > 0);
                bool flag5 = flag1;               
                MethodInfo method3 = this.m_objLoader.ProfileLoaded.GetType().GetMethod("GetDirectReportsFromCache", BindingFlags.NonPublic | BindingFlags.Instance);
                this.m_objDirectReports = (UserProfile[])method3.Invoke(this.m_objLoader.ProfileLoaded, null);

                flag2 = (this.m_objDirectReports == null ? false : (int)this.m_objDirectReports.Length > 0);
                bool flag6 = flag2;
                if (!flag6)
                {
                    MethodInfo method4 = this.m_objLoader.ProfileLoaded.GetType().GetMethod("GetPeersFromCache", BindingFlags.NonPublic | BindingFlags.Instance);
                    this.m_objPeers = (UserProfile[])method4.Invoke(this.m_objLoader.ProfileLoaded, null);
                }
                flag3 = (this.m_objPeers == null ? false : (int)this.m_objPeers.Length > 0);
                bool flag7 = flag3;
                if (!flag5 && !flag6 && !flag7)
                {
                    this.Hidden = true;
                }
            }
        }

        protected override void CreateChildControls()
        {        
            
        }     
       

        protected override void RenderWebPart(HtmlTextWriter writer)
        {
            if (!IsUserProfileApplicationProxyAvailable())
            {
                return;
            }

            if (this.m_objLoader != null && !this.m_objLoader.IsError && this.m_objLoader.LoadedProfile != null)
            {
                this.RenderWebPartHierarchySection(writer);
                MethodInfo dynMethod2 = this.GetType().BaseType.GetMethod("RenderWebPartOrgBrowserLink", BindingFlags.NonPublic | BindingFlags.Instance);
                dynMethod2.Invoke(this, new object[] { writer });
            }

            writer.WriteLine("<table style=\"table-layout:fixed;word-wrap:break-word\" width=\"100%\">");
            if (this.ChromeType == PartChromeType.None)
            {
                this.RenderSectionHeader(this.Title, writer);
            }
            //MethodInfo dynMethod3 = this.GetType().BaseType.GetMethod("RenderManagerRows", BindingFlags.NonPublic | BindingFlags.Instance);
            //dynMethod3.Invoke(this, new object[] { writer, null, 1962, "m" });
            //RenderManagerRows(writer, null, 1962, "m");
            writer.WriteLine("</table>");  
        }

        private void RenderManagerRows(HtmlTextWriter writer, Colleague objLink, int locid, string prefix)
        {
            GetStringResMgrString(1962); //StringResourceManager.GetString(1962);
            writer.WriteLine("<TR>");
            writer.WriteLine("<TD valign=middle align=center height='16px' width='16px' style=\"font-family:Wingdings 3;font-size:8px\">");
            writer.Write("&#211;");
            writer.WriteLine("</TD>");
            writer.WriteLine("<TD CLASS=\"ms-announcementtitle\" WIDTH=\"100%\" style=\"vertical-align: middle\"><span class=\"ms-profilesection\">");
            writer.WriteLine(HttpUtility.HtmlEncode(GetStringResMgrString(locid)));
            writer.WriteLine("</span></TD></TR>");
            if (objLink == null)
            {
                writer.Write("<TR><td width=\"100%\" colspan=2><span>");
                writer.Write(HttpUtility.HtmlEncode(GetStringResMgrString(1966)));
                writer.WriteLine("</span></TD></TR>");
                return;
            }
            string value = (string)objLink.Profile["AccountName"].Value;
            string str = (string)objLink.Profile["PreferredName"].Value;
            string value1 = (string)objLink.Profile["WorkEmail"].Value;
            string str1 = (string)objLink.Profile["SPS-SipAddress"].Value;
            Guid d = objLink.Profile.ID;
            MethodInfo RenderOnePersonRowMethod = this.GetType().BaseType.GetMethod("RenderOnePersonRow", BindingFlags.NonPublic | BindingFlags.Instance);
            RenderOnePersonRowMethod.Invoke(this, new object[] { writer, prefix, value1, str1, string.Empty, GetPersonLink(str, value, d, prefix) });
            //this.RenderOnePersonRow(writer, prefix, value1, str1, string.Empty, GetPersonLink(str, value, d, prefix));
        }

        private string GetPersonLink(string strPreferredName, string strAccountName, Guid guid, string idPrefix)
        {
            string getPersonLink = "";
            MethodInfo GetPersonLinkMethod = this.GetType().BaseType.GetMethod("GetPersonLink", BindingFlags.NonPublic | BindingFlags.Instance);
            getPersonLink = (string)GetPersonLinkMethod.Invoke(this, new object[] { strPreferredName, strAccountName, guid, idPrefix });
            return getPersonLink;
        }

        private void RenderWebPartHierarchySection(HtmlTextWriter writer)
        {          
                writer.WriteLine("<table width=\"100%\" id=\"ReportingHierarchy\" cellspacing='0' cellpadding='0' border='0'>");
                UserProfile commonManager = this.m_objLoader.ProfileLoaded.GetCommonManager();
                if (this.m_objManagers != null && (int)this.m_objManagers.Length > 0)
                {
                  /*  MethodInfo RenderMultipleRowsMethod = this.GetType().BaseType.GetMethod("RenderMultipleRows", BindingFlags.NonPublic | BindingFlags.Instance); 
                    RenderMultipleRowsMethod.Invoke(this, new object[] { writer, this.m_objManagers, commonManager, "m" });  */
                    RenderMultipleRows(writer, this.m_objManagers, commonManager, "m");
                }

                List<UserProfile> userProfiles = new List<UserProfile>();
                if (this.m_objDirectReports != null)
                {
                    UserProfile[] mObjDirectReports = this.m_objDirectReports;
                    for (int i = 0; i < (int)mObjDirectReports.Length; i++)
                    {
                        UserProfile userProfile = mObjDirectReports[i];
                        userProfiles.Add(userProfile);
                    }
                }
                if (userProfiles.Count <= 0)
                {
                    List<UserProfile> userProfiles1 = new List<UserProfile>();
                    userProfiles1.Add(this.m_objLoader.ProfileLoaded);
                    if (this.m_objPeers != null)
                    {
                        UserProfile[] mObjPeers = this.m_objPeers;
                        for (int j = 0; j < (int)mObjPeers.Length; j++)
                        {
                            UserProfile userProfile1 = mObjPeers[j];
                            userProfiles1.Add(userProfile1);
                        }
                    }                   
                    MethodInfo RenderSubProfilesMethod = this.GetType().BaseType.GetMethod("RenderSubProfiles", BindingFlags.NonPublic | BindingFlags.Instance);
                    RenderSubProfilesMethod.Invoke(this, new object[] { writer, userProfiles1.ToArray(), true });
                }
                else
                {
                    string str = GetStringResMgrString(1963);
                    writer.Write("\n<tr height=3><td></td></tr>\n");
                    writer.Write("<TR class=\"ms-orgme\"><TD width=\"100%\" colspan=2>");
                    writer.Write("<table width=\"100%\" cellspacing=0 cellpadding=0 ><tr><td height='16px' width='16px' valign='middle' align='center'>");
                    string[] strArrays = new string[] { "<img src=\"", SPHttpUtility.HtmlUrlAttributeEncode(ResourceFilePath.GetResourceFileUrl(ResourceFileContext.SharedImage, "reportsup.gif")), "\" alt=\"", HttpUtility.HtmlEncode(str), "\">" };
                    writer.Write(string.Concat(strArrays));
                    writer.Write("</TD>");
                    writer.Write(string.Concat("<td class=\"ms-orgme\" style='padding-left:2px;padding-right:2px;padding-bottom:3px;padding-top:3px;font-weight:bold' class='ms-textSmall ms-subtleLink' align='", SiteInfo.GetLeftAlignValue(), "'>"));
                    writer.Write(HttpUtility.HtmlEncode((string)this.m_objLoader.ProfileLoaded["PreferredName"].Value));
                    string value = (string)this.m_objLoader.ProfileLoaded["Title"].Value;
                    if (!string.IsNullOrEmpty(value))
                    {
                        writer.Write("<span class='ms_metadata'>&nbsp;&nbsp;<nobr>{0}</nobr></span>", HttpUtility.HtmlEncode(value));
                    }
                    writer.WriteLine("</TD></TR></TABLE></td></tr>");
                    
                    MethodInfo RenderSubProfilesMethod = this.GetType().BaseType.GetMethod("RenderSubProfiles", BindingFlags.NonPublic | BindingFlags.Instance);
                    RenderSubProfilesMethod.Invoke(this, new object[] { writer, userProfiles.ToArray(), false });                    
                }
                writer.WriteLine("</table>");            
        }

        private void RenderSectionHeader(string sectionTitle, HtmlTextWriter writer)
        {
            writer.Write("<tr class='ms-profilehierarchysectionheader'><td height='16px' colspan=2><div class=\"ms-profilehierarchysectionheader\"><nobr>");
            SPHttpUtility.HtmlEncode(sectionTitle, writer);
            writer.WriteLine("</nobr></div></td></tr>");
        }

        private void RenderMultipleRows(HtmlTextWriter writer, UserProfile[] objProfiles, UserProfile objStartProfile, string prefix)
        {
            bool flag = objStartProfile == null;
            for (int i = 0; i < (int)objProfiles.Length; i++)
            {
                UserProfile userProfile = objProfiles[i];
                if (objStartProfile != null ) //&& objStartProfile.ID == userProfile.ID)
                {
                    flag = true;
                }
                if (flag)
                {
                    Guid d = userProfile.ID;
                    string value = (string)userProfile["AccountName"].Value;
                    string str = (string)userProfile["PreferredName"].Value;
                    string value1 = (string)userProfile["Title"].Value;
                    string str1 = (string)userProfile["WorkEmail"].Value;
                    string value2 = (string)userProfile["SPS-SipAddress"].Value;

                    MethodInfo method = this.m_objLoader.ProfileLoaded.GetType().GetMethod("GetDirectReportsFromCache", BindingFlags.NonPublic | BindingFlags.Instance);
                    this.m_objDirectReports = (UserProfile[])method.Invoke(this.m_objLoader.ProfileLoaded, null);

                    MethodInfo dynMethod = this.GetType().BaseType.GetMethod("RenderOnePersonRow", BindingFlags.NonPublic | BindingFlags.Instance);
                    dynMethod.Invoke(this, new object[] { writer, string.Concat(prefix, i.ToString(CultureInfo.InvariantCulture)), str1, value2, value1, this.GetPersonLink(str, value, d, string.Concat(prefix, i.ToString(CultureInfo.InvariantCulture))) });
                                       
                   // this.RenderOnePersonRow(writer, string.Concat(prefix, i.ToString(CultureInfo.InvariantCulture)), str1, value2, value1, this.GetPersonLink(str, value, d, string.Concat(prefix, i.ToString(CultureInfo.InvariantCulture))));
                }
            }
        }

        private string GetStringResMgrString(int id)
        {            
            var stringResourceManagerType = Type.GetType("Microsoft.SharePoint.Portal.WebControls.StringResourceManager, Microsoft.Office.Server.Search, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
            var getStringProperty = stringResourceManagerType.GetMethod("GetString", AllBindings);
            object[] parameters = { id };
            return getStringProperty.Invoke(null, parameters) as string;
        }

        private static BindingFlags AllBindings
        {
            get
            {
                return BindingFlags.CreateInstance |
                    BindingFlags.FlattenHierarchy |
                    BindingFlags.GetField |
                    BindingFlags.GetProperty |
                    BindingFlags.IgnoreCase |
                    BindingFlags.Instance |
                    BindingFlags.InvokeMethod |
                    BindingFlags.NonPublic |
                    BindingFlags.Public |
                    BindingFlags.SetField |
                    BindingFlags.SetProperty |
                    BindingFlags.Static;
            }
        }

        private bool IsUserProfileApplicationProxyAvailable()
        {
            bool IsUPSAAvailable = true;
            var upaProxyType = Type.GetType("Microsoft.Office.Server.Administration.UserProfileApplicationProxy, Microsoft.Office.Server.UserProfiles, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
            var userProfileManager = new UserProfileManager(SPServiceContext.Current, false, false);
            var prop = userProfileManager.GetType().GetProperty("UserProfileApplicationProxy", AllBindings);
            var proxy = prop.GetValue(userProfileManager, null);
            var upaProxyIsAvailableProperty = upaProxyType.GetMethod("IsAvailable", AllBindings);
            object[] parameters = { SPServiceContext.Current };
            Object ret = upaProxyIsAvailableProperty.Invoke(proxy, parameters);
            if (ret != null)
                IsUPSAAvailable = (bool)ret;
            return IsUPSAAvailable;
        }

    }
}

5. Create your .dwp file (older .webpart file) :

To do this, add a file with the same name as your webpart in the location of your ‘.webpart’ file but give it an extension of ‘.dwp’. (I mean if your webpart file is Organization.webpart, create and add Organization.dwp to the same place in your project).

Copy and paste the following code into your .dwp file and update all the attributes from your .webpart file.

<?xml version="1.0" encoding="utf-8"?>
<WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">
  <Assembly>$SharePoint.Project.AssemblyFullName$,Version=1.0.0.0,Culture=Neutral,PublicToken="Your assembly publickeytoken"</Assembly>
  <TypeName>Copy from .webpart file</TypeName>
  <Title>Copy from .webpart file</Title>
  <Description>Copy from .webpart file</Description>
</WebPart>

How to find publickeyttoken : Go to the location – C:\Windows\Microsoft.NET\assembly\GAC_MSIL. Find the folder for your assembly (You may need to deploy your project to see your assembly there). Go inside that folder. There you will find another folder similar to “v4.0_15.0.0.0__71e9bce111e9429c”. The last part of that folder name is your publickeytoken.

6. Build and Deploy your project.

7. Navigate to MySite host. Go to Site Settings -> Webparts and upload your Organization.dwp file.

8. Edit Person.aspx inside your MySite host and add our Organization webpart. Save the page ad see your webpart :-)

Powershell to create and reuse Managed Navigation for multiple sites in SharePoint 2013

As promised earlier, below is a powershell script to create managed navigation in SharePoint 2013 using powershell. Before reading below, I would strongly suggest to read my previous post on managed navigation here

Add-PSSnapin Microsoft.SharePoint.Powershell

#change these variables based on environment
$siteName = "Site url" 
$siteSearchName = "Search site url" 
$termStoreName = “Managed Metadata Service”
$termGroupName = “My Navigation”
$termsetName = "MyNavigationTermset"
$termsetSearchName = "MyNavigationTermset Search"
$appendUrl = "Site url"

function CreateTerm( $parent, $name, $url )  
 {  
  Write-Host "Adding term $name to parent $parent.Name"  
   $term = $parent.CreateTerm("$name", 1033)  
  $term.IsAvailableForTagging = $true    
  $newUrl = $url
  if(!$url.ToLower().Contains("http"))
  {  
  # Logic to append site url to relative path to create complete url which is needed for Simple Link Navigation
    $newUrl = $appendUrl + $url
  }
  $term.SetLocalCustomProperty("_Sys_Nav_SimpleLinkUrl", $newUrl)   
  return $term  
 } 

# Create termgroup and termset for My Site

$site = Get-SPSite -Identity $siteName
$session = Get-SPTaxonomySession -Site $siteName
$termStore = $session.TermStores[$termStoreName]
$group = $termstore.CreateGroup($termGroupName)
$group.Description = "Term Group description"
$termStore.CommitAll()

Write-Host "TermGroup - $termGroupName created successfully"  

$termsetName = "Name of your navigation termset"
$termSet = $group.CreateTermSet($termsetName,1033)
$termSet.Description = “Navigation TermSet description”
$termSet.IsAvailableForTagging = $false
$termSet.IsOpenForTermCreation = $false
$navigationSet = $group.TermSets[$termsetName]
$navigationSet.SetCustomProperty("_Sys_Nav_IsNavigationTermSet", "True")
$termStore.CommitAll()

Write-Host "TermSet - $termsetName created successfully"  

# create terms for My site

$wwrTerm = CreateTerm $termSet “WHO WE ARE” "/who"
$wavTerm = CreateTerm $termSet “WORKING AT OUR COMPANY” "/working"
$wynTerm = CreateTerm $termSet “ABOUT US” "/what"

$termStore.CommitAll()

$termSet.CustomSortOrder = $wwrTerm.id.ToString()+":"+$wavTerm.id.ToString()+":"+$wynTerm.id.ToString()

# create sub terms for My site

$term1 = CreateTerm $wwrTerm "Company" "/who/company"
$term2 = CreateTerm $wwrTerm "Organization" "/who/organization"
$term3 = CreateTerm $wwrTerm "Products" "/who/products"

$term4 = CreateTerm $wavTerm "Perks" "/working/perks"
$term5 = CreateTerm $wavTerm "Recognition" "/working/recognition"
$term6 = CreateTerm $wavTerm "HR Info" "/who/organization/hr"

$term7 = CreateTerm $wynTerm "Resources and Services" "/what/resources/"
$term8 = CreateTerm $wynTerm "Policies" "/what/policies"
$term9 = CreateTerm $wynTerm "Contact Us" "/what/contact"

$wwrTerm.CustomSortOrder = $term1.id.ToString() +":"+ $term2.id.ToString() +":"+ $term3.id.ToString()
$wavTerm.CustomSortOrder = $term4.id.ToString() +":"+ $term5.id.ToString() +":"+ $term6.id.ToString()
$wynTerm.CustomSortOrder = $term7.id.ToString() +":"+ $term8.id.ToString() +":"+ $term9.id.ToString()
$termStore.CommitAll()

Write-Host "Terms for TermSet - $termsetName created successfully"  

# create terms for My Site Search

$termSet = $group.CreateTermSet($termsetSearchName,1033)
$termSet.Description = “Navigation TermSet for Search site”
$termSet.IsAvailableForTagging = $false
$termSet.IsOpenForTermCreation = $false
$navigationSet = $group.TermSets[$termsetName]
$navigationSet.SetCustomProperty("_Sys_Nav_IsNavigationTermSet", "True")
$termStore.CommitAll()

Write-Host "TermSet - $termsetSearchName created successfully"  

$wwrTermSearch = $termSet.ReuseTerm($wwrTerm,$true);
$wavTermSearch = $termSet.ReuseTerm($wavTerm,$true);
$wynTermSearch = $termSet.ReuseTerm($wynTerm,$true);

$termSet.CustomSortOrder = $wwrTerm.id.ToString()+":"+$wavTerm.id.ToString()+":"+$wynTerm.id.ToString()

$termStore.CommitAll()

$wwrTermSearch.CustomSortOrder = $term1.id.ToString() +":"+ $term2.id.ToString() +":"+ $term3.id.ToString()
$wavTermSearch.CustomSortOrder = $term4.id.ToString() +":"+ $term5.id.ToString() +":"+ $term6.id.ToString()
$wynTermSearch.CustomSortOrder = $term7.id.ToString() +":"+ $term8.id.ToString() +":"+ $term9.id.ToString()
$termStore.CommitAll()

Write-Host "Terms for TermSet - $termsetName created successfully"

The script above creates a navigation termset with some dummy terms for “My Site” and reuses the same terms for “My Site Search” so that both the sites can have the same managed navigation. This script also takes care of custom sort order for parent and child level terms. Also, note that there is a concept of “append url” which helps in creating the full url before assigning it to the term.

Create, update and order user profile properties using Powershell in Sharepoint 2013

User profile properties can be created and managed using the graphical interface inside SharePoint central admin.(Central Admin -> Manage Service Applications -> User Profile Service Application -> Manage User Properties). But in a real production scenario where lot of user profile properties need to be created and ordered, the GUI way becomes extremely tedious and timetaking. But, don’t worry, we can automate everything using powershell :-) .

Below is the powershell script which creates and updates (if prop already exists) the different user profile properties and orders them also by reading from a XML file.

#Script to find all employees/contractors who roll up to a manager and add their usernames to the manager's colleague list in SharePoint 
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server")  
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles")  
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles.UserProfileManager")  
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")  

$filePath = "Path to XML file containing user profile props";
$siteName = "Site url";

clear-host
#add sharepoint cmdlets
if ( (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{    
      Add-PsSnapin Microsoft.SharePoint.PowerShell
}

[System.Xml.XmlDocument] $XmlDoc = new-object System.Xml.XmlDocument
$file = resolve-path($filePath)

if (!$file)
{
    Write-Host "Could not find the configuration file specified. Aborting." -ForegroundColor red       
    Break
}

write-host "Parsing file: " $file
$XmlDoc = [xml](Get-Content $file)

                #Preparing connection manager#

        $site = new-object Microsoft.SharePoint.SPSite($siteName);  
        $context = [Microsoft.SharePoint.SPServiceContext]::GetContext($site);  

        if (!$context)
        {
        write-host "context is empty, quitting!"
        break
        }

            #$context = new-object Microsoft.SharePoint.SPServiceContext.GetContext($site); 
            $UPAConfMgr = new-object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($context)

        if (!$UPAConfMgr)
        {
        write-host "confmgr is empty, quitting!"
        break
        }

        $UPAConnMgr = $UPAConfMgr.ConnectionManager
                $userprofiletype = [Microsoft.Office.Server.UserProfiles.ProfileType]::User
                    $CurrentConnection = $UPAConnMgr | where {$_.AccountDomain -eq $ConnectionDomain}
                $PropertyMapping = $CurrentConnection.PropertyMapping

                                $userProfilePropertyManager = $UPAConfMgr.ProfilePropertyManager
                                $userProfileTypeProperties = $userProfilePropertyManager.GetProfileTypeProperties($userprofiletype)

                                #Creating core properties
                                $CoreProperties = $UPAConfMgr.ProfilePropertyManager.GetCoreProperties()                              

                    #####################
                    #Creating properties#
                    #####################

Write-Host "Creating user profile properties." -ForegroundColor green
                                $PropertyNodeList = $XmlDoc.Connections.Connection.PropertyCreations

                                #Checking if the servicecontext (the site specified in the  attribute
                                #in the parse file actually exist. If not, we cannot continue.
                                if (!$context) 
                                {
                                                Write-Host "There are entries in the  node in the parse file, but the  node contains a URL to a site that does not exist. Critical error, cannot continue." -ForegroundColor red
                                                Break
                                }
                                $userProfileSubTypeManager = [Microsoft.Office.Server.UserProfiles.ProfileSubtypeManager]::Get($context)
                                $userProfile = $userProfileSubTypeManager.GetProfileSubtype([Microsoft.Office.Server.UserProfiles.ProfileSubtypeManager]::GetDefaultProfileName($userprofiletype))
                                $userProfileProperties = $userProfile.Properties

        #$userProfileProperties.RemovePropertyByName("State");  
        #write-host -f red "deleted state"
        #break

                                foreach ($PropertyNode in $PropertyNodeList.ProfilePropertyCreation) 
                    {
            #write-host "Creating " $PropertyNode.InnerText
                                    if ($PropertyNode)
                                                {
               #$ProfilePropertyCreation.Privacy = "public"
                #$ProfilePropertyCreation.PrivacyPolicy = "option"

                                                                #Fetching information for the new property.   userOverridePrivacy="0" isReplicable="1"
                                                                $ProfilePropertyCreation = $PropertyNode#.SelectSingleNode("ProfilePropertyCreation")
                                                                $SharePointProp = $ProfilePropertyCreation.InnerText
                                                                $SharePointPropDisplayName = $ProfilePropertyCreation.DisplayName
                                                                $SharePointPropPrivacy = $ProfilePropertyCreation.Privacy.ToLower()
                                                                $SharePointPropPrivacyPolicy = $ProfilePropertyCreation.PrivacyPolicy.ToLower()
                                                                $SharePointPropType = $ProfilePropertyCreation.PropType.ToLower()
                                                                $SharePointPropLength = $ProfilePropertyCreation.PropLength
                                                                $SharePointPropIsVisibleOnEditor = $ProfilePropertyCreation.IsVisibleOnEditor
                                                                $SharePointPropIsVisibleOnViewer = $ProfilePropertyCreation.IsVisibleOnViewer
                                                                $SharePointPropDisplayOrder = $ProfilePropertyCreation.DisplayOrder
                                                                $SharePointPropIsEventLog = $ProfilePropertyCreation.IsEventLog
                                                                $SharePointPropIsUserEditable = $ProfilePropertyCreation.IsUserEditable
                                                                $SharePointUserOverrideprivacy = $ProfilePropertyCreation.UserOverridePrivacy  
                                                                $SharePointSectionName = $ProfilePropertyCreation.Section     

                                                                $SharePointPropDisplayOrder = [int]$SharePointPropDisplayOrder                                                                                                                         

                                                                #Basic syntax checking of $SharePointPropPrivacy attribute
                                                                $PrivacyMembers = "public", "contacts", "Organization", "Manager", "Private"
                                                                if (!($PrivacyMembers -contains $SharePointPropPrivacy))
                                                                {
                                                                                Write-Host "The new SharePoint property `"$SharePointProp`" contains an invalid privacy attribute ($SharePointPropPrivacy). The allowed entries are: public, contacts, Organization, Manager, Private. Skipping attribute." -ForegroundColor red
                                                                                Continue
                                                                }

                                                                #Basic syntax checking of $SharePointPropPrivacyPolicy attribute
                                                                $PrivacyPolicyMembers = "mandatory", "optin","optout", "disabled"
                                                                if (!($PrivacyPolicyMembers -contains $SharePointPropPrivacyPolicy))
                                                                {
                                                                                Write-Host "The new SharePoint property `"$SharePointProp`" contains an invalid privacy policy attribute ($SharePointPropPrivacyPolicy). The allowed entries are: mandatory, optin,optout, disabled. Skipping attribute $SharePointProp." -ForegroundColor red
                                                                                Continue
                                                                }

                                                                #Basic syntax checking of $SharePointPropType attribute
                                                                if ($SharePointPropType -ne "string")
                                                                {
                                                                                Write-Host "Currently the script only able to create `"String`" type of attributes. The attribute provided is: $SharePointPropType. Skipping attribute $SharePointProp." -ForegroundColor red
                                                                                Continue
                                                                }

                                                                #Basic syntax checking of $SharePointPropLength attribute
                                                                $loaderror = [Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
                                                                if ($loaderror -ne "Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
                                                                {
                                                                                $IsNumber = [Microsoft.VisualBasic.Information]::isnumeric($SharePointPropLength)

                                                                                if (!$IsNumber)
                                                                                {
                                                                                                Write-Host "The property length specified for this attribute is invalid. Skipping attribute $SharePointProp." -ForegroundColor red
                                                                                                Continue
                                                                                }
                                                                }

                                                                #Basic syntax checking of $SharePointPropLength attribute
                                                                $loaderror = [Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
                                                                if ($loaderror -ne "Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
                                                                {
                                                                                $IsNumber = [Microsoft.VisualBasic.Information]::isnumeric($SharePointPropDisplayOrder)

                                                                                if (!$IsNumber)
                                                                                {
                                                                                                Write-Host "The property length specified for this attribute is invalid. Skipping attribute $SharePointPropDisplayOrder." -ForegroundColor red
                                                                                                Continue
                                                                                }
                                                                }

                                                                if($SharePointPropIsVisibleOnEditor -eq "1")
                                                                {
                                                                    $SharePointPropIsVisibleOnEditor = $true
                                                                }
                                                                else
                                                                {
                                                                    $SharePointPropIsVisibleOnEditor = $false
                                                                }
                                                                if($SharePointPropIsVisibleOnViewer -eq "1")
                                                                {
                                                                    $SharePointPropIsVisibleOnViewer = $true
                                                                }
                                                                else
                                                                {
                                                                    $SharePointPropIsVisibleOnViewer = $false
                                                                }
                                                                if($SharePointPropIsEventLog -eq "1")
                                                                {
                                                                    $SharePointPropIsEventLog = $true
                                                                }
                                                                else
                                                                {
                                                                    $SharePointPropIsEventLog = $false
                                                                }
                                                                if($SharePointPropIsUserEditable -eq "1")
                                                                {
                                                                    $SharePointPropIsUserEditable = $true
                                                                }
                                                                else
                                                                {
                                                                    $SharePointPropIsUserEditable = $false
                                                                }
                                                                 if($SharePointUserOverrideprivacy -eq "1")
                                                                {
                                                                    $SharePointUserOverrideprivacy = $true
                                                                }
                                                                else
                                                                {
                                                                    $SharePointUserOverrideprivacy = $false
                                                                }                                                              

                                                                $sec = $userProfileProperties.GetSectionByName($SharePointSectionName);

                                                                $NewUPProperty = $userProfileProperties.GetPropertyByName($SharePointProp)   #sproperty

                                                                if($NewUPProperty -eq $null)
                                                                {
                               Write-Host "Creating " $sharepointpropdisplayname                                 

                                                                #Creating a NewProperty with basic attributes
                                                                $NewProperty = $CoreProperties.Create($false)
                                                                $Newproperty.Name = $SharePointProp
                                                                $NewProperty.DisplayName = $SharePointPropDisplayName
                                                                $NewProperty.Type = $SharePointPropType
                                                                $NewProperty.Length = $SharePointPropLength

                            #Write-Host $sharepointpropdisplayname 

                                                                #Adding the new property to the core properties list
                                                                $CoreProperties.Add($NewProperty)

                                                                #Reinitializing the newly created property to change secondary attributes.
                                                                $NewTypeProperty = $userProfileTypeProperties.Create($NewProperty)                                                                

                                                                #Display attributes
                                                                $NewTypeProperty.IsVisibleOnEditor = $SharePointPropIsVisibleOnEditor
                                                                $NewTypeProperty.IsVisibleOnViewer = $SharePointPropIsVisibleOnViewer 
                                                                $NewTypeProperty.IsEventLog = $SharePointPropIsEventLog

                                                                #Updating the new property's secondary attributes
                                                                $userProfileTypeProperties.Add($NewTypeProperty)

                                                                #Reinicializing the newly created property to privacy attributes.
                                                                $NewSubProperty = $userProfileProperties.Create($NewTypeProperty)
                                                                $NewSubProperty.DefaultPrivacy = [Microsoft.Office.Server.UserProfiles.Privacy]::$SharePointPropPrivacy
                                                                $NewSubProperty.PrivacyPolicy = [Microsoft.Office.Server.UserProfiles.PrivacyPolicy]::$SharePointPropPrivacyPolicy

                                                                $NewSubProperty.IsUserEditable = $SharePointPropIsUserEditable

                                                                $NewSubProperty.UserOverridePrivacy = $SharePointUserOverrideprivacy

                                                                #Finalizing the new property.
                                                                $userProfileProperties.Add($NewSubProperty)

                                                                }
                                                                else
                                                                {

                                                        Write-Host "Updating " $sharepointpropdisplayname     

                                                                $NewUPTypeProperty = $userProfileTypeProperties.GetPropertyByName($SharePointProp)

                                                                $NewUPTypeProperty.CoreProperty.DisplayName = $SharePointPropDisplayName    

                                                                $NewUPTypeProperty.IsVisibleOnViewer = $SharePointPropIsVisibleOnViewer 
                                                                $NewUPTypeProperty.IsVisibleOnEditor = $SharePointPropIsVisibleOnEditor
                                                                $NewUPTypeProperty.IsEventLog = $SharePointPropIsEventLog

                                                                $NewUPProperty.DefaultPrivacy = [Microsoft.Office.Server.UserProfiles.Privacy]::$SharePointPropPrivacy
                                                                $NewUPProperty.PrivacyPolicy = [Microsoft.Office.Server.UserProfiles.PrivacyPolicy]::$SharePointPropPrivacyPolicy

                                                                $NewUPProperty.IsUserEditable = $SharePointPropIsUserEditable                                                                
                                                                $NewUPProperty.UserOverridePrivacy = $SharePointUserOverrideprivacy

                                                                $NewUPTypeProperty.CoreProperty.Commit()
                                                                $NewUPProperty.Commit()
                                                                $NewUPTypeProperty.Commit()

                                                                }

                                                                $err = $userProfileProperties.GetEnumerator()

                                                                #Setting the display order
                                                                if($ProfilePropertyCreation.DisplayOrder -ne "0")
                                                                {
                                                                    $userProfileProperties.SetDisplayOrderByPropertyName($SharePointProp,$sec.DisplayOrder + $SharePointPropDisplayOrder)
																	Write-Host "    Set user profile property `"$SharePointProp`"." " with display order $SharePointPropDisplayOrder +" $sec.DisplayOrder                                                     
                                                                }

                                                                Write-Host "    User profile property `"$SharePointProp`"." " added or updated"
                                                }
                                                else
                                                {
                                                                Write-Host "There are no profiles to be created in the parse file."
                                                }
                                }

                                $userProfileProperties.CommitDisplayOrder()

The input file format looks like this -

<?xml version="1.0" encoding="utf-8"?>
<Connections>
  <Connection name="Test">
    <PropertyCreations>
      <ProfilePropertyCreation DisplayName="Education" Privacy="public" PrivacyPolicy="optin" UserOverridePrivacy="1" PropType="string" PropLength="25" IsUserEditable="1" IsVisibleOnEditor="1" IsVisibleOnViewer="1" IsEventLog="1" DisplayOrder="0" Section="SPS-Section-Details">SPS-School</ProfilePropertyCreation>
    </PropertyCreations>
  </Connection>
</Connections>

The attributes in the input file are pretty direct and have a direct correlation to the attributes found in central admin. The extra attributes to note here are “DisplayOrder” and “Section”. These are used for ordering the attribute. “Section” refers to the section name which will contain our user profile property and “Order” refers to the position of our attribute inside the specified section.

To re-order a section, use a script like this-

 $sec = $userProfileProperties.GetSectionByName("SPS-Section-ContactInfo");  
 Write-Host $sec.DisplayOrder;
 $userProfileProperties. SetDisplayOrderBySectionName("MySection",$sec.DisplayOrder+12)

This script fetches the order of the “Contact Info” section and adds a custom section named “MySection” (created earlier) after that.

Also, please note that the above script currently works only for string type properties but it can be very easily modified to accomodate all kinds of properties. Also, it doesnot map the properties to AD props.

Managed Navigation(or Term based navigation) in SharePoint 2013 – How manageable is it and how to configure global navigation across multiple site collections

Managed navigation is introduced in SharePoint 2013. It provides a great way to build and manage navigation of a site collection and completely decouples the navigation from the actual site structure. The following post explains nicely how to configure navigation -

Term based navigation in SharePoint 2013

Now, coming to the manageability part, you can manage navigation for a site collection very easily by simply managing its navigation termset.

Unfortunately, all these bells and whistles are only limited to a particular site collection. SharePoint 2013 allows a termset to be associated only to a single site collection for navigation. So, if you want to have a consistent/same navigation across multiple site collections, there is no direct way to implement and manage it. Let us analyse the indirect ways in detail :

1) Create a master navigation termset with all the required navigation terms in the proper order. Associate it to the site collection to provide the navigation structure. For any other site collection which needs the same navigation, create a new navigation termset (dont add any new terms) and reuse/pin all the terms from the master navigation termset. Follow the same process for all site collections that need the same navigation.

Note : Add the url to each term as “Simple Link or Header”, else in many scenarios, it will not appear for other site collections.

Let us discuss the pros and cons of this approach -

Pros

a) Any name/property changes (except some – mentioned in cons) to terms in master termset will be reflected across all other termsets with reused/pinned terms.
b) The term GUIDs in reused term sets are same as the master termset. This will be helpful to manage them via code/powershell.

Cons

a) Since, you need to create a separate termset for each site collection, the no of termsets may be huge.
b) The custom order of terms and child terms in master termset is not propagated to other termsets even though they reuse the same terms.
c) If a navigation url is changed for a term in master termset, it also doesnot get propagated to other termsets.

You can write a powershell to create terms, reuse terms in other termsets, set the term order and also for propagation of order to reused terms, association of termset to site collection etc. This will ease a lot of burden. I will share a script very soon.

2) Create a completely custom navigation provider which uses the termset as the source for links and builds the navigation by itself. I will share a sample soon.

3) Write a custom javascript to workaround these issues to a good extent. A nice sample is available in the following blog -

Building global navigation in SharePoint 2013

Narahari

Using Like and Rating functionality in SharePoint 2013 Pages (and SP2013 Custom like button)

Wow!!! SharePoint 2013 includes Like and Rating functionality out of the box. Just navigate to List/Library Settings -> Rating settings and set it up. It is that easy :-) .

Library Settings

However, you can only like/unlike or rate a item/page in the “All Items” view of the list/library. So, If you want to show this flashy “Like” functionality to your users on different publishing pages of your site, there is no out of the box way to do it. In this post, I will explain a simple way to add a custom like button to your publishing pages by using SharePoint’s built in Javascript methods. I will also give pointers on how to add a rating button to your page.

Like Button

(In the image above, I have changed the image for the Like Button to display a Thumbs up instead of a smiley.)

In a nutshell, the process goes like this. Create/modify the page layout/add a content editor used for the content pages of your site to add a little html. Then enable the liking settings on the library (mostly Pages Library) where the content/publishing pages are stored. Reference a small Javascript code in your masterpage/page layout and add some css to style your Like button. Finally, deploy the custom javascript file to SharePoint.

Now, let us understand the process in detail.

1. Create/modify the page layout or add :

Page Layout provides an excellent way to maintain a consistent and desirable look and feel for your content pages in the site. Most of the publishing sites use custom page layouts. If you do not use custom Page Layouts, don’t worry. You can still do this by adding a simple content editor webpart/script editor to your pages and adding the same code inside that. Add the following html code to your Page layout/content editor.

<div class="LikeSection"><span class="likecount"></span><a href="#" onclick="LikePage()" class="LikeButton"></a></div>

Explanation : In the above html code, the anchor tag will be our Like/Unlike button and the span will display the no of likes for the current page. I have added classes to all these elements and enclosed them in a div so that you can give your own look and feel to the like section :-) .

2. Enable liking settings on the library -

Navigate to library settings -> Rating Settings. Select the option “Yes” for “Allow items in this list to be rated” and select “Likes” for “Which voting/rating experience you would like to enable for this list”.

Explanation : Once you enable liking, SharePoint will add two columns to the list named “LikedBy” and “LikesCount”. As the names suggest, “LikedBy” will store the user identities who liked the item and “LikesCount” will store the no of likes respectively. This step builds the SP liking infrastructure for you.

3. Create the javascript file -

Create a javascript file named Like.js and paste the following code into it.

function LikePage() {
    var like = false;
    var likeButtonText = $("a.LikeButton").text();
    if (likeButtonText != "") {
        if (likeButtonText == "Like")
            like = true;

        var aContextObject = new SP.ClientContext();
        EnsureScriptFunc('reputation.js', 'Microsoft.Office.Server.ReputationModel.Reputation', function () {
            Microsoft.Office.Server.ReputationModel.
            Reputation.setLike(aContextObject,
                _spPageContextInfo.pageListId.substring(1, 37),
                _spPageContextInfo.pageItemId, like);

            aContextObject.executeQueryAsync(
                function () {
                    //alert(String(like));
                    GetLikeCount();
                }, function (sender, args) {
                    //alert('F0');
                });
        });
    }

}

function GetLikeCount() {

    var context = new SP.ClientContext(_spPageContextInfo.webServerRelativeUrl);
    var list = context.get_web().get_lists().getById(_spPageContextInfo.pageListId);
    var item = list.getItemById(_spPageContextInfo.pageItemId);

    context.load(item, "LikedBy", "ID", "LikesCount");
    context.executeQueryAsync(Function.createDelegate(this, function (success) {
        // Check if the user id of the current users is in the collection LikedBy. 
        var likeDisplay = true;
        var $v_0 = item.get_item('LikedBy');
        var itemc = item.get_item('LikesCount');
        if (!SP.ScriptHelpers.isNullOrUndefined($v_0)) {
            for (var $v_1 = 0, $v_2 = $v_0.length; $v_1 &lt; $v_2; $v_1++) {
                var $v_3 = $v_0[$v_1];
                if ($v_3.$1E_1 === _spPageContextInfo.userId) {
                    //cb(true, item.get_item('LikesCount'));
                    //alert("Liked by me");
                    likeDisplay = false;
                }
            }
        }
        ChangeLikeText(likeDisplay, itemc);

    }), Function.createDelegate(this, function (sender, args) {
        //alert('F1');
    }));

}

function ChangeLikeText(like, count) {
    if (like) {
        $("a.LikeButton").text('Like');
    }
    else {
        $("a.LikeButton").text('Unlike');
    }
    var htmlstring = "<img alt="" src="/_layouts/15/images/LikeFull.11x11x32.png" />" + " " + String(count);
    if (count &gt; 0)
        $(".likecount").html(htmlstring)
    else
        $(".likecount").html("");
}

$(document).ready(function () {
    GetLikeCount();
    $("a.LikeButton").click(function () {
        LikePage();
    });
});

Explanation : In our Like.js JS file, we have 4 methods -

LikePage() – This function calls the out of the box SharePoint method named “Microsoft.Office.Server.ReputationModel.Reputation.setLike” present in the “Reputation.js” javascript file (present inside Layouts folder). To see the function clearly, open the readable version of the file called “Reputation.debug.js”. This setLike function takes the parameters – context, listId, itemId and like(bool). We get the current context very easily using the SP.ClientContext(). Then we use the properties of the magical variable “_spPageContextInfo” to get the list ID and item ID respectively. Finally we pass the 4th parameter which is boolean to like or unlike a page.

GetLikeCount() – This function queries the list to get the “LikesCount” for the current list item (in our case, current page). It also checks the field “LikedBy” to see if the current user’s id is present in it to determine whether the current user has liked the page or not. Then it passes these values to the ChangeLikeText function.

ChangeLikeText() – This function displays the total likes for the current page and changes the text of the anchor tag to Like or Unlike based on whether the current user has liked the page or not.

Finally, in the document ready function, we display the like count and add the click handler to the like button.

4. Reference JS and css files -

If you have many content pages, just reference the SharePoint JS files in your masterpage as shown below. You can also reference them in your Page Layout if you wish to.

<SharePoint:ScriptLink ID="ScriptLink6" name="SP.js" runat="server" ondemand="false" localizable="false" loadafterui="true" />
<SharePoint:ScriptLink ID="ScriptLink8" name="SP.Core.js" runat="server" ondemand="false" localizable="false" loadafterui="true" />
<SharePoint:ScriptLink ID="ScriptLink9" name="Reputation.js" runat="server" ondemand="false" localizable="false" loadafterui="true" />
<script src="/_layouts/15/InSite/Scripts/Like.js" type="text/javascript"></script>

As you can see, the last one is a link to our like.js file which will do all the magic for us.

Add the following css to your global css file or inside PageLayouts

.likecount {
margin-right:5px;
}

.LikeSection {
margin-bottom:15px;
}

5. Deploy Like.js -

You can deploy this into any custom folder inside layouts folder or into a library. Accordingly, change the path to like.js file in our step 4.

Similarly, if you want to add rating control to your page, leverage the function “Microsoft.Office.Server.ReputationModel.Reputation.SetRating” instead of “SetLike” in out custom javascript file and change the logic slighly to display the rating stars instead of Like controls.