<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Jason Vallery's SharePoint Notes</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/javaller/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/javaller/feed.xml</link>
    <description />
    <lastBuildDate>Tue, 16 Apr 2019 05:12:36 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Why isn't my ADFS SSL certificate updating?</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/why-isnt-my-adfs-ssl-certificate-updating</link>
      <pubDate>Wed, 18 Jun 2014 13:45:00 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2014/06/18/why-isnt-my-adfs-ssl-certificate-updating/</guid>
      <description><![CDATA[After spending more time on this then I care to admit I figured I'd write this up on my...]]></description>
      <content:encoded><![CDATA[<p>After spending more time on this then I care to admit I figured I'd write this up on my blog.&nbsp;&nbsp; The SSL certificate expired in one of my ADFS for Windows Server 2012R2 environments.&nbsp;&nbsp; Logically&nbsp;you would expect that you can do this via the ADFS management console and clicking "Set Service Communication Certificate".&nbsp;&nbsp;&nbsp; I set the&nbsp;cert and restarted ADFS however&nbsp;users were still getting the original expired cert.&nbsp;&nbsp;&nbsp;</p>
<p>&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/60/59/3051.adfsmmc.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-60-59/3051.adfsmmc.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/60/59/3051.adfsmmc.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-60-59/3051.adfsmmc.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<p>After a bit of digging I uncovered the Get-AdfsSslCertificate and Set-AdfsSslCertificate.&nbsp;&nbsp;&nbsp; Sure enough when I executed Get-AdfsSslCertificate the thumbprint of the original expired certificate was returned.&nbsp;&nbsp; This is because the change in the ADFS management console only updates the ADFS configuration database.&nbsp;&nbsp;&nbsp; It does not change the binding in HTTP.SYS.&nbsp;&nbsp;&nbsp; Since 2012R2 has no IIS dependency there is no user interface to manage the SSL certificate binding.&nbsp;&nbsp; As a result we must use powershell to do it.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/60/59/6114.adfspowershell.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-60-59/6114.adfspowershell.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/60/59/6114.adfspowershell.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-60-59/6114.adfspowershell.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<p>Relevant TechNet link: <a href="https://technet.microsoft.com/en-us/library/dn479374.aspx">https://technet.microsoft.com/en-us/library/dn479374.aspx</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Publishing CRM Internet Facing Deployment using Web Application Proxy and</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/publishing-crm-internet-facing-deployment-using-web-application-proxy-and</link>
      <pubDate>Mon, 13 Jan 2014 17:29:00 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2014/01/13/publishing-crm-internet-facing-deployment-using-web-application-proxy-and/</guid>
      <description><![CDATA[If you've been following the TMG/UAG product line for a while you'll know that those productsare end...]]></description>
      <content:encoded><![CDATA[<p>If you've been following the TMG/UAG product line for a while you'll know that those products<br />are end of life.&nbsp;&nbsp; The official announcement was recently made on the Server &amp; Cloud Blog &lt;<a href="https://blogs.technet.com/b/server-cloud/archive/2013/12/17/important-changes-to-the-forefront-product-line.aspx">https://blogs.technet.com/b/server-cloud/archive/2013/12/17/important-changes-to-the-forefront-product-line.aspx</a>&gt;</p>
<p>I've been working on deploying Web Application Proxy with one of my customers recently in an effort to transition them off UAG.&nbsp;&nbsp; We were successful in getting SharePoint working quickly but we ran into some problems with CRM.&nbsp;&nbsp; Given how new WAP is I thought it would be appropriate to document what we had to do in order to get it working.</p>
<p>The customer has 3 externally facing URLs that we need to publish via WAP:</p>
<p><a href="https://auth.crm.contoso.com/">https://auth.crm.contoso.com/</a></p>
<p><a href="https://org.crm.contoso.com/">https://org.crm.contoso.com/</a></p>
<p><a href="https://discovery.crm.contoso.com/">https://discovery.crm.contoso.com/</a></p>
<p>&nbsp;</p>
<p>Additionally WAP and ADFS are published at</p>
<p><a href="https://fs.contoso.com/">https://fs.contoso.com/</a></p>
<p>&nbsp;</p>
<p>The default configuration of WAP is to translate internal URLs to external URLs when you do pass-through authentication.&nbsp;&nbsp; We had configured the org URL as ADFS pre-auth.&nbsp; The discovery and auth URLs both used pass-through authentication.&nbsp;&nbsp;&nbsp; The<br />external and internal names were the same and we used split DNS to handle internal versus external traffic.</p>
<p>The way CRM does authentication when using an internet facing deployment&nbsp;and ADFS is with the HTTP POST binding back to the auth URL.&nbsp;&nbsp; The auth URL issues a DOMAIN cookie and redirects to the org URL.&nbsp; In our case the domain cookie was scoped to *.crm.contoso.com.&nbsp;&nbsp; The org web application gets the cookie and lets the user in.&nbsp;</p>
<p>This breaks down with WAP because it attempts to remap the outbound cookie from the auth web application and reissue it under the external name (auth.crm.contoso.com).&nbsp;&nbsp;&nbsp; When we redirect to the org URL it can't see the cookie because the browser doesn't pass it back.&nbsp;&nbsp; The org URL then redirects back to ADFS, which redirects to auth, which reissues the cookie.&nbsp;&nbsp; As you can see we're stuck in an infinite redirect loop.&nbsp;&nbsp; Eventually ADFS gets tried of this and fails issuing the following error in the log:</p>
<p>&nbsp;</p>
<p>Microsoft.IdentityServer.Web.InvalidRequestException: MSIS7042: The same client browser session has made '6' requests in the last '3'<br />seconds. Contact your administrator for details.</p>
<p>&nbsp;&nbsp; at<br />Microsoft.IdentityServer.Web.Protocols.PassiveProtocolHandler.UpdateLoopDetectionCookie(WrappedHttpListenerContext<br />context)</p>
<p>&nbsp;&nbsp; at<br />Microsoft.IdentityServer.Web.Protocols.WSFederation.WSFederationProtocolHandler.SendSignInResponse(WSFederationContext<br />context, MSISSignInResponse response)</p>
<p>&nbsp;&nbsp; at<br />Microsoft.IdentityServer.Web.PassiveProtocolListener.ProcessProtocolRequest(ProtocolContext<br />protocolContext, PassiveProtocolHandler protocolHandler)</p>
<p>&nbsp;&nbsp; at<br />Microsoft.IdentityServer.Web.PassiveProtocolListener.OnGetContext(WrappedHttpListenerContext<br />context)</p>
<p>&nbsp;</p>
<p>The solution for this is to tell Web Application Proxy not to translate the outbound URLs in the response header.&nbsp;&nbsp; This can be done via powershell and the Set-WebApplicationProxyApplication cmdlet.</p>
<p>&nbsp;</p>
<p><a href="https://technet.microsoft.com/en-us/library/dn283415.aspx">https://technet.microsoft.com/en-us/library/dn283415.aspx</a></p>
<p>&nbsp;</p>
<pre class="scroll"><code class="csharp"><br /> <br /> Set-WebApplicationProxyApplication -ID &lt;id of auth web app&gt; -DisableTranslateUrlInResponseHeaders<br /> </code></pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Now WAP will ignore the outbound cookie and let it pass along to the client as CRM had intended.&nbsp;&nbsp; Presto!</p>]]></content:encoded>
    </item>
    <item>
      <title>Using System.DirectoryServices to add, remove, and verify group membership</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/using-system-directoryservices-to-add-remove-and-verify-group-membership</link>
      <pubDate>Fri, 01 Nov 2013 15:24:00 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2013/11/01/using-system-directoryservices-to-add-remove-and-verify-group-membership/</guid>
      <description><![CDATA[I've had several customers who have built SharePoint solutions to manage domain group membership in...]]></description>
      <content:encoded><![CDATA[<p>I've had several customers who have built SharePoint solutions to manage domain group membership in lieu of rolling out full on Forefront Identity Manager.&nbsp;&nbsp; I'm not one to debate the decision as to if SharePoint is the right tool or not but I do have occasion to support them on their endeavors.&nbsp;</p>
<p>There are several ways to skin this cat in .NET.&nbsp;&nbsp; You could use the System.DirectoryServices.AccountManagement (<a href="https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement(v=vs.110).aspx">https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement(v=vs.110).aspx)</a>&nbsp; namespace.&nbsp; It was introduced in .NET 3.5 so you can use it with SharePoint 2010 full trust code.</p>
<p>I'm a big fan of doing things the old fashioned way so to speak using System.DirectoryServices (<a href="https://msdn.microsoft.com/en-us/library/system.directoryservices(v=vs.110).aspx">https://msdn.microsoft.com/en-us/library/system.directoryservices(v=vs.110).aspx)</a>.&nbsp;&nbsp; That stuff has been around since .NET 1.1.&nbsp;&nbsp; By using ADSI you can query via LDAP and manually manipulate the DirectoryEntry objects.&nbsp;&nbsp; I find this solution just makes more sense to me.&nbsp; It also is transportable and if for some reason you weren't using Active Directory and instead you were using some other LDAP provider your code would still work.</p>
<p>To that end I created a simple helper class that can be used to add, remove, and verify group membership for a user.&nbsp;&nbsp; Here is a class you can use followed by an example of how to utilize it from a console app.</p>
<pre class="scroll"><code class="csharp">using System;<br /> using System.DirectoryServices;<br /> <br /> namespace ADHelper<br /> {<br /> public class ADHelper<br /> {<br /> <br /> private string username;<br /> private string password;<br /> public string domain;<br /> <br /> public ADHelper(string username, string password, string domain)<br /> {<br /> this.username = username;<br /> this.password = password;<br /> this.domain = domain;<br /> }<br /> <br /> //Looks up the DN for a user based on the sAMAccountName<br /> public string GetUserDN(string identity)<br /> {<br /> //Split out the identity to get just the username portion<br /> if (identity.Contains("\\"))<br /> {<br /> string[] identityList = identity.Split('\\'); <br /> string userName = identityList[1];<br /> using (var rootEntry = new DirectoryEntry("LDAP://" + domain, this.username, this.password, AuthenticationTypes.Secure))<br /> {<br /> using (var directorySearcher = new DirectorySearcher(rootEntry, String.Format("(sAMAccountName={0})", userName)))<br /> {<br /> var searchResult = directorySearcher.FindOne();<br /> if (searchResult != null)<br /> {<br /> using (var userEntry = searchResult.GetDirectoryEntry())<br /> {<br /> return (string)userEntry.Properties["distinguishedName"].Value;<br /> }<br /> }<br /> }<br /> }<br /> }<br /> return null;<br /> }<br /> <br /> <br /> //Looks up the DN for the group based on the name<br /> public string GetGroupDN(string groupname)<br /> {<br /> using (var rootEntry = new DirectoryEntry("LDAP://" + domain, this.username, this.password, AuthenticationTypes.Secure))<br /> {<br /> using (var directorySearcher = new DirectorySearcher(rootEntry, String.Format("(&amp;(objectCategory=group)(cn={0}))", groupname)))<br /> {<br /> var searchResult = directorySearcher.FindOne();<br /> if (searchResult != null)<br /> {<br /> using (var userEntry = searchResult.GetDirectoryEntry())<br /> {<br /> return (string)userEntry.Properties["distinguishedName"].Value;<br /> }<br /> }<br /> }<br /> }<br /> return null;<br /> }<br /> <br /> <br /> <br /> //Checks to see if a user is in a group and returns a bool<br /> public bool isUserInGroup(string userDn, string groupDn)<br /> {<br /> bool match = false;<br /> <br /> <br /> DirectoryEntry user = new DirectoryEntry("LDAP://" + userDn, this.username, this.password, AuthenticationTypes.Secure);<br /> foreach (string group in user.Properties["memberof"])<br /> if (group == groupDn)<br /> match = true;<br /> <br /> return match;<br /> }<br /> <br /> <br /> //Adds a given user to a group <br /> public bool AddToGroup(string userDn, string groupDn)<br /> {<br /> bool success = false;<br /> try<br /> {<br /> DirectoryEntry dirEntry = new DirectoryEntry("LDAP://" + groupDn, this.username, this.password, AuthenticationTypes.Secure);<br /> dirEntry.Properties["member"].Add(userDn);<br /> dirEntry.CommitChanges();<br /> dirEntry.Close();<br /> success = true;<br /> }<br /> catch (System.DirectoryServices.DirectoryServicesCOMException ex)<br /> {<br /> success = false;<br /> }<br /> <br /> return success;<br /> }<br /> <br /> //Removes a user from a group<br /> public bool RemoveUserFromGroup(string userDn, string groupDn)<br /> {<br /> bool success = false;<br /> try<br /> {<br /> DirectoryEntry dirEntry = new DirectoryEntry("LDAP://" + groupDn);<br /> dirEntry.Properties["member"].Remove(userDn);<br /> dirEntry.CommitChanges();<br /> dirEntry.Close();<br /> success = true;<br /> }<br /> catch (System.DirectoryServices.DirectoryServicesCOMException ex)<br /> {<br /> <br /> success = false;<br /> }<br /> return success;<br /> }<br /> }<br /> }</code></pre>
<pre class="scroll"><code class="csharp"></code><code class="csharp"> namespace ADHelper<br /> {<br /> class Program<br /> {<br /> static void Main(string[] args)<br /> {<br /> <br /> string username = "jason";<br /> string password = "supersecretpassword";<br /> string domain = "home.vallery.net";<br /> <br /> //Create new helper object with the username and password of service account<br /> ADHelper helper = new ADHelper(username, password, domain);<br /> <br /> //Lookup the DN of our user<br /> string userDN = helper.GetUserDN("home\\jason");<br /> <br /> //Lookup the DN of our group<br /> string groupDN = helper.GetGroupDN("AWESOME PEOPLE GROUP");<br /> <br /> <br /> //Determine if our user is in the group<br /> Console.WriteLine(string.Format("Is {0} a member of {1}: {2}", userDN, groupDN, helper.isUserInGroup(userDN, groupDN).ToString()));<br /> <br /> //Add our user to the group<br /> helper.AddToGroup(userDN, groupDN);<br /> <br /> //Determine if our user is in the group<br /> Console.WriteLine(string.Format("Is {0} a member of {1}: {2}", userDN, groupDN, helper.isUserInGroup(userDN, groupDN).ToString()));<br /> <br /> //Remove user from the group<br /> helper.RemoveUserFromGroup(userDN, groupDN);<br /> <br /> //Determine if our user is in the group<br /> Console.WriteLine(string.Format("Is {0} a member of {1}: {2}", userDN, groupDN, helper.isUserInGroup(userDN, groupDN).ToString()));<br /> <br /> Console.ReadLine();<br /> <br /> }<br /> }<br /> }</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>InfoPath and SharePoint auto number form ID using ItemAdded event receiver</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/infopath-and-sharepoint-auto-number-form-id-using-itemadded-event-receiver</link>
      <pubDate>Thu, 19 Sep 2013 10:50:00 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2013/09/19/infopath-and-sharepoint-auto-number-form-id-using-itemadded-event-receiver/</guid>
      <description><![CDATA[InfoPath combined with SharePoint is a fantastic way to manage forms in an organization.&nbsp;&nbsp;...]]></description>
      <content:encoded><![CDATA[<p>InfoPath combined with SharePoint is a fantastic way to manage forms in an organization.&nbsp;&nbsp; One challenge though that comes up quite often is how to uniquely assign each submitted form an identifier.&nbsp;&nbsp; There are various solutions that have been blogged about for this problem including using a timestamp, or querying the list service and getting the MAX value of the ID column.&nbsp;&nbsp; All of these have one particular drawback however.&nbsp;&nbsp; The ID is assigned by the client and not the server.&nbsp;&nbsp; Because the form is responsible for the naming these solutions can all fail in high concurrency situations.&nbsp;&nbsp; You either end up with forms with nonsense names by concatenating a bunch of fields (time, date, name of user) or you use the MAX(ID) approach and risk 2 forms getting submitted with the same ID at the same time and creating a duplicate situation.</p>
<p>My customer recently faced this problem and asked me for a solution.&nbsp;&nbsp; They wanted a simple integer ID that was incremented whenever a form was submitted to SharePoint.&nbsp; Nothing fancy.&nbsp;&nbsp; At first we investigated using the list service to query for the MAX(ID) and using that number.&nbsp;&nbsp; We quickly ran into the concurrency situation though and had duplicate forms.&nbsp;&nbsp; I decided to take matters into my own hand and find an infallible solution once and for all.</p>
<p>The approach we took was to use an ItemAdded event receiver on the forms library.&nbsp;&nbsp; Under the hood a forms library is no different than any other SPList and the form is no different than any other SPListItem.&nbsp;&nbsp; The SPListItem object already has a property called ID which is a unique auto sequential ID.&nbsp;&nbsp; The challenge is renaming the file to match the ID and populating that in the form. &nbsp;I used LINQ to XML to change the contents of the submitted form and store the newly assigned ID on save. &nbsp;&nbsp;</p>
<p>Once you have the ID in the form you can use it to populate the form name for subsequent saves in case a user makes an update.</p>
<p>&nbsp;If you need a basic overview on how to implement and bind an event receiver here is a good tutorial: <a href="https://msdn.microsoft.com/en-us/library/gg749858(v=office.14).aspx">https://msdn.microsoft.com/en-us/library/gg749858(v=office.14).aspx</a></p>
<p>For a refresher on how to configure your InfoPath form to submit to SharePoint and to pull in our FormID here is a good tutorial: <a href="https://office.microsoft.com/en-001/infopath-help/submit-form-data-to-a-sharepoint-library-HA010107057.aspx">https://office.microsoft.com/en-001/infopath-help/submit-form-data-to-a-sharepoint-library-HA010107057.aspx</a></p>
<p>&nbsp;</p>
<p>Here is the complete code for the ItemAdded event receiver.</p>
<p>&nbsp;</p>
<pre class="scroll"><code class="csharp">using System;<br /> using System.IO;<br /> using System.Text;<br /> using System.Linq;<br /> using System.Xml.Linq;<br /> using Microsoft.SharePoint;<br /> using Microsoft.SharePoint.Administration;<br /> <br /> namespace EventReceivers<br /> {<br /> public class AutoNumberFormID : SPItemEventReceiver<br /> {<br /> public override void ItemAdded(SPItemEventProperties properties)<br /> {<br /> base.ItemAdded(properties);<br /> try<br /> {<br /> //Turn off event firing so we don't end up with a conflict <br /> base.EventFiringEnabled = false;<br /> <br /> //Populate our current user so we can keep track of it<br /> SPUser user = properties.Web.CurrentUser;<br /> <br /> //Run the code elevated so we can rename our item<br /> SPSecurity.RunWithElevatedPrivileges(delegate()<br /> {<br /> using (SPSite site = new SPSite(properties.Web.Site.ID))<br /> {<br /> using (SPWeb web = site.OpenWeb(properties.Web.ID))<br /> {<br /> //Grab a new copy of our list and listitem under the new security context<br /> SPList list = web.Lists[properties.List.ID];<br /> SPListItem item = list.Items.GetItemById(properties.ListItemId);<br /> <br /> //Change name and title to match item ID<br /> item.File.CheckOut();<br /> item["Title"] = item.ID;<br /> item["Name"] = item.ID;<br /> <br /> //System update instead of update so we don't add a new version or update the modified dates<br /> item.SystemUpdate(false);<br /> item.File.CheckIn("Changed name");<br /> <br /> //Get a fresh copy of the object to ensure we don't end up with a conflict<br /> item = list.Items.GetItemById(properties.ListItemId);<br /> <br /> //Read contents of XML and update the FormID field to match the ID of the document <br /> SPFile file = item.File;<br /> <br /> //Load the XML byte array and then convert it to a string using default encoding <br /> string xml = Encoding.Default.GetString(file.OpenBinary());<br /> <br /> //Parse the XML into a LINQ compatible XDocument<br /> XDocument doc = XDocument.Parse(xml);<br /> <br /> //We must specify the namespace that InfoPath uses for the "My" fields<br /> XNamespace myNamespace = "https://schemas.microsoft.com/office/infopath/2003/myXSD/2013-09-18T16:52:38";<br /> <br /> //Using LINQ to XML we query the document for all instances of a element named "FormID" <br /> var fields = from field in doc.Root.Descendants(myNamespace + "FormID") select field;<br /> <br /> //Loop through the results and set the value of that element based on the ID of the current item<br /> foreach (XElement field in fields)<br /> field.Value = item.ID.ToString();<br /> <br /> //If we made any changes to the document we convert back to a byte array and save it into the file object<br /> if (fields.Count() &gt; 0)<br /> file.SaveBinary(Encoding.Default.GetBytes(doc.ToString()));<br /> <br /> //Check out, push to DB, check in the file<br /> file.CheckOut();<br /> file.Update();<br /> file.CheckIn("Changed name and ID");<br /> <br /> }<br /> }<br /> });<br /> }<br /> catch (Exception ex)<br /> {<br /> //Log an errors to the ULS<br /> SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("FBMS Event Reciever", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, String.Format("[FBMS Item Level Permissions] Unexpected exception on item added. Error: {0}", ex.Message), null);<br /> }<br /> finally<br /> {<br /> //Turn event firing back on<br /> base.EventFiringEnabled = true;<br /> }<br /> }<br /> <br /> }<br /> }<br /> </code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Searching across active directory domains in PowerShell</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/searching-across-active-directory-domains-in-powershell</link>
      <pubDate>Mon, 29 Jul 2013 18:02:00 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2013/07/29/searching-across-active-directory-domains-in-powershell/</guid>
      <description><![CDATA[I needed to locate the LDAP distinguished name of an individual user account in a remote domain via...]]></description>
      <content:encoded><![CDATA[<p>I needed to locate the LDAP distinguished name of an individual user account in a remote domain via PowerShell.&nbsp;&nbsp; Assuming your script is running on a box that is part of a domain that has a trust to the remote domain we can do this by running a query against Active Directory with LDAP.</p>
<p>By using the DirectorySearcher class we can build complex LDAP queries to find objects in Active Directory.&nbsp;&nbsp; With this information you can do all kinds of fun scripting things.</p>
<p>Here is a sample script:</p>
<p>&nbsp;</p>
<pre class="scroll"><code class="csharp">#Specify the search criteria<br /> $samname="jasonv"<br /> $domain="dev.lcl"<br /> <br /> #Get a list of domains in the forest and grab the DN of the one matching the above parameter.<br /> $forest= [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()<br /> $domain= $forest.Domains | ? {$_.Name -eq$domain}<br /> $domainDN=$domain.GetDirectoryEntry().distinguishedName <br /> Write-Output "Found the remote domain, the full LDAP distinguished name is $DomainDN"<br /> <br /> #Create an LDAP searcher object and pass in the DN of the domain we wish to query<br /> <br /> $Searcher=New-Object System.DirectoryServices.DirectorySearcher([ADSI]"LDAP://$domainDN")<br /> <br /> #Pass in the ceriteria we are searching for.<br /> #In this case we're looking for users with a particular SAM name.<br /> <br /> $Searcher.filter="(&amp;(objectCategory=person)(objectClass=user)(sAMAccountName= $samname))"<br /> <br /> $results=$Searcher.Findall()<br /> <br /> #Loop through the results<br /> Foreach($result in $results){<br /> $User=$result.GetDirectoryEntry()<br /> $userDN=$user.DistinguishedName<br /> Write-Output "Found a user matching with the distingused name of $userDN"<br /> }<br /> </code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Enumerate user profile properties in SharePoint 2010 using PowerShell</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/enumerate-user-profile-properties-in-sharepoint-2010-using-powershell</link>
      <pubDate>Mon, 29 Jul 2013 17:59:28 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2013/07/29/enumerate-user-profile-properties-in-sharepoint-2010-using-powershell/</guid>
      <description><![CDATA[#This script connects to the User Profile managed service application and iterates through all of...]]></description>
      <content:encoded><![CDATA[<p>#This script connects to the User Profile managed service application and iterates through all of the properties that have been configured dumping the result to XML. The script additionally pulls in any mappings to active directory.</p>
<p>&nbsp;</p>
<p>#Define our configuration. This is the name you gave the import connection to AD<br />$url="<a href="https://sharepoint.vallery.net/">https://sharepoint.vallery.net/</a>";<br />$connectionName="Profile Sync";</p>
<p>#Setup our SharePoint objects<br />$site=Get-SPSite$url;<br />$serviceContext= Get-SPServiceContext($site);<br />$upManager=new-objectMicrosoft.Office.Server.UserProfiles.UserProfileConfigManager($serviceContext);<br />$syncConnection=$upManager.ConnectionManager[$connectionName];</p>
<p>#This is a collection of mappings to AD that we will use later <br />$pmc=$syncConnection.PropertyMapping;</p>
<p>#This is a collection of all of the properties which we will iterate<br />$properties=$upManager.GetProperties();</p>
<p># Create a new XML writer settings object <br />$settings=New-Objectsystem.Xml.XmlWriterSettings;<br />$settings.Indent=$true; <br />$settings.OmitXmlDeclaration=$false; <br />$settings.NewLineOnAttributes=$true;</p>
<p># Create a new string writer to capture the output <br />$sw=new-objectSystem.IO.StringWriter;</p>
<p># Create a new XmlWriter <br />$writer= [system.xml.XmlWriter]::Create($sw, $settings);</p>
<p>#Start the document and add the root node<br />$writer.WriteStartDocument();<br />$writer.WriteStartElement("properties");</p>
<p>#Iterate through the properties<br />foreach ($item in $properties)<br />{<br />&nbsp;&nbsp;&nbsp; #Create the property element<br />&nbsp;$writer.WriteStartElement("property");</p>
<p>&nbsp;&nbsp;&nbsp; #Add in the fields as attributes<br />&nbsp;$writer.WriteAttributeString("Name", $item.Name); <br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("DisplayName",$item.DisplayName);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("ManagedPropertyName",$item.ManagedPropertyName);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("Type",$item.Type);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("ChoiceList",$item.ChoiceList);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("Description",$item.Description);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("URI",$item.URI);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsSystem",$item.IsSystem);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("AllowPolicyOverride",$item.AllowPolicyOverride);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsUserEditable",$item.IsUserEditable);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsAdminEditable",$item.IsAdminEditable);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsImported",$item.IsImported);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("Length",$item.Length);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsMultivalued",$item.IsMultivalued);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("ChoiceType",$item.ChoiceType);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("DefaultPrivacy",$item.DefaultPrivacy);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("UserOverridePrivacy",$item.UserOverridePrivacy);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsReplicable",$item.IsReplicable);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("PrivacyPolicy",$item.PrivacyPolicy);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("DisplayOrder",$item.DisplayOrder);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsColleagueEventLog",$item.IsColleagueEventLog);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsAlias",$item.IsAlias);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsSearchable",$item.IsSearchable);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsUpgrade",$item.IsUpgrade);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsUpgradePrivate",$item.IsUpgradePrivate);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsVisibleOnEditor",$item.IsVisibleOnEditor);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsVisibleOnViewer",$item.IsVisibleOnViewer);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsTaxonomic",$item.IsTaxonomic);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("Separator",$item.Separator);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("MaximumShown",$item.MaximumShown);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsSection",$item.IsSection);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsRequired",$item.IsRequired);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("SubtypeName",$item.SubtypeName);</p>
<p>&nbsp;&nbsp;&nbsp; #Look up any AD mappings in the PropertyManagerCollection and include them<br />&nbsp;$writer.WriteAttributeString("IsImport",$pmc.Item($item.Name).IsImport);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("IsExport",$pmc.Item($item.Name).IsExport);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("DataSourcePropertyName",$pmc.Item($item.Name).DataSourcePropertyName);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("OriginalDataSourcePropertyName",$pmc.Item($item.Name).OriginalDataSourcePropertyName);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("AssociationName",$pmc.Item($item.Name).AssociationName);<br />&nbsp;&nbsp;&nbsp; $writer.WriteAttributeString("Connection",$pmc.Item($item.Name).Connection.DisplayName);<br />&nbsp;&nbsp;&nbsp; $writer.WriteEndElement();</p>
<p>}</p>
<p>#Finish up<br />$writer.WriteEndElement();<br />$writer.WriteEndDocument();<br />$writer.Flush(); <br />$writer.Close();</p>
<p>#Capture the output into a string<br />$result=$sw.ToString();</p>
<p># Write the XML out<br />Write-Output$result;</p>]]></content:encoded>
    </item>
    <item>
      <title>Disable email notifications in SharePoint 2010</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/disable-email-notifications-in-sharepoint-2010</link>
      <pubDate>Mon, 29 Jul 2013 17:46:00 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2013/07/29/disable-email-notifications-in-sharepoint-2010/</guid>
      <description><![CDATA[Out of the box in SharePoint 2010 all users who have a profile are also defaulted to having email...]]></description>
      <content:encoded><![CDATA[<p>Out of the box in SharePoint 2010 all users who have a profile are also defaulted to having email notifications set to "on".&nbsp;&nbsp; This might make sense in a small scale implementation but for my customer that was unacceptable.&nbsp; I created a powershell script that iterates through the user profiles and turns the notifications off.&nbsp; This script is similar to ones I've posted before for working with the user profile however it uses different fields.&nbsp; The SharePoint field is called SPS-EmailOptin. This field disables both SharePoint and NewsGator emails.</p>
<p>Of course in future releases these field names might change so you should verify before using this script. &nbsp;In my case this was SharePoint 2010 October CU and NewsGator Social Sites 1.2.2419.</p>
<p>#Load the SharePoint snap-in<br />Add-PsSnapinMicrosoft.SharePoint.PowerShell;</p>
<p>#Load the SharePoint assemblies<br />[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server");<br />[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles");</p>
<p>#Specify the MySite URL<br />$MySiteUrl="<a href="https://sharepoint.vallery.net/">https://sharepoint.vallery.net/</a>";</p>
<p>#Get the server context for the profile manager<br />$site=Get-SPSite$MySiteUrl;<br />$ServerContext=Get-SPServiceContext$site;<br />$UPManager=new-objectMicrosoft.Office.Server.UserProfiles.UserProfileManager($ServerContext);</p>
<p>#Count variables$ucount=0;<br />$enumProfiles=$UPManager.GetEnumerator();<br />"Total User Profiles available:"+$UPManager.Count<br />$count=0;</p>
<p>#Loop through the profile entries and update the property<br />#Recieve Instant Notifications - NGAllowMetaEmail (bool)<br />#24 Hour Digest Email - NGReceiveDigestEmail (bool)<br />#RSS NewsFeed Email - NGAllowRssEmail (bool)<br />#SharePoint Notification emails - SPS-EmailOptin (int)<br />#This field has 3 values one for each email type<br />foreach ($oUserin$enumProfiles)<br />{<br />&nbsp;&nbsp;&nbsp; $count=$count+1;<br />&nbsp;&nbsp;&nbsp; $u=$oUser.Item("Accountname");<br />&nbsp;&nbsp;&nbsp; Write-Output"($count):&nbsp; Setting values for $u";</p>
<p>&nbsp;&nbsp;&nbsp; $oUser["NGAllowMetaEmail"].Value =$false;<br />&nbsp;&nbsp;&nbsp; $oUser["NGReceiveDigestEmail"].Value =$false;<br />&nbsp;&nbsp;&nbsp; $oUser["NGAllowRssEmail"].Value =$false;<br />&nbsp;&nbsp;&nbsp; $oUser["SPS-EmailOptin"].Value =111;</p>
<p>&nbsp;&nbsp;&nbsp; $oUser.Commit();<br />}</p>
<p>#Dispose of site object<br />$site.Dispose();</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>What tools to install on a SharePoint 2010 developer box</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/what-tools-to-install-on-a-sharepoint-2010-developer-box</link>
      <pubDate>Mon, 29 Jul 2013 17:45:40 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2013/07/29/what-tools-to-install-on-a-sharepoint-2010-developer-box/</guid>
      <description><![CDATA[I&rsquo;ve built out SharePoint development environments many times but I&rsquo;ve never really sat...]]></description>
      <content:encoded><![CDATA[<p>I&rsquo;ve built out SharePoint development environments many times but I&rsquo;ve never really sat down and made a list of the tools I use before.&nbsp;&nbsp; A good friend emailed me today and asked what tools I have on my SharePoint development box and it prompted me to put a list together.&nbsp;&nbsp; I thought I&rsquo;d share what tools fit into my day to day usage.</p>
<p>First a bit about the infrastructure and topology of my development environment.&nbsp;&nbsp; I&rsquo;ve got a dedicated VMWare ESXi 5.0 box in my basement with an i7-920, 24GB of ram, and 4x2TB hard drives in Raid 1+0.&nbsp; I&rsquo;m using an Adaptec hardware raid controller all inside of&nbsp; a Super Micro tower.</p>
<p>With that box I&rsquo;m able to run quite a number of virtual machines for a lot of different tasks beyond SharePoint development.&nbsp;&nbsp; I&rsquo;ve got a combination of Linux and Windows servers and clients setup for building and testing various applications.&nbsp;&nbsp; A couple boxes are mainstays however and provide resources that are shared across the rest.&nbsp;&nbsp; These are my domain controller and SQL box.&nbsp;&nbsp; Both are running server 2008R2.&nbsp;&nbsp; The SQL box has multiple SQL instances on it (2008R2 and 2012).&nbsp;&nbsp; I have a couple of different SharePoint VMs and fire them up depending on what I&rsquo;m working on.</p>
<p>When setting up the development box I always turn to AutoSPInstaller to actually install the SharePoint bits.&nbsp;&nbsp; It automates the process and makes it repeatable.&nbsp;&nbsp; It can handle installing the prequisites, configuring the farm, configuring service applications, creating web applications, creating site applications, etc.&nbsp;&nbsp; It is really very powerful and saves a ton of time in setting up a new environment.&nbsp;&nbsp; You can get it at <a href="https://autospinstaller.codeplex.com/">https://autospinstaller.codeplex.com/</a>.</p>
<p>&nbsp;</p>
<p>For my primary 2010 development box I also have the following tools installed:</p>
<ul>
<li>Server 2008R2 made to be just a bit more workstation friendly with tools and guide over at <a title="https://www.win2008r2workstation.com/" href="https://www.win2008r2workstation.com/">https://www.win2008r2workstation.com/</a></li>
<li>Visual Studio 2010 Ultimate</li>
<li>Community Kit for SharePoint Tools - <a href="https://cksdev.codeplex.com/">https://cksdev.codeplex.com/</a>, <a href="https://cks.codeplex.com/">https://cks.codeplex.com/</a></li>
<li>ULS Viewer for viewing log files - <a href="https://archive.msdn.microsoft.com/ULSViewer">https://archive.msdn.microsoft.com/ULSViewer</a></li>
<li>ILSpy for reflecting the SharePoint assemblies - <a href="https://wiki.sharpdevelop.net/ILSpy.ashx">https://wiki.sharpdevelop.net/ILSpy.ashx</a> --&nbsp;&nbsp; You could also use RedGate&rsquo;s reflector but it is no longer free.&nbsp;&nbsp; It is a better product but I don&rsquo;t feel like buying a license <a href="https://www.reflector.net/">https://www.reflector.net/</a></li>
<li>PowerGUI is really a must for tweaking configuration, deployment scripts, etc -&nbsp; <a href="https://powergui.org/index.jspa">https://powergui.org/index.jspa</a></li>
<li>Pex and Moles for unit tests - <a href="https://research.microsoft.com/en-us/projects/pex/">https://research.microsoft.com/en-us/projects/pex/</a></li>
<li>Fiddler for looking at raw HTTP requests - I&rsquo;ve been doing a lot of authentication work recently so this is important for me but maybe not for the average SP developer &ndash; <a href="https://www.fiddler2.com/fiddler2/">https://www.fiddler2.com/fiddler2/</a></li>
<li>SharePoint Manager allows you to explore site properties and settings which is handy - <a href="https://spm.codeplex.com/">https://spm.codeplex.com/</a></li>
<li>HttpWatch is good for performance tuning - <a href="https://www.httpwatch.com/">https://www.httpwatch.com/</a></li>
<li>Firefox with the Firebug add-on for looking at elements on the page and watching XmlHttpRequest when debugging AJAX code - <a title="https://www.mozilla.org/en-US/firefox/new/" href="https://www.mozilla.org/en-US/firefox/new/">https://www.mozilla.org/en-US/firefox/new/</a>, <a href="https://getfirebug.com/">https://getfirebug.com/</a></li>
</ul>
<p>And while I hesitate to admit it given how much I hate the headache it can cause, I do also use SharePoint Designer from time to time.&nbsp;&nbsp; While I&rsquo;m a firm believer that you shouldn&rsquo;t do SharePoint customizations with it (everything should be done via Visual Studio and packaged into a WSP) the truth is that SharePoint Designer is really good for working on masterpages.&nbsp;&nbsp; If you&rsquo;re doing branding work then I&rsquo;d recommend it be included into your tool arsenal.</p>]]></content:encoded>
    </item>
    <item>
      <title>Powershell one liner for finding lowest latency domain controller</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/powershell-one-liner-for-finding-lowest-latency-domain-controller</link>
      <pubDate>Mon, 29 Jul 2013 17:44:27 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2013/07/29/powershell-one-liner-for-finding-lowest-latency-domain-controller/</guid>
      <description><![CDATA[I needed to find a list of all of the domain controllers in a given domain and then ping them to...]]></description>
      <content:encoded><![CDATA[<p>I needed to find a list of all of the domain controllers in a given domain and then ping them to find which had the lowest latency. This is useful in order to find which DC will respond to LDAP requests faster for SharePoint user profile synchronization.</p>
<p>A quick powershell one-liner did the trick:</p>
<blockquote>
<p>[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() | % { $_.DomainControllers } | %{Test-Connection $_.Name}</p>
</blockquote>
<p>Similarly this one-liner connects to port 389 (LDAP) on the DC to ensure there is no firewall blocking. If the connection hits the timeout limit or failed it throws an exception.:</p>
<blockquote>
<p>[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() | % { $_.DomainControllers } | %{$o=new-object Net.Sockets.TcpClient;$o.Connect( $_.IPAddress, 389); new-object PSObject -Property @{name=$_.Name;connected=$o.Connected}}</p>
</blockquote>]]></content:encoded>
    </item>
    <item>
      <title>Client integration with SharePoint 2010 gotcha</title>
      <link>https://docs.microsoft.com/archive/blogs/javaller/client-integration-with-sharepoint-2010-gotcha</link>
      <pubDate>Mon, 29 Jul 2013 17:43:02 GMT</pubDate>
      <dc:creator><![CDATA[Jason Vallery]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/javaller/2013/07/29/client-integration-with-sharepoint-2010-gotcha/</guid>
      <description><![CDATA[Under the hood client integration in SharePoint uses WebDAV.&nbsp;&nbsp; SharePoint has its own...]]></description>
      <content:encoded><![CDATA[<p>Under the hood client integration in SharePoint uses WebDAV.&nbsp;&nbsp; SharePoint has its own implementation of the WebDAV server (bypassing the IIS ability).&nbsp;&nbsp; We had a problem in one of our environments where when a user would open a file in SharePoint it would always display as read-only in the office client application.&nbsp; In an "identical" environment that I had setup for testing the same process worked fine.</p>
<p>To troubleshoot I first opened up Fiddler to see what was happening under the hood.&nbsp;&nbsp; When word (or any office app) requests the file from SharePoint it first sends the OPTIONS verb to the folder in which the file lives.&nbsp; The request would come back from SharePoint as a 404 (even though the folder was there) with an interesting HTTP header.</p>
<blockquote>
<p>X-MS-InvokeApp: 1; RequireReadOnly</p>
</blockquote>
<p>When I did the same request on the environment that worked I would get a 200 with the expected list of valid operations:</p>
<blockquote>
<p>Allow: GET, POST, OPTIONS, HEAD, MKCOL, PUT, PROPFIND, PROPPATCH, DELETE, MOVE, COPY, GETLIB, LOCK, UNLOCK</p>
</blockquote>
<p>Digging further I discovered that in our production environment there was an additional entry in the web.config.&nbsp;&nbsp; This limited the OPTIONS verb.</p>
<blockquote>
<p>&lt;system.webServer&gt;</p>
<p>&lt;security&gt;</p>
<p>&lt;requestFiltering allowDoubleEscaping="true"&gt;</p>
<p>&lt;verbs allowUnlisted="true"&gt;</p>
<p>&lt;add verb="OPTIONS" allowed="false" /&gt;</p>
<p>&lt;add verb="PROPFIND" allowed="false" /&gt;</p>
<p>&lt;/verbs&gt;</p>
<p>&lt;requestLimits maxAllowedContentLength="2147483647" /&gt;</p>
<p>&lt;/requestFiltering&gt;</p>
<p>&lt;/security&gt;</p>
</blockquote>
<p>After removing the verbs section everything started working as expected.</p>]]></content:encoded>
    </item>
  </channel>
</rss>