Today I was asked to change some text to the email that is being sent to the users who's password is about to expire. Now being the quick & dirty developer that I was (.. really.. I'm a professional now), this line of text is hard coded in the source. So I had to open my old solution file and came to the discovery that this piece of code was still based on Password Reminder updated! and not utilizing the new features of .NET 3.5 where we have some actual AD support in the DirectoryServices namespace. Therefore I decided to update this solution to make use of it so here it is. The best part is that it cleans my code up quite drastically as well!
Instead of this :
DirectoryEntry entry = new DirectoryEntry("LDAP://domain.com"); DirectorySearcher search = new DirectorySearcher(entry); search.Filter = "(SAMAccountName=" + LogUser + ")"; SearchResult LDAPresult = search.FindOne(); entry = LDAPresult.GetDirectoryEntry(); // Pulling the informtion on when the password was last changed and converting it to a LargeInteger. LargeInteger liAcctPwdChange = entry.Properties["pwdLastSet"].Value as LargeInteger; // Convert the highorder/loworder parts of the property pulled to a long. long dateAcctPwdChange = (((long)(liAcctPwdChange.HighPart) << 32) + (long)liAcctPwdChange.LowPart); // Convert FileTime to DateTime and get what today's date is. DateTime dtNow = DateTime.Now; // I added 180 days because I know what my password expiration is set to, if not you need to pull that information and add the number of days it is set for. CultureInfo ci = new CultureInfo("nl-NL"); DateTime dtAcctPwdChange = DateTime.FromFileTime(dateAcctPwdChange).AddDays(180); string strAcctPwdChange = DateTime.FromFileTime(dateAcctPwdChange).ToString("d",ci); string strAcctPwdExpires = DateTime.FromFileTime(dateAcctPwdChange).AddDays(180).ToString("d",ci); // Calculate the difference between the date the pasword was changed, and what day it is now and display the # of days. TimeSpan time; time = dtAcctPwdChange - dtNow; if (time.Days < 20 && time.Days >= 0) { if (Convert.ToInt32(entry.Properties["userAccountControl"].Value.ToString() == 544)) { } }
We now have this :
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "DOMAIN", "DC=DOMAIN,DC=COM"); DateTime dt = DateTime.Today; PrincipalSearchResult<UserPrincipal> results = UserPrincipal.FindByPasswordSetTime(ctx, dt.AddDays(-160), MatchType.LessThanOrEquals); foreach (UserPrincipal result in results) { if ((bool)result.Enabled) { } }
Pretty good stuff eh? ;)
The very first zevenseas solution by my mate Daniel is publicly available as a beta for you to download. Now let me indulge you in the history of the solution.. First of all it was called ShareMark and while the name isn't that bad, it was not meant to actually share Mark but rather a bookmark. So hence the name "Tagged Links" ;) Secondly it got Daniel to learn C# and leaving his beloved VB.NET behind him (still amazed me that even working at Microsoft you could get away only knowing VB ;). And thirdly, like I said in the beginning of the post, this is the first real solution of zevenseas! Expect more (elegant, flexible, dreamy, topnotch, etc) solutions coming from us ;)
Check out the following posts to see what Tagged Links is all about :
Introducing the Tagged Links Solution (beta) Social Bookmarking for SharePoint
Tagged Links Walk-through
btw.. the thing that impressed me most was the view with the statistics and the tagcloud (as seen in the image below)
While patching an environment with SP1 I was stumbled by the fact that it took a really long time to complete the patch process. So I opened up the upgrade.log file (which you can find in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\LOGS folder). And there a hour long the following line was written :
[SPManager] [DEBUG] [4/19/2008 10:51:53 AM]: SyncUpgradeTimerJob: sleeping for 10 seconds
So I fired up Google and found the following post by Bill Bear : KB934525 Troubleshooting "Cannot start service SPAdmin on computer '.'." , now the answer was not directly found in the post but in the comments section where Chris Fields had the same problem I had. His problem was the following :
We noticed that in our from dev and stage farms this message was only logged for a minute or two before the upgrade process proceeded. We also noticed that the upgrade had stopped the sptimerv3 service but we had an automated MOM rule that had restarted it. We simply stopped sptimerv3 for a few minutes then re-started it. Within 5 minutes the upgrade had proceeded passed the sleeping phase and was upgrading databases. It completed successfully.
Acting on his solution I also restarted the SPTimer job, but didn't succeed to eliminate the the logging of the 'sleeping for 10 seconds' event. What I did next was to restart the SPAdmin service and after that restart the patch process happily continued and the environment had SP1 on it ;)
While testing the installation of SP1 on our new environment having four front-end servers the error "The installation of this package failed' popped up on two servers. Being the Google fanatic that I am, I found a KB article that explained the error and the error was related to having not enough space available. So I checked the space available and I had >5gb of storage available on the system drive so this was, as you may have guessed, not the problem. I opened up the eventviewer of the servers and find the following errors :
Event Type: Error Event Source: Service Control Manager Event Category: None Event ID: 7024 Date: 4/17/2008 Time: 12:37:26 PM User: N/A Computer: webserver1 Description: The IIS Admin Service service terminated with service-specific error 2149648394 (0x8021080A).
For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
Event Type: Error Event Source: IIS Config Event Category: Catalog Event ID: 4275 Date: 4/17/2008 Time: 12:37:26 PM User: N/A Computer: webserver1 Description: Error parsing XML file. Reason: An invalid character was found in text content. Incorrect XML: ErrorCode : 0xC00CE508 Interceptor : 14 OperationType : Populate Table : MBProperty ConfigurationSource : file:///C:/WINDOWS/system32/inetsrv/MetaBase.xml Row : 0 Column : 0
So next thing to Google was the "The IIS Admin Service service terminated with service-specific error 2149648394 " error! And luckily the first hit was The IIS Admin Service service terminated with service-specific error 2149648394 (0x8021080A). by Mike Hamilton ;) Strangely enough I wasn't the only with the same symptoms and problems and it was all related to the SP1 upgrade:
Interstingly, during that reboot - which for all intents should have gone fine - I see a hoard of DSKCHK processes running. Invariably, something got out of wack on the NTFS volumn between my shutting the image down and bringing it back up. During the 'fixing' process - a number of files invaribly can become corrupt - and the Metabase.xml - the blood-life of your IIS Admin service - is no exception.
Hope this helps someone ;)
So in addition to my 'get to know Robin better by reading his movie & xbox blog'-post, I now need to expose eight facts about myself because Bart Wessels has tagged me.. ;)
Well here goes :
And as the tradition goes.. I have to tag other bloggers so here goes !
As you may have noticed in my previous post I called the SSP in my code using hard-coded text..
string sspname = "SharedServices1"; ServerContext current = ServerContext.GetContext(sspname);
Apart from the fact that I'm a bit ashamed by doing this there was a reason why it was still hard-coded and that was because I couldn't find an easy way to determine if a SPWebApplication was a SSP. All the examples from Microsoft on TechNet show the following snippets when try to open a SSP in code :
Not very helpful eh ? :) Now with some Googling I found the following blog : Chris Hernandez's Blog with the following post Creating Shared Service Provider (SSP) via command-line options and in the comments section there is the answer to retrieve the SSP Web Applications while looping through the Web Applications. While you are looping there is a certain property in the propertybag that identifies what kind of webapplication it is and apparently for a SSP that key is :
spWebApp.Properties.ContainsKey("Microsoft.Office.Server.SharedResourceProvider")
Now we get all the WebApplications that use the Microsoft.Office.Server.SharedResourceProvider! Next step is to filter the MySites SSP from the Admin SSP ;) And that is pretty easy since we know that the mysites are using their own sitetemplates (MSITEHOST) and the admin page is created using the "OSRV" sitetemplate. By having narrowed down all possibilities we can get the ServerContext of our admin SSP :)
if (spWebApp.Sites[0].RootWeb.WebTemplate == "OSRV") { ServerContext SSP = ServerContext.GetContext(spWebApp.Sites[0]); }
So the code in total looks like this :
SPFarm farm = SPFarm.Local; SPWebService service = farm.Services.GetValue<SPWebService>(""); foreach (SPWebApplication spWebApp in service.WebApplications) { if (spWebApp.Properties.ContainsKey("Microsoft.Office.Server.SharedResourceProvider")) { if (spWebApp.Sites[0].RootWeb.WebTemplate == "OSRV") { ServerContext SSP = ServerContext.GetContext(spWebApp.Sites[0]); } } }
During the development of the LCM, I needed to know when a site is marked as 'unused' using the following three choices :
Now the first two choices are easily retrieved using the SPSite.LastSecurityModifiedDate and SPSite.LastContentModifiedDate methods. The third option is more of a challenge, but giving the fact that in V1 of the LifeCycleManagement source this was already developed (and nothing has changed since 2003->2007 in that area) I could re-use that code ;) Now the challenge here is that we have to determine first if Usage Analysis is properly set in the Central Admin, else we only get errors while retrieving the UsageDetails (SPWeb.GetUsageData)..
So I looked in the OM to find the first setting.. the so called "WSS Usage logging". To retrieve this setting you can use the following bit of code:
SPFarm farm = SPFarm.Local; SPWebService service = farm.Services.GetValue<SPWebService>(""); WSSUsageAnalysis = service.UsageSettings.UsageProcessingEnabled;
Up to the Office SharePoint Usage Processing setting! Now this is a bit harder since there is no OM available to get this option. Thankfully Gary Lapointe (the guy who is responsible for the excellent STSADM Custom Extensions blog) paved the way for me here! This post about Set Usage Analysis directed me in the right direction.. in order to get the setting we have the use reflection on the Microsoft.SharePoint.Portal.Analytics.Configuration class (pretty funny that Microsoft still uses the good 'ol .Portal namespace).
Now when we use reflection on this class we see the following methods we can use :
Well we want to know if Usage Analytics is set on the SSP so we go for the IsAnalyticsEnabledOnSrp method. All we have to do now is to create a instance of Microsoft.Office.Server.Administration.SharedResourceProvider (the SSP context). To do this we use the Microsoft.Office.Server namespace where we can use the ServerContext.GetContext("SSP Name") to have an instance. The code to do all this looks like this :
string sspname = "SharedServices1"; ServerContext current = ServerContext.GetContext(sspname); object sharedResourceProvider = current.GetType().GetProperty("SharedResourceProvider", AllBindings).GetValue(current, null); Type configurationType = Type.GetType("Microsoft.SharePoint.Portal.Analytics.Configuration, Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"); MethodInfo methodinfo = configurationType.GetMethod("IsAnalyticsEnabledOnSrp"); object IsAnalyticsEnabledOnSrp = methodinfo.Invoke(null, new object[] { sharedResourceProvider }); OfficeUsageAnalysis = (bool)IsAnalyticsEnabledOnSrp;
I'm still a bit of newbie when it comes to reflection so don't ask any specific details on what I'm doing here.. but it works! :)
And I assume that will be a lot (Google Analytics show that the majority of you are based in the US ;), I thought it was quite funny to show that even SharePoint makes it to a national newspaper here! It's an article about the fact that more and more (dutch) companies are investing in social networking and instant messaging and are embracing the "New way of Work" and that the tool for doing this is SharePoint. It also states that 65% of the "top 200 companies" in the Netherlands are using SharePoint today!
Here is the link to the dutch article : 'Steeds meer bedrijven omarmen sociale software'
So Joel.. maybe an opportunity? ;)
I was wondering about this matter since I developed the What's New webpart (like Jan did as well with the The What's New Web Part for SharePoint 2007) a couple of months ago but never took the time to do some metrics to see what the best way to go was. Fortunately there is Waldek Mastykarz! He posted a very nice post Content Query Web Part vs. Custom Aggregation Web Part with graphs and a very detailed plan what all the differences are and what the best solution is to aggregate information.
In addition of the announcement Want to blog about SharePoint using SharePoint? I want to say, this is not an attempt to be a competitor of SharePointBlogs.com! :)
At zevenseas we think the SharePoint community is one of the best communities around and the last thing we want is to have competition between the community sites and maybe even the bloggers themselves.
What we offer though is a free blogging environment that runs on SharePoint with the CKS:EBE (the Enhanced Blog Edition). So the more people are using it the better idea we all get on what is missing in the product and thus how we can improve it! A perfect example of such an improvement is what Daniel posted a couple of days ago Adding Search to the CKS-Enhanced Blog Edition (CKS-EBE)